This commit is contained in:
Lalle 2023-05-07 18:13:05 +02:00
parent f869225c41
commit 55dd58b8c6
No known key found for this signature in database
GPG Key ID: A6583D207A8F6B0D
18 changed files with 167 additions and 174 deletions

View File

@ -38,7 +38,7 @@ const Get = {
if (index) { return post.clones[+index] } else { return post } if (index) { return post.clones[+index] } else { return post }
}, },
postFromNode(root): Post { postFromNode(root): Post {
return Get.postFromRoot($.x(`ancestor-or-self::${g.SITE.xpath.postContainer}[1]`, root)) return Get.postFromRoot($.x(`ancestor-or-self::${g.SITE.xpath.postContainer}[1]`, root)) as Post
}, },
postDataFromLink(link) { postDataFromLink(link) {
let boardID, postID, threadID let boardID, postID, threadID

View File

@ -3,11 +3,7 @@ import { Conf, doc } from "../globals/globals"
import $ from "../platform/$" import $ from "../platform/$"
import $$ from "../platform/$$" 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 AntiAutoplay = { const AntiAutoplay = {
init() { init() {
if (!Conf['Disable Autoplaying Sounds']) { return } if (!Conf['Disable Autoplaying Sounds']) { return }
@ -31,10 +27,10 @@ const AntiAutoplay = {
}, },
node() { node() {
return AntiAutoplay.process(this.nodes.comment) return AntiAutoplay.process(this.node())
}, },
process(root) { process(root: HTMLElement) {
for (const iframe of $$('iframe[src*="youtube"][src*="autoplay=1"]', root)) { for (const iframe of $$('iframe[src*="youtube"][src*="autoplay=1"]', root)) {
AntiAutoplay.processVideo(iframe, 'src') AntiAutoplay.processVideo(iframe, 'src')
} }
@ -43,7 +39,7 @@ const AntiAutoplay = {
} }
}, },
processVideo(el, attr) { processVideo(el: HTMLIFrameElement | HTMLObjectElement, attr: 'src' | 'data') {
el[attr] = el[attr].replace(/\?autoplay=1&?/, '?').replace('&autoplay=1', '') el[attr] = el[attr].replace(/\?autoplay=1&?/, '?').replace('&autoplay=1', '')
if (window.getComputedStyle(el).display === 'none') { el.style.display = 'block' } if (window.getComputedStyle(el).display === 'none') { el.style.display = 'block' }
return $.addClass(el, 'autoplay-removed') return $.addClass(el, 'autoplay-removed')

View File

@ -6,13 +6,7 @@ import $ from "../platform/$"
import $$ from "../platform/$$" import $$ from "../platform/$$"
import { dict } from "../platform/helpers" import { dict } from "../platform/helpers"
/*
* 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 Banner = { const Banner = {
init() { init() {
if (Conf['Custom Board Titles']) { if (Conf['Custom Board Titles']) {

View File

@ -81,7 +81,7 @@ const CatalogLinks = {
}, },
toggle() { toggle() {
$.event('CloseMenu') $.event('CloseMenu', { menu: Header.menu })
$.set('Header catalog links', this.checked) $.set('Header catalog links', this.checked)
return CatalogLinks.set(this.checked) return CatalogLinks.set(this.checked)
}, },

View File

@ -14,7 +14,7 @@ export default class Board {
config: any config: any
toString() { return this.ID } toString() { return this.ID }
constructor(ID) { constructor(ID: string) {
this.ID = ID this.ID = ID
this.boardID = this.ID this.boardID = this.ID
this.siteID = g.SITE.ID this.siteID = g.SITE.ID
@ -22,7 +22,7 @@ export default class Board {
this.posts = new SimpleDict() this.posts = new SimpleDict()
this.config = BoardConfig.boards?.[this.ID] || {} this.config = BoardConfig.boards?.[this.ID] || {}
g.boards[this] = this g.boards[this.ID] = this
} }
cooldowns() { cooldowns() {

View File

@ -26,7 +26,7 @@ export default class Callbacks {
return this[name] = cb return this[name] = cb
} }
execute(node, keys = this.keys, force = false) { execute(node: Post, keys = this.keys, force = false) {
let errors let errors
if (node.callbacksExecuted && !force) { return } if (node.callbacksExecuted && !force) { return }
node.callbacksExecuted = true node.callbacksExecuted = true

View File

@ -4,8 +4,8 @@ import Callbacks from "./Callbacks"
export default class Connection { export default class Connection {
target: any target: Window | HTMLIFrameElement
origin: any origin: string
cb: Callbacks cb: Callbacks
constructor(target: Window, origin: string, cb: Callbacks) { constructor(target: Window, origin: string, cb: Callbacks) {
this.send = this.send.bind(this) this.send = this.send.bind(this)

View File

@ -1,6 +1,7 @@
import { Conf, d, g } from "../globals/globals" import { Conf, d, g } from "../globals/globals"
import $ from "../platform/$" import $ from "../platform/$"
import { dict, HOUR } from "../platform/helpers" import { dict, HOUR } from "../platform/helpers"
import { CacheOptions } from "../types/globals"
/* /*
* decaffeinate suggestions: * decaffeinate suggestions:
@ -20,7 +21,6 @@ export default class DataBoard {
static keys: string[] static keys: string[]
static changes: string[] static changes: string[]
key: string key: string
sync: VoidFunction
data: any data: any
static initClass() { static initClass() {
this.keys = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads', 'watcherLastModified', 'customTitles'] this.keys = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads', 'watcherLastModified', 'customTitles']
@ -98,7 +98,7 @@ export default class DataBoard {
} else if (threadID) { } else if (threadID) {
if (!this.data[siteID].boards[boardID]) { return } if (!this.data[siteID].boards[boardID]) { return }
delete this.data[siteID].boards[boardID][threadID] delete this.data[siteID].boards[boardID][threadID]
return this.deleteIfEmpty({ siteID, boardID }) return this.deleteIfEmpty({ siteID, boardID, threadID: null })
} else { } else {
return delete this.data[siteID].boards[boardID] return delete this.data[siteID].boards[boardID]
} }
@ -111,7 +111,7 @@ export default class DataBoard {
if (threadID) { if (threadID) {
if (!Object.keys(this.data[siteID].boards[boardID][threadID]).length) { if (!Object.keys(this.data[siteID].boards[boardID][threadID]).length) {
delete this.data[siteID].boards[boardID][threadID] delete this.data[siteID].boards[boardID][threadID]
return this.deleteIfEmpty({ siteID, boardID }) return this.deleteIfEmpty({ siteID, boardID, threadID: null })
} }
} else if (!Object.keys(this.data[siteID].boards[boardID]).length) { } else if (!Object.keys(this.data[siteID].boards[boardID]).length) {
return delete this.data[siteID].boards[boardID] return delete this.data[siteID].boards[boardID]
@ -157,7 +157,10 @@ export default class DataBoard {
setLastChecked(key = 'lastChecked') { setLastChecked(key = 'lastChecked') {
return this.save(() => { return this.save(() => {
return this.data[key] = Date.now() return this.data[key] = Date.now()
}) }, () => {
return this.sync?.()
}
)
} }
get({ siteID, boardID, threadID, postID, defaultValue }) { get({ siteID, boardID, threadID, postID, defaultValue }) {
@ -203,21 +206,21 @@ export default class DataBoard {
} }
} }
ajaxClean(boardID) { ajaxClean(boardID: string) {
const that = this const that = this
const siteID = g.SITE.ID const siteID = g.SITE.ID
const threadsList = g.SITE.urls.threadsListJSON?.({ siteID, boardID }) const threadsList = g.SITE.urls.threadsListJSON?.({ siteID, boardID })
if (!threadsList) { return } if (!threadsList) { return }
return $.cache(threadsList, function () { return $.cache(threadsList, () => {
if (this.status !== 200) { return } if (this.status !== 200) { return }
const archiveList = g.SITE.urls.archiveListJSON?.({ siteID, boardID }) const archiveList = g.SITE.urls.archiveListJSON?.({ siteID, boardID })
if (!archiveList) { return that.ajaxCleanParse(boardID, this.response) } if (!archiveList) { return that.ajaxCleanParse(boardID, this.response) }
const response1 = this.response const response1 = this.response
return $.cache(archiveList, function () { return $.cache(archiveList, () => {
if ((this.status !== 200) && (!!g.SITE.archivedBoardsKnown || (this.status !== 404))) { return } if ((this.status !== 200) && (!!g.SITE.archivedBoardsKnown || (this.status !== 404))) { return }
return that.ajaxCleanParse(boardID, response1, this.response) return that.ajaxCleanParse(boardID, response1, this.response)
}) }, { type: 'json' }) as CacheOptions
}) }, { type: 'json' }) as CacheOptions
} }
ajaxCleanParse(boardID, response1, response2) { ajaxCleanParse(boardID, response1, response2) {

View File

@ -9,6 +9,7 @@ import Callbacks from "./Callbacks"
import type Thread from "./Thread" import type Thread from "./Thread"
export default class Post { export default class Post {
callbacksExecuted: boolean
declare root: HTMLElement declare root: HTMLElement
declare thread: Thread declare thread: Thread
declare board: Board declare board: Board

View File

@ -1,9 +1,8 @@
import $ from '../platform/$'
class ShimSet { class ShimSet {
elements: any elements: Element
size: number size: number
constructor() { constructor() {
this.elements = $.dict() this.elements
this.size = 0 this.size = 0
} }
has(value) { has(value) {

View File

@ -1,5 +1,3 @@
import $ from "../platform/$"
export default class SimpleDict<T> { export default class SimpleDict<T> {
keys: string[] keys: string[]
@ -9,17 +7,15 @@ export default class SimpleDict<T> {
push(key: string, data: T): T { push(key: string, data: T): T {
key = `${key}` key = `${key}`
if (!this[key]) { this.keys.push(key) } this[key] = data
return this[key] = data this.keys.push(key)
return data
} }
rm(key: string) { rm(key: string) {
let i: number
key = `${key}` key = `${key}`
if ((i = this.keys.indexOf(key)) !== -1) { delete this[key]
this.keys.splice(i, 1) this.keys = this.keys.filter(k => k !== key)
return delete this[key]
}
} }
forEach(fn: (value: T) => void): void { forEach(fn: (value: T) => void): void {
@ -27,10 +23,6 @@ export default class SimpleDict<T> {
} }
get(key: string): T { get(key: string): T {
if (key === 'keys') { return this[key]
return undefined
} else {
return $.getOwn(this, key)
}
} }
} }

View File

@ -7,7 +7,7 @@ import SimpleDict from "./SimpleDict"
export default class Thread { export default class Thread {
catalogViewNative: CatalogThreadNative catalogViewNative: CatalogThreadNative
ID: number | string ID: string | number
OP: Post OP: Post
isArchived: boolean isArchived: boolean
isClosed: boolean isClosed: boolean
@ -16,7 +16,7 @@ export default class Thread {
board: Board board: Board
threadID: number threadID: number
boardID: number | string boardID: number | string
siteID: number siteID: number | string
fullID: string fullID: string
isDead: boolean isDead: boolean
isHidden: boolean isHidden: boolean
@ -52,7 +52,7 @@ export default class Thread {
this.nodes = { root: null } this.nodes = { root: null }
this.board.threads.push(this.ID, this) this.board.threads.push(this.ID.toString(), this)
g.threads.push(this.fullID, this) g.threads.push(this.fullID, this)
} }

View File

@ -763,7 +763,7 @@ const Config = {
comment: `\ comment: `\
# Filter Stallman copypasta on /g/: # Filter Stallman copypasta on /g/:
#/what you\'re refer+ing to as linux/i;boards:g #/what you're refer+ing to as linux/i;boards:g
# Filter posts with 20 or more quote links: # Filter posts with 20 or more quote links:
#/(?:>>\\d(?:(?!>>\\d)[^])*){20}/ #/(?:>>\\d(?:(?!>>\\d)[^])*){20}/
# Filter posts like T H I S / H / I / S: # Filter posts like T H I S / H / I / S:
@ -817,7 +817,7 @@ http://eye.swfchan.com/search/?q=%name;types:swf
`, `,
FappeT: { FappeT: {
werk: false werk: false
}, },
'Custom CSS': true, 'Custom CSS': true,
@ -826,29 +826,29 @@ http://eye.swfchan.com/search/?q=%name;types:swf
'Index Mode': 'paged', 'Index Mode': 'paged',
'Previous Index Mode': 'paged', 'Previous Index Mode': 'paged',
'Index Size': 'small', 'Index Size': 'small',
'Show Replies': [true, 'Show replies in the index, and also in the catalog if "Catalog hover expand" is checked.'], 'Show Replies': [true, 'Show replies in the index, and also in the catalog if "Catalog hover expand" is checked.'],
'Catalog Hover Expand': [false, 'Expand the comment and show more details when you hover over a thread in the catalog.'], 'Catalog Hover Expand': [false, 'Expand the comment and show more details when you hover over a thread in the catalog.'],
'Catalog Hover Toggle': [true, 'Turn "Catalog hover expand" on and off by clicking in the catalog.'], 'Catalog Hover Toggle': [true, 'Turn "Catalog hover expand" on and off by clicking in the catalog.'],
'Pin Watched Threads': [false, 'Move watched threads to the start of the index.'], 'Pin Watched Threads': [false, 'Move watched threads to the start of the index.'],
'Anchor Hidden Threads': [true, 'Move hidden threads to the end of the index.'], 'Anchor Hidden Threads': [true, 'Move hidden threads to the end of the index.'],
'Refreshed Navigation': [false, 'Refresh index when navigating through pages.'] 'Refreshed Navigation': [false, 'Refresh index when navigating through pages.']
}, },
Header: { Header: {
'Fixed Header': true, 'Fixed Header': true,
'Header auto-hide': false, 'Header auto-hide': false,
'Header auto-hide on scroll': false, 'Header auto-hide on scroll': false,
'Bottom Header': false, 'Bottom Header': false,
'Centered links': false, 'Centered links': false,
'Header catalog links': false, 'Header catalog links': false,
'Bottom Board List': true, 'Bottom Board List': true,
'Shortcut Icons': true, 'Shortcut Icons': true,
'Custom Board Navigation': true 'Custom Board Navigation': true
}, },
archives: { archives: {
archiveLists: 'https://4chenz.github.io/archives.json/archives.json', archiveLists: 'https://4chenz.github.io/archives.json/archives.json',
lastarchivecheck: 0, lastarchivecheck: 0,
archiveAutoUpdate: true archiveAutoUpdate: true
}, },
@ -937,7 +937,7 @@ https://*.hcaptcha.com
'Alt+c', 'Alt+c',
'Insert code tags.' 'Insert code tags.'
], ],
'Eqn tags': [ 'Eqn tags': [
'Alt+e', 'Alt+e',
'Insert eqn tags.' 'Insert eqn tags.'
], ],
@ -1184,11 +1184,11 @@ https://*.hcaptcha.com
'Autohiding Scrollbar': false, 'Autohiding Scrollbar': false,
position: { position: {
'embedding.position': 'top: 50px; right: 0px;', 'embedding.position': 'top: 50px; right: 0px;',
'thread-stats.position': 'bottom: 0px; right: 0px;', 'thread-stats.position': 'bottom: 0px; right: 0px;',
'updater.position': 'bottom: 0px; left: 0px;', 'updater.position': 'bottom: 0px; left: 0px;',
'thread-watcher.position': 'top: 50px; left: 0px;', 'thread-watcher.position': 'top: 50px; left: 0px;',
'qr.position': 'top: 50px; right: 0px;' 'qr.position': 'top: 50px; right: 0px;'
}, },
fourchanImageHost: 'i.4cdn.org', fourchanImageHost: 'i.4cdn.org',

View File

@ -61,7 +61,7 @@ export const g: {
VERSION: string, VERSION: string,
NAMESPACE: string, NAMESPACE: string,
sites: (typeof SWTinyboard)[], sites: (typeof SWTinyboard)[],
boards: Board[], boards: SimpleDict<Board>,
posts?: SimpleDict<Post>, posts?: SimpleDict<Post>,
threads?: SimpleDict<Thread> threads?: SimpleDict<Thread>
THREADID?: number, THREADID?: number,
@ -90,7 +90,7 @@ export const E = (function () {
const output = function (text: string) { const output = function (text: string) {
return text.toString().replace(regex, fn) return text.toString().replace(regex, fn)
} }
output.cat = function (templates) { output.cat = function (templates: HTMLCollectionOf<Element>) {
let html = '' let html = ''
for (let i = 0; i < templates.length; i++) { for (let i = 0; i < templates.length; i++) {
html += templates[i].innerHTML html += templates[i].innerHTML

View File

@ -62,6 +62,97 @@ $.setValue = function (key: string, value: string, cb) {
} }
} }
interface AjaxDetail {
url: string;
timeout: number;
responseType: XMLHttpRequestResponseType;
withCredentials: boolean;
type: string;
onprogress?: (e: ProgressEvent) => void;
form?: [string, string][];
headers?: Record<string, string>;
id: string;
}
$.ajaxPageInit = function (): void {
$.global(function (): void {
const r = new XMLHttpRequest()
window.FCX.requests = Object.create(null)
document.addEventListener('4chanXAjax', function (e): void {
let fd: FormData | null
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 (const key in object) {
const 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: ProgressEvent) {
const { loaded, total } = e
const detail = { loaded, total, id }
return document.dispatchEvent(new CustomEvent('4chanXAjaxProgress', { bubbles: true, detail }))
}
}
r.onloadend = function (): void {
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 })) as any
}
// connection error or content blocker
r.onerror = function (): void {
if (!r.status) { return console.warn(`4chan X failed to load: ${url}`) }
}
if (form) {
fd = new FormData()
for (const entry of form) {
fd.append(entry[0], entry[1])
}
} else {
fd = null
}
return r.send(fd)
}, false)
return document.addEventListener('4chanXAbort', function (e): void {
const { id } = e.detail
if (window.FCX.requests[id]) {
window.FCX.requests[id].abort()
return delete window.FCX.requests[id]
}
}, false)
}, '4chanXAjax')
$.on(d, '4chanXAjaxProgress', function (e: CustomEvent<{ id: string; loaded: number; total: number }>): void {
let req: XMLHttpRequest
if (!(req = requests[e.detail.id])) { return }
return req.upload.onprogress.call(req.upload, e.detail)
})
return $.on(d, '4chanXAjaxLoadend', function (e: CustomEvent<AjaxDetail & { status: number; statusText: string; response: string; responseHeaderString: string }>): void {
let req: XMLHttpRequest
if (!(req = Request[e.detail.id])) { return }
delete Request[e.detail.id]
if (e.detail.status) {
for (const key of ['status', 'statusText', 'response', 'responseHeaderString']) {
req[key] = e.detail[key]
}
if (req.responseType === 'document') {
req.response = new DOMParser().parseFromString
(req.response, 'text/html')
}
return req.onloadend.call(req)
}
})
}
$.ajaxPage = function (url: string, options: AjaxPageOptions) { $.ajaxPage = function (url: string, options: AjaxPageOptions) {
const { const {
responseType = 'json', responseType = 'json',
@ -192,84 +283,7 @@ $.ajax = (function () {
// # XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638 // # XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638
let requestID = 0 let requestID = 0
const requests = dict() const requests = dict()
$.ajaxPageInit()
$.ajaxPageInit = function () {
$.global(function () {
window.FCX.requests = Object.create(null)
document.addEventListener('4chanXAjax', function (e: CustomEvent) {
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 (const key in object) {
const 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 (const 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)
}, '4chanXAjax')
$.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 (const 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 = {}) { return $.ajaxPage = function (url, options = {}) {
let req: XMLHttpRequest let req: XMLHttpRequest
const { onloadend, timeout, responseType, withCredentials, type, onprogress, headers } = options const { onloadend, timeout, responseType, withCredentials, type, onprogress, headers } = options
@ -314,7 +328,7 @@ $.whenModified = function (url, bucket, cb, options = {}) {
return r return r
} }
$.cache = function (url, cb, options = {}) { $.cache = function (url, cb, options) {
const reqs = dict() const reqs = dict()
let req let req
const { ajax } = options const { ajax } = options
@ -350,18 +364,9 @@ $.cleanCache = function (testf) {
} }
$.cb = { $.cb = function (cb: VoidCallback) {
checked() { if (cb) {
if ($.hasOwn(Conf, this.name)) { return cb()
$.set(this.name, this.checked, this.type)
return Conf[this.name] = this.checked
}
},
value() {
if ($.hasOwn(Conf, this.name)) {
$.set(this.name, this.value.trim(), this.type)
return Conf[this.name] = this.value
}
} }
} }
@ -492,7 +497,7 @@ $.one = function (el, events, handler) {
return $.on(el, events, cb) return $.on(el, events, cb)
} }
let cloneInto: (obj: object, win: Window) => object let cloneInto: (obj: object, win: Window) => object
$.event = function (event: Event, detail: object, root = d) { $.event = function (event: string, detail: object, root = d) {
if (!globalThis.chrome?.extension) { if (!globalThis.chrome?.extension) {
if ((detail != null) && (typeof cloneInto === 'function')) { if ((detail != null) && (typeof cloneInto === 'function')) {
detail = cloneInto(detail, d.defaultView) detail = cloneInto(detail, d.defaultView)
@ -978,7 +983,7 @@ if (platform === 'crx') {
}) })
$.forceSync = function () {/* empty */ } $.forceSync = function () {/* empty */ }
} else if ((typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) || $.hasStorage) { } else if ((typeof GM_deleteValue !== 'undefined' && GM_deleteValue !== null) || $.hasStorage) {
$.sync = function (key, cb) { $.sync = function (key: string, cb: (newValue: any, key: string) => void) {
key = g.NAMESPACE + key key = g.NAMESPACE + key
$.syncing[key] = cb $.syncing[key] = cb
return $.oldValue[key] = $.getValue(key, cb) return $.oldValue[key] = $.getValue(key, cb)
@ -1000,10 +1005,7 @@ if (platform === 'crx') {
} }
$.on(window, 'storage', onChange) $.on(window, 'storage', onChange)
return $.forceSync = function (key, cb) { return $.forceSync = function (key: string, cb: (newValue: any, key: string) => void) {
// 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 key = g.NAMESPACE + key
return onChange({ key, newValue: $.getValue(key, cb) }) return onChange({ key, newValue: $.getValue(key, cb) })
} }

View File

@ -19,6 +19,7 @@ const SWTinyboard = {
} }
} }
}, },
ID: 'sw-tinyboard',
name: 'Tinyboard', name: 'Tinyboard',
software: 'Tinyboard', software: 'Tinyboard',
isOPContainerThread: true, isOPContainerThread: true,

View File

@ -17,3 +17,8 @@ export interface File {
isDead: boolean isDead: boolean
docIndex: number docIndex: number
} }
export interface CacheOptions {
dataType: string
sync: boolean
dontClean: boolean
}

View File

@ -9,7 +9,7 @@
"allowJs": true, "allowJs": true,
"checkJs": true, "checkJs": true,
//TODO: Flip this to true //TODO: Flip this to true
"strict": false, "strict": true,
"noEmit": true, "noEmit": true,
"jsx": "react", "jsx": "react",
"jsxFactory": "h", "jsxFactory": "h",