Compare commits

..

No commits in common. "aaa196e3b7d0aa52191bae9bfe88154a7b1ec8b4" and "159b3cc760733cfa99b406c339850f8cde02bb22" have entirely different histories.

19 changed files with 2951 additions and 1690 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,43 +0,0 @@
selectRev: 0,
selectAll: false,
selectMode: false,
showHiddenThreads: false,
changed: false,
currentPage: 0,
currentSort: '',
navLinks: null,
selectSort: null,
selectSize: null,
lastLongOptions: null,
root: null,
button: null,
inputs: null,
pagelist: null,
search: '',
pageNum: 0,
req: null,
isReply: false,
isThread: false,
isClone: false,
enabled: false,
searchInput: null,
hideLabel: null,
lastLongInputs: null,
lastLongThresholds: null,
loaded: false,
liveThreadData: null,
pagesNum: 0,
ID: 0,
thread: null,
threadPosition: 0,
threadsNumPerPage: 0,
notice: null,
nodes: null,
nTimeout: 0,
replyData: null,
replyNodes: null,
sortedThreadIDs: null,
liveThreadDict: null,
parsedThreads: null,
liveThreadIDs: null,
initFinishedFired: false,

View File

@ -79,7 +79,7 @@ const ImageCommon = {
} }
} }
const threadJSON = g.SITE.urls.threadJSON?.(post, fileObj) const threadJSON = g.SITE.urls.threadJSON?.(post)
if (!threadJSON) { return } if (!threadJSON) { return }
const parseJSON = function (isArchiveURL) { const parseJSON = function (isArchiveURL) {
let needle, postObj let needle, postObj

View File

@ -4,7 +4,12 @@ import $ from "../platform/$"
import { dict } from "../platform/helpers" import { dict } from "../platform/helpers"
import QR from "../Posting/QR" import QR from "../Posting/QR"
import Menu from "./Menu" import Menu from "./Menu"
/*
* decaffeinate suggestions:
* 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 DeleteLink = { const DeleteLink = {
auto: [dict(), dict()], auto: [dict(), dict()],
@ -114,7 +119,8 @@ const DeleteLink = {
withCredentials: true, withCredentials: true,
onloadend() { return DeleteLink.load(link, post, fileOnly, this.response) }, onloadend() { return DeleteLink.load(link, post, fileOnly, this.response) },
form: $.formData(form) form: $.formData(form)
}) }
)
}, },
load(link, post, fileOnly, resDoc) { load(link, post, fileOnly, resDoc) {

View File

@ -1,8 +1,13 @@
import { Conf, g } from "../globals/globals" import { Conf,g } from "../globals/globals"
import ImageCommon from "../Images/ImageCommon" import ImageCommon from "../Images/ImageCommon"
import $ from "../platform/$" import $ from "../platform/$"
import Menu from "./Menu" import Menu from "./Menu"
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
const DownloadLink = { const DownloadLink = {
init() { init() {
if (!['index', 'thread'].includes(g.VIEW) || !Conf['Menu'] || !Conf['Download Link']) { return } if (!['index', 'thread'].includes(g.VIEW) || !Conf['Menu'] || !Conf['Download Link']) { return }
@ -19,9 +24,9 @@ const DownloadLink = {
return Menu.menu.addEntry({ return Menu.menu.addEntry({
el: a, el: a,
order: 100, order: 100,
open({ file }) { open({file}) {
if (!file) { return false } if (!file) { return false }
a.href = file.url a.href = file.url
a.download = file.name a.download = file.name
return true return true
} }

View File

@ -33,7 +33,7 @@ const FileInfo = {
const oldInfo = $.el('span', {className: 'fileText-original'}) const oldInfo = $.el('span', {className: 'fileText-original'})
$.prepend(this.file.link.parentNode, oldInfo) $.prepend(this.file.link.parentNode, oldInfo)
$.add(oldInfo, this.file.link) $.add(oldInfo, [this.file.link.previousSibling, this.file.link, this.file.link.nextSibling])
const info = $.el('span', {className: 'file-info'}) const info = $.el('span', {className: 'file-info'})
FileInfo.format(Conf['fileInfo'], this, info) FileInfo.format(Conf['fileInfo'], this, info)

View File

@ -15,12 +15,12 @@ const Flash = {
initReady() { initReady() {
if ($.hasStorage) { if ($.hasStorage) {
return $.global(function () { if (JSON.parse(localStorage['4chan-settings'] || '{}').disableAll) { return window.SWFEmbed.init() } }, 'SWFEmbed') return $.global(function () { if (JSON.parse(localStorage['4chan-settings'] || '{}').disableAll) { return window.SWFEmbed.init() } })
} else { } else {
if (g.VIEW === 'thread') { if (g.VIEW === 'thread') {
$.global(() => window.Main.tid = location.pathname.split(/\/+/)[3], 'Main') $.global(() => window.Main.tid = location.pathname.split(/\/+/)[3])
} }
return $.global(() => window.SWFEmbed.init(), 'SWFEmbed') return $.global(() => window.SWFEmbed.init())
} }
} }
} }

View File

@ -6,7 +6,11 @@ import $ from "../platform/$"
import $$ from "../platform/$$" import $$ from "../platform/$$"
import ExpandComment from "./ExpandComment" import ExpandComment from "./ExpandComment"
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
const Fourchan = { const Fourchan = {
init() { init() {
if ((g.SITE.software !== 'yotsuba') || !['index', 'thread', 'archive'].includes(g.VIEW)) { return } if ((g.SITE.software !== 'yotsuba') || !['index', 'thread', 'archive'].includes(g.VIEW)) { return }
@ -61,7 +65,7 @@ const Fourchan = {
} }
} }
} }
, false), 'MathJax') , false))
Callbacks.Post.push({ Callbacks.Post.push({
name: 'Parse [math] tags', name: 'Parse [math] tags',
cb: Fourchan.math cb: Fourchan.math

View File

@ -1,5 +1,4 @@
import Callbacks from "../classes/Callbacks" import Callbacks from "../classes/Callbacks"
import Post from "../classes/Post"
import { g } from "../globals/globals" import { g } from "../globals/globals"
import $ from "../platform/$" import $ from "../platform/$"
@ -26,12 +25,12 @@ const IDHighlight = {
if (!this.isClone) { return IDHighlight.set(this) } if (!this.isClone) { return IDHighlight.set(this) }
}, },
set(post: Post) { set(post) {
const match = (post.info.uniqueID || post.info.capcode) === IDHighlight.uniqueID const match = (post.info.uniqueID || post.info.capcode) === IDHighlight.uniqueID
return $[match ? 'addClass' : 'rmClass'](post.nodes.post, 'highlight') return $[match ? 'addClass' : 'rmClass'](post.nodes.post, 'highlight')
}, },
click(post: Post) { click(post) {
return function () { return function () {
const uniqueID = post.info.uniqueID || post.info.capcode const uniqueID = post.info.uniqueID || post.info.capcode
IDHighlight.uniqueID = IDHighlight.uniqueID === uniqueID ? null : uniqueID IDHighlight.uniqueID = IDHighlight.uniqueID === uniqueID ? null : uniqueID

View File

@ -6,7 +6,6 @@ import { Conf, g } from "../globals/globals"
import $ from "../platform/$" import $ from "../platform/$"
const IDPostCount = { const IDPostCount = {
thread: null as Thread,
init() { init() {
if ((g.VIEW !== 'thread') || !Conf['Count Posts by ID']) { return } if ((g.VIEW !== 'thread') || !Conf['Count Posts by ID']) { return }
Callbacks.Thread.push({ Callbacks.Thread.push({

View File

@ -10,7 +10,6 @@ import CaptchaT from "./Captcha.t"
import QR from "./QR" import QR from "./QR"
const Captcha = { const Captcha = {
replace: CaptchaReplace,
cache: { cache: {
init() { init() {
$.on(d, 'SaveCaptcha', e => { $.on(d, 'SaveCaptcha', e => {

View File

@ -80,12 +80,7 @@ const Quotify = {
// Don't (Dead) when quotifying in an archived post, // Don't (Dead) when quotifying in an archived post,
// and we know the post still exists. // and we know the post still exists.
a = $.el('a', { a = $.el('a', {
href: g.SITE.Build.postURL({ href: g.SITE.Build.postURL(boardID, post.thread.ID, postID),
siteID: boardID,
boardID,
threadID: post.thread.ID,
postID
}),
className: 'quotelink', className: 'quotelink',
textContent: quote textContent: quote
} }
@ -93,17 +88,12 @@ const Quotify = {
} else { } else {
// Replace the .deadlink span if we can redirect. // Replace the .deadlink span if we can redirect.
a = $.el('a', { a = $.el('a', {
href: g.SITE.Build.postURL({ href: g.SITE.Build.postURL(boardID, post.thread.ID, postID),
siteID: boardID,
boardID,
threadID: 0,
postID
}),
className: 'quotelink deadlink', className: 'quotelink deadlink',
textContent: quote textContent: quote
} }
) )
$.add(a, Post.deadMark.cloneNode(true) as Element) $.add(a, Post.deadMark.cloneNode(true))
$.extend(a.dataset, { boardID, threadID: post.thread.ID, postID }) $.extend(a.dataset, { boardID, threadID: post.thread.ID, postID })
} }
@ -118,7 +108,7 @@ const Quotify = {
textContent: quote textContent: quote
} }
) )
$.add(a, Post.deadMark.cloneNode(true) as Element) $.add(a, Post.deadMark.cloneNode(true))
if (fetchable) { if (fetchable) {
// Make it function as a normal quote if we can fetch the post. // Make it function as a normal quote if we can fetch the post.
$.addClass(a, 'quotelink') $.addClass(a, 'quotelink')
@ -130,7 +120,7 @@ const Quotify = {
if (!this.quotes.includes(quoteID)) { this.quotes.push(quoteID) } if (!this.quotes.includes(quoteID)) { this.quotes.push(quoteID) }
if (!a) { if (!a) {
$.add(deadlink, Post.deadMark.cloneNode(true) as Element) $.add(deadlink, Post.deadMark.cloneNode(true))
return return
} }
@ -148,11 +138,7 @@ const Quotify = {
$.before(deadlink, green) $.before(deadlink, green)
$.add(green, deadlink) $.add(green, deadlink)
} }
return $.replace(deadlink, $.el('span', { return $.replace(deadlink, [...Array.from(deadlink.childNodes)])
className: 'deadlink',
textContent: deadlink.textContent
}
))
} }
} }
export default Quotify export default Quotify

View File

@ -7,16 +7,9 @@ export default class CatalogThread {
ID: string | number ID: string | number
thread: Thread thread: Thread
board: Board board: Board
nodes: { nodes: { root: Post; thumb: HTMLElement; icons: any; postCount: number; fileCount: number; pageCount: number; replies: any }
root: Post,
thumb: Element,
icons: Element,
postCount: Element,
fileCount: Element,
pageCount: Element,
replies: Element
}
toString() { return this.ID } toString() { return this.ID }
constructor(root: Post, thread: Thread) { constructor(root: Post, thread: Thread) {
this.thread = thread this.thread = thread
this.ID = this.thread.ID + '' this.ID = this.thread.ID + ''

View File

@ -58,7 +58,6 @@ export default class Post {
})() })()
normalizedOriginal: any normalizedOriginal: any
indexRefreshSeen: any indexRefreshSeen: any
callbacksExecuted: boolean
toString() { return this.ID } toString() { return this.ID }

23
src/classes/ShimSet.ts Normal file
View File

@ -0,0 +1,23 @@
class ShimSet {
elements: Element
size: number
constructor() {
this.elements
this.size = 0
}
has(value) {
return value in this.elements
}
add(value) {
if (this.elements[value]) { return }
this.elements[value] = true
return this.size++
}
delete(value) {
if (!this.elements[value]) { return }
delete this.elements[value]
return this.size--
}
}
if (!('Set' in window)) { window.Set = ShimSet }

View File

@ -10,12 +10,10 @@ declare global {
wrappedJSObject: any wrappedJSObject: any
Tegaki: any Tegaki: any
FCX: any FCX: any
Parser: any
} }
} }
// interfaces might be incomplete // interfaces might be incomplete
export interface BoardConfig { export interface BoardConfig {
code_tags: 1 | 0,
sjis_tags: string, sjis_tags: string,
math_tags: string, math_tags: string,
forced_anon: boolean, forced_anon: boolean,

View File

@ -239,7 +239,7 @@ $.ajax = (function () {
pageXHR = XMLHttpRequest pageXHR = XMLHttpRequest
} }
const r = (function (url: string, options = dict(), cb?: (this: XMLHttpRequest, e: ProgressEvent) => void) { const r = (function (url: string, options = dict(), cb: Callbacks) {
if (options.responseType == null) { options.responseType = 'json' } if (options.responseType == null) { options.responseType = 'json' }
if (!options.type) { options.type = (options.form && 'post') || 'get' } if (!options.type) { options.type = (options.form && 'post') || 'get' }
url = url.replace(/^((?:https?:)?\/\/(?:\w+\.)?(?:4chan|4channel|4cdn)\.org)\/adv\//, '$1//adv/') url = url.replace(/^((?:https?:)?\/\/(?:\w+\.)?(?:4chan|4channel|4cdn)\.org)\/adv\//, '$1//adv/')

View File

@ -74,6 +74,7 @@ const SWTinyboard = {
} }
return false return false
}, },
ID: 'sw-tinyboard',
parseThreadMetadata(el: HTMLElement) { parseThreadMetadata(el: HTMLElement) {
const thread = dict() const thread = dict()
const op = el.querySelector('.op') const op = el.querySelector('.op')
@ -218,9 +219,6 @@ $\
}, },
Build: { Build: {
postURL({ siteID, boardID, threadID, postID }) {
return `${Conf['siteProperties'][siteID]?.root || `http://${siteID}/`}${boardID}/res/${threadID}.html#${postID}`
},
parseJSON(data, board) { parseJSON(data, board) {
const o = this.parseJSON(data, board) const o = this.parseJSON(data, board)
if (data.ext === 'deleted') { if (data.ext === 'deleted') {
@ -317,7 +315,7 @@ $\
if (!(info = infoNode.textContent.match(/\((.*,\s*)?([\d.]+ ?[KMG]?B).*\)/))) { return false } if (!(info = infoNode.textContent.match(/\((.*,\s*)?([\d.]+ ?[KMG]?B).*\)/))) { return false }
const nameNode = $('.postfilename', text) const nameNode = $('.postfilename', text)
$.extend(file, { $.extend(file, {
name: nameNode ? (nameNode.textContent || '').trim() : '', name: nameNode ? (nameNode.title || nameNode.textContent) : link.pathname.match(/[^/]*$/)[0],
size: info[2], size: info[2],
dimensions: info[0].match(/\d+x\d+/)?.[0] dimensions: info[0].match(/\d+x\d+/)?.[0]
}) })

View File

@ -306,19 +306,11 @@ $\
insertTags(bq) { insertTags(bq) {
let node let node
const nodes = [] for (node of $$('s, .removed-spoiler', bq)) {
const {children} = bq $.replace(node, [$.tn('[spoiler]'), ...Array.from(node.childNodes), $.tn('[/spoiler]')])
for (let i = 0; i < children.length; i++) {
if ((node = children[i]) && (node.nodeName === 'A') && (node.textContent === '>>')) {
nodes.push(node)
}
} }
for (const node of nodes) { for (node of $$('.prettyprint', bq)) {
const {href} = node $.replace(node, [$.tn('[code]'), ...Array.from(node.childNodes), $.tn('[/code]')])
const {textContent} = node.nextSibling
const tag = $.el('a', {href, textContent})
$.after(node, tag)
$.rm(node)
} }
}, },
@ -350,7 +342,7 @@ $\
}, },
transformBoardList() { transformBoardList() {
let node = null let node
const nodes = [] const nodes = []
const spacer = () => $.el('span', {className: 'spacer'}) const spacer = () => $.el('span', {className: 'spacer'})
const items = $.X('.//a|.//text()[not(ancestor::a)]', $(SWYotsuba.selectors.boardList)) const items = $.X('.//a|.//text()[not(ancestor::a)]', $(SWYotsuba.selectors.boardList))
@ -630,7 +622,7 @@ $\
thread(thread, data, withReplies) { thread(thread, data, withReplies) {
let root let root
if (root = thread.nodes.root) { if (root = thread.nodes.root) {
$.rmAll(root, thread.OP.nodes.root) $.rmAll(root)
} else { } else {
thread.nodes.root = (root = $.el('div', { thread.nodes.root = (root = $.el('div', {
className: 'thread', className: 'thread',
@ -686,7 +678,7 @@ $\
'div', 'div',
generateCatalogThreadHtml(thread, src, imgClass, data, postCount, fileCount, pageCount, staticPath, gifIcon) generateCatalogThreadHtml(thread, src, imgClass, data, postCount, fileCount, pageCount, staticPath, gifIcon)
) )
$.before(thread.OP.nodes.info, container) $.before(thread.OP.nodes.info, [...Array.from(container.childNodes)])
for (const br of $$('br', thread.OP.nodes.comment)) { for (const br of $$('br', thread.OP.nodes.comment)) {
if (br.previousSibling && (br.previousSibling.nodeName === 'BR')) { if (br.previousSibling && (br.previousSibling.nodeName === 'BR')) {
@ -699,7 +691,7 @@ $\
id: `t${thread}` id: `t${thread}`
} }
) )
if (thread.OP.highlights) { $.addClass(root, 'highlight') } if (thread.OP.highlights) { $.addClass(root, ...Array.from(thread.OP.highlights)) }
if (!thread.OP.file) { $.addClass(root, 'noFile') } if (!thread.OP.file) { $.addClass(root, 'noFile') }
root.style.cssText = cssText || '' root.style.cssText = cssText || ''