diff --git a/README.md b/README.md index 0fffd40d2..0ea91ef5c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,9 @@ The 4chan XT project is a migration of 4chan X from coffeescript to TypeScript/J ## Other notes -- A lot of files have circular dependencies, but rollup can handle that, but for some scripts that add to the same object I had to merge them, like Posting/QR and site/SW.yotsuba.js +- A lot of files have circular dependencies, but rollup can handle that + - but for some scripts that add to the same object I had to merge them, like Posting/QR and site/SW.yotsuba.js + - sometimes something might not be initialized before use - tsconfig.json has `"checkJs": true,`, and a lot of js files report type errors when opened because of unknown properties on objects and reassigning variables with different types. These errors don't block the bundle at this moment. - old files in the builds directory stay as reference until the new builds are functional, new files go in the builds/test directory - old build scripts are also kept around for reference until the new build output is fully functional diff --git a/src/Filtering/Recursive.js b/src/Filtering/Recursive.js index 4eb0c2521..faa5ceb3a 100644 --- a/src/Filtering/Recursive.js +++ b/src/Filtering/Recursive.js @@ -1,6 +1,5 @@ import Callbacks from "../classes/Callbacks"; import { g } from "../globals/globals"; -import $ from "../platform/$"; /* * decaffeinate suggestions: @@ -9,7 +8,7 @@ import $ from "../platform/$"; * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ const Recursive = { - recursives: $.dict(), + recursives: Object.create(null), init() { if (!['index', 'thread'].includes(g.VIEW)) { return; } return Callbacks.Post.push({ diff --git a/src/General/Index.js b/src/General/Index.js index 182314cd8..7616f9b36 100644 --- a/src/General/Index.js +++ b/src/General/Index.js @@ -1,6 +1,5 @@ /* * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from * 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 @@ -30,6 +29,7 @@ import Menu from '../Menu/Menu'; import NavLinksPage from './Index/NavLinks.html'; import PageListPage from './Index/PageList.html'; import BoardConfig from './BoardConfig'; +import Get from './Get'; const Index = { showHiddenThreads: false, @@ -316,7 +316,7 @@ const Index = { cycleSortType() { let i; - const types = [...Array.from(Index.selectSort.options)].filter(option => !option.disabled); + const types = Index.selectSort.options.filter(option => !option.disabled); for (i = 0; i < types.length; i++) { var type = types[i]; if (type.selected) { break; } @@ -378,7 +378,7 @@ const Index = { }, lastLongThresholds() { - const i = [...Array.from(this.parentNode.children)].indexOf(this); + const i = Array.from(this.parentNode.children).indexOf(this); const value = +this.value; if (!Number.isFinite(value)) { this.value = Index.lastLongThresholds[i]; @@ -692,14 +692,14 @@ const Index = { const pagesRoot = $('.pages', Index.pagelist); // Previous/Next buttons - const prev = pagesRoot.previousSibling.firstChild; - const next = pagesRoot.nextSibling.firstChild; + const prev = pagesRoot.previousElementSibling.firstElementChild; + const next = pagesRoot.nextElementSibling.firstElementChild; let href = Math.max(pageNum - 1, 1); prev.href = href === 1 ? './' : href; - prev.firstChild.disabled = href === pageNum; + prev.firstElementChild.disabled = href === pageNum; href = Math.min(pageNum + 1, maxPageNum); next.href = href === 1 ? './' : href; - next.firstChild.disabled = href === pageNum; + next.firstElementChild.disabled = href === pageNum; // current page if (strong = $('strong', pagesRoot)) { @@ -1065,15 +1065,15 @@ const Index = { } return [...Array.from(liveThreadData)].sort((a, b) => lastlongD[b.no] - lastlongD[a.no]).map(post => post.no); case 'bump': return liveThreadIDs; - case 'birth': return [...Array.from(liveThreadIDs)].sort((a, b) => b - a); - case 'replycount': return [...Array.from(liveThreadData)].sort((a, b) => b.replies - a.replies).map(post => post.no); - case 'filecount': return [...Array.from(liveThreadData)].sort((a, b) => b.images - a.images).map(post => post.no); - case 'activity': return [...Array.from(liveThreadData)].sort((a, b) => ((tmp_time - a.time) / (a.replies + 1)) - ((tmp_time - b.time) / (b.replies + 1))).map(post => post.no); + case 'birth': return Array.from(liveThreadIDs).sort((a, b) => b - a); + case 'replycount': return Array.from(liveThreadData).sort((a, b) => b.replies - a.replies).map(post => post.no); + case 'filecount': return Array.from(liveThreadData).sort((a, b) => b.images - a.images).map(post => post.no); + case 'activity': return Array.from(liveThreadData).sort((a, b) => ((tmp_time - a.time) / (a.replies + 1)) - ((tmp_time - b.time) / (b.replies + 1))).map(post => post.no); default: return liveThreadIDs; } })(); if (/-rev$/.test(Index.currentSort)) { - Index.sortedThreadIDs = [...Array.from(Index.sortedThreadIDs)].reverse(); + Index.sortedThreadIDs = Array.from(Index.sortedThreadIDs).reverse(); } if (Index.search && (threadIDs = Index.querySearch(Index.search))) { Index.sortedThreadIDs = threadIDs; diff --git a/src/Images/Gallery.js b/src/Images/Gallery.js index d8f2196ae..956d6bdc0 100644 --- a/src/Images/Gallery.js +++ b/src/Images/Gallery.js @@ -17,6 +17,7 @@ import Volume from './Volume'; import Header from '../General/Header'; import { Conf, g } from '../globals/globals'; import UI from '../General/UI'; +import Get from '../General/Get'; const Gallery = { init() { diff --git a/src/Miscellaneous/RelativeDates.js b/src/Miscellaneous/RelativeDates.js index 79f217a0e..d4c92b5d8 100644 --- a/src/Miscellaneous/RelativeDates.js +++ b/src/Miscellaneous/RelativeDates.js @@ -11,7 +11,7 @@ import $ from "../platform/$"; * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ const RelativeDates = { - INTERVAL: $.MINUTE / 2, + INTERVAL: 30000, init() { if ( diff --git a/src/Posting/Captcha.t.js b/src/Posting/Captcha.t.js index a559bc1c9..428198d6e 100644 --- a/src/Posting/Captcha.t.js +++ b/src/Posting/Captcha.t.js @@ -1,3 +1,4 @@ +import { g } from "../globals/globals"; import $ from "../platform/$"; import QR from "./QR"; diff --git a/src/classes/Callbacks.js b/src/classes/Callbacks.js index c93561010..01dbd54c6 100644 --- a/src/classes/Callbacks.js +++ b/src/classes/Callbacks.js @@ -1,3 +1,5 @@ +import Main from "../main/Main"; + export default class Callbacks { static Post = new Callbacks('Post'); static Thread = new Callbacks('Thread'); diff --git a/src/classes/CatalogThread.js b/src/classes/CatalogThread.js index 2cb43a69f..9acfd8a72 100644 --- a/src/classes/CatalogThread.js +++ b/src/classes/CatalogThread.js @@ -1,3 +1,5 @@ +import $ from "../platform/$"; + export default class CatalogThread { toString() { return this.ID; } diff --git a/src/classes/CatalogThreadNative.js b/src/classes/CatalogThreadNative.js index 6f2198f5e..6289ad3fd 100644 --- a/src/classes/CatalogThreadNative.js +++ b/src/classes/CatalogThreadNative.js @@ -1,3 +1,8 @@ +import { g } from "../globals/globals"; +import $ from "../platform/$"; +import Board from "./Board"; +import Thread from "./Thread"; + export default class CatalogThreadNative { toString() { return this.ID; } diff --git a/src/classes/Post.js b/src/classes/Post.js index bab2b6d7e..99f3668df 100644 --- a/src/classes/Post.js +++ b/src/classes/Post.js @@ -11,8 +11,14 @@ import Callbacks from "./Callbacks"; * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ export default class Post { - // \u00A0 is nbsp - static deadMark = $.el('span', { textContent: '\u00A0(Dead)', className: 'qmark-dead' }); + // because of a circular dependency $ might not be initialized, so we can't use $.el + static deadMark = (() => { + const el = document.createElement('span'); + // \u00A0 is nbsp + el.textContent = '\u00A0(Dead)'; + el.className = 'qmark-dead'; + return el; + })(); toString() { return this.ID; } diff --git a/src/platform/$.js b/src/platform/$.js index 9f16e2f02..dbe110b8d 100644 --- a/src/platform/$.js +++ b/src/platform/$.js @@ -239,7 +239,7 @@ $.ajax = (function () { 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. diff --git a/src/platform/CrossOrigin.js b/src/platform/CrossOrigin.js index c947379dc..09ef5c015 100644 --- a/src/platform/CrossOrigin.js +++ b/src/platform/CrossOrigin.js @@ -1,3 +1,4 @@ +import QR from "../Posting/QR"; import $ from "./$"; /* @@ -8,9 +9,9 @@ import $ from "./$"; * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md */ +let eventPageRequest; if (globalThis.chrome?.extension) { - let Request; - const eventPageRequest = (function () { + eventPageRequest = (function () { const callbacks = []; chrome.runtime.onMessage.addListener(function (response) { callbacks[response.id](response.data); @@ -107,34 +108,29 @@ const CrossOrigin = { }); }, - Request: (Request = (function () { - Request = class Request { - static initClass() { - this.prototype.status = 0; - this.prototype.statusText = ''; - this.prototype.response = null; - this.prototype.responseHeaderString = null; - } - getResponseHeader(headerName) { - if ((this.responseHeaders == null) && (this.responseHeaderString != null)) { - this.responseHeaders = $.dict(); - for (var header of this.responseHeaderString.split('\r\n')) { - var i; - if ((i = header.indexOf(':')) >= 0) { - var key = header.slice(0, i).trim().toLowerCase(); - var val = header.slice(i + 1).trim(); - this.responseHeaders[key] = val; - } + Request: class Request { + static status = 0; + static statusText = ''; + static response = null; + static responseHeaderString = null; + + getResponseHeader(headerName) { + if ((this.responseHeaders == null) && (Request.responseHeaderString != null)) { + this.responseHeaders = $.dict(); + for (var header of Request.responseHeaderString.split('\r\n')) { + var i; + if ((i = header.indexOf(':')) >= 0) { + var key = header.slice(0, i).trim().toLowerCase(); + var val = header.slice(i + 1).trim(); + this.responseHeaders[key] = val; } } - return this.responseHeaders?.[headerName.toLowerCase()] ?? null; } - abort() { } - onloadend() { } - }; - Request.initClass(); - return Request; - })()), + return this.responseHeaders?.[headerName.toLowerCase()] ?? null; + } + abort() { } + onloadend() { } + }, // Attempts to fetch `url` using cross-origin privileges, if available. // Interface is a subset of that of $.ajax. diff --git a/src/site/SW.yotsuba.Build/PostInfoHtml.js b/src/site/SW.yotsuba.Build/PostInfoHtml.js index a9f34dcf2..ddb99879e 100644 --- a/src/site/SW.yotsuba.Build/PostInfoHtml.js +++ b/src/site/SW.yotsuba.Build/PostInfoHtml.js @@ -11,8 +11,8 @@ export default function generatePostInfoHtml( output += ``; if (!o.isReply || boardID === "f" || subject) output += `${E(subject || "")}`; - output += ``; - if (email) output += ``; + output += ``; + if (email) output += ``; output += `${E(name)}`; if (tripcode) output += ` ${E(tripcode)}`; if (pass) output += ` `; @@ -26,14 +26,14 @@ export default function generatePostInfoHtml( output += ` ${E(capcode)} Icon`; } - if (!(uniqueID && !capcode)) { + if (uniqueID && !capcode) { output += `(ID: ${E(uniqueID)})`; } if (flagCode) output += ` `; if (flagCodeTroll) output += ` `; output += ` - ${E(dateText)} + ${E(dateText)} No. ${ID}`; @@ -50,8 +50,6 @@ export default function generatePostInfoHtml( if (o.isArchived) { output += ` Archived`; - } else { - output += ` Archived`; } if (!o.isReply && g.VIEW === "index") output += `   [Reply]`; output += ''; diff --git a/src/site/SW.yotsuba.js b/src/site/SW.yotsuba.js index 4d077134b..d419e2f42 100644 --- a/src/site/SW.yotsuba.js +++ b/src/site/SW.yotsuba.js @@ -8,6 +8,7 @@ import PostSuccessful from "../Posting/PostSuccessful"; import ImageHost from "../Images/ImageHost"; import { g, Conf } from "../globals/globals"; import BoardConfig from "../General/BoardConfig"; +import CSS from "../css/CSS"; import generatePostInfoHtml from './SW.yotsuba.Build/PostInfoHtml'; import generateFileHtml from "./SW.yotsuba.Build/FileHtml"; @@ -366,7 +367,7 @@ $\ Build: { staticPath: '//s.4cdn.org/image/', gifIcon: window.devicePixelRatio >= 2 ? '@2x.gif' : '.gif', - spoilerRange: $.dict(), + spoilerRange: Object.create(null), shortFilename(filename) { const ext = filename.match(/\.?[^\.]*$/)[0]; @@ -443,7 +444,7 @@ $\ } o.files = []; if (data.ext) { - o.file = SWYotsuba.this.parseJSONFile(data, { siteID, boardID }); + o.file = this.parseJSONFile(data, { siteID, boardID }); o.files.push(o.file); } // Temporary JSON properties for events such as April 1 / Halloween @@ -511,7 +512,7 @@ $\ post(o) { const { ID, threadID, boardID, file } = o; const { subject, email, name, tripcode, capcode, pass, uniqueID, flagCode, flagCodeTroll, flag, dateUTC, dateText, commentHTML } = o.info; - const { staticPath, gifIcon } = Build; + const { staticPath, gifIcon } = this; /* Post Info */ diff --git a/src/types/globals.d.ts b/src/types/globals.d.ts new file mode 100644 index 000000000..2cc8ff683 --- /dev/null +++ b/src/types/globals.d.ts @@ -0,0 +1,2 @@ +declare const cloneInto: Function; +declare const XPCNativeWrapper: any;