Revert QR making dollar TS

This commit is contained in:
Lalle 2023-04-18 02:16:10 +02:00
parent d46c6e42c2
commit e23dd2366a
No known key found for this signature in database
GPG Key ID: A6583D207A8F6B0D
8 changed files with 5760 additions and 3974 deletions

2585
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -148,5 +148,9 @@
"build": "node ./tools/rollup",
"build:beta": "node ./tools/rollup -beta",
"build:noupdate": "node ./tools/rollup -noupdate"
},
"dependencies": {
"@types/jquery": "^3.5.16",
"jquery": "^3.6.4"
}
}

22
pnpm-lock.yaml generated
View File

@ -1,5 +1,13 @@
lockfileVersion: '6.0'
dependencies:
'@types/jquery':
specifier: ^3.5.16
version: 3.5.16
jquery:
specifier: ^3.6.4
version: 3.6.4
devDependencies:
'@rollup/plugin-typescript':
specifier: ^11.0.0
@ -236,6 +244,12 @@ packages:
resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==}
dev: true
/@types/jquery@3.5.16:
resolution: {integrity: sha512-bsI7y4ZgeMkmpG9OM710RRzDFp+w4P1RGiIt30C1mSBT+ExCleeh4HObwgArnDFELmRrOpXgSYN9VF1hj+f1lw==}
dependencies:
'@types/sizzle': 2.3.3
dev: false
/@types/json-schema@7.0.11:
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
dev: true
@ -264,6 +278,10 @@ packages:
resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==}
dev: true
/@types/sizzle@2.3.3:
resolution: {integrity: sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==}
dev: false
/@typescript-eslint/eslint-plugin@5.58.0(@typescript-eslint/parser@5.58.0)(eslint@8.38.0)(typescript@4.9.5):
resolution: {integrity: sha512-vxHvLhH0qgBd3/tW6/VccptSfc8FxPQIkmNTVLWcCOVqSBvqpnKkBTYrhcGlXfSnd78azwe+PsjYFj0X34/njA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -1645,6 +1663,10 @@ packages:
resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
dev: true
/jquery@3.6.4:
resolution: {integrity: sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ==}
dev: false
/js-sdsl@4.4.0:
resolution: {integrity: sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==}
dev: true

2154
src/Posting/QR.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

994
src/platform/$.ts Normal file
View File

@ -0,0 +1,994 @@
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS205: Consider reworking code to avoid use of IIFEs
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
// loosely follows the jquery api:
// http://api.jquery.com/
import Notice from "../classes/Notice";
import { c, Conf, d, doc, g } from "../globals/globals";
import CrossOrigin from "./CrossOrigin";
import { debounce, dict, MINUTE, platform, SECOND } from "./helpers";
// not chainable
const $ = (selector: string, root: HTMLElement = doc) => root.querySelector(selector);
$.id = id => d.getElementById(id);
$.ready = function(fc) {
if (d.readyState !== 'loading') {
$.queueTask(fc);
return;
}
var cb = function() {
$.off(d, 'DOMContentLoaded', cb);
return fc();
};
return $.on(d, 'DOMContentLoaded', cb);
};
$.formData = function(form) {
if (form instanceof HTMLFormElement) {
return new FormData(form);
}
const fd = new FormData();
for (var key in form) {
var val = form[key];
if (val) {
if ((typeof val === 'object') && 'newName' in val) {
fd.append(key, val, val.newName);
} else {
fd.append(key, val);
}
}
}
return fd;
};
$.extend = function(object, properties) {
for (var key in properties) {
var val = properties[key];
object[key] = val;
}
};
$.hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
$.getOwn = function(obj, key) {
if (Object.prototype.hasOwnProperty.call(obj, key)) { return obj[key]; } else { return undefined; }
};
$.ajax = (function() {
let pageXHR;
if (window.wrappedJSObject && !XMLHttpRequest.wrappedJSObject) {
pageXHR = XPCNativeWrapper(window.wrappedJSObject.XMLHttpRequest);
} else {
pageXHR = XMLHttpRequest;
}
const r = (function (url, options={}) {
if (options.responseType == null) { options.responseType = 'json'; }
if (!options.type) { options.type = (options.form && 'post') || 'get'; }
// XXX https://forums.lanik.us/viewtopic.php?f=64&t=24173&p=78310
url = url.replace(/^((?:https?:)?\/\/(?:\w+\.)?(?:4chan|4channel|4cdn)\.org)\/adv\//, '$1//adv/');
if (platform === 'crx') {
// XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638
if (Conf['Work around CORB Bug'] && g.SITE.software === 'yotsuba' && !options.testCORB && FormData.prototype.entries) {
return $.ajaxPage(url, options);
}
}
const {onloadend, timeout, responseType, withCredentials, type, onprogress, form, headers} = options;
const r = new pageXHR();
try {
r.open(type, url, true);
const object = headers || {};
for (var key in object) {
var value = object[key];
r.setRequestHeader(key, value);
}
$.extend(r, {onloadend, timeout, responseType, withCredentials});
$.extend(r.upload, {onprogress});
// connection error or content blocker
$.on(r, 'error', function() { if (!r.status) { return c.warn(`4chan X failed to load: ${url}`); } });
if (platform === 'crx') {
// https://bugs.chromium.org/p/chromium/issues/detail?id=920638
$.on(r, 'load', () => {
if (!Conf['Work around CORB Bug'] && r.readyState === 4 && r.status === 200 && r.statusText === '' && r.response === null) {
$.set('Work around CORB Bug', (Conf['Work around CORB Bug'] = Date.now()));
}
});
}
r.send(form);
} catch (err) {
// XXX Some content blockers in Firefox (e.g. Adblock Plus and NoScript) throw an exception instead of simulating a connection error.
if (err.result !== 0x805e0006) { throw err; }
r.onloadend = onloadend;
$.queueTask($.event, 'error', null, r);
$.queueTask($.event, 'loadend', null, r);
}
return r;
});
if (platform === 'userscript') {
return r;
} else {
// # XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638
let requestID = 0;
const requests = dict();
$.ajaxPageInit = function() {
$.global(function() {
window.FCX.requests = Object.create(null);
document.addEventListener('4chanXAjax', function(e) {
let fd, r;
const {url, timeout, responseType, withCredentials, type, onprogress, form, headers, id} = e.detail;
window.FCX.requests[id] = (r = new XMLHttpRequest());
r.open(type, url, true);
const object = headers || {};
for (var key in object) {
var value = object[key];
r.setRequestHeader(key, value);
}
r.responseType = responseType === 'document' ? 'text' : responseType;
r.timeout = timeout;
r.withCredentials = withCredentials;
if (onprogress) {
r.upload.onprogress = function(e) {
const {loaded, total} = e;
const detail = {loaded, total, id};
return document.dispatchEvent(new CustomEvent('4chanXAjaxProgress', {bubbles: true, detail}));
};
}
r.onloadend = function() {
delete window.FCX.requests[id];
const {status, statusText, response} = this;
const responseHeaderString = this.getAllResponseHeaders();
const detail = {status, statusText, response, responseHeaderString, id};
return document.dispatchEvent(new CustomEvent('4chanXAjaxLoadend', {bubbles: true, detail}));
};
// connection error or content blocker
r.onerror = function() {
if (!r.status) { return console.warn(`4chan X failed to load: ${url}`); }
};
if (form) {
fd = new FormData();
for (var entry of form) {
fd.append(entry[0], entry[1]);
}
} else {
fd = null;
}
return r.send(fd);
}
, false);
return document.addEventListener('4chanXAjaxAbort', function(e) {
let r;
if (!(r = window.FCX.requests[e.detail.id])) { return; }
return r.abort();
}
, false);
});
$.on(d, '4chanXAjaxProgress', function(e) {
let req;
if (!(req = requests[e.detail.id])) { return; }
return req.upload.onprogress.call(req.upload, e.detail);
});
return $.on(d, '4chanXAjaxLoadend', function(e) {
let req;
if (!(req = requests[e.detail.id])) { return; }
delete requests[e.detail.id];
if (e.detail.status) {
for (var key of ['status', 'statusText', 'response', 'responseHeaderString']) {
req[key] = e.detail[key];
}
if (req.responseType === 'document') {
req.response = new DOMParser().parseFromString(e.detail.response, 'text/html');
}
}
return req.onloadend();
});
};
return $.ajaxPage = function(url, options={}) {
let req;
let {onloadend, timeout, responseType, withCredentials, type, onprogress, form, headers} = options;
const id = requestID++;
requests[id] = (req = new CrossOrigin.Request());
$.extend(req, {responseType, onloadend});
req.upload = {onprogress};
req.abort = () => $.event('4chanXAjaxAbort', {id});
if (form) { form = Array.from(form.entries()); }
$.event('4chanXAjax', {url, timeout, responseType, withCredentials, type, onprogress: !!onprogress, form, headers, id});
return req;
};
}
})();
// Status Code 304: Not modified
// With the `If-Modified-Since` header we only receive the HTTP headers and no body for 304 responses.
// This saves a lot of bandwidth and CPU time for both the users and the servers.
$.lastModified = dict();
$.whenModified = function(url, bucket, cb, options={}) {
let t;
const {timeout, ajax} = options;
const params = [];
// XXX https://bugs.chromium.org/p/chromium/issues/detail?id=643659
if ($.engine === 'blink') { params.push(`s=${bucket}`); }
if (url.split('/')[2] === 'a.4cdn.org') { params.push(`t=${Date.now()}`); }
const url0 = url;
if (params.length) { url += '?' + params.join('&'); }
const headers = dict();
if ((t = $.lastModified[bucket]?.[url0]) != null) {
headers['If-Modified-Since'] = t;
}
const r = (ajax || $.ajax)(url, {
onloadend() {
($.lastModified[bucket] || ($.lastModified[bucket] = dict()))[url0] = this.getResponseHeader('Last-Modified');
return cb.call(this);
},
timeout,
headers
});
return r;
};
(function() {
const reqs = dict();
$.cache = function(url, cb, options={}) {
let req;
const {ajax} = options;
if (req = reqs[url]) {
if (req.callbacks) {
req.callbacks.push(cb);
} else {
$.queueTask(() => cb.call(req, {isCached: true}));
}
return req;
}
const onloadend = function() {
if (!this.status) {
delete reqs[url];
}
for (cb of this.callbacks) {
(cb => $.queueTask(() => cb.call(this, {isCached: false})))(cb);
}
return delete this.callbacks;
};
req = (ajax || $.ajax)(url, {onloadend});
req.callbacks = [cb];
return reqs[url] = req;
};
return $.cleanCache = function(testf) {
for (var url in reqs) {
if (testf(url)) {
delete reqs[url];
}
}
};
})();
$.cb = {
checked() {
if ($.hasOwn(Conf, this.name)) {
$.set(this.name, this.checked);
return Conf[this.name] = this.checked;
}
},
value() {
if ($.hasOwn(Conf, this.name)) {
$.set(this.name, this.value.trim());
return Conf[this.name] = this.value;
}
}
};
$.asap = function(test, cb) {
if (test()) {
return cb();
} else {
return setTimeout($.asap, 25, test, cb);
}
};
$.onExists = function(root, selector, cb) {
let el;
if (el = $(selector, root)) {
return cb(el);
}
var observer = new MutationObserver(function() {
if (el = $(selector, root)) {
observer.disconnect();
return cb(el);
}
});
return observer.observe(root, {childList: true, subtree: true});
};
$.addStyle = function(css, id, test='head') {
const style = $.el('style',
{textContent: css});
if (id != null) { style.id = id; }
$.onExists(doc, test, () => $.add(d.head, style));
return style;
};
$.addCSP = function(policy) {
const meta = $.el('meta', {
httpEquiv: 'Content-Security-Policy',
content: policy
}
);
if (d.head) {
$.add(d.head, meta);
return $.rm(meta);
} else {
const head = $.add((doc || d), $.el('head'));
$.add(head, meta);
return $.rm(head);
}
};
$.x = function(path, root) {
if (!root) { root = d.body; }
// XPathResult.ANY_UNORDERED_NODE_TYPE === 8
return d.evaluate(path, root, null, 8, null).singleNodeValue;
};
$.X = function(path, root) {
if (!root) { root = d.body; }
// XPathResult.ORDERED_NODE_SNAPSHOT_TYPE === 7
return d.evaluate(path, root, null, 7, null);
};
$.addClass = function(el, ...classNames) {
for (var className of classNames) { el.classList.add(className); }
};
$.rmClass = function(el, ...classNames) {
for (var className of classNames) { el.classList.remove(className); }
};
$.toggleClass = (el, className) => el.classList.toggle(className);
$.hasClass = (el, className) => el.classList.contains(className);
$.rm = el => el?.remove();
$.rmAll = root => // https://gist.github.com/MayhemYDG/8646194
root.textContent = null;
$.tn = s => d.createTextNode(s);
$.frag = () => d.createDocumentFragment();
$.nodes = function(nodes) {
if (!(nodes instanceof Array)) {
return nodes;
}
const frag = $.frag();
for (var node of nodes) {
frag.appendChild(node);
}
return frag;
};
$.add = (parent, el) => parent.appendChild($.nodes(el));
$.prepend = (parent, el) => parent.insertBefore($.nodes(el), parent.firstChild);
$.after = (root, el) => root.parentNode.insertBefore($.nodes(el), root.nextSibling);
$.before = (root, el) => root.parentNode.insertBefore($.nodes(el), root);
$.replace = (root, el) => root.parentNode.replaceChild($.nodes(el), root);
$.el = function(tag, properties, properties2) {
const el = d.createElement(tag);
if (properties) { $.extend(el, properties); }
if (properties2) { $.extend(el, properties2); }
return el;
};
$.on = function(el, events, handler) {
for (var event of events.split(' ')) {
el.addEventListener(event, handler, false);
}
};
$.off = function(el, events, handler) {
for (var event of events.split(' ')) {
el.removeEventListener(event, handler, false);
}
};
$.one = function(el, events, handler) {
var cb = function(e) {
$.off(el, events, cb);
return handler.call(this, e);
};
return $.on(el, events, cb);
};
$.event = function(event, detail, root=d) {
if (!globalThis.chrome?.extension) {
if ((detail != null) && (typeof cloneInto === 'function')) {
detail = cloneInto(detail, d.defaultView);
}
}
return root.dispatchEvent(new CustomEvent(event, {bubbles: true, cancelable: true, detail}));
};
if (platform === 'userscript') {
// XXX Make $.event work in Pale Moon with GM 3.x (no cloneInto function).
(function() {
if (!/PaleMoon\//.test(navigator.userAgent) || (+GM_info?.version?.split('.')[0] < 2) || (typeof cloneInto !== 'undefined')) { return; }
try {
return new CustomEvent('x', {detail: {}});
} catch (err) {
const unsafeConstructors = {
Object: unsafeWindow.Object,
Array: unsafeWindow.Array
};
var clone = function(obj) {
let constructor;
if ((obj != null) && (typeof obj === 'object') && (constructor = unsafeConstructors[obj.constructor.name])) {
const obj2 = new constructor();
for (var key in obj) { var val = obj[key]; obj2[key] = clone(val); }
return obj2;
} else {
return obj;
}
};
return $.event = (event, detail, root=d) => root.dispatchEvent(new CustomEvent(event, {bubbles: true, cancelable: true, detail: clone(detail)}));
}
})();
}
$.modifiedClick = e => e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || (e.button !== 0);
if (!globalThis.chrome?.extension) {
$.open =
(GM?.openInTab != null) ?
GM.openInTab
: (typeof GM_openInTab !== 'undefined' && GM_openInTab !== null) ?
GM_openInTab
:
url => window.open(url, '_blank');
} else {
$.open =
url => window.open(url, '_blank');
}
$.debounce = function(wait, fn) {
let lastCall = 0;
let timeout = null;
let that = null;
let args = null;
const exec = function() {
lastCall = Date.now();
return fn.apply(that, args);
};
return function() {
args = arguments;
that = this;
if (lastCall < (Date.now() - wait)) {
return exec();
}
// stop current reset
clearTimeout(timeout);
// after wait, let next invocation execute immediately
return timeout = setTimeout(exec, wait);
};
};
$.queueTask = (function() {
// inspired by https://www.w3.org/Bugs/Public/show_bug.cgi?id=15007
const taskQueue = [];
const execTask = function() {
const task = taskQueue.shift();
const func = task[0];
const args = Array.prototype.slice.call(task, 1);
return func.apply(func, args);
};
if (window.MessageChannel) {
const taskChannel = new MessageChannel();
taskChannel.port1.onmessage = execTask;
return function() {
taskQueue.push(arguments);
return taskChannel.port2.postMessage(null);
};
} else { // XXX Firefox
return function() {
taskQueue.push(arguments);
return setTimeout(execTask, 0);
};
}
})();
$.global = function(fn, data) {
if (doc) {
const script = $.el('script',
{textContent: `(${fn}).call(document.currentScript.dataset);`});
if (data) { $.extend(script.dataset, data); }
$.add((d.head || doc), script);
$.rm(script);
return script.dataset;
} else {
// XXX dwb
try {
fn.call(data);
} catch (error) {}
return data;
}
};
$.bytesToString = function(size) {
let unit = 0; // Bytes
while (size >= 1024) {
size /= 1024;
unit++;
}
// Remove trailing 0s.
size =
unit > 1 ?
// Keep the size as a float if the size is greater than 2^20 B.
// Round to hundredth.
Math.round(size * 100) / 100
:
// Round to an integer otherwise.
Math.round(size);
return `${size} ${['B', 'KB', 'MB', 'GB'][unit]}`;
};
$.minmax = (value, min, max) => value < min ?
min
:
value > max ?
max
:
value;
$.hasAudio = video => video.mozHasAudio || !!video.webkitAudioDecodedByteCount;
$.luma = rgb => (rgb[0] * 0.299) + (rgb[1] * 0.587) + (rgb[2] * 0.114);
$.unescape = function(text) {
if (text == null) { return text; }
return text.replace(/<[^>]*>/g, '').replace(/&(amp|#039|quot|lt|gt|#44);/g, c => ({'&amp;': '&', '&#039;': "'", '&quot;': '"', '&lt;': '<', '&gt;': '>', '&#44;': ','})[c]);
};
$.isImage = url => /\.(jpe?g|jfif|png|gif|bmp|webp|avif|jxl)$/i.test(url);
$.isVideo = url => /\.(webm|mp4|ogv)$/i.test(url);
$.engine = (function() {
if (/Edge\//.test(navigator.userAgent)) { return 'edge'; }
if (/Chrome\//.test(navigator.userAgent)) { return 'blink'; }
if (/WebKit\//.test(navigator.userAgent)) { return 'webkit'; }
if (/Gecko\/|Goanna/.test(navigator.userAgent)) { return 'gecko'; } // Goanna = Pale Moon 26+
})();
$.hasStorage = (function() {
try {
if (localStorage.getItem(g.NAMESPACE + 'hasStorage') === 'true') { return true; }
localStorage.setItem(g.NAMESPACE + 'hasStorage', 'true');
return localStorage.getItem(g.NAMESPACE + 'hasStorage') === 'true';
} catch (error) {
return false;
}
})();
$.item = function(key, val) {
const item = dict();
item[key] = val;
return item;
};
$.oneItemSugar = fn => (function(key, val, cb) {
if (typeof key === 'string') {
return fn($.item(key, val), cb);
} else {
return fn(key, val);
}
});
$.syncing = dict();
$.securityCheck = function(data) {
if (location.protocol !== 'https:') {
return delete data['Redirect to HTTPS'];
}
};
if (platform === 'crx') {
// https://developer.chrome.com/extensions/storage.html
$.oldValue = {
local: dict(),
sync: dict()
};
chrome.storage.onChanged.addListener(function(changes, area) {
for (var key in changes) {
var oldValue = $.oldValue.local[key] ?? $.oldValue.sync[key];
$.oldValue[area][key] = dict.clone(changes[key].newValue);
var newValue = $.oldValue.local[key] ?? $.oldValue.sync[key];
var cb = $.syncing[key];
if (cb && (JSON.stringify(newValue) !== JSON.stringify(oldValue))) {
cb(newValue, key);
}
}
});
$.sync = (key, cb) => $.syncing[key] = cb;
$.forceSync = function() { };
$.crxWorking = function() {
try {
if (chrome.runtime.getManifest()) {
return true;
}
} catch (error) {}
if (!$.crxWarningShown) {
const msg = $.el('div',
{innerHTML: '4chan X seems to have been updated. You will need to <a href="javascript:;">reload</a> the page.'});
$.on($('a', msg), 'click', () => location.reload());
new Notice('warning', msg);
$.crxWarningShown = true;
}
return false;
};
$.get = $.oneItemSugar(function(data, cb) {
if (!$.crxWorking()) { return; }
const results = {};
const get = function(area) {
let keys = Object.keys(data);
// XXX slow performance in Firefox
if (($.engine === 'gecko') && (area === 'sync') && (keys.length > 3)) {
keys = null;
}
return chrome.storage[area].get(keys, function(result) {
let key;
result = dict.clone(result);
if (chrome.runtime.lastError) {
c.error(chrome.runtime.lastError.message);
}
if (keys === null) {
const result2 = dict();
for (key in result) { var val = result[key]; if ($.hasOwn(data, key)) { result2[key] = val; } }
result = result2;
}
for (key in data) {
$.oldValue[area][key] = result[key];
}
results[area] = result;
if (results.local && results.sync) {
$.extend(data, results.sync);
$.extend(data, results.local);
return cb(data);
}
});
};
get('local');
return get('sync');
});
(function() {
const items = {
local: dict(),
sync: dict()
};
const exceedsQuota = (key, value) => // bytes in UTF-8
unescape(encodeURIComponent(JSON.stringify(key))).length + unescape(encodeURIComponent(JSON.stringify(value))).length > chrome.storage.sync.QUOTA_BYTES_PER_ITEM;
$.delete = function(keys) {
if (!$.crxWorking()) { return; }
if (typeof keys === 'string') {
keys = [keys];
}
for (var key of keys) {
delete items.local[key];
delete items.sync[key];
}
chrome.storage.local.remove(keys);
return chrome.storage.sync.remove(keys);
};
const timeout = {};
var setArea = function(area, cb) {
const data = dict();
$.extend(data, items[area]);
if (!Object.keys(data).length || (timeout[area] > Date.now())) { return; }
return chrome.storage[area].set(data, function() {
let err;
let key;
if (err = chrome.runtime.lastError) {
c.error(err.message);
setTimeout(setArea, MINUTE, area);
timeout[area] = Date.now() + MINUTE;
return cb?.(err);
}
delete timeout[area];
for (key in data) { if (items[area][key] === data[key]) { delete items[area][key]; } }
if (area === 'local') {
for (key in data) { var val = data[key]; if (!exceedsQuota(key, val)) { items.sync[key] = val; } }
setSync();
} else {
chrome.storage.local.remove(((() => {
const result = [];
for (key in data) {
if (!(key in items.local)) {
result.push(key);
}
}
return result;
})()));
}
return cb?.();
});
};
var setSync = debounce(SECOND, () => setArea('sync'));
$.set = $.oneItemSugar(function(data, cb) {
if (!$.crxWorking()) { return; }
$.securityCheck(data);
$.extend(items.local, data);
return setArea('local', cb);
});
return $.clear = function(cb) {
if (!$.crxWorking()) { return; }
items.local = dict();
items.sync = dict();
let count = 2;
let err = null;
const done = function() {
if (chrome.runtime.lastError) {
c.error(chrome.runtime.lastError.message);
}
if (err == null) { err = chrome.runtime.lastError; }
if (!--count) { return cb?.(err); }
};
chrome.storage.local.clear(done);
return chrome.storage.sync.clear(done);
};
})();
} else {
// http://wiki.greasespot.net/Main_Page
// https://tampermonkey.net/documentation.php
if ((GM?.deleteValue != null) && window.BroadcastChannel && (typeof GM_addValueChangeListener === 'undefined' || GM_addValueChangeListener === null)) {
$.syncChannel = new BroadcastChannel(g.NAMESPACE + 'sync');
$.on($.syncChannel, 'message', e => (() => {
const result = [];
for (var key in e.data) {
var cb;
var val = e.data[key];
if (cb = $.syncing[key]) {
result.push(cb(dict.json(JSON.stringify(val)), key));
}
}
return result;
})());
$.sync = (key, cb) => $.syncing[key] = cb;
$.forceSync = function() {};
$.delete = function(keys, cb) {
let key;
if (!(keys instanceof Array)) {
keys = [keys];
}
return Promise.all((() => {
const result = [];
for (key of keys) { result.push(GM.deleteValue(g.NAMESPACE + key));
}
return result;
})()).then(function() {
const items = dict();
for (key of keys) { items[key] = undefined; }
$.syncChannel.postMessage(items);
return cb?.();
});
};
$.get = $.oneItemSugar(function(items, cb) {
const keys = Object.keys(items);
return Promise.all(keys.map((key) => GM.getValue(g.NAMESPACE + key))).then(function(values) {
for (let i = 0; i < values.length; i++) {
var val = values[i];
if (val) {
items[keys[i]] = dict.json(val);
}
}
return cb(items);
});
});
$.set = $.oneItemSugar(function(items, cb) {
$.securityCheck(items);
return Promise.all((() => {
const result = [];
for (var key in items) {
var val = items[key];
result.push(GM.setValue(g.NAMESPACE + key, JSON.stringify(val)));
}
return result;
})()).then(function() {
$.syncChannel.postMessage(items);
return cb?.();
});
});
$.clear = cb => GM.listValues().then(keys => $.delete(keys.map(key => key.replace(g.NAMESPACE, '')), cb)).catch( () => $.delete(Object.keys(Conf).concat(['previousversion', 'QR Size', 'QR.persona']), cb));
} else {
if (typeof GM_deleteValue === 'undefined' || GM_deleteValue === null) {
$.perProtocolSettings = true;
}
if (typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) {
$.getValue = GM_getValue;
$.listValues = () => GM_listValues(); // error when called if missing
} else if ($.hasStorage) {
$.getValue = key => localStorage.getItem(key);
$.listValues = () => (() => {
const result = [];
for (var key in localStorage) {
if (key.slice(0, g.NAMESPACE.length) === g.NAMESPACE) {
result.push(key);
}
}
return result;
})();
} else {
$.getValue = function() {};
$.listValues = () => [];
}
if (typeof GM_addValueChangeListener !== 'undefined' && GM_addValueChangeListener !== null) {
$.setValue = GM_setValue;
$.deleteValue = GM_deleteValue;
} else if (typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) {
$.oldValue = dict();
$.setValue = function(key, val) {
GM_setValue(key, val);
if (key in $.syncing) {
$.oldValue[key] = val;
if ($.hasStorage) { return localStorage.setItem(key, val); } // for `storage` events
}
};
$.deleteValue = function(key) {
GM_deleteValue(key);
if (key in $.syncing) {
delete $.oldValue[key];
if ($.hasStorage) { return localStorage.removeItem(key); } // for `storage` events
}
};
if (!$.hasStorage) { $.cantSync = true; }
} else if ($.hasStorage) {
$.oldValue = dict();
$.setValue = function(key, val) {
if (key in $.syncing) { $.oldValue[key] = val; }
return localStorage.setItem(key, val);
};
$.deleteValue = function(key) {
if (key in $.syncing) { delete $.oldValue[key]; }
return localStorage.removeItem(key);
};
} else {
$.setValue = function() {};
$.deleteValue = function() {};
$.cantSync = ($.cantSet = true);
}
if (typeof GM_addValueChangeListener !== 'undefined' && GM_addValueChangeListener !== null) {
$.sync = (key, cb) => $.syncing[key] = GM_addValueChangeListener(g.NAMESPACE + key, function(key2, oldValue, newValue, remote) {
if (remote) {
if (newValue !== undefined) { newValue = dict.json(newValue); }
return cb(newValue, key);
}
});
$.forceSync = function() {};
} else if ((typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) || $.hasStorage) {
$.sync = function(key, cb) {
key = g.NAMESPACE + key;
$.syncing[key] = cb;
return $.oldValue[key] = $.getValue(key);
};
(function() {
const onChange = function({key, newValue}) {
let cb;
if (!(cb = $.syncing[key])) { return; }
if (newValue != null) {
if (newValue === $.oldValue[key]) { return; }
$.oldValue[key] = newValue;
return cb(dict.json(newValue), key.slice(g.NAMESPACE.length));
} else {
if ($.oldValue[key] == null) { return; }
delete $.oldValue[key];
return cb(undefined, key.slice(g.NAMESPACE.length));
}
};
$.on(window, 'storage', onChange);
return $.forceSync = function(key) {
// Storage events don't work across origins
// e.g. http://boards.4chan.org and https://boards.4chan.org
// so force a check for changes to avoid lost data.
key = g.NAMESPACE + key;
return onChange({key, newValue: $.getValue(key)});
};
})();
} else {
$.sync = function() {};
$.forceSync = function() {};
}
$.delete = function(keys) {
if (!(keys instanceof Array)) {
keys = [keys];
}
for (var key of keys) {
$.deleteValue(g.NAMESPACE + key);
}
};
$.get = $.oneItemSugar((items, cb) => $.queueTask($.getSync, items, cb));
$.getSync = function(items, cb) {
for (var key in items) {
var val2;
if (val2 = $.getValue(g.NAMESPACE + key)) {
try {
items[key] = dict.json(val2);
} catch (err) {
// XXX https://github.com/ccd0/4chan-x/issues/2218
if (!/^(?:undefined)*$/.test(val2)) {
throw err;
}
}
}
}
return cb(items);
};
$.set = $.oneItemSugar(function(items, cb) {
$.securityCheck(items);
return $.queueTask(function() {
for (var key in items) {
var value = items[key];
$.setValue(g.NAMESPACE + key, JSON.stringify(value));
}
return cb?.();
});
});
$.clear = function(cb) {
// XXX https://github.com/greasemonkey/greasemonkey/issues/2033
// Also support case where GM_listValues is not defined.
$.delete(Object.keys(Conf));
$.delete(['previousversion', 'QR Size', 'QR.persona']);
try {
$.delete($.listValues().map(key => key.replace(g.NAMESPACE, '')));
} catch (error) {}
return cb?.();
};
}
}
export default $;

View File

@ -4,6 +4,7 @@
"noImplicitAny": false,
"removeComments": false,
"sourceMap": true,
"allowSyntheticDefaultImports": true,
"target": "ES2020",
"allowJs": true,
"checkJs": true,
@ -13,7 +14,8 @@
"jsxFragmentFactory": "hFragment",
"types": [
"@violentmonkey/types",
"@types/chrome"
"@types/chrome",
"@types/jquery"
],
"lib": [
"DOM",