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==", "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==",
"dev": true "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": { "cliui": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
@ -2450,6 +2461,16 @@
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
"dev": true "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": { "compress-commons": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz",
@ -2952,6 +2973,12 @@
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"dev": true "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": { "delegates": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@ -3011,6 +3038,15 @@
"esutils": "^2.0.2" "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": { "domain-browser": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
@ -3198,6 +3234,11 @@
"is-symbol": "^1.0.2" "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": { "escape-string-regexp": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@ -4330,6 +4371,15 @@
"minimatch": "~3.0.2" "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": { "graceful-fs": {
"version": "4.2.4", "version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
@ -4987,8 +5037,7 @@
"is-extendable": { "is-extendable": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
"dev": true
}, },
"is-extglob": { "is-extglob": {
"version": "2.1.1", "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": { "private": {
"version": "0.1.8", "version": "0.1.8",
"resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", "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": { "semver": {
"version": "5.7.1", "version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@ -8226,6 +8289,12 @@
"setimmediate": "^1.0.4" "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": { "tmp": {
"version": "0.0.33", "version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@ -8385,6 +8454,24 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true "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": { "unicode-canonical-property-names-ecmascript": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", "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-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": { "vue-style-loader": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz", "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz",

View file

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

View file

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