diff --git a/extensions/web-scripts/script-editor.xml b/extensions/web-scripts/script-editor.xml index a100f25..147c6e4 100644 --- a/extensions/web-scripts/script-editor.xml +++ b/extensions/web-scripts/script-editor.xml @@ -13,6 +13,6 @@
- +
\ No newline at end of file diff --git a/extensions/web-scripts/script-view-config.xml b/extensions/web-scripts/script-view-config.xml new file mode 100644 index 0000000..b1437ee --- /dev/null +++ b/extensions/web-scripts/script-view-config.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/extensions/web-scripts/script-view.xml b/extensions/web-scripts/script-view.xml index e2e4e75..c72f03d 100644 --- a/extensions/web-scripts/script-view.xml +++ b/extensions/web-scripts/script-view.xml @@ -9,10 +9,11 @@
- +
\ No newline at end of file diff --git a/extensions/web-scripts/view-init.js b/extensions/web-scripts/view-init.js new file mode 100644 index 0000000..acbc2a3 --- /dev/null +++ b/extensions/web-scripts/view-init.js @@ -0,0 +1,70 @@ +define(['./config.json', './view.xml'], function(config, viewXml) { + + var viewTemplate = [ + '', + viewXml, + '' + ].join('\n'); + + var viewVue = new Vue({ + template: viewTemplate, + data: { + property: {} + }, + methods: { + onShow: function() { + this.onDataChange(); + }, + onDataChange: function() { + Promise.all([ + app.getThings(), + app.getPropertiesByThingId() + ]).then(apply(this, function(things, properties) { + this.refreshProperties(things, properties); + })); + }, + refreshProperties: function(things, properties) { + var propertyMap = {}; + for (var i = 0; i < config.properties.length; i++) { + var cfgProp = config.properties[i]; + var parts = cfgProp.path.split('/', 2); + var thingId = parts[0]; + var propName = parts[1]; + var props = properties[thingId]; + var value; + if (props) { + value = props[propName]; + if (value !== undefined) { + propertyMap[cfgProp.name] = value; + continue; + } + } + var thing = things[thingId]; + if (thing) { + var thgProp = thing.properties[propName]; + if (thgProp) { + var propType = thgProp['@type']; + switch (propType) { + case 'integer': + case 'number': + value = 0; + break; + case 'string': + value = ''; + break; + case 'boolean': + value = false; + break; + } + propertyMap[cfgProp.name] = value; + } + } + } + this.property = propertyMap; + } + } + }); + + addPageComponent(viewVue, config.icon); + +}); \ No newline at end of file diff --git a/extensions/web-scripts/view-schema.json b/extensions/web-scripts/view-schema.json new file mode 100644 index 0000000..8c30ae6 --- /dev/null +++ b/extensions/web-scripts/view-schema.json @@ -0,0 +1,41 @@ +{ + "type": "object", + "properties": { + "id": { + "title": "Id", + "type": "string", + "default": "view", + "required": true + }, + "title": { + "title": "Title", + "type": "string", + "default": "View", + "required": true + }, + "icon": { + "title": "Icon", + "description": "The fontawesome icon to use for the view tile and menu entry, such as 'house'", + "type": "string" + }, + "properties": { + "title": "Properties", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "title": "Name", + "type": "string", + "required": true + }, + "path": { + "title": "Property", + "type": "string", + "enumVar": "readablePropertyPaths" + } + } + } + } + } +} \ No newline at end of file diff --git a/extensions/web-scripts/view-script.lua b/extensions/web-scripts/view-script.lua new file mode 100644 index 0000000..1e803f4 --- /dev/null +++ b/extensions/web-scripts/view-script.lua @@ -0,0 +1,5 @@ +local extension = ... +local loader = require('jls.lang.loader') +local coreExtPath = extension:getEngine().lhaExtensionsDir:getPath() +local webBaseAddons = loader.load('web-base.addons', coreExtPath) +webBaseAddons.registerAddonExtension(extension, 'init.js') \ No newline at end of file diff --git a/extensions/web-scripts/web-scripts.js b/extensions/web-scripts/web-scripts.js index 26a3c09..0d8ba0a 100644 --- a/extensions/web-scripts/web-scripts.js +++ b/extensions/web-scripts/web-scripts.js @@ -1,5 +1,11 @@ -define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script-view.xml', './script-editor.xml', './toolbox.xml', './blocks.json', './blocks-lua'], - function(scriptsTemplate, scriptsAddTemplate, scriptBlocklyTemplate, scriptViewTemplate, scriptEditorTemplate, toolboxXml, blocks, blocksLua) +define(['./scripts.xml', './scripts-add.xml', + './script-blockly.xml', './toolbox.xml', './blocks.json', './blocks-lua', + './script-view.xml', './script-view-config.xml', './view-schema.json', './view-init.js!', './view-script.lua', + './script-editor.xml'], + function(scriptsTemplate, scriptsAddTemplate, + scriptBlocklyTemplate, toolboxXml, blocks, blocksLua, + scriptViewTemplate, scriptViewConfigTemplate, scriptsViewConfigSchema, viewInitJs, viewScriptLua, + scriptEditorTemplate) { function getMatches(s, r) { @@ -104,6 +110,9 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- return workspace; }; + var scriptPath = '/engine/scripts/'; + var scriptFilesPath = '/engine/scriptFiles/'; + var blockEnv = { lhaDataColor: 38, lhaEventColor: 58, @@ -150,7 +159,7 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- console.log('scriptsEditor.onDelete(), scriptId is "' + scriptId + '"'); if (scriptId) { confirmation.ask('Delete the script?').then(function() { - fetch('/engine/scripts/' + scriptId + '/', { + fetch(scriptPath + scriptId + '/', { method: 'DELETE' }).then(assertIsOk).then(function() { app.replacePage('scripts'); @@ -161,7 +170,7 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- } function onRename() { var self = this; - return fetch('/engine/scripts/' + this.scriptId + '/name', { + return fetch(scriptPath + this.scriptId + '/name', { method: 'PUT', body: this.newName }).then(assertIsOk).then(function() { @@ -173,7 +182,7 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- function onApply() { var scriptId = this.scriptId; return this.onSave().then(function() { - return fetch('/engine/scripts/' + scriptId + '/reload', {method: 'POST'}); + return fetch(scriptPath + scriptId + '/reload', {method: 'POST'}); }).then(function() { toaster.toast('Script reloaded'); }); @@ -215,7 +224,7 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- return; } var self = this; - fetch('/engine/scriptFiles/' + self.scriptId + '/blocks.xml', { + fetch(scriptFilesPath + self.scriptId + '/blocks.xml', { method: 'PUT', body: input.files[0] }).then(assertIsOk).then(function() { @@ -278,11 +287,11 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- } // save return Promise.all([ - fetch('/engine/scriptFiles/' + scriptId + '/blocks.xml', { + fetch(scriptFilesPath + scriptId + '/blocks.xml', { method: 'PUT', body: xmlText }).then(assertIsOk), - fetch('/engine/scriptFiles/' + scriptId + '/script.lua', { + fetch(scriptFilesPath + scriptId + '/script.lua', { method: 'PUT', body: code }).then(assertIsOk) @@ -296,10 +305,10 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- return Promise.reject('workspace not initialized'); } return Promise.all([ - fetch('/engine/scriptFiles/' + this.scriptId + '/blocks.xml').then(function(response) { + fetch(scriptFilesPath + this.scriptId + '/blocks.xml').then(function(response) { return response.text(); }), - fetch('/engine/scriptFiles/' + this.scriptId + '/manifest.json').then(function(response) { + fetch(scriptFilesPath + this.scriptId + '/manifest.json').then(function(response) { return response.json(); }) ]).then(apply(this, function(xmlText, manifest) { @@ -336,17 +345,17 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- if (!scriptId) { return; } - return fetch('/engine/scriptFiles/' + scriptId + '/view.xml', { - method: 'PUT', - body: this.text - }).then(assertIsOk).then(function() { + return Promise.all([ + fetch(scriptFilesPath + scriptId + '/view.xml', {method: 'PUT', body: this.text}).then(assertIsOk), + fetch(scriptFilesPath + scriptId + '/init.js', {method: 'PUT', body: viewInitJs}).then(assertIsOk) + ]).then(function() { toaster.toast('Saved'); }); }, refresh: function() { return Promise.all([ - fetch('/engine/scriptFiles/' + this.scriptId + '/view.xml').then(assertIsOk).then(getResponseText), - fetch('/engine/scriptFiles/' + this.scriptId + '/manifest.json').then(assertIsOk).then(getJson) + fetch(scriptFilesPath + this.scriptId + '/view.xml').then(assertIsOk).then(getResponseText), + fetch(scriptFilesPath + this.scriptId + '/manifest.json').then(assertIsOk).then(getJson) ]).then(apply(this, function(text, manifest) { this.text = text; this.name = manifest.name; @@ -355,6 +364,37 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- } }); + var scriptsViewConfigVue = new Vue({ + template: scriptViewConfigTemplate, + data: { + scriptId: '', + schema: {}, + config: {} + }, + methods: { + onShow: function(scriptId) { + this.scriptId = scriptId; + return Promise.all([ + fetch(scriptFilesPath + this.scriptId + '/config.json').then(getJson), + app.getEnumsById() + ]).then(apply(this, function(config, enumsById) { + //console.info('schema:', populateJsonSchema(scriptsViewConfigSchema, enumsById)); + this.schema = populateJsonSchema(scriptsViewConfigSchema, enumsById); + this.config = config; + })); + }, + onSave: function() { + return fetch(scriptFilesPath + this.scriptId + '/config.json', { + method: 'PUT', + body: JSON.stringify(this.config) + }).then(assertIsOk).then(function() { + app.back(); + toaster.toast('Saved'); + }); + } + } + }); + var scriptsEditorVue = new Vue({ template: scriptEditorTemplate, data: { @@ -379,7 +419,7 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- if (!scriptId) { return; } - return fetch('/engine/scriptFiles/' + scriptId + '/script.lua', { + return fetch(scriptFilesPath + scriptId + '/script.lua', { method: 'PUT', body: this.text }).then(assertIsOk).then(function() { @@ -388,8 +428,8 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- }, refresh: function() { return Promise.all([ - fetch('/engine/scriptFiles/' + this.scriptId + '/script.lua').then(assertIsOk).then(getResponseText), - fetch('/engine/scriptFiles/' + this.scriptId + '/manifest.json').then(assertIsOk).then(getJson) + fetch(scriptFilesPath + this.scriptId + '/script.lua').then(assertIsOk).then(getResponseText), + fetch(scriptFilesPath + this.scriptId + '/manifest.json').then(assertIsOk).then(getJson) ]).then(apply(this, function(luaText, manifest) { this.text = luaText; this.name = manifest.name; @@ -402,17 +442,17 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- template: scriptsAddTemplate, methods: { newScript: function () { - fetch('/engine/scripts/', { + fetch(scriptPath, { method: 'PUT' }).then(assertIsOk).then(function() { app.replacePage('scripts'); }); }, newBlocks: function () { - fetch('/engine/scripts/', { + fetch(scriptPath, { method: 'PUT' }).then(assertIsOk).then(getResponseText).then(function(scriptId) { - return fetch('/engine/scriptFiles/' + scriptId + '/blocks.xml', { + return fetch(scriptFilesPath + scriptId + '/blocks.xml', { method: 'PUT', body: '' }); @@ -421,38 +461,14 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- }); }, newView: function () { - fetch('/engine/scripts/', { + fetch(scriptPath, { method: 'PUT' }).then(assertIsOk).then(getResponseText).then(function(scriptId) { return Promise.all([ - fetch('/engine/scriptFiles/' + scriptId + '/view.xml', { - method: 'PUT', - body: [ - '', - '' - ].join('\n') - }), - fetch('/engine/scriptFiles/' + scriptId + '/init.js', { - method: 'PUT', - body: [ - "define(['./view.xml'], function(viewTemplate) {", - " var viewVue = new Vue({", - " template: viewTemplate", - " });", - " addPageComponent(viewVue);", - "});" - ].join('\n') - }), - fetch('/engine/scriptFiles/' + scriptId + '/script.lua', { - method: 'PUT', - body: [ - "local extension = ...", - "local loader = require('jls.lang.loader')", - "local coreExtPath = extension:getEngine().lhaExtensionsDir:getPath()", - "local webBaseAddons = loader.load('web-base.addons', coreExtPath)", - "webBaseAddons.registerAddonExtension(extension, 'init.js')" - ].join('\n') - }), + fetch(scriptFilesPath + scriptId + '/view.xml', {method: 'PUT', body: ''}), + fetch(scriptFilesPath + scriptId + '/config.json', {method: 'PUT', body: '{"id": "view-' + scriptId + '", "title": "View ' + scriptId + '"}'}), + fetch(scriptFilesPath + scriptId + '/init.js', {method: 'PUT', body: viewInitJs}), + fetch(scriptFilesPath + scriptId + '/script.lua', {method: 'PUT', body: viewScriptLua}), ]); }).then(function() { app.replacePage('scripts'); @@ -470,7 +486,7 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- onShow: function () { this.scripts = []; var self = this; - fetch('/engine/scripts/', { + fetch(scriptPath, { headers: { "Accept": 'application/json' } @@ -486,7 +502,7 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- }); }, reloadScript: function (script) { - fetch('/engine/scripts/' + script.id + '/reload', {method: 'POST'}).then(function() { + fetch(scriptPath + script.id + '/reload', {method: 'POST'}).then(function() { toaster.toast('Script reloaded'); }); }, @@ -515,7 +531,8 @@ define(['./scripts.xml', './scripts-add.xml', './script-blockly.xml', './script- addPageComponent(scriptsAddVue); addPageComponent(scriptsBlocklyVue); addPageComponent(scriptsViewVue); + addPageComponent(scriptsViewConfigVue); addPageComponent(scriptsEditorVue); - addPageComponent(scriptsVue, 'fa-scroll'); + addPageComponent(scriptsVue, 'scroll'); }); diff --git a/extensions/web-scripts/web-scripts.lua b/extensions/web-scripts/web-scripts.lua index f43d6f8..9acf6f8 100644 --- a/extensions/web-scripts/web-scripts.lua +++ b/extensions/web-scripts/web-scripts.lua @@ -2,7 +2,6 @@ local extension = ... local logger = extension:getLogger() local File = require('jls.io.File') -local HTTP_CONST = require('jls.net.http.HttpMessage').CONST local FileHttpHandler = require('jls.net.http.handler.FileHttpHandler') local RestHttpHandler = require('jls.net.http.handler.RestHttpHandler') local HttpExchange = require('jls.net.http.HttpExchange')