add supporting changes
This commit is contained in:
parent
90004305a0
commit
0d8ad0a786
|
@ -6,6 +6,34 @@
|
|||
globalThis.LCNSite = class LCNSite {
|
||||
static INSTANCE = null;
|
||||
|
||||
static "createAbortable" () {
|
||||
const obj = { "abort": null, "controller": null, "signal": null }
|
||||
const setupController = () => {
|
||||
obj.controller = new AbortController()
|
||||
obj.signal = obj.controller.signal
|
||||
}
|
||||
|
||||
obj.abort = () => {
|
||||
obj.controller.abort()
|
||||
setupController()
|
||||
}
|
||||
|
||||
setupController()
|
||||
return obj
|
||||
}
|
||||
|
||||
static "getThreadFromPages" (pages, thread_id) {
|
||||
for (const page of pages) {
|
||||
for (const thread of page.threads) {
|
||||
if (thread_id == String(thread.no)) {
|
||||
return { "page": page.page, ...thread }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
#isModerator = document.body.classList.contains("is-moderator");
|
||||
#isThreadPage = document.body.classList.contains("active-thread");
|
||||
#isBoardPage = document.body.classList.contains("active-board");
|
||||
|
@ -53,10 +81,28 @@ globalThis.LCNSite = class LCNSite {
|
|||
|
||||
this.#favicon.href = `/favicon${type ? "-" + type : ""}.ico`
|
||||
}
|
||||
|
||||
"getFloaterLContainer" () { return document.getElementById("bar-bottom-l"); }
|
||||
"getFloaterRContainer" () { return document.getElementById("bar-bottom-r"); }
|
||||
"getThreadStatsLContainer" () { return document.getElementById("lcn-threadstats-l"); }
|
||||
"getThreadStatsRContainer" () { return document.getElementById("lcn-threadstats-r"); }
|
||||
|
||||
#generatedStyle = null
|
||||
"writeCSSStyle" (origin, stylesheet) {
|
||||
if (this.#generatedStyle == null && (this.#generatedStyle = document.querySelector("head > style.generated-css")) == null) {
|
||||
this.#generatedStyle = document.createElement("style")
|
||||
this.#generatedStyle.classList.add("generated-css")
|
||||
document.head.appendChild(this.#generatedStyle)
|
||||
}
|
||||
this.#generatedStyle.textContent += `${this.#generatedStyle.textContent.length ? "\n\n" : ""}/*** Generated by ${origin} ***/\n${stylesheet}`
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
globalThis.LCNPostInfo = class LCNPostInfo {
|
||||
|
||||
static nodeAttrib = "$LCNPostInfo";
|
||||
static selector = ".post:not(.grid-li)";
|
||||
#boardId = null;
|
||||
#threadId = null;
|
||||
#postId = null;
|
||||
|
@ -74,7 +120,7 @@ globalThis.LCNPostInfo = class LCNPostInfo {
|
|||
#isLocked = false;
|
||||
#isSticky = false;
|
||||
|
||||
static "assign" (post) { return post.$LCNPostInfo ?? (post.$LCNPostInfo = this.from(post)); }
|
||||
static "assign" (post) { return post[this.nodeAttrib] ?? (post[this.nodeAttrib] = this.from(post)); }
|
||||
static "from" (post) {
|
||||
assert.ok(post.classList.contains("post"), "Arty must be expected Element.")
|
||||
const inst = new this()
|
||||
|
@ -130,6 +176,8 @@ globalThis.LCNPostInfo = class LCNPostInfo {
|
|||
|
||||
globalThis.LCNPost = class LCNPost {
|
||||
|
||||
static nodeAttrib = "$LCNPost";
|
||||
static selector = ".post:not(.grid-li)";
|
||||
#parent = null;
|
||||
#post = null;
|
||||
#info = null;
|
||||
|
@ -137,7 +185,7 @@ globalThis.LCNPost = class LCNPost {
|
|||
#controls = null;
|
||||
#customControlsSeperatorNode = null;
|
||||
|
||||
static "assign" (post) { return post.$LCNPost ?? (post.$LCNPost = this.from(post)); }
|
||||
static "assign" (post) { return post[this.nodeAttrib] ?? (post[this.nodeAttrib] = this.from(post)); }
|
||||
static "from" (post) { return new this(post); }
|
||||
|
||||
"constructor" (post) {
|
||||
|
@ -146,7 +194,7 @@ globalThis.LCNPost = class LCNPost {
|
|||
this.#post = post
|
||||
this.#info = LCNPostInfo.assign(post)
|
||||
this.#ipLink = intro.querySelector(".ip-link")
|
||||
this.#controls = arrLast(post.querySelectorAll(".controls"))
|
||||
this.#controls = Array.prototype.at.apply(post.querySelectorAll(".controls"), [ -1 ])
|
||||
|
||||
assert.equal(this.#info.getParent(), null, "Info should not have parent.")
|
||||
this.#info.__setParent(this)
|
||||
|
@ -193,26 +241,28 @@ globalThis.LCNPost = class LCNPost {
|
|||
|
||||
globalThis.LCNThread = class LCNThread {
|
||||
|
||||
static nodeAttrib = "$LCNThread";
|
||||
static selector = ".thread:not(.grid-li)";
|
||||
#element = null;
|
||||
#parent = null;
|
||||
#thread = null;
|
||||
#op = null;
|
||||
|
||||
static "assign" (thread) { return thread.$LCNThread ?? (thread.$LCNThread = this.from(thread)); }
|
||||
static "assign" (thread) { return thread[this.nodeAttrib] ?? (thread[this.nodeAttrib] = this.from(thread)); }
|
||||
static "from" (thread) { return new this(thread); }
|
||||
|
||||
"constructor" (thread) {
|
||||
assert.ok(thread.classList.contains("thread"), "Arty must be expected Element.")
|
||||
this.#thread = thread
|
||||
this.#op = LCNPost.assign(this.#thread.querySelector(".post.op"))
|
||||
this.#element = thread
|
||||
this.#op = LCNPost.assign(this.#element.querySelector(".post.op"))
|
||||
|
||||
assert.equal(this.#op.getParent(), null, "Op should not have parent.")
|
||||
this.#op.__setParent(this)
|
||||
}
|
||||
|
||||
"getElement" () { return this.#thread; }
|
||||
"getElement" () { return this.#element; }
|
||||
"getContent" () { return this.#op; }
|
||||
"getPosts" () { return Array.prototype.map.apply(this.#thread.querySelectorAll(".post"), [ el => LCNPost.assign(el) ]); }
|
||||
"getReplies" () { return Array.prototype.map.apply(this.#thread.querySelectorAll(".post:not(.op)"), [ el => LCNPost.assign(el) ]); }
|
||||
"getPosts" () { return Array.prototype.map.apply(this.#element.querySelectorAll(".post"), [ el => LCNPost.assign(el) ]); }
|
||||
"getReplies" () { return Array.prototype.map.apply(this.#element.querySelectorAll(".post:not(.op)"), [ el => LCNPost.assign(el) ]); }
|
||||
|
||||
"getParent" () { return this.#parent; }
|
||||
"__setParent" (inst) { return this.#parent = inst; }
|
||||
|
@ -221,19 +271,21 @@ globalThis.LCNThread = class LCNThread {
|
|||
|
||||
globalThis.LCNPostContainer = class LCNPostContainer {
|
||||
|
||||
static nodeAttrib = "$LCNPostContainer";
|
||||
static selector = ".postcontainer";
|
||||
#parent = null;
|
||||
#container = null;
|
||||
#element = null;
|
||||
#content = null;
|
||||
#postId = null;
|
||||
#boardId = null;
|
||||
|
||||
static "assign" (container) { return container.$LCNPostContainer ?? (container.$LCNPostContainer = this.from(container)); }
|
||||
static "assign" (container) { return container[this.nodeAttrib] ?? (container[this.nodeAttrib] = this.from(container)); }
|
||||
static "from" (container) { return new this(container); }
|
||||
|
||||
"constructor" (container) {
|
||||
assert.ok(container.classList.contains("postcontainer"), "Arty must be expected Element.")
|
||||
const child = container.querySelector(".thread, .post")
|
||||
this.#container = container
|
||||
this.#element = container
|
||||
this.#content = child.classList.contains("thread") ? LCNThread.assign(child) : LCNPost.assign(child)
|
||||
this.#boardId = container.dataset.board
|
||||
this.#postId = container.id.slice(2)
|
||||
|
@ -242,7 +294,7 @@ globalThis.LCNPostContainer = class LCNPostContainer {
|
|||
this.#content.__setParent(this)
|
||||
}
|
||||
|
||||
"getContainer" () { return this.#container; }
|
||||
"getElement" () { return this.#element; }
|
||||
"getContent" () { return this.#content; }
|
||||
"getBoardId" () { return this.#boardId; }
|
||||
"getPostId" () { return this.#postId; }
|
||||
|
@ -254,13 +306,15 @@ globalThis.LCNPostContainer = class LCNPostContainer {
|
|||
|
||||
globalThis.LCNPostWrapper = class LCNPostWrapper {
|
||||
|
||||
static nodeAttrib = "$LCNPostWrapper";
|
||||
static selector = ".post-wrapper";
|
||||
#wrapper = null;
|
||||
#eitaLink = null;
|
||||
#eitaId = null;
|
||||
#eitaHref = null
|
||||
#content = null;
|
||||
|
||||
static "assign" (wrapper) { return wrapper.$LCNPostWrapper ?? (wrapper.$LCNPostWrapper = this.from(wrapper)); }
|
||||
static "assign" (wrapper) { return wrapper[this.nodeAttrib] ?? (wrapper[this.nodeAttrib] = this.from(wrapper)); }
|
||||
static "from" (wrapper) { return new this(wrapper); }
|
||||
|
||||
"constructor" (wrapper) {
|
||||
|
@ -298,23 +352,140 @@ globalThis.LCNPostWrapper = class LCNPostWrapper {
|
|||
|
||||
}
|
||||
|
||||
globalThis.LCNPost.all = () => Array.prototype.map.apply(document.querySelectorAll(".post:not(.grid-li)"), [ node => LCNPost.assign(node) ]);
|
||||
globalThis.LCNThread.all = () => Array.prototype.map.apply(document.querySelectorAll(".thread:not(.grid-li)"), [ node => LCNThread.assign(node) ]);
|
||||
globalThis.LCNPostContainer.all = () => Array.prototype.map.apply(document.querySelectorAll(".postcontainer"), [ node => LCNPostContainer.assign(node) ]);
|
||||
globalThis.LCNPostWrapper.all = () => Array.prototype.map.apply(document.querySelectorAll(".post-wrapper"), [ node => LCNPostWrapper.assign(node) ]);
|
||||
globalThis.LCNSetting = class LCNSetting {
|
||||
#id = null;
|
||||
#eventId = null;
|
||||
#label = null;
|
||||
#value = null;
|
||||
#valueDefault = null;
|
||||
|
||||
static "build" (id) { return new this(id); }
|
||||
|
||||
"constructor" (id) {
|
||||
this.#id = id;
|
||||
this.#eventId = `lcnsetting::${this.#id}`
|
||||
}
|
||||
|
||||
#getValue () {
|
||||
const v = localStorage.getItem(this.#id)
|
||||
if (v != null) {
|
||||
return this.__builtinValueImporter(v)
|
||||
} else {
|
||||
return this.#valueDefault
|
||||
}
|
||||
}
|
||||
|
||||
"getValue" () { return this.#value ?? (this.#value = this.#getValue()); }
|
||||
"setValue" (v) {
|
||||
if (this.#value !== v) {
|
||||
this.#value = v
|
||||
localStorage.setItem(this.#id, this.__builtinValueExporter(this.#value))
|
||||
setTimeout(() => $(document).trigger(`${this.#eventId}::change`, [ v, this ]), 1)
|
||||
}
|
||||
}
|
||||
|
||||
"getLabel" () { return this.#label; }
|
||||
"setLabel" (label) { this.#label = label; return this; }
|
||||
|
||||
"getDefaultValue" () { return this.#valueDefault; }
|
||||
"setDefaultValue" (vd) { this.#valueDefault = vd; return this; }
|
||||
|
||||
"onChange" (fn) { $(document).on(`${this.#eventId}::change`, (_,v,i) => fn(v, i)); }
|
||||
__setIdPrefix (prefix) { this.#id = `${prefix}_${this.#id}`; }
|
||||
}
|
||||
|
||||
globalThis.LCNToggleSetting = class LCNToggleSetting extends LCNSetting {
|
||||
__builtinValueImporter (v) { return v == "1"; }
|
||||
__builtinValueExporter (v) { return v ? "1" : ""; }
|
||||
__builtinDOMConstructor () {
|
||||
const div = document.createElement("div")
|
||||
const chk = document.createElement("input")
|
||||
const txt = document.createElement("label")
|
||||
txt.innerText = this.getLabel()
|
||||
chk.type = "checkbox"
|
||||
chk.checked = this.getValue()
|
||||
chk.addEventListener("click", e => {
|
||||
e.preventDefault();
|
||||
this.setValue(!this.getValue())
|
||||
})
|
||||
this.onChange(v => chk.checked = v)
|
||||
|
||||
div.appendChild(chk)
|
||||
div.appendChild(txt)
|
||||
return div
|
||||
}
|
||||
}
|
||||
|
||||
globalThis.LCNSettingsSubcategory = class LCNSettingsSubcategory {
|
||||
|
||||
#tab_id = null;
|
||||
#id = null;
|
||||
|
||||
#fieldset = null;
|
||||
#legend = null
|
||||
#label = null;
|
||||
|
||||
static "for" (tab_id, id) {
|
||||
const domid = `lcnssc_${tab_id}_${id}`
|
||||
const inst = document.getElementById(domid)?.$LCNSettingsSubcategory
|
||||
if (inst == null) {
|
||||
const fieldset = document.createElement("fieldset")
|
||||
const legend = document.createElement("legend")
|
||||
fieldset.id = domid
|
||||
fieldset.appendChild(legend)
|
||||
|
||||
// XXX: extend_tab only takes a string so this hacky workaround is used to let us use the regular dom api
|
||||
Options.extend_tab(tab_id, `<div id="__${domid}" hidden></div>`)
|
||||
const div = document.getElementById(`__${domid}`)?.parentElement
|
||||
assert.ok(div)
|
||||
|
||||
div.replaceChildren(fieldset)
|
||||
return new this(tab_id, id, fieldset)
|
||||
} else {
|
||||
return inst
|
||||
}
|
||||
}
|
||||
|
||||
"constructor" (tab_id, id, fieldset) {
|
||||
this.#tab_id = tab_id
|
||||
this.#id = id
|
||||
this.#fieldset = fieldset
|
||||
this.#legend = this.#fieldset.querySelector("legend")
|
||||
this.#fieldset.$LCNSettingsSubcategory = this
|
||||
}
|
||||
|
||||
"getLabel" () { return this.#label; }
|
||||
"setLabel" (label) { this.#legend.innerText = this.#label = label; return this; }
|
||||
"addSetting" (setting) {
|
||||
assert.ok(setting instanceof LCNSetting)
|
||||
setting.__setIdPrefix(`lcnsetting_${this.#tab_id}_${this.#id}`)
|
||||
if (setting.__builtinDOMConstructor != null) {
|
||||
const div = setting.__builtinDOMConstructor()
|
||||
div.classList.add("lcn-setting-entry")
|
||||
this.#fieldset.appendChild(div)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$().ready(() => {
|
||||
LCNSite.INSTANCE = new LCNSite();
|
||||
|
||||
const clazzes = [ LCNPost, LCNThread, LCNPostContainer, LCNPostWrapper ]
|
||||
for (const clazz of clazzes) {
|
||||
clazz.forEach = fn => clazz.all().forEach(fn)
|
||||
clazz.filter = fn => clazz.all().filter(fn)
|
||||
for (const clazz of [ LCNPost, LCNPostInfo, LCNThread, LCNPostContainer, LCNPostWrapper ]) {
|
||||
clazz.allNodes = (node=document) => node.querySelectorAll(clazz.selector)
|
||||
clazz.all = (node=document) => Array.prototype.map.apply(clazz.allNodes(node), [ elem => clazz.assign(elem) ]);
|
||||
clazz.clear = (node=document) => Array.prototype.forEach.apply(clazz.allNodes(node), [ elem => elem[clazz.nodeAttrib] = null ])
|
||||
clazz.forEach = (fn, node=document) => clazz.allNodes(node).forEach(elem => fn(clazz.assign(elem)))
|
||||
clazz.filter = (fn, node=document) => clazz.all(node).filter(fn)
|
||||
clazz.find = fn => clazz.all().find(fn)
|
||||
clazz.first = (node=document) => clazz.assign(node.querySelector(clazz.selector))
|
||||
clazz.last = (node=document) => clazz.assign(Array.prototype.at.apply(clazz.allNodes(node), [ -1 ]))
|
||||
}
|
||||
|
||||
// XXX: May be a cleaner way to do this but this should be fine for now.
|
||||
for (const clazz of clazzes) { void clazz.all(); }
|
||||
for (const clazz of [ LCNPostContainer, LCNPostWrapper, LCNThread, LCNPost ]) { void clazz.all(); }
|
||||
$(document).on("new_post", (e, post) => {
|
||||
if (LCNSite.INSTANCE.isModRecentsPage()) {
|
||||
void LCNPostWrapper.all()
|
||||
|
@ -322,4 +493,7 @@ $().ready(() => {
|
|||
void LCNPostContainer.all()
|
||||
}
|
||||
})
|
||||
|
||||
$(window).on("focus", () => LCNSite.INSTANCE.clearUnseen())
|
||||
$(document.body).on("mousemove", () => LCNSite.INSTANCE.clearUnseen())
|
||||
})
|
||||
|
|
|
@ -3,14 +3,6 @@
|
|||
* @author jonsmy
|
||||
*/
|
||||
|
||||
const arrLast = arr => arr[arr.length-1] ?? undefined;
|
||||
const getConfigBool = (k,d) => { const v = localStorage.getItem(`jon-modjs::${k}`); return v ? v == "1" : d; }
|
||||
const writeCSSStyle = textContent => {
|
||||
const style = document.createElement("style")
|
||||
style.textContent = textContent
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
|
||||
const assert = {
|
||||
"equal": (actual, expected, message="No message set") => {
|
||||
if (actual !== expected) {
|
||||
|
|
Loading…
Reference in New Issue