Merge branch 'xsite-middleware'
Conflicts: htdocs/frontend/index.html htdocs/frontend/javascripts/wui.js
This commit is contained in:
commit
81d64f0f69
13 changed files with 565 additions and 528 deletions
BIN
htdocs/frontend/images/cross.png
Normal file
BIN
htdocs/frontend/images/cross.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 655 B |
BIN
htdocs/frontend/images/tick.png
Normal file
BIN
htdocs/frontend/images/tick.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 537 B |
|
@ -15,21 +15,17 @@
|
|||
<script type="text/javascript" src="javascripts/jquery/jquery-treeTable.js"></script>
|
||||
<script type="text/javascript" src="javascripts/jquery/jquery-extensions.js"></script>
|
||||
|
||||
<!-- misc helpers -->
|
||||
<script type="text/javascript" src="javascripts/helper.js"></script>
|
||||
|
||||
<!--[if IE]><script language="javascript" type="text/javascript" src="javascripts/flot/excanvas.min.js"></script><![endif]-->
|
||||
<script type="text/javascript" src="javascripts/flot/jquery.flot.js"></script>
|
||||
<script type="text/javascript" src="javascripts/flot/jquery.flot.crosshair.js"></script>
|
||||
<script type="text/javascript" src="javascripts/flot/jquery.flot.selection.js"></script>
|
||||
|
||||
<!-- volkszaehler.org code -->
|
||||
<script type="text/javascript" src="javascripts/init.js"></script>
|
||||
<script type="text/javascript" src="javascripts/options.js"></script>
|
||||
<script type="text/javascript" src="javascripts/functions.js"></script>
|
||||
<script type="text/javascript" src="javascripts/entity.js"></script>
|
||||
<script type="text/javascript" src="javascripts/entities.js"></script>
|
||||
<script type="text/javascript" src="javascripts/wui.js"></script>
|
||||
<script type="text/javascript" src="javascripts/helper.js"></script>
|
||||
<script type="text/javascript" src="javascripts/functions.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="stylesheets/ui-lightness/jquery-ui-1.8.10.css" />
|
||||
<link rel="stylesheet" type="text/css" href="stylesheets/jquery.treeTable.css">
|
||||
|
@ -137,7 +133,7 @@
|
|||
<div id="entity-subscribe">
|
||||
<p>Hier können Sie einen existierenden Kanal über seine UUID hinzufügen</p>
|
||||
<table>
|
||||
<tr><td><label for="middleware">Middleware:</label></td><td><input id="middleware" type="text" size="36" /></td></tr>
|
||||
<tr><td><label for="middleware">Middleware URL:</label></td><td><input id="middleware" type="text" /></td></tr>
|
||||
<tr><td><label for="uuid">UUID:</label></td><td><input id="uuid" type="text" size="36" maxlength="36" /></td></tr>
|
||||
</table>
|
||||
<p><input type="button" value="Abonnieren" /> <label for="cookie">Cookie:</label> <input class="cookie" type="checkbox" /></p>
|
||||
|
@ -145,24 +141,26 @@
|
|||
<div id="entity-subscribe-public">
|
||||
<p>Hier können Sie öffentliche Kanäle abonnieren.</p>
|
||||
<table>
|
||||
<tr><td><label for="middleware">Middleware:</label></td><td><select id="middleware" /></td></tr>
|
||||
<tr><td><label for="middleware">Middleware:</label></td><td><select id="middleware"><option value="../middleware.php">default (local)</option></select></td></tr>
|
||||
<tr><td><label for="uuid">Kanal:</label></td><td><select id="public" size="1"></select></td></tr>
|
||||
</table>
|
||||
<p><input type="button" value="Abonnieren" /> <label for="cookie">Cookie:</label> <input class="cookie" type="checkbox" /></p>
|
||||
</div>
|
||||
<div id="entity-create">
|
||||
<form method="post" target="_blank">
|
||||
<form method="get" target="_blank">
|
||||
<table>
|
||||
<tr><td>Middleware URL:</td><td><input type="text" id="entity-create-middlware" /><p style="font-size: small">Bsp: http://volkszaehler.org/demo/middleware.php</p></td></tr>
|
||||
<tr><td>Typ:</td><td><select name="type" size="1"></select></td></tr>
|
||||
<tr><td>Öffentlich:</td><td><input type="radio" name="public" value="1"> ja<br /><input type="radio" name="public" value="0"> nein</td></tr>
|
||||
<tr><td>Titel:</td><td><input type="text" name="title" value="Kühlschrank" /></td></tr>
|
||||
<tr><td>Auflösung:</td><td><input type="text" name="resolution" value="1000" /></td></tr>
|
||||
<tr><td>Kosten:<br /><p style="font-size: small">pro Wh</p></td><td><input type="text" name="cost" value="0.00025" /> €</td></tr>
|
||||
<tr><td>Beschreibung:</td><td><input type="text" name="description" value="Swissnox 1-phasig" /></td></tr>
|
||||
<tr class="property"><th>Eigenschaft</th><th>Wert</th></tr>
|
||||
<tr class="property"><td>Middleware URL:</td><td><input type="text" id="entity-create-middlware" value="../middleware.php" /></td></tr>
|
||||
<tr class="property"><td>Typ:</td><td><select name="type" size="1"></select></td></tr>
|
||||
<tr class="property"><td>Öffentlich:</td><td><input type="radio" name="public" value="1"> ja <input type="radio" name="public" value="0"> nein</td></tr>
|
||||
<tr class="property"><td>Titel:</td><td><input type="text" name="title" value="Kühlschrank" /></td></tr>
|
||||
<tr class="property"><td>Auflösung:</td><td><input type="text" name="resolution" value="1000" /></td></tr>
|
||||
<tr class="property"><td>Kosten:<br /><p style="font-size: small">pro Wh</p></td><td><input type="text" name="cost" value="0.00025" /> €</td></tr>
|
||||
<tr class="property"><td>Beschreibung:</td><td><input type="text" name="description" value="Swissnox 1-phasig" /></td></tr>
|
||||
</table>
|
||||
<input type="hidden" name="operation" value="add" />
|
||||
<p><input type="submit" value="Erstellen" /></p>
|
||||
<p>Es wird ein neuer Kanal erstellt. Die Antwort der Middlewarenthält dann die UUID.<br />
|
||||
<p>Es wird ein neuer Kanal erstellt. Die Antwort der Middleware enthält dann die UUID.<br />
|
||||
Diese muss dann im Controller angegeben werden (z.B. AVR Net-IO).</p>
|
||||
</form>
|
||||
</div>
|
||||
|
|
191
htdocs/frontend/javascripts/entities.js
Normal file
191
htdocs/frontend/javascripts/entities.js
Normal file
|
@ -0,0 +1,191 @@
|
|||
/**
|
||||
*
|
||||
*
|
||||
* @author Florian Ziegler <fz@f10-home.de>
|
||||
* @author Justin Otherguy <justin@justinotherguy.org>
|
||||
* @author Steffen Vogel <info@steffenvogel.de>
|
||||
* @copyright Copyright (c) 2011, The volkszaehler.org project
|
||||
* @package default
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*/
|
||||
/*
|
||||
* This file is part of volkzaehler.org
|
||||
*
|
||||
* volkzaehler.org is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or any later version.
|
||||
*
|
||||
* volkzaehler.org is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Save minimal Entity in JSON cookie
|
||||
*/
|
||||
vz.entities.saveCookie = function() {
|
||||
var expires = new Date(new Date().getTime() + 3e10); // in about a year
|
||||
var arr = new Array;
|
||||
|
||||
this.each(function(entity) {
|
||||
if (entity.cookie === true) {
|
||||
arr.push(entity.uuid + '@' + entity.middleware);
|
||||
}
|
||||
}, true); // recursive!
|
||||
|
||||
$.setCookie('vz_entities', arr.join('|'), {expires: expires});
|
||||
};
|
||||
|
||||
/**
|
||||
* Load entities from JSON cookie
|
||||
*/
|
||||
vz.entities.loadCookie = function() {
|
||||
var cookie = $.getCookie('vz_entities');
|
||||
if (cookie) {
|
||||
var arr = cookie.split('|');
|
||||
arr.each(function(index, entry) {
|
||||
var entity = entry.split('@');
|
||||
vz.entities.push(new Entity({
|
||||
middleware: entity[1],
|
||||
uuid: entity[0],
|
||||
cookie: true
|
||||
}));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Load JSON data from the middleware
|
||||
*/
|
||||
vz.entities.loadData = function() {
|
||||
$('#overlay').html('<img src="images/loading.gif" alt="loading..." /><p>loading...</p>');
|
||||
|
||||
var queue = new Array;
|
||||
this.each(function(entity) {
|
||||
if (entity.active && entity.definition.model == 'Volkszaehler\\Model\\Channel') {
|
||||
queue.push(entity.loadData());
|
||||
}
|
||||
}, true); // recursive!
|
||||
|
||||
return $.when.apply($, queue);
|
||||
};
|
||||
|
||||
/**
|
||||
* Overwritten each iterator to iterate recursively throug all entities
|
||||
*/
|
||||
vz.entities.each = function(cb, recursive) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
cb(this[i]);
|
||||
|
||||
if (recursive) {
|
||||
this[i].each(cb, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create nested entity list
|
||||
*
|
||||
* @todo move to Entity class
|
||||
*/
|
||||
vz.entities.showTable = function() {
|
||||
$('#entity-list tbody').empty();
|
||||
|
||||
vz.entities.sort(Entity.compare);
|
||||
|
||||
var c = 0; // for colors
|
||||
this.each(function(entity, parent) {
|
||||
entity.color = vz.options.plot.colors[c++ % vz.options.plot.colors.length];
|
||||
$('#entity-list tbody').append(entity.getDOMRow(parent));
|
||||
}, true); // recursive!
|
||||
|
||||
/*
|
||||
* Initialize treeTable
|
||||
*
|
||||
* http://ludo.cubicphuse.nl/jquery-plugins/treeTable/doc/index.html
|
||||
* https://github.com/ludo/jquery-plugins/tree/master/treeTable
|
||||
*/
|
||||
// configure entities as draggable
|
||||
$('#entity-list tr.channel span.indicator, #entity-list tr.aggregator span.indicator').draggable({
|
||||
helper: 'clone',
|
||||
opacity: 0.75,
|
||||
refreshPositions: true, // Performance?
|
||||
revert: 'invalid',
|
||||
revertDuration: 300,
|
||||
scroll: true
|
||||
});
|
||||
|
||||
// configure aggregators as droppable
|
||||
$('#entity-list tr.aggregator span.indicator').each(function() {
|
||||
$(this).parents('tr').droppable({
|
||||
//accept: 'tr.channel span.indicator, tr.aggregator span.indicator', // TODO
|
||||
drop: function(event, ui) {
|
||||
var child = $(ui.draggable.parents('tr')[0]).data('entity');
|
||||
var from = child.parent;
|
||||
var to = $(this).data('entity');
|
||||
|
||||
$('#entity-move').dialog({ // confirm prompt
|
||||
resizable: false,
|
||||
modal: true,
|
||||
title: 'Verschieben',
|
||||
width: 400,
|
||||
buttons: {
|
||||
'Verschieben': function() {
|
||||
try {
|
||||
var queue = new Array;
|
||||
queue.push(to.addChild(child)); // add to new aggregator
|
||||
|
||||
if (from !== undefined) {
|
||||
queue.push(from.removeChild(child)); // remove from aggregator
|
||||
}
|
||||
else {
|
||||
child.cookie = false; // remove from cookies
|
||||
vz.entities.saveCookie();
|
||||
}
|
||||
} catch (e) {
|
||||
vz.wui.dialogs.exception(e);
|
||||
} finally {
|
||||
$.when(queue).done(function() {
|
||||
// wait for middleware
|
||||
$.when(from.loadDetails(), to.loadDetails).done(vz.entities.showDetails);
|
||||
});
|
||||
$(this).dialog('close');
|
||||
}
|
||||
},
|
||||
'Abbrechen': function() {
|
||||
$(this).dialog('close');
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
hoverClass: 'accept',
|
||||
over: function(event, ui) {
|
||||
// make the droppable branch expand when a draggable node is moved over it
|
||||
if (this.id != $(ui.draggable.parents('tr')[0]).id && !$(this).hasClass('expanded')) {
|
||||
$(this).expand();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// make visible that a row is clicked
|
||||
$('#entity-list table tbody tr').mousedown(function() {
|
||||
$('tr.selected').removeClass('selected'); // deselect currently selected rows
|
||||
$(this).addClass('selected');
|
||||
});
|
||||
|
||||
// make sure row is selected when span is clicked
|
||||
$('#entity-list table tbody tr span').mousedown(function() {
|
||||
$($(this).parents('tr')[0]).trigger('mousedown');
|
||||
});
|
||||
|
||||
$('#entity-list table').treeTable({
|
||||
treeColumn: 2,
|
||||
clickableNodeNames: true,
|
||||
initialState: 'expanded'
|
||||
});
|
||||
};
|
|
@ -1,7 +1,6 @@
|
|||
/**
|
||||
* Entity handling, parsing & validation
|
||||
*
|
||||
* @author Florian Ziegler <fz@f10-home.de>
|
||||
* @author Justin Otherguy <justin@justinotherguy.org>
|
||||
* @author Steffen Vogel <info@steffenvogel.de>
|
||||
* @copyright Copyright (c) 2011, The volkszaehler.org project
|
||||
|
@ -28,23 +27,70 @@
|
|||
* Entity constructor
|
||||
* @todo add validation
|
||||
*/
|
||||
var Entity = function(json, parent) {
|
||||
$.extend(true, this, json);
|
||||
this.parent = parent;
|
||||
|
||||
var Entity = function(json) {
|
||||
this.parseJSON(json);
|
||||
|
||||
if (this.active === undefined) {
|
||||
this.active = true; // active by default
|
||||
this.active = true; // activate by default
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
Entity.prototype.parseJSON = function(json) {
|
||||
$.extend(true, this, json);
|
||||
|
||||
if (this.children) {
|
||||
for (var i = 0; i < this.children.length; i++) {
|
||||
this.children[i] = new Entity(this.children[i], this);
|
||||
};
|
||||
this.children[i] = new Entity(this.children[i]);
|
||||
}
|
||||
|
||||
this.children.sort(Entity.compare);
|
||||
}
|
||||
|
||||
if (this.type !== undefined) {
|
||||
this.definition = vz.capabilities.definitions.get('entities', this.type);
|
||||
}
|
||||
};
|
||||
|
||||
this.definition = vz.capabilities.definitions.get('entities', this.type);
|
||||
/**
|
||||
* Query middleware for details
|
||||
*/
|
||||
Entity.prototype.loadDetails = function() {
|
||||
return vz.load({
|
||||
url: this.middleware,
|
||||
controller: 'entity',
|
||||
identifier: this.uuid,
|
||||
context: this,
|
||||
success: function(json) {
|
||||
this.parseJSON(json.entity);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Entity.prototype.loadData = function() {
|
||||
return vz.load({
|
||||
controller: 'data',
|
||||
url: this.middleware,
|
||||
identifier: this.uuid,
|
||||
context: this,
|
||||
data: {
|
||||
from: Math.floor(vz.options.plot.xaxis.min),
|
||||
to: Math.ceil(vz.options.plot.xaxis.max),
|
||||
tuples: vz.options.tuples
|
||||
},
|
||||
success: function(json) {
|
||||
this.data = json.data;
|
||||
|
||||
if (this.data.count > 0) {
|
||||
if (this.data.min[1] < vz.options.plot.yaxis.min) { // allow negative values for temperature sensors
|
||||
vz.options.plot.yaxis.min = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.updateDOMRow();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -53,7 +99,7 @@ var Entity = function(json, parent) {
|
|||
Entity.prototype.showDetails = function() {
|
||||
$('<div>')
|
||||
.addClass('details')
|
||||
.append(this.getDOM())
|
||||
.append(this.getDOMDetails())
|
||||
.dialog({
|
||||
title: 'Details für ' + this.title,
|
||||
width: 480,
|
||||
|
@ -66,63 +112,98 @@ Entity.prototype.showDetails = function() {
|
|||
*
|
||||
* @todo implement/test
|
||||
*/
|
||||
Entity.prototype.getDOM = function(edit) {
|
||||
Entity.prototype.getDOMDetails = function(edit) {
|
||||
var table = $('<table><thead><tr><th>Eigenschaft</th><th>Wert</th></tr></thead></table>');
|
||||
var data = $('<tbody>');
|
||||
|
||||
for (var property in this) {
|
||||
if (this.hasOwnProperty(property) && !['data', 'definition', 'children', 'parent'].contains(property)) {
|
||||
switch(property) {
|
||||
case 'type':
|
||||
var title = 'Typ';
|
||||
var value = this.definition.translation[vz.options.language];
|
||||
break;
|
||||
|
||||
case 'uuid':
|
||||
var title = 'UUID';
|
||||
var value = '<a href="' + vz.options.middlewareUrl + '/entity/' + this[property] + '.json">' + this[property] + '</a>';
|
||||
break;
|
||||
|
||||
// general properties
|
||||
var general = ['uuid', 'middleware', 'type', 'color', 'cookie'];
|
||||
var sections = ['required', 'optional'];
|
||||
|
||||
general.each(function(index, property) {
|
||||
switch(property) {
|
||||
case 'type':
|
||||
var title = 'Typ';
|
||||
var value = this.definition.translation[vz.options.language];
|
||||
break;
|
||||
|
||||
case 'color':
|
||||
var title = 'Farbe';
|
||||
var value = '<span style="background-color: ' + this[property] + '">' + this[property] + '</span>';
|
||||
break;
|
||||
|
||||
case 'public':
|
||||
var title = vz.capabilities.definitions.get('properties', property).translation[vz.options.language];
|
||||
var value = (this[property]) ? 'ja' : 'nein';
|
||||
break;
|
||||
|
||||
case 'middleware':
|
||||
var title = 'Middleware';
|
||||
var value = '<a href="' + this.middleware + '/capabilities.json">' + this.middleware + '</a>';
|
||||
break;
|
||||
|
||||
case 'active':
|
||||
var title = 'Aktiv';
|
||||
var value = (this[property]) ? 'ja' : 'nein';
|
||||
break;
|
||||
|
||||
default:
|
||||
var title = vz.capabilities.definitions.get('properties', property).translation[vz.options.language];
|
||||
var value = this[property];
|
||||
}
|
||||
|
||||
data.append($('<tr>')
|
||||
.append($('<td>')
|
||||
.addClass('key')
|
||||
.text(title)
|
||||
)
|
||||
.append($('<td>')
|
||||
.addClass('value')
|
||||
.append(value)
|
||||
)
|
||||
);
|
||||
case 'uuid':
|
||||
var title = 'UUID';
|
||||
var value = '<a href="' + this.middleware + '/entity/' + this.uuid + '.json">' + this.uuid + '</a>';
|
||||
break;
|
||||
|
||||
case 'color':
|
||||
var title = 'Farbe';
|
||||
var value = '<span style="background-color: ' + this.color + '">' + this.color + '</span>';
|
||||
break;
|
||||
|
||||
case 'cookie':
|
||||
var title = 'Cookie';
|
||||
value = '<img src="images/' + ((this.cookie) ? 'tick' : 'cross') + '.png" alt="' + ((value) ? 'ja' : 'nein') + '" />';
|
||||
break;
|
||||
case 'active':
|
||||
var title = 'Aktiv';
|
||||
var value = '<img src="images/' + ((this.active) ? 'tick' : 'cross') + '.png" alt="' + ((this.active) ? 'ja' : 'nein') + '" />';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data.append($('<tr>')
|
||||
.addClass('property')
|
||||
.addClass('general')
|
||||
.append($('<td>')
|
||||
.addClass('key')
|
||||
.text(title)
|
||||
)
|
||||
.append($('<td>')
|
||||
.addClass('value')
|
||||
.append(value)
|
||||
)
|
||||
);
|
||||
}, this);
|
||||
|
||||
sections.each(function(index, section) {
|
||||
this.definition[section].each(function(index, property) {
|
||||
if (this.hasOwnProperty(property)) {
|
||||
var definition = vz.capabilities.definitions.get('properties', property);
|
||||
var title = definition.translation[vz.options.language];
|
||||
var value = this[property];
|
||||
|
||||
if (definition.type == 'boolean') {
|
||||
value = '<img src="images/' + ((value) ? 'tick' : 'cross') + '.png" alt="' + ((value) ? 'ja' : 'nein') + '" />';
|
||||
}
|
||||
|
||||
if (property == 'cost') {
|
||||
value = (value * 1000 * 100) + ' ct/k' + this.definition.unit + 'h'; // ct per kWh
|
||||
}
|
||||
|
||||
data.append($('<tr>')
|
||||
.addClass('property')
|
||||
.addClass(section)
|
||||
.append($('<td>')
|
||||
.addClass('key')
|
||||
.text(title)
|
||||
)
|
||||
.append($('<td>')
|
||||
.addClass('value')
|
||||
.append(value)
|
||||
)
|
||||
);
|
||||
}
|
||||
}, this);
|
||||
}, this);
|
||||
return table.append(data);
|
||||
};
|
||||
|
||||
Entity.prototype.getRow = function() {
|
||||
Entity.prototype.getDOMRow = function(parent) {
|
||||
var row = $('<tr>')
|
||||
.addClass((this.parent) ? 'child-of-entity-' + this.parent.uuid : '')
|
||||
.addClass((parent) ? 'child-of-entity-' + parent.uuid : '')
|
||||
.addClass((this.definition.model == 'Volkszaehler\\Model\\Aggregator') ? 'aggregator' : 'channel')
|
||||
.addClass('entity')
|
||||
.attr('id', 'entity-' + this.uuid)
|
||||
.append($('<td>')
|
||||
.addClass('visibility')
|
||||
|
@ -170,82 +251,62 @@ Entity.prototype.getRow = function() {
|
|||
)
|
||||
)
|
||||
.data('entity', this);
|
||||
|
||||
if (vz.uuids.contains(this.uuid)) { // removable from cookies?
|
||||
|
||||
if (this.cookie) {
|
||||
$('td.ops', row).prepend($('<input>')
|
||||
.attr('type', 'image')
|
||||
.attr('src', 'images/delete.png')
|
||||
.attr('alt', 'delete')
|
||||
.bind('click', this, function(event) {
|
||||
vz.uuids.remove(event.data.uuid);
|
||||
vz.uuids.save();
|
||||
|
||||
vz.entities.remove(event.data);
|
||||
vz.entities.saveCookie();
|
||||
vz.entities.showTable();
|
||||
|
||||
vz.wui.drawPlot();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return row;
|
||||
};
|
||||
|
||||
Entity.prototype.loadData = function() {
|
||||
return vz.load({
|
||||
controller: 'data',
|
||||
identifier: this.uuid,
|
||||
context: this,
|
||||
data: {
|
||||
from: Math.floor(vz.options.plot.xaxis.min),
|
||||
to: Math.ceil(vz.options.plot.xaxis.max),
|
||||
tuples: vz.options.tuples
|
||||
},
|
||||
success: function(json) {
|
||||
this.data = json.data;
|
||||
|
||||
var year = 60*60*24*365*1000; /* in seconds */
|
||||
var delta = this.data.to - this.data.from;
|
||||
Entity.prototype.updateDOMRow = function() {
|
||||
var row = $('#entity-' + this.uuid);
|
||||
|
||||
var delta = this.data.to - this.data.from;
|
||||
var year = 365*24*60*60*1000;
|
||||
|
||||
if (this.data.count > 0) { // update statistics if data available
|
||||
$('.min', row)
|
||||
.text(vz.wui.formatNumber(this.data.min[1], true) + this.definition.unit)
|
||||
.attr('title', $.plot.formatDate(new Date(this.data.min[0]), '%d. %b %y %h:%M:%S', vz.options.plot.xaxis.monthNames));
|
||||
$('.max', row)
|
||||
.text(vz.wui.formatNumber(this.data.max[1], true) + this.definition.unit)
|
||||
.attr('title', $.plot.formatDate(new Date(this.data.max[0]), '%d. %b %y %h:%M:%S', vz.options.plot.xaxis.monthNames));
|
||||
$('.average', row)
|
||||
.text(vz.wui.formatNumber(this.data.average, true) + this.definition.unit);
|
||||
$('.last', row)
|
||||
.text(vz.wui.formatNumber(this.data.tuples.last()[1], true) + this.definition.unit);
|
||||
|
||||
if (this.data.count > 0) {
|
||||
if (this.data.min[1] < vz.options.plot.yaxis.min) { // allow negative values for temperature sensors
|
||||
vz.options.plot.yaxis.min = null;
|
||||
}
|
||||
|
||||
// update details in table
|
||||
$('#entity-' + this.uuid + ' .min')
|
||||
.text(vz.wui.formatNumber(this.data.min[1]) + ' ' + this.definition.unit)
|
||||
.attr('title', $.plot.formatDate(new Date(this.data.min[0] + vz.options.timezoneOffset), '%d. %b %h:%M:%S', vz.options.plot.xaxis.monthNames));
|
||||
$('#entity-' + this.uuid + ' .max')
|
||||
.text(vz.wui.formatNumber(this.data.max[1]) + ' ' + this.definition.unit)
|
||||
.attr('title', $.plot.formatDate(new Date(this.data.max[0] + vz.options.timezoneOffset), '%d. %b %h:%M:%S', vz.options.plot.xaxis.monthNames));
|
||||
$('#entity-' + this.uuid + ' .average')
|
||||
.text(vz.wui.formatNumber(this.data.average) + ' ' + this.definition.unit);
|
||||
$('#entity-' + this.uuid + ' .last')
|
||||
.text(vz.wui.formatNumber(this.data.tuples.last()[1]) + ' ' + this.definition.unit);
|
||||
if (this.definition.interpreter == 'Volkszaehler\\Interpreter\\MeterInterpreter') { // sensors have no consumption
|
||||
$('#entity-' + this.uuid + ' .consumption')
|
||||
.text(vz.wui.formatNumber((this.data.consumption > 1000) ? this.data.consumption / 1000 : this.data.consumption) +
|
||||
((this.data.consumption > 1000) ? ' k' : ' ') + this.definition.unit + 'h')
|
||||
.attr('title', vz.wui.formatNumber((this.data.consumption * (year/delta) > 1000) ? (this.data.consumption * (year/delta)) / 1000 : this.data.consumption * (year/delta)) +
|
||||
((this.data.consumption * (year/delta) > 1000) ? ' k' : ' ') + this.definition.unit + 'h' + '/Jahr');
|
||||
}
|
||||
if (this.cost !== undefined) {
|
||||
$('#entity-' + this.uuid + ' .cost')
|
||||
.text(vz.wui.formatNumber(this.cost * this.data.consumption) + ' €')
|
||||
.attr('title', vz.wui.formatNumber(this.cost * this.data.consumption * (year/delta)) + ' €/Jahr');
|
||||
}
|
||||
}
|
||||
else { // no data available, clear table
|
||||
$('#entity-' + this.uuid + ' .min').text('').attr('title', '');
|
||||
$('#entity-' + this.uuid + ' .max').text('').attr('title', '');
|
||||
$('#entity-' + this.uuid + ' .average').text('');
|
||||
$('#entity-' + this.uuid + ' .last').text('');
|
||||
$('#entity-' + this.uuid + ' .consumption').text('');
|
||||
$('#entity-' + this.uuid + ' .cost').text('');
|
||||
}
|
||||
if (this.definition.interpreter == 'Volkszaehler\\Interpreter\\MeterInterpreter') { // sensors have no consumption
|
||||
$('.consumption', row)
|
||||
.text(vz.wui.formatNumber(this.data.consumption, true) + this.definition.unit + 'h')
|
||||
.attr('title', vz.wui.formatNumber(this.data.consumption * (year/delta), true) + this.definition.unit + 'h' + '/Jahr');
|
||||
}
|
||||
});
|
||||
|
||||
if (this.cost) {
|
||||
$('.cost', row)
|
||||
.text(vz.wui.formatNumber(this.cost * this.data.consumption) + ' €')
|
||||
.attr('title', vz.wui.formatNumber(this.cost * this.data.consumption * (year/delta)) + ' €/Jahr');
|
||||
}
|
||||
}
|
||||
else { // no data available, clear table
|
||||
$('.min', row).text('').attr('title', '');
|
||||
$('.max', row).text('').attr('title', '');
|
||||
$('.average', row).text('');
|
||||
$('.last', row).text('');
|
||||
$('.consumption', row).text('');
|
||||
$('.cost', row).text('');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -255,10 +316,11 @@ Entity.prototype.addChild = function(child) {
|
|||
if (this.definition.model != 'Volkszaehler\\Model\\Aggregator') {
|
||||
throw new Exception('EntityException', 'Entity is not an Aggregator');
|
||||
}
|
||||
|
||||
|
||||
return vz.load({
|
||||
controller: 'group',
|
||||
identifier: this.uuid,
|
||||
url: this.middleware,
|
||||
data: {
|
||||
uuid: child.uuid
|
||||
},
|
||||
|
@ -273,6 +335,7 @@ Entity.prototype.removeChild = function(child) {
|
|||
return vz.load({
|
||||
controller: 'group',
|
||||
identifier: this.uuid,
|
||||
url: this.middleware,
|
||||
data: {
|
||||
uuid: child.uuid,
|
||||
operation: 'delete'
|
||||
|
@ -280,38 +343,19 @@ Entity.prototype.removeChild = function(child) {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate Entity for required and optional properties and their values
|
||||
*
|
||||
* @return boolean
|
||||
* @todo implement/test
|
||||
*/
|
||||
Entity.prototype.validate = function() {
|
||||
this.definition.required.each(function(index, property) {
|
||||
var propertyDefinition = vz.capabilities.definitions.get('properties', property);
|
||||
if (!validateProperty(property, form.elements[property.name].value)) {
|
||||
throw new Exception('EntityException', 'Invalid property: ' + property.name + ' = ' + form.elements[property.name].value);
|
||||
}
|
||||
});
|
||||
|
||||
entity.optional.each(function(index, property) {
|
||||
var property = getDefinition(properties, property);
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls the callback function for the entity and all nested children
|
||||
*
|
||||
* @param cb callback function
|
||||
*/
|
||||
Entity.prototype.each = function(cb) {
|
||||
cb(this, this.parent);
|
||||
|
||||
Entity.prototype.each = function(cb, recursive) {
|
||||
if (this.children) {
|
||||
for (var i = 0; i < this.children.length; i++) {
|
||||
this.children[i].each(cb, this); // call recursive
|
||||
cb(this.children[i], this);
|
||||
|
||||
if (recursive) {
|
||||
this.children[i].each(cb, true); // call recursive
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -24,13 +24,20 @@
|
|||
* volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var Exception = function(type, message, code) {
|
||||
return {
|
||||
type: type,
|
||||
message: message,
|
||||
code: code
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Universal helper for middleware ajax requests with error handling
|
||||
*/
|
||||
vz.load = function(args) {
|
||||
$.extend(args, {
|
||||
url: this.options.middlewareUrl,
|
||||
dataType: 'json',
|
||||
accepts: 'application/json',
|
||||
error: function(xhr) {
|
||||
try {
|
||||
if (xhr.getResponseHeader('Content-type') == 'application/json') {
|
||||
|
@ -40,8 +47,9 @@ vz.load = function(args) {
|
|||
throw new Exception(json.exception.type, json.exception.message, (json.exception.code) ? json.exception.code : xhr.status);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception(xhr.statusText, 'Unknown middleware response', xhr.status)
|
||||
else {
|
||||
throw new Exception(xhr.statusText, 'Unknown middleware response', xhr.status)
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
vz.wui.dialogs.exception(e);
|
||||
|
@ -49,14 +57,28 @@ vz.load = function(args) {
|
|||
}
|
||||
});
|
||||
|
||||
if (args.controller) {
|
||||
if (args.url === undefined) { // local middleware by default
|
||||
args.url = vz.middleware[0].url;
|
||||
}
|
||||
|
||||
if (args.url == vz.middleware[0].url) { // local request
|
||||
args.dataType = 'json';
|
||||
}
|
||||
else { // remote request
|
||||
args.dataType = 'jsonp';
|
||||
args.jsonp = 'padding';
|
||||
}
|
||||
|
||||
if (args.controller !== undefined) {
|
||||
args.url += '/' + args.controller;
|
||||
}
|
||||
if (args.identifier) {
|
||||
|
||||
if (args.identifier !== undefined) {
|
||||
args.url += '/' + args.identifier;
|
||||
}
|
||||
|
||||
args.url += '.json';
|
||||
|
||||
|
||||
return $.ajax(args);
|
||||
};
|
||||
|
||||
|
@ -65,14 +87,18 @@ vz.load = function(args) {
|
|||
*/
|
||||
vz.parseUrlParams = function() {
|
||||
var vars = $.getUrlParams();
|
||||
var uuids = new Array;
|
||||
var save = false;
|
||||
|
||||
for (var key in vars) {
|
||||
if (vars.hasOwnProperty(key)) {
|
||||
switch (key) {
|
||||
case 'uuid': // add optional uuid from url
|
||||
var uuids = (typeof vars[key] == 'string') ? [vars[key]] : vars[key]; // handle multiple uuids
|
||||
uuids.each(function(index, uuid) {
|
||||
try { vz.uuids.add(uuid); } catch (exception) { /* ignore exception */ }
|
||||
});
|
||||
uuids = (typeof vars[key] == 'string') ? [vars[key]] : vars[key]; // handle multiple uuids
|
||||
break;
|
||||
|
||||
case 'save': // save new uuids in cookie
|
||||
save = true;
|
||||
break;
|
||||
|
||||
case 'from':
|
||||
|
@ -85,6 +111,22 @@ vz.parseUrlParams = function() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
uuids.each(function(index, uuid) {
|
||||
try {
|
||||
vz.entities.push(new Entity({
|
||||
middleware: vz.middleware[0].url,
|
||||
uuid: uuid,
|
||||
cookie: save
|
||||
}));
|
||||
} catch (exception) {
|
||||
/* ignore exception */
|
||||
}
|
||||
});
|
||||
|
||||
if (save) {
|
||||
vz.entities.saveCookie();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,8 +32,10 @@
|
|||
* we dont want to pollute the global namespace
|
||||
*/
|
||||
var vz = {
|
||||
// entity properties + data
|
||||
entities: new Array, // TODO new Entity?
|
||||
entities: new Array, // entity properties + data
|
||||
middleware: [{ // default middleware
|
||||
url: '../middleware.php',
|
||||
}],
|
||||
|
||||
// web user interface
|
||||
wui: {
|
||||
|
@ -41,17 +43,14 @@ var vz = {
|
|||
timeout: null
|
||||
},
|
||||
|
||||
// known UUIDs in the browser
|
||||
uuids: new Array,
|
||||
|
||||
// flot instance
|
||||
plot: { },
|
||||
|
||||
// debugging and runtime information from middleware
|
||||
capabilities: {
|
||||
definitions: { } // definitions of entities & properties
|
||||
},
|
||||
|
||||
|
||||
// flot instance
|
||||
plot: { },
|
||||
|
||||
// options loaded from cookies in options.js
|
||||
options: { }
|
||||
};
|
||||
|
@ -72,15 +71,15 @@ $(document).ready(function() {
|
|||
vz.wui.dialogs.error('Javascript Runtime Error', errorMsg);
|
||||
};
|
||||
|
||||
vz.uuids.load(); // load uuids from cookie
|
||||
vz.options.load(); // load options from cookie
|
||||
vz.entities.loadCookie(); // load uuids from cookie
|
||||
vz.options.loadCookies(); // load options from cookie
|
||||
vz.parseUrlParams(); // parse additional url params (new uuid etc..)
|
||||
|
||||
// initialize user interface
|
||||
vz.wui.init();
|
||||
vz.wui.initEvents();
|
||||
|
||||
if (vz.uuids.length == 0) {
|
||||
if (vz.entities.length == 0) {
|
||||
$('#entity-add').dialog('open');
|
||||
}
|
||||
|
||||
|
@ -90,7 +89,12 @@ $(document).ready(function() {
|
|||
$('#snapshot').show();
|
||||
}
|
||||
|
||||
vz.entities.loadDetails().done(function(a, b, c, d) {
|
||||
var queue = new Array;
|
||||
vz.entities.each(function(entity) {
|
||||
queue.push(entity.loadDetails());
|
||||
}, true);
|
||||
|
||||
$.when.apply($, queue).done(function() {
|
||||
vz.entities.showTable();
|
||||
vz.entities.loadData().done(vz.wui.drawPlot);
|
||||
});
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
* jQuery treeTable Plugin 2.3.0
|
||||
* http://ludo.cubicphuse.nl/jquery-plugins/treeTable/
|
||||
* jQuery treeTable Plugin unreleased
|
||||
* http://ludo.cubicphuse.nl/jquery-plugins/treeTable/doc/
|
||||
*
|
||||
* Copyright 2010, Ludo van den Boom
|
||||
* Copyright 2011, Ludo van den Boom
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
*/
|
||||
(function($) {
|
||||
|
@ -11,21 +11,21 @@
|
|||
// trees on a page. The options shouldn't be global to all these instances!
|
||||
var options;
|
||||
var defaultPaddingLeft;
|
||||
|
||||
|
||||
$.fn.treeTable = function(opts) {
|
||||
options = $.extend({}, $.fn.treeTable.defaults, opts);
|
||||
|
||||
|
||||
return this.each(function() {
|
||||
$(this).addClass("treeTable").find("tbody tr").each(function() {
|
||||
// Initialize root nodes only if possible
|
||||
if(!options.expandable || $(this)[0].className.search(options.childPrefix) == -1) {
|
||||
// To optimize performance of indentation, I retrieve the padding-left
|
||||
// value of the first root node. This way I only have to call +css+
|
||||
// value of the first root node. This way I only have to call +css+
|
||||
// once.
|
||||
if (isNaN(defaultPaddingLeft)) {
|
||||
defaultPaddingLeft = parseInt($($(this).children("td")[options.treeColumn]).css('padding-left'), 10);
|
||||
}
|
||||
|
||||
|
||||
initialize($(this));
|
||||
} else if(options.initialState == "collapsed") {
|
||||
this.style.display = "none"; // Performance! $(this).hide() is slow...
|
||||
|
@ -33,46 +33,51 @@
|
|||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$.fn.treeTable.defaults = {
|
||||
childPrefix: "child-of-",
|
||||
clickableNodeNames: false,
|
||||
expandable: true,
|
||||
indent: 19,
|
||||
initialState: "collapsed",
|
||||
onNodeShow: null,
|
||||
treeColumn: 0
|
||||
};
|
||||
|
||||
|
||||
// Recursively hide all node's children in a tree
|
||||
$.fn.collapse = function() {
|
||||
$(this).addClass("collapsed");
|
||||
|
||||
|
||||
childrenOf($(this)).each(function() {
|
||||
if(!$(this).hasClass("collapsed")) {
|
||||
$(this).collapse();
|
||||
}
|
||||
|
||||
|
||||
this.style.display = "none"; // Performance! $(this).hide() is slow...
|
||||
});
|
||||
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
// Recursively show all node's children in a tree
|
||||
$.fn.expand = function() {
|
||||
$(this).removeClass("collapsed").addClass("expanded");
|
||||
|
||||
|
||||
childrenOf($(this)).each(function() {
|
||||
initialize($(this));
|
||||
|
||||
|
||||
if($(this).is(".expanded.parent")) {
|
||||
$(this).expand();
|
||||
}
|
||||
|
||||
|
||||
// this.style.display = "table-row"; // Unfortunately this is not possible with IE :-(
|
||||
$(this).show();
|
||||
|
||||
if($.isFunction(options.onNodeShow)) {
|
||||
options.onNodeShow.call();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -82,7 +87,7 @@
|
|||
initialize($(this));
|
||||
$(this).expand().show();
|
||||
});
|
||||
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -90,9 +95,9 @@
|
|||
$.fn.appendBranchTo = function(destination) {
|
||||
var node = $(this);
|
||||
var parent = parentOf(node);
|
||||
|
||||
|
||||
var ancestorNames = $.map(ancestorsOf($(destination)), function(a) { return a.id; });
|
||||
|
||||
|
||||
// Conditions:
|
||||
// 1: +node+ should not be inserted in a location in a branch if this would
|
||||
// result in +node+ being an ancestor of itself.
|
||||
|
@ -102,22 +107,22 @@
|
|||
// 3: +node+ should not be inserted as a child of +node+ itself.
|
||||
if($.inArray(node[0].id, ancestorNames) == -1 && (!parent || (destination.id != parent[0].id)) && destination.id != node[0].id) {
|
||||
indent(node, ancestorsOf(node).length * options.indent * -1); // Remove indentation
|
||||
|
||||
|
||||
if(parent) { node.removeClass(options.childPrefix + parent[0].id); }
|
||||
|
||||
|
||||
node.addClass(options.childPrefix + destination.id);
|
||||
move(node, destination); // Recursively move nodes to new location
|
||||
indent(node, ancestorsOf(node).length * options.indent);
|
||||
}
|
||||
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
// Add reverse() function from JS Arrays
|
||||
$.fn.reverse = function() {
|
||||
return this.pushStack(this.get().reverse(), arguments);
|
||||
};
|
||||
|
||||
|
||||
// Toggle an entire branch
|
||||
$.fn.toggleBranch = function() {
|
||||
if($(this).hasClass("collapsed")) {
|
||||
|
@ -125,12 +130,12 @@
|
|||
} else {
|
||||
$(this).removeClass("expanded").collapse();
|
||||
}
|
||||
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
// === Private functions
|
||||
|
||||
|
||||
function ancestorsOf(node) {
|
||||
var ancestors = [];
|
||||
while(node = parentOf(node)) {
|
||||
|
@ -138,47 +143,47 @@
|
|||
}
|
||||
return ancestors;
|
||||
};
|
||||
|
||||
|
||||
function childrenOf(node) {
|
||||
return $("table.treeTable tbody tr." + options.childPrefix + node[0].id);
|
||||
};
|
||||
|
||||
|
||||
function getPaddingLeft(node) {
|
||||
var paddingLeft = parseInt(node[0].style.paddingLeft, 10);
|
||||
return (isNaN(paddingLeft)) ? defaultPaddingLeft : paddingLeft;
|
||||
}
|
||||
|
||||
|
||||
function indent(node, value) {
|
||||
var cell = $(node.children("td")[options.treeColumn]);
|
||||
cell[0].style.paddingLeft = getPaddingLeft(cell) + value + "px";
|
||||
|
||||
|
||||
childrenOf(node).each(function() {
|
||||
indent($(this), value);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
function initialize(node) {
|
||||
if(!node.hasClass("initialized")) {
|
||||
node.addClass("initialized");
|
||||
|
||||
|
||||
var childNodes = childrenOf(node);
|
||||
|
||||
|
||||
if(!node.hasClass("parent") && childNodes.length > 0) {
|
||||
node.addClass("parent");
|
||||
}
|
||||
|
||||
|
||||
if(node.hasClass("parent")) {
|
||||
var cell = $(node.children("td")[options.treeColumn]);
|
||||
var padding = getPaddingLeft(cell) + options.indent;
|
||||
|
||||
|
||||
childNodes.each(function() {
|
||||
$(this).children("td")[options.treeColumn].style.paddingLeft = padding + "px";
|
||||
});
|
||||
|
||||
|
||||
if(options.expandable) {
|
||||
cell.prepend('<span style="margin-left: -' + options.indent + 'px; padding-left: ' + options.indent + 'px" class="expander"></span>');
|
||||
$(cell[0].firstChild).click(function() { node.toggleBranch(); });
|
||||
|
||||
|
||||
if(options.clickableNodeNames) {
|
||||
cell[0].style.cursor = "pointer";
|
||||
$(cell).click(function(e) {
|
||||
|
@ -188,7 +193,7 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Check for a class set explicitly by the user, otherwise set the default class
|
||||
if(!(node.hasClass("expanded") || node.hasClass("collapsed"))) {
|
||||
node.addClass(options.initialState);
|
||||
|
@ -201,19 +206,21 @@
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function move(node, destination) {
|
||||
node.insertAfter(destination);
|
||||
childrenOf(node).reverse().each(function() { move($(this), node[0]); });
|
||||
};
|
||||
|
||||
|
||||
function parentOf(node) {
|
||||
var classNames = node[0].className.split(' ');
|
||||
|
||||
for(key in classNames) {
|
||||
|
||||
for(var key=0; key<classNames.length; key++) {
|
||||
if(classNames[key].match(options.childPrefix)) {
|
||||
return $("#" + classNames[key].substring(9));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
})(jQuery);
|
||||
|
|
|
@ -27,9 +27,8 @@
|
|||
// default time interval to show
|
||||
vz.options = {
|
||||
language: 'de',
|
||||
middlewareUrl: '../middleware.php', // TODO default middleware, store middleware urls in cookies
|
||||
tuples: 300,
|
||||
precision: 2, // TODO update from middleware capabilities?
|
||||
tuples: null, // automatically determined by plot size
|
||||
render: 'lines',
|
||||
refresh: false,
|
||||
minTimeout: 3000, // minimum refresh time in ms
|
||||
|
@ -43,7 +42,6 @@ vz.options.plot = {
|
|||
shadowSize: 0,
|
||||
points: {
|
||||
radius: 1,
|
||||
//symbol: 'square'
|
||||
symbol: function(ctx, x, y, radius, shadow) { // just draw simple pixels
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeRect(x-1, y-1, 2, 2);
|
||||
|
@ -70,7 +68,7 @@ vz.options.plot = {
|
|||
}
|
||||
}
|
||||
|
||||
vz.options.save = function() {
|
||||
vz.options.saveCookies = function() {
|
||||
for (var key in vz.options) {
|
||||
if (vz.options.hasOwnProperty(key) &&
|
||||
typeof vz.options[key] != 'function' &&
|
||||
|
@ -82,7 +80,7 @@ vz.options.save = function() {
|
|||
}
|
||||
};
|
||||
|
||||
vz.options.load = function() {
|
||||
vz.options.loadCookies = function() {
|
||||
for (var key in this) {
|
||||
var value = $.getCookie('vz_' + key);
|
||||
if (value !== undefined) {
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
/**
|
||||
* UUID handling
|
||||
*
|
||||
* @author Florian Ziegler <fz@f10-home.de>
|
||||
* @author Justin Otherguy <justin@justinotherguy.org>
|
||||
* @author Steffen Vogel <info@steffenvogel.de>
|
||||
* @copyright Copyright (c) 2011, The volkszaehler.org project
|
||||
* @package default
|
||||
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
||||
*/
|
||||
/*
|
||||
* This file is part of volkzaehler.org
|
||||
*
|
||||
* volkzaehler.org is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or any later version.
|
||||
*
|
||||
* volkzaehler.org is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* volkszaehler.org. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add given UUID and update cookie
|
||||
*/
|
||||
vz.uuids.add = function(uuid) {
|
||||
if (this.validate(uuid)) {
|
||||
if (!this.contains(uuid)) {
|
||||
this.push(uuid);
|
||||
}
|
||||
else {
|
||||
throw new Exception('UUIDException', 'UUID already added: ' + uuid);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new Exception('UUIDException', 'Invalid UUID');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove UUID and update cookie
|
||||
*/
|
||||
vz.uuids.remove = function(uuid) {
|
||||
if (this.contains(uuid)) {
|
||||
this.splice(this.indexOf(uuid), 1); // remove uuid from array
|
||||
}
|
||||
else {
|
||||
throw new Exception('UUIDException', 'Unknown UUID: ' + uuid);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate UUID
|
||||
*/
|
||||
vz.uuids.validate = function(uuid) {
|
||||
return uuid.match(/^[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}$/);
|
||||
};
|
||||
|
||||
/**
|
||||
* Save uuids as cookie
|
||||
*/
|
||||
vz.uuids.save = function() {
|
||||
var expires = new Date(new Date().getTime() + 31536e6); // expires in a year
|
||||
$.setCookie('vz_uuids', this.join(';'), {expires: expires});
|
||||
};
|
||||
|
||||
/**
|
||||
* Load uuids from cookie
|
||||
*/
|
||||
vz.uuids.load = function() {
|
||||
var cookie = $.getCookie('vz_uuids');
|
||||
if (cookie) {
|
||||
cookie.split(';').each(function(index, uuid) {
|
||||
vz.uuids.add(uuid);
|
||||
});
|
||||
}
|
||||
};
|
|
@ -28,6 +28,8 @@
|
|||
* Initialize the WUI (Web User Interface)
|
||||
*/
|
||||
vz.wui.init = function() {
|
||||
vz.options.tuples = Math.round($('#flot').width() / 3);
|
||||
|
||||
// initialize dropdown accordion
|
||||
$('#accordion h3').click(function() {
|
||||
$(this).next().toggle('fast');
|
||||
|
@ -37,7 +39,7 @@ vz.wui.init = function() {
|
|||
|
||||
// buttons
|
||||
$('button, input[type=button],[type=image],[type=submit]').button();
|
||||
$('button[name=options-save]').click(vz.options.save);
|
||||
$('button[name=options-save]').click(vz.options.saveCookies);
|
||||
$('button[name=entity-add]').click(this.dialogs.init);
|
||||
$('#permalink').click(function() { window.location = vz.wui.getPermalink(); });
|
||||
$('#snapshot').click(function() { window.location = vz.wui.getSnaplink(); }).hide();
|
||||
|
@ -46,20 +48,6 @@ vz.wui.init = function() {
|
|||
$('#controls button').click(this.handleControls);
|
||||
$('#controls').buttonset();
|
||||
|
||||
// tuple resolution
|
||||
vz.options.tuples = Math.round($('#flot').width() / 4);
|
||||
$('#tuples').val(vz.options.tuples).change(function() {
|
||||
vz.options.tuples = $(this).val();
|
||||
vz.entities.loadData().done(vz.wui.drawPlot);
|
||||
});
|
||||
|
||||
// middleware address
|
||||
$('#middleware-url')
|
||||
.val(vz.options.middlewareUrl)
|
||||
.change(function() {
|
||||
vz.options.middlewareUrl = $(this).val();
|
||||
});
|
||||
|
||||
// auto refresh
|
||||
if (vz.options.refresh) {
|
||||
$('#refresh').attr('checked', true);
|
||||
|
@ -102,7 +90,8 @@ vz.wui.dialogs.init = function() {
|
|||
controller: 'entity',
|
||||
success: function(json) {
|
||||
if (json.entities.length > 0) {
|
||||
json.entities.each(function(index, entity) {
|
||||
json.entities.each(function(index, json) {
|
||||
var entity = new Entity(json);
|
||||
$('#entity-subscribe-public select#public').append(
|
||||
$('<option>').html(entity.title).data('entity', entity)
|
||||
);
|
||||
|
@ -119,25 +108,20 @@ vz.wui.dialogs.init = function() {
|
|||
});
|
||||
$('#entity-create option[value=power]').attr('selected', 'selected');
|
||||
|
||||
/*$('#entity-create select[name=type] option:selected').data('definition').required.each(function(index, property) {
|
||||
$('#entity-create #properties').append(
|
||||
vz.capabilities.definitions.get('properties', property).getDOM()
|
||||
)
|
||||
});*/
|
||||
|
||||
$('#entity-create-middlware').val(vz.options.middlewareUrl);
|
||||
$('#entity-create-middleware').val(vz.middleware[0].url);
|
||||
|
||||
// actions
|
||||
$('#entity-subscribe input[type=button]').click(function() {
|
||||
try {
|
||||
var uuid = $('#entity-subscribe input#uuid');
|
||||
vz.uuids.add(uuid.val());
|
||||
|
||||
if ($('#entity-subscribe input.cookie').attr('checked')) {
|
||||
vz.uuids.save();
|
||||
}
|
||||
var entity = new Entity({
|
||||
uuid: $('#entity-subscribe input#uuid').val(),
|
||||
middleware: $('#entity-subscribe input#middleware').val(),
|
||||
cookie: Boolean($('#entity-subscribe input.cookie').attr('checked'))
|
||||
});
|
||||
|
||||
vz.entities.loadDetails().done(function() {
|
||||
entity.loadDetails().done(function() {
|
||||
vz.entities.push(entity);
|
||||
vz.entities.saveCookie();
|
||||
vz.entities.showTable();
|
||||
vz.entities.loadData().done(vz.wui.drawPlot);
|
||||
}); // reload entity details and load data
|
||||
|
@ -147,8 +131,6 @@ vz.wui.dialogs.init = function() {
|
|||
}
|
||||
finally {
|
||||
$('#entity-add').dialog('close');
|
||||
$('#entity-add input[type!=button]').val(''); // reset form
|
||||
$('#entity-add input.cookie').attr('checked', false); // reset form
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -156,24 +138,19 @@ vz.wui.dialogs.init = function() {
|
|||
var entity = $('#entity-subscribe-public select#public option:selected').data('entity');
|
||||
|
||||
try {
|
||||
vz.uuids.add(entity.uuid);
|
||||
|
||||
if ($('#entity-subscribe-public input.cookie').attr('checked')) {
|
||||
vz.uuids.save();
|
||||
}
|
||||
entity.cookie = Boolean($('#entity-subscribe-public input.cookie').attr('checked'));
|
||||
entity.middleware = vz.middleware[0].url;
|
||||
|
||||
vz.entities.loadDetails().done(function() {
|
||||
vz.entities.showTable();
|
||||
vz.entities.loadData().done(vz.wui.drawPlot);
|
||||
}); // reload entity details and load data
|
||||
vz.entities.push(entity);
|
||||
vz.entities.saveCookie();
|
||||
vz.entities.showTable();
|
||||
vz.entities.loadData().done(vz.wui.drawPlot);
|
||||
}
|
||||
catch (e) {
|
||||
vz.wui.dialogs.exception(e);
|
||||
}
|
||||
finally {
|
||||
$('#entity-add').dialog('close');
|
||||
$('#entity-add input[type!=button]').val(''); // reset form
|
||||
$('#entity-add input.cookie').attr('checked', false); // reset form
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -200,7 +177,7 @@ vz.wui.getPermalink = function() {
|
|||
if (entity.active && entity.definition.model == 'Volkszaehler\\Model\\Channel') {
|
||||
uuids.push(entity.uuid);
|
||||
}
|
||||
});
|
||||
}, true); // recursive!
|
||||
|
||||
var params = $.param({
|
||||
from: Math.floor(vz.options.plot.xaxis.min),
|
||||
|
@ -222,7 +199,7 @@ vz.wui.getSnaplink = function() {
|
|||
if (entity.active) {
|
||||
uuids.push(entity.uuid);
|
||||
}
|
||||
});
|
||||
}, true); // recursive!
|
||||
|
||||
return vz.options.middlewareUrl + '/data/' + uuids[0] + '.png?' + $.param({
|
||||
from: Math.floor(vz.options.plot.xaxis.min),
|
||||
|
@ -404,169 +381,30 @@ vz.wui.clearTimeout = function(text) {
|
|||
* therefore "vz.options.precision" needs
|
||||
* to be set to 1 (for 1 decimal) in that case
|
||||
*/
|
||||
vz.wui.formatNumber = function(number) {
|
||||
return Math.round(number * Math.pow(10, vz.options.precision)) / Math.pow(10, vz.options.precision);
|
||||
vz.wui.formatNumber = function(number, prefix) {
|
||||
var siPrefixes = ['k', 'M', 'G', 'T'];
|
||||
var siIndex = 0;
|
||||
|
||||
while (prefix && number > 1000 && siIndex < siPrefixes.length-1) {
|
||||
number /= 1000;
|
||||
siIndex++;
|
||||
}
|
||||
|
||||
number = Math.round(number * Math.pow(10, vz.options.precision)) / Math.pow(10, vz.options.precision); // rounding
|
||||
|
||||
if (prefix) {
|
||||
number += (siIndex > 0) ? ' ' + siPrefixes[siIndex-1] : ' ';
|
||||
}
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
vz.wui.updateHeadline = function() {
|
||||
var from = $.plot.formatDate(new Date(vz.options.plot.xaxis.min + vz.options.timezoneOffset), '%d. %b %y %h:%M', vz.options.plot.xaxis.monthNames);
|
||||
var to = $.plot.formatDate(new Date(vz.options.plot.xaxis.max + vz.options.timezoneOffset), '%d. %b %y %h:%M', vz.options.plot.xaxis.monthNames);
|
||||
var from = $.plot.formatDate(new Date(vz.options.plot.xaxis.min), '%d. %b %y %h:%M', vz.options.plot.xaxis.monthNames);
|
||||
var to = $.plot.formatDate(new Date(vz.options.plot.xaxis.max), '%d. %b %y %h:%M', vz.options.plot.xaxis.monthNames);
|
||||
$('#title').html(from + ' - ' + to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwritten each iterator to iterate recursively throug all entities
|
||||
*/
|
||||
vz.entities.each = function(cb) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
this[i].each(cb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all entity information from middleware
|
||||
*/
|
||||
vz.entities.loadDetails = function() {
|
||||
this.clear();
|
||||
|
||||
var queue = new Array;
|
||||
|
||||
vz.uuids.each(function(index, uuid) {
|
||||
queue.push(vz.load({
|
||||
controller: 'entity',
|
||||
identifier: uuid,
|
||||
success: function(json) {
|
||||
vz.entities.push(new Entity(json.entity));
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
return $.when.apply($, queue);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create nested entity list
|
||||
*
|
||||
* @todo move to Entity class
|
||||
*/
|
||||
vz.entities.showTable = function() {
|
||||
$('#entity-list tbody').empty();
|
||||
|
||||
vz.entities.sort(Entity.compare);
|
||||
|
||||
var c = 0; // for colors
|
||||
vz.entities.each(function(entity, parent) {
|
||||
entity.color = vz.options.plot.colors[c++ % vz.options.plot.colors.length];
|
||||
|
||||
$('#entity-list tbody').append(entity.getRow());
|
||||
});
|
||||
|
||||
/*
|
||||
* Initialize treeTable
|
||||
*
|
||||
* http://ludo.cubicphuse.nl/jquery-plugins/treeTable/doc/index.html
|
||||
* https://github.com/ludo/jquery-plugins/tree/master/treeTable
|
||||
*/
|
||||
// configure entities as draggable
|
||||
$('#entity-list tr.channel span.indicator, #entity-list tr.aggregator span.indicator').draggable({
|
||||
helper: 'clone',
|
||||
opacity: .75,
|
||||
refreshPositions: true, // Performance?
|
||||
revert: 'invalid',
|
||||
revertDuration: 300,
|
||||
scroll: true
|
||||
});
|
||||
|
||||
// configure aggregators as droppable
|
||||
$('#entity-list tr.aggregator span.indicator').each(function() {
|
||||
$(this).parents('tr').droppable({
|
||||
//accept: 'tr.channel span.indicator, tr.aggregator span.indicator', // TODO
|
||||
drop: function(event, ui) {
|
||||
var child = $(ui.draggable.parents('tr')[0]).data('entity');
|
||||
var from = child.parent;
|
||||
var to = $(this).data('entity');
|
||||
|
||||
$('#entity-move').dialog({ // confirm prompt
|
||||
resizable: false,
|
||||
modal: true,
|
||||
title: 'Verschieben',
|
||||
width: 400,
|
||||
buttons: {
|
||||
'Verschieben': function() {
|
||||
try {
|
||||
var queue = new Array;
|
||||
queue.push(to.addChild(child)); // add to new aggregator
|
||||
|
||||
if (from !== undefined) {
|
||||
queue.push(from.removeChild(child)); // remove from aggregator
|
||||
}
|
||||
else {
|
||||
vz.uuids.remove(child.uuid); // remove from cookies
|
||||
vz.uuids.save();
|
||||
}
|
||||
} catch (e) {
|
||||
vz.wui.dialogs.exception(e);
|
||||
} finally {
|
||||
$.when(queue).done(function() {
|
||||
// wait for middleware
|
||||
vz.entities.loadDetails().done(vz.entities.showTable);
|
||||
});
|
||||
$(this).dialog('close');
|
||||
}
|
||||
},
|
||||
'Abbrechen': function() {
|
||||
$(this).dialog('close');
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
hoverClass: 'accept',
|
||||
over: function(event, ui) {
|
||||
// make the droppable branch expand when a draggable node is moved over it
|
||||
if (this.id != $(ui.draggable.parents('tr')[0]).id && !$(this).hasClass('expanded')) {
|
||||
$(this).expand();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// make visible that a row is clicked
|
||||
$('#entity-list table tbody tr').mousedown(function() {
|
||||
$('tr.selected').removeClass('selected'); // deselect currently selected rows
|
||||
$(this).addClass('selected');
|
||||
});
|
||||
|
||||
// make sure row is selected when span is clicked
|
||||
$('#entity-list table tbody tr span').mousedown(function() {
|
||||
$($(this).parents('tr')[0]).trigger('mousedown');
|
||||
});
|
||||
|
||||
$('#entity-list table').treeTable({
|
||||
treeColumn: 2,
|
||||
clickableNodeNames: true,
|
||||
initialState: 'expanded'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Load json data from the middleware
|
||||
*
|
||||
* @todo move to Entity class
|
||||
*/
|
||||
vz.entities.loadData = function() {
|
||||
$('#overlay').html('<img src="images/loading.gif" alt="loading..." /><p>loading...</p>');
|
||||
|
||||
var queue = new Array;
|
||||
|
||||
vz.entities.each(function(entity) {
|
||||
if (entity.active && entity.definition.model == 'Volkszaehler\\Model\\Channel') {
|
||||
queue.push(entity.loadData());
|
||||
}
|
||||
});
|
||||
|
||||
return $.when.apply($, queue);
|
||||
};
|
||||
|
||||
/**
|
||||
* Draws plot to container
|
||||
*/
|
||||
|
@ -588,7 +426,7 @@ vz.wui.drawPlot = function () {
|
|||
|
||||
series.push(serie);
|
||||
}
|
||||
});
|
||||
}, true); // recursive!
|
||||
|
||||
if (series.length == 0) {
|
||||
$('#overlay').html('<img src="images/empty.png" alt="no data..." /><p>nothing to plot...</p>');
|
||||
|
@ -612,14 +450,6 @@ vz.wui.drawPlot = function () {
|
|||
/*
|
||||
* Error & Exception handling
|
||||
*/
|
||||
|
||||
var Exception = function(type, message, code) {
|
||||
return {
|
||||
type: type,
|
||||
message: message,
|
||||
code: code
|
||||
};
|
||||
}
|
||||
|
||||
vz.wui.dialogs.error = function(error, description, code) {
|
||||
if (code !== undefined) {
|
||||
|
|
|
@ -11,14 +11,18 @@ table {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
thead tr th {
|
||||
tr th {
|
||||
border-bottom: 2px solid grey;
|
||||
|
||||
text-align: left;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
tbody tr td {
|
||||
tr.property td {
|
||||
border-top: 1px solid #A7A7A7;
|
||||
}
|
||||
|
||||
tr.entity td {
|
||||
border-top: 1px solid #A7A7A7;
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ class JSON extends View {
|
|||
$json = $this->json->encode((Util\Debug::isActivated()) ? JSON_PRETTY : 0);
|
||||
|
||||
if ($this->padding) {
|
||||
$json = 'if (' . $this->padding . ') { ' . $this->padding . '(' . $json . '); }';
|
||||
$json = $this->padding . '(' . $json . ');';
|
||||
}
|
||||
echo $json;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue