|
@@ -1,130 +1,95 @@
|
|
-/**
|
|
|
|
- * All functions to be injected into web page must be independent.
|
|
|
|
- * They must be assigned to `bridge` so that they can be serialized.
|
|
|
|
- */
|
|
|
|
-import base from './bridge';
|
|
|
|
|
|
+import { getUniqId, bindEvents } from '../utils';
|
|
|
|
+import { includes, forEach, map, utf8decode } from './helpers';
|
|
|
|
+import bridge from './bridge';
|
|
|
|
+import { onRequestCreate, onRequestStart, onRequestCallback } from './requests';
|
|
|
|
+import {
|
|
|
|
+ onNotificationCreate,
|
|
|
|
+ onNotificationClicked,
|
|
|
|
+ onNotificationClosed,
|
|
|
|
+} from './notifications';
|
|
|
|
+import { onTabCreate, onTabClosed } from './tabs';
|
|
|
|
|
|
-export default Object.assign({}, base, {
|
|
|
|
- utf8decode,
|
|
|
|
- getRequest,
|
|
|
|
- getTab,
|
|
|
|
- wrapGM,
|
|
|
|
- getWrapper,
|
|
|
|
- onLoadScripts,
|
|
|
|
- exposeVM,
|
|
|
|
- runCode,
|
|
|
|
- initialize,
|
|
|
|
- state: 0,
|
|
|
|
- handle: handleWeb,
|
|
|
|
-});
|
|
|
|
|
|
+let state = 0;
|
|
|
|
|
|
-/**
|
|
|
|
- * http://www.webtoolkit.info/javascript-utf8.html
|
|
|
|
- */
|
|
|
|
-function utf8decode(utftext) {
|
|
|
|
- /* eslint-disable no-bitwise */
|
|
|
|
- let string = '';
|
|
|
|
- let i = 0;
|
|
|
|
- let c1 = 0;
|
|
|
|
- let c2 = 0;
|
|
|
|
- let c3 = 0;
|
|
|
|
- while (i < utftext.length) {
|
|
|
|
- c1 = utftext.charCodeAt(i);
|
|
|
|
- if (c1 < 128) {
|
|
|
|
- string += String.fromCharCode(c1);
|
|
|
|
- i += 1;
|
|
|
|
- } else if (c1 > 191 && c1 < 224) {
|
|
|
|
- c2 = utftext.charCodeAt(i + 1);
|
|
|
|
- string += String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
|
|
|
|
- i += 2;
|
|
|
|
- } else {
|
|
|
|
- c2 = utftext.charCodeAt(i + 1);
|
|
|
|
- c3 = utftext.charCodeAt(i + 2);
|
|
|
|
- string += String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
|
|
|
|
- i += 3;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return string;
|
|
|
|
- /* eslint-enable no-bitwise */
|
|
|
|
|
|
+export default function initialize(webId, contentId, props) {
|
|
|
|
+ bridge.props = props;
|
|
|
|
+ bridge.post = bindEvents(webId, contentId, onHandle);
|
|
|
|
+ document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
+ state = 1;
|
|
|
|
+ bridge.load();
|
|
|
|
+ }, false);
|
|
|
|
+ bridge.checkLoad();
|
|
}
|
|
}
|
|
|
|
|
|
-function exposeVM() {
|
|
|
|
- const bridge = this;
|
|
|
|
- const Violentmonkey = {};
|
|
|
|
- const checking = {};
|
|
|
|
- let key = 0;
|
|
|
|
- bridge.onScriptChecked = ({ callback, result }) => {
|
|
|
|
- const cb = checking[callback];
|
|
|
|
- if (cb) {
|
|
|
|
- cb(result);
|
|
|
|
- delete checking[callback];
|
|
|
|
- }
|
|
|
|
- };
|
|
|
|
- Object.defineProperty(Violentmonkey, 'getVersion', {
|
|
|
|
- value() {
|
|
|
|
- return Promise.resolve({
|
|
|
|
- version: bridge.version,
|
|
|
|
- });
|
|
|
|
- },
|
|
|
|
- });
|
|
|
|
- Object.defineProperty(Violentmonkey, 'isInstalled', {
|
|
|
|
- value(name, namespace) {
|
|
|
|
- return new Promise(resolve => {
|
|
|
|
- key += 1;
|
|
|
|
- const callback = checking[key];
|
|
|
|
- checking[callback] = resolve;
|
|
|
|
- bridge.post({
|
|
|
|
- cmd: 'CheckScript',
|
|
|
|
- data: {
|
|
|
|
- name,
|
|
|
|
- namespace,
|
|
|
|
- callback,
|
|
|
|
- },
|
|
|
|
- });
|
|
|
|
- });
|
|
|
|
- },
|
|
|
|
- });
|
|
|
|
- Object.defineProperty(window.external, 'Violentmonkey', {
|
|
|
|
- value: Violentmonkey,
|
|
|
|
- });
|
|
|
|
|
|
+const commands = {};
|
|
|
|
+const ainject = {};
|
|
|
|
+const values = {};
|
|
|
|
+
|
|
|
|
+const handlers = {
|
|
|
|
+ LoadScripts: onLoadScripts,
|
|
|
|
+ Command(data) {
|
|
|
|
+ const func = commands[data];
|
|
|
|
+ if (func) func();
|
|
|
|
+ },
|
|
|
|
+ GotRequestId: onRequestStart,
|
|
|
|
+ HttpRequested: onRequestCallback,
|
|
|
|
+ TabClosed: onTabClosed,
|
|
|
|
+ UpdateValues(data) {
|
|
|
|
+ if (values[data.uri]) values[data.uri] = data.values;
|
|
|
|
+ },
|
|
|
|
+ NotificationClicked: onNotificationClicked,
|
|
|
|
+ NotificationClosed: onNotificationClosed,
|
|
|
|
+ // advanced inject
|
|
|
|
+ Injected(id) {
|
|
|
|
+ const item = ainject[id];
|
|
|
|
+ const func = window[`VM_${id}`];
|
|
|
|
+ delete window[`VM_${id}`];
|
|
|
|
+ delete ainject[id];
|
|
|
|
+ if (item && func) runCode(item[0], func, item[1], item[2]);
|
|
|
|
+ },
|
|
|
|
+ ScriptChecked(data) {
|
|
|
|
+ if (bridge.onScriptChecked) bridge.onScriptChecked(data);
|
|
|
|
+ },
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+function onHandle(obj) {
|
|
|
|
+ const handle = handlers[obj.cmd];
|
|
|
|
+ if (handle) handle(obj.data);
|
|
}
|
|
}
|
|
|
|
|
|
function onLoadScripts(data) {
|
|
function onLoadScripts(data) {
|
|
- const bridge = this;
|
|
|
|
const start = [];
|
|
const start = [];
|
|
const idle = [];
|
|
const idle = [];
|
|
const end = [];
|
|
const end = [];
|
|
- bridge.command = {};
|
|
|
|
- bridge.notif = {};
|
|
|
|
- bridge.ainject = {};
|
|
|
|
bridge.version = data.version;
|
|
bridge.version = data.version;
|
|
- const { helpers } = bridge;
|
|
|
|
- if (helpers.includes([
|
|
|
|
|
|
+ if (includes([
|
|
'greasyfork.org',
|
|
'greasyfork.org',
|
|
], location.host)) {
|
|
], location.host)) {
|
|
- bridge.exposeVM();
|
|
|
|
|
|
+ exposeVM();
|
|
}
|
|
}
|
|
- bridge.values = {};
|
|
|
|
// reset load and checkLoad
|
|
// reset load and checkLoad
|
|
bridge.load = () => {
|
|
bridge.load = () => {
|
|
run(end);
|
|
run(end);
|
|
setTimeout(run, 0, idle);
|
|
setTimeout(run, 0, idle);
|
|
};
|
|
};
|
|
bridge.checkLoad = () => {
|
|
bridge.checkLoad = () => {
|
|
- if (!bridge.state && helpers.includes(['interactive', 'complete'], document.readyState)) bridge.state = 1;
|
|
|
|
- if (bridge.state) bridge.load();
|
|
|
|
|
|
+ if (!state && includes(['interactive', 'complete'], document.readyState)) {
|
|
|
|
+ state = 1;
|
|
|
|
+ }
|
|
|
|
+ if (state) bridge.load();
|
|
};
|
|
};
|
|
const listMap = {
|
|
const listMap = {
|
|
'document-start': start,
|
|
'document-start': start,
|
|
'document-idle': idle,
|
|
'document-idle': idle,
|
|
'document-end': end,
|
|
'document-end': end,
|
|
};
|
|
};
|
|
- helpers.forEach(data.scripts, script => {
|
|
|
|
- bridge.values[script.uri] = data.values[script.uri] || {};
|
|
|
|
|
|
+ forEach(data.scripts, script => {
|
|
|
|
+ values[script.uri] = data.values[script.uri] || {};
|
|
if (script && script.enabled) {
|
|
if (script && script.enabled) {
|
|
const list = listMap[
|
|
const list = listMap[
|
|
- script.custom.runAt || script.custom['run-at']
|
|
|
|
- || script.meta.runAt || script.meta['run-at']
|
|
|
|
|
|
+ // XXX: use camelCase since v2.6.3
|
|
|
|
+ script.custom.runAt || script.custom['run-at'] ||
|
|
|
|
+ script.meta.runAt || script.meta['run-at']
|
|
] || end;
|
|
] || end;
|
|
list.push(script);
|
|
list.push(script);
|
|
}
|
|
}
|
|
@@ -133,12 +98,12 @@ function onLoadScripts(data) {
|
|
bridge.checkLoad();
|
|
bridge.checkLoad();
|
|
function buildCode(script) {
|
|
function buildCode(script) {
|
|
const requireKeys = script.meta.require || [];
|
|
const requireKeys = script.meta.require || [];
|
|
- const wrapper = bridge.wrapGM(script, data.cache);
|
|
|
|
|
|
+ const wrapper = wrapGM(script, data.cache);
|
|
// Must use Object.getOwnPropertyNames to list unenumerable properties
|
|
// Must use Object.getOwnPropertyNames to list unenumerable properties
|
|
const wrapperKeys = Object.getOwnPropertyNames(wrapper);
|
|
const wrapperKeys = Object.getOwnPropertyNames(wrapper);
|
|
- const wrapperInit = helpers.map(wrapperKeys, name => `this["${name}"]=${name}`).join(';');
|
|
|
|
|
|
+ const wrapperInit = map(wrapperKeys, name => `this["${name}"]=${name}`).join(';');
|
|
const codeSlices = [`${wrapperInit};with(this)!function(){`];
|
|
const codeSlices = [`${wrapperInit};with(this)!function(){`];
|
|
- helpers.forEach(requireKeys, key => {
|
|
|
|
|
|
+ forEach(requireKeys, key => {
|
|
const requireCode = data.require[key];
|
|
const requireCode = data.require[key];
|
|
if (requireCode) {
|
|
if (requireCode) {
|
|
codeSlices.push(requireCode);
|
|
codeSlices.push(requireCode);
|
|
@@ -151,10 +116,10 @@ function onLoadScripts(data) {
|
|
codeSlices.push('}.call(this);');
|
|
codeSlices.push('}.call(this);');
|
|
const code = codeSlices.join('\n');
|
|
const code = codeSlices.join('\n');
|
|
const name = script.custom.name || script.meta.name || script.id;
|
|
const name = script.custom.name || script.meta.name || script.id;
|
|
- const args = helpers.map(wrapperKeys, key => wrapper[key]);
|
|
|
|
|
|
+ const args = map(wrapperKeys, key => wrapper[key]);
|
|
const thisObj = wrapper.window || wrapper;
|
|
const thisObj = wrapper.window || wrapper;
|
|
- const id = bridge.getUniqId();
|
|
|
|
- bridge.ainject[id] = [name, args, thisObj];
|
|
|
|
|
|
+ const id = getUniqId();
|
|
|
|
+ ainject[id] = [name, args, thisObj];
|
|
bridge.post({ cmd: 'Inject', data: [id, wrapperKeys, code] });
|
|
bridge.post({ cmd: 'Inject', data: [id, wrapperKeys, code] });
|
|
}
|
|
}
|
|
function run(list) {
|
|
function run(list) {
|
|
@@ -162,207 +127,9 @@ function onLoadScripts(data) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-function handleWeb(obj) {
|
|
|
|
- const bridge = this;
|
|
|
|
- const handlers = {
|
|
|
|
- LoadScripts(data) {
|
|
|
|
- bridge.onLoadScripts(data);
|
|
|
|
- },
|
|
|
|
- Command(data) {
|
|
|
|
- const func = bridge.command[data];
|
|
|
|
- if (func) func();
|
|
|
|
- },
|
|
|
|
- GotRequestId(id) {
|
|
|
|
- const req = bridge.requests.queue.shift();
|
|
|
|
- req.start(req, id);
|
|
|
|
- },
|
|
|
|
- HttpRequested(res) {
|
|
|
|
- const req = bridge.requests.map[res.id];
|
|
|
|
- if (req) req.callback(req, res);
|
|
|
|
- },
|
|
|
|
- TabClosed(key) {
|
|
|
|
- const item = bridge.tabs[key];
|
|
|
|
- if (item) {
|
|
|
|
- item.closed = true;
|
|
|
|
- const { onclose } = item;
|
|
|
|
- if (onclose) onclose();
|
|
|
|
- delete bridge.tabs[key];
|
|
|
|
- }
|
|
|
|
- },
|
|
|
|
- UpdateValues(data) {
|
|
|
|
- const { values } = bridge;
|
|
|
|
- if (values && values[data.uri]) values[data.uri] = data.values;
|
|
|
|
- },
|
|
|
|
- NotificationClicked(id) {
|
|
|
|
- const options = bridge.notif[id];
|
|
|
|
- if (options) {
|
|
|
|
- const { onclick } = options;
|
|
|
|
- if (onclick) onclick();
|
|
|
|
- }
|
|
|
|
- },
|
|
|
|
- NotificationClosed(id) {
|
|
|
|
- const options = bridge.notif[id];
|
|
|
|
- if (options) {
|
|
|
|
- delete bridge.notif[id];
|
|
|
|
- const { ondone } = options;
|
|
|
|
- if (ondone) ondone();
|
|
|
|
- }
|
|
|
|
- },
|
|
|
|
- // advanced inject
|
|
|
|
- Injected(id) {
|
|
|
|
- const item = bridge.ainject[id];
|
|
|
|
- const func = window[`VM_${id}`];
|
|
|
|
- delete window[`VM_${id}`];
|
|
|
|
- delete bridge.ainject[id];
|
|
|
|
- if (item && func) bridge.runCode(item[0], func, item[1], item[2]);
|
|
|
|
- },
|
|
|
|
- ScriptChecked(data) {
|
|
|
|
- if (bridge.onScriptChecked) bridge.onScriptChecked(data);
|
|
|
|
- },
|
|
|
|
- };
|
|
|
|
- const handle = handlers[obj.cmd];
|
|
|
|
- if (handle) handle(obj.data);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-function runCode(name, func, args, thisObj) {
|
|
|
|
- if (process.env.DEBUG) {
|
|
|
|
- console.log(`Run script: ${name}`); // eslint-disable-line no-console
|
|
|
|
- }
|
|
|
|
- try {
|
|
|
|
- func.apply(thisObj, args);
|
|
|
|
- } catch (e) {
|
|
|
|
- let msg = `Error running script: ${name}\n${e}`;
|
|
|
|
- if (e.message) msg = `${msg}\n${e.message}`;
|
|
|
|
- console.error(msg);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-function getRequest(arg) {
|
|
|
|
- const bridge = this;
|
|
|
|
- const { helpers } = bridge;
|
|
|
|
- init();
|
|
|
|
- return bridge.getRequest(arg);
|
|
|
|
- function init() {
|
|
|
|
- bridge.requests = {
|
|
|
|
- map: {},
|
|
|
|
- queue: [],
|
|
|
|
- };
|
|
|
|
- bridge.getRequest = details => {
|
|
|
|
- const req = {
|
|
|
|
- details,
|
|
|
|
- callback,
|
|
|
|
- start,
|
|
|
|
- req: {
|
|
|
|
- abort: reqAbort,
|
|
|
|
- },
|
|
|
|
- };
|
|
|
|
- details.url = getFullUrl(details.url);
|
|
|
|
- bridge.requests.queue.push(req);
|
|
|
|
- bridge.post({ cmd: 'GetRequestId' });
|
|
|
|
- return req.req;
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
- function reqAbort() {
|
|
|
|
- bridge.post({ cmd: 'AbortRequest', data: this.id });
|
|
|
|
- }
|
|
|
|
- function parseData(req, details) {
|
|
|
|
- if (req.resType) {
|
|
|
|
- // blob or arraybuffer
|
|
|
|
- let data = req.data.response.split(',');
|
|
|
|
- const mimetype = data[0].match(/^data:(.*?);base64$/);
|
|
|
|
- if (!mimetype) {
|
|
|
|
- // invalid
|
|
|
|
- req.data.response = null;
|
|
|
|
- } else {
|
|
|
|
- data = window.atob(data[1]);
|
|
|
|
- const arr = new window.Uint8Array(data.length);
|
|
|
|
- for (let i = 0; i < data.length; i += 1) arr[i] = data.charCodeAt(i);
|
|
|
|
- if (details.responseType === 'blob') {
|
|
|
|
- // blob
|
|
|
|
- return new Blob([arr], { type: mimetype });
|
|
|
|
- }
|
|
|
|
- // arraybuffer
|
|
|
|
- return arr.buffer;
|
|
|
|
- }
|
|
|
|
- } else if (details.responseType === 'json') {
|
|
|
|
- // json
|
|
|
|
- return JSON.parse(req.data.response);
|
|
|
|
- } else {
|
|
|
|
- // text
|
|
|
|
- return req.data.response;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- // request object functions
|
|
|
|
- function callback(req, res) {
|
|
|
|
- const cb = req.details[`on${res.type}`];
|
|
|
|
- if (cb) {
|
|
|
|
- if (res.data.response) {
|
|
|
|
- if (!req.data) req.data = [parseData(res, req.details)];
|
|
|
|
- res.data.response = req.data[0];
|
|
|
|
- }
|
|
|
|
- res.data.context = req.details.context;
|
|
|
|
- cb(res.data);
|
|
|
|
- }
|
|
|
|
- if (res.type === 'loadend') delete bridge.requests.map[req.id];
|
|
|
|
- }
|
|
|
|
- function start(req, id) {
|
|
|
|
- const { details } = req;
|
|
|
|
- const payload = {
|
|
|
|
- id,
|
|
|
|
- method: details.method,
|
|
|
|
- url: details.url,
|
|
|
|
- user: details.user,
|
|
|
|
- password: details.password,
|
|
|
|
- headers: details.headers,
|
|
|
|
- overrideMimeType: details.overrideMimeType,
|
|
|
|
- };
|
|
|
|
- req.id = id;
|
|
|
|
- bridge.requests.map[id] = req;
|
|
|
|
- if (helpers.includes(['arraybuffer', 'blob'], details.responseType)) {
|
|
|
|
- payload.responseType = 'blob';
|
|
|
|
- }
|
|
|
|
- helpers.encodeBody(details.data)
|
|
|
|
- .then(body => {
|
|
|
|
- payload.data = body;
|
|
|
|
- bridge.post({
|
|
|
|
- cmd: 'HttpRequest',
|
|
|
|
- data: payload,
|
|
|
|
- });
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- function getFullUrl(url) {
|
|
|
|
- const a = document.createElement('a');
|
|
|
|
- a.setAttribute('href', url);
|
|
|
|
- return a.href;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-function getTab(detail) {
|
|
|
|
- const bridge = this;
|
|
|
|
- init();
|
|
|
|
- return bridge.getTab(detail);
|
|
|
|
- function init() {
|
|
|
|
- bridge.tabs = {};
|
|
|
|
- bridge.getTab = data => {
|
|
|
|
- const key = bridge.getUniqId();
|
|
|
|
- const item = {
|
|
|
|
- close() {
|
|
|
|
- bridge.post({ cmd: 'TabClose', data: key });
|
|
|
|
- },
|
|
|
|
- onclose: null,
|
|
|
|
- closed: false,
|
|
|
|
- };
|
|
|
|
- bridge.tabs[key] = item;
|
|
|
|
- bridge.post({ cmd: 'TabOpen', data: { key, data } });
|
|
|
|
- return item;
|
|
|
|
- };
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
function wrapGM(script, cache) {
|
|
function wrapGM(script, cache) {
|
|
// Add GM functions
|
|
// Add GM functions
|
|
// Reference: http://wiki.greasespot.net/Greasemonkey_Manual:API
|
|
// Reference: http://wiki.greasespot.net/Greasemonkey_Manual:API
|
|
- const bridge = this;
|
|
|
|
const gm = {};
|
|
const gm = {};
|
|
const grant = script.meta.grant || [];
|
|
const grant = script.meta.grant || [];
|
|
const urls = {};
|
|
const urls = {};
|
|
@@ -370,12 +137,11 @@ function wrapGM(script, cache) {
|
|
// @grant none
|
|
// @grant none
|
|
grant.pop();
|
|
grant.pop();
|
|
} else {
|
|
} else {
|
|
- gm.window = bridge.getWrapper();
|
|
|
|
|
|
+ gm.window = getWrapper();
|
|
}
|
|
}
|
|
- const { helpers } = bridge;
|
|
|
|
- if (!helpers.includes(grant, 'unsafeWindow')) grant.push('unsafeWindow');
|
|
|
|
- if (!helpers.includes(grant, 'GM_info')) grant.push('GM_info');
|
|
|
|
- if (helpers.includes(grant, 'window.close')) gm.window.close = () => { bridge.post({ cmd: 'TabClose' }); };
|
|
|
|
|
|
+ if (!includes(grant, 'unsafeWindow')) grant.push('unsafeWindow');
|
|
|
|
+ if (!includes(grant, 'GM_info')) grant.push('GM_info');
|
|
|
|
+ if (includes(grant, 'window.close')) gm.window.close = () => { bridge.post({ cmd: 'TabClose' }); };
|
|
const resources = script.meta.resources || {};
|
|
const resources = script.meta.resources || {};
|
|
const dataEncoders = {
|
|
const dataEncoders = {
|
|
o: val => JSON.stringify(val),
|
|
o: val => JSON.stringify(val),
|
|
@@ -418,25 +184,25 @@ function wrapGM(script, cache) {
|
|
},
|
|
},
|
|
GM_deleteValue: {
|
|
GM_deleteValue: {
|
|
value(key) {
|
|
value(key) {
|
|
- const values = getValues();
|
|
|
|
- delete values[key];
|
|
|
|
|
|
+ const value = getValues();
|
|
|
|
+ delete value[key];
|
|
saveValues();
|
|
saveValues();
|
|
},
|
|
},
|
|
},
|
|
},
|
|
GM_getValue: {
|
|
GM_getValue: {
|
|
value(key, def) {
|
|
value(key, def) {
|
|
- const values = getValues();
|
|
|
|
- const raw = values[key];
|
|
|
|
|
|
+ const value = getValues();
|
|
|
|
+ const raw = value[key];
|
|
if (raw) {
|
|
if (raw) {
|
|
const type = raw[0];
|
|
const type = raw[0];
|
|
const handle = dataDecoders[type] || dataDecoders[''];
|
|
const handle = dataDecoders[type] || dataDecoders[''];
|
|
- let value = raw.slice(1);
|
|
|
|
|
|
+ let val = raw.slice(1);
|
|
try {
|
|
try {
|
|
- value = handle(value);
|
|
|
|
|
|
+ val = handle(val);
|
|
} catch (e) {
|
|
} catch (e) {
|
|
console.warn(e);
|
|
console.warn(e);
|
|
}
|
|
}
|
|
- return value;
|
|
|
|
|
|
+ return val;
|
|
}
|
|
}
|
|
return def;
|
|
return def;
|
|
},
|
|
},
|
|
@@ -450,10 +216,9 @@ function wrapGM(script, cache) {
|
|
value(key, val) {
|
|
value(key, val) {
|
|
const type = (typeof val)[0];
|
|
const type = (typeof val)[0];
|
|
const handle = dataEncoders[type] || dataEncoders[''];
|
|
const handle = dataEncoders[type] || dataEncoders[''];
|
|
- let value = val;
|
|
|
|
- value = type + handle(value);
|
|
|
|
- const values = getValues();
|
|
|
|
- values[key] = value;
|
|
|
|
|
|
+ const raw = type + handle(val);
|
|
|
|
+ const value = getValues();
|
|
|
|
+ value[key] = raw;
|
|
saveValues();
|
|
saveValues();
|
|
},
|
|
},
|
|
},
|
|
},
|
|
@@ -508,19 +273,17 @@ function wrapGM(script, cache) {
|
|
active: !options,
|
|
active: !options,
|
|
};
|
|
};
|
|
data.url = url;
|
|
data.url = url;
|
|
- return bridge.getTab(data);
|
|
|
|
|
|
+ return onTabCreate(data);
|
|
},
|
|
},
|
|
},
|
|
},
|
|
GM_registerMenuCommand: {
|
|
GM_registerMenuCommand: {
|
|
value(cap, func, acc) {
|
|
value(cap, func, acc) {
|
|
- bridge.command[cap] = func;
|
|
|
|
|
|
+ commands[cap] = func;
|
|
bridge.post({ cmd: 'RegisterMenu', data: [cap, acc] });
|
|
bridge.post({ cmd: 'RegisterMenu', data: [cap, acc] });
|
|
},
|
|
},
|
|
},
|
|
},
|
|
GM_xmlhttpRequest: {
|
|
GM_xmlhttpRequest: {
|
|
- value(details) {
|
|
|
|
- return bridge.getRequest(details);
|
|
|
|
- },
|
|
|
|
|
|
+ value: onRequestCreate,
|
|
},
|
|
},
|
|
GM_notification: {
|
|
GM_notification: {
|
|
value(text, title, image, onclick) {
|
|
value(text, title, image, onclick) {
|
|
@@ -533,18 +296,7 @@ function wrapGM(script, cache) {
|
|
if (!options.text) {
|
|
if (!options.text) {
|
|
throw new Error('GM_notification: `text` is required!');
|
|
throw new Error('GM_notification: `text` is required!');
|
|
}
|
|
}
|
|
- const id = (bridge.notif[''] || 0) + 1;
|
|
|
|
- bridge.notif[''] = id;
|
|
|
|
- bridge.notif[id] = options;
|
|
|
|
- bridge.post({
|
|
|
|
- cmd: 'Notification',
|
|
|
|
- data: {
|
|
|
|
- id,
|
|
|
|
- text: options.text,
|
|
|
|
- title: options.title,
|
|
|
|
- image: options.image,
|
|
|
|
- },
|
|
|
|
- });
|
|
|
|
|
|
+ onNotificationCreate(options);
|
|
},
|
|
},
|
|
},
|
|
},
|
|
GM_setClipboard: {
|
|
GM_setClipboard: {
|
|
@@ -556,13 +308,13 @@ function wrapGM(script, cache) {
|
|
},
|
|
},
|
|
},
|
|
},
|
|
};
|
|
};
|
|
- helpers.forEach(grant, name => {
|
|
|
|
|
|
+ forEach(grant, name => {
|
|
const prop = gmFunctions[name];
|
|
const prop = gmFunctions[name];
|
|
if (prop) addProperty(name, prop, gm);
|
|
if (prop) addProperty(name, prop, gm);
|
|
});
|
|
});
|
|
return gm;
|
|
return gm;
|
|
function getValues() {
|
|
function getValues() {
|
|
- return bridge.values[script.uri];
|
|
|
|
|
|
+ return values[script.uri];
|
|
}
|
|
}
|
|
function propertyToString() {
|
|
function propertyToString() {
|
|
return '[Violentmonkey property]';
|
|
return '[Violentmonkey property]';
|
|
@@ -590,16 +342,14 @@ function wrapGM(script, cache) {
|
|
function getWrapper() {
|
|
function getWrapper() {
|
|
// http://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects
|
|
// http://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects
|
|
// http://developer.mozilla.org/docs/Web/API/Window
|
|
// http://developer.mozilla.org/docs/Web/API/Window
|
|
- const bridge = this;
|
|
|
|
const wrapper = {};
|
|
const wrapper = {};
|
|
- const { helpers } = bridge;
|
|
|
|
- helpers.forEach([
|
|
|
|
|
|
+ forEach([
|
|
// `eval` should be called directly so that it is run in current scope
|
|
// `eval` should be called directly so that it is run in current scope
|
|
'eval',
|
|
'eval',
|
|
], name => {
|
|
], name => {
|
|
wrapper[name] = window[name];
|
|
wrapper[name] = window[name];
|
|
});
|
|
});
|
|
- helpers.forEach([
|
|
|
|
|
|
+ forEach([
|
|
// 'uneval',
|
|
// 'uneval',
|
|
'isFinite',
|
|
'isFinite',
|
|
'isNaN',
|
|
'isNaN',
|
|
@@ -677,7 +427,7 @@ function getWrapper() {
|
|
});
|
|
});
|
|
}
|
|
}
|
|
// Wrap properties
|
|
// Wrap properties
|
|
- helpers.forEach(bridge.props, name => {
|
|
|
|
|
|
+ forEach(bridge.props, name => {
|
|
if (name in wrapper) return;
|
|
if (name in wrapper) return;
|
|
if (name.slice(0, 2) === 'on') defineReactedProperty(name);
|
|
if (name.slice(0, 2) === 'on') defineReactedProperty(name);
|
|
else defineProtectedProperty(name);
|
|
else defineProtectedProperty(name);
|
|
@@ -685,11 +435,51 @@ function getWrapper() {
|
|
return wrapper;
|
|
return wrapper;
|
|
}
|
|
}
|
|
|
|
|
|
-function initialize(src, dest, props) {
|
|
|
|
- const bridge = this;
|
|
|
|
- bridge.prepare(src, dest);
|
|
|
|
- bridge.props = props;
|
|
|
|
- const { noop } = bridge.helpers;
|
|
|
|
- bridge.load = noop;
|
|
|
|
- bridge.checkLoad = noop;
|
|
|
|
|
|
+function runCode(name, func, args, thisObj) {
|
|
|
|
+ if (process.env.DEBUG) {
|
|
|
|
+ console.log(`Run script: ${name}`); // eslint-disable-line no-console
|
|
|
|
+ }
|
|
|
|
+ try {
|
|
|
|
+ func.apply(thisObj, args);
|
|
|
|
+ } catch (e) {
|
|
|
|
+ let msg = `Error running script: ${name}\n${e}`;
|
|
|
|
+ if (e.message) msg = `${msg}\n${e.message}`;
|
|
|
|
+ console.error(msg);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function exposeVM() {
|
|
|
|
+ const Violentmonkey = {};
|
|
|
|
+ const checking = {};
|
|
|
|
+ let key = 0;
|
|
|
|
+ bridge.onScriptChecked = ({ callback, result }) => {
|
|
|
|
+ const cb = checking[callback];
|
|
|
|
+ if (cb) {
|
|
|
|
+ cb(result);
|
|
|
|
+ delete checking[callback];
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ Object.defineProperty(Violentmonkey, 'getVersion', {
|
|
|
|
+ value: () => Promise.resolve({
|
|
|
|
+ version: bridge.version,
|
|
|
|
+ }),
|
|
|
|
+ });
|
|
|
|
+ Object.defineProperty(Violentmonkey, 'isInstalled', {
|
|
|
|
+ value: (name, namespace) => new Promise(resolve => {
|
|
|
|
+ key += 1;
|
|
|
|
+ const callback = checking[key];
|
|
|
|
+ checking[callback] = resolve;
|
|
|
|
+ bridge.post({
|
|
|
|
+ cmd: 'CheckScript',
|
|
|
|
+ data: {
|
|
|
|
+ name,
|
|
|
|
+ namespace,
|
|
|
|
+ callback,
|
|
|
|
+ },
|
|
|
|
+ });
|
|
|
|
+ }),
|
|
|
|
+ });
|
|
|
|
+ Object.defineProperty(window.external, 'Violentmonkey', {
|
|
|
|
+ value: Violentmonkey,
|
|
|
|
+ });
|
|
}
|
|
}
|