47446baf29
Would a bit of consistency in variable names be too much to ask?
204 lines
6.8 KiB
JavaScript
204 lines
6.8 KiB
JavaScript
(function() {
|
|
const _qute_script_id = "__gm_{{ scriptName }}";
|
|
|
|
function GM_log(text) {
|
|
console.log(text);
|
|
}
|
|
|
|
const GM_info = {
|
|
'script': {{ scriptInfo }},
|
|
'scriptMetaStr': "{{ scriptMeta }}",
|
|
'scriptWillUpdate': false,
|
|
'version': "0.0.1",
|
|
// so scripts don't expect exportFunction
|
|
'scriptHandler': 'Tampermonkey',
|
|
};
|
|
|
|
function checkKey(key, funcName) {
|
|
if (typeof key !== "string") {
|
|
throw new Error(`${funcName} requires the first parameter to be of type string, not '${typeof key}'`);
|
|
}
|
|
}
|
|
|
|
function GM_setValue(key, value) {
|
|
checkKey(key, "GM_setValue");
|
|
if (typeof value !== "string" &&
|
|
typeof value !== "number" &&
|
|
typeof value !== "boolean") {
|
|
throw new Error(`GM_setValue requires the second parameter to be of type string, number or boolean, not '${typeof value}'`);
|
|
}
|
|
localStorage.setItem(_qute_script_id + key, value);
|
|
}
|
|
|
|
function GM_getValue(key, default_) {
|
|
checkKey(key, "GM_getValue");
|
|
return localStorage.getItem(_qute_script_id + key) || default_;
|
|
}
|
|
|
|
function GM_deleteValue(key) {
|
|
checkKey(key, "GM_deleteValue");
|
|
localStorage.removeItem(_qute_script_id + key);
|
|
}
|
|
|
|
function GM_listValues() {
|
|
const keys = [];
|
|
for (let i = 0; i < localStorage.length; i++) {
|
|
if (localStorage.key(i).startsWith(_qute_script_id)) {
|
|
keys.push(localStorage.key(i).slice(_qute_script_id.length));
|
|
}
|
|
}
|
|
return keys;
|
|
}
|
|
|
|
function GM_openInTab(url) {
|
|
window.open(url);
|
|
}
|
|
|
|
|
|
// Almost verbatim copy from Eric
|
|
function GM_xmlhttpRequest(/* object */ details) {
|
|
details.method = details.method ? details.method.toUpperCase() : "GET";
|
|
|
|
if (!details.url) {
|
|
throw new Error("GM_xmlhttpRequest requires an URL.");
|
|
}
|
|
|
|
// build XMLHttpRequest object
|
|
const oXhr = new XMLHttpRequest();
|
|
// run it
|
|
if ("onreadystatechange" in details) {
|
|
oXhr.onreadystatechange = function() {
|
|
details.onreadystatechange(oXhr);
|
|
};
|
|
}
|
|
if ("onload" in details) {
|
|
oXhr.onload = function() { details.onload(oXhr); };
|
|
}
|
|
if ("onerror" in details) {
|
|
oXhr.onerror = function () { details.onerror(oXhr); };
|
|
}
|
|
|
|
oXhr.open(details.method, details.url, true);
|
|
|
|
if ("headers" in details) {
|
|
for (const header in details.headers) {
|
|
oXhr.setRequestHeader(header, details.headers[header]);
|
|
}
|
|
}
|
|
|
|
if ("data" in details) {
|
|
oXhr.send(details.data);
|
|
} else {
|
|
oXhr.send();
|
|
}
|
|
}
|
|
|
|
function GM_addStyle(/* String */ styles) {
|
|
const oStyle = document.createElement("style");
|
|
oStyle.setAttribute("type", "text/css");
|
|
oStyle.appendChild(document.createTextNode(styles));
|
|
|
|
const head = document.getElementsByTagName("head")[0];
|
|
if (head === undefined) {
|
|
// no head yet, stick it whereever
|
|
document.documentElement.appendChild(oStyle);
|
|
} else {
|
|
head.appendChild(oStyle);
|
|
}
|
|
}
|
|
|
|
// Stub these two so that the gm4 polyfill script doesn't try to
|
|
// create broken versions as attributes of window.
|
|
function GM_getResourceText(caption, commandFunc, accessKey) {
|
|
console.error(`${GM_info.script.name} called unimplemented GM_getResourceText`);
|
|
}
|
|
|
|
function GM_registerMenuCommand(caption, commandFunc, accessKey) {
|
|
console.error(`${GM_info.script.name} called unimplemented GM_registerMenuCommand`);
|
|
}
|
|
|
|
// Mock the greasemonkey 4.0 async API.
|
|
const GM = {};
|
|
GM.info = GM_info;
|
|
const entries = {
|
|
'log': GM_log,
|
|
'addStyle': GM_addStyle,
|
|
'deleteValue': GM_deleteValue,
|
|
'getValue': GM_getValue,
|
|
'listValues': GM_listValues,
|
|
'openInTab': GM_openInTab,
|
|
'setValue': GM_setValue,
|
|
'xmlHttpRequest': GM_xmlhttpRequest,
|
|
}
|
|
for (newKey in entries) {
|
|
let old = entries[newKey];
|
|
if (old && (typeof GM[newKey] == 'undefined')) {
|
|
GM[newKey] = function(...args) {
|
|
return new Promise((resolve, reject) => {
|
|
try {
|
|
resolve(old(...args));
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
});
|
|
};
|
|
}
|
|
};
|
|
|
|
{% if use_proxy %}
|
|
/*
|
|
* Try to give userscripts an environment that they expect. Which
|
|
* seems to be that the global window object should look the same as
|
|
* the page's one and that if a script writes to an attribute of
|
|
* window it should be able to access that variable in the global
|
|
* scope.
|
|
* Use a Proxy to stop scripts from actually changing the global
|
|
* window (that's what unsafeWindow is for).
|
|
* Use the "with" statement to make the proxy provide what looks
|
|
* like global scope.
|
|
*
|
|
* There are other Proxy functions that we may need to override.
|
|
* set, get and has are definitely required.
|
|
*/
|
|
const unsafeWindow = window;
|
|
const qute_gm_window_shadow = {}; // stores local changes to window
|
|
const qute_gm_windowProxyHandler = {
|
|
get: function(target, prop) {
|
|
if (prop in qute_gm_window_shadow)
|
|
return qute_gm_window_shadow[prop];
|
|
if (prop in target) {
|
|
if (typeof target[prop] === 'function' && typeof target[prop].prototype == 'undefined')
|
|
// Getting TypeError: Illegal Execution when callers try to execute
|
|
// eg addEventListener from here because they were returned
|
|
// unbound
|
|
return target[prop].bind(target);
|
|
return target[prop];
|
|
}
|
|
},
|
|
set: function(target, prop, val) {
|
|
return qute_gm_window_shadow[prop] = val;
|
|
},
|
|
has: function(target, key) {
|
|
return key in qute_gm_window_shadow || key in target;
|
|
}
|
|
};
|
|
const qute_gm_window_proxy = new Proxy(
|
|
unsafeWindow, qute_gm_windowProxyHandler);
|
|
|
|
with (qute_gm_window_proxy) {
|
|
// We can't return `this` or `qute_gm_window_proxy` from
|
|
// `qute_gm_window_proxy.get('window')` because the Proxy implementation
|
|
// does typechecking on read-only things. So we have to shadow `window`
|
|
// more conventionally here.
|
|
const window = qute_gm_window_proxy;
|
|
// ====== The actual user script source ====== //
|
|
{{ scriptSource }}
|
|
// ====== End User Script ====== //
|
|
};
|
|
{% else %}
|
|
// ====== The actual user script source ====== //
|
|
{{ scriptSource }}
|
|
// ====== End User Script ====== //
|
|
{% endif %}
|
|
})();
|