formats and more of dollar

This commit is contained in:
Lalle 2023-04-18 02:45:38 +02:00
parent e23dd2366a
commit f79199366e
No known key found for this signature in database
GPG Key ID: A6583D207A8F6B0D

View File

@ -18,19 +18,19 @@ const $ = (selector: string, root: HTMLElement = doc) => root.querySelector(sele
$.id = id => d.getElementById(id);
$.ready = function(fc) {
$.ready = function (fc) {
if (d.readyState !== 'loading') {
$.queueTask(fc);
return;
}
var cb = function() {
var cb = function () {
$.off(d, 'DOMContentLoaded', cb);
return fc();
};
return $.on(d, 'DOMContentLoaded', cb);
};
$.formData = function(form) {
$.formData = function (form) {
if (form instanceof HTMLFormElement) {
return new FormData(form);
}
@ -48,7 +48,7 @@ $.formData = function(form) {
return fd;
};
$.extend = function(object, properties) {
$.extend = function (object, properties) {
for (var key in properties) {
var val = properties[key];
object[key] = val;
@ -57,11 +57,11 @@ $.extend = function(object, properties) {
$.hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
$.getOwn = function(obj, key) {
$.getOwn = function (obj, key) {
if (Object.prototype.hasOwnProperty.call(obj, key)) { return obj[key]; } else { return undefined; }
};
$.ajax = (function() {
$.ajax = (function () {
let pageXHR;
if (window.wrappedJSObject && !XMLHttpRequest.wrappedJSObject) {
pageXHR = XPCNativeWrapper(window.wrappedJSObject.XMLHttpRequest);
@ -69,7 +69,7 @@ $.ajax = (function() {
pageXHR = XMLHttpRequest;
}
const r = (function (url, options={}) {
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
@ -80,7 +80,7 @@ $.ajax = (function() {
return $.ajaxPage(url, options);
}
}
const {onloadend, timeout, responseType, withCredentials, type, onprogress, form, headers} = options;
const { onloadend, timeout, responseType, withCredentials, type, onprogress, form, headers } = options;
const r = new pageXHR();
try {
r.open(type, url, true);
@ -89,15 +89,15 @@ $.ajax = (function() {
var value = object[key];
r.setRequestHeader(key, value);
}
$.extend(r, {onloadend, timeout, responseType, withCredentials});
$.extend(r.upload, {onprogress});
$.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}`); } });
$.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()));
$.set('Work around CORB Bug', (Conf['Work around CORB Bug'] = Date.now()), cb => cb());
}
});
}
@ -106,8 +106,7 @@ $.ajax = (function() {
// 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);
r.onerror();
}
return r;
});
@ -115,17 +114,16 @@ $.ajax = (function() {
if (platform === 'userscript') {
return r;
} else {
// # XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638
let requestID = 0;
let requestID: number
const requests = dict();
$.ajaxPageInit = function() {
$.global(function() {
$.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;
document.addEventListener('4chanXAjax', function (e) {
let fd: FormData, r: XMLHttpRequest;
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 || {};
@ -137,51 +135,42 @@ $.ajax = (function() {
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.upload.onprogress = function (e: ProgressEvent) {
const { loaded, total } = e;
const detail = { loaded, total, id };
return document.dispatchEvent(new CustomEvent('4chanXAjaxProgress', { bubbles: true, detail }));
};
}
r.onloadend = function() {
r.onloadend = function () {
delete window.FCX.requests[id];
const {status, statusText, response} = this;
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}));
const detail = { status, statusText, response, responseHeaderString, id };
return document.dispatchEvent(new CustomEvent('4chanXAjaxLoadend', { bubbles: true, detail }));
};
// connection error or content blocker
r.onerror = function() {
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]);
for (var [key, value] of form.entries()) {
fd.append(key, value);
}
} else {
fd = null;
}
return r.send(fd);
}
, false);
});
}, 'ajaxPageInit');
};
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) {
$.on(d, '4chanXAjaxProgress', function (e: CustomEvent) {
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;
return $.on(d, '4chanXAjaxLoadend', function (e: CustomEvent) {
let req: XMLHttpRequest;
if (!(req = requests[e.detail.id])) { return; }
delete requests[e.detail.id];
if (e.detail.status) {
@ -192,22 +181,8 @@ $.ajax = (function() {
req.response = new DOMParser().parseFromString(e.detail.response, 'text/html');
}
}
return req.onloadend();
return req.onloadend.call(req);
});
};
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;
};
}
})();
@ -215,9 +190,9 @@ $.ajax = (function() {
// 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={}) {
$.whenModified = function (url, bucket, cb, options = {}) {
let t;
const {timeout, ajax} = options;
const { timeout, ajax } = options;
const params = [];
// XXX https://bugs.chromium.org/p/chromium/issues/detail?id=643659
if ($.engine === 'blink') { params.push(`s=${bucket}`); }
@ -239,33 +214,33 @@ $.whenModified = function(url, bucket, cb, options={}) {
return r;
};
(function() {
(function () {
const reqs = dict();
$.cache = function(url, cb, options={}) {
$.cache = function (url, cb, options = {}) {
let req;
const {ajax} = options;
const { ajax } = options;
if (req = reqs[url]) {
if (req.callbacks) {
req.callbacks.push(cb);
} else {
$.queueTask(() => cb.call(req, {isCached: true}));
$.queueTask(() => cb.call(req, { isCached: true }));
}
return req;
}
const onloadend = function() {
const onloadend = function () {
if (!this.status) {
delete reqs[url];
}
for (cb of this.callbacks) {
(cb => $.queueTask(() => cb.call(this, {isCached: false})))(cb);
(cb => $.queueTask(() => cb.call(this, { isCached: false })))(cb);
}
return delete this.callbacks;
};
req = (ajax || $.ajax)(url, {onloadend});
req = (ajax || $.ajax)(url, { onloadend });
req.callbacks = [cb];
return reqs[url] = req;
};
return $.cleanCache = function(testf) {
return $.cleanCache = function (testf) {
for (var url in reqs) {
if (testf(url)) {
delete reqs[url];
@ -289,7 +264,7 @@ $.cb = {
}
};
$.asap = function(test, cb) {
$.asap = function (test, cb) {
if (test()) {
return cb();
} else {
@ -297,32 +272,32 @@ $.asap = function(test, cb) {
}
};
$.onExists = function(root, selector, cb) {
$.onExists = function (root, selector, cb) {
let el;
if (el = $(selector, root)) {
return cb(el);
}
var observer = new MutationObserver(function() {
var observer = new MutationObserver(function () {
if (el = $(selector, root)) {
observer.disconnect();
return cb(el);
}
});
return observer.observe(root, {childList: true, subtree: true});
return observer.observe(root, { childList: true, subtree: true });
};
$.addStyle = function(css, id, test='head') {
$.addStyle = function (css, id, test = 'head') {
const style = $.el('style',
{textContent: css});
{ textContent: css });
if (id != null) { style.id = id; }
$.onExists(doc, test, () => $.add(d.head, style));
return style;
};
$.addCSP = function(policy) {
$.addCSP = function (policy) {
const meta = $.el('meta', {
httpEquiv: 'Content-Security-Policy',
content: policy
content: policy
}
);
if (d.head) {
@ -335,23 +310,23 @@ $.addCSP = function(policy) {
}
};
$.x = function(path, root) {
$.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) {
$.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) {
$.addClass = function (el, ...classNames) {
for (var className of classNames) { el.classList.add(className); }
};
$.rmClass = function(el, ...classNames) {
$.rmClass = function (el, ...classNames) {
for (var className of classNames) { el.classList.remove(className); }
};
@ -362,13 +337,13 @@ $.hasClass = (el, className) => el.classList.contains(className);
$.rm = el => el?.remove();
$.rmAll = root => // https://gist.github.com/MayhemYDG/8646194
root.textContent = null;
root.textContent = null;
$.tn = s => d.createTextNode(s);
$.frag = () => d.createDocumentFragment();
$.nodes = function(nodes) {
$.nodes = function (nodes) {
if (!(nodes instanceof Array)) {
return nodes;
}
@ -389,55 +364,55 @@ $.before = (root, el) => root.parentNode.insertBefore($.nodes(el), root);
$.replace = (root, el) => root.parentNode.replaceChild($.nodes(el), root);
$.el = function(tag, properties, properties2) {
$.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) {
$.on = function (el, events, handler) {
for (var event of events.split(' ')) {
el.addEventListener(event, handler, false);
}
};
$.off = function(el, events, handler) {
$.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) {
$.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) {
$.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}));
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() {
(function () {
if (!/PaleMoon\//.test(navigator.userAgent) || (+GM_info?.version?.split('.')[0] < 2) || (typeof cloneInto !== 'undefined')) { return; }
try {
return new CustomEvent('x', {detail: {}});
return new CustomEvent('x', { detail: {} });
} catch (err) {
const unsafeConstructors = {
Object: unsafeWindow.Object,
Array: unsafeWindow.Array
Array: unsafeWindow.Array
};
var clone = function(obj) {
var clone = function (obj) {
let constructor;
if ((obj != null) && (typeof obj === 'object') && (constructor = unsafeConstructors[obj.constructor.name])) {
const obj2 = new constructor();
@ -447,7 +422,7 @@ if (platform === 'userscript') {
return obj;
}
};
return $.event = (event, detail, root=d) => root.dispatchEvent(new CustomEvent(event, {bubbles: true, cancelable: true, detail: clone(detail)}));
return $.event = (event, detail, root = d) => root.dispatchEvent(new CustomEvent(event, { bubbles: true, cancelable: true, detail: clone(detail) }));
}
})();
}
@ -458,25 +433,25 @@ if (!globalThis.chrome?.extension) {
$.open =
(GM?.openInTab != null) ?
GM.openInTab
: (typeof GM_openInTab !== 'undefined' && GM_openInTab !== null) ?
GM_openInTab
:
url => window.open(url, '_blank');
: (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) {
$.debounce = function (wait, fn) {
let lastCall = 0;
let timeout = null;
let that = null;
let args = null;
const exec = function() {
let timeout = null;
let that = null;
let args = null;
const exec = function () {
lastCall = Date.now();
return fn.apply(that, args);
};
return function() {
return function () {
args = arguments;
that = this;
if (lastCall < (Date.now() - wait)) {
@ -489,34 +464,17 @@ $.debounce = function(wait, fn) {
};
};
$.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);
};
$.queueTask = function (fn: VoidFunction) {
if (typeof queueMicrotask === 'function') {
return queueMicrotask(fn);
} else {
return setTimeout(fn, 0);
}
})();
};
$.global = function(fn, data) {
$.global = function (fn, data) {
if (doc) {
const script = $.el('script',
{textContent: `(${fn}).call(document.currentScript.dataset);`});
const script = $.el('script', { type: 'application/json' }, { textContent: JSON.stringify(data) });
if (data) { $.extend(script.dataset, data); }
$.add((d.head || doc), script);
$.rm(script);
@ -525,12 +483,12 @@ $.global = function(fn, data) {
// XXX dwb
try {
fn.call(data);
} catch (error) {}
} catch (error) { }
return data;
}
};
$.bytesToString = function(size) {
$.bytesToString = function (size: number) {
let unit = 0; // Bytes
while (size >= 1024) {
size /= 1024;
@ -542,40 +500,39 @@ $.bytesToString = function(size) {
// 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 ?
$.minmax = (value: number, min: number, max: number) => value < min ?
min
:
value > max ?
: 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) {
$.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]);
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() {
$.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() {
$.hasStorage = (function () {
try {
if (localStorage.getItem(g.NAMESPACE + 'hasStorage') === 'true') { return true; }
localStorage.setItem(g.NAMESPACE + 'hasStorage', 'true');
@ -585,13 +542,13 @@ $.hasStorage = (function() {
}
})();
$.item = function(key, val) {
$.item = function (key, val) {
const item = dict();
item[key] = val;
return item;
};
$.oneItemSugar = fn => (function(key, val, cb) {
$.oneItemSugar = fn => (function (key, val, cb) {
if (typeof key === 'string') {
return fn($.item(key, val), cb);
} else {
@ -601,7 +558,7 @@ $.oneItemSugar = fn => (function(key, val, cb) {
$.syncing = dict();
$.securityCheck = function(data) {
$.securityCheck = function (data) {
if (location.protocol !== 'https:') {
return delete data['Redirect to HTTPS'];
}
@ -611,10 +568,10 @@ if (platform === 'crx') {
// https://developer.chrome.com/extensions/storage.html
$.oldValue = {
local: dict(),
sync: dict()
sync: dict()
};
chrome.storage.onChanged.addListener(function(changes, area) {
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);
@ -626,17 +583,17 @@ if (platform === 'crx') {
}
});
$.sync = (key, cb) => $.syncing[key] = cb;
$.forceSync = function() { };
$.forceSync = function () { };
$.crxWorking = function() {
$.crxWorking = function () {
try {
if (chrome.runtime.getManifest()) {
return true;
}
} catch (error) {}
} 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.'});
{ 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;
@ -644,16 +601,16 @@ if (platform === 'crx') {
return false;
};
$.get = $.oneItemSugar(function(data, cb) {
$.get = $.oneItemSugar(function (data, cb) {
if (!$.crxWorking()) { return; }
const results = {};
const get = function(area) {
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) {
return chrome.storage[area].get(keys, function (result) {
let key;
result = dict.clone(result);
if (chrome.runtime.lastError) {
@ -679,16 +636,16 @@ if (platform === 'crx') {
return get('sync');
});
(function() {
(function () {
const items = {
local: dict(),
sync: 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;
unescape(encodeURIComponent(JSON.stringify(key))).length + unescape(encodeURIComponent(JSON.stringify(value))).length > chrome.storage.sync.QUOTA_BYTES_PER_ITEM;
$.delete = function(keys) {
$.delete = function (keys) {
if (!$.crxWorking()) { return; }
if (typeof keys === 'string') {
keys = [keys];
@ -702,11 +659,11 @@ if (platform === 'crx') {
};
const timeout = {};
var setArea = function(area, cb) {
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() {
return chrome.storage[area].set(data, function () {
let err;
let key;
if (err = chrome.runtime.lastError) {
@ -738,20 +695,20 @@ if (platform === 'crx') {
var setSync = debounce(SECOND, () => setArea('sync'));
$.set = $.oneItemSugar(function(data, cb) {
$.set = $.oneItemSugar(function (data, cb) {
if (!$.crxWorking()) { return; }
$.securityCheck(data);
$.extend(items.local, data);
return setArea('local', cb);
});
return $.clear = function(cb) {
return $.clear = function (cb) {
if (!$.crxWorking()) { return; }
items.local = dict();
items.sync = dict();
items.sync = dict();
let count = 2;
let err = null;
const done = function() {
let err = null;
const done = function () {
if (chrome.runtime.lastError) {
c.error(chrome.runtime.lastError.message);
}
@ -785,19 +742,20 @@ if (platform === 'crx') {
$.sync = (key, cb) => $.syncing[key] = cb;
$.forceSync = function() {};
$.forceSync = function () { };
$.delete = function(keys, cb) {
$.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));
for (key of keys) {
result.push(GM.deleteValue(g.NAMESPACE + key));
}
return result;
})()).then(function() {
})()).then(function () {
const items = dict();
for (key of keys) { items[key] = undefined; }
$.syncChannel.postMessage(items);
@ -805,9 +763,9 @@ if (platform === 'crx') {
});
};
$.get = $.oneItemSugar(function(items, 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) {
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) {
@ -818,7 +776,7 @@ if (platform === 'crx') {
});
});
$.set = $.oneItemSugar(function(items, cb) {
$.set = $.oneItemSugar(function (items, cb) {
$.securityCheck(items);
return Promise.all((() => {
const result = [];
@ -827,13 +785,13 @@ if (platform === 'crx') {
result.push(GM.setValue(g.NAMESPACE + key, JSON.stringify(val)));
}
return result;
})()).then(function() {
})()).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));
$.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) {
@ -841,7 +799,7 @@ if (platform === 'crx') {
}
if (typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) {
$.getValue = GM_getValue;
$.getValue = GM_getValue;
$.listValues = () => GM_listValues(); // error when called if missing
} else if ($.hasStorage) {
$.getValue = key => localStorage.getItem(key);
@ -855,23 +813,23 @@ if (platform === 'crx') {
return result;
})();
} else {
$.getValue = function() {};
$.getValue = function () { };
$.listValues = () => [];
}
if (typeof GM_addValueChangeListener !== 'undefined' && GM_addValueChangeListener !== null) {
$.setValue = GM_setValue;
$.setValue = GM_setValue;
$.deleteValue = GM_deleteValue;
} else if (typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) {
$.oldValue = dict();
$.setValue = function(key, val) {
$.setValue = function (key, val) {
GM_setValue(key, val);
if (key in $.syncing) {
$.oldValue[key] = val;
$.oldValue[key] = val;
if ($.hasStorage) { return localStorage.setItem(key, val); } // for `storage` events
}
};
$.deleteValue = function(key) {
$.deleteValue = function (key) {
GM_deleteValue(key);
if (key in $.syncing) {
delete $.oldValue[key];
@ -881,37 +839,37 @@ if (platform === 'crx') {
if (!$.hasStorage) { $.cantSync = true; }
} else if ($.hasStorage) {
$.oldValue = dict();
$.setValue = function(key, val) {
if (key in $.syncing) { $.oldValue[key] = val; }
$.setValue = function (key, val) {
if (key in $.syncing) { $.oldValue[key] = val; }
return localStorage.setItem(key, val);
};
$.deleteValue = function(key) {
$.deleteValue = function (key) {
if (key in $.syncing) { delete $.oldValue[key]; }
return localStorage.removeItem(key);
};
} else {
$.setValue = function() {};
$.deleteValue = function() {};
$.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) {
$.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() {};
$.forceSync = function () { };
} else if ((typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) || $.hasStorage) {
$.sync = function(key, cb) {
$.sync = function (key, cb) {
key = g.NAMESPACE + key;
$.syncing[key] = cb;
return $.oldValue[key] = $.getValue(key);
};
(function() {
const onChange = function({key, newValue}) {
(function () {
const onChange = function ({ key, newValue }) {
let cb;
if (!(cb = $.syncing[key])) { return; }
if (newValue != null) {
@ -926,20 +884,20 @@ if (platform === 'crx') {
};
$.on(window, 'storage', onChange);
return $.forceSync = function(key) {
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)});
return onChange({ key, newValue: $.getValue(key) });
};
})();
} else {
$.sync = function() {};
$.forceSync = function() {};
$.sync = function () { };
$.forceSync = function () { };
}
$.delete = function(keys) {
$.delete = function (keys) {
if (!(keys instanceof Array)) {
keys = [keys];
}
@ -948,9 +906,9 @@ if (platform === 'crx') {
}
};
$.get = $.oneItemSugar((items, cb) => $.queueTask($.getSync, items, cb));
$.get = $.oneItemSugar((items, cb) => $.queueTask(() => $.getSync(items, cb)));
$.getSync = function(items, cb) {
$.getSync = function (items, cb) {
for (var key in items) {
var val2;
if (val2 = $.getValue(g.NAMESPACE + key)) {
@ -967,9 +925,9 @@ if (platform === 'crx') {
return cb(items);
};
$.set = $.oneItemSugar(function(items, cb) {
$.set = $.oneItemSugar(function (items, cb) {
$.securityCheck(items);
return $.queueTask(function() {
return $.queueTask(function () {
for (var key in items) {
var value = items[key];
$.setValue(g.NAMESPACE + key, JSON.stringify(value));
@ -978,14 +936,14 @@ if (platform === 'crx') {
});
});
$.clear = function(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) {}
} catch (error) { }
return cb?.();
};
}