diff --git a/src/General/Header.ts b/src/General/Header.ts index 721af30..6e48eb8 100644 --- a/src/General/Header.ts +++ b/src/General/Header.ts @@ -663,12 +663,10 @@ const Header = { case 'granted': Header.areNotificationsEnabled = true return - break case 'denied': // requestPermission doesn't work if status is 'denied', // but it'll still work if status is 'default'. return - break } const el = $.el('span', diff --git a/src/General/Index.ts b/src/General/Index.ts index 7ad38c5..36c2608 100644 --- a/src/General/Index.ts +++ b/src/General/Index.ts @@ -15,7 +15,7 @@ import Config from '../config/Config' import Filter from '../Filtering/Filter' import PostHiding from '../Filtering/PostHiding' import ThreadHiding from '../Filtering/ThreadHiding' -import { c, Conf, d, doc, g } from '../globals/globals' +import { Board, c, Conf, d, doc, g } from '../globals/globals' import Main from '../main/Main' import Menu from '../Menu/Menu' import CatalogLinks from '../Miscellaneous/CatalogLinks' @@ -31,8 +31,85 @@ import Header from './Header' import NavLinksPage from './Index/NavLinks.html' import PageList from './Index/PageList.html' import UI from './UI' - -const Index = { +interface Index { + req: boolean + liveThreadData: any + pageNum: any + currentPage: any + pagesNum: number + changed: any + showHiddenThreads: boolean + threadPosition: any + threadsNumPerPage: any + selectSort: any + initFinishedFired: boolean + cb: any + loaded: any + lastLongThresholds: any + inputs: any + selectRev: any + hashCommands: any + search: any + liveThreadDict: any + parsedThreads: any + replyData: any + selectMode: any + lastLongOptions: any + sortedThreadIDs: any + hideLabel: any + liveThreadIDs: any + notice: any + nTimeout: any + searchInput: any + update(): unknown + currentSort(currentSort: any): any + root(board: any, root: any): unknown + navLinks(topNavPos: Element, navLinks: any): unknown + pagelist(pagelist: any, pagelist1: any): unknown + endNotice(): unknown + threadsOnPage(pageNum: number): unknown + buildStructure(threadIDs: any): unknown + enabledOn(BOARD: Board): unknown + toggleHide(thread: any): unknown + sort(): unknown + buildIndex(): unknown + pushState(arg0: { mode: any }): unknown + pageLoad(arg0: boolean): unknown + saveSort(): unknown + saveLastLongThresholds(i: number): unknown + getCurrentPage(): unknown + setState(arg0: { search: any; mode: any; sort: any; page: any; hash: string }): unknown + processHash(): unknown + userPageNav(arg0: number): unknown + buildCatalogReplies(arg0: any): unknown + savePerBoard(arg0: string, currentSort: any): unknown + buildPagelist(): unknown + setupSearch(): unknown + setupMode(): unknown + setupSort(): unknown + setPage(): unknown + scrollToIndex(): unknown + getPagesNum(): number + getMaxPageNum(): unknown + isHidden(threadID: any): unknown + load(arg0: string, arg1: string, load: any): any + button(button: any, arg1: string): unknown + parse(response: any): unknown + parseThreadList(pages: any): unknown + buildReplies(threads: any[]): any + updateHideLabel(): unknown + isHiddenReply(ID: any, data: any): unknown + querySearch(search: any): any + sortOnTop(arg0: (obj: any) => any): unknown + buildCatalog(threadIDs: any): unknown + buildThreads(threadIDs: any, arg1: boolean, arg2: any): unknown + buildCatalogPart(arg0: any): unknown + buildCatalogViews(threads: any): unknown + sizeCatalogViews(threads: any): unknown + onSearchInput(): unknown + searchMatch(arg0: any, keywords: any): unknown +} +const Index: Index = { showHiddenThreads: false, changed: {}, diff --git a/src/classes/CatalogThread.ts b/src/classes/CatalogThread.ts index 06adda6..ab9eb25 100644 --- a/src/classes/CatalogThread.ts +++ b/src/classes/CatalogThread.ts @@ -4,9 +4,9 @@ import Post from "./Post" import Thread from "./Thread" export default class CatalogThread { - ID: any + ID: string | number thread: Thread - board: any + board: Board nodes: { root: Post; thumb: HTMLElement; icons: any; postCount: number; fileCount: number; pageCount: number; replies: any } toString() { return this.ID } diff --git a/src/classes/Post.ts b/src/classes/Post.ts index 54ffd19..c63314a 100644 --- a/src/classes/Post.ts +++ b/src/classes/Post.ts @@ -54,6 +54,7 @@ export default class Post { return el })() normalizedOriginal: any + indexRefreshSeen: any toString() { return this.ID } diff --git a/src/classes/ShimSet.ts b/src/classes/ShimSet.ts index a19e9cd..7954f6d 100644 --- a/src/classes/ShimSet.ts +++ b/src/classes/ShimSet.ts @@ -1,8 +1,4 @@ -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md - */ +import $ from '../platform/$' class ShimSet { elements: any size: number diff --git a/src/classes/Thread.ts b/src/classes/Thread.ts index 1ff90b1..d0b8f27 100644 --- a/src/classes/Thread.ts +++ b/src/classes/Thread.ts @@ -6,7 +6,7 @@ import SimpleDict from "./SimpleDict" export default class Thread { - ID: number + ID: number | string OP: Post isArchived: boolean isClosed: boolean diff --git a/src/platform/$.ts b/src/platform/$.ts index 5e848f2..1e9c0bb 100644 --- a/src/platform/$.ts +++ b/src/platform/$.ts @@ -8,7 +8,6 @@ // loosely follows the jquery api: // http://api.jquery.com/ -import Callbacks from "../classes/Callbacks" import Notice from "../classes/Notice" import { c, Conf, d, doc, g } from "../globals/globals" import CrossOrigin from "./CrossOrigin" @@ -18,33 +17,48 @@ import { debounce, dict, MINUTE, platform, SECOND } from "./helpers" const $ = (selector, root = document.body) => root.querySelector(selector) $.id = id => d.getElementById(id) -$.cache = (function () { - const cache = {} - return function (key, value) { - if (value != null) { - return (cache[key] = value) - } else { - return cache[key] + +type AjaxPageOptions = { + responseType?: string; + type?: string; + form?: Document | null; + headers?: { [key: string]: string }; + onloadend?: () => void; + timeout?: number; + withCredentials?: boolean; + onprogress?: (event: ProgressEvent) => void; +} + +$.ajaxPage = function (url: string, options: AjaxPageOptions) { + const { + responseType = 'json', + type = options.form ? 'post' : 'get', + onloadend, + timeout, + withCredentials, + onprogress, + form, + headers = {}, + } = options + + const xhr = new XMLHttpRequest() + xhr.open(type, url, true) + + for (const key in headers) { + xhr.setRequestHeader(key, headers[key]) + } + + Object.assign(xhr, { onloadend, timeout, responseType, withCredentials }) + Object.assign(xhr.upload, { onprogress }) + + xhr.addEventListener('error', () => { + if (!xhr.status) { + console.warn(`4chan X failed to load: ${url}`) } - } -})() -$.ajaxPage = function (url, options) { - if (options.responseType == null) { options.responseType = 'json' } - if (!options.type) { options.type = (options.form && 'post') || 'get' } - const { onloadend, timeout, responseType, withCredentials, type, onprogress, form, headers } = options - const r = new XMLHttpRequest() - r.open(type, url, true) - const object = headers || {} - for (const key in object) { - const 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}`) } }) - r.send(form) - return r + }) + + xhr.send(form) + return xhr } $.ready = function (fc) { if (d.readyState !== 'loading') { @@ -98,7 +112,7 @@ $.ajax = (function () { pageXHR = XMLHttpRequest } - const r = (function (url: string, 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 @@ -201,7 +215,7 @@ $.ajax = (function () { return r.abort() } , false) - }) + }, '4chanXAjax') $.on(d, '4chanXAjaxProgress', function (e) { let req @@ -518,15 +532,31 @@ $.debounce = function (wait, fn) { } } -$.queueTask = function (fn) { - if (typeof requestIdleCallback === 'function') { - return requestIdleCallback(fn) - } else { - return setTimeout(fn, 0) +$.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?) { +$.global = function (fn, data) { if (doc) { const script = $.el('script', { textContent: `(${fn}).call(document.currentScript.dataset);` }) @@ -639,13 +669,8 @@ if (platform === 'crx') { } }) $.sync = (key, cb) => $.syncing[key] = cb - $.forceSync = function (key, cb) { - chrome.storage.sync.get(key, function (data) { - if (data[key] != null) { - cb(data[key], key) - } - }) - } + $.forceSync = function () { } + $.crxWorking = function () { try { if (chrome.runtime.getManifest()) { @@ -997,15 +1022,15 @@ if (platform === 'crx') { }) }) - $.clear = function (cb: Callbacks) { + $.clear = function (cb) { // XXX https://github.com/greasemonkey/greasemonkey/issues/2033 // Also support case where GM_listValues is not defined. - $.delete(Object.keys(Conf), cb) - $.delete(['previousversion', 'QR Size', 'QR.persona'], cb) + $.delete(Object.keys(Conf)) + $.delete(['previousversion', 'QR Size', 'QR.persona']) try { - $.delete($.listValues().map(key => key.replace(g.NAMESPACE, '')), cb) + $.delete($.listValues().map(key => key.replace(g.NAMESPACE, ''))) } catch (error) { } - return cb + return cb?.() } } }