improved dynamic creation/modification of channel properties

This commit is contained in:
Steffen Vogel 2012-02-21 17:49:26 +01:00
parent 700c515b88
commit 0b10eadb6a
8 changed files with 627 additions and 167 deletions

View File

@ -27,6 +27,7 @@
<script type="text/javascript" src="javascripts/entities.js"></script>
<script type="text/javascript" src="javascripts/wui.js"></script>
<script type="text/javascript" src="javascripts/entity.js"></script>
<script type="text/javascript" src="javascripts/property.js"></script>
<link rel="stylesheet" type="text/css" href="stylesheets/jquery-ui-vz/jquery-ui-1.8.17.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/jquery-treeTable.css">
@ -156,7 +157,7 @@
<p><input type="button" value="Abonnieren" /> <label for="entity-public-cookie">Cookie:</label> <input id="entity-public-cookie" type="checkbox" /></p>
</div>
<div id="entity-create">
<form method="get" target="_blank">
<form>
<table>
<tr class="property"><th>Eigenschaft</th><th>Wert</th></tr>
<tr class="property"><td>Middleware:</td><td><input type="text" id="entity-create-middleware" /></td></tr>

View File

@ -127,74 +127,71 @@ Entity.prototype.loadData = function() {
*/
Entity.prototype.showDetails = function() {
var entity = this;
var dialog = $('<div>');
dialog.addClass('details')
.append(this.getDOMDetails())
.dialog({
title: 'Details f&uuml;r ' + this.title,
width: 480,
resizable: false,
buttons : {
'Schließen': function() {
$(this).dialog('close');
},
'Löschen' : function() {
$('#entity-delete').dialog({ // confirm prompt
resizable: false,
modal: true,
title: 'Löschen',
width: 400,
buttons: {
'Löschen': function() {
entity.delete().done(function() {
entity.cookie = false;
vz.entities.saveCookie();
var dialog = $('<div>')
.addClass('details')
.append(this.getDOMDetails())
.dialog({
title: 'Details f&uuml;r ' + this.title,
width: 480,
resizable: false,
buttons : {
'Bearbeiten' : function() {
$('table', this).remove();
$(this).append(entity.getDOMDetails(true));
},
'Löschen' : function() {
$('#entity-delete').dialog({ // confirm prompt
resizable: false,
modal: true,
title: 'Löschen',
width: 400,
buttons: {
'Löschen': function() {
entity.delete().done(function() {
entity.cookie = false;
vz.entities.saveCookie();
vz.entities.each(function(it, parent) { // remove from tree
if (entity.uuid == it.uuid) {
var array = (parent) ? parent.children : vz.entities;
array.remove(it);
}
}, true);
vz.entities.each(function(it, parent) { // remove from tree
if (entity.uuid == it.uuid) {
var array = (parent) ? parent.children : vz.entities;
array.remove(it);
}
}, true);
vz.entities.showTable();
vz.wui.drawPlot();
dialog.dialog('close');
});
vz.entities.showTable();
vz.wui.drawPlot();
dialog.dialog('close');
});
$(this).dialog('close');
},
'Abbrechen': function() {
$(this).dialog('close');
$(this).dialog('close');
},
'Abbrechen': function() {
$(this).dialog('close');
}
}
}
});
});
}
}
}
});
});
};
/**
* Show from for new Channel
* used to create info dialog
*/
Entity.prototype.getDOMDetails = function(edit) {
Entity.prototype.getDOMDetails = function(editable) {
var table = $('<table><thead><tr><th>Eigenschaft</th><th>Wert</th></tr></thead></table>');
var data = $('<tbody>');
// general properties
var general = ['title', 'type', 'uuid', 'middleware', 'color', 'style', 'active', 'cookie'];
var sections = ['required', 'optional'];
// frontend properties
var general = ['type', 'uuid', 'middleware', 'cookie'];
general.each(function(index, property) {
var definition = vz.capabilities.definitions.get('properties', property);
var title = (definition) ? definition.translation[vz.options.language] : property;
var value = this[property];
var title;
switch(property) {
case 'type':
var title = 'Typ';
title = 'Typ';
var icon = $('<img>').
attr('src', 'images/types/' + this.definition.icon)
.css('margin-right', 4);
@ -202,39 +199,21 @@ Entity.prototype.getDOMDetails = function(edit) {
.text(this.definition.translation[vz.options.language])
.prepend(icon);
break;
case 'middleware':
var title = 'Middleware';
var value = '<a href="' + this.middleware + '/capabilities.json">' + this.middleware + '</a>';
title = 'Middleware';
value = '<a href="' + this.middleware + '/capabilities.json">' + this.middleware + '</a>';
break;
case 'uuid':
var title = 'UUID';
var value = '<a href="' + this.middleware + '/entity/' + this.uuid + '.json">' + this.uuid + '</a>';
title = 'UUID';
value = '<a href="' + this.middleware + '/entity/' + this.uuid + '.json">' + this.uuid + '</a>';
break;
case 'color':
var value = $('<span>')
.text(this.color)
.css('background-color', this.color)
.css('padding-left', 5)
.css('padding-right', 5);
break;
case 'cookie':
var title = 'Cookie';
title = 'Cookie';
value = '<img src="images/' + ((this.cookie) ? 'tick' : 'cross') + '.png" alt="' + ((value) ? 'ja' : 'nein') + '" />';
break;
case 'active':
var value = '<img src="images/' + ((this.active) ? 'tick' : 'cross') + '.png" alt="' + ((this.active) ? 'ja' : 'nein') + '" />';
break;
case 'style':
switch (this.style) {
case 'lines': var value = 'Linien'; break;
case 'steps': var value = 'Stufen'; break;
case 'points': var value = 'Punkte'; break;
}
break;
}
data.append($('<tr>')
@ -251,21 +230,46 @@ Entity.prototype.getDOMDetails = function(edit) {
);
}, this);
// middleware properties
var sections = ['required', 'optional'];
sections.each(function(index, section) {
this.definition[section].each(function(index, property) {
if (this.hasOwnProperty(property) && !general.contains(property)) {
var definition = vz.capabilities.definitions.get('properties', property);
var title = definition.translation[vz.options.language];
if (editable || this.hasOwnProperty(property)) {
var propDef = vz.capabilities.definitions.get('properties', property);
var title = (propDef) ? propDef.translation[vz.options.language] : property;
var value = this[property];
if (definition.type == 'boolean') {
if (propDef && propDef.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
switch (property) {
case 'cost':
value = (value * 1000 * 100) + ' ct/k' + this.definition.unit + 'h'; // ct per kWh
break;
case 'color':
value = $('<span>')
.text(this.color)
.css('background-color', this.color)
.css('padding-left', 5)
.css('padding-right', 5);
break;
case 'style':
switch (this.style) {
case 'lines': value = 'Linien'; break;
case 'steps': value = 'Stufen'; break;
case 'points': value = 'Punkte'; break;
}
break;
}
if (editable && propDef) {
value = propDef.getInput(this[property]);
}
data.append($('<tr>')
.addClass('property')
.addClass(section)
@ -281,6 +285,7 @@ Entity.prototype.getDOMDetails = function(edit) {
}
}, this);
}, this);
return table.append(data);
};

View File

@ -200,6 +200,10 @@ vz.capabilities.load = function() {
controller: 'capabilities',
success: function(json) {
$.extend(true, vz.capabilities, json.capabilities);
vz.capabilities.definitions.properties.each(function(index, propdef) {
vz.capabilities.definitions.properties[index] = new Property(propdef);
});
}
});
};

View File

@ -39,7 +39,7 @@ var vz = {
timeout: null
},
capabilities: { // debugging and runtime information from middleware
definitions: {} // definitions of entities & properties
definitions: { }
},
plot: { }, // flot instance
options: { } // options loaded from cookies in options.js

View File

@ -38,69 +38,94 @@ var Property = function(json) {
* @todo implement/test
*/
Property.prototype.validate = function(value) {
var invalid = false;
switch (this.type) {
case 'string':
case 'text':
// TODO check pattern
// TODO check string length
return true;
invalid |= (this.pattern != undefined) && value.match(this.pattern);
invalid |= (this.min != undefined) && value.length < this.min;
invalid |= (this.max != undefined) && value.length > this.max;
break;
case 'integer':
invalid |= !value.match(/^[+-]?\d*$/);
case 'float':
// TODO check format
// TODO check min/max
return true;
case 'integer':
// TODO check format
// TODO check min/max
return true;
invalid |= !value.match(/^[+-]?\d*(\.\d*)?$/);
invalid |= (this.min != undefined) && value < this.min;
invalid |= (this.max != undefined) && value > this.max;
break;
case 'boolean':
return value == '1' || value == '';
invalid |= value != '1' && value != '';
break;
case 'multiple':
return this.options.contains(value);
invalid |= !this.options.contains(value);
break;
default:
throw new Exception('EntityException', 'Unknown property');
}
return !invalid;
};
/**
*
* @todo implement/test
* Get form element for property
*/
Property.prototype.getInput = function(value) {
var elm;
switch (this.type) {
case 'string':
case 'float':
case 'integer':
return $('<input>')
.attr('type', 'text')
.attr('name=', this.name)
.attr('maxlength', (property.type == 'string') ? this.max : 0);
case 'string':
elm = $('<input>').attr('type', 'text');
if (this.type == 'string' && this.max) {
elm.attr('maxlength', this.max);
}
break;
case 'text':
return $('<textarea>')
.attr('name', this.name);
elm = $('<textarea>');
break;
case 'boolean':
return $('<input>')
.attr('type', 'checkbox')
.attr('name', this.name)
.attr('checked', true);
elm = $('<input>').attr('type', 'checkbox');
break;
case 'multiple':
var dom = $('<select>').attr('name', property.name)
property.options.each(function(index, option) {
dom.append($('<option>')
.value(option)
.text(option)
elm = $('<select>').attr('size', 1);
this.options.each(function(index, option) {
elm.append(
$('<option>')
.val(option)
.text(option)
);
});
return dom;
break;
default:
throw new Exception('PropertyException', 'Unknown property');
}
elm
.attr('name', this.name)
.val(value)
.bind('keyup keydown change', this, function(event) { // live validation
var propdef = event.data;
var elm = $(this);
if (propdef.validate(elm.val()) == false && elm.val() != '') {
elm.addClass('invalid');
}
else {
elm.removeClass('invalid');
}
});
return elm;
};

View File

@ -197,49 +197,22 @@ vz.wui.dialogs.init = function() {
// show available properties for selected type
function addProperties(proplist, className) {
proplist.each(function(index, def) {
vz.capabilities.definitions.properties.each(function(propindex, propdef) {
if (def == propdef.name) {
var cntrl = null;
var row = $('<tr>')
.addClass("property")
.append(
$('<td>').text(propdef.translation[vz.options.language])
);
switch (propdef.type) {
case 'float':
case 'integer':
case 'string':
cntrl = $('<input>').attr("type", "text");
break;
case 'text':
cntrl = $('<textarea>');
break;
case 'boolean':
cntrl = $('<input>').attr("type", "checkbox");
break;
proplist.each(function(index, name) {
var propdef = vz.capabilities.definitions.get('properties', name);
if (propdef) {
var row = $('<tr>')
.addClass("property")
.addClass(className)
.append(
$('<td>').text(propdef.translation[vz.options.language])
)
.append(
$('<td>').append(propdef.getInput())
);
case 'multiple':
cntrl = $('<select>').attr("Size", "1");
propdef.options.each(function(optindex, optdef) {
cntrl.append(
$('<option>').html(optdef)
);
});
break;
}
if (cntrl != null) {
row.addClass(className);
cntrl.attr("name", propdef.name);
row.append($('<td>').append(cntrl));
$('#entity-create form table').append(row);
}
}
});
$('#entity-create form table').append(row);
}
});
}
@ -256,10 +229,10 @@ vz.wui.dialogs.init = function() {
$('#entity-create form').submit(function() {
var def = $('select[name=type] option:selected', this).data('definition');
var properties = [];
$(this).serializeArray().each(function(index, value) {
if (value.value != '') {
properties.push(value);
$(this).serializeArray().each(function(index, prop) {
if (prop.value != '') { // strip empty properties
properties.push(prop);
}
});

View File

@ -0,0 +1,439 @@
/*! normalize.css 2011-08-31T22:02 UTC · http://github.com/necolas/normalize.css */
/* =============================================================================
HTML5 display definitions
========================================================================== */
/*
* Corrects block display not defined in IE6/7/8/9 & FF3
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section {
display: block;
}
/*
* Corrects inline-block display not defined in IE6/7/8/9 & FF3
*/
audio,
canvas,
video {
display: inline-block;
*display: inline;
*zoom: 1;
}
/*
* Prevents modern browsers from displaying 'audio' without controls
*/
audio:not([controls]) {
display: none;
}
/*
* Addresses styling for 'hidden' attribute not present in IE7/8/9, FF3, S4
* Known issue: no IE6 support
*/
[hidden] {
display: none;
}
/* =============================================================================
Base
========================================================================== */
/*
* 1. Corrects text resizing oddly in IE6/7 when body font-size is set using em units
* http://clagnut.com/blog/348/#c790
* 2. Keeps page centred in all browsers regardless of content height
* 3. Prevents iOS text size adjust after orientation change, without disabling user zoom
* www.456bereastreet.com/archive/201012/controlling_text_size_in_safari_for_ios_without_disabling_user_zoom/
*/
html {
font-size: 100%; /* 1 */
overflow-y: scroll; /* 2 */
-webkit-text-size-adjust: 100%; /* 3 */
-ms-text-size-adjust: 100%; /* 3 */
}
/*
* Addresses margins handled incorrectly in IE6/7
*/
body {
margin: 0;
}
/*
* Addresses font-family inconsistency between 'textarea' and other form elements.
*/
body,
button,
input,
select,
textarea {
font-family: sans-serif;
}
/* =============================================================================
Links
========================================================================== */
a {
color: #00e;
}
a:visited {
color: #551a8b;
}
/*
* Addresses outline displayed oddly in Chrome
*/
a:focus {
outline: thin dotted;
}
/*
* Improves readability when focused and also mouse hovered in all browsers
* people.opera.com/patrickl/experiments/keyboard/test
*/
a:hover,
a:active {
outline: 0;
}
/* =============================================================================
Typography
========================================================================== */
/*
* Addresses styling not present in IE7/8/9, S5, Chrome
*/
abbr[title] {
border-bottom: 1px dotted;
}
/*
* Addresses style set to 'bolder' in FF3/4, S4/5, Chrome
*/
b,
strong {
font-weight: bold;
}
blockquote {
margin: 1em 40px;
}
/*
* Addresses styling not present in S5, Chrome
*/
dfn {
font-style: italic;
}
/*
* Addresses styling not present in IE6/7/8/9
*/
mark {
background: #ff0;
color: #000;
}
/*
* Corrects font family set oddly in IE6, S4/5, Chrome
* en.wikipedia.org/wiki/User:Davidgothberg/Test59
*/
pre,
code,
kbd,
samp {
font-family: monospace, serif;
_font-family: 'courier new', monospace;
font-size: 1em;
}
/*
* Improves readability of pre-formatted text in all browsers
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/*
* 1. Addresses CSS quotes not supported in IE6/7
* 2. Addresses quote property not supported in S4
*/
/* 1 */
q {
quotes: none;
}
/* 2 */
q:before,
q:after {
content: '';
content: none;
}
small {
font-size: 75%;
}
/*
* Prevents sub and sup affecting line-height in all browsers
* gist.github.com/413930
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* =============================================================================
Lists
========================================================================== */
ul,
ol {
margin: 1em 0;
padding: 0 0 0 40px;
}
dd {
margin: 0 0 0 40px;
}
nav ul,
nav ol {
list-style: none;
list-style-image: none;
}
/* =============================================================================
Embedded content
========================================================================== */
/*
* 1. Removes border when inside 'a' element in IE6/7/8/9, F3
* 2. Improves image quality when scaled in IE7
* code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/
*/
img {
border: 0; /* 1 */
-ms-interpolation-mode: bicubic; /* 2 */
}
/*
* Corrects overflow displayed oddly in IE9
*/
svg:not(:root) {
overflow: hidden;
}
/* =============================================================================
Figures
========================================================================== */
/*
* Addresses margin not present in IE6/7/8/9, S5, O11
*/
figure {
margin: 0;
}
/* =============================================================================
Forms
========================================================================== */
/*
* Corrects margin displayed oddly in IE6/7
*/
form {
margin: 0;
}
/*
* Define consistent margin and padding
*/
fieldset {
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/*
* 1. Corrects color not being inherited in IE6/7/8/9
* 2. Corrects alignment displayed oddly in IE6/7
*/
legend {
border: 0; /* 1 */
*margin-left: -7px; /* 2 */
}
/*
* 1. Corrects font size not being inherited in all browsers
* 2. Addresses margins set differently in IE6/7, F3/4, S5, Chrome
* 3. Improves appearance and consistency in all browsers
*/
button,
input,
select,
textarea {
font-size: 100%; /* 1 */
margin: 0; /* 2 */
vertical-align: baseline; /* 3 */
*vertical-align: middle; /* 3 */
}
/*
* 1. Addresses FF3/4 setting line-height using !important in the UA stylesheet
* 2. Corrects inner spacing displayed oddly in IE6/7
*/
button,
input {
line-height: normal; /* 1 */
*overflow: visible; /* 2 */
}
/*
* Corrects overlap and whitespace issue for buttons and inputs in IE6/7
* Known issue: reintroduces inner spacing
*/
table button,
table input {
*overflow: auto;
}
/*
* 1. Improves usability and consistency of cursor style between image-type 'input' and others
* 2. Corrects inability to style clickable 'input' types in iOS
*/
button,
html input[type="button"],
input[type="reset"],
input[type="submit"] {
cursor: pointer; /* 1 */
-webkit-appearance: button; /* 2 */
}
/*
* 1. Addresses box sizing set to content-box in IE8/9
* 2. Addresses excess padding in IE8/9
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Addresses appearance set to searchfield in S5, Chrome
* 2. Addresses box sizing set to border-box in S5, Chrome (include -moz to future-proof)
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/*
* Corrects inner padding displayed oddly in S5, Chrome on OSX
*/
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
* Corrects inner padding and border displayed oddly in FF3/4
* www.sitepen.com/blog/2008/05/14/the-devils-in-the-details-fixing-dojos-toolbar-buttons/
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/*
* 1. Removes default vertical scrollbar in IE6/7/8/9
* 2. Improves readability and alignment in all browsers
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* =============================================================================
Tables
========================================================================== */
/*
* Remove most spacing between table cells
*/
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@ -11,15 +11,28 @@ table {
width: 100%;
}
.property input,
.property textarea {
border: 1px solid grey;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
width: 100%;
}
.invalid {
background-color: #A20000;
color: white;
}
tr th {
border-bottom: 2px solid grey;
text-align: left;
font-size: 0.9em;
}
tr.optional {
color: silver;
color: #7A7A7A;
}
tr.property td {