types and fixes

This commit is contained in:
Lalle 2023-05-08 00:25:33 +02:00
parent 55dd58b8c6
commit 543ff3437b
No known key found for this signature in database
GPG Key ID: A6583D207A8F6B0D
16 changed files with 110 additions and 87 deletions

View File

@ -1,11 +1,6 @@
import { Conf, doc } from "../globals/globals" import { Conf, doc } from "../globals/globals"
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 Anonymize = { const Anonymize = {
init() { init() {
if (!Conf['Anonymize']) { return } if (!Conf['Anonymize']) { return }

View File

@ -14,14 +14,6 @@ import QuoteYou from "../Quotelinks/QuoteYou"
import PostHiding from "./PostHiding" import PostHiding from "./PostHiding"
import ThreadHiding from "./ThreadHiding" import ThreadHiding from "./ThreadHiding"
/*
* 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
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/
interface FilterObj { interface FilterObj {
isstring: boolean; isstring: boolean;
@ -40,10 +32,6 @@ type FilterType = "postID" | "name" | "uniqueID" | "tripcode" | "capcode" | "pas
| "flag" | "filename" | "dimensions" | "filesize" | "MD5" | "flag" | "filename" | "dimensions" | "filesize" | "MD5"
const Filter = { const Filter = {
/**
* Uses a Map for string types, with the value to filter for as the key.
* This allows faster lookup than iterating over every filter.
*/
filters: new Map<FilterType, FilterObj[] | Map<string, FilterObj[]>>(), filters: new Map<FilterType, FilterObj[] | Map<string, FilterObj[]>>(),
init(this: typeof Filter) { init(this: typeof Filter) {
@ -295,7 +283,7 @@ const Filter = {
if (!(url = g.SITE.urls.catalogJSON?.(g.BOARD))) { return } if (!(url = g.SITE.urls.catalogJSON?.(g.BOARD))) { return }
Filter.catalogData = dict() Filter.catalogData = dict()
$.ajax(url, $.ajax(url,
{ onloadend: Filter.catalogParse }) { onloadend: Filter.catalogParse }, this)
return Callbacks.CatalogThreadNative.push({ return Callbacks.CatalogThreadNative.push({
name: 'Filter', name: 'Filter',
cb: this.catalogNode cb: this.catalogNode

View File

@ -21,7 +21,7 @@ const PostHiding = {
$.addClass(doc, "reply-hide") $.addClass(doc, "reply-hide")
} }
this.db = new DataBoard('hiddenPosts') this.db = new DataBoard('hiddenPosts', true, false)
return Callbacks.Post.push({ return Callbacks.Post.push({
name: 'Reply Hiding', name: 'Reply Hiding',
cb: this.node cb: this.node

View File

@ -14,7 +14,7 @@ import { dict } from "../platform/helpers"
const ThreadHiding = { const ThreadHiding = {
init() { init() {
if (!['index', 'catalog'].includes(g.VIEW) || (!Conf['Thread Hiding Buttons'] && !(Conf['Menu'] && Conf['Thread Hiding Link']) && !Conf['JSON Index'])) { return } if (!['index', 'catalog'].includes(g.VIEW) || (!Conf['Thread Hiding Buttons'] && !(Conf['Menu'] && Conf['Thread Hiding Link']) && !Conf['JSON Index'])) { return }
this.db = new DataBoard('hiddenThreads') this.db = new DataBoard('hiddenThreads', true)
if (g.VIEW === 'catalog') { return this.catalogWatch() } if (g.VIEW === 'catalog') { return this.catalogWatch() }
this.catalogSet(g.BOARD) this.catalogSet(g.BOARD)
$.on(d, 'IndexRefreshInternal', this.onIndexRefresh) $.on(d, 'IndexRefreshInternal', this.onIndexRefresh)

View File

@ -8,6 +8,45 @@ import { SECOND } from "../platform/helpers"
import type { File } from "../types/globals" import type { File } from "../types/globals"
import ImageCommon from "./ImageCommon" import ImageCommon from "./ImageCommon"
import Volume from "./Volume" import Volume from "./Volume"
interface File {
isVideo: boolean;
isExpanding: boolean;
isExpanded: boolean;
url: string;
dimensions: string | null;
}
interface ImageHover {
error(post: Post, file: File): () => void;
}
interface ImageCommon {
cache?: HTMLElement & { dataset: { fileID: string } };
popCache(): HTMLElement;
rewind(el: HTMLElement): void;
pause(el: HTMLElement): void;
pushCache(el: HTMLElement): void;
}
interface Volume {
setup(el: HTMLVideoElement): void;
}
interface UI {
hover(params: {
root: HTMLElement;
el: HTMLElement;
latestEvent: MouseEvent;
endEvents: string;
height: number;
width: number;
noRemove: boolean;
cb: () => void;
}): void;
}
const ImageHover = { const ImageHover = {
init() { init() {
if (!['index', 'thread'].includes(g.VIEW)) { return } if (!['index', 'thread'].includes(g.VIEW)) { return }
@ -42,23 +81,27 @@ const ImageHover = {
return $.on(this.nodes.thumb, 'mouseover', hover) return $.on(this.nodes.thumb, 'mouseover', hover)
}, },
mouseover(post: Post, file: File) { mouseover(post: Post, file: File): (e: MouseEvent) => void {
return function (e) { return function (e: MouseEvent) {
let el, height, width let el: HTMLElement, height: number, width: number
if (!doc.contains(this)) { return } if (!doc.contains(this)) {
return
}
const { isVideo } = file const { isVideo } = file
if (file.isExpanding || file.isExpanded || g.SITE.isThumbExpanded?.(file)) { return } if (file.isExpanding || file.isExpanded || g.SITE.isThumbExpanded?.(file)) {
return
}
const error = ImageHover.error(this.post, file) const error = ImageHover.error(this.post, file)
if (ImageCommon.cache?.dataset.fileID === `${post.fullID}.${file.index}`) { if (ImageCommon.cache?.dataset.fileID === `${post.fullID}.${file.index}`) {
el = ImageCommon.popCache() el = ImageCommon.popCache()
$.on(el, 'error', error) $.on(el, 'error', error)
} else { } else {
el = $.el((isVideo ? 'video' : 'img'), { el = $.el(isVideo ? 'video' : 'img', {
className: 'ihover', className: 'ihover',
style: { style: {
maxWidth: '100%', maxWidth: '100%',
maxHeight: '100%' maxHeight: '100%',
} },
}) })
el.dataset.fileID = `${post.fullID}.${file.index}` el.dataset.fileID = `${post.fullID}.${file.index}`
$.on(el, 'error', error) $.on(el, 'error', error)
@ -74,14 +117,16 @@ const ImageHover = {
if (isVideo) { if (isVideo) {
el.loop = true el.loop = true
el.controls = false el.controls = false
Volume.setup(el) Volume.setup(el as HTMLVideoElement)
if (Conf['Autoplay']) { if (Conf['Autoplay']) {
el.play() (el as HTMLVideoElement).play()
if (this.nodeName === 'VIDEO') { this.currentTime = el.currentTime } if (this.nodeName === 'VIDEO') {
(this as HTMLVideoElement).currentTime = (el as HTMLVideoElement).currentTime
}
} }
} }
if (file.dimensions) { if (file.dimensions) {
[width, height] = Array.from((file.dimensions.split('x').map((x) => +x))) [width, height] = Array.from(file.dimensions.split('x').map((x) => +x))
const maxWidth = doc.clientWidth const maxWidth = doc.clientWidth
const maxHeight = doc.clientHeight - UI.hover.padding const maxHeight = doc.clientHeight - UI.hover.padding
const scale = Math.min(1, maxWidth / width, maxHeight / height) const scale = Math.min(1, maxWidth / width, maxHeight / height)
@ -91,7 +136,7 @@ const ImageHover = {
el.style.maxHeight = `${height}px` el.style.maxHeight = `${height}px`
} }
return UI.hover({ return UI.hover({
root: this, root: this as HTMLElement,
el, el,
latestEvent: e, latestEvent: e,
endEvents: 'mouseout click', endEvents: 'mouseout click',
@ -105,7 +150,7 @@ const ImageHover = {
$.rm(el) $.rm(el)
el.removeAttribute('style') el.removeAttribute('style')
return el.remove() return el.remove()
} },
}) })
} }
}, },

View File

@ -8,6 +8,7 @@ import { dict } from "../platform/helpers"
const Banner = { const Banner = {
db: DataBoard,
init() { init() {
if (Conf['Custom Board Titles']) { if (Conf['Custom Board Titles']) {
this.db = new DataBoard('customTitles', null, true) this.db = new DataBoard('customTitles', null, true)
@ -89,18 +90,13 @@ const Banner = {
return Banner.db.set({ return Banner.db.set({
boardID: g.BOARD.ID, boardID: g.BOARD.ID,
threadID: this.className, threadID: this.className,
val: { title: this.textContent,
title: this.textContent, orig: Banner.original[this.className].textContent
orig: Banner.original[this.className].textContent }, true)
}
})
} else { } else {
$.rmAll(this) $.rmAll(this)
$.add(this, [...Array.from(Banner.original[this.className].cloneNode(true).childNodes)]) $.add(this, [...Array.from(Banner.original[this.className].cloneNode(true).childNodes)])
return Banner.db.delete({ return Banner.db.delete({ boardID: g.BOARD.ID, threadID: this.className }, true)
boardID: g.BOARD.ID,
threadID: this.className
})
} }
} }
}, },

View File

@ -149,12 +149,12 @@ export default class Fetcher {
if (post.no !== this.postID) { if (post.no !== this.postID) {
// Cached requests can be stale and must be rechecked. // Cached requests can be stale and must be rechecked.
if (isCached) { if (isCached) {
const api = g.SITE.urls.threadJSON({ boardID: this.boardID, threadID: this.threadID }) const api = g.SITE.urls.threadJSON({ boardID: this.boardID, threadID: this.threadID }, true)
$.cleanCache(url => url === api) $.cleanCache(url => url === api)
const that = this const that = this
$.cache(api, function () { $.cache(api, function () {
return that.fetchedPost(this, false) return that.fetchedPost(this, false)
}) }, { force: true })
return return
} }

View File

@ -9,7 +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 origin: Post
declare root: HTMLElement declare root: HTMLElement
declare thread: Thread declare thread: Thread
declare board: Board declare board: Board
@ -33,6 +33,7 @@ export default class Post {
declare files: File[] declare files: File[]
declare info: { declare info: {
comment: string,
subject: string, subject: string,
name: string, name: string,
email: string, email: string,

View File

@ -25,7 +25,7 @@ export default class Thread {
fileLimit: boolean fileLimit: boolean
ipCount: number ipCount: number
json: JSON json: JSON
catalogView: Node catalogView: any
nodes: { root: Post } nodes: { root: Post }
constructor(ID: number | string, board: Board) { constructor(ID: number | string, board: Board) {

View File

@ -42,15 +42,15 @@ import yotsuba from './yotsuba.css'
import yotsubaB from './yotsuba-b.css' import yotsubaB from './yotsuba-b.css'
// <% // <%
// var inc = require['style']; // var inc = require['style'];
// var faCSS = read('/node_modules/font-awesome/css/font-awesome.css'); // var faCSS = read('/node_modules/font-awesome/css/font-awesome.css');
// var faWebFont = readBase64('/node_modules/font-awesome/fonts/fontawesome-webfont.woff'); // var faWebFont = readBase64('/node_modules/font-awesome/fonts/fontawesome-webfont.woff');
// var mainCSS = ['font-awesome', 'style', 'yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon', 'spooky'].map(x => read(`${x}.css`)).join(''); // var mainCSS = ['font-awesome', 'style', 'yotsuba', 'yotsuba-b', 'futaba', 'burichan', 'tomorrow', 'photon', 'spooky'].map(x => read(`${x}.css`)).join('');
// var iconNames = files.filter(f => /^linkify\.[^.]+\.png$/.test(f)); // var iconNames = files.filter(f => /^linkify\.[^.]+\.png$/.test(f));
// var icons = iconNames.map(readBase64); // var icons = iconNames.map(readBase64);
// %> // %>
const mainCSS = fontAwesome + style + yotsuba +yotsubaB+futaba+burichan+tomorrow + photon + spooky const mainCSS = fontAwesome + style + yotsuba + yotsubaB + futaba + burichan + tomorrow + photon + spooky
const faIcons: { name: string, data: string }[] = [ const faIcons: { name: string, data: string }[] = [
{ name: "Audio", data: linkifyAudio }, { name: "Audio", data: linkifyAudio },
{ name: "Bitchute", data: linkifyBitchute }, { name: "Bitchute", data: linkifyBitchute },
@ -83,11 +83,11 @@ const CSS = {
www, www,
sub: function(css: string) { sub: function (css: string) {
const variables = { const variables = {
site: g.SITE.selectors site: g.SITE.selectors
} }
return css.replace(/\$[\w\$]+/g, function(name) { return css.replace(/\$[\w\$]+/g, function (name) {
const words = name.slice(1).split('$') const words = name.slice(1).split('$')
let sel = variables let sel = variables
for (let i = 0; i < words.length; i++) { for (let i = 0; i < words.length; i++) {

View File

@ -4,7 +4,6 @@
font-style: normal; font-style: normal;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
text-decoration: inherit; text-decoration: inherit;
speak: none;
display: inline-block; display: inline-block;
font-size: 13px; font-size: 13px;
visibility: visible; visibility: visible;

View File

@ -1,37 +1,26 @@
// == Reprocess Font Awesome CSS == // // == Reprocess Font Awesome CSS == //
export const fa = (css: string, font: string) => ( export const fa = (css: string, font: string) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const [license, classes] = css.match(/(\/\*![^]*?\*\/)[\s\S]*?((\.fa-[^{]*{\s*content:[^}]*}\s*)+)/)!.slice(1)
return `${license}
// Font Awesome CSS attribution and license @font-face {
css.match(/\/\*\![^]*?\*\//)[0] + '\n' +
// Font Awesome web font
`@font-face {
font-family: FontAwesome; font-family: FontAwesome;
src: url('data:application/font-woff;base64,${font}') format('woff'); src: url('data:application/font-woff;base64,${font}') format('woff');
font-weight: 400; font-weight: 400;
font-style: normal; font-style: normal;
} }
` + ${classes.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 == // // == Create CSS for Link Title Favicons == //
export const icons = (data: { name: string, data: string }[]) => ( export const icons = (data: { name: string, data: string }[]) => {
return `/* Link Title Favicons */\n${data.map(({ name, data }) => {
'/* Link Title Favicons */\n' + return `.linkify.${name}::before {
data.map(({ name, data }) =>
`.linkify.${name}::before {
content: ""; content: "";
background: transparent url('data:image/png;base64,${data}') center left no-repeat!important; background: transparent url('data:image/png;base64,${data}') center left no-repeat!important;
padding-left: 18px; padding-left: 18px;
} }
` `
).join('') }).join('')}`
}
)

View File

@ -6,6 +6,9 @@ import { dict } from "../platform/helpers"
const SWTinyboard = { const SWTinyboard = {
sfwBoards() {
return Conf['boardConfig'].config.sfw_boards
},
insertTags() { insertTags() {
const { config } = Conf['boardConfig'] const { config } = Conf['boardConfig']
const { markup_tags } = config const { markup_tags } = config

View File

@ -24,14 +24,20 @@ const SWYotsuba = {
urls: { urls: {
thread({boardID, threadID}) { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/thread/${threadID}` }, thread({boardID, threadID}) { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/thread/${threadID}` },
post({postID}) { return `#p${postID}` }, post({postID})
index({boardID}) { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/` }, { return `#p${postID}` },
catalog({boardID}) { if (boardID === 'f') { return undefined } else { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/catalog` } }, index({boardID}) { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/` },
archive({boardID}) { if (BoardConfig.isArchived(boardID)) { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/archive` } else { return undefined } }, catalog({boardID})
{ if (boardID === 'f') { return undefined } else { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/catalog` } },
archive({boardID})
{ if (BoardConfig.isArchived(boardID)) { return `${location.protocol}//${BoardConfig.domain(boardID)}/${boardID}/archive` } else { return undefined } },
threadJSON({boardID, threadID}) { return `${location.protocol}//a.4cdn.org/${boardID}/thread/${threadID}.json` }, threadJSON({boardID, threadID}) { return `${location.protocol}//a.4cdn.org/${boardID}/thread/${threadID}.json` },
threadsListJSON({boardID}) { return `${location.protocol}//a.4cdn.org/${boardID}/threads.json` }, threadsListJSON({boardID})
archiveListJSON({boardID}) { if (BoardConfig.isArchived(boardID)) { return `${location.protocol}//a.4cdn.org/${boardID}/archive.json` } else { return '' } }, { return `${location.protocol}//a.4cdn.org/${boardID}/threads.json` },
catalogJSON({boardID}) { return `${location.protocol}//a.4cdn.org/${boardID}/catalog.json` }, archiveListJSON({boardID})
{ if (BoardConfig.isArchived(boardID)) { return `${location.protocol}//a.4cdn.org/${boardID}/archive.json` } else { return '' } },
catalogJSON({boardID})
{ return `${location.protocol}//a.4cdn.org/${boardID}/catalog.json` },
file({boardID}, filename) { file({boardID}, filename) {
const hostname = boardID === 'f' ? ImageHost.flashHost() : ImageHost.host() const hostname = boardID === 'f' ? ImageHost.flashHost() : ImageHost.host()
return `${location.protocol}//${hostname}/${boardID}/${filename}` return `${location.protocol}//${hostname}/${boardID}/${filename}`

View File

@ -1,5 +1,6 @@
declare const XPCNativeWrapper: any declare const XPCNativeWrapper: any
export interface File { export interface File {
MD5: string
name: string name: string
isImage: boolean isImage: boolean
isVideo: boolean isVideo: 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": true, "strict": false,
"noEmit": true, "noEmit": true,
"jsx": "react", "jsx": "react",
"jsxFactory": "h", "jsxFactory": "h",