var to const

This commit is contained in:
Lalle 2023-04-28 00:30:44 +02:00
parent 80167e23f4
commit b25d2023ca
No known key found for this signature in database
GPG Key ID: A6583D207A8F6B0D
6 changed files with 283 additions and 287 deletions

View File

@ -5,14 +5,9 @@ import $$ from "../platform/$$"
import Header from "./Header" import Header from "./Header"
/* /*
* decaffeinate suggestions: Very sensitive, changing var to const
* DS101: Remove unnecessary use of Array.from
* DS102: Remove unnecessary code created because of implicit returns
* DS206: Consider reworking classes to avoid initClass
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/ */
const dialog = function(id, properties) { const dialog = function (id, properties) {
const el = $.el('div', { const el = $.el('div', {
className: 'dialog', className: 'dialog',
id id
@ -30,13 +25,13 @@ const dialog = function(id, properties) {
return el return el
} }
//eslint-disable-next-line
var Menu = (function() { var Menu = (function () {
let currentMenu = undefined let currentMenu = undefined
let lastToggledButton = undefined let lastToggledButton = undefined
Menu = class Menu { Menu = class Menu {
static initClass() { static initClass() {
currentMenu = null currentMenu = null
lastToggledButton = null lastToggledButton = null
} }
@ -48,7 +43,7 @@ var Menu = (function() {
this.onFocus = this.onFocus.bind(this) this.onFocus = this.onFocus.bind(this)
this.addEntry = this.addEntry.bind(this) this.addEntry = this.addEntry.bind(this)
this.type = type this.type = type
$.on(d, 'AddMenuEntry', ({detail}) => { $.on(d, 'AddMenuEntry', ({ detail }) => {
if (detail.type !== this.type) { return } if (detail.type !== this.type) { return }
delete detail.open delete detail.open
return this.addEntry(detail) return this.addEntry(detail)
@ -59,8 +54,8 @@ var Menu = (function() {
makeMenu() { makeMenu() {
const menu = $.el('div', { const menu = $.el('div', {
className: 'dialog', className: 'dialog',
id: 'menu', id: 'menu',
tabIndex: 0 tabIndex: 0
} }
) )
menu.dataset.type = this.type menu.dataset.type = this.type
@ -88,7 +83,7 @@ var Menu = (function() {
open(button, data) { open(button, data) {
let entry let entry
const menu = (this.menu = this.makeMenu()) const menu = (this.menu = this.makeMenu())
currentMenu = this currentMenu = this
lastToggledButton = button lastToggledButton = button
this.entries.sort((first, second) => first.order - second.order) this.entries.sort((first, second) => first.order - second.order)
@ -116,21 +111,21 @@ var Menu = (function() {
} }
setPosition() { setPosition() {
const mRect = this.menu.getBoundingClientRect() const mRect = this.menu.getBoundingClientRect()
const bRect = lastToggledButton.getBoundingClientRect() const bRect = lastToggledButton.getBoundingClientRect()
const bTop = window.scrollY + bRect.top const bTop = window.scrollY + bRect.top
const bLeft = window.scrollX + bRect.left const bLeft = window.scrollX + bRect.left
const cHeight = doc.clientHeight const cHeight = doc.clientHeight
const cWidth = doc.clientWidth const cWidth = doc.clientWidth
const [top, bottom] = Array.from((bRect.top + bRect.height + mRect.height) < cHeight ? const [top, bottom] = Array.from((bRect.top + bRect.height + mRect.height) < cHeight ?
[`${bRect.bottom}px`, ''] [`${bRect.bottom}px`, '']
: :
['', `${cHeight - bRect.top}px`]) ['', `${cHeight - bRect.top}px`])
const [left, right] = Array.from((bRect.left + mRect.width) < cWidth ? const [left, right] = Array.from((bRect.left + mRect.width) < cWidth ?
[`${bRect.left}px`, ''] [`${bRect.left}px`, '']
: :
['', `${cWidth - bRect.right}px`]) ['', `${cWidth - bRect.right}px`])
$.extend(this.menu.style, {top, right, bottom, left}) $.extend(this.menu.style, { top, right, bottom, left })
return this.menu.classList.toggle('left', right) return this.menu.classList.toggle('left', right)
} }
@ -155,7 +150,7 @@ var Menu = (function() {
$.rm(submenu) $.rm(submenu)
} }
submenu = $.el('div', submenu = $.el('div',
{className: 'dialog submenu'}) { className: 'dialog submenu' })
for (const subEntry of entry.subEntries) { for (const subEntry of entry.subEntries) {
this.insertEntry(subEntry, submenu, data) this.insertEntry(subEntry, submenu, data)
} }
@ -166,7 +161,7 @@ var Menu = (function() {
$.rm(this.menu) $.rm(this.menu)
delete this.menu delete this.menu
$.rmClass(lastToggledButton, 'active') $.rmClass(lastToggledButton, 'active')
currentMenu = null currentMenu = null
lastToggledButton = null lastToggledButton = null
$.off(d, 'click scroll CloseMenu', this.close) $.off(d, 'click scroll CloseMenu', this.close)
$.off(d, 'scroll', this.setPosition) $.off(d, 'scroll', this.setPosition)
@ -244,23 +239,23 @@ var Menu = (function() {
// Submenu positioning. // Submenu positioning.
if (!(submenu = $('.submenu', entry))) { return } if (!(submenu = $('.submenu', entry))) { return }
const sRect = submenu.getBoundingClientRect() const sRect = submenu.getBoundingClientRect()
const eRect = entry.getBoundingClientRect() const eRect = entry.getBoundingClientRect()
const cHeight = doc.clientHeight const cHeight = doc.clientHeight
const cWidth = doc.clientWidth const cWidth = doc.clientWidth
const [top, bottom] = Array.from((eRect.top + sRect.height) < cHeight ? const [top, bottom] = Array.from((eRect.top + sRect.height) < cHeight ?
['0px', 'auto'] ['0px', 'auto']
: :
['auto', '0px']) ['auto', '0px'])
const [left, right] = Array.from((eRect.right + sRect.width) < (cWidth - 150) ? const [left, right] = Array.from((eRect.right + sRect.width) < (cWidth - 150) ?
['100%', 'auto'] ['100%', 'auto']
: :
['auto', '100%']) ['auto', '100%'])
const {style} = submenu const { style } = submenu
style.top = top style.top = top
style.bottom = bottom style.bottom = bottom
style.left = left style.left = left
return style.right = right return style.right = right
} }
addEntry(entry) { addEntry(entry) {
@ -269,7 +264,7 @@ var Menu = (function() {
} }
parseEntry(entry) { parseEntry(entry) {
const {el, subEntries} = entry const { el, subEntries } = entry
$.addClass(el, 'entry') $.addClass(el, 'entry')
$.on(el, 'focus mouseover', this.onFocus) $.on(el, 'focus mouseover', this.onFocus)
el.style.order = entry.order || 100 el.style.order = entry.order || 100
@ -284,7 +279,7 @@ var Menu = (function() {
return Menu return Menu
})() })()
export var dragstart = function (e) { export const dragstart = function (e) {
let isTouching let isTouching
if ((e.type === 'mousedown') && (e.button !== 0)) { return } // not LMB if ((e.type === 'mousedown') && (e.button !== 0)) { return } // not LMB
// prevent text selection // prevent text selection
@ -296,14 +291,14 @@ export var dragstart = function (e) {
const el = $.x('ancestor::div[contains(@class,"dialog")][1]', this) const el = $.x('ancestor::div[contains(@class,"dialog")][1]', this)
const rect = el.getBoundingClientRect() const rect = el.getBoundingClientRect()
const screenHeight = doc.clientHeight const screenHeight = doc.clientHeight
const screenWidth = doc.clientWidth const screenWidth = doc.clientWidth
const o = { const o = {
id: el.id, id: el.id,
style: el.style, style: el.style,
dx: e.clientX - rect.left, dx: e.clientX - rect.left,
dy: e.clientY - rect.top, dy: e.clientY - rect.top,
height: screenHeight - rect.height, height: screenHeight - rect.height,
width: screenWidth - rect.width, width: screenWidth - rect.width,
screenHeight, screenHeight,
screenWidth, screenWidth,
isTouching isTouching
@ -311,26 +306,26 @@ export var dragstart = function (e) {
[o.topBorder, o.bottomBorder] = Array.from(Conf['Header auto-hide'] || !Conf['Fixed Header'] ? [o.topBorder, o.bottomBorder] = Array.from(Conf['Header auto-hide'] || !Conf['Fixed Header'] ?
[0, 0] [0, 0]
: Conf['Bottom Header'] ? : Conf['Bottom Header'] ?
[0, Header.bar.getBoundingClientRect().height] [0, Header.bar.getBoundingClientRect().height]
: :
[Header.bar.getBoundingClientRect().height, 0]) [Header.bar.getBoundingClientRect().height, 0])
if (isTouching) { if (isTouching) {
o.identifier = e.identifier o.identifier = e.identifier
o.move = touchmove.bind(o) o.move = touchmove.bind(o)
o.up = touchend.bind(o) o.up = touchend.bind(o)
$.on(d, 'touchmove', o.move) $.on(d, 'touchmove', o.move)
return $.on(d, 'touchend touchcancel', o.up) return $.on(d, 'touchend touchcancel', o.up)
} else { // mousedown } else { // mousedown
o.move = drag.bind(o) o.move = drag.bind(o)
o.up = dragend.bind(o) o.up = dragend.bind(o)
$.on(d, 'mousemove', o.move) $.on(d, 'mousemove', o.move)
return $.on(d, 'mouseup', o.up) return $.on(d, 'mouseup', o.up)
} }
} }
export var touchmove = function (e) { export const touchmove = function (e) {
for (const touch of e.changedTouches) { for (const touch of e.changedTouches) {
if (touch.identifier === this.identifier) { if (touch.identifier === this.identifier) {
drag.call(this, touch) drag.call(this, touch)
@ -339,43 +334,43 @@ export var touchmove = function (e) {
} }
} }
export var drag = function (e) { export const drag = function (e) {
const {clientX, clientY} = e const { clientX, clientY } = e
let left = clientX - this.dx let left = clientX - this.dx
left = left < 10 ? left = left < 10 ?
0 0
: (this.width - left) < 10 ? : (this.width - left) < 10 ?
'' ''
: :
((left / this.screenWidth) * 100) + '%' ((left / this.screenWidth) * 100) + '%'
let top = clientY - this.dy let top = clientY - this.dy
top = top < (10 + this.topBorder) ? top = top < (10 + this.topBorder) ?
this.topBorder + 'px' this.topBorder + 'px'
: (this.height - top) < (10 + this.bottomBorder) ? : (this.height - top) < (10 + this.bottomBorder) ?
'' ''
: :
((top / this.screenHeight) * 100) + '%' ((top / this.screenHeight) * 100) + '%'
const right = left === '' ? const right = left === '' ?
0 0
: :
'' ''
const bottom = top === '' ? const bottom = top === '' ?
this.bottomBorder + 'px' this.bottomBorder + 'px'
: :
'' ''
const {style} = this const { style } = this
style.left = left style.left = left
style.right = right style.right = right
style.top = top style.top = top
return style.bottom = bottom return style.bottom = bottom
} }
export var touchend = function (e) { export const touchend = function (e) {
for (const touch of e.changedTouches) { for (const touch of e.changedTouches) {
if (touch.identifier === this.identifier) { if (touch.identifier === this.identifier) {
dragend.call(this) dragend.call(this)
@ -384,13 +379,13 @@ export var touchend = function (e) {
} }
} }
export var dragend = function () { export const dragend = function () {
if (this.isTouching) { if (this.isTouching) {
$.off(d, 'touchmove', this.move) $.off(d, 'touchmove', this.move)
$.off(d, 'touchend touchcancel', this.up) $.off(d, 'touchend touchcancel', this.up)
} else { // mouseup } else { // mouseup
$.off(d, 'mousemove', this.move) $.off(d, 'mousemove', this.move)
$.off(d, 'mouseup', this.up) $.off(d, 'mouseup', this.up)
} }
return $.set(`${this.id}.position`, this.style.cssText) return $.set(`${this.id}.position`, this.style.cssText)
} }
@ -406,43 +401,43 @@ const hoverstart = function ({ root, el, latestEvent, endEvents, height, width,
endEvents, endEvents,
latestEvent, latestEvent,
clientHeight: doc.clientHeight, clientHeight: doc.clientHeight,
clientWidth: doc.clientWidth, clientWidth: doc.clientWidth,
height, height,
width, width,
noRemove, noRemove,
clientX: (rect.left + rect.right) / 2, clientX: (rect.left + rect.right) / 2,
clientY: (rect.top + rect.bottom) / 2 clientY: (rect.top + rect.bottom) / 2
} }
o.hover = hover.bind(o) o.hover = hover.bind(o)
o.hoverend = hoverend.bind(o) o.hoverend = hoverend.bind(o)
o.hover(o.latestEvent) o.hover(o.latestEvent)
new MutationObserver(function() { new MutationObserver(function () {
if (el.parentNode) { return o.hover(o.latestEvent) } if (el.parentNode) { return o.hover(o.latestEvent) }
}).observe(el, {childList: true}) }).observe(el, { childList: true })
$.on(root, endEvents, o.hoverend) $.on(root, endEvents, o.hoverend)
if ($.x('ancestor::div[contains(@class,"inline")][1]', root)) { if ($.x('ancestor::div[contains(@class,"inline")][1]', root)) {
$.on(d, 'keydown', o.hoverend) $.on(d, 'keydown', o.hoverend)
} }
$.on(root, 'mousemove', o.hover) $.on(root, 'mousemove', o.hover)
// Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=674955 // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=674955
o.workaround = function(e) { if (!root.contains(e.target)) { return o.hoverend(e) } } o.workaround = function (e) { if (!root.contains(e.target)) { return o.hoverend(e) } }
return $.on(doc, 'mousemove', o.workaround) return $.on(doc, 'mousemove', o.workaround)
} }
hoverstart.padding = 25 hoverstart.padding = 25
export var hover = function (e) { export const hover = function (e) {
this.latestEvent = e this.latestEvent = e
const height = (this.height || this.el.offsetHeight) + hoverstart.padding const height = (this.height || this.el.offsetHeight) + hoverstart.padding
const width = (this.width || this.el.offsetWidth) const width = (this.width || this.el.offsetWidth)
const {clientX, clientY} = Conf['Follow Cursor'] ? e : this const { clientX, clientY } = Conf['Follow Cursor'] ? e : this
const top = this.isImage ? const top = this.isImage ?
Math.max(0, (clientY * (this.clientHeight - height)) / this.clientHeight) Math.max(0, (clientY * (this.clientHeight - height)) / this.clientHeight)
: :
Math.max(0, Math.min(this.clientHeight - height, clientY - 120)) Math.max(0, Math.min(this.clientHeight - height, clientY - 120))
let threshold = this.clientWidth / 2 let threshold = this.clientWidth / 2
@ -452,27 +447,27 @@ export var hover = function (e) {
marginX += 'px' marginX += 'px'
const [left, right] = Array.from(clientX <= threshold ? [marginX, ''] : ['', marginX]) const [left, right] = Array.from(clientX <= threshold ? [marginX, ''] : ['', marginX])
const {style} = this const { style } = this
style.top = top + 'px' style.top = top + 'px'
style.left = left style.left = left
return style.right = right return style.right = right
} }
export var hoverend = function (e) { export const hoverend = function (e) {
if (((e.type === 'keydown') && (e.keyCode !== 13)) || (e.target.nodeName === "TEXTAREA")) { return } if (((e.type === 'keydown') && (e.keyCode !== 13)) || (e.target.nodeName === "TEXTAREA")) { return }
if (!this.noRemove) { $.rm(this.el) } if (!this.noRemove) { $.rm(this.el) }
$.off(this.root, this.endEvents, this.hoverend) $.off(this.root, this.endEvents, this.hoverend)
$.off(d, 'keydown', this.hoverend) $.off(d, 'keydown', this.hoverend)
$.off(this.root, 'mousemove', this.hover) $.off(this.root, 'mousemove', this.hover)
// Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=674955 // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=674955
$.off(doc, 'mousemove', this.workaround) $.off(doc, 'mousemove', this.workaround)
if (this.cb) { return this.cb.call(this) } if (this.cb) { return this.cb.call(this) }
} }
export const checkbox = function (name, text, checked) { export const checkbox = function (name, text, checked) {
if (checked == null) { checked = Conf[name] } if (checked == null) { checked = Conf[name] }
const label = $.el('label') const label = $.el('label')
const input = $.el('input', {type: 'checkbox', name, checked}) const input = $.el('input', { type: 'checkbox', name, checked })
$.add(label, [input, $.tn(` ${text}`)]) $.add(label, [input, $.tn(` ${text}`)])
return label return label
} }
@ -480,7 +475,7 @@ export const checkbox = function (name, text, checked) {
const UI = { const UI = {
dialog, dialog,
Menu, Menu,
hover: hoverstart, hover: hoverstart,
checkbox checkbox
} }
export default UI export default UI

View File

@ -25,18 +25,18 @@ import QuickReplyPage from './QR/QuickReply.html'
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/ */
var QR = { const QR = {
mimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'application/vnd.adobe.flash.movie', 'application/x-shockwave-flash', 'video/webm'], mimeTypes: ['image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'application/vnd.adobe.flash.movie', 'application/x-shockwave-flash', 'video/webm'],
validExtension: /\.(jpe?g|png|gif|pdf|swf|webm)$/i, validExtension: /\.(jpe?g|png|gif|pdf|swf|webm)$/i,
typeFromExtension: { typeFromExtension: {
'jpg': 'image/jpeg', 'jpg': 'image/jpeg',
'jpeg': 'image/jpeg', 'jpeg': 'image/jpeg',
'png': 'image/png', 'png': 'image/png',
'gif': 'image/gif', 'gif': 'image/gif',
'pdf': 'application/pdf', 'pdf': 'application/pdf',
'swf': 'application/vnd.adobe.flash.movie', 'swf': 'application/vnd.adobe.flash.movie',
'webm': 'video/webm' 'webm': 'video/webm'
}, },
@ -60,7 +60,7 @@ var QR = {
Callbacks.Post.push({ Callbacks.Post.push({
name: 'Quick Reply', name: 'Quick Reply',
cb: this.node cb: this.node
}) })
this.shortcut = (sc = $.el('a', { this.shortcut = (sc = $.el('a', {
@ -70,7 +70,7 @@ var QR = {
href: 'javascript:;' href: 'javascript:;'
} }
)) ))
$.on(sc, 'click', function() { $.on(sc, 'click', function () {
if (!QR.postingIsEnabled) { return } if (!QR.postingIsEnabled) { return }
if (Conf['Persistent QR'] || !QR.nodes || QR.nodes.el.hidden) { if (Conf['Persistent QR'] || !QR.nodes || QR.nodes.el.hidden) {
QR.open() QR.open()
@ -89,33 +89,33 @@ var QR = {
QR.captcha = Captcha[captchaVersion] QR.captcha = Captcha[captchaVersion]
QR.postingIsEnabled = true QR.postingIsEnabled = true
const {config} = g.BOARD const { config } = g.BOARD
const prop = (key, def) => +(config[key] ?? def) const prop = (key, def) => +(config[key] ?? def)
QR.min_width = prop('min_image_width', 1) QR.min_width = prop('min_image_width', 1)
QR.min_height = prop('min_image_height', 1) QR.min_height = prop('min_image_height', 1)
QR.max_width = (QR.max_height = 10000) QR.max_width = (QR.max_height = 10000)
QR.max_size = prop('max_filesize', 4194304) QR.max_size = prop('max_filesize', 4194304)
QR.max_size_video = prop('max_webm_filesize', QR.max_size) QR.max_size_video = prop('max_webm_filesize', QR.max_size)
QR.max_comment = prop('max_comment_chars', 2000) QR.max_comment = prop('max_comment_chars', 2000)
QR.max_width_video = (QR.max_height_video = 2048) QR.max_width_video = (QR.max_height_video = 2048)
QR.max_duration_video = prop('max_webm_duration', 120) QR.max_duration_video = prop('max_webm_duration', 120)
QR.forcedAnon = !!config.forced_anon QR.forcedAnon = !!config.forced_anon
QR.spoiler = !!config.spoilers QR.spoiler = !!config.spoilers
if (origToggle = $.id('togglePostFormLink')) { if (origToggle = $.id('togglePostFormLink')) {
const link = $.el('h1', const link = $.el('h1',
{className: "qr-link-container"}) { className: "qr-link-container" })
$.extend(link, { $.extend(link, {
innerHTML: innerHTML:
`<a href="javascript:;" class="qr-link">${g.VIEW === "thread" ? "Reply to Thread" : "Start a Thread"}</a>` `<a href="javascript:;" class="qr-link">${g.VIEW === "thread" ? "Reply to Thread" : "Start a Thread"}</a>`
}) })
QR.link = link.firstElementChild QR.link = link.firstElementChild
$.on(link.firstChild, 'click', function() { $.on(link.firstChild, 'click', function () {
QR.open() QR.open()
return QR.nodes.com.focus() return QR.nodes.com.focus()
}) })
@ -127,10 +127,10 @@ var QR = {
if (g.VIEW === 'thread') { if (g.VIEW === 'thread') {
let navLinksBot let navLinksBot
const linkBot = $.el('div', const linkBot = $.el('div',
{className: "brackets-wrap qr-link-container-bottom"}) { className: "brackets-wrap qr-link-container-bottom" })
$.extend(linkBot, {innerHTML: '<a href="javascript:;" class="qr-link-bottom">Reply to Thread</a>'}) $.extend(linkBot, { innerHTML: '<a href="javascript:;" class="qr-link-bottom">Reply to Thread</a>' })
$.on(linkBot.firstElementChild, 'click', function() { $.on(linkBot.firstElementChild, 'click', function () {
QR.open() QR.open()
return QR.nodes.com.focus() return QR.nodes.com.focus()
}) })
@ -138,14 +138,14 @@ var QR = {
if (navLinksBot = $('.navLinksBot')) { $.prepend(navLinksBot, linkBot) } if (navLinksBot = $('.navLinksBot')) { $.prepend(navLinksBot, linkBot) }
} }
$.on(d, 'QRGetFile', QR.getFile) $.on(d, 'QRGetFile', QR.getFile)
$.on(d, 'QRDrawFile', QR.drawFile) $.on(d, 'QRDrawFile', QR.drawFile)
$.on(d, 'QRSetFile', QR.setFile) $.on(d, 'QRSetFile', QR.setFile)
$.on(d, 'paste', QR.paste) $.on(d, 'paste', QR.paste)
$.on(d, 'dragover', QR.dragOver) $.on(d, 'dragover', QR.dragOver)
$.on(d, 'drop', QR.dropFile) $.on(d, 'drop', QR.dropFile)
$.on(d, 'dragstart dragend', QR.drag) $.on(d, 'dragstart dragend', QR.drag)
$.on(d, 'IndexRefreshInternal', QR.generatePostableThreadsList) $.on(d, 'IndexRefreshInternal', QR.generatePostableThreadsList)
$.on(d, 'ThreadUpdate', QR.statusCheck) $.on(d, 'ThreadUpdate', QR.statusCheck)
@ -157,7 +157,7 @@ var QR = {
statusCheck() { statusCheck() {
if (!QR.nodes) { return } if (!QR.nodes) { return }
const {thread} = QR.posts[0] const { thread } = QR.posts[0]
if ((thread !== 'new') && g.threads.get(`${g.BOARD}.${thread}`).isDead) { if ((thread !== 'new') && g.threads.get(`${g.BOARD}.${thread}`).isDead) {
return QR.abort() return QR.abort()
} else { } else {
@ -210,7 +210,7 @@ var QR = {
}, },
focus() { focus() {
return $.queueTask(function() { return $.queueTask(function () {
if (!QR.inBubble()) { if (!QR.inBubble()) {
QR.hasFocus = d.activeElement && QR.nodes.el.contains(d.activeElement) QR.hasFocus = d.activeElement && QR.nodes.el.contains(d.activeElement)
return QR.nodes.el.classList.toggle('focus', QR.hasFocus) return QR.nodes.el.classList.toggle('focus', QR.hasFocus)
@ -310,20 +310,21 @@ var QR = {
// Firefox automatically closes notifications // Firefox automatically closes notifications
// so we can't control the onclose properly. // so we can't control the onclose properly.
notif.onclose = () => notice.close() notif.onclose = () => notice.close()
return notif.onshow = () => setTimeout(function() { return notif.onshow = () => setTimeout(function () {
notif.onclose = null notif.onclose = null
return notif.close() return notif.close()
} }
, 7 * SECOND) , 7 * SECOND)
} }
} }
}, },
connectionError() { connectionError() {
return $.el('span', return $.el('span',
{ innerHTML: {
'Connection error while posting. ' + innerHTML:
'[<a href="' + meta.faq + '#connection-errors" target="_blank">More info</a>]' 'Connection error while posting. ' +
'[<a href="' + meta.faq + '#connection-errors" target="_blank">More info</a>]'
} }
) )
}, },
@ -340,25 +341,25 @@ var QR = {
status() { status() {
let disabled, value let disabled, value
if (!QR.nodes) { return } if (!QR.nodes) { return }
const {thread} = QR.posts[0] const { thread } = QR.posts[0]
if ((thread !== 'new') && g.threads.get(`${g.BOARD}.${thread}`).isDead) { if ((thread !== 'new') && g.threads.get(`${g.BOARD}.${thread}`).isDead) {
value = 'Dead' value = 'Dead'
disabled = true disabled = true
QR.cooldown.auto = false QR.cooldown.auto = false
} }
value = QR.req ? value = QR.req ?
QR.req.progress QR.req.progress
: :
QR.cooldown.seconds || value QR.cooldown.seconds || value
const {status} = QR.nodes const { status } = QR.nodes
status.value = !value ? status.value = !value ?
'Submit' 'Submit'
: QR.cooldown.auto ? : QR.cooldown.auto ?
`Auto ${value}` `Auto ${value}`
: :
value value
return status.disabled = disabled || false return status.disabled = disabled || false
}, },
@ -366,7 +367,7 @@ var QR = {
QR.open() QR.open()
if (QR.selected.isLocked) { if (QR.selected.isLocked) {
const index = QR.posts.indexOf(QR.selected); const index = QR.posts.indexOf(QR.selected);
(QR.posts[index+1] || new QR.post()).select() (QR.posts[index + 1] || new QR.post()).select()
$.addClass(QR.nodes.el, 'dump') $.addClass(QR.nodes.el, 'dump')
return QR.cooldown.auto = true return QR.cooldown.auto = true
} }
@ -376,9 +377,9 @@ var QR = {
let range let range
e?.preventDefault() e?.preventDefault()
if (!QR.postingIsEnabled) { return } if (!QR.postingIsEnabled) { return }
const sel = d.getSelection() const sel = d.getSelection()
const post = Get.postFromNode(this) const post = Get.postFromNode(this)
const {root} = post.nodes const { root } = post.nodes
const postRange = new Range() const postRange = new Range()
postRange.selectNode(root) postRange.selectNode(root)
let text = post.board.ID === g.BOARD.ID ? `>>${post}\n` : `>>>/${post.board}/${post}\n` let text = post.board.ID === g.BOARD.ID ? `>>${post}\n` : `>>>/${post.board}/${post}\n`
@ -396,7 +397,7 @@ var QR = {
if (!range.toString().trim()) { continue } if (!range.toString().trim()) { continue }
const frag = range.cloneContents() const frag = range.cloneContents()
const ancestor = range.commonAncestorContainer const ancestor = range.commonAncestorContainer
// Quoting the insides of a spoiler/code tag. // Quoting the insides of a spoiler/code tag.
if ($.x('ancestor-or-self::*[self::s or contains(@class,"removed-spoiler")]', ancestor)) { if ($.x('ancestor-or-self::*[self::s or contains(@class,"removed-spoiler")]', ancestor)) {
@ -426,7 +427,7 @@ var QR = {
} }
QR.openPost() QR.openPost()
const {com, thread} = QR.nodes const { com, thread } = QR.nodes
if (!com.value) { thread.value = Get.threadFromNode(this) } if (!com.value) { thread.value = Get.threadFromNode(this) }
const wasOnlyQuotes = QR.selected.isOnlyQuotes() const wasOnlyQuotes = QR.selected.isOnlyQuotes()
@ -448,9 +449,9 @@ var QR = {
characterCount() { characterCount() {
const counter = QR.nodes.charCount const counter = QR.nodes.charCount
const count = QR.nodes.com.value.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '_').length const count = QR.nodes.com.value.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '_').length
counter.textContent = count counter.textContent = count
counter.hidden = count < (QR.max_comment/2) counter.hidden = count < (QR.max_comment / 2)
return (count > QR.max_comment ? $.addClass : $.rmClass)(counter, 'warning') return (count > QR.max_comment ? $.addClass : $.rmClass)(counter, 'warning')
}, },
@ -464,7 +465,7 @@ var QR = {
const isVideo = /^video\//.test(file) const isVideo = /^video\//.test(file)
const el = $.el((isVideo ? 'video' : 'img')) const el = $.el((isVideo ? 'video' : 'img'))
$.on(el, 'error', () => QR.openError()) $.on(el, 'error', () => QR.openError())
$.on(el, (isVideo ? 'loadeddata' : 'load'), function() { $.on(el, (isVideo ? 'loadeddata' : 'load'), function () {
e.target.getContext('2d').drawImage(el, 0, 0) e.target.getContext('2d').drawImage(el, 0, 0)
URL.revokeObjectURL(el.src) URL.revokeObjectURL(el.src)
return $.event('QRImageDrawn', null, e.target) return $.event('QRImageDrawn', null, e.target)
@ -482,8 +483,8 @@ var QR = {
}, },
setFile(e) { setFile(e) {
const {file, name, source} = e.detail const { file, name, source } = e.detail
if (name != null) { file.name = name } if (name != null) { file.name = name }
if (source != null) { file.source = source } if (source != null) { file.source = source }
QR.open() QR.open()
return QR.handleFiles([file]) return QR.handleFiles([file])
@ -493,7 +494,7 @@ var QR = {
// Let it drag anything from the page. // Let it drag anything from the page.
const toggle = e.type === 'dragstart' ? $.off : $.on const toggle = e.type === 'dragstart' ? $.off : $.on
toggle(d, 'dragover', QR.dragOver) toggle(d, 'dragover', QR.dragOver)
return toggle(d, 'drop', QR.dropFile) return toggle(d, 'drop', QR.dropFile)
}, },
dragOver(e) { dragOver(e) {
@ -516,7 +517,7 @@ var QR = {
for (const item of e.clipboardData.items) { for (const item of e.clipboardData.items) {
var file2 var file2
if ((item.kind === 'file') && (file2 = item.getAsFile())) { if ((item.kind === 'file') && (file2 = item.getAsFile())) {
const score2 = (2*(file2.size <= QR.max_size)) + (file2.type === 'image/png') const score2 = (2 * (file2.size <= QR.max_size)) + (file2.type === 'image/png')
if (score2 > score) { if (score2 > score) {
file = file2 file = file2
score = score2 score = score2
@ -524,8 +525,8 @@ var QR = {
} }
} }
if (file) { if (file) {
const {type} = file const { type } = file
const blob = new Blob([file], {type}) const blob = new Blob([file], { type })
blob.name = `${Conf['pastedname']}.${$.getOwn(QR.extensionFromType, type) || 'jpg'}` blob.name = `${Conf['pastedname']}.${$.getOwn(QR.extensionFromType, type) || 'jpg'}`
QR.open() QR.open()
QR.handleFiles([blob]) QR.handleFiles([blob])
@ -534,20 +535,20 @@ var QR = {
}, },
pasteFF() { pasteFF() {
const {pasteArea} = QR.nodes const { pasteArea } = QR.nodes
if (!pasteArea.childNodes.length) { return } if (!pasteArea.childNodes.length) { return }
const images = $$('img', pasteArea) const images = $$('img', pasteArea)
$.rmAll(pasteArea) $.rmAll(pasteArea)
for (const img of images) { for (const img of images) {
var m var m
const {src} = img const { src } = img
if (m = src.match(/data:(image\/(\w+));base64,(.+)/)) { if (m = src.match(/data:(image\/(\w+));base64,(.+)/)) {
const bstr = atob(m[3]) const bstr = atob(m[3])
const arr = new Uint8Array(bstr.length) const arr = new Uint8Array(bstr.length)
for (let i = 0, end = bstr.length, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) { for (let i = 0, end = bstr.length, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) {
arr[i] = bstr.charCodeAt(i) arr[i] = bstr.charCodeAt(i)
} }
const blob = new Blob([arr], {type: m[1]}) const blob = new Blob([arr], { type: m[1] })
blob.name = `${Conf['pastedname']}.${m[2]}` blob.name = `${Conf['pastedname']}.${m[2]}`
QR.handleFiles([blob]) QR.handleFiles([blob])
} else if (/^https?:\/\//.test(src)) { } else if (/^https?:\/\//.test(src)) {
@ -559,11 +560,11 @@ var QR = {
handleUrl(urlDefault) { handleUrl(urlDefault) {
QR.open() QR.open()
QR.selected.preventAutoPost() QR.selected.preventAutoPost()
return CrossOrigin.permission(function() { return CrossOrigin.permission(function () {
const url = prompt('Enter a URL:', urlDefault) const url = prompt('Enter a URL:', urlDefault)
if (url === null) { return } if (url === null) { return }
QR.nodes.fileButton.focus() QR.nodes.fileButton.focus()
return CrossOrigin.file(url, function(blob) { return CrossOrigin.file(url, function (blob) {
if (blob && !/^text\//.test(blob.type)) { if (blob && !/^text\//.test(blob.type)) {
return QR.handleFiles([blob]) return QR.handleFiles([blob])
} else { } else {
@ -575,7 +576,7 @@ var QR = {
handleFiles(files) { handleFiles(files) {
if (this !== QR) { // file input if (this !== QR) { // file input
files = [...Array.from(this.files)] files = [...Array.from(this.files)]
this.value = null this.value = null
} }
if (!files.length) { return } if (!files.length) { return }
@ -611,7 +612,7 @@ var QR = {
generatePostableThreadsList() { generatePostableThreadsList() {
if (!QR.nodes) { return } if (!QR.nodes) { return }
const list = QR.nodes.thread const list = QR.nodes.thread
const options = [list.firstElementChild] const options = [list.firstElementChild]
for (const thread of g.BOARD.threads.keys) { for (const thread of g.BOARD.threads.keys) {
options.push($.el('option', { options.push($.el('option', {
@ -629,7 +630,7 @@ var QR = {
// Fix the value if the option disappeared. // Fix the value if the option disappeared.
list.value = g.VIEW === 'thread' ? list.value = g.VIEW === 'thread' ?
g.THREADID g.THREADID
: :
'new' 'new'
return (g.VIEW === 'thread' ? $.addClass : $.rmClass)(QR.nodes.el, 'reply-to-thread') return (g.VIEW === 'thread' ? $.addClass : $.rmClass)(QR.nodes.el, 'reply-to-thread')
}, },
@ -644,50 +645,50 @@ var QR = {
const setNode = (name, query) => nodes[name] = $(query, dialog) const setNode = (name, query) => nodes[name] = $(query, dialog)
setNode('move', '.move') setNode('move', '.move')
setNode('autohide', '#autohide') setNode('autohide', '#autohide')
setNode('close', '.close') setNode('close', '.close')
setNode('thread', 'select') setNode('thread', 'select')
setNode('form', 'form') setNode('form', 'form')
setNode('sjisToggle', '#sjis-toggle') setNode('sjisToggle', '#sjis-toggle')
setNode('texButton', '#tex-preview-button') setNode('texButton', '#tex-preview-button')
setNode('name', '[data-name=name]') setNode('name', '[data-name=name]')
setNode('email', '[data-name=email]') setNode('email', '[data-name=email]')
setNode('sub', '[data-name=sub]') setNode('sub', '[data-name=sub]')
setNode('com', '[data-name=com]') setNode('com', '[data-name=com]')
setNode('charCount', '#char-count') setNode('charCount', '#char-count')
setNode('texPreview', '#tex-preview') setNode('texPreview', '#tex-preview')
setNode('dumpList', '#dump-list') setNode('dumpList', '#dump-list')
setNode('addPost', '#add-post') setNode('addPost', '#add-post')
setNode('oekaki', '.oekaki') setNode('oekaki', '.oekaki')
setNode('drawButton', '#qr-draw-button') setNode('drawButton', '#qr-draw-button')
setNode('fileSubmit', '#file-n-submit') setNode('fileSubmit', '#file-n-submit')
setNode('fileButton', '#qr-file-button') setNode('fileButton', '#qr-file-button')
setNode('noFile', '#qr-no-file') setNode('noFile', '#qr-no-file')
setNode('filename', '#qr-filename') setNode('filename', '#qr-filename')
setNode('spoiler', '#qr-file-spoiler') setNode('spoiler', '#qr-file-spoiler')
setNode('oekakiButton', '#qr-oekaki-button') setNode('oekakiButton', '#qr-oekaki-button')
setNode('fileRM', '#qr-filerm') setNode('fileRM', '#qr-filerm')
setNode('urlButton', '#url-button') setNode('urlButton', '#url-button')
setNode('pasteArea', '#paste-area') setNode('pasteArea', '#paste-area')
setNode('customCooldown', '#custom-cooldown-button') setNode('customCooldown', '#custom-cooldown-button')
setNode('dumpButton', '#dump-button') setNode('dumpButton', '#dump-button')
setNode('status', '[type=submit]') setNode('status', '[type=submit]')
setNode('flashTag', '[name=filetag]') setNode('flashTag', '[name=filetag]')
setNode('fileInput', '[type=file]') setNode('fileInput', '[type=file]')
const {config} = g.BOARD const { config } = g.BOARD
const {classList} = QR.nodes.el const { classList } = QR.nodes.el
classList.toggle('forced-anon', QR.forcedAnon) classList.toggle('forced-anon', QR.forcedAnon)
classList.toggle('has-spoiler', QR.spoiler) classList.toggle('has-spoiler', QR.spoiler)
classList.toggle('has-sjis', !!config.sjis_tags) classList.toggle('has-sjis', !!config.sjis_tags)
classList.toggle('has-math', !!config.math_tags) classList.toggle('has-math', !!config.math_tags)
classList.toggle('sjis-preview', !!config.sjis_tags && Conf['sjisPreview']) classList.toggle('sjis-preview', !!config.sjis_tags && Conf['sjisPreview'])
classList.toggle('show-new-thread-option', Conf['Show New Thread Option in Threads']) classList.toggle('show-new-thread-option', Conf['Show New Thread Option in Threads'])
if (parseInt(Conf['customCooldown'], 10) > 0) { if (parseInt(Conf['customCooldown'], 10) > 0) {
$.addClass(QR.nodes.fileSubmit, 'custom-cooldown') $.addClass(QR.nodes.fileSubmit, 'custom-cooldown')
$.get('customCooldownEnabled', Conf['customCooldownEnabled'], function({customCooldownEnabled}) { $.get('customCooldownEnabled', Conf['customCooldownEnabled'], function ({ customCooldownEnabled }) {
QR.setCustomCooldown(customCooldownEnabled) QR.setCustomCooldown(customCooldownEnabled)
return $.sync('customCooldownEnabled', QR.setCustomCooldown) return $.sync('customCooldownEnabled', QR.setCustomCooldown)
}) })
@ -695,29 +696,29 @@ var QR = {
QR.flagsInput() QR.flagsInput()
$.on(nodes.autohide, 'change', QR.toggleHide) $.on(nodes.autohide, 'change', QR.toggleHide)
$.on(nodes.close, 'click', QR.close) $.on(nodes.close, 'click', QR.close)
$.on(nodes.status, 'click', QR.submit) $.on(nodes.status, 'click', QR.submit)
$.on(nodes.form, 'submit', QR.submit) $.on(nodes.form, 'submit', QR.submit)
$.on(nodes.sjisToggle, 'click', QR.toggleSJIS) $.on(nodes.sjisToggle, 'click', QR.toggleSJIS)
$.on(nodes.texButton, 'mousedown', QR.texPreviewShow) $.on(nodes.texButton, 'mousedown', QR.texPreviewShow)
$.on(nodes.texButton, 'mouseup', QR.texPreviewHide) $.on(nodes.texButton, 'mouseup', QR.texPreviewHide)
$.on(nodes.addPost, 'click', () => new QR.post(true)) $.on(nodes.addPost, 'click', () => new QR.post(true))
$.on(nodes.drawButton, 'click', QR.oekaki.draw) $.on(nodes.drawButton, 'click', QR.oekaki.draw)
$.on(nodes.fileButton, 'click', QR.openFileInput) $.on(nodes.fileButton, 'click', QR.openFileInput)
$.on(nodes.noFile, 'click', QR.openFileInput) $.on(nodes.noFile, 'click', QR.openFileInput)
$.on(nodes.filename, 'focus', function() { return $.addClass(this.parentNode, 'focus') }) $.on(nodes.filename, 'focus', function () { return $.addClass(this.parentNode, 'focus') })
$.on(nodes.filename, 'blur', function() { return $.rmClass(this.parentNode, 'focus') }) $.on(nodes.filename, 'blur', function () { return $.rmClass(this.parentNode, 'focus') })
$.on(nodes.spoiler, 'change', () => QR.selected.nodes.spoiler.click()) $.on(nodes.spoiler, 'change', () => QR.selected.nodes.spoiler.click())
$.on(nodes.oekakiButton, 'click', QR.oekaki.button) $.on(nodes.oekakiButton, 'click', QR.oekaki.button)
$.on(nodes.fileRM, 'click', () => QR.selected.rmFile()) $.on(nodes.fileRM, 'click', () => QR.selected.rmFile())
$.on(nodes.urlButton, 'click', () => QR.handleUrl('')) $.on(nodes.urlButton, 'click', () => QR.handleUrl(''))
$.on(nodes.customCooldown, 'click', QR.toggleCustomCooldown) $.on(nodes.customCooldown, 'click', QR.toggleCustomCooldown)
$.on(nodes.dumpButton, 'click', () => nodes.el.classList.toggle('dump')) $.on(nodes.dumpButton, 'click', () => nodes.el.classList.toggle('dump'))
$.on(nodes.fileInput, 'change', QR.handleFiles) $.on(nodes.fileInput, 'change', QR.handleFiles)
window.addEventListener('focus', QR.focus, true) window.addEventListener('focus', QR.focus, true)
window.addEventListener('blur', QR.focus, true) window.addEventListener('blur', QR.focus, true)
// We don't receive blur events from captcha iframe. // We don't receive blur events from captcha iframe.
$.on(d, 'click', QR.focus) $.on(d, 'click', QR.focus)
@ -726,12 +727,12 @@ var QR = {
if (($.engine === 'gecko') && !window.DataTransferItemList) { if (($.engine === 'gecko') && !window.DataTransferItemList) {
nodes.pasteArea.hidden = false nodes.pasteArea.hidden = false
} }
new MutationObserver(QR.pasteFF).observe(nodes.pasteArea, {childList: true}) new MutationObserver(QR.pasteFF).observe(nodes.pasteArea, { childList: true })
// save selected post's data // save selected post's data
const items = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag'] const items = ['thread', 'name', 'email', 'sub', 'com', 'filename', 'flag']
let i = 0 let i = 0
const save = function() { return QR.selected.save(this) } const save = function () { return QR.selected.save(this) }
while ((name = items[i++])) { while ((name = items[i++])) {
var node var node
if (!(node = nodes[name])) { continue } if (!(node = nodes[name])) { continue }
@ -742,7 +743,7 @@ var QR = {
// XXX Blink and WebKit treat width and height of <textarea>s as min-width and min-height // XXX Blink and WebKit treat width and height of <textarea>s as min-width and min-height
if (($.engine === 'gecko') && Conf['Remember QR Size']) { if (($.engine === 'gecko') && Conf['Remember QR Size']) {
$.get('QR Size', '', item => nodes.com.style.cssText = item['QR Size']) $.get('QR Size', '', item => nodes.com.style.cssText = item['QR Size'])
$.on(nodes.com, 'mouseup', function(e) { $.on(nodes.com, 'mouseup', function (e) {
if (e.button !== 0) { return } if (e.button !== 0) { return }
return $.set('QR Size', this.style.cssText) return $.set('QR Size', this.style.cssText)
}) })
@ -766,12 +767,12 @@ var QR = {
flags() { flags() {
const select = $.el('select', { const select = $.el('select', {
name: 'flag', name: 'flag',
className: 'flagSelector' className: 'flagSelector'
} }
) )
const addFlag = (value, textContent) => $.add(select, $.el('option', {value, textContent})) const addFlag = (value, textContent) => $.add(select, $.el('option', { value, textContent }))
addFlag('0', (g.BOARD.config.country_flags ? 'Geographic Location' : 'None')) addFlag('0', (g.BOARD.config.country_flags ? 'Geographic Location' : 'None'))
for (const value in g.BOARD.config.board_flags) { for (const value in g.BOARD.config.board_flags) {
@ -783,7 +784,7 @@ var QR = {
}, },
flagsInput() { flagsInput() {
const {nodes} = QR const { nodes } = QR
if (!nodes) { return } if (!nodes) { return }
if (nodes.flag) { if (nodes.flag) {
$.rm(nodes.flag) $.rm(nodes.flag)
@ -792,7 +793,7 @@ var QR = {
if (g.BOARD.config.board_flags) { if (g.BOARD.config.board_flags) {
const flag = QR.flags() const flag = QR.flags()
flag.dataset.name = 'flag' flag.dataset.name = 'flag'
flag.dataset.default = '0' flag.dataset.default = '0'
nodes.flag = flag nodes.flag = flag
return $.add(nodes.form, flag) return $.add(nodes.form, flag)
@ -876,17 +877,17 @@ var QR = {
const formData = { const formData = {
MAX_FILE_SIZE: QR.max_size, MAX_FILE_SIZE: QR.max_size,
mode: 'regist', mode: 'regist',
pwd: QR.persona.getPassword(), pwd: QR.persona.getPassword(),
resto: threadID, resto: threadID,
name: (!QR.forcedAnon ? post.name : undefined), name: (!QR.forcedAnon ? post.name : undefined),
email: post.email, email: post.email,
sub: (!QR.forcedAnon && !threadID ? post.sub : undefined), sub: (!QR.forcedAnon && !threadID ? post.sub : undefined),
com: post.com, com: post.com,
upfile: post.file, upfile: post.file,
filetag, filetag,
spoiler: post.spoiler, spoiler: post.spoiler,
flag: post.flag, flag: post.flag,
} }
const options = { const options = {
@ -896,7 +897,7 @@ var QR = {
form: $.formData(formData) form: $.formData(formData)
} }
if (Conf['Show Upload Progress']) { if (Conf['Show Upload Progress']) {
options.onprogress = function(e) { options.onprogress = function (e) {
if (this !== QR.req?.upload) { return } // aborted if (this !== QR.req?.upload) { return } // aborted
if (e.loaded < e.total) { if (e.loaded < e.total) {
// Uploading... // Uploading...
@ -910,7 +911,7 @@ var QR = {
} }
} }
let cb = function(response) { let cb = function (response) {
if (response != null) { if (response != null) {
QR.currentCaptcha = response QR.currentCaptcha = response
if (QR.captcha === Captcha.v2) { if (QR.captcha === Captcha.v2) {
@ -942,7 +943,7 @@ var QR = {
return cb = null return cb = null
} }
} }
captcha(function(response) { captcha(function (response) {
if ((QR.captcha === Captcha.v2) && Captcha.cache.haveCookie()) { if ((QR.captcha === Captcha.v2) && Captcha.cache.haveCookie()) {
cb?.() cb?.()
if (response) { return Captcha.cache.save(response) } if (response) { return Captcha.cache.save(response) }
@ -1032,9 +1033,9 @@ var QR = {
const h1 = $('h1', this.response) const h1 = $('h1', this.response)
let [_, threadID, postID] = Array.from(h1.nextSibling.textContent.match(/thread:(\d+),no:(\d+)/)) let [_, threadID, postID] = Array.from(h1.nextSibling.textContent.match(/thread:(\d+),no:(\d+)/))
postID = +postID postID = +postID
threadID = +threadID || postID threadID = +threadID || postID
const isReply = threadID !== postID const isReply = threadID !== postID
// Post/upload confirmed as successful. // Post/upload confirmed as successful.
$.event('QRPostSuccessful', { $.event('QRPostSuccessful', {
@ -1043,13 +1044,13 @@ var QR = {
postID postID
}) })
// XXX deprecated // XXX deprecated
$.event('QRPostSuccessful_', {boardID: g.BOARD.ID, threadID, postID}) $.event('QRPostSuccessful_', { boardID: g.BOARD.ID, threadID, postID })
// Enable auto-posting if we have stuff left to post, disable it otherwise. // Enable auto-posting if we have stuff left to post, disable it otherwise.
const postsCount = QR.posts.length - 1 const postsCount = QR.posts.length - 1
QR.cooldown.auto = postsCount && isReply QR.cooldown.auto = postsCount && isReply
const lastPostToThread = !((function() { for (const p of QR.posts.slice(1)) { if (p.thread === post.thread) { return true } } })()) const lastPostToThread = !((function () { for (const p of QR.posts.slice(1)) { if (p.thread === post.thread) { return true } } })())
if (postsCount) { if (postsCount) {
post.rm() post.rm()
@ -1081,7 +1082,7 @@ var QR = {
if (URL) { if (URL) {
const open = Conf['Open Post in New Tab'] || postsCount ? const open = Conf['Open Post in New Tab'] || postsCount ?
() => $.open(URL) () => $.open(URL)
: :
() => location.href = URL () => location.href = URL
if (threadID === postID) { if (threadID === postID) {
@ -1097,7 +1098,7 @@ var QR = {
waitForThread(url, cb) { waitForThread(url, cb) {
let attempts = 0 let attempts = 0
const check = function() { const check = function () {
return $.ajax(url, { return $.ajax(url, {
onloadend() { onloadend() {
attempts++ attempts++

View File

@ -16,19 +16,19 @@ import { dict } from "../platform/helpers"
<3 aeosynth <3 aeosynth
*/ */
var QuoteThreading = { const QuoteThreading = {
init() { init() {
if (!Conf['Quote Threading'] || (g.VIEW !== 'thread')) { return } if (!Conf['Quote Threading'] || (g.VIEW !== 'thread')) { return }
this.controls = $.el('label', this.controls = $.el('label',
{innerHTML: "<input id=\"threadingControl\" name=\"Thread Quotes\" type=\"checkbox\"> Threading"}) { innerHTML: "<input id=\"threadingControl\" name=\"Thread Quotes\" type=\"checkbox\"> Threading" })
this.threadNewLink = $.el('span', { this.threadNewLink = $.el('span', {
className: 'brackets-wrap threadnewlink', className: 'brackets-wrap threadnewlink',
hidden: true hidden: true
} }
) )
$.extend(this.threadNewLink, {innerHTML: "<a href=\"javascript:;\">Thread New Posts</a>"}) $.extend(this.threadNewLink, { innerHTML: "<a href=\"javascript:;\">Thread New Posts</a>" })
this.input = $('input', this.controls) this.input = $('input', this.controls)
this.input.checked = Conf['Thread Quotes'] this.input.checked = Conf['Thread Quotes']
@ -39,23 +39,23 @@ var QuoteThreading = {
$.on(d, '4chanXInitFinished', () => { return this.ready = true }) $.on(d, '4chanXInitFinished', () => { return this.ready = true })
Header.menu.addEntry(this.entry = { Header.menu.addEntry(this.entry = {
el: this.controls, el: this.controls,
order: 99 order: 99
} }
) )
Callbacks.Thread.push({ Callbacks.Thread.push({
name: 'Quote Threading', name: 'Quote Threading',
cb: this.setThread cb: this.setThread
}) })
return Callbacks.Post.push({ return Callbacks.Post.push({
name: 'Quote Threading', name: 'Quote Threading',
cb: this.node cb: this.node
}) })
}, },
parent: dict(), parent: dict(),
children: dict(), children: dict(),
inserted: dict(), inserted: dict(),
@ -83,7 +83,7 @@ var QuoteThreading = {
setThread() { setThread() {
QuoteThreading.thread = this QuoteThreading.thread = this
return $.asap((() => !Conf['Thread Updater'] || $('.navLinksBot > .updatelink')), function() { return $.asap((() => !Conf['Thread Updater'] || $('.navLinksBot > .updatelink')), function () {
let navLinksBot let navLinksBot
if (navLinksBot = $('.navLinksBot')) { return $.add(navLinksBot, [$.tn(' '), QuoteThreading.threadNewLink]) } if (navLinksBot = $('.navLinksBot')) { return $.add(navLinksBot, [$.tn(' '), QuoteThreading.threadNewLink]) }
}) })
@ -137,15 +137,15 @@ var QuoteThreading = {
const descendants = QuoteThreading.descendants(post) const descendants = QuoteThreading.descendants(post)
if (!Unread.posts.has(parent.ID)) { if (!Unread.posts.has(parent.ID)) {
if ((function() { for (const x of descendants) { if (Unread.posts.has(x.ID)) { return true } } })()) { if ((function () { for (const x of descendants) { if (Unread.posts.has(x.ID)) { return true } } })()) {
QuoteThreading.threadNewLink.hidden = false QuoteThreading.threadNewLink.hidden = false
return false return false
} }
} }
const {order} = Unread const { order } = Unread
const children = (QuoteThreading.children[parent.fullID] || (QuoteThreading.children[parent.fullID] = [])) const children = (QuoteThreading.children[parent.fullID] || (QuoteThreading.children[parent.fullID] = []))
const threadContainer = parent.nodes.threadContainer || $.el('div', {className: 'threadContainer'}) const threadContainer = parent.nodes.threadContainer || $.el('div', { className: 'threadContainer' })
const nodes = [post.nodes.root] const nodes = [post.nodes.root]
if (post.nodes.threadContainer) { nodes.push(post.nodes.threadContainer) } if (post.nodes.threadContainer) { nodes.push(post.nodes.threadContainer) }
@ -160,7 +160,7 @@ var QuoteThreading = {
let prev2 let prev2
let prev = parent let prev = parent
while ((prev2 = QuoteThreading.children[prev.fullID]) && prev2.length) { while ((prev2 = QuoteThreading.children[prev.fullID]) && prev2.length) {
prev = prev2[prev2.length-1] prev = prev2[prev2.length - 1]
} }
for (let k = descendants.length - 1; k >= 0; k--) { x = descendants[k]; order.after(order[prev.ID], order[x.ID]) } for (let k = descendants.length - 1; k >= 0; k--) { x = descendants[k]; order.after(order[prev.ID], order[x.ID]) }
children.push(post) children.push(post)
@ -180,8 +180,8 @@ var QuoteThreading = {
rethread() { rethread() {
if (!QuoteThreading.ready) { return } if (!QuoteThreading.ready) { return }
const {thread} = QuoteThreading const { thread } = QuoteThreading
const {posts} = thread const { posts } = thread
QuoteThreading.threadNewLink.hidden = true QuoteThreading.threadNewLink.hidden = true
@ -191,7 +191,7 @@ var QuoteThreading = {
const nodes = [] const nodes = []
Unread.order = new RandomAccessList() Unread.order = new RandomAccessList()
QuoteThreading.inserted = dict() QuoteThreading.inserted = dict()
posts.forEach(function(post) { posts.forEach(function (post) {
if (post.isFetchedQuote) { return } if (post.isFetchedQuote) { return }
Unread.order.push(post) Unread.order.push(post)
if (post.isReply) { nodes.push(post.nodes.root) } if (post.isReply) { nodes.push(post.nodes.root) }

View File

@ -15,18 +15,18 @@ import PostRedirect from "../Posting/PostRedirect"
* DS102: Remove unnecessary code created because of implicit returns * DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/ */
var QuoteYou = { const QuoteYou = {
init() { init() {
if (!Conf['Remember Your Posts']) { return } if (!Conf['Remember Your Posts']) { return }
this.db = new DataBoard('yourPosts') this.db = new DataBoard('yourPosts')
$.sync('Remember Your Posts', enabled => Conf['Remember Your Posts'] = enabled) $.sync('Remember Your Posts', enabled => Conf['Remember Your Posts'] = enabled)
$.on(d, 'QRPostSuccessful', function(e) { $.on(d, 'QRPostSuccessful', function (e) {
const cb = PostRedirect.delay() const cb = PostRedirect.delay()
return $.get('Remember Your Posts', Conf['Remember Your Posts'], function(items) { return $.get('Remember Your Posts', Conf['Remember Your Posts'], function (items) {
if (!items['Remember Your Posts']) { return } if (!items['Remember Your Posts']) { return }
const {boardID, threadID, postID} = e.detail const { boardID, threadID, postID } = e.detail
return QuoteYou.db.set({boardID, threadID, postID, val: true}, cb) return QuoteYou.db.set({ boardID, threadID, postID, val: true }, cb)
}) })
}) })
@ -47,12 +47,12 @@ var QuoteYou = {
// \u00A0 is nbsp // \u00A0 is nbsp
this.mark = $.el('span', { this.mark = $.el('span', {
textContent: '\u00A0(You)', textContent: '\u00A0(You)',
className: 'qmark-you' className: 'qmark-you'
} }
) )
Callbacks.Post.push({ Callbacks.Post.push({
name: 'Mark Quotes of You', name: 'Mark Quotes of You',
cb: this.node cb: this.node
}) })
return QuoteYou.menu.init() return QuoteYou.menu.init()
@ -60,9 +60,9 @@ var QuoteYou = {
isYou(post) { isYou(post) {
return !!QuoteYou.db?.get({ return !!QuoteYou.db?.get({
boardID: post.boardID, boardID: post.boardID,
threadID: post.threadID, threadID: post.threadID,
postID: post.ID postID: post.ID
}) })
}, },
@ -88,9 +88,9 @@ var QuoteYou = {
menu: { menu: {
init() { init() {
const label = $.el('label', const label = $.el('label',
{className: 'toggle-you'} { className: 'toggle-you' }
, ,
{innerHTML: '<input type="checkbox"> You'}) { innerHTML: '<input type="checkbox"> You' })
const input = $('input', label) const input = $('input', label)
$.on(input, 'change', QuoteYou.menu.toggle) $.on(input, 'change', QuoteYou.menu.toggle)
return Menu.menu?.addEntry({ return Menu.menu?.addEntry({
@ -105,8 +105,8 @@ var QuoteYou = {
}, },
toggle() { toggle() {
const {post} = QuoteYou.menu const { post } = QuoteYou.menu
const data = {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID, val: true} const data = { boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID, val: true }
if (this.checked) { if (this.checked) {
QuoteYou.db.set(data) QuoteYou.db.set(data)
} else { } else {
@ -134,7 +134,7 @@ var QuoteYou = {
seek(type) { seek(type) {
let highlighted, post let highlighted, post
let result let result
const {highlight} = g.SITE.classes const { highlight } = g.SITE.classes
if (highlighted = $(`.${highlight}`)) { $.rmClass(highlighted, highlight) } if (highlighted = $(`.${highlight}`)) { $.rmClass(highlighted, highlight) }
if (!QuoteYou.lastRead || !doc.contains(QuoteYou.lastRead) || !$.hasClass(QuoteYou.lastRead, 'quotesYou')) { if (!QuoteYou.lastRead || !doc.contains(QuoteYou.lastRead) || !$.hasClass(QuoteYou.lastRead, 'quotesYou')) {

View File

@ -1,7 +1,7 @@
import Redirect from "../Archive/Redirect" import Redirect from "../Archive/Redirect"
import Callbacks from "../classes/Callbacks" import Callbacks from "../classes/Callbacks"
import Post from "../classes/Post" import Post from "../classes/Post"
import { Conf, doc,g } from "../globals/globals" import { Conf, doc, g } from "../globals/globals"
import ExpandComment from "../Miscellaneous/ExpandComment" import ExpandComment from "../Miscellaneous/ExpandComment"
import $ from "../platform/$" import $ from "../platform/$"
import $$ from "../platform/$$" import $$ from "../platform/$$"
@ -12,7 +12,7 @@ import $$ from "../platform/$$"
* DS102: Remove unnecessary code created because of implicit returns * DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
*/ */
var Quotify = { const Quotify = {
init() { init() {
if (!['index', 'thread'].includes(g.VIEW) || !Conf['Resurrect Quotes']) { return } if (!['index', 'thread'].includes(g.VIEW) || !Conf['Resurrect Quotes']) { return }
@ -24,7 +24,7 @@ var Quotify = {
return Callbacks.Post.push({ return Callbacks.Post.push({
name: 'Resurrect Quotes', name: 'Resurrect Quotes',
cb: this.node cb: this.node
}) })
}, },
@ -45,12 +45,12 @@ var Quotify = {
let m let m
if (!(m = link.pathname.match(/^\/([^/]+)\/thread\/S?(\d+)\/?$/))) { return } if (!(m = link.pathname.match(/^\/([^/]+)\/thread\/S?(\d+)\/?$/))) { return }
if (['boards.4chan.org', 'boards.4channel.org'].includes(link.hostname)) { return } if (['boards.4chan.org', 'boards.4channel.org'].includes(link.hostname)) { return }
const boardID = m[1] const boardID = m[1]
const threadID = m[2] const threadID = m[2]
const postID = link.hash.match(/^#[pq]?(\d+)$|$/)[1] || threadID const postID = link.hash.match(/^#[pq]?(\d+)$|$/)[1] || threadID
if (Redirect.to('post', {boardID, postID})) { if (Redirect.to('post', { boardID, postID })) {
$.addClass(link, 'quotelink') $.addClass(link, 'quotelink')
$.extend(link.dataset, {boardID, threadID, postID}) $.extend(link.dataset, { boardID, threadID, postID })
return this.nodes.archivelinks.push(link) return this.nodes.archivelinks.push(link)
} }
}, },
@ -76,7 +76,7 @@ var Quotify = {
} }
const boardID = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? const boardID = (m = quote.match(/^>>>\/([a-z\d]+)/)) ?
m[1] m[1]
: :
this.board.ID this.board.ID
const quoteID = `${boardID}.${postID}` const quoteID = `${boardID}.${postID}`
@ -85,31 +85,31 @@ var 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(boardID, post.thread.ID, postID), href: g.SITE.Build.postURL(boardID, post.thread.ID, postID),
className: 'quotelink', className: 'quotelink',
textContent: quote textContent: quote
} }
) )
} 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(boardID, post.thread.ID, postID), href: g.SITE.Build.postURL(boardID, post.thread.ID, postID),
className: 'quotelink deadlink', className: 'quotelink deadlink',
textContent: quote textContent: quote
} }
) )
$.add(a, Post.deadMark.cloneNode(true)) $.add(a, Post.deadMark.cloneNode(true))
$.extend(a.dataset, {boardID, threadID: post.thread.ID, postID}) $.extend(a.dataset, { boardID, threadID: post.thread.ID, postID })
} }
} else { } else {
const redirect = Redirect.to('thread', {boardID, threadID: 0, postID}) const redirect = Redirect.to('thread', { boardID, threadID: 0, postID })
const fetchable = Redirect.to('post', {boardID, postID}) const fetchable = Redirect.to('post', { boardID, postID })
if (redirect || fetchable) { if (redirect || fetchable) {
// Replace the .deadlink span if we can redirect or fetch the post. // Replace the .deadlink span if we can redirect or fetch the post.
a = $.el('a', { a = $.el('a', {
href: redirect || 'javascript:;', href: redirect || 'javascript:;',
className: 'deadlink', className: 'deadlink',
textContent: quote textContent: quote
} }
) )
@ -117,7 +117,7 @@ var Quotify = {
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')
$.extend(a.dataset, {boardID, postID}) $.extend(a.dataset, { boardID, postID })
} }
} }
} }
@ -139,7 +139,7 @@ var Quotify = {
let el let el
if (!(el = deadlink.previousSibling) || (el.nodeName === 'BR')) { if (!(el = deadlink.previousSibling) || (el.nodeName === 'BR')) {
const green = $.el('span', const green = $.el('span',
{className: 'quote'}) { className: 'quote' })
$.before(deadlink, green) $.before(deadlink, green)
$.add(green, deadlink) $.add(green, deadlink)
} }

View File

@ -111,12 +111,12 @@ const Main = {
if (platform === 'crx') { w = (w.wrappedJSObject || w) } if (platform === 'crx') { w = (w.wrappedJSObject || w) }
if (`${meta.name} antidup` in w) { return } if (`${meta.name} antidup` in w) { return }
w[`${meta.name} antidup`] = true w[`${meta.name} antidup`] = true
} catch (error) { } } catch (error) { /* empty */ }
// Don't run inside ad iframes. // Don't run inside ad iframes.
try { try {
if (window.frameElement && ['', 'about:blank'].includes(window.frameElement.src)) { return } if (window.frameElement && ['', 'about:blank'].includes(window.frameElement.src)) { return }
} catch (error1) { } } catch (error1) { /* empty */ }
// Detect multiple copies of 4chan X // Detect multiple copies of 4chan X
if (doc && $.hasClass(doc, 'fourchan-x')) { return } if (doc && $.hasClass(doc, 'fourchan-x')) { return }