1
0
Fork 0
mirror of https://github.com/Eggbertx/gochan.git synced 2025-08-27 11:26:22 -07:00

Fix errors pointed out by eslint

This commit is contained in:
Eggbertx 2023-05-23 12:32:46 -07:00
parent 8dd98d2abf
commit 04d0948dc2
23 changed files with 345 additions and 343 deletions

View file

@ -22,9 +22,9 @@ module.exports = {
"@typescript-eslint"
],
"rules": {
"indent": ["error", "tab"],
"indent": ["warn", "tab"],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double", {
"quotes": ["warn", "double", {
"allowTemplateLiterals": true
}],
"semi": ["error", "always"],

View file

@ -20,7 +20,6 @@ export async function getBoardList() {
url: webroot + "boards.json",
cache: false,
dataType: "json",
success: (d2 => {}),
error: function(_err, _status, statusText) {
console.error("Error getting board list: " + statusText);
return nullBoardsList;
@ -33,13 +32,12 @@ export async function getBoardList() {
}
export async function getCatalog(board = "") {
let useBoard = (board != "")?board:currentBoard();
const useBoard = (board != "")?board:currentBoard();
const data = await $.ajax({
url: webroot + useBoard + "/catalog.json",
cache: false,
dataType: "json",
success: (() => { }),
error: function (err, status, statusText) {
console.error(`Error getting catalog for /${board}/: ${statusText}`);
}
@ -52,7 +50,7 @@ export async function getCatalog(board = "") {
}
export async function getThread(board = "", thread = 0) {
let threadInfo = currentThread();
const threadInfo = currentThread();
if(board != "")
threadInfo.board = board;
if(thread > 0)

View file

@ -3,9 +3,9 @@ import $ from "jquery";
import { openQR } from "./dom/qr";
export function handleKeydown(e: JQuery.KeyDownEvent) {
let ta = e.target;
let isPostMsg = ta.nodeName == "TEXTAREA" && ta.name == "postmsg";
let inForm = ta.form != undefined;
const ta = e.target;
const isPostMsg = ta.nodeName == "TEXTAREA" && ta.name == "postmsg";
const inForm = ta.form != undefined;
if(!inForm && !e.ctrlKey && e.keyCode == 81) {
openQR();
} else if(isPostMsg && e.ctrlKey) {
@ -16,35 +16,35 @@ export function handleKeydown(e: JQuery.KeyDownEvent) {
export function applyBBCode(e: JQuery.KeyDownEvent) {
let tag = "";
switch(e.keyCode) {
case 10: // Enter key
case 13: // Enter key in Chrome/IE
// trigger the form submit event, whether the QR post box or the static post box is currently
$(e.target).parents("form#postform,form#qrpostform").trigger("submit");
case 10: // Enter key
case 13: // Enter key in Chrome/IE
// trigger the form submit event, whether the QR post box or the static post box is currently
$(e.target).parents("form#postform,form#qrpostform").trigger("submit");
break;
case 66: // B
tag = "b"; // bold
case 66: // B
tag = "b"; // bold
break;
case 73: // I
tag = "i"; // italics
case 73: // I
tag = "i"; // italics
break;
case 82: // R
tag = "s"; // strikethrough
case 82: // R
tag = "s"; // strikethrough
break;
case 83:
tag = "?"; // spoiler (not yet implemented)
case 83:
tag = "?"; // spoiler (not yet implemented)
break;
case 85: // U
tag = "u"; // underline
case 85: // U
tag = "u"; // underline
break;
}
if(tag == "") return;
e.preventDefault();
let ta = e.target;
let val = ta.value;
let ss = ta.selectionStart;
let se = ta.selectionEnd;
let r = se + 2 + tag.length;
const ta = e.target;
const val = ta.value;
const ss = ta.selectionStart;
const se = ta.selectionEnd;
const r = se + 2 + tag.length;
ta.value = val.slice(0, ss) +
`[${tag}]` +
val.slice(ss, se) +

View file

@ -7,10 +7,10 @@ const YEAR_IN_MS = 365*24*60*60*1000;
*/
export function getCookie(name: string, defaultVal = "") {
let val = defaultVal;
let cookieArr = document.cookie.split("; ");
const cookieArr = document.cookie.split("; ");
for(const cookie of cookieArr) {
let pair = cookie.split("=");
const pair = cookie.split("=");
if(pair[0] != name) continue;
try {
val = decodeURIComponent(pair[1]).replace("+", " ");
@ -47,7 +47,7 @@ export function setCookie(name: string, value: string, expires = "", root = webr
let expiresStr = "";
if(expires == "") {
expiresStr = ";expires=";
let d = new Date();
const d = new Date();
d.setTime(d.getTime() + YEAR_IN_MS);
expiresStr += d.toUTCString();
}

View file

@ -1,7 +1,8 @@
import $ from "jquery";
const emptyFunc = () => {};
const noop = () => {
return;
};
export function removeLightbox(...customs: any) {
$(".lightbox, .lightbox-bg").remove();
for(const custom of customs) {
@ -20,7 +21,7 @@ export function showLightBox(title: string, innerHTML: string) {
function simpleLightbox(properties: any = {}, customCSS: any = {}, $elements: any[] = []) {
if(properties["class"] === undefined)
properties["class"] = "lightbox";
let defaultCSS: {[key: string]: string} = {
const defaultCSS: {[key: string]: string} = {
"display": "inline-block",
"top": "50%",
"left": "50%",
@ -35,7 +36,7 @@ function simpleLightbox(properties: any = {}, customCSS: any = {}, $elements: an
customCSS[key] = defaultCSS[key];
}
let $box = $("<div/>").prop(properties).css(customCSS).prependTo(document.body).append($elements);
const $box = $("<div/>").prop(properties).css(customCSS).prependTo(document.body).append($elements);
$("<div />").prop({
class: "lightbox-bg"
}).on("click", function() {
@ -45,21 +46,21 @@ function simpleLightbox(properties: any = {}, customCSS: any = {}, $elements: an
return $box;
}
export function promptLightbox(defVal = "", isMasked = false, onOk: ($el:JQuery<HTMLElement>, val: any) => any = emptyFunc, title = "") {
let $ok = $("<button/>").prop({
export function promptLightbox(defVal = "", isMasked = false, onOk: ($el:JQuery<HTMLElement>, val: any) => any = noop, title = "") {
const $ok = $("<button/>").prop({
"id": "okbutton"
}).text("OK");
let $cancel = $("<button/>").prop({
const $cancel = $("<button/>").prop({
"id": "cancelbutton"
}).text("Cancel");
let val = (typeof defVal == "string")?defVal:"";
let $promptInput = $("<input/>").prop({
const val = (typeof defVal == "string")?defVal:"";
const $promptInput = $("<input/>").prop({
id: "promptinput",
type: isMasked?"password":"text"
}).val(val);
let $form = $("<form/>").prop({
const $form = $("<form/>").prop({
"action": "javascript:;",
"autocomplete": "off"
}).append(
@ -69,7 +70,7 @@ export function promptLightbox(defVal = "", isMasked = false, onOk: ($el:JQuery<
$ok,
$cancel
);
let $lb = simpleLightbox({}, {}, [$form]);
const $lb = simpleLightbox({}, {}, [$form]);
$promptInput.trigger("focus");
$ok.on("click", function() {
if(onOk($lb, $promptInput.val()) == false)
@ -82,11 +83,11 @@ export function promptLightbox(defVal = "", isMasked = false, onOk: ($el:JQuery<
return $lb;
}
export function alertLightbox(msg = "", title = location.hostname, onOk: ($el: JQuery<HTMLElement>) => any = emptyFunc) {
let $ok = $("<button/>").prop({
export function alertLightbox(msg = "", title = location.hostname, onOk: ($el: JQuery<HTMLElement>) => any = noop) {
const $ok = $("<button/>").prop({
"id": "okbutton"
}).text("OK");
let $lb = simpleLightbox({}, {}, [
const $lb = simpleLightbox({}, {}, [
$("<b/>").prop({id:"alertTitle"}).text(title),
"<hr/>",
$("<span/>").prop({id:"alertText"}).text(msg),

View file

@ -11,7 +11,7 @@ import { updateThreadLock } from "../api/management";
const idRe = /^((reply)|(op))(\d+)/;
function editPost(id: number, _board: string) {
let cookiePass = getCookie("password");
const cookiePass = getCookie("password");
promptLightbox(cookiePass, true, (_jq, inputData) => {
$("input[type=checkbox]").prop("checked", false);
$(`input#check${id}`).prop("checked", true);
@ -21,7 +21,7 @@ function editPost(id: number, _board: string) {
}
function moveThread(id: number, _board: string) {
let cookiePass = getCookie("password");
const cookiePass = getCookie("password");
promptLightbox(cookiePass, true, (_jq, inputData) => {
$("input[type=checkbox]").prop("checked", false);
$(`input#check${id}`).prop("checked", true);
@ -33,7 +33,7 @@ function moveThread(id: number, _board: string) {
function reportPost(id: number, board: string) {
promptLightbox("", false, ($lb, reason) => {
if(reason == "" || reason === null) return;
let xhrFields: {[k: string]: string} = {
const xhrFields: {[k: string]: string} = {
board: board,
report_btn: "Report",
reason: reason,
@ -56,7 +56,7 @@ function reportPost(id: number, board: string) {
}
function deletePostFile(id: number) {
let $elem = $(`div#op${id}.op-post, div#reply${id}.reply`);
const $elem = $(`div#op${id}.op-post, div#reply${id}.reply`);
if($elem.length === 0) return;
$elem.find(".file-info,.upload-container").remove();
$("<div/>").prop({
@ -68,7 +68,7 @@ function deletePostFile(id: number) {
}
function deletePostElement(id: number) {
let $elem = $(`div#op${id}.op-post`);
const $elem = $(`div#op${id}.op-post`);
if($elem.length > 0) {
$elem.parent().next().remove(); // also removes the <hr> element after
$elem.parent().remove();
@ -79,9 +79,9 @@ function deletePostElement(id: number) {
}
function deletePost(id: number, board: string, fileOnly = false) {
let cookiePass = getCookie("password");
promptLightbox(cookiePass, true, ($lb, password) => {
let xhrFields: {[k: string]: any} = {
const cookiePass = getCookie("password");
promptLightbox(cookiePass, true, (_lb, password) => {
const xhrFields: {[k: string]: any} = {
board: board,
boardid: $("input[name=boardid]").val(),
delete_btn: "Delete",
@ -117,106 +117,106 @@ function deletePost(id: number, board: string, fileOnly = false) {
}
function handleActions(action: string, postIDStr: string) {
let idArr = idRe.exec(postIDStr);
const idArr = idRe.exec(postIDStr);
if(!idArr) return;
let postID = Number.parseInt(idArr[4]);
let board = currentBoard();
const postID = Number.parseInt(idArr[4]);
const board = currentBoard();
switch(action) {
case "Watch thread":
watchThread(postID, board);
break;
case "Unwatch thread":
unwatchThread(postID, board);
break;
case "Show thread":
setThreadVisibility(postID, true);
break;
case "Hide thread":
setThreadVisibility(postID, false);
break;
case "Move thread":
moveThread(postID, board);
break;
case "Show post":
setPostVisibility(postID, true);
break;
case "Hide post":
setPostVisibility(postID, false);
break;
case "Edit post":
editPost(postID, board);
break;
case "Report post":
reportPost(postID, board);
break;
case "Delete file":
deletePost(postID, board, true);
break;
case "Delete thread":
case "Delete post":
deletePost(postID, board, false);
break;
// manage stuff
case "Lock thread":
console.log(`Locking /${board}/${postID}`);
updateThreadLock(board, postID, true);
break;
case "Unlock thread":
console.log(`Unlocking /${board}/${postID}`);
updateThreadLock(board, postID, false);
break;
case "Posts from this IP":
getPostInfo(postID).then((info: any) => {
window.open(`${webroot}manage/ipsearch?limit=100&ip=${info.ip}`);
}).catch((reason: JQuery.jqXHR) => {
alertLightbox(`Failed getting post IP: ${reason.statusText}`, "Error");
});
break;
case "Ban filename":
case "Ban file checksum": {
let banType = (action == "Ban filename")?"filename":"checksum";
getPostInfo(postID).then((info: any) => {
return banFile(banType, info.originalFilename, info.checksum, `Added from post dropdown for post /${board}/${postID}`);
}).then((result: any) => {
if(result.error !== undefined && result.error != "") {
if(result.message !== undefined)
alertLightbox(`Failed applying ${banType} ban: ${result.message}`, "Error");
else
alertLightbox(`Failed applying ${banType} ban: ${result.error}`, "Error");
case "Watch thread":
watchThread(postID, board);
break;
case "Unwatch thread":
unwatchThread(postID, board);
break;
case "Show thread":
setThreadVisibility(postID, true);
break;
case "Hide thread":
setThreadVisibility(postID, false);
break;
case "Move thread":
moveThread(postID, board);
break;
case "Show post":
setPostVisibility(postID, true);
break;
case "Hide post":
setPostVisibility(postID, false);
break;
case "Edit post":
editPost(postID, board);
break;
case "Report post":
reportPost(postID, board);
break;
case "Delete file":
deletePost(postID, board, true);
break;
case "Delete thread":
case "Delete post":
deletePost(postID, board, false);
break;
// manage stuff
case "Lock thread":
console.log(`Locking /${board}/${postID}`);
updateThreadLock(board, postID, true);
break;
case "Unlock thread":
console.log(`Unlocking /${board}/${postID}`);
updateThreadLock(board, postID, false);
break;
case "Posts from this IP":
getPostInfo(postID).then((info: any) => {
window.open(`${webroot}manage/ipsearch?limit=100&ip=${info.ip}`);
}).catch((reason: JQuery.jqXHR) => {
alertLightbox(`Failed getting post IP: ${reason.statusText}`, "Error");
});
break;
case "Ban filename":
case "Ban file checksum": {
const banType = (action == "Ban filename")?"filename":"checksum";
getPostInfo(postID).then((info: any) => {
return banFile(banType, info.originalFilename, info.checksum, `Added from post dropdown for post /${board}/${postID}`);
}).then((result: any) => {
if(result.error !== undefined && result.error != "") {
if(result.message !== undefined)
alertLightbox(`Failed applying ${banType} ban: ${result.message}`, "Error");
else
alertLightbox(`Failed applying ${banType} ban: ${result.error}`, "Error");
} else {
alertLightbox(`Successfully applied ${banType} ban`, "Success");
}
}).catch((reason: any) => {
let messageDetail = "";
try {
const responseJSON = JSON.parse(reason.responseText);
if((typeof responseJSON.message) == "string" && responseJSON.message != "") {
messageDetail = responseJSON.message;
} else {
alertLightbox(`Successfully applied ${banType} ban`, "Success");
}
}).catch((reason: any) => {
let messageDetail = "";
try {
const responseJSON = JSON.parse(reason.responseText);
if((typeof responseJSON.message) == "string" && responseJSON.message != "") {
messageDetail = responseJSON.message;
} else {
messageDetail = reason.statusText;
}
} catch(e) {
messageDetail = reason.statusText;
}
alertLightbox(`Failed banning file: ${messageDetail}`, "Error");
});
}
} catch(e) {
messageDetail = reason.statusText;
}
alertLightbox(`Failed banning file: ${messageDetail}`, "Error");
});
}
}
}
export function addPostDropdown($post: JQuery<HTMLElement>) {
if($post.find("select.post-actions").length > 0)
return $post;
let $postInfo = $post.find("label.post-info");
let isOP = $post.prop("class").split(" ").indexOf("op-post") > -1;
let hasUpload = $postInfo.siblings("div.file-info").length > 0;
let postID = $postInfo.parent().attr("id");
let threadPost = isOP?"thread":"post";
let $ddownMenu = $("<select />", {
const $postInfo = $post.find("label.post-info");
const isOP = $post.prop("class").split(" ").indexOf("op-post") > -1;
const hasUpload = $postInfo.siblings("div.file-info").length > 0;
const postID = $postInfo.parent().attr("id");
const threadPost = isOP?"thread":"post";
const $ddownMenu = $("<select />", {
class: "post-actions",
id: postID
}).append("<option disabled selected>Actions</option>");
let idNum = Number.parseInt(idRe.exec(postID)[4]);
const idNum = Number.parseInt(idRe.exec(postID)[4]);
if(isOP) {
if(isThreadWatched(idNum, currentBoard())) {
$ddownMenu.append("<option>Unwatch thread</option>");
@ -225,17 +225,17 @@ export function addPostDropdown($post: JQuery<HTMLElement>) {
}
$ddownMenu.append("<option>Move thread</option>");
}
let showHide = isPostVisible(idNum)?"Hide":"Show";
const showHide = isPostVisible(idNum)?"Hide":"Show";
$ddownMenu.append(
`<option>${showHide} ${threadPost}</option>`,
"<option>Edit post</option>",
"<option>Report post</option>",
`<option>Delete ${threadPost}</option>`,
).insertAfter($postInfo)
.on("change", _e => {
handleActions($ddownMenu.val() as string, postID);
$ddownMenu.val("Actions");
});
.on("change", _e => {
handleActions($ddownMenu.val() as string, postID);
$ddownMenu.val("Actions");
});
if(hasUpload)
$ddownMenu.append("<option>Delete file</option>");
$post.trigger("postDropdownAdded", {

View file

@ -7,7 +7,7 @@ import { getThumbFilename } from "../postinfo";
* creates an element from the given post data
*/
export function createPostElement(post: ThreadPost, boardDir: string, elementClass = "inlinepostprev") {
let $post = $("<div/>")
const $post = $("<div/>")
.prop({
id: `reply${post.no}`,
class: elementClass
@ -40,9 +40,9 @@ export function createPostElement(post: ThreadPost, boardDir: string, elementCla
href: `javascript:quote(${post.no})`
}).text(post.no), "<br/>",
);
let $postInfo = $post.find("label.post-info");
let postName = (post.name == "" && post.trip == "")?"Anonymous":post.name;
let $postName = $("<span/>").prop({class: "postername"});
const $postInfo = $post.find("label.post-info");
const postName = (post.name == "" && post.trip == "")?"Anonymous":post.name;
const $postName = $("<span/>").prop({class: "postername"});
if(post.email == "") {
$postName.text(postName);
} else {
@ -61,7 +61,7 @@ export function createPostElement(post: ThreadPost, boardDir: string, elementCla
$postInfo.prepend($("<span/>").prop({class:"subject"}).text(post.sub), " ");
if(post.filename != "" && post.filename != "deleted") {
let thumbFile = getThumbFilename(post.tim);
const thumbFile = getThumbFilename(post.tim);
$post.append(
$("<div/>").prop({class: "file-info"})
.append(
@ -102,8 +102,8 @@ export function createPostElement(post: ThreadPost, boardDir: string, elementCla
export function shrinkOriginalFilenames(elem = $(document.body)) {
elem.find<HTMLAnchorElement>("a.file-orig").each((i, el) => {
let ext = extname(el.innerText);
let noExt = el.innerText.slice(0,el.innerText.lastIndexOf("."));
const ext = extname(el.innerText);
const noExt = el.innerText.slice(0,el.innerText.lastIndexOf("."));
if(noExt.length > 16) {
const trimmed = noExt.slice(0, 15).trim() + "…" + ext;
el.setAttribute("trimmed", trimmed);

View file

@ -1,15 +1,16 @@
import $ from "jquery";
import { getStorageVal, setStorageVal } from "../storage";
const emptyFunc = () => {};
const noop = () => {
return;
};
/**
* isPostVisible returns true if the post exists and is visible, otherwise false
* @param id the id of the post
*/
export function isPostVisible(id: number) {
let $post = $(`div#op${id}.op-post,div#reply${id}.reply`);
const $post = $(`div#op${id}.op-post,div#reply${id}.reply`);
if($post.length === 0)
return false;
return $post.find(".post-text").is(":visible");
@ -22,21 +23,21 @@ export function isPostVisible(id: number) {
* @param visibility the visibility to be set
* @param onComplete called after the visibility is set
*/
export function setPostVisibility(id: number|string, visibility: boolean, onComplete = emptyFunc) {
let $post = $(`div#op${id}.op-post, div#reply${id}.reply`);
export function setPostVisibility(id: number|string, visibility: boolean, onComplete = noop) {
const $post = $(`div#op${id}.op-post, div#reply${id}.reply`);
if($post.length === 0)
return false;
let $toSet = $post.find(".file-info,.post-text,.upload,.file-deleted-box,br");
let $backlink = $post.find("a.backlink-click");
let hiddenStorage = getStorageVal("hiddenposts", "").split(",");
const $toSet = $post.find(".file-info,.post-text,.upload,.file-deleted-box,br");
const $backlink = $post.find("a.backlink-click");
const hiddenStorage = getStorageVal("hiddenposts", "").split(",");
if(visibility) {
$toSet.show(0, onComplete);
$post.find<HTMLOptionElement>("select.post-actions option").each((e, elem) => {
elem.text = elem.text.replace("Show", "Hide");
});
$backlink.text(id);
let newHidden = [];
const newHidden = [];
for(const sID of hiddenStorage) {
if(sID != id && newHidden.indexOf(sID) == -1) newHidden.push(sID);
}
@ -61,10 +62,10 @@ export function setPostVisibility(id: number|string, visibility: boolean, onComp
* @param visibility the visibility to be set
*/
export function setThreadVisibility(opID: number|string, visibility: boolean) {
let $thread = $(`div#op${opID}.op-post`).parent(".thread");
const $thread = $(`div#op${opID}.op-post`).parent(".thread");
if($thread.length === 0) return false;
return setPostVisibility(opID, visibility, () => {
let $toSet = $thread.find(".reply-container,b,br");
const $toSet = $thread.find(".reply-container,b,br");
if(visibility) {
$toSet.show();
} else {
@ -79,7 +80,7 @@ $(() => {
let hiddenPosts = getStorageVal("hiddenposts", "").split(",");
if(typeof hiddenPosts === "number") hiddenPosts = [hiddenPosts];
for(let i = 0; i < hiddenPosts.length; i++) {
let id = hiddenPosts[i];
const id = hiddenPosts[i];
setThreadVisibility(id, false);
setPostVisibility(id, false);
}

View file

@ -23,12 +23,12 @@ let threadCooldown = 0;
let replyCooldown = 0;
const qrButtonHTML =
'<input type="file" id="imagefile" name="imagefile" accept="image/jpeg,image/png,image/gif,video/webm,video/mp4"/>' +
'<input type="submit" value="Post" style="float:right;min-width:50px"/>';
`<input type="file" id="imagefile" name="imagefile" accept="image/jpeg,image/png,image/gif,video/webm,video/mp4"/>` +
`<input type="submit" value="Post" style="float:right;min-width:50px"/>`;
const qrTitleBar =
'<div id="qr-title">' +
'<span id="qr-message"></span>' +
`<div id="qr-title">` +
`<span id="qr-message"></span>` +
`<span id="qr-buttons"><a href="javascript:toBottom();">${downArrow}</a>` +
`<a href="javascript:toTop();">${upArrow}</a><a href="javascript:closeQR();">X</a></span></div>`;
@ -46,7 +46,7 @@ function setSubmitButtonText(text: string) {
}
function setSubmitButtonEnabled(enabled = true) {
let $submit = $qr.find("input[type=submit]");
const $submit = $qr.find("input[type=submit]");
if(enabled) {
$submit.removeAttr("disabled");
} else {
@ -56,15 +56,15 @@ function setSubmitButtonEnabled(enabled = true) {
function unsetQrUpload() {
$("#imagefile").val("");
let $uploadContainer = $qr.find("div#upload-container");
const $uploadContainer = $qr.find("div#upload-container");
$uploadContainer.empty();
$uploadContainer.css("display","none");
}
function qrUploadChange() {
let $uploadContainer = $qr.find("div#upload-container");
const $uploadContainer = $qr.find("div#upload-container");
$uploadContainer.empty();
let filename = getUploadFilename();
const filename = getUploadFilename();
$uploadContainer.append($(this).prop({
"title": filename
}).css({
@ -78,7 +78,7 @@ function qrUploadChange() {
function setButtonTimeout(prefix = "", cooldown = 5) {
let currentSeconds = cooldown;
let interval: NodeJS.Timer;
let interval: NodeJS.Timer = null;
const timeoutCB = () => {
if(currentSeconds == 0) {
setSubmitButtonEnabled(true);
@ -103,8 +103,7 @@ export function initQR() {
return closeQR();
}
let onPostingPage = $("form input[name=boardid]").length > 0;
const onPostingPage = $("form input[name=boardid]").length > 0;
// don't open the QR box if we aren't on a board or thread page
if(!onPostingPage)
return;
@ -113,10 +112,10 @@ export function initQR() {
const emailCookie = getCookie("email");
const $oldForm = $("form#postform");
let $qrbuttons = $("<div/>")
const $qrbuttons = $("<div/>")
.prop("id", "qrbuttons")
.append(qrButtonHTML);
let $postform = $("<form/>").prop({
const $postform = $("<form/>").prop({
id: "qrpostform",
name: "qrpostform",
action: webroot + "post",
@ -168,10 +167,10 @@ export function initQR() {
let qrTop = 32;
let pintopbar = getBooleanStorageVal("pintopbar", true);
const pintopbar = getBooleanStorageVal("pintopbar", true);
if(pintopbar)
qrTop = $topbar.outerHeight() + 16;
let qrPos = getJsonStorageVal("qrpos", {top: qrTop, left: 16});
const qrPos = getJsonStorageVal("qrpos", {top: qrTop, left: 16});
if(!(qrPos.top > -1))
qrPos.top = qrTop;
if(!(qrPos.left > -1))
@ -210,10 +209,10 @@ export function initQR() {
return;
}
$postform.on("submit", function(e) {
let $form = $<HTMLFormElement>(this as HTMLFormElement);
const $form = $<HTMLFormElement>(this as HTMLFormElement);
e.preventDefault();
copyCaptchaResponse($form);
let data = new FormData(this as HTMLFormElement);
const data = new FormData(this as HTMLFormElement);
$.ajax({
type: "POST",
@ -228,10 +227,10 @@ export function initQR() {
return;
}
clearQR();
let cooldown = (currentThread().id > 0)?replyCooldown:threadCooldown;
const cooldown = (currentThread().id > 0)?replyCooldown:threadCooldown;
setButtonTimeout("", cooldown);
updateThread().then(clearQR).then(() => {
let persist = getBooleanStorageVal("persistentqr", false);
const persist = getBooleanStorageVal("persistentqr", false);
if(!persist) closeQR();
});
return false;
@ -245,12 +244,12 @@ export function initQR() {
}
function copyCaptchaResponse($copyToForm: JQuery<HTMLElement>) {
let $captchaResp = $("textarea[name=h-captcha-response]");
const $captchaResp = $("textarea[name=h-captcha-response]");
if($captchaResp.length > 0) {
$("<textarea/>").prop({
"name": "h-captcha-response"
}).val($("textarea[name=h-captcha-response]").val()).css("display", "none")
.appendTo($copyToForm);
.appendTo($copyToForm);
}
}
@ -279,7 +278,7 @@ export function closeQR() {
window.closeQR = closeQR;
$(() => {
let board = currentBoard();
const board = currentBoard();
if(board == "") return; // not on a board
getThreadCooldown(board).then(cd => threadCooldown = cd);
getReplyCooldown(board).then(cd => replyCooldown = cd);

View file

@ -1,4 +1,4 @@
import $ from "jquery";
import $, { noop } from "jquery";
import { getBooleanStorageVal } from "../storage";
@ -20,7 +20,7 @@ export class TopBarButton {
* @param title The text shown on the button
* @param action The function executed when the button is clicked
*/
constructor(title: string, action: ()=>any = ()=>{}, beforeAfter:BeforeAfter = {}) {
constructor(title: string, action: ()=>any = noop, beforeAfter: BeforeAfter = {}) {
this.title = title;
this.buttonAction = action;
this.button = $<HTMLLinkElement>("<a/>").prop({
@ -29,8 +29,8 @@ export class TopBarButton {
"id": title.toLowerCase()
}).text(title + "▼");
let $before = $topbar.find(beforeAfter.before);
let $after = $topbar.find(beforeAfter.after);
const $before = $topbar.find(beforeAfter.before);
const $after = $topbar.find(beforeAfter.after);
if($before.length > 0) {
this.button.insertBefore($before);
} else if($after.length > 0) {

View file

@ -1,16 +1,18 @@
let noop = ()=>{};
const noop = ()=>{
return;
};
export function updateUploadImage($elem: JQuery<HTMLElement>, onLoad = noop) {
if($elem.length == 0) return;
$elem[0].onchange = function() {
let img = new Image();
const img = new Image();
img.src = URL.createObjectURL((this as any).files[0]);
img.onload = onLoad;
};
}
export function getUploadFilename(): string {
let elem = document.getElementById("imagefile") as HTMLInputElement;
const elem = document.getElementById("imagefile") as HTMLInputElement;
if(elem === null) return "";
if(elem.files === undefined || elem.files.length < 1) return "";
return elem.files[0].name;

View file

@ -3,7 +3,7 @@
* @param dateStr timestamp string, assumed to be in ISO Date-Time format
*/
export function formatDateString(dateStr: string) {
let date = new Date(dateStr);
const date = new Date(dateStr);
return date.toDateString() + ", " + date.toLocaleTimeString();
}

View file

@ -24,17 +24,17 @@ export function toBottom() {
window.toBottom = toBottom;
$(() => {
let style = getStorageVal("style", defaultStyle);
let themeElem = document.getElementById("theme");
const style = getStorageVal("style", defaultStyle);
const themeElem = document.getElementById("theme");
if(themeElem) themeElem.setAttribute("href", `${webroot}css/${style}`);
let pageThread = getPageThread();
const pageThread = getPageThread();
initStaff()
.then(createStaffMenu)
.catch(() => {
// not logged in
});
.catch(() => {
// not logged in
});
let passwordText = $("input#postpassword").val();
const passwordText = $("input#postpassword").val();
$("input#delete-password").val(passwordText);
setPageBanner();

View file

@ -1,10 +1,10 @@
import $ from 'jquery';
import $ from "jquery";
import { alertLightbox } from "../dom/lightbox";
import { $topbar, TopBarButton } from '../dom/topbar';
import { $topbar, TopBarButton } from "../dom/topbar";
import "./sections";
import "./filebans";
import { isThreadLocked } from '../api/management';
import { isThreadLocked } from "../api/management";
const notAStaff: StaffInfo = {
ID: 0,
@ -39,7 +39,7 @@ function setupManagementEvents() {
const $post = $(el.parentElement);
const isLocked = isThreadLocked($post);
if(!dropdownHasItem(el, "Staff Actions")) {
$el.append('<option disabled="disabled">Staff Actions</option>');
$el.append(`<option disabled="disabled">Staff Actions</option>`);
}
if($post.hasClass("op-post")) {
if(isLocked) {
@ -51,7 +51,7 @@ function setupManagementEvents() {
if(!dropdownHasItem(el, "Posts from this IP")) {
$el.append("<option>Posts from this IP</option>");
}
let filenameOrig = $post.find("div.file-info a.file-orig").text();
const filenameOrig = $post.find("div.file-info a.file-orig").text();
if(filenameOrig != "" && !dropdownHasItem(el, "Ban filename")) {
$el.append(
"<option>Ban filename</option>",
@ -84,16 +84,16 @@ export function banFile(banType: string, filename: string, checksum: string, sta
json: 1
};
switch(banType) {
case "filename":
xhrFields.filename = filename;
xhrFields.dofilenameban = "Create";
break;
case "checksum":
xhrFields.checksum = checksum;
xhrFields.dochecksumban = "Create";
break;
default:
break;
case "filename":
xhrFields.filename = filename;
xhrFields.dofilenameban = "Create";
break;
case "checksum":
xhrFields.checksum = checksum;
xhrFields.dochecksumban = "Create";
break;
default:
break;
}
return $.ajax({
method: "POST",
@ -121,7 +121,7 @@ export async function initStaff() {
staffActions = result;
}
},
error: (e) => {
error: (e: JQuery.jqXHR) => {
console.error("Error getting actions list:", e);
}
}).then(getStaffInfo).then(info => {
@ -174,10 +174,10 @@ export async function isLoggedIn() {
}
export function banSelectedPost() {
let boardDirArr = location.pathname.split("/");
const boardDirArr = location.pathname.split("/");
if(boardDirArr.length < 2) return;
let boardDir = boardDirArr[1];
let checks = $("input[type=checkbox]");
const boardDir = boardDirArr[1];
const checks = $("input[type=checkbox]");
if(checks.length === 0) {
alertLightbox("No posts selected");
return false;
@ -197,10 +197,10 @@ export function banSelectedPost() {
*/
function menuItem(action: StaffAction|string, isCategory = false) {
return isCategory ? $("<div/>").append($("<b/>").text(action as string)) : $("<div/>").append(
$("<a/>").prop({
href: `${webroot}manage/${(action as StaffAction).id}`
}).text((action as StaffAction).title)
);
$("<a/>").prop({
href: `${webroot}manage/${(action as StaffAction).id}`
}).text((action as StaffAction).title)
);
}
function getAction(id: string) {
@ -224,7 +224,7 @@ function filterAction(action: StaffAction, perms: number) {
* @param staff an object representing the staff's username and rank
*/
export function createStaffMenu(staff = staffInfo) {
let rank = staff.Rank;
const rank = staff.Rank;
if(rank === 0) return;
$staffMenu = $("<div/>").prop({
id: "staffmenu",
@ -235,14 +235,14 @@ export function createStaffMenu(staff = staffInfo) {
menuItem(getAction("logout")),
menuItem(getAction("dashboard")));
let janitorActions = staffActions.filter(val => filterAction(val, 1));
const janitorActions = staffActions.filter(val => filterAction(val, 1));
$staffMenu.append(menuItem("Janitorial", true));
for(const action of janitorActions) {
$staffMenu.append(menuItem(action));
}
if(rank >= 2) {
let modActions = staffActions.filter(val => filterAction(val, 2));
const modActions = staffActions.filter(val => filterAction(val, 2));
if(modActions.length > 0)
$staffMenu.append(menuItem("Moderation", true));
for(const action of modActions) {
@ -251,7 +251,7 @@ export function createStaffMenu(staff = staffInfo) {
getReports().then(updateReports);
}
if(rank == 3) {
let adminActions = staffActions.filter(val => filterAction(val, 3));
const adminActions = staffActions.filter(val => filterAction(val, 3));
if(adminActions.length > 0)
$staffMenu.append(menuItem("Administration", true));
for(const action of adminActions) {
@ -273,7 +273,7 @@ function updateReports(reports: any[]) {
// append " (#)" to the Reports link, replacing # with the number of reports
$staffMenu.find("a").each((e, elem) => {
if(elem.text.search(reportsTextRE) != 0) return;
let $span = $("<span/>").text(` (${reports.length})`).appendTo(elem);
const $span = $("<span/>").text(` (${reports.length})`).appendTo(elem);
if(reports.length > 0) {
// make it bold and red if there are reports
$span.css({

View file

@ -9,18 +9,18 @@ import { alertLightbox } from "../dom/lightbox";
let $sectionsTable: JQuery<HTMLTableElement> = null;
let changesButtonAdded = false;
let initialOrders: string[] = [];
const initialOrders: string[] = [];
function applyOrderChanges() {
let $sections = $sectionsTable.find("tr.sectionrow");
const $sections = $sectionsTable.find("tr.sectionrow");
let errorShown = false; // only show one error if something goes wrong
$sections.each((i, el) => {
let $el = $(el);
let updatesection = /^section(\d+)$/.exec(el.id)[1];
let sectionname = $el.find(":nth-child(1)").html();
let sectionabbr = $el.find(":nth-child(2)").html();
let sectionpos = $el.find(":nth-child(3)").html();
let sectionhidden = $el.find(":nth-child(4)").html().toLowerCase() == "yes"?"on":"off";
const $el = $(el);
const updatesection = /^section(\d+)$/.exec(el.id)[1];
const sectionname = $el.find(":nth-child(1)").html();
const sectionabbr = $el.find(":nth-child(2)").html();
const sectionpos = $el.find(":nth-child(3)").html();
const sectionhidden = $el.find(":nth-child(4)").html().toLowerCase() == "yes"?"on":"off";
$.ajax({
method: "POST",
url: webroot + "manage/boardsections",
@ -54,7 +54,7 @@ function applyOrderChanges() {
function cancelOrderChanges() {
$sectionsTable.find("tbody").sortable("cancel");
let $sections = $sectionsTable.find("tr.sectionrow");
const $sections = $sectionsTable.find("tr.sectionrow");
$sections.each((i, el) => {
$(el).find(":nth-child(3)").text(initialOrders[i]);
});
@ -85,7 +85,7 @@ $(() => {
items: "tr.sectionrow",
stop: () => {
$sectionsTable.find("tr.sectionrow").each((i, el) => {
let $order = $(el).find(":nth-child(3)");
const $order = $(el).find(":nth-child(3)");
initialOrders.push($order.text());
$order.text(i + 1);
});

View file

@ -9,7 +9,7 @@ function canNotify() {
}
export function notify(title: string, body: string, img = noteIcon) {
let n = new Notification(title, {
const n = new Notification(title, {
body: body,
image: img,
icon: noteIcon
@ -19,7 +19,7 @@ export function notify(title: string, body: string, img = noteIcon) {
}, noteCloseTime);
}
$(document).on("ready", () => {
$(() => {
if(!canNotify())
return;

View file

@ -5,7 +5,7 @@ const opRE = /\/res\/(\d+)(p(\d)+)?.html$/;
const threadRE = /^\d+/;
export function currentBoard() {
let board = $("form#main-form input[type=hidden][name=board]").val();
const board = $("form#main-form input[type=hidden][name=board]").val();
if(typeof board == "string")
return board;
return "";
@ -15,12 +15,12 @@ export function getPageThread() {
let pathname = window.location.pathname;
if(typeof webroot == "string" && webroot != "/") {
pathname = pathname.slice(webroot.length);
if(pathname === "" || pathname[0] != '/') {
if(pathname === "" || pathname[0] != "/") {
pathname = "/" + pathname;
}
}
let arr = opRE.exec(pathname);
let info = {
const arr = opRE.exec(pathname);
const info = {
board: currentBoard(),
boardID: -1,
op: -1,
@ -36,18 +36,18 @@ export function getPageThread() {
export function currentThread(): WatchedThreadJSON {
// returns the board and thread ID if we are viewing a thread
let thread = {board: currentBoard(), id: 0};
const thread = {board: currentBoard(), id: 0};
let pathname = location.pathname;
if(typeof webroot == "string" && webroot != "/") {
pathname = pathname.slice(webroot.length);
if(pathname === "" || pathname[0] != '/') {
if(pathname === "" || pathname[0] != "/") {
pathname = "/" + pathname;
}
}
let splits = pathname.split("/");
const splits = pathname.split("/");
if(splits.length != 4)
return thread;
let reArr = threadRE.exec(splits[3]);
const reArr = threadRE.exec(splits[3]);
if(reArr.length > 0)
thread.id = Number.parseInt(reArr[0]);
return thread;
@ -61,9 +61,9 @@ export function insideOP(elem: any) {
* Return the appropriate thumbnail filename for the given upload filename (replacing gif/webm with jpg, etc)
*/
export function getThumbFilename(filename: string) {
let nameParts = /([^.]+)\.([^.]+)$/.exec(filename);
const nameParts = /([^.]+)\.([^.]+)$/.exec(filename);
if(nameParts === null) return filename;
let name = nameParts[1] + "t";
const name = nameParts[1] + "t";
let ext = nameParts[2];
if(ext == "gif" || ext == "webm")
ext = "jpg";

View file

@ -24,21 +24,21 @@ let currentThreadJSON: BoardThread = {
export function getUploadPostID(upload: any, container: any) {
// if container, upload is div.upload-container
// otherwise it's img or video
let jqu = container? $(upload) : $(upload).parent();
const jqu = container? $(upload) : $(upload).parent();
return insideOP(jqu) ? jqu.siblings().eq(4).text() : jqu.siblings().eq(3).text();
}
export async function updateThreadJSON() {
let thread = currentThread();
const thread = currentThread();
if(thread.id === 0) return; // not in a thread
const json = await getThreadJSON(thread.id, thread.board);
if (!(json.posts instanceof Array) || json.posts.length === 0)
if(!(json.posts instanceof Array) || json.posts.length === 0)
return;
currentThreadJSON = json;
}
function updateThreadHTML() {
let thread = currentThread();
const thread = currentThread();
if(thread.id === 0) return; // not in a thread
let numAdded = 0;
for(const post of currentThreadJSON.posts) {
@ -47,12 +47,12 @@ function updateThreadHTML() {
selector += `div#op${post.no}`;
else
selector += `div#reply${post.no}`;
let elementExists = $(selector).length > 0;
const elementExists = $(selector).length > 0;
if(elementExists)
continue; // TODO: check for edits
let $post = createPostElement(post, thread.board, "reply");
let $replyContainer = $("<div/>").prop({
const $post = createPostElement(post, thread.board, "reply");
const $replyContainer = $("<div/>").prop({
id: `replycontainer${post.no}`,
class: "reply-container"
}).append($post);
@ -94,20 +94,20 @@ function previewMoveHandler(e: JQuery.Event) {
function expandPost(e: JQuery.MouseEventBase) {
e.preventDefault();
if($hoverPreview !== null) $hoverPreview.remove();
let $next = $(e.target).next();
const $next = $(e.target).next();
if($next.prop("class") == "inlinepostprev" && e.type == "click") {
// inline preview is already opened, close it
$next.remove();
return;
}
let href = e.target.href;
let hrefArr = postrefRE.exec(href);
const href = e.target.href;
const hrefArr = postrefRE.exec(href);
if(hrefArr === null) return; // not actually a link to a post, abort
let postID = hrefArr[4]?hrefArr[4]:hrefArr[2];
const postID = hrefArr[4]?hrefArr[4]:hrefArr[2];
let $post = $(`div#op${postID}, div#reply${postID}`).first();
if($post.length > 0) {
let $preview = createPostPreview(e, $post, e.type == "click");
const $preview = createPostPreview(e, $post, e.type == "click");
if(e.type == "mouseenter") {
$hoverPreview = $preview.insertAfter(e.target);
$(document.body).on("mousemove", previewMoveHandler);
@ -165,41 +165,40 @@ export function prepareThumbnails($parent: JQuery<HTMLElement> = null) {
e.preventDefault();
const thumb = $a.find("img.upload");
const thumbURL = thumb.attr("src");
const uploadURL = thumb.attr("alt");
thumb.removeAttr("width").removeAttr("height");
const $thumb = $a.find("img.upload");
const thumbURL = $thumb.attr("src");
const uploadURL = $thumb.attr("alt");
$thumb.removeAttr("width").removeAttr("height");
var fileInfoElement = $a.prevAll(".file-info:first");
const $fileInfo = $a.prevAll(".file-info:first");
if(videoTestRE.test(thumbURL + uploadURL)) {
// Upload is a video
thumb.hide();
var video = $("<video />")
.prop({
src: uploadURL,
autoplay: true,
controls: true,
class: "upload",
loop: true
}).insertAfter(fileInfoElement);
$thumb.hide();
const $video = $("<video />")
.prop({
src: uploadURL,
autoplay: true,
controls: true,
class: "upload",
loop: true
}).insertAfter($fileInfo);
fileInfoElement.append($("<a />")
.prop("href", "javascript:;")
.on("click", function() {
video.remove();
thumb.show();
this.remove();
thumb.prop({
src: thumbURL,
alt: uploadURL
});
}).css({
"padding-left": "8px"
}).html("[Close]<br />"));
$fileInfo.append($("<a />")
.prop("href", "javascript:;").on("click", function() {
$video.remove();
$thumb.show();
this.remove();
$thumb.prop({
src: thumbURL,
alt: uploadURL
});
}).css({
"padding-left": "8px"
}).html("[Close]<br />"));
} else {
// upload is an image
thumb.attr({
$thumb.attr({
src: uploadURL,
alt: thumbURL
});
@ -217,25 +216,23 @@ export function quote(no: number) {
if(getBooleanStorageVal("useqr", true)) {
openQR();
}
let msgboxID = "postmsg";
const msgboxID = "postmsg";
let msgbox = document.getElementById("qr" + msgboxID) as HTMLInputElement;
if(msgbox === null)
msgbox = document.getElementById(msgboxID) as HTMLInputElement;
let selected = selectedText();
let lines = selected.split("\n");
const selected = selectedText();
const lines = selected.split("\n");
if(selected !== "") {
for(let l = 0; l < lines.length; l++) {
lines[l] = ">" + lines[l];
}
}
let cursor = (msgbox.selectionStart !== undefined)?msgbox.selectionStart:msgbox.value.length;
const cursor = (msgbox.selectionStart !== undefined)?msgbox.selectionStart:msgbox.value.length;
let quoted = lines.join("\n");
if(quoted != "") quoted += "\n";
msgbox.value = msgbox.value.slice(0, cursor) + `>>${no}\n` +
quoted +
msgbox.value.slice(cursor);
quoted + msgbox.value.slice(cursor);
if(msgbox.id == "postmsg")
window.scroll(0,msgbox.offsetTop - 48);

View file

@ -13,19 +13,23 @@ const settings: Map<string, Setting<boolean|number|string,HTMLElement>> = new Ma
type ElementValue = string|number|string[];
const noop = () => {
return;
};
class Setting<T = any, E extends HTMLElement = HTMLElement> {
key: string;
title: string;
defaultVal: T;
onSave: () => any;
element: JQuery<E>
element: JQuery<E>;
/**
* @param key The name of the setting
* @param title text that gets shown in the Settings lightbox
* @param defaultVal the setting's default value
* @param onSave function that gets called when you save the settings
*/
constructor(key: string, title: string, defaultVal:T, onSave = () => {}) {
constructor(key: string, title: string, defaultVal:T, onSave = noop) {
this.key = key;
this.title = title;
this.defaultVal = defaultVal;
@ -55,11 +59,11 @@ class Setting<T = any, E extends HTMLElement = HTMLElement> {
}
class TextSetting extends Setting<string, HTMLTextAreaElement> {
constructor(key: string, title: string, defaultVal = "", onSave = () => {}) {
constructor(key: string, title: string, defaultVal = "", onSave = noop) {
super(key, title, defaultVal, onSave);
this.element = this.createElement("<textarea/>");
this.element.text(defaultVal);
let val = this.getStorageValue();
const val = this.getStorageValue();
if(val != "") {
this.setElementValue(val);
}
@ -69,12 +73,11 @@ class TextSetting extends Setting<string, HTMLTextAreaElement> {
}
}
class DropdownSetting<T> extends Setting<ElementValue, HTMLSelectElement> {
constructor(key: string, title: string, options:any[] = [], defaultVal: ElementValue, onSave = () => {}) {
class DropdownSetting extends Setting<ElementValue, HTMLSelectElement> {
constructor(key: string, title: string, options:any[] = [], defaultVal: ElementValue, onSave = noop) {
super(key, title, defaultVal, onSave);
this.element = this.createElement("<select/>");
for(const option of options) {
let s: HTMLSelectElement
$<HTMLSelectElement>("<option/>").val(option.val).text(option.text).appendTo(this.element);
}
this.element.val(this.getStorageValue());
@ -82,7 +85,7 @@ class DropdownSetting<T> extends Setting<ElementValue, HTMLSelectElement> {
}
class BooleanSetting extends Setting<boolean, HTMLInputElement> {
constructor(key: string, title: string, defaultVal = false, onSave = () => {}) {
constructor(key: string, title: string, defaultVal = false, onSave = noop) {
super(key, title, defaultVal, onSave);
this.element = this.createElement("<input/>", {
type: "checkbox",
@ -96,7 +99,7 @@ class BooleanSetting extends Setting<boolean, HTMLInputElement> {
this.element.prop("checked", newVal);
}
getStorageValue() {
let val = super.getStorageValue();
const val = super.getStorageValue();
return val == true;
}
}
@ -107,9 +110,9 @@ interface MinMax {
max?: number;
}
class NumberSetting extends Setting<number, HTMLInputElement> {
constructor(key: string, title: string, defaultVal = 0, minMax: MinMax = {min: null, max: null}, onSave = () => {}) {
constructor(key: string, title: string, defaultVal = 0, minMax: MinMax = {min: null, max: null}, onSave = noop) {
super(key, title, defaultVal, onSave);
let props: MinMax = {
const props: MinMax = {
type: "number"
};
if(typeof minMax.min == "number" && !isNaN(minMax.min))
@ -127,8 +130,8 @@ class NumberSetting extends Setting<number, HTMLInputElement> {
}
function createLightbox() {
let settingsHTML =
'<div id="settings-container" style="overflow:auto"><table width="100%"><colgroup><col span="1" width="50%"><col span="1" width="50%"></colgroup></table></div><div class="lightbox-footer"><hr /><button id="save-settings-button">Save Settings</button></div>';
const settingsHTML =
`<div id="settings-container" style="overflow:auto"><table width="100%"><colgroup><col span="1" width="50%"><col span="1" width="50%"></colgroup></table></div><div class="lightbox-footer"><hr /><button id="save-settings-button">Save Settings</button></div>`;
showLightBox("Settings", settingsHTML);
$("button#save-settings-button").on("click", () => {
settings.forEach((setting, key) => {
@ -136,9 +139,9 @@ function createLightbox() {
setting.onSave();
});
});
let $settingsTable = $("#settings-container table");
const $settingsTable = $("#settings-container table");
settings.forEach((setting) => {
let $tr = $("<tr/>").appendTo($settingsTable);
const $tr = $("<tr/>").appendTo($settingsTable);
$("<td/>").append($("<b/>").text(setting.title)).appendTo($tr);
$("<td/>").append(setting.element).appendTo($tr);
});
@ -148,7 +151,7 @@ function createLightbox() {
* executes the custom JavaScript set in the settings
*/
export function setCustomJS() {
let customJS = getStorageVal("customjs");
const customJS = getStorageVal("customjs");
if(customJS != "") {
eval(customJS);
}
@ -158,22 +161,21 @@ export function setCustomJS() {
* applies the custom CSS set in the settings
*/
export function setCustomCSS() {
let customCSS = getStorageVal("customcss");
const customCSS = getStorageVal("customcss");
if(customCSS != "") {
$("style#customCSS").remove();
$("<style/>").prop({
id: "customCSS"
}).html(customCSS)
.appendTo(document.head);
}).html(customCSS).appendTo(document.head);
}
}
$(() => {
let styleOptions = [];
const styleOptions = [];
for(const style of styles) {
styleOptions.push({text: style.Name, val: style.Filename});
}
settings.set("style", new DropdownSetting<string>("style", "Style", styleOptions, defaultStyle, function() {
settings.set("style", new DropdownSetting("style", "Style", styleOptions, defaultStyle, function() {
document.getElementById("theme").setAttribute("href",
`${webroot}css/${this.getElementValue()}`);
}) as Setting);

View file

@ -4,14 +4,14 @@ import { getCookie, setCookie } from "./cookies";
export function getStorageVal(key: string, defaultVal = "") {
if(localStorage == undefined)
return getCookie(key, defaultVal);
let val = localStorage.getItem(key);
const val = localStorage.getItem(key);
if(val === null)
return defaultVal;
return val;
}
export function getBooleanStorageVal(key: string, defaultVal = false) {
let val = getStorageVal(key, defaultVal?"true":"false");
const val = getStorageVal(key, defaultVal?"true":"false");
return val == "true";
}
@ -30,7 +30,7 @@ export function getJsonStorageVal<T>(key: string, defaultVal: T) {
}
export function setStorageVal(key: string, val: any, isJSON = false) {
let storeVal = isJSON?JSON.stringify(val):val;
const storeVal = isJSON?JSON.stringify(val):val;
if(localStorage == undefined)
setCookie(key, storeVal);
else

View file

@ -1,3 +1,5 @@
/* eslint no-var: 0 */
import "jquery";
declare global {

View file

@ -18,7 +18,7 @@ function addThreadToMenu(thread: WatchedThreadJSON) {
return;
}
if(thread.op == "") thread.op = "Anonymous";
let $replyCounter = $("<span/>")
const $replyCounter = $("<span/>")
.prop({id: "reply-counter"})
.text(`(Replies: ${thread.posts - 1})`);
let infoElem = ` - <b>OP:</b> ${thread.op}<br/>`;
@ -27,7 +27,7 @@ function addThreadToMenu(thread: WatchedThreadJSON) {
} else {
infoElem += `<b>Subject: </b> ${thread.subject}`;
}
let $watcherItem = $("<div/>").prop({
const $watcherItem = $("<div/>").prop({
id: `thread${thread.id}`,
class: "watcher-item"
}).append(
@ -45,10 +45,10 @@ function addThreadToMenu(thread: WatchedThreadJSON) {
}).text("X"), " "
);
if(thread.err !== undefined)
$watcherItem.append($("<span/>")
.prop({class: "warning"})
.text(`(${thread.err})`)
);
$watcherItem.append($("<span/>")
.prop({class: "warning"})
.text(`(${thread.err})`)
);
$watcherMenu.append(
$watcherItem.append(infoElem)
);
@ -59,16 +59,16 @@ function addThreadToMenu(thread: WatchedThreadJSON) {
function removeThreadFromMenu(threadID: number) {
$watcherMenu.find(`div#thread${threadID}`).remove();
if($watcherMenu.find("div.watcher-item").length == 0)
$watcherMenu.append('<i id="no-threads">no watched threads</i>');
$watcherMenu.append(`<i id="no-threads">no watched threads</i>`);
}
function updateThreadInWatcherMenu(thread: WatchedThreadJSON) {
let currentPage = currentThread();
const currentPage = currentThread();
let $item = $watcherMenu.find(`div#thread${thread.op}`);
const $item = $watcherMenu.find(`div#thread${thread.op}`);
if($item.length == 0) return; // watched thread isn't in the menu
$item.find("span#reply-counter").remove();
let $replyCounter = $("<span>").prop({
const $replyCounter = $("<span>").prop({
id: "reply-counter"
}).insertBefore($item.find(`a#unwatch${thread.op}`));
@ -101,7 +101,7 @@ $(() => {
$watcherMenu = $("<div/>").prop({
id: "watchermenu",
class: "dropdown-menu"
}).append('<b>Watched threads</b><br/><i id="no-threads">no watched threads</i>');
}).append(`<b>Watched threads</b><br/><i id="no-threads">no watched threads</i>`);
}
if(watcherBtn === null) {
watcherBtn = new TopBarButton("Watcher", () => {
@ -117,8 +117,8 @@ $(() => {
.on("beginNewPostsCheck", () => {
numUpdatedThreads = 0;
});
let watched = getJsonStorageVal<WatchedThreadsListJSON>("watched", {});
let boards = Object.keys(watched);
const watched = getJsonStorageVal<WatchedThreadsListJSON>("watched", {});
const boards = Object.keys(watched);
for(const board of boards) {
for(const thread of watched[board]) {
addThreadToMenu(thread);

View file

@ -7,12 +7,12 @@ import "./menu";
const subjectCuttoff = 24;
let watcherInterval = -1;
const watcherInterval = -1;
export function updateWatchedThreads() {
let watched = getJsonStorageVal<any>("watched", {});
let boards = Object.keys(watched);
let currentPage = currentThread();
const watched = getJsonStorageVal<any>("watched", {});
const boards = Object.keys(watched);
const currentPage = currentThread();
for(const board of boards) {
if(!(watched[board] instanceof Array)) {
console.error(`Invalid data for board ${board}: expected Array object, deleting.`);
@ -67,8 +67,8 @@ export interface WatchedThreadJSON {
}
export function isThreadWatched(threadID: number, board: string) {
let watched = getJsonStorageVal<WatchedThreadsListJSON>("watched", {});
let threads = watched[board];
const watched = getJsonStorageVal<WatchedThreadsListJSON>("watched", {});
const threads = watched[board];
if(threads == undefined) return false;
for(const thread of threads) {
if(thread.id == threadID) return true;
@ -77,7 +77,7 @@ export function isThreadWatched(threadID: number, board: string) {
}
export function watchThread(threadID: string|number, board: string) {
let watched = getJsonStorageVal<WatchedThreadsListJSON>("watched", {});
const watched = getJsonStorageVal<WatchedThreadsListJSON>("watched", {});
if(typeof threadID == "string") {
threadID = parseInt(threadID);
}
@ -93,7 +93,7 @@ export function watchThread(threadID: string|number, board: string) {
}
getThreadJSON(threadID, board).then(data => {
const op = data.posts[0];
let threadObj: WatchedThreadJSON = {
const threadObj: WatchedThreadJSON = {
id: threadID as number,
board: board,
posts: data.posts.length,
@ -114,7 +114,7 @@ export function watchThread(threadID: string|number, board: string) {
}
export function unwatchThread(threadID: number, board: string) {
let watched = getJsonStorageVal<WatchedThreadsListJSON>("watched", {});
const watched = getJsonStorageVal<WatchedThreadsListJSON>("watched", {});
if(!(watched[board] instanceof Array))
return;
for(const i in watched[board]) {