You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3469 lines
140 KiB

/// <reference path="./monaco-editor/monaco.d.ts" />
/* global _, monaco */
function Scripts(main) {
/** @type {Scripts} */
const that = this;
this.list = [];
this.groups = [];
this.hosts = [];
this.$grid = $('#grid-scripts');
this.$dialogCron = $('#dialog-cron');
this.$dialogScript = $('#dialog-script');
this.$dialogExport = $('#dialog-export-blockly');
this.editor = null;
this.editorDialog = null;
this.changed = false;
this.main = main;
this.currentId = null;
this.engines = [];
this.currentEngine = '';
this.languageLoaded = [false, false];
this.blocklyWorkspace = null;
this.prepared = false;
this.typings = {}; // TypeScript declarations
this.globalTypingHandles = []; // Handles to the global typings added to the editor
this.alive = false;
function setChanged(isChanged) {
that.changed = isChanged;
if (typeof parent !== 'undefined' && parent) {
parent.configNotSaved = isChanged;
}
}
function addScript(group) {
$('#dialog-new-script').data('callback', function (type) {
group = group || 'script.js.common';
// Find new unique name
var newText = _('Script');
var idx = 1;
var name = newText + idx;
while (that.main.objects[group + '.' + name]) {
if (idx === '') idx = 0;
idx++;
name = newText + idx;
}
var instance = '';
var engineType = type;
// find first instance
for (var i = 0; i < that.main.instances.length; i++) {
if (that.main.objects[that.main.instances[i]] && that.main.objects[that.main.instances[i]] && that.main.objects[that.main.instances[i]].common.engineTypes) {
instance = that.main.instances[i];
if (!engineType) {
if (typeof that.main.objects[main.instances[i]].common.engineTypes === 'string') {
engineType = that.main.objects[that.main.instances[i]].common.engineTypes;
} else {
engineType = that.main.objects[that.main.instances[i]].common.engineTypes[0];
}
}
break;
}
}
var id = group + '.' + name.replace(/[\s"']/g, '_');
that.main.socket.emit('setObject', id, {
common: {
name: name,
engineType: engineType,
source: '',
enabled: false,
engine: instance
},
type: 'script'
}, function (err) {
if (err) {
that.main.showError(err);
that.init(true);
} else {
setTimeout(function () {
that.$grid.treeTable('show', id);
editScript(id);
}, 500);
}
});
}).dialog('open');
}
function addScriptInGroup(_group) {
fillGroups('edit-new-group-group');
if (that.main.objects[_group] && that.main.objects[_group].type === 'script') {
_group = getGroup(_group);
}
$('#edit-new-group-group').val(_group || 'script.js');
if (!that.$newGroupDialog) {
that.$newGroupDialog = $('#dialog-new-group').dialog({
autoOpen: false,
modal: true,
width: 400,
height: 220,
resizable: false,
title: _('Create new group'),
buttons: [
{
id: 'script-group-button-save',
text: _('Ok'),
click: function () {
var group = $('#edit-new-group-group').val() || 'script.js';
var name = $('#edit-new-group-name').val();
if (!name) {
that.main.showError(_('No group name'));
that.$newGroupDialog.dialog('close');
return;
}
group += '.' + name.replace(/["'\s.]+/g, '_');
// check if object with such name exists
if (that.main.objects[group]) {
that.main.showMessage(_('Object %s yet exists', group));
that.$newGroupDialog.dialog('close');
} else {
$('#script-group-button-save').button('disable');
$('#script-group-button-cancel').button('disable');
that.main.socket.emit('setObject', group, {
common: {
name: name
},
type: 'channel'
}, function (err) {
$('#script-group-button-cancel').button('enable');
that.$newGroupDialog.dialog('close');
if (err) {
that.main.showError(err);
that.init(true);
} else {
setTimeout(function () {
that.$grid.treeTable('show', group);
editScript(group);
}, 500);
}
});
}
}
},
{
id: 'script-group-button-cancel',
text: _('Cancel'),
click: function () {
that.$newGroupDialog.dialog('close');
}
}
],
open: function (event) {
$(event.target).parent().find('.ui-dialog-titlebar-close .ui-button-text').html('');
$('#script-group-button-save').button('disable');
$('#script-group-button-cancel').button('enable');
$('#edit-new-group-name').val('');
}
});
$('#edit-new-group-name').on('change', function () {
if ($(this).val()) {
$('#script-group-button-save').button('enable');
} else {
$('#script-group-button-save').button('disable');
}
}).on('keyup', function (e) {
$(this).trigger('change');
if (e.keyCode === 13) $('#script-group-button-save').trigger('click');
});
}
that.$newGroupDialog.dialog('open');
}
this.prepare = function () {
if (this.prepared) return;
this.prepared = true;
this.$dialogCron.dialog({
autoOpen: false,
modal: true,
width: 700,
height: 550,
resizable: false,
title: _('Cron expression'),
buttons: [
{
id: 'dialog_cron_insert',
text: _('Insert'),
click: function () {
var val = $('#div-cron').cron('value');
that.$dialogCron.dialog('close');
if (!$('#dialog-script').is(':visible')) {
insertTextIntoEditor(that.editor, '"' + val + '"');
that.editor.focus();
} else {
insertTextIntoEditor(that.editorDialog, '"' + val + '"');
that.editorDialog.focus();
}
}
},
{
id: 'dialog_cron_clear',
text: _('Clear'),
click: function () {
$('#div-cron').cron('value', '* * * * *');
}
},
{
id: 'dialog_cron_callback',
text: _('Set CRON'),
click: function () {
}
},
{
text: _('Cancel'),
click: function () {
that.$dialogCron.dialog('close');
}
}
]
});
// used to store the size difference between the dialog and the editor
// the monaco editor has problems automatically resizing to a smaller size
let dialogSizeDelta = {};
this.$dialogScript.dialog({
autoOpen: false,
modal: true,
width: 700,
height: 550,
resizable: true,
title: _('Edit script'),
resize: function () {
that.main.saveConfig('script-edit-width', $(this).parent().width());
that.main.saveConfig('script-edit-height', $(this).parent().height() + 10);
if (that.editorDialog) {
// restore the editor layout using the stored values
that.editorDialog.layout({
width: that.$dialogScript.width() - dialogSizeDelta.width,
height: that.$dialogScript.height() - dialogSizeDelta.height
});
}
},
open: function() {
that.$dialogScript.css('overflow', 'hidden');
if (that.editorDialog) {
that.editorDialog.layout();
// remember how the editor is arranged in the dialog
dialogSizeDelta = {
width: that.$dialogScript.width() - $('#dialog-script-editor').width(),
height: that.$dialogScript.height() - $('#dialog-script-editor').height()
};
}
},
beforeClose: function () {
if (that.editorDialog._changed) {
if (window.confirm(_('Script changes are not saved. Discard?'))) {
that.editorDialog._changed = false;
return null;
} else {
return false;
}
}
},
buttons: [
{
id: 'dialog_script_save',
text: _('Save'),
click: function () {
that.editorDialog._changed = false;
that.$dialogScript.dialog('close');
const val = that.editorDialog.getValue();
var cb = that.$dialogScript.data('callback');
that.$dialogScript.data('callback', null);
if (typeof cb === 'function') cb(val);
if (that.editorDialog._isReturn && val.indexOf('return ') === -1) {
that.main.showMessage(_('No return found'), _('Error'), 'alert');
}
}
},
{
text: _('Cancel'),
click: function () {
that.$dialogScript.dialog('close');
}
}
]
});
$('#div-cron').cron({
value: '* * * * *'
});
$('#script-edit-button-save').button({
icons: {
primary: 'ui-icon-disk'
},
label: _('Save')
}).click(function () {
that.saveScript();
});
$('#script-edit-button-cancel').button({
icons: {
primary: 'ui-icon-cancel'
},
label: _('Cancel')
}).click(function () {
editScript(that.currentId);
});
$('#script-output-clear').button({
icons: {
primary: 'ui-icon-trash'
},
text: false
}).css({width: 20, height: 20}).click(function () {
$('#script-output').html('');
}).attr('title', _('Clear output'));
$('#script-output-down').button({
icons: {
primary: 'ui-icon-arrowthickstop-1-s'
},
text: false
}).css({width: 20, height: 20}).click(function () {
if (that.$parentOutput) that.$parentOutput.scrollTop($('#script-output').height());
}).attr('title', _('Scroll down'));
$('#edit-wrap-lines').on('change', function () {
that.main.saveConfig('script-editor-wrap-lines', $(this).prop('checked'));
setEditorOptions(that.editor, {
lineWrap: $(this).prop('checked')
});
});
$('#dialog-edit-wrap-lines').on('change', function () {
that.main.saveConfig('script-editor-dialog-wrap-lines', $(this).prop('checked'));
setEditorOptions(that.editorDialog, {
lineWrap: $(this).prop('checked')
});
});
fillGroups('edit-script-group');
$('.import-drop-file').on('change', function (e) {
fileHandler(e);
});
$('.import-text-drop').click(function (e) {
$('.import-drop-file').trigger('click');
});
$('#start_import_scripts').button().click(function () {
$('#dialog_import_scripts').dialog('close');
main.confirmMessage(_('Existing scripts will be overwritten. Are you sure?'), null, null, 700, function (result) {
if (result) {
var host = getLiveHost();
if (!host) {
window.alert('No active host found');
return;
}
main.socket.emit('sendToHost', host, 'writeObjectsAsZip', {
data: $('.import-file-name').data('file'),
adapter: 'javascript',
id: 'script.js'
}, function (data) {
if (data === 'permissionError') {
main.showError(_(data));
} else if (!data || data.error) {
main.showError(data ? data.error : 'Unknown error');
} else {
main.showMessage(_('Ok'));
that.init(true);
}
});
}
});
});
window.addEventListener('resize', this.resize, false);
// Load typings for the JS editor
/** @type {string} */
let scriptAdapterInstance = that.main.instances.find(function (inst) {return /javascript\.\d+$/.test(inst)});
if (scriptAdapterInstance != null) {
scriptAdapterInstance = scriptAdapterInstance.substr(scriptAdapterInstance.indexOf('javascript.'));
that.main.socket.emit('sendTo', scriptAdapterInstance, 'loadTypings', null, function (result) {
that.alive = true;
setTypeCheck(that.alive);
if (result.typings) {
that.typings = result.typings;
setEditorTypings();
} else {
console.error(`failed to load typings: ${result.error}`);
}
});
}
// load blockly language
var fileLang = document.createElement('script');
fileLang.setAttribute('type', 'text/javascript');
fileLang.setAttribute('src', 'google-blockly/msg/js/' + (systemLang || 'en') + '.js');
// most browsers
fileLang.onload = function () {
that.languageLoaded[0] = true;
};
// IE 6 & 7
fileLang.onreadystatechange = function() {
if (this.readyState === 'complete') {
that.languageLoaded[0] = true;
}
};
document.getElementsByTagName('head')[0].appendChild(fileLang);
var fileCustom = document.createElement('script');
fileCustom.setAttribute('type', 'text/javascript');
fileCustom.setAttribute('src', 'google-blockly/own/msg/' + (systemLang || 'en') + '.js');
// most browsers
fileCustom.onload = function () {
that.languageLoaded[1] = true;
};
// IE 6 & 7
fileCustom.onreadystatechange = function() {
if (this.readyState === 'complete') {
that.languageLoaded[1] = true;
}
};
document.getElementsByTagName('head')[0].appendChild(fileCustom);
};
this.resize = function (width, height) {
var $blocklyEditor = $('#blockly-editor');
var wasVisible = $blocklyEditor.data('wasVisible');
if (wasVisible !== true && wasVisible !== false) {
wasVisible = $blocklyEditor.is(':visible');
}
// Set the height of svg
if (wasVisible === true) {
$blocklyEditor.hide();
$blocklyWidgetDiv = $('.blocklyWidgetDiv');
$blocklyTooltipDiv = $('.blocklyTooltipDiv');
$blocklyToolboxDiv = $('.blocklyToolboxDiv');
$blocklyWidgetDiv.hide();
$blocklyTooltipDiv.hide();
$blocklyToolboxDiv.hide();
$blocklyEditor.find('svg').height($('#height-editor').height());
$blocklyEditor.show();
$blocklyWidgetDiv.show();
$blocklyTooltipDiv.show();
$blocklyToolboxDiv.show();
} else {
$blocklyEditor.find('svg').height($('#height-editor').height());
}
$blocklyEditor.data('wasVisible', null);
if (that.blocklyWorkspace) Blockly.svgResize(that.blocklyWorkspace);
if (that.editor) {
that.editor.layout();
}
var $scriptEditor = $('#script-editor');
wasVisible = $scriptEditor.data('wasVisible');
if (wasVisible !== true && wasVisible !== false) {
wasVisible = $scriptEditor.is(':visible');
}
if (wasVisible === true) {
$scriptEditor.show();
}
};
/** @typedef {"javascript" | "typescript" | "coffee"} EditorLanguage */
/**
* Sets the language of the code editor
* @param {monaco.editor.IStandaloneCodeEditor} editorInstance The editor instance to change the options for
* @param {EditorLanguage} language
*/
function setEditorLanguage(editorInstance, language) {
monaco.editor.setModelLanguage(
editorInstance.getModel(),
language
);
}
/**
* Sets some options of the code editor
* @param {monaco.editor.IStandaloneCodeEditor} editorInstance The editor instance to change the options for
* @param {Partial<{readOnly: boolean, lineWrap: boolean, language: EditorLanguage, typeCheck: boolean}>} options
*/
function setEditorOptions(editorInstance, options) {
if (!options) return;
if (!editorInstance) return;
if (options.language != null) setEditorLanguage(editorInstance, options.language);
if (options.readOnly != null) editorInstance.updateOptions({readOnly: options.readOnly});
if (options.lineWrap != null) editorInstance.updateOptions({wordWrap: options.lineWrap ? 'on' : 'off'});
if (options.typeCheck != null) setTypeCheck(options.typeCheck);
}
/**
* Enables or disables the type checking in the editor
* @param {boolean} enabled - Whether type checking is enabled or not
*/
function setTypeCheck(enabled) {
const options = {
noSemanticValidation: !that.alive || !enabled, // toggle the type checking
noSyntaxValidation: !that.alive // always check the syntax
};
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions(options);
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions(options);
}
/**
* Adds the given declaration file to the editor
* @param {string} path The file path of the typings to add
* @param {string} typings The declaration file to add
* @param {boolean} [isGlobal=false] Whethere the file is a global declaration file
* @returns {void}
*/
function addTypingsToEditor(path, typings, isGlobal) {
try {
const handle = monaco.languages.typescript.javascriptDefaults.addExtraLib(typings, path);
if (isGlobal) that.globalTypingHandles.push(handle);
} catch (e) { /* might be added already */}
try {
const handle = monaco.languages.typescript.typescriptDefaults.addExtraLib(typings, path);
if (isGlobal) that.globalTypingHandles.push(handle);
} catch (e) { /* might be added already */}
}
function setEditorTypings() {
// clear previously added global typings
for (const handle of that.globalTypingHandles) {
if (handle != null) handle.dispose();
}
const isGlobalScript = isIdOfGlobalScript(that.currentId);
// The filename of the declarations this script can see if it is a global script
const partialDeclarationsPath = that.currentId + '.d.ts';
for (const path of Object.keys(that.typings)) {
// global scripts don't get to see all other global scripts
// but only a part of them
if (isGlobalScript) {
if (path === 'global.d.ts') continue;
if (path.startsWith('script.js.global') && path !== partialDeclarationsPath) continue;
}
addTypingsToEditor(path, that.typings[path], isGlobalScript);
}
}
/**
* Inserts some text into the given editor
* @param {monaco.editor.IStandaloneCodeEditor} editorInstance The editor instance to add the code into
* @param {string} text The text to add
*/
function insertTextIntoEditor(editorInstance, text) {
const selection = editorInstance.getSelection();
const range = new monaco.Range(
selection.startLineNumber, selection.startColumn,
selection.endLineNumber, selection.endColumn
);
editorInstance.executeEdits('', [{range: range, text: text, forceMoveMarkers: true}]);
}
function blockly2JS(oneWay) {
$('#edit-script-engine-type').find('option[value="Blockly"]').remove();
blocklyCode2JSCode(oneWay);
setEditorOptions(that.editor, {
readOnly: false
});
// that.editor.setReadOnly(false);
setChanged(true);
$('#script-edit-button-save').button('enable');
$('#script-edit-button-cancel').button('enable');
switchViews(false, that.currentEngine);
if (that.currentEngine.match(/^[jJ]ava[sS]cript/)) {
//that.editor.getSession().setMode('ace/mode/javascript');
setEditorOptions(that.editor, {
language: 'javascript',
typeCheck: true // not suppported
});
} else if (that.currentEngine.match(/^[cC]offee[sS]cript/)) {
// that.editor.getSession().setMode('ace/mode/coffee');
setEditorOptions(that.editor, {
language: 'coffee',
typeCheck: false // not suppported
});
} else if (that.currentEngine.match(/^[tT]ype[sS]cript/)) {
// that.editor.getSession().setMode('ace/mode/typescript');
setEditorOptions(that.editor, {
language: 'typescript',
typeCheck: true // not suppported
});
}
}
function blocklyCode2JSCode(oneWay, justConvert) {
var code = Blockly.JavaScript.workspaceToCode(that.blocklyWorkspace);
if (!oneWay) {
code += '\n';
var dom = Blockly.Xml.workspaceToDom(that.blocklyWorkspace);
var text = Blockly.Xml.domToText(dom);
code += '//' + btoa(encodeURIComponent(text));
}
if (!justConvert) that.editor.setValue(code /*, -1*/);
return code;
}
function jsCode2Blockly(text) {
text = text || '';
var lines = text.split(/[\r\n]+|\r|\n/g);
var xml = '';
for (var l = lines.length - 1; l >= 0; l--) {
if (lines[l].substring(0, 2) === '//') {
xml = lines[l].substring(2);
break;
}
}
if (xml.substring(0, 4) === '<xml') {
return xml;
} else {
var code;
try {
code = decodeURIComponent(atob(xml));
} catch (e) {
code = null;
console.error('cannot decode: ' + xml);
console.error(e);
}
return code;
}
}
function removeBlocklyFromCode(text) {
text = text || '';
var lines = text.split(/[\r\n]+|\r|\n/g);
var xml = '';
for (var l = lines.length - 1; l >= 0; l--) {
if (lines[l].substring(0, 2) === '//') {
xml = lines[l].substring(2);
if (xml.substring(0, 4) === '<xml') {
lines.splice(l, 1);
break;
} else {
try {
if (decodeURIComponent(atob(xml)).substring(0, 4) === '<xml') {
lines.splice(l, 1);
break;
}
} catch (e) {
console.error('cannot decode: ' + xml);
console.error(e);
}
}
}
}
return lines.join('\n');
}
function buildRules() {
var rules_basic = {
condition: 'AND',
rules: [{
id: 'id',
operator: 'is',
value: 'abc'
}]
};
var $builderWidgets = $('#builder-widgets');
// Fix for Selectize
$builderWidgets.on('afterCreateRuleInput.queryBuilder', function(e, rule) {
if (rule.filter.plugin === 'selectize') {
rule.$el.find('.rule-value-container').css('min-width', '200px')
.find('.selectize-control').removeClass('form-control');
}
}).queryBuilder({
//plugins: ['bt-tooltip-errors'],
operators: [
{ type: 'equal', optgroup: 'basic' },
{ type: 'not_equal', optgroup: 'basic' },
{ type: 'is', optgroup: 'custom', nb_inputs: 2, multiple: false, apply_to: ['string'] }
],
filters: [{
id: 'id',
label: _('Value of'),
type: 'string',
operators: ['is']
}, {
id: 'time',
label: _('Time'),
placeholder: 'HH:mm',
//input: 'select',
type: 'string',
validation: {
format: /^\d{2}:\d{2}$/
},
operators: ['equal']
}, {
id: 'test',
label: _('test'),
placeholder: 'HH:mm',
//input: 'select',
type: 'string',
operators: ['is'],
input: function(rule, name) {
var $container = rule.$el.find('.rule-value-container');
$container.on('change', '[name='+ name +'_1]', function(){
var h = '';
switch ($(this).val()) {
case 'A':
h = '<option value="-1">-</option> <option value="1">1</option> <option value="2">2</option>';
break;
case 'B':
h = '<option value="-1">-</option> <option value="3">3</option> <option value="4">4</option>';
break;
case 'C':
h = '<option value="-1">-</option> <option value="5">5</option> <option value="6">6</option>';
break;
}
$container.find('[name$=_2]')
.html(h).toggle(!!h)
.val('-1').trigger('change');
});
return '\
<select name="' + name + '_id"> \
<option value="-1">-</option> \
<option value="A">A</option> \
<option value="B">B</option> \
<option value="C">C</option> \
</select> \
is \
<input name="' + name + '_value" />';
},
valueGetter: function(rule) {
return rule.$el.find('.rule-value-container [name$=_1]').val()
+'.'+ rule.$el.find('.rule-value-container [name$=_2]').val();
},
valueSetter: function(rule, value) {
if (rule.operator.nb_inputs > 0) {
var val = value.split('.');
rule.$el.find('.rule-value-container [name$=_1]').val(val[0]).trigger('change');
rule.$el.find('.rule-value-container [name$=_2]').val(val[1]).trigger('change');
}
}
}],
/*filters: [{
id: 'date',
label: 'Datepicker',
type: 'date',
validation: {
format: 'YYYY/MM/DD'
},
plugin: 'datepicker',
plugin_config: {
format: 'yyyy/mm/dd',
todayBtn: 'linked',
todayHighlight: true,
autoclose: true
}
}, {
id: 'rate',
label: 'Slider',
type: 'integer',
validation: {
min: 0,
max: 100
},
plugin: 'slider',
plugin_config: {
min: 0,
max: 100,
value: 0
},
valueSetter: function(rule, value) {
if (rule.operator.nb_inputs == 1) value = [value];
rule.$el.find('.rule-value-container input').each(function(i) {
//$(this).slider('setValue', value[i] || 0);
});
},
valueGetter: function(rule) {
var value = [];
rule.$el.find('.rule-value-container input').each(function() {
//value.push($(this).slider('getValue'));
});
return rule.operator.nb_inputs == 1 ? value[0] : value;
}
}, {
id: 'category',
label: 'Selectize',
type: 'string',
plugin: 'selectize',
plugin_config: {
valueField: 'id',
labelField: 'name',
searchField: 'name',
sortField: 'name',
create: true,
maxItems: 1,
plugins: ['remove_button'],
onInitialize: function() {
var that = this;
//if (localStorage.demoData === undefined) {
// $.getJSON(baseurl + '/assets/demo-data.json', function(data) {
// localStorage.demoData = JSON.stringify(data);
// data.forEach(function(item) {
// that.addOption(item);
// });
// });
//}
//else {
// JSON.parse(localStorage.demoData).forEach(function(item) {
// that.addOption(item);
// });
//}
}
},
valueSetter: function(rule, value) {
rule.$el.find('.rule-value-container input')[0].selectize.setValue(value);
}
}, {
id: 'coord',
label: 'Coordinates',
type: 'string',
validation: {
format: /^[A-C]{1}.[1-6]{1}$/
},
input: function(rule, name) {
var $container = rule.$el.find('.rule-value-container');
$container.on('change', '[name='+ name +'_1]', function(){
var h = '';
switch ($(this).val()) {
case 'A':
h = '<option value="-1">-</option> <option value="1">1</option> <option value="2">2</option>';
break;
case 'B':
h = '<option value="-1">-</option> <option value="3">3</option> <option value="4">4</option>';
break;
case 'C':
h = '<option value="-1">-</option> <option value="5">5</option> <option value="6">6</option>';
break;
}
$container.find('[name$=_2]')
.html(h).toggle(!!h)
.val('-1').trigger('change');
});
return '\
<select name="'+ name +'_1"> \
<option value="-1">-</option> \
<option value="A">A</option> \
<option value="B">B</option> \
<option value="C">C</option> \
</select> \
<select name="'+ name +'_2" style="display:none;"></select>';
},
valueGetter: function(rule) {
return rule.$el.find('.rule-value-container [name$=_1]').val()
+'.'+ rule.$el.find('.rule-value-container [name$=_2]').val();
},
valueSetter: function(rule, value) {
if (rule.operator.nb_inputs > 0) {
var val = value.split('.');
rule.$el.find('.rule-value-container [name$=_1]').val(val[0]).trigger('change');
rule.$el.find('.rule-value-container [name$=_2]').val(val[1]).trigger('change');
}
}
}],*/
rules: rules_basic
});
$('#btn-reset').on('click', function() {
$builderWidgets.queryBuilder('reset');
});
$('#btn-set').on('click', function() {
$builderWidgets.queryBuilder('setRules', rules_basic);
});
$('#btn-get').on('click', function() {
var result = $builderWidgets.queryBuilder('getRules');
if (!$.isEmptyObject(result)) {
alert(JSON.stringify(result, null, 2));
}
});
}
function editScript(id) {
that.initEditor();
if (that.currentId !== id) {
if (that.changed) {
that.main.confirmMessage(_('Script not saved'), _('Save?'), 'help', [_('Save'), _('Discard'), _('Cancel')], function (result) {
if (result === 0) {
that.saveScript();
setChanged(false);
setTimeout(function() {
editScript(id);
}, 0);
} else if (result === 1) {
setChanged(false);
setTimeout(function() {
editScript(id);
}, 0);
} else {
that.$grid.treeTable('show', that.currentId);
}
});
return;
}
that.currentId = id;
$('#script-output').html('');
main.saveConfig('script-editor-current-id', that.currentId);
}
if (id && main.objects[id] && main.objects[id].type === 'script') {
$('#editor-scripts').show();
applyResizableV();
var obj = main.objects[id];
$('.script-edit').show();
if (isIdOfGlobalScript(id)) {
$('#global_hint').show();
} else {
$('#global_hint').hide();
}
$('#edit-script-group').val(getGroup(id));
$('#edit-script-name').val(obj.common.name);
$('#edit-script-debug').prop('checked', !!obj.common.debug);
$('#edit-script-verbose').prop('checked', !!obj.common.verbose);
setEditorOptions(that.editor, {
lineWrap: $('#edit-wrap-lines').prop('checked')
});
var $editType = $('#edit-script-engine-type');
if (obj.common.engineType !== 'Blockly' && obj.common.engineType !== 'Rule') {
// remove Blockly from list
$editType.find('option[value="Blockly"]').remove();
$editType.find('option[value="Rule"]').remove();
} else if (obj.common.engineType === 'Blockly') {
$editType.find('option[value="Rule"]').remove();
if (!$editType.find('option[value="Blockly"]').length) {
$editType.prepend('<option value="Blockly">Blockly</option>');
}
} else if (obj.common.engineType === 'Rule') {
$editType.find('option[value="Blockly"]').remove();
if (!$editType.find('option[value="Rule"]').length) {
$editType.prepend('<option value="Rule">' + _('Rule') + '</option>');
}
}
that.currentEngine = obj.common.engineType;
// Add engine even if it is not installed
if (that.engines.indexOf(obj.common.engineType) === -1) {
if (!$editType.find('option[value="' + obj.common.engineType + '"]').length) {
$editType.append('<option value="' + obj.common.engineType + '">' + obj.common.engineType + '</option>');
}
}
$editType.val(obj.common.engineType);
setEditorTypings();
if (obj.common.engineType === 'Blockly') {
setEditorOptions(that.editor, {
language: 'javascript',
readOnly: true,
typeCheck: false,
});
switchViews(true, obj.common.engineType);
if (!that.blocklyWorkspace) return;
that.blocklyWorkspace.clear();
try {
var xml = jsCode2Blockly(obj.common.source) || '<xml xmlns="http://www.w3.org/1999/xhtml"></xml>';
var dom = Blockly.Xml.textToDom(xml);
Blockly.Xml.domToWorkspace(dom, that.blocklyWorkspace);
} catch (e) {
console.error(e);
window.alert('Cannot extract Blockly code!');
}
} else if (obj.common.engineType && obj.common.engineType.match(/^[jJ]ava[sS]cript/)) {
setEditorOptions(that.editor, {
language: 'javascript',
readOnly: false,
typeCheck: true
});
switchViews(false, obj.common.engineType);
} else if (obj.common.engineType && obj.common.engineType.match(/^[cC]offee[sS]cript/)) {
setEditorOptions(that.editor, {
language: 'coffee',
readOnly: false,
typeCheck: false // not supported
});
switchViews(false, obj.common.engineType);
} else if (obj.common.engineType && obj.common.engineType.match(/^[tT]ype[sS]cript/)) {
setEditorOptions(that.editor, {
language: 'typescript',
readOnly: false,
typeCheck: true
});
switchViews(false, obj.common.engineType);
} else if(obj.common.engineType === 'Rule') {
switchViews(true, obj.common.engineType);
buildRules();
}
setChanged(false);
//$('#edit-script-source').val(obj.common.source);
// that.editor.setValue(obj.common.source, -1);
that.editor.setValue(obj.common.source);
applyResizableV();
setTimeout(function () {
setChanged(false);
$('#script-edit-button-save').button('disable');
$('#script-edit-button-cancel').button('disable');
//that.editor.focus();
}, 100);
} else if (id && main.objects[id] && main.objects[id].type === 'channel' && id !== 'script.js.global' && id !== 'script.js.common') {
$('#editor-scripts').show();
var obj = main.objects[id];
$('#edit-script-group').val(getGroup(id));
$('#edit-script-name').val(obj.common.name);
$('#edit-script-debug').prop('checked', !!obj.common.debug);
$('#edit-script-verbose').prop('checked', !!obj.common.verbose);
setChanged(false);
var $editorScriptsTextarea = $('#editor-scripts-textarea');
$editorScriptsTextarea.height(100);
if ($editorScriptsTextarea.hasClass('ui-resizable')) $('#editor-scripts-textarea').resizable('destroy');
switchViews(false, null);
$('#script-edit-button-save').button('disable');
$('#script-edit-button-cancel').button('disable');
} else {
$('#editor-scripts').hide();
}
setTimeout(function () {
that.resize();
}, 100)
}
function blocklyBlinkBlock(block) {
for (var i = 300; i < 3000; i = i + 300) {
setTimeout(function () {
block.select();
}, i);
setTimeout(function () {
block.unselect();
}, i + 150);
}
}
function blocklyCheckBlocks(cb) {
var warningText;
if (that.blocklyWorkspace.getAllBlocks().length === 0) {
cb && cb('no blocks found');
return;
}
var badBlock = blocklyGetUnconnectedBlock();
if (badBlock) {
warningText = 'not properly connected';
} else {
badBlock = blocklyGetBlockWithWarning();
if (badBlock) warningText = 'warning on this block';
}
if (badBlock) {
if (cb) {
cb(warningText, badBlock);
} else {
blocklyBlinkBlock(badBlock);
}
return false;
}
cb();
return true;
}
//get unconnected block
function blocklyGetUnconnectedBlock () {
var blocks = that.blocklyWorkspace.getAllBlocks();
for (var i = 0, block; block = blocks[i]; i++) {
var connections = block.getConnections_(true);
for (var j = 0, conn; conn = connections[j]; j++) {
if (!conn.sourceBlock_ || ((conn.type === Blockly.INPUT_VALUE || conn.type === Blockly.OUTPUT_VALUE) && !conn.targetConnection && !conn._optional)) {
return block;
}
}
}
return null;
}
//get block with warning
function blocklyGetBlockWithWarning() {
var blocks = that.blocklyWorkspace.getAllBlocks();
for (var i = 0, block; block = blocks[i]; i++) {
if (block.warning) {
return block;
}
}
return null;
}
// Find all script engines
this.fillEngines = function (elemName) {
var _engines = [];
for (var t = 0; t < that.main.instances.length; t++) {
if (that.main.objects[that.main.instances[t]] && that.main.objects[that.main.instances[t]].common && that.main.objects[that.main.instances[t]].common.engineTypes) {
var engineTypes = that.main.objects[that.main.instances[t]].common.engineTypes;
if (typeof engineTypes === 'string') {
if (_engines.indexOf(engineTypes) === -1) _engines.push(engineTypes);
} else {
for (var z = 0; z < engineTypes.length; z++) {
if (_engines.indexOf(engineTypes[z]) === -1) _engines.push(engineTypes[z]);
}
}
}
}
if (elemName) {
var text = '';
for (var u = 0; u < _engines.length; u++) {
text += '<option value="' + _engines[u] + '">' + _engines[u] + '</option>';
}
$('#' + elemName).html(text);
}
return _engines;
};
function getGroup(id) {
var parts = id.split('.');
parts.pop();
return parts.join('.');
}
function fillGroups(elemName) {
var groups = ['script.js', 'script.js.common', 'script.js.global'];
var g;
for (var i = 0; i < that.list.length; i++) {
g = getGroup(that.list[i]);
if (groups.indexOf(g) === -1 ) {
groups.push(g);
}
}
for (var j = 0; j < that.groups.length; j++) {
if (groups.indexOf(that.groups[j]) === -1) {
groups.push(that.groups[j]);
}
}
var text = '';
for (g = 0; g < groups.length; g++) {
var name = groups[g].substring('script.js.'.length);
if (name === 'global' || name === 'common') {
name = _(name);
}
if (!name) name = _('no group');
if (that.main.objects[groups[g]] && that.main.objects[groups[g]].common && that.main.objects[groups[g]].common.name) {
name = that.main.objects[groups[g]].common.name;
}
if (name.indexOf('.') === -1) {
var parts = groups[g].split('.');
if (parts.length > 3) {
parts.splice(0, 2);
parts.pop();
name = parts.join('/') + '/' + name;
}
}
text += '<option value="' + groups[g] + '">' + name + '</option>\n';
// create group if not exists
if (groups[g] !== 'script.js' && groups[g] !== 'script' && (!that.main.objects[groups[g]] || !that.main.objects[groups[g]].common)) {
that.main.socket.emit('setObject', groups[g], {
common: {
name: groups[g].split('.').pop()
},
type: 'channel'
}, function (err) {
if (err) {
that.main.showError(err);
that.init(true);
}
});
}
}
if (elemName) {
var $elemName = $('#' + elemName);
var val = $elemName.val();
$elemName.html(text).val(val);
}
}
this.updateScript = function (oldId, newId, newCommon, cb) {
this.main.socket.emit('getObject', oldId, function (err, _obj) {
setTimeout(function () {
var obj = {common: {}};
if (newCommon.engine !== undefined) obj.common.engine = newCommon.engine;
if (newCommon.enabled !== undefined) obj.common.enabled = newCommon.enabled;
if (newCommon.source !== undefined) obj.common.source = newCommon.source;
if (newCommon.debug !== undefined) obj.common.debug = newCommon.debug;
if (newCommon.verbose !== undefined) obj.common.verbose = newCommon.verbose;
if (oldId === newId && _obj && _obj.common && newCommon.name === _obj.common.name) {
if (!newCommon.engineType || newCommon.engineType !== _obj.common.engineType) {
if (newCommon.engineType !== undefined) obj.common.engineType = newCommon.engineType || 'Javascript/js';
that.main.socket.emit('extendObject', oldId, obj, function (err) {
if (err) {
that.main.showError(err);
that.init(true);
}
cb && cb(err);
});
} else {
that.main.socket.emit('extendObject', oldId, obj, function (err) {
if (err) {
that.main.showError(err);
that.init(true);
}
cb && cb(err);
});
}
} else {
// var prefix;
// var parts = _obj.common.engineType.split('/');
// prefix = 'script.' + (parts[1] || parts[0]) + '.';
if (_obj && _obj.common) {
_obj.common.engineType = newCommon.engineType || _obj.common.engineType || 'Javascript/js';
that.main.socket.emit('delObject', oldId, function (err) {
if (err) {
that.main.showError(err);
that.init(true);
}
});
if (obj.common.engine !== undefined) _obj.common.engine = obj.common.engine;
if (obj.common.enabled !== undefined) _obj.common.enabled = obj.common.enabled;
if (obj.common.source !== undefined) _obj.common.source = obj.common.source;
if (obj.common.name !== undefined) _obj.common.name = obj.common.name;
if (obj.common.debug !== undefined) _obj.common.debug = obj.common.debug;
if (obj.common.verbose !== undefined) _obj.common.verbose = obj.common.verbose;
delete _obj._rev;
} else {
_obj = obj;
}
// Name must always exist
_obj.common.name = newCommon.name;
_obj._id = newId; // prefix + newCommon.name.replace(/[\s"']/g, '_');
that.main.socket.emit('setObject', newId, _obj, function (err) {
if (err) {
that.main.showError(err);
that.init(true);
} else {
setTimeout(function () {
that.$grid.treeTable('show', newId);
}, 500);
}
cb && cb(err);
});
}
}, 0);
});
};
function switchViews(isBlocklyView, engineType) {
if (engineType === null) {
$('#builder-widgets').hide();
$('#script-buttons').hide();
$('.script-edit').hide();
$('#show-blockly-id').hide();
$('#script-editor').hide();
$('#blockly-editor').hide();
$('.edit-wrap-lines').hide();
$('.blocklyWidgetDiv').hide();
$('.blocklyTooltipDiv').hide();
$('.blocklyToolboxDiv').hide();
$('#edit-check-blocks').hide();
$('#edit-export-blocks').hide();
$('#edit-import-blocks').hide();
return;
}
$('.script-edit').show();
$('#script-buttons').show();
engineType = engineType || 'Blockly';
if (engineType === 'Blockly') {
if (isBlocklyView === undefined) {
isBlocklyView = !$('#script-editor').is(':visible');
}
$('#edit-insert-id').hide();
$('.edit-cron-id').hide();
if (isBlocklyView) {
$('#show-blockly-id')
.button('option', 'label', _('Show code'))
.button('option', 'icons', {primary: 'ui-icon-script'}).show();
$('#builder-widgets').hide();
$('#script-editor').hide();
$('#blockly-editor').show();
$('.blocklyWidgetDiv').show();
$('.blocklyTooltipDiv').show();
$('.blocklyToolboxDiv').show();
$('.edit-wrap-lines').hide();
$('#edit-check-blocks').show();
$('#edit-export-blocks').show();
$('#edit-import-blocks').show();
if (that.blocklyWorkspace) Blockly.svgResize(that.blocklyWorkspace);
} else {
$('#show-blockly-id')
.button('option', 'label', _('Show blockly'))
.button('option', 'icons', {primary: 'ui-icon-calculator'}).show();
// update script editor
$('#builder-widgets').hide();
$('#script-editor').show();
if (that.editor) that.editor.layout();
$('#blockly-editor').hide();
$('.blocklyWidgetDiv').hide();
$('.blocklyTooltipDiv').hide();
$('.blocklyToolboxDiv').hide();
$('.edit-wrap-lines').show();
$('#edit-check-blocks').hide();
$('#edit-export-blocks').hide();
$('#edit-import-blocks').hide();
}
} else if (engineType === 'Rule') {
$('#edit-insert-id').hide();
$('.edit-cron-id').hide();
$('#builder-widgets').show();
$('#show-blockly-id').hide();
$('#script-editor').hide();
$('#blockly-editor').hide();
$('.edit-wrap-lines').show();
$('.blocklyWidgetDiv').hide();
$('.blocklyTooltipDiv').hide();
$('.blocklyToolboxDiv').hide();
$('#edit-check-blocks').hide();
$('#edit-export-blocks').hide();
$('#edit-import-blocks').hide();
} else {
$('#edit-insert-id').show();
$('.edit-cron-id').show();
$('#builder-widgets').hide();
$('#show-blockly-id').hide();
$('#script-editor').show();
if (that.editor) that.editor.layout();
$('#blockly-editor').hide();
$('.edit-wrap-lines').show();
$('.blocklyWidgetDiv').hide();
$('.blocklyTooltipDiv').hide();
$('.blocklyToolboxDiv').hide();
$('#edit-check-blocks').hide();
$('#edit-export-blocks').hide();
$('#edit-import-blocks').hide();
}
}
// used for ace editor
/*var funcNames = [
{score: 1001, meta: 'yunkong2', value: 'getState'},
{score: 1001, meta: 'yunkong2', value: 'setState'},
{score: 1000, meta: 'yunkong2', value: 'request'},
{score: 1000, meta: 'yunkong2', value: 'getObject'},
{score: 1000, meta: 'yunkong2', value: 'setObject'},
{score: 1000, meta: 'yunkong2', value: 'require'},
{score: 1000, meta: 'yunkong2', value: 'console'},
{score: 1000, meta: 'yunkong2', value: 'exec'},
{score: 1000, meta: 'yunkong2', value: 'email'},
{score: 1000, meta: 'yunkong2', value: 'pushover'},
{score: 1000, meta: 'yunkong2', value: 'subscribe'},
{score: 1000, meta: 'yunkong2', value: 'getSubscriptions'},
{score: 1000, meta: 'yunkong2', value: 'adapterSubscribe'},
{score: 1000, meta: 'yunkong2', value: 'adapterUnsubscribe'},
{score: 1000, meta: 'yunkong2', value: 'unsubscribe'},
{score: 1000, meta: 'yunkong2', value: 'on'},
{score: 1000, meta: 'yunkong2', value: 'schedule'},
{score: 1000, meta: 'yunkong2', value: 'getAstroDate'},
{score: 1000, meta: 'yunkong2', value: 'isAstroDay'},
{score: 1000, meta: 'yunkong2', value: 'clearSchedule'},
{score: 1000, meta: 'yunkong2', value: 'setStateDelayed'},
{score: 1000, meta: 'yunkong2', value: 'clearStateDelayed'},
{score: 1000, meta: 'yunkong2', value: 'getStateDelayed'},
{score: 1000, meta: 'yunkong2', value: 'existsState'},
{score: 1000, meta: 'yunkong2', value: 'existsObject'},
{score: 1000, meta: 'yunkong2', value: 'getIdByName'},
{score: 1000, meta: 'yunkong2', value: 'extendObject'},
{score: 1000, meta: 'yunkong2', value: 'getEnums'},
{score: 1000, meta: 'yunkong2', value: 'createState'},
{score: 1000, meta: 'yunkong2', value: 'deleteState'},
{score: 1000, meta: 'yunkong2', value: 'sendTo'},
{score: 1000, meta: 'yunkong2', value: 'sendToHost'},
{score: 1000, meta: 'yunkong2', value: 'setInterval'},
{score: 1000, meta: 'yunkong2', value: 'clearInterval'},
{score: 1000, meta: 'yunkong2', value: 'setTimeout'},
{score: 1000, meta: 'yunkong2', value: 'clearTimeout'},
{score: 1000, meta: 'yunkong2', value: 'setImmediate'},
{score: 1000, meta: 'yunkong2', value: 'compareTime'},
{score: 1000, meta: 'yunkong2', value: 'onStop'},
{score: 1000, meta: 'yunkong2', value: 'formatValue'},
{score: 1000, meta: 'yunkong2', value: 'formatDate'},
{score: 1000, meta: 'yunkong2', value: 'getDateObject'},
{score: 1000, meta: 'yunkong2', value: 'writeFile'},
{score: 1000, meta: 'yunkong2', value: 'readFile'},
{score: 1000, meta: 'yunkong2', value: 'unlink'},
{score: 1000, meta: 'yunkong2', value: 'delFile'},
{score: 1000, meta: 'yunkong2', value: 'getHistory'},
{score: 1000, meta: 'yunkong2', value: 'runScript'},
{score: 1000, meta: 'yunkong2', value: 'startScript'},
{score: 1000, meta: 'yunkong2', value: 'stopScript'},
{score: 1000, meta: 'yunkong2', value: 'isScriptActive'},
{score: 1000, meta: 'yunkong2', value: 'toInt'},
{score: 1000, meta: 'yunkong2', value: 'toFloat'},
{score: 1000, meta: 'yunkong2', value: 'toBoolean'}
];*/
this.initEditor = function () {
if (!this.editor) {
// compiler options
const compilerOptions = {
target: monaco.languages.typescript.ScriptTarget.ES6,
lib: [],
noLib: true, // we manually provide the lib files because the editor includes the DOM typings
allowNonTsExtensions: true,
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
module: monaco.languages.typescript.ModuleKind.CommonJS,
typeRoots: ['node_modules/@types'],
};
monaco.languages.typescript.javascriptDefaults.setCompilerOptions(compilerOptions);
monaco.languages.typescript.typescriptDefaults.setCompilerOptions(compilerOptions);
setTypeCheck(!that.alive);
this.editor = monaco.editor.create(document.getElementById('script-editor'), {
language: 'javascript',
lineNumbers: 'on',
scrollBeyondLastLine: false,
automaticLayout: true
});
this.editorDialog = monaco.editor.create(document.getElementById('dialog-script-editor'), {
language: 'javascript',
lineNumbers: 'on',
scrollBeyondLastLine: false,
automaticLayout: true
});
// this.editorDialog = ace.edit('dialog-script-editor');
// this.editorDialog.getSession().setMode('ace/mode/javascript');
// this.editorDialog.setOptions({
// enableBasicAutocompletion: true,
// enableSnippets: true,
// enableLiveAutocompletion: true
// });
// this.editorDialog.completers && this.editorDialog.completers.push({
// getCompletions: function (editor, session, pos, prefix, callback) {
// callback(null, funcNames);
// }
// });
// this.editorDialog.$blockScrolling = Infinity;
$('#dialog-edit-insert-id').button({
icons: {primary: 'ui-icon-note'}
}).css('height', '30px').click(function () {
var sid = that.main.initSelectId();
sid.selectId('show', function (newId) {
insertTextIntoEditor(that.editorDialog, '"' + newId + '"' + ((that.main.objects[newId] && that.main.objects[newId].common && that.main.objects[newId].common.name) ? ('/*' + that.main.objects[newId].common.name + '*/') : ''));
that.editorDialog.focus();
});
});
$('#edit-insert-id').button({
icons: {primary: 'ui-icon-note'}
}).css('height', '30px').click(function () {
var sid = that.main.initSelectId();
sid.selectId('show', function (newId) {
insertTextIntoEditor(that.editor, '"' + newId + '"' + ((that.main.objects[newId] && that.main.objects[newId].common && that.main.objects[newId].common.name) ? ('/*' + that.main.objects[newId].common.name + '*/') : ''));
that.editor.focus();
});
});
$('.edit-cron-id').button({
icons: {primary: 'ui-icon-clock'}
}).css('height', '30px').click(function () {
var text;
if (!$('#dialog-script').is(':visible')) {
text = that.editor.getModel().getValueInRange(that.editor.getSelection());
} else {
text = that.editorDialog.getModel().getValueInRange(that.editorDialog.getSelection());
}
if (text) {
text = text.replace(/"/g, '').replace(/'/g, '');
if (text) {
try {
$('#div-cron').cron('value', text);
} catch (e) {
alert(_('Cannot parse text as cron message'));
}
}
}
$('#dialog_cron_callback').hide();
$('#dialog_cron_insert').show();
that.$dialogCron.dialog('open');
});
// toggle blockly <=> javascript
$('#show-blockly-id').button({
icons: {primary: 'ui-icon-script'}
}).css({height: 30, width: 200}).click(function () {
if ($('#script-editor').is(':visible')) {
switchViews(true);
} else {
blocklyCode2JSCode();
switchViews(false);
}
});
// this.editor.on('input', function() {
this.editor.onDidChangeModelContent(function (e) {
if (that.currentEngine !== 'Blockly') {
setChanged(true);
$('#script-edit-button-save').button('enable');
$('#script-edit-button-cancel').button('enable');
}
});
this.editorDialog.onDidChangeModelContent(function (e) {
that.editorDialog._changed = true;
$('#dialog_script_save').button('enable');
});
$('#edit-script-name').on('change', function () {
setChanged(true);
$('#script-edit-button-save').button('enable');
$('#script-edit-button-cancel').button('enable');
}).on('keyup', function () {
$(this).trigger('change');
});
$('#edit-script-debug').on('change', function () {
if ($(this).prop('checked')) {
that.main.showMessage(_('debug_help'));
}
setChanged(true);
$('#script-edit-button-save').button('enable');
$('#script-edit-button-cancel').button('enable');
});
$('#edit-script-verbose').on('change', function () {
if ($(this).prop('checked')) {
that.main.showMessage(_('verbose_help'));
}
setChanged(true);
$('#script-edit-button-save').button('enable');
$('#script-edit-button-cancel').button('enable');
});
$('#edit-script-engine-type').on('change', function () {
if (that.currentEngine === 'Blockly' && that.editor.getValue()) {
main.confirmMessage(_('You cannot go back!'), null, null, function (result) {
if (result) {
that.currentEngine = $('#edit-script-engine-type').val();
blockly2JS(true);
} else {
// return value back
$('#edit-script-engine-type').val('Blockly');
}
});
return;
}
if (that.currentEngine === 'Blockly') {
that.currentEngine = $(this).val();
switchViews(false, that.currentEngine);
blockly2JS(true);
} else {
that.currentEngine = $(this).val();
switchViews(false, that.currentEngine);
}
setChanged(true);
$('#script-edit-button-save').button('enable');
$('#script-edit-button-cancel').button('enable');
});
$('#edit-script-group').on('change', function () {
setChanged(true);
$('#script-edit-button-save').button('enable');
$('#script-edit-button-cancel').button('enable');
});
setEditorOptions(that.editor, {
lineWrap: $('#edit-wrap-lines').prop('checked')
});
}
};
function _deleteGroup(id, originalGroup, confirmed, deleted) {
if (confirmed.indexOf(id) === -1) {
confirmed.push(id);
}
// find all elements
for (var l = 0; l < that.list.length; l++) {
if (that.list[l].substring(0, id.length + 1) === id + '.' && (!deleted || deleted.indexOf(that.list[l]) === -1)) {
deleteId(that.list[l], id, confirmed, deleted);
return;
}
}
for (var g = 0; g < that.groups.length; g++) {
if (that.groups[g].substring(0, id.length + 1) === id + '.') {
deleteId(that.groups[g], id, confirmed, deleted);
return;
}
}
that.main.socket.emit('delObject', id, function (err) {
if (err) {
if (err) {
that.main.showError(err);
that.init(true);
}
} else if (originalGroup !== id) {
setTimeout(function () {
deleteId(originalGroup, null, confirmed, deleted);
}, 0);
} else {
// finish
}
});
}
function deleteId(id, originalGroup, confirmed, deleted) {
originalGroup = originalGroup || id;
confirmed = confirmed || [];
deleted = deleted || [];
if (that.main.objects[id] && that.main.objects[id].type === 'script') {
that.main.confirmMessage(_('Are you sure to delete script %s?', that.main.objects[id].common.name), null, 'help', function (result) {
if (result) {
that.main.socket.emit('delObject', id, function (err) {
if (err) {
that.main.showError(err);
that.init(true);
} else {
deleted.push(id);
setTimeout(function () {
deleteId(originalGroup, null, confirmed, deleted);
}, 0);
}
});
} else {
// Do nothing
}
});
} else {
var name = id;
if (confirmed.indexOf(id) === -1) {
if (that.main.objects[id] && that.main.objects[id].common && that.main.objects[id].common.name) {
name = that.main.objects[id].common.name;
}
that.main.confirmMessage(_('Are you sure to delete group <span style="color: blue">%s</span> and <span style="color: red">all</span> scripts in it?', name), null, 'help', function (result) {
if (result) {
_deleteGroup(id, originalGroup, confirmed, deleted);
}
});
} else {
_deleteGroup(id, originalGroup, confirmed, deleted);
}
}
}
function renameGroup(id, newId, newName, _list, cb) {
if (typeof _list === 'function') {
cb = _list;
_list = null;
}
if (!_list) {
_list = [];
that.renaming = true;
// collect all elements to rename
// find all elements
for (var l = 0; l < that.list.length; l++) {
if (that.list[l].substring(0, id.length + 1) === id + '.') _list.push(that.list[l]);
}
for (var g = 0; g < that.groups.length; g++) {
if (that.groups[g].substring(0, id.length + 1) === id + '.') _list.push(that.list[l]);
}
that.main.socket.emit('getObject', id, function (err, obj) {
if (err) {
that.renaming = false;
that.main.showError(err);
that.init(true);
cb && cb(err);
} else {
obj = obj || {common: {}};
obj.common.name = newName;
obj._id = newId;
that.main.socket.emit('delObject', id, function (err) {
if (err) {
that.renaming = false;
that.main.showError(err);
that.init(true);
cb && cb(err);
} else {
that.main.socket.emit('setObject', newId, obj, function (err) {
if (err) {
that.renaming = false;
that.main.showError(err);
that.init(true);
cb && cb(err);
} else {
setTimeout(function () {
renameGroup(id, newId, newName, _list, cb);
}, 0);
}
});
}
});
}
});
} else {
if (_list.length) {
var nId = _list.pop();
that.main.socket.emit('getObject', nId, function (err, obj) {
if (err) {
that.renaming = false;
that.main.showError(err);
that.init(true);
cb && cb(err);
} else {
that.main.socket.emit('delObject', nId, function (err) {
if (err) {
that.renaming = false;
that.main.showError(err);
that.init(true);
cb && cb(err);
} else {
nId = newId + nId.substring(id.length);
that.main.socket.emit('setObject', nId, obj, function (err) {
if (err) {
that.renaming = false;
that.main.showError(err);
that.init(true);
cb && cb(err);
} else {
setTimeout(function () {
renameGroup(id, newId, newName, _list, cb);
}, 0);
}
});
}
});
}
});
} else {
fillGroups('edit-script-group');
that.$grid.treeTable('reinit');
applyResizableH(true, 1000);
cb && cb();
}
}
}
function getLiveHost () {
var _hosts = [];
for (var h = 0; h < that.hosts.length; h++) {
if (main.states[that.hosts[h] + '.alive'] && main.states[that.hosts[h] + '.alive'].val) {
return that.hosts[h];
}
}
return '';
}
function exportScripts() {
var host = getLiveHost();
if (!host) {
window.alert('No active host found');
return;
}
main.socket.emit('sendToHost', host, 'readObjectsAsZip', {
adapter: 'javascript',
id: 'script.js'
}, function (data) {
if (data.error) console.error(data.error);
if (data.data) {
var d = new Date();
var date = d.getFullYear();
var m = d.getMonth() + 1;
if (m < 10) m = '0' + m;
date += '-' + m;
m = d.getDate();
if (m < 10) m = '0' + m;
date += '-' + m + '-';
$('body').append('<a id="zip_download" href="data: application/zip;base64,' + data.data + '" download="' + date + 'scripts.zip"></a>');
document.getElementById('zip_download').click();
document.getElementById('zip_download').remove();
}
});
}
function editGetReadableSize (bytes) {
var text;
if (bytes < 1024) {
text = bytes + ' ' + _('bytes');
} else if (bytes < 1024 * 1024) {
text = Math.round(bytes * 10 / 1024) / 10 + ' ' + _('Kb');
} else {
text = Math.round(bytes * 10 / (1024 * 1024)) / 10 + ' ' + _('Mb');
}
if (main.systemConfig.common.isFloatComma) text = text.replace('.', ',');
return text;
}
function fileHandler (event) {
event.preventDefault();
var file = event.dataTransfer ? event.dataTransfer.files[0] : event.target.files[0];
var $dz = $('.import-drop-zone').show();
if (!file || !file.name || !file.name.match(/\.zip$/)) {
$('.import-drop-text').html(_('Invalid file extenstion!'));
$dz.addClass('import-dropzone-error').animate({opacity: 0}, 1000, function () {
$dz.hide().removeClass('import-dropzone-error').css({opacity: 1});
$('.import-drop-text').html(_('Drop the files here'));
});
return false;
}
if (file.size > 50000000) {
$('.import-drop-text').html(_('File is too big!'));
$dz.addClass('import-dropzone-error').animate({opacity: 0}, 1000, function () {
$dz.hide().removeClass('import-dropzone-error').css({opacity: 1});
$('.import-drop-text').html(_('Drop the files here'));
});
return false;
}
$dz.hide();
var reader = new FileReader();
reader.onload = function (evt) {
var $importFileName = $('.import-file-name');
$importFileName.html('<img src="zip.png" /><br><span style="color: black; font-weight: bold">[' + editGetReadableSize(file.size) + ']</span><br><span style="color: black; font-weight: bold">' + file.name + '</span>');
// string has form data:;base64,TEXT==
$importFileName.data('file', evt.target.result.split(',')[1]);
$('.import-text-drop-plus').hide();
if ($importFileName.data('file')) {
$('#start_import_scripts').button('enable');
} else {
$('#start_import_scripts').button('disable');
}
};
reader.readAsDataURL(file);
}
function importScripts() {
$('#dialog_import_scripts').dialog({
autoOpen: true,
resizable: false,
width: 600,
height: 280,
modal: true,
open: function (event, ui) {
$(event.target).parent().find('.ui-dialog-titlebar-close .ui-button-text').html('');
$('[aria-describedby="dialog_import_scripts"]').css('z-index', 1002);
$('.ui-widget-overlay').css('z-index', 1001);
$('.import-file-name').data('file', null).html(_('Drop files here or click to select one'));
$('#start_import_scripts').button('disable');
$('.import-drop-file').val('');
$('.import-text-drop-plus').show();
var $dropZone = $('#dialog_import_scripts');
if (typeof(window.FileReader) !== 'undefined' && !$dropZone.data('installed')) {
$dropZone.data('installed', true);
var $dz = $('.import-drop-zone');
$('.import-drop-text').html(_('Drop the files here'));
$dropZone[0].ondragover = function() {
$dz.unbind('click');
$dz.show();
return false;
};
$dz.click(function () {
$dz.hide();
});
$dz[0].ondragleave = function() {
$dz.hide();
return false;
};
$dz[0].ondrop = function (e) {
fileHandler(e);
}
}
}
});
}
function initBlocklyDialog(title) {
if (!that.$dialogExport.data('inited')) {
that.$dialogExport.dialog({
autoOpen: false,
modal: true,
width: 700,
height: 400,
resizable: false,
title: _('Export selected blocks'),
close: function () {
$('#dialog-export-blockly-textarea').val('');
},
open: function (event) {
$(event.target).parent().find('.ui-dialog-titlebar-close .ui-button-text').html('');
$('#dialog-export-blockly-textarea').focus();
},
buttons: [
{
id: 'blockly-export-button-ok',
text: _('Ok'),
click: function () {
var val = $('#dialog-export-blockly-textarea').val();
if (val.trim()) {
try {
var xmlBlocks = Blockly.Xml.textToDom(val);
if (xmlBlocks.nodeName === 'xml') {
for (var b = 0; b < xmlBlocks.children.length; b++) {
that.blocklyWorkspace.paste(xmlBlocks.children[b]);
}
} else {
that.blocklyWorkspace.paste(xmlBlocks);
}
that.$dialogExport.dialog('close');
} catch (e) {
that.main.showError(e, _('Import error'));
}
} else {
that.main.showMessage(_('Nothing imported'));
that.$dialogExport.dialog('close');
}
}
},
{
text: _('Cancel'),
click: function () {
that.$dialogExport.dialog('close');
}
}
]
});
that.$dialogExport.data('inited', true);
}
that.$dialogExport.dialog('option', 'title', title || _('Export selected blocks'));
}
function showExportBlocklyDialog() {
initBlocklyDialog(_('Export selected blocks'));
if (Blockly.selected) {
var xmlBlock = Blockly.Xml.blockToDom(Blockly.selected);
if (Blockly.dragMode_ != Blockly.DRAG_FREE) {
Blockly.Xml.deleteNext(xmlBlock);
}
// Encode start position in XML.
var xy = Blockly.selected.getRelativeToSurfaceXY();
xmlBlock.setAttribute('x', Blockly.selected.RTL ? -xy.x : xy.x);
xmlBlock.setAttribute('y', xy.y);
$('#dialog-export-blockly-textarea').val(Blockly.Xml.domToPrettyText(xmlBlock)).prop('readonly', true).select();
} else {
var dom = Blockly.Xml.workspaceToDom(that.blocklyWorkspace);
var text = Blockly.Xml.domToPrettyText(dom);
$('#dialog-export-blockly-textarea').val(text).prop('readonly', true).select()
}
$('#blockly-export-button-ok').hide();
that.$dialogExport.dialog('open');
}
function showImportBlocklyDialog() {
initBlocklyDialog(_('Import selected blocks'));
$('#blockly-export-button-ok').show();
$('#dialog-export-blockly-textarea').prop('readonly', false).val('');
that.$dialogExport.dialog('open');
}
function loadScripts(scripts, callback) {
if (!scripts || !scripts.length) {
return callback();
}
var adapter = scripts.pop();
$.getScript('../../adapter/' + adapter + '/blockly.js', function (/*data, textStatus, jqxhr*/) {
setTimeout(function () {
loadScripts(scripts, callback);
}, 0);
}).fail(function (jqxhr, settings, exception) {
console.warn('cannot load ' + '../../adapter/' + adapter + '/blockly.js: ' + exception);
setTimeout(function () {
loadScripts(scripts, callback);
}, 0);
});
}
function loadCustomBlockly(callback) {
// get all adapters, that can have blockly
var toLoad = [];
for (var id in that.main.objects) {
if (!that.main.objects.hasOwnProperty(id) || !that.main.objects[id]) continue;
if (!id.match(/^system\.adapter\./)) continue;
if (that.main.objects[id].type !== 'adapter') continue;
if (that.main.objects[id].common && that.main.objects[id].common.blockly) {
console.log('Detected custom blockly: ' + that.main.objects[id].common.name);
toLoad.push(that.main.objects[id].common.name);
}
}
loadScripts(toLoad, callback);
}
function enableScript(id, isEnable) {
that.main.socket.emit('extendObject', id, {
common: {
enabled: isEnable
}
}, function (err) {
if (err) {
that.main.showError(err);
that.init(true);
}
});
}
this.init = function (update) {
if (this.inited && !update) return;
var that = this;
if (!this.main.objectsLoaded || !this.languageLoaded[0] || !this.languageLoaded[1]) {
setTimeout(function () {
that.init(update);
}, 250);
return;
}
this.inited = true;
var $blocklyEditor = $('#blockly-editor');
if (!$blocklyEditor.data('inited')) {
$blocklyEditor.data('inited', true);
loadCustomBlockly(function () {
MSG.catSystem = Blockly.Words['System'][systemLang];
MSG.catSendto = Blockly.Words['Sendto'][systemLang];
// Interpolate translated messages into toolbox.
var toolboxText = document.getElementById('toolbox').outerHTML;
toolboxText = toolboxText.replace(/{(\w+)}/g,
function(m, p1) {return MSG[p1]});
var blocks = '';
for (var cb = 0; cb < Blockly.CustomBlocks.length; cb++) {
var name = Blockly.CustomBlocks[cb];
// add blocks
blocks += '<category name="' + Blockly.Words[name][systemLang] + '" colour="' + Blockly[name].HUE + '">';
for (var _b in Blockly[name].blocks) {
if (Blockly[name].blocks.hasOwnProperty(_b)) {
blocks += Blockly[name].blocks[_b];
}
}
blocks += '</category>';
}
toolboxText = toolboxText.replace('<category><block>%%CUSTOM_BLOCKS%%</block></category>', blocks);
var toolboxXml = Blockly.Xml.textToDom(toolboxText);
that.blocklyWorkspace = Blockly.inject(
'blockly-editor',
{
media: '/adapter/javascript/google-blockly/media/',
toolbox: toolboxXml,
zoom: {
controls: true,
wheel: false,
startScale: 1.0,
maxScale: 3,
minScale: 0.3,
scaleSpeed: 1.2
},
trashcan: true,
grid: {
spacing: 25,
length: 3,
colour: '#ccc',
snap: true
}
}
);
if (that.currentId) {
var obj = main.objects[that.currentId];
if (obj && obj.common && obj.common.engineType === 'Blockly') {
editScript(that.currentId);
}
}
// Listen to events on master workspace.
that.blocklyWorkspace.addChangeListener(function (masterEvent) {
if (masterEvent.type === Blockly.Events.UI) {
return; // Don't mirror UI events.
}
setChanged(true);
$('#script-edit-button-save').button('enable');
$('#script-edit-button-cancel').button('enable');
});
});
$('#dialog-new-script').dialog({
autoOpen: false,
modal: true,
width: 550,
height: 170,
open: function (event) {
$(event.target).parent().find('.ui-dialog-titlebar-close .ui-button-text').html('');
$('#dialog-new-script').css({height: 170, overflow: 'hidden'});
},
resizable: false,
buttons: {}
});
$('.dialog-new-script-button').click(function () {
var callback = $('#dialog-new-script').dialog('close').data('callback');
if (callback) {
callback($(this).data('type'));
}
});
}
if (typeof this.$grid !== 'undefined') {
that.engines = this.fillEngines('edit-script-engine-type');
/*this.$grid.selectId('init', {
objects: main.objects,
noDialog: true,
texts: {
select: _('Select'),
cancel: _('Cancel'),
all: _('All'),
id: _('Scripts'),
name: _('Name'),
role: _('Role'),
room: _('Room'),
value: _('Value'),
type: _('Type'),
selectid: _('Select ID'),
from: _('From'),
lc: _('Last changed'),
ts: _('Time stamp'),
wait: _('Processing...'),
ack: _('Acknowledged'),
edit: _('Edit'),
ok: _('Ok'),
enum: _('Members')
},
noCopyToClipboard: true,
root: 'script.js.',
useNameAsId: true,
noColumnResize: true,
firstMinWidth: '*',
columns: [
{
name: 'instance',
data: function (id, name) {
return that.main.objects[id] && that.main.objects[id].common && that.main.objects[id].common.engine ? that.main.objects[id].common.engine.substring('system.adapter.javascript.'.length) : '';
},
title: function (id, name) {
return that.main.objects[id] && that.main.objects[id].common && that.main.objects[id].common.engine ? _('Instance') + ' ' + that.main.objects[id].common.engine : '';
}
},
'button'
],
widths: ['100px', '140px'],
buttons: [
{
text: false,
icons: {
primary:'ui-icon-play'
},
click: function (id) {
if (this.length === 1) this.button('disable');
var enabled = !(that.main.objects[id] && that.main.objects[id].common && that.main.objects[id].common.enabled);
// If script not saved ask about saving
if (enabled && !$('#script-edit-button-save').hasClass('ui-button-disabled')) {
that.main.confirmMessage(_('Do you want to save script %s?', that.main.objects[id].common.name), null, 'help', function (result) {
if (result) {
that.saveScript(function () {
// toggle state
enableScript(id, enabled);
});
} else {
// toggle state
enableScript(id, enabled);
}
});
} else {
// toggle state
enableScript(id, enabled);
}
},
match: function (id) {
if (that.main.objects[id] && that.main.objects[id].type ==='script') {
if (that.main.objects[id] && that.main.objects[id].common && that.main.objects[id].common.enabled) {
this.button('option', 'icons', {
primary:'ui-icon-pause'
}).attr('title', _('Activated. Click to stop.')).css({'background-color': 'lightgreen'});
} else {
this.button('option', 'icons', {
primary:'ui-icon-play'
}).attr('title', _('Deactivated. Click to start.')).css({'background-color': '#FF9999'});
}
} else {
this.hide();
}
},
width: 26,
height: 20
},
{
text: false,
icons: {
primary:'ui-icon-trash'
},
click: function (id) {
if (!that.main.objects[id] || that.main.objects[id].type !== 'script') {
deleteId(id);
} else {
that.main.confirmMessage(_('Are you sure to delete script %s?', that.main.objects[id].common.name), null, 'help', function (result) {
if (result) that.main.socket.emit('delObject', id);
});
}
},
match: function (id) {
if (!main.objects[id] || !main.objects[id].common || main.objects[id].common.nondeletable) this.hide();
},
width: 26,
height: 20
},
{
text: false,
icons: {
primary:'ui-icon-copy'
},
click: function (id) {
that.main.socket.emit('getObject', id, function (err, obj) {
if (err) {
that.main.showError(err);
return;
}
// find new name
var i = 0;
//build name
var newId;
do {
i++;
if (obj._id.match(/\(\d+\)/)) {
newId = obj._id.replace(/\(\d+\)/, '(' + i + ')');
} else {
newId = obj._id + '(' + i + ')';
}
} while (that.list.indexOf(newId) !== -1);
obj._id = newId;
that.main.socket.emit('setObject', newId, obj, function (err, obj) {
if (err) {
that.main.showError(err);
}
});
});
},
match: function (id) {
if (!that.main.objects[id] || that.main.objects[id].type !=='script') this.hide();
},
width: 26,
height: 20
},
{
text: false,
icons: {
primary:'ui-icon-refresh'
},
click: function (id) {
that.main.socket.emit('extendObject', id, {});
},
match: function (id) {
if (!that.main.objects[id] || that.main.objects[id].type !=='script') this.hide();
},
width: 26,
height: 20
}
],
panelButtons: [
{
text: false,
title: _('New script'),
icons: {
primary: 'ui-icon-document'
},
click: function () {
var group = that.currentId || 'script.js';
if (that.main.objects[group] && that.main.objects[group].type === 'script') group = getGroup(group);
addScript(group);
}
},
{
text: false,
title: _('New group'),
icons: {
primary: 'ui-icon-circle-plus'
},
click: function () {
addScriptInGroup(that.currentId);
}
},
{
text: false,
title: _('Export'),
icons: {
primary: 'ui-icon-arrowthickstop-1-s'
},
click: function () {
exportScripts();
}
},
{
text: false,
title: _('Import'),
icons: {
primary: 'ui-icon-arrowthickstop-1-n'
},
click: function () {
importScripts();
}
}
],
onChange: function (id, oldId) {
if (id !== oldId || !that.editor) {
editScript(id);
} else {
// focus again on editor
that.editor.focus();
}
},
quickEdit: [{
name: 'instance',
options: function (id, name) {
var ins = {};
if (that.main.objects[id].type !== 'script') {
return false;
}
for (var i = 0; i < main.instances.length; i++) {
if (main.instances[i].substring(0, 'system.adapter.javascript.'.length) === 'system.adapter.javascript.') {
var inst = main.instances[i].substring('system.adapter.javascript.'.length);
ins[inst] = inst;
}
}
return ins;
}
}],
quickEditCallback: function (id, attr, newValue, oldValue) {
main.socket.emit('getObject', id, function (err, _obj) {
if (err) return that.main.showError(err);
_obj.common.engine = 'system.adapter.javascript.' + newValue;
main.socket.emit('setObject', _obj._id, _obj, function (err) {
if (err) that.main.showError(err);
});
});
}
}).selectId('show', update ? undefined : main.config['script-editor-current-id'] || undefined);
*/
this.$grid.treeTable({
objects: that.main.objects,
root: 'script.js',
widths: ['calc(100% - 106px)', '20px'],
columns: ['name', 'instance'],
name: 'scripts',
buttonsWidth: '86px',
buttonsStyle: 'text-align: left',
buttons: [
{
text: false,
icons: {
primary:'ui-icon-play'
},
click: function (id) {
if (this.length === 1) this.button('disable');
var enabled = !(that.main.objects[id] && that.main.objects[id].common && that.main.objects[id].common.enabled);
// If script not saved ask about saving
if (enabled && !$('#script-edit-button-save').hasClass('ui-button-disabled')) {
that.main.confirmMessage(_('Do you want to save script %s?', that.main.objects[id].common.name), null, 'help', function (result) {
if (result) {
that.saveScript(function () {
// toggle state
enableScript(id, enabled);
});
} else {
// toggle state
enableScript(id, enabled);
}
});
} else {
// toggle state
enableScript(id, enabled);
}
},
match: function (id) {
if (typeof this.hide !== 'function') return;
if (that.main.objects[id] && that.main.objects[id].type ==='script') {
if (that.main.objects[id] && that.main.objects[id].common && that.main.objects[id].common.enabled) {
this.button('option', 'icons', {
primary:'ui-icon-pause'
}).attr('title', _('Activated. Click to stop.')).css({'background-color': 'lightgreen'});
} else {
this.button('option', 'icons', {
primary:'ui-icon-play'
}).attr('title', _('Deactivated. Click to start.')).css({'background-color': '#FF9999'});
}
} else {
this.hide();
}
},
width: 26,
height: 20
},
{
text: false,
icons: {
primary:'ui-icon-trash'
},
click: function (id) {
if (!that.main.objects[id] || that.main.objects[id].type !== 'script') {
deleteId(id);
} else {
that.main.confirmMessage(_('Are you sure to delete script %s?', that.main.objects[id].common.name), null, 'help', function (result) {
if (result) that.main.socket.emit('delObject', id);
});
}
},
match: function (id) {
if (typeof this.hide !== 'function') return;
if (id === 'script.js.global' || id === 'script.js.common' || !main.objects[id] || !main.objects[id].common || main.objects[id].common.nondeletable) this.hide();
},
width: 26,
height: 20
},
{
text: false,
icons: {
primary:'ui-icon-copy'
},
click: function (id) {
that.main.socket.emit('getObject', id, function (err, obj) {
if (err) {
that.main.showError(err);
return;
}
// find new name
var i = 0;
//build name
var newId;
do {
i++;
if (obj._id.match(/\(\d+\)/)) {
newId = obj._id.replace(/\(\d+\)/, '(' + i + ')');
} else {
newId = obj._id + '(' + i + ')';
}
} while (that.list.indexOf(newId) !== -1);
obj._id = newId;
that.main.socket.emit('setObject', newId, obj, function (err, obj) {
if (err) {
that.main.showError(err);
}
});
});
},
match: function (id) {
if (typeof this.hide !== 'function') return;
if (!that.main.objects[id] || that.main.objects[id].type !== 'script') this.hide();
},
width: 26,
height: 20
},
{
text: false,
icons: {
primary:'ui-icon-refresh'
},
click: function (id) {
that.main.socket.emit('extendObject', id, {});
},
match: function (id) {
if (typeof this.hide !== 'function') return;
if (!that.main.objects[id] || that.main.objects[id].type !== 'script') this.hide();
},
width: 26,
height: 20
}
],
moveId: function (oldId, newId, callback) {
var obj = that.main.objects[oldId];
if (newId[0] === '.') {
newId = 'script.js' + newId;
}
if (obj === undefined) {
callback && callback('Not found');
} else {
that.main.socket.emit('delObject', oldId, function (err) {
obj._id = newId;
that.main.socket.emit('setObject', newId, obj, callback);
});
}
},
panelButtons: [
{
text: false,
title: _('New script'),
icons: {
primary: 'ui-icon-document'
},
click: function () {
var group = that.currentId || 'script.js';
if (that.main.objects[group] && that.main.objects[group].type === 'script') group = getGroup(group);
addScript(group);
}
},
{
text: false,
title: _('New group'),
icons: {
primary: 'ui-icon-circle-plus'
},
click: function () {
addScriptInGroup(that.currentId);
}
},
{
text: false,
title: _('Export'),
icons: {
primary: 'ui-icon-arrowthickstop-1-s'
},
click: function () {
exportScripts();
}
},
{
text: false,
title: _('Import'),
icons: {
primary: 'ui-icon-arrowthickstop-1-n'
},
click: function () {
importScripts();
}
},
{
text: false,
title: _('Expert mode'),
icons: {
primary: 'ui-icon-arrowthickstop-1-n'
},
click: function () {
}
}
],
onChange: function (id, oldId) {
if (id !== oldId || !that.editor) {
editScript(id);
} else {
// focus again on editor
that.editor.focus();
}
},
onEdit: function (id, attr, value) {
if (attr === 'instance') {
that.main.socket.emit('getObject', id, function (err, obj) {
if (obj) {
obj.common.engine = 'system.adapter.javascript.' + value;
that.main.socket.emit('setObject', obj._id, obj, function (err) {
if (err) {
that.main.showError(err);
that.init(true);
}
});
} else {
window.alert('Object "' + id + '" not exists');
that.init(true);
}
});
}
}
}).treeTable('show', update ? undefined : main.config['script-editor-current-id'] || undefined);
// Show add button
setTimeout(function () {
// show blink on start
var $addNew = $('#btn_custom_0_0');
var background = $addNew.css('background-color');
$addNew
.css({
'background-color': 'red'
}, 'red')
.animate({'background-color': background}, 500, function () {
$addNew.animate({'background-color': 'red'}, 500, function () {
$addNew.animate({'background-color': background}, 3000);
});
});
}, 500);
applyResizableH(true);
if (this.main.config['script-editor-wrap-lines']) $('#edit-wrap-lines').prop('checked', true);
if (this.main.config['script-editor-dialog-wrap-lines']) $('#dialog-edit-wrap-lines').prop('checked', true);
$('#edit-check-blocks').button({
icons: {
primary: 'ui-icon-check'
}
}).click(function () {
blocklyCheckBlocks(function (err, badBlock) {
if (!err) {
that.main.showMessage(_('Ok'));
} else {
if (badBlock) blocklyBlinkBlock(badBlock);
that.main.showError(_(err), function () {
if (badBlock) blocklyBlinkBlock(badBlock);
});
}
});
});
$('#edit-export-blocks')
.button({
icons: {
primary: 'ui-icon-arrowthickstop-1-s'
},
text: false
})
.attr('title', _('Export blocks'))
.css({width: 32, height: 32})
.click(function () {
showExportBlocklyDialog();
});
$('#edit-import-blocks')
.button({
icons: {
primary: 'ui-icon-arrowthickstop-1-n'
},
text: false
})
.attr('title', _('Import blocks'))
.css({width: 32, height: 32})
.click(function () {
showImportBlocklyDialog();
});
if (!update && main.config['script-editor-current-id']) {
editScript(main.config['script-editor-current-id']);
}
}
};
this.saveScript = function (isConvert, cb) {
if (typeof isConvert === 'function') {
cb = isConvert;
isConvert = false;
}
var that = this;
var obj = {};
var name = $('#edit-script-name').val();
var newId = $('#edit-script-group').val() + '.' + name.replace(/["'\s.]/g, '_');
obj.name = name;
obj.engineType = $('#edit-script-engine-type').val() || '';
obj.debug = $('#edit-script-debug').prop('checked');
obj.verbose = $('#edit-script-verbose').prop('checked');
// Try to detect blockly type
if (obj.engineType === 'Blockly') {
if (!isConvert) {
obj.source = blocklyCode2JSCode(false, true);
} else {
obj.source = that.editor.getValue();
}
}
if (that.currentId !== newId && that.main.objects[newId]) {
that.main.showError(_('Duplicate name'));
cb && cb();
return;
}
if (obj.engineType !== 'Blockly') {
obj.source = that.editor.getValue();
var blocklyText = jsCode2Blockly(obj.source);
if (blocklyText && blocklyText.substring(0, 4) === '<xml') {
// ask about change the script type
that.main.confirmMessage(_('Convert to Blockly'), _('Convert?'), 'help', function (result) {
if (result) {
obj.engineType = 'Blockly';
if (!$('#edit-script-engine-type').find('option[value="Blockly"]').length) {
$('#edit-script-engine-type').prepend('<option value="Blockly">Blockly</option>');
}
$('#edit-script-engine-type').val(obj.engineType);
setChanged(true);
that.saveScript(true, function () {
setTimeout(function () {
editScript(that.currentId);
}, 500);
});
} else {
// remove blockly text
obj.source = removeBlocklyFromCode(obj.source);
that.editor.setValue(obj.source /*, -1*/);
// wait till editor script updates
setTimeout(function () {
that.saveScript();
}, 300);
}
});
return;
}
}
setChanged(false);
$('#script-edit-button-save').button('disable');
$('#script-edit-button-cancel').button('disable');
if (that.main.objects[that.currentId] && that.main.objects[that.currentId].type === 'script') {
that.updateScript(that.currentId, newId, obj, function (err) {
if (err) {
console.error(err);
$('#script-edit-button-save').button('enable');
$('#script-edit-button-cancel').button('enable');
}
cb && cb();
});
} else {
renameGroup(that.currentId, newId, obj.name, function (err) {
if (err) {
console.error(err);
$('#script-edit-button-save').button('enable');
$('#script-edit-button-cancel').button('enable');
}
cb && cb();
});
}
that.currentId = newId;
};
this.objectChange = function (id, obj) {
// Update scripts
if (id.match(/^script\./)) {
if (obj) {
if (this.list.indexOf(id) === -1) this.list.push(id);
// if script type was changed
if (id === this.currentId) {
var $name = $('#edit-script-name');
if (obj.common.name !== $name.val()) {
$name.val(obj.common.name);
}
if (obj.common.engineType !== $('#edit-script-engine-type').val()) {
editScript(id);
}
}
} else {
// deleted
var j = this.list.indexOf(id);
if (j !== -1) this.list.splice(j, 1);
if (id === this.currentId) {
setChanged(false);
editScript(null);
}
}
if (this.updateTimer) clearTimeout(this.updateTimer);
this.updateTimer = setTimeout(function () {
that.updateTimer = null;
that.$grid.treeTable('reinit');
applyResizableH(true, 1000);
}, 200);
if (this.$grid) {
this.$grid.treeTable('object', id, obj);
}
} else
if (id.match(/^system\.adapter\.[-\w\d]+\.[0-9]+$/)) {
var $editScript = $('#edit-script-engine-type');
var val = $editScript.val();
that.engines = that.fillEngines('edit-script-engine-type');
$editScript.val(val);
}
else
if (id.match(/^system\.adapter\.[-\w\d]+\$/)) {
if (obj[id].common && obj[id].common.blockly) {
main.confirmMessage(_('Some blocks were updated. Reload admin?'), null, null, 700, function (result) {
if (result) {
window.location.reload();
}
});
}
}
if (id.match(/^script\.js\./) && obj && obj.type === 'channel') {
scripts.groups.push(id);
if (!that.renaming) {
fillGroups('edit-script-group');
}
}
};
function getTimeString(d) {
var text = '';
var i = d.getHours();
if (i < 10) i = '0' + i.toString();
text = i + ':';
i = d.getMinutes();
if (i < 10) i = '0' + i.toString();
text += i + ':';
i = d.getSeconds();
if (i < 10) i = '0' + i.toString();
text += i + '.';
i = d.getMilliseconds();
if (i < 10) {
i = '00' + i.toString();
} else if (i < 100) {
i = '0' + i.toString();
}
text += i;
return text;
}
this.onLog = function (message) {
if (!this.$parentOutput) this.$parentOutput = $('#script-output').parent().parent();
//{"message":"javascript.0 Stop script script.js.Script4","severity":"info","from":"javascript.0","ts":1455490697111,"_id":364}
if (that.currentId && message.message.indexOf(that.currentId) !== -1) {
var text = '<tr class="' + message.severity + '"><td>' + getTimeString(new Date(message.ts)) + '</td><td>[' + message.severity + ']</td><td>' + message.message + '</td></tr>';
var h = this.$parentOutput.height();
var oldHeight = $('#script-output').height();
var scrollTop = this.$parentOutput.scrollTop();
var shiftToEnd = (scrollTop + h >= oldHeight - 5);
if (oldHeight > 2000) {
$('#script-output tr:first').remove();
var oldHeight1 = $('#script-output').height();
this.$parentOutput.scrollTop(scrollTop - (oldHeight - oldHeight1));
oldHeight = oldHeight1;
}
var scrollTop = this.$parentOutput.scrollTop();
var shiftToEnd = (scrollTop + h >= oldHeight - 5);
$('#script-output').append(text);
if (shiftToEnd) {
this.$parentOutput.scrollTop(oldHeight + 50);
}
}
};
this.showCronDialog = function (value, cb) {
if (value) {
value = value.replace(/\"/g, '').replace(/\'/g, '');
if (value) {
try {
$('#div-cron').cron('value', value);
} catch (e) {
alert(_('Cannot parse value as cron'));
}
}
}
$('#dialog_cron_callback').show();
$('#dialog_cron_insert').hide();
$('#dialog_cron_callback').unbind('click').click(function () {
var val = $('#div-cron').cron('value');
that.$dialogCron.dialog('close');
if (cb) cb(val);
});
this.$dialogCron.dialog('open');
};
this.showScriptDialog = function (value, args, isReturn, cb) {
this.editorDialog.setValue(value || '');
var width = 700;
var height = 550;
if (this.main.config['script-edit-width']) width = this.main.config['script-edit-width'];
if (this.main.config['script-edit-height']) height = this.main.config['script-edit-height'];
this.$dialogScript.data('callback', cb);
if (args && args.length) {
this.$dialogScript.dialog('option', 'title', _('Edit script') + '. ' + _('Arguments: ') + args.join(', '));
} else {
this.$dialogScript.dialog('option', 'title', _('Edit script'));
}
setEditorOptions(this.editorDialog, {
lineWrap: $('#dialog-edit-wrap-lines').prop('checked'),
});
this.$dialogScript
.dialog('option', 'width', width)
.dialog('option', 'height', height)
.dialog('open');
this.editorDialog.focus();
that.editorDialog._isReturn = isReturn;
setTimeout(function () {
that.editorDialog._changed = false;
$('#dialog_script_save').button('disable');
}, 100);
};
}
/**
* Tests if the given ID belongs to a global script
* @param {string} id
* @returns {boolean}
*/
function isIdOfGlobalScript(id) {
return /^script\.js\.global\./.test(id);
}
var main = {
socket: io.connect(location.protocol + '//' + location.host, {
query: 'ws=true'
}),
saveConfig: function (attr, value) {
if (!main.config) return;
if (attr) main.config[attr] = value;
if (typeof storage !== 'undefined') {
storage.set('adminConfig', JSON.stringify(main.config));
}
},
showError: function (error, cb) {
main.showMessage(_(error), _('Error'), 'alert', cb);
},
showMessage: function (message, title, icon, cb) {
if (typeof title === 'function') {
cb = title;
title = null;
icon = null;
}
if (typeof icon === 'function') {
cb = icon;
icon = null;
}
$dialogMessage.dialog('option', 'title', title || _('Message'));
$('#dialog-message-text').html(message);
if (icon) {
if (!icon.match(/^ui\-icon\-/)) icon = 'ui-icon-' + icon;
$('#dialog-message-icon')
.show()
.attr('class', '')
.addClass('ui-icon ' + icon);
} else {
$('#dialog-message-icon').hide();
}
$dialogMessage.data('callback', cb);
$dialogMessage.dialog('open');
},
confirmMessage: function (message, title, icon, buttons, callback) {
if (typeof buttons === 'function') {
callback = buttons;
$dialogConfirm.dialog('option', 'buttons', [
{
text: _('Ok'),
click: function () {
var cb = $(this).data('callback');
$(this).dialog('close');
if (cb) cb(true);
}
},
{
text: _('Cancel'),
click: function () {
var cb = $(this).data('callback');
$(this).dialog('close');
if (cb) cb(false);
}
}
]);
} else if (typeof buttons === 'object') {
for (var b = 0; b < buttons.length; b++) {
buttons[b] = {
text: buttons[b],
id: 'dialog-confirm-button-' + b,
click: function (e) {
var id = parseInt(e.currentTarget.id.substring('dialog-confirm-button-'.length), 10);
var cb = $(this).data('callback');
$(this).dialog('close');
if (cb) cb(id);
}
}
}
$dialogConfirm.dialog('option', 'buttons', buttons);
}
$dialogConfirm.dialog('option', 'title', title || _('Message'));
$('#dialog-confirm-text').html(message);
if (icon) {
$('#dialog-confirm-icon')
.show()
.attr('class', '')
.addClass('ui-icon ui-icon-' + icon);
} else {
$('#dialog-confirm-icon').hide();
}
$dialogConfirm.data('callback', callback);
$dialogConfirm.dialog('open');
},
initSelectId: function () {
if (main.selectId) return main.selectId;
main.selectId = $('#dialog-select-member').selectId('init', {
objects: main.objects,
states: main.states,
noMultiselect: true,
imgPath: '../../lib/css/fancytree/',
filter: {type: 'state'},
getObjects: getObjects,
texts: {
select: _('Select'),
cancel: _('Cancel'),
all: _('All'),
id: _('ID'),
name: _('Name'),
role: _('Role'),
room: _('Room'),
value: _('Value'),
selectid: _('Select ID'),
from: _('From'),
lc: _('Last changed'),
ts: _('Time stamp'),
wait: _('Processing...'),
ack: _('Acknowledged')
},
columns: ['image', 'name', 'role', 'room', 'value']
});
return main.selectId;
},
subscribe: function (isSubscribe) {
if (!main.socket) return;
if (isSubscribe) {
main.socket.emit('subscribeObjects', 'script.*');
main.socket.emit('subscribeObjects', 'system.adapter.*');
main.socket.emit('requireLog', true);
} else {
main.socket.emit('unsubscribeObjects', 'script.*');
main.socket.emit('unsubscribeObjects', 'system.adapter.*');
main.socket.emit('requireLog', false);
}
},
objects: {},
states: {},
currentHost: '',
instances: [],
objectsLoaded: false,
waitForRestart: false,
selectId: null
};
var $dialogMessage = $('#dialog-message');
var $dialogConfirm = $('#dialog-confirm');
// Read all positions, selected widgets for every view,
// Selected view, selected menu page,
// Selected widget or view page
// Selected filter
if (typeof storage !== 'undefined') {
try {
main.config = storage.get('adminConfig');
if (main.config) {
main.config = JSON.parse(main.config);
} else {
main.config = {};
}
} catch (e) {
console.log('Cannot load edit config');
main.config = {};
}
}
var firstConnect = true;
var scripts = new Scripts(main);
function getStates(callback) {
main.socket.emit('getStates', function (err, res) {
main.states = res;
if (typeof callback === 'function') {
setTimeout(function () {
callback();
}, 0);
}
});
}
function getObjects(callback) {
main.socket.emit('getAllObjects', function (err, res) {
setTimeout(function () {
var obj;
main.objects = res;
for (var id in main.objects) {
if (!main.objects.hasOwnProperty(id) || id.slice(0, 7) === '_design') continue;
obj = res[id];
if (obj.type === 'instance') main.instances.push(id);
if (obj.type === 'script') scripts.list.push(id);
if (obj.type === 'channel' && id.match(/^script\.js\./)) scripts.groups.push(id);
if (obj.type === 'host') scripts.hosts.push(id);
}
main.objectsLoaded = true;
scripts.prepare();
scripts.init();
if (typeof callback === 'function') callback(null, res);
}, 0);
});
}
function objectChange(id, obj) {
// update main.objects cache
if (obj) {
if (obj._rev && main.objects[id]) main.objects[id]._rev = obj._rev;
if (!main.objects[id] || JSON.stringify(main.objects[id]) !== JSON.stringify(obj)) {
main.objects[id] = obj;
if (obj.type === 'instance') {
pos = main.instances.indexOf(id);
if (pos === -1) main.instances.push(id);
} else
if (obj.type === 'script') {
pos = scripts.list.indexOf(id);
if (pos === -1) scripts.list.push(id);
} else
if (id.match(/^script\.js\./) && obj.type === 'channel') {
pos = scripts.groups.indexOf(id);
if (pos === -1) scripts.groups.push(id);
}
}
} else if (main.objects[id]) {
var oldObj = {_id: id, type: main.objects[id].type};
delete main.objects[id];
var pos;
if (oldObj.type === 'instance') {
pos = main.instances.indexOf(id);
if (pos !== -1) main.instances.splice(pos, 1);
} else
if (oldObj.type === 'script') {
pos = scripts.list.indexOf(id);
if (pos !== -1) scripts.list.splice(pos, 1);
} else
if (id.match(/^script\.js\./) && oldObj.type === 'channel') {
pos = scripts.groups.indexOf(id);
if (pos !== -1) scripts.groups.splice(pos, 1);
}
}
if (main.selectId) main.selectId.selectId('object', id, obj);
if (id.match(/^system\.adapter\.[-\w]+\.[0-9]+$/)) {
// Disable scripts tab if no one script engine instance found
var engines = scripts.fillEngines();
$('#tabs').tabs('option', 'disabled', (engines && engines.length) ? [] : [4]);
}
scripts.objectChange(id, obj);
}
function stateChange(id, state) {
var rowData;
id = id ? id.replace(/[\s'"]/g, '_') : '';
if (!id || !id.match(/\.messagebox$/)) {
if (main.selectId) main.selectId.selectId('state', id, state);
}
}
function onLog(message) {
scripts.onLog(message);
}
main.socket.on('permissionError', function (err) {
main.showMessage(_('Has no permission to %s %s %s', err.operation, err.type, (err.id || '')));
});
main.socket.on('objectChange', function (id, obj) {
setTimeout(objectChange, 0, id, obj);
});
main.socket.on('stateChange', function (id, obj) {
setTimeout(stateChange, 0, id, obj);
});
main.socket.on('connect', function () {
$('#connecting').hide();
if (firstConnect) {
firstConnect = false;
main.socket.emit('getUserPermissions', function (err, acl) {
main.acl = acl;
// Read system configuration
main.socket.emit('getObject', 'system.config', function (err, data) {
main.systemConfig = data;
if (!err && main.systemConfig && main.systemConfig.common) {
systemLang = main.systemConfig.common.language;
} else {
systemLang = window.navigator.userLanguage || window.navigator.language;
if (systemLang !== 'en' && systemLang !== 'de' && systemLang !== 'ru') {
main.systemConfig.common.language = 'en';
systemLang = 'en';
}
}
translateAll();
$dialogMessage.dialog({
autoOpen: false,
modal: true,
buttons: [
{
text: _('Ok'),
click: function () {
$(this).dialog('close');
var cb = $(this).data('callback');
if (typeof cb === 'function') {
$(this).data('callback', null);
cb();
}
}
}
]
});
$dialogConfirm.dialog({
autoOpen: false,
modal: true,
width: 400,
height: 200,
buttons: [
{
text: _('Ok'),
click: function () {
var cb = $(this).data('callback');
$(this).dialog('close');
if (cb) cb(true);
}
},
{
text: _('Cancel'),
click: function () {
var cb = $(this).data('callback');
$(this).dialog('close');
if (cb) cb(false);
}
}
]
});
getStates(getObjects);
});
});
}
main.subscribe(true);
if (main.waitForRestart) {
location.reload();
}
});
main.socket.on('disconnect', function () {
$('#connecting').show();
});
main.socket.on('reconnect', function () {
$('#connecting').hide();
if (main.waitForRestart) {
location.reload();
}
});
main.socket.on('reauthenticate', function () {
location.reload();
});
main.socket.on('log', function (message) {
setTimeout(onLog, 0, message);
});
function applyResizableH(install, timeout) {
if (timeout) {
setTimeout(function () {
applyResizableH(install);
}, timeout);
} else {
var $gridScripts = $('#grid-scripts');
if ($gridScripts.hasClass('ui-resizable')) {
$gridScripts.resizable('destroy');
}
if (!install) return;
var width = parseInt(main.config['script-editor-width'] || '30%', 10);
$gridScripts.width(width + '%').next().width(100 - width + '%');
$gridScripts.resizable({
autoHide: false,
handles: 'e',
start: function (e, ui) {
var $editor = $('#blockly-editor');
$editor.data('wasVisible', $editor.is(':visible'));
$editor.hide();
$editor = $('#script-editor');
$editor.data('wasVisible', $editor.is(':visible'));
$editor.hide();
$('.blocklyWidgetDiv').hide();
$('.blocklyTooltipDiv').hide();
$('.blocklyToolboxDiv').hide();
},
resize: function(e, ui) {
var parent = ui.element.parent();
var remainingSpace = parent.width() - ui.element.outerWidth();
var divTwo = ui.element.next();
var divTwoWidth = (remainingSpace - (divTwo.outerWidth() - divTwo.width())) / parent.width() * 100 + '%';
divTwo.width(divTwoWidth);
},
stop: function(e, ui) {
var parent = ui.element.parent();
var width = ui.element.width() / parent.width() * 100 + '%';
ui.element.css({
width: width
});
main.saveConfig('script-editor-width', width);
scripts.resize();
}
});
}
}
function applyResizableV() {
var height = parseInt(main.config['script-editor-height'] || '80%', 10);
var $textarea = $('#editor-scripts-textarea');
$textarea
.height(height + '%')
.next()
.height(100 - height + '%');
$textarea.resizable({
autoHide: false,
handles: 's',
start: function (e, ui) {
var $editor = $('#blockly-editor');
$editor.data('wasVisible', $editor.is(':visible'));
$editor.hide();
$('.blocklyWidgetDiv').hide();
$('.blocklyTooltipDiv').hide();
$('.blocklyToolboxDiv').hide();
},
resize: function (e, ui) {
var parent = ui.element.parent();
var remainingSpace = parent.height() - ui.element.outerHeight();
var divTwo = ui.element.next();
var divTwoWidth = (remainingSpace - (divTwo.outerHeight() - divTwo.height())) / parent.height() * 100 + '%';
divTwo.height(divTwoWidth);
},
stop: function (e, ui) {
var parent = ui.element.parent();
var height = ui.element.height() / parent.height() * 100 + '%';
ui.element.css({
height: height
});
main.saveConfig('script-editor-height', height);
scripts.resize();
}
});
}
window.onbeforeunload = function (evt) {
if (scripts.changed) {
if (window.confirm(_('Script changes are not saved. Discard?'))) {
main.subscribe(false);
return null;
} else {
return _('Configuration not saved.');
}
}
main.subscribe(false);
return null;
};