diff --git a/htdocs/frontend/images/cross.png b/htdocs/frontend/images/cross.png new file mode 100644 index 0000000..1514d51 Binary files /dev/null and b/htdocs/frontend/images/cross.png differ diff --git a/htdocs/frontend/images/tick.png b/htdocs/frontend/images/tick.png new file mode 100644 index 0000000..a9925a0 Binary files /dev/null and b/htdocs/frontend/images/tick.png differ diff --git a/htdocs/frontend/index.html b/htdocs/frontend/index.html index 4ce4476..419e8c5 100644 --- a/htdocs/frontend/index.html +++ b/htdocs/frontend/index.html @@ -15,21 +15,17 @@ - - - - - - + + @@ -137,7 +133,7 @@

Hier können Sie einen existierenden Kanal über seine UUID hinzufügen

- +

@@ -145,24 +141,26 @@

Hier können Sie öffentliche Kanäle abonnieren.

- +

-
+ - - - - - - - + + + + + + + +
Middleware URL:

Bsp: http://volkszaehler.org/demo/middleware.php

Typ:
Öffentlich: ja
nein
Titel:
Auflösung:
Kosten:

pro Wh

Beschreibung:
EigenschaftWert
Middleware URL:
Typ:
Öffentlich: ja   nein
Titel:
Auflösung:
Kosten:

pro Wh

Beschreibung:
+

-

Es wird ein neuer Kanal erstellt. Die Antwort der Middlewarenthält dann die UUID.
+

Es wird ein neuer Kanal erstellt. Die Antwort der Middleware enthält dann die UUID.
Diese muss dann im Controller angegeben werden (z.B. AVR Net-IO).

diff --git a/htdocs/frontend/javascripts/entities.js b/htdocs/frontend/javascripts/entities.js new file mode 100644 index 0000000..0287d92 --- /dev/null +++ b/htdocs/frontend/javascripts/entities.js @@ -0,0 +1,191 @@ +/** + * + * + * @author Florian Ziegler + * @author Justin Otherguy + * @author Steffen Vogel + * @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 . + */ + +/** + * 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('loading...

loading...

'); + + 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' + }); +}; diff --git a/htdocs/frontend/javascripts/entity.js b/htdocs/frontend/javascripts/entity.js index 4f5e813..8afd4ff 100644 --- a/htdocs/frontend/javascripts/entity.js +++ b/htdocs/frontend/javascripts/entity.js @@ -1,7 +1,6 @@ /** * Entity handling, parsing & validation * - * @author Florian Ziegler * @author Justin Otherguy * @author Steffen Vogel * @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() { $('
') .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 = $('
EigenschaftWert
'); var data = $(''); - - 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 = '' + this[property] + ''; - 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 = '' + this[property] + ''; - 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 = '' + this.middleware + ''; + 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($('') - .append($('') - .addClass('key') - .text(title) - ) - .append($('') - .addClass('value') - .append(value) - ) - ); + case 'uuid': + var title = 'UUID'; + var value = '' + this.uuid + ''; + break; + + case 'color': + var title = 'Farbe'; + var value = '' + this.color + ''; + break; + + case 'cookie': + var title = 'Cookie'; + value = '' + ((value) ? 'ja' : 'nein') + ''; + break; + case 'active': + var title = 'Aktiv'; + var value = '' + ((this.active) ? 'ja' : 'nein') + ''; + break; } - } + + data.append($('') + .addClass('property') + .addClass('general') + .append($('') + .addClass('key') + .text(title) + ) + .append($('') + .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 = '' + ((value) ? 'ja' : 'nein') + ''; + } + + if (property == 'cost') { + value = (value * 1000 * 100) + ' ct/k' + this.definition.unit + 'h'; // ct per kWh + } + + data.append($('') + .addClass('property') + .addClass(section) + .append($('') + .addClass('key') + .text(title) + ) + .append($('') + .addClass('value') + .append(value) + ) + ); + } + }, this); + }, this); return table.append(data); }; -Entity.prototype.getRow = function() { +Entity.prototype.getDOMRow = function(parent) { var row = $('') - .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($('') .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($('') .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 + } } } }; diff --git a/htdocs/frontend/javascripts/functions.js b/htdocs/frontend/javascripts/functions.js index 70b700a..8e9a785 100644 --- a/htdocs/frontend/javascripts/functions.js +++ b/htdocs/frontend/javascripts/functions.js @@ -24,13 +24,20 @@ * volkszaehler.org. If not, see . */ +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(); + } }; /** diff --git a/htdocs/frontend/javascripts/init.js b/htdocs/frontend/javascripts/init.js index fdf8667..608bc44 100644 --- a/htdocs/frontend/javascripts/init.js +++ b/htdocs/frontend/javascripts/init.js @@ -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); }); diff --git a/htdocs/frontend/javascripts/jquery/jquery-treeTable.js b/htdocs/frontend/javascripts/jquery/jquery-treeTable.js index 70c52c4..2731b24 100644 --- a/htdocs/frontend/javascripts/jquery/jquery-treeTable.js +++ b/htdocs/frontend/javascripts/jquery/jquery-treeTable.js @@ -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(''); $(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 - * @author Justin Otherguy - * @author Steffen Vogel - * @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 . - */ - -/** - * 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); - }); - } -}; diff --git a/htdocs/frontend/javascripts/wui.js b/htdocs/frontend/javascripts/wui.js index 02850f5..b71bb46 100644 --- a/htdocs/frontend/javascripts/wui.js +++ b/htdocs/frontend/javascripts/wui.js @@ -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( $('