';
$('#' + wid).prepend($(text).css(css)).css('overflow', 'visible');
},
isUserMemberOf: function (user, userGroups) {
if (!this.userGroups) return true;
if (typeof userGroups !== 'object') userGroups = [userGroups];
for (var g = 0; g < userGroups.length; g++) {
var group = this.userGroups['system.group.' + userGroups[g]];
if (!group || !group.common || !group.common.members || !group.common.members.length) continue;
if (group.common.members.indexOf('system.user.' + user) !== -1) return true;
}
return false;
},
renderWidget: function (viewDiv, view, id, groupId) {
var $view;
var that = this;
if (!groupId) {
$view = $('#visview_' + viewDiv);
} else {
$view = $('#' + groupId);
}
if (!$view.length) return;
var widget = this.views[view].widgets[id];
if (groupId && widget) {
widget = JSON.parse(JSON.stringify(widget));
var aCount = parseInt(this.views[view].widgets[groupId].data.attrCount, 10);
if (aCount) {
$.map(widget.data, function(val, key) {
var m;
if (typeof val === 'string' && (m = val.match(/^groupAttr(\d+)$/))) {
widget.data[key] = that.views[view].widgets[groupId].data[m[0]] || '';
}
});
}
}
var isRelative = widget && widget.style && (widget.style.position === 'relative' || widget.style.position === 'static' || widget.style.position === 'sticky');
// if widget has relative position => insert it into relative div
if (this.editMode && isRelative && viewDiv === view) {
if (this.views[view].settings && this.views[view].settings.sizex) {
var $relativeView = $view.find('.vis-edit-relative');
if (!$relativeView.length) {
var ww = this.views[view].settings.sizex;
var hh = this.views[view].settings.sizey;
if (parseFloat(ww).toString() === ww.toString()) ww = parseFloat(ww);
if (parseFloat(hh).toString() === hh.toString()) hh = parseFloat(hh);
if (typeof ww === 'number' || ww[ww.length - 1] < '0' || ww[ww.length - 1] > '9') {
ww = ww + 'px';
}
if (typeof hh === 'number' || hh[hh.length - 1] < '0' || hh[hh.length - 1] > '9') {
hh = hh + 'px';
}
$view.append('');
$view = $view.find('.vis-edit-relative');
} else {
$view = $relativeView;
}
}
}
// Add to the global array of widgets
try {
var userGroups;
if (!this.editMode && widget.data['visibility-groups'] && widget.data['visibility-groups'].length) {
userGroups = widget.data['visibility-groups'];
if (widget.data['visibility-groups-action'] === 'hide') {
if (!this.isUserMemberOf(this.conn.getUser(), userGroups)) return;
userGroups = null;
}
}
this.widgets[id] = {
wid: id,
data: new can.Map($.extend({
wid: id
}, widget.data))
};
} catch (e) {
console.log('Cannot bind data of widget widget:' + id);
return;
}
// Register oid to detect changes
// if (widget.data.oid !== 'nothing_selected')
// $.homematic("advisState", widget.data.oid, widget.data.hm_wid);
var widgetData = this.widgets[id].data;
try {
//noinspection JSJQueryEfficiency
var $widget = $('#' + id);
if ($widget.length) {
var destroy = $widget.data('destroy');
if (typeof destroy === 'function') {
$widget.off('resize'); // remove resize handler
destroy(id, $widget);
$widget.data('destroy', null);
}
if (isRelative && !$view.find('#' + id).length) {
$widget.remove();
$widget.length = 0;
} else {
$widget.html('').attr('id', id + '_removed');
}
}
var canWidget;
// Append html element to view
if (widget.data && widget.data.oid) {
canWidget = can.view(widget.tpl, {
val: this.states.attr(widget.data.oid + '.val'),
data: widgetData,
viewDiv: viewDiv,
view: view
});
if ($widget.length) {
if ($widget.parent().attr('id') !== $view.attr('id')) $widget.appendTo($view);
$widget.replaceWith(canWidget);
// shift widget to group if required
} else {
$view.append(canWidget);
}
} else if (widget.tpl) {
canWidget = can.view(widget.tpl, {
data: widgetData,
viewDiv: viewDiv,
view: view
});
if ($widget.length) {
if ($widget.parent().attr('id') !== $view.attr('id')) $widget.appendTo($view);
$widget.replaceWith(canWidget);
// shift widget to group if required
} else {
$view.append(canWidget);
}
} else {
console.error('Widget "' + id + '" is invalid. Please delete it.');
return;
}
var $wid = null;
if (widget.style && !widgetData._no_style) {
$wid = $wid || $('#' + id);
// fix position
for (var attr in widget.style) {
if (!widget.style.hasOwnProperty(attr)) continue;
if (attr === 'top' || attr === 'left' || attr === 'width' || attr === 'height') {
var val = widget.style[attr];
if (val !== '0' && val !== 0 && val !== null && val !== '' && val.toString().match(/^[-+]?\d+$/)) {
widget.style[attr] = val + 'px';
}
}
}
$wid.css(widget.style);
}
if (widget.data && widget.data.class) {
$wid = $wid || $('#' + id);
$wid.addClass(widget.data.class);
}
var $tpl = $('#' + widget.tpl);
$wid.addClass('vis-tpl-' + $tpl.data('vis-set') + '-' + $tpl.data('vis-name'));
if (!this.editMode) {
if (this.isWidgetFilteredOut(view, id) || this.isWidgetHidden(view, id, undefined, widget.data)) {
var mWidget = document.getElementById(id);
$(mWidget).hide();
if (mWidget &&
mWidget._customHandlers &&
mWidget._customHandlers.onHide) {
mWidget._customHandlers.onHide(mWidget, id);
}
}
// Processing of gestures
if (typeof $$ !== 'undefined') this.addGestures(id, widget.data);
}
// processing of signals
var s = 0;
while (widget.data['signals-oid-' + s]) {
this.addSignalIcon(view, id, widget.data, s);
s++;
}
if (widget.data['lc-oid']) {
this.addLastChange(view, id, widget.data);
}
// If edit mode, bind on click event to open this widget in edit dialog
if (this.editMode) {
this.bindWidgetClick(viewDiv, view, id);
// @SJ cannot select menu and dialogs if it is enabled
/*if ($('#wid_all_lock_f').hasClass("ui-state-active")) {
$('#' + id).addClass("vis-widget-lock")
}*/
}
$(document).trigger('wid_added', id);
if (id[0] === 'g') {
for (var w = 0; w < widget.data.members.length; w++) {
if (widget.data.members[w] === id) continue;
this.renderWidget(viewDiv, view, widget.data.members[w], id);
}
}
} catch (e) {
var lines = (e.toString() + e.stack.toString()).split('\n');
this.conn.logError('can\'t render ' + widget.tpl + ' ' + id + ' on "' + view + '": ');
for (var l = 0; l < lines.length; l++) {
this.conn.logError(l + ' - ' + lines[l]);
}
}
if (userGroups && $wid && $wid.length) {
if (!this.isUserMemberOf(this.conn.getUser(), userGroups)) {
$wid.addClass('vis-user-disabled');
}
}
},
changeView: function (viewDiv, view, hideOptions, showOptions, sync, callback) {
var that = this;
if (typeof view === 'object') {
callback = sync;
sync = showOptions;
hideOptions = showOptions;
view = viewDiv;
}
if (!view && viewDiv) view = viewDiv;
if (typeof hideOptions === 'function') {
callback = hideOptions;
hideOptions = undefined;
}
if (typeof showOptions === 'function') {
callback = showOptions;
showOptions = undefined;
}
if (typeof sync === 'function') {
callback = sync;
sync = undefined;
}
var effect = (hideOptions !== undefined) && (hideOptions.effect !== undefined) && hideOptions.effect;
if (!effect) {
effect = (showOptions !== undefined) && (showOptions.effect !== undefined) && showOptions.effect;
}
if (effect && ((showOptions === undefined) || !showOptions.effect)) {
showOptions = {effect: hideOptions.effect, options: {}, duration: hideOptions.duration};
}
if (effect && ((hideOptions === undefined) || !hideOptions.effect)) {
hideOptions = {effect: showOptions.effect, options: {}, duration: showOptions.duration};
}
hideOptions = $.extend(true, {effect: undefined, options: {}, duration: 0}, hideOptions);
showOptions = $.extend(true, {effect: undefined, options: {}, duration: 0}, showOptions);
if (hideOptions.effect === 'show') effect = false;
if (this.editMode && this.activeView !== this.activeViewDiv) {
this.destroyGroupEdit(this.activeViewDiv, this.activeView);
}
if (!this.views[view]) {
//noinspection JSUnusedAssignment
view = null;
for (var prop in this.views) {
if (prop === '___settings') continue;
view = prop;
break;
}
}
// If really changed
if (this.activeView !== viewDiv) {
if (effect) {
this.renderView(viewDiv, view, true, function (_viewDiv, _view) {
var $view = $('#visview_' + _viewDiv);
// Get the view, if required, from Container
if ($view.parent().attr('id') !== 'vis_container') $view.appendTo('#vis_container');
var oldView = that.activeView;
that.postChangeView(_viewDiv, _view, callback);
// If hide and show at the same time
if (sync) {
$view.show(showOptions.effect, showOptions.options, parseInt(showOptions.duration, 10)).dequeue();
}
$('#visview_' + oldView).hide(hideOptions.effect, hideOptions.options, parseInt(hideOptions.duration, 10), function () {
// If first hide, than show
if (!sync) {
$view.show(showOptions.effect, showOptions.options, parseInt(showOptions.duration, 10), function () {
that.destroyUnusedViews();
});
} else {
that.destroyUnusedViews();
}
});
});
} else {
var $oldView = $('#visview_' + that.activeViewDiv);
// disable view and show some action
$oldView.find('> .vis-view-disabled').show();
this.renderView(viewDiv, view, true, function (_viewDiv, _view) {
var $oldView;
if (that.activeViewDiv !== _viewDiv) {
$oldView = $('#visview_' + that.activeViewDiv);
// hide old view
$oldView.hide();
$oldView.find('.vis-view-disabled').hide();
}
var $view = $('#visview_' + _viewDiv);
// Get the view, if required, from Container
if ($view.parent().attr('id') !== 'vis_container') {
$view.appendTo('#vis_container');
}
// show new view
$view.show();
$view.find('.vis-view-disabled').hide();
if (that.activeViewDiv !== _viewDiv) {
if ($oldView.hasClass('vis-edit-group')) {
that.destroyView(that.activeViewDiv, that.activeView);
} else {
$oldView.hide();
}
}
that.postChangeView(_viewDiv, _view, callback);
that.destroyUnusedViews();
});
}
// remember last click for de-bounce
this.lastChange = Date.now();
} else {
this.renderView(viewDiv, view, false, function (_viewDiv, _view) {
var $view = $('#visview_' + _viewDiv);
// Get the view, if required, from Container
if ($view.parent().attr('id') !== 'vis_container') $view.appendTo('#vis_container');
$view.show();
that.postChangeView(_viewDiv, _view, callback);
that.destroyUnusedViews();
});
}
},
postChangeView: function (viewDiv, view, callback) {
this.activeView = view;
this.activeViewDiv = viewDiv;
/*$('#visview_' + viewDiv).find('.vis-view-container').each(function () {
$('#visview_' + $(this).attr('data-vis-contains')).show();
});*/
this.updateContainers(viewDiv, view);
if (!this.editMode) {
this.conn.sendCommand(this.instance, 'changedView', this.projectPrefix ? (this.projectPrefix + this.activeView) : this.activeView);
$(window).trigger('viewChanged', viewDiv);
}
if (window.location.hash.slice(1) !== view) {
if (history && history.pushState) {
history.pushState({}, '', '#' + viewDiv);
}
}
// Navigation-Widgets
for (var i = 0; i < this.navChangeCallbacks.length; i++) {
this.navChangeCallbacks[i](viewDiv, view);
}
// --------- Editor -----------------
if (this.editMode) {
this.changeViewEdit(viewDiv, view, false, callback);
} else if (typeof callback === 'function') {
callback(viewDiv, view);
}
this.updateIframeZoom();
},
loadRemote: function (callback, callbackArg) {
var that = this;
if (!this.projectPrefix) {
if (callback) callback.call(that, callbackArg);
return;
}
this.conn.readFile(this.projectPrefix + 'vis-views.json', function (err, data) {
if (err) {
window.alert(that.projectPrefix + 'vis-views.json ' + err);
if (err === 'permissionError') {
that.showWaitScreen(true, '', _('Loading stopped', location.protocol + '//' + location.host, location.protocol + '//' + location.host), 0);
// do nothing any more
return;
}
}
if (typeof app !== 'undefined' && app.replaceFilesInViewsWeb) {
data = app.replaceFilesInViewsWeb(data);
}
if (data) {
if (typeof data === 'string') {
try {
that.views = JSON.parse(data.trim());
} catch (e) {
console.log('Cannot parse views file "' + that.projectPrefix + 'vis-views.json"');
window.alert('Cannot parse views file "' + that.projectPrefix + 'vis-views.json');
that.views = null;
}
} else {
that.views = data;
}
var _data = that.getUsedObjectIDs();
that.subscribing.IDs = _data.IDs;
that.subscribing.byViews = _data.byViews;
} else {
that.views = null;
}
if (callback) callback.call(that, callbackArg);
});
},
wakeUpCallbacks: [],
initWakeUp: function () {
var that = this;
var oldTime = Date.now();
setInterval(function () {
var currentTime = Date.now();
//console.log("checkWakeUp "+ (currentTime - oldTime));
if (currentTime > (oldTime + 10000)) {
oldTime = currentTime;
for (var i = 0; i < that.wakeUpCallbacks.length; i++) {
//console.log("calling wakeUpCallback!");
that.wakeUpCallbacks[i]();
}
} else {
oldTime = currentTime;
}
}, 2500);
},
onWakeUp: function (callback) {
this.wakeUpCallbacks.push(callback);
},
showMessage: function (message, title, icon, width, callback) {
// load some theme to show message
if (!this.editMode && !$('#commonTheme').length) {
$('head').prepend('');
}
if (typeof icon === 'number') {
callback = width;
width = icon;
icon = null;
}
if (typeof title === 'function') {
callback = title;
title = null;
} else if (typeof icon === 'function') {
callback = icon;
icon = null;
} else if (typeof width === 'function') {
callback = width;
width = null;
}
if (!this.$dialogMessage) {
this.$dialogMessage = $('#dialog-message');
this.$dialogMessage.dialog({
autoOpen: false,
modal: true,
open: function () {
$(this).parent().css({'z-index': 1003});
var callback = $(this).data('callback');
if (callback) {
$(this).find('#dialog_message_cancel').show();
} else {
$(this).find('#dialog_message_cancel').hide();
}
},
buttons: [
{
text: _('Ok'),
click: function () {
var callback = $(this).data('callback');
$(this).dialog('close');
if (typeof callback === 'function') {
callback(true);
$(this).data('callback', null);
}
}
},
{
id: 'dialog_message_cancel',
text: _('Cancel'),
click: function () {
var callback = $(this).data('callback');
$(this).dialog('close');
if (typeof callback === 'function') {
callback(false);
$(this).data('callback', null);
}
}
}
]
});
}
this.$dialogMessage.dialog('option', 'title', title || _('Message'));
if (width) {
this.$dialogMessage.dialog('option', 'width', width);
} else {
this.$dialogMessage.dialog('option', 'width', 300);
}
$('#dialog-message-text').html(message);
this.$dialogMessage.data('callback', callback ? callback : null);
if (icon) {
$('#dialog-message-icon')
.show()
.attr('class', '')
.addClass('ui-icon ui-icon-' + icon);
} else {
$('#dialog-message-icon').hide();
}
this.$dialogMessage.dialog('open');
},
showError: function (error) {
this.showMessage(error, _('Error'), 'alert', 400);
},
waitScreenVal: 0,
showWaitScreen: function (isShow, appendText, newText, step) {
var waitScreen = document.getElementById("waitScreen");
if (!waitScreen && isShow) {
$('body').append('
');
waitScreen = document.getElementById("waitScreen");
this.waitScreenVal = 0;
}
$('.vis-progressbar').progressbar({value: this.waitScreenVal}).height(19);
if (isShow) {
$(waitScreen).show();
if (newText !== null && newText !== undefined) {
$('#waitText').html(newText);
}
if (appendText !== null && appendText !== undefined) {
$('#waitText').append(appendText);
}
if (step !== undefined) {
this.waitScreenVal += step;
_setTimeout(function (_val) {
$('.vis-progressbar').progressbar('value', _val);
}, 0, this.waitScreenVal);
}
} else if (waitScreen) {
$(waitScreen).remove();
}
},
registerOnChange: function (callback, arg) {
for (var i = 0, len = this.onChangeCallbacks.length; i < len; i++) {
if (this.onChangeCallbacks[i].callback === callback &&
this.onChangeCallbacks[i].arg === arg) {
return;
}
}
this.onChangeCallbacks[this.onChangeCallbacks.length] = {callback: callback, arg: arg};
},
unregisterOnChange: function (callback, arg) {
for (var i = 0, len = this.onChangeCallbacks.length; i < len; i++) {
if (this.onChangeCallbacks[i].callback === callback &&
(arg === undefined || this.onChangeCallbacks[i].arg === arg)) {
this.onChangeCallbacks.slice(i, 1);
return;
}
}
},
isWidgetHidden: function (view, widget, val, widgetData) {
widgetData = widgetData || this.views[view].widgets[widget].data;
var oid = widgetData['visibility-oid'];
var condition = widgetData['visibility-cond'];
if (oid) {
if (val === undefined) val = this.states.attr(oid + '.val');
if (val === undefined) return (condition === 'not exist');
var value = widgetData['visibility-val'];
if (!condition || value === undefined) return (condition === 'not exist');
if (val === 'null' && condition !== 'exist' && condition !== 'not exist') return false;
var t = typeof val;
if (t === 'boolean' || val === 'false' || val === 'true') {
value = (value === 'true' || value === true || value === 1 || value === '1');
} else
if (t === 'number') {
value = parseFloat(value);
} else
if (t === 'object') {
val = JSON.stringify(val);
}
// Take care: return true if widget is hidden!
switch (condition) {
case '==':
value = value.toString();
val = val.toString();
if (val === '1') val = 'true';
if (value === '1') value = 'true';
if (val === '0') val = 'false';
if (value === '0') value = 'false';
return value !== val;
case '!=':
value = value.toString();
val = val.toString();
if (val === '1') val = 'true';
if (value === '1') value = 'true';
if (val === '0') val = 'false';
if (value === '0') value = 'false';
return value === val;
case '>=':
return val < value;
case '<=':
return val > value;
case '>':
return val <= value;
case '<':
return val >= value;
case 'consist':
value = value.toString();
val = val.toString();
return (val.toString().indexOf(value) === -1);
case 'not consist':
value = value.toString();
val = val.toString();
return (val.toString().indexOf(value) !== -1);
case 'exist':
return val === 'null';
case 'not exist':
return val !== 'null';
default:
console.log('Unknown visibility condition for ' + widget + ': ' + condition);
return false;
}
} else {
return (condition === 'not exist');
}
},
isWidgetFilteredOut: function (view, widget) {
var w = this.views[view].widgets[widget];
var v = this.viewsActiveFilter[view];
return (w &&
w.data &&
w.data.filterkey &&
widget &&
widget.data &&
v.length > 0 &&
v.indexOf(widget.data.filterkey) === -1);
},
calcCommonStyle: function (recalc) {
if (!this.commonStyle || recalc) {
if (this.editMode) {
this.commonStyle = this.config.editorTheme || 'redmond';
return this.commonStyle;
}
var styles = {};
if (this.views) {
for (var view in this.views) {
if (!this.views.hasOwnProperty(view)) continue;
if (view === '___settings') continue;
if (!this.views[view] || !this.views[view].settings.theme) continue;
if (this.views[view].settings.theme && styles[this.views[view].settings.theme]) {
styles[this.views[view].settings.theme]++;
} else {
styles[this.views[view].settings.theme] = 1;
}
}
}
var max = 0;
this.commonStyle = '';
for (var s in styles) {
if (styles[s] > max) {
max = styles[s];
this.commonStyle = s;
}
}
}
return this.commonStyle;
},
formatValue: function formatValue(value, decimals, _format) {
if (typeof decimals !== 'number') {
decimals = 2;
_format = decimals;
}
//format = (_format === undefined) ? (that.isFloatComma) ? ".," : ",." : _format;
// does not work...
// using default german...
var format = (_format === undefined) ? ".," : _format;
if (typeof value !== "number") value = parseFloat(value);
return isNaN(value) ? "" : value.toFixed(decimals || 0).replace(format[0], format[1]).replace(/\B(?=(\d{3})+(?!\d))/g, format[0]);
},
formatDate: function formatDate(dateObj, isDuration, _format) {
// copied from js-controller/lib/adapter.js
if ((typeof isDuration === 'string' && isDuration.toLowerCase() === 'duration') || isDuration === true) {
isDuration = true;
}
if (typeof isDuration !== 'boolean') {
_format = isDuration;
isDuration = false;
}
if (!dateObj) return '';
var type = typeof dateObj;
if (type === 'string') dateObj = new Date(dateObj);
if (type !== 'object') {
var j = parseInt(dateObj, 10);
if (j == dateObj) {
// may this is interval
if (j < 946681200) {
isDuration = true;
dateObj = new Date(dateObj);
} else {
// if less 2000.01.01 00:00:00
dateObj = (j < 946681200000) ? new Date(j * 1000) : new Date(j);
}
} else {
dateObj = new Date(dateObj);
}
}
var format = _format || this.dateFormat || 'DD.MM.YYYY';
if (isDuration) dateObj.setMilliseconds(dateObj.getMilliseconds() + dateObj.getTimezoneOffset() * 60 * 1000);
var validFormatChars = 'YJГMМDTДhSчmмsс';
var s = '';
var result = '';
function put(s) {
var v = '';
switch (s) {
case 'YYYY':
case 'JJJJ':
case 'ГГГГ':
case 'YY':
case 'JJ':
case 'ГГ':
v = dateObj.getFullYear();
if (s.length === 2) v %= 100;
break;
case 'MM':
case 'M':
case 'ММ':
case 'М':
v = dateObj.getMonth() + 1;
if ((v < 10) && (s.length === 2)) v = '0' + v;
break;
case 'DD':
case 'TT':
case 'D':
case 'T':
case 'ДД':
case 'Д':
v = dateObj.getDate();
if ((v < 10) && (s.length === 2)) v = '0' + v;
break;
case 'hh':
case 'SS':
case 'h':
case 'S':
case 'чч':
case 'ч':
v = dateObj.getHours();
if ((v < 10) && (s.length === 2)) v = '0' + v;
break;
case 'mm':
case 'm':
case 'мм':
case 'м':
v = dateObj.getMinutes();
if ((v < 10) && (s.length === 2)) v = '0' + v;
break;
case 'ss':
case 's':
case 'cc':
case 'c':
v = dateObj.getSeconds();
if ((v < 10) && (s.length === 2)) v = '0' + v;
v = v.toString();
break;
case 'sss':
case 'ссс':
v = dateObj.getMilliseconds();
if (v < 10) {
v = '00' + v;
} else if (v < 100) {
v = '0' + v;
}
v = v.toString();
}
return result += v;
}
for (var i = 0; i < format.length; i++) {
if (validFormatChars.indexOf(format[i]) >= 0)
s += format[i];
else {
put(s);
s = '';
result += format[i];
}
}
put(s);
return result;
},
extractBinding: function (format) {
if (this.editMode || !format) return null;
if (this.bindingsCache[format]) return JSON.parse(JSON.stringify(this.bindingsCache[format]));
var result = extractBinding(format);
// cache bindings
if (result) {
this.bindingsCache = this.bindingsCache || {};
this.bindingsCache[format] = JSON.parse(JSON.stringify(result));
}
return result;
},
getSpecialValues: function (name, view, wid, widget) {
switch (name) {
case 'username.val':
return this.user;
case 'login.val':
return this.loginRequired;
case 'instance.val':
return this.instance;
case 'language.val':
return this.language;
case 'wid.val':
return wid;
case 'wname.val':
return widget && (widget.data.name || wid);
case 'view.val':
return view;
default:
return undefined;
}
},
formatBinding: function (format, view, wid, widget) {
var oids = this.extractBinding(format);
for (var t = 0; t < oids.length; t++) {
var value;
if (oids[t].visOid) {
value = this.getSpecialValues(oids[t].visOid, view, wid, widget);
if (value === undefined) {
value = this.states.attr(oids[t].visOid);
}
}
if (oids[t].operations) {
for (var k = 0; k < oids[t].operations.length; k++) {
switch (oids[t].operations[k].op) {
case 'eval':
var string = '';//'(function() {';
for (var a = 0; a < oids[t].operations[k].arg.length; a++) {
if (!oids[t].operations[k].arg[a].name) continue;
value = this.getSpecialValues(oids[t].operations[k].arg[a].visOid, view, wid, widget);
if (value === undefined) {
value = this.states.attr(oids[t].operations[k].arg[a].visOid);
}
string += 'var ' + oids[t].operations[k].arg[a].name + ' = "' + value + '";';
}
var formula = oids[t].operations[k].formula;
if (formula && formula.indexOf('widget.') !== -1) {
string += 'var widget = ' + JSON.stringify(widget) + ';';
}
string += 'return ' + oids[t].operations[k].formula + ';';
//string += '}())';
try {
value = new Function(string)();
} catch (e) {
console.error('Error in eval[value] : ' + format);
console.error('Error in eval[script]: ' + string);
console.error('Error in eval[error] : ' + e);
value = 0;
}
break;
case '*':
if (oids[t].operations[k].arg !== undefined) {
value = parseFloat(value) * oids[t].operations[k].arg;
}
break;
case '/':
if (oids[t].operations[k].arg !== undefined) {
value = parseFloat(value) / oids[t].operations[k].arg;
}
break;
case '+':
if (oids[t].operations[k].arg !== undefined) {
value = parseFloat(value) + oids[t].operations[k].arg;
}
break;
case '-':
if (oids[t].operations[k].arg !== undefined) {
value = parseFloat(value) - oids[t].operations[k].arg;
}
break;
case '%':
if (oids[t].operations[k].arg !== undefined) {
value = parseFloat(value) % oids[t].operations[k].arg;
}
break;
case 'round':
if (oids[t].operations[k].arg === undefined) {
value = Math.round(parseFloat(value));
} else {
value = parseFloat(value).toFixed(oids[t].operations[k].arg);
}
break;
case 'pow':
if (oids[t].operations[k].arg === undefined) {
value = Math.pow(parseFloat(value), 2);
} else {
value = Math.pow(parseFloat(value), oids[t].operations[k].arg);
}
break;
case 'sqrt':
value = Math.sqrt(parseFloat(value));
break;
case 'hex':
value = Math.round(parseFloat(value)).toString(16);
break;
case 'hex2':
value = Math.round(parseFloat(value)).toString(16);
if (value.length < 2) value = '0' + value;
break;
case 'HEX':
value = Math.round(parseFloat(value)).toString(16).toUpperCase();
break;
case 'HEX2':
value = Math.round(parseFloat(value)).toString(16).toUpperCase();
if (value.length < 2) value = '0' + value;
break;
case 'value':
value = this.formatValue(value, parseInt(oids[t].operations[k].arg, 10));
break;
case 'array':
value = oids[t].operations[k].arg [~~value];
break;
case 'date':
value = this.formatDate(value, oids[t].operations[k].arg);
break;
case 'min':
value = parseFloat(value);
value = (value < oids[t].operations[k].arg) ? oids[t].operations[k].arg : value;
break;
case 'max':
value = parseFloat(value);
value = (value > oids[t].operations[k].arg) ? oids[t].operations[k].arg : value;
break;
case 'random':
if (oids[t].operations[k].arg === undefined) {
value = Math.random();
} else {
value = Math.random() * oids[t].operations[k].arg;
}
break;
case 'floor':
value = Math.floor(parseFloat(value));
break;
case 'ceil':
value = Math.ceil(parseFloat(value));
break;
} //switch
}
} //if for
format = format.replace(oids[t].token, value);
}//for
format = format.replace(/{{/g, '{').replace(/}}/g, '}');
return format;
},
findNearestResolution: function (resultRequiredOrX, height) {
var w;
var h;
if (height !== undefined) {
w = resultRequiredOrX;
h = height;
resultRequiredOrX = false;
} else {
w = $(window).width();
h = $(window).height();
}
var result = null;
var views = [];
var difference = 10000;
// First find all with best fitting width
for (var view in this.views) {
if (!this.views.hasOwnProperty(view)) continue;
if (view === '___settings') continue;
if (this.views[view].settings && this.views[view].settings.useAsDefault) {
// If difference less than 20%
if (Math.abs(this.views[view].settings.sizex - w) / this.views[view].settings.sizex < 0.2) {
views.push(view);
}
}
}
for (var i = 0; i < views.length; i++) {
if (Math.abs(this.views[views[i]].settings.sizey - h) < difference) {
result = views[i];
difference = Math.abs(this.views[views[i]].settings.sizey - h);
}
}
// try to find by ratio
if (!result) {
var ratio = w / h;
difference = 10000;
for (var view_ in this.views) {
if (!this.views.hasOwnProperty(view_)) continue;
if (view_ === '___settings') continue;
if (this.views[view_].settings && this.views[view_].settings.useAsDefault) {
// If difference less than 20%
if (this.views[view_].settings.sizey && Math.abs(ratio - (this.views[view_].settings.sizex / this.views[view_].settings.sizey)) < difference) {
result = view_;
difference = Math.abs(ratio - (this.views[view_].settings.sizex / this.views[view_].settings.sizey));
}
}
}
}
if (!result && resultRequiredOrX) {
for (var view__ in this.views) {
if (!this.views.hasOwnProperty(view__)) continue;
if (view__ === '___settings') continue;
return view__;
}
}
return result;
},
orientationChange: function () {
if (this.resolutionTimer) return;
var that = this;
this.resolutionTimer = setTimeout(function () {
that.resolutionTimer = null;
var view = that.findNearestResolution();
if (view && view !== that.activeView) {
that.changeView(view, view);
}
}, 200);
},
detectBounce: function (el, isUp) {
if (!this.isTouch) return false;
// Protect against two events
var now = Date.now();
//console.log('gclick: ' + this.lastChange + ' ' + (now - this.lastChange));
if (this.lastChange && now - this.lastChange < this.debounceInterval) {
//console.log('gclick: filtered');
return true;
}
var $el = $(el);
var tag = $(el).prop('tagName').toLowerCase();
while (tag !== 'div') {
$el = $el.parent();
tag = $el.prop('tagName').toLowerCase();
}
var lastClick = $el.data(isUp ? 'lcu' : 'lc');
//console.log('click: ' + lastClick + ' ' + (now - lastClick));
if (lastClick && now - lastClick < this.debounceInterval) {
//console.log('click: filtered');
return true;
}
$el.data(isUp ? 'lcu' : 'lc', now);
return false;
},
createDemoStates: function () {
// Create demo variables
this.states.attr({'demoTemperature.val': 25.4});
this.states.attr({'demoHumidity.val': 55});
},
getHistory: function (id, options, callback) {
// Possible options:
// - **instance - (mandatory) sql.x or history.y
// - **start** - (optional) time in ms - *Date.now()*'
// - **end** - (optional) time in ms - *Date.now()*', by default is (now + 5000 seconds)
// - **step** - (optional) used in aggregate (m4, max, min, average, total) step in ms of intervals
// - **count** - number of values if aggregate is 'onchange' or number of intervals if other aggregate method. Count will be ignored if step is set.
// - **from** - if *from* field should be included in answer
// - **ack** - if *ack* field should be included in answer
// - **q** - if *q* field should be included in answer
// - **addId** - if *id* field should be included in answer
// - **limit** - do not return more entries than limit
// - **ignoreNull** - if null values should be include (false), replaced by last not null value (true) or replaced with 0 (0)
// - **aggregate** - aggregate method:
// - *minmax* - used special algorithm. Splice the whole time range in small intervals and find for every interval max, min, start and end values.
// - *max* - Splice the whole time range in small intervals and find for every interval max value and use it for this interval (nulls will be ignored).
// - *min* - Same as max, but take minimal value.
// - *average* - Same as max, but take average value.
// - *total* - Same as max, but calculate total value.
// - *count* - Same as max, but calculate number of values (nulls will be calculated).
// - *none* - no aggregation
this.conn.getHistory(id, options, callback);
},
destroyView: function (viewDiv, view) {
var $view = $('#visview_' + viewDiv);
console.debug('Destroy ' + view);
// Get all widgets and try to destroy them
for (var wid in this.views[view].widgets) {
if (!this.views[view].widgets.hasOwnProperty(wid)) continue;
this.destroyWidget(viewDiv, view, wid);
}
$view.remove();
this.unsubscribeStates(view);
},
findAndDestroyViews: function () {
if (this.destroyTimeout) {
clearTimeout(this.destroyTimeout);
this.destroyTimeout = null;
}
var containers = [];
var $createdViews = $('.vis-view');
for (var view in this.views) {
if (!this.views.hasOwnProperty(view) || view === '___settings') continue;
if (this.views[view].settings.alwaysRender || view === this.activeView) {
if (containers.indexOf(view) === -1) containers.push(view);
var $containers = $('#visview_' + view).find('.vis-view-container');
$containers.each(function () {
var cview = $(this).attr('data-vis-contains');
if (containers.indexOf(cview) === -1) containers.push(cview);
});
// check dialogs too
var $dialogs = $('.vis-widget-dialog');
$dialogs.each(function () {
if ($(this).is(':visible')) {
var $containers = $(this).find('.vis-view-container');
$containers.each(function () {
var cview = $(this).attr('data-vis-contains');
if (containers.indexOf(cview) === -1) containers.push(cview);
});
}
});
}
}
var that = this;
$createdViews.each(function () {
var $this = $(this);
var view = $this.data('view');
var viewDiv = $this.attr('id').substring('visview_'.length);
// If this view is used as container
if (containers.indexOf(viewDiv) !== -1) return;
if ($this.hasClass('vis-edit-group')) return;
if ($this.data('persistent')) return;
that.destroyView(viewDiv, view);
});
},
destroyUnusedViews: function () {
if (this.destroyTimeout) clearTimeout(this.destroyTimeout);
var timeout = 30000;
if (this.views.___settings && this.views.___settings.destroyViewsAfter !== undefined) {
timeout = this.views.___settings.destroyViewsAfter * 1000;
}
if (timeout) {
this.destroyTimeout = _setTimeout(function (that) {
that.destroyTimeout = null;
that.findAndDestroyViews();
}, timeout, this);
}
},
generateInstance: function () {
if (typeof storage !== 'undefined') {
this.instance = (Math.random() * 4294967296).toString(16);
this.instance = '0000000' + this.instance;
this.instance = this.instance.substring(this.instance.length - 8);
$('#vis_instance').val(this.instance);
storage.set(this.storageKeyInstance, this.instance);
}
},
subscribeStates: function (view, callback) {
if (!view || this.editMode) {
if (callback) callback();
return;
}
// view yet active
if (this.subscribing.activeViews.indexOf(view) !== -1) {
if (callback) callback();
return;
}
this.subscribing.activeViews.push(view);
this.subscribing.byViews[view] = this.subscribing.byViews[view] || [];
// subscribe
var oids = [];
for (var i = 0; i < this.subscribing.byViews[view].length; i++) {
if (this.subscribing.active.indexOf(this.subscribing.byViews[view][i]) === -1) {
this.subscribing.active.push(this.subscribing.byViews[view][i]);
oids.push(this.subscribing.byViews[view][i]);
}
}
if (oids.length) {
var that = this;
console.debug('[' + Date.now() + '] Request ' + oids.length + ' states.');
this.conn.getStates(oids, function (error, data) {
if (error) that.showError(error);
that.updateStates(data);
that.conn.subscribe(oids);
if (callback) callback();
});
} else {
if (callback) callback();
}
},
unsubscribeStates: function (view) {
if (!view || this.editMode) return;
// view yet active
var pos = this.subscribing.activeViews.indexOf(view);
if (pos === -1) return;
this.subscribing.activeViews.splice(pos, 1);
// unsubscribe
var oids = [];
// check every OID
for (var i = 0; i < this.subscribing.byViews[view].length; i++) {
var id = this.subscribing.byViews[view][i];
pos = this.subscribing.active.indexOf(id);
if (pos !== -1) {
var isUsed = false;
// Is OID is used something else
for (var v = 0; v < this.subscribing.activeViews.length; v++) {
if (this.subscribing.byViews[this.subscribing.activeViews[v]].indexOf(id) !== -1) {
isUsed = true;
break;
}
}
if (!isUsed) {
oids.push(id);
this.subscribing.active.splice(pos, 1);
}
}
}
if (oids.length) this.conn.unsubscribe(oids);
},
updateState: function (id, state) {
if (this.editMode) {
this.states[id + '.val'] = state.val;
this.states[id + '.ts'] = state.ts;
this.states[id + '.ack'] = state.ack;
this.states[id + '.lc'] = state.lc;
if (state.q !== undefined) this.states[id + '.q'] = state.q;
} else {
var o = {};
// Check new model
o[id + '.val'] = state.val;
o[id + '.ts'] = state.ts;
o[id + '.ack'] = state.ack;
o[id + '.lc'] = state.lc;
if (state.q !== undefined) o[id + '.q'] = state.q;
try {
this.states.attr(o);
} catch (e) {
this.conn.logError('Error: can\'t create states object for ' + id + '(' + e + '): ' + JSON.stringify(e.stack));
}
}
if (!this.editMode && this.visibility[id]) {
for (var k = 0; k < this.visibility[id].length; k++) {
var mmWidget = document.getElementById(this.visibility[id][k].widget);
if (!mmWidget) continue;
if (this.isWidgetHidden(this.visibility[id][k].view, this.visibility[id][k].widget, state.val) ||
this.isWidgetFilteredOut(this.visibility[id][k].view, this.visibility[id][k].widget)) {
$(mmWidget).hide();
if (mmWidget &&
mmWidget._customHandlers &&
mmWidget._customHandlers.onHide) {
mmWidget._customHandlers.onHide(mmWidget, id);
}
} else {
$(mmWidget).show();
if (mmWidget &&
mmWidget._customHandlers &&
mmWidget._customHandlers.onShow) {
mmWidget._customHandlers.onShow(mmWidget, id);
}
}
}
}
// process signals
if (!this.editMode && this.signals[id]) {
for (var s = 0; s < this.signals[id].length; s++) {
var signal = this.signals[id][s];
var mWidget = document.getElementById(signal.widget);
if (!mWidget) continue;
if (this.isSignalVisible(signal.view, signal.widget, signal.index, state.val)) {
$(mWidget).find('.vis-signal[data-index="' + signal.index + '"]').show();
} else {
$(mWidget).find('.vis-signal[data-index="' + signal.index + '"]').hide();
}
}
}
// Process last update
if (!this.editMode && this.lastChanges[id]) {
for (var l = 0; l < this.lastChanges[id].length; l++) {
var update = this.lastChanges[id][l];
var uWidget = document.getElementById(update.widget);
if (uWidget) {
var $lc = $(uWidget).find('.vis-last-change');
$lc.html(this.binds.basic.formatDate($lc.data('type') === 'last-change' ? state.lc : state.ts, $lc.data('format'), $lc.data('interval') === 'true'));
}
}
}
// Bindings on every element
if (!this.editMode && this.bindings[id]) {
for (var i = 0; i < this.bindings[id].length; i++) {
var widget = this.views[this.bindings[id][i].view].widgets[this.bindings[id][i].widget];
var value = this.formatBinding(this.bindings[id][i].format, this.bindings[id][i].view, this.bindings[id][i].widget, widget);
widget[this.bindings[id][i].type][this.bindings[id][i].attr] = value;
if (this.widgets[this.bindings[id][i].widget] && this.bindings[id][i].type === 'data') {
this.widgets[this.bindings[id][i].widget][this.bindings[id][i].type + '.' + this.bindings[id][i].attr] = value;
}
this.reRenderWidget(this.bindings[id][i].view, this.bindings[id][i].view, this.bindings[id][i].widget);
}
}
// Inform other widgets, that do not support canJS
for (var j = 0, len = this.onChangeCallbacks.length; j < len; j++) {
this.onChangeCallbacks[j].callback(this.onChangeCallbacks[j].arg, id, state.val, state.ack);
}
if (this.editMode && $.fn.selectId) $.fn.selectId('stateAll', id, state);
},
updateStates: function (data) {
if (data) {
for (var id in data) {
if (!data.hasOwnProperty(id)) continue;
var obj = data[id];
if (!obj) continue;
try {
if (this.editMode) {
this.states[id + '.val'] = obj.val;
this.states[id + '.ts'] = obj.ts;
this.states[id + '.ack'] = obj.ack;
this.states[id + '.lc'] = obj.lc;
if (obj.q !== undefined) this.states[id + '.q'] = obj.q;
} else {
var oo = {};
oo[id + '.val'] = obj.val;
oo[id + '.ts'] = obj.ts;
oo[id + '.ack'] = obj.ack;
oo[id + '.lc'] = obj.lc;
if (obj.q !== undefined) oo[id + '.q'] = obj.q;
this.states.attr(oo);
}
} catch (e) {
this.conn.logError('Error: can\'t create states object for ' + id + '(' + e + ')');
}
if (!this.editMode && this.bindings[id]) {
for (var i = 0; i < this.bindings[id].length; i++) {
var widget = this.views[this.bindings[id][i].view].widgets[this.bindings[id][i].widget];
widget[this.bindings[id][i].type][this.bindings[id][i].attr] = this.formatBinding(this.bindings[id][i].format, this.bindings[id][i].view, this.bindings[id][i].widget, widget);
}
}
}
}
},
updateIframeZoom: function (zoom) {
if (zoom === undefined) zoom = document.body.style.zoom;
if (zoom) {
$('iframe').each(function () {
if (this.contentWindow.document.body) {
this.contentWindow.document.body.style.zoom = zoom;
}
}).unbind('onload').load(function () {
if (this.contentWindow.document.body) {
this.contentWindow.document.body.style.zoom = zoom;
}
});
}
}
};
// WebApp Cache Management
if ('applicationCache' in window) {
window.addEventListener('load', function (/* e */) {
window.applicationCache.addEventListener('updateready', function (e) {
if (window.applicationCache.status === window.applicationCache.UPDATEREADY) {
vis.showWaitScreen(true, null, _('Update found, loading new Files...'), 100);
$('#waitText').attr('id', 'waitTextDisabled');
$('.vis-progressbar').hide();
try {
window.applicationCache.swapCache();
} catch (_e) {
servConn.logError('Cannot execute window.applicationCache.swapCache - ' + _e);
}
setTimeout(function () {
window.location.reload();
}, 1000);
}
}, false);
}, false);
}
// Parse Querystring
window.onpopstate = function () {
var match,
pl = /\+/g,
search = /([^&=]+)=?([^&]*)/g,
decode = function (s) {
return decodeURIComponent(s.replace(pl, ' '));
},
query = window.location.search.substring(1);
vis.urlParams = {};
while ((match = search.exec(query))) {
vis.urlParams[decode(match[1])] = decode(match[2]);
}
vis.editMode = (
window.location.href.indexOf('edit.html') !== -1 ||
window.location.href.indexOf('edit.full.html') !== -1 ||
window.location.href.indexOf('edit.src.html') !== -1 ||
vis.urlParams.edit === '');
};
window.onpopstate();
if (!vis.editMode) {
// Protection after view change
$(window).on('click touchstart mousedown', function (e) {
if (Date.now() - vis.lastChange < vis.debounceInterval) {
e.stopPropagation();
e.preventDefault();
return false;
}
});
/*$(window).on('touchend mouseup', function () {
vis.lastChange = null;
var $log = $('#w00039');
var $log1 = $('#w00445');
$log.append(' gclick touchend: ' + vis.lastChange);
$log1.append(' gclick touchend: ' + vis.lastChange);
});*/
}
function main($, onReady) {
// parse arguments
var args = document.location.href.split('?')[1];
vis.args = {};
if (args) {
vis.projectPrefix = 'main/';
var pos = args.indexOf('#');
if (pos !== -1) {
args = args.substring(0, pos);
}
args = args.split('&');
for (var a = 0; a < args.length; a++) {
var parts = args[a].split('=');
vis.args[parts[0]] = parts[1];
if (!parts[1]) vis.projectPrefix = parts[0] + '/';
}
if (vis.args.project) vis.projectPrefix = vis.args.project + '/';
}
// If cordova project => take cordova project name
if (typeof app !== 'undefined') vis.projectPrefix = app.settings.project ? app.settings.project + '/' : null;
// On some platforms, the can.js is not immediately ready
vis.states = new can.Map({
'nothing_selected.val': null
});
if (vis.editMode) {
vis.states.__attrs = vis.states.attr;
vis.states.attr = function (attr, val) {
var type = typeof attr;
if (type !== 'string' && type !== 'number') {
for (var o in attr) {
// allow only dev1, dev2, ... to be bound
if (o && attr.hasOwnProperty(o) && o.match(/^dev\d+(.val|.ack|.tc|.lc)+/)) {
return this.__attrs(attr, val);
}
}
} else if (arguments.length === 1 && attr) {
if (attr.match(/^dev\d+(.val|.ack|.tc|.lc)+/)) {
can.__reading(this, attr);
return this._get(attr);
} else {
return vis.states[attr];
}
} else {
console.log('This is ERROR!');
this._set(attr, val);
return this;
}
};
// binding
vis.states.___bind = vis.states.bind;
vis.states.bind = function (id, callback) {
// allow only dev1, dev2, ... to be bound
if (id && id.match(/^dev\d+(.val|.ack|.tc|.lc)+/)) {
return vis.states.___bind(id, callback);
}
//console.log('ERROR: binding in edit mode is not allowed on ' + id);
};
}
// für iOS Safari - wirklich notwendig?
$('body').on('touchmove', function (e) {
if (!$(e.target).closest('body').length) e.preventDefault();
});
vis.preloadImages(['img/disconnect.png']);
/*$('#server-disconnect').dialog({
modal: true,
closeOnEscape: false,
autoOpen: false,
dialogClass: 'noTitle',
width: 400,
height: 90
});*/
$('.vis-version').html(vis.version);
vis.showWaitScreen(true, null, _('Connecting to Server...') + ' ', 0);
function compareVersion(instVersion, availVersion) {
var instVersionArr = instVersion.replace(/beta/, '.').split('.');
var availVersionArr = availVersion.replace(/beta/, '.').split('.');
var updateAvailable = false;
for (var k = 0; k < 3; k++) {
instVersionArr[k] = parseInt(instVersionArr[k], 10);
if (isNaN(instVersionArr[k])) instVersionArr[k] = -1;
availVersionArr[k] = parseInt(availVersionArr[k], 10);
if (isNaN(availVersionArr[k])) availVersionArr[k] = -1;
}
if (availVersionArr[0] > instVersionArr[0]) {
updateAvailable = true;
} else if (availVersionArr[0] === instVersionArr[0]) {
if (availVersionArr[1] > instVersionArr[1]) {
updateAvailable = true;
} else if (availVersionArr[1] === instVersionArr[1]) {
if (availVersionArr[2] > instVersionArr[2]) {
updateAvailable = true;
}
}
}
return updateAvailable;
}
vis.conn = servConn;
// old !!!
// First of all load project/vis-user.css
//$('#project_css').attr('href', '/' + vis.conn.namespace + '/' + vis.projectPrefix + 'vis-user.css');
if (typeof app === 'undefined') {
$.ajax({
url: 'css/vis-common-user.css',
type: 'GET',
dataType: 'html',
cache: vis.useCache,
success: function (data) {
if (data && typeof app !== 'undefined' && app.replaceFilesInViewsWeb) {
data = app.replaceFilesInViewsWeb(data);
}
if (data || vis.editMode) $('head').append('');
$(document).trigger('vis-common-user');
},
error: function (jqXHR, textStatus, errorThrown) {
vis.conn.logError('Cannot load vis-common-user.css - ' + errorThrown);
$('head').append('');
$(document).trigger('vis-common-user');
}
});
$.ajax({
url: '/' + vis.conn.namespace + '/' + vis.projectPrefix + 'vis-user.css',
type: 'GET',
dataType: 'html',
cache: vis.useCache,
success: function (data) {
if (data && typeof app !== 'undefined' && app.replaceFilesInViewsWeb) {
data = app.replaceFilesInViewsWeb(data);
}
if (data || vis.editMode) {
$('head').append('');
}
$(document).trigger('vis-user');
},
error: function (jqXHR, textStatus, errorThrown) {
vis.conn.logError('Cannot load /' + vis.conn.namespace + '/' + vis.projectPrefix + 'vis-user.css - ' + errorThrown);
$('head').append('');
$(document).trigger('vis-user');
}
});
}
function createIds(IDs, index, callback) {
if (typeof index === 'function') {
callback = index;
index = 0;
}
index = index || 0;
var j;
var now = Date.now();
var obj = {};
for (j = index; j < vis.subscribing.IDs.length && j < index + 100; j++) {
var _id = vis.subscribing.IDs[j];
if (vis.states[_id + '.val'] === undefined) {
if (!_id || !_id.match(/^dev\d+$/)) {
console.log('Create inner vis object ' + _id);
}
if (vis.editMode) {
vis.states[_id + '.val'] = 'null';
vis.states[_id + '.ts'] = now;
vis.states[_id + '.ack'] = false;
vis.states[_id + '.lc'] = now;
} else {
obj[_id + '.val'] = 'null';
obj[_id + '.ts'] = now;
obj[_id + '.ack'] = false;
obj[_id + '.lc'] = now;
}
if (!vis.editMode && vis.bindings[_id]) {
for (var k = 0; k < vis.bindings[_id].length; k++) {
var _widget = vis.views[vis.bindings[_id][k].view].widgets[vis.bindings[_id][k].widget];
_widget[vis.bindings[_id][k].type][vis.bindings[_id][k].attr] = vis.formatBinding(vis.bindings[_id][k].format, vis.bindings[_id][k].view, vis.bindings[_id][k].widget, _widget);
}
}
}
}
try {
vis.states.attr(obj);
} catch (e) {
vis.conn.logError('Error: can\'t create states objects (' + e + ')');
}
if (j < vis.subscribing.IDs.length) {
setTimeout(function () {
createIds(IDs, j, callback);
}, 0)
} else {
callback();
}
}
function afterInit(error, onReady) {
if (error) {
console.log('Possibly not authenticated, wait for request from server');
// Possibly not authenticated, wait for request from server
} else {
// Get user groups info
vis.conn.getGroups(function (err, userGroups) {
vis.userGroups = userGroups || {};
// Get Server language
vis.conn.getConfig(function (err, config) {
systemLang = vis.args.lang || config.language || systemLang;
vis.language = systemLang;
vis.dateFormat = config.dateFormat;
vis.isFloatComma = config.isFloatComma;
// set moment language
if (typeof moment !== 'undefined') {
//moment.lang(vis.language);
moment.locale(vis.language);
}
translateAll();
if (vis.isFirstTime) {
// Init edit dialog
if (vis.editMode && vis.editInit) vis.editInit();
vis.isFirstTime = false;
vis.init(onReady);
}
});
});
// If metaIndex required, load it
if (vis.editMode) {
/* socket.io */
if (vis.isFirstTime) vis.showWaitScreen(true, _('Loading data objects...'), null, 20);
// Read all data objects from server
vis.conn.getObjects(function (err, data) {
vis.objects = data;
// Detect if objects are loaded
for (var ob in data) {
if (data.hasOwnProperty(ob)) {
vis.objectSelector = true;
break;
}
}
if (vis.editMode && vis.objectSelector) {
vis.inspectWidgets(vis.activeViewDiv, vis.activeView, true);
}
});
}
//console.log((new Date()) + " socket.io reconnect");
if (vis.isFirstTime) {
setTimeout(function () {
if (vis.isFirstTime) {
// Init edit dialog
if (vis.editMode && vis.editInit) vis.editInit();
vis.isFirstTime = false;
vis.init(onReady);
}
}, 1000);
}
}
}
vis.conn.init(null, {
mayReconnect: typeof app !== 'undefined' ? app.mayReconnect : null,
onAuthError: typeof app !== 'undefined' ? app.onAuthError : null,
onConnChange: function (isConnected) {
//console.log("onConnChange isConnected="+isConnected);
if (isConnected) {
//$('#server-disconnect').dialog('close');
if (vis.isFirstTime) {
vis.conn.getVersion(function (version) {
if (version) {
if (compareVersion(version, vis.requiredServerVersion)) {
vis.showMessage(_('Warning: requires Server version %s - found Server version %s - please update Server.', vis.requiredServerVersion, version));
}
}
//else {
// Possible not authenticated, wait for request from server
//}
});
vis.showWaitScreen(true, _('Loading data values...') + ' ', null, 20);
}
vis.conn.getLoggedUser(function (authReq, user) {
vis.user = user;
vis.loginRequired = authReq;
vis.states.attr({
'username.val' : vis.user,
'login.val' : vis.loginRequired,
'username' : vis.user,
'login' : vis.loginRequired
});
// first of all try to load views
vis.loadRemote(function () {
vis.subscribing.IDs = vis.subscribing.IDs || [];
vis.subscribing.byViews = vis.subscribing.byViews || {};
vis.conn.subscribe([vis.conn.namespace + '.control.instance', vis.conn.namespace + '.control.data', vis.conn.namespace + '.control.command']);
// first of all add custom scripts
if (!vis.editMode && vis.views && vis.views.___settings) {
if (vis.views.___settings.scripts) {
var script = document.createElement('script');
script.innerHTML = vis.views.___settings.scripts;
document.head.appendChild(script);
}
}
// Read all states from server
console.debug('Request ' + (vis.editMode ? 'all' : vis.subscribing.active.length) + ' states.');
vis.conn.getStates(vis.editMode ? null : vis.subscribing.active, function (error, data) {
if (error) vis.showError(error);
vis.updateStates(data);
if (vis.subscribing.active.length) {
vis.conn.subscribe(vis.subscribing.active);
}
// Create non-existing IDs
if (vis.subscribing.IDs) {
createIds(vis.subscribing.IDs, function () {
afterInit(error, onReady);
});
} else {
afterInit(error, onReady);
}
});
});
});
} else {
//console.log((new Date()) + " socket.io disconnect");
//$('#server-disconnect').dialog('open');
}
},
onRefresh: function () {
window.location.reload();
},
onUpdate: function (id, state) {
_setTimeout(function (_id, _state) {
vis.updateState(_id, _state);
}, 0, id, state);
},
onAuth: function (message, salt) {
if (vis.authRunning) {
return;
}
vis.authRunning = true;
var users;
if (visConfig.auth.users && visConfig.auth.users.length) {
users = '';
} else {
users = '';
}
var text = '
' +
'
' + message + '
' +
'
' +
'' +
'' +
'' +
'
' +
'
';
// Add the mask to body
$('body')
.append(text)
.append('');
var loginBox = $('#login-box');
//Fade in the Popup
$(loginBox).fadeIn(300);
//Set the center alignment padding + border see css style
var popMargTop = ($(loginBox).height() + 24) / 2;
var popMargLeft = ($(loginBox).width() + 24) / 2;
$(loginBox).css({
'margin-top': -popMargTop,
'margin-left': -popMargLeft
});
$('#login-mask').fadeIn(300);
// When clicking on the button close or the mask layer the popup closed
$('#login-password').keypress(function (e) {
if (e.which === 13) {
$('.login-button').trigger('click');
}
});
$('.login-button').bind('click', function () {
var user = $('#login-username').val();
var pass = $('#login-password').val();
$('#login_mask , .login-popup').fadeOut(300, function () {
$('#login-mask').remove();
$('#login-box').remove();
});
setTimeout(function () {
vis.authRunning = false;
console.log('user ' + user + ', ' + pass + ' ' + salt);
vis.conn.authenticate(user, pass, salt);
}, 500);
return true;
});
},
onCommand: function (instance, command, data) {
var parts;
if (!instance || (instance !== vis.instance && instance !== 'FFFFFFFF' && instance.indexOf('*') === -1)) return false;
if (command) {
if (vis.editMode && command !== 'tts' && command !== 'playSound') return;
// external Commands
switch (command) {
case 'alert':
parts = data.split(';');
vis.showMessage(parts[0], parts[1], parts[2]);
break;
case 'changedView':
// Do nothing
return false;
case 'changeView':
parts = data.split('/');
if (parts[1]) {
// detect actual project
var actual = vis.projectPrefix ? vis.projectPrefix.substring(0, vis.projectPrefix.length - 1) : 'main';
if (parts[0] !== actual) {
document.location.href = 'index.html?' + actual + '#' + parts[1];
return;
}
}
var view = parts[1] || parts[0];
vis.changeView(view, view);
break;
case 'refresh':
case 'reload':
setTimeout(function () {
window.location.reload();
}, 1);
break;
case 'dialog':
case 'dialogOpen':
//noinspection JSJQueryEfficiency
$('#' + data + '_dialog').dialog('open');
break;
case 'dialogClose':
//noinspection JSJQueryEfficiency
$('#' + data + '_dialog').dialog('close');
break;
case 'popup':
window.open(data);
break;
case 'playSound':
setTimeout(function () {
var href;
if (data && data.match(/^http(s)?:\/\//)) {
href = data;
} else {
href = location.protocol + '//' + location.hostname + ':' + location.port + data;
}
// force read from server
href += '?' + Date.now();
if (typeof Audio !== 'undefined') {
var snd = new Audio(href); // buffers automatically when created
snd.play();
} else {
//noinspection JSJQueryEfficiency
var $sound = $('#external_sound');
if (!$sound.length) {
$('body').append('');
$sound = $('#external_sound');
}
$sound.attr('src', href);
document.getElementById('external_sound').play();
}
}, 1);
break;
case 'tts':
if (typeof app !== 'undefined') {
app.tts(data);
}
break;
default:
vis.conn.logError('unknown external command ' + command);
}
}
return true;
},
onObjectChange: function(id, obj) {
if (!vis.objects || !vis.editMode) return;
if (obj) {
vis.objects[id] = obj;
} else {
if (vis.objects[id]) delete vis.objects[id];
}
if ($.fn.selectId) $.fn.selectId('objectAll', id, obj);
},
onError: function (err) {
if (err.arg === 'vis.0.control.instance' || err.arg === 'vis.0.control.data' || err.arg === 'vis.0.control.command') {
console.warn('Cannot set ' + err.arg + ', because of insufficient permissions');
} else {
vis.showMessage(_('Cannot execute %s for %s, because of insufficient permissions', err.command, err.arg), _('Insufficient permissions'), 'alert', 600);
}
}
}, vis.editMode, vis.editMode);
if (!vis.editMode) {
// Listen for resize changes
window.addEventListener('orientationchange', function () {
vis.orientationChange();
}, false);
window.addEventListener('resize', function () {
vis.orientationChange();
}, false);
}
//vis.preloadImages(["../../lib/css/themes/jquery-ui/redmond/images/modalClose.png"]);
vis.initWakeUp();
}
// Start of initialisation: main ()
if (typeof app === 'undefined') {
$(document).ready(function () {
main(jQuery);
});
}
// IE8 indexOf compatibility
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (obj, start) {
for (var i = (start || 0), j = this.length; i < j; i++) {
if (this[i] === obj) {
return i;
}
}
return -1;
};
}
function _setTimeout(func, timeout, arg1, arg2, arg3, arg4, arg5, arg6) {
return setTimeout(function () {
func(arg1, arg2, arg3, arg4, arg5, arg6);
}, timeout);
}
function _setInterval(func, timeout, arg1, arg2, arg3, arg4, arg5, arg6) {
return setInterval(function () {
func(arg1, arg2, arg3, arg4, arg5, arg6);
}, timeout);
}
/*if (window.location.search === '?edit') {
window.alert(_('please use /vis/edit.html instead of /vis/?edit'));
location.href = './edit.html' + window.location.hash;
}*/
// TODO find out if iPad 1 has map or not.
// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.io/#x15.4.4.19
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this === null || this === undefined || this === 0) {
throw new TypeError('this is null or not defined');
}
// 1. Let O be the result of calling ToObject passing the |this|
// value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get internal
// method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 6. Let A be a new array created as if by the expression new Array(len)
// where Array is the standard built-in constructor with that name and
// len is the value of len.
A = new Array(len);
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while (k < len) {
var kValue, mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
// ii. Let mappedValue be the result of calling the Call internal
// method of callback with T as the this value and argument
// list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor
// { Value: mappedValue,
// Writable: true,
// Enumerable: true,
// Configurable: true },
// and false.
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, k, {
// value: mappedValue,
// writable: true,
// enumerable: true,
// configurable: true
// });
// For best browser support, use the following:
A[k] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
}