diff options
| author | Shyam Sunder | 2020-06-05 18:03:37 -0400 |
|---|---|---|
| committer | Shyam Sunder | 2020-06-06 08:58:23 -0400 |
| commit | 57193b57157b7a42896c887a2d5930493ac7b290 (patch) | |
| tree | 9555453a944caae64d8a00e4b073482d9e7fb244 /client/js/util | |
| parent | c06aaa63af977e64ef08bfba7829214f0f0ed38e (diff) | |
client+server: implement code autoformatting using prettier and black
Diffstat (limited to 'client/js/util')
| -rw-r--r-- | client/js/util/keyboard.js | 8 | ||||
| -rw-r--r-- | client/js/util/markdown.js | 83 | ||||
| -rw-r--r-- | client/js/util/misc.js | 110 | ||||
| -rw-r--r-- | client/js/util/optimized_resize.js | 10 | ||||
| -rw-r--r-- | client/js/util/polyfill.js | 28 | ||||
| -rw-r--r-- | client/js/util/progress.js | 4 | ||||
| -rw-r--r-- | client/js/util/search.js | 14 | ||||
| -rw-r--r-- | client/js/util/touch.js | 65 | ||||
| -rw-r--r-- | client/js/util/uri.js | 35 | ||||
| -rw-r--r-- | client/js/util/views.js | 454 |
10 files changed, 433 insertions, 378 deletions
diff --git a/client/js/util/keyboard.js b/client/js/util/keyboard.js index 8ee6ee9..3fe3263 100644 --- a/client/js/util/keyboard.js +++ b/client/js/util/keyboard.js @@ -1,12 +1,12 @@ -'use strict'; +"use strict"; -const mousetrap = require('mousetrap'); -const settings = require('../models/settings.js'); +const mousetrap = require("mousetrap"); +const settings = require("../models/settings.js"); let paused = false; const _originalStopCallback = mousetrap.prototype.stopCallback; // eslint-disable-next-line func-names -mousetrap.prototype.stopCallback = function(...args) { +mousetrap.prototype.stopCallback = function (...args) { var self = this; if (paused) { return true; diff --git a/client/js/util/markdown.js b/client/js/util/markdown.js index 210280e..792c938 100644 --- a/client/js/util/markdown.js +++ b/client/js/util/markdown.js @@ -1,6 +1,6 @@ -'use strict'; +"use strict"; -const marked = require('marked'); +const marked = require("marked"); class BaseMarkdownWrapper { preprocess(text) { @@ -20,42 +20,44 @@ class SjisWrapper extends BaseMarkdownWrapper { preprocess(text) { return text.replace( - /\[sjis\]((?:[^\[]|\[(?!\/?sjis\]))+)\[\/sjis\]/ig, + /\[sjis\]((?:[^\[]|\[(?!\/?sjis\]))+)\[\/sjis\]/gi, (match, capture) => { - var ret = '%%%SJIS' + this.buf.length; + var ret = "%%%SJIS" + this.buf.length; this.buf.push(capture); return ret; - }); + } + ); } postprocess(text) { return text.replace( /(?:<p>)?%%%SJIS(\d+)(?:<\/p>)?/, (match, capture) => { - return '<div class="sjis">' + this.buf[capture] + '</div>'; - }); + return '<div class="sjis">' + this.buf[capture] + "</div>"; + } + ); } } // fix \ before ~ being stripped away class TildeWrapper extends BaseMarkdownWrapper { preprocess(text) { - return text.replace(/\\~/g, '%%%T'); + return text.replace(/\\~/g, "%%%T"); } postprocess(text) { - return text.replace(/%%%T/g, '\\~'); + return text.replace(/%%%T/g, "\\~"); } } // prevent ^#... from being treated as headers, due to tag permalinks class TagPermalinkFixWrapper extends BaseMarkdownWrapper { preprocess(text) { - return text.replace(/^#/g, '%%%#'); + return text.replace(/^#/g, "%%%#"); } postprocess(text) { - return text.replace(/%%%#/g, '#'); + return text.replace(/%%%#/g, "#"); } } @@ -63,19 +65,23 @@ class TagPermalinkFixWrapper extends BaseMarkdownWrapper { class EntityPermalinkWrapper extends BaseMarkdownWrapper { preprocess(text) { // URL-based permalinks + text = text.replace(new RegExp("\\b/post/(\\d+)/?\\b", "g"), "@$1"); text = text.replace( - new RegExp('\\b/post/(\\d+)/?\\b', 'g'), '@$1'); - text = text.replace( - new RegExp('\\b/tag/([a-zA-Z0-9_-]+?)/?', 'g'), '#$1'); + new RegExp("\\b/tag/([a-zA-Z0-9_-]+?)/?", "g"), + "#$1" + ); text = text.replace( - new RegExp('\\b/user/([a-zA-Z0-9_-]+?)/?', 'g'), '+$1'); + new RegExp("\\b/user/([a-zA-Z0-9_-]+?)/?", "g"), + "+$1" + ); text = text.replace( /(^|^\(|(?:[^\]])\(|[\s<>\[\]\)])([+#@][a-zA-Z0-9_-]+)/g, - '$1[$2]($2)'); - text = text.replace(/\]\(@(\d+)\)/g, '](/post/$1)'); - text = text.replace(/\]\(\+([a-zA-Z0-9_-]+)\)/g, '](/user/$1)'); - text = text.replace(/\]\(#([a-zA-Z0-9_-]+)\)/g, '](/posts/query=$1)'); + "$1[$2]($2)" + ); + text = text.replace(/\]\(@(\d+)\)/g, "](/post/$1)"); + text = text.replace(/\]\(\+([a-zA-Z0-9_-]+)\)/g, "](/user/$1)"); + text = text.replace(/\]\(#([a-zA-Z0-9_-]+)\)/g, "](/posts/query=$1)"); return text; } } @@ -83,51 +89,58 @@ class EntityPermalinkWrapper extends BaseMarkdownWrapper { class SearchPermalinkWrapper extends BaseMarkdownWrapper { postprocess(text) { return text.replace( - /\[search\]((?:[^\[]|\[(?!\/?search\]))+)\[\/search\]/ig, - '<a href="/posts/query=$1"><code>$1</code></a>'); + /\[search\]((?:[^\[]|\[(?!\/?search\]))+)\[\/search\]/gi, + '<a href="/posts/query=$1"><code>$1</code></a>' + ); } } class SpoilersWrapper extends BaseMarkdownWrapper { postprocess(text) { return text.replace( - /\[spoiler\]((?:[^\[]|\[(?!\/?spoiler\]))+)\[\/spoiler\]/ig, - '<span class="spoiler">$1</span>'); + /\[spoiler\]((?:[^\[]|\[(?!\/?spoiler\]))+)\[\/spoiler\]/gi, + '<span class="spoiler">$1</span>' + ); } } class SmallWrapper extends BaseMarkdownWrapper { postprocess(text) { return text.replace( - /\[small\]((?:[^\[]|\[(?!\/?small\]))+)\[\/small\]/ig, - '<small>$1</small>'); + /\[small\]((?:[^\[]|\[(?!\/?small\]))+)\[\/small\]/gi, + "<small>$1</small>" + ); } } class StrikeThroughWrapper extends BaseMarkdownWrapper { postprocess(text) { - text = text.replace(/(^|[^\\])(~~|~)([^~]+)\2/g, '$1<del>$3</del>'); - return text.replace(/\\~/g, '~'); + text = text.replace(/(^|[^\\])(~~|~)([^~]+)\2/g, "$1<del>$3</del>"); + return text.replace(/\\~/g, "~"); } } function createRenderer() { function sanitize(str) { - return str.replace(/&<"/g, m => { - if (m === '&') { - return '&'; + return str.replace(/&<"/g, (m) => { + if (m === "&") { + return "&"; } - if (m === '<') { - return '<'; + if (m === "<") { + return "<"; } - return '"'; + return """; }); } const renderer = new marked.Renderer(); renderer.image = (href, title, alt) => { - let [_, url, width, height] = - (/^(.+?)(?:\s=\s*(\d*)\s*x\s*(\d*)\s*)?$/).exec(href); + let [ + _, + url, + width, + height, + ] = /^(.+?)(?:\s=\s*(\d*)\s*x\s*(\d*)\s*)?$/.exec(href); let res = '<img src="' + sanitize(url) + '" alt="' + sanitize(alt); if (width) { res += '" width="' + width; diff --git a/client/js/util/misc.js b/client/js/util/misc.js index c922bb1..b299d3a 100644 --- a/client/js/util/misc.js +++ b/client/js/util/misc.js @@ -1,18 +1,18 @@ -'use strict'; +"use strict"; -const markdown = require('./markdown.js'); -const uri = require('./uri.js'); -const settings = require('../models/settings.js'); +const markdown = require("./markdown.js"); +const uri = require("./uri.js"); +const settings = require("../models/settings.js"); function decamelize(str, sep) { - sep = sep === undefined ? '-' : sep; + sep = sep === undefined ? "-" : sep; return str - .replace(/([a-z\d])([A-Z])/g, '$1' + sep + '$2') - .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + sep + '$2') + .replace(/([a-z\d])([A-Z])/g, "$1" + sep + "$2") + .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, "$1" + sep + "$2") .toLowerCase(); } -function *range(start = 0, end = null, step = 1) { +function* range(start = 0, end = null, step = 1) { if (end === null) { end = start; start = 0; @@ -45,16 +45,17 @@ function formatFileSize(fileSize) { return _formatUnits( fileSize, 1024, - ['B', 'K', 'M', 'G'], + ["B", "K", "M", "G"], (number, suffix) => { - const decimalPlaces = number < 20 && suffix !== 'B' ? 1 : 0; + const decimalPlaces = number < 20 && suffix !== "B" ? 1 : 0; return number.toFixed(decimalPlaces) + suffix; - }); + } + ); } function formatRelativeTime(timeString) { if (!timeString) { - return 'never'; + return "never"; } const then = Date.parse(timeString); @@ -63,17 +64,17 @@ function formatRelativeTime(timeString) { const future = now < then; const descriptions = [ - [60, 'a few seconds', null], - [60 * 2, 'a minute', null], - [60 * 60, '% minutes', 60], - [60 * 60 * 2, 'an hour', null], - [60 * 60 * 24, '% hours', 60 * 60], - [60 * 60 * 24 * 2, 'a day', null], - [60 * 60 * 24 * 30.42, '% days', 60 * 60 * 24], - [60 * 60 * 24 * 30.42 * 2, 'a month', null], - [60 * 60 * 24 * 30.42 * 12, '% months', 60 * 60 * 24 * 30.42], - [60 * 60 * 24 * 30.42 * 12 * 2, 'a year', null], - [8640000000000000 /* max*/, '% years', 60 * 60 * 24 * 30.42 * 12], + [60, "a few seconds", null], + [60 * 2, "a minute", null], + [60 * 60, "% minutes", 60], + [60 * 60 * 2, "an hour", null], + [60 * 60 * 24, "% hours", 60 * 60], + [60 * 60 * 24 * 2, "a day", null], + [60 * 60 * 24 * 30.42, "% days", 60 * 60 * 24], + [60 * 60 * 24 * 30.42 * 2, "a month", null], + [60 * 60 * 24 * 30.42 * 12, "% months", 60 * 60 * 24 * 30.42], + [60 * 60 * 24 * 30.42 * 12 * 2, "a year", null], + [8640000000000000 /* max*/, "% years", 60 * 60 * 24 * 30.42 * 12], ]; let text = null; @@ -87,10 +88,10 @@ function formatRelativeTime(timeString) { } } - if (text === 'a day') { - return future ? 'tomorrow' : 'yesterday'; + if (text === "a day") { + return future ? "tomorrow" : "yesterday"; } - return future ? 'in ' + text : text + ' ago'; + return future ? "in " + text : text + " ago"; } function formatMarkdown(text) { @@ -102,7 +103,7 @@ function formatInlineMarkdown(text) { } function splitByWhitespace(str) { - return str.split(/\s+/).filter(s => s); + return str.split(/\s+/).filter((s) => s); } function unindent(callSite, ...args) { @@ -110,28 +111,30 @@ function unindent(callSite, ...args) { let size = -1; return str.replace(/\n(\s+)/g, (m, m1) => { if (size < 0) { - size = m1.replace(/\t/g, ' ').length; + size = m1.replace(/\t/g, " ").length; } - return '\n' + m1.slice(Math.min(m1.length, size)); + return "\n" + m1.slice(Math.min(m1.length, size)); }); } - if (typeof callSite === 'string') { + if (typeof callSite === "string") { return format(callSite); } - if (typeof callSite === 'function') { + if (typeof callSite === "function") { return (...args) => format(callSite(...args)); } let output = callSite .slice(0, args.length + 1) - .map((text, i) => (i === 0 ? '' : args[i - 1]) + text) - .join(''); + .map((text, i) => (i === 0 ? "" : args[i - 1]) + text) + .join(""); return format(output); } function enableExitConfirmation() { - window.onbeforeunload = e => { - return 'Are you sure you want to leave? ' + - 'Data you have entered may not be saved.'; + window.onbeforeunload = (e) => { + return ( + "Are you sure you want to leave? " + + "Data you have entered may not be saved." + ); }; } @@ -150,16 +153,17 @@ function confirmPageExit() { } function makeCssName(text, suffix) { - return suffix + '-' + text.replace(/[^a-z0-9]/g, '_'); + return suffix + "-" + text.replace(/[^a-z0-9]/g, "_"); } function escapeHtml(unsafe) { - return unsafe.toString() - .replace(/&/g, '&') - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); + return unsafe + .toString() + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); } function arraysDiffer(source1, source2, orderImportant) { @@ -177,25 +181,27 @@ function arraysDiffer(source1, source2, orderImportant) { return false; } return ( - source1.filter(value => !source2.includes(value)).length > 0 || - source2.filter(value => !source1.includes(value)).length > 0); + source1.filter((value) => !source2.includes(value)).length > 0 || + source2.filter((value) => !source1.includes(value)).length > 0 + ); } function escapeSearchTerm(text) { - return text.replace(/([a-z_-]):/g, '$1\\:'); + return text.replace(/([a-z_-]):/g, "$1\\:"); } function dataURItoBlob(dataURI) { - const chunks = dataURI.split(','); - const byteString = chunks[0].indexOf('base64') >= 0 ? - window.atob(chunks[1]) : - unescape(chunks[1]); - const mimeString = chunks[0].split(':')[1].split(';')[0]; + const chunks = dataURI.split(","); + const byteString = + chunks[0].indexOf("base64") >= 0 + ? window.atob(chunks[1]) + : unescape(chunks[1]); + const mimeString = chunks[0].split(":")[1].split(";")[0]; const data = new Uint8Array(byteString.length); for (let i = 0; i < byteString.length; i++) { data[i] = byteString.charCodeAt(i); } - return new Blob([data], {type: mimeString}); + return new Blob([data], { type: mimeString }); } function getPrettyTagName(tag) { diff --git a/client/js/util/optimized_resize.js b/client/js/util/optimized_resize.js index 545c458..d45df4a 100644 --- a/client/js/util/optimized_resize.js +++ b/client/js/util/optimized_resize.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; let callbacks = []; let running = false; @@ -15,7 +15,7 @@ function resize() { } function runCallbacks() { - callbacks.forEach(callback => { + callbacks.forEach((callback) => { callback(); }); running = false; @@ -26,8 +26,8 @@ function add(callback) { } function remove(callback) { - callbacks = callbacks.filter(c => c !== callback); + callbacks = callbacks.filter((c) => c !== callback); } -window.addEventListener('resize', resize); -module.exports = {add: add, remove: remove}; +window.addEventListener("resize", resize); +module.exports = { add: add, remove: remove }; diff --git a/client/js/util/polyfill.js b/client/js/util/polyfill.js index 09f5835..ec809ff 100644 --- a/client/js/util/polyfill.js +++ b/client/js/util/polyfill.js @@ -1,11 +1,11 @@ /* eslint-disable func-names, no-extend-native */ -'use strict'; +"use strict"; // fix iterating over NodeList in Chrome and Opera NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; -NodeList.prototype.querySelector = function(...args) { +NodeList.prototype.querySelector = function (...args) { for (let node of this) { if (node.nodeType === 3) { continue; @@ -18,7 +18,7 @@ NodeList.prototype.querySelector = function(...args) { return null; }; -NodeList.prototype.querySelectorAll = function(...args) { +NodeList.prototype.querySelectorAll = function (...args) { let result = []; for (let node of this) { if (node.nodeType === 3) { @@ -32,7 +32,7 @@ NodeList.prototype.querySelectorAll = function(...args) { }; // non standard -Node.prototype.prependChild = function(child) { +Node.prototype.prependChild = function (child) { if (this.firstChild) { this.insertBefore(child, this.firstChild); } else { @@ -41,29 +41,25 @@ Node.prototype.prependChild = function(child) { }; // non standard -Promise.prototype.always = function(onResolveOrReject) { - return this.then( - onResolveOrReject, - reason => { - onResolveOrReject(reason); - throw reason; - }); +Promise.prototype.always = function (onResolveOrReject) { + return this.then(onResolveOrReject, (reason) => { + onResolveOrReject(reason); + throw reason; + }); }; // non standard -Number.prototype.between = function(a, b, inclusive) { +Number.prototype.between = function (a, b, inclusive) { const min = Math.min(a, b); const max = Math.max(a, b); - return inclusive ? - this >= min && this <= max : - this > min && this < max; + return inclusive ? this >= min && this <= max : this > min && this < max; }; // non standard Promise.prototype.abort = () => {}; // non standard -Date.prototype.addDays = function(days) { +Date.prototype.addDays = function (days) { let dat = new Date(this.valueOf()); dat.setDate(dat.getDate() + days); return dat; diff --git a/client/js/util/progress.js b/client/js/util/progress.js index 98df179..d6d12cb 100644 --- a/client/js/util/progress.js +++ b/client/js/util/progress.js @@ -1,6 +1,6 @@ -'use strict'; +"use strict"; -const nprogress = require('nprogress'); +const nprogress = require("nprogress"); let nesting = 0; diff --git a/client/js/util/search.js b/client/js/util/search.js index 8d5fca4..c540c2f 100644 --- a/client/js/util/search.js +++ b/client/js/util/search.js @@ -1,14 +1,16 @@ -'use strict'; +"use strict"; -const misc = require('./misc.js'); -const keyboard = require('../util/keyboard.js'); -const views = require('./views.js'); +const misc = require("./misc.js"); +const keyboard = require("../util/keyboard.js"); +const views = require("./views.js"); function searchInputNodeFocusHelper(inputNode) { - keyboard.bind('q', () => { + keyboard.bind("q", () => { inputNode.focus(); inputNode.setSelectionRange( - inputNode.value.length, inputNode.value.length); + inputNode.value.length, + inputNode.value.length + ); }); } diff --git a/client/js/util/touch.js b/client/js/util/touch.js index 53b0978..64bd00a 100644 --- a/client/js/util/touch.js +++ b/client/js/util/touch.js @@ -1,11 +1,11 @@ -'use strict'; +"use strict"; const direction = { NONE: null, - LEFT: 'left', - RIGHT: 'right', - DOWN: 'down', - UP: 'up' + LEFT: "left", + RIGHT: "right", + DOWN: "down", + UP: "up", }; function handleTouchStart(handler, evt) { @@ -37,20 +37,20 @@ function handleTouchMove(handler, evt) { function handleTouchEnd(handler) { switch (handler._direction) { - case direction.NONE: - return; - case direction.LEFT: - handler._swipeLeftTask(); - break; - case direction.RIGHT: - handler._swipeRightTask(); - break; - case direction.DOWN: - handler._swipeDownTask(); - break; - case direction.UP: - handler._swipeUpTask(); - // no default + case direction.NONE: + return; + case direction.LEFT: + handler._swipeLeftTask(); + break; + case direction.RIGHT: + handler._swipeRightTask(); + break; + case direction.DOWN: + handler._swipeDownTask(); + break; + case direction.UP: + handler._swipeUpTask(); + // no default } handler._xStart = null; @@ -58,11 +58,13 @@ function handleTouchEnd(handler) { } class Touch { - constructor(target, + constructor( + target, swipeLeft = () => {}, swipeRight = () => {}, swipeUp = () => {}, - swipeDown = () => {}) { + swipeDown = () => {} + ) { this._target = target; this._swipeLeftTask = swipeLeft; @@ -74,18 +76,15 @@ class Touch { this._yStart = null; this._direction = direction.NONE; - this._target.addEventListener('touchstart', - evt => { - handleTouchStart(this, evt); - }); - this._target.addEventListener('touchmove', - evt => { - handleTouchMove(this, evt); - }); - this._target.addEventListener('touchend', - () => { - handleTouchEnd(this); - }); + this._target.addEventListener("touchstart", (evt) => { + handleTouchStart(this, evt); + }); + this._target.addEventListener("touchmove", (evt) => { + handleTouchMove(this, evt); + }); + this._target.addEventListener("touchend", () => { + handleTouchEnd(this); + }); } } diff --git a/client/js/util/uri.js b/client/js/util/uri.js index 868545f..16fa4f8 100644 --- a/client/js/util/uri.js +++ b/client/js/util/uri.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; function formatApiLink(...values) { let parts = []; @@ -9,18 +9,19 @@ function formatApiLink(...values) { for (let key of Object.keys(value)) { if (value[key]) { variableParts.push( - key + '=' + encodeURIComponent(value[key].toString())); + key + "=" + encodeURIComponent(value[key].toString()) + ); } } if (variableParts.length) { - parts.push('?' + variableParts.join('&')); + parts.push("?" + variableParts.join("&")); } break; } else { parts.push(encodeURIComponent(value.toString())); } } - return '/' + parts.join('/'); + return "/" + parts.join("/"); } function escapeParam(text) { @@ -40,48 +41,52 @@ function formatClientLink(...values) { for (let key of Object.keys(value)) { if (value[key]) { variableParts.push( - key + '=' + escapeParam(value[key].toString())); + key + "=" + escapeParam(value[key].toString()) + ); } } if (variableParts.length) { - parts.push(variableParts.join(';')); + parts.push(variableParts.join(";")); } break; } else { parts.push(escapeParam(value.toString())); } } - return parts.join('/'); + return parts.join("/"); } function extractHostname(url) { // https://stackoverflow.com/a/23945027 return url - .split('/')[url.indexOf("//") > -1 ? 2 : 0] - .split(':')[0] - .split('?')[0]; + .split("/") + [url.indexOf("//") > -1 ? 2 : 0].split(":")[0] + .split("?")[0]; } function extractRootDomain(url) { // https://stackoverflow.com/a/23945027 let domain = extractHostname(url); - let splitArr = domain.split('.'); + let splitArr = domain.split("."); let arrLen = splitArr.length; // if there is a subdomain if (arrLen > 2) { - domain = splitArr[arrLen - 2] + '.' + splitArr[arrLen - 1]; + domain = splitArr[arrLen - 2] + "." + splitArr[arrLen - 1]; // check to see if it's using a Country Code Top Level Domain (ccTLD) (i.e. ".me.uk") - if (splitArr[arrLen - 2].length === 2 && splitArr[arrLen - 1].length === 2) { + if ( + splitArr[arrLen - 2].length === 2 && + splitArr[arrLen - 1].length === 2 + ) { // this is using a ccTLD - domain = splitArr[arrLen - 3] + '.' + domain; + domain = splitArr[arrLen - 3] + "." + domain; } } return domain; } function escapeColons(text) { - return text.replace(new RegExp(':', 'g'), '\\:'); + return text.replace(new RegExp(":", "g"), "\\:"); } module.exports = { diff --git a/client/js/util/views.js b/client/js/util/views.js index afdda76..f69b34b 100644 --- a/client/js/util/views.js +++ b/client/js/util/views.js @@ -1,27 +1,27 @@ -'use strict'; +"use strict"; -require('../util/polyfill.js'); -const api = require('../api.js'); -const templates = require('../templates.js'); +require("../util/polyfill.js"); +const api = require("../api.js"); +const templates = require("../templates.js"); const domParser = new DOMParser(); -const misc = require('./misc.js'); -const uri = require('./uri.js'); +const misc = require("./misc.js"); +const uri = require("./uri.js"); function _imbueId(options) { if (!options.id) { - options.id = 'gen-' + Math.random().toString(36).substring(7); + options.id = "gen-" + Math.random().toString(36).substring(7); } } function _makeLabel(options, attrs) { if (!options.text) { - return ''; + return ""; } if (!attrs) { attrs = {}; } attrs.for = options.id; - return makeElement('label', attrs, options.text); + return makeElement("label", attrs, options.text); } function makeFileSize(fileSize) { @@ -34,251 +34,282 @@ function makeMarkdown(text) { function makeRelativeTime(time) { return makeElement( - 'time', {datetime: time, title: time}, misc.formatRelativeTime(time)); + "time", + { datetime: time, title: time }, + misc.formatRelativeTime(time) + ); } function makeThumbnail(url) { return makeElement( - 'span', - url ? - {class: 'thumbnail', style: `background-image: url(\'${url}\')`} : - {class: 'thumbnail empty'}, - makeElement('img', {alt: 'thumbnail', src: url})); + "span", + url + ? { + class: "thumbnail", + style: `background-image: url(\'${url}\')`, + } + : { class: "thumbnail empty" }, + makeElement("img", { alt: "thumbnail", src: url }) + ); } function makeRadio(options) { _imbueId(options); return makeElement( - 'label', - {for: options.id}, - makeElement( - 'input', - { - id: options.id, - name: options.name, - value: options.value, - type: 'radio', - checked: options.selectedValue === options.value, - disabled: options.readonly, - required: options.required, - }), - makeElement('span', {class: 'radio'}, options.text)); + "label", + { for: options.id }, + makeElement("input", { + id: options.id, + name: options.name, + value: options.value, + type: "radio", + checked: options.selectedValue === options.value, + disabled: options.readonly, + required: options.required, + }), + makeElement("span", { class: "radio" }, options.text) + ); } function makeCheckbox(options) { _imbueId(options); return makeElement( - 'label', - {for: options.id}, - makeElement( - 'input', - { - id: options.id, - name: options.name, - value: options.value, - type: 'checkbox', - checked: options.checked !== undefined ? - options.checked : false, - disabled: options.readonly, - required: options.required, - }), - makeElement('span', {class: 'checkbox'}, options.text)); + "label", + { for: options.id }, + makeElement("input", { + id: options.id, + name: options.name, + value: options.value, + type: "checkbox", + checked: options.checked !== undefined ? options.checked : false, + disabled: options.readonly, + required: options.required, + }), + makeElement("span", { class: "checkbox" }, options.text) + ); } function makeSelect(options) { - return _makeLabel(options) + + return ( + _makeLabel(options) + makeElement( - 'select', + "select", { id: options.id, name: options.name, disabled: options.readonly, }, - ...Object.keys(options.keyValues).map(key => makeElement( - 'option', - {value: key, selected: key === options.selectedKey}, - options.keyValues[key]))); + ...Object.keys(options.keyValues).map((key) => + makeElement( + "option", + { value: key, selected: key === options.selectedKey }, + options.keyValues[key] + ) + ) + ) + ); } function makeInput(options) { - options.value = options.value || ''; - return _makeLabel(options) + makeElement('input', options); + options.value = options.value || ""; + return _makeLabel(options) + makeElement("input", options); } function makeButton(options) { - options.type = 'button'; + options.type = "button"; return makeInput(options); } function makeTextInput(options) { - options.type = 'text'; + options.type = "text"; return makeInput(options); } function makeTextarea(options) { - const value = options.value || ''; + const value = options.value || ""; delete options.value; - return _makeLabel(options) + makeElement('textarea', options, value); + return _makeLabel(options) + makeElement("textarea", options, value); } function makePasswordInput(options) { - options.type = 'password'; + options.type = "password"; return makeInput(options); } function makeEmailInput(options) { - options.type = 'email'; + options.type = "email"; return makeInput(options); } function makeColorInput(options) { - const textInput = makeElement( - 'input', { - type: 'text', - value: options.value || '', - required: options.required, - class: 'color', - }); - const backgroundPreviewNode = makeElement( - 'div', - { - class: 'preview background-preview', - style: - `border-color: ${options.value}; + const textInput = makeElement("input", { + type: "text", + value: options.value || "", + required: options.required, + class: "color", + }); + const backgroundPreviewNode = makeElement("div", { + class: "preview background-preview", + style: `border-color: ${options.value}; background-color: ${options.value}`, - }); - const textPreviewNode = makeElement( - 'div', - { - class: 'preview text-preview', - style: - `border-color: ${options.value}; + }); + const textPreviewNode = makeElement("div", { + class: "preview text-preview", + style: `border-color: ${options.value}; color: ${options.value}`, - }); + }); return makeElement( - 'label', {class: 'color'}, textInput, backgroundPreviewNode, textPreviewNode); + "label", + { class: "color" }, + textInput, + backgroundPreviewNode, + textPreviewNode + ); } function makeNumericInput(options) { - options.type = 'number'; + options.type = "number"; return makeInput(options); } function makeDateInput(options) { - options.type = 'date'; - return makeInput(options) + options.type = "date"; + return makeInput(options); } function getPostUrl(id, parameters) { return uri.formatClientLink( - 'post', id, parameters ? {query: parameters.query} : {}); + "post", + id, + parameters ? { query: parameters.query } : {} + ); } function getPostEditUrl(id, parameters) { return uri.formatClientLink( - 'post', id, 'edit', parameters ? {query: parameters.query} : {}); + "post", + id, + "edit", + parameters ? { query: parameters.query } : {} + ); } function makePostLink(id, includeHash) { let text = id; if (includeHash) { - text = '@' + id; + text = "@" + id; } - return api.hasPrivilege('posts:view') ? - makeElement( - 'a', - {href: uri.formatClientLink('post', id)}, - misc.escapeHtml(text)) : - misc.escapeHtml(text); + return api.hasPrivilege("posts:view") + ? makeElement( + "a", + { href: uri.formatClientLink("post", id) }, + misc.escapeHtml(text) + ) + : misc.escapeHtml(text); } function makeTagLink(name, includeHash, includeCount, tag) { - const category = tag ? tag.category : 'unknown'; + const category = tag ? tag.category : "unknown"; let text = misc.getPrettyTagName(name); if (includeHash === true) { - text = '#' + text; + text = "#" + text; } if (includeCount === true) { - text += ' (' + (tag ? tag.postCount : 0) + ')'; + text += " (" + (tag ? tag.postCount : 0) + ")"; } - return api.hasPrivilege('tags:view') ? - makeElement( - 'a', - { - href: uri.formatClientLink('tag', name), - class: misc.makeCssName(category, 'tag'), - }, - misc.escapeHtml(text)) : - makeElement( - 'span', - {class: misc.makeCssName(category, 'tag')}, - misc.escapeHtml(text)); + return api.hasPrivilege("tags:view") + ? makeElement( + "a", + { + href: uri.formatClientLink("tag", name), + class: misc.makeCssName(category, "tag"), + }, + misc.escapeHtml(text) + ) + : makeElement( + "span", + { class: misc.makeCssName(category, "tag") }, + misc.escapeHtml(text) + ); } function makePoolLink(id, includeHash, includeCount, pool, name) { - const category = pool ? pool.category : 'unknown'; + const category = pool ? pool.category : "unknown"; let text = name ? name : pool.names[0]; if (includeHash === true) { - text = '#' + text; + text = "#" + text; } if (includeCount === true) { - text += ' (' + (pool ? pool.postCount : 0) + ')'; + text += " (" + (pool ? pool.postCount : 0) + ")"; } - return api.hasPrivilege('pools:view') ? - makeElement( - 'a', - { - href: uri.formatClientLink('pool', id), - class: misc.makeCssName(category, 'pool'), - }, - misc.escapeHtml(text)) : - makeElement( - 'span', - {class: misc.makeCssName(category, 'pool')}, - misc.escapeHtml(text)); + return api.hasPrivilege("pools:view") + ? makeElement( + "a", + { + href: uri.formatClientLink("pool", id), + class: misc.makeCssName(category, "pool"), + }, + misc.escapeHtml(text) + ) + : makeElement( + "span", + { class: misc.makeCssName(category, "pool") }, + misc.escapeHtml(text) + ); } function makeUserLink(user) { let text = makeThumbnail(user ? user.avatarUrl : null); - text += user && user.name ? misc.escapeHtml(user.name) : 'Anonymous'; - const link = user && api.hasPrivilege('users:view') ? - makeElement( - 'a', {href: uri.formatClientLink('user', user.name)}, text) : - text; - return makeElement('span', {class: 'user'}, link); + text += user && user.name ? misc.escapeHtml(user.name) : "Anonymous"; + const link = + user && api.hasPrivilege("users:view") + ? makeElement( + "a", + { href: uri.formatClientLink("user", user.name) }, + text + ) + : text; + return makeElement("span", { class: "user" }, link); } function makeFlexboxAlign(options) { return [...misc.range(20)] - .map(() => '<li class="flexbox-dummy"></li>').join(''); + .map(() => '<li class="flexbox-dummy"></li>') + .join(""); } function makeAccessKey(html, key) { - const regex = new RegExp('(' + key + ')', 'i'); + const regex = new RegExp("(" + key + ")", "i"); html = html.replace( - regex, '<span class="access-key" data-accesskey="$1">$1</span>'); + regex, + '<span class="access-key" data-accesskey="$1">$1</span>' + ); return html; } function _serializeElement(name, attributes) { return [name] - .concat(Object.keys(attributes).map(key => { - if (attributes[key] === true) { - return key; - } else if (attributes[key] === false || - attributes[key] === undefined) { - return ''; - } - const attribute = misc.escapeHtml(attributes[key] || ''); - return `${key}="${attribute}"`; - })) - .join(' '); + .concat( + Object.keys(attributes).map((key) => { + if (attributes[key] === true) { + return key; + } else if ( + attributes[key] === false || + attributes[key] === undefined + ) { + return ""; + } + const attribute = misc.escapeHtml(attributes[key] || ""); + return `${key}="${attribute}"`; + }) + ) + .join(" "); } function makeElement(name, attrs, ...content) { - return content.length !== undefined ? - `<${_serializeElement(name, attrs)}>${content.join('')}</${name}>` : - `<${_serializeElement(name, attrs)}/>`; + return content.length !== undefined + ? `<${_serializeElement(name, attrs)}>${content.join("")}</${name}>` + : `<${_serializeElement(name, attrs)}/>`; } function emptyContent(target) { @@ -302,25 +333,25 @@ function replaceContent(target, source) { function showMessage(target, message, className) { if (!message) { - message = 'Unknown message'; + message = "Unknown message"; } - const messagesHolderNode = target.querySelector('.messages'); + const messagesHolderNode = target.querySelector(".messages"); if (!messagesHolderNode) { return false; } - const textNode = document.createElement('div'); - textNode.innerHTML = message.replace(/\n/g, '<br/>'); - textNode.classList.add('message'); + const textNode = document.createElement("div"); + textNode.innerHTML = message.replace(/\n/g, "<br/>"); + textNode.classList.add("message"); textNode.classList.add(className); - const wrapperNode = document.createElement('div'); - wrapperNode.classList.add('message-wrapper'); + const wrapperNode = document.createElement("div"); + wrapperNode.classList.add("message-wrapper"); wrapperNode.appendChild(textNode); messagesHolderNode.appendChild(wrapperNode); return true; } function appendExclamationMark() { - if (!document.title.startsWith('!')) { + if (!document.title.startsWith("!")) { document.oldTitle = document.title; document.title = `! ${document.title}`; } @@ -328,15 +359,15 @@ function appendExclamationMark() { function showError(target, message) { appendExclamationMark(); - return showMessage(target, misc.formatInlineMarkdown(message), 'error'); + return showMessage(target, misc.formatInlineMarkdown(message), "error"); } function showSuccess(target, message) { - return showMessage(target, misc.formatInlineMarkdown(message), 'success'); + return showMessage(target, misc.formatInlineMarkdown(message), "success"); } function showInfo(target, message) { - return showMessage(target, misc.formatInlineMarkdown(message), 'info'); + return showMessage(target, misc.formatInlineMarkdown(message), "info"); } function clearMessages(target) { @@ -344,7 +375,7 @@ function clearMessages(target) { document.title = document.oldTitle; document.oldTitle = null; } - for (let messagesHolderNode of target.querySelectorAll('.messages')) { + for (let messagesHolderNode of target.querySelectorAll(".messages")) { emptyContent(messagesHolderNode); } } @@ -352,15 +383,15 @@ function clearMessages(target) { function htmlToDom(html) { // code taken from jQuery + Krasimir Tsonev's blog const wrapMap = { - _: [1, '<div>', '</div>'], - option: [1, '<select multiple>', '</select>'], - legend: [1, '<fieldset>', '</fieldset>'], - area: [1, '<map>', '</map>'], - param: [1, '<object>', '</object>'], - thead: [1, '<table>', '</table>'], - tr: [2, '<table><tbody>', '</tbody></table>'], - td: [3, '<table><tbody><tr>', '</tr></tbody></table>'], - col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'], + _: [1, "<div>", "</div>"], + option: [1, "<select multiple>", "</select>"], + legend: [1, "<fieldset>", "</fieldset>"], + area: [1, "<map>", "</map>"], + param: [1, "<object>", "</object>"], + thead: [1, "<table>", "</table>"], + tr: [2, "<table><tbody>", "</tbody></table>"], + td: [3, "<table><tbody><tr>", "</tr></tbody></table>"], + col: [2, "<table><tbody></tbody><colgroup>", "</colgroup></table>"], }; wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.thead; @@ -369,8 +400,8 @@ function htmlToDom(html) { wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; - let element = document.createElement('div'); - const match = (/<\s*(\w+)[^>]*?>/g).exec(html); + let element = document.createElement("div"); + const match = /<\s*(\w+)[^>]*?>/g.exec(html); if (match) { const tag = match[1]; @@ -382,9 +413,9 @@ function htmlToDom(html) { } else { element.innerHTML = html; } - return element.childNodes.length > 1 ? - element.childNodes : - element.firstChild; + return element.childNodes.length > 1 + ? element.childNodes + : element.firstChild; } function getTemplate(templatePath) { @@ -392,7 +423,7 @@ function getTemplate(templatePath) { throw `Missing template: ${templatePath}`; } const templateFactory = templates[templatePath]; - return ctx => { + return (ctx) => { if (!ctx) { ctx = {}; } @@ -423,7 +454,7 @@ function getTemplate(templatePath) { makeElement: makeElement, makeCssName: misc.makeCssName, makeNumericInput: makeNumericInput, - formatClientLink: uri.formatClientLink + formatClientLink: uri.formatClientLink, }); return htmlToDom(templateFactory(ctx)); }; @@ -432,49 +463,51 @@ function getTemplate(templatePath) { function decorateValidator(form) { // postpone showing form fields validity until user actually tries // to submit it (seeing red/green form w/o doing anything breaks POLA) - let submitButton = form.querySelector('.buttons input'); + let submitButton = form.querySelector(".buttons input"); if (!submitButton) { - submitButton = form.querySelector('input[type=submit]'); + submitButton = form.querySelector("input[type=submit]"); } if (submitButton) { - submitButton.addEventListener('click', e => { - form.classList.add('show-validation'); + submitButton.addEventListener("click", (e) => { + form.classList.add("show-validation"); }); } - form.addEventListener('submit', e => { - form.classList.remove('show-validation'); + form.addEventListener("submit", (e) => { + form.classList.remove("show-validation"); }); } function disableForm(form) { - for (let input of form.querySelectorAll('input')) { + for (let input of form.querySelectorAll("input")) { input.disabled = true; } } function enableForm(form) { - for (let input of form.querySelectorAll('input')) { + for (let input of form.querySelectorAll("input")) { input.disabled = false; } } function syncScrollPosition() { - window.requestAnimationFrame( - () => { - if (history.state && Object.prototype.hasOwnProperty.call(history.state, 'scrollX')) { - window.scrollTo(history.state.scrollX, history.state.scrollY); - } else { - window.scrollTo(0, 0); - } - }); + window.requestAnimationFrame(() => { + if ( + history.state && + Object.prototype.hasOwnProperty.call(history.state, "scrollX") + ) { + window.scrollTo(history.state.scrollX, history.state.scrollY); + } else { + window.scrollTo(0, 0); + } + }); } function slideDown(element) { const duration = 500; return new Promise((resolve, reject) => { const height = element.getBoundingClientRect().height; - element.style.maxHeight = '0'; - element.style.overflow = 'hidden'; + element.style.maxHeight = "0"; + element.style.overflow = "hidden"; window.setTimeout(() => { element.style.transition = `all ${duration}ms ease`; element.style.maxHeight = `${height}px`; @@ -489,7 +522,7 @@ function slideUp(element) { const duration = 500; return new Promise((resolve, reject) => { const height = element.getBoundingClientRect().height; - element.style.overflow = 'hidden'; + element.style.overflow = "hidden"; element.style.maxHeight = `${height}px`; element.style.transition = `all ${duration}ms ease`; window.setTimeout(() => { @@ -502,26 +535,27 @@ function slideUp(element) { } function monitorNodeRemoval(monitoredNode, callback) { - const mutationObserver = new MutationObserver( - mutations => { - for (let mutation of mutations) { - for (let node of mutation.removedNodes) { - if (node.contains(monitoredNode)) { - mutationObserver.disconnect(); - callback(); - return; - } + const mutationObserver = new MutationObserver((mutations) => { + for (let mutation of mutations) { + for (let node of mutation.removedNodes) { + if (node.contains(monitoredNode)) { + mutationObserver.disconnect(); + callback(); + return; } } - }); - mutationObserver.observe( - document.body, {childList: true, subtree: true}); + } + }); + mutationObserver.observe(document.body, { + childList: true, + subtree: true, + }); } -document.addEventListener('input', e => { - if (e.target.classList.contains('color')) { - let bkNode = e.target.parentNode.querySelector('.background-preview'); - let textNode = e.target.parentNode.querySelector('.text-preview'); +document.addEventListener("input", (e) => { + if (e.target.classList.contains("color")) { + let bkNode = e.target.parentNode.querySelector(".background-preview"); + let textNode = e.target.parentNode.querySelector(".text-preview"); bkNode.style.backgroundColor = e.target.value; bkNode.style.borderColor = e.target.value; textNode.style.color = e.target.value; @@ -530,8 +564,8 @@ document.addEventListener('input', e => { }); // prevent opening buttons in new tabs -document.addEventListener('click', e => { - if (e.target.getAttribute('href') === '' && e.which === 2) { +document.addEventListener("click", (e) => { + if (e.target.getAttribute("href") === "" && e.which === 2) { e.preventDefault(); } }); |