UNPKG

iobroker.vis

Version:

Graphical user interface for iobroker.

1,146 lines (1,055 loc) 149 kB
/** * ioBroker.vis * https://github.com/ioBroker/ioBroker.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', 'de': 'Keine Verbindung zum Server', 'ru': 'Нет соединения с сервером'}, 'Loading Views...': {'en': 'Loading Views...', 'de': 'Lade Views...', 'ru': 'Загрузка пользовательских страниц...'}, 'Connecting to Server...': {'en': 'Connecting to Server...', 'de': 'Verbinde mit dem Server...', 'ru': 'Соединение с сервером...'}, 'Loading data objects...': {'en': 'Loading data...', 'de': 'Lade Daten...', 'ru': 'Загрузка данных...'}, 'Loading data values...': {'en': 'Loading values...', 'de': 'Lade Werte...', 'ru': 'Загрузка значений...'}, 'error - View doesn\'t exist': {'en': 'View doesn\'t exist!', 'de': 'View existiert nicht!', 'ru': 'Страница не существует!'}, 'no views found!': {'en': 'No views found!', 'de': 'Keine Views gefunden!', 'ru': 'Не найдено страниц!'}, 'No Views found on Server': { 'en': 'No Views found on Server', 'de': 'Keine Views am Server gefunden.', 'ru': 'На сервере не найдено никаких страниц.' }, 'All changes are saved locally. To reset changes clear the cache.': { 'en': 'All changes are saved locally. To reset changes clear the browser cache.', 'de': 'Alle Änderungen sind lokal gespeichert. Um Änderungen zu löschen, lösche Browsercache.', 'ru': 'Все изменения сохранены локально. Для отмены локальных изменений очистите кеш броузера.' }, 'please use /vis/edit.html instead of /vis/?edit': { 'en': 'Please use /vis/edit.html instead of /vis/?edit', 'de': 'Bitte geben Sie /vis/edit.html statt /vis/?edit', 'ru': 'Используйте /vis/edit.html вместо /vis/?edit' }, 'no views found on server.\nCreate new %s ?': { 'en': 'no views found on server.\nCreate new %s?', 'de': 'Keine Views am Server gefunden am.\nErzeugen %s?', 'ru': 'На сервере не найдено никаких страниц. Создать %s?' }, 'Update found, loading new Files...': { 'en': 'Update found.<br/>Loading new Files...', 'de': 'Neue Version gefunden.<br/>Lade neue Dateien...', 'ru': 'Обнаружено Обновление.<br/>Загружаю новые файлы...' }, 'Loading Widget-Sets...': { 'en': 'Loading Widget-Sets...', 'de': 'Lade Widget-Sätze...', 'ru': 'Загрузка наборов элементов...' }, 'error: view not found.': { 'en': 'Error: view not found', 'de': 'Fehler: View wurde nicht gefunden', 'ru': 'Ошибка: Страница не существует' }, 'error: view container recursion.': { 'en': 'Error: view container recursion', 'de': 'Fehler: View ist rekursiv', 'ru': 'Ошибка: Страница вызывет саму себя' }, "Cannot execute %s for %s, because of insufficient permissions": { "en": "Cannot execute %s for %s, because of insufficient permissions.", "de": "Kann das Kommando \"%s\" für %s nicht ausführen, weil nicht genügend Zugriffsrechte vorhanden sind.", "ru": "Не могу выполнить \"%s\" для %s, так как недостаточно прав." }, "Insufficient permissions": { "en": "Insufficient permissions", "de": "Nicht genügend Zugriffsrechte", "ru": "Недостаточно прав" }, "View disabled for user %s": { "en": "View disabled for user <b>%s</b>", "de": "View ist für Anwender <b>%s</b> deaktiviert", "ru": "Страница недоступна для пользователя <b>%s</b>" } }); } if (typeof systemLang !== 'undefined' && typeof cordova === 'undefined') { systemLang = visConfig.language || systemLang; } var vis = { version: '1.1.8', 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) {