Files
yunkong2.vis/www/js/vis.js
2018-09-16 10:58:21 +08:00

3448 lines
143 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* yunkong2.vis
* https://github.com/yunkong2/yunkong2.vis
*
* Copyright (c) 2013-2017 bluefox https://github.com/GermanBluefox, hobbyquaker https://github.com/hobbyquaker
* Creative Common Attribution-NonCommercial (CC BY-NC)
*
* http://creativecommons.org/licenses/by-nc/4.0/
*
*/
/* jshint browser:true */
/* global document */
/* global console */
/* global session */
/* global window */
/* global location */
/* global setTimeout */
/* global clearTimeout */
/* global io */
/* global visConfig */
/* global systemLang:true */
/* global _ */
/* global can */
/* global storage */
/* global servConn */
/* global systemDictionary */
/* global $ */
/* global app */
/* global Audio */
/* global cordova */
/* global translateAll */
/* global jQuery */
/* global document */
/* global moment */
/* jshint -W097 */// jshint strict:false
'use strict';
if (typeof systemDictionary !== 'undefined') {
$.extend(systemDictionary, {
'No connection to Server': {'en': 'No connection to Server', 'cn': '未连接服务器'},
'Loading Views...': {'en': 'Loading Views...', 'cn': '加载视图...'},
'Connecting to Server...': {'en': 'Connecting to Server...', 'cn': '连接到服务器...'},
'Loading data objects...': {'en': 'Loading data...', 'cn': '加载结构...'},
'Loading data values...': {'en': 'Loading values...', 'cn': '加载数据...'},
'error - View doesn\'t exist': {'en': 'View doesn\'t exist!', 'cn': '视图不存在!'},
'no views found!': {'en': 'No views found!', 'cn': '未找到视图!'},
'No Views found on Server': {
'en': 'No Views found on Server',
'cn': '视图不存在.'
},
'All changes are saved locally. To reset changes clear the cache.': {
'en': 'All changes are saved locally. To reset changes clear the browser cache.'
},
'please use /vis/edit.html instead of /vis/?edit': {
'en': 'Please use /vis/edit.html instead of /vis/?edit'
},
'no views found on server.\nCreate new %s ?': {
'en': 'no views found on server.\nCreate new %s?'
},
'Update found, loading new Files...': {
'en': 'Update found.<br/>Loading new Files...'
},
'Loading Widget-Sets...': {
'en': 'Loading Widget-Sets...',
'cn': 'Loading Widget-Sets...'
},
'error: view not found.': {
'en': 'Error: view not found',
'cn': 'Error: 未找到视图'
},
'error: view container recursion.': {
'en': 'Error: view container recursion',
'cn': 'Error: view container recursion'
},
"Cannot execute %s for %s, because of insufficient permissions": {
"en": "Cannot execute %s for %s, because of insufficient permissions."
},
"Insufficient permissions": {
"en": "Insufficient permissions",
"cn": "Insufficient permissions"
},
"View disabled for user %s": {
"en": "View disabled for user <b>%s</b>",
"cn": "View disabled for user <b>%s</b>"
}
});
}
if (typeof systemLang !== 'undefined' && typeof cordova === 'undefined') {
systemLang = visConfig.language || systemLang;
}
var vis = {
version: '1.1.7',
requiredServerVersion: '0.0.0',
storageKeyViews: 'visViews',
storageKeySettings: 'visSettings',
storageKeyInstance: 'visInstance',
instance: null,
urlParams: {},
settings: {},
views: null,
widgets: {},
activeView: '',
activeViewDiv: '',
widgetSets: visConfig.widgetSets,
initialized: false,
toLoadSetsCount: 0, // Count of widget sets that should be loaded
isFirstTime: true,
useCache: false,
authRunning: false,
cssChecked: false,
isTouch: 'ontouchstart' in document.documentElement,
binds: {},
onChangeCallbacks: [],
viewsActiveFilter: {},
projectPrefix: window.location.search ? window.location.search.slice(1) + '/' : 'main/',
navChangeCallbacks: [],
editMode: false,
language: (typeof systemLang !== 'undefined') ? systemLang : visConfig.language,
statesDebounce: {},
visibility: {},
signals: {},
lastChanges: {},
bindings: {},
bindingsCache: {},
subscribing: {
IDs: [],
byViews: {},
active: [],
activeViews: []
},
commonStyle: null,
debounceInterval: 700,
user: '', // logged in user
loginRequired: false,
_setValue: function (id, state, isJustCreated) {
var that = this;
var oldValue = this.states.attr(id + '.val');
this.conn.setState(id, state[id + '.val'], function (err) {
if (err) {
//state[id + '.val'] = oldValue;
that.showMessage(_('Cannot execute %s for %s, because of insufficient permissions', 'setState', id), _('Insufficient permissions'), 'alert', 600);
}
if (that.states.attr(id) || that.states.attr(id + '.val') !== undefined) {
that.states.attr(state);
// If error set value back, but we need generate the edge
if (err) {
if (isJustCreated) {
that.states.removeAttr(id + '.val');
that.states.removeAttr(id + '.q');
that.states.removeAttr(id + '.from');
that.states.removeAttr(id + '.ts');
that.states.removeAttr(id + '.lc');
that.states.removeAttr(id + '.ack');
} else {
state[id + '.val'] = oldValue;
that.states.attr(state);
}
}
// Inform other widgets, that does not support canJS
for (var i = 0, len = that.onChangeCallbacks.length; i < len; i++) {
that.onChangeCallbacks[i].callback(that.onChangeCallbacks[i].arg, id, state);
}
}
});
},
setValue: function (id, val) {
if (!id) {
console.log('ID is null for val=' + val);
return;
}
var d = new Date();
var t = d.getFullYear() + '-' + ('0' + (d.getMonth() + 1)).slice(-2) + '-' + ('0' + d.getDate()).slice(-2) + " " + ('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2) + ':' + ('0' + d.getSeconds()).slice(-2);
var o = {};
var created = false;
if (this.states.attr(id + '.val') != val) {
o[id + '.lc'] = t;
} else {
o[id + '.lc'] = this.states.attr(id + '.lc');
}
o[id + '.val'] = val;
o[id + '.ts'] = t;
o[id + '.ack'] = false;
// Create this value
if (this.states.attr(id + '.val') === undefined) {
created = true;
this.states.attr(o);
}
var that = this;
// if no de-bounce running
if (!this.statesDebounce[id]) {
// send control command
this._setValue(id, o, created);
// Start timeout
this.statesDebounce[id] = {
timeout: _setTimeout(function () {
if (that.statesDebounce[id]) {
if (that.statesDebounce[id].state) that._setValue(id, that.statesDebounce[id].state);
delete that.statesDebounce[id];
}
}, 1000, id),
state: null
};
} else {
// If some de-bounce running, change last value
this.statesDebounce[id].state = o;
}
},
loadWidgetSet: function (name, callback) {
var url = './widgets/' + name + '.html?visVersion=' + this.version;
var that = this;
$.ajax({
url: url,
type: 'GET',
dataType: 'html',
cache: this.useCache,
success: function (data) {
setTimeout(function () {
try {
$('head').append(data);
} catch (e) {
console.error('Cannot load widget set "' + name + '": ' + e);
}
that.toLoadSetsCount -= 1;
if (that.toLoadSetsCount <= 0) {
that.showWaitScreen(true, null, null, 100);
setTimeout(function () {
callback.call(that);
}, 100);
} else {
that.showWaitScreen(true, null, null, parseInt((100 - that.waitScreenVal) / that.toLoadSetsCount, 10));
}
}, 0);
},
error: function (jqXHR, textStatus, errorThrown) {
that.conn.logError('Cannot load widget set ' + name + ' ' + errorThrown);
}
});
},
// Return as array used widgetSets or null if no information about it
getUsedWidgetSets: function () {
var widgetSets = [];
if (!this.views) {
console.log('Check why views are not yet loaded!');
return null;
}
// Convert visConfig.widgetSets to object for easier dependency search
var widgetSetsObj = {};
for (var i = 0; i < visConfig.widgetSets.length; i++) {
if (typeof visConfig.widgetSets[i] === 'object') {
if (!visConfig.widgetSets[i].depends) {
visConfig.widgetSets[i].depends = [];
}
widgetSetsObj[visConfig.widgetSets[i].name] = visConfig.widgetSets[i];
} else {
widgetSetsObj[visConfig.widgetSets[i]] = {depends: []};
}
}
for (var view in this.views) {
if (!this.views.hasOwnProperty(view) || view === '___settings') continue;
for (var id in this.views[view].widgets) {
if (!this.views[view].widgets.hasOwnProperty(id)) continue;
if (!this.views[view].widgets[id].widgetSet) {
// Views are not yet converted and have no widgetSet information)
return null;
} else if (widgetSets.indexOf(this.views[view].widgets[id].widgetSet) === -1) {
var wset = this.views[view].widgets[id].widgetSet;
widgetSets.push(wset);
// Add dependencies
if (widgetSetsObj[wset]) {
for (var u = 0, ulen = widgetSetsObj[wset].depends.length; u < ulen; u++) {
if (widgetSets.indexOf(widgetSetsObj[wset].depends[u]) === -1) {
widgetSets.push(widgetSetsObj[wset].depends[u]);
}
}
}
}
}
}
return widgetSets;
},
// Return as array used widgetSets or null if no information about it
getUsedObjectIDs: function () {
var result = getUsedObjectIDs(this.views, !this.editMode);
if (!result) {
return result;
}
this.visibility = result.visibility;
this.bindings = result.bindings;
this.signals = result.signals;
this.lastChanges = result.lastChanges;
return {IDs: result.IDs, byViews: result.byViews};
},
getWidgetGroup: function (view, widget) {
return getWidgetGroup(this.views, view, widget);
},
loadWidgetSets: function (callback) {
this.showWaitScreen(true, '<br>' + _('Loading Widget-Sets...') + ' <span id="widgetset_counter"></span>', null, 20);
var arrSets = [];
// If widgets are pre-loaded
if (this.binds && this.binds.stateful !== undefined) {
this.toLoadSetsCount = 0;
} else {
// Get list of used widget sets. if Edit mode list is null.
var widgetSets = this.editMode ? null : this.getUsedWidgetSets();
// First calculate how many sets to load
for (var i = 0; i < this.widgetSets.length; i++) {
var name = this.widgetSets[i].name || this.widgetSets[i];
// Skip unused widget sets in non-edit mode
if (!this.widgetSets[i].always) {
if (this.widgetSets[i].widgetSets && widgetSets.indexOf(name) === -1) {
continue;
}
} else {
if (widgetSets && widgetSets.indexOf(name) === -1) widgetSets.push(name);
}
arrSets[arrSets.length] = name;
if (this.editMode && this.widgetSets[i].edit) {
arrSets[arrSets.length] = this.widgetSets[i].edit;
}
}
this.toLoadSetsCount = arrSets.length;
$("#widgetset_counter").html("<span style='font-size:10px'>(" + (this.toLoadSetsCount) + ")</span>");
}
var that = this;
if (this.toLoadSetsCount) {
for (var j = 0, len = this.toLoadSetsCount; j < len; j++) {
_setTimeout(function (_i) {
that.loadWidgetSet(arrSets[_i], callback);
}, 100, j);
}
} else {
if (callback) callback.call(this);
}
},
bindInstance: function () {
if (typeof app !== 'undefined' && app.settings) {
this.instance = app.settings.instance;
}
if (typeof storage !== 'undefined') {
this.instance = this.instance || storage.get(this.storageKeyInstance);
}
if (this.editMode) {
this.bindInstanceEdit();
}
this.states.attr({'instance.val': this.instance, 'instance': this.instance});
},
init: function (onReady) {
if (this.initialized) return;
if (typeof storage !== 'undefined') {
var settings = storage.get(this.storageKeySettings);
if (settings) {
this.settings = $.extend(this.settings, settings);
}
}
// Late initialization (used only for debug)
/*if (this.binds.hqWidgetsExt) {
this.binds.hqWidgetsExt.hqInit();
}*/
var that = this;
//this.loadRemote(this.loadWidgetSets, this.initNext);
this.loadWidgetSets(function () {
that.initNext(onReady);
});
},
initNext: function (onReady) {
this.showWaitScreen(false);
var that = this;
// First start.
if (!this.views) {
this.initViewObject();
} else {
this.showWaitScreen(false);
}
var hash = decodeURIComponent(window.location.hash.substring(1));
// create demo states
if (this.views && this.views.DemoView) this.createDemoStates();
if (!this.views || (!this.views[hash] && typeof app !== 'undefined')) hash = null;
// View selected?
if (!hash) {
// Take first view in the list
this.activeView = this.findNearestResolution(true);
this.activeViewDiv = this.activeView;
// Create default view in demo mode
if (typeof io === 'undefined') {
if (!this.activeView) {
if (!this.editMode) {
window.alert(_('error - View doesn\'t exist'));
if (typeof app === 'undefined') {
// try to find first view
window.location.href = 'edit.html?' + this.projectPrefix.substring(0, this.projectPrefix.length - 1);
}
} else {
this.views.DemoView = this.createDemoView ? this.createDemoView() : {
settings: {style: {}},
widgets: {}
};
this.activeView = 'DemoView';
this.activeViewDiv = this.activeView;
}
}
} else if (!this.activeView) {
if (!this.editMode) {
if (typeof app === 'undefined') {
window.alert(_('error - View doesn\'t exist'));
window.location.href = 'edit.html?' + this.projectPrefix.substring(0, this.projectPrefix.length - 1);
}
} else {
// All views were deleted, but file exists. Create demo View
//window.alert("unexpected error - this should not happen :(");
//$.error("this should not happen :(");
// create demoView
this.views.DemoView = this.createDemoView ? this.createDemoView() : {
settings: {style: {}},
widgets: {}
};
this.activeView = 'DemoView';
this.activeViewDiv = this.activeView;
}
}
} else {
if (this.views[hash]) {
this.activeView = hash;
this.activeViewDiv = this.activeView;
} else {
window.alert(_('error - View doesn\'t exist'));
if (typeof app === 'undefined') window.location.href = 'edit.html?' + this.projectPrefix.substring(0, this.projectPrefix.length - 1);
$.error("vis Error can't find view");
}
}
if (this.views && this.views.___settings) {
if (this.views.___settings.reloadOnSleep !== undefined) this.conn.setReloadTimeout(this.views.___settings.reloadOnSleep);
if (this.views.___settings.darkReloadScreen) {
$('#server-disconnect').removeClass('disconnect-light').addClass('disconnect-dark');
}
if (this.views.___settings.reconnectInterval !== undefined) this.conn.setReconnectInterval(this.views.___settings.reconnectInterval);
if (this.views.___settings.destroyViewsAfter !== undefined) this.views.___settings.destroyViewsAfter = parseInt(this.views.___settings.destroyViewsAfter, 10);
}
// Navigation
$(window).bind('hashchange', function (/* e */) {
var view = window.location.hash.slice(1);
that.changeView(view, view);
});
this.bindInstance();
// EDIT mode
if (this.editMode) this.editInitNext();
this.initialized = true;
// If this function called earlier, it makes problems under FireFox.
// render all views, that should be always rendered
var containers = [];
var cnt = 0;
if (this.views && !this.editMode) {
for (var view in this.views) {
if (!this.views.hasOwnProperty(view) || view === '___settings') continue;
if (this.views[view].settings.alwaysRender) {
containers.push({view: view});
}
}
if (containers.length) {
cnt++;
this.renderViews(that.activeViewDiv, containers, function () {
cnt--;
if (that.activeView) {
that.changeView(that.activeViewDiv, that.activeView, function () {
if (!cnt && onReady) {
onReady();
}
});
}
});
}
}
if (!containers.length && this.activeView) {
this.changeView(this.activeViewDiv, this.activeView, onReady);
}
},
initViewObject: function () {
if (!this.editMode) {
if (typeof app !== 'undefined') {
this.showMessage(_('no views found!'));
} else {
window.location.href = 'edit.html?' + this.projectPrefix.substring(0, this.projectPrefix.length - 1);
}
} else {
if (window.confirm(_('no views found on server.\nCreate new %s ?', this.projectPrefix + 'vis-views.json'))) {
this.views = {};
this.views.DemoView = this.createDemoView ? this.createDemoView() : {
settings: {style: {}},
widgets: {}
};
if (this.saveRemote) {
this.saveRemote(true, function () {
//window.location.reload();
});
}
} else {
window.location.reload();
}
}
},
setViewSize: function (viewDiv, view) {
var $view = $('#visview_' + viewDiv);
var width;
var height;
if (this.views[view]) {
// Because of background, set the width and height of the view
width = parseInt(this.views[view].settings.sizex, 10);
height = parseInt(this.views[view].settings.sizey, 10);
}
var $vis_container = $('#vis_container');
if (!width || width < $vis_container.width()) width = '100%';
if (!height || height < $vis_container.height()) height = '100%';
$view.css({width: width, height: height});
},
updateContainers: function (viewDiv, view) {
var that = this;
// Set ths views for containers
$('#visview_' + viewDiv).find('.vis-view-container').each(function () {
var cview = $(this).attr('data-vis-contains');
if (!that.views[cview]) {
$(this).html('<span style="color: red" class="container-error">' + _('error: view not found.') + '</span>');
} else if (cview === view || cview === viewDiv) {
$(this).html('<span style="color: red" class="container-error">' + _('error: view container recursion.') + '</span>');
} else {
if ($(this).find('.container-error').length) {
$(this).html('');
}
var targetView = this;
if (!$(this).find('.vis-widget:first').length) {
that.renderView(cview, cview, function (_viewDiv) {
$('#visview_' + _viewDiv)
.appendTo(targetView)
.show();
});
} else {
$('#visview_' + cview)
.appendTo(targetView)
.show();
}
}
});
},
renderViews: function (viewDiv, views, index, callback) {
if (typeof index === 'function') {
callback = index;
index = 0;
}
index = index || 0;
if (!views || index >= views.length) {
if (callback) callback(viewDiv, views);
return;
}
var item = views[index];
var that = this;
this.renderView(this.views[item.view] ? item.view : viewDiv, item.view, true, function () {
that.renderViews(viewDiv, views, index + 1, callback);
});
},
renderView: function (viewDiv, view, hidden, callback) {
var that = this;
if (typeof hidden === 'function') {
callback = hidden;
hidden = undefined;
}
if (typeof view === 'boolean') {
callback = hidden;
hidden = undefined;
view = viewDiv;
}
if (!this.editMode && !$('#commonTheme').length) {
$('head').prepend('<link rel="stylesheet" type="text/css" href="' + ((typeof app === 'undefined') ? '../../' : '') + 'lib/css/themes/jquery-ui/' + (this.calcCommonStyle() || 'redmond') + '/jquery-ui.min.css" id="commonTheme"/>');
}
if (!this.views[view] || !this.views[view].settings) {
window.alert('Cannot render view ' + view + '. Invalid settings');
if (callback) {
setTimeout(function () {
callback(viewDiv, view);
}, 0);
}
return false;
}
// try to render background
// collect all IDs, used in this view and in containers
this.subscribeStates(view, function () {
var isViewsConverted = false; // Widgets in the views hav no information which WidgetSet they use, this info must be added and this flag says if that happens to store the views
that.views[view].settings.theme = that.views[view].settings.theme || 'redmond';
if (that.views[view].settings.filterkey) {
that.viewsActiveFilter[view] = that.views[view].settings.filterkey.split(',');
} else {
that.viewsActiveFilter[view] = [];
}
//noinspection JSJQueryEfficiency
var $view = $('#visview_' + viewDiv);
// apply group policies
if (!that.editMode && that.views[view].settings.group && that.views[view].settings.group.length) {
if (that.views[view].settings.group_action === 'hide') {
if (!that.isUserMemberOf(that.conn.getUser(), that.views[view].settings.group)) {
if (!$view.length) {
$('#vis_container').append('<div id="visview_' + viewDiv + '" class="vis-view vis-user-disabled"></div>');
$view = $('#visview_' + viewDiv);
}
$view.html('<div class="vis-view-disabled-text">' + _('View disabled for user %s', that.conn.getUser()) + '</div>');
if (callback) {
setTimeout(function () {
callback(viewDiv, view);
}, 0);
}
return;
}
}
}
if (!$view.length) {
$('#vis_container').append('<div style="display: none;" id="visview_' + viewDiv + '" ' +
'data-view="' + view + '" ' +
'class="vis-view ' + (viewDiv !== view ? 'vis-edit-group' : '') + '" ' +
(that.views[view].settings.alwaysRender ? 'data-persistent="true"' : '') + '>' +
'<div class="vis-view-disabled" style="display: none"></div>' +
'</div>');
that.addViewStyle(viewDiv, view, that.views[view].settings.theme);
$view = $('#visview_' + viewDiv);
$view.css(that.views[view].settings.style);
if (that.views[view].settings.style.background_class) {
$view.addClass(that.views[view].settings.style.background_class);
}
var id;
if (viewDiv !== view && that.editMode) {
//noinspection JSJQueryEfficiency
var $widget = $('#' + viewDiv);
if (!$widget.length) {
that.renderWidget(view, view, viewDiv);
$widget = $('#' + viewDiv);
}
$view.append('<div class="group-edit-header" data-view="' + viewDiv + '">' + _('Edit group:') + ' <b>' + viewDiv + '</b><button class="group-edit-close"></button></div>');
$view.find('.group-edit-close').button({
icons: {
primary: 'ui-icon-close'
},
text: false
}).data('view', view).css({width: 20, height: 20}).click(function () {
var view = $(this).data('view');
that.changeView(view, view);
});
$widget.appendTo($view);
$widget.css({top: 0, left: 0});
/*$widget.unbind('click dblclick');
$widget.find('.vis-widget').each(function () {
var id = $(this).attr('id');
that.bindWidgetClick(view, id, true);
});*/
} else {
that.setViewSize(viewDiv, view);
// Render all widgets
for (id in that.views[view].widgets) {
if (!that.views[view].widgets.hasOwnProperty(id)) continue;
// Try to complete the widgetSet information to optimize the loading of widgetSets
if (id[0] !== 'g' && !that.views[view].widgets[id].widgetSet) {
var obj = $('#' + that.views[view].widgets[id].tpl);
if (obj) {
that.views[view].widgets[id].widgetSet = obj.attr('data-vis-set');
isViewsConverted = true;
}
}
if (!that.views[view].widgets[id].renderVisible && !that.views[view].widgets[id].grouped) that.renderWidget(viewDiv, view, id);
}
}
if (that.editMode) {
if (that.binds.jqueryui) that.binds.jqueryui._disable();
that.droppable(viewDiv, view);
}
}
// move views in container
var containers = [];
$view.find('.vis-view-container').each(function () {
var cview = $(this).attr('data-vis-contains');
if (!that.views[cview]) {
$(this).append('error: view not found.');
return false;
} else if (cview === view) {
$(this).append('error: view container recursion.');
return false;
}
containers.push({thisView: this, view: cview});
});
// add view class
if (that.views[view].settings['class']) {
$view.addClass(that.views[view].settings['class'])
}
var wait = false;
if (containers.length) {
wait = true;
that.renderViews(viewDiv, containers, function (_viewDiv, _containers) {
for (var c = 0; c < _containers.length; c++) {
$('#visview_' + _containers[c].view)
.appendTo(_containers[c].thisView)
.show();
}
if (!hidden) $view.show();
$('#visview_' + _viewDiv).trigger('rendered');
if (callback) callback(_viewDiv, view);
});
}
// Store modified view
if (isViewsConverted && that.saveRemote) that.saveRemote();
if (that.editMode && $('#wid_all_lock_function').prop('checked')) {
$('.vis-widget').addClass('vis-widget-lock');
if (viewDiv !== view) {
$('#' + viewDiv).removeClass('vis-widget-lock');
}
}
if (!wait) {
if (!hidden) $view.show();
setTimeout(function () {
$('#visview_' + viewDiv).trigger('rendered');
if (callback) callback(viewDiv, view);
}, 0);
}
// apply group policies
if (!that.editMode && that.views[view].settings.group && that.views[view].settings.group.length) {
if (that.views[view].settings.group_action !== 'hide') {
if (!that.isUserMemberOf(that.conn.getUser(), that.views[view].settings.group)) {
$view.addClass('vis-user-disabled');
}
}
}
});
},
addViewStyle: function (viewDiv, view, theme) {
var _view = 'visview_' + viewDiv;
if (this.calcCommonStyle() === theme) return;
$.ajax({
url: ((typeof app === 'undefined') ? '../../' : '') + 'lib/css/themes/jquery-ui/' + theme + '/jquery-ui.min.css',
cache: false,
success: function (data) {
$('#' + viewDiv + '_style').remove();
data = data.replace('.ui-helper-hidden', '#' + _view + ' .ui-helper-hidden');
data = data.replace(/(}.)/g, '}#' + _view + ' .');
data = data.replace(/,\./g, ',#' + _view + ' .');
data = data.replace(/images/g, ((typeof app === 'undefined') ? '../../' : '') + 'lib/css/themes/jquery-ui/' + theme + '/images');
var $view = $('#' + _view);
$view.append('<style id="' + viewDiv + '_style">' + data + '</style>');
$('#' + viewDiv + '_style_common_user').remove();
$view.append('<style id="' + viewDiv + '_style_common_user" class="vis-common-user">' + $('#vis-common-user').html() + '</style>');
$('#' + viewDiv + '_style_user').remove();
$view.append('<style id="' + viewDiv + '_style_user" class="vis-user">' + $('#vis-user').html() + '</style>');
}
});
},
preloadImages: function (srcs) {
if (!this.preloadImages.cache) {
this.preloadImages.cache = [];
}
var img;
for (var i = 0; i < srcs.length; i++) {
img = new Image();
img.src = srcs[i];
this.preloadImages.cache.push(img);
}
},
destroyWidget: function (viewDiv, view, widget) {
var $widget = $('#' + widget);
if ($widget.length) {
var widgets = this.views[view].widgets[widget].data.members;
if (widgets) {
for (var w = 0; w < widgets.length; w++) {
if (widgets[w] !== widget) {
this.destroyWidget(viewDiv, view, widgets[w]);
} else {
console.warn('Cyclic structure in ' + widget + '!');
}
}
}
try {
// get array of bound OIDs
var bound = $widget.data('bound');
if (bound) {
var bindHandler = $widget.data('bindHandler');
for (var b = 0; b < bound.length; b++) {
if (typeof bindHandler === 'function') {
this.states.unbind(bound[b], bindHandler);
} else {
this.states.unbind(bound[b], bindHandler[b]);
}
}
$widget.data('bindHandler', null);
$widget.data('bound', null);
}
// If destroy function exists => destroy it
var destroy = $widget.data('destroy');
if (typeof destroy === 'function') {
destroy(widget, $widget);
}
} catch (e) {
console.error('Cannot destroy "' + widget + '": ' + e);
}
}
},
reRenderWidget: function (viewDiv, view, widget) {
var $widget = $('#' + widget);
var updateContainers = $widget.find('.vis-view-container').length;
view = view || this.activeView;
viewDiv = viewDiv || this.activeViewDiv;
this.destroyWidget(viewDiv, view, widget);
this.renderWidget(viewDiv, view, widget, !this.views[viewDiv] && viewDiv !== widget ? viewDiv : null);
if (updateContainers) this.updateContainers(viewDiv, view);
},
changeFilter: function (view, filter, showEffect, showDuration, hideEffect, hideDuration) {
view = view || this.activeView;
// convert from old style
if (!this.views[view]) {
hideDuration = hideEffect;
hideEffect = showDuration;
showDuration = showEffect;
showEffect = filter;
filter = view;
view = this.activeView;
}
var widgets = this.views[view].widgets;
var that = this;
var widget;
var mWidget;
if (!(filter || '').trim()) {
// show all
for (widget in widgets) {
if (!widgets.hasOwnProperty(widget)) continue;
if (widgets[widget] && widgets[widget].data && widgets[widget].data.filterkey) {
$('#' + widget).show(showEffect, null, parseInt(showDuration));
}
}
// Show complex widgets
setTimeout(function () {
var mWidget;
for (var widget in widgets) {
if (!widgets.hasOwnProperty(widget)) continue;
mWidget = document.getElementById(widget);
if (widgets[widget] &&
widgets[widget].data &&
widgets[widget].data.filterkey &&
mWidget &&
mWidget._customHandlers &&
mWidget._customHandlers.onShow) {
mWidget._customHandlers.onShow(mWidget, widget);
}
}
}, parseInt(showDuration) + 10);
} else if (filter === '$') {
// hide all
for (widget in widgets) {
if (!widgets.hasOwnProperty(widget)) continue;
if (!widgets[widget] || !widgets[widget].data || !widgets[widget].data.filterkey) continue;
mWidget = document.getElementById(widget);
if (mWidget &&
mWidget._customHandlers &&
mWidget._customHandlers.onHide) {
mWidget._customHandlers.onHide(mWidget, widget);
}
$('#' + widget).hide(hideEffect, null, parseInt(hideDuration));
}
} else {
this.viewsActiveFilter[this.activeView] = filter.split(',');
var vFilters = this.viewsActiveFilter[this.activeView];
for (widget in widgets) {
if (!widgets.hasOwnProperty(widget) || !widgets[widget] || !widgets[widget].data) continue;
var wFilters = widgets[widget].data.filterkey;
if (wFilters) {
if (typeof wFilters !== 'object') {
widgets[widget].data.filterkey = wFilters.split(/[;,]+/);
wFilters = widgets[widget].data.filterkey;
}
var found = false;
// optimization
if (wFilters.length === 1) {
found = vFilters.indexOf(wFilters[0]) !== -1;
} else if (vFilters.length === 1) {
found = wFilters.indexOf(vFilters[0]) !== -1;
} else {
for (var f = 0; f < wFilters.length; f++) {
if (vFilters.indexOf(wFilters[f]) !== -1) {
found = true;
break;
}
}
}
if (!found) {
mWidget = document.getElementById(widget);
if (mWidget &&
mWidget._customHandlers &&
mWidget._customHandlers.onHide) {
mWidget._customHandlers.onHide(mWidget, widget);
}
$('#' + widget).hide(hideEffect, null, parseInt(hideDuration));
} else {
mWidget = document.getElementById(widget);
if (mWidget && mWidget._customHandlers && mWidget._customHandlers.onShow) {
mWidget._customHandlers.onShow(mWidget, widget);
}
$('#' + widget).show(showEffect, null, parseInt(showDuration));
}
}
}
setTimeout(function () {
var mWidget;
// Show complex widgets like hqWidgets or bars
for (var widget in widgets) {
if (!widgets.hasOwnProperty(widget)) continue;
mWidget = document.getElementById(widget);
if (mWidget &&
mWidget._customHandlers &&
mWidget._customHandlers.onShow) {
if (widgets[widget] && widgets[widget].data && widgets[widget].data.filterkey) {
if (!(that.viewsActiveFilter[that.activeView].length > 0 &&
that.viewsActiveFilter[that.activeView].indexOf(widgets[widget].data.filterkey) === -1)) {
mWidget._customHandlers.onShow(mWidget, widget);
}
}
}
}
}, parseInt(showDuration) + 10);
}
if (this.binds.bars && this.binds.bars.filterChanged) {
this.binds.bars.filterChanged(view, filter);
}
},
isSignalVisible: function (view, widget, index, val, widgetData) {
widgetData = widgetData || this.views[view].widgets[widget].data;
var oid = widgetData['signals-oid-' + index];
if (oid) {
if (val === undefined) val = this.states.attr(oid + '.val');
var condition = widgetData['signals-cond-' + index];
var value = widgetData['signals-val-' + index];
if (val === undefined) return (condition === 'not exist');
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);
}
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 (value !== 'null');
case 'not exist':
return (value === 'null');
default:
console.log('Unknown signals condition for ' + widget + ': ' + condition);
return false;
}
} else {
return false;
}
},
addSignalIcon: function (view, wid, data, index) {
// show icon
var display = (this.editMode || this.isSignalVisible(view, wid, index, undefined, data)) ? '' : 'none';
if (this.editMode && data['signals-hide-edit-' + index]) display = 'none';
$('#' + wid).append('<div class="vis-signal ' + (data['signals-blink-' + index] ? 'vis-signals-blink' : '') + ' ' + (data['signals-text-class-' + index] || '') + ' " data-index="' + index + '" style="display: ' + display + '; pointer-events: none; position: absolute; z-index: 10; top: ' + (data['signals-vert-' + index] || 0) + '%; left: ' + (data['signals-horz-' + index] || 0) + '%"><img class="vis-signal-icon" src="' + data['signals-icon-' + index] + '" style="width: ' + (data['signals-icon-size-' + index] || 32) + 'px; height: auto;' + (data['signals-icon-style-' + index] || '') + '"/>' +
(data['signals-text-' + index] ? ('<div class="vis-signal-text " style="' + (data['signals-text-style-' + index] || '') + '">' + data['signals-text-' + index] + '</div>') : '') + '</div>');
},
addGestures: function (id, wdata) {
// gestures
var gestures = ['swipeRight', 'swipeLeft', 'swipeUp', 'swipeDown', 'rotateLeft', 'rotateRight', 'pinchIn', 'pinchOut', 'swiping', 'rotating', 'pinching'];
var $$wid = $$('#' + id);
var $wid = $('#' + id);
var offsetX = parseInt(wdata['gestures-offsetX']) || 0;
var offsetY = parseInt(wdata['gestures-offsetY']) || 0;
var that = this;
gestures.forEach(function (gesture) {
if (wdata && wdata['gestures-' + gesture + '-oid']) {
var oid = wdata['gestures-' + gesture + '-oid'];
if (oid) {
var val = wdata['gestures-' + gesture + '-value'];
var delta = parseInt(wdata['gestures-' + gesture + '-delta']) || 10;
var limit = parseFloat(wdata['gestures-' + gesture + '-limit']) || false;
var max = parseFloat(wdata['gestures-' + gesture + '-maximum']) || 100;
var min = parseFloat(wdata['gestures-' + gesture + '-minimum']) || 0;
var valState = that.states.attr(oid + '.val');
var newVal = null;
var $indicator;
if (valState !== undefined) {
$wid.on('touchmove', function (evt) {
evt.preventDefault();
});
$wid.css({
'-webkit-user-select': 'none',
'-khtml-user-select': 'none',
'-moz-user-select': 'none',
'-ms-user-select': 'none',
'user-select': 'none'
});
$$wid[gesture](function (data) {
valState = that.states.attr(oid + '.val');
if (val === 'toggle') {
if (valState === true) {
newVal = false;
} else if (valState === false) {
newVal = true;
} else {
newVal = null;
return;
}
} else if (gesture === 'swiping' || gesture === 'rotating' || gesture === 'pinching') {
if (newVal === null) {
$indicator = $('#' + wdata['gestures-indicator']);
// create default indicator
if (!$indicator.length) {
//noinspection JSJQueryEfficiency
$indicator = $('#gestureIndicator');
if (!$indicator.length) {
$('body').append('<div id="gestureIndicator" style="position: absolute; pointer-events: none; z-index: 100; box-shadow: 2px 2px 5px 1px gray;height: 21px; border: 1px solid #c7c7c7; border-radius: 5px; text-align: center; padding-top: 6px; padding-left: 2px; padding-right: 2px; background: lightgray;"></div>');
$indicator = $('#gestureIndicator');
$indicator.on('gestureUpdate', function (event, evData) {
if (evData.val === null) {
$(this).hide();
} else {
$(this).html(evData.val);
$(this).css({
left: parseInt(evData.x) - $(this).width() / 2 + 'px',
top: parseInt(evData.y) - $(this).height() / 2 + 'px'
}).show();
}
});
}
}
$('#vis_container').css({
'-webkit-user-select': 'none',
'-khtml-user-select': 'none',
'-moz-user-select': 'none',
'-ms-user-select': 'none',
'user-select': 'none'
});
$(document).on('mouseup.gesture touchend.gesture', function () {
if (newVal !== null) {
that.setValue(oid, newVal);
newVal = null;
}
$indicator.trigger('gestureUpdate', {val: null});
$(document).off('mouseup.gesture touchend.gesture');
$('#vis_container').css({
'-webkit-user-select': 'text',
'-khtml-user-select': 'text',
'-moz-user-select': 'text',
'-ms-user-select': 'text',
'user-select': 'text'
});
});
}
var swipeDelta, indicatorX, indicatorY = 0;
switch (gesture) {
case 'swiping':
swipeDelta = Math.abs(data.touch.delta.x) > Math.abs(data.touch.delta.y) ? data.touch.delta.x : data.touch.delta.y * (-1);
swipeDelta = swipeDelta > 0 ? Math.floor(swipeDelta / delta) : Math.ceil(swipeDelta / delta);
indicatorX = data.touch.x;
indicatorY = data.touch.y;
break;
case 'rotating':
swipeDelta = data.touch.delta;
swipeDelta = swipeDelta > 0 ? Math.floor(swipeDelta / delta) : Math.ceil(swipeDelta / delta);
if (data.touch.touches[0].y < data.touch.touches[1].y) {
indicatorX = data.touch.touches[1].x;
indicatorY = data.touch.touches[1].y;
} else {
indicatorX = data.touch.touches[0].x;
indicatorY = data.touch.touches[0].y;
}
break;
case 'pinching':
swipeDelta = data.touch.delta;
swipeDelta = swipeDelta > 0 ? Math.floor(swipeDelta / delta) : Math.ceil(swipeDelta / delta);
if (data.touch.touches[0].y < data.touch.touches[1].y) {
indicatorX = data.touch.touches[1].x;
indicatorY = data.touch.touches[1].y;
} else {
indicatorX = data.touch.touches[0].x;
indicatorY = data.touch.touches[0].y;
}
break;
default:
break;
}
newVal = (parseFloat(valState) || 0) + (parseFloat(val) || 1) * swipeDelta;
newVal = Math.max(min, Math.min(max, newVal));
$indicator.trigger('gestureUpdate', {
val: newVal,
x: indicatorX + offsetX,
y: indicatorY + offsetY
});
return;
} else if (limit !== false) {
newVal = (parseFloat(valState) || 0) + (parseFloat(val) || 1);
if (parseFloat(val) > 0 && newVal > limit) {
newVal = limit;
} else if (parseFloat(val) < 0 && newVal < limit) {
newVal = limit;
}
} else {
newVal = val;
}
that.setValue(oid, newVal);
newVal = null;
});
}
}
}
});
},
addLastChange: function (view, wid, data) {
// show last change
var border = (parseInt(data['lc-border-radius'], 10) || 0) + 'px';
var css = {
background: 'rgba(182,182,182,0.6)',
'font-family': 'Tahoma',
position: 'absolute',
'z-index': 0,
'border-radius': data['lc-position-horz'] === 'left' ? (border + ' 0 0 ' + border) : (data['lc-position-horz'] === 'right' ? '0 ' + border + ' ' + border + ' 0' : border),
'white-space': 'nowrap'
};
if (data['lc-font-size']) {
css['font-size'] = data['lc-font-size'];
}
if (data['lc-font-style']) {
css['font-style'] = data['lc-font-style'];
}
if (data['lc-font-family']) {
css['font-family'] = data['lc-font-family'];
}
if (data['lc-bkg-color']) {
css['background'] = data['lc-bkg-color'];
}
if (data['lc-color']) {
css['color'] = data['lc-color'];
}
if (data['lc-border-width']) {
css['border-width'] = parseInt(data['lc-border-width'], 10) || 0;
}
if (data['lc-border-style']) {
css['border-style'] = data['lc-border-style'];
}
if (data['lc-border-color']) {
css['border-color'] = data['lc-border-color'];
}
if (data['lc-padding']) {
css['padding'] = data['lc-padding'];
} else {
css['padding-top'] = 3;
css['padding-bottom'] = 3;
}
if (data['lc-zindex']) {
css['z-index'] = data['lc-zindex'];
}
if (data['lc-position-vert'] === 'top') {
css.top = parseInt(data['lc-offset-vert'], 10);
} else if (data['lc-position-vert'] === 'bottom') {
css.bottom = parseInt(data['lc-offset-vert'], 10);
} else if (data['lc-position-vert'] === 'middle') {
css.top = 'calc(50% + ' + (parseInt(data['lc-offset-vert'], 10) - 10) + 'px)';
}
var offset = parseFloat(data['lc-offset-horz']) || 0;
if (data['lc-position-horz'] === 'left') {
css.right = 'calc(100% - ' + offset + 'px)';
if (!data['lc-padding']) {
css['padding-right'] = 10;
css['padding-left'] = 10;
}
} else if (data['lc-position-horz'] === 'right') {
css.left = 'calc(100% + ' + offset + 'px)';
if (!data['lc-padding']) {
css['padding-right'] = 10;
css['padding-left'] = 10;
}
} else if (data['lc-position-horz'] === 'middle') {
css.left = 'calc(50% + ' + offset + 'px)';
}
var text = '<div class="vis-last-change" data-type="' + data['lc-type'] + '" data-format="' + data['lc-format'] + '" data-interval="' + data['lc-is-interval'] + '">' + this.binds.basic.formatDate(this.states.attr(data['lc-oid'] + '.ts'), data['lc-format'], data['lc-is-interval'], data['lc-is-moment']) + '</div>';
$('#' + 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('<div class="vis-edit-relative" style="width: ' + ww + '; height: ' + hh + '"></div>');
$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('<div></div>').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('<link rel="stylesheet" type="text/css" href="' + ((typeof app === 'undefined') ? '../../' : '') + 'lib/css/themes/jquery-ui/' + (this.calcCommonStyle() || 'redmond') + '/jquery-ui.min.css" id="commonTheme"/>');
}
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('<div id="waitScreen" class="vis-wait-screen"><div id="waitDialog" class="waitDialog"><div class="vis-progressbar"></div><div class="vis-wait-text" id="waitText"></div></div></div>');
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('<br>gclick touchend: ' + vis.lastChange);
$log1.append('<br>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...') + '<br/>', 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('<style id="vis-common-user" class="vis-common-user">' + data + '</style>');
$(document).trigger('vis-common-user');
},
error: function (jqXHR, textStatus, errorThrown) {
vis.conn.logError('Cannot load vis-common-user.css - ' + errorThrown);
$('head').append('<style id="vis-common-user" class="vis-common-user"></style>');
$(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('<style id="vis-user" class="vis-user">' + data + '</style>');
}
$(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('<style id="vis-user" class="vis-user"></style>');
$(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...') + '<br>', 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 = '<select id="login-username" class="login-input-field">';
for (var z = 0; z < visConfig.auth.users.length; z++) {
users += '<option value="' + visConfig.auth.users[z] + '" ' + (!z ? 'selected' : '') + '>' + visConfig.auth.users[z] + '</option>';
}
users += '</select>';
} else {
users = '<input id="login-username" value="" type="text" autocomplete="on" class="login-input-field" placeholder="' + _('User name') + '">';
}
var text = '<div id="login-box" class="login-popup" style="display:none">' +
'<div class="login-message">' + message + '</div>' +
'<div class="login-input-field">' +
'<label class="username">' +
'<span class="_">' + _('User name') + '</span>' +
users +
'</label>' +
'<label class="password">' +
'<span class="_">' + _('Password') + '</span>' +
'<input id="login-password" value="" type="password" class="login-input-field" placeholder="' + _('Password') + '">' +
'</label>' +
'<button class="login-button" type="button" class="_">' + _('Sign in') + '</button>' +
'</div>' +
'</div>';
// Add the mask to body
$('body')
.append(text)
.append('<div id="login-mask"></div>');
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('<audio id="external_sound"></audio>');
$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;
};
}