Initial commit
This commit is contained in:
File diff suppressed because it is too large
Load Diff
+2198
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,60 @@
|
||||
function Config(main) {
|
||||
'use strict';
|
||||
var that = this;
|
||||
this.$dialog = $('#dialog-config');
|
||||
this.$configFrame = this.$dialog.find('#config-iframe');
|
||||
this.main = main;
|
||||
|
||||
this.prepare = function () {
|
||||
// id = 'system.adapter.NAME.X'
|
||||
$iframeDialog = this;
|
||||
};
|
||||
|
||||
this.init = function () {
|
||||
if (this.inited) return;
|
||||
|
||||
this.inited = true;
|
||||
|
||||
|
||||
var id = this.main.navigateGetParams();
|
||||
|
||||
var parts = id.split('.');
|
||||
if (this.main.objects[id] && this.main.objects[id].common && this.main.objects[id].common.materialize) {
|
||||
this.$configFrame.attr('src', 'adapter/' + parts[2] + '/index_m.html?' + parts[3]);
|
||||
} else {
|
||||
this.$configFrame.attr('src', 'adapter/' + parts[2] + '/?' + parts[3]);
|
||||
}
|
||||
|
||||
var name = id.replace(/^system\.adapter\./, '');
|
||||
this.$dialog.data('name', name);
|
||||
this.$dialog.find('.title').html(_('Adapter configuration') + ': ' + name);
|
||||
};
|
||||
|
||||
this.allStored = function () {
|
||||
return !window.frames['config-iframe'].changed;
|
||||
};
|
||||
|
||||
// this function is called by the configuration code in iFrame
|
||||
this.close = function () {
|
||||
that.main.navigate();
|
||||
};
|
||||
|
||||
this.destroy = function () {
|
||||
if (this.inited) {
|
||||
this.inited = false;
|
||||
this.$configFrame.attr('src', '');
|
||||
|
||||
// If after wizard some configurations must be shown
|
||||
if (typeof showConfig !== 'undefined' && showConfig && showConfig.length) {
|
||||
var configId = showConfig.shift();
|
||||
setTimeout(function () {
|
||||
that.main.navigate({
|
||||
tab: 'instances',
|
||||
dialog: 'config',
|
||||
params: configId
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,627 @@
|
||||
function EditObject(main) {
|
||||
'use strict';
|
||||
|
||||
var that = this;
|
||||
this.$dialog = $('#dialog-editobject');
|
||||
this.$dialogNewField = $('#dialog-new-field');
|
||||
this.main = main;
|
||||
this.prepared = false;
|
||||
this.inited = false;
|
||||
this.$dialogSave = this.$dialog.find('.dialog-editobject-buttons .btn-save');
|
||||
this.iconVal = null;
|
||||
|
||||
function loadObjectFields(selector, object, part, objectType) {
|
||||
var text = '';
|
||||
for (var attr in object) {
|
||||
if (!object.hasOwnProperty(attr) || (part === 'common' && (attr === 'name' || attr === 'icon'))) continue;
|
||||
|
||||
if (false && objectType === 'state' && part === 'common' && attr === 'role') { // autocomplete is temporally disabled because buggy
|
||||
text += '<div class="input-field col s11">' +
|
||||
'<i class="material-icons prefix">textsms</i>' +
|
||||
'<input type="text" class="object-tab-edit-string autocomplete" data-attr="' + attr + '" value="' + object[attr] + '"/>';
|
||||
} else {
|
||||
text += '<div class="row">\n<div class="col s11">\n';
|
||||
if (objectType === 'state' && part === 'common' && attr === 'type') {
|
||||
text += '<select class="object-tab-edit-string" data-attr="' + attr + '">' +
|
||||
'<option value="boolean" ' + (object[attr] === 'boolean' ? 'selected' : '') + '>' + _('boolean') + '</option>' +
|
||||
'<option value="string" ' + (object[attr] === 'string' ? 'selected' : '') + '>' + _('string') + '</option>' +
|
||||
'<option value="number" ' + (object[attr] === 'number' ? 'selected' : '') + '>' + _('number') + '</option>' +
|
||||
'<option value="array" ' + (object[attr] === 'array' ? 'selected' : '') + '>' + _('array') + '</option>' +
|
||||
'<option value="object" ' + (object[attr] === 'object' ? 'selected' : '') + '>' + _('object') + '</option>' +
|
||||
'<option value="mixed" ' + (object[attr] === 'mixed' ? 'selected' : '') + '>' + _('mixed') + '</option>' +
|
||||
'</select>';
|
||||
} else if (typeof object[attr] === 'string') {
|
||||
text += '<input type="text" class="object-tab-edit-string" data-attr="' + attr + '" value="' + object[attr] + '" />\n';
|
||||
} else if (typeof object[attr] === 'number') {
|
||||
text += '<input type="text" class="object-tab-edit-number" data-attr="' + attr + '" value="' + object[attr] + '" />\n';
|
||||
} else if (typeof object[attr] === 'boolean') {
|
||||
text += '<input type="checkbox" class="object-tab-edit-boolean filled-in" data-attr="' + attr + '" ' + (object[attr] ? 'checked' : '') + ' />\n';
|
||||
} else {
|
||||
text += '<textarea class="object-tab-edit-object" style="width: 100%" rows="3" data-attr="' + attr + '">' + JSON.stringify(object[attr], null, 2) + '</textarea>\n';
|
||||
}
|
||||
}
|
||||
|
||||
var title = attr;
|
||||
// translations
|
||||
if (part === 'common' && systemDictionary['common_' + attr] && systemDictionary['common_' + attr][systemLang]) {
|
||||
title = _('common_' + attr);
|
||||
}
|
||||
|
||||
// workaround for materialize
|
||||
if (typeof object[attr] === 'boolean') {
|
||||
text += '<span>' + title + '</span>\n';
|
||||
} else {
|
||||
text += '<label>' + title + '</label>\n';
|
||||
}
|
||||
|
||||
text += '</div>\n<div class="col s1"><a class="btn-floating waves-effect waves-light red object-tab-field-delete" data-attr="' + attr + '" data-part="' + part + '"><i class="material-icons">delete</i></a></div>\n';
|
||||
text += '</div>\n';
|
||||
}
|
||||
|
||||
that.$dialog.find(selector).html(text);
|
||||
/*that.$dialog.find(selector).find('.autocomplete').each(function () {
|
||||
$(this).mautocomplete({
|
||||
data: {
|
||||
'state': null,
|
||||
'switch': null,
|
||||
'button': null,
|
||||
'value': null,
|
||||
'level': null,
|
||||
'indicator': null,
|
||||
'value.temperature': null,
|
||||
'value.humidity': null,
|
||||
'level.temperature': null,
|
||||
'level.dimmer': null
|
||||
},
|
||||
minLength: 0 // The minimum length of the input for the autocomplete to start. Default: 1.
|
||||
});
|
||||
});*/
|
||||
}
|
||||
|
||||
function saveObjectFields(selector, object) {
|
||||
var $htmlId = that.$dialog.find(selector);
|
||||
$htmlId.find('.object-tab-edit-string').each(function () {
|
||||
object[$(this).data('attr')] = $(this).val();
|
||||
});
|
||||
$htmlId.find('.object-tab-edit-number').each(function () {
|
||||
object[$(this).data('attr')] = parseFloat($(this).val());
|
||||
});
|
||||
$htmlId.find('.object-tab-edit-boolean').each(function () {
|
||||
object[$(this).data('attr')] = $(this).prop('checked');
|
||||
});
|
||||
var err = null;
|
||||
$htmlId.find('.object-tab-edit-object').each(function () {
|
||||
try {
|
||||
object[$(this).data('attr')] = JSON.parse($(this).val());
|
||||
} catch (e) {
|
||||
err = $(this).data('attr');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (object.write !== undefined) {
|
||||
if (object.write === 'false' || object.write === '0' || object.write === 0) object.write = false;
|
||||
if (object.write === 'true' || object.write === '1' || object.write === 1) object.write = true;
|
||||
}
|
||||
|
||||
if (object.read !== undefined) {
|
||||
if (object.read === 'false' || object.read === '0' || object.read === 0) object.read = false;
|
||||
if (object.read === 'true' || object.read === '1' || object.read === 1) object.read = true;
|
||||
}
|
||||
|
||||
if (object.min === null) {
|
||||
delete object.min;
|
||||
}
|
||||
if (object.min !== undefined) {
|
||||
var f = parseFloat(object.min);
|
||||
if (f.toString() === object.min.toString()) object.min = f;
|
||||
|
||||
if (object.min === 'false') object.min = false;
|
||||
if (object.min === 'true') object.min = true;
|
||||
}
|
||||
if (object.max === null) {
|
||||
delete object.max;
|
||||
}
|
||||
if (object.max !== undefined) {
|
||||
var m = parseFloat(object.max);
|
||||
if (m.toString() === object.max.toString()) object.max = m;
|
||||
|
||||
if (object.max === 'false') object.max = false;
|
||||
if (object.max === 'true') object.max = true;
|
||||
}
|
||||
if (object.def === null) {
|
||||
delete object.def;
|
||||
}
|
||||
|
||||
if (object.def !== undefined) {
|
||||
var d = parseFloat(object.def);
|
||||
if (d.toString() === object.def.toString()) object.def = d;
|
||||
|
||||
if (object.def === 'false') object.def = false;
|
||||
if (object.def === 'true') object.def = true;
|
||||
}
|
||||
|
||||
// common part cannot have "true" or "false". Only true and false.
|
||||
if (selector.indexOf('common') !== -1) {
|
||||
for (var attr in object) {
|
||||
if (object.hasOwnProperty(attr)) {
|
||||
if (object[attr] === 'true') {
|
||||
object[attr] = true;
|
||||
}
|
||||
if (object[attr] === 'false') {
|
||||
object[attr] = false;
|
||||
}
|
||||
if (parseFloat(object[attr]).toString() === object[attr]) {
|
||||
object[attr] = parseFloat(object[attr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
function showMessage(text, duration, isError) {
|
||||
if (typeof duration === 'boolean') {
|
||||
isError = duration;
|
||||
duration = 3000;
|
||||
}
|
||||
that.main.showToast(that.$dialog, text, null, duration, isError);
|
||||
}
|
||||
|
||||
// only init if required
|
||||
this._prepare = function () {
|
||||
if (this.prepared) {
|
||||
return;
|
||||
}
|
||||
this.prepared = true;
|
||||
this.$dialogSave.on('click', function () {
|
||||
that.save();
|
||||
});
|
||||
this.$dialog.find('.dialog-editobject-buttons .btn-cancel').on('click', function () {
|
||||
that.editor.setValue('');
|
||||
that.$dialogSave.addClass('disabled');
|
||||
that.main.navigate();
|
||||
});
|
||||
|
||||
this.$dialog.find('.btn-add-common').on('click', function () {
|
||||
that.$dialogNewField.find('.object-tab-new-icon').show();
|
||||
that.$dialogNewField.modal('open');
|
||||
var $name = that.$dialogNewField.find('.object-tab-new-name');
|
||||
$name.data('type', 'common').focus();
|
||||
if (!$name.hasClass('autocomplete')) {
|
||||
$name.addClass('autocomplete');
|
||||
$name.mautocomplete({
|
||||
data: {
|
||||
type: null,
|
||||
desc: null,
|
||||
min: null,
|
||||
max: null,
|
||||
def: null,
|
||||
role: null,
|
||||
unit: null,
|
||||
read: null,
|
||||
write: null,
|
||||
states: null
|
||||
},
|
||||
minLength: 0 // The minimum length of the input for the autocomplete to start. Default: 1.
|
||||
});
|
||||
}
|
||||
$name.focus()
|
||||
});
|
||||
|
||||
this.$dialog.find('.btn-add-native').on('click', function () {
|
||||
that.$dialogNewField.find('.object-tab-new-icon').hide();
|
||||
that.$dialogNewField.modal('open');
|
||||
var $name = that.$dialogNewField.find('.object-tab-new-name');
|
||||
if ($name.hasClass('autocomplete')) {
|
||||
$name.mautocomplete('destroy');
|
||||
$name.removeClass('autocomplete');
|
||||
}
|
||||
M.updateTextFields('#dialog-new-field');
|
||||
$name.data('type', 'native').focus();
|
||||
});
|
||||
this.$dialogNewField.find('.object-tab-new-name').keypress(function (e) {
|
||||
if (e.which === 13) {
|
||||
that.$dialogNewField.find('.btn-add').trigger('click');
|
||||
}
|
||||
});
|
||||
|
||||
if (!this.editor) {
|
||||
this.editor = ace.edit('view-object-raw');
|
||||
this.editor.getSession().setMode('ace/mode/json');
|
||||
this.editor.$blockScrolling = true;
|
||||
this.editor.getSession().on('change', function() {
|
||||
that.$dialogSave.removeClass('disabled');
|
||||
});
|
||||
}
|
||||
this.$dialogNewField.modal();
|
||||
|
||||
this.$dialog.find('.tabs').mtabs({
|
||||
onShow: function (tab) {
|
||||
if (!tab) return;
|
||||
var id = $(tab).attr('id');
|
||||
if (id === 'object-tab-common') {
|
||||
showMessage(_('Drop the icons here'));
|
||||
} else
|
||||
if (id === 'object-tab-raw') {
|
||||
var obj = that.saveFromTabs();
|
||||
|
||||
if (!obj) return false;
|
||||
|
||||
that.editor.setValue(JSON.stringify(obj, null, 2));
|
||||
} else if (id === 'object-tab-raw') {
|
||||
var _obj;
|
||||
try {
|
||||
_obj = JSON.parse(that.editor.getValue());
|
||||
} catch (e) {
|
||||
that.main.showMessage(e, _('Parse error'), 'error_outline');
|
||||
if (!that.main.noSelect) {
|
||||
that.$dialog.find('.tabs').mtabs('select', 'object-tab-raw');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
that.load(_obj);
|
||||
}
|
||||
that.main.saveConfig('object-edit-active', id);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
this.$dialogNewField.find('.btn-add').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
var $tab = that.$dialogNewField.find('.object-tab-new-name');
|
||||
var type = $tab.data('type') || 'common';
|
||||
var field = $tab.val().trim();
|
||||
var obj = that.saveFromTabs();
|
||||
|
||||
if (!field || field.indexOf(' ') !== -1) {
|
||||
that.main.showError(_('Invalid field name: %s', field));
|
||||
return;
|
||||
}
|
||||
if (obj[type][field] !== undefined) {
|
||||
that.main.showError(_('Field %s yet exists!', field));
|
||||
return;
|
||||
}
|
||||
|
||||
obj[type][field] = '';
|
||||
|
||||
that.load(obj);
|
||||
that.$dialogNewField.find('.object-tab-new-name').val('');
|
||||
that.$dialogNewField.modal('close');
|
||||
that.$dialogSave.removeClass('disabled');
|
||||
});
|
||||
this.$dialogNewField.find('.btn-cancel').on('click', function (e) {
|
||||
that.$dialogNewField.find('.object-tab-new-name').val('');
|
||||
});
|
||||
this.$dialog.find('.edit-object-name').on('change', function () {
|
||||
that.$dialogSave.removeClass('disabled');
|
||||
}).on('keyup', function () {
|
||||
$(this).trigger('change');
|
||||
});
|
||||
this.$dialog.find('.edit-object-type').on('change', function () {
|
||||
that.$dialogSave.removeClass('disabled');
|
||||
});
|
||||
this.$dialog.find('.object-tab-rights input').on('change', function () {
|
||||
that.$dialogSave.removeClass('disabled');
|
||||
});
|
||||
|
||||
installFileUpload(this.$dialog.find('#object-tab-common'), 50000, function (err, text) {
|
||||
if (err) {
|
||||
showMessage(err, true);
|
||||
} else {
|
||||
if (!text.match(/^data:image\//)) {
|
||||
showMessage(_('Unsupported image format'), true);
|
||||
return;
|
||||
}
|
||||
|
||||
that.$dialogSave.removeClass('disabled');
|
||||
that.iconVal = text;
|
||||
|
||||
var $tab = that.$dialog.find('.icon-editor');
|
||||
$tab.find('.icon').show().html('<img class="treetable-icon" />');
|
||||
$tab.find('.icon .treetable-icon').attr('src', text);
|
||||
}
|
||||
});
|
||||
this.$dialog.find('.icon-editor .icon-upload').off('click').on('click', function () {
|
||||
that.$dialog.find('.drop-file').trigger('click');
|
||||
});
|
||||
this.$dialog.find('.icon-editor .icon-clear').off('click').on('click', function () {
|
||||
if (that.iconVal) {
|
||||
that.iconVal = null;
|
||||
that.$dialog.find('.icon-editor').hide().appendTo(that.$dialog);
|
||||
that.$dialogSave.removeClass('disabled');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.init = function () {
|
||||
this._prepare();
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
this.inited = true;
|
||||
var id = that.main.navigateGetParams();
|
||||
var isSetDefaultState = !!(id || '').match(/,def$/);
|
||||
id = id.replace(/,def$/, '');
|
||||
|
||||
var obj = this.main.objects[id];
|
||||
if (!obj) return;
|
||||
|
||||
if (this.main.config['object-edit-active'] !== undefined && !that.main.noSelect) {
|
||||
this.$dialog.find('.tabs').mtabs('select', this.main.config['object-edit-active']);
|
||||
}
|
||||
|
||||
// fill users
|
||||
var text = '';
|
||||
var name;
|
||||
for (var u = 0; u < this.main.tabs.users.list.length; u++) {
|
||||
name = translateName(this.main.objects[this.main.tabs.users.list[u]].common.name);
|
||||
text += '<option value="' + this.main.tabs.users.list[u] + '">' + (name || this.main.tabs.users.list[u]) + '</option>';
|
||||
}
|
||||
this.$dialog.find('.object-tab-acl-owner').html(text);
|
||||
|
||||
// fill groups
|
||||
text = '';
|
||||
for (u = 0; u < this.main.tabs.users.groups.length; u++) {
|
||||
name = translateName(this.main.objects[this.main.tabs.users.groups[u]].common.name);
|
||||
text += '<option value="' + this.main.tabs.users.groups[u] + '">' + (name || this.main.tabs.users.groups[u]) + '</option>';
|
||||
}
|
||||
this.$dialog.find('.object-tab-acl-group').html(text);
|
||||
this.load(obj);
|
||||
|
||||
if (isSetDefaultState) {
|
||||
this.$dialog.data('cb', function (_obj) {
|
||||
if (_obj.type === 'state') {
|
||||
// create state
|
||||
that.main.socket.emit('getState', _obj._id, function (err, state) {
|
||||
if (!state || state.val === null || state.val === undefined) {
|
||||
that.main.socket.emit('setState', _obj._id, _obj.common.def === undefined ? null : _obj.common.def, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$dialog.data('cb', null);
|
||||
}
|
||||
that.$dialogSave.addClass('disabled');
|
||||
};
|
||||
|
||||
this.destroy = function () {
|
||||
if (this.inited) {
|
||||
this.inited = false;
|
||||
}
|
||||
};
|
||||
|
||||
this.load = function (obj) {
|
||||
if (!obj) return;
|
||||
obj.common = obj.common || {};
|
||||
obj.native = obj.native || {};
|
||||
obj.acl = obj.acl || {};
|
||||
this.$dialog.find('.title-id').text(obj._id);
|
||||
this.$dialog.find('.edit-object-name').val(obj.common ? translateName(obj.common.name) : obj._id);
|
||||
this.$dialog.find('.edit-object-type').val(obj.type);
|
||||
this.$dialog.find('.object-tab-acl-owner').val(obj.acl.owner || 'system.user.admin');
|
||||
this.$dialog.find('.object-tab-acl-group').val(obj.acl.ownerGroup || 'system.group.administrator');
|
||||
|
||||
this.$dialog.find('.icon-editor').hide().appendTo(this.$dialog);
|
||||
|
||||
loadObjectFields('.object-tab-common-table', obj.common || {}, 'common', obj.type);
|
||||
loadObjectFields('.object-tab-native-table', obj.native || {}, 'native', obj.type);
|
||||
|
||||
if (obj.common.icon !== undefined) {
|
||||
this.iconVal = obj.common.icon;
|
||||
this.$dialog.find('.object-tab-common-table').prepend(this.$dialog.find('.icon-editor').show());
|
||||
this.$dialog.find('.icon-editor .icon').html(that.main.getIconFromObj(obj));
|
||||
} else {
|
||||
this.iconVal = null;
|
||||
}
|
||||
|
||||
this.$dialog.find('.object-tab-field-delete').on('click', function () {
|
||||
var part = $(this).data('part');
|
||||
var field = $(this).data('attr');
|
||||
that.main.confirmMessage(_('Delete attribute'), _('Please confirm'), 'error_outline', function (result) {
|
||||
if (result) {
|
||||
var _obj = that.saveFromTabs();
|
||||
delete _obj[part][field];
|
||||
that.load(_obj);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
obj.acl = obj.acl || {};
|
||||
if (obj.acl.object === undefined) obj.acl.object = 0x666;
|
||||
|
||||
this.$dialog.find('#object-tab-acl-obj-owner-read') .prop('checked', obj.acl.object & 0x400);
|
||||
this.$dialog.find('#object-tab-acl-obj-owner-write').prop('checked', obj.acl.object & 0x200);
|
||||
this.$dialog.find('#object-tab-acl-obj-group-read'). prop('checked', obj.acl.object & 0x40);
|
||||
this.$dialog.find('#object-tab-acl-obj-group-write').prop('checked', obj.acl.object & 0x20);
|
||||
this.$dialog.find('#object-tab-acl-obj-every-read'). prop('checked', obj.acl.object & 0x4);
|
||||
this.$dialog.find('#object-tab-acl-obj-every-write').prop('checked', obj.acl.object & 0x2);
|
||||
|
||||
if (obj.type !== 'state') {
|
||||
this.$dialog.find('.object-tab-acl-state').hide();
|
||||
} else {
|
||||
this.$dialog.find('.object-tab-acl-state').show();
|
||||
if (obj.acl.state === undefined) obj.acl.state = 0x666;
|
||||
|
||||
this.$dialog.find('#object-tab-acl-state-owner-read') .prop('checked', obj.acl.state & 0x400);
|
||||
this.$dialog.find('#object-tab-acl-state-owner-write').prop('checked', obj.acl.state & 0x200);
|
||||
this.$dialog.find('#object-tab-acl-state-group-read'). prop('checked', obj.acl.state & 0x40);
|
||||
this.$dialog.find('#object-tab-acl-state-group-write').prop('checked', obj.acl.state & 0x20);
|
||||
this.$dialog.find('#object-tab-acl-state-every-read'). prop('checked', obj.acl.state & 0x4);
|
||||
this.$dialog.find('#object-tab-acl-state-every-write').prop('checked', obj.acl.state & 0x2);
|
||||
}
|
||||
|
||||
var _obj = JSON.parse(JSON.stringify(obj));
|
||||
this.editor.setValue(JSON.stringify(_obj, null, 2));
|
||||
if (_obj._id) delete _obj._id;
|
||||
if (_obj.common) delete _obj.common;
|
||||
if (_obj.type) delete _obj.type;
|
||||
if (_obj.native) delete _obj.native;
|
||||
if (_obj.acl) delete _obj.acl;
|
||||
this.$dialog.find('#view-object-rest').val(JSON.stringify(_obj, null, ' '));
|
||||
this.$dialog.find('select').select();
|
||||
|
||||
// workaround for materialize checkbox problem
|
||||
this.$dialog.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');
|
||||
}
|
||||
});
|
||||
// enable save
|
||||
this.$dialog.find('input').on('change', function () {
|
||||
that.$dialogSave.removeClass('disabled');
|
||||
}).on('keyup', function () {
|
||||
$(this).trigger('change');
|
||||
});
|
||||
|
||||
this.$dialog.find('select').on('change', function () {
|
||||
that.$dialogSave.removeClass('disabled');
|
||||
});
|
||||
|
||||
this.$dialog.find('textarea').on('change', function () {
|
||||
that.$dialogSave.removeClass('disabled');
|
||||
}).on('keyup', function () {
|
||||
$(this).trigger('change');
|
||||
});
|
||||
|
||||
if (obj.common.color !== undefined) {
|
||||
var time = Date.now();
|
||||
|
||||
var $color = this.$dialog.find('.object-tab-edit-string[data-attr="color"]').parent();
|
||||
$color.prepend('<i class="material-icons prefix tab-enums-dialog-new-color-icon">color_lens</i><a class="btn edit-color translate">' + _('Color') + '</a>');
|
||||
$color.colorpicker({
|
||||
component: '.btn',
|
||||
color: $color,
|
||||
container: true
|
||||
}).colorpicker('setValue', obj.common.color || '#fff').on('showPicker.colorpicker', function (/* event */) {
|
||||
}).on('changeColor.colorpicker', function (event){
|
||||
if (Date.now() - time > 100) {
|
||||
$color.find('input').val(event.color.toHex()).trigger('change');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.saveFromTabs = function () {
|
||||
var obj;
|
||||
try {
|
||||
obj = this.$dialog.find('#view-object-rest').val();
|
||||
if (!obj) {
|
||||
obj = {};
|
||||
} else {
|
||||
obj = JSON.parse(obj);
|
||||
}
|
||||
} catch (err) {
|
||||
this.main.showMessage(_('Cannot parse.'), _('Error in %s', err), 'error_outline');
|
||||
return false;
|
||||
}
|
||||
|
||||
obj.common = {};
|
||||
obj.native = {};
|
||||
obj.acl = {};
|
||||
obj._id = this.$dialog.find('.title-id').text();
|
||||
obj.common.name = this.$dialog.find('.edit-object-name').val(); // no support of multilanguage if edited
|
||||
obj.type = this.$dialog.find('.edit-object-type').val();
|
||||
var err = saveObjectFields('.object-tab-common-table', obj.common);
|
||||
if (err) {
|
||||
this.main.showMessage(_('Cannot parse.'), _('Error in %s', err), 'error_outline');
|
||||
return false;
|
||||
}
|
||||
err = saveObjectFields('.object-tab-native-table', obj.native);
|
||||
if (err) {
|
||||
this.main.showMessage(_('Cannot parse.'), _('Error in %s', err), 'error_outline');
|
||||
return false;
|
||||
}
|
||||
obj.acl.object = 0;
|
||||
obj.acl.object |= this.$dialog.find('#object-tab-acl-obj-owner-read').prop('checked') ? 0x400 : 0;
|
||||
obj.acl.object |= this.$dialog.find('#object-tab-acl-obj-owner-write').prop('checked') ? 0x200 : 0;
|
||||
obj.acl.object |= this.$dialog.find('#object-tab-acl-obj-group-read').prop('checked') ? 0x40 : 0;
|
||||
obj.acl.object |= this.$dialog.find('#object-tab-acl-obj-group-write').prop('checked') ? 0x20 : 0;
|
||||
obj.acl.object |= this.$dialog.find('#object-tab-acl-obj-every-read').prop('checked') ? 0x4 : 0;
|
||||
obj.acl.object |= this.$dialog.find('#object-tab-acl-obj-every-write').prop('checked') ? 0x2 : 0;
|
||||
|
||||
obj.acl.owner = this.$dialog.find('.object-tab-acl-owner').val();
|
||||
obj.acl.ownerGroup = this.$dialog.find('.object-tab-acl-group').val();
|
||||
|
||||
if (obj.type === 'state') {
|
||||
obj.acl.state = 0;
|
||||
obj.acl.state |= this.$dialog.find('#object-tab-acl-state-owner-read').prop('checked') ? 0x400 : 0;
|
||||
obj.acl.state |= this.$dialog.find('#object-tab-acl-state-owner-write').prop('checked') ? 0x200 : 0;
|
||||
obj.acl.state |= this.$dialog.find('#object-tab-acl-state-group-read').prop('checked') ? 0x40 : 0;
|
||||
obj.acl.state |= this.$dialog.find('#object-tab-acl-state-group-write').prop('checked') ? 0x20 : 0;
|
||||
obj.acl.state |= this.$dialog.find('#object-tab-acl-state-every-read').prop('checked') ? 0x4 : 0;
|
||||
obj.acl.state |= this.$dialog.find('#object-tab-acl-state-every-write').prop('checked') ? 0x2 : 0;
|
||||
}
|
||||
|
||||
if (this.iconVal !== null && this.iconVal !== undefined) {
|
||||
obj.common.icon = this.iconVal;
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
this.saveFromRaw = function () {
|
||||
var obj;
|
||||
try {
|
||||
obj = JSON.parse(this.editor.getValue());
|
||||
} catch (e) {
|
||||
this.main.showMessage(e, _('Parse error'), 'error_outline');
|
||||
if (!that.main.noSelect) {
|
||||
this.$dialog.find('.tabs').mtabs('select', 'object-tab-raw');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
this.save = function () {
|
||||
if (this.main.config['object-edit-active'] === 'object-tab-raw') {
|
||||
var _obj = this.saveFromRaw();
|
||||
if (!_obj) return;
|
||||
|
||||
this.main.socket.emit('setObject', _obj._id, _obj, function (err) {
|
||||
if (err) {
|
||||
that.main.showError(err);
|
||||
} else {
|
||||
var cb = that.$dialog.data('cb');
|
||||
if (cb) cb(_obj);
|
||||
that.$dialogSave.addClass('disabled');
|
||||
that.main.navigate();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var obj = that.saveFromTabs();
|
||||
if (!obj) return;
|
||||
this.main.socket.emit('getObject', obj._id, function (err, _obj) {
|
||||
if (err) {
|
||||
return that.main.showError(err);
|
||||
}
|
||||
|
||||
_obj.common = obj.common;
|
||||
_obj.native = obj.native;
|
||||
_obj.acl = obj.acl;
|
||||
that.main.socket.emit('setObject', obj._id, _obj, function (err) {
|
||||
if (err) {
|
||||
that.main.showError(err);
|
||||
} else {
|
||||
var cb = that.$dialog.data('cb');
|
||||
if (cb) cb(obj);
|
||||
that.$dialogSave.addClass('disabled');
|
||||
that.main.navigate();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.allStored = function () {
|
||||
return that.$dialogSave.hasClass('disabled');
|
||||
};
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,347 @@
|
||||
function Events(main) {
|
||||
'use strict';
|
||||
|
||||
var that = this;
|
||||
this.main = main;
|
||||
this.$tab = $('#tab-events'); // body
|
||||
var isRemote = location.hostname === 'yunkong2.net' || location.hostname === 'yunkong2.pro';
|
||||
|
||||
var list = {
|
||||
count: 0,
|
||||
start: 0,
|
||||
limit: 500 //const
|
||||
};
|
||||
var timeout = null;
|
||||
|
||||
var pause = {
|
||||
list: [],
|
||||
mode: false,
|
||||
counter: 0,
|
||||
overflow: false,
|
||||
$counterSpan: null
|
||||
};
|
||||
|
||||
var $header;
|
||||
var hdr;
|
||||
var $table;
|
||||
var $outer;
|
||||
var $pause;
|
||||
|
||||
var columnResizeInit = {
|
||||
done: false,
|
||||
timer: null
|
||||
};
|
||||
|
||||
this.prepare = function () {
|
||||
$outer = this.$tab.find('#event-outer');
|
||||
$table = this.$tab.find('#event-table');
|
||||
$pause = this.$tab.find('#event-pause');
|
||||
|
||||
// install header
|
||||
$header = this.$tab.find('#events-table-tr');
|
||||
hdr = new IobListHeader($header, {list: $outer, colWidthOffset: 1, prefix: 'event-filter'});
|
||||
hdr.doFilter = filterEvents;
|
||||
|
||||
hdr.add('combobox', 'type');
|
||||
hdr.add('edit', 'id', 'ID');
|
||||
//hdr.add('edit', 'val', 'Value');
|
||||
hdr.add('edit', 'val', 'value');
|
||||
hdr.add('combobox', 'ack', 'ack', [
|
||||
{val: '', name: 'all'},
|
||||
{val: 'true', name: 'ack'},
|
||||
{val: 'false', name: 'not ack'}
|
||||
]);
|
||||
hdr.add('combobox', 'from', 'from');
|
||||
hdr.add('text', 'ts');
|
||||
hdr.add('text', 'lc');
|
||||
|
||||
Object.defineProperty(hdr, 'getValues', {
|
||||
value: function () {
|
||||
hdr.ID.selectedVal = hdr.ID.selectedVal.toLocaleLowerCase();
|
||||
if (hdr.ack.selectedVal === 'true') hdr.ack.selectedVal = true;
|
||||
if (hdr.ack.selectedVal === 'false') hdr.ack.selectedVal = false;
|
||||
},
|
||||
enumerateble: false
|
||||
});
|
||||
|
||||
$pause.on('click', function () {
|
||||
that.pause();
|
||||
});
|
||||
|
||||
// bind "clear events" button
|
||||
var $eventClear = this.$tab.find('#event-clear');
|
||||
$eventClear
|
||||
.off('click').on('click', function () {
|
||||
list.count = 0;
|
||||
list.start = 0;
|
||||
that.$tab.find('#event-table').html('');
|
||||
});
|
||||
};
|
||||
|
||||
this.init = function () {
|
||||
if (isRemote) {
|
||||
that.$tab.find('#grid-events').html(_('You can\'t see events via cloud') + '<br><i class="large material-icons">cloud_off</i>').addClass('no-cloud-events');
|
||||
return;
|
||||
}
|
||||
if (!hdr) return;
|
||||
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
|
||||
installColResize();
|
||||
|
||||
this.inited = true;
|
||||
this.main.subscribeObjects('*');
|
||||
this.main.subscribeStates('*');
|
||||
};
|
||||
|
||||
this.destroy = function () {
|
||||
if (this.inited) {
|
||||
this.inited = false;
|
||||
this.main.unsubscribeObjects('*');
|
||||
this.main.unsubscribeStates('*');
|
||||
}
|
||||
};
|
||||
|
||||
var widthSet = false;
|
||||
|
||||
function installColResize() {
|
||||
if (!$.fn.colResizable) return;
|
||||
if ($outer.is(':visible')) {
|
||||
$outer.colResizable({
|
||||
liveDrag: true,
|
||||
|
||||
partialRefresh: true,
|
||||
marginLeft: 5,
|
||||
postbackSafe:true,
|
||||
|
||||
onResize: function (event) {
|
||||
return hdr.syncHeader();
|
||||
// // read width of data.$tree and set the same width for header
|
||||
// var thDest = $('#log-outer-header >thead>tr>th'); //if table headers are specified in its semantically correct tag, are obtained
|
||||
// var thSrc = $outer.find('>tbody>tr:first>td');
|
||||
// for (var i = 1; i < thSrc.length; i++) {
|
||||
// $(thDest[i]).attr('width', $(thSrc[i]).width());
|
||||
// }
|
||||
}
|
||||
});
|
||||
hdr.syncHeader();
|
||||
} else {
|
||||
setTimeout(function () {
|
||||
installColResize();
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
|
||||
function updateResizersHeight() {
|
||||
columnResizeInit.timer = null;
|
||||
$(window).trigger('resize.JColResizer');
|
||||
}
|
||||
|
||||
// ----------------------------- Show events ------------------------------------------------
|
||||
this.add = function (id, stateOrObj, isMessage, isState) {
|
||||
if (isRemote) return;
|
||||
|
||||
var type = isState ? 'stateChange' : (isMessage ? 'message' : 'objChange');
|
||||
var value;
|
||||
var ack;
|
||||
var from = '';
|
||||
var ts;
|
||||
var lc;
|
||||
if (hdr) {
|
||||
if (hdr.getValues) {
|
||||
hdr.getValues();
|
||||
}
|
||||
|
||||
hdr.type.checkAddOption(type);
|
||||
}
|
||||
if (!columnResizeInit.done) {
|
||||
// if the height not 100%, the column resizer is too short. Wait till the table will be really full and reinit resizer.
|
||||
// update resizer once and remeber it if the table has full height
|
||||
if (!columnResizeInit.timer) {
|
||||
columnResizeInit.timer = setTimeout(updateResizersHeight, 1000);
|
||||
}
|
||||
|
||||
if (list.count > 20) {
|
||||
columnResizeInit.done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pause.mode) {
|
||||
if (list.count >= that.limit) {
|
||||
list.start++;
|
||||
var e = document.getElementById('event_' + list.start);
|
||||
if (e) e.outerHTML = '';
|
||||
} else {
|
||||
list.count++;
|
||||
}
|
||||
}
|
||||
|
||||
// if Object
|
||||
if (!isMessage && !isState) {
|
||||
if (!stateOrObj) {
|
||||
value = 'deleted';
|
||||
ts = main.formatDate(new Date());
|
||||
} else {
|
||||
value = JSON.stringify(stateOrObj, '\x0A', 2);
|
||||
if (value !== undefined && value.length > 30) {
|
||||
value = '<span title="' + value.replace(/"/g, '\'') + '">' + value.substring(0, 30) + '...</span>';
|
||||
}
|
||||
ts = main.formatDate(stateOrObj.ts);
|
||||
}
|
||||
} else
|
||||
// if state
|
||||
if (isState) {
|
||||
if (!stateOrObj) {
|
||||
value = 'deleted';
|
||||
ts = main.formatDate(new Date());
|
||||
} else {
|
||||
stateOrObj.from = stateOrObj.from || '';
|
||||
stateOrObj.from = stateOrObj.from.replace('system.adapter.', '');
|
||||
stateOrObj.from = stateOrObj.from.replace('system.', '');
|
||||
|
||||
hdr && hdr.from.checkAddOption(stateOrObj.from, function (o) {
|
||||
return {val: o.replace(/\./g, '-'), name: o};
|
||||
});
|
||||
|
||||
from = stateOrObj.from;
|
||||
|
||||
value = JSON.stringify(stateOrObj.val);
|
||||
if (value !== undefined && value.length > 30) {
|
||||
value = '<div title="' + value.replace(/"/g, '') + '">' + value.substring(0, 30) + '...</div>';
|
||||
}
|
||||
ack = stateOrObj.ack ? 'true' : 'false';
|
||||
ts = main.formatDate(stateOrObj.ts);
|
||||
lc = main.formatDate(stateOrObj.lc);
|
||||
}
|
||||
} else
|
||||
// if message
|
||||
if (isMessage) {
|
||||
// todo
|
||||
}
|
||||
|
||||
var visible = true;
|
||||
if (hdr) {
|
||||
if (hdr.type.selectedVal && hdr.type.selectedVal !== type) {
|
||||
visible = false;
|
||||
} else if (hdr.ID.selectedVal && id.toLocaleLowerCase().indexOf(hdr.ID.selectedVal) === -1) {
|
||||
visible = false;
|
||||
} else if (hdr.value.selectedVal !== '' && value !== null && value !== undefined && value.indexOf(hdr.value.selectedVal) === -1) {
|
||||
visible = false;
|
||||
} else if (hdr.ack.selectedVal !== '' && hdr.ack.selectedVal !== ack) {
|
||||
visible = false;
|
||||
} else if (hdr.from.selectedVal && hdr.from.selectedVal !== from) {
|
||||
visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var text = '<tr id="event_' + (list.start + list.count) + '" class="event-line event-type-' + type + ' event-from-' + from.replace('.', '-') + ' event-ack-' + ack + '" style="' + (visible ? '' : 'display:none') + '">';
|
||||
text += '<td>' + type + '</td>';
|
||||
text += '<td class="event-column-id">' + id + '</td>';
|
||||
if (isNaN(value)) {
|
||||
text += '<td class="event-column-value">' + (value || '') + '</td>';
|
||||
} else {
|
||||
text += '<td class="event-column-value" style="text-align: right; padding-right: 5px;">' + (value || '') + '</td>';
|
||||
}
|
||||
text += '<td>' + (ack || '') + '</td>';
|
||||
text += '<td>' + (from || '') + '</td>';
|
||||
text += '<td>' + (ts || '') + '</td>';
|
||||
text += '<td>' + (lc || '') + '</td>';
|
||||
text += '</tr>';
|
||||
|
||||
if (pause.mode) {
|
||||
pause.list.push(text);
|
||||
pause.counter++;
|
||||
|
||||
if (pause.counter > list.limit) {
|
||||
if (!pause.overflow) {
|
||||
$pause.addClass('red lighten3')
|
||||
.attr('title', _('Message buffer overflow. Losing oldest'));
|
||||
pause.overflow = true;
|
||||
}
|
||||
pause.list.shift();
|
||||
}
|
||||
pause.$counterSpan.html(pause.counter);
|
||||
} else if ($table) {
|
||||
$table.prepend(text);
|
||||
if (!widthSet && (window.location.hash === '#tab-events' || window.location.hash === '#events')) {
|
||||
hdr && hdr.syncHeader();
|
||||
widthSet = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*this.onSelected = function () {
|
||||
hdr && hdr.syncHeader();
|
||||
};*/
|
||||
|
||||
function filterEvents() {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
if (hdr.getValues) {
|
||||
hdr.getValues();
|
||||
}
|
||||
|
||||
$table && $table.find('.event-line').each(function (index) {
|
||||
var isShow = true;
|
||||
var $this = $(this);
|
||||
if (hdr.type.selectedVal && !$this.hasClass('event-type-' + hdr.type.selectedVal)) {
|
||||
isShow = false;
|
||||
} else
|
||||
if (hdr.from.selectedVal && !$this.hasClass('event-from-' + hdr.from.selectedVal)) {
|
||||
isShow = false;
|
||||
} else
|
||||
if (hdr.ack.selectedVal !== '' && !$this.hasClass('event-ack-' + hdr.ack.selectedVal)) {
|
||||
isShow = false;
|
||||
} else
|
||||
if (hdr.ID.selectedVal && $(this).find('td.event-column-id').text().toLocaleLowerCase().indexOf(hdr.ID.selectedVal) === -1) {
|
||||
isShow = false;
|
||||
} else
|
||||
if (hdr.value.selectedVal !== '' && $(this).find('td.event-column-value').text().indexOf(hdr.value.selectedVal) === -1) {
|
||||
isShow = false;
|
||||
}
|
||||
|
||||
if (isShow) {
|
||||
$this.show();
|
||||
} else {
|
||||
$this.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.pause = function () {
|
||||
if (!pause.mode) {
|
||||
$pause.addClass('yellow btn-pause-button-active');
|
||||
|
||||
pause.$counterSpan = $pause;
|
||||
pause.$counterSpan.html('0');
|
||||
pause.counter = 0;
|
||||
pause.mode = true;
|
||||
} else {
|
||||
pause.mode = false;
|
||||
for (var i = 0; i < pause.list.length; i++) {
|
||||
if (list.count >= 500) {
|
||||
list.start++;
|
||||
var e = document.getElementById('event_' + list.start);
|
||||
if (e) e.outerHTML = '';
|
||||
} else {
|
||||
list.count++;
|
||||
}
|
||||
$table.prepend(pause.list[i]);
|
||||
}
|
||||
pause.overflow = false;
|
||||
pause.list = [];
|
||||
pause.counter = 0;
|
||||
|
||||
$pause
|
||||
.removeClass('yellow btn-pause-button-active')
|
||||
.html('<i class="material-icons">pause</i>');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
function fileHandler(event) {
|
||||
event.preventDefault();
|
||||
var file = event.dataTransfer ? event.dataTransfer.files[0] : event.target.files[0];
|
||||
|
||||
var $dz = $(this).find('.drop-zone');
|
||||
var callback = $(this).data('drop-zone-cb');
|
||||
var limit = $(this).data('limit');
|
||||
if (file.size > (limit || 10000)) {
|
||||
callback && callback(_('File is too big!'));
|
||||
$dz.hide();
|
||||
return false;
|
||||
}
|
||||
$dz.show();
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (evt) {
|
||||
$dz.hide();
|
||||
callback && callback(null, evt.target.result);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Install file upload on some div
|
||||
* @param {object} $dropZone is jquery object of the div (DOM element) where the drop zone must be installed
|
||||
* @param {number} limit is maximal size of file in bytes
|
||||
* @param {function} callback is callback in form function (err, fileDataBase64) {}
|
||||
*/
|
||||
function installFileUpload($dropZone, limit, callback) {
|
||||
if (typeof window.FileReader !== 'undefined' && !$dropZone.data('installed')) {
|
||||
$dropZone.data('installed', true);
|
||||
$dropZone.prepend('<div class="drop-zone" style="display: none"><input type="file" class="drop-file" style="display: none" /></div>');
|
||||
var $dz = $dropZone.find('.drop-zone');
|
||||
$dropZone[0].ondragover = function() {
|
||||
$dz.off('click');
|
||||
$dz.show();
|
||||
return false;
|
||||
};
|
||||
|
||||
$dz[0].ondragleave = function() {
|
||||
$dz.hide();
|
||||
return false;
|
||||
};
|
||||
|
||||
$dz[0].ondrop = fileHandler.bind($dropZone[0]);
|
||||
}
|
||||
$dropZone.data('drop-zone-cb', callback);
|
||||
$dropZone.data('limit', limit);
|
||||
$dropZone.find('.drop-file').on('change', fileHandler.bind($dropZone[0]));
|
||||
}
|
||||
@@ -0,0 +1,663 @@
|
||||
function Hosts(main) {
|
||||
'use strict';
|
||||
|
||||
var that = this;
|
||||
this.main = main;
|
||||
this.list = [];
|
||||
this.$tab = $('#tab-hosts');
|
||||
this.$grid = this.$tab.find('#hosts');
|
||||
this.$table = this.$tab.find('#grid-hosts');
|
||||
this.inited = false;
|
||||
this.isTiles = true;
|
||||
this.words = {};
|
||||
|
||||
this.prepare = function () {
|
||||
this.isTiles = (this.main.config.hostsIsTiles !== undefined && this.main.config.hostsIsTiles !== null) ? this.main.config.hostsIsTiles : true;
|
||||
|
||||
// fix for IE
|
||||
if (this.main.browser === 'ie' && this.main.browserVersion <= 10) {
|
||||
this.isTiles = false;
|
||||
this.$tab.find('.btn-switch-tiles').hide();
|
||||
}
|
||||
|
||||
this.$tab.find('.btn-reload')
|
||||
.attr('title', _('Update'))
|
||||
.on('click', function () {
|
||||
that.init(true);
|
||||
});
|
||||
|
||||
this.$tab.find('.btn-switch-tiles').off('click').on('click', function () {
|
||||
that.isTiles = !that.isTiles;
|
||||
|
||||
if (that.isTiles) {
|
||||
$(this).find('i').text('view_list');
|
||||
} else {
|
||||
$(this).find('i').text('view_module');
|
||||
}
|
||||
|
||||
that.main.saveConfig('hostsIsTiles', that.isTiles);
|
||||
|
||||
setTimeout(function () {
|
||||
that._postInit();
|
||||
}, 50);
|
||||
});
|
||||
|
||||
if (this.isTiles) {
|
||||
this.$tab.find('.btn-switch-tiles').find('i').text('view_list');
|
||||
} else {
|
||||
this.$tab.find('.btn-switch-tiles').find('i').text('view_module');
|
||||
}
|
||||
|
||||
this.$tab.find('.filter-clear').on('click', function () {
|
||||
that.$tab.find('.filter-input').val('').trigger('change');
|
||||
});
|
||||
|
||||
var $hostsFilter = this.$tab.find('.filter-input');
|
||||
$hostsFilter.on('change', function () {
|
||||
var filter = $(this).val();
|
||||
if (filter) {
|
||||
$(this).addClass('input-not-empty');
|
||||
that.$tab.find('.filter-clear').show();
|
||||
} else {
|
||||
that.$tab.find('.filter-clear').hide();
|
||||
$(this).removeClass('input-not-empty');
|
||||
}
|
||||
|
||||
that.main.saveConfig('hostsFilter', filter);
|
||||
applyFilter(filter);
|
||||
}).on('keyup', function () {
|
||||
if (that.filterTimeout) clearTimeout(that.filterTimeout);
|
||||
that.filterTimeout = setTimeout(function () {
|
||||
that.$tab.find('.filter-input').trigger('change');
|
||||
}, 300);
|
||||
});
|
||||
|
||||
if (this.main.config.hostsFilter && this.main.config.hostsFilter[0] !== '{') {
|
||||
$hostsFilter.val(that.main.config.hostsFilter).addClass('input-not-empty');
|
||||
this.$tab.find('.filter-clear').show();
|
||||
} else {
|
||||
this.$tab.find('.filter-clear').hide();
|
||||
}
|
||||
|
||||
// cache translations
|
||||
this.words['Title'] = _('Title');
|
||||
this.words['OS'] = _('OS');
|
||||
this.words['Available'] = _('Available');
|
||||
this.words['Installed'] = _('Installed');
|
||||
this.words['Events'] = _('Events');
|
||||
this.words['Title'] = _('Title');
|
||||
that.words['Type'] = _('Type');
|
||||
};
|
||||
|
||||
// ----------------------------- Hosts show and Edit ------------------------------------------------
|
||||
this.initButtons = function (id) {
|
||||
var selector = id ? '[data-host-id="' + id + '"]' : '';
|
||||
|
||||
this.$tab.find('.host-update-submit' + selector).off('click').on('click', function () {
|
||||
that.main.cmdExec($(this).attr('data-host-name'), 'upgrade self', function (exitCode) {
|
||||
if (!exitCode) that.init(true);
|
||||
});
|
||||
});
|
||||
|
||||
this.$tab.find('.host-restart-submit' + selector).off('click').on('click', function () {
|
||||
that.main.waitForRestart = true;
|
||||
that.main.cmdExec($(this).attr('data-host-name'), '_restart');
|
||||
});
|
||||
this.$tab.find('.host-delete' + selector).off('click').on('click', function () {
|
||||
that.main.cmdExec(that.main.currentHost, 'host remove ' + $(this).attr('data-host-name'));
|
||||
});
|
||||
|
||||
this.$tab.find('.host-edit' + selector).off('click').on('click', function () {
|
||||
editHost($(this).attr('data-host-id'));
|
||||
});
|
||||
|
||||
this.$tab.find('.host-update-hint-submit' + selector).off('click').on('click', function () {
|
||||
var infoTimeout = setTimeout(function () {
|
||||
showUpdateInfo();
|
||||
infoTimeout = null;
|
||||
}, 1000);
|
||||
|
||||
that.main.socket.emit('sendToHost', $(this).attr('data-host-name'), 'getLocationOnDisk', null, function (data) {
|
||||
if (infoTimeout) clearTimeout(infoTimeout);
|
||||
infoTimeout = null;
|
||||
showUpdateInfo(data);
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
function showUpdateInfo(data) {
|
||||
var $dialog = $('#dialog-host-update');
|
||||
if (data) {
|
||||
var path = data.path;
|
||||
path = path.replace(/\\/g, '/');
|
||||
var parts = path.split('/');
|
||||
parts.pop(); // js-controller
|
||||
parts.pop(); // node_modules
|
||||
|
||||
if (data.platform === 'linux' || data.platform === 'darwin' || data.platform === 'freebsd' || data.platform === 'lin') {
|
||||
// linux
|
||||
$dialog.find('#dialog-host-update-instructions').val('cd ' + parts.join('/') + '\nsudo yunkong2 stop\nsudo yunkong2 update\nsudo yunkong2 upgrade self\nsudo yunkong2 start')
|
||||
} else {
|
||||
// windows
|
||||
$dialog.find('#dialog-host-update-instructions').val('cd ' + parts.join('\\') + '\nyunkong2 stop\nyunkong2 update\nyunkong2 upgrade self\nyunkong2 start')
|
||||
}
|
||||
} else {
|
||||
$dialog.find('#dialog-host-update-instructions').val('cd /opt/yunkong2\nsudo yunkong2 stop\nsudo yunkong2 update\nsudo yunkong2 upgrade self\nsudo yunkong2 start')
|
||||
}
|
||||
|
||||
if (!$dialog.data('inited')) {
|
||||
$dialog.data('inited', true);
|
||||
$dialog.modal();
|
||||
}
|
||||
$dialog.modal('open');
|
||||
}
|
||||
|
||||
function applyFilter(filter) {
|
||||
filter = (filter || '').toLowerCase().trim();
|
||||
|
||||
if (!filter) {
|
||||
that.$tab.find('.hosts-host').show();
|
||||
that.$tab.find('.hosts-host-filtered-out').hide();
|
||||
} else {
|
||||
var someVisible = false;
|
||||
that.$tab.find('.hosts-host').each(function () {
|
||||
var text = $(this).data('host-filter');
|
||||
if (text.toLowerCase().indexOf(filter) !== -1) {
|
||||
$(this).show();
|
||||
someVisible = true;
|
||||
} else {
|
||||
$(this).hide();
|
||||
}
|
||||
});
|
||||
if (!someVisible) {
|
||||
that.$tab.find('.hosts-host-filtered-out').show();
|
||||
} else {
|
||||
that.$tab.find('.hosts-host-filtered-out').hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showOneHostRow(index) {
|
||||
var obj = that.main.objects[that.list[index].id];
|
||||
var alive = that.main.states[obj._id + '.alive'] && that.main.states[obj._id + '.alive'].val && that.main.states[obj._id + '.alive'].val !== 'null';
|
||||
obj.common = obj.common || {};
|
||||
obj.native = obj.native || {};
|
||||
|
||||
var text = '<tr class="hosts-host" data-host-id="' + obj._id + '" data-host-filter="' + (obj.common.title || '') + ' ' + (obj.common.hostname || '') + ' ' + (obj.common.name || '') + '">';
|
||||
//LED
|
||||
text += '<td class="tab-hosts-header-led"><div class="hosts-led ' + (alive ? 'led-green' : 'led-red') + '" data-host-id="' + obj._id + '"></div></td>';
|
||||
// icon
|
||||
text += '<td class="hosts-icon">' + that.main.getHostIcon(obj) + '</td>';
|
||||
// name
|
||||
text += '<td class="hosts-name" style="font-weight: bold">' + obj.common.hostname + '</td>';
|
||||
// type
|
||||
text += '<td class="tab-hosts-header-type">' + obj.common.type + '</td>';
|
||||
var title = obj.common.titleLang || obj.common.title;
|
||||
if (typeof title === 'object') {
|
||||
title = title[systemLang] || title.en;
|
||||
}
|
||||
// description
|
||||
text += '<td class="tab-hosts-header-title">' + title + '</td>';
|
||||
// platform
|
||||
// text += '<td>' + obj.common.platform + '</td>'; // actually only one platform
|
||||
// OS
|
||||
text += '<td class="tab-hosts-header-os">' + (obj.native.os ? obj.native.os.platform : _('unknown')) + '</td>';
|
||||
// Available
|
||||
text += '<td class="tab-hosts-header-available"><span data-host-id="' + obj._id + '" data-type="' + obj.common.type + '" class="hosts-version-available"></span>' +
|
||||
'<button class="small-button host-update-submit" data-host-name="' + obj.common.hostname + '" style="display: none; opacity: 0;" title="' + _('update') + '"><i class="material-icons">refresh</i></button>' +
|
||||
'<button class="small-button host-update-hint-submit" data-host-name="' + obj.common.hostname + '" style="display: none;" title="' + _('update') + '"><i class="material-icons">refresh</i></button>' +
|
||||
'</td>';
|
||||
|
||||
// installed
|
||||
text += '<td class="hosts-version-installed tab-hosts-header-installed" data-host-id="' + obj._id + '">' + obj.common.installedVersion + '</td>';
|
||||
|
||||
// event rates
|
||||
if (that.main.states[obj._id + '.inputCount']) {
|
||||
text += '<td class="tab-hosts-header-events" style="text-align: center"><span title="in" data-host-id="' + obj._id + '" class="host-in">⇥' + that.main.states[obj._id + '.inputCount'].val + '</span> / <span title="out" data-host-id="' + obj._id + '" class="host-out">↦' + that.main.states[obj._id + '.outputCount'].val + '</span></td>';
|
||||
} else {
|
||||
text += '<td class="tab-hosts-header-events" style="text-align: center"><span title="in" data-host-id="' + obj._id + '" class="host-in"></span> / <span title="out" data-host-id="' + obj._id + '" class="host-out"></span></td>';
|
||||
}
|
||||
|
||||
// restart button
|
||||
text += '<td class="tab-hosts-header-restart"><button class="small-button host-restart-submit" style="' + (alive ? '' : 'display: none') + '" data-host-id="' + obj._id + '" title="' + _('restart') + '"><i class="material-icons">autorenew</i></button></td>';
|
||||
|
||||
text += '</tr>';
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
function showOneHostTile(index) {
|
||||
var obj = that.main.objects[that.list[index].id];
|
||||
var alive = that.main.states[obj._id + '.alive'] && that.main.states[obj._id + '.alive'].val && that.main.states[obj._id + '.alive'].val !== 'null';
|
||||
obj.common = obj.common || {};
|
||||
obj.native = obj.native || {};
|
||||
|
||||
var color;
|
||||
if (obj.common.color) {
|
||||
color = that.main.invertColor(obj.common.color);
|
||||
}
|
||||
|
||||
var text = ' <div class="col s12 hosts-host" data-host-id="' + obj._id + '" data-host-filter="' + (obj.common.title || '') + ' ' + (obj.common.hostname || '') + ' ' + (obj.common.name || '') + '">'+
|
||||
' <div class="host z-depth-1 hoverable">' +
|
||||
' <div class="image center">'+
|
||||
' ' + that.main.getHostIcon(obj, ' ') +
|
||||
' <div class="hosts-led ' + (alive ? 'led-green' : 'led-red') +'" data-host-id="' + obj._id + '"></div>' +
|
||||
' </div>'+
|
||||
' <div class="system" style="' + (obj.common.color ? 'color: ' + (color ? 'white' : 'black') + '; background: ' + obj.common.color : '') + '">' +
|
||||
' <span class="nameHost" title="name">' + obj.common.hostname + '</span>' +
|
||||
' <ul>'+
|
||||
' <li class="tab-hosts-header-title"><span class="title-val">' + that.words['Type'] + ':</span> <span class="type">' + obj.common.type + '</span></li>' +
|
||||
' <li><span class="title-val">' + that.words['Title'] + ':</span> <span class="title">' + obj.common.title + '</span></li>' +
|
||||
' <li><span class="title-val">' + that.words['OS'] + ':</span> <span class="os">' + (obj.native.os ? obj.native.os.platform : _('unknown')) + '</span></li>' +
|
||||
' <li><span class="title-val">' + that.words['Available'] + ':</span> <span class="available hosts-version-available"></span></li>' +
|
||||
' <li><span class="title-val">' + that.words['Installed'] + ':</span> <span class="installed"> ' + obj.common.installedVersion + '</span></li>';
|
||||
|
||||
if (that.main.states[obj._id + '.inputCount']) {
|
||||
text += '<li class="tab-hosts-header-events"><span class="title-val">' + that.words['Events'] + ':</span> <span title="in" data-host-id="' + obj._id + '" class="host-in">⇥' + that.main.states[obj._id + '.inputCount'].val + '</span> / <span title="out" data-host-id="' + obj._id + '" class="host-out">↦' + that.main.states[obj._id + '.outputCount'].val + '</span></li>';
|
||||
} else {
|
||||
text += '<li class="tab-hosts-header-events"><span class="title-val">' + that.words['Events'] + ':</span> <span title="in" data-host-id="' + obj._id + '" class="host-in"></span> / <span title="out" data-host-id="' + obj._id + '" class="host-out"></span></li>';
|
||||
}
|
||||
|
||||
text += '</ul>'+
|
||||
' </div>'+
|
||||
' <div class="icon center">'+
|
||||
' <i class="material-icons host-edit" data-host-id="' + obj._id + '">edit</i>' +
|
||||
' <i class="material-icons host-restart-submit" data-host-name="' + obj.common.hostname + '" title="' + _('restart') + '">autorenew</i>';
|
||||
if (obj.common.hostname !== that.main.currentHost) {
|
||||
text += ' <i class="material-icons host-delete" data-host-name="' + obj.common.hostname + '" title="' + _('remove') + '">delete</i>';
|
||||
}
|
||||
text += ' <i class="material-icons host-update-hint-submit" data-host-name="' + obj.common.hostname + '" style="display: none">refresh</i>' +
|
||||
' <i class="material-icons host-update-submit" data-host-name="' + obj.common.hostname + '" style="display: none; opacity: 0">refresh</i>' +
|
||||
' </div>'+
|
||||
' </div>'+
|
||||
' </div>';
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
function editHost(id) {
|
||||
var $dialog = $('#tab-host-dialog-edit');
|
||||
|
||||
var titleVal = '';
|
||||
var iconVal = '';
|
||||
var colorVal = '';
|
||||
|
||||
installFileUpload($dialog, 50000, function (err, text) {
|
||||
if (err) {
|
||||
that.main.showToast($dialog, err);
|
||||
} else {
|
||||
if (!text.match(/^data:image\//)) {
|
||||
that.main.showToast($dialog, _('Unsupported image format'));
|
||||
return;
|
||||
}
|
||||
$dialog.find('.tab-host-dialog-ok').removeClass('disabled');
|
||||
iconVal = text;
|
||||
|
||||
$dialog.find('.tab-host-dialog-edit-icon').show().html('<img class="" />');
|
||||
$dialog.find('.tab-host-dialog-edit-icon img').attr('src', text);
|
||||
$dialog.find('.tab-host-dialog-edit-icon-clear').show();
|
||||
}
|
||||
});
|
||||
|
||||
if (that.main.objects[id] && that.main.objects[id].common) {
|
||||
titleVal = that.main.objects[id].common.title;
|
||||
if (typeof titleVal === 'object') {
|
||||
titleVal = titleVal[systemLang] || titleVal.en;
|
||||
}
|
||||
iconVal = that.main.objects[id].common.icon;
|
||||
colorVal = that.main.objects[id].common.color;
|
||||
}
|
||||
|
||||
$dialog.find('#tab-host-dialog-edit-title')
|
||||
.val(titleVal)
|
||||
.off('change')
|
||||
.on('change', function () {
|
||||
$dialog.find('.tab-host-dialog-ok').removeClass('disabled');
|
||||
}).off('keyup').on('keyup', function () {
|
||||
$(this).trigger('change');
|
||||
});
|
||||
|
||||
$dialog.find('.tab-host-dialog-ok')
|
||||
.addClass('disabled')
|
||||
.off('click')
|
||||
.on('click', function () {
|
||||
var obj = JSON.parse(JSON.stringify(that.main.objects[id]));
|
||||
obj.common.title = $dialog.find('#tab-host-dialog-edit-title').val();
|
||||
obj.common.icon = iconVal;
|
||||
obj.common.color = colorVal;
|
||||
if (JSON.stringify(obj) !== JSON.stringify(that.main.objects[id])) {
|
||||
that.main.socket.emit('setObject', obj._id, obj, function (err) {
|
||||
that.main.showToast($dialog, _('Updated'));
|
||||
});
|
||||
} else {
|
||||
that.main.showToast($dialog, _('Nothing changed'));
|
||||
}
|
||||
});
|
||||
|
||||
if (iconVal) {
|
||||
$dialog.find('.tab-host-dialog-edit-icon').show().html(that.main.getIcon(id));
|
||||
$dialog.find('.tab-host-dialog-edit-icon-clear').show();
|
||||
} else {
|
||||
$dialog.find('.tab-host-dialog-edit-icon').hide();
|
||||
$dialog.find('.tab-host-dialog-edit-icon-clear').hide();
|
||||
}
|
||||
|
||||
colorVal = colorVal || false;
|
||||
|
||||
if (colorVal) {
|
||||
$dialog.find('.tab-host-dialog-edit-color').val(colorVal);
|
||||
} else {
|
||||
$dialog.find('.tab-host-dialog-edit-color').val();
|
||||
}
|
||||
|
||||
M.updateTextFields('#tab-host-dialog-edit');
|
||||
that.main.showToast($dialog, _('Drop the icons here'));
|
||||
|
||||
$dialog.find('.tab-host-dialog-edit-upload').off('click').on('click', function () {
|
||||
$dialog.find('.drop-file').trigger('click');
|
||||
});
|
||||
|
||||
$dialog.find('.tab-host-dialog-edit-icon-clear').off('click').on('click', function () {
|
||||
if (iconVal) {
|
||||
iconVal = '';
|
||||
$dialog.find('.tab-host-dialog-edit-icon').hide();
|
||||
$dialog.find('.tab-host-dialog-ok').removeClass('disabled');
|
||||
$dialog.find('.tab-host-dialog-edit-icon-clear').hide();
|
||||
}
|
||||
});
|
||||
$dialog.find('.tab-host-dialog-edit-color-clear').off('click').on('click', function () {
|
||||
if (colorVal) {
|
||||
$dialog.find('.tab-host-dialog-ok').removeClass('disabled');
|
||||
$dialog.find('.tab-host-dialog-edit-color-clear').hide();
|
||||
$dialog.find('.tab-host-dialog-edit-colorpicker').colorpicker({
|
||||
component: '.btn',
|
||||
color: colorVal,
|
||||
container: $dialog.find('.tab-host-dialog-edit-colorpicker')
|
||||
}).colorpicker('setValue', '');
|
||||
colorVal = '';
|
||||
}
|
||||
});
|
||||
var time = Date.now();
|
||||
try {
|
||||
$dialog.find('.tab-host-dialog-edit-colorpicker').colorpicker('destroy');
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
$dialog.find('.tab-host-dialog-edit-colorpicker').colorpicker({
|
||||
component: '.btn',
|
||||
color: colorVal,
|
||||
container: $dialog.find('.tab-host-dialog-edit-colorpicker')
|
||||
}).colorpicker('setValue', colorVal).on('showPicker.colorpicker', function (/* event */) {
|
||||
//$dialog.find('.tab-host-dialog-edit-colorpicker')[0].scrollIntoView(false);
|
||||
var $modal = $dialog.find('.modal-content');
|
||||
$modal[0].scrollTop = $modal[0].scrollHeight;
|
||||
}).on('changeColor.colorpicker', function (event){
|
||||
if (Date.now() - time > 100) {
|
||||
colorVal = event.color.toHex();
|
||||
$dialog.find('.tab-host-dialog-ok').removeClass('disabled');
|
||||
$dialog.find('.tab-host-dialog-edit-icon-clear').show();
|
||||
}
|
||||
});
|
||||
if (colorVal) {
|
||||
$dialog.find('.tab-host-dialog-edit-color-clear').show();
|
||||
} else {
|
||||
$dialog.find('.tab-host-dialog-edit-color-clear').hide();
|
||||
}
|
||||
|
||||
$dialog.modal().modal('open');
|
||||
}
|
||||
|
||||
function showHostsTile() {
|
||||
var text = '';
|
||||
for (var i = 0; i < that.list.length; i++) {
|
||||
text += showOneHostTile(i);
|
||||
}
|
||||
that.$table.html('');
|
||||
that.$tab.find('.hosts-table').hide();
|
||||
that.$grid.html(text).show();
|
||||
that.$grid.append('<div class="col s12 hosts-host-filtered-out"><div class="host z-depth-1 hoverable">' + _('Filtered out') + '</div></div>');
|
||||
}
|
||||
|
||||
function showHostsTable() {
|
||||
var text = '';
|
||||
for (var i = 0; i < that.list.length; i++) {
|
||||
text += showOneHostRow(i);
|
||||
}
|
||||
that.$grid.html('').hide();
|
||||
that.$table.html(text);
|
||||
that.$tab.find('.hosts-table').show();
|
||||
that.$table.append('<tr class="hosts-host-filtered-out"><td colspan="9">' + _('Filtered out') + '</td></tr>');
|
||||
}
|
||||
|
||||
this.updateCounter = function (counter) {
|
||||
if (counter === undefined) {
|
||||
this.main.tabs.adapters.getAdaptersInfo(this.main.currentHost, false, false, function (repository, installedList) {
|
||||
var hostsToUpdate = 0;
|
||||
if (!installedList || !installedList.hosts) return;
|
||||
|
||||
for (var id in installedList.hosts) {
|
||||
if (!installedList.hosts.hasOwnProperty(id)) continue;
|
||||
var obj = that.main.objects['system.host.' + id];
|
||||
if (!obj || !obj.common) continue;
|
||||
var installedVersion = obj.common.installedVersion;
|
||||
var availableVersion = obj.common ? (repository && repository[obj.common.type] ? repository[obj.common.type].version : '') : '';
|
||||
|
||||
if (installedVersion && availableVersion && !that.main.upToDate(availableVersion, installedVersion)) {
|
||||
id = 'system.host.' + id.trim().replace(FORBIDDEN_CHARS, '_').replace(/\./g, '_');
|
||||
if (that.main.states[id + '.alive'] && that.main.states[id + '.alive'].val && that.main.states[id + '.alive'].val !== 'null') {
|
||||
hostsToUpdate++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
that.updateCounter(hostsToUpdate);
|
||||
});
|
||||
} else if (counter) {
|
||||
var $updates = $('#updates-for-hosts');
|
||||
if ($updates.length) {
|
||||
$updates.text(counter);
|
||||
} else {
|
||||
$('<span id="updates-for-hosts" title="' + _('updates') + '" class="new badge updates-for-hosts" data-badge-caption="">' + counter + '</span>').appendTo('.admin-sidemenu-items[data-tab="tab-hosts"] a');
|
||||
}
|
||||
} else {
|
||||
$('#updates-for-hosts').remove();
|
||||
}
|
||||
};
|
||||
|
||||
this._postInit = function () {
|
||||
if (typeof that.$grid !== 'undefined') {
|
||||
if (this.isTiles) {
|
||||
showHostsTile();
|
||||
} else {
|
||||
showHostsTable()
|
||||
}
|
||||
applyFilter(this.$tab.find('.filter-input').val());
|
||||
|
||||
var timer = setTimeout(function () {
|
||||
console.warn('Timeout for repository');
|
||||
timer = null;
|
||||
that.initButtons();
|
||||
}, 2000);
|
||||
|
||||
var host = that.main.currentHost;
|
||||
if (!host) {
|
||||
// find alive host
|
||||
for (var i = 0; i < that.list.length; i++) {
|
||||
if (that.main.states[that.list[i].id + '.alive'] && that.main.states[that.list[i].id + '.alive'].val) {
|
||||
host = that.list[i].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
that.main.tabs.adapters.getAdaptersInfo(host, true, false, function (repository, installedList) {
|
||||
if (!installedList || !installedList.hosts) return;
|
||||
|
||||
for (var id in installedList.hosts) {
|
||||
if (!installedList.hosts.hasOwnProperty(id)) continue;
|
||||
var obj = that.main.objects['system.host.' + id];
|
||||
var installed = installedList.hosts[id].version;
|
||||
if (installed !== installedList.hosts[id].runningVersion) installed += '(' + _('Running: ') + installedList.hosts[id].runningVersion + ')';
|
||||
if (!installed && obj.common && obj.common.installedVersion) installed = obj.common.installedVersion;
|
||||
|
||||
id = 'system.host.' + id.trim().replace(FORBIDDEN_CHARS, '_').replace(/\./g, '_');
|
||||
that.$tab.find('.hosts-version-installed[data-host-id="' + id + '"]').html(installed);
|
||||
}
|
||||
|
||||
that.$tab.find('.hosts-host').each(function () {
|
||||
var id = $(this).data('host-id');
|
||||
var obj = that.main.objects[id];
|
||||
var installedVersion = obj.common.installedVersion;
|
||||
var availableVersion = obj.common ? (repository && repository[obj.common.type] ? repository[obj.common.type].version : '') : '';
|
||||
if (installedVersion && availableVersion) {
|
||||
if (!that.main.upToDate(availableVersion, installedVersion)) {
|
||||
// show button
|
||||
if (that.main.states[id + '.alive'] && that.main.states[id + '.alive'].val && that.main.states[id + '.alive'].val !== 'null') {
|
||||
$(this).find('.host-update-submit').show();
|
||||
$(this).find('.host-update-hint-submit').show();
|
||||
$(this).find('.hosts-version-installed').addClass('updateReady');
|
||||
$(this).find('.hosts-version-available').addClass('hosts-version-available-updatable');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (availableVersion) {
|
||||
$(this).find('.hosts-version-available').html(availableVersion);
|
||||
}
|
||||
});
|
||||
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
that.initButtons();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.init = function (update) {
|
||||
if (this.inited && !update) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getHosts(function () {
|
||||
that._postInit();
|
||||
});
|
||||
if (!this.inited) {
|
||||
this.inited = true;
|
||||
this.main.subscribeObjects('system.host.*');
|
||||
this.main.subscribeStates('system.host.*');
|
||||
}
|
||||
};
|
||||
|
||||
this.destroy = function () {
|
||||
if (this.inited) {
|
||||
this.inited = false;
|
||||
this.main.unsubscribeObjects('system.host.*');
|
||||
this.main.unsubscribeStates('system.host.*');
|
||||
}
|
||||
};
|
||||
|
||||
this.addHost = function (obj) {
|
||||
var addr = null;
|
||||
// Find first non internal IP and use it as identifier
|
||||
if (obj.native.hardware && obj.native.hardware.networkInterfaces) {
|
||||
for (var eth in obj.native.hardware.networkInterfaces) {
|
||||
if (!obj.native.hardware.networkInterfaces.hasOwnProperty(eth)) continue;
|
||||
for (var num = 0; num < obj.native.hardware.networkInterfaces[eth].length; num++) {
|
||||
if (!obj.native.hardware.networkInterfaces[eth][num].internal) {
|
||||
addr = obj.native.hardware.networkInterfaces[eth][num].address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (addr) break;
|
||||
}
|
||||
}
|
||||
if (addr) {
|
||||
this.list.push({name: obj.common.hostname, address: addr, id: obj._id});
|
||||
} else {
|
||||
this.list.push({name: obj.common.hostname, address: '127.0.0.1', id: obj._id});
|
||||
}
|
||||
};
|
||||
|
||||
this.getHosts = function (callback) {
|
||||
this.main.socket.emit('getForeignObjects', 'system.host.*', 'state', function (err, res) {
|
||||
for (var id in res) {
|
||||
if (!res.hasOwnProperty(id)) continue;
|
||||
that.main.objects[id] = res[id];
|
||||
}
|
||||
that.main.socket.emit('getForeignStates', 'system.host.*', function (err, res) {
|
||||
for (var id in res) {
|
||||
if (!res.hasOwnProperty(id)) continue;
|
||||
that.main.states[id] = res[id];
|
||||
}
|
||||
that.main.socket.emit('getForeignObjects', 'system.host.*', 'host', function (err, res) {
|
||||
that.list = [];
|
||||
for (var id in res) {
|
||||
if (!res.hasOwnProperty(id)) continue;
|
||||
var obj = res[id];
|
||||
|
||||
that.main.objects[id] = obj;
|
||||
|
||||
if (obj.type === 'host') {
|
||||
that.addHost(obj);
|
||||
}
|
||||
}
|
||||
main.initHostsList();
|
||||
if (callback) callback();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.objectChange = function (id, obj, action) {
|
||||
// Update hosts
|
||||
if (id.match(/^system\.host\.[-\w]+$/)) {
|
||||
var found = false;
|
||||
var i;
|
||||
for (i = 0; i < this.list.length; i++) {
|
||||
if (this.list[i].id === id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj) {
|
||||
if (!found) this.list.push({id: id, address: obj.common.address ? obj.common.address[0] : '', name: obj.common.name});
|
||||
} else {
|
||||
if (found) this.list.splice(i, 1);
|
||||
}
|
||||
|
||||
if (this.updateTimer) clearTimeout(this.updateTimer);
|
||||
|
||||
this.updateTimer = setTimeout(function () {
|
||||
that.updateTimer = null;
|
||||
that._postInit();
|
||||
}, 200);
|
||||
}
|
||||
};
|
||||
|
||||
this.stateChange = function (id, state) {
|
||||
if (id.match(/^system\.host\..+\.alive$/)) {
|
||||
id = id.substring(0, id.length - 6);
|
||||
if (state && state.val) {
|
||||
this.$tab.find('.hosts-led[data-host-id="' + id + '"]').removeClass('led-red').addClass('led-green');
|
||||
} else {
|
||||
this.$tab.find('.hosts-led[data-host-id="' + id + '"]').removeClass('led-green').addClass('led-red');
|
||||
this.$tab.find('.host-update-submit[data-host-id="' + id + '"]').hide();
|
||||
this.$tab.find('.host-update-hint-submit[data-host-id="' + id + '"]').hide();
|
||||
this.$tab.find('.host-restart-submit[data-host-id="' + id + '"]').hide();
|
||||
this.$tab.find('.hosts-version-available[data-host-id="' + id + '"]').removeClass('hosts-version-available-updatable');
|
||||
}
|
||||
} else if (id.match(/^system\.host\..+\.outputCount$/)) {
|
||||
id = id.substring(0, id.length - 12);
|
||||
|
||||
this.$tab.find('.host-out[data-host-id="' + id + '"]').html('<span class="highlight">↦' + state.val + '</span>');
|
||||
} else if (id.match(/^system\.host\..+\.inputCount$/)) {
|
||||
id = id.substring(0, id.length - 11);
|
||||
|
||||
this.$tab.find('.host-in[data-host-id="' + id + '"]').html('<span class="highlight">↦' + state.val + '</span>');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,587 @@
|
||||
function Intro(main) {
|
||||
'use strict';
|
||||
|
||||
var that = this;
|
||||
|
||||
this.$tab = $('#tab-intro');
|
||||
this.$tiles = this.$tab.find('.tab-intro-cards');
|
||||
this.main = main;
|
||||
this.inited = false;
|
||||
this.$template = $('#intro-template');
|
||||
|
||||
function readInstances(callback) {
|
||||
that.main.socket.emit('getObjectView', 'system', 'instance', {startkey: 'system.adapter.', endkey: 'system.adapter.\u9999'}, function (err, doc) {
|
||||
if (err) {
|
||||
if (callback) callback (err, []);
|
||||
} else {
|
||||
if (doc.rows.length === 0) {
|
||||
if (callback) callback (err, []);
|
||||
} else {
|
||||
that.main.instances = [];
|
||||
for (var i = 0; i < doc.rows.length; i++) {
|
||||
that.main.instances.push(doc.rows[i].id);
|
||||
that.main.objects[doc.rows[i].id] = doc.rows[i].value;
|
||||
}
|
||||
if (callback) callback(err, that.main.instances);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Format number in seconds to time text
|
||||
* @param {!number} seconds
|
||||
* @returns {String}
|
||||
*/
|
||||
function formatSeconds(seconds) {
|
||||
var days = Math.floor(seconds / (3600 * 24));
|
||||
seconds %= 3600 * 24;
|
||||
var hours = Math.floor(seconds / 3600);
|
||||
if (hours < 10) {
|
||||
hours = '0' + hours;
|
||||
}
|
||||
seconds %= 3600;
|
||||
var minutes = Math.floor(seconds / 60);
|
||||
if (minutes < 10) {
|
||||
minutes = '0' + minutes;
|
||||
}
|
||||
seconds %= 60;
|
||||
seconds = Math.floor(seconds);
|
||||
if (seconds < 10) {
|
||||
seconds = '0' + seconds;
|
||||
}
|
||||
var text = '';
|
||||
if (days) {
|
||||
text += days + ' ' + _('daysShortText') + ' ';
|
||||
}
|
||||
text += hours + ':' + minutes + ':' + seconds;
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format bytes to MB or GB
|
||||
* @param {!number} bytes
|
||||
* @returns {String}
|
||||
*/
|
||||
function formatRam(bytes) {
|
||||
var GB = Math.floor(bytes / (1024 * 1024 * 1024) * 10) / 10;
|
||||
bytes %= (1024 * 1024 * 1024);
|
||||
var MB = Math.floor(bytes / (1024 * 1024) * 10) / 10;
|
||||
var text = '';
|
||||
if (GB > 1) {
|
||||
text += GB + ' GB ';
|
||||
} else {
|
||||
text += MB + ' MB ';
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
function formatSpeed(mhz) {
|
||||
return mhz + ' MHz';
|
||||
}
|
||||
|
||||
/**
|
||||
* FormatObject for host informations
|
||||
* @type type
|
||||
*/
|
||||
var formatInfo = {
|
||||
'Uptime': formatSeconds,
|
||||
'System uptime': formatSeconds,
|
||||
'RAM': formatRam,
|
||||
'Speed': formatSpeed,
|
||||
'Disk size': that.main.formatBytes,
|
||||
'Disk free': that.main.formatBytes
|
||||
};
|
||||
|
||||
function copyToClipboard(e) {
|
||||
var $input = $('<textarea>');
|
||||
$(this).append($input);
|
||||
$input.val($(this).data('clippy'));
|
||||
$input.trigger('select');
|
||||
document.execCommand('copy');
|
||||
$input.remove();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
that.main.showToast(that.$tiles, _('copied'))
|
||||
}
|
||||
|
||||
function buildInfoCard(host) {
|
||||
var $card = that.$template.clone();
|
||||
$card.removeAttr('id');
|
||||
$card.addClass('card-system-info');
|
||||
$card.find('.btn-small').addClass('disabled');
|
||||
$card.find('.card-titles').text(host.name);
|
||||
$card.find('.btn-card-enabled').data('host', host.id);
|
||||
|
||||
// button enabled
|
||||
if (that.main.systemConfig.common.intro[host.id] === false) {
|
||||
if (that.$tab.hasClass('edit-active')) {
|
||||
$card.addClass('card-disabled').find('.btn-card-enabled').removeClass('blue').addClass('gray').find('i').text('close');
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
var icon;
|
||||
if (that.main.objects[host.id] && that.main.objects[host.id].common) {
|
||||
icon = that.main.objects[host.id].common.icon;
|
||||
}
|
||||
$card.find('.card-image-img').attr('src', icon || 'img/no-image.png');
|
||||
$card.find('.card-content-text').html('<div class="preloader-wrapper small active">\n' +
|
||||
' <div class="spinner-layer spinner-green-only">\n' +
|
||||
' <div class="circle-clipper left">\n' +
|
||||
' <div class="circle"></div>\n' +
|
||||
' </div><div class="gap-patch">\n' +
|
||||
' <div class="circle"></div>\n' +
|
||||
' </div><div class="circle-clipper right">\n' +
|
||||
' <div class="circle"></div>\n' +
|
||||
' </div>\n' +
|
||||
' </div>\n' +
|
||||
' </div>');
|
||||
|
||||
var timeout = setTimeout(function () {
|
||||
if (timeout) {
|
||||
timeout = null;
|
||||
$card.find('.btn-small').addClass('disabled');
|
||||
$card.find('.card-content-text').html(_('offline'));
|
||||
}
|
||||
}, 6000);
|
||||
|
||||
formatInfo['Disk size'] = formatInfo['Disk size'] || that.main.formatBytes;
|
||||
formatInfo['Disk free'] = formatInfo['Disk size'] || that.main.formatBytes;
|
||||
|
||||
that.main.socket.emit('sendToHost', host.id, 'getHostInfo', null, function (data) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
if (data === 'permissionError') {
|
||||
console.error('May not read "getHostInfo"');
|
||||
} else if (!data) {
|
||||
console.error('Cannot read "getHostInfo"');
|
||||
} else {
|
||||
$card.find('.btn-small').removeClass('disabled');
|
||||
}
|
||||
|
||||
var diskWarning = that.main.states['system.host.' + that.main.currentHost + '.diskWarning'];
|
||||
if (diskWarning) {
|
||||
diskWarning = parseFloat(diskWarning.val);
|
||||
} else {
|
||||
diskWarning = 5;
|
||||
}
|
||||
var hasWarning = data['Disk size'] > 0 && data['Disk free'] && Math.round((data['Disk free'] / data['Disk size']) * 100) < diskWarning;
|
||||
|
||||
var text = '<div class="card-content-text">';
|
||||
if (data) {
|
||||
text += '<ul>';
|
||||
for (var item in data) {
|
||||
if (data.hasOwnProperty(item) && (item === 'Platform' || item === 'NPM' || item === 'RAM' || item === 'Node.js')) {
|
||||
text += '<li><b>' + _(item) + ': </b>';
|
||||
text += '<span class="system-info" data-attribute="' + item + '">' + (formatInfo[item] ? formatInfo[item](data[item]) : data[item] || ' --') + '</span></li>';
|
||||
}
|
||||
}
|
||||
if (hasWarning) {
|
||||
text += '<li><b>' + _('Disk free') + ': </b>';
|
||||
text += '<span class="system-info system-warning" data-attribute="Disk free">' + (formatInfo['Disk free'] ? formatInfo['Disk free'](data['Disk free']) : data['Disk free'] || ' --') + '</span></li>';
|
||||
text += '<li><b>' + _('Disk size') + ': </b>';
|
||||
text += '<span class="system-info" data-attribute="Disk size">' + (formatInfo['Disk size'] ? formatInfo['Disk size'](data['Disk size']) : data['Disk size'] || ' --') + '</span></li>';
|
||||
}
|
||||
|
||||
text += '</ul>';
|
||||
}
|
||||
text += '</div>';
|
||||
$card.find('.card-content-text').replaceWith($(text));
|
||||
|
||||
text = '<div class="card-reveal"><h5>' + _('Info') + '</h5><a class="btn-info" title="' + _('Copy to clipboard') + '"><i class="material-icons">content_copy</i></a><span class="card-title grey-text text-darken-4"><i class="material-icons right">close</i></span>';
|
||||
var clippy = [];
|
||||
|
||||
if (data) {
|
||||
text += '<ul>';
|
||||
for (var item_ in data) {
|
||||
if (data.hasOwnProperty(item_)) {
|
||||
text += '<li><b>' + _(item_) + ': </b>';
|
||||
var formatted = (formatInfo[item_] ? formatInfo[item_](data[item_]) : data[item_]);
|
||||
clippy.push(item_ + ': ' + formatted);
|
||||
if (item_ === 'Disk free' && hasWarning) {
|
||||
text += '<span class="system-info system-warning" data-attribute="' + item_ + '">' + formatted + '</span></li>';
|
||||
} else {
|
||||
text += '<span class="system-info" data-attribute="' + item_ + '">' + formatted + '</span></li>';
|
||||
}
|
||||
}
|
||||
}
|
||||
text += '</ul>';
|
||||
}
|
||||
text += '</div>';
|
||||
$card.find('.card-reveal').replaceWith($(text));
|
||||
if (that.$tab.hasClass('edit-active')) {
|
||||
$card.find('.btn-small').hide();
|
||||
} else {
|
||||
$card.find('.btn-info').data('clippy', clippy.join('\r\n')).on('click', copyToClipboard);
|
||||
}
|
||||
});
|
||||
return $card;
|
||||
}
|
||||
|
||||
function buildOneCard(adapter, instance, welcomeScreen, common, url, web, enabled) {
|
||||
var $card = that.$template.clone();
|
||||
$card.removeAttr('id');
|
||||
var urlText = url.replace(/^https?:\/\//, '');
|
||||
var pos = urlText.indexOf('/');
|
||||
if (pos !== -1) {
|
||||
urlText = urlText.substring(0, pos);
|
||||
}
|
||||
if (adapter === 'admin' && urlText === location.host) return null;
|
||||
if (adapter === 'web') return null;
|
||||
if (adapter !== 'vis-web-admin' && adapter.match(/^vis-/)) return null; // no widgets
|
||||
if (adapter.match(/^icons-/)) return null; // no icons
|
||||
if (common && common.noIntro) return null;
|
||||
|
||||
$card.find('.btn-card-enabled').data('instance', adapter + '.' + instance + (welcomeScreen && welcomeScreen.name ? '.' + welcomeScreen.name : '')).data('web', web);
|
||||
|
||||
// button enabled
|
||||
if (!enabled) {
|
||||
$card.addClass('card-disabled').find('.btn-card-enabled').removeClass('blue').addClass('gray').find('i').text('close');
|
||||
} else {
|
||||
$card.on('click', function () {
|
||||
var editActive = that.$tab.hasClass('edit-active');
|
||||
if (editActive) return;
|
||||
window.open($(this).find('.url').attr('href'));
|
||||
})
|
||||
}
|
||||
|
||||
// link
|
||||
$card.find('.url').attr('href', typeof url === 'object' ? url._first : url || '').text(urlText + (web ? ' (' + web + ')' : ''));
|
||||
// icon
|
||||
$card.find('.card-image-img').attr('src', common.icon ? 'adapter/' + adapter + '/' + common.icon : 'img/no-image.png');
|
||||
if (welcomeScreen && welcomeScreen.color) {
|
||||
$card.find('.card-image').css('background', welcomeScreen.color);
|
||||
}
|
||||
|
||||
// title
|
||||
var title = (welcomeScreen && welcomeScreen.name) || common.titleLang || common.title;
|
||||
if (typeof title === 'object') {
|
||||
title = title[systemLang] || title.en;
|
||||
}
|
||||
$card.find('.card-titles').text(title || adapter);
|
||||
|
||||
var desc = common.desc;
|
||||
if (typeof desc === 'object') {
|
||||
desc = desc[systemLang] || desc.en;
|
||||
}
|
||||
$card.find('.card-content-text').text(desc || '');
|
||||
return $card;
|
||||
}
|
||||
|
||||
function getCards(instances, callback) {
|
||||
var list = JSON.parse(JSON.stringify(instances));
|
||||
var a;
|
||||
var $cards = [];
|
||||
var enabled;
|
||||
var $card;
|
||||
list.sort(function (a, b) {
|
||||
a = that.main.objects[a] && that.main.objects[a].common;
|
||||
b = that.main.objects[b] && that.main.objects[b].common;
|
||||
a = a || {};
|
||||
b = b || {};
|
||||
if (a.order === undefined && b.order === undefined) {
|
||||
if (a.name.toLowerCase() > b.name.toLowerCase()) return 1;
|
||||
if (a.name.toLowerCase() < b.name.toLowerCase()) return -1;
|
||||
return 0;
|
||||
} else if (a.order === undefined) {
|
||||
return -1;
|
||||
} else if (b.order === undefined) {
|
||||
return 1;
|
||||
} else {
|
||||
if (a.order > b.order) return 1;
|
||||
if (a.order < b.order) return -1;
|
||||
if (a.name.toLowerCase() > b.name.toLowerCase()) return 1;
|
||||
if (a.name.toLowerCase() < b.name.toLowerCase()) return -1;
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
var editActive = that.$tab.hasClass('edit-active');
|
||||
|
||||
var urls = [];
|
||||
|
||||
for (a = 0; a < list.length; a++) {
|
||||
var obj = that.main.objects[list[a]];
|
||||
var common = obj && obj.common;
|
||||
|
||||
if (common && (common.enabled || common.onlyWWW) && (common.localLinks || common.localLink)) {
|
||||
var link = common.localLinks || common.localLink || '';
|
||||
var adapter = list[a].substring('system.adapter.'.length).split('.');
|
||||
var instance = adapter[1];
|
||||
adapter = adapter[0];
|
||||
var url = link ? that.main.tabs.instances.replaceInLink(link, adapter, instance) : '';
|
||||
$card = null;
|
||||
var intro = that.main.systemConfig.common.intro[adapter + '.' + instance];
|
||||
if (typeof url === 'object') {
|
||||
var first = true;
|
||||
for (var inst in url) {
|
||||
if (url.hasOwnProperty(inst)) {
|
||||
enabled = true;
|
||||
if (intro !== undefined) {
|
||||
if (first && typeof intro !== 'object') {
|
||||
var val = intro;
|
||||
intro = {};
|
||||
that.main.systemConfig.common.intro[adapter + '.' + instance] = intro;
|
||||
intro[inst] = val;
|
||||
}
|
||||
if (intro[inst] === false) {
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
first = false;
|
||||
|
||||
if (!editActive && !enabled) continue;
|
||||
if (urls.indexOf(url[inst]) === -1) {
|
||||
$card = buildOneCard(adapter, instance, null, common, url[inst], inst, enabled);
|
||||
$card && $cards.push($card);
|
||||
urls.push(url[inst]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
enabled = true;
|
||||
if (!editActive && intro !== undefined) {
|
||||
if (typeof intro === 'object') {
|
||||
for (var aa in intro) {
|
||||
if (intro.hasOwnProperty(aa)) {
|
||||
intro = intro[aa];
|
||||
that.main.systemConfig.common.intro[adapter + '.' + instance] = intro;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (intro === false) {
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
var welcomeScreen = common.welcomeScreen; //common.welcomeScreenPro || common.welcomeScreen;
|
||||
while (welcomeScreen) {
|
||||
intro = that.main.systemConfig.common.intro[adapter + '.' + instance + '.' + welcomeScreen.name];
|
||||
if (!editActive && intro !== undefined) {
|
||||
if (typeof intro === 'object') {
|
||||
for (var aaa in intro) {
|
||||
if (intro.hasOwnProperty(aaa)) {
|
||||
intro = intro[aaa];
|
||||
that.main.systemConfig.common.intro[adapter + '.' + instance + '.' + welcomeScreen.name] = intro;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (intro === false) {
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
that.main.systemConfig.common.intro[adapter + '.' + instance + '.' + welcomeScreen.name] = intro;
|
||||
if (editActive || enabled) {
|
||||
var welcomeUrl = url;
|
||||
var m = welcomeUrl.match(/https?:\/\/[.\w\d-]+:\d+\/(.*)$/);
|
||||
if (m) {
|
||||
welcomeUrl = welcomeUrl.replace(m[1], welcomeScreen.link);
|
||||
} else {
|
||||
welcomeUrl += welcomeScreen.link;
|
||||
}
|
||||
if (welcomeUrl !== url && urls.indexOf(welcomeUrl) === -1) {
|
||||
$card = buildOneCard(adapter, instance, welcomeScreen, common, welcomeUrl, null, enabled, welcomeScreen);
|
||||
$card && $cards.push($card);
|
||||
urls.push(welcomeUrl);
|
||||
}
|
||||
}
|
||||
|
||||
if (welcomeScreen === common.welcomeScreenPro) {
|
||||
welcomeScreen = common.welcomeScreen;
|
||||
} else {
|
||||
welcomeScreen = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!editActive && !enabled) continue;
|
||||
|
||||
if (urls.indexOf(url) === -1) {
|
||||
$card = buildOneCard(adapter, instance, null, common, url, null, enabled);
|
||||
$card && $cards.push($card);
|
||||
urls.push(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < that.main.tabs.hosts.list.length; i++) {
|
||||
$card = buildInfoCard(that.main.tabs.hosts.list[i]);
|
||||
$card && $cards.push($card);
|
||||
}
|
||||
|
||||
callback(null, $cards);
|
||||
}
|
||||
|
||||
function updateConfig(callback) {
|
||||
var values = [];
|
||||
that.$tiles.find('.btn-card-enabled').each(function () {
|
||||
var inst = $(this).data('instance');
|
||||
if (inst) {
|
||||
values.push({
|
||||
id: inst,
|
||||
enabled: !$(this).hasClass('gray'),
|
||||
web: $(this).data('web')
|
||||
});
|
||||
} else {
|
||||
values.push({
|
||||
id: $(this).data('host'),
|
||||
enabled: !$(this).hasClass('gray')
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// update all values
|
||||
var intro = that.main.systemConfig.common.intro;
|
||||
var changed = true;
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var actual;
|
||||
var id = values[i].id;
|
||||
if (values[i].web) {
|
||||
if (intro[id] !== undefined && typeof intro[id] !== 'object') {
|
||||
var val = intro[id];
|
||||
intro[id] = {};
|
||||
intro[id][values[i].web] = val;
|
||||
}
|
||||
actual = intro[id] && intro[id][values[i].web] !== undefined ? intro[id][values[i].web] : true;
|
||||
} else {
|
||||
if (intro[id] !== undefined && typeof intro[id] === 'object') {
|
||||
for (var aa in intro[id]) {
|
||||
if (intro[id].hasOwnProperty(aa)) {
|
||||
intro[id] = intro[id][aa];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
actual = intro[id] !== undefined ? intro[id] : true;
|
||||
}
|
||||
if (values[i].enabled !== actual) {
|
||||
changed = true;
|
||||
if (values[i].web) {
|
||||
intro[id] = intro[id] || {};
|
||||
intro[id][values[i].web] = values[i].enabled;
|
||||
} else {
|
||||
intro[id] = values[i].enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
that.main.socket.emit('getObject', 'system.config', function (err, obj) {
|
||||
if (obj) {
|
||||
obj.common.intro = that.main.systemConfig.common.intro;
|
||||
that.main.socket.emit('setObject', obj._id, obj, function (err) {
|
||||
callback && callback();
|
||||
});
|
||||
} else {
|
||||
callback && callback();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback && callback();
|
||||
}
|
||||
}
|
||||
|
||||
this.prepare = function () {
|
||||
this.$tab.find('.btn-edit').off('click').on('click', function () {
|
||||
that.$tab.addClass('edit-active');
|
||||
showTiles();
|
||||
});
|
||||
this.$tab.find('.btn-edit-ok').off('click').on('click', function () {
|
||||
updateConfig(function () {
|
||||
that.$tab.removeClass('edit-active');
|
||||
showTiles();
|
||||
that.main.showToast(that.$tiles, _('Updated'));
|
||||
});
|
||||
});
|
||||
this.$tab.find('.btn-edit-cancel').off('click').on('click', function () {
|
||||
that.$tab.removeClass('edit-active');
|
||||
showTiles();
|
||||
});
|
||||
};
|
||||
|
||||
function showTiles(instances, callback) {
|
||||
instances = instances || that.main.instances;
|
||||
|
||||
getCards(instances, function (err, $cards) {
|
||||
that.$tiles.html('');
|
||||
for (var c = 0; c < $cards.length; c++) {
|
||||
that.$tiles.append($cards[c]);
|
||||
}
|
||||
|
||||
if (that.$tab.hasClass('edit-active')) {
|
||||
that.$tiles.find('.btn-card-enabled').on('click', function () {
|
||||
var enabled = !$(this).hasClass('gray');
|
||||
if (enabled) {
|
||||
$(this).removeClass('blue').addClass('gray').find('i').text('close');
|
||||
$(this).parent().addClass('card-disabled');
|
||||
} else {
|
||||
$(this).addClass('blue').removeClass('gray').find('i').text('check');
|
||||
$(this).parent().removeClass('card-disabled')
|
||||
}
|
||||
});
|
||||
}
|
||||
callback && callback();
|
||||
});
|
||||
}
|
||||
// ----------------------------- Site cards show and Edit ------------------------------------------------
|
||||
this.init = function (update) {
|
||||
if (this.inited && !update) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.main.objectsLoaded) {
|
||||
setTimeout(function () {
|
||||
that.init(update);
|
||||
}, 250);
|
||||
return;
|
||||
}
|
||||
|
||||
that.main.systemConfig.common.intro = that.main.systemConfig.common.intro || {};
|
||||
|
||||
// update info
|
||||
readInstances(function (err, instances) {
|
||||
showTiles(instances, function () {
|
||||
that.restoreScroll();
|
||||
});
|
||||
});
|
||||
|
||||
// Required is list of hosts and repository (done in getAdaptersInfo)
|
||||
if (!this.inited) {
|
||||
this.inited = true;
|
||||
this.main.subscribeObjects('system.adapter.*');
|
||||
this.main.subscribeObjects('system.host.*');
|
||||
}
|
||||
};
|
||||
this.saveScroll = function () {
|
||||
this.scrollTop = this.$tiles.scrollTop();
|
||||
};
|
||||
this.restoreScroll = function () {
|
||||
if (that.scrollTop) {
|
||||
that.$tiles.scrollTop(that.scrollTop);
|
||||
}
|
||||
};
|
||||
this.destroy = function () {
|
||||
if (this.inited) {
|
||||
this.saveScroll();
|
||||
this.inited = false;
|
||||
this.main.unsubscribeObjects('system.adapter.*');
|
||||
this.main.unsubscribeObjects('system.host.*');
|
||||
}
|
||||
};
|
||||
|
||||
this.objectChange = function (id /*, obj, action*/) {
|
||||
// Update Adapter Table
|
||||
if (this.inited && (id.match(/^system\.adapter\.[a-zA-Z0-9-_]+\.\d+$/) || id.match(/^system\.host\./))) {
|
||||
if (this.updateTimeout) {
|
||||
this.updateTimeout = clearTimeout(this.updateTimeout);
|
||||
}
|
||||
this.updateTimeout = setTimeout(function () {
|
||||
showTiles();
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/* global systemLang, showdown */
|
||||
|
||||
function Issue(main) {
|
||||
'use strict';
|
||||
|
||||
var that = this;
|
||||
this.$dialogIssue = $('#dialog-issue');
|
||||
this.main = main;
|
||||
|
||||
this.prepare = function () {
|
||||
};
|
||||
|
||||
this.init = function () {
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.inited = true;
|
||||
|
||||
showdown.setFlavor('github');
|
||||
|
||||
var id = this.main.navigateGetParams();
|
||||
var name = id.replace(/^system\.adapter\./, '');
|
||||
|
||||
var adapter = this.main.objects[id];
|
||||
if (adapter && adapter.common && adapter.common.extIcon) {
|
||||
var tmp = adapter.common.extIcon.split('/');
|
||||
var $table = that.$dialogIssue.find('#result-issue');
|
||||
$table.html(
|
||||
'<div class="loader"><svg class="spinner" width="100%" height="100%" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">\n' +
|
||||
' <circle class="path" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle>\n' +
|
||||
'</svg></div>');
|
||||
var count = 0;
|
||||
|
||||
$.getJSON('https://api.github.com/repos/' + tmp[3] + '/' + tmp[4] + '/issues', function (data) {
|
||||
var bug = false;
|
||||
|
||||
$table.empty();
|
||||
for (var i in data) {
|
||||
if (!data.hasOwnProperty(i)) continue;
|
||||
if (i === 'remove') {
|
||||
break;
|
||||
}
|
||||
var issue = data[i];
|
||||
if (issue.hasOwnProperty('pull_request')) {
|
||||
continue;
|
||||
}
|
||||
bug = true;
|
||||
|
||||
var $issueElement = $('#issueTable').children().clone(true, true);
|
||||
$issueElement.find('.collapsible-header-title').text(issue.title);
|
||||
$issueElement.find('.goto').attr('href', issue.html_url);
|
||||
$issueElement.find('.user').text(issue.user.login);
|
||||
$issueElement.find('.form-row').html(new showdown.Converter().makeHtml(issue.body).replace(/src="/g, 'class="responsive-img" src="'));
|
||||
var issueDate = new Date(new Date(issue.created_at));
|
||||
$issueElement.find('.created').text(issueDate.toLocaleDateString(systemLang, {'weekday': 'short', 'year': 'numeric', 'month': 'long', 'day': '2-digit', 'hour': '2-digit', 'minute': '2-digit', 'second': '2-digit'}));
|
||||
if (issue.labels.length > 0) {
|
||||
for (var k in issue.labels) {
|
||||
if (!issue.labels.hasOwnProperty(k)) continue;
|
||||
if (k === 'remove') {
|
||||
break;
|
||||
}
|
||||
$issueElement.find('.category').append('<a class="btn-floating btn-small translateT" style="background:#' + issue.labels[k].color + ';" title="' + issue.labels[k].name + '"><span>' + issue.labels[k].name + '</span></a>');
|
||||
}
|
||||
}
|
||||
|
||||
$table.append($issueElement);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (!bug) {
|
||||
$table.append($('<li><h3 class="title">' + _('No bug') + '</h3></li>'));
|
||||
}
|
||||
|
||||
}).done(that.$dialogIssue.find('.collapsible').collapsible());
|
||||
}
|
||||
|
||||
that.$dialogIssue.data('name', name);
|
||||
that.$dialogIssue.find('.title').html(_('Known bugs for') + ': ' + name);
|
||||
that.$dialogIssue.find('.dialog-system-buttons .btn-add').attr('href', 'https://github.com/' + tmp[3] + '/' + tmp[4] + '/issues/new');
|
||||
that.$dialogIssue.find('.dialog-system-buttons .btn-cancel').off('click').on('click', function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
that.main.navigate();
|
||||
});
|
||||
};
|
||||
|
||||
this.destroy = function () {
|
||||
if (this.inited) {
|
||||
this.$dialogIssue.find('.collapsible').collapsible('destroy');
|
||||
this.inited = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
function Logs(main) { 'use strict';
|
||||
|
||||
var that = this;
|
||||
this.main = main;
|
||||
this.$tab = $('#tab-logs');
|
||||
|
||||
var list = {
|
||||
count: 0,
|
||||
start: 0,
|
||||
limit: 2000 //const
|
||||
};
|
||||
|
||||
var $table;
|
||||
var $outer;
|
||||
var $pause;
|
||||
|
||||
var filters = {
|
||||
host: '',
|
||||
severity: '',
|
||||
message: ''
|
||||
};
|
||||
|
||||
var pause = {
|
||||
list: [],
|
||||
mode: false,
|
||||
counter: 0,
|
||||
overflow: false,
|
||||
$counterSpan: null
|
||||
};
|
||||
|
||||
var hdr;
|
||||
|
||||
this.prepare = function () {
|
||||
$outer = this.$tab.find('#log-outer');
|
||||
$table = this.$tab.find('#log-table');
|
||||
$pause = this.$tab.find('#log-pause');
|
||||
|
||||
hdr = new IobListHeader('log-outer-header', {list: $outer, colWidthOffset: 1, prefix: 'log-filter'});
|
||||
hdr.doFilter = that.filter;
|
||||
|
||||
hdr.add('combobox', 'from', 'host');
|
||||
hdr.add('text', 'Time');
|
||||
hdr.add('combobox', '', 'severity', [
|
||||
{val: '', name: 'debug'},
|
||||
{val: 'silly', name: 'silly'},
|
||||
{val: 'info', name: 'info'},
|
||||
{val: 'warn', name: 'warn'},
|
||||
{val: 'error', name: 'error'}
|
||||
]).$filter.attr('title', _('severity'));
|
||||
hdr.add('edit', 'Message', 'message');
|
||||
|
||||
this.$tab.find('#log-clear-on-disk').on('click', function () {
|
||||
that.main.confirmMessage(_('Log file will be deleted. Are you sure?'), null, null, function (result) {
|
||||
if (result) {
|
||||
that.main.socket.emit('sendToHost', main.currentHost, 'delLogs', null, function (err) {
|
||||
if (err) {
|
||||
that.main.showError(err);
|
||||
} else {
|
||||
that.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}).addClass('ui-state-error');
|
||||
|
||||
this.$tab.find('#log-refresh').on('click', function () {
|
||||
that.clear();
|
||||
});
|
||||
|
||||
$pause
|
||||
.attr('title', _('Pause output'))
|
||||
.on('click', function () {
|
||||
that.pause();
|
||||
});
|
||||
|
||||
pause.$counterSpan = $pause.find('ui-button-text');
|
||||
|
||||
this.$tab.find('#log-clear').on('click', function () {
|
||||
that.clear(false);
|
||||
});
|
||||
|
||||
$('#log-copy-text').on('click', function () {
|
||||
$(this).hide().html('');
|
||||
});
|
||||
|
||||
this.$tab.find('#log-copy').on('click', function () {
|
||||
var text = '<span class="error">' + _('copy note') + '</span>';
|
||||
$('#log-copy-text').show().html(text + '<br><table style="width: 100%; font-size: 12px" id="log-copy-table">' + $table.html() + '</table>');
|
||||
var lines = that.$tab.find('#log-copy-table').find('.log-column-4');
|
||||
for (var t = 0; t < lines.length; t++) {
|
||||
var q = $(lines[t]);
|
||||
q.html(q.attr('title'));
|
||||
q.attr('title', '');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function installColResize() {
|
||||
if (!$.fn.colResizable) return;
|
||||
if ($outer.is(':visible')) {
|
||||
$outer.colResizable({
|
||||
liveDrag: true,
|
||||
|
||||
partialRefresh: true,
|
||||
marginLeft: 5,
|
||||
postbackSafe:true,
|
||||
|
||||
onResize: function (event) {
|
||||
return hdr.syncHeader();
|
||||
}
|
||||
});
|
||||
hdr && hdr.syncHeader();
|
||||
} else {
|
||||
setTimeout(function () {
|
||||
installColResize();
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------- Logs ------------------------------------------------------------
|
||||
this.init = function (update) {
|
||||
if (this.inited && !update) {
|
||||
return;
|
||||
}
|
||||
if (!this.main.currentHost) {
|
||||
setTimeout(function () {
|
||||
that.init(update);
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
$table.html('');
|
||||
var doSubscribe = !this.inited;
|
||||
this.inited = true;
|
||||
|
||||
this.main.socket.emit('sendToHost', this.main.currentHost, 'getLogs', 200, function (lines) {
|
||||
setTimeout(function () {
|
||||
var message = {message: '', severity: 'debug', from: '', ts: ''};
|
||||
var size = lines ? lines.pop() : -1;
|
||||
if (size !== -1) {
|
||||
size = parseInt(size);
|
||||
that.$tab.find('#log-size').html((_('Log size:') + ' ' + ((size / (1024 * 1024)).toFixed(2) + ' MB ')).replace(/ /g, ' '));
|
||||
}
|
||||
for (var i = 0, len = lines.length; i < len; i++) {
|
||||
if (!lines[i]) continue;
|
||||
var line = lines[i];
|
||||
// 2014-12-05 14:47:10.739 - info: yunkong2 ERR! network In most cases you are behind a proxy or have bad network settings.npm ERR! network
|
||||
if (line[4] === '-' && line[7] === '-') {
|
||||
line = line.replace(/(\[[0-9]+m)/g, '');
|
||||
message.ts = line.substring(0, 23);
|
||||
line = line.substring(27);
|
||||
|
||||
var pos = line.indexOf(':');
|
||||
message.severity = line.substring(0, pos);
|
||||
if (message.severity.charCodeAt(message.severity.length - 1) === 27) message.severity = message.severity.substring(0, message.severity.length - 1);
|
||||
if (message.severity.charCodeAt(0) === 27) message.severity = message.severity.substring(1);
|
||||
|
||||
line = line.substring(pos + 2);
|
||||
pos = line.indexOf(' ');
|
||||
message.from = line.substring(0, pos);
|
||||
message.message = line.substring(pos);
|
||||
} else {
|
||||
message.message = line;
|
||||
}
|
||||
that.add(message);
|
||||
}
|
||||
|
||||
installColResize();
|
||||
|
||||
filters.host = hdr.host.val();
|
||||
filters.message = hdr.message.val();
|
||||
filters.severity = hdr.severity.val();
|
||||
if (doSubscribe) {
|
||||
that.main.subscribeLogs(true);
|
||||
}
|
||||
|
||||
// prepare log list
|
||||
that.main.socket.emit('readLogs', function (err, list) {
|
||||
if (list && list.length) {
|
||||
var html = '';
|
||||
list.reverse();
|
||||
// first 2018-01-01
|
||||
for (var l = 0; l < list.length; l++) {
|
||||
var parts = list[l].split('/');
|
||||
var name = parts.pop().replace(/yunkong2\.?/, '').replace('.log', '');
|
||||
if (name[0] <= '9') {
|
||||
html += '<li><a data-value="' + list[l] + '">' + name + '</a></li>';
|
||||
}
|
||||
}
|
||||
// then restart.log ans so on
|
||||
list.sort();
|
||||
for (var ll = 0; ll < list.length; ll++) {
|
||||
var parts_ = list[ll].split('/');
|
||||
var name_ = parts_.pop().replace(/yunkong2\.?/, '').replace('.log', '');
|
||||
if (name_[0] > '9') {
|
||||
html += '<li><a data-value="' + list[ll] + '">' + name_ + '</a></li>';
|
||||
}
|
||||
}
|
||||
|
||||
that.$tab.find('#log-files-btn').show().dropdown();
|
||||
that.$tab.find('#log-files')
|
||||
.html(html)
|
||||
.find('a').on('click', function () {
|
||||
var val = $(this).data('value');
|
||||
if (val) {
|
||||
$(this).val('');
|
||||
var win = window.open(val, '_blank');
|
||||
win.focus();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
that.$tab.find('#log-files').hide();
|
||||
that.$tab.find('#log-files-btn').hide();
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
});
|
||||
};
|
||||
|
||||
this.destroy = function () {
|
||||
if (this.inited) {
|
||||
this.inited = false;
|
||||
this.main.subscribeLogs(false);
|
||||
}
|
||||
};
|
||||
|
||||
this.add = function (message) {
|
||||
if (!$table) return;
|
||||
// remove instance name from text
|
||||
if (message.message.substring(0, message.from.length) === message.from) {
|
||||
message.message = message.message.substring(message.from.length + 1);
|
||||
}
|
||||
|
||||
if (pause.mode) {
|
||||
pause.list.push(message);
|
||||
pause.counter++;
|
||||
|
||||
if (pause.counter > list.limit) {
|
||||
if (!pause.overflow) {
|
||||
$pause.addClass('ui-state-error')
|
||||
.attr('title', _('Message buffer overflow. Losing oldest'));
|
||||
pause.overflow = true;
|
||||
}
|
||||
pause.list.shift();
|
||||
}
|
||||
pause.$counterSpan.html(tdp(pause.counter));
|
||||
return;
|
||||
}
|
||||
|
||||
//message = {message: msg, severity: level, from: this.namespace, ts: (new Date()).getTime()}
|
||||
if (list.count >= list.limit) {
|
||||
var line = document.getElementById('log-line-' + (list.start + 1));
|
||||
if (line) line.outerHTML = '';
|
||||
list.start++;
|
||||
} else {
|
||||
list.count++;
|
||||
}
|
||||
|
||||
// if (message.from && this.logHosts.indexOf(message.from) === -1) {
|
||||
// this.logHosts.push(message.from);
|
||||
// this.logHosts.sort();
|
||||
// this.$logFilterHost.html('<option value="">' + _('all') + '</option>');
|
||||
// for (var i = 0; i < this.logHosts.length; i++) {
|
||||
// this.$logFilterHost.append('<option value="' + this.logHosts[i].replace(/\./g, '-') + '" ' + ((this.logHosts[i] === filters.host) ? 'selected' : '') + '>' + this.logHosts[i] + '</option>');
|
||||
// }
|
||||
// }
|
||||
|
||||
if (message.from && hdr) {
|
||||
hdr.host.checkAddOption(message.from, function (o) {
|
||||
return { val: o.replace(/\./g, '-'), name: o };
|
||||
});
|
||||
}
|
||||
|
||||
var visible = '';
|
||||
var from = message.from ? message.from.replace(/\./g, '-') : '';
|
||||
|
||||
if (filters.host && filters.host !== from) visible = 'display: none';
|
||||
|
||||
if (!visible && filters.severity) {
|
||||
if (filters.severity === 'debug' && message.severity === 'silly') {
|
||||
visible = 'display: none';
|
||||
} else if (filters.severity === 'info' && (message.severity === 'debug' || message.severity === 'silly')) {
|
||||
visible = 'display: none';
|
||||
} else if (filters.severity === 'warn' && message.severity !== 'warn' && message.severity !== 'error') {
|
||||
visible = 'display: none';
|
||||
} else if (filters.severity === 'error' && message.severity !== 'error') {
|
||||
visible = 'display: none';
|
||||
}
|
||||
}
|
||||
|
||||
if (!visible && filters.message && message.message.indexOf(filters.message) === -1) {
|
||||
visible = 'display: none';
|
||||
}
|
||||
|
||||
if (message.severity === 'error') {
|
||||
that.$tabLogs = that.$tabLogs || $('a[href="#tab-logs"]');
|
||||
that.$tabLogs.addClass('errorLog');
|
||||
}
|
||||
|
||||
var text = '<tr id="log-line-' + (list.start + list.count) + '" class="log-line log-severity-' + message.severity + ' ' + (from ? 'log-from-' + from : '') + '" style="' + visible + '">';
|
||||
text += '<td class="log-column-1">' + (message.from || '') + '</td>';
|
||||
text += '<td class="log-column-2">' + this.main.formatDate(message.ts) + '</td>';
|
||||
text += '<td class="log-column-3">' + message.severity + '</td>';
|
||||
text += '<td class="log-column-4" title="' + message.message.replace(/"/g, "'") + '">' + message.message.substring(0, 200).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>') + '</td></tr>';
|
||||
|
||||
$table.prepend(text);
|
||||
};
|
||||
|
||||
this.filter = function () {
|
||||
filters.host = hdr.host.val();
|
||||
filters.message = hdr.message.val();
|
||||
filters.severity = hdr.severity.val();
|
||||
|
||||
if (filters.severity === 'error') {
|
||||
$outer.find('.log-severity-silly').hide();
|
||||
$outer.find('.log-severity-debug').hide();
|
||||
$outer.find('.log-severity-info').hide();
|
||||
$outer.find('.log-severity-warn').hide();
|
||||
$outer.find('.log-severity-error').show();
|
||||
} else
|
||||
if (filters.severity === 'warn') {
|
||||
$outer.find('.log-severity-silly').hide();
|
||||
$outer.find('.log-severity-debug').hide();
|
||||
$outer.find('.log-severity-info').hide();
|
||||
$outer.find('.log-severity-warn').show();
|
||||
$outer.find('.log-severity-error').show();
|
||||
} else
|
||||
if (filters.severity === 'info') {
|
||||
$outer.find('.log-severity-silly').hide();
|
||||
$outer.find('.log-severity-debug').hide();
|
||||
$outer.find('.log-severity-info').show();
|
||||
$outer.find('.log-severity-warn').show();
|
||||
$outer.find('.log-severity-error').show();
|
||||
} else
|
||||
if (filters.severity === 'silly') {
|
||||
$outer.find('.log-severity-silly').show();
|
||||
$outer.find('.log-severity-debug').show();
|
||||
$outer.find('.log-severity-info').show();
|
||||
$outer.find('.log-severity-warn').show();
|
||||
$outer.find('.log-severity-error').show();
|
||||
} else {
|
||||
$outer.find('.log-severity-silly').hide();
|
||||
$outer.find('.log-severity-debug').show();
|
||||
$outer.find('.log-severity-info').show();
|
||||
$outer.find('.log-severity-warn').show();
|
||||
$outer.find('.log-severity-error').show();
|
||||
}
|
||||
|
||||
if (filters.host || filters.message) {
|
||||
$outer.find('.log-line').each(function () {
|
||||
if (filters.host && !$(this).hasClass('log-from-' + filters.host)) {
|
||||
$(this).hide();
|
||||
} else if (filters.message && $(this).html().indexOf(filters.message) === -1) {
|
||||
$(this).hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.clear = function (isReload) {
|
||||
if (isReload === undefined) isReload = true;
|
||||
$table.html('');
|
||||
list.count = 0;
|
||||
list.start = 0;
|
||||
|
||||
that.$tabLogs = that.$tabLogs || $('a[href="#tab-logs"]');
|
||||
that.$tabLogs.removeClass('errorLog');
|
||||
|
||||
if (isReload) {
|
||||
setTimeout(function () {
|
||||
that.init(isReload);
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
this.pause = function () {
|
||||
if (!pause.mode) {
|
||||
$pause
|
||||
.addClass('yellow btn-pause-button-active');
|
||||
|
||||
pause.$counterSpan = $pause;
|
||||
pause.$counterSpan.html('0');
|
||||
pause.counter = 0;
|
||||
pause.mode = true;
|
||||
} else {
|
||||
pause.mode = false;
|
||||
for (var i = 0; i < pause.list.length; i++) {
|
||||
this.add(pause.list[i]);
|
||||
}
|
||||
pause.overflow = false;
|
||||
pause.list = [];
|
||||
pause.counter = 0;
|
||||
|
||||
$pause
|
||||
.removeClass('yellow btn-pause-button-active')
|
||||
.html('<i class="material-icons">pause</i>');
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/* global systemLang, showdown */
|
||||
|
||||
function Issue(main) {
|
||||
'use strict';
|
||||
|
||||
var that = this;
|
||||
this.$dialog = $('#dialog-issue');
|
||||
this.main = main;
|
||||
|
||||
this.prepare = function () {
|
||||
};
|
||||
|
||||
this.init = function () {
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.inited = true;
|
||||
|
||||
showdown.setFlavor('github');
|
||||
|
||||
var id = this.main.navigateGetParams();
|
||||
var name = id.replace(/^system\.adapter\./, '');
|
||||
|
||||
var adapter = this.main.objects[id];
|
||||
if (adapter && adapter.common && adapter.common.extIcon) {
|
||||
var tmp = adapter.common.extIcon.split('/');
|
||||
var $table = $('#result-issue');
|
||||
$table.html(
|
||||
'<div class="loader"><svg class="spinner" width="100%" height="100%" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg">\n' +
|
||||
' <circle class="path" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle>\n' +
|
||||
'</svg></div>');
|
||||
var count = 0;
|
||||
|
||||
$.getJSON('https://api.github.com/repos/' + tmp[3] + '/' + tmp[4] + '/issues', function (data) {
|
||||
var bug = false;
|
||||
|
||||
$table.empty();
|
||||
for (var i in data) {
|
||||
if (!data.hasOwnProperty(i)) continue;
|
||||
if (i === 'remove') {
|
||||
break;
|
||||
}
|
||||
var issue = data[i];
|
||||
if (issue.hasOwnProperty('pull_request')) {
|
||||
continue;
|
||||
}
|
||||
bug = true;
|
||||
|
||||
var $issueElement = $('#issueTable').children().clone(true, true);
|
||||
$issueElement.find('.collapsible-header-title').text(issue.title);
|
||||
$issueElement.find('.goto').attr('href', issue.html_url);
|
||||
$issueElement.find('.user').text(issue.user.login);
|
||||
$issueElement.find('.form-row').html(new showdown.Converter().makeHtml(issue.body).replace(/src="/g, 'class="responsive-img" src="'));
|
||||
var issueDate = new Date(new Date(issue.created_at));
|
||||
$issueElement.find('.created').text(issueDate.toLocaleDateString(systemLang, {'weekday': 'short', 'year': 'numeric', 'month': 'long', 'day': '2-digit', 'hour': '2-digit', 'minute': '2-digit', 'second': '2-digit'}));
|
||||
if (issue.labels.length > 0) {
|
||||
for (var k in issue.labels) {
|
||||
if (!issue.labels.hasOwnProperty(k)) continue;
|
||||
if (k === 'remove') {
|
||||
break;
|
||||
}
|
||||
$issueElement.find('.category').append('<a class="btn-floating btn-small translateT" style="background:#' + issue.labels[k].color + ';" title="' + issue.labels[k].name + '"><span>' + issue.labels[k].name + '</span></a>');
|
||||
}
|
||||
}
|
||||
|
||||
$table.append($issueElement);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (!bug) {
|
||||
$table.append($('<li><h3 class="title">' + _('No bug') + '</h3></li>'));
|
||||
}
|
||||
|
||||
}).done(that.$dialog.find('.collapsible').collapsible());
|
||||
}
|
||||
|
||||
that.$dialog.data('name', name);
|
||||
that.$dialog.find('.title').html(_('Known bugs for') + ': ' + name);
|
||||
that.$dialog.find('.dialog-system-buttons .btn-add').attr('href', 'https://github.com/' + tmp[3] + '/' + tmp[4] + '/issues/new');
|
||||
that.$dialog.find('.dialog-system-buttons .btn-cancel').off('click').on('click', function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
that.main.navigate();
|
||||
});
|
||||
};
|
||||
|
||||
this.destroy = function () {
|
||||
if (this.inited) {
|
||||
this.$dialog.find('.collapsible').collapsible('destroy');
|
||||
this.inited = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,778 @@
|
||||
function Objects(main) {
|
||||
'use strict';
|
||||
|
||||
var that = this;
|
||||
this.$grid = $('#grid-objects');
|
||||
this.subscribes = {};
|
||||
this.main = main;
|
||||
|
||||
var selectId = function () {
|
||||
if (!that.$grid || !that.$grid.selectId) return;
|
||||
selectId = that.$grid.selectId.bind(that.$grid);
|
||||
return that.$grid.selectId.apply(that.$grid, arguments);
|
||||
};
|
||||
|
||||
this.prepare = function () {
|
||||
$(document).on('click', '.jump', function (e) {
|
||||
that.main.navigate({
|
||||
dialog: 'editobject',
|
||||
params: $(this).attr('data-jump-to')
|
||||
});
|
||||
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#load_grid-objects').show();
|
||||
|
||||
// Init "new object" dialog
|
||||
var $dialogNewObject = $('#dialog-new-object');
|
||||
$dialogNewObject.modal();
|
||||
|
||||
$dialogNewObject.find('.btn-add').off('click').on('click', function () {
|
||||
var name = $dialogNewObject.find('#object-tab-new-object-name').val();
|
||||
var id = name.trim();
|
||||
var parent = $dialogNewObject.find('#object-tab-new-object-parent').val();
|
||||
id = parent ? parent + '.' + id : id;
|
||||
|
||||
var type = $dialogNewObject.find('#object-tab-new-object-type').val();
|
||||
var stype = $dialogNewObject.find('#object-tab-new-state-type').val();
|
||||
id = id.replace(FORBIDDEN_CHARS, '_');
|
||||
|
||||
if (that.main.objects[id]) {
|
||||
that.main.showError(_('Object "%s" yet exists!', id));
|
||||
return;
|
||||
}
|
||||
|
||||
var obj;
|
||||
// = name.split('.').pop();
|
||||
if (type === 'state') {
|
||||
obj = {
|
||||
_id: id,
|
||||
type: 'state',
|
||||
common: {
|
||||
name: name,
|
||||
role: '',
|
||||
type: stype,
|
||||
read: true,
|
||||
write: true,
|
||||
desc: _('Manually created')
|
||||
},
|
||||
native: {}
|
||||
};
|
||||
if (stype === 'boolean') {
|
||||
obj.common.def = false;
|
||||
} else if (stype === 'switch') {
|
||||
obj.common.type = 'boolean';
|
||||
obj.common.def = false;
|
||||
obj.common.states = 'false:no;true:yes';
|
||||
} else if (stype === 'string') {
|
||||
obj.common.def = '';
|
||||
} else if (stype === 'number') {
|
||||
obj.common.min = 0;
|
||||
obj.common.max = 100;
|
||||
obj.common.def = 0;
|
||||
obj.common.unit = '%';
|
||||
} else if (stype === 'enum') {
|
||||
obj.common.type = 'number';
|
||||
obj.common.min = 0;
|
||||
obj.common.max = 5;
|
||||
obj.common.def = 0;
|
||||
obj.common.states = '0:zero;1:one;2:two;3:three;4:four;5:five';
|
||||
}
|
||||
} else if (type === 'channel') {
|
||||
obj = {
|
||||
_id: id,
|
||||
type: 'channel',
|
||||
common: {
|
||||
name: name,
|
||||
role: '',
|
||||
icon: '',
|
||||
desc: _('Manually created')
|
||||
},
|
||||
native: {}
|
||||
};
|
||||
} else {
|
||||
obj = {
|
||||
_id: id,
|
||||
type: 'device',
|
||||
common: {
|
||||
name: name,
|
||||
role: '',
|
||||
icon: '',
|
||||
desc: _('Manually created')
|
||||
},
|
||||
native: {}
|
||||
};
|
||||
}
|
||||
|
||||
that.main.socket.emit('setObject', id, obj, function (err) {
|
||||
if (err) {
|
||||
that.main.showError(err);
|
||||
return;
|
||||
}
|
||||
setTimeout(function () {
|
||||
that.main.navigate({
|
||||
dialog: 'editobject',
|
||||
params: id + ',def'
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
$dialogNewObject.find('#object-tab-new-object-type').select();
|
||||
$dialogNewObject.find('#object-tab-new-state-type').select();
|
||||
$dialogNewObject.find('#object-tab-new-object-name').on('keyup', function () {
|
||||
$(this).trigger('change');
|
||||
}).on('change', function () {
|
||||
var parent = $dialogNewObject.find('#object-tab-new-object-parent').val();
|
||||
var id = $dialogNewObject.find('#object-tab-new-object-name').val();
|
||||
id = parent ? parent + '.' + id : id;
|
||||
|
||||
$dialogNewObject.find('.title').html(_('Add new object: %s', id));
|
||||
});
|
||||
|
||||
$dialogNewObject.find('#object-tab-new-object-type').on('change', function () {
|
||||
if ($(this).val() === 'state') {
|
||||
$dialogNewObject.find('.object-tabe-new-object-tr').show();
|
||||
} else {
|
||||
$dialogNewObject.find('.object-tab-new-object-tr').hide();
|
||||
}
|
||||
});
|
||||
|
||||
/*$dialogNewObject.find('.btn-add').on('keydown', function (e) {
|
||||
if (e.keyCode === 13) {
|
||||
setTimeout(function () {
|
||||
$('#dialog-object-tab-new').trigger('click');
|
||||
}, 100);
|
||||
}
|
||||
});*/
|
||||
};
|
||||
|
||||
this.stateChange = function (id, state) {
|
||||
if (this.$grid) selectId('state', id, state);
|
||||
};
|
||||
|
||||
this.objectChange = function (id, obj, action) {
|
||||
if (this.$grid) selectId('object', id, obj, action);
|
||||
};
|
||||
|
||||
this.reinit = function () {
|
||||
this.main.dialogs.customs.check();
|
||||
if (this.$grid) {
|
||||
selectId('option', 'useHistory', this.main.dialogs.customs.customEnabled);
|
||||
selectId('reinit');
|
||||
}
|
||||
};
|
||||
|
||||
function _syncEnum(id, enumIds, newArray, cb) {
|
||||
if (!enumIds || !enumIds.length) {
|
||||
cb && cb();
|
||||
return;
|
||||
}
|
||||
|
||||
var enumId = enumIds.pop();
|
||||
if (that.main.objects[enumId] && that.main.objects[enumId].common) {
|
||||
var count = 0;
|
||||
if (that.main.objects[enumId].common.members && that.main.objects[enumId].common.members.length) {
|
||||
var pos = that.main.objects[enumId].common.members.indexOf(id);
|
||||
if (pos !== -1 && newArray.indexOf(enumId) === -1) {
|
||||
// delete from members
|
||||
that.main.objects[enumId].common.members.splice(pos, 1);
|
||||
count++;
|
||||
that.main.socket.emit('setObject', enumId, that.main.objects[enumId], function (err) {
|
||||
if (err) that.main.showError(err);
|
||||
if (!--count) {
|
||||
setTimeout(function () {
|
||||
_syncEnum(id, enumIds, newArray, cb);
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// add to it
|
||||
if (newArray.indexOf(enumId) !== -1 && (!that.main.objects[enumId].common.members || that.main.objects[enumId].common.members.indexOf(id) === -1)) {
|
||||
// add to object
|
||||
that.main.objects[enumId].common.members = that.main.objects[enumId].common.members || [];
|
||||
that.main.objects[enumId].common.members.push(id);
|
||||
count++;
|
||||
that.main.socket.emit('setObject', enumId, that.main.objects[enumId], function (err) {
|
||||
if (err) that.main.showError(err);
|
||||
if (!--count) {
|
||||
setTimeout(function () {
|
||||
_syncEnum(id, enumIds, newArray, cb);
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
setTimeout(function () {
|
||||
_syncEnum(id, enumIds, newArray, cb);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function syncEnum(id, enumName, newArray) {
|
||||
var enums = that.main.tabs.enums.list;
|
||||
var toCheck = [];
|
||||
for (var e = 0; e < enums.length; e++) {
|
||||
if (enums[e].substring(0, 'enum.'.length + enumName.length + 1) === 'enum.' + enumName + '.') {
|
||||
toCheck.push(enums[e]);
|
||||
}
|
||||
}
|
||||
|
||||
_syncEnum(id, toCheck, newArray, function (err) {
|
||||
if (err) that.main.showError(err);
|
||||
// force update of object
|
||||
selectId('object', id, that.main.objects[id]);
|
||||
});
|
||||
}
|
||||
|
||||
function requestStates(pattern) {
|
||||
console.log('Subscribe: ' + pattern);
|
||||
that.main.subscribeStates(pattern);
|
||||
that.main.socket.emit('getForeignStates', pattern, function (err, states) {
|
||||
if (states) {
|
||||
for (var _id in states) {
|
||||
console.log('Update ' + _id);
|
||||
if (!states.hasOwnProperty(_id)) continue;
|
||||
|
||||
if (!states[_id] && that.main.states[_id]) {
|
||||
that.main.states[_id] = {val: null};
|
||||
that.stateChange(_id, that.main.states[_id]); // may be call main.stateChange
|
||||
} else
|
||||
if (!that.main.states[_id] || that.main.states[_id].ts !== states[_id].ts) {
|
||||
that.main.states[_id] = states[_id];
|
||||
that.stateChange(_id, states[_id]); // may be call main.stateChange
|
||||
}
|
||||
}
|
||||
} else if (err) {
|
||||
console.error('requestStates error: ' + err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function subscribe(ids) {
|
||||
if (typeof ids === 'string') {
|
||||
ids = [ids];
|
||||
}
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
console.log('Expanded: ' + ids[i]);
|
||||
if (that.subscribes[ids[i]]) {
|
||||
that.subscribes[ids[i]]++;
|
||||
return;
|
||||
}
|
||||
for (var pattern in that.subscribes) {
|
||||
if (that.subscribes.hasOwnProperty(pattern) && ids[i].substring(0, pattern.length) + '.' === pattern + '.') {
|
||||
that.subscribes[pattern]++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
that.subscribes[ids[i]] = 1;
|
||||
var obj = that.main.objects[ids[i]];
|
||||
if (obj && obj.type === 'state') {
|
||||
requestStates(ids[i]);
|
||||
} else {
|
||||
requestStates(ids[i] + '.*');
|
||||
}
|
||||
}
|
||||
}
|
||||
function unsubscribe(id) {
|
||||
console.log('Collapsed: ' + id);
|
||||
if (!that.subscribes[id]) {
|
||||
for (var pattern in that.subscribes) {
|
||||
if (that.subscribes.hasOwnProperty(pattern) && pattern.substring(0, id.length) + '.' === id + '.') {
|
||||
that.subscribes[pattern]--;
|
||||
if (!that.subscribes[pattern]) {
|
||||
var obj = that.main.objects[pattern];
|
||||
if (obj && obj.type === 'state') {
|
||||
that.main.unsubscribeStates(pattern);
|
||||
console.log('Unsubscribe: ' + pattern);
|
||||
} else {
|
||||
that.main.unsubscribeStates(pattern + '.*');
|
||||
console.log('Unsubscribe: ' + pattern + '.*');
|
||||
}
|
||||
|
||||
delete that.subscribes[pattern]; // may be that.subscribes[id] = undefined; for speed up
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
that.subscribes[id]--;
|
||||
if (!that.subscribes[id]) {
|
||||
var _obj = that.main.objects[id];
|
||||
if (_obj && _obj.type === 'state') {
|
||||
console.log('Unsubscribe: ' + id);
|
||||
that.main.unsubscribeStates(id);
|
||||
} else {
|
||||
console.log('Unsubscribe: ' + id + '.*');
|
||||
that.main.unsubscribeStates(id + '.*');
|
||||
}
|
||||
|
||||
delete that.subscribes[id]; // may be that.subscribes[id] = undefined; for speed up
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function unsubscribeAll() {
|
||||
for (var pattern in that.subscribes) {
|
||||
if (that.subscribes.hasOwnProperty(pattern)) {
|
||||
var obj = that.main.objects[pattern];
|
||||
if (obj && that.main.objects[pattern].type === 'state') {
|
||||
that.main.unsubscribeStates(pattern);
|
||||
console.log('Unsubscribe: ' + pattern);
|
||||
} else {
|
||||
that.main.unsubscribeStates(pattern + '.*');
|
||||
console.log('Unsubscribe: ' + pattern + '.*');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function subscribeAll() {
|
||||
for (var pattern in that.subscribes) {
|
||||
if (that.subscribes.hasOwnProperty(pattern)) {
|
||||
var obj = that.main.objects[pattern];
|
||||
if (obj && that.main.objects[pattern].type === 'state') {
|
||||
requestStates(pattern);
|
||||
} else {
|
||||
requestStates(pattern + '.*');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.saveScroll = function () {
|
||||
this.scrollTop = this.$grid.find('.grid-main-wh-div').scrollTop();
|
||||
};
|
||||
this.restoreScroll = function () {
|
||||
if (this.scrollTop) {
|
||||
this.$grid.find('.grid-main-wh-div').scrollTop(this.scrollTop);
|
||||
}
|
||||
};
|
||||
|
||||
function generateFile(filename, obj) {
|
||||
var el = document.createElement('a');
|
||||
el.setAttribute('href', 'data:application/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(obj, null, 2)));
|
||||
el.setAttribute('download', filename);
|
||||
|
||||
el.style.display = 'none';
|
||||
document.body.appendChild(el);
|
||||
|
||||
el.click();
|
||||
|
||||
document.body.removeChild(el);
|
||||
}
|
||||
|
||||
this.init = function (update) {
|
||||
if (this.inited && !update) {
|
||||
return;
|
||||
}
|
||||
if (update) {
|
||||
unsubscribeAll();
|
||||
this.subscribes = {};
|
||||
}
|
||||
|
||||
// may be it can be deleted
|
||||
/*if (!main.objectsLoaded) {
|
||||
setTimeout(function () {
|
||||
that.init(update);
|
||||
}, 250);
|
||||
return;
|
||||
}*/
|
||||
|
||||
if (typeof this.$grid !== 'undefined') {
|
||||
if (this.main.dialogs.customs.customEnabled === null) {
|
||||
this.main.dialogs.customs.check();
|
||||
}
|
||||
|
||||
var settings = {
|
||||
objects: this.main.objects,
|
||||
states: this.main.states,
|
||||
noDialog: true,
|
||||
stats: true,
|
||||
name: 'admin-objects',
|
||||
useValues: ['ID', 'name', 'value.from', 'value.q', 'value.ts', 'value.lc', 'value.val', 'button'],
|
||||
useHistory: this.main.dialogs.customs.customEnabled,
|
||||
showButtonsForNotExistingObjects: true,
|
||||
expertModeRegEx: /^system\.|^yunkong2\.|^_|^[\w-]+$|^enum\.|^[\w-]+\.admin|^script\./,
|
||||
texts: {
|
||||
select: _('Select'),
|
||||
cancel: _('Cancel'),
|
||||
all: _('All'),
|
||||
id: _('ID'),
|
||||
ID: _('ID'),
|
||||
name: _('Name'),
|
||||
role: _('Role'),
|
||||
room: _('Room'),
|
||||
'function': _('Function'),
|
||||
value: _('Value'),
|
||||
type: _('Type'),
|
||||
selectid: _('Select ID'),
|
||||
from: _('From'),
|
||||
lc: _('Last changed'),
|
||||
ts: _('Time stamp'),
|
||||
wait: _('Processing...'),
|
||||
ack: _('Acknowledged'),
|
||||
expand: _('Expand all nodes'),
|
||||
collapse: _('Collapse all nodes'),
|
||||
refresh: _('Rebuild tree'),
|
||||
edit: _('Edit'),
|
||||
push: _('Trigger event'),
|
||||
ok: _('Ok'),
|
||||
with: _('With'),
|
||||
without: _('Without'),
|
||||
copyToClipboard: _('Copy to clipboard'),
|
||||
expertMode: _('Toggle expert mode'),
|
||||
sort: _('Sort alphabetically'),
|
||||
button: _('Settings'),
|
||||
editDialog: _('Edit in dialog'),
|
||||
noData: _('No data'),
|
||||
Objects: _('Objects'),
|
||||
States: _('States'),
|
||||
toggleValues: _('Toggle states view'),
|
||||
user: _('User')
|
||||
},
|
||||
columns: ['ID', 'name', 'type', 'role', 'room', 'function', 'value', 'button'],
|
||||
expandedCallback: function (id, childrenCount, hasStates) {
|
||||
// register this in subscription
|
||||
if (hasStates) {
|
||||
subscribe(id);
|
||||
}
|
||||
},
|
||||
collapsedCallback: function (id, childrenCount, hasStates) {
|
||||
// un-register this in subscription
|
||||
unsubscribe(id);
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
text: false,
|
||||
icons: {
|
||||
primary: 'ui-icon-pencil'
|
||||
},
|
||||
'material-icon': 'edit',
|
||||
click: function (id) {
|
||||
that.main.navigate({
|
||||
dialog: 'editobject',
|
||||
params: id
|
||||
});
|
||||
},
|
||||
match: function (id) {
|
||||
if (!that.main.objects[id]) {
|
||||
this[0].outerHTML = '<div class="td-button-placeholder"></div>';
|
||||
}
|
||||
},
|
||||
width: 26,
|
||||
height: 20
|
||||
},
|
||||
{
|
||||
text: false,
|
||||
icons: {
|
||||
primary:'ui-icon-trash'
|
||||
},
|
||||
'material-icon': 'delete',
|
||||
click: function (id) {
|
||||
// Delete all children
|
||||
if (id) {
|
||||
that.main.delObject(that.$grid, id, function (err) {
|
||||
if (err) that.main.showError(err);
|
||||
});
|
||||
}
|
||||
},
|
||||
match: function (id) {
|
||||
if (that.main.objects[id] && that.main.objects[id].common && that.main.objects[id].common.dontDelete) {
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
width: 26,
|
||||
height: 20
|
||||
},
|
||||
{
|
||||
text: false,
|
||||
icons: {
|
||||
primary: 'ui-icon-gear'
|
||||
},
|
||||
'material-icon': 'build',
|
||||
click: function (id) {
|
||||
that.main.dialogs.customs.ids = null;
|
||||
that.main.navigate({dialog: 'customs', params: id});
|
||||
},
|
||||
width: 26,
|
||||
height: 20,
|
||||
match: function (id) {
|
||||
// Show special button only if one of supported adapters is enabled
|
||||
if (that.main.objects[id] && that.main.dialogs.customs.customEnabled && !id.match(/\.messagebox$/) && that.main.objects[id].type === 'state') {
|
||||
// Check if some custom settings enabled
|
||||
var enabled = false;
|
||||
if (that.main.objects[id] && that.main.objects[id].common && that.main.objects[id].common.custom) {
|
||||
var custom = that.main.objects[id].common.custom;
|
||||
// convert old structure
|
||||
// TODO: remove some day (08.2016)
|
||||
if (custom.enabled !== undefined) {
|
||||
custom = that.main.objects[id].common.custom = custom.enabled ? {'history.0': custom} : {};
|
||||
}
|
||||
|
||||
for (var h in custom) {
|
||||
if (custom.hasOwnProperty(h)) {
|
||||
enabled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (enabled) {
|
||||
this.addClass('custom-enabled').removeClass('custom-disabled');
|
||||
} else {
|
||||
delete that.main.objects[id].common.custom;
|
||||
this.addClass('custom-disabled').removeClass('custom-enabled');
|
||||
}
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
],
|
||||
panelButtons: [
|
||||
{
|
||||
text: false,
|
||||
icons: {
|
||||
primary: 'ui-icon-plus'
|
||||
},
|
||||
'material-icon': 'add',
|
||||
title: _('Add new child object to selected parent'),
|
||||
click: function () {
|
||||
var id = selectId('getActual') || '';
|
||||
var $dialog = $('#dialog-new-object');
|
||||
$dialog.find('#object-tab-new-object-parent').val(id);
|
||||
$dialog.find('#object-tab-new-object-name').val(_('newObject'));
|
||||
|
||||
if (that.main.objects[id] && that.main.objects[id].type === 'device') {
|
||||
$dialog.find('#object-tab-new-object-type').val('channel');
|
||||
} else if (that.main.objects[id] && that.main.objects[id].type === 'channel') {
|
||||
$dialog.find('#object-tab-new-object-type').val('state');
|
||||
} else {
|
||||
$dialog.find('#object-tab-new-object-type').val('state');
|
||||
}
|
||||
|
||||
$dialog.modal('open');
|
||||
$dialog.find('h6').html(_('Add new object: %s', (id ? id + '.' : '') + _('newObject')));
|
||||
$dialog.find('#object-tab-new-object-name').focus();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: false,
|
||||
id: 'add_object_tree',
|
||||
'material-icon': 'file_upload',
|
||||
icons: {
|
||||
primary: 'ui-icon-arrowthickstop-1-n'
|
||||
},
|
||||
title: _('Add Objecttree from JSON File'), // let Objecttree be (fixed in translation)
|
||||
click: function () {
|
||||
var input = document.createElement('input');
|
||||
input.setAttribute('type', 'file');
|
||||
input.setAttribute('id', 'files');
|
||||
input.setAttribute('opacity', 0);
|
||||
input.addEventListener('change', function (e) {
|
||||
handleFileSelect(e, function () {});
|
||||
}, false);
|
||||
(input.click)();
|
||||
}
|
||||
},
|
||||
{
|
||||
text: false,
|
||||
id: 'save_object_tree',
|
||||
icons: {
|
||||
primary: 'ui-icon-arrowthickstop-1-s'
|
||||
},
|
||||
'material-icon': 'file_download',
|
||||
title: _('Save Objecttree as JSON File'),
|
||||
click: function () {
|
||||
var id = selectId('getActual') || '';
|
||||
var result = {};
|
||||
$.map(that.main.objects, function (val, key) {
|
||||
if (!key.search(id)) result[key] = val;
|
||||
});
|
||||
if (result !== undefined) {
|
||||
generateFile(id + '.json', result);
|
||||
} else {
|
||||
alert(_('Save of objects-tree is not possible'));
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
quickEdit: ['name', 'value', 'role', 'function', 'room', 'value.val'],
|
||||
quickEditCallback: function (id, attr, newValue, oldValue, newAck) {
|
||||
if (attr === 'room') {
|
||||
syncEnum(id, 'rooms', newValue);
|
||||
} else if (attr === 'function') {
|
||||
syncEnum(id, 'functions', newValue);
|
||||
} else
|
||||
if (attr === 'value') {
|
||||
if (that.main.objects[id] && that.main.objects[id].common && that.main.objects[id].common.type) {
|
||||
switch (that.main.objects[id].common.type) {
|
||||
case 'number':
|
||||
var v = parseFloat(newValue);
|
||||
if (isNaN(v)) {
|
||||
v = newValue === 'false' ? 0 : ~~newValue;
|
||||
}
|
||||
newValue = v;
|
||||
break;
|
||||
|
||||
case 'boolean':
|
||||
if (newValue === 'true') newValue = true;
|
||||
if (newValue === 'false') newValue = false;
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
newValue = newValue.toString();
|
||||
break;
|
||||
|
||||
default:
|
||||
if (newValue === 'true') newValue = true;
|
||||
if (newValue === 'false') newValue = false;
|
||||
// '4.0' !== parseFloat('4.0').toString()
|
||||
if (parseFloat(newValue).toString() === newValue.toString().replace(/[.,]0*$/, '')) newValue = parseFloat(newValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
newAck = newAck || false;
|
||||
that.main.socket.emit('setState', id, {val: newValue, ack: newAck}, function (err) {
|
||||
if (err) return that.main.showError(err);
|
||||
});
|
||||
} else {
|
||||
that.main.socket.emit('getObject', id, function (err, _obj) {
|
||||
if (err) return that.main.showError(err);
|
||||
|
||||
if (!_obj) {
|
||||
_obj = {
|
||||
type: 'meta',
|
||||
common: {
|
||||
typ: 'meta.user',
|
||||
role: ''
|
||||
},
|
||||
native: {},
|
||||
_id: id
|
||||
}
|
||||
}
|
||||
|
||||
_obj.common[attr] = newValue;
|
||||
that.main.socket.emit('setObject', _obj._id, _obj, function (err) {
|
||||
if (err) that.main.showError(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (this.main.dialogs.customs.customEnabled) {
|
||||
settings.customButtonFilter = {
|
||||
icons: {primary: 'ui-icon-gear'},
|
||||
'material-icon': 'build',
|
||||
text: false,
|
||||
callback: function () {
|
||||
var _ids = selectId('getFilteredIds');
|
||||
var ids = [];
|
||||
for (var i = 0; i < _ids.length; i++) {
|
||||
if (that.main.objects[_ids[i]] && that.main.objects[_ids[i]].type === 'state') ids.push(_ids[i]);
|
||||
}
|
||||
if (ids && ids.length) {
|
||||
if (ids.length < 10) {
|
||||
that.main.dialogs.customs.ids = null;
|
||||
that.main.navigate({dialog: 'customs', params: ids.join(',')});
|
||||
} else {
|
||||
that.main.dialogs.customs.ids = ids;
|
||||
that.main.navigate({dialog: 'customs'});
|
||||
}
|
||||
} else {
|
||||
that.main.showMessage(_('No states selected!'), '', 'info_outline');
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
settings.customButtonFilter = null;
|
||||
}
|
||||
|
||||
selectId('init', settings)
|
||||
.selectId('show', null, null, function () {
|
||||
that.restoreScroll();
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.inited) {
|
||||
this.inited = true;
|
||||
this.main.subscribeObjects('*');
|
||||
// resubscribe all
|
||||
subscribeAll();
|
||||
}
|
||||
};
|
||||
|
||||
this.destroy = function () {
|
||||
if (this.inited) {
|
||||
this.saveScroll();
|
||||
that.main.unsubscribeObjects('*');
|
||||
this.inited = false;
|
||||
unsubscribeAll();
|
||||
}
|
||||
};
|
||||
|
||||
function loadObjects(objs, callback) {
|
||||
if (objs) {
|
||||
for (var id in objs) {
|
||||
if (!objs.hasOwnProperty(id) || !objs[id]) continue;
|
||||
var obj = objs[id];
|
||||
objs[id] = null;
|
||||
that.main.socket.emit('setObject', id, obj, function (err) {
|
||||
if (err) {
|
||||
that.main.showError(err);
|
||||
} else if (obj.type === 'state') {
|
||||
that.main.socket.emit('getState', obj._id, function (err, state) {
|
||||
if (!state || state.val === null) {
|
||||
that.main.socket.emit('setState', obj._id, !obj.common || obj.common.def === undefined ? null : obj.common.def, true);
|
||||
}
|
||||
setTimeout(loadObjects, 0, objs, callback);
|
||||
});
|
||||
} else {
|
||||
setTimeout(loadObjects, 0, objs, callback);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
callback && callback();
|
||||
}
|
||||
|
||||
function handleFileSelect(evt) {
|
||||
var f = evt.target.files[0];
|
||||
if (f) {
|
||||
var r = new FileReader();
|
||||
r.onload = function(e) {
|
||||
var contents = e.target.result;
|
||||
var json = JSON.parse(contents);
|
||||
var len = Object.keys(json).length;
|
||||
var id = json._id;
|
||||
if (id === undefined && len > 1) {
|
||||
loadObjects(json, function () {
|
||||
that.main.showToast(that.$grid.find('.main-toolbar-table'), _('%s object(s) processed', Object.keys(json).length));
|
||||
});
|
||||
} else {
|
||||
that.main.socket.emit('setObject', json._id, json, function (err) {
|
||||
if (err) {
|
||||
that.main.showError(err);
|
||||
return;
|
||||
}
|
||||
if (json.type === 'state') {
|
||||
that.main.socket.emit('setState', json._id, json.common.def === undefined ? null : json.common.def, true);
|
||||
}
|
||||
that.main.showToast(that.$grid.find('.main-toolbar-table'), _('%s was imported', json._id));
|
||||
});
|
||||
}
|
||||
};
|
||||
r.readAsText(f);
|
||||
} else {
|
||||
alert('Failed to open JSON File');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/* global systemLang, showdown */
|
||||
|
||||
function Readme(main) {
|
||||
'use strict';
|
||||
|
||||
var that = this;
|
||||
this.$dialog = $('#dialog-readme');
|
||||
this.$divReadme = this.$dialog.find('.result-readme');
|
||||
this.$divChangeLog = this.$dialog.find('.result-changelog');
|
||||
this.$divLicense = this.$dialog.find('.result-license');
|
||||
this.$divLogo = this.$dialog.find('.result-logo');
|
||||
this.$tabs = this.$dialog.find('.tabs');
|
||||
this.mainUrl = '';
|
||||
this.main = main;
|
||||
|
||||
this.prepare = function () {
|
||||
};
|
||||
|
||||
this.init = function () {
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.inited = true;
|
||||
|
||||
if (!this.$tabs.data('inited')) {
|
||||
this.$tabs.data('inited', true);
|
||||
this.$tabs.mtabs();
|
||||
}
|
||||
|
||||
showdown.setFlavor('github');
|
||||
|
||||
var adapterName = this.main.navigateGetParams();
|
||||
|
||||
var url = that.main.tabs.adapters.$tab.find('.adapter-readme-submit[data-adapter-name="' + adapterName + '"]').data('adapter-url');
|
||||
if (!url) {
|
||||
url = localStorage.getItem('original-md-url');
|
||||
}
|
||||
if (url) {
|
||||
localStorage.setItem('original-md-url', url);
|
||||
url = url.replace('https://github.com', 'https://raw.githubusercontent.com').replace('blob/', '');
|
||||
var tmp = url.split('/');
|
||||
that.mainUrl = 'https://raw.githubusercontent.com/' + tmp[3] + '/' + tmp[4] + '/master/';
|
||||
that.$dialog.find('.title').html(adapterName);
|
||||
that.$divReadme.empty();
|
||||
that.fillDiv(url);
|
||||
}
|
||||
|
||||
that.$dialog.find('.dialog-system-buttons .btn-cancel').off('click').on('click', function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
localStorage.removeItem('original-md-url');
|
||||
that.main.navigate();
|
||||
});
|
||||
};
|
||||
|
||||
function md2html(data, link) {
|
||||
var html = new showdown.Converter().makeHtml(data);
|
||||
html = html.replace(/id="/g, 'id="mdid-');
|
||||
html = html.replace(/src="(?!http)/g, 'class="responsive-img" src="' + link);
|
||||
html = html.replace(/href="#/g, 'href="" class="goto-link" data-goto="#mdid-');
|
||||
html = html.replace(/href="(\S*).md/g, function (match) {
|
||||
return 'href="" class="md-link" data-url="' + that.mainUrl + match.replace('href="', '');
|
||||
});
|
||||
return html.replace(/href="http/g, 'target="_blank" href="http');
|
||||
}
|
||||
|
||||
function trimArr(lines) {
|
||||
var j = lines.length - 1;
|
||||
while (j >= 0 && !lines[j]) {
|
||||
j--;
|
||||
}
|
||||
if (j !== lines.length - 1) {
|
||||
lines.splice(j);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
function splitReadMe(html, link) {
|
||||
var result = {logo: '', readme: [], changeLog: [], license: []};
|
||||
var lines = html.trim().split(/\r\n|\n/);
|
||||
|
||||
// second line is main title
|
||||
if (lines[2].match(/^#\sio/)) {
|
||||
lines.splice(2, 1);
|
||||
}
|
||||
if (lines[1].match(/^#\sio/)) {
|
||||
lines.splice(1, 1);
|
||||
}
|
||||
// first line is logo
|
||||
if (lines[0].match(/!\[[-_\w\d]*]\([-._\w\d\/]+\.png\)/)) {
|
||||
result.logo = link + lines[0].match(/\((.+)\)/)[1];
|
||||
lines.splice(0, 1);
|
||||
}
|
||||
var part = 'readme';
|
||||
var i = 0;
|
||||
while (i < lines.length) {
|
||||
if (lines[i].match(/^====/)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (lines[i].match(/^###?\s+Changelog/)) {
|
||||
part = 'changeLog';
|
||||
i++;
|
||||
continue;
|
||||
} else if (lines[i].match(/^###?\s+License/)) {
|
||||
part = 'license';
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (!result[part].length && !lines[i]) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
result[part].push(lines[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (result.logo) {
|
||||
that.$divLogo.html('<img src="' + result.logo + '" />').show();
|
||||
} else {
|
||||
that.$divLogo.html('').hide();
|
||||
}
|
||||
trimArr(result.readme);
|
||||
trimArr(result.changeLog);
|
||||
trimArr(result.license);
|
||||
|
||||
if (result.readme.length) {
|
||||
result.readme = md2html(result.readme.join('\n'), link);
|
||||
} else {
|
||||
result.readme = '';
|
||||
}
|
||||
if (result.changeLog.length) {
|
||||
result.changeLog = md2html(result.changeLog.join('\n'), link);
|
||||
} else {
|
||||
delete result.changeLog;
|
||||
}
|
||||
if (result.license.length) {
|
||||
result.license[0] = '## ' + result.license[0];
|
||||
result.license = md2html(result.license.join('\n'), link);
|
||||
} else {
|
||||
delete result.license;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
this.fillDiv = function (url) {
|
||||
$.get(url, function (data) {
|
||||
var orgUrl = url.replace('https://raw.githubusercontent.com', 'https://github.com').replace('/master/', '/blob/master/');
|
||||
that.$dialog.find('.dialog-system-buttons .btn-open-org').attr('href', orgUrl);
|
||||
// Split data into 3 parts: Readme, ChangeLog and License
|
||||
var parts = splitReadMe(data, url.substring(0, url.lastIndexOf('/') + 1));
|
||||
that.$divReadme.html(parts.readme);
|
||||
if (parts.changeLog) {
|
||||
that.$divChangeLog.html(parts.changeLog);
|
||||
that.$tabs.find('.tab-changelog').show();
|
||||
} else {
|
||||
that.$divChangeLog.html('');
|
||||
that.$tabs.find('.tab-changelog').hide();
|
||||
}
|
||||
if (parts.license) {
|
||||
that.$divLicense.html(parts.license);
|
||||
that.$tabs.find('.tab-license').show();
|
||||
} else {
|
||||
that.$divLicense.html('');
|
||||
that.$tabs.find('.tab-license').hide();
|
||||
}
|
||||
}).done(function () {
|
||||
that.$divReadme.on('click', '.md-link', function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
that.fillDiv($(this).data('url'));
|
||||
});
|
||||
that.$divReadme.on('click', '.goto-link', function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
var $elemId = $($(this).data('goto'));
|
||||
if ($elemId.length) {
|
||||
that.$divReadme.animate({
|
||||
scrollTop: that.$divReadme.scrollTop() - that.$divReadme.offset().top + $elemId.offset().top
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.destroy = function () {
|
||||
if (this.inited) {
|
||||
// this.$dialog.find('.collapsible').collapsible('destroy');
|
||||
localStorage.removeItem('original-md-url');
|
||||
this.inited = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,748 @@
|
||||
function initMap () {
|
||||
gMain.dialogs.system.mapLoaded = true;
|
||||
gMain.dialogs.system.updateMap(true);
|
||||
}
|
||||
|
||||
function System(main) {
|
||||
'use strict';
|
||||
var that = this;
|
||||
this.$dialog = $('#dialog-system');
|
||||
this.main = main;
|
||||
|
||||
this.systemRepos = null;
|
||||
this.systemCerts = null;
|
||||
this.mapLoaded = false;
|
||||
var mapTimer;
|
||||
var mapInited;
|
||||
var longitude;
|
||||
var latitude;
|
||||
var useOpenLayers = true;
|
||||
|
||||
function string2cert(name, str) {
|
||||
if (str.length < 700 && (str.indexOf('/') !== -1 || str.indexOf('\\') !== -1)) {
|
||||
// it is a path
|
||||
return str;
|
||||
}
|
||||
// expected format: -----BEGIN CERTIFICATE-----certif...icate==-----END CERTIFICATE-----
|
||||
if (str.length < '-----BEGIN CERTIFICATE-----==-----END CERTIFICATE-----'.length) {
|
||||
showMessage(_('Invalid certificate "%s". To short.', name), true);
|
||||
return '';
|
||||
}
|
||||
var lines = [];
|
||||
if (str.substring(0, '-----BEGIN RSA PRIVATE KEY-----'.length) === '-----BEGIN RSA PRIVATE KEY-----') {
|
||||
if (str.substring(str.length - '-----END RSA PRIVATE KEY-----'.length) !== '-----END RSA PRIVATE KEY-----') {
|
||||
showMessage(_('Certificate "%s" must end with "-----END RSA PRIVATE KEY-----".', name), true);
|
||||
return '';
|
||||
}
|
||||
str = str.substring('-----BEGIN RSA PRIVATE KEY-----'.length);
|
||||
str = str.substring(0, str.length - '-----END RSA PRIVATE KEY-----'.length);
|
||||
str = str.replace(/\s/g, '');
|
||||
while (str.length) {
|
||||
lines.push(str.substring(0, 64));
|
||||
str = str.substring(64);
|
||||
}
|
||||
return '-----BEGIN RSA PRIVATE KEY-----\r\n' + lines.join('\r\n') + '\r\n-----END RSA PRIVATE KEY-----\r\n';
|
||||
} else if (str.substring(0, '-----BEGIN PRIVATE KEY-----'.length) === '-----BEGIN PRIVATE KEY-----') {
|
||||
if (str.substring(str.length - '-----END PRIVATE KEY-----'.length) !== '-----END PRIVATE KEY-----') {
|
||||
showMessage(_('Certificate "%s" must end with "-----BEGIN PRIVATE KEY-----".', name), true);
|
||||
return '';
|
||||
}
|
||||
str = str.substring('-----BEGIN PRIVATE KEY-----'.length);
|
||||
str = str.substring(0, str.length - '-----END PRIVATE KEY-----'.length);
|
||||
str = str.replace(/\s/g, '');
|
||||
while (str.length) {
|
||||
lines.push(str.substring(0, 64));
|
||||
str = str.substring(64);
|
||||
}
|
||||
return '-----BEGIN PRIVATE KEY-----\r\n' + lines.join('\r\n') + '\r\n-----END PRIVATE KEY-----\r\n';
|
||||
} else {
|
||||
if (str.substring(0, '-----BEGIN CERTIFICATE-----'.length) !== '-----BEGIN CERTIFICATE-----') {
|
||||
showMessage(_('Certificate "%s" must start with "-----BEGIN CERTIFICATE-----".', name), true);
|
||||
return '';
|
||||
}
|
||||
if (str.substring(str.length - '-----END CERTIFICATE-----'.length) !== '-----END CERTIFICATE-----') {
|
||||
showMessage(_('Certificate "%s" must end with "-----END CERTIFICATE-----".', name), true);
|
||||
return '';
|
||||
}
|
||||
// process chained certificates
|
||||
var parts = str.split('-----END CERTIFICATE-----');
|
||||
for (var p = parts.length - 1; p >= 0; p--) {
|
||||
if (!parts[p].replace(/[\r\n|\r|\n]+/, '').trim()) {
|
||||
parts.splice(p, 1);
|
||||
continue;
|
||||
}
|
||||
str = parts[p];
|
||||
str = str.substring('-----BEGIN CERTIFICATE-----'.length);
|
||||
str = str.replace(/\s/g, '');
|
||||
lines = [];
|
||||
while (str.length) {
|
||||
lines.push(str.substring(0, 64));
|
||||
str = str.substring(64);
|
||||
}
|
||||
parts[p] = '-----BEGIN CERTIFICATE-----\r\n' + lines.join('\r\n') + '\r\n-----END CERTIFICATE-----\r\n';
|
||||
}
|
||||
|
||||
return parts.join('');
|
||||
}
|
||||
}
|
||||
|
||||
function cert2string(cert) {
|
||||
return cert.replace(/(?:\\[rn]|[\r\n]+)+/g, '');
|
||||
}
|
||||
|
||||
function addCert(name, text) {
|
||||
// Get data and Names
|
||||
var values = table2values('tab-system-certs');
|
||||
var ids = [];
|
||||
for (var d = 0; d < values.length; d++) {
|
||||
ids.push(values[d].name);
|
||||
}
|
||||
// Find new unique name
|
||||
var found;
|
||||
var newText = name || _('New');
|
||||
var idx = 1;
|
||||
do {
|
||||
found = ids.indexOf(newText + idx) !== -1;
|
||||
if (found) {
|
||||
idx++;
|
||||
}
|
||||
} while (found);
|
||||
|
||||
values.push({name: newText + idx, certificate: text || ''});
|
||||
values2table('tab-system-certs', values);
|
||||
}
|
||||
|
||||
// ----------------------------- Repositories show and Edit ------------------------------------------------
|
||||
function initRepoGrid(/* update */) {
|
||||
if (that.systemRepos && that.systemRepos.native.repositories) {
|
||||
var values = [];
|
||||
// list of the repositories
|
||||
for (var repo in that.systemRepos.native.repositories) {
|
||||
if (!that.systemRepos.native.repositories.hasOwnProperty(repo)) continue;
|
||||
var obj = that.systemRepos.native.repositories[repo];
|
||||
|
||||
values.push({
|
||||
name: repo,
|
||||
link: (typeof obj === 'object') ? obj.link : obj
|
||||
});
|
||||
}
|
||||
|
||||
values2table('tab-system-repo', values, {
|
||||
onChange: function (attr /* , index */) {
|
||||
that.$dialog.find('.btn-save').removeClass('disabled');
|
||||
if (!attr || attr === 'name') {
|
||||
updateRepoListSelect();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (that.systemRepos.nonEdit && that.systemRepos.nonEdit.native && that.systemRepos.nonEdit.native.repositories) {
|
||||
var $repos = that.$dialog.find('#grid-repos');
|
||||
$repos.find('input').each(function () {
|
||||
$(this).prop('disabled', true).addClass('disabled');
|
||||
});
|
||||
$repos.find('select').each(function () {
|
||||
$(this).prop('disabled', true).addClass('disabled');
|
||||
});
|
||||
$repos.find('.btn-floating').each(function () {
|
||||
$(this).addClass('disabled');
|
||||
});
|
||||
that.$dialog.find('#tab-system-repo .table-button-add').addClass('disabled');
|
||||
}
|
||||
} else {
|
||||
that.$dialog.find('#tab-system-repo').html(_('permissionError'));
|
||||
}
|
||||
}
|
||||
|
||||
function updateRepoListSelect() {
|
||||
var $system_activeRepo = that.$dialog.find('#system_activeRepo');
|
||||
var selectedRepo = $system_activeRepo.val();
|
||||
var isFound = false;
|
||||
$system_activeRepo.html('');
|
||||
var data = table2values('tab-system-repo');
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
$system_activeRepo.append('<option value="' + data[i].name + '">' + data[i].name + '</option>');
|
||||
if (selectedRepo === data[i].name) {
|
||||
isFound = true;
|
||||
}
|
||||
}
|
||||
if (isFound) $system_activeRepo.val(selectedRepo);
|
||||
$system_activeRepo.select();
|
||||
}
|
||||
|
||||
// ----------------------------- Certificates show and Edit ------------------------------------------------
|
||||
function initCertsGrid() {
|
||||
var $dropZone = that.$dialog.find('#tab-system-certs');
|
||||
if (that.systemCerts && that.systemCerts.native.certificates) {
|
||||
var values = [];
|
||||
// list of the repositories
|
||||
for (var cert in that.systemCerts.native.certificates) {
|
||||
if (!that.systemCerts.native.certificates.hasOwnProperty(cert)) continue;
|
||||
|
||||
values.push({
|
||||
name: cert,
|
||||
certificate: cert2string(that.systemCerts.native.certificates[cert])
|
||||
});
|
||||
}
|
||||
|
||||
values2table('tab-system-certs', values, {
|
||||
onChange: function (attr /* , index */) {
|
||||
that.$dialog.find('.btn-save').removeClass('disabled');
|
||||
}
|
||||
});
|
||||
|
||||
if (that.systemCerts.nonEdit && that.systemCerts.nonEdit.native && that.systemCerts.nonEdit.native.certificates) {
|
||||
var $repos = that.$dialog.find('#grid-certs');
|
||||
$repos.find('input').each(function () {
|
||||
$(this).prop('disabled', true).addClass('disabled');
|
||||
});
|
||||
$repos.find('select').each(function () {
|
||||
$(this).prop('disabled', true).addClass('disabled');
|
||||
});
|
||||
$repos.find('.btn-floating').each(function () {
|
||||
$(this).addClass('disabled');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$dropZone.html(_('permissionError'));
|
||||
}
|
||||
|
||||
installFileUpload($dropZone, 10000, function (err, text) {
|
||||
if (err) {
|
||||
showMessage(err, true);
|
||||
} else {
|
||||
try {
|
||||
text = atob(text.split(',')[1]); // string has form data:;base64,TEXT==
|
||||
} catch (err) {
|
||||
showMessage(_('Cannot read file!'), true);
|
||||
return;
|
||||
}
|
||||
text = text.replace(/(\r\n|\n|\r)/gm, '');
|
||||
if (text.indexOf('BEGIN RSA PRIVATE KEY') !== -1) {
|
||||
addCert('private', text);
|
||||
} else if (text.indexOf('BEGIN PRIVATE KEY') !== -1) {
|
||||
addCert('private', text);
|
||||
} else if (text.indexOf('BEGIN CERTIFICATE') !== -1) {
|
||||
var m = text.split('-----END CERTIFICATE-----');
|
||||
var count = 0;
|
||||
for (var _m = 0; _m < m.length; _m++) {
|
||||
if (m[_m].replace(/[\r\n]+|\n|\r/, '').trim()) count++;
|
||||
}
|
||||
if (count > 1) {
|
||||
addCert('chained', text);
|
||||
} else {
|
||||
addCert('public', text);
|
||||
}
|
||||
} else {
|
||||
showMessage(_('Unknown file format!'), true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateCertListSelect() {
|
||||
// todo
|
||||
}
|
||||
|
||||
function showMessage(text, isError, duration) {
|
||||
that.main.showToast(that.$dialog.find('#tab-system-certs'), text, null, duration, isError);
|
||||
}
|
||||
|
||||
function initRights() {
|
||||
that.main.systemConfig.common.defaultNewAcl = that.main.systemConfig.common.defaultNewAcl || {};
|
||||
var acl = that.main.systemConfig.common.defaultNewAcl;
|
||||
|
||||
// fill users
|
||||
var text = '';
|
||||
var name;
|
||||
for (var u = 0; u < that.main.tabs.users.list.length; u++) {
|
||||
name = translateName(that.main.objects[that.main.tabs.users.list[u]].common.name);
|
||||
text += '<option value="' + that.main.tabs.users.list[u] + '">' + (name || that.main.tabs.users.list[u]) + '</option>';
|
||||
}
|
||||
that.$dialog.find('#tab-system-acl-owner').html(text).val(acl.owner || 'system.user.admin');
|
||||
|
||||
// fill groups
|
||||
text = '';
|
||||
for (u = 0; u < that.main.tabs.users.groups.length; u++) {
|
||||
name = translateName(that.main.objects[that.main.tabs.users.groups[u]].common.name);
|
||||
text += '<option value="' + that.main.tabs.users.groups[u] + '">' + (name || that.main.tabs.users.groups[u]) + '</option>';
|
||||
}
|
||||
that.$dialog.find('#tab-system-acl-group').html(text).val(acl.ownerGroup || 'system.group.administrator');
|
||||
|
||||
if (acl.object === undefined) acl.object = 0x664;
|
||||
|
||||
that.$dialog.find('#tab-system-acl-obj-owner-read') .prop('checked', acl.object & 0x400);
|
||||
that.$dialog.find('#tab-system-acl-obj-owner-write').prop('checked', acl.object & 0x200);
|
||||
that.$dialog.find('#tab-system-acl-obj-group-read'). prop('checked', acl.object & 0x40);
|
||||
that.$dialog.find('#tab-system-acl-obj-group-write').prop('checked', acl.object & 0x20);
|
||||
that.$dialog.find('#tab-system-acl-obj-every-read'). prop('checked', acl.object & 0x4);
|
||||
that.$dialog.find('#tab-system-acl-obj-every-write').prop('checked', acl.object & 0x2);
|
||||
|
||||
if (acl.state === undefined) acl.state = 0x664;
|
||||
|
||||
that.$dialog.find('#tab-system-acl-state-owner-read') .prop('checked', acl.state & 0x400);
|
||||
that.$dialog.find('#tab-system-acl-state-owner-write').prop('checked', acl.state & 0x200);
|
||||
that.$dialog.find('#tab-system-acl-state-group-read'). prop('checked', acl.state & 0x40);
|
||||
that.$dialog.find('#tab-system-acl-state-group-write').prop('checked', acl.state & 0x20);
|
||||
that.$dialog.find('#tab-system-acl-state-every-read'). prop('checked', acl.state & 0x4);
|
||||
that.$dialog.find('#tab-system-acl-state-every-write').prop('checked', acl.state & 0x2);
|
||||
|
||||
if (acl.file === undefined) acl.file = 0x664;
|
||||
that.$dialog.find('#tab-system-acl-file-owner-read') .prop('checked', acl.file & 0x400);
|
||||
that.$dialog.find('#tab-system-acl-file-owner-write').prop('checked', acl.file & 0x200);
|
||||
that.$dialog.find('#tab-system-acl-file-group-read'). prop('checked', acl.file & 0x40);
|
||||
that.$dialog.find('#tab-system-acl-file-group-write').prop('checked', acl.file & 0x20);
|
||||
that.$dialog.find('#tab-system-acl-file-every-read'). prop('checked', acl.file & 0x4);
|
||||
that.$dialog.find('#tab-system-acl-file-every-write').prop('checked', acl.file & 0x2);
|
||||
|
||||
// workaround for materialize checkbox problem
|
||||
that.$dialog.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');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function finishEditingRights () {
|
||||
that.main.systemConfig.common.defaultNewAcl = that.main.systemConfig.common.defaultNewAcl || {};
|
||||
var acl = that.main.systemConfig.common.defaultNewAcl;
|
||||
var old = JSON.stringify(acl);
|
||||
acl.object = 0;
|
||||
acl.object |= that.$dialog.find('#tab-system-acl-obj-owner-read').prop('checked') ? 0x400 : 0;
|
||||
acl.object |= that.$dialog.find('#tab-system-acl-obj-owner-write').prop('checked') ? 0x200 : 0;
|
||||
acl.object |= that.$dialog.find('#tab-system-acl-obj-group-read').prop('checked') ? 0x40 : 0;
|
||||
acl.object |= that.$dialog.find('#tab-system-acl-obj-group-write').prop('checked') ? 0x20 : 0;
|
||||
acl.object |= that.$dialog.find('#tab-system-acl-obj-every-read').prop('checked') ? 0x4 : 0;
|
||||
acl.object |= that.$dialog.find('#tab-system-acl-obj-every-write').prop('checked') ? 0x2 : 0;
|
||||
|
||||
acl.owner = that.$dialog.find('#tab-system-acl-owner').val();
|
||||
acl.ownerGroup = that.$dialog.find('#tab-system-acl-group').val();
|
||||
|
||||
acl.state = 0;
|
||||
acl.state |= that.$dialog.find('#tab-system-acl-state-owner-read').prop('checked') ? 0x400 : 0;
|
||||
acl.state |= that.$dialog.find('#tab-system-acl-state-owner-write').prop('checked') ? 0x200 : 0;
|
||||
acl.state |= that.$dialog.find('#tab-system-acl-state-group-read').prop('checked') ? 0x40 : 0;
|
||||
acl.state |= that.$dialog.find('#tab-system-acl-state-group-write').prop('checked') ? 0x20 : 0;
|
||||
acl.state |= that.$dialog.find('#tab-system-acl-state-every-read').prop('checked') ? 0x4 : 0;
|
||||
acl.state |= that.$dialog.find('#tab-system-acl-state-every-write').prop('checked') ? 0x2 : 0;
|
||||
|
||||
acl.file = 0;
|
||||
acl.file |= that.$dialog.find('#tab-system-acl-file-owner-read').prop('checked') ? 0x400 : 0;
|
||||
acl.file |= that.$dialog.find('#tab-system-acl-file-owner-write').prop('checked') ? 0x200 : 0;
|
||||
acl.file |= that.$dialog.find('#tab-system-acl-file-group-read').prop('checked') ? 0x40 : 0;
|
||||
acl.file |= that.$dialog.find('#tab-system-acl-file-group-write').prop('checked') ? 0x20 : 0;
|
||||
acl.file |= that.$dialog.find('#tab-system-acl-file-every-read').prop('checked') ? 0x4 : 0;
|
||||
acl.file |= that.$dialog.find('#tab-system-acl-file-every-write').prop('checked') ? 0x2 : 0;
|
||||
return old !== JSON.stringify(acl);
|
||||
}
|
||||
|
||||
function requestInfo(callback) {
|
||||
that.main.socket.emit('getObject', 'system.repositories', function (errRepo, repo) {
|
||||
that.systemRepos = repo;
|
||||
that.main.socket.emit('getObject', 'system.certificates', function (errCerts, certs) {
|
||||
that.systemCerts = certs;
|
||||
that.main.socket.emit('getObject', 'system.config', function (errConfig, config) {
|
||||
that.main.systemConfig = config;
|
||||
callback(errRepo || errCerts || errConfig);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function onButtonSave() {
|
||||
var common = that.main.systemConfig.common;
|
||||
var languageChanged = false;
|
||||
var activeRepoChanged = false;
|
||||
|
||||
finishEditingRights();
|
||||
|
||||
that.$dialog.find('.system-settings.value').each(function () {
|
||||
var $this = $(this);
|
||||
var id = $this.attr('id');
|
||||
if (!id) return;
|
||||
id = id.substring('system_'.length);
|
||||
|
||||
if ($this.attr('type') === 'checkbox') {
|
||||
common[id] = $this.prop('checked');
|
||||
} else {
|
||||
if (id === 'language' && common.language !== $this.val()) languageChanged = true;
|
||||
if (id === 'activeRepo' && common.activeRepo !== $this.val()) activeRepoChanged = true;
|
||||
common[id] = $this.val();
|
||||
if (id === 'isFloatComma') {
|
||||
common[id] = (common[id] === 'true' || common[id] === true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Fill the repositories list
|
||||
var links = {};
|
||||
if (that.systemRepos) {
|
||||
for (var r in that.systemRepos.native.repositories) {
|
||||
if (that.systemRepos.native.repositories.hasOwnProperty(r) && typeof that.systemRepos.native.repositories[r] === 'object' && that.systemRepos.native.repositories[r].json) {
|
||||
links[that.systemRepos.native.repositories[r].link] = that.systemRepos.native.repositories[r].json;
|
||||
}
|
||||
}
|
||||
that.systemRepos.native.repositories = {};
|
||||
}
|
||||
|
||||
var data = table2values('tab-system-repo');
|
||||
if (that.systemRepos) {
|
||||
var first = null;
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
that.systemRepos.native.repositories[data[i].name] = {link: data[i].link, json: null};
|
||||
if (links[data[i].link]) that.systemRepos.native.repositories[data[i].name].json = links[data[i].link];
|
||||
if (!first) first = data[i].name;
|
||||
}
|
||||
// Check if the active repository still exist in the list
|
||||
if (!first) {
|
||||
if (common.activeRepo) {
|
||||
activeRepoChanged = true;
|
||||
common.activeRepo = '';
|
||||
}
|
||||
} else if (!that.systemRepos.native.repositories[common.activeRepo]) {
|
||||
activeRepoChanged = true;
|
||||
common.activeRepo = first;
|
||||
}
|
||||
}
|
||||
common.diag = that.$dialog.find('#diagMode').val();
|
||||
|
||||
if (that.systemCerts) {
|
||||
// Fill the certificates list
|
||||
that.systemCerts.native.certificates = {};
|
||||
data = table2values('tab-system-certs');
|
||||
for (var j = 0; j < data.length; j++) {
|
||||
that.systemCerts.native.certificates[data[j].name] = string2cert(data[j].name, data[j].certificate);
|
||||
}
|
||||
|
||||
that.$dialog.find('.system-le-settings.value').each(function () {
|
||||
var $this = $(this);
|
||||
var id = $this.data('name');
|
||||
|
||||
if ($this.attr('type') === 'checkbox') {
|
||||
that.systemCerts.native.letsEncrypt[id] = $this.prop('checked');
|
||||
} else {
|
||||
that.systemCerts.native.letsEncrypt[id] = $this.val();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// we must to disable save button before the extendObject will be sent, because this is indicator for showing dialog
|
||||
|
||||
that.$dialog.find('.btn-save').addClass('disabled');
|
||||
|
||||
that.main.socket.emit('extendObject', 'system.config', {common: common}, function (err) {
|
||||
if (!err) {
|
||||
that.main.socket.emit('extendObject', 'system.repositories', that.systemRepos, function () {
|
||||
that.main.socket.emit('extendObject', 'system.certificates', that.systemCerts, function () {
|
||||
if (languageChanged) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
that.main.navigate();
|
||||
if (activeRepoChanged) {
|
||||
setTimeout(function () {
|
||||
that.main.tabs.adapters.init(true);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
that.main.showError(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.updateMap = function (immediately) {
|
||||
if (useOpenLayers) {
|
||||
// OPEN STREET MAPS
|
||||
if (typeof ol === 'undefined') {
|
||||
return setTimeout(that.updateMap, 200);
|
||||
}
|
||||
var point = ol.proj.fromLonLat([parseFloat(longitude), parseFloat(latitude)]);
|
||||
if (!that.OSM) {
|
||||
that.OSM = {};
|
||||
that.OSM.markerSource = new ol.source.Vector();
|
||||
|
||||
that.OSM.markerStyle = new ol.style.Style({
|
||||
image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({
|
||||
anchor: [0.5, 49],
|
||||
anchorXUnits: 'fraction',
|
||||
anchorYUnits: 'pixels',
|
||||
opacity: 0.75,
|
||||
src: 'img/pin.png'
|
||||
}))
|
||||
});
|
||||
|
||||
that.OSM.oMap = new ol.Map({
|
||||
target: 'map',
|
||||
layers: [
|
||||
new ol.layer.Tile({source: new ol.source.OSM()}),
|
||||
new ol.layer.Vector({
|
||||
source: that.OSM.markerSource,
|
||||
style: that.OSM.markerStyle,
|
||||
})
|
||||
],
|
||||
view: new ol.View({center: point, zoom: 17})
|
||||
});
|
||||
|
||||
that.OSM.marker = new ol.Feature({
|
||||
geometry: new ol.geom.Point(point),
|
||||
name: _('Your home')
|
||||
});
|
||||
|
||||
that.OSM.markerSource.addFeature(that.OSM.marker);
|
||||
|
||||
that.OSM.oMap.on('singleclick', function (event){
|
||||
var lonLat = ol.proj.toLonLat(event.coordinate);
|
||||
longitude = lonLat[0];
|
||||
that.$dialog.find('#system_longitude').val(lonLat[0]);
|
||||
latitude = lonLat[1];
|
||||
that.$dialog.find('#system_latitude').val(lonLat[1]).trigger('change');
|
||||
});
|
||||
}
|
||||
var zoom = that.OSM.oMap.getView().getZoom();
|
||||
that.OSM.marker.setGeometry(new ol.geom.Point(point));
|
||||
that.OSM.oMap.setView(new ol.View({center: point, zoom: zoom}));
|
||||
} else {
|
||||
// GOOGLE MAPS
|
||||
if (!this.mapLoaded) return;
|
||||
if (!immediately) {
|
||||
clearTimeout(mapTimer);
|
||||
mapTimer = setTimeout(function () {
|
||||
that.updateMap(true);
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
if (mapTimer) {
|
||||
clearTimeout(mapTimer);
|
||||
mapTimer = null;
|
||||
}
|
||||
|
||||
if (latitude || longitude) {
|
||||
var map = new google.maps.Map(that.$dialog.find('.map')[0], {
|
||||
zoom: 14,
|
||||
center: {lat: parseFloat(latitude), lng: parseFloat(longitude)}
|
||||
});
|
||||
|
||||
var marker = new google.maps.Marker({
|
||||
position: {lat: parseFloat(latitude), lng: parseFloat(longitude)},
|
||||
map: map,
|
||||
title: _('Your home')
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function preInitMap() {
|
||||
if (!mapInited) {
|
||||
mapInited = true;
|
||||
if (useOpenLayers) {
|
||||
that.mapLoaded = true;
|
||||
// load google API
|
||||
$.ajax({
|
||||
// please do not miss use this api key!
|
||||
url: 'lib/js/ol.js',
|
||||
dataType: 'script',
|
||||
cache: true
|
||||
}).done(function() {
|
||||
setTimeout(that.updateMap, 500);
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: 'lib/css/ol.css',
|
||||
success: function(data){
|
||||
$('head').append('<style>' + data + '</style>');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var key1 = 'AIzaSyCIrBRZfZAE';
|
||||
var key2 = '_0C1OplAUy7OXhiWLoZc3eY';
|
||||
var key = key1 + key2;
|
||||
|
||||
// load google API
|
||||
$.ajax({
|
||||
// please do not miss use this api key!
|
||||
url: 'https://maps.googleapis.com/maps/api/js?key=' + key + '&signed_in=true&callback=initMap',
|
||||
dataType: 'script',
|
||||
cache: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initTab(id) {
|
||||
if (id === 'tab-system-main') {
|
||||
that.updateMap();
|
||||
}
|
||||
|
||||
// Detect materialize
|
||||
if ((id === 'tab-system-letsencrypt' || id === 'tab-system-main' || id === 'tab-system-acl') && window.M && window.M.toast) {
|
||||
M.updateTextFields('#' + id);
|
||||
that.$dialog.find('optgroup').each(function () {
|
||||
if (!$(this).data('lang')) {
|
||||
var label = $(this).attr('label');
|
||||
$(this).data('lang', label);
|
||||
$(this).attr('label', _(label));
|
||||
}
|
||||
});
|
||||
|
||||
that.$dialog.find('select').select();
|
||||
} else
|
||||
if (id === 'tab-system-certs') {
|
||||
showMessage(_('Drop the files here'));
|
||||
}
|
||||
}
|
||||
|
||||
this.init = function () {
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
this.inited = true;
|
||||
// request all info anew
|
||||
requestInfo(function (error) {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
showMessage(error, true);
|
||||
return;
|
||||
}
|
||||
var $system_activeRepo = that.$dialog.find('#system_activeRepo');
|
||||
$system_activeRepo.html('');
|
||||
if (that.systemRepos && that.systemRepos.native.repositories) {
|
||||
for (var repo in that.systemRepos.native.repositories) {
|
||||
$system_activeRepo.append('<option value="' + repo + '">' + repo + '</option>');
|
||||
}
|
||||
} else {
|
||||
that.$dialog.find('#tab-system-repo').html(_('permissionError'));
|
||||
}
|
||||
|
||||
that.$dialog.find('#diagMode')
|
||||
.val(that.main.systemConfig.common.diag)
|
||||
.on('change', function () {
|
||||
that.main.socket.emit('sendToHost', that.main.currentHost, 'getDiagData', $(this).val(), function (obj) {
|
||||
that.$dialog.find('#diagSample').html(JSON.stringify(obj, null, 2));
|
||||
});
|
||||
})
|
||||
.trigger('change');
|
||||
|
||||
// collect all history instances
|
||||
var $system_defaultHistory = that.$dialog.find('#system_defaultHistory');
|
||||
$system_defaultHistory.html('<option value=""></option>');
|
||||
for (var id = 0; id < that.main.instances.length; id++) {
|
||||
if (main.objects[main.instances[id]].common.type === 'storage') {
|
||||
$system_defaultHistory.append('<option value="' + that.main.instances[id].substring('system.adapter.'.length) + '">' + main.instances[id].substring('system.adapter.'.length) + '</option>');
|
||||
}
|
||||
}
|
||||
longitude = that.main.systemConfig.common.longitude;
|
||||
latitude = that.main.systemConfig.common.latitude;
|
||||
preInitMap();
|
||||
|
||||
that.$dialog.find('.system-settings.value').each(function () {
|
||||
var $this = $(this);
|
||||
var id = $this.attr('id');
|
||||
if (!id) return;
|
||||
id = id.substring('system_'.length);
|
||||
|
||||
if ($this.attr('type') === 'checkbox') {
|
||||
$this.prop('checked', that.main.systemConfig.common[id]);
|
||||
} else {
|
||||
if (id === 'isFloatComma') {
|
||||
$this.val(that.main.systemConfig.common[id] ? 'true' : 'false');
|
||||
} else {
|
||||
$this.val(that.main.systemConfig.common[id]);
|
||||
}
|
||||
}
|
||||
if (that.main.systemConfig.nonEdit && that.main.systemConfig.nonEdit.common) {
|
||||
if (that.main.systemConfig.nonEdit.common[id] !== undefined) {
|
||||
$this.addClass('disabled');
|
||||
$this.prop('disabled', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
that.$dialog.find('#system_latitude').off('change').on('change', function () {
|
||||
latitude = $(this).val();
|
||||
that.updateMap();
|
||||
}).off('keyup').on('keyup', function () {
|
||||
$(this).trigger('change');
|
||||
});
|
||||
|
||||
that.$dialog.find('#system_longitude').off('change').on('change', function () {
|
||||
longitude = $(this).val();
|
||||
that.updateMap();
|
||||
}).off('keyup').on('keyup', function () {
|
||||
$(this).trigger('change');
|
||||
});
|
||||
if (!that.systemCerts.native.letsEncrypt) {
|
||||
that.systemCerts.native.letsEncrypt = {
|
||||
path: 'letsencrypt'
|
||||
};
|
||||
}
|
||||
|
||||
that.$dialog.find('.system-le-settings.value').each(function () {
|
||||
var $this = $(this);
|
||||
var id = $this.data('name');
|
||||
if (that.systemCerts && that.systemCerts.native.letsEncrypt) {
|
||||
if ($this.attr('type') === 'checkbox') {
|
||||
$this.prop('checked', that.systemCerts.native.letsEncrypt[id]);
|
||||
} else {
|
||||
$this.val(that.systemCerts.native.letsEncrypt[id]);
|
||||
}
|
||||
}
|
||||
if (that.systemCerts.nonEdit && that.systemCerts.nonEdit.native && that.systemCerts.nonEdit.native.letsEncrypt) {
|
||||
if (that.systemCerts.nonEdit.native.letsEncrypt[id] !== undefined) {
|
||||
$this.addClass('disabled');
|
||||
$this.prop('disabled', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var $tabs = that.$dialog.find('#tabs-system');
|
||||
|
||||
$tabs.find('.tabs').mtabs({
|
||||
onShow: function (tab) {
|
||||
if (!tab) return;
|
||||
initTab($(tab).attr('id'));
|
||||
}
|
||||
});
|
||||
|
||||
that.$dialog.find('.dialog-system-buttons .btn-save').off('click').on('click', onButtonSave);
|
||||
that.$dialog.find('.dialog-system-buttons .btn-cancel').off('click').on('click', function () {
|
||||
that.main.navigate();
|
||||
});
|
||||
|
||||
initRepoGrid();
|
||||
initRights();
|
||||
initCertsGrid();
|
||||
|
||||
that.$dialog.find('.value').on('change', function () {
|
||||
that.$dialog.find('.btn-save').removeClass('disabled');
|
||||
}).on('keyup', function () {
|
||||
$(this).trigger('change');
|
||||
});
|
||||
|
||||
that.$dialog.find('.btn-save').addClass('disabled');
|
||||
|
||||
initTab('tab-system-main');
|
||||
});
|
||||
};
|
||||
|
||||
this.destroy = function () {
|
||||
if (this.inited) {
|
||||
this.inited = false;
|
||||
}
|
||||
};
|
||||
|
||||
this.allStored = function () {
|
||||
return that.$dialog.find('.btn-save').hasClass('disabled');
|
||||
};
|
||||
|
||||
this.prepare = function () {
|
||||
if (!that.main.systemConfig.error) {
|
||||
$('#button-system').off('click').on('click', function () {
|
||||
that.main.navigate({dialog: 'system'});
|
||||
});
|
||||
} else {
|
||||
$('#button-system').hide();
|
||||
}
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
var license = {
|
||||
'en': 'The MIT License (MIT)<br>' +
|
||||
'<br>' +
|
||||
'Copyright © 2014-2018 bluefox, 2014 hobbyquaker<br>' +
|
||||
'<br>' +
|
||||
'Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:<br>' +
|
||||
'The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.<br>' +
|
||||
'THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.<br>',
|
||||
|
||||
"de": 'MIT-Lizenz (MIT)<br>' +
|
||||
'<br>' +
|
||||
'Copyright © 2014-2018 bluefox, 2014 hobbyquaker<br>' +
|
||||
'<br>' +
|
||||
'Hiermit wird unentgeltlich jeder Person, die eine Kopie der Software und der zugehörigen Dokumentationen (die "Software") erhält, die Erlaubnis erteilt, sie uneingeschränkt zu benutzen, inklusive und ohne Ausnahme dem Recht, sie zu verwenden, kopieren, ändern, fusionieren, verlegen, verbreiten, unterlizenzieren und/oder zu verkaufen, und Personen, die diese Software erhalten, diese Rechte zu geben, unter den folgenden Bedingungen:<br>' +
|
||||
'Der obige Urheberrechtsvermerk und dieser Erlaubnisvermerk sind in allen Kopien oder Teilkopien der Software beizulegen.<br>' +
|
||||
'DIE SOFTWARE WIRD OHNE JEDE AUSDRÜCKLICHE ODER IMPLIZIERTE GARANTIE BEREITGESTELLT, EINSCHLIESSLICH DER GARANTIE ZUR BENUTZUNG FÜR DEN VORGESEHENEN ODER EINEM BESTIMMTEN ZWECK SOWIE JEGLICHER RECHTSVERLETZUNG, JEDOCH NICHT DARAUF BESCHRÄNKT. IN KEINEM FALL SIND DIE AUTOREN ODER COPYRIGHTINHABER FÜR JEGLICHEN SCHADEN ODER SONSTIGE ANSPRÜCHE HAFTBAR ZU MACHEN, OB INFOLGE DER ERFÜLLUNG EINES VERTRAGES, EINES DELIKTES ODER ANDERS IM ZUSAMMENHANG MIT DER SOFTWARE ODER SONSTIGER VERWENDUNG DER SOFTWARE ENTSTANDEN.',
|
||||
|
||||
"ru": 'Лицензия MIT<br>' +
|
||||
'<br>' +
|
||||
'Copyright © 2014-2018 bluefox, 2014 hobbyquaker<br>' +
|
||||
'<br>' +
|
||||
'Данная лицензия разрешает лицам, получившим копию данного программного обеспечения и сопутствующей документации (в дальнейшем именуемыми «Программное Обеспечение»), безвозмездно использовать Программное Обеспечение без ограничений, включая неограниченное право на использование, копирование, изменение, добавление, публикацию, распространение, сублицензирование и/или продажу копий Программного Обеспечения, также как и лицам, которым предоставляется данное Программное Обеспечение, при соблюдении следующих условий:<br>' +
|
||||
'Указанное выше уведомление об авторском праве и данные условия должны быть включены во все копии или значимые части данного Программного Обеспечения.<br>' +
|
||||
'ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ГАРАНТИЯМИ ТОВАРНОЙ ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ ПРАВ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО ИСКАМ О ВОЗМЕЩЕНИИ УЩЕРБА, УБЫТКОВ ИЛИ ДРУГИХ ТРЕБОВАНИЙ ПО ДЕЙСТВУЮЩИМ КОНТРАКТАМ, ДЕЛИКТАМ ИЛИ ИНОМУ, ВОЗНИКШИМ ИЗ, ИМЕЮЩИМ ПРИЧИНОЙ ИЛИ СВЯЗАННЫМ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫМИ ДЕЙСТВИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.',
|
||||
|
||||
'it': 'Licenza MIT<br>' +
|
||||
'<br>' +
|
||||
'Copyright © 2014-2018 bluefox, 2014 hobbyquaker<br>' +
|
||||
'<br>' +
|
||||
'Si concede gratuitamente l\'autorizzazione, a chiunque ottenga una copia di questo software e dei file di documentazione associati (il "Software"), di dare opera al Software senza restrizioni, compresi senza limitazione i diritti di utilizzare, copiare, modificare, unire, pubblicare, distribuire, concedere in sublicenza ovvero vendere copie del Software, e di consentire alle persone a cui il Software è fornito di fare altrettanto, posto che siano rispettate le seguenti condizioni:<br>' +
|
||||
'l\'avviso di copyright unitamente a questo avviso di licenza devono essere sempre inclusi in tutte le copie o parti sostanziali del Software.<br>' +
|
||||
'IL SOFTWARE VIENE FORNITO "COSÌ COM\'È" SENZA GARANZIE DI ALCUN TIPO, ESPLICITE O IMPLICITE, COMPRESE, MA NON SOLO, LE GARANZIE DI COMMERCIABILITÀ, IDONEITÀ AD UN PARTICOLARE SCOPO E NON VIOLAZIONE DI DIRITTI ALTRUI. IN NESSUN CASO GLI AUTORI DEL SOFTWARE O I TITOLARI DEL COPYRIGHT POTRANNO ESSERE RITENUTI RESPONSABILI DI RECLAMI, DANNI O ALTRE RESPONSABILITÀ, DERIVANTI DA O COLLEGATI A CONTRATTO, ILLECITO CIVILE O IN ALTRA RELAZIONE CON IL SOFTWARE O CON IL SUO UTILIZZO O CON ALTRE OPERAZIONI DEL SOFTWARE.',
|
||||
|
||||
'fr': 'Licence MIT<br>' +
|
||||
'<br>' +
|
||||
'Copyright © 2014-2018 bluefox, 2014 hobbyquaker<br>' +
|
||||
'<br>' +
|
||||
'Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:<br>' +
|
||||
'The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.<br>' +
|
||||
'THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.<br>',
|
||||
|
||||
'pt': 'Licença MIT<br>' +
|
||||
'<br>' +
|
||||
'Copyright © 2014-2018 bluefox, 2014 hobbyquaker<br>' +
|
||||
'<br>' +
|
||||
'Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:<br>' +
|
||||
'The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.<br>' +
|
||||
'THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.<br>',
|
||||
|
||||
'nl': 'MIT-licentie<br>' +
|
||||
'<br>' +
|
||||
'Copyright © 2014-2018 bluefox, 2014 hobbyquaker<br>' +
|
||||
'<br>' +
|
||||
'Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:<br>' +
|
||||
'The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.<br>' +
|
||||
'THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.<br>',
|
||||
|
||||
'es': 'Licencia MIT<br>' +
|
||||
'<br>' +
|
||||
'Copyright © 2014-2018 bluefox, 2014 hobbyquaker<br>' +
|
||||
'<br>' +
|
||||
'Se concede permiso, de forma gratuita, a cualquier persona que obtenga una copia de este software y de los archivos de documentación asociados (el "Software"), para utilizar el Software sin restricción, incluyendo sin limitación los derechos a usar, copiar, modificar, fusionar, publicar, distribuir, sublicenciar, y/o vender copias del Software, y a permitir a las personas a las que se les proporcione el Software a hacer lo mismo, sujeto a las siguientes condiciones:<br>' +
|
||||
'El aviso de copyright anterior y este aviso de permiso se incluirán en todas las copias o partes sustanciales del Software.<br>' +
|
||||
'EL SOFTWARE SE PROPORCIONA "TAL CUAL", SIN GARANTÍA DE NINGÚN TIPO, EXPRESA O IMPLÍCITA, INCLUYENDO PERO NO LIMITADO A GARANTÍAS DE COMERCIALIZACIÓN, IDONEIDAD PARA UN PROPÓSITO PARTICULAR Y NO INFRACCIÓN. EN NINGÚN CASO LOS AUTORES O TITULARES DEL COPYRIGHT SERÁN RESPONSABLES DE NINGUNA RECLAMACIÓN, DAÑOS U OTRAS RESPONSABILIDADES, YA SEA EN UNA ACCIÓN DE CONTRATO, AGRAVIO O CUALQUIER OTRO MOTIVO, QUE SURJA DE O EN CONEXIÓN CON EL SOFTWARE O EL USO U OTRO TIPO DE ACCIONES EN EL SOFTWARE.',
|
||||
|
||||
'pl': 'Licencja MIT<br>' +
|
||||
'<br>' +
|
||||
'Copyright © 2014-2018 bluefox, 2014 hobbyquaker<br>' +
|
||||
'<br>' +
|
||||
'Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:<br>' +
|
||||
'The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.<br>' +
|
||||
'THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.<br>',
|
||||
};
|
||||
@@ -0,0 +1,197 @@
|
||||
var addAll2FilterCombobox = false;
|
||||
|
||||
function IobListHeader(header, options) {
|
||||
if (!(this instanceof IobListHeader)) return new IobListHeader(header, options);
|
||||
|
||||
if (options === undefined) options = {};
|
||||
if (options.colWidthOffset === undefined) options.colWidthOffset = 0;
|
||||
|
||||
var $headerThs;
|
||||
var $listTds;
|
||||
var $header;
|
||||
$header = typeof header === 'object' ? $(header) : $('#' + header);
|
||||
if ($header[0].tagName === 'TABLE') $header = $header.find('tr:first');
|
||||
header = $header[0];
|
||||
|
||||
var that = this;
|
||||
that.selectIdOffset = [];
|
||||
|
||||
$header.html('');
|
||||
|
||||
this.setList = function (_list) {
|
||||
var $tds;
|
||||
if (typeof _list === 'string') {
|
||||
if (_list[0] !== '#') {
|
||||
_list = '#' + _list;
|
||||
}
|
||||
$tds = $(_list)
|
||||
} else {
|
||||
$tds = _list;
|
||||
}
|
||||
if (!$tds || !$tds.length) return;
|
||||
if ($tds[0].tagName !== 'TD' && $tds[0].tagName !== 'TH') {
|
||||
$tds = $tds.find('>thead>tr:first>th,>thead>tr:first>td');
|
||||
if (!$tds.length) $tds = _list.find('>tbody>tr:first>th,>tr:first>th,>tbody>tr:first>td, >tr:first>td');
|
||||
}
|
||||
$listTds = $tds;
|
||||
};
|
||||
if (options.list) {
|
||||
this.setList(options.list);
|
||||
}
|
||||
|
||||
this.syncHeader = function () {
|
||||
if (typeof $listTds !== 'object') return;
|
||||
var syncHeader = function () {
|
||||
$listTds.each(function (i, o) {
|
||||
if (i >= $listTds.length - 1) return;
|
||||
var x = $(o).width();
|
||||
var offset = that.selectIdOffset[i] || 0;
|
||||
if (x + offset) {
|
||||
$($headerThs[i]).width(Math.round(x + offset));
|
||||
}
|
||||
});
|
||||
if ($listTds.length && !that.selectIdOffset.length) {
|
||||
that.selectIdOffset[0] = 0;
|
||||
$listTds.each(function (i, o) {
|
||||
//if (i >= $listTds.length - 1) return;
|
||||
var x = $($listTds[i]).offset().left;
|
||||
if (x) {
|
||||
that.selectIdOffset[i] = x - $($headerThs[i]).offset().left;
|
||||
}
|
||||
});
|
||||
syncHeader();
|
||||
}
|
||||
};
|
||||
syncHeader();
|
||||
};
|
||||
|
||||
var resizeTimer;
|
||||
$(window).on('resize', function (x, y) {
|
||||
if (resizeTimer) clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(that.syncHeader.bind(that), 100);
|
||||
});
|
||||
|
||||
function buildId(id, fis) {
|
||||
if (!id || (fis && id[0] === '#')) return id;
|
||||
if (options.prefix && id.substr(0, options.prefix.length) !== options.prefix) {
|
||||
id = options.prefix + '-' + id;
|
||||
}
|
||||
return (fis ? '#' : '') + id;
|
||||
}
|
||||
|
||||
this.doFilter = function () {};
|
||||
|
||||
function allOption(name, selectedVal) {
|
||||
if (addAll2FilterCombobox) {
|
||||
name = name ? _(name) + ' ('+_('all') + ')' : _('all');
|
||||
}
|
||||
return '<option value="" ' + ((selectedVal === '') ? 'selected' : '') + '>' + name + '</option>';
|
||||
}
|
||||
|
||||
that.ids = [];
|
||||
that.add = function (what, title, _id, selectOptions) {
|
||||
if (_id === undefined) _id = title;
|
||||
var id = buildId(_id);
|
||||
title = _(title);
|
||||
|
||||
var txt = '';
|
||||
switch (what) {
|
||||
case 'combobox':
|
||||
txt =
|
||||
' <select class="list-header-input" id="' + id + '" title="' + title + '"></select>' +
|
||||
' <button class="list-header-clear" id="' + id + '-clear" role="button" title=""></button>';
|
||||
break;
|
||||
case 'edit':
|
||||
txt =
|
||||
' <input class="list-header-input" placeholder="' + title + '" id="' + id + '" title="' + title + '">' +
|
||||
' <button class="list-header-clear" id="' + id + '-clear" role="button" title="' + title + '"></button>';
|
||||
break;
|
||||
case 'text':
|
||||
txt =
|
||||
'<span class="list-header-text">' + title + '</span>';
|
||||
break;
|
||||
}
|
||||
|
||||
$header.append('<th>' + txt + '</th>');
|
||||
|
||||
var fisId = '#' + id;
|
||||
var $id = $(fisId);
|
||||
var elem = that[_id] = {
|
||||
$filter: $id,
|
||||
val: $id.val.bind($id),
|
||||
selectedVal: $id.val() || ''
|
||||
};
|
||||
that.ids.push(_id);
|
||||
|
||||
if (what === 'combobox') {
|
||||
elem.options = [];
|
||||
elem.checkAddOption = function (text, cb, noAll) {
|
||||
if (this.options.indexOf(text) !== -1) return;
|
||||
this.options.push(text);
|
||||
this.options.sort();
|
||||
var selectedVal = $id.val();
|
||||
var txt = noAll ? '' : allOption(title, selectedVal);
|
||||
|
||||
function addOption(val, name) {
|
||||
txt += '<option value="' + val + '" ' + ((name === selectedVal) ? 'selected' : '') + '>' + name + '</option>'
|
||||
}
|
||||
for (var i = 0, len = this.options.length; i < len; i++) {
|
||||
var option = this.options[i];
|
||||
if (cb) {
|
||||
var v = cb(option, i);
|
||||
if (typeof v === 'object') {
|
||||
addOption(v.val, v.name);
|
||||
} else {
|
||||
txt += v;
|
||||
}
|
||||
} else {
|
||||
addOption(option, option);
|
||||
}
|
||||
}
|
||||
$id.html(txt);
|
||||
};
|
||||
|
||||
if (selectOptions) {
|
||||
for (var i = 0; i < selectOptions.length; i++) {
|
||||
elem.checkAddOption(selectOptions[i].name, function (o, i) {
|
||||
return selectOptions [i];
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var $btnClear = $(fisId + '-clear');
|
||||
$btnClear.on('click', function () {
|
||||
if ($id.val() !== '') {
|
||||
$id.val('').trigger('change');
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof M === 'undefined') {
|
||||
$btnClear.button({icons: {primary: 'ui-icon-close'}, text: false})
|
||||
} else {
|
||||
$btnClear.prepend('<i class="material-icons">close</i>');
|
||||
}
|
||||
|
||||
var eventFilterTimeout;
|
||||
$id.on('change', function (event) {
|
||||
if (eventFilterTimeout) clearTimeout(eventFilterTimeout);
|
||||
elem.selectedVal = $id.val();
|
||||
eventFilterTimeout = setTimeout(that.doFilter, what !== 'combobox' ? 400 : 0);
|
||||
//_filterChanged($id);
|
||||
if (elem.selectedVal) {
|
||||
$id.parent().addClass('filter-active');
|
||||
} else {
|
||||
$id.parent().removeClass('filter-active');
|
||||
}
|
||||
}).on('keyup', function (event) {
|
||||
if (event.which === 13) {
|
||||
that.doFilter();
|
||||
} else {
|
||||
$id.trigger('change');
|
||||
}
|
||||
});
|
||||
$headerThs = $header.find('>th');
|
||||
return elem;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
/**
|
||||
* Create edit table from javascript array.
|
||||
*
|
||||
* This function creates a html edit table.
|
||||
*
|
||||
* <pre><code>
|
||||
* <div id="values" style="width: 100%; height: calc(100% - 205px)">
|
||||
* <button class="table-button-add" style="margin-left: 10px"></button>
|
||||
* <div style="width: 100%; height: calc(100% - 30px); overflow: auto;">
|
||||
* <table class="table-values" style="width: 100%;">
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th data-name="_index" style="30px" data-style="width: 100%; text-align: right">Context</th>
|
||||
* <th data-name="regex" class="translate" style="width: 30%" data-style="text-align: right">Context</th>
|
||||
* <th data-name="room" class="translate" data-type="select">Room</th>
|
||||
* <th data-name="aaa" class="translate" data-options="1/A;2/B;3/C;4" data-type="select">Room</th>
|
||||
* <th data-name="enabled" class="translate" data-type="checkbox">Enabled</th>
|
||||
* <th data-buttons="delete up down" style="width: 32px"></th>
|
||||
* </tr>
|
||||
* </thead>
|
||||
* </table>
|
||||
* </div>
|
||||
* </div>
|
||||
* </pre></code>
|
||||
*
|
||||
* @param {string} divId name of the html element (or empty).
|
||||
* @param {string} values data array
|
||||
* @param {object} options settings
|
||||
* <pre><code>
|
||||
* {
|
||||
* onChange: this function will be called if something changed. function (attr, index) {}
|
||||
* onReady: called, when the table is ready (may be to modify some elements of it),
|
||||
* maxRaw: maximal number of rows
|
||||
* }
|
||||
* </pre></code>
|
||||
* @return {object} array with values
|
||||
*/
|
||||
function values2table(divId, values, options) {
|
||||
if (typeof divId === 'object') {
|
||||
options = values;
|
||||
values = divId;
|
||||
divId = '';
|
||||
}
|
||||
var maxRaw = (options && options.maxRaw) || null;
|
||||
var onChange = (options && options.onChange) || null;
|
||||
var onReady = (options && options.onReady) || null;
|
||||
|
||||
values = values || [];
|
||||
var names = [];
|
||||
var $div;
|
||||
if (!divId) {
|
||||
$div = $('body');
|
||||
} else {
|
||||
$div = $('#' + divId);
|
||||
}
|
||||
var $add = $div.find('.table-button-add');
|
||||
$add.data('raw', values.length);
|
||||
|
||||
if (maxRaw) {
|
||||
$add.data('maxRaw', maxRaw);
|
||||
}
|
||||
|
||||
if (!$add.data('inited')) {
|
||||
$add.data('inited', true);
|
||||
|
||||
// var addText = $add.text();
|
||||
|
||||
$add.on('click', function () {
|
||||
if (!$add.data('maxRaw') || ($add.data('raw') < $add.data('maxRaw'))) {
|
||||
var $table = $div.find('.table-values');
|
||||
var values = $table.data('values');
|
||||
var names = $table.data('names');
|
||||
var maxRaw = $table.data('maxRaw');
|
||||
var obj = {};
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
if (!names[i]) continue;
|
||||
obj[names[i].name] = names[i].def;
|
||||
}
|
||||
values.push(obj);
|
||||
onChange && onChange();
|
||||
setTimeout(function () {
|
||||
values2table(divId, values, onChange, onReady, maxRaw);
|
||||
}, 100);
|
||||
$add.data('raw', $add.data('raw') + 1);
|
||||
} else {
|
||||
confirmMessage(_('maxTableRaw') + ': ' + $add.data('maxRaw'), _('maxTableRawInfo'), 'alert', ['Ok']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (values) {
|
||||
var buttons = [];
|
||||
var $table = $div.find('.table-values');
|
||||
$table.data('values', values);
|
||||
|
||||
// load rooms
|
||||
if (!$table.data('rooms') && $table.find('th[data-name="room"]').length) {
|
||||
getEnums('rooms', function (err, list) {
|
||||
var result = {};
|
||||
var trRooms = _('nonerooms');
|
||||
if (trRooms !== 'nonerooms') {
|
||||
result[_('none')] = trRooms;
|
||||
} else {
|
||||
result[_('none')] = '';
|
||||
}
|
||||
var nnames = [];
|
||||
for (var n in list) {
|
||||
if (list.hasOwnProperty(n)) {
|
||||
nnames.push(n);
|
||||
}
|
||||
}
|
||||
nnames.sort(function (a, b) {
|
||||
a = a.toLowerCase();
|
||||
b = b.toLowerCase();
|
||||
if (a > b) return 1;
|
||||
if (a < b) return -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
for (var l = 0; l < nnames.length; l++) {
|
||||
result[nnames[l]] = list[nnames[l]].common.name || l;
|
||||
}
|
||||
$table.data('rooms', result);
|
||||
values2table(divId, values, onChange, onReady, maxRaw);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// load functions
|
||||
if (!$table.data('functions') && $table.find('th[data-name="func"]').length) {
|
||||
getEnums('functions', function (err, list) {
|
||||
var result = {};
|
||||
var trFuncs = _('nonefunctions');
|
||||
if (trFuncs !== 'nonefunctions') {
|
||||
result[_('none')] = trFuncs;
|
||||
} else {
|
||||
result[_('none')] = '';
|
||||
}
|
||||
|
||||
var nnames = [];
|
||||
for (var n in list) {
|
||||
if (list.hasOwnProperty(n)) {
|
||||
nnames.push(n);
|
||||
}
|
||||
}
|
||||
nnames.sort(function (a, b) {
|
||||
a = a.toLowerCase();
|
||||
b = b.toLowerCase();
|
||||
if (a > b) return 1;
|
||||
if (a < b) return -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
for (var l = 0; l < nnames.length; l++) {
|
||||
result[nnames[l]] = list[nnames[l]].common.name || l;
|
||||
}
|
||||
$table.data('functions', result);
|
||||
values2table(divId, values, onChange, onReady, maxRaw);
|
||||
});
|
||||
return;
|
||||
}
|
||||
$table.find('th').each(function () {
|
||||
var name = $(this).data('name');
|
||||
if (name) {
|
||||
var obj = {
|
||||
name: name,
|
||||
type: $(this).data('type') || 'text',
|
||||
def: $(this).data('default'),
|
||||
'class': ($(this).attr('class') || '').replace('translate', ''),
|
||||
style: $(this).data('style'),
|
||||
readOnly: $(this).data('readOnly'),
|
||||
tdstyle: $(this).data('tdstyle')
|
||||
};
|
||||
if (obj.type === 'checkbox') {
|
||||
if (obj.def === 'false') obj.def = false;
|
||||
if (obj.def === 'true') obj.def = true;
|
||||
obj.def = !!obj.def;
|
||||
} else if (obj.type === 'select' || obj.type === 'select multiple') {
|
||||
var vals = ($(this).data('options') || '').split(';');
|
||||
obj.options = {};
|
||||
for (var v = 0; v < vals.length; v++) {
|
||||
var parts = vals[v].split('/');
|
||||
obj.options[parts[0]] = _(parts[1] || parts[0]);
|
||||
if (v === 0) obj.def = (obj.def === undefined) ? parts[0] : obj.def;
|
||||
}
|
||||
} else {
|
||||
obj.def = obj.def || '';
|
||||
}
|
||||
names.push(obj);
|
||||
} else {
|
||||
names.push(null);
|
||||
}
|
||||
|
||||
name = $(this).data('buttons');
|
||||
|
||||
if (name) {
|
||||
buttons.push({
|
||||
btn: name.split(' '),
|
||||
'class': ($(this).attr('class') || '').replace('translate', ''),
|
||||
style: $(this).data('style'),
|
||||
tdstyle: $(this).data('tdstyle')
|
||||
});
|
||||
} else {
|
||||
buttons.push(null);
|
||||
}
|
||||
});
|
||||
|
||||
$table.data('names', names);
|
||||
|
||||
var text = '';
|
||||
for (var v = 0; v < values.length; v++) {
|
||||
var idName = values[v] && values[v].id;
|
||||
if (!idName && values[v]) {
|
||||
if (names[0] === '_index') {
|
||||
idName = values[v][names[1]];
|
||||
} else {
|
||||
idName = values[v][names[0]];
|
||||
}
|
||||
}
|
||||
text += '<tr data-id="' + idName + '" data-index="' + v + '">';
|
||||
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
text += '<td';
|
||||
var line = '';
|
||||
var style = '';
|
||||
var tdstyle = '';
|
||||
if (names[i]) {
|
||||
if (names[i]['class']) {
|
||||
text += ' class="' + names[i]['class'] + '" ';
|
||||
}
|
||||
if (names[i].name !== '_index') {
|
||||
tdstyle = names[i].tdstyle || '';
|
||||
if (tdstyle && tdstyle[0] !== ';') tdstyle = ';' + tdstyle;
|
||||
}
|
||||
if (names[i].name === '_index') {
|
||||
style = (names[i].style ? names[i].style : 'text-align: right;');
|
||||
line += (v + 1);
|
||||
} else if (names[i].type === 'checkbox') {
|
||||
line += '<input style="' + (names[i].style || '') + '" class="values-input" type="checkbox" data-index="' + v + '" data-name="' + names[i].name + '" ' + (values[v][names[i].name] ? 'checked' : '') + '" data-old-value="' + (values[v][names[i].name] === undefined ? '' : values[v][names[i].name]) + '"/>';
|
||||
} else if (names[i].type.substring(0, 6) === 'select') {
|
||||
line += (names[i].type.substring(7, 16) === 'multiple' ? '<select multiple style="' : '<select style="') + (names[i].style ? names[i].style : 'width: 100%') + '" class="values-input" data-index="' + v + '" data-name="' + names[i].name + '">';
|
||||
var options;
|
||||
if (names[i].name === 'room') {
|
||||
options = $table.data('rooms');
|
||||
} else if (names[i].name === 'func') {
|
||||
options = $table.data('functions');
|
||||
if (names[i].type === 'select multiple') delete options[_('none')];
|
||||
} else {
|
||||
options = names[i].options;
|
||||
}
|
||||
|
||||
var val = (values[v][names[i].name] === undefined ? '' : values[v][names[i].name]);
|
||||
if (typeof val !== 'object') val = [val];
|
||||
for (var p in options) {
|
||||
line += '<option value="' + p + '" ' + (val.indexOf(p) !== -1 ? ' selected' : '') + '>' + options[p] + '</option>';
|
||||
}
|
||||
line += '</select>';
|
||||
} else {
|
||||
line += '<input class="values-input" style="' + (names[i].style ? names[i].style : 'width: 100%') + '" type="' + names[i].type + '" data-index="' + v + '" data-name="' + names[i].name + '"/>';
|
||||
}
|
||||
}
|
||||
|
||||
if (buttons[i]) {
|
||||
style = 'text-align: center; ' + (buttons[i].style || '') + (buttons[i].tdstyle || '');
|
||||
for (var b = 0; b < buttons[i].btn.length; b++) {
|
||||
if ((!v && buttons[i].btn[b] === 'up') || v === values.length - 1 && buttons[i].btn[b] === 'down') {
|
||||
line += '<a class="btn-floating disabled" data-command="' + buttons[i].btn[b] + '" class="values-buttons"><i class="material-icons"></i></a>';
|
||||
continue;
|
||||
}
|
||||
line += '<a class="btn-floating" data-index="' + v + '" data-command="' + buttons[i].btn[b] + '" class="values-buttons"><i class="material-icons"></i></a>';
|
||||
}
|
||||
if (buttons[i]['class']) {
|
||||
text += ' class="' + buttons[i]['class'] + '" ';
|
||||
}
|
||||
}
|
||||
if (style.length || tdstyle.length) {
|
||||
text += ' style="' + style + tdstyle + '">' + line + '</td>';
|
||||
} else {
|
||||
text += '>' + line + '</td>';
|
||||
}
|
||||
}
|
||||
|
||||
text += '</tr>';
|
||||
}
|
||||
var $lines = $table.find('.table-lines');
|
||||
if (!$lines.length) {
|
||||
$table.append('<tbody class="table-lines"></tbody>');
|
||||
$lines = $table.find('.table-lines');
|
||||
}
|
||||
|
||||
$lines.html(text);
|
||||
|
||||
$lines.find('.values-input').each(function () {
|
||||
var $this = $(this);
|
||||
var type = $this.attr('type');
|
||||
var name = $this.data('name');
|
||||
var id = $this.data('index');
|
||||
$this.data('old-value', values[id][name]);
|
||||
if (type === 'checkbox') {
|
||||
$this.prop('checked', values[id][name]);
|
||||
} else {
|
||||
$this.val(values[id][name]);
|
||||
}
|
||||
});
|
||||
$lines.find('a[data-command]').each(function () {
|
||||
var command = $(this).data('command');
|
||||
if (command === 'delete') {
|
||||
$(this).on('click', function () {
|
||||
var id = $(this).data('index');
|
||||
var elem = values[id];
|
||||
values.splice(id, 1);
|
||||
onChange && onChange();
|
||||
|
||||
setTimeout(function () {
|
||||
if (typeof tableEvents === 'function') {
|
||||
tableEvents(id, elem, 'delete');
|
||||
}
|
||||
|
||||
values2table(divId, values, onChange, onReady, maxRaw);
|
||||
}, 100);
|
||||
|
||||
if ($add.data('maxRaw')) {
|
||||
$add.data('raw', $add.data('raw') - 1);
|
||||
}
|
||||
})
|
||||
.addClass('red')
|
||||
.find('.material-icons')
|
||||
.html('delete');
|
||||
} else if (command === 'up') {
|
||||
$(this).on('click', function () {
|
||||
var id = $(this).data('index');
|
||||
var elem = values[id];
|
||||
values.splice(id, 1);
|
||||
values.splice(id - 1, 0, elem);
|
||||
onChange && onChange();
|
||||
setTimeout(function () {
|
||||
values2table(id, values, onChange, onReady, maxRaw);
|
||||
}, 100);
|
||||
}).find('i').html('arrow_upward');
|
||||
} else if (command === 'down') {
|
||||
$(this).on('click', function () {
|
||||
var id = $(this).data('index');
|
||||
var elem = values[id];
|
||||
values.splice(id, 1);
|
||||
values.splice(id + 1, 0, elem);
|
||||
onChange && onChange();
|
||||
setTimeout(function () {
|
||||
values2table(id, values, onChange, onReady, maxRaw);
|
||||
}, 100);
|
||||
}).find('i').html('arrow_downward');
|
||||
} else if (command === 'pair') {
|
||||
$(this).on('click', function () {
|
||||
if (typeof tableEvents === 'function') {
|
||||
var id = $(this).data('index');
|
||||
var elem = values[id];
|
||||
tableEvents(id, elem, 'pair');
|
||||
}
|
||||
}).attr('title', _('pair')).find('i').html('insert_link');
|
||||
} else if (command === 'unpair') {
|
||||
$(this).on('click', function () {
|
||||
if (typeof tableEvents === 'function') {
|
||||
var id = $(this).data('index');
|
||||
var elem = values[id];
|
||||
tableEvents(id, elem, 'unpair');
|
||||
}
|
||||
}).attr('title', _('unpair')).find('i').html('not_interested');
|
||||
}
|
||||
});
|
||||
|
||||
$lines.find('.values-input').on('change.adaptersettings', function () {
|
||||
var index = $(this).data('index');
|
||||
var name = $(this).data('name');
|
||||
if ($(this).attr('type') === 'checkbox') {
|
||||
if ($(this).prop('checked').toString() !== $(this).data('old-value') && onChange) onChange(name, index);
|
||||
values[index][name] = $(this).prop('checked');
|
||||
} else {
|
||||
if ($(this).val() !== $(this).data('old-value') && onChange) onChange(name, index);
|
||||
values[index][name] = $(this).val();
|
||||
|
||||
}
|
||||
}).on('keyup', function () {
|
||||
$(this).trigger('change.adaptersettings');
|
||||
});
|
||||
}
|
||||
if (typeof onReady === 'function') onReady();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the values from table.
|
||||
*
|
||||
* This function extracts the values from edit table, that was generated with values2table function.
|
||||
*
|
||||
* @param {string} divId name of the html element (or nothing).
|
||||
* @return {object} array with values
|
||||
*/
|
||||
function table2values(divId) {
|
||||
var $div;
|
||||
if (!divId) {
|
||||
$div = $('body');
|
||||
} else {
|
||||
$div = $('#' + divId);
|
||||
}
|
||||
var names = [];
|
||||
$div.find('.table-values th').each(function () {
|
||||
var name = $(this).data('name');
|
||||
if (name) {
|
||||
names.push(name);
|
||||
} else {
|
||||
names.push('___ignore___');
|
||||
}
|
||||
});
|
||||
|
||||
var values = [];
|
||||
var j = 0;
|
||||
$div.find('.table-lines tr').each(function () {
|
||||
values[j] = {};
|
||||
|
||||
$(this).find('td').each(function () {
|
||||
var $input = $(this).find('input');
|
||||
if ($input.length) {
|
||||
var name = $input.data('name');
|
||||
if ($input.attr('type') === 'checkbox') {
|
||||
values[j][name] = $input.prop('checked');
|
||||
} else {
|
||||
values[j][name] = $input.val();
|
||||
}
|
||||
}
|
||||
var $select = $(this).find('select');
|
||||
if ($select.length) {
|
||||
var name = $select.data('name');
|
||||
values[j][name] = $select.val() || '';
|
||||
}
|
||||
});
|
||||
j++;
|
||||
});
|
||||
|
||||
return values;
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
'use strict';
|
||||
|
||||
var systemLang = 'en';
|
||||
var systemDictionary = {};
|
||||
|
||||
function translateWord(text, lang, dictionary) {
|
||||
if (!text) return '';
|
||||
lang = lang || systemLang;
|
||||
dictionary = dictionary || systemDictionary;
|
||||
|
||||
text = text.toString();
|
||||
|
||||
if (dictionary[text]) {
|
||||
var newText = dictionary[text][lang];
|
||||
if (newText) {
|
||||
return newText;
|
||||
} else if (lang !== 'en') {
|
||||
newText = dictionary[text].en;
|
||||
if (newText) {
|
||||
return newText;
|
||||
}
|
||||
}
|
||||
} else if (typeof text === 'string' && !text.match(/_tooltip$/)) {
|
||||
console.log('"' + text + '": {"en": "' + text + '", "de": "' + text + '", "ru": "' + text + '", "pt": "' + text + '", "nl": "' + text + '", "fr": "' + text + '", "es": "' + text + '", "pl": "' + text + '", "it": "' + text + '"},');
|
||||
} else if (typeof text !== 'string') {
|
||||
console.warn('Trying to translate non-text:' + text);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function translateAll(selector, lang, dictionary) {
|
||||
lang = lang || systemLang;
|
||||
dictionary = dictionary || systemDictionary;
|
||||
if (!selector) {
|
||||
selector = 'body';
|
||||
}
|
||||
var $selector = $(selector);
|
||||
|
||||
// translate <div class="translate">textToTranslate</div>
|
||||
$selector.find('.translate').each(function (idx) {
|
||||
var text = $(this).attr('data-lang');
|
||||
if (!text) {
|
||||
text = $(this).html();
|
||||
$(this).attr('data-lang', text);
|
||||
}
|
||||
|
||||
var transText = translateWord(text, lang, dictionary);
|
||||
if (transText) {
|
||||
$(this).html(transText);
|
||||
}
|
||||
});
|
||||
// translate <input type="button" class="translateV" value="textToTranslate">
|
||||
$selector.find('.translateV').each(function (idx) {
|
||||
var text = $(this).attr('data-lang-value');
|
||||
if (!text) {
|
||||
text = $(this).attr('value');
|
||||
$(this).attr('data-lang-value', text);
|
||||
}
|
||||
|
||||
var transText = translateWord(text, lang, dictionary);
|
||||
if (transText) {
|
||||
$(this).attr('value', transText);
|
||||
}
|
||||
});
|
||||
$selector.find('.translateT').each(function (idx) {
|
||||
//<span class="ui-button-text translateT" title="TextToTranslate">Save</span>
|
||||
var text = $(this).attr('data-lang-title');
|
||||
if (!text) {
|
||||
text = $(this).attr('title');
|
||||
$(this).attr('data-lang-title', text);
|
||||
}
|
||||
var transText = translateWord(text, lang, dictionary);
|
||||
if (transText) {
|
||||
$(this).attr('title', transText);
|
||||
}
|
||||
});
|
||||
$selector.find('.translateP').each(function (idx) {
|
||||
//<span class="ui-button-text translateP" placeholder="TextToTranslate">Save</span>
|
||||
var text = $(this).attr('data-lang-placeholder');
|
||||
if (!text) {
|
||||
text = $(this).attr('placeholder');
|
||||
$(this).attr('data-lang-placeholder', text);
|
||||
}
|
||||
var transText = translateWord(text, lang, dictionary);
|
||||
if (transText) {
|
||||
$(this).attr('placeholder', transText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function translateName(name) {
|
||||
if (name && typeof name === 'object') {
|
||||
return name[systemLang] || name.en;
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
// make possible _('words to translate')
|
||||
var _ = function (text, arg1, arg2, arg3) {
|
||||
text = translateWord(text);
|
||||
|
||||
var pos = text.indexOf('%s');
|
||||
if (pos !== -1) {
|
||||
text = text.replace('%s', arg1);
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
|
||||
pos = text.indexOf('%s');
|
||||
if (pos !== -1) {
|
||||
text = text.replace('%s', arg2);
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
|
||||
pos = text.indexOf('%s');
|
||||
if (pos !== -1) {
|
||||
text = text.replace('%s', arg3);
|
||||
}
|
||||
|
||||
return text;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,834 @@
|
||||
(function ($) {
|
||||
'use strict';
|
||||
|
||||
if ($.fn.treeTable) return;
|
||||
|
||||
function nodeExpand() {
|
||||
var id = this.id;
|
||||
var $table = this.row.parent().parent(); // table > tbody > tr
|
||||
var options = $table.data('options');
|
||||
|
||||
if (options.name) {
|
||||
options.expanded = options.expanded || [];
|
||||
if (options.expanded.indexOf(id) === -1) {
|
||||
options.expanded.push(id);
|
||||
if (typeof Storage !== 'undefined') {
|
||||
window.localStorage.setItem(options.name + '-treetable', JSON.stringify(options.expanded));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var parentNode = $table.treetable('node', id);
|
||||
}
|
||||
|
||||
function nodeCollapse() {
|
||||
var id = this.id;
|
||||
var $table = this.row.parent().parent(); // table > tbody > tr
|
||||
var options = $table.data('options');
|
||||
|
||||
if (options.name && options.expanded) {
|
||||
var pos = options.expanded.indexOf(id);
|
||||
if (pos !== -1) {
|
||||
options.expanded.splice(pos, 1);
|
||||
if (typeof Storage !== 'undefined') {
|
||||
window.localStorage.setItem(options.name + '-treetable', JSON.stringify(options.expanded));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var parentNode = $table.treetable('node', id);
|
||||
}
|
||||
|
||||
function filter($table, word) {
|
||||
if (word) {
|
||||
word = word.toLowerCase();
|
||||
var options = $table.data('options');
|
||||
$table.find('tr').each(function () {
|
||||
if ($(this).hasClass('tree-table-main-header')) return;
|
||||
|
||||
if (!$(this).data('tt-branch') && $(this).find('td:first-child').text().indexOf(word) === -1) {
|
||||
$(this).addClass('filtered-out');
|
||||
} else {
|
||||
$(this).removeClass('filtered-out');
|
||||
}
|
||||
});
|
||||
var branch = '';
|
||||
var isOneVisible = false;
|
||||
// hide branches without children
|
||||
$table.find('tr').each(function () {
|
||||
if ($(this).data('tt-branch')) {
|
||||
if (branch) {
|
||||
if (!isOneVisible) {
|
||||
$table.find('tr[data-tt-id="' + branch + '"]').addClass('filtered-out');
|
||||
} else {
|
||||
$table.find('tr[data-tt-id="' + branch + '"]').removeClass('filtered-out');
|
||||
}
|
||||
}
|
||||
isOneVisible = false;
|
||||
branch = $(this).data('tt-id');
|
||||
} else if (branch) {
|
||||
if (!$(this).hasClass('filtered-out')) isOneVisible = true;
|
||||
}
|
||||
});
|
||||
if (branch) {
|
||||
if (!isOneVisible) {
|
||||
$table.find('tr[data-tt-id="' + branch + '"]').addClass('filtered-out');
|
||||
} else {
|
||||
$table.find('tr[data-tt-id="' + branch + '"]').removeClass('filtered-out');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$table.find('tr').removeClass('filtered-out');
|
||||
}
|
||||
}
|
||||
|
||||
function processMoveTasks(options, tasks, callback) {
|
||||
if (!tasks || !tasks.length) {
|
||||
callback && callback();
|
||||
return;
|
||||
}
|
||||
var task = tasks.shift();
|
||||
|
||||
options.moveId && options.moveId(task.oldId, task.newId, function (err) {
|
||||
setTimeout(function () {
|
||||
processMoveTasks(options, tasks, callback);
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
|
||||
function buildList(options, noButtons) {
|
||||
var table = noButtons ? '' : '<div class="treetablelist-buttons"><button class="treetable-list-btn-ok"></button><button class="treetable-list-btn-cancel"></button></div>';
|
||||
table += '<ul class="treetable-list">';
|
||||
var rows = options.rows;
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var parents = 0;
|
||||
var current = rows[i];
|
||||
while (current.parent) {
|
||||
var found = false;
|
||||
for (var j = 0; j < rows.length; j++) {
|
||||
if (rows[j].id === current.parent) {
|
||||
current = rows[j];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) break;
|
||||
parents++;
|
||||
}
|
||||
var title = rows[i].title;
|
||||
if (typeof title === 'object') {
|
||||
title = title[systemLang] || title.en;
|
||||
}
|
||||
var isNotFolder = rows[i].instance === undefined ? 0 : 1;
|
||||
table += '<li data-id="' + rows[i].id + '" class="' + (!isNotFolder ? 'treetable-list-folder' : 'treetable-list-item') + '" style="margin-left: ' + (parents * 19) + 'px; width: calc(100% - ' + (parents * 15 + 2 + isNotFolder * 7) + 'px);' +
|
||||
(rows[i].id === 'script.js.global' ? 'color: rgb(0, 128, 0);' : '') + '">' +
|
||||
(!isNotFolder ? '<span class="fancytree-expander"></span>' : '') + '<span class="fancytree-icon"></span>' + title + '</li>';
|
||||
}
|
||||
table += '</ul>';
|
||||
var $dlg = $(this);
|
||||
var $table = $(table);
|
||||
|
||||
$dlg.find('.treetablelist-buttons').remove();
|
||||
$dlg.find('.treetable-list').remove();
|
||||
$dlg.find('.tree-table-buttons').remove();
|
||||
$dlg.find('.tree-table-main').remove();
|
||||
$dlg.prepend($table);
|
||||
|
||||
var $buttons = $($table).find('.treetablelist-buttons');
|
||||
var $list = $($table).find('.treetable-list');
|
||||
|
||||
$list.sortable({
|
||||
cancel: '.treetable-list-folder',
|
||||
axis: 'y'
|
||||
}).data('options', options);
|
||||
|
||||
var that = this;
|
||||
|
||||
$buttons.find('.treetable-list-btn-ok').button({
|
||||
icons: {primary: 'ui-icon-check'},
|
||||
text: false
|
||||
})
|
||||
.css({width: 24, height: 24})
|
||||
.on('click', function () {
|
||||
// analyse new structure
|
||||
var currentFolder = '';
|
||||
var tasks = [];
|
||||
$list.find('li').each(function () {
|
||||
var id = $(this).data('id');
|
||||
if ($(this).hasClass('treetable-list-folder')) {
|
||||
currentFolder = id;
|
||||
} else {
|
||||
var parts = id.split('.');
|
||||
var name = parts.pop();
|
||||
if (parts.join('.') !== currentFolder) {
|
||||
tasks.push({oldId: id, newId: currentFolder + '.' + name});
|
||||
}
|
||||
}
|
||||
});
|
||||
processMoveTasks(options, tasks, function () {
|
||||
buildTable.call(that, options);
|
||||
});
|
||||
});
|
||||
$buttons.find('.treetable-list-btn-cancel').button({
|
||||
icons: {primary: 'ui-icon-cancel'},
|
||||
text: false
|
||||
})
|
||||
.css({width: 24, height: 24})
|
||||
.on('click', function () {
|
||||
buildTable.call(that, options);
|
||||
});
|
||||
}
|
||||
|
||||
function getIconFromObj(obj, imgPath, classes) {
|
||||
var icon = '';
|
||||
var alt = '';
|
||||
var isCommon = obj && obj.common;
|
||||
|
||||
if (isCommon) {
|
||||
if (isCommon.icon) {
|
||||
if (!isCommon.icon.match(/^data:image\//)) {
|
||||
if (isCommon.icon.indexOf('.') !== -1) {
|
||||
var instance;
|
||||
if (obj.type === 'instance') {
|
||||
icon = '/adapter/' + obj.common.name + '/' + obj.common.icon;
|
||||
} else if (obj._id.match(/^system\.adapter\./)) {
|
||||
instance = obj._id.split('.', 3);
|
||||
if (obj.common.icon[0] === '/') {
|
||||
instance[2] += obj.common.icon;
|
||||
} else {
|
||||
instance[2] += '/' + obj.common.icon;
|
||||
}
|
||||
icon = '/adapter/' + instance[2];
|
||||
} else {
|
||||
instance = obj._id.split('.', 2);
|
||||
if (obj.common.icon[0] === '/') {
|
||||
instance[0] += obj.common.icon;
|
||||
} else {
|
||||
instance[0] += '/' + obj.common.icon;
|
||||
}
|
||||
icon = '/adapter/' + instance[0];
|
||||
}
|
||||
} else {
|
||||
return '<i class="material-icons ' + (classes || 'treetable-icon') + '">' + isCommon.icon + '</i>';
|
||||
}
|
||||
|
||||
} else {
|
||||
icon = isCommon.icon;
|
||||
}
|
||||
alt = obj.type;
|
||||
} else {
|
||||
imgPath = imgPath || 'lib/css/fancytree/';
|
||||
if (obj.type === 'device') {
|
||||
icon = imgPath + 'device.png';
|
||||
alt = 'device';
|
||||
} else if (obj.type === 'channel') {
|
||||
icon = imgPath + 'channel.png';
|
||||
alt = 'channel';
|
||||
} else if (obj.type === 'state') {
|
||||
icon = imgPath + 'state.png';
|
||||
alt = 'state';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (icon) return '<img class="' + (classes || 'treetable-icon') + '" src="' + icon + '" alt="' + alt + '" />';
|
||||
return '';
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/35969656/how-can-i-generate-the-opposite-color-according-to-current-color
|
||||
/*function invertColor(hex) {
|
||||
if (hex.indexOf('#') === 0) {
|
||||
hex = hex.slice(1);
|
||||
}
|
||||
// convert 3-digit hex to 6-digits.
|
||||
if (hex.length === 3) {
|
||||
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
||||
}
|
||||
if (hex.length !== 6) {
|
||||
return false;
|
||||
}
|
||||
var r = parseInt(hex.slice(0, 2), 16),
|
||||
g = parseInt(hex.slice(2, 4), 16),
|
||||
b = parseInt(hex.slice(4, 6), 16);
|
||||
// http://stackoverflow.com/a/3943023/112731
|
||||
return (r * 0.299 + g * 0.587 + b * 0.114) <= 186;
|
||||
}*/
|
||||
function getUsersGroups(objects, groups) {
|
||||
var usersGroups = {};
|
||||
for (var g = 0; g < groups.length; g++) {
|
||||
if (objects[groups[g]] && objects[groups[g]].common && objects[groups[g]].common.members) {
|
||||
var users = objects[groups[g]].common.members;
|
||||
for (var u = 0; u < users.length; u++) {
|
||||
usersGroups[users[u]] = usersGroups[users[u]] || [];
|
||||
var name = objects[groups[g]].common.name;
|
||||
if (name && typeof name === 'object') {
|
||||
name = name[systemLang] || 'en';
|
||||
}
|
||||
usersGroups[users[u]].push({id: groups[g], name: name || id.replace('system.group.', '')});
|
||||
}
|
||||
}
|
||||
}
|
||||
return usersGroups;
|
||||
}
|
||||
|
||||
function buildTable(options) {
|
||||
var table = '';
|
||||
// detect materialize
|
||||
var buttonTag = window.M && window.M.toast ? 'a' : 'button';
|
||||
if (options.panelButtons) {
|
||||
table += '<div class="row tree-table-buttons m">';
|
||||
for (var z = 0; z < options.panelButtons.length; z++) {
|
||||
table += '<' + buttonTag + ' class="btn-floating waves-effect waves-light blue btn-custom-' + z + '" title="' + (options.panelButtons[z].title || '') + '" ' + (options.panelButtons[z].id ? 'id="' + options.panelButtons[z].id + '"' : '') + '>';
|
||||
// detect materialize
|
||||
if (window.M && window.M.toast) {
|
||||
table += '<i class="material-icons">' + (options.panelButtons[z].icon || '') + '</i>';
|
||||
}
|
||||
table += '</' + buttonTag + '>';
|
||||
}
|
||||
if (options.moveId) {
|
||||
table += '<' + buttonTag + ' class="btn-floating waves-effect waves-light blue treetable-sort" title="' + _('reorder') + '">';
|
||||
// detect materialize
|
||||
if (window.M && window.M.toast) {
|
||||
table += '<i class="material-icons">import_export</i>';
|
||||
}
|
||||
table += '</' + buttonTag + '>';
|
||||
}
|
||||
table += '</div>';
|
||||
}
|
||||
|
||||
// build header
|
||||
table += '<div class="row tree-table-body"><table class="tree-table-main">';
|
||||
table += ' <thead>';
|
||||
table += ' <tr class="tree-table-main-header">';
|
||||
for (var ch = 0; ch < options.columns.length; ch++) {
|
||||
if (options.columns[ch] === 'name') {
|
||||
table += ' <th' + (options.widths && options.widths[ch] ? ' class="treetable-th-name" style="width: ' + options.widths[ch] + '"' : '') + '>';
|
||||
table += ' <input placeholder="' + _('name') + '" class="filter_name treetable-filter" />';
|
||||
table += ' <button data-id="filter_name" role="button" class="filter-clear"></button>';
|
||||
table += ' </th>';
|
||||
} else {
|
||||
table += ' <th' + (options.widths && options.widths[ch] ? ' style="width: ' + options.widths[ch] + '"' : '') + '>' + _(options.columns[ch]) + '</th>';
|
||||
}
|
||||
}
|
||||
if (options.buttons) {
|
||||
table += ' <th' + (options.buttonsWidth ? ' style="width: ' + options.buttonsWidth + '"' : '') + '></th>';
|
||||
}
|
||||
table += ' </tr>';
|
||||
table += '</thead>';
|
||||
// build body
|
||||
table += '<tbody>';
|
||||
|
||||
// <tr data-tt-id="system.adapter.0" data-tt-branch='true'>
|
||||
var rows = [];
|
||||
var rootEx = options.root ? new RegExp('^' + options.root.replace(/\./g, '\\.') + '\\.') : null;
|
||||
var instances = options.columns.indexOf('instance') !== -1 ? [] : null;
|
||||
|
||||
for (var id in options.objects) {
|
||||
if (!options.objects.hasOwnProperty(id)) continue;
|
||||
var m;
|
||||
if (instances && options.objects[id].type === 'instance' &&
|
||||
(m = id.match(/^system\.adapter\.javascript\.(\d+)$/))) {
|
||||
instances.push(m[1]);
|
||||
}
|
||||
|
||||
if (rootEx && !rootEx.test(id)) continue;
|
||||
var common = options.objects[id].common;
|
||||
|
||||
var obj = {
|
||||
id: id,
|
||||
parent: null,
|
||||
_class: 'treetable-' + options.objects[id].type
|
||||
};
|
||||
|
||||
if (options.objects[id].type === 'channel') {
|
||||
obj.folder = true;
|
||||
}
|
||||
for (var cb = 0; cb < options.columns.length; cb++) {
|
||||
if (options.columns[cb] === 'instance') {
|
||||
if (options.objects[id].type === 'script') {
|
||||
obj.instance = common ? common.engine.split('.').pop() : 0;
|
||||
}
|
||||
} else if (common && options.columns[cb] !== 'id') {
|
||||
var val = common[options.columns[cb]];
|
||||
if (val !== undefined) {
|
||||
obj[options.columns[cb]] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options.members) {
|
||||
obj.members = common.members;
|
||||
}
|
||||
if (options.colors) {
|
||||
obj.color = common.color;
|
||||
}
|
||||
|
||||
rows.push(obj);
|
||||
}
|
||||
|
||||
// sort
|
||||
rows.sort(function (a, b) {
|
||||
if (a.id > b.id) return 1;
|
||||
if (a.id < b.id) return -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
// find parents and extend members
|
||||
for (var pp = 0; pp < rows.length; pp++) {
|
||||
// find parent:
|
||||
var parts = rows[pp].id.split('.');
|
||||
var title = parts.pop();
|
||||
var parent = parts.join('.');
|
||||
rows[pp].title = title;
|
||||
for (var p = 0; p < rows.length; p++) {
|
||||
if (rows[p].id === parent) {
|
||||
rows[pp].parent = parent;
|
||||
rows[p].children = rows[p].children || [];
|
||||
rows[p].children.push(pp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (parts.length === 1) {
|
||||
rows[pp]._class += ' treetable-root'
|
||||
}
|
||||
|
||||
}
|
||||
for (var ppp = 0; ppp < rows.length; ppp++) {
|
||||
rows[ppp].realChildren = rows[ppp].children ? !!rows[ppp].children.length : false;
|
||||
}
|
||||
|
||||
if (options.groups) {
|
||||
var users = getUsersGroups(options.objects, options.groups);
|
||||
|
||||
for (var g = rows.length - 1; g >= 0; g--) {
|
||||
rows[g].groups = users[rows[g].id] || [];
|
||||
}
|
||||
}
|
||||
|
||||
if (options.members) {
|
||||
for (var k = rows.length - 1; k >= 0; k--) {
|
||||
// extend members
|
||||
if (rows[k].members) {
|
||||
rows[k].children = rows[k].children || [];
|
||||
var members = Object.assign([], rows[k].members);
|
||||
members.sort();
|
||||
for (var mm = 0; mm < members.length; mm++) {
|
||||
obj = {
|
||||
id: members[mm],
|
||||
title: members[mm],
|
||||
parent: rows[k].id,
|
||||
_class: 'treetable-member'
|
||||
};
|
||||
rows[k].children.push(members[mm]);
|
||||
|
||||
if (options.objects[obj.id]) {
|
||||
var ccommon = options.objects[obj.id].common;
|
||||
if (ccommon) {
|
||||
for (var ccb = 0; ccb < options.columns.length; ccb++) {
|
||||
var attr = options.columns[ccb];
|
||||
if (attr === 'members' || attr === 'id' || attr === 'title' || attr === 'icon') continue;
|
||||
var vval = ccommon[options.columns[ccb]];
|
||||
if (vval !== undefined) {
|
||||
obj[attr] = vval;
|
||||
}
|
||||
}
|
||||
if (options.colors) {
|
||||
obj.color = common.color;
|
||||
}
|
||||
}
|
||||
}
|
||||
rows.splice(k + 1, 0, obj);
|
||||
}
|
||||
delete rows[k].members;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var instSelect = '';
|
||||
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
// title
|
||||
table += '<tr data-tt-id="' + rows[i].id + '"' + (!!rows[i].children ? ' data-tt-branch="true"' : '') + (rows[i].parent ? ' data-tt-parent-id="' + rows[i].parent + '"' : '') + ' class="';
|
||||
if (rows[i]._class) {
|
||||
table += rows[i]._class + ' ';
|
||||
}
|
||||
if (options.draggable) {
|
||||
table += options.draggable + ' ';
|
||||
}
|
||||
if (rows[i].children && rows[i].children.length) {
|
||||
table += 'not-empty';
|
||||
} else {
|
||||
table += 'is-empty';
|
||||
}
|
||||
|
||||
table += '">';
|
||||
for (var c = 0; c < options.columns.length; c++) {
|
||||
var aattr = options.columns[c];
|
||||
var style = '';
|
||||
var _class = (options.classes && options.classes[c]) || '';
|
||||
|
||||
if (aattr === 'groups') {
|
||||
_class += ' m';
|
||||
}
|
||||
|
||||
if (!c && rows[i].hasOwnProperty('children')) {
|
||||
_class += ' treetable-folder fancytree-exp-c fancytree-has-children fancytree-ico-cf';
|
||||
if (rows[i].id === 'script.js.global') {
|
||||
style += ' color: rgb(0, 128, 0);'; // green
|
||||
} else {
|
||||
style += ' color: rgb(0, 0, 128);'
|
||||
}
|
||||
table += '<td style="' + style + '" class="' + _class + '">';
|
||||
if (rows[i].children && rows[i].children.length) {
|
||||
table += '<span class="treetable-counter">' + rows[i].children.length + '</span>';
|
||||
}
|
||||
} else {
|
||||
table += '<td style="' + style + '" class="' + _class + '">';
|
||||
}
|
||||
if (!c && options.colors && rows[i].color) {
|
||||
table += '<div style="background: ' + rows[i].color + '" class="treetable-color"></div>';
|
||||
}
|
||||
if (!c && options.icons) {
|
||||
table += getIconFromObj(options.objects[rows[i].id], options.imgPath) || '<div class="treetable-icon-empty"> </div>';
|
||||
}
|
||||
if (aattr === 'enabled') {
|
||||
table += '<input data-attr="' + aattr + '" data-id="' + rows[i].id + '" class="treetable-input" type="checkbox" ' + (rows[i][aattr] ? 'checked' : '') + ' ' + (options.readOnly && options.readOnly[c] !== false ? 'disabled' : '') + '>';
|
||||
} else
|
||||
if (aattr === 'groups') {
|
||||
for (var gg = 0; gg < rows[i].groups.length; gg++) {
|
||||
var gId = rows[i].groups[gg].id;
|
||||
table += '<div class="chip">' + getIconFromObj(options.objects[gId], null, '') + rows[i].groups[gg].name + '</div>'; //<i class="close material-icons" data-group="' + rows[i].groups[gg].id + '" data-user="' + rows[i].id + '">close</i></div>';
|
||||
}
|
||||
} else
|
||||
// edit instance
|
||||
if (aattr === 'instance') {
|
||||
if (rows[i].instance !== undefined && instances.length > 1) {
|
||||
instSelect = '<select class="treetable-instance" data-id="' + rows[i].id + '">';
|
||||
for (var ii = 0; ii < instances.length; ii++) {
|
||||
instSelect += '<option value="' + instances[ii] + '" ' + (instances[ii] === rows[i].instance ? 'selected' : '') + '>' + instances[ii] + '</option>';
|
||||
}
|
||||
instSelect += '</select>';
|
||||
|
||||
table += instSelect;
|
||||
} else {
|
||||
table += '<span>' + (rows[i].instance === undefined ? '' : rows[i].instance) + '</span>';
|
||||
}
|
||||
} else if (aattr === 'icon') {
|
||||
table += getIconFromObj(options.objects[rows[i].id], options.imgPath);
|
||||
} else {
|
||||
var vall = rows[i][aattr] || '';
|
||||
if (vall && typeof vall === 'object' && vall.en) {
|
||||
vall = vall[systemLang] || vall.en;
|
||||
}
|
||||
|
||||
table += '<span>' + vall + '</span>';
|
||||
}
|
||||
table += '</td>';
|
||||
}
|
||||
// add buttons
|
||||
if (options.buttons) {
|
||||
table += '<td class="treetable-buttons" style="' + (options.buttonsStyle || '') + '">';
|
||||
var text = '';
|
||||
for (var jj = 0; jj < options.buttons.length; jj++) {
|
||||
if (options.buttons[jj].match && !options.buttons[jj].match(rows[i].id, rows[i].parent)) {
|
||||
text += '<div class="treetable-button-empty"> </div>';
|
||||
} else {
|
||||
text += '<' + buttonTag + ' data-id="' + rows[i].id + '" class="select-button-' + jj + ' select-button-custom td-button" style="margin-right: 3px;'+ '" data-parent="' + rows[i].parent + '" data-children="' + !!rows[i].realChildren + '" title="' + (options.buttons[jj].title || '') + '">';
|
||||
// detect materialize
|
||||
if (window.M && window.M.toast) {
|
||||
text += '<i class="material-icons">' + (options.buttons[jj].icon || '') + '</i>';
|
||||
}
|
||||
text += '</' + buttonTag + '>';
|
||||
}
|
||||
}
|
||||
|
||||
table += text + '</td>';
|
||||
}
|
||||
|
||||
table += '</tr>';
|
||||
}
|
||||
table += '</body>';
|
||||
|
||||
var $dlg = $(this);
|
||||
var $table = $(table);
|
||||
|
||||
$dlg.find('span:first-child').remove();
|
||||
$dlg.find('.treetablelist-buttons').remove();
|
||||
$dlg.find('.treetable-list').remove();
|
||||
$dlg.find('.tree-table-buttons').remove();
|
||||
$dlg.find('.tree-table-main').remove();
|
||||
|
||||
$dlg.prepend($table);
|
||||
options.rows = rows;
|
||||
var $treeTable = $($table[1]).find('>table');
|
||||
var $buttons = $($table[0]);
|
||||
|
||||
$treeTable.data('options', options);
|
||||
|
||||
$treeTable.treetable({
|
||||
expandable: true,
|
||||
clickableNodeNames: true,
|
||||
expanderTemplate: '',
|
||||
indenterTemplate: '<span class="fancytree-expander"></span><span class="fancytree-icon"></span>',
|
||||
onNodeExpand: nodeExpand,
|
||||
onNodeCollapse: nodeCollapse,
|
||||
stringCollapse: _('collapse'),
|
||||
stringExpand: _('expand')
|
||||
});
|
||||
|
||||
var $tbody = $treeTable.find('tbody');
|
||||
|
||||
$tbody.on('click', 'tr', function() {
|
||||
$('.selected').not(this).removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
var $table = $(this).parent().parent();
|
||||
var options = $table.data('options');
|
||||
var id = $(this).data('tt-id');
|
||||
options.onChange && options.onChange($(this).data('tt-id'), options.oldId);
|
||||
options.oldId = id;
|
||||
});
|
||||
|
||||
if (options.buttons) {
|
||||
for (var b = 0; b < options.buttons.length; b++) {
|
||||
var $btn = $tbody.find('.select-button-' + b).button(options.buttons[b]).on('click', function () {
|
||||
var cb = $(this).data('callback');
|
||||
if (cb) {
|
||||
cb.call($(this), $(this).data('id'), $(this).data('children'), $(this).data('parent'));
|
||||
}
|
||||
}).data('callback', options.buttons[b].click).attr('title', options.buttons[b].title || '');
|
||||
|
||||
if ($btn.length === 0) continue;
|
||||
if (options.buttons[b].width) $btn.css({width: options.buttons[b].width});
|
||||
if (options.buttons[b].height) $btn.css({height: options.buttons[b].height});
|
||||
/*if (options.buttons[b].match) {
|
||||
$btn.each(function () {
|
||||
options.buttons[b].match.call($(this), $(this).data('id'));
|
||||
});
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
if (options.panelButtons) {
|
||||
for (var zz = 0; zz < options.panelButtons.length; zz++) {
|
||||
var $zz = $buttons.find('.btn-custom-' + zz);
|
||||
$zz
|
||||
.on('click', options.panelButtons[zz].click)
|
||||
.attr('title', options.panelButtons[zz].title || '');
|
||||
|
||||
// detect materialize
|
||||
if (!window.M || !window.M.toast) {
|
||||
$zz
|
||||
.button(options.panelButtons[zz])
|
||||
.css({width: 24, height: 24})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$treeTable.find('.filter_name').on('change', function () {
|
||||
var timer = $(this).data('timer');
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
var $this = $(this);
|
||||
$this.data('timer', setTimeout(function () {
|
||||
$this.data('timer', null);
|
||||
var val = $table.find('.filter_name').val();
|
||||
if (val) {
|
||||
$this.addClass('input-not-empty');
|
||||
} else {
|
||||
$this.removeClass('input-not-empty');
|
||||
}
|
||||
filter($($table[1]), $table.find('.filter_name').val());
|
||||
}));
|
||||
}).on('keyup', function () {
|
||||
$(this).trigger('change');
|
||||
});
|
||||
$treeTable.find('.filter-clear')
|
||||
.button({icons: {primary: 'ui-icon-close'}, text: false})
|
||||
.on('click', function () {
|
||||
var name = $(this).data('id');
|
||||
$table.find('.' + name).val('').trigger('change');
|
||||
});
|
||||
var that = this;
|
||||
$buttons.find('.treetable-sort')
|
||||
.button({icons: {primary: 'ui-icon-arrowthick-2-n-s'}, text: false})
|
||||
.css({width: 24, height: 24})
|
||||
.on('click', function () {
|
||||
buildList.call(that, options);
|
||||
});
|
||||
|
||||
if (options.onEdit) {
|
||||
$treeTable.find('.treetable-instance').on('change', function () {
|
||||
options.onEdit($(this).data('id'), 'instance', $(this).val());
|
||||
});
|
||||
|
||||
$treeTable.find('.treetable-input').on('change', function (e) {
|
||||
e.stopPropagation();
|
||||
var val;
|
||||
if ($(this).attr('type') === 'checkbox') {
|
||||
val = $(this).prop('checked');
|
||||
} else {
|
||||
val = $(this).val();
|
||||
}
|
||||
var id = $(this).data('id');
|
||||
if (options.onEdit) {
|
||||
if (options.onEdit(id, $(this).data('attr'), val) === false) {
|
||||
// todo support more types
|
||||
$(this).prop('checked', true);
|
||||
}
|
||||
}
|
||||
}).on('keyup', function () {
|
||||
$(this).trigger('change');
|
||||
});
|
||||
} else {
|
||||
$treeTable.find('.treetable-instance').prop('disabled', true)
|
||||
}
|
||||
if (typeof options.onReady === 'function') {
|
||||
options.onReady($treeTable);
|
||||
}
|
||||
}
|
||||
|
||||
function reInit() {
|
||||
var $table = $(this).find('.tree-table-main');
|
||||
// remember selected
|
||||
var id = $table.find('.selected').data('tt-id');
|
||||
var expanded = $table.find('.expanded');
|
||||
var exIDs = [];
|
||||
expanded.each(function (el) {
|
||||
exIDs.push($(this).data('tt-id'));
|
||||
});
|
||||
var nameFilter = $table.find('.filter_name').val();
|
||||
var options = $table.data('options');
|
||||
buildTable.call(this, options);
|
||||
$table = $(this).find('.tree-table-main');
|
||||
for (var e = 0; e < exIDs.length; e++) {
|
||||
try {
|
||||
$table.treetable('expandNode', exIDs[e]);
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
if (id) {
|
||||
var node = $table.treetable('node', id);
|
||||
node && $table.treetable('reveal', id);
|
||||
node && node.row && node.row.addClass('selected');
|
||||
}
|
||||
if (nameFilter) $table.find('.filter_name').val(nameFilter).trigger('change');
|
||||
}
|
||||
|
||||
var methods = {
|
||||
init: function (options) {
|
||||
options.imgPath = options.imgPath || 'lib/css/fancytree/';
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
buildTable.call(this[i], options);
|
||||
|
||||
if (typeof Storage !== 'undefined') {
|
||||
var exIDs = window.localStorage.getItem(options.name + '-treetable');
|
||||
if (exIDs) {
|
||||
exIDs = JSON.parse(exIDs);
|
||||
var $table = $(this[i]).find('.tree-table-main');
|
||||
for (var e = 0; e < exIDs.length; e++) {
|
||||
try {
|
||||
$table.treetable('expandNode', exIDs[e]);
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
destroy: function () {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
var $table = $(this[i]).find('.tree-table-main');
|
||||
if ($table.length && $table.data('options')) {
|
||||
$table.data('options', null);
|
||||
$(this[i]).html('');
|
||||
}
|
||||
}
|
||||
},
|
||||
expand: function (id) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
var $table = $(this[i]).find('.tree-table-main');
|
||||
try {
|
||||
if (id) {
|
||||
$table.treetable('expandNode', id);
|
||||
} else {
|
||||
$table.treetable('expandAll', id);
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
},
|
||||
collapse: function (id) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
var $table = $(this[i]).find('.tree-table-main');
|
||||
try {
|
||||
if (id) {
|
||||
$table.treetable('collapseNode', id);
|
||||
} else {
|
||||
$table.treetable('collapseAll');
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
},
|
||||
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 $table = $(this[i]).find('.tree-table-main');
|
||||
$table.find('.selected').removeClass('selected');
|
||||
try {
|
||||
$table.treetable('reveal', currentId);
|
||||
} catch (e) {
|
||||
}
|
||||
var node = $table.treetable('node', currentId);
|
||||
node && node.row.addClass('selected');
|
||||
}
|
||||
onSuccess && onSuccess();
|
||||
return this;
|
||||
},
|
||||
reinit: function () {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
reInit.call(this[i]);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
'object': function (id, obj) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
var $table = $(this[i]).find('.tree-table-main');
|
||||
if ($table.updateTimer) {
|
||||
clearTimeout($table.updateTimer);
|
||||
}
|
||||
var options = $table.data('options');
|
||||
if (options && options.root && !id.match('^' + options.root.replace(/\./g, '\\.') + '\\.')) continue;
|
||||
|
||||
var elem = this[i];
|
||||
// do not update too often. de-bounce it
|
||||
(function (_elem, _$table) {
|
||||
_$table.updateTimer = setTimeout(function () {
|
||||
reInit.call(_elem);
|
||||
}, 300);
|
||||
})(elem, $table);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.treeTable = 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.treeTable');
|
||||
}
|
||||
};
|
||||
})(jQuery);
|
||||
Reference in New Issue
Block a user