types and bug fixes

This commit is contained in:
Lalle 2023-05-05 00:43:45 +02:00
parent a1e1a2c6b1
commit e4dda4dbf8
No known key found for this signature in database
GPG Key ID: A6583D207A8F6B0D
16 changed files with 261 additions and 196 deletions

31
.vscode/settings.json vendored
View File

@ -1,16 +1,17 @@
{
"search.exclude": {
"*.jst": true,
"*.md": true,
"*.yaml": true,
"*.yml": true,
"*.css": true,
"package*.json": true
},
"typescript.tsdk": "node_modules/typescript/lib",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll": true
}
}
"search.exclude": {
"*.jst": true,
"*.md": true,
"*.yaml": true,
"*.yml": true,
"*.css": true,
"non-property-errors-logs.txt": true,
"package*.json": true
},
"typescript.tsdk": "node_modules/typescript/lib",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll": true
}
}

View File

@ -125,7 +125,7 @@ const Redirect = {
$.set(items)
$.extend(Conf, items)
Redirect.selectArchives()
return cb?.()
return cb
},
to(dest, data) {

View File

@ -378,7 +378,7 @@ const Filter = {
}
},
addFilter(type: FilterType, re: string, cb?: () => void) {
addFilter(type: FilterType, re: string, cb?: Callbacks) {
if (!$.hasOwn(Config.filter, type)) { return }
return $.get(type, Conf[type], function (item) {
let save = item[type]
@ -392,7 +392,7 @@ const Filter = {
})
},
removeFilters(type: FilterType, res: FilterObj[] | Map<string, FilterObj[]>, cb?: () => void) {
removeFilters(type: FilterType, res: FilterObj[] | Map<string, FilterObj[]>, cb?: Callbacks) {
return $.get(type, Conf[type], function (item) {
let save = item[type]
const filterArray = Array.isArray(res) ? res : [...res.values()].flat()

View File

@ -3,13 +3,7 @@ import { Conf, g } from "../globals/globals"
import $ from "../platform/$"
import { dict, HOUR } from "../platform/helpers"
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS104: Avoid inline assignments
* DS205: Consider reworking code to avoid use of IIFEs
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
const BoardConfig = {
cbs: [],
@ -19,7 +13,7 @@ const BoardConfig = {
const now = Date.now()
if (now - (2 * HOUR) >= ((middle = Conf['boardConfig'].lastChecked || 0)) || middle > now) {
return $.ajax(`${location.protocol}//a.4cdn.org/boards.json`,
{ onloadend: this.load })
{ onloadend: this.load }, dict())
} else {
const { boards } = Conf['boardConfig']
return this.set(boards)

View File

@ -1,13 +1,7 @@
import { Conf, g } from "../globals/globals"
import $ from "../platform/$"
/*
* decaffeinate suggestions:
* DS101: Remove unnecessary use of Array.from
* DS102: Remove unnecessary code created because of implicit returns
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
const Get = {
url(type, IDs, ...args) {
let f, site

View File

@ -12,13 +12,7 @@ import Get from "./Get"
import Settings from "./Settings"
import UI from "./UI"
/*
* decaffeinate suggestions:
* DS101: Remove unnecessary use of Array.from
* DS102: Remove unnecessary code created because of implicit returns
* DS104: Avoid inline assignments
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
const Header = {
init() {
$.onExists(doc, 'body', () => {
@ -404,7 +398,7 @@ const Header = {
Header.setBarFixed(this.checked)
Conf['Fixed Header'] = this.checked
return $.set('Fixed Header', this.checked)
return $.set('Fixed Header', this.checked, true)
},
setShortcutIcons(show) {

View File

@ -71,9 +71,9 @@ const Settings = {
return localStorage.setItem('4chan-settings', JSON.stringify(settings))
} catch (error) {
return Object.defineProperty(window, 'Config', {value: {disableAll: true}})
}})
}}, true)
} else {
return $.global(() => Object.defineProperty(window, 'Config', {value: {disableAll: true}}))
return $.global(() => Object.defineProperty(window, 'Config', {value: {disableAll: true}}), true)
}
}
},
@ -81,7 +81,7 @@ const Settings = {
open(openSection) {
let dialog, sectionToOpen
if (Settings.dialog) { return }
$.event('CloseMenu')
$.event('CloseMenu', null)
Settings.dialog = (dialog = $.el('div',
{ id: 'overlay' }
@ -258,7 +258,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri
inputs[key].checked = val
inputs[key].parentNode.parentNode.dataset.checked = val
}
})
}, 'Settings')
const div = $.el('div',
{innerHTML: '<button></button><span class="description">: Clear manually-hidden threads and posts on all boards. Reload the page to apply.'})
@ -299,6 +299,8 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri
}
}
return button.textContent = `Hidden: ${hiddenNum}`
}, function() {
return button.textContent = 'Hidden: 0'
})
$.on(button, 'click', function() {
this.textContent = 'Hidden: 0'
@ -312,7 +314,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri
localStorage.removeItem(`4chan-hide-t-${boardID}`)
}
}
return ($.delete(['hiddenThreads', 'hiddenPosts']))
return ($.delete(['hiddenThreads', 'hiddenPosts'], dict())
})
})
return $.after($('input[name="Stubs"]', section).parentNode.parentNode, div)
@ -326,7 +328,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri
// Don't export cached JSON data.
delete Conf2['boardConfig']
return (Settings.downloadExport({version: g.VERSION, date: Date.now(), Conf: Conf2}))
})
}, 'Settings')
},
downloadExport(data) {
@ -918,6 +920,9 @@ vp-replace
Settings[key].call(input)
}
}
}, function(err) {
if (err) { return }
return $.id('lastarchivecheck').textContent = new Date(Conf['lastarchivecheck']).toLocaleString()
})
const listImageHost = $.id('list-fourchanImageHost')
@ -944,6 +949,9 @@ vp-replace
$.extend(Conf, itemsArchive)
Redirect.selectArchives()
return Settings.addArchiveTable(section)
}, function(err) {
if (err) { return }
return $.id('lastarchivecheck').textContent = new Date(Conf['lastarchivecheck']).toLocaleString()
})
const boardSelect = $('#archive-board-select', section)
@ -1069,7 +1077,7 @@ vp-replace
saveSelectedArchive() {
return $.get('selectedArchives', Conf['selectedArchives'], ({selectedArchives}) => {
(selectedArchives[this.dataset.boardid] || (selectedArchives[this.dataset.boardid] = dict()))[this.dataset.type] = JSON.parse(this.value)
$.set('selectedArchives', selectedArchives)
$.set('selectedArchives', selectedArchives, () => Conf['selectedArchives'] = selectedArchives)
Conf['selectedArchives'] = selectedArchives
return Redirect.selectArchives()
})
@ -1157,7 +1165,7 @@ vp-replace
const val = items[key]
inputs[key].value = val
}
})
}, true)
},
keybind(e) {

View File

@ -1,13 +1,10 @@
import Callbacks from "../classes/Callbacks"
import Post from "../classes/Post"
import Thread from "../classes/Thread"
import Get from "../General/Get"
import { Conf, g } from "../globals/globals"
import $ from "../platform/$"
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
const IDPostCount = {
init() {
if ((g.VIEW !== 'thread') || !Conf['Count Posts by ID']) { return }
@ -21,19 +18,19 @@ const IDPostCount = {
})
},
node() {
node(): void {
if (this.nodes.uniqueID && (this.thread === IDPostCount.thread)) {
return $.on(this.nodes.uniqueID, 'mouseover', IDPostCount.count)
}
},
count() {
count(): string {
const { uniqueID } = Get.postFromNode(this).info
let n = 0
IDPostCount.thread.posts.forEach(function (post) {
IDPostCount.thread.posts.forEach((post: Post) => {
if (post.info.uniqueID === uniqueID) { return n++ }
})
return this.title = `${n} post${n === 1 ? '' : 's'} by this ID`
}
}
export default IDPostCount
export default IDPostCount

View File

@ -1,18 +1,22 @@
import Callbacks from "../classes/Callbacks"
import { Conf,g } from "../globals/globals"
import type Post from "../classes/Post"
import { Conf, g } from "../globals/globals"
const ThreadLinks = {
init(): void {
if ((g.VIEW !== 'index') || !Conf['Open Threads in New Tab']) { return }
Callbacks.Post.push({
const postCallback: Post = {
name: 'Thread Links',
cb: this.node.bind(this)
})
Callbacks.CatalogThread.push({
cb: this.node.bind(this)
}
const catalogThreadCallback: Post = {
name: 'Thread Links',
cb: this.catalogNode.bind(this)
})
cb: this.catalogNode.bind(this)
}
Callbacks.Post.push(postCallback)
Callbacks.CatalogThread.push(catalogThreadCallback)
},
node(): void {
@ -29,4 +33,4 @@ const ThreadLinks = {
}
}
export default ThreadLinks
export default ThreadLinks

View File

@ -2,31 +2,54 @@ import Callbacks from "../classes/Callbacks"
import { Conf, g } from "../globals/globals"
import $ from "../platform/$"
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
const Time = {
init() {
if (!['index', 'thread', 'archive'].includes(g.VIEW) || !Conf['Time Formatting']) { return }
interface TimeFormatters {
a(): string;
A(): string;
b(): string;
B(): string;
d(): string;
e(): number;
H(): string;
I(): string;
k(): number;
l(): number;
m(): string;
M(): string;
p(): string;
P(): string;
S(): string;
y(): string;
Y(): number;
'%'(): string;
}
return Callbacks.Post.push({
const Time = {
init(): void {
if (!['index', 'thread', 'archive'].includes(g.VIEW) || !Conf['Time Formatting']) {
return
}
Callbacks.Post.push({
name: 'Time Formatting',
cb: this.node
cb: this.node,
})
},
node() {
if (!this.info.date || this.isClone) { return }
node(): void {
if (!this.info.date || this.isClone) {
return
}
const { textContent } = this.nodes.date
return this.nodes.date.textContent = textContent.match(/^\s*/)[0] + Time.format(Conf['time'], this.info.date) + textContent.match(/\s*$/)[0]
this.nodes.date.textContent =
textContent.match(/^\s*/)[0] +
Time.format(Conf['time'], this.info.date) +
textContent.match(/\s*$/)[0]
},
format(formatString, date) {
return formatString.replace(/%(.)/g, function (s, c) {
format(formatString: string, date: Date): string {
return formatString.replace(/%(.)/g, (s: string, c: string): string => {
if ($.hasOwn(Time.formatters, c)) {
return Time.formatters[c].call(date)
return (Time.formatters as TimeFormatters)[c].call(date)
} else {
return s
}
@ -40,7 +63,7 @@ const Time = {
'Wednesday',
'Thursday',
'Friday',
'Saturday'
'Saturday',
],
month: [
@ -55,49 +78,95 @@ const Time = {
'September',
'October',
'November',
'December'
'December',
],
localeFormat(date, options, defaultValue) {
localeFormat(date: Date, options: Intl.DateTimeFormatOptions, defaultValue: string): string {
if (Conf['timeLocale']) {
try {
return Intl.DateTimeFormat(Conf['timeLocale'], options).format(date)
} catch (error) { }
} catch (error) {/* empty */ }
}
return defaultValue
},
localeFormatPart(date, options, part, defaultValue) {
localeFormatPart(
date: Date,
options: Intl.DateTimeFormatOptions,
part: string,
defaultValue: string,
): string {
if (Conf['timeLocale']) {
try {
const parts = Intl.DateTimeFormat(Conf['timeLocale'], options).formatToParts(date)
return parts.map(function (x) { if (x.type === part) { return x.value } else { return '' } }).join('')
} catch (error) { }
return parts
.map((x) => (x.type === part ? x.value : ''))
.join('')
} catch (error) { /* empty */ }
}
return defaultValue
},
zeroPad(n) { if (n < 10) { return `0${n}` } else { return n } },
zeroPad(n: number): string | number {
return n < 10 ? `0${n}` : n
},
formatters: {
a() { return Time.localeFormat(this, { weekday: 'short' }, Time.day[this.getDay()].slice(0, 3)) },
A() { return Time.localeFormat(this, { weekday: 'long' }, Time.day[this.getDay()]) },
b() { return Time.localeFormat(this, { month: 'short' }, Time.month[this.getMonth()].slice(0, 3)) },
B() { return Time.localeFormat(this, { month: 'long' }, Time.month[this.getMonth()]) },
d() { return Time.zeroPad(this.getDate()) },
e() { return this.getDate() },
H() { return Time.zeroPad(this.getHours()) },
I() { return Time.zeroPad((this.getHours() % 12) || 12) },
k() { return this.getHours() },
l() { return (this.getHours() % 12) || 12 },
m() { return Time.zeroPad(this.getMonth() + 1) },
M() { return Time.zeroPad(this.getMinutes()) },
p() { return Time.localeFormatPart(this, { hour: 'numeric', hour12: true }, 'dayperiod', (this.getHours() < 12 ? 'AM' : 'PM')) },
P() { return Time.formatters.p.call(this).toLowerCase() },
S() { return Time.zeroPad(this.getSeconds()) },
y() { return this.getFullYear().toString().slice(2) },
Y() { return this.getFullYear() },
'%'() { return '%' }
}
a(): string {
return Time.localeFormat(this, { weekday: 'short' }, Time.day[this.getDay()].slice(0, 3))
},
A(): string {
return Time.localeFormat(this, { weekday: 'long' }, Time.day[this.getDay()])
},
b(): string {
return Time.localeFormat(this, { month: 'short' }, Time.month[this.getMonth()].slice(0, 3))
},
B(): string {
return Time.localeFormat(this, { month: 'long' }, Time.month[this.getMonth()])
},
d(): string | number {
return Time.zeroPad(this.getDate())
},
e(): number {
return this.getDate()
},
H(): string | number {
return Time.zeroPad(this.getHours())
},
I(): string | number {
return Time.zeroPad((this.getHours() % 12) || 12)
},
k(): number {
return this.getHours()
},
l(): number {
return (this.getHours() % 12) || 12
},
m(): string | number {
return Time.zeroPad(this.getMonth() + 1)
},
M(): string | number {
return Time.zeroPad(this.getMinutes())
},
p(): string {
return Time.localeFormatPart(this, { hour: 'numeric', hour12: true }, 'dayperiod', this.getHours() < 12 ? 'AM' : 'PM')
},
P(): string {
return Time.formatters.p.call(this).toLowerCase()
},
S(): string | number {
return Time.zeroPad(this.getSeconds())
},
y(): string {
return this.getFullYear().toString().slice(2)
},
Y(): number {
return this.getFullYear()
},
'%'(): string {
return '%'
},
},
}
export default Time
export default Time

View File

@ -10,12 +10,11 @@ interface QRPostDetail {
}
const Tinyboard = {
init() {
init(): void {
if (g.SITE.software !== 'tinyboard') { return }
if (g.VIEW === 'thread') {
return Main.ready(() => $.global(function() {
let base
const { boardID, threadID } = document.currentScript.dataset
return Main.ready(() => $.global(function (data: { boardID: string, threadID: number }) {
const { boardID, threadID } = data
const threadIdNum = +threadID
const form = document.querySelector('form[name="post"]') as HTMLFormElement
window.$(document).ajaxComplete((event, request, settings) => {
@ -29,16 +28,15 @@ const Tinyboard = {
if (redirect && (originalNoko != null) && !originalNoko && !noko) {
detail.redirect = redirect
}
} catch (error) {}
} catch (error) { }
event = new CustomEvent('QRPostSuccessful', { bubbles: true, detail })
return document.dispatchEvent(event)
})
const originalNoko = window.tb_settings?.ajax?.always_noko_replies;
((base = window.tb_settings || (window.tb_settings = {})).ajax || (base.ajax = {})).always_noko_replies = true
}
, { boardID: g.BOARD.ID, threadID: g.THREADID }))
((window.tb_settings || (window.tb_settings = {})).ajax || (window.tb_settings.ajax = {})).always_noko_replies = true
}, { boardID: g.BOARD.ID, threadID: g.THREADID }))
}
}
}
export default Tinyboard
export default Tinyboard

View File

@ -2,25 +2,22 @@ import { d, g } from "../globals/globals"
import $ from "../platform/$"
import QR from "./QR"
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
const CaptchaT = {
init() {
if (d.cookie.indexOf('pass_enabled=1') >= 0) { return }
if (!(this.isEnabled = !!$('#t-root') || !$.id('postForm'))) { return }
const root = $.el('div', {className: 'captcha-root'})
this.nodes = {root}
const root = $.el('div', { className: 'captcha-root' })
this.nodes = { root }
$.addClass(QR.nodes.el, 'has-captcha', 'captcha-t')
return $.after(QR.nodes.com.parentNode, root)
},
moreNeeded() {
return this.isEnabled && !this.nodes.container
},
currentThread: null,
getThread() {
let threadID
@ -30,26 +27,28 @@ const CaptchaT = {
} else {
threadID = '' + QR.posts[0].thread
}
return {boardID, threadID}
return { boardID, threadID }
},
setup(focus) {
setup(focus?: boolean) {
if (!this.isEnabled) { return }
if (!this.nodes.container) {
this.nodes.container = $.el('div', {className: 'captcha-container'})
this.nodes.container = $.el('div', { className: 'captcha-container' })
$.prepend(this.nodes.root, this.nodes.container)
CaptchaT.currentThread = CaptchaT.getThread()
$.global(function() {
$.global(function () {
const el = document.querySelector('#qr .captcha-container')
window.TCaptcha.init(el, this.boardID, +this.threadID)
return window.TCaptcha.setErrorCb(err => window.dispatchEvent(new CustomEvent('CreateNotification', {detail: {
type: 'warning',
content: '' + err
}})
return window.TCaptcha.setErrorCb(err => window.dispatchEvent(new CustomEvent('CreateNotification', {
detail: {
type: 'warning',
content: '' + err
}
})
))
}
, CaptchaT.currentThread)
, CaptchaT.currentThread)
}
if (focus) {
@ -59,14 +58,14 @@ const CaptchaT = {
destroy() {
if (!this.isEnabled || !this.nodes.container) { return }
$.global(() => window.TCaptcha.destroy())
$.global(() => window.TCaptcha.destroy(), CaptchaT.currentThread)
$.rm(this.nodes.container)
return delete this.nodes.container
},
updateThread() {
if (!this.isEnabled) { return }
const {boardID, threadID} = (CaptchaT.currentThread || {})
const { boardID, threadID } = (CaptchaT.currentThread || {})
const newThread = CaptchaT.getThread()
if ((newThread.boardID !== boardID) || (newThread.threadID !== threadID)) {
CaptchaT.destroy()
@ -91,7 +90,7 @@ const CaptchaT = {
setUsed() {
if (!this.isEnabled) { return }
if (this.nodes.container) {
return $.global(() => window.TCaptcha.clearChallenge())
return $.global(() => window.TCaptcha.clearChallenge(), CaptchaT.currentThread)
}
},

View File

@ -65,7 +65,7 @@ export default class DataBoard {
this.data.version = (this.data.version || 0) + 1
return $.set(this.key, this.data, () => {
if (needSync) { this.sync?.() }
return cb?.()
return cb
})
})
}
@ -77,7 +77,7 @@ export default class DataBoard {
for (const change of DataBoard.changes) { change() }
this.sync?.()
}
return cb?.()
return cb
})
}

View File

@ -3,6 +3,8 @@ import { d } from "../globals/globals"
import $ from "../platform/$"
import { SECOND } from "../platform/helpers"
type NoticeType = "success" | "warning" | "error"
export default class Notice {
private el: HTMLDivElement
private timeout?: number
@ -10,7 +12,7 @@ export default class Notice {
private closed = false
constructor(
private type: string,
private type: NoticeType,
private content: string | Node,
timeout?: number,
onclose?: () => void
@ -36,7 +38,7 @@ export default class Notice {
this.onclose = onclose
}
private setType(type: string) {
private setType(type: NoticeType) {
this.el.className = `notification ${type}`
}
@ -69,4 +71,4 @@ export default class Notice {
$.rm(this.el)
this.onclose?.()
}
}
}

View File

@ -4,7 +4,6 @@ import Board from "./Board"
import Post from "./Post"
import SimpleDict from "./SimpleDict"
export default class Thread {
ID: number | string
OP: Post
@ -26,7 +25,6 @@ export default class Thread {
json: JSON
catalogView: Node
nodes: { root: Post }
toString() { return this.ID }
constructor(ID: number | string, board: Board) {
this.board = board
@ -50,8 +48,7 @@ export default class Thread {
this.OP = null
this.catalogView = null
this.nodes =
{ root: null }
this.nodes = { root: null }
this.board.threads.push(this.ID, this)
g.threads.push(this.fullID, this)
@ -66,14 +63,18 @@ export default class Thread {
}
icon.title = `This thread is on page ${pageNum} in the original index.`
icon.textContent = `[${pageNum}]`
if (this.catalogView) { return this.catalogView.nodes.pageCount.textContent = pageNum }
if (this.catalogView) { this.catalogView.nodes.pageCount.textContent = pageNum }
}
setCount(type: string, count: number, reachedLimit: boolean) {
if (!this.catalogView) { return }
const el = this.catalogView.nodes[`${type}Count`]
el.textContent = count
return (reachedLimit ? $.addClass : $.rmClass)(el, 'warning')
if (reachedLimit) {
$.addClass(el, 'warning')
} else {
$.rmClass(el, 'warning')
}
}
setStatus(type: string, status: boolean) {
@ -83,7 +84,7 @@ export default class Thread {
if (!this.OP) { return }
this.setIcon('Sticky', this.isSticky)
this.setIcon('Closed', this.isClosed && !this.isArchived)
return this.setIcon('Archived', this.isArchived)
this.setIcon('Archived', this.isArchived)
}
setIcon(type: string, status: boolean) {
@ -118,21 +119,25 @@ export default class Thread {
}
kill() {
return this.isDead = true
this.isDead = true
}
collect() {
let n = 0
this.posts.forEach(function (post) {
this.posts.forEach(post => {
if (post.clones.length) {
return n++
n++
} else {
return post.collect()
post.collect()
}
})
if (!n) {
g.threads.rm(this.fullID)
return this.board.threads.rm(this)
this.board.threads.rm(this)
}
}
}
toString() {
return this.ID
}
}

View File

@ -93,7 +93,6 @@ $.ajaxPage = function (url: string, options: AjaxPageOptions) {
xhr.send(form)
return xhr
}
$.cache = dict()
$.ready = function (fc: () => void) {
if (d.readyState !== 'loading') {
@ -293,7 +292,7 @@ $.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: string
const { timeout, ajax } = options
const params = []
@ -315,42 +314,43 @@ $.whenModified = function (url, bucket, cb, options) {
headers
})
return r
};
}
(function () {
$.cache = function (url, cb, options = {}) {
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
let req
const { ajax } = options
if (req = reqs[url]) {
if (req.callbacks) {
req.callbacks.push(cb)
} else {
$.queueTask(() => cb.call(req, { isCached: true }))
}
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 req
}
return $.cleanCache = function (testf) {
for (const url in reqs) {
if (testf(url)) {
delete reqs[url]
}
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
}
$.cleanCache = function (testf) {
const reqs = dict()
for (const url in reqs) {
if (testf(url)) {
delete reqs[url]
}
}
})()
}
$.cb = {
checked() {
@ -788,7 +788,7 @@ if (platform === 'crx') {
c.error(err.message)
setTimeout(setArea, MINUTE, area)
timeout[area] = Date.now() + MINUTE
return cb?.(err)
return cb
}
delete timeout[area]
@ -807,7 +807,7 @@ if (platform === 'crx') {
return result
})()))
}
return cb?.()
return cb
})
}
@ -831,7 +831,7 @@ if (platform === 'crx') {
c.error(chrome.runtime.lastError.message)
}
if (err == null) { err = chrome.runtime.lastError }
if (!--count) { return cb?.(err) }
if (!--count) { return cb }
}
chrome.storage.local.clear(done)
return chrome.storage.sync.clear(done)
@ -862,7 +862,7 @@ if (platform === 'crx') {
$.forceSync = function () { }
$.delete = function (keys, cb) {
$.delete = function (keys: string | string[], cb: Callbacks) {
let key
if (!(keys instanceof Array)) {
keys = [keys]
@ -877,7 +877,7 @@ if (platform === 'crx') {
const items = dict()
for (key of keys) { items[key] = undefined }
$.syncChannel.postMessage(items)
return cb?.()
return cb
})
}
@ -905,7 +905,7 @@ if (platform === 'crx') {
return result
})()).then(function () {
$.syncChannel.postMessage(items)
return cb?.()
return cb
})
})
@ -1044,7 +1044,7 @@ if (platform === 'crx') {
const value = items[key]
$.setValue(g.NAMESPACE + key, JSON.stringify(value), cb)
}
return cb?.()
return cb
})
})
@ -1056,7 +1056,7 @@ if (platform === 'crx') {
try {
$.delete($.listValues().map(key => key.replace(g.NAMESPACE, '')), cb)
} catch (error) { }
return cb?.()
return cb
}
}
}