From 6fa11c42a05572779870f94b7ef4ea8dac373450 Mon Sep 17 00:00:00 2001 From: Tuxedo Takodachi Date: Sun, 26 Feb 2023 19:55:02 +0100 Subject: [PATCH] work in progress: load userscript in browser and fix bugs --- README.md | 3 ++- src/General/Settings.js | 10 +++++----- src/Miscellaneous/FileInfo.js | 6 +++--- src/Posting/QR.js | 2 +- src/classes/DataBoard.js | 13 ++++++------- src/css/style.ts | 28 ++++++++++++++-------------- src/main/Main.js | 6 +++--- src/platform/$.js | 8 +++++--- src/types/customImports.d.ts | 1 + tools/rollup-plugin-base64.js | 27 +++++++++++++++++++++++++++ tools/rollup-plugin-inline-file.js | 3 +-- tools/rollup.js | 9 ++------- 12 files changed, 70 insertions(+), 46 deletions(-) create mode 100644 tools/rollup-plugin-base64.js diff --git a/README.md b/README.md index 6462c4634..b254c6d30 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,11 @@ The 4chan XT project is a migration of 4chan X from coffeescript to TypeScript/J ## TODO -- [ ] look for TODO comments +- [x] look for TODO comments - [ ] find alternative for `<% if (` - [x] meta info in compilation step - [ ] actual userscript and extension builds +- [ ] alternative for all the templates in some html files ## Other notes diff --git a/src/General/Settings.js b/src/General/Settings.js index ee15b52ce..4da5989be 100644 --- a/src/General/Settings.js +++ b/src/General/Settings.js @@ -27,7 +27,7 @@ import Unread from '../Monitoring/Unread'; import $$ from '../platform/$$'; import $ from '../platform/$'; import meta from '../../package.json'; -import { Conf, g } from '../globals/globals'; +import { Conf, E, g } from '../globals/globals'; import Header from './Header'; const Settings = { @@ -203,7 +203,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri if (arr instanceof Array) { var description = arr[1]; var div = $.el('div', - { innerHTML: ': ${description}' }); + { innerHTML: `: ${description}` }); div.dataset.name = key; var input = $('input', div); $.on(input, 'change', $.cb.checked); @@ -228,7 +228,7 @@ Enable it on boards.${location.hostname.split('.')[1]}.org in your browser's pri for (var keyFS in Config.main) { var obj = Config.main[keyFS]; var fs = $.el('fieldset', - { innerHTML: '${keyFS}' }); + { innerHTML: `${keyFS}` }); addCheckboxes(fs, obj); if (keyFS === 'Posting and Captchas') { $.add(fs, $.el('p', @@ -850,7 +850,7 @@ vp-replace return; } const filterTypes = Object.keys(Config.filter).filter(x => x !== 'general').map((x, i) => ({ - innerHTML: '?{i}{,}${x}' + innerHTML: (i ? "," : "") + `${E(x)}` })); $.extend(div, { innerHTML: FilterGuidePage }); return $('.warning', div).hidden = Conf['Filter']; @@ -1132,7 +1132,7 @@ vp-replace for (key in Config.hotkeys) { var arr = Config.hotkeys[key]; var tr = $.el('tr', - { innerHTML: '${arr[1]}' }); + { innerHTML: `${arr[1]}` }); var input = $('input', tr); input.name = key; input.spellcheck = false; diff --git a/src/Miscellaneous/FileInfo.js b/src/Miscellaneous/FileInfo.js index 4fc5ec02e..978cc0ccd 100644 --- a/src/Miscellaneous/FileInfo.js +++ b/src/Miscellaneous/FileInfo.js @@ -82,9 +82,9 @@ const FileInfo = { f() { return { innerHTML: "" }; }, p() { return { innerHTML: ((this.file.isSpoiler) ? "Spoiler, " : "") }; }, s() { return { innerHTML: E(this.file.size) }; }, - B() { return { innerHTML: E(Math.round(this.file.sizeInBytes)) + " Bytes" }; }, - K() { return { innerHTML: E(Math.round(this.file.sizeInBytes / 1024)) + " KB" }; }, - M() { return { innerHTML: E(Math.round(this.file.sizeInBytes / 1048576 * 100) / 100) + " MB" }; }, + B() { return { innerHTML: Math.round(this.file.sizeInBytes) + " Bytes" }; }, + K() { return { innerHTML: (Math.round(this.file.sizeInBytes / 1024)) + " KB" }; }, + M() { return { innerHTML: (Math.round(this.file.sizeInBytes / 1048576 * 100) / 100) + " MB" }; }, r() { return { innerHTML: E(this.file.dimensions || "PDF") }; }, g() { return { innerHTML: ((this.file.tag) ? ", " + E(this.file.tag) : "") }; }, '%'() { return { innerHTML: "%" }; } diff --git a/src/Posting/QR.js b/src/Posting/QR.js index 96c73e399..8527f3080 100644 --- a/src/Posting/QR.js +++ b/src/Posting/QR.js @@ -1868,7 +1868,7 @@ var QR = { error(className, message, link) { const div = $.el('div', { className }); - $.extend(div, { innerHTML: '${message}?{link}{ [More info]}
[delete post] [delete all]' }); + $.extend(div, { innerHTML: `${message}?{link}{ [More info]}
[delete post] [delete all]` }); (this.errors || (this.errors = [])).push(div); const [rm, rmAll] = Array.from($$('a', div)); $.on(div, 'click', () => { diff --git a/src/classes/DataBoard.js b/src/classes/DataBoard.js index 486575e03..aac75c4eb 100644 --- a/src/classes/DataBoard.js +++ b/src/classes/DataBoard.js @@ -10,6 +10,7 @@ import $ from "../platform/$"; */ export default class DataBoard { static keys = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads', 'watcherLastModified', 'customTitles']; + static changes = []; constructor(key, sync, dontClean) { this.onSync = this.onSync.bind(this); @@ -25,8 +26,6 @@ export default class DataBoard { return this.sync = sync; }; $.on(document, '4chanXInitFinished', init); - - this.changes = []; } initData(data) { @@ -44,15 +43,15 @@ export default class DataBoard { save(change, cb) { change(); - this.changes.push(change); + DataBoard.changes.push(change); return $.get(this.key, { boards: $.dict() }, items => { - if (!this.changes.length) { return; } + if (!DataBoard.changes.length) { return; } const needSync = ((items[this.key].version || 0) > (this.data.version || 0)); if (needSync) { this.initData(items[this.key]); - for (change of this.changes) { change(); } + for (change of DataBoard.changes) { change(); } } - this.changes = []; + DataBoard.changes = []; this.data.version = (this.data.version || 0) + 1; return $.set(this.key, this.data, () => { if (needSync) { this.sync?.(); } @@ -65,7 +64,7 @@ export default class DataBoard { return $.get(this.key, { boards: $.dict() }, items => { if ((items[this.key].version || 0) > (this.data.version || 0)) { this.initData(items[this.key]); - for (var change of this.changes) { change(); } + for (var change of DataBoard.changes) { change(); } this.sync?.(); } return cb?.(); diff --git a/src/css/style.ts b/src/css/style.ts index f2b8aadf4..f13f37f8e 100644 --- a/src/css/style.ts +++ b/src/css/style.ts @@ -1,11 +1,11 @@ // == Reprocess Font Awesome CSS == // export const fa = (css: string, font: string) => ( -// Font Awesome CSS attribution and license -css.match(/\/\*\![^]*?\*\//)[0] + '\n' + + // Font Awesome CSS attribution and license + css.match(/\/\*\![^]*?\*\//)[0] + '\n' + -// Font Awesome web font -`@font-face { + // Font Awesome web font + `@font-face { font-family: FontAwesome; src: url('data:application/font-woff;base64,${font}') format('woff'); font-weight: 400; @@ -13,25 +13,25 @@ css.match(/\/\*\![^]*?\*\//)[0] + '\n' + } ` + -// fa-[icon name] classes -css - .match(/(\.fa-[^{]*{\s*content:[^}]*}\s*)+/)[0] - .replace(/([,{;])\s+/g, '$1') - .replace(/,/g, ', ') + // fa-[icon name] classes + css + .match(/(\.fa-[^{]*{\s*content:[^}]*}\s*)+/)[0] + .replace(/([,{;])\s+/g, '$1') + .replace(/,/g, ', ') ); // == Create CSS for Link Title Favicons == // -export const icons = (data: {name: string, data: string}[]) => ( +export const icons = (data: { name: string, data: string }[]) => ( -'/* Link Title Favicons */\n' + -data.map(({ name, data }) => -`.linkify.${name}::before { + '/* Link Title Favicons */\n' + + data.map(({ name, data }) => + `.linkify.${name}::before { content: ""; background: transparent url('data:image/png;base64,${data}') center left no-repeat!important; padding-left: 18px; } ` -).join('') + ).join('') ); diff --git a/src/main/Main.js b/src/main/Main.js index 56f27eaea..960f1cd4c 100644 --- a/src/main/Main.js +++ b/src/main/Main.js @@ -83,7 +83,7 @@ import SW from "../site/SW"; import CSS from "../css/CSS"; import meta from '../../package.json'; import Header from "../General/Header"; -import { Conf, g } from "../globals/globals"; +import { Conf, E, g } from "../globals/globals"; import Menu from "../Menu/Menu"; import BoardConfig from "../General/BoardConfig"; import CaptchaReplace from "../Posting/Captcha.replace"; @@ -803,7 +803,7 @@ const Main = { parseError(data, reportLink) { console.error(data.message, data.error.stack); const message = $.el('div', - { innerHTML: '${data.message}?{reportLink}{&{reportLink}}' }); + { innerHTML: E(data.message) + ((reportLink) ? (reportLink).innerHTML : "") }); const error = $.el('div', { textContent: `${data.error.name || 'Error'}: ${data.error.message || 'see console for details'}` }); const lines = data.error.stack?.match(/\d+(?=:\d+\)?$)/mg)?.join().replace(/^/, ' at ') || ''; @@ -960,7 +960,7 @@ User agent: ${navigator.userAgent}\ ] }; export default Main; -Main.init(); +$.ready(() => Main.init()); // <% if (readJSON('/.tests_enabled')) { %> // Main.features.push ['Build Test', Test] diff --git a/src/platform/$.js b/src/platform/$.js index a593c9541..9f16e2f02 100644 --- a/src/platform/$.js +++ b/src/platform/$.js @@ -99,7 +99,7 @@ $.ajax = (function () { pageXHR = XMLHttpRequest; } - (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 @@ -142,7 +142,9 @@ $.ajax = (function () { return r; }); - if (globalThis.chrome?.extension) { + if (!globalThis.chrome?.extension) { + return r; + } else { // # XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638 let requestID = 0; const requests = $.dict(); @@ -224,7 +226,7 @@ $.ajax = (function () { }); }; - $.ajaxPage = function (url, options = {}) { + return $.ajaxPage = function (url, options = {}) { let req; let { onloadend, timeout, responseType, withCredentials, type, onprogress, form, headers } = options; const id = requestID++; diff --git a/src/types/customImports.d.ts b/src/types/customImports.d.ts index 182807e80..754f1213c 100644 --- a/src/types/customImports.d.ts +++ b/src/types/customImports.d.ts @@ -1,5 +1,6 @@ declare module '*.css' { const css: string; export default css; } declare module '*.woff' { const woff: string; export default woff; } +declare module '*.woff2' { const woff2: string; export default woff2; } declare module '*.html' { const html: string; export default html; } declare module '*.gif' { const gif: string; export default gif; } declare module '*.png' { const png: string; export default png; } diff --git a/tools/rollup-plugin-base64.js b/tools/rollup-plugin-base64.js new file mode 100644 index 000000000..06175bfe7 --- /dev/null +++ b/tools/rollup-plugin-base64.js @@ -0,0 +1,27 @@ +import { readFile } from 'fs/promises'; +import { createFilter } from "@rollup/pluginutils"; + +/** + * @param {{ + * include: import("@rollup/pluginutils").FilterPattern, + * exclude?: import("@rollup/pluginutils").FilterPattern, + * }} opts + * @returns {import("rollup").Plugin} + */ +export default function importBase64(opts) { + if (!opts.include) { + throw Error("include option should be specified"); + } + const filter = createFilter(opts.include, opts.exclude); + + return { + name: "base64", + + async load(id) { + if (!filter(id)) return; + + const file = await readFile(id); + return `export default '${file.toString('base64')}';`; + } + }; +}; diff --git a/tools/rollup-plugin-inline-file.js b/tools/rollup-plugin-inline-file.js index 6ceefa153..932073686 100644 --- a/tools/rollup-plugin-inline-file.js +++ b/tools/rollup-plugin-inline-file.js @@ -1,6 +1,5 @@ import { createFilter } from "@rollup/pluginutils"; -import { readFile } from 'fs/promises'; -import { dirname, resolve } from "path"; +import { dirname } from "path"; import { fileURLToPath } from "url"; const __dirname = dirname(fileURLToPath(import.meta.url)); diff --git a/tools/rollup.js b/tools/rollup.js index 93df7112f..7e6c84b31 100644 --- a/tools/rollup.js +++ b/tools/rollup.js @@ -5,6 +5,7 @@ import { dirname, resolve } from "path"; import { fileURLToPath } from 'url'; import generateMetadata from "../src/meta/metadata.js"; import { readFile, writeFile } from "fs/promises"; +import importBase64 from "./rollup-plugin-base64.js"; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -24,13 +25,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url)); inlineFile({ include: ["**/*.html", "**/*.css"], }), - inlineFile({ - include: ["**/*.png", "**/*.gif", "**/*.wav", "**/*.woff"], - // base64 encode - transformer(input) { - return Buffer.from(input).toString('base64'); - } - }), + importBase64({ include: ["**/*.png", "**/*.gif", "**/*.wav", "**/*.woff", "**/*.woff2"] }), inlineFile({ include: "**/package.json", wrap: false,