190e8449ce
- rename the directory from webgui to Phi - let the webserver's root point to Phi instead of Docs - redirect old /gui/ url to the new location (/)
352 lines
9.6 KiB
JavaScript
352 lines
9.6 KiB
JavaScript
define([
|
|
'jquery', 'knockout', 'text!./Properties.html', 'flot', 'flotresize', 'flottime'
|
|
], function(jquery, ko, htmlString) {
|
|
|
|
function SampleSource(prop, source, params) {
|
|
params = params || {};
|
|
|
|
this.source = source;
|
|
this.path = prop.path;
|
|
this.maxSamples = params.maxSamples || 100;
|
|
|
|
this.samples = [];
|
|
this.sample = function(timeStamp) {
|
|
while (this.samples.length >= this.maxSamples) {
|
|
this.samples.shift();
|
|
}
|
|
this.samples.push([
|
|
timeStamp, this.source()
|
|
]);
|
|
}
|
|
}
|
|
|
|
function PropertySampler(params) {
|
|
|
|
params = params || {};
|
|
|
|
this.sources = {};
|
|
this.sampleInterval = params.sampleInterval || 1000;
|
|
|
|
this.start = function() {
|
|
this.update(++this.updateId);
|
|
return this;
|
|
}
|
|
|
|
this.stop = function() {
|
|
++this.updateId;
|
|
return this;
|
|
}
|
|
|
|
this.addSource = function(source) {
|
|
this.sources[source.path] = source;
|
|
return this;
|
|
}
|
|
|
|
this.removeSource = function(source) {
|
|
var s = this.sources[source].source;
|
|
delete this.sources[source];
|
|
return s;
|
|
}
|
|
|
|
this.containsSource = function(source) {
|
|
return source in this.sources;
|
|
}
|
|
|
|
this.updateId = 0;
|
|
this.update = function(id) {
|
|
if (id != this.updateId)
|
|
return;
|
|
|
|
var now = Date.now();
|
|
for ( var key in this.sources) {
|
|
this.sources[key].sample(now);
|
|
}
|
|
|
|
var self = this;
|
|
setTimeout(function() {
|
|
self.update(id);
|
|
}, self.sampleInterval);
|
|
}
|
|
|
|
}
|
|
|
|
function PropertyViewModel(propertyPlotter) {
|
|
var self = this;
|
|
|
|
function load() {
|
|
jquery.get('/json' + self.path, null, function(data) {
|
|
self.hasChildren = data.nChildren > 0;
|
|
self.index = data.index;
|
|
self.type = data.type;
|
|
if (typeof (data.value) != 'undefined') {
|
|
self.value(data.value);
|
|
self.hasValue = true;
|
|
} else {
|
|
self.value('');
|
|
self.hasValue = false;
|
|
}
|
|
|
|
var a = [];
|
|
if (data.children) {
|
|
data.children.forEach(function(prop) {
|
|
var p = new PropertyViewModel(propertyPlotter);
|
|
p.name = prop.name;
|
|
p.path = prop.path;
|
|
p.index = prop.index;
|
|
p.type = prop.type;
|
|
p.hasChildren = prop.nChildren > 0;
|
|
if (typeof (prop.value) != 'undefined') {
|
|
p.value(prop.value);
|
|
p.hasValue = true;
|
|
} else {
|
|
p.hasValue = false;
|
|
}
|
|
a.push(p);
|
|
});
|
|
self.children(a.sort(function(a, b) {
|
|
if (a.name == b.name) {
|
|
return a.index - b.index;
|
|
}
|
|
return a.name.localeCompare(b.name);
|
|
}));
|
|
}
|
|
|
|
});
|
|
}
|
|
self.name = '';
|
|
self.value = ko.observable('');
|
|
self.children = ko.observableArray([]);
|
|
self.index = 0;
|
|
self.path = '';
|
|
self.hasChildren = false;
|
|
self.hasValue = false;
|
|
self.type = '';
|
|
|
|
self.indexedName = ko.pureComputed(function() {
|
|
if (0 == self.index)
|
|
return self.name;
|
|
return self.name + "[" + self.index + "]";
|
|
});
|
|
|
|
self.isExpanded = ko.observable(false);
|
|
self.isExpanded.subscribe(function(newValue) {
|
|
if (newValue) {
|
|
load();
|
|
} else {
|
|
self.children.removeAll();
|
|
}
|
|
});
|
|
|
|
self.isPlottable = ko.pureComputed(function() {
|
|
return [
|
|
"double", "float", "int"
|
|
].indexOf(self.type) != -1;
|
|
});
|
|
|
|
self.toggle = function() {
|
|
if (self.hasChildren) {
|
|
self.isExpanded(!self.isExpanded());
|
|
} else {
|
|
load();
|
|
}
|
|
}
|
|
|
|
self.togglePlot = function(prop, evt) {
|
|
propertyPlotter.toggleProp(prop);
|
|
}
|
|
|
|
self.valueEdit = function(prop, evt) {
|
|
var inplaceEditor = jquery(jquery('#inplace-editor-template').html());
|
|
|
|
var elem = jquery(evt.target);
|
|
elem.hide();
|
|
elem.after(inplaceEditor);
|
|
inplaceEditor.val(elem.text());
|
|
inplaceEditor.focus();
|
|
|
|
function endEdit(val) {
|
|
inplaceEditor.remove();
|
|
elem.show();
|
|
|
|
if (typeof (val) === 'undefined')
|
|
return;
|
|
var val = val.trim();
|
|
elem.text(val);
|
|
|
|
jquery.post('/json' + self.path, JSON.stringify({
|
|
value : val
|
|
}));
|
|
}
|
|
|
|
inplaceEditor.on('keyup', function(evt) {
|
|
switch (evt.keyCode) {
|
|
case 27:
|
|
endEdit();
|
|
break;
|
|
case 13:
|
|
endEdit(inplaceEditor.val());
|
|
break;
|
|
}
|
|
});
|
|
|
|
inplaceEditor.blur(function() {
|
|
endEdit(inplaceEditor.val());
|
|
});
|
|
}
|
|
}
|
|
|
|
function ViewModel(params) {
|
|
var self = this;
|
|
|
|
self.root = new PropertyViewModel(self);
|
|
self.root.name = "root";
|
|
self.root.path = "/";
|
|
self.root.isExpanded(true);
|
|
self.properties = self.root.children;
|
|
|
|
self.startLabel = ko.pureComputed(function() {
|
|
return self.running() ? "Pause" : "Start";
|
|
});
|
|
|
|
self.startIcons = ko.pureComputed(function() {
|
|
return self.running() ? {
|
|
primary : 'ui-icon-pause'
|
|
} : {
|
|
primary : 'ui-icon-play'
|
|
};
|
|
});
|
|
|
|
self.settings = function() {
|
|
}
|
|
|
|
self.running = ko.observable(false);
|
|
self.startPause = function() {
|
|
if( self.running() ) {
|
|
self.stop();
|
|
} else {
|
|
self.start();
|
|
}
|
|
}
|
|
|
|
self.flotOptions = ko.observable({
|
|
xaxes : [
|
|
{
|
|
mode : "time"
|
|
}
|
|
],
|
|
yaxes : [
|
|
{
|
|
position : "right"
|
|
}, {
|
|
position : "left"
|
|
}
|
|
|
|
],
|
|
legend : {
|
|
show : true,
|
|
labelFormatter: null,
|
|
backgroundOpacity: 0.5,
|
|
sorted: "ascending",
|
|
},
|
|
grid : {
|
|
hoverable : false,
|
|
backgroundColor: { colors: ["#eee", "#888"] }
|
|
}
|
|
});
|
|
|
|
self.flotData = ko.observableArray([]);
|
|
|
|
self.graphHover = function() {
|
|
}
|
|
|
|
self.hasGraphItems = ko.pureComputed(function() {
|
|
return self.flotData().length > 0;
|
|
});
|
|
|
|
self.propertySampler = new PropertySampler({
|
|
sampleInterval : 100,
|
|
});
|
|
|
|
self.propertySampler.start();
|
|
self.running(true);
|
|
|
|
self.toggleProp = function(prop) {
|
|
|
|
if (self.propertySampler.containsSource(prop.path)) {
|
|
var obs = self.propertySampler.removeSource(prop.path);
|
|
ko.utils.knockprops.removeListener(prop.path, obs);
|
|
return;
|
|
}
|
|
|
|
var obs = ko.observable(0);
|
|
ko.utils.knockprops.addListener(prop.path, obs);
|
|
self.propertySampler.addSource(new SampleSource(prop, obs, {
|
|
maxSamples : 300,
|
|
}));
|
|
}
|
|
|
|
self.updateId = 0;
|
|
self.update = function(id) {
|
|
|
|
if (self.updateId != id)
|
|
return;
|
|
|
|
var sources = self.propertySampler.sources;
|
|
var data = [];
|
|
|
|
var i = 1;
|
|
for ( var key in sources) {
|
|
var source = sources[key];
|
|
data.push({
|
|
// color : 'rgb(192, 128, 0)',
|
|
data : source.samples,
|
|
label : key,
|
|
lines : {
|
|
show : true
|
|
},
|
|
points : {
|
|
show : false
|
|
},
|
|
bars : {
|
|
show : false
|
|
},
|
|
shadowSize : 0,
|
|
yaxis : i++,
|
|
});
|
|
}
|
|
|
|
self.flotData(data);
|
|
|
|
setTimeout(function() {
|
|
self.update(id);
|
|
}, 100);
|
|
}
|
|
|
|
self.start = function() {
|
|
self.update(++self.updateId);
|
|
self.propertySampler.start();
|
|
self.running(true);
|
|
}
|
|
|
|
self.stop = function() {
|
|
self.updateId++;
|
|
self.propertySampler.stop();
|
|
self.running(false);
|
|
}
|
|
|
|
self.start();
|
|
|
|
}
|
|
|
|
ViewModel.prototype.dispose = function() {
|
|
console.log("disposing pal");
|
|
this.propertySampler.stop();
|
|
this.updateId++;
|
|
}
|
|
|
|
// Return component definition
|
|
return {
|
|
viewModel : ViewModel,
|
|
template : htmlString
|
|
};
|
|
});
|