Skip to content

Instantly share code, notes, and snippets.

@sorear
Created June 1, 2012 06:14
Show Gist options
  • Save sorear/2849465 to your computer and use it in GitHub Desktop.
Save sorear/2849465 to your computer and use it in GitHub Desktop.
ExtJS and node.js autodidacticism.
var AGELIMIT=120;
Ext.application({
name: 'HelloExt',
launch: function() {
// time vs realTime is a horrible hack. time is what gets displayed,
// it is 0-AGELIMIT always
Ext.define('LoadPoint', {
extend: 'Ext.data.Model',
fields: ['load','time','realTime']
});
var store = Ext.create('Ext.data.Store', {
proxy: {
type: 'memory'
},
model: 'LoadPoint'
});
var chart = Ext.create('Ext.chart.Chart', {
width: 400,
height: 300,
store: store,
axes: [
{
title: 'Load',
type: 'Numeric',
position: 'left',
fields: ['load'],
minimum: 0,
maximum: 5
},
{
title: 'Seconds',
type: 'Numeric',
position: 'bottom',
fields: ['time'],
minimum: 0,
maximum: AGELIMIT
}
],
series: [
{
type: 'line',
xField: 'time',
yField: 'load'
}
]
});
var timeAxis = chart.axes.get(1);
var fetchIndex = -1;
var loadResults = function(obj) {
if (fetchIndex != obj.first) {
console.log('server-side connection reset');
fetchIndex = obj.first;
store.removeAll();
}
fetchIndex = obj.first + obj.items.length;
var lastTime = obj.items[obj.items.length - 1].time;
// server always sends us at least one item, so we always have
// at least one here.
while (store.count() != 0 &&
lastTime - store.getAt(0).get('realTime') > AGELIMIT) {
store.removeAt(0);
}
//timeAxis.maximum = lastTime;
//timeAxis.minimum = lastTime - AGELIMIT;
for (var ix in obj.items) {
obj.items[ix].realTime = obj.items[ix].time;
var mis = store.add(obj.items[ix]);
}
var ct = store.count();
console.log(lastTime);
for (var ix = 0; ix < ct; ix++) {
var model = store.getAt(ix);
model.set('time', model.get('realTime') - lastTime + AGELIMIT);
console.log(model.get('time'));
console.log(model.get('realTime'));
}
//store.loadData(obj.items, true);
//console.log(timeAxis);
console.log(chart);
};
var fetch = function() {
Ext.Ajax.request({
url: '/tail?from=' + fetchIndex,
success: function(response) {
loadResults(Ext.JSON.decode(response.responseText));
fetch();
},
failure: function() {
// XXX set a 'Failed' banner
console.log('request failed');
setTimeout(60000, fetch);
}
});
};
Ext.create('Ext.container.Viewport', {
layout: 'fit',
items: [
chart
]
});
fetch();
}
});
<html>
<head>
<title>Load average thingy</title>
<script type="text/javascript" charset="utf-8" src="http://cdn.sencha.io/ext-4.1.0-gpl/ext-all.js"></script>
<link rel="stylesheet" type="text/css" href="http://cdn.sencha.io/ext-4.1.0-gpl/resources/css/ext-all.css">
<script type="text/javascript" src="app.js"></script>
</head>
<body></body>
</html>
var http = require('http');
var fs = require('fs');
var util = require('util');
var url = require('url');
var os = require('os');
var POLL_MS = 2000;
var KEEP_VALUES = 500;
// state - holds the length few minutes of uptime records
// first, max, length, and the numeric slots are part of the public API
// but only for reading.
function MeasureBuffer(max) {
this.max = max;
this.first = 0;
this.length = 0;
this.callbacks = new Array();
}
MeasureBuffer.prototype.slice = Array.prototype.slice;
MeasureBuffer.prototype.add = function (value) {
this[this.length++] = value;
if (this.length - this.first > this.max) {
delete this[this.first++];
}
// snapshot callback list so that recursive adds and waits won't cause
// problems
var callbacks = this.callbacks;
this.callbacks = new Array();
for (var cb in callbacks) {
callbacks[cb]();
}
};
MeasureBuffer.prototype.wait = function (callback) {
this.callbacks.push(callback);
};
var uptime = new MeasureBuffer(KEEP_VALUES);
setInterval(function () {
// get loadavg data, add to set, kick listeners
uptime.add({ time: process.uptime(), load: os.loadavg()[0] });
}, POLL_MS);
function tail(queue, req, res, from) {
var respond = function () {
res.writeHead(200, {'Content-Type': 'application/json'});
var bits = { first: from, items: queue.slice(from) };
console.log(util.inspect(queue, true));
console.log('-> ', JSON.stringify(bits));
res.end(JSON.stringify(bits));
};
from = (typeof from == 'undefined') ? -1 : Number(from);
// strictness on queue.length is essential here to allow updating
if (from < queue.first || from > queue.length) {
// Server crash, new client, or other situation which requires
// a protocol reset.
from = queue.first;
}
if (from == queue.length) {
// Don't return until at least one value is available
queue.wait(respond);
} else {
respond();
}
}
function serve_file(res, type, name) {
fs.readFile(name, function(err,data){
res.writeHead(200, {'Content-Type': type});
res.end(data);
});
}
http.createServer(function (req, res) {
console.log('<- ', req.url);
var q = url.parse(req.url, true);
if (q.pathname == '/tail') {
tail(uptime, req, res, q.query.from);
} else if (req.url == '/app.js') {
serve_file(res, 'application/javascript', 'app.js');
} else if (req.url == '/index.html') {
serve_file(res, 'text/html', 'index.html');
} else {
res.writeHead(404, {'Content-Type': 'text/plain'});
res.end('404 File Not Found\n');
}
}).listen(1337, "127.0.0.1");
console.log('Server running at http://127.0.0.1:1337/');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment