/* global jQuery */ /* global document */ /* jshint -W097 */ /* jshint strict: false */ /* MIT, Copyright 2014-2018 bluefox , soef version: 1.1.6 (2018.08.03) To use this dialog as standalone in yunkong2 environment include: To use as part, just Interface: + init(options) - init select ID dialog. Following options are supported { currentId: '', // Current ID or empty if nothing preselected objects: null, // All objects that should be shown. It can be empty if connCfg used. getObjects: null, // null or function to read all objects anew on refresh (because of subscripitons): funsubscriptionsjects) {} states: null, // All states of objects. It can be empty if connCfg used. If objects are set and no states, states will no be shown. filter: null, // filter imgPath: 'lib/css/fancytree/', // Path to images device.png, channel.png and state.png connCfg: null, // configuration for dialog, ti read objects itself: {socketUrl: socketUrl, socketSession: socketSession} onSuccess: null, // callback function to be called if user press "Select". Can be overwritten in "show" - function (newId, oldId, newObj) onChange: null, // called every time the new object selected - function (newId, oldId, newObj) noDialog: false, // do not make dialog stats: false, // show objects statistics noMultiselect: false, // do not make multiselect useValues: false, // show button to toggle objects<=>values buttons: null, // array with buttons, that should be shown in last column // if array is not empty it can has following fields // [{ // text: false, // same as jquery button // icons: { // same as jquery bdata.columnsutton // primary: 'ui-icon-gear' // }, // click: function (id) { // // do on click // }, // match: function (id) { // // you have here object "this" pointing to $('button') // }, // width: 26, // same as jquery button // height: 20 // same as jquery button // }], panelButtons: null, // array with buttons, that should be shown at the top of dialog (near expand all) list: false, // tree view or list view name: null, // name of the dialog to store filter settings noCopyToClipboard: false, // do not show button for copy to clipboard root: null, // root node, e.g. "script.js" useNameAsId: false, // use name of object as ID noColumnResize: false, // do not allow column resize firstMinWidth: null, // width if ID column, default 400 showButtonsForNotExistingObjects: false, webServer: null, // link to webserver, by default ":8082" filterPresets: null, // Object with predefined filters, eg {role: 'level.dimmer'} or {type: 'state'} roleExactly: false, // If the role must be equal or just content the filter value sortConfig: { statesFirst: true, // Show states before folders ignoreSortOrder: false // Ignore standard sort order of fancytree }, texts: { select: 'Select', cancel: 'Cancel', all: 'All', id: 'ID', name: 'Name', role: 'Role', type: 'Type', room: 'Room', 'function': 'Function', enum: 'Members', value: 'Value', selectid: 'Select ID', from: 'From', user: 'user', lc: 'Last changed', ts: 'Time stamp', ack: 'Acknowledged', expand: 'Expand all nodes', collapse: 'Collapse all nodes', refresh: 'Rebuild tree', edit: 'Edit', ok: 'Ok', push: 'Trigger event' wait: 'Processing...', list: 'Show list view', tree: 'Show tree view', selectAll: 'Select all', unselectAll: 'Unselect all', invertSelection: 'Invert selection', copyToClipboard: 'Copy to clipboard', expertMode: 'Toggle expert mode', button: 'Settings', noData: 'No data', Objects: 'Objects', States: 'States', toggleValues: 'Toggle states view' }, columns: ['image', 'name', 'type', 'role', 'enum', 'room', 'function', 'value', 'button', 'value.val', 'value.ts', 'value.lc', 'value.from', 'value.q'], // some elements of columns could be an object {name: field, data: function (id, name){}, title: function (id, name) {}} widths: null, // array with width for every column editEnd: null, // function (id, newValues) for edit lines (only id and name can be edited) editStart: null, // function (id, $inputs) called after edit start to correct input fields (inputs are jquery objects), zindex: null, // z-index of dialog or table customButtonFilter: null, // if in the filter over the buttons some specific button must be shown. It has type like {icons:{primary: 'ui-icon-close'}, text: false, callback: function ()} expertModeRegEx: null // list of regex with objects, that will be shown only in expert mode, like /^system\.|^yunkong2\.|^_|^[\w-]+$|^enum\.|^[\w-]+\.admin/ quickEdit: null, // list of fields with edit on click. Elements can be just names from standard list or objects like: // {name: 'field', options: {a1: 'a111_Text', a2: 'a22_Text'}}, options can be a function (id, name), that give back such an object quickEditCallback: null, // function (id, attr, newValue, oldValue), readyCallback: null // called when objects and states are read from server (only if connCfg is not null). function (err, objects, states) expandedCallback: null, // called when some node was expanded. function (id, childrenCount, statesCount) collapsedCallback: null, // called when some node was expanded. function (id, childrenCount, statesCount) } + show(currentId, filter, callback) - all arguments are optional if set by "init". Callback is like function (newId, oldId) {}. If multiselect, so the arguments are arrays. + clear() - clear object tree to read and build anew (used only if objects set by "init") + getInfo(id) - get information about ID + getTreeInfo(id) - get {id, parent, children, object} + state(id, val) - update states in tree + object(id, obj) - update object info in tree + reinit() - draw tree anew filter is like: common: { history: true } or type: "state" */ var addAll2FilterCombobox = false; function tdp(x, decimals) { // TODO support of US format too return isNaN(x) ? '' : x.toFixed(decimals || 0).replace('.', ',').replace(/\B(?=(\d{3})+(?!\d))/g, '.'); } function removeImageFromSettings(data) { if (!data || !data.columns) return; var idx = data.columns.indexOf('image'); if (idx >= 0) data.columns.splice(idx, 1); } var lineIndent = '5px'; function span(txt, attr) { //if (txt === undefined) txt = ''; //return txt; var style = 'padding-left: ' + lineIndent + ';'; if (attr) style += attr; return '' + txt + ''; } function filterChanged(e) { var $e = $(e); var val = $e.val(); var td = $e.parent(); if (val) { td.addClass('filter-active'); } else { td.removeClass('filter-active'); } } (function ($) { 'use strict'; if ($.fn.selectId) return; var isMaterial; function getNameObj(obj, id) { if (obj && obj.common) { return getName(obj.common.name || (id || '').split('.').pop()); } else { return (id || '').split('.').pop(); } } function formatDate(dateObj) { //return dateObj.getFullYear() + '-' + // ('0' + (dateObj.getMonth() + 1).toString(10)).slice(-2) + '-' + // ('0' + (dateObj.getDate()).toString(10)).slice(-2) + ' ' + // ('0' + (dateObj.getHours()).toString(10)).slice(-2) + ':' + // ('0' + (dateObj.getMinutes()).toString(10)).slice(-2) + ':' + // ('0' + (dateObj.getSeconds()).toString(10)).slice(-2); // Following implementation is 5 times faster if (!dateObj) return ''; var text = dateObj.getFullYear(); var v = dateObj.getMonth() + 1; if (v < 10) { text += '-0' + v; } else { text += '-' + v; } v = dateObj.getDate(); if (v < 10) { text += '-0' + v; } else { text += '-' + v; } v = dateObj.getHours(); if (v < 10) { text += ' 0' + v; } else { text += ' ' + v; } v = dateObj.getMinutes(); if (v < 10) { text += ':0' + v; } else { text += ':' + v; } v = dateObj.getSeconds(); if (v < 10) { text += ':0' + v; } else { text += ':' + v; } v = dateObj.getMilliseconds(); if (v < 10) { text += '.00' + v; } else if (v < 100) { text += '.0' + v; } else { text += '.' + v; } return text; } function filterId(data, id) { if (data.rootExp) { if (!data.rootExp.test(id)) return false; } // ignore system objects in expert mode if (data.expertModeRegEx && !data.expertMode && data.expertModeRegEx.test(id)) { return false; } // ignore exeprt objects in expert mode if (!data.expertMode && data.objects[id] && data.objects[id].common && data.objects[id].common.expert) { return; } if (data.filter) { if (data.filter.type && data.filter.type !== data.objects[id].type) return false; if (data.filter.common && data.filter.common.custom) { if (!data.objects[id].common) return false; // todo: remove history sometime 09.2016 var custom = data.objects[id].common.custom || data.objects[id].common.history; if (!custom) return false; if (data.filter.common.custom === true) { return true; } else { if (!custom[data.filter.common.custom]) return false; } } } return true; } function getExpandeds(data) { if (!data.$tree) return null; var expandeds = {}; (function getIt(nodes) { if (!Array.isArray(nodes.children)) return; for (var i = 0, len = nodes.children.length; i < len; i++) { var node = nodes.children[i]; if (node.expanded) { expandeds[node.key] = true; } getIt(node); } })(data.$tree.fancytree('getRootNode')); return expandeds; } function restoreExpandeds(data, expandeds) { if (!expandeds || !data.$tree) return; (function setIt(nodes) { if (!Array.isArray(nodes.children)) return; for (var i = 0, len = nodes.children.length; i < len; i++) { var node = nodes.children[i]; if (expandeds[node.key]) { try { node.setExpanded(); } catch (e) { console.log('Cannot expand: ' + e); } //node.setActive(); } setIt(node); } })(data.$tree.fancytree('getRootNode')); expandeds = null; } function sortTree(data) { var objects = data.objects; var checkStatesFirst; switch (data.sortConfig.statesFirst) { case undefined: checkStatesFirst = function () { return 0 }; break; case true: checkStatesFirst = function (child1, child2) { return ((~~child2.folder) - (~~child1.folder))}; break; case false: checkStatesFirst = function (child1, child2) { return ((~~child1.folder) - (~~child2.folder))}; break; } // function compAdapterAndInstance(c1, c2) { // var s1 = c1.key.substr(0, c1.key.lastIndexOf('.')); // var s2 = c2.key.substr(0, c2.key.lastIndexOf('.')); // // if (s1 > s2) return 1; // if (s1 < s2) return -1; // return 0; // } function sortByName(child1, child2) { var ret = checkStatesFirst(child1, child2); if (ret) return ret; var o1 = objects[child1.key], o2 = objects[child2.key]; if (o1 && o2) { var c1 = o1.common, c2 = o2.common; if (c1 && c2) { // var s1 = child1.key.substr(0, child1.key.lastIndexOf('.')); // faster than regexp. // var s2 = child2.key.substr(0, child2.key.lastIndexOf('.')); // if (s1 > s2) return 1; // if (s1 < s2) return -1; if (!data.sortConfig.ignoreSortOrder && c1.sortOrder && c2.sortOrder) { if (c1.sortOrder > c2.sortOrder) return 1; if (c1.sortOrder < c2.sortOrder) return -1; return 0; } var name1; var name2; if (c1.name) { name1 = c1.name; if (typeof name1 === 'object') { name1 = (name1[systemLang] || name1.en).toLowerCase(); } else { name1 = name1.toLowerCase(); } } else { name1 = child1.key; } if (c2.name) { name2 = c2.name; if (typeof name2 === 'object') { name2 = (name2[systemLang] || name2.en).toLowerCase(); } else { name2 = name2.toLowerCase(); } } else { name2 = child1.key; } if (name1 > name2) return 1; if (name1 < name2) return -1; } } if (child1.key > child2.key) return 1; if (child1.key < child2.key) return -1; return 0; } function sortByKey(child1, child2) { var ret = checkStatesFirst(child1, child2); if (ret) return ret; if (!data.sortConfig.ignoreSortOrder) { var o1 = objects[child1.key], o2 = objects[child2.key]; if (o1 && o2) { var c1 = o1.common, c2 = o2.common; if (c1 && c2 && c1.sortOrder && c2.sortOrder) { // var s1 = child1.key.substr(0, child1.key.lastIndexOf('.')); // faster than regexp. // var s2 = child2.key.substr(0, child2.key.lastIndexOf('.')); // if (s1 > s2) return 1; // if (s1 < s2) return -1; if (c1.sortOrder > c2.sortOrder) return 1; if (c1.sortOrder < c2.sortOrder) return -1; return 0; } } } if (child1.key > child2.key) return 1; if (child1.key < child2.key) return -1; return 0; } var sortFunc = data.sort ? sortByName : sortByKey; var sfunc = sortByKey; // sort the root always by key return (function sort(tree) { if (!tree || !tree.children) return; try { tree.sortChildren(sfunc); } catch (e) { console.log(e); } sfunc = sortFunc; for (var i=tree.children.length-1; i>=0; i--) { sort(tree.children[i]); } })(data.$tree.fancytree('getRootNode')); // var sortFunc = data.sort ? sortByName : sortByKey; // var root = data.$tree.fancytree('getRootNode'); // root.sortChildren(sortByKey, false); // return (function sort(tree) { // if (!tree) return; // for (var i=tree.children.length-1; i>=0; i--) { // var child = tree.children[i]; // if (!child) return; // child.sortChildren(sortFunc); // sort(child.children); // } // })(root.children); //data.$tree.fancytree('getRootNode').sortChildren(data.sort ? sortByName : sortByKey, true); //var tree = data.$tree.fancytree('getTree'); //var node = tree.getActiveNode(); } function getAllStates(data) { var stats = data.stats ? {objs: 0, states: 0} : null; var objects = data.objects; var isType = data.columns.indexOf('type') !== -1; var isRoom = data.columns.indexOf('room') !== -1; var isFunc = data.columns.indexOf('function') !== -1; var isRole = data.columns.indexOf('role') !== -1; var isHist = data.columns.indexOf('button') !== -1; data.tree = {title: '', children: [], count: 0, root: true}; data.roomEnums = []; data.funcEnums = []; data.ids = []; for (var id in objects) { if (!objects.hasOwnProperty(id)) continue; if (!id) { console.error('Invalid empty ID found! Please fix it'); continue; } stats && stats.objs++; if (objects[id].type === 'state') { stats && stats.states++; } else if (data.valuesActive) { continue; } if (isRoom) { if (objects[id].type === 'enum' && data.regexEnumRooms.test(id) && data.roomEnums.indexOf(id) === -1) data.roomEnums.push(id); if (objects[id].enums) { for (var e in objects[id].enums) { if (data.regexEnumRooms.test(e) && data.roomEnums.indexOf(e) === -1) { data.roomEnums.push(e); } data.objects[e] = data.objects[e] || { _id: e, common: { name: objects[id].enums[e], members: [id] } }; data.objects[e].common.members = data.objects[e].common.members || []; if (data.objects[e].common.members.indexOf(id) === -1) { data.objects[e].common.members.push(id); } } } } if (isFunc) { if (objects[id].type === 'enum' && data.regexEnumFuncs.test(id) && data.funcEnums.indexOf(id) === -1) { data.funcEnums.push(id); } if (objects[id].enums) { for (var e in objects[id].enums) { if (data.regexEnumFuncs.test(e) && data.funcEnums.indexOf(e) === -1) { data.funcEnums.push(e); } data.objects[e] = data.objects[e] || { _id: e, common: { name: objects[id].enums[e], members: [id] } }; data.objects[e].common.members = data.objects[e].common.members || []; if (data.objects[e].common.members.indexOf(id) === -1) { data.objects[e].common.members.push(id); } } } } if (isType && objects[id].type && data.types.indexOf(objects[id].type) === -1) data.types.push(objects[id].type); if (isRole && objects[id].common && objects[id].common.role) { try { var parts = objects[id].common.role.split('.'); var role = ''; for (var u = 0; u < parts.length; u++) { role += (role ? '.' : '') + parts[u]; if (data.roles.indexOf(role) === -1) data.roles.push(role); } } catch (e) { console.error('Cannot parse role "' + objects[id].common.role + '" by ' + id); } } if (isHist && objects[id].type === 'instance' && (objects[id].common.type === 'storage' || objects[id].common.supportCustoms)) { var h = id.substring('system.adapter.'.length); if (data.histories.indexOf(h) === -1) { data.histories.push(h); } } if (!filterId(data, id)) continue; treeInsert(data, id, data.currentId === id); if (objects[id].enums) { for (var ee in objects[id].enums) { if (objects[id].enums.hasOwnProperty(ee) && objects[ee] && objects[ee].common && objects[ee].common.members && objects[ee].common.members.indexOf(id) === -1) { objects[ee].common.members.push(id); } } } // fill counters if (data.expertMode) { data.ids.push(id); } } data.inited = true; data.roles.sort(); data.types.sort(); data.roomEnums.sort(); data.funcEnums.sort(); data.histories.sort(); data.ids.sort(); if (stats) { data.stats = stats; } } function treeSplit(data, id) { if (!id) return null; if (data.root) { id = id.substring(data.root.length); } var parts = id.split('.'); if (data.regexSystemAdapter.test(id)) { if (parts.length > 3) { parts[0] = 'system.adapter.' + parts[2] + '.' + parts[3]; parts.splice(1, 3); } else { parts[0] = 'system.adapter.' + parts[2]; parts.splice(1, 2); } } else if (data.regexSystemHost.test(id)) { parts[0] = 'system.host.' + parts[2]; parts.splice(1, 2); } else if (parts.length > 1 && !data.root) { parts[0] = parts[0] + '.' + parts[1]; parts.splice(1, 1); } /*if (optimized) { parts = treeOptimizePath(parts); }*/ return parts; } function _deleteTree(node, deletedNodes) { if (node.parent) { if (deletedNodes && node.id) { deletedNodes.push(node); } var p = node.parent; if (p.children.length <= 1) { _deleteTree(node.parent); } else { for (var z = 0; z < p.children.length; z++) { if (node.key === p.children[z].key) { p.children.splice(z, 1); break; } } } } else { //error } } function deleteTree(data, id, deletedNodes) { var node = findTree(data, id); if (!node) { console.log('deleteTree: Id ' + id + ' not found'); return; } _deleteTree(node, deletedNodes); } function findTree(data, id) { return (function find(tree) { if (!tree.children) return; for (var i = tree.children.length - 1; i >= 0; i--) { var child = tree.children[i]; if (id === child.key) return child; if (id.startsWith(child.key + '.')) { //if (id === child.key) return child; return find(child); } } return null; })(data.tree); } // function xfindTree(data, id) { // return _findTree(data.tree, treeSplit(data, id, false), 0); // } // function _findTree(tree, parts, index) { // var num = -1; // for (var j = 0; j < tree.children.length; j++) { // if (tree.children[j].title === parts[index]) { // num = j; // break; // } // //if (tree.children[j].title > parts[index]) break; // } // // if (num === -1) return null; // // if (parts.length - 1 === index) { // return tree.children[num]; // } else { // return _findTree(tree.children[num], parts, index + 1); // } // } /* function treeInsert(data, id, isExpanded, addedNodes) { var idArr = data.list ? [id] : treeSplit(data, id); if (!idArr) return console.error('Empty object ID!'); (function insert(tree, idx) { for ( ; idx < idArr.length; idx += 1) { for (var i = tree.children.length - 1; i >= 0; i--) { var child = tree.children[i]; if (id === child.key) return child; if (id.startsWith (child.key + '.')) { //if (id === child.key) return child; child.expanded = child.expanded || isExpanded; return insert (child, idx + 1); } } tree.folder = true; tree.expanded = isExpanded; var obj = { key: (data.root || '') + idArr.slice (0, idx + 1).join ('.'), children: [], title: idArr[idx], folder: false, expanded: false, parent: tree }; //data.objects[obj.key].node = obj; tree.children.push (obj); if (addedNodes) { addedNodes.push (obj); } tree = obj; } tree.id = id; })(data.tree, 0); } */ function treeInsert(data, id, isExpanded, addedNodes) { return _treeInsert(data.tree, data.list ? [id] : treeSplit(data, id, false), id, 0, isExpanded, addedNodes, data); } function _treeInsert(tree, parts, id, index, isExpanded, addedNodes, data) { index = index || 0; if (!parts) { console.error('Empty object ID!'); return; } var num = -1; var j; for (j = 0; j < tree.children.length; j++) { if (tree.children[j].title === parts[index]) { num = j; break; } //if (tree.children[j].title > parts[index]) break; } if (num === -1) { tree.folder = true; tree.expanded = isExpanded; var fullName = ''; for (var i = 0; i <= index; i++) { fullName += ((fullName) ? '.' : '') + parts[i]; } var obj = { key: (data.root || '') + fullName, children: [], title: parts[index], folder: false, expanded: false, parent: tree }; if (j === tree.children.length) { num = tree.children.length; tree.children.push(obj); } else { num = j; tree.children.splice(num, 0, obj); } if (addedNodes) { addedNodes.push(tree.children[num]); } } if (parts.length - 1 === index) { tree.children[num].id = id; } else { tree.children[num].expanded = tree.children[num].expanded || isExpanded; _treeInsert(tree.children[num], parts, id, index + 1, isExpanded, addedNodes, data); } } function showActive($dlg, scrollIntoView) { var data = $dlg.data('selectId'); // Select current element if (data.selectedID) { data.$tree.fancytree('getTree').visit(function (node) { if (node.key === data.selectedID) { try { node.setActive(); node.makeVisible({scrollIntoView: scrollIntoView || false}); //$(node).find('table.fancytree-ext-table tbody tr td') //xxx } catch (err) { console.error(err); } return false; } }); } } function syncHeader($dlg) { var data = $dlg.data('selectId'); if (!data) return; var $header = $dlg.find('.main-header-table'); var thDest = $header.find('>tbody>tr>th'); //if table headers are specified in its semantically correct tag, are obtained var thSrc = data.$tree.find('>tbody>tr>td'); var x, o; for (var i = 0; i < thDest.length - 1; i++) { if ((x = $(thSrc[i]).width())) { $(thDest[i]).attr('width', x); if ((o = $(thSrc[i + 1]).offset().left)) { if ((o -= $(thDest[i + 1]).offset().left)) { $(thDest[i]).attr('width', x + o); } } } } } function getName(name) { if (name && typeof name === 'object') { return name[systemLang] || name.en; } else { return name || ''; } } function findRoomsForObject(data, id, withParentInfo, rooms) { if (!id) { return []; } rooms = rooms || []; for (var i = 0; i < data.roomEnums.length; i++) { var common = data.objects[data.roomEnums[i]] && data.objects[data.roomEnums[i]].common; var name = getName(common.name); if (common.members && common.members.indexOf(id) !== -1 && rooms.indexOf(name) === -1) { if (!withParentInfo) { rooms.push(name); } else { rooms.push({name: name, origin: id}); } } } var parts = id.split('.'); parts.pop(); id = parts.join('.'); if (data.objects[id]) findRoomsForObject(data, id, withParentInfo, rooms); return rooms; } function findRoomsForObjectAsIds(data, id, rooms) { if (!id) { return []; } rooms = rooms || []; for (var i = 0; i < data.roomEnums.length; i++) { var common = data.objects[data.roomEnums[i]] && data.objects[data.roomEnums[i]].common; if (common && common.members && common.members.indexOf(id) !== -1 && rooms.indexOf(data.roomEnums[i]) === -1) { rooms.push(data.roomEnums[i]); } } return rooms; } function findFunctionsForObject(data, id, withParentInfo, funcs) { if (!id) { return []; } funcs = funcs || []; for (var i = 0; i < data.funcEnums.length; i++) { var common = data.objects[data.funcEnums[i]] && data.objects[data.funcEnums[i]].common; var name = getName(common.name); if (common && common.members && common.members.indexOf(id) !== -1 && funcs.indexOf(name) === -1) { if (!withParentInfo) { funcs.push(name); } else { funcs.push({name: name, origin: id}); } } } var parts = id.split('.'); parts.pop(); id = parts.join('.'); if (data.objects[id]) findFunctionsForObject(data, id, withParentInfo, funcs); return funcs; } function findFunctionsForObjectAsIds(data, id, funcs) { if (!id) { return []; } funcs = funcs || []; for (var i = 0; i < data.funcEnums.length; i++) { var common = data.objects[data.funcEnums[i]] && data.objects[data.funcEnums[i]].common; if (common && common.members && common.members.indexOf(id) !== -1 && funcs.indexOf(data.funcEnums[i]) === -1) { funcs.push(data.funcEnums[i]); } } return funcs; } function clippyCopy(e) { var $input = $(''); $(this).append($input); $input.val($(this).parent().data('clippy')); $input.trigger('select'); document.execCommand('copy'); $input.remove(); e.preventDefault(); e.stopPropagation(); } function editValueDialog() { var data = $(this).data('data'); var $parent = $(this).parent(); var value = $parent.data('clippy'); var id = $parent.data('id'); var $dlg = $('#dialog-value-edit'); if (typeof M !== 'undefined' && $dlg.length) { $dlg.find('textarea').val(value); $dlg.find('input[type="checkbox"]').prop('checked', false); // workaround for materialize checkbox problem $dlg.find('input[type="checkbox"]+span').off('click').on('click', function () { var $input = $(this).prev(); if (!$input.prop('disabled')) { $input.prop('checked', !$input.prop('checked')).trigger('change'); } }); $dlg.find('.btn-set').off('click').on('click', function () { var val = $dlg.find('textarea').val(); var ack = $dlg.find('input[type="checkbox"]').prop('checked'); if (val !== value || ack) { data.quickEditCallback(id, 'value', val, value, ack); value = '' + value + ''; $parent.html(value); } $dlg.modal('close'); }); $dlg.modal().modal('open'); } else { $('
' + '
' + '' + _('ack') + '
') .dialog({ autoOpen: true, modal: true, title: data.texts.edit, width: '50%', height: 200, open: function (event) { $(this).find('textarea').val(value); $(this).find('input[type="checkbox"]').prop('checked', false); $(event.target).parent().find('.ui-dialog-titlebar-close .ui-button-text').html(''); }, buttons: [ { text: data.texts.select, click: function () { var val = $(this).find('textarea').val(); var ack = $(this).find('input[type="checkbox"]').prop('checked'); if (val !== value || ack) { data.quickEditCallback(id, 'value', val, value, ack); value = '' + value + ''; $parent.html(value); } $(this).dialog('close').dialog('destroy').remove(); } }, { text: data.texts.cancel, click: function () { $(this).dialog('close').dialog('destroy').remove(); } } ] }); } } function editEnumsDialog() { var data = $(this).data('data'); var $parent = $(this).parent(); var id = $parent.data('id'); var oldVal = $parent.data('old-value'); var $dlg = $('#dialog-enum-edit'); var attr = $parent.data('name'); if (typeof M !== 'undefined' && $dlg.length) { var funcs = []; var text = ''; $dlg.find('.name').html(getNameObj(data.objects[id], id)); var enums = attr === 'function' ? data.funcEnums : data.roomEnums; for (var i = 0; i < enums.length; i++) { var common = data.objects[enums[i]] && data.objects[enums[i]].common; var name = getName(common.name); if (funcs.indexOf(name) === -1) { funcs.push(name); var checked = common && common.members && common.members.indexOf(id) !== -1; text += '
  • ' + getSelectIdIcon(data, data.objects[enums[i]]) + ' ' + name + '' + enums[i] + '' + ' ' + ' ' + '
  • '; } } $dlg.find('.collection').html(text); // workaround for materialize checkbox problem $dlg.find('input[type="checkbox"]').off('click').on('click', function () { var $input = $(this).prev(); if (!$input.prop('disabled')) { $input.prop('checked', !$input.prop('checked')).trigger('change'); } }); $dlg.find('.btn-set').off('click').on('click', function () { var checks = $dlg.find('input[type="checkbox"]'); var val = []; var names = []; checks.each(function () { if ($(this).prop('checked')) { val.push($(this).data('id')); names.push($(this).data('name')) } }); if (JSON.stringify(oldVal) !== JSON.stringify(val)) { data.quickEditCallback(id, attr, val, oldVal); var value = '' + names.join(', ') + ''; $parent.html(value); } $dlg.modal('close'); }); $dlg.modal().modal('open'); } else { // todo } } function getSelectIdIcon(data, obj, key) { var icon = ''; var alt = ''; var _id_ = 'system.adapter.' + key; if (key && data.objects[_id_] && data.objects[_id_].common && data.objects[_id_].common.icon) { // if not BASE64 if (!data.objects[_id_].common.icon.match(/^data:image\//)) { if (data.objects[_id_].common.icon.indexOf('.') !== -1) { icon = '/adapter/' + data.objects[_id_].common.name + '/' + data.objects[_id_].common.icon; } else { return '' + data.objects[_id_].common.icon + ''; } } else { icon = data.objects[_id_].common.icon; } } else if (obj && obj.common) { if (obj.common.icon) { if (!obj.common.icon.match(/^data:image\//)) { if (obj.common.icon.indexOf('.') !== -1) { var instance; if (obj.type === 'instance') { icon = '/adapter/' + obj.common.name + '/' + obj.common.icon; } else if (key && key.match(/^system\.adapter\./)) { instance = key.split('.', 3); if (obj.common.icon[0] === '/') { instance[2] += obj.common.icon; } else { instance[2] += '/' + obj.common.icon; } icon = '/adapter/' + instance[2]; } else { instance = key.split('.', 2); if (obj.common.icon[0] === '/') { instance[0] += obj.common.icon; } else { instance[0] += '/' + obj.common.icon; } icon = '/adapter/' + instance[0]; } } else { return '' + obj.common.icon + ''; } } else { // base 64 image icon = obj.common.icon; } } else if (obj.type === 'device') { icon = data.imgPath + 'device.png'; alt = 'device'; } else if (obj.type === 'channel') { icon = data.imgPath + 'channel.png'; alt = 'channel'; } else if (obj.type === 'state') { icon = data.imgPath + 'state.png'; alt = 'state'; } } if (icon) { return '' + alt + ''; } else { return ''; } } function clippyShow(e) { var text; var data; if ($(this).hasClass('clippy') && !$(this).find('.clippy-button').length) { data = data || $(this).data('data'); text = ''; $(this).append(text); var $clippy = $(this).find('.clippy-button'); $clippy.on('click', clippyCopy); } if ($(this).hasClass('edit-dialog') && !$(this).find('.edit-dialog-button').length) { data = data || $(this).data('data'); text = ''; $(this).append(text); var name = $(this).data('name'); if (name === 'function' || name === 'room') { $(this).find('.edit-dialog-button').on('click', editEnumsDialog).data('data', data); } else { $(this).find('.edit-dialog-button').on('click', editValueDialog).data('data', data); } } } function clippyHide(e) { $(this).find('.clippy-button').remove(); $(this).find('.edit-dialog-button').remove(); } function installColResize(data, $dlg) { if (data.noColumnResize || !$.fn.colResizable) return; if (data.$tree.is(':visible')) { data.$tree.colResizable({ liveDrag: true, //resizeMode: 'flex', resizeMode: 'fit', minWidth: 50, partialRefresh: true, marginLeft: 5, postbackSafe: true, onResize: function (/* event */) { syncHeader($dlg); } }); syncHeader($dlg); } else { setTimeout(function () { installColResize(data, $dlg); }, 400) } } function getStates(data, id) { var states; if (data.objects[id] && data.objects[id].common && data.objects[id].common.states) { states = data.objects[id].common.states; } if (states) { if (typeof states === 'string' && states[0] === '{') { try { states = JSON.parse(states); } catch (ex) { console.error('Cannot parse states: ' + states); states = null; } } else // if old format val1:text1;val2:text2 if (typeof states === 'string') { var parts = states.split(';'); states = {}; for (var p = 0; p < parts.length; p++) { var s = parts[p].split(':'); states[s[0]] = s[1]; } } } return states; } function setFilterVal(data, field, val) { if (!field) return; data.$dlg.find('.filter[data-index="' + field + '"]').val(val).trigger('change'); } function onQuickEditField(event) { var $this = $(this); var id = $this.data('id'); var attr = $this.data('name'); var data = $this.data('selectId'); var type = $this.data('type'); var innerHTML = this.innerHTML; var $parentTR = $(event.currentTarget).parent(); // actually $parentTR === $thisParent, but I dont know var $thisParent = $this.parent(); var clippy = $thisParent.hasClass('clippy'); var editDialog = $thisParent.hasClass('edit-dialog'); var options = $this.data('options'); var oldVal = $this.data('old-value'); var states = null; //var activeNode = $(this).fancytree('getTree').getActiveNode(); if (clippy) { $thisParent.removeClass('clippy'); $thisParent.find('.clippy-button').remove(); // delete clippy buttons because they overlay the edit field } if (editDialog) { $thisParent.removeClass('edit-dialog'); $thisParent.find('.edit-dialog-button').remove(); // delete edit buttons because they overlay the edit field } $thisParent.css({overflow: 'visible'}); $this.css({overflow: 'visible'}); $this.closest('td').css({overflow: 'visible'}); $this.off('click').removeClass('select-id-quick-edit').css('position', 'relative'); type = type === 'boolean' ? 'checkbox' : 'text'; var text; var editType = type; data.editing = true; // ignore pressing of DEL button switch (attr) { case 'value': states = getStates(data, id); if (states) { text = ''; editType = 'select'; } break; case 'room': states = findRoomsForObjectAsIds (data, id) || []; text = ''; editType = 'select'; break; case 'function': states = findFunctionsForObjectAsIds (data, id) || []; text = ''; editType = 'select'; break; default: if (options) { if (typeof options === 'function') { states = options(id, attr); } else { states = options; } if (states) { text = ''; editType = 'select'; } else if (states === false) { return; } } } text = text || ''; var timeout = null; if (attr === 'room' || attr === 'function' || attr === 'role') { editType = 'select'; } var oldLeftPadding = $this.css('padding-left'); var oldWidth = $this.css('width'); var isTitleEdit = $this.is('.objects-name-coll-title'); $this.html(text + '
    ' + '
    ' + '
    ' + '
    '); $this.css({'padding-left': 2, width: isTitleEdit ? 'calc(100% - 28px)' : '100%'}); var $input = (attr === 'function' || attr === 'room' || states) ? $this.find('select') : $this.find('input'); if (attr === 'room' || attr === 'function') { $input.multiselect({ autoOpen: true, close: function () { $input.trigger('blur'); } }); } else if (attr === 'role') { // remove jquery UI - todo $input.autocomplete({ minLength: 0, source: data.roles }).on('focus', function () { $(this).autocomplete('search', ''); }); } if (editType === 'select') { if ($input.width() > $this.width() - 34) { var x = Math.max($input.width() - ($this.width() - 34), 34); $input.css({'padding-right': x}); } } function editDone(ot) { if (ot === undefined) ot = innerHTML; innerHTML = null; if (clippy) { $thisParent.addClass('clippy'); } if (editDialog) { $thisParent.addClass('edit-dialog'); } $thisParent.css({overflow: 'hidden'}); $this.css({overflow: 'hidden'}); $this.closest('td').css({overflow: 'hidden'}); $this.css({'padding-left': oldLeftPadding, width: oldWidth ? oldWidth: null}); $this.html(ot).off('click').on('click', onQuickEditField).addClass('select-id-quick-edit'); //if (activeNode && activeNode.length) activeNode.setActive(); setTimeout(function () { //$(event.currentTarget).parent().trigger('click'); // re-select the line so we can continue using the keyboard $parentTR.trigger('click'); // re-select the line so we can continue using the keyboard }, 50); data.editing = false; //var $parentTR = $(event.currentTarget).parent(); $parentTR.focus().trigger('select'); } function handleCancel(e) { if (timeout) { clearTimeout(timeout); timeout = null; } e.preventDefault(); e.stopPropagation(); editDone(); } $this .find('.select-id-quick-edit-cancel') .on('click', handleCancel).on('mousedown', function (e) { handleCancel(e); }); $this .find('.select-id-quick-edit-ok') .on('click', function () { var _$input = (attr === 'function' || attr === 'room' || states) ? $this.find('select') : $this.find('input'); _$input.trigger('blur'); }); if (type === 'checkbox') { $input.prop('checked', oldVal); } else { if (attr !== 'room' && attr !== 'function') { $input.val(oldVal); } } $input.blur(function () { if (timeout) clearTimeout(timeout); timeout = setTimeout(function () { var _oldText = $this.data('old-value'); var val = $(this).attr('type') === 'checkbox' ? $(this).prop('checked') : $(this).val(); if ((attr === 'room' || attr === 'function') && !val) { val = []; } if (attr === 'value' || JSON.stringify(val) !== JSON.stringify(_oldText)) { data.quickEditCallback(id, attr, val, _oldText); _oldText = '' + _oldText + ''; } editDone(_oldText); }.bind(this), 100); }).keyup(function (e) { if (e.which === 13) $(this).trigger('blur'); if (e.which === 27) { handleCancel(e); } }); if (typeof event === 'object') { event.preventDefault(); event.stopPropagation(); } setTimeout(function () { $input.focus().trigger('select'); }, 100); } function quality2text(q) { if (!q) return 'ok'; var custom = q & 0xFFFF0000; var text = ''; if (q & 0x40) text += 'device'; if (q & 0x80) text += 'sensor'; if (q & 0x01) text += ' bad'; if (q & 0x02) text += ' not connected'; if (q & 0x04) text += ' error'; return text + (custom ? '|0x' + (custom >> 16).toString(16).toUpperCase() : '') + ' [0x' + q.toString(16).toUpperCase() + ']'; } function forEachColumn (data, cb) { for (var c = 0; c < data.columns.length; c++) { var name = data.columns[c]; if (typeof name === 'object') { if (name.hasOwnProperty('name')) { name = name.name; } else if (name.hasOwnProperty('en')) { name = name[systemLang] || name.en; } } cb (name, c); } } function initTreeDialog($dlg) { var c; $dlg.addClass('dialog-select-object-ids'); if ($dlg.attr('id') !== 'dialog-select-member' && $dlg.attr('id') !== 'dialog-select-members') { $dlg.css({height: '100%', width: '100%'}); } else { $dlg.css({height: 'calc(100% - 110px)', width: 'calc(100% - 20px)'}); } var data = $dlg.data('selectId'); if (!data) return; //var noStates = (data.objects && !data.states); var multiselect = (!data.noDialog && !data.noMultiselect); // load expert mode flag if (typeof Storage !== 'undefined') { if (data.name) { if (data.expertModeRegEx) { data.expertMode = window.localStorage.getItem(data.name + '-expert'); data.expertMode = (data.expertMode === true || data.expertMode === 'true'); } data.sort = window.localStorage.getItem(data.name + '-sort'); data.sort = (data.sort === true || data.sort === 'true'); if (data.useValues) { data.valuesActive = window.localStorage.getItem(data.name + '-values'); data.valuesActive = (data.valuesActive === true || data.valuesActive === 'true'); } } } // switch columns if (data.useValues && data.valuesActive) { data._columns = data._columns || data.columns; data.columns = data.useValues === true ? ['ID', 'name', 'value.from', 'value.q', 'value.ts', 'value.lc', 'value.val', 'button'] : data.useValues; } else if (data._columns) { data.columns = data._columns; } if (data.columns && data.columns[0] !== 'ID') { data.columns.unshift('ID'); if (data.widths) data.widths.unshift('200px'); } removeImageFromSettings(data); // Get all states var expandeds = getExpandeds(data); getAllStates(data); if (!data.noDialog && !data.buttonsDlg) { if (typeof M !== 'undefined') { data.buttonsDlg = true; // following structure is expected // $dlg.find('.btn-set').off('click').on('click', function () { var _data = $dlg.data('selectId'); if (_data && _data.onSuccess) _data.onSuccess(_data.selectedID, _data.currentId, _data.objects[_data.selectedID]); _data.currentId = _data.selectedID; storeSettings(_data); }); $dlg.find('.btn-close').off('click').on('click', function () { var _data = $dlg.data('selectId'); storeSettings(_data); }); $dlg.modal({ dismissible: false }); } else { data.buttonsDlg = [ { id: 'button-ok', text: data.texts.select, click: function () { var _data = $dlg.data('selectId'); if (_data && _data.onSuccess) _data.onSuccess(_data.selectedID, _data.currentId, _data.objects[_data.selectedID]); _data.currentId = _data.selectedID; storeSettings(_data); $dlg.dialog('close'); } }, { text: data.texts.cancel, click: function () { var _data = $dlg.data('selectId'); storeSettings(_data); $(this).dialog('close'); } } ]; $dlg.dialog ({ autoOpen: false, modal: true, resizable: false, width: '90%', open: function (event) { $(event.target).parent().find('.ui-dialog-titlebar-close .ui-button-text').html(''); }, close: function () { storeSettings (data); }, buttons: data.buttonsDlg }); if (data.zindex !== null) { $('div[aria-describedby="' + $dlg.attr('id') + '"]').css({'z-index': data.zindex}) } } } var filter = {}; forEachColumn(data, function (name) { filter[name] = $dlg.find('.filter[data-index="' + name + '"]').val(); }); function getComboBoxEnums(kind) { var i, ret = []; switch (kind) { case 'room': for (i = 0; i < data.roomEnums.length; i++) { ret.push(getNameObj(data.objects[data.roomEnums[i]], data.roomEnums[i])); } // if (data.rooms) delete data.rooms; // if (data.roomsColored) delete data.roomsColored; return ret; case 'function': for (i = 0; i < data.funcEnums.length; i++) { ret.push(getNameObj(data.objects[data.funcEnums[i]], data.funcEnums[i])); } // if (data.funcs) delete data.funcs; // if (data.funcsColored) delete data.funcsColored; return ret; case 'role': for (var j = 0; j < data.roles.length; j++) { ret.push(data.roles[j]); } return ret; case 'type': for (var k = 0; k < data.types.length; k++) { ret.push(data.types[k]); } return ret; case 'button': ret.push([data.texts.all, '']); ret.push([data.texts.with, 'true']); ret.push([data.texts.without, 'false']); for (var h = 0; h < data.histories.length; h++) { ret.push(data.histories[h]); } return ret; } return ret; } // toolbar buttons var tds = '\n' + '\n' + '\n' + '\n' + '
    \n'; if (data.useValues) { tds += '\n'; } if (data.filter && data.filter.type === 'state' && multiselect) { tds += '
    \n' + '\n' + '\n' + '\n'; } if (data.expertModeRegEx) { tds += '
    '; } tds += ''; if (data.panelButtons) { tds += '
    \n'; for (c = 0; c < data.panelButtons.length; c++) { tds += '\n'; } } if (data.useHistory) { tds += '\n'; } if (typeof data.stats === 'object') { tds += '
    ' + '' + data.texts['Objects'] + ': ' + '' + data.stats.objs + ', ' + '' + data.texts['States'] + ': ' + '' + data.stats.states + '
    '; } var height = '100%'; if (!data.noDialog && !isMaterial) { height = Math.round(window.innerHeight * 0.6) + 'px'; } var text = '
    \n' + '
    ' + tds + '
    \n' + ' \n' ; function textFilterText(filterNo, placeholder) { if (placeholder === undefined) { placeholder = data.texts[filterNo.toLowerCase()] || ''; } return '' + '\n'; } function textCombobox(filterNo, placeholder) { var txt = ''; if (data.columns.indexOf(filterNo) !== -1) { if (placeholder === undefined) placeholder = data.texts[filterNo.toLowerCase()] || ''; var cbEntries = getComboBoxEnums(filterNo); var cbText = ''; txt = cbText + '\n'; } else { if (filterNo === 'room') { if (data.rooms) delete data.rooms; if (data.roomsColored) delete data.roomsColored; } else if (filterNo === 'function') { if (data.funcs) delete data.funcs; if (data.funcsColored) delete data.funcsColored; } } return txt; } function detectStates(node, patterns) { if (node && node.children) { var hasStates = false; var someExpanded = false; for (var c = 0; c < node.children.length; c++) { if (!node.children[c].expanded) { if (data.objects[node.children[c].data.id] && data.objects[node.children[c].data.id].type === 'state') { hasStates = true; break; } } else { someExpanded = true; } } if (hasStates) { patterns.push(node.data.id || node.key); } else if (someExpanded) { for (var cc = 0; cc < node.children.length; cc++) { if (node.children[cc].expanded) { detectStates(node.children[cc], patterns); } } } } } text += ' \n'; text += ' \n'; //'; forEachColumn(data, function (name) { text += ''; }); text += ' \n'; text += ' \n'; text += '
    '; // we may not search by value if (name === 'ID' || name === 'name' || name === 'enum') { text += textFilterText(name); } else if (name === 'type' || name === 'role' || name === 'room' || name === 'function') { text += textCombobox(name); } else if (name === 'button') { if (data.customButtonFilter) { text += textCombobox(name); } else { //if (name === 'buttons' || name === 'button') { text += ''; // } else { // text += '
    ' + _(name) + '
    '; // } } } else { text += '' + _(name) + ''; } text += '
    \n'; text += '
    \n'; text += ' \n'; text += ' \n'; var thead = '\n'; var widths = { ID: data.firstMinWidth ? data.firstMinWidth : '20%', name: '20%', type: '6%', role: '10%', room: '10%', 'function': '10%', value: '10%', button: '9%', enum: '2%', 'value.val': '10%', 'value.ts': '15%', 'value.lc': '15%', 'value.from': '10%', 'value.q': '10%', 'value.ack': '2%', }; forEachColumn(data, function (name, i) { var w = data.widths ? data.widths[i] : widths[name] || '2%'; text += ''; thead += ''; }); text += ' \n'; text += thead + '\n\n'; text += ' \n'; text += ' \n'; text += '
    \n
    \n'; if (isMaterial) { text += '
    \n' + ' \n' + '
    \n' } else { text += '\n'; } text += '
    \n' ; function addClippyToElement($elem, key, objectId) { if (!data.noCopyToClipboard) { var name = $elem.data('name'); if (name === 'function' || name === 'room') { $elem.addClass('edit-enum edit-dialog'); } else { $elem.addClass('clippy' + (objectId ? ' edit-dialog' : '')); } if (key !== undefined) { $elem.data('clippy', key); } if (objectId) { $elem.data('id', objectId); } $elem.css({position: 'relative'}) .data('data', data) .mouseenter(clippyShow) .mouseleave(clippyHide); } } if (typeof M !== 'undefined' && (!data.noDialog || data.buttonsDlg)) { var $content = $dlg.find('.dialog-content'); if (!$content.length) { $dlg.html('\n' + ' '); $content = $dlg.find('.dialog-content'); if (!$dlg.closest('.m').length) { var $body = $('body'); $body.append('
    '); $dlg.appendTo($body.find('.material-dialogs')); } } $content.html(text) } else { $dlg.html(text); } data.$tree = $dlg.find('.objects-list-table'); if (!data.$tree.length) { } data.$tree[0]._onChange = data.onSuccess || data.onChange; var foptions = { titlesTabbable: true, // Add all node titles to TAB chain quicksearch: true, /////////////////////////// //autoScroll: true, /////////////////////////////////////////// source: data.tree.children, extensions: ['table', 'gridnav', 'filter', 'themeroller'], strings: { noData: data.texts.noData }, themeroller: { addClass: '',  // no rounded corners selectedClass: 'iob-state-active' }, checkbox: multiselect, table: { //indentation: 20, indentation: 9, nodeColumnIdx: 0 }, gridnav: { autofocusInput: false, handleCursorKeys: true }, filter: { mode: 'hide', autoApply: true, counter: false }, // keydown: function (event, data){ // var KC = $.ui.keyCode; // // if( $(event.originalEvent.target).is(':input') ){ // // // When inside an input, let the control handle the keys // data.result = 'preventNav'; // // // But do the tree navigation on Ctrl + NAV_KEY // switch( event.which ){ // case KC.LEFT: // case KC.RIGHT: // case KC.BACKSPACE: // case KC.SPACE: // if( e.shiftKey ){ // data.node.navigate(event.which); // } // } // } // }, activate: function (event, data) { // A node was activated: display its title: // On change if (!multiselect) { var _data = $dlg.data('selectId'); var newId = data.node.key; if (_data.onChange) _data.onChange(newId, _data.selectedID, _data.objects[newId]); _data.selectedID = newId; if (!_data.noDialog) { // Set title of dialog box var title = _data.texts.selectid + ' - ' + getNameObj(_data.objects[newId], newId); if (typeof M !== 'undefined') { $dlg.find('.title').text(title); } else { $dlg.dialog('option', 'title', title); } // Enable/ disable 'Select' button if (_data.objects[newId]) { // && _data.objects[newId].type === 'state') { $dlg.find('#button-ok').removeClass('ui-state-disabled'); $dlg.find('.btn-set').removeClass('disabled'); } else { $dlg.find('#button-ok').addClass('ui-state-disabled'); $dlg.find('.btn-set').addClass('disabled'); } } } }, select: function (event, data) { var _data = $dlg.data('selectId'); var newIds = []; var selectedNodes = data.tree.getSelectedNodes(); for (var i = 0; i < selectedNodes.length; i++) { newIds.push(selectedNodes[i].key); } if (_data.onChange) { _data.onChange(newIds, _data.selectedID); } _data.selectedID = newIds; // Enable/ disable 'Select' button if (newIds.length > 0) { $dlg.find('#button-ok').removeClass('ui-state-disabled'); $dlg.find('.btn-set').removeClass('disabled'); } else { $dlg.find('#button-ok').addClass('ui-state-disabled'); $dlg.find('.btn-set').addClass('disabled'); } }, renderColumns: function (event, _data) { var node = _data.node; var key = node.key; var obj = data.objects[key]; var $tr = $(node.tr); var $tdList = $tr.find('>td'); var isCommon = obj && obj.common; var $firstTD = $tdList.eq(0); $firstTD.css({'overflow': 'hidden'}); var cnt = countChildren(key, data); if (isCommon && obj.type) { $tr.addClass('fancytree-type-' + obj.type + (data.draggable && data.draggable.indexOf(obj.type) !== -1 ? ' fancytree-type-draggable' : ' fancytree-type-not-draggable')); if (data.draggable && data.draggable.indexOf(obj.type) === -1) { $tr.attr('data-nodrag', 'true'); } } else { $tr.attr('data-nodrag', 'true'); } $tr.attr('data-id', key); // Show number of all children as small grey number var $cnt = $firstTD.find('.select-id-cnt'); // If node has some children if (cnt) { if ($cnt.length) { // modify it if span yet exists $cnt.text('#' + cnt); } else { // create new span $firstTD.append('#' + cnt + ''); } } else { // remove this span, because object may be was updated $cnt.remove(); } var base = 0; // hide checkbox if only states should be selected if (data.filter && data.filter.type === 'state' && (!obj || obj.type !== 'state')) { $firstTD.find('.fancytree-checkbox').hide(); } // special case for javascript scripts if (obj && (key.match(/^script\.js\./) || key.match(/^enum\.[\w\d_-]+$/))) { if (obj.type !== 'script') { // force folder icon and change color if (node.key !== 'script.js.global') { $firstTD.find('.fancytree-title').css({'font-weight': 'bold', color: '#000080'}); } else { $firstTD.find('.fancytree-title').css({'font-weight': 'bold', color: '#078a0c'}); } $firstTD.addClass('fancytree-force-folder'); } } if (!data.noCopyToClipboard) { addClippyToElement($firstTD, node.key); } if (data.useNameAsId) { $firstTD.find('.fancytree-title').html(getNameObj(obj, key)); } var $elem; var val; for (var c = 0; c < data.columns.length; c++) { var name = data.columns[c]; $elem = $tdList.eq(base); var setText = function (txt) { $elem.html(span(txt)); }; if (typeof name === 'object') { if (name.hasOwnProperty('name')) { name = name.name; } else { name = name[systemLang] || name.en; } } switch (name) { case 'id': case 'ID': break; case 'name': var icon = getSelectIdIcon(data, obj, key); var t = isCommon ? getName(obj.common.name || '') : ''; $elem.html('' + (icon ? '' + icon + '' : '') + '
    ' + t + '
    ' + '
    '); var $e = $elem.find('.objects-name-coll-title'); if (!t) $e.css({'vertical-align': 'middle'}); $e.attr('title', t); // why here was the obj commented ?? if (data.quickEdit && obj && data.quickEdit.indexOf('name') !== -1) { $e.data('old-value', t); $e.on('click', onQuickEditField).data('id', node.key).data('name', 'name').data('selectId', data).addClass('select-id-quick-edit'); } break; case 'type': setText(obj ? obj.type || '' : ''); break; case 'role': val = isCommon ? obj.common.role || '' : ''; setText(val); if (data.quickEdit && obj && data.quickEdit.indexOf('role') !== -1) { $elem.data('old-value', val); $elem.on('click', onQuickEditField).data('id', node.key).data('name', 'role').data('selectId', data).addClass('select-id-quick-edit'); } break; case 'room': // Try to find room if (data.roomsColored) { var room = data.roomsColored[node.key]; if (!room) room = data.roomsColored[node.key] = findRoomsForObject(data, node.key, true); val = room.map(function (e) { return getName(e.name); }).join(', '); if (room.length && room[0].origin !== node.key) { $elem.css({color: 'gray'}).attr('title', room[0].origin); } else { $elem.css({color: 'inherit'}).attr('title', null); } } else { val = ''; } setText(val); if (data.quickEdit && obj && data.quickEdit.indexOf('room') !== -1) { $elem .data('old-value', val) .data('id', node.key) .data('name', 'room') .data('selectId', data); addClippyToElement($elem, val, obj && data.quickEditCallback ? key : undefined); } break; case 'function': // Try to find function if (data.funcsColored) { if (!data.funcsColored[node.key]) data.funcsColored[node.key] = findFunctionsForObject(data, node.key, true); val = data.funcsColored[node.key].map(function (e) { return getName(e.name); }).join (', '); if (data.funcsColored[node.key].length && data.funcsColored[node.key][0].origin !== node.key) { $elem.css({color: 'gray'}).attr('title', data.funcsColored[node.key][0].origin); } else { $elem.css({color: 'inherit'}).attr('title', null); } } else { val = ''; } setText(val); if (data.quickEdit && obj && data.quickEdit.indexOf('function') !== -1) { $elem .data('old-value', val) .data('id', node.key) .data('name', 'function') .data('selectId', data); addClippyToElement($elem, val, obj && data.quickEditCallback ? key : undefined); } break; case 'value.ts': if (data.states && obj && obj.type === 'state') { var state = data.states[node.key]; state = state || {ts: data.states[node.key + '.ts']}; var val = state.ts; if (val === undefined) { val = ' '; } else { val = val ? formatDate(new Date(val)) : ''; } $elem.html('' + val + ''); } else { $elem.text(''); } break; case 'value.lc': if (data.states && obj && obj.type === 'state') { var state = data.states[node.key] || {}; state = state || {lc: data.states[node.key + '.lc']}; var val = state.lc; if (val === undefined) { val = ' '; } else { val = val ? formatDate(new Date(val)) : ''; } $elem.html('' + val + ''); } else { $elem.text(''); } break; case 'value.from': if (data.states && obj && obj.type === 'state') { var state = data.states[node.key] || {}; state = state || {from: data.states[node.key + '.from'], user: data.states[node.key + '.user']}; var val = state.from; if (val === undefined) { val = ' '; } else { val = val ? val.replace(/^system\.adapter\.|^system\./, '') : ''; } if (state.user) { val += '/' + state.user.replace('system.user.', ''); } $elem.html('' + val + ''); } else { $elem.text(''); } break; case 'value.q': if (data.states && obj && obj.type === 'state') { var state = data.states[node.key] || {}; state = state || {q: data.states[node.key + '.q']}; var q = state.q; var val = q; if (val === undefined) { val = ' '; } else { val = quality2text(val); } $elem.html('' + val + ''); } else { $elem.text(''); } break; case 'value.val': if (data.states && obj && obj.type === 'state') { var state = data.states[node.key] || {}; if (!state) { state = { val: data.states[node.key + '.val'], ack: (data.states[node.key + '.ack'] === undefined) ? '' : data.states[node.key + '.ack'] }; } var val = state.val; var ack = state.ack; var states = getStates(data, node.key); if (isCommon && isCommon.role && isCommon.role.match(/^value\.time|^date/)) { val = val ? (new Date(val)).toString() : val; } if (states && states[val] !== undefined) { val = states[val] + '(' + val + ')'; } if (val === undefined) { val = ' '; } else if (val === null || val === '') { val = ' '; } if (typeof val === 'string' && val !== '$nbsp;') { val = val.replace(/&/g,'&').replace(//g,'>'); } $elem.html('' + val + ''); if (obj && obj.type === 'state' && isCommon && isCommon.type !== 'file') { addClippyToElement($elem, state.val === undefined || state.val === null ? '' : state.val.toString(), obj && data.quickEditCallback && obj.type === 'state' && (data.expertMode || isCommon.write !== false) ? key : undefined); } } else { $elem.text('') .removeClass('clippy'); } $elem.dblclick (function (e) { e.preventDefault (); }); if (data.quickEdit && isCommon && obj.type === 'state' && data.quickEdit.indexOf('value') !== -1 && (data.expertMode || isCommon.write !== false) ) { if (isCommon.role && isCommon.role.match(/^button/) && !data.expertMode) { $elem.html(''); } else if (!isCommon || isCommon.type !== 'file') { var val_ = data.states[node.key]; val_ = val_ ? val_.val : ''; var $span_ = $elem.find('span'); $span_.data('old-value', val_).data('type', isCommon.type || typeof val_); $span_.on('click', onQuickEditField) .data('id', node.key) .data('name', 'value') .data('selectId', data) .addClass('select-id-quick-edit'); } var $btnPush = $tr.find('.select-button-push[data-id="' + node.key + '"]'); $btnPush.on('click', function () { var id = $(this).data('id'); data.quickEditCallback(id, 'value', true); }).attr('title', data.texts.push); if (!isMaterial) { $btnPush.button({ text: false, icons: { primary: 'ui-icon-arrowthickstop-1-s' } }); } else { $btnPush.prepend('room_service') } } if (isCommon && isCommon.type === 'file') { data.webServer = data.webServer || (window.location.protocol + '//' + window.location.hostname + ':8082'); // link $elem.html('' + data.webServer + '/state/' + node.key + '') .attr('title', data.texts.linkToFile); } break; case 'value': var state; if (data.states && obj && obj.type === 'state') { state = data.states[node.key]; var states = getStates(data, node.key); if (!state) { state = { val: data.states[node.key + '.val'], ts: data.states[node.key + '.ts'], lc: data.states[node.key + '.lc'], from: data.states[node.key + '.from'], user: data.states[node.key + '.user'], ack: (data.states[node.key + '.ack'] === undefined) ? '' : data.states[node.key + '.ack'], q: (data.states[node.key + '.q'] === undefined) ? 0 : data.states[node.key + '.q'] }; } else { state = Object.assign({}, state); } if (isCommon && isCommon.role && isCommon.role.match(/^value\.time|^date/)) { state.val = state.val ? (new Date(state.val)).toString() : state.val; } var originalVal = state.val; if (states && states[state.val] !== undefined) { state.val = states[state.val] + '(' + state.val + ')'; } var fullVal; if (state.val === undefined) { state.val = ' '; } else { // if less 2000.01.01 00:00:00 if (state.ts < 946681200000) state.ts *= 1000; if (state.lc < 946681200000) state.lc *= 1000; if (isCommon && isCommon.unit) state.val += ' ' + isCommon.unit; fullVal = data.texts.value + ': ' + state.val; fullVal += '\x0A' + data.texts.ack + ': ' + state.ack; fullVal += '\x0A' + data.texts.ts + ': ' + (state.ts ? formatDate(new Date(state.ts)) : ''); fullVal += '\x0A' + data.texts.lc + ': ' + (state.lc ? formatDate(new Date(state.lc)) : ''); fullVal += '\x0A' + data.texts.from + ': ' + (state.from || ''); if (state.user) fullVal += '\x0A' + data.texts.user + ': ' + (state.user || ''); fullVal += '\x0A' + data.texts.quality + ': ' + quality2text(state.q || 0); } if (state.val === null || state.val === '') { state.val = ' '; } if (typeof state.val === 'string' && state.val !== ' ') { state.val = state.val.replace(/&/g,'&').replace(//g,'>'); } $elem.html('' + state.val + '') .attr('title', fullVal); var $span = $elem.find('span'); $span.css({color: state.ack ? (state.q ? 'orange' : '') : '#c00000'}); if (obj && obj.type === 'state' && isCommon && isCommon.type !== 'file') { addClippyToElement($elem, originalVal === undefined || originalVal === null ? '' : originalVal.toString(), obj && data.quickEditCallback && obj.type === 'state' && (data.expertMode || isCommon.write !== false) ? key : undefined); } } else { $elem .text('') .attr('title', '') .removeClass('clippy'); } $elem.dblclick (function (e) { e.preventDefault (); }); if (data.quickEdit && isCommon && obj.type === 'state' && data.quickEdit.indexOf('value') !== -1 && (data.expertMode || isCommon.write !== false) ) { if (isCommon.role && isCommon.role.match(/^button/) && !data.expertMode) { $elem.html(''); } else if (!isCommon || isCommon.type !== 'file') { var val_ = data.states[node.key]; val_ = val_ ? val_.val : ''; var $span_ = $elem.find('span'); $span_.data('old-value', val_).data('type', isCommon.type || typeof val_); $span_.on('click', onQuickEditField) .data('id', node.key) .data('name', 'value') .data('selectId', data) .addClass('select-id-quick-edit'); } var $btnPush = $tr.find('.select-button-push[data-id="' + node.key + '"]'); $btnPush.on('click', function () { var id = $(this).data('id'); data.quickEditCallback(id, 'value', true); }).attr('title', data.texts.push); if (!isMaterial) { $btnPush.button({ text: false, icons: { primary: 'ui-icon-arrowthickstop-1-s' } }); } else { $btnPush.prepend('room_service') } } if (isCommon && isCommon.type === 'file') { data.webServer = data.webServer || (window.location.protocol + '//' + window.location.hostname + ':8082'); // link $elem.html('' + data.webServer + '/state/' + node.key + '') .attr('title', data.texts.linkToFile); } break; case 'button': // Show buttons var text; if (data.buttons) { if (obj || data.showButtonsForNotExistingObjects) { text = ''; if (data.editEnd) { text += '' + '' + '' + ''; } for (var j = 0; j < data.buttons.length; j++) { text += ''; } setText(text); for (var p = 0; p < data.buttons.length; p++) { var $btn = $tr.find('.select-button-' + p + '[data-id="' + node.key + '"]'); if ($btn.length === 0) continue; $btn .on('click', function () { var cb = $(this).data('callback'); if (cb) cb.call($(this), $(this).data('id')); }) .data('callback', data.buttons[p].click) .attr('title', data.buttons[p].title || ''); if (data.buttons[p].match) data.buttons[p].match.call($btn, node.key); if (!isMaterial) { // if (data.buttons[p].width) $btn.css({width: data.buttons[p].width}); // if (data.buttons[p].height) $btn.css({height: data.buttons[p].height}); $btn.button(data.buttons[p]); } else { $btn.addClass('custom-obj-btn').prepend('' + data.buttons[p]['material-icon'] + ''); } } } else { $elem.text(''); } } else if (data.editEnd) { text = '' + '' + ''; } if (data.editEnd) { var $btnEdit = $tr.find('.select-button-edit[data-id="' + node.key + '"]'); $btnEdit .on('click', function () { $(this).data('node').editStart(); }) .attr('title', data.texts.edit) .data('node', node); if (!isMaterial) { $btnEdit.button({ text: false, icons: { primary: 'ui-icon-pencil' } }); } else { $btnEdit.prepend('edit'); } $btnEdit = $tr.find('.select-button-ok[data-id="' + node.key + '"]'); $btnEdit.on('click', function () { var node = $(this).data('node'); node.editFinished = true; node.editEnd (true); }).attr('title', data.texts.ok).data('node', node).hide(); if (!isMaterial) { $btnEdit.button({ text: false, icons: { primary: 'ui-icon-check' } }); } else { $btnEdit.prepend('done'); } $btnEdit = $tr.find('.select-button-cancel[data-id="' + node.key + '"]'); $btnEdit.on('click', function () { var node = $(this).data('node'); node.editFinished = true; node.editEnd (false); }).attr('title', data.texts.cancel).data('node', node).hide(); if (!isMaterial) { $btnEdit.button({ text: false, icons: { primary: 'ui-icon-close' } }); } else { $btnEdit.prepend('close'); } } break; case 'enum': if (isCommon && obj.common.members && obj.common.members.length > 0) { var te; if (obj.common.members.length < 4) { te = '#' + obj.common.members.length + ' ' + obj.common.members.join (', '); } else { te = '#' + obj.common.members.length; } $elem.html('
    ' + te + '
    '); $elem.attr('title', obj.common.members.join ('\x0A')); } else { $elem.text(''); $elem.attr('title', ''); } break; default: if (typeof data.columns[c].data === 'function') { //$elem = $tdList.eq(base); var val = data.columns[c].data(node.key, data.columns[c].name); var title = ''; if (data.columns[c].title) { title = data.columns[c].title(node.key, data.columns[c].name); } $elem.html(val).attr('title', title); if (data.quickEdit && obj) { for (var q = 0; q < data.quickEdit.length; q++) { if (data.quickEdit[q] === data.columns[c].name || data.quickEdit[q].name === data.columns[c].name) { $elem.data('old-value', val).data('type', typeof val); $elem.on('click', onQuickEditField) .data('id', node.key) .data('name', data.columns[c].name) .data('selectId', data) .data('options', data.quickEdit[q].options) .addClass('select-id-quick-edit'); break; } } } } break; } /// base++; /// } }, dblclick: function (event, _data) { if (data.buttonsDlg && !data.quickEditCallback) { if (_data && _data.node && !_data.node.folder) { if (typeof M !== 'undefined') { $dlg.find('.btn-set').trigger('click'); $dlg.modal('close'); } else { $('#button-ok').trigger('click'); } } } else if (data.dblclick) { var tree = data.$tree.fancytree('getTree'); var node = tree.getActiveNode(); if (node) { data.dblclick(node.key); } } }, beforeExpand: function (event, _data) { if (data.expandedCallback) { if (_data && _data.node) { // if will be expanded if (!_data.node.expanded) { var childrenCount = 0; var hasStates = false; var patterns = []; if (_data.node.children) { childrenCount = _data.node.children.length; detectStates(_data.node, patterns); if (patterns.length) hasStates = true; } if (!patterns.length) { patterns.push(_data.node.key); } data.expandedCallback(patterns, childrenCount, hasStates); } else { data.collapsedCallback(_data.node.key); } } } }, collapse: function (event, _data) { /*if (data.collapsedCallback) { if (_data && _data.node) { data.collapsedCallback(_data.node.key); } }*/ } }; if (data.editEnd) { foptions.extensions.push('edit'); foptions.edit = { triggerStart: ['f2', 'dblclick', 'shift+click', 'mac+enter'], triggerStop: ['esc'], beforeEdit: function (event, _data) { // Return false to prevent edit mode if (!data.objects[_data.node.key]) return false; }, edit: function (event, _data) { $dlg.find('.select-button-edit[data-id="' + _data.node.key + '"]').hide(); $dlg.find('.select-button-cancel[data-id="' + _data.node.key + '"]').show(); $dlg.find('.select-button-ok[data-id="' + _data.node.key + '"]').show(); $dlg.find('.select-button-custom[data-id="' + _data.node.key + '"]').hide(); var node = _data.node; var $tdList = $(node.tr).find('>td'); // Editor was opened (available as data.input) var inputs = {id: _data.input}; forEachColumn(data, function (name, c) { if (name === 'name') { inputs[name] = $(''); $tdList.eq(c).html(inputs[name]); } }); for (var i in inputs) { inputs[i].keyup(function (e) { var node; if (e.which === 13) { // end edit node = $(this).data('node'); node.editFinished = true; node.editEnd(true); } else if (e.which === 27) { // end edit node = $(this).data('node'); node.editFinished = true; node.editEnd(false); } }).data('node', node); } if (data.editStart) data.editStart(_data.node.key, inputs); node.editFinished = false; }, beforeClose: function (event, _data) { // Return false to prevent cancel/save (data.input is available) return _data.node.editFinished; }, save: function (event, _data) { var editValues = {id: _data.input.val()}; forEachColumn (data, function (name) { if (name === 'name') { editValues[name] = $dlg.find('.select-edit[data-name="' + name + '"]').val(); } }); // Save data.input.val() or return false to keep editor open if (data.editEnd) data.editEnd(_data.node.key, editValues); _data.node.render(true); // We return true, so ext-edit will set the current user input // as title return true; }, close: function (event, _data) { $dlg.find('.select-button-edit[data-id="' + _data.node.key + '"]').show(); $dlg.find('.select-button-cancel[data-id="' + _data.node.key + '"]').hide(); $dlg.find('.select-button-ok[data-id="' + _data.node.key + '"]').hide(); $dlg.find('.select-button-custom[data-id="' + _data.node.key + '"]').show(); if (_data.node.editFinished !== undefined) delete _data.node.editFinished; // Editor was removed if (data.save) { // Since we started an async request, mark the node as preliminary $(data.node.span).addClass('pending'); } } }; } data.$tree.fancytree(foptions).on('nodeCommand', function (event, bData) { // Custom event handler that is triggered by keydown-handler and // context menu: var refNode; var tree = $(this).fancytree('getTree'); var node = tree.getActiveNode(); switch (bData.cmd) { case 'moveUp': node.moveTo(node.getPrevSibling(), 'before'); node.setActive(); break; case 'moveDown': node.moveTo(node.getNextSibling(), 'after'); node.setActive(); break; case 'indent': refNode = node.getPrevSibling(); node.moveTo(refNode, 'child'); refNode.setExpanded(); node.setActive(); break; case 'outdent': node.moveTo(node.getParent(), 'after'); node.setActive(); break; case 'delete': var button = $(node.tr).find('.select-button-1'); if (button && button.length) { button.trigger('click'); } break; case 'rename': var e = $(node.tr).find('.objects-name-coll-title'); if (e && e.length) { e.trigger('click'); } break; /*case 'copy': CLIPBOARD = { mode: data.cmd, data: node.toDict(function (n) { delete n.key; }) }; break; case 'clear': CLIPBOARD = null; break;*/ default: alert('Unhandled command: ' + bData.cmd); return; } }).on('keydown', function (e) { if (data.editing) { return; } var cmd = null; if (e.ctrlKey) { switch (e.which) { case 'c': cmd = 'copy'; break; case $.ui.keyCode.UP: cmd = 'moveUp'; break; case $.ui.keyCode.DOWN: cmd = 'moveDown'; break; case $.ui.keyCode.RIGHT: cmd = 'indent'; break; case $.ui.keyCode.LEFT: cmd = 'outdent'; break; } } else { switch (e.which) { case $.ui.keyCode.DELETE: cmd = 'delete'; break; case 113: // F2 cmd = 'rename'; break; } } if (cmd) { $(this).trigger('nodeCommand', {cmd: cmd}); return false; } }); function customFilter(node) { if (node.parent && node.parent.match) return true; // Read all filter settings if (data.filterVals === null) { data.filterVals = {length: 0}; var value_; forEachColumn (data, function (name) { //if (name === 'image') return; value_ = $dlg.find('.filter[data-index="' + name + '"]').val(); if (name !== 'role' && name !== 'type' && name !== 'room' && name !== 'function' && value_) { value_ = value_.toLowerCase(); } if (value_) { data.filterVals[name] = value_; data.filterVals.length++; } }); // if no clear "close" event => store on change if (data.noDialog) storeSettings(data); } var obj = data.objects[node.key]; var isCommon = obj && obj.common; var value; for (var f in data.filterVals) { //if (f === 'length') continue; //if (isCommon === null) isCommon = obj && obj.common; if (!data.filterVals.hasOwnProperty(f)) continue; switch (f) { case 'length': continue; case 'ID': if (node.key.toLowerCase ().indexOf(data.filterVals[f]) === -1) return false; break; case 'name': case 'enum': if (!isCommon || obj.common[f] === undefined) return false; value = obj.common[f]; if (typeof value === 'object') { value = value[systemLang] || value.en || ''; } if ((value || '').toLowerCase().indexOf(data.filterVals[f]) === -1) { return false; } break; case 'role': if (data.roleExactly) { if (!isCommon || obj.common[f] === undefined || obj.common[f] !== data.filterVals[f]) return false; } else { if (!isCommon || obj.common[f] === undefined || obj.common[f].indexOf(data.filterVals[f]) === -1) return false; } break; case 'type': if (!obj || obj[f] === undefined || obj[f] !== data.filterVals[f]) return false; break; /*case 'value': if (!data.states[node.key] || data.states[node.key].val === undefined || data.states[node.key].val === null || data.states[node.key].val.toString ().toLowerCase ().indexOf(data.filterVals[f]) === -1) return false; break;*/ case 'button': if (data.filterVals[f] === 'true') { if (!isCommon || !obj.common.custom || obj.common.custom.enabled === false) return false; } else if (data.filterVals[f] === 'false') { if (!isCommon || obj.type !== 'state' || obj.common.custom) return false; } else if (data.filterVals[f]) { if (!isCommon || !obj.common.custom || !obj.common.custom[data.filterVals[f]]) return false; } break; case 'room': if (!obj || !data.rooms) return false; // Try to find room if (!data.rooms[node.key]) data.rooms[node.key] = findRoomsForObject(data, node.key); if (data.rooms[node.key].indexOf(data.filterVals[f]) === -1) return false; break; case 'function': if (!obj || !data.funcs) return false; // Try to find functions if (!data.funcs[node.key]) data.funcs[node.key] = findFunctionsForObject(data, node.key); if (data.funcs[node.key].indexOf(data.filterVals[f]) === -1) return false; break; } } return true; } restoreExpandeds(data, expandeds); var resizeTimer; $(window).on('resize', function (/* x, y */) { if (resizeTimer) clearTimeout(resizeTimer); resizeTimer = setTimeout(function () { syncHeader($dlg); }, 100); }); $dlg.trigger('resize'); var changeTimer; $dlg.find('.filter').change(function (event) { data.filterVals = null; if (changeTimer) clearTimeout(changeTimer); //changeTimer = setTimeout(function () { if (event && event.target) { filterChanged(event.target); } var $ee = $dlg.find('.objects-list-running'); $ee.show(); data.$tree.fancytree('getTree').filterNodes(customFilter, false); $ee.hide(); //}, 0); }).keyup(function () { var tree = data.$tree[0]; if (tree._timer) tree._timer = clearTimeout(tree._timer); var that = this; tree._timer = setTimeout(function () { $(that).trigger('change'); }, 200); }); var $btn = $dlg.find('.filter-btn'); $btn.on('click', function () { $dlg.find('.filter[data-index="' + $(this).data('index') + '"]').val('').trigger('change'); //filter buttons action }); if (!isMaterial) { $btn.button({icons: {primary: 'ui-icon-close'}, text: false}); } else { $btn.prepend('close'); } $btn = $dlg.find('.btn-collapse'); $btn.on('click', function () { $dlg.find('.objects-list-running').show(); setTimeout(function () { data.$tree.fancytree('getRootNode').visit(function (node) { if (!data.filterVals.length || node.match || node.subMatchCount) { node.setExpanded(false); } }); $dlg.find('.objects-list-running').hide(); }, 100); }).attr('title', data.texts.collapse); if (!isMaterial) { $btn.button({icons: {primary: 'ui-icon-folder-collapsed'}, text: false}); } else { $btn.prepend('folder'); } $btn = $dlg.find('.btn-expand'); $btn.on('click', function () { $dlg.find('.objects-list-running').show(); setTimeout(function () { data.$tree.fancytree('getRootNode').visit(function (node) { if (!data.filterVals.length || node.match || node.subMatchCount) { node.setExpanded(true); } }); $dlg.find('.objects-list-running').hide(); }, 100); }).attr('title', data.texts.expand); if (!isMaterial) { $btn.button({icons: {primary: 'ui-icon-folder-open'}, text: false}); } else { $btn.prepend('folder_open'); } $btn = $dlg.find('.btn-list'); $btn.on('click', function () { $dlg.find('.objects-list-running').show(); data.list = !data.list; if (data.list) { $dlg.find('.btn-list').addClass('ui-state-error red lighten-3'); $dlg.find('.btn-collapse').hide(); $(this).attr('title', data.texts.list); } else { $dlg.find('.btn-list').removeClass('ui-state-error red lighten-3'); $dlg.find('.btn-expand').show(); $dlg.find('.btn-collapse').show(); $(this).attr('title', data.texts.tree); } $dlg.find('.objects-list-running').show(); setTimeout(function () { data.inited = false; initTreeDialog(data.$dlg); $dlg.find('.objects-list-running').hide(); }, 200); }).attr('title', data.texts.tree); if (!isMaterial) { $btn.button({icons: {primary: 'ui-icon-grip-dotted-horizontal'}, text: false}); } else { $btn.prepend('format_list_bulleted'); } if (data.list) { $dlg.find('.btn-list') .addClass('ui-state-error red lighten-3') .attr('title', data.texts.list); $dlg.find('.btn-expand').hide(); $dlg.find('.btn-collapse').hide(); } $btn = $dlg.find('.btn-refresh'); $btn.on('click', function () { $dlg.find('.objects-list-running').show(); setTimeout(function () { data.inited = false; // request all objects anew on refresh if (data.getObjects) { data.getObjects(function (err, objs) { data.objects = objs; initTreeDialog(data.$dlg, false); $dlg.find('.objects-list-running').hide(); }); } else { initTreeDialog(data.$dlg, false); $dlg.find('.objects-list-running').hide(); } }, 100); }).attr('title', data.texts.refresh); if (!isMaterial) { $btn.button({icons: {primary: 'ui-icon-refresh'}, text: false}); } else { $btn.prepend('refresh'); } $btn = $dlg.find('.btn-sort'); $btn.on('click', function () { $dlg.find('.objects-list-running').show(); data.sort = !data.sort; if (data.sort) { $dlg.find('.btn-sort').addClass('ui-state-error red lighten-3'); } else { $dlg.find('.btn-sort').removeClass('ui-state-error red lighten-3'); } storeSettings(data, true); setTimeout(function () { data.inited = false; sortTree(data); $dlg.find('.objects-list-running').hide(); }, 100); }).attr('title', data.texts.sort); if (data.sort) $dlg.find('.btn-sort').addClass('ui-state-error red lighten-3'); if (!isMaterial) { $btn.button({icons: {primary: 'ui-icon-bookmark'}, text: false}); } else { $btn.prepend('sort_by_alpha'); } $btn = $dlg.find('.btn-history'); $btn.on('click', function () { $dlg.find('.objects-list-running').show(); setTimeout(function () { data.customButtonFilter.callback(); $dlg.find('.objects-list-running').hide(); }, 1); }).attr('title', data.texts.history); if (!isMaterial) { $btn.button({icons: {primary: 'ui-icon-gear'}, text: false}); } else { $btn.prepend('build'); } $btn = $dlg.find('.btn-select-all'); $btn.on('click', function () { $dlg.find('.objects-list-running').show(); setTimeout(function () { data.$tree.fancytree('getRootNode').visit(function (node) { if (!data.filterVals.length || node.match || node.subMatchCount) { // hide checkbox if only states should be selected if (data.objects[node.key] && data.objects[node.key].type === 'state') { node.setSelected(true); } } }); $dlg.find('.objects-list-running').hide(); }, 100); }).attr('title', data.texts.selectAll); if (!isMaterial) { $btn.button({icons: {primary: 'ui-icon-circle-check'}, text: false}); } else { $btn.prepend('playlist_add_check'); } if (data.expertModeRegEx) { $btn = $dlg.find('.btn-expert'); $btn.on('click', function () { $dlg.find('.objects-list-running').show(); data.expertMode = !data.expertMode; if (data.expertMode) { $dlg.find('.btn-expert').addClass('ui-state-error red lighten-3'); } else { $dlg.find('.btn-expert').removeClass('ui-state-error red lighten-3'); } storeSettings(data, true); setTimeout(function () { data.inited = false; initTreeDialog(data.$dlg); $dlg.find('.objects-list-running').hide(); }, 200); }).attr('title', data.texts.expertMode); if (data.expertMode) $dlg.find('.btn-expert').addClass('ui-state-error red lighten-3'); if (!isMaterial) { $btn.button({icons: {primary: 'ui-icon-person'}, text: false}); } else { $btn.prepend('assignment_ind'); } } $btn = $dlg.find('.btn-unselectall'); $btn.on('click', function () { $dlg.find('.objects-list-running').show(); setTimeout(function () { data.$tree.fancytree('getRootNode').visit(function (node) { node.setSelected(false); }); $dlg.find('.objects-list-running').hide(); }, 100); }).attr('title', data.texts.unselectAll); if (!isMaterial) { $btn.button({icons: {primary: 'ui-icon-circle-close'}, text: false}); } else { $btn.prepend('cancel');// todo } $btn = $dlg.find('.btn-invert-selection'); $btn.on('click', function () { $dlg.find('.objects-list-running').show(); setTimeout(function () { data.$tree.fancytree('getRootNode').visit(function (node) { if (!data.filterVals.length || node.match || node.subMatchCount){ if (data.objects[node.key] && data.objects[node.key].type === 'state') { node.toggleSelected(); } } }); $dlg.find('.objects-list-running').hide(); }, 100); }).attr('title', data.texts.invertSelection); if (!isMaterial) { $btn.button({icons: {primary: 'ui-icon-transferthick-e-w'}, text: false}); } else { $btn.prepend('invert_colors'); // todo } if (data.useValues) { $btn = $dlg.find('.btn-values'); $btn.on('click', function () { data.valuesActive = !data.valuesActive; updateValuesButton(data); window.localStorage && window.localStorage.setItem(data.name + '-values', data.valuesActive ? 'true' : 'false'); $dlg.find('.objects-list-running').show(); setTimeout(function () { data.inited = false; initTreeDialog(data.$dlg); $dlg.find('.objects-list-running').hide(); }, 100); }).attr('title', data.texts.toggleValues); updateValuesButton(data); } if (!isMaterial) { $btn.button({icons: {primary: 'ui-icon-tag'}, text: false}); } else { $btn.prepend('looks_one'); } for (var f in filter) { try { if (f) setFilterVal(data, f, filter[f]); } catch (err) { console.error('Cannot apply filter: ' + err) } } if (data.panelButtons) { for (var z = 0; z < data.panelButtons.length; z++) { $btn = $dlg.find('.btn-custom-' + z); $btn.attr('title', data.panelButtons[z].title || ''); $btn.on('click', data.panelButtons[z].click); if (!isMaterial) { $btn.button(data.panelButtons[z]); } else { $btn.addClass('custom-toolbar-btn').prepend('' + data.panelButtons[z]['material-icon'] + ''); } } } /*if (data.useHistory) { $dlg.find('.filter_button_' + data.instance + '_btn') .button(data.customButtonFilter) .on('click', data.customButtonFilter.callback); }*/ showActive($dlg); installColResize(data, $dlg); loadSettings(data); if ($dlg.attr('id') !== 'dialog-select-member' && $dlg.attr('id') !== 'dialog-select-members') { setTimeout(function () { $dlg.css({height: '100%'}); //xxx }, 500); } else if ($dlg.attr('id') === 'dialog-select-members') { //$dlg.find('div:first-child').css({height: 'calc(100% - 50px)'}); } // set preset filters for (var field in data.filterPresets) { if (!data.filterPresets.hasOwnProperty(field) || !data.filterPresets[field]) continue; if (typeof data.filterPresets[field] === 'object') { setFilterVal(data, field, data.filterPresets[field][0]); } else { setFilterVal(data, field, data.filterPresets[field]); } } sortTree(data); } function storeSettings(data, force) { if (typeof Storage === 'undefined' || !data.name) return; if (data.timer) clearTimeout(data.timer); if (force) { window.localStorage.setItem(data.name + '-filter', JSON.stringify(data.filterVals)); window.localStorage.setItem(data.name + '-expert', JSON.stringify(data.expertMode)); window.localStorage.setItem(data.name + '-sort', JSON.stringify(data.sort)); window.localStorage.setItem(data.name + '-values', data.valuesActive ? 'true' : 'false'); data.timer = null; } else { data.timer = setTimeout(function () { window.localStorage.setItem(data.name + '-filter', JSON.stringify(data.filterVals)); window.localStorage.setItem(data.name + '-expert', JSON.stringify(data.expertMode)); window.localStorage.setItem(data.name + '-sort', JSON.stringify(data.sort)); window.localStorage.setItem(data.name + '-values', data.valuesActive ? 'true' : 'false'); }, 500); } } function loadSettings(data) { if (typeof Storage !== 'undefined' && data.name) { var f = window.localStorage.getItem(data.name + '-filter'); if (f) { try{ f = JSON.parse(f); removeImageFromSettings(f); //setTimeout(function () { for (var field in f) { if (!f.hasOwnProperty(field) || field === 'length') continue; if (data.filterPresets[field]) continue; setFilterVal(data, field, f[field]); } //}, 0); } catch (e) { console.error('Cannot parse settings: ' + e); } } else if (!data.filter) { // set default filter: state setFilterVal(data, 'type', 'state'); } } } function updateValuesButton(data) { if (data.valuesActive) { data.$dlg.find('.btn-values').addClass('ui-state-error red lighten-3'); } else { data.$dlg.find('.btn-values').removeClass('ui-state-error red lighten-3'); } } function countChildren(id, data) { var pos = data.ids.indexOf(id); var len = data.ids.length; var cnt = 0; if (id.indexOf('.') === -1 || ( data.objects[id] && (data.objects[id].type === 'state' || data.objects[id].type === 'adapter'))) { return cnt; } if (pos === -1) { pos = 0; while (pos < len && data.ids[pos] < id) { pos++; } pos--; } if (pos !== -1) { pos++; while (pos < len && data.ids[pos].startsWith(id + '.')) { pos++; cnt++; } } return cnt; } function updateStats(data) { data.$dlg.find('.objects-info .objects-val-objs').html('' + data.stats.objs + ''); data.$dlg.find('.objects-info .objects-val-states').html('' + data.stats.states + ''); } function recalcChildrenCounters(node, data) { var id = node.key; var $tr = $(node.tr); var $firstTD = $tr.find('>td').eq(0); var cnt = countChildren(id, data); if (cnt) { var $cnt = $firstTD.find('.select-id-cnt'); if ($cnt.length) { $cnt.text('#' + cnt); } else { //$firstTD.append('#' + cnt + ''); $firstTD.append('#' + cnt + ''); } } else { $firstTD.find('.select-id-cnt').remove(); } if (node.children && node.children.length) { for (var c = 0; c < node.children.length; c++) { recalcChildrenCounters(node.children[c], data); } } } var methods = { init: function (options) { isMaterial = typeof M !== 'undefined'; // is material UI // done, just to show possible settings, this is not required var settings = $.extend({ currentId: '', objects: null, states: null, filter: null, imgPath: 'lib/css/fancytree/', connCfg: null, onSuccess: null, onChange: null, zindex: null, list: false, name: null, sortConfig: { statesFirst: true, ignoreSortOrder: false }, //columns: ['image', 'name', 'type', 'role', 'enum', 'room', 'function', 'value', 'button'] columns: ['name', 'type', 'role', 'enum', 'room', 'function', 'value', 'button'] }, options); settings.texts = settings.texts || {}; settings.texts = $.extend({ select: 'Select', cancel: 'Cancel', all: 'All', id: 'ID', name: 'Name', role: 'Role', type: 'Type', room: 'Room', 'function': 'Function', enum: 'Members', value: 'Value', selectid: 'Select ID', from: 'From', quality: 'Quality', lc: 'Last changed', ts: 'Time stamp', ack: 'Acknowledged', expand: 'Expand all nodes', collapse: 'Collapse all nodes', refresh: 'Rebuild tree', edit: 'Edit', ok: 'Ok', push: 'Trigger event', wait: 'Processing...', list: 'Show list view', tree: 'Show tree view', selectAll: 'Select all', unselectAll: 'Unselect all', invertSelection: 'Invert selection', copyToClipboard: 'Copy to clipboard', expertMode: 'Toggle expert mode', button: 'Settings', noData: 'No data', Objects: 'Objects', States: 'States', toggleValues: 'Toggle states view' }, settings.texts); var that = this; for (var i = 0; i < this.length; i++) { var dlg = this[i]; var $dlg = $(dlg); var data = $dlg.data('selectId'); // Init data if (!data) { data = { tree: {title: '', children: [], count: 0, root: true}, roomEnums: [], rooms: {}, roomsColored: {}, funcEnums: [], funcs: {}, funcsColored: {}, roles: [], histories: [], types: [], regexSystemAdapter: new RegExp('^system\\.adapter\\.'), regexSystemHost: new RegExp('^system\\.host\\.'), regexEnumRooms: new RegExp('^enum\\.rooms\\.'), regexEnumFuncs: new RegExp('^enum\\.functions\\.'), inited: false, filterPresets: {} }; $dlg.data('selectId', data); } if (data.inited) { // Re-init tree if filter or selectedID changed if ((data.filter && !settings.filter && settings.filter !== undefined) || (!data.filter && settings.filter) || (data.filter && settings.filter && JSON.stringify(data.filter) !== JSON.stringify(settings.filter))) { data.inited = false; } if (data.inited && settings.currentId !== undefined && (data.currentId !== settings.currentId)) { // Deactivate current line var tree = data.$tree.fancytree('getTree'); tree.visit(function (node) { if (node.key === data.currentId) { node.setActive(false); return false; } }); } } data = $.extend(data, settings); data.rootExp = data.root ? new RegExp('^' + data.root.replace('.', '\\.')) : null; data.selectedID = data.currentId; // make a copy of filter data.filter = JSON.parse(JSON.stringify(data.filter)); if (!data.objects && data.connCfg) { // Read objects and states data.socketURL = ''; data.socketSESSION = ''; if (typeof data.connCfg.socketUrl !== 'undefined') { data.socketURL = data.connCfg.socketUrl; if (data.socketURL && data.socketURL[0] === ':') { data.socketURL = location.protocol + '//' + location.hostname + data.socketURL; } data.socketSESSION = data.connCfg.socketSession; data.socketUPGRADE = data.connCfg.upgrade; data.socketRememberUpgrade = data.connCfg.rememberUpgrade; data.socketTransports = data.connCfg.transports; } var connectTimeout = setTimeout(function () { // noinspection JSJQueryEfficiency var $dlg = $('#select-id-dialog'); if (!$dlg.length) { $('body').append('
    ' + (data.texts.noconnection || 'No connection to server') + '
    '); $dlg = $('#select-id-dialog'); } if (typeof M !== 'undefined') { $dlg.modal(); } else { $dlg.dialog({ modal: true, open: function (event) { $(event.target).parent().find('.ui-dialog-titlebar-close .ui-button-text').html(''); } }); } }, 5000); data.socket = io.connect(data.socketURL, { query: 'key=' + data.socketSESSION, 'reconnection limit': 10000, 'max reconnection attempts': Infinity, upgrade: data.socketUPGRADE, rememberUpgrade: data.socketRememberUpgrade, transports: data.socketTransports }); data.socket.on('connect', function () { if (connectTimeout) clearTimeout(connectTimeout); this.emit('name', data.connCfg.socketName || 'selectId'); this.emit('getObjects', function (err, res) { data.objects = res; data.socket.emit('getStates', function (err, res) { data.states = res; if (data.readyCallback) { data.readyCallback(err, data.objects, data.states); } }); }); }); data.socket.on('stateChange', function (id, obj) { that.selectId('state', id, obj); }); data.socket.on('objectChange', function (id, obj) { that.selectId('object', id, obj); }); } $dlg.data('selectId', data); } return this; }, show: function (currentId, filter, onSuccess) { if (typeof filter === 'function') { onSuccess = filter; filter = undefined; } if (typeof currentId === 'function') { onSuccess = currentId; currentId = undefined; } for (var i = 0; i < this.length; i++) { var dlg = this[i]; var $dlg = $(dlg); var data = $dlg.data('selectId'); if (!data) continue; if (data.inited) { // Re-init tree if filter or selectedID changed if ((data.filter && !filter && filter !== undefined) || (!data.filter && filter) || (data.filter && filter && JSON.stringify(data.filter) !== JSON.stringify(filter))) { data.inited = false; } if (data.inited && currentId !== undefined && (data.currentId !== currentId)) { // Deactivate current line var tree_ = data.$tree.fancytree('getTree'); tree_.visit(function (node) { if (node.key === data.currentId) { node.setActive(false); return false; } }); } } if (currentId !== undefined) data.currentId = currentId; if (filter !== undefined) data.filter = JSON.parse(JSON.stringify(filter)); if (onSuccess !== undefined) { data.onSuccess = onSuccess; data.$tree = $dlg.find('.objects-list-table'); if (data.$tree[0]) data.$tree[0]._onSuccess = data.onSuccess; } data.selectedID = data.currentId; if (!data.inited || !data.noDialog) { data.$dlg = $dlg; initTreeDialog($dlg); } else { if (data.selectedID) { var tree__ = data.$tree.fancytree('getTree'); tree__.visit(function (node) { if (node.key === data.selectedID) { node.setActive(); node.makeVisible({scrollIntoView: false}); return false; } }); } } if (!data.noDialog) { if (typeof M !== 'undefined') { if (data.currentId) { $dlg.find('.title').text(data.texts.selectid + ' - ' + getNameObj(data.objects[data.currentId], data.currentId)); $dlg.find('.btn-set').removeClass('disabled'); } else { $dlg.find('.title').text(data.texts.selectid); $dlg.find('.btn-set').addClass('disabled'); } $dlg.show().modal('open'); } else { if (data.currentId) { $dlg.dialog('option', 'title', data.texts.selectid + ' - ' + getNameObj(data.objects[data.currentId], data.currentId)); $dlg.find('#button-ok').removeClass('ui-state-disabled'); } else { $dlg.dialog('option', 'title', data.texts.selectid); $dlg.find('#button-ok').addClass('ui-state-disabled'); } $dlg.dialog('open'); } showActive($dlg, true); } else { $dlg.show(); showActive($dlg, true); } } return this; }, hide: function () { for (var i = 0; i < this.length; i++) { var dlg = this[i]; var $dlg = $(dlg); var data = $dlg.data('selectId'); if (data && !data.noDialog) { $dlg.dialog('hide'); } else { $dlg.hide(); } } return this; }, clear: function () { for (var i = 0; i < this.length; i++) { var dlg = this[i]; var $dlg = $(dlg); var data = $dlg.data('selectId'); // Init data if (data) { data.tree = {title: '', children: [], count: 0, root: true}; data.rooms = {}; data.roomEnums = []; data.funcs = {}; data.funcEnums = []; data.roles = []; data.types = []; data.histories = []; } } return this; }, getInfo: function (id) { for (var i = 0; i < this.length; i++) { var dlg = this[i]; var $dlg = $(dlg); var data = $dlg.data('selectId'); if (data && data.objects) { return data.objects[id]; } } return null; }, getTreeInfo: function (id) { for (var i = 0; i < this.length; i++) { var dlg = this[i]; var $dlg = $(dlg); var data = $dlg.data('selectId'); if (!data || !data.$tree) continue; var tree = data.$tree.fancytree('getTree'); var node = tree && tree.getNodeByKey(id); // var node = null; // tree.visit(function (n) { // if (n.key === id) { // node = n; // return false; // } // }); var result = { id: id, parent: (node && node.parent && node.parent.parent) ? node.parent.key : null, children: null, obj: data.objects ? data.objects[id] : null }; if (node && node.children) { result.children = []; for (var t = 0; t < node.children.length; t++) { result.children.push(node.children[t].key); } if (!result.children.length) delete result.children; } return result; } return null; }, destroy: function () { for (var i = 0; i < this.length; i++) { var $dlg = $(this[i]); var data = $dlg.data('selectId'); if (data) { $dlg.data('selectId', null); $dlg.find('.dialog-select-container').remove(); } } return this; }, reinit: function () { for (var i = 0; i < this.length; i++) { var dlg = this[i]; var $dlg = $(dlg); var data = $dlg.data('selectId'); if (data) { data.inited = false; initTreeDialog(data.$dlg); } } return this; }, // update states state: function (id, state) { for (var i = 0; i < this.length; i++) { var dlg = this[i]; var $dlg = $(dlg); var data = $dlg.data('selectId'); if (!data || !data.states || !data.$tree) continue; /*if (data.states[id] && state && data.states[id].val === state.val && data.states[id].ack === state.ack && data.states[id].q === state.q && data.states[id].from === state.from && data.states[id].ts === state.ts ) return;*/ data.states[id] = state; var tree = data.$tree.fancytree('getTree'); var node = tree.getNodeByKey(id); // var node = null; // tree.visit(function (n) { // if (n.key === id) { // node = n; // return false; // } // }); if (node) node.render(true); } return this; }, // update objects object: function (id, obj, action) { for (var k = 0, len = this.length; k < len; k++) { var dlg = this[k]; var $dlg = $(dlg); var data = $dlg.data('selectId'); if (!data || !data.$tree || !data.objects) continue; if (id.match(/^enum\.rooms/)) { data.rooms = {}; data.roomsColored = {}; } if (id.match(/^enum\.functions/)) { data.funcs = {}; data.funcsColored = {}; } var tree = data.$tree.fancytree('getTree'); var node = tree.getNodeByKey(id); // var node = null; // tree.visit(function (n) { // if (n.key === id) { // node = n; // return false; // } // }); // If new node if (!node && obj) { // Filter it if (typeof data.stats === 'object' && action === 'add') { data.stats.objs++; if (obj.type === 'state') { data.stats.states++; } updateStats(data); } data.objects[id] = obj; var addedNodes = []; if (!filterId(data, id)) { return; } // add ID to IDS; if (data.ids.length) { var p = 0; while (data.ids[p] < id) { p++; } data.ids.splice(p, 0, id); } treeInsert(data, id, false, addedNodes); for (var i = 0; i < addedNodes.length; i++) { if (!addedNodes[i].parent.root) { node = tree.getNodeByKey(addedNodes[i].parent.key); // tree.visit(function (n) { // if (n.key === addedNodes[i].parent.key) { // node = n; // return false; // } // }); } else { node = data.$tree.fancytree('getRootNode'); } // if no children if (!node.children || !node.children.length) { // add node.addChildren(addedNodes[i]); node.folder = true; node.expanded = false; node.render(true); node.children[0].match = true; } else { var c; for (c = 0; c < node.children.length; c++) { if (node.children[c].key > addedNodes[i].key) break; } // if some found greater than new one if (c !== node.children.length) { node.addChildren(addedNodes[i], node.children[c]); node.children[c].match = true; node.render(true); } else { // just add node.addChildren(addedNodes[i]); node.children[node.children.length - 1].match = true; node.render(true); } } } } else if (!obj) { if (typeof data.stats === 'object' && action === 'delete') { data.stats.objs--; if (data.objects[id] && data.objects[id].type === 'state') { data.stats.states--; } updateStats(data); } // object deleted delete data.objects[id]; deleteTree(data, id); if (data.ids.length) { var pos = data.ids.indexOf(id); if (pos !== -1) { data.ids.splice(pos, 1); } } if (node) { var prev = node.getPrevSibling(); var parent = node.parent; node.removeChildren(); node.remove(); prev && prev.setActive(); while (parent && (!parent.children || !parent.children.length)) { var _parent = parent.parent; parent.remove(); if (_parent) { _parent.setActive(); } parent = _parent; } // recalculate numbers of all children if (data.ids.length) { recalcChildrenCounters(parent, data); } // if (node.children && node.children.length) { // if (node.children.length === 1) { // node.folder = false; // node.expanded = false; // } // node.render(true); // } else { // if (node.parent && node.parent.children.length === 1) { // node.parent.folder = false; // node.parent.expanded = false; // node.parent.render(true); // } // node.remove(); // } } } else { // object updated if (node) { node.render(true); } } } return this; }, option: function (name, value) { for (var k = 0; k < this.length; k++) { var dlg = this[k]; var $dlg = $(dlg); var data = $dlg.data('selectId'); if (!data) continue; if (data[name] !== undefined) { data[name] = value; } else { console.error('Unknown options for selectID: ' + name); } } }, objectAll: function (id, obj) { $('.select-id-dialog-marker').selectId('object', id, obj); }, stateAll: function (id, state) { $('.select-id-dialog-marker').selectId('state', id, state); }, getFilteredIds: function () { for (var k = 0; k < this.length; k++) { var dlg = this[k]; var $dlg = $(dlg); var data = $dlg.data('selectId'); if (!data || !data.$tree || !data.objects) continue; var tree = data.$tree.fancytree('getTree'); var nodes = []; tree.visit(function (n) { if (n.match) { nodes.push(n.key); } }); return nodes; } return null; }, getActual: function () { //for (var k = 0; k < this.length; k++) { // //} var dlg = this[0]; var $dlg = $(dlg); var data = $dlg.data('selectId'); return data ? data.selectedID : null; } }; $.fn.selectId = function (method) { if (methods[method]) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else if (typeof method === 'object' || !method) { return methods.init.apply(this, arguments); } else { $.error('Method "' + method + '" not found in jQuery.selectId'); } }; })(jQuery);