2020-06-12 22:43:43 +02:00
|
|
|
import axios from 'axios';
|
2020-06-29 20:26:58 +02:00
|
|
|
import Vue from 'vue';
|
2020-07-04 00:53:14 +02:00
|
|
|
import _script from './script';
|
2020-06-12 22:43:43 +02:00
|
|
|
|
|
|
|
export default {
|
2020-06-13 17:28:50 +02:00
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
loading: false,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2020-06-12 22:43:43 +02:00
|
|
|
methods: {
|
2020-07-13 01:39:59 +02:00
|
|
|
getExtensionId() {
|
|
|
|
return browser.i18n.getMessage('@@extension_id');
|
|
|
|
},
|
|
|
|
|
2020-07-04 00:53:14 +02:00
|
|
|
async notify(message, title = 'platypush', error = false) {
|
|
|
|
let msg = '';
|
|
|
|
if (title && title.length) {
|
|
|
|
msg = `${title}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (message && message.length) {
|
|
|
|
if (msg.length > 0) {
|
|
|
|
msg += ': ';
|
|
|
|
}
|
|
|
|
msg += message;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg.length) {
|
|
|
|
if (error) {
|
|
|
|
console.error(msg);
|
|
|
|
} else {
|
|
|
|
console.log(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
await browser.notifications.create({
|
2020-06-12 22:43:43 +02:00
|
|
|
type: 'basic',
|
|
|
|
title: title,
|
|
|
|
message: message,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2020-06-24 01:17:58 +02:00
|
|
|
async getCurrentTab() {
|
2020-07-13 01:39:59 +02:00
|
|
|
let tabs = [];
|
|
|
|
|
|
|
|
try {
|
|
|
|
tabs = await browser.tabs.query({
|
|
|
|
currentWindow: true,
|
|
|
|
active: true,
|
|
|
|
});
|
|
|
|
} catch (e) {
|
|
|
|
console.warn('Could not get active tab', e);
|
|
|
|
return;
|
|
|
|
}
|
2020-06-24 01:17:58 +02:00
|
|
|
|
|
|
|
if (!tabs.length) {
|
2020-07-04 00:53:14 +02:00
|
|
|
await this.notify('No active tab', '', true);
|
2020-06-24 01:17:58 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return tabs[0];
|
|
|
|
},
|
|
|
|
|
|
|
|
async getURL() {
|
|
|
|
const tab = await this.getCurrentTab();
|
2020-07-13 01:39:59 +02:00
|
|
|
try {
|
|
|
|
return await browser.tabs.sendMessage(tab.id, { type: 'getURL' });
|
|
|
|
} catch (e) {
|
|
|
|
console.warn('Could not get URL', e);
|
|
|
|
}
|
2020-06-24 01:17:58 +02:00
|
|
|
},
|
|
|
|
|
2020-06-25 23:20:46 +02:00
|
|
|
async getDOM() {
|
2020-07-13 01:39:59 +02:00
|
|
|
try {
|
|
|
|
const tab = await this.getCurrentTab();
|
|
|
|
return await browser.tabs.sendMessage(tab.id, { type: 'getDOM' });
|
|
|
|
} catch (e) {
|
|
|
|
console.warn('Could not get DOM', e);
|
|
|
|
}
|
2020-06-24 01:17:58 +02:00
|
|
|
},
|
|
|
|
|
2020-06-25 23:20:46 +02:00
|
|
|
async setDOM(html) {
|
2020-06-24 01:17:58 +02:00
|
|
|
const tab = await this.getCurrentTab();
|
2020-07-13 01:39:59 +02:00
|
|
|
try {
|
|
|
|
await browser.tabs.sendMessage(tab.id, { type: 'setDOM', html: html });
|
|
|
|
} catch (e) {
|
|
|
|
console.warn('Could not set DOM', e);
|
|
|
|
}
|
2020-06-24 01:17:58 +02:00
|
|
|
},
|
|
|
|
|
2020-06-29 02:21:00 +02:00
|
|
|
async getTargetElement() {
|
|
|
|
const tab = await this.getCurrentTab();
|
2020-07-13 01:39:59 +02:00
|
|
|
let target = null;
|
|
|
|
try {
|
|
|
|
target = await browser.tabs.sendMessage(tab.id, { type: 'getTargetElement' });
|
|
|
|
} catch (e) {
|
|
|
|
console.warn('Could not get current element', e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-29 02:21:00 +02:00
|
|
|
if (!target) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new DOMParser().parseFromString(target, 'text/html').documentElement.getElementsByTagName('body')[0].firstChild;
|
|
|
|
},
|
|
|
|
|
|
|
|
async run(action, host, url) {
|
|
|
|
const execURL = (host.ssl ? 'https' : 'http') + '://' + host.address + ':' + host.port + '/execute';
|
2020-06-12 22:43:43 +02:00
|
|
|
const config = {};
|
2020-06-18 21:27:51 +02:00
|
|
|
let args = action.args || {};
|
2020-06-29 02:21:00 +02:00
|
|
|
let currentURL = url;
|
2020-06-24 01:17:58 +02:00
|
|
|
|
2020-06-29 02:21:00 +02:00
|
|
|
if (!url) {
|
|
|
|
try {
|
|
|
|
currentURL = await this.getURL();
|
|
|
|
} catch (e) {}
|
|
|
|
}
|
2020-06-24 01:17:58 +02:00
|
|
|
|
2020-06-12 22:43:43 +02:00
|
|
|
if (Array.isArray(action.args)) {
|
|
|
|
args = action.args
|
2020-07-13 01:39:59 +02:00
|
|
|
.filter(arg => arg.value != null && (typeof arg.value !== 'string' || arg.value.length))
|
2020-06-12 22:43:43 +02:00
|
|
|
.reduce((obj, arg) => {
|
|
|
|
obj[arg.name] = arg.value;
|
|
|
|
return obj;
|
|
|
|
}, {});
|
2020-06-30 11:58:26 +02:00
|
|
|
} else {
|
|
|
|
args = Object.entries(args)
|
2020-07-13 01:39:59 +02:00
|
|
|
.filter(([, value]) => value != null && (typeof value !== 'string' || value.length))
|
2020-06-30 11:58:26 +02:00
|
|
|
.reduce((obj, [name, value]) => {
|
|
|
|
obj[name] = value;
|
|
|
|
return obj;
|
|
|
|
}, {});
|
2020-06-12 22:43:43 +02:00
|
|
|
}
|
|
|
|
|
2020-06-14 02:34:35 +02:00
|
|
|
Object.keys(args).forEach(name => {
|
2020-07-04 00:53:14 +02:00
|
|
|
// URL wildcard
|
2020-06-14 02:34:35 +02:00
|
|
|
if (args[name] === '$URL$') {
|
2020-06-24 01:17:58 +02:00
|
|
|
if (!currentURL) {
|
|
|
|
console.warn('Unable to get the current URL');
|
|
|
|
} else {
|
|
|
|
args[name] = currentURL;
|
|
|
|
}
|
2020-06-14 02:34:35 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-06-12 22:43:43 +02:00
|
|
|
if (host.token && host.token.length) {
|
|
|
|
config.headers = {
|
|
|
|
'X-Token': host.token,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
const msg = await axios.post(
|
2020-06-29 02:21:00 +02:00
|
|
|
execURL,
|
2020-06-12 22:43:43 +02:00
|
|
|
{
|
|
|
|
type: 'request',
|
2020-07-05 00:56:03 +02:00
|
|
|
action: action.action,
|
2020-06-12 22:43:43 +02:00
|
|
|
args: args,
|
|
|
|
},
|
|
|
|
config
|
|
|
|
);
|
|
|
|
|
|
|
|
const errors = msg.data.response.errors;
|
|
|
|
if (errors && errors.length) {
|
2020-07-04 00:53:14 +02:00
|
|
|
// noinspection ExceptionCaughtLocallyJS
|
|
|
|
throw errors[0];
|
2020-06-12 22:43:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return msg.data.response.output;
|
|
|
|
} catch (e) {
|
2020-07-04 00:53:14 +02:00
|
|
|
await this.notify(e.toString(), 'Request error');
|
2020-06-12 22:43:43 +02:00
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
},
|
2020-06-13 17:28:50 +02:00
|
|
|
|
2020-07-04 00:53:14 +02:00
|
|
|
prepareScript(script, host, tab, target, ...args) {
|
|
|
|
args = JSON.stringify({
|
|
|
|
host: host,
|
|
|
|
tabId: tab ? tab.id : null,
|
|
|
|
target: typeof target === 'object' ? target.outerHTML : target,
|
|
|
|
...args,
|
|
|
|
});
|
|
|
|
|
|
|
|
return `(${script})(${_script.api}, ${args})`;
|
|
|
|
},
|
|
|
|
|
2020-06-29 02:21:00 +02:00
|
|
|
async runScript(script, host, tab, target, ...args) {
|
2020-06-19 00:26:39 +02:00
|
|
|
this.loading = true;
|
|
|
|
|
|
|
|
try {
|
2020-07-04 00:53:14 +02:00
|
|
|
if (!tab) {
|
|
|
|
tab = await this.getCurrentTab();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tab) {
|
|
|
|
return;
|
2020-06-19 00:26:39 +02:00
|
|
|
}
|
|
|
|
|
2020-07-04 00:53:14 +02:00
|
|
|
const code = this.prepareScript(script, host, tab, target, ...args);
|
|
|
|
return await browser.tabs.executeScript(tab.id, {
|
|
|
|
code: code,
|
|
|
|
});
|
2020-06-19 00:26:39 +02:00
|
|
|
} catch (e) {
|
2020-07-04 00:53:14 +02:00
|
|
|
await this.notify(e.message, 'Script error', true);
|
2020-06-19 00:26:39 +02:00
|
|
|
throw e;
|
|
|
|
} finally {
|
|
|
|
this.loading = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-06-15 22:33:06 +02:00
|
|
|
async getHosts() {
|
2020-06-13 17:28:50 +02:00
|
|
|
this.loading = true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
const response = await browser.storage.local.get('hosts');
|
2020-06-15 22:33:06 +02:00
|
|
|
if (!response.hosts) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-06-15 01:03:09 +02:00
|
|
|
return JSON.parse(response.hosts);
|
2020-06-13 17:28:50 +02:00
|
|
|
} finally {
|
|
|
|
this.loading = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-06-15 01:03:09 +02:00
|
|
|
async saveHosts(hosts) {
|
|
|
|
this.loading = true;
|
|
|
|
try {
|
|
|
|
await browser.storage.local.set({ hosts: JSON.stringify(hosts) });
|
|
|
|
} finally {
|
|
|
|
this.loading = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-06-15 22:33:06 +02:00
|
|
|
async getActions() {
|
2020-06-15 01:03:09 +02:00
|
|
|
this.loading = true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
const response = await browser.storage.local.get('actions');
|
2020-06-15 22:33:06 +02:00
|
|
|
if (!response.actions) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-06-15 01:03:09 +02:00
|
|
|
return JSON.parse(response.actions);
|
|
|
|
} finally {
|
|
|
|
this.loading = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-07-04 00:53:14 +02:00
|
|
|
async getScripts() {
|
2020-06-19 19:01:30 +02:00
|
|
|
this.loading = true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
const response = await browser.storage.local.get('scripts');
|
|
|
|
if (!response.scripts) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
return Object.entries(JSON.parse(response.scripts)).reduce((obj, [name, info]) => {
|
|
|
|
obj[name] = info;
|
|
|
|
return obj;
|
|
|
|
}, {});
|
|
|
|
} finally {
|
|
|
|
this.loading = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-06-15 22:33:06 +02:00
|
|
|
async saveActions(actions) {
|
|
|
|
this.loading = true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
await browser.storage.local.set({ actions: JSON.stringify(actions) });
|
|
|
|
} finally {
|
|
|
|
this.loading = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-06-15 01:03:09 +02:00
|
|
|
async saveAction(action) {
|
2020-06-15 22:33:06 +02:00
|
|
|
const actions = await this.getActions();
|
2020-06-15 01:03:09 +02:00
|
|
|
if (action.displayName in actions) {
|
|
|
|
if (!confirm('An action with this name already exists. Do you want to overwrite it?')) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
actions[action.displayName] = action;
|
2020-06-15 22:33:06 +02:00
|
|
|
await this.saveActions(actions);
|
2020-07-04 00:53:14 +02:00
|
|
|
await this.notify('You can find this action under the Local Actions menu', 'Action saved');
|
2020-06-15 01:03:09 +02:00
|
|
|
},
|
|
|
|
|
2020-06-19 19:01:30 +02:00
|
|
|
async saveScripts(scripts) {
|
|
|
|
this.loading = true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
scripts = Object.entries(scripts).reduce((obj, [name, info]) => {
|
|
|
|
if (typeof info.script === 'function') {
|
|
|
|
info.script = info.script.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
obj[name] = info;
|
|
|
|
return obj;
|
|
|
|
}, {});
|
|
|
|
|
|
|
|
await browser.storage.local.set({ scripts: JSON.stringify(scripts) });
|
|
|
|
} catch (e) {
|
2020-07-04 00:53:14 +02:00
|
|
|
await this.notify(e.message, 'Error on script save');
|
2020-06-19 19:01:30 +02:00
|
|
|
} finally {
|
|
|
|
this.loading = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
async saveScript(script) {
|
|
|
|
const scripts = await this.getScripts(false);
|
|
|
|
if (script.displayName in scripts) {
|
|
|
|
if (!confirm('A script with this name already exists. Do you want to overwrite it?')) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
scripts[script.displayName] = script;
|
|
|
|
await this.saveScripts(scripts);
|
2020-07-04 00:53:14 +02:00
|
|
|
await this.notify('You can find this script under the Local Actions menu', 'Script saved');
|
2020-06-19 19:01:30 +02:00
|
|
|
},
|
|
|
|
|
2020-07-13 01:39:59 +02:00
|
|
|
async getCommands() {
|
|
|
|
this.loading = true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
const response = await browser.storage.local.get('commands');
|
|
|
|
if (!response.commands) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
return JSON.parse(response.commands);
|
|
|
|
} finally {
|
|
|
|
this.loading = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
async saveCommands(commands) {
|
|
|
|
this.loading = true;
|
|
|
|
try {
|
|
|
|
await browser.storage.local.set({ commands: JSON.stringify(commands) });
|
|
|
|
} finally {
|
|
|
|
this.loading = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
async saveCommand(command, action) {
|
|
|
|
const commands = await this.getCommands();
|
|
|
|
if (command in commands) {
|
|
|
|
if (action === commands[command]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!confirm(`The action ${commands[command]} is already linked to this key binding. Do you want to overwrite it?`)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Object.entries(commands).forEach(([cmd, act]) => {
|
|
|
|
if (act === action) {
|
|
|
|
delete commands[cmd];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
commands[command] = action;
|
|
|
|
await this.saveCommands(commands);
|
|
|
|
},
|
|
|
|
|
|
|
|
async removeCommand(command, action) {
|
|
|
|
const commands = await this.getCommands();
|
|
|
|
if (!(command in commands) || !confirm('Are you sure that you want to remove this key binding?')) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete commands[command];
|
|
|
|
await this.saveCommands(commands);
|
|
|
|
},
|
|
|
|
|
2020-06-15 01:03:09 +02:00
|
|
|
async loadConfig() {
|
2020-06-16 01:43:04 +02:00
|
|
|
this.loading = true;
|
|
|
|
|
|
|
|
try {
|
2020-07-13 01:39:59 +02:00
|
|
|
const [hosts, actions, scripts, commands] = await Promise.all([this.getHosts(), this.getActions(), this.getScripts(false), this.getCommands()]);
|
2020-06-16 01:43:04 +02:00
|
|
|
return {
|
|
|
|
hosts: hosts,
|
|
|
|
actions: actions,
|
2020-06-19 19:01:30 +02:00
|
|
|
scripts: scripts,
|
2020-07-13 01:39:59 +02:00
|
|
|
commands: commands,
|
2020-06-16 01:43:04 +02:00
|
|
|
};
|
|
|
|
} finally {
|
|
|
|
this.loading = false;
|
2020-06-16 18:46:38 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
async saveConfig(config) {
|
|
|
|
this.loading = true;
|
|
|
|
const hosts = config.hosts || {};
|
|
|
|
const actions = config.actions || {};
|
2020-06-19 19:01:30 +02:00
|
|
|
const scripts = config.scripts || {};
|
2020-07-13 01:39:59 +02:00
|
|
|
const commands = config.commands || {};
|
2020-06-16 18:46:38 +02:00
|
|
|
|
|
|
|
try {
|
2020-07-13 01:39:59 +02:00
|
|
|
await Promise.all([this.saveHosts(hosts), this.saveActions(actions), this.saveScripts(scripts), this.saveCommands(commands)]);
|
2020-06-16 18:46:38 +02:00
|
|
|
} finally {
|
|
|
|
this.loading = false;
|
2020-06-16 01:43:04 +02:00
|
|
|
}
|
2020-06-13 17:28:50 +02:00
|
|
|
},
|
|
|
|
|
2020-06-30 11:58:26 +02:00
|
|
|
async backupConfig(host) {
|
|
|
|
if (typeof host === 'string') {
|
|
|
|
const hosts = await this.getHosts();
|
|
|
|
if (!(host in hosts)) {
|
2020-07-04 00:53:14 +02:00
|
|
|
await this.notify(host, 'No such Platypush host');
|
2020-06-30 11:58:26 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
host = hosts[host];
|
|
|
|
}
|
|
|
|
|
|
|
|
this.loading = true;
|
|
|
|
const config = JSON.stringify(await this.loadConfig());
|
|
|
|
const basedir = `\${Config.get("workdir")}/webext`;
|
|
|
|
const filename = `${basedir}/config.json`;
|
|
|
|
|
|
|
|
try {
|
|
|
|
await this.run(
|
|
|
|
{
|
2020-07-05 00:56:03 +02:00
|
|
|
action: 'file.mkdir',
|
2020-06-30 11:58:26 +02:00
|
|
|
args: { directory: basedir },
|
|
|
|
},
|
|
|
|
host
|
|
|
|
);
|
|
|
|
|
|
|
|
await this.run(
|
|
|
|
{
|
2020-07-05 00:56:03 +02:00
|
|
|
action: 'file.write',
|
2020-06-30 11:58:26 +02:00
|
|
|
args: {
|
|
|
|
file: filename,
|
|
|
|
content: config,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
host
|
|
|
|
);
|
|
|
|
|
2020-07-04 00:53:14 +02:00
|
|
|
await this.notify(`Configuration successfully backed up to ${host.name}`, 'Backup successful');
|
2020-06-30 11:58:26 +02:00
|
|
|
} finally {
|
|
|
|
this.loading = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
async getConfig(host) {
|
|
|
|
if (typeof host === 'string') {
|
|
|
|
const hosts = await this.getHosts();
|
|
|
|
if (!(host in hosts)) {
|
2020-07-04 00:53:14 +02:00
|
|
|
await this.notify(host, 'No such Platypush host');
|
2020-06-30 11:58:26 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
host = hosts[host];
|
|
|
|
}
|
|
|
|
|
|
|
|
this.loading = true;
|
|
|
|
const basedir = `\${Config.get("workdir")}/webext`;
|
|
|
|
const filename = `${basedir}/config.json`;
|
|
|
|
|
|
|
|
try {
|
2020-07-04 00:53:14 +02:00
|
|
|
return await this.run(
|
2020-06-30 11:58:26 +02:00
|
|
|
{
|
2020-07-05 00:56:03 +02:00
|
|
|
action: 'file.read',
|
2020-06-30 11:58:26 +02:00
|
|
|
args: { file: filename },
|
|
|
|
},
|
|
|
|
host
|
|
|
|
);
|
|
|
|
} finally {
|
|
|
|
this.loading = false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2020-06-13 17:28:50 +02:00
|
|
|
formToHost(form) {
|
|
|
|
return {
|
|
|
|
name: form.name.value,
|
|
|
|
address: form.address.value,
|
|
|
|
port: parseInt(form.port.value),
|
|
|
|
websocketPort: parseInt(form.websocketPort.value),
|
|
|
|
ssl: form.ssl.checked,
|
|
|
|
token: form.token.value,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
onAddrChange(form) {
|
|
|
|
if (form.name.value.length && !form.address.value.startsWith(form.name.value)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
form.name.value = form.address.value;
|
|
|
|
},
|
|
|
|
|
|
|
|
onPortChange(form) {
|
|
|
|
const port = form.port.value;
|
|
|
|
if (!this.isPortValid(port)) return;
|
|
|
|
form.websocketPort.value = '' + (parseInt(port) + 1);
|
|
|
|
},
|
|
|
|
|
|
|
|
isPortValid(port) {
|
|
|
|
port = parseInt(port);
|
|
|
|
return !isNaN(port) && port > 0 && port < 65536;
|
|
|
|
},
|
|
|
|
|
|
|
|
isHostFormValid(form) {
|
|
|
|
return form.name.value.length && form.address.value.length && this.isPortValid(form.port.value) && this.isPortValid(form.websocketPort.value);
|
|
|
|
},
|
2020-06-12 22:43:43 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2020-06-29 20:26:58 +02:00
|
|
|
export const bus = new Vue();
|
|
|
|
|
2020-06-12 22:43:43 +02:00
|
|
|
// vim:sw=2:ts=2:et:
|