Support for custom scripts

This commit is contained in:
Fabio Manganiello 2020-06-19 00:26:39 +02:00
parent 52c643e4c8
commit 5d5ff69318
4 changed files with 156 additions and 27 deletions

101
package-lock.json generated
View file

@ -2333,6 +2333,17 @@
"integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==",
"dev": true
},
"clipboard": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz",
"integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==",
"optional": true,
"requires": {
"good-listener": "^1.2.2",
"select": "^1.1.2",
"tiny-emitter": "^2.0.0"
}
},
"cliui": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
@ -2450,6 +2461,16 @@
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
"dev": true
},
"component-props": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/component-props/-/component-props-1.1.1.tgz",
"integrity": "sha1-+bffm5kntubZfJvScqqGdnDzSUQ="
},
"component-xor": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/component-xor/-/component-xor-0.0.4.tgz",
"integrity": "sha1-xV2DzMG5TNUImk6T+niRxyY+Wao="
},
"compress-commons": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz",
@ -2952,6 +2973,12 @@
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true
},
"delegate": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
"integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
"optional": true
},
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@ -3011,6 +3038,15 @@
"esutils": "^2.0.2"
}
},
"dom-iterator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/dom-iterator/-/dom-iterator-1.0.0.tgz",
"integrity": "sha512-7dsMOQI07EMU98gQM8NSB3GsAiIeBYIPKpnxR3c9xOvdvBjChAcOM0iJ222I3p5xyiZO9e5oggkNaCusuTdYig==",
"requires": {
"component-props": "1.1.1",
"component-xor": "0.0.4"
}
},
"domain-browser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
@ -3198,6 +3234,11 @@
"is-symbol": "^1.0.2"
}
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@ -4330,6 +4371,15 @@
"minimatch": "~3.0.2"
}
},
"good-listener": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
"integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
"optional": true,
"requires": {
"delegate": "^3.1.2"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
@ -4987,8 +5037,7 @@
"is-extendable": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
"dev": true
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
},
"is-extglob": {
"version": "2.1.1",
@ -6833,6 +6882,14 @@
}
}
},
"prismjs": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.20.0.tgz",
"integrity": "sha512-AEDjSrVNkynnw6A+B1DsFkd6AVdTnp+/WoUixFRULlCLZVRZlVQMVWio/16jv7G1FscUxQxOQhWwApgbnxr6kQ==",
"requires": {
"clipboard": "^2.0.0"
}
},
"private": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
@ -7422,6 +7479,12 @@
}
}
},
"select": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
"integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
"optional": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@ -8226,6 +8289,12 @@
"setimmediate": "^1.0.4"
}
},
"tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
"optional": true
},
"tmp": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@ -8385,6 +8454,24 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true
},
"unescape": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unescape/-/unescape-1.0.1.tgz",
"integrity": "sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==",
"requires": {
"extend-shallow": "^2.0.1"
},
"dependencies": {
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"requires": {
"is-extendable": "^0.1.0"
}
}
}
},
"unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
@ -8686,6 +8773,16 @@
"vue-style-loader": "^4.1.0"
}
},
"vue-prism-editor": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/vue-prism-editor/-/vue-prism-editor-0.6.1.tgz",
"integrity": "sha512-UyFLZ242eAplU0C1Tx/ZHSKFTPODQDMBuW9qqgMJyZqHFL2iuIbfT8EWmKtoNUn8w9VWS9IIicPs2odz2eni4Q==",
"requires": {
"dom-iterator": "^1.0.0",
"escape-html": "^1.0.3",
"unescape": "^1.0.1"
}
},
"vue-style-loader": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz",

View file

@ -24,7 +24,9 @@
},
"dependencies": {
"axios": "^0.19.0",
"prismjs": "^1.20.0",
"vue": "^2.6.10",
"vue-prism-editor": "^0.6.1",
"webextension-polyfill": "^0.3.1"
},
"devDependencies": {

View file

@ -71,21 +71,19 @@
</div>
<div v-else>
<form ref="runForm" @submit.prevent="runScript">
<textarea v-model="script" />
<form ref="runForm" @submit.prevent="runAction">
<PrismEditor v-model="script" language="js" />
<div class="row buttons">
<button type="button" @click="saveMode = true" :disabled="loading || !(action.name && action.name.length && action.name in actions)" v-if="!saveMode">
<i class="fas fa-save" /> &nbsp; Save Action
</button>
<button type="button" @click="saveMode = true" :disabled="loading || script === scriptTemplate" v-if="!saveMode"><i class="fas fa-save" /> &nbsp; Save Script</button>
<button type="submit" :disabled="loading"><i class="fas fa-play" /> &nbsp; Run</button>
</div>
</form>
</div>
<form class="save-form" ref="scriptForm" @submit.prevent="storeAction" v-if="saveMode">
<form class="save-form" ref="scriptForm" @submit.prevent="storeScript" v-if="saveMode">
<div class="row">
<input type="text" name="displayName" placeholder="Action display name" />
<input type="text" name="displayName" placeholder="Script display name" />
</div>
<div class="row">
@ -94,14 +92,14 @@
<div class="row multiple-host-selector">
<div class="desc">
Install action on these devices
Install script on these devices
</div>
<MultipleHostSelector :hosts="hosts" :selected="[host.name]" />
</div>
<div class="row buttons">
<button type="submit" :disabled="loading"><i class="fas fa-save" /> &nbsp; Save Action</button>
<button type="submit" :disabled="loading"><i class="fas fa-save" /> &nbsp; Save Script</button>
<button type="button" @click="saveMode = false" :disabled="loading"><i class="fas fa-times" /> &nbsp; Cancel</button>
</div>
</form>
@ -112,6 +110,10 @@
</template>
<script>
import 'prismjs';
import 'prismjs/themes/prism.css';
import PrismEditor from 'vue-prism-editor';
import mixins from '../utils';
import Autocomplete from './Autocomplete';
import MultipleHostSelector from './MultipleHostSelector';
@ -121,11 +123,25 @@ export default {
mixins: [mixins],
props: {
host: Object,
scriptTemplate: {
type: String,
default: `async (app, host, browser, window) => {
// Run some action on the host
const status = await app.run({ name: 'music.mpd.pause' }, host);
// Send notifications to the browser
app.notify(status.state, 'Music status changed');
// Return values back to the app
return status;
}`,
},
},
components: {
Autocomplete,
MultipleHostSelector,
PrismEditor,
},
data() {
@ -137,13 +153,10 @@ export default {
actionResponse: null,
actionError: null,
hosts: {},
script: `(browser, window, document) => {
// Do something
}`,
script: this.scriptTemplate,
actionMode: 'request',
action: {
name: null,
script: null,
args: [],
},
};
@ -212,13 +225,17 @@ export default {
this.loading = true;
try {
this.actionResponse = await this.run(
{
name: this.action.name,
args: this.getActionArgs(),
},
this.host
);
if (this.actionMode === 'request') {
this.actionResponse = await this.run(
{
name: this.action.name,
args: this.getActionArgs(),
},
this.host
);
} else {
this.actionResponse = await this.runScript(this.script, this.host);
}
this.actionError = null;
} catch (e) {
@ -229,11 +246,6 @@ export default {
}
},
async runScript() {
this.loading = true;
console.log(this.script);
},
addActionArgument() {
this.action.args.push({
name: '',

View file

@ -65,6 +65,24 @@ export default {
}
},
async runScript(script, host) {
this.loading = true;
try {
if (typeof script === 'string') {
/* eslint no-eval: "off" */
script = eval(this.script);
}
return await script(this, host, browser, window);
} catch (e) {
this.notify(e.message, 'Script error');
throw e;
} finally {
this.loading = false;
}
},
async getHosts() {
this.loading = true;