diff options
Diffstat (limited to 'client')
126 files changed, 5538 insertions, 4681 deletions
diff --git a/client/.eslintrc.yml b/client/.eslintrc.yml index a176248..a9d703f 100644 --- a/client/.eslintrc.yml +++ b/client/.eslintrc.yml @@ -2,7 +2,7 @@ env: browser: true commonjs: true es6: true -extends: 'eslint:recommended' +extends: 'prettier' globals: Atomics: readonly SharedArrayBuffer: readonly @@ -10,284 +10,3 @@ ignorePatterns: - build.js parserOptions: ecmaVersion: 11 -rules: - accessor-pairs: error - array-bracket-newline: error - array-bracket-spacing: - - error - - never - array-callback-return: error - array-element-newline: 'off' - arrow-body-style: 'off' - arrow-parens: - - error - - as-needed - arrow-spacing: - - error - - after: true - before: true - block-scoped-var: error - block-spacing: error - brace-style: - - error - - 1tbs - callback-return: 'off' - camelcase: error - class-methods-use-this: 'off' - comma-dangle: 'off' - comma-spacing: - - error - - after: true - before: false - comma-style: - - error - - last - complexity: 'off' - computed-property-spacing: - - error - - never - consistent-return: 'off' - consistent-this: 'off' - curly: error - default-case: error - default-case-last: error - default-param-last: error - dot-location: - - error - - property - dot-notation: - - error - - allowKeywords: true - eol-last: error - eqeqeq: error - func-call-spacing: error - func-name-matching: error - func-names: error - func-style: - - error - - declaration - - allowArrowFunctions: true - function-call-argument-newline: - - error - - consistent - function-paren-newline: 'off' - generator-star-spacing: error - global-require: 'off' - grouped-accessor-pairs: 'off' - guard-for-in: error - handle-callback-err: error - id-blacklist: error - id-length: 'off' - id-match: error - implicit-arrow-linebreak: - - error - - beside - indent: - - error - - 4 - indent-legacy: 'off' - init-declarations: error - jsx-quotes: error - key-spacing: error - keyword-spacing: - - error - - after: true - before: true - line-comment-position: 'off' - linebreak-style: - - error - - unix - lines-around-comment: error - lines-around-directive: error - lines-between-class-members: - - error - - always - max-classes-per-file: 'off' - max-depth: error - max-len: 'off' - max-lines: 'off' - max-lines-per-function: 'off' - max-nested-callbacks: error - max-params: 'off' - max-statements: 'off' - max-statements-per-line: error - multiline-comment-style: - - error - - separate-lines - multiline-ternary: 'off' - new-cap: error - new-parens: error - newline-after-var: 'off' - newline-before-return: 'off' - newline-per-chained-call: 'off' - no-alert: 'off' - no-array-constructor: error - no-await-in-loop: error - no-bitwise: 'off' - no-buffer-constructor: 'off' - no-caller: error - no-catch-shadow: error - no-confusing-arrow: error - no-console: error - no-constructor-return: error - no-continue: 'off' - no-div-regex: 'off' - no-duplicate-imports: error - no-else-return: 'off' - no-empty-function: 'off' - no-eq-null: error - no-eval: error - no-extend-native: error - no-extra-bind: error - no-extra-label: error - no-extra-parens: 'off' - no-floating-decimal: error - no-implicit-globals: error - no-implied-eval: error - no-inline-comments: 'off' - no-invalid-this: error - no-iterator: error - no-label-var: error - no-labels: error - no-lone-blocks: error - no-lonely-if: error - no-loop-func: 'off' - no-loss-of-precision: error - no-magic-numbers: 'off' - no-mixed-operators: error - no-mixed-requires: error - no-multi-assign: error - no-multi-spaces: - - error - - ignoreEOLComments: true - no-multi-str: error - no-multiple-empty-lines: error - no-native-reassign: error - no-negated-condition: 'off' - no-negated-in-lhs: error - no-nested-ternary: error - no-new: 'off' - no-new-func: error - no-new-object: error - no-new-require: error - no-new-wrappers: error - no-octal-escape: error - no-param-reassign: 'off' - no-path-concat: error - no-plusplus: 'off' - no-process-env: error - no-process-exit: error - no-proto: error - no-restricted-exports: error - no-restricted-globals: error - no-restricted-imports: error - no-restricted-modules: error - no-restricted-properties: error - no-restricted-syntax: error - no-return-assign: error - no-return-await: error - no-script-url: error - no-self-compare: error - no-sequences: error - no-shadow: 'off' - no-spaced-func: error - no-sync: error - no-tabs: error - no-template-curly-in-string: error - no-ternary: 'off' - no-throw-literal: 'off' - no-trailing-spaces: error - no-undef-init: error - no-undefined: 'off' - no-underscore-dangle: 'off' - no-unmodified-loop-condition: error - no-unneeded-ternary: error - no-unused-expressions: error - no-unused-vars: 'off' - no-use-before-define: 'off' - no-useless-backreference: error - no-useless-call: error - no-useless-computed-key: error - no-useless-concat: error - no-useless-constructor: error - no-useless-escape: 'off' - no-useless-rename: error - no-useless-return: error - no-var: 'off' - no-void: error - no-warning-comments: warn - no-whitespace-before-property: error - nonblock-statement-body-position: error - object-curly-newline: error - object-curly-spacing: - - error - - never - object-shorthand: 'off' - one-var: 'off' - one-var-declaration-per-line: error - operator-assignment: - - error - - always - operator-linebreak: 'off' - padded-blocks: 'off' - padding-line-between-statements: error - prefer-arrow-callback: error - prefer-const: 'off' - prefer-destructuring: 'off' - prefer-exponentiation-operator: 'off' - prefer-named-capture-group: 'off' - prefer-numeric-literals: error - prefer-object-spread: 'off' - prefer-promise-reject-errors: 'off' - prefer-reflect: 'off' - prefer-regex-literals: warn - prefer-rest-params: 'off' - prefer-spread: 'off' - prefer-template: 'off' - quote-props: 'off' - quotes: 'off' - radix: - - error - - as-needed - require-atomic-updates: error - require-await: error - require-jsdoc: 'off' - require-unicode-regexp: 'off' - rest-spread-spacing: error - semi: 'off' - semi-spacing: - - error - - after: true - before: false - semi-style: - - error - - last - sort-imports: error - sort-keys: 'off' - sort-vars: error - space-before-blocks: error - space-before-function-paren: 'off' - space-in-parens: - - error - - never - space-infix-ops: error - space-unary-ops: error - spaced-comment: - - error - - always - strict: error - switch-colon-spacing: error - symbol-description: error - template-curly-spacing: - - error - - never - template-tag-spacing: error - unicode-bom: - - error - - never - valid-jsdoc: error - vars-on-top: error - wrap-iife: error - wrap-regex: error - yield-star-spacing: error - yoda: 'off' diff --git a/client/.prettierrc.yml b/client/.prettierrc.yml new file mode 100644 index 0000000..925bde8 --- /dev/null +++ b/client/.prettierrc.yml @@ -0,0 +1,4 @@ +parser: babel +printWidth: 79 +tabWidth: 4 +quoteProps: consistent diff --git a/client/js/api.js b/client/js/api.js index befe02f..5bde6d8 100644 --- a/client/js/api.js +++ b/client/js/api.js @@ -1,10 +1,10 @@ -'use strict'; +"use strict"; -const cookies = require('js-cookie'); -const request = require('superagent'); -const events = require('./events.js'); -const progress = require('./util/progress.js'); -const uri = require('./util/uri.js'); +const cookies = require("js-cookie"); +const request = require("superagent"); +const events = require("./events.js"); +const progress = require("./util/progress.js"); +const uri = require("./util/uri.js"); let fileTokens = {}; let remoteConfig = null; @@ -18,22 +18,22 @@ class Api extends events.EventTarget { this.token = null; this.cache = {}; this.allRanks = [ - 'anonymous', - 'restricted', - 'regular', - 'power', - 'moderator', - 'administrator', - 'nobody', + "anonymous", + "restricted", + "regular", + "power", + "moderator", + "administrator", + "nobody", ]; this.rankNames = new Map([ - ['anonymous', 'Anonymous'], - ['restricted', 'Restricted user'], - ['regular', 'Regular user'], - ['power', 'Power user'], - ['moderator', 'Moderator'], - ['administrator', 'Administrator'], - ['nobody', 'Nobody'], + ["anonymous", "Anonymous"], + ["restricted", "Restricted user"], + ["regular", "Regular user"], + ["power", "Power user"], + ["moderator", "Moderator"], + ["administrator", "Administrator"], + ["nobody", "Nobody"], ]); } @@ -43,11 +43,12 @@ class Api extends events.EventTarget { resolve(this.cache[url]); }); } - return this._wrappedRequest(url, request.get, {}, {}, options) - .then(response => { + return this._wrappedRequest(url, request.get, {}, {}, options).then( + (response) => { this.cache[url] = response; return Promise.resolve(response); - }); + } + ); } post(url, data, files, options) { @@ -67,10 +68,9 @@ class Api extends events.EventTarget { fetchConfig() { if (remoteConfig === null) { - return this.get(uri.formatApiLink('info')) - .then(response => { - remoteConfig = response.config; - }); + return this.get(uri.formatApiLink("info")).then((response) => { + remoteConfig = response.config; + }); } else { return Promise.resolve(); } @@ -115,7 +115,8 @@ class Api extends events.EventTarget { continue; } const rankIndex = this.allRanks.indexOf( - remoteConfig.privileges[p]); + remoteConfig.privileges[p] + ); if (minViableRank === null || rankIndex < minViableRank) { minViableRank = rankIndex; } @@ -123,17 +124,16 @@ class Api extends events.EventTarget { if (minViableRank === null) { throw `Bad privilege name: ${lookup}`; } - let myRank = this.user !== null ? - this.allRanks.indexOf(this.user.rank) : - 0; + let myRank = + this.user !== null ? this.allRanks.indexOf(this.user.rank) : 0; return myRank >= minViableRank; } loginFromCookies() { - const auth = cookies.getJSON('auth'); - return auth && auth.user && auth.token ? - this.loginWithToken(auth.user, auth.token, true) : - Promise.resolve(); + const auth = cookies.getJSON("auth"); + return auth && auth.user && auth.token + ? this.loginWithToken(auth.user, auth.token, true) + : Promise.resolve(); } loginWithToken(userName, token, doRemember) { @@ -141,63 +141,74 @@ class Api extends events.EventTarget { return new Promise((resolve, reject) => { this.userName = userName; this.token = token; - this.get('/user/' + userName + '?bump-login=true') - .then(response => { + this.get("/user/" + userName + "?bump-login=true").then( + (response) => { const options = {}; if (doRemember) { options.expires = 365; } cookies.set( - 'auth', - {'user': userName, 'token': token}, - options); + "auth", + { user: userName, token: token }, + options + ); this.user = response; resolve(); - this.dispatchEvent(new CustomEvent('login')); - }, error => { + this.dispatchEvent(new CustomEvent("login")); + }, + (error) => { reject(error); this.logout(); - }); + } + ); }); } createToken(userName, options) { let userTokenRequest = { enabled: true, - note: 'Web Login Token' + note: "Web Login Token", }; - if (typeof options.expires !== 'undefined') { - userTokenRequest.expirationTime = new Date().addDays(options.expires).toISOString() + if (typeof options.expires !== "undefined") { + userTokenRequest.expirationTime = new Date() + .addDays(options.expires) + .toISOString(); } return new Promise((resolve, reject) => { - this.post('/user-token/' + userName, userTokenRequest) - .then(response => { + this.post("/user-token/" + userName, userTokenRequest).then( + (response) => { cookies.set( - 'auth', - {'user': userName, 'token': response.token}, - options); + "auth", + { user: userName, token: response.token }, + options + ); this.userName = userName; this.token = response.token; this.userPassword = null; - }, error => { + }, + (error) => { reject(error); - }); + } + ); }); } deleteToken(userName, userToken) { return new Promise((resolve, reject) => { - this.delete('/user-token/' + userName + '/' + userToken, {}) - .then(response => { + this.delete("/user-token/" + userName + "/" + userToken, {}).then( + (response) => { const options = {}; cookies.set( - 'auth', - {'user': userName, 'token': null}, - options); + "auth", + { user: userName, token: null }, + options + ); resolve(); - }, error => { + }, + (error) => { reject(error); - }); + } + ); }); } @@ -206,8 +217,8 @@ class Api extends events.EventTarget { return new Promise((resolve, reject) => { this.userName = userName; this.userPassword = userPassword; - this.get('/user/' + userName + '?bump-login=true') - .then(response => { + this.get("/user/" + userName + "?bump-login=true").then( + (response) => { const options = {}; if (doRemember) { options.expires = 365; @@ -215,22 +226,26 @@ class Api extends events.EventTarget { this.createToken(this.userName, options); this.user = response; resolve(); - this.dispatchEvent(new CustomEvent('login')); - }, error => { + this.dispatchEvent(new CustomEvent("login")); + }, + (error) => { reject(error); this.logout(); - }); + } + ); }); } logout() { let self = this; - this.deleteToken(this.userName, this.token) - .then(response => { + this.deleteToken(this.userName, this.token).then( + (response) => { self._logout(); - }, error => { + }, + (error) => { self._logout(); - }); + } + ); } _logout() { @@ -238,17 +253,19 @@ class Api extends events.EventTarget { this.userName = null; this.userPassword = null; this.token = null; - this.dispatchEvent(new CustomEvent('logout')); + this.dispatchEvent(new CustomEvent("logout")); } forget() { - cookies.remove('auth'); + cookies.remove("auth"); } isLoggedIn(user) { if (user) { - return this.userName !== null && - this.userName.toLowerCase() === user.name.toLowerCase(); + return ( + this.userName !== null && + this.userName.toLowerCase() === user.name.toLowerCase() + ); } else { return this.userName !== null; } @@ -259,8 +276,7 @@ class Api extends events.EventTarget { } _getFullUrl(url) { - const fullUrl = - ('api/' + url).replace(/([^:])\/+/g, '$1/'); + const fullUrl = ("api/" + url).replace(/([^:])\/+/g, "$1/"); const matches = fullUrl.match(/^([^?]*)\??(.*)$/); const baseUrl = matches[1]; const request = matches[2]; @@ -285,7 +301,7 @@ class Api extends events.EventTarget { const file = files[key]; const fileId = this._getFileId(file); if (fileTokens[fileId]) { - data[key + 'Token'] = fileTokens[fileId]; + data[key + "Token"] = fileTokens[fileId]; } else { promise = promise .then(() => { @@ -293,33 +309,40 @@ class Api extends events.EventTarget { abortFunction = () => uploadPromise.abort(); return uploadPromise; }) - .then(token => { + .then((token) => { abortFunction = () => {}; fileTokens[fileId] = token; - data[key + 'Token'] = token; + data[key + "Token"] = token; return Promise.resolve(); }); } } } - promise = promise.then( - () => { + promise = promise + .then(() => { let requestPromise = this._rawRequest( - url, requestFactory, data, {}, options); + url, + requestFactory, + data, + {}, + options + ); abortFunction = () => requestPromise.abort(); return requestPromise; }) - .catch(error => { - if (error.response && error.response.name === - 'MissingOrExpiredRequiredFileError') { + .catch((error) => { + if ( + error.response && + error.response.name === "MissingOrExpiredRequiredFileError" + ) { for (let key of Object.keys(files)) { const file = files[key]; const fileId = this._getFileId(file); fileTokens[fileId] = null; } error.message = - 'The uploaded file has expired; ' + - 'please resend the form to reupload.'; + "The uploaded file has expired; " + + "please resend the form to reupload."; } return Promise.reject(error); }); @@ -331,13 +354,17 @@ class Api extends events.EventTarget { let abortFunction = () => {}; let returnedPromise = new Promise((resolve, reject) => { let uploadPromise = this._rawRequest( - 'uploads', request.post, {}, {content: file}, options); + "uploads", + request.post, + {}, + { content: file }, + options + ); abortFunction = () => uploadPromise.abort(); - return uploadPromise.then( - response => { - abortFunction = () => {}; - return resolve(response.token); - }, reject); + return uploadPromise.then((response) => { + abortFunction = () => {}; + return resolve(response.token); + }, reject); }); returnedPromise.abort = () => abortFunction(); return returnedPromise; @@ -352,7 +379,7 @@ class Api extends events.EventTarget { let returnedPromise = new Promise((resolve, reject) => { let req = requestFactory(fullUrl); - req.set('Accept', 'application/json'); + req.set("Accept", "application/json"); if (query) { req.query(query); @@ -362,7 +389,7 @@ class Api extends events.EventTarget { for (let key of Object.keys(files)) { const value = files[key]; if (value.constructor === String) { - data[key + 'Url'] = value; + data[key + "Url"] = value; } else { req.attach(key, value || new Blob()); } @@ -371,9 +398,9 @@ class Api extends events.EventTarget { if (data) { if (files && Object.keys(files).length) { - req.attach('metadata', new Blob([JSON.stringify(data)])); + req.attach("metadata", new Blob([JSON.stringify(data)])); } else { - req.set('Content-Type', 'application/json'); + req.set("Content-Type", "application/json"); req.send(data); } } @@ -382,19 +409,28 @@ class Api extends events.EventTarget { if (this.userName && this.token) { req.auth = null; // eslint-disable-next-line no-undef - req.set('Authorization', 'Token ' + new Buffer( - this.userName + ":" + this.token).toString('base64')) + req.set( + "Authorization", + "Token " + + new Buffer( + this.userName + ":" + this.token + ).toString("base64") + ); } else if (this.userName && this.userPassword) { req.auth( this.userName, - encodeURIComponent(this.userPassword) - .replace(/%([0-9A-F]{2})/g, (match, p1) => { - return String.fromCharCode('0x' + p1); - })); + encodeURIComponent(this.userPassword).replace( + /%([0-9A-F]{2})/g, + (match, p1) => { + return String.fromCharCode("0x" + p1); + } + ) + ); } } catch (e) { reject( - new Error('Authentication error (malformed credentials)')); + new Error("Authentication error (malformed credentials)") + ); } if (!options.noProgress) { @@ -402,10 +438,11 @@ class Api extends events.EventTarget { } abortFunction = () => { - req.abort(); // does *NOT* call the callback passed in .end() + req.abort(); // does *NOT* call the callback passed in .end() progress.done(); reject( - new Error('The request was aborted due to user cancel.')); + new Error("The request was aborted due to user cancel.") + ); }; req.end((error, response) => { @@ -414,7 +451,8 @@ class Api extends events.EventTarget { if (error) { if (response && response.body) { error = new Error( - response.body.description || 'Unknown error'); + response.body.description || "Unknown error" + ); error.response = response.body; } reject(error); diff --git a/client/js/config.js b/client/js/config.js index 4524be6..da8fb8f 100644 --- a/client/js/config.js +++ b/client/js/config.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; -const config = require('./.config.autogen.json'); +const config = require("./.config.autogen.json"); module.exports = config; diff --git a/client/js/controllers/auth_controller.js b/client/js/controllers/auth_controller.js index a6ec97d..e01fb9b 100644 --- a/client/js/controllers/auth_controller.js +++ b/client/js/controllers/auth_controller.js @@ -1,38 +1,40 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const api = require('../api.js'); -const tags = require('../tags.js'); -const pools = require('../pools.js'); -const uri = require('../util/uri.js'); -const topNavigation = require('../models/top_navigation.js'); -const LoginView = require('../views/login_view.js'); +const router = require("../router.js"); +const api = require("../api.js"); +const tags = require("../tags.js"); +const pools = require("../pools.js"); +const uri = require("../util/uri.js"); +const topNavigation = require("../models/top_navigation.js"); +const LoginView = require("../views/login_view.js"); class LoginController { constructor() { api.forget(); - topNavigation.activate('login'); - topNavigation.setTitle('Login'); + topNavigation.activate("login"); + topNavigation.setTitle("Login"); this._loginView = new LoginView(); - this._loginView.addEventListener('submit', e => this._evtLogin(e)); + this._loginView.addEventListener("submit", (e) => this._evtLogin(e)); } _evtLogin(e) { this._loginView.clearMessages(); this._loginView.disableForm(); api.forget(); - api.login(e.detail.name, e.detail.password, e.detail.remember) - .then(() => { + api.login(e.detail.name, e.detail.password, e.detail.remember).then( + () => { const ctx = router.show(uri.formatClientLink()); - ctx.controller.showSuccess('Logged in'); + ctx.controller.showSuccess("Logged in"); // reload tag category color map, this is required when `tag_categories:list` has a permission other than anonymous tags.refreshCategoryColorMap(); pools.refreshCategoryColorMap(); - }, error => { + }, + (error) => { this._loginView.showError(error.message); this._loginView.enableForm(); - }); + } + ); } } @@ -41,15 +43,15 @@ class LogoutController { api.forget(); api.logout(); const ctx = router.show(uri.formatClientLink()); - ctx.controller.showSuccess('Logged out'); + ctx.controller.showSuccess("Logged out"); } } -module.exports = router => { - router.enter(['login'], (ctx, next) => { +module.exports = (router) => { + router.enter(["login"], (ctx, next) => { ctx.controller = new LoginController(); }); - router.enter(['logout'], (ctx, next) => { + router.enter(["logout"], (ctx, next) => { ctx.controller = new LogoutController(); }); }; diff --git a/client/js/controllers/base_post_controller.js b/client/js/controllers/base_post_controller.js index b86aea7..7a21449 100644 --- a/client/js/controllers/base_post_controller.js +++ b/client/js/controllers/base_post_controller.js @@ -1,19 +1,19 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const topNavigation = require('../models/top_navigation.js'); -const EmptyView = require('../views/empty_view.js'); +const api = require("../api.js"); +const topNavigation = require("../models/top_navigation.js"); +const EmptyView = require("../views/empty_view.js"); class BasePostController { constructor(ctx) { - if (!api.hasPrivilege('posts:view')) { + if (!api.hasPrivilege("posts:view")) { this._view = new EmptyView(); - this._view.showError('You don\'t have privileges to view posts.'); + this._view.showError("You don't have privileges to view posts."); return; } - topNavigation.activate('posts'); - topNavigation.setTitle('Post #' + ctx.parameters.id.toString()); + topNavigation.activate("posts"); + topNavigation.setTitle("Post #" + ctx.parameters.id.toString()); } } diff --git a/client/js/controllers/comments_controller.js b/client/js/controllers/comments_controller.js index 67e44ac..d54059e 100644 --- a/client/js/controllers/comments_controller.js +++ b/client/js/controllers/comments_controller.js @@ -1,51 +1,55 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const PostList = require('../models/post_list.js'); -const topNavigation = require('../models/top_navigation.js'); -const PageController = require('../controllers/page_controller.js'); -const CommentsPageView = require('../views/comments_page_view.js'); -const EmptyView = require('../views/empty_view.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const PostList = require("../models/post_list.js"); +const topNavigation = require("../models/top_navigation.js"); +const PageController = require("../controllers/page_controller.js"); +const CommentsPageView = require("../views/comments_page_view.js"); +const EmptyView = require("../views/empty_view.js"); -const fields = ['id', 'comments', 'commentCount', 'thumbnailUrl']; +const fields = ["id", "comments", "commentCount", "thumbnailUrl"]; class CommentsController { constructor(ctx) { - if (!api.hasPrivilege('comments:list')) { + if (!api.hasPrivilege("comments:list")) { this._view = new EmptyView(); this._view.showError( - 'You don\'t have privileges to view comments.'); + "You don't have privileges to view comments." + ); return; } - topNavigation.activate('comments'); - topNavigation.setTitle('Listing comments'); + topNavigation.activate("comments"); + topNavigation.setTitle("Listing comments"); this._pageController = new PageController(); this._pageController.run({ parameters: ctx.parameters, defaultLimit: 10, getClientUrlForPage: (offset, limit) => { - const parameters = Object.assign( - {}, ctx.parameters, {offset: offset, limit: limit}); - return uri.formatClientLink('comments', parameters); + const parameters = Object.assign({}, ctx.parameters, { + offset: offset, + limit: limit, + }); + return uri.formatClientLink("comments", parameters); }, requestPage: (offset, limit) => { return PostList.search( - 'sort:comment-date comment-count-min:1', + "sort:comment-date comment-count-min:1", offset, limit, - fields); + fields + ); }, - pageRenderer: pageCtx => { + pageRenderer: (pageCtx) => { Object.assign(pageCtx, { - canViewPosts: api.hasPrivilege('posts:view'), + canViewPosts: api.hasPrivilege("posts:view"), }); const view = new CommentsPageView(pageCtx); - view.addEventListener('submit', e => this._evtUpdate(e)); - view.addEventListener('score', e => this._evtScore(e)); - view.addEventListener('delete', e => this._evtDelete(e)); + view.addEventListener("submit", (e) => this._evtUpdate(e)); + view.addEventListener("score", (e) => this._evtScore(e)); + view.addEventListener("delete", (e) => this._evtDelete(e)); return view; }, }); @@ -54,26 +58,27 @@ class CommentsController { _evtUpdate(e) { // TODO: disable form e.detail.comment.text = e.detail.text; - e.detail.comment.save() - .catch(error => { - e.detail.target.showError(error.message); - // TODO: enable form - }); + e.detail.comment.save().catch((error) => { + e.detail.target.showError(error.message); + // TODO: enable form + }); } _evtScore(e) { - e.detail.comment.setScore(e.detail.score) - .catch(error => window.alert(error.message)); + e.detail.comment + .setScore(e.detail.score) + .catch((error) => window.alert(error.message)); } _evtDelete(e) { - e.detail.comment.delete() - .catch(error => window.alert(error.message)); + e.detail.comment + .delete() + .catch((error) => window.alert(error.message)); } } -module.exports = router => { - router.enter(['comments'], (ctx, next) => { +module.exports = (router) => { + router.enter(["comments"], (ctx, next) => { new CommentsController(ctx); }); -} +}; diff --git a/client/js/controllers/help_controller.js b/client/js/controllers/help_controller.js index 8e65346..c290a6b 100644 --- a/client/js/controllers/help_controller.js +++ b/client/js/controllers/help_controller.js @@ -1,24 +1,24 @@ -'use strict'; +"use strict"; -const topNavigation = require('../models/top_navigation.js'); -const HelpView = require('../views/help_view.js'); +const topNavigation = require("../models/top_navigation.js"); +const HelpView = require("../views/help_view.js"); class HelpController { constructor(section, subsection) { - topNavigation.activate('help'); - topNavigation.setTitle('Help'); + topNavigation.activate("help"); + topNavigation.setTitle("Help"); this._helpView = new HelpView(section, subsection); } } -module.exports = router => { - router.enter(['help'], (ctx, next) => { +module.exports = (router) => { + router.enter(["help"], (ctx, next) => { new HelpController(); }); - router.enter(['help', ':section'], (ctx, next) => { + router.enter(["help", ":section"], (ctx, next) => { new HelpController(ctx.parameters.section); }); - router.enter(['help', ':section', ':subsection'], (ctx, next) => { + router.enter(["help", ":section", ":subsection"], (ctx, next) => { new HelpController(ctx.parameters.section, ctx.parameters.subsection); }); }; diff --git a/client/js/controllers/home_controller.js b/client/js/controllers/home_controller.js index 590da67..cf56e27 100644 --- a/client/js/controllers/home_controller.js +++ b/client/js/controllers/home_controller.js @@ -1,26 +1,26 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const config = require('../config.js'); -const Info = require('../models/info.js'); -const topNavigation = require('../models/top_navigation.js'); -const HomeView = require('../views/home_view.js'); +const api = require("../api.js"); +const config = require("../config.js"); +const Info = require("../models/info.js"); +const topNavigation = require("../models/top_navigation.js"); +const HomeView = require("../views/home_view.js"); class HomeController { constructor() { - topNavigation.activate('home'); - topNavigation.setTitle('Home'); + topNavigation.activate("home"); + topNavigation.setTitle("Home"); this._homeView = new HomeView({ name: api.getName(), version: config.meta.version, buildDate: config.meta.buildDate, - canListSnapshots: api.hasPrivilege('snapshots:list'), - canListPosts: api.hasPrivilege('posts:list'), + canListSnapshots: api.hasPrivilege("snapshots:list"), + canListPosts: api.hasPrivilege("posts:list"), }); - Info.get() - .then(info => { + Info.get().then( + (info) => { this._homeView.setStats({ diskUsage: info.diskUsage, postCount: info.postCount, @@ -31,7 +31,8 @@ class HomeController { featuringTime: info.featuringTime, }); }, - error => this._homeView.showError(error.message)); + (error) => this._homeView.showError(error.message) + ); } showSuccess(message) { @@ -43,8 +44,8 @@ class HomeController { } } -module.exports = router => { +module.exports = (router) => { router.enter([], (ctx, next) => { ctx.controller = new HomeController(); }); -} +}; diff --git a/client/js/controllers/not_found_controller.js b/client/js/controllers/not_found_controller.js index 8ceeef0..c99e308 100644 --- a/client/js/controllers/not_found_controller.js +++ b/client/js/controllers/not_found_controller.js @@ -1,18 +1,18 @@ -'use strict'; +"use strict"; -const topNavigation = require('../models/top_navigation.js'); -const NotFoundView = require('../views/not_found_view.js'); +const topNavigation = require("../models/top_navigation.js"); +const NotFoundView = require("../views/not_found_view.js"); class NotFoundController { constructor(path) { - topNavigation.activate(''); - topNavigation.setTitle('Not found'); + topNavigation.activate(""); + topNavigation.setTitle("Not found"); this._notFoundView = new NotFoundView(path); } } -module.exports = router => { +module.exports = (router) => { router.enter(null, (ctx, next) => { ctx.controller = new NotFoundController(ctx.canonicalPath); }); -} +}; diff --git a/client/js/controllers/page_controller.js b/client/js/controllers/page_controller.js index ae55804..d3ee7e9 100644 --- a/client/js/controllers/page_controller.js +++ b/client/js/controllers/page_controller.js @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; -const settings = require('../models/settings.js'); -const EndlessPageView = require('../views/endless_page_view.js'); -const ManualPageView = require('../views/manual_page_view.js'); +const settings = require("../models/settings.js"); +const EndlessPageView = require("../views/endless_page_view.js"); +const ManualPageView = require("../views/manual_page_view.js"); class PageController { constructor(ctx) { diff --git a/client/js/controllers/password_reset_controller.js b/client/js/controllers/password_reset_controller.js index e0a9801..aa66c03 100644 --- a/client/js/controllers/password_reset_controller.js +++ b/client/js/controllers/password_reset_controller.js @@ -1,19 +1,20 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const topNavigation = require('../models/top_navigation.js'); -const PasswordResetView = require('../views/password_reset_view.js'); +const router = require("../router.js"); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const topNavigation = require("../models/top_navigation.js"); +const PasswordResetView = require("../views/password_reset_view.js"); class PasswordResetController { constructor() { - topNavigation.activate('login'); - topNavigation.setTitle('Password reminder'); + topNavigation.activate("login"); + topNavigation.setTitle("Password reminder"); this._passwordResetView = new PasswordResetView(); - this._passwordResetView.addEventListener( - 'submit', e => this._evtReset(e)); + this._passwordResetView.addEventListener("submit", (e) => + this._evtReset(e) + ); } _evtReset(e) { @@ -21,15 +22,20 @@ class PasswordResetController { this._passwordResetView.disableForm(); api.forget(); api.logout(); - api.get(uri.formatApiLink('password-reset', e.detail.userNameOrEmail)) - .then(() => { + api.get( + uri.formatApiLink("password-reset", e.detail.userNameOrEmail) + ).then( + () => { this._passwordResetView.showSuccess( - 'E-mail has been sent. To finish the procedure, ' + - 'please click the link it contains.'); - }, error => { + "E-mail has been sent. To finish the procedure, " + + "please click the link it contains." + ); + }, + (error) => { this._passwordResetView.showError(error.message); this._passwordResetView.enableForm(); - }); + } + ); } } @@ -38,26 +44,30 @@ class PasswordResetFinishController { api.forget(); api.logout(); let password = null; - api.post(uri.formatApiLink('password-reset', name), {token: token}) - .then(response => { + api.post(uri.formatApiLink("password-reset", name), { token: token }) + .then((response) => { password = response.password; return api.login(name, password, false); - }).then(() => { - const ctx = router.show(uri.formatClientLink()); - ctx.controller.showSuccess('New password: ' + password); - }, error => { - const ctx = router.show(uri.formatClientLink()); - ctx.controller.showError(error.message); - }); + }) + .then( + () => { + const ctx = router.show(uri.formatClientLink()); + ctx.controller.showSuccess("New password: " + password); + }, + (error) => { + const ctx = router.show(uri.formatClientLink()); + ctx.controller.showError(error.message); + } + ); } } -module.exports = router => { - router.enter(['password-reset'], (ctx, next) => { +module.exports = (router) => { + router.enter(["password-reset"], (ctx, next) => { ctx.controller = new PasswordResetController(); }); - router.enter(['password-reset', ':descriptor'], (ctx, next) => { - const [name, token] = ctx.parameters.descriptor.split(':', 2); + router.enter(["password-reset", ":descriptor"], (ctx, next) => { + const [name, token] = ctx.parameters.descriptor.split(":", 2); ctx.controller = new PasswordResetFinishController(name, token); }); }; diff --git a/client/js/controllers/pool_categories_controller.js b/client/js/controllers/pool_categories_controller.js index 4c2c3b7..4db725c 100644 --- a/client/js/controllers/pool_categories_controller.js +++ b/client/js/controllers/pool_categories_controller.js @@ -1,57 +1,69 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const pools = require('../pools.js'); -const PoolCategoryList = require('../models/pool_category_list.js'); -const topNavigation = require('../models/top_navigation.js'); -const PoolCategoriesView = require('../views/pool_categories_view.js'); -const EmptyView = require('../views/empty_view.js'); +const api = require("../api.js"); +const pools = require("../pools.js"); +const PoolCategoryList = require("../models/pool_category_list.js"); +const topNavigation = require("../models/top_navigation.js"); +const PoolCategoriesView = require("../views/pool_categories_view.js"); +const EmptyView = require("../views/empty_view.js"); class PoolCategoriesController { constructor() { - if (!api.hasPrivilege('poolCategories:list')) { + if (!api.hasPrivilege("poolCategories:list")) { this._view = new EmptyView(); this._view.showError( - 'You don\'t have privileges to view pool categories.'); + "You don't have privileges to view pool categories." + ); return; } - topNavigation.activate('pools'); - topNavigation.setTitle('Listing pools'); - PoolCategoryList.get().then(response => { - this._poolCategories = response.results; - this._view = new PoolCategoriesView({ - poolCategories: this._poolCategories, - canEditName: api.hasPrivilege('poolCategories:edit:name'), - canEditColor: api.hasPrivilege('poolCategories:edit:color'), - canDelete: api.hasPrivilege('poolCategories:delete'), - canCreate: api.hasPrivilege('poolCategories:create'), - canSetDefault: api.hasPrivilege('poolCategories:setDefault'), - }); - this._view.addEventListener('submit', e => this._evtSubmit(e)); - }, error => { - this._view = new EmptyView(); - this._view.showError(error.message); - }); + topNavigation.activate("pools"); + topNavigation.setTitle("Listing pools"); + PoolCategoryList.get().then( + (response) => { + this._poolCategories = response.results; + this._view = new PoolCategoriesView({ + poolCategories: this._poolCategories, + canEditName: api.hasPrivilege("poolCategories:edit:name"), + canEditColor: api.hasPrivilege( + "poolCategories:edit:color" + ), + canDelete: api.hasPrivilege("poolCategories:delete"), + canCreate: api.hasPrivilege("poolCategories:create"), + canSetDefault: api.hasPrivilege( + "poolCategories:setDefault" + ), + }); + this._view.addEventListener("submit", (e) => + this._evtSubmit(e) + ); + }, + (error) => { + this._view = new EmptyView(); + this._view.showError(error.message); + } + ); } _evtSubmit(e) { this._view.clearMessages(); this._view.disableForm(); - this._poolCategories.save() - .then(() => { + this._poolCategories.save().then( + () => { pools.refreshCategoryColorMap(); this._view.enableForm(); - this._view.showSuccess('Changes saved.'); - }, error => { + this._view.showSuccess("Changes saved."); + }, + (error) => { this._view.enableForm(); this._view.showError(error.message); - }); + } + ); } } -module.exports = router => { - router.enter(['pool-categories'], (ctx, next) => { +module.exports = (router) => { + router.enter(["pool-categories"], (ctx, next) => { ctx.controller = new PoolCategoriesController(ctx, next); }); }; diff --git a/client/js/controllers/pool_controller.js b/client/js/controllers/pool_controller.js index a06ba5c..6844171 100644 --- a/client/js/controllers/pool_controller.js +++ b/client/js/controllers/pool_controller.js @@ -1,63 +1,76 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const api = require('../api.js'); -const misc = require('../util/misc.js'); -const uri = require('../util/uri.js'); -const Pool = require('../models/pool.js'); -const Post = require('../models/post.js'); -const PoolCategoryList = require('../models/pool_category_list.js'); -const topNavigation = require('../models/top_navigation.js'); -const PoolView = require('../views/pool_view.js'); -const EmptyView = require('../views/empty_view.js'); +const router = require("../router.js"); +const api = require("../api.js"); +const misc = require("../util/misc.js"); +const uri = require("../util/uri.js"); +const Pool = require("../models/pool.js"); +const Post = require("../models/post.js"); +const PoolCategoryList = require("../models/pool_category_list.js"); +const topNavigation = require("../models/top_navigation.js"); +const PoolView = require("../views/pool_view.js"); +const EmptyView = require("../views/empty_view.js"); class PoolController { constructor(ctx, section) { - if (!api.hasPrivilege('pools:view')) { + if (!api.hasPrivilege("pools:view")) { this._view = new EmptyView(); - this._view.showError('You don\'t have privileges to view pools.'); + this._view.showError("You don't have privileges to view pools."); return; } Promise.all([ PoolCategoryList.get(), - Pool.get(ctx.parameters.id) - ]).then(responses => { - const [poolCategoriesResponse, pool] = responses; + Pool.get(ctx.parameters.id), + ]).then( + (responses) => { + const [poolCategoriesResponse, pool] = responses; - topNavigation.activate('pools'); - topNavigation.setTitle('Pool #' + pool.names[0]); + topNavigation.activate("pools"); + topNavigation.setTitle("Pool #" + pool.names[0]); - this._name = ctx.parameters.name; - pool.addEventListener('change', e => this._evtSaved(e, section)); + this._name = ctx.parameters.name; + pool.addEventListener("change", (e) => + this._evtSaved(e, section) + ); - const categories = {}; - for (let category of poolCategoriesResponse.results) { - categories[category.name] = category.name; - } + const categories = {}; + for (let category of poolCategoriesResponse.results) { + categories[category.name] = category.name; + } - this._view = new PoolView({ - pool: pool, - section: section, - canEditAnything: api.hasPrivilege('pools:edit'), - canEditNames: api.hasPrivilege('pools:edit:names'), - canEditCategory: api.hasPrivilege('pools:edit:category'), - canEditDescription: api.hasPrivilege('pools:edit:description'), - canEditPosts: api.hasPrivilege('pools:edit:posts'), - canMerge: api.hasPrivilege('pools:merge'), - canDelete: api.hasPrivilege('pools:delete'), - categories: categories, - escapeColons: uri.escapeColons, - }); + this._view = new PoolView({ + pool: pool, + section: section, + canEditAnything: api.hasPrivilege("pools:edit"), + canEditNames: api.hasPrivilege("pools:edit:names"), + canEditCategory: api.hasPrivilege("pools:edit:category"), + canEditDescription: api.hasPrivilege( + "pools:edit:description" + ), + canEditPosts: api.hasPrivilege("pools:edit:posts"), + canMerge: api.hasPrivilege("pools:merge"), + canDelete: api.hasPrivilege("pools:delete"), + categories: categories, + escapeColons: uri.escapeColons, + }); - this._view.addEventListener('change', e => this._evtChange(e)); - this._view.addEventListener('submit', e => this._evtUpdate(e)); - this._view.addEventListener('merge', e => this._evtMerge(e)); - this._view.addEventListener('delete', e => this._evtDelete(e)); - }, error => { - this._view = new EmptyView(); - this._view.showError(error.message); - }); + this._view.addEventListener("change", (e) => + this._evtChange(e) + ); + this._view.addEventListener("submit", (e) => + this._evtUpdate(e) + ); + this._view.addEventListener("merge", (e) => this._evtMerge(e)); + this._view.addEventListener("delete", (e) => + this._evtDelete(e) + ); + }, + (error) => { + this._view = new EmptyView(); + this._view.showError(error.message); + } + ); } _evtChange(e) { @@ -67,7 +80,11 @@ class PoolController { _evtSaved(e, section) { misc.disableExitConfirmation(); if (this._name !== e.detail.pool.names[0]) { - router.replace(uri.formatClientLink('pool', e.detail.pool.id, section), null, false); + router.replace( + uri.formatClientLink("pool", e.detail.pool.id, section), + null, + false + ); } } @@ -86,62 +103,74 @@ class PoolController { if (e.detail.posts !== undefined) { e.detail.pool.posts.clear(); for (let postId of e.detail.posts) { - e.detail.pool.posts.add(Post.fromResponse({id: parseInt(postId)})); + e.detail.pool.posts.add( + Post.fromResponse({ id: parseInt(postId) }) + ); } } - e.detail.pool.save().then(() => { - this._view.showSuccess('Pool saved.'); - this._view.enableForm(); - }, error => { - this._view.showError(error.message); - this._view.enableForm(); - }); + e.detail.pool.save().then( + () => { + this._view.showSuccess("Pool saved."); + this._view.enableForm(); + }, + (error) => { + this._view.showError(error.message); + this._view.enableForm(); + } + ); } _evtMerge(e) { this._view.clearMessages(); this._view.disableForm(); - e.detail.pool - .merge(e.detail.targetPoolId, e.detail.addAlias) - .then(() => { - this._view.showSuccess('Pool merged.'); + e.detail.pool.merge(e.detail.targetPoolId, e.detail.addAlias).then( + () => { + this._view.showSuccess("Pool merged."); this._view.enableForm(); router.replace( uri.formatClientLink( - 'pool', e.detail.targetPoolId, 'merge'), + "pool", + e.detail.targetPoolId, + "merge" + ), null, - false); - }, error => { + false + ); + }, + (error) => { this._view.showError(error.message); this._view.enableForm(); - }); + } + ); } _evtDelete(e) { this._view.clearMessages(); this._view.disableForm(); - e.detail.pool.delete() - .then(() => { - const ctx = router.show(uri.formatClientLink('pools')); - ctx.controller.showSuccess('Pool deleted.'); - }, error => { + e.detail.pool.delete().then( + () => { + const ctx = router.show(uri.formatClientLink("pools")); + ctx.controller.showSuccess("Pool deleted."); + }, + (error) => { this._view.showError(error.message); this._view.enableForm(); - }); + } + ); } } -module.exports = router => { - router.enter(['pool', ':id', 'edit'], (ctx, next) => { - ctx.controller = new PoolController(ctx, 'edit'); +module.exports = (router) => { + router.enter(["pool", ":id", "edit"], (ctx, next) => { + ctx.controller = new PoolController(ctx, "edit"); }); - router.enter(['pool', ':id', 'merge'], (ctx, next) => { - ctx.controller = new PoolController(ctx, 'merge'); + router.enter(["pool", ":id", "merge"], (ctx, next) => { + ctx.controller = new PoolController(ctx, "merge"); }); - router.enter(['pool', ':id', 'delete'], (ctx, next) => { - ctx.controller = new PoolController(ctx, 'delete'); + router.enter(["pool", ":id", "delete"], (ctx, next) => { + ctx.controller = new PoolController(ctx, "delete"); }); - router.enter(['pool', ':id'], (ctx, next) => { - ctx.controller = new PoolController(ctx, 'summary'); + router.enter(["pool", ":id"], (ctx, next) => { + ctx.controller = new PoolController(ctx, "summary"); }); }; diff --git a/client/js/controllers/pool_create_controller.js b/client/js/controllers/pool_create_controller.js index 2b8e7e6..7140aac 100644 --- a/client/js/controllers/pool_create_controller.js +++ b/client/js/controllers/pool_create_controller.js @@ -1,58 +1,65 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const api = require('../api.js'); -const misc = require('../util/misc.js'); -const uri = require('../util/uri.js'); -const PoolCategoryList = require('../models/pool_category_list.js'); -const PoolCreateView = require('../views/pool_create_view.js'); -const EmptyView = require('../views/empty_view.js'); +const router = require("../router.js"); +const api = require("../api.js"); +const misc = require("../util/misc.js"); +const uri = require("../util/uri.js"); +const PoolCategoryList = require("../models/pool_category_list.js"); +const PoolCreateView = require("../views/pool_create_view.js"); +const EmptyView = require("../views/empty_view.js"); class PoolCreateController { constructor(ctx) { - if (!api.hasPrivilege('pools:create')) { + if (!api.hasPrivilege("pools:create")) { this._view = new EmptyView(); - this._view.showError('You don\'t have privileges to create pools.'); + this._view.showError("You don't have privileges to create pools."); return; } - PoolCategoryList.get().then(poolCategoriesResponse => { - const categories = {}; - for (let category of poolCategoriesResponse.results) { - categories[category.name] = category.name; - } + PoolCategoryList.get().then( + (poolCategoriesResponse) => { + const categories = {}; + for (let category of poolCategoriesResponse.results) { + categories[category.name] = category.name; + } - this._view = new PoolCreateView({ - canCreate: api.hasPrivilege('pools:create'), - categories: categories, - escapeColons: uri.escapeColons, - }); + this._view = new PoolCreateView({ + canCreate: api.hasPrivilege("pools:create"), + categories: categories, + escapeColons: uri.escapeColons, + }); - this._view.addEventListener('submit', e => this._evtCreate(e)); - }, error => { - this._view = new EmptyView(); - this._view.showError(error.message); - }); + this._view.addEventListener("submit", (e) => + this._evtCreate(e) + ); + }, + (error) => { + this._view = new EmptyView(); + this._view.showError(error.message); + } + ); } _evtCreate(e) { this._view.clearMessages(); this._view.disableForm(); - api.post(uri.formatApiLink('pool'), e.detail) - .then(() => { + api.post(uri.formatApiLink("pool"), e.detail).then( + () => { this._view.clearMessages(); misc.disableExitConfirmation(); - const ctx = router.show(uri.formatClientLink('pools')); - ctx.controller.showSuccess('Pool created.'); - }, error => { + const ctx = router.show(uri.formatClientLink("pools")); + ctx.controller.showSuccess("Pool created."); + }, + (error) => { this._view.showError(error.message); this._view.enableForm(); - }); + } + ); } } -module.exports = router => { - router.enter(['pool', 'create'], (ctx, next) => { - ctx.controller = new PoolCreateController(ctx, 'create'); +module.exports = (router) => { + router.enter(["pool", "create"], (ctx, next) => { + ctx.controller = new PoolCreateController(ctx, "create"); }); }; diff --git a/client/js/controllers/pool_list_controller.js b/client/js/controllers/pool_list_controller.js index dff649f..91d655c 100644 --- a/client/js/controllers/pool_list_controller.js +++ b/client/js/controllers/pool_list_controller.js @@ -1,47 +1,51 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const PoolList = require('../models/pool_list.js'); -const topNavigation = require('../models/top_navigation.js'); -const PageController = require('../controllers/page_controller.js'); -const PoolsHeaderView = require('../views/pools_header_view.js'); -const PoolsPageView = require('../views/pools_page_view.js'); -const EmptyView = require('../views/empty_view.js'); +const router = require("../router.js"); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const PoolList = require("../models/pool_list.js"); +const topNavigation = require("../models/top_navigation.js"); +const PageController = require("../controllers/page_controller.js"); +const PoolsHeaderView = require("../views/pools_header_view.js"); +const PoolsPageView = require("../views/pools_page_view.js"); +const EmptyView = require("../views/empty_view.js"); const fields = [ - 'id', - 'names', - 'posts', - 'creationTime', - 'postCount', - 'category' + "id", + "names", + "posts", + "creationTime", + "postCount", + "category", ]; class PoolListController { constructor(ctx) { this._pageController = new PageController(); - if (!api.hasPrivilege('pools:list')) { + if (!api.hasPrivilege("pools:list")) { this._view = new EmptyView(); - this._view.showError('You don\'t have privileges to view pools.'); + this._view.showError("You don't have privileges to view pools."); return; } this._ctx = ctx; - topNavigation.activate('pools'); - topNavigation.setTitle('Listing pools'); + topNavigation.activate("pools"); + topNavigation.setTitle("Listing pools"); this._headerView = new PoolsHeaderView({ hostNode: this._pageController.view.pageHeaderHolderNode, parameters: ctx.parameters, - canCreate: api.hasPrivilege('pools:create'), - canEditPoolCategories: api.hasPrivilege('poolCategories:edit'), + canCreate: api.hasPrivilege("pools:create"), + canEditPoolCategories: api.hasPrivilege("poolCategories:edit"), }); this._headerView.addEventListener( - 'submit', e => this._evtSubmit(e), 'navigate', e => this._evtNavigate(e)); + "submit", + (e) => this._evtSubmit(e), + "navigate", + (e) => this._evtNavigate(e) + ); this._syncPageController(); } @@ -57,24 +61,27 @@ class PoolListController { _evtSubmit(e) { this._view.clearMessages(); this._view.disableForm(); - e.detail.pool.save() - .then(() => { - this._installView(e.detail.pool, 'edit'); - this._view.showSuccess('Pool created.'); + e.detail.pool.save().then( + () => { + this._installView(e.detail.pool, "edit"); + this._view.showSuccess("Pool created."); router.replace( - uri.formatClientLink( - 'pool', e.detail.pool.id, 'edit'), + uri.formatClientLink("pool", e.detail.pool.id, "edit"), null, - false); - }, error => { + false + ); + }, + (error) => { this._view.showError(error.message); this._view.enableForm(); - }); + } + ); } _evtNavigate(e) { router.showNoDispatch( - uri.formatClientLink('pools', e.detail.parameters)); + uri.formatClientLink("pools", e.detail.parameters) + ); Object.assign(this._ctx.parameters, e.detail.parameters); this._syncPageController(); } @@ -84,25 +91,29 @@ class PoolListController { parameters: this._ctx.parameters, defaultLimit: 50, getClientUrlForPage: (offset, limit) => { - const parameters = Object.assign( - {}, this._ctx.parameters, {offset: offset, limit: limit}); - return uri.formatClientLink('pools', parameters); + const parameters = Object.assign({}, this._ctx.parameters, { + offset: offset, + limit: limit, + }); + return uri.formatClientLink("pools", parameters); }, requestPage: (offset, limit) => { return PoolList.search( - this._ctx.parameters.query, offset, limit, fields); + this._ctx.parameters.query, + offset, + limit, + fields + ); }, - pageRenderer: pageCtx => { + pageRenderer: (pageCtx) => { return new PoolsPageView(pageCtx); }, }); } } -module.exports = router => { - router.enter( - ['pools'], - (ctx, next) => { - ctx.controller = new PoolListController(ctx); - }); +module.exports = (router) => { + router.enter(["pools"], (ctx, next) => { + ctx.controller = new PoolListController(ctx); + }); }; diff --git a/client/js/controllers/post_detail_controller.js b/client/js/controllers/post_detail_controller.js index bd9c2e6..9bab1ce 100644 --- a/client/js/controllers/post_detail_controller.js +++ b/client/js/controllers/post_detail_controller.js @@ -1,28 +1,33 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const api = require('../api.js'); -const misc = require('../util/misc.js'); -const uri = require('../util/uri.js'); -const settings = require('../models/settings.js'); -const Post = require('../models/post.js'); -const PostList = require('../models/post_list.js'); -const PostDetailView = require('../views/post_detail_view.js'); -const BasePostController = require('./base_post_controller.js'); -const EmptyView = require('../views/empty_view.js'); +const router = require("../router.js"); +const api = require("../api.js"); +const misc = require("../util/misc.js"); +const uri = require("../util/uri.js"); +const settings = require("../models/settings.js"); +const Post = require("../models/post.js"); +const PostList = require("../models/post_list.js"); +const PostDetailView = require("../views/post_detail_view.js"); +const BasePostController = require("./base_post_controller.js"); +const EmptyView = require("../views/empty_view.js"); class PostDetailController extends BasePostController { constructor(ctx, section) { super(ctx); - Post.get(ctx.parameters.id).then(post => { - this._id = ctx.parameters.id; - post.addEventListener('change', e => this._evtSaved(e, section)); - this._installView(post, section); - }, error => { - this._view = new EmptyView(); - this._view.showError(error.message); - }); + Post.get(ctx.parameters.id).then( + (post) => { + this._id = ctx.parameters.id; + post.addEventListener("change", (e) => + this._evtSaved(e, section) + ); + this._installView(post, section); + }, + (error) => { + this._view = new EmptyView(); + this._view.showError(error.message); + } + ); } showSuccess(message) { @@ -33,58 +38,68 @@ class PostDetailController extends BasePostController { this._view = new PostDetailView({ post: post, section: section, - canMerge: api.hasPrivilege('posts:merge'), + canMerge: api.hasPrivilege("posts:merge"), }); - this._view.addEventListener('select', e => this._evtSelect(e)); - this._view.addEventListener('merge', e => this._evtMerge(e)); + this._view.addEventListener("select", (e) => this._evtSelect(e)); + this._view.addEventListener("merge", (e) => this._evtMerge(e)); } _evtSelect(e) { this._view.clearMessages(); this._view.disableForm(); - Post.get(e.detail.postId).then(post => { - this._view.selectPost(post); - this._view.enableForm(); - }, error => { - this._view.showError(error.message); - this._view.enableForm(); - }); + Post.get(e.detail.postId).then( + (post) => { + this._view.selectPost(post); + this._view.enableForm(); + }, + (error) => { + this._view.showError(error.message); + this._view.enableForm(); + } + ); } _evtSaved(e, section) { misc.disableExitConfirmation(); if (this._id !== e.detail.post.id) { router.replace( - uri.formatClientLink('post', e.detail.post.id, section), + uri.formatClientLink("post", e.detail.post.id, section), null, - false); + false + ); } } _evtMerge(e) { this._view.clearMessages(); this._view.disableForm(); - e.detail.post.merge(e.detail.targetPost.id, e.detail.useOldContent) - .then(() => { - this._installView(e.detail.post, 'merge'); - this._view.showSuccess('Post merged.'); - router.replace( - uri.formatClientLink( - 'post', e.detail.targetPost.id, 'merge'), - null, - false); - }, error => { - this._view.showError(error.message); - this._view.enableForm(); - }); + e.detail.post + .merge(e.detail.targetPost.id, e.detail.useOldContent) + .then( + () => { + this._installView(e.detail.post, "merge"); + this._view.showSuccess("Post merged."); + router.replace( + uri.formatClientLink( + "post", + e.detail.targetPost.id, + "merge" + ), + null, + false + ); + }, + (error) => { + this._view.showError(error.message); + this._view.enableForm(); + } + ); } } -module.exports = router => { - router.enter( - ['post', ':id', 'merge'], - (ctx, next) => { - ctx.controller = new PostDetailController(ctx, 'merge'); - }); +module.exports = (router) => { + router.enter(["post", ":id", "merge"], (ctx, next) => { + ctx.controller = new PostDetailController(ctx, "merge"); + }); }; diff --git a/client/js/controllers/post_list_controller.js b/client/js/controllers/post_list_controller.js index 357386c..941ea9d 100644 --- a/client/js/controllers/post_list_controller.js +++ b/client/js/controllers/post_list_controller.js @@ -1,48 +1,56 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const api = require('../api.js'); -const settings = require('../models/settings.js'); -const uri = require('../util/uri.js'); -const PostList = require('../models/post_list.js'); -const topNavigation = require('../models/top_navigation.js'); -const PageController = require('../controllers/page_controller.js'); -const PostsHeaderView = require('../views/posts_header_view.js'); -const PostsPageView = require('../views/posts_page_view.js'); -const EmptyView = require('../views/empty_view.js'); +const router = require("../router.js"); +const api = require("../api.js"); +const settings = require("../models/settings.js"); +const uri = require("../util/uri.js"); +const PostList = require("../models/post_list.js"); +const topNavigation = require("../models/top_navigation.js"); +const PageController = require("../controllers/page_controller.js"); +const PostsHeaderView = require("../views/posts_header_view.js"); +const PostsPageView = require("../views/posts_page_view.js"); +const EmptyView = require("../views/empty_view.js"); const fields = [ - 'id', 'thumbnailUrl', 'type', 'safety', - 'score', 'favoriteCount', 'commentCount', 'tags', 'version' + "id", + "thumbnailUrl", + "type", + "safety", + "score", + "favoriteCount", + "commentCount", + "tags", + "version", ]; class PostListController { constructor(ctx) { this._pageController = new PageController(); - if (!api.hasPrivilege('posts:list')) { + if (!api.hasPrivilege("posts:list")) { this._view = new EmptyView(); - this._view.showError('You don\'t have privileges to view posts.'); + this._view.showError("You don't have privileges to view posts."); return; } this._ctx = ctx; - topNavigation.activate('posts'); - topNavigation.setTitle('Listing posts'); + topNavigation.activate("posts"); + topNavigation.setTitle("Listing posts"); this._headerView = new PostsHeaderView({ hostNode: this._pageController.view.pageHeaderHolderNode, parameters: ctx.parameters, enableSafety: api.safetyEnabled(), - canBulkEditTags: api.hasPrivilege('posts:bulk-edit:tags'), - canBulkEditSafety: api.hasPrivilege('posts:bulk-edit:safety'), + canBulkEditTags: api.hasPrivilege("posts:bulk-edit:tags"), + canBulkEditSafety: api.hasPrivilege("posts:bulk-edit:safety"), bulkEdit: { - tags: this._bulkEditTags + tags: this._bulkEditTags, }, }); - this._headerView.addEventListener( - 'navigate', e => this._evtNavigate(e)); + this._headerView.addEventListener("navigate", (e) => + this._evtNavigate(e) + ); this._syncPageController(); } @@ -52,33 +60,35 @@ class PostListController { } get _bulkEditTags() { - return (this._ctx.parameters.tag || '').split(/\s+/).filter(s => s); + return (this._ctx.parameters.tag || "").split(/\s+/).filter((s) => s); } _evtNavigate(e) { router.showNoDispatch( - uri.formatClientLink('posts', e.detail.parameters)); + uri.formatClientLink("posts", e.detail.parameters) + ); Object.assign(this._ctx.parameters, e.detail.parameters); this._syncPageController(); } _evtTag(e) { Promise.all( - this._bulkEditTags.map(tag => e.detail.post.tags.addByName(tag))) + this._bulkEditTags.map((tag) => e.detail.post.tags.addByName(tag)) + ) .then(e.detail.post.save()) - .catch(error => window.alert(error.message)); + .catch((error) => window.alert(error.message)); } _evtUntag(e) { for (let tag of this._bulkEditTags) { e.detail.post.tags.removeByName(tag); } - e.detail.post.save().catch(error => window.alert(error.message)); + e.detail.post.save().catch((error) => window.alert(error.message)); } _evtChangeSafety(e) { e.detail.post.safety = e.detail.safety; - e.detail.post.save().catch(error => window.alert(error.message)); + e.detail.post.save().catch((error) => window.alert(error.message)); } _syncPageController() { @@ -86,39 +96,45 @@ class PostListController { parameters: this._ctx.parameters, defaultLimit: parseInt(settings.get().postsPerPage), getClientUrlForPage: (offset, limit) => { - const parameters = Object.assign( - {}, this._ctx.parameters, {offset: offset, limit: limit}); - return uri.formatClientLink('posts', parameters); + const parameters = Object.assign({}, this._ctx.parameters, { + offset: offset, + limit: limit, + }); + return uri.formatClientLink("posts", parameters); }, requestPage: (offset, limit) => { return PostList.search( - this._ctx.parameters.query, offset, limit, fields); + this._ctx.parameters.query, + offset, + limit, + fields + ); }, - pageRenderer: pageCtx => { + pageRenderer: (pageCtx) => { Object.assign(pageCtx, { - canViewPosts: api.hasPrivilege('posts:view'), - canBulkEditTags: api.hasPrivilege('posts:bulk-edit:tags'), - canBulkEditSafety: - api.hasPrivilege('posts:bulk-edit:safety'), + canViewPosts: api.hasPrivilege("posts:view"), + canBulkEditTags: api.hasPrivilege("posts:bulk-edit:tags"), + canBulkEditSafety: api.hasPrivilege( + "posts:bulk-edit:safety" + ), bulkEdit: { tags: this._bulkEditTags, }, }); const view = new PostsPageView(pageCtx); - view.addEventListener('tag', e => this._evtTag(e)); - view.addEventListener('untag', e => this._evtUntag(e)); - view.addEventListener( - 'changeSafety', e => this._evtChangeSafety(e)); + view.addEventListener("tag", (e) => this._evtTag(e)); + view.addEventListener("untag", (e) => this._evtUntag(e)); + view.addEventListener("changeSafety", (e) => + this._evtChangeSafety(e) + ); return view; }, }); } } -module.exports = router => { - router.enter( - ['posts'], - (ctx, next) => { - ctx.controller = new PostListController(ctx); - }); +module.exports = (router) => { + router.enter(["posts"], (ctx, next) => { + ctx.controller = new PostListController(ctx); + }); }; diff --git a/client/js/controllers/post_main_controller.js b/client/js/controllers/post_main_controller.js index 924e918..95cfdb5 100644 --- a/client/js/controllers/post_main_controller.js +++ b/client/js/controllers/post_main_controller.js @@ -1,16 +1,16 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const misc = require('../util/misc.js'); -const settings = require('../models/settings.js'); -const Comment = require('../models/comment.js'); -const Post = require('../models/post.js'); -const PostList = require('../models/post_list.js'); -const PostMainView = require('../views/post_main_view.js'); -const BasePostController = require('./base_post_controller.js'); -const EmptyView = require('../views/empty_view.js'); +const router = require("../router.js"); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const misc = require("../util/misc.js"); +const settings = require("../models/settings.js"); +const Comment = require("../models/comment.js"); +const Post = require("../models/post.js"); +const PostList = require("../models/post_list.js"); +const PostMainView = require("../views/post_main_view.js"); +const BasePostController = require("./base_post_controller.js"); +const EmptyView = require("../views/empty_view.js"); class PostMainController extends BasePostController { constructor(ctx, editMode) { @@ -21,74 +21,107 @@ class PostMainController extends BasePostController { Post.get(ctx.parameters.id), PostList.getAround( ctx.parameters.id, - parameters ? parameters.query : null), - ]).then(responses => { - const [post, aroundResponse] = responses; + parameters ? parameters.query : null + ), + ]).then( + (responses) => { + const [post, aroundResponse] = responses; - // remove junk from query, but save it into history so that it can - // be still accessed after history navigation / page refresh - if (parameters.query) { - ctx.state.parameters = parameters; - const url = editMode ? - uri.formatClientLink('post', ctx.parameters.id, 'edit') : - uri.formatClientLink('post', ctx.parameters.id); - router.replace(url, ctx.state, false); - } + // remove junk from query, but save it into history so that it can + // be still accessed after history navigation / page refresh + if (parameters.query) { + ctx.state.parameters = parameters; + const url = editMode + ? uri.formatClientLink( + "post", + ctx.parameters.id, + "edit" + ) + : uri.formatClientLink("post", ctx.parameters.id); + router.replace(url, ctx.state, false); + } - this._post = post; - this._view = new PostMainView({ - post: post, - editMode: editMode, - prevPostId: aroundResponse.prev ? aroundResponse.prev.id : null, - nextPostId: aroundResponse.next ? aroundResponse.next.id : null, - canEditPosts: api.hasPrivilege('posts:edit'), - canDeletePosts: api.hasPrivilege('posts:delete'), - canFeaturePosts: api.hasPrivilege('posts:feature'), - canListComments: api.hasPrivilege('comments:list'), - canCreateComments: api.hasPrivilege('comments:create'), - parameters: parameters, - }); + this._post = post; + this._view = new PostMainView({ + post: post, + editMode: editMode, + prevPostId: aroundResponse.prev + ? aroundResponse.prev.id + : null, + nextPostId: aroundResponse.next + ? aroundResponse.next.id + : null, + canEditPosts: api.hasPrivilege("posts:edit"), + canDeletePosts: api.hasPrivilege("posts:delete"), + canFeaturePosts: api.hasPrivilege("posts:feature"), + canListComments: api.hasPrivilege("comments:list"), + canCreateComments: api.hasPrivilege("comments:create"), + parameters: parameters, + }); - if (this._view.sidebarControl) { - this._view.sidebarControl.addEventListener( - 'favorite', e => this._evtFavoritePost(e)); - this._view.sidebarControl.addEventListener( - 'unfavorite', e => this._evtUnfavoritePost(e)); - this._view.sidebarControl.addEventListener( - 'score', e => this._evtScorePost(e)); - this._view.sidebarControl.addEventListener( - 'fitModeChange', e => this._evtFitModeChange(e)); - this._view.sidebarControl.addEventListener( - 'change', e => this._evtPostChange(e)); - this._view.sidebarControl.addEventListener( - 'submit', e => this._evtUpdatePost(e)); - this._view.sidebarControl.addEventListener( - 'feature', e => this._evtFeaturePost(e)); - this._view.sidebarControl.addEventListener( - 'delete', e => this._evtDeletePost(e)); - this._view.sidebarControl.addEventListener( - 'merge', e => this._evtMergePost(e)); - } + if (this._view.sidebarControl) { + this._view.sidebarControl.addEventListener( + "favorite", + (e) => this._evtFavoritePost(e) + ); + this._view.sidebarControl.addEventListener( + "unfavorite", + (e) => this._evtUnfavoritePost(e) + ); + this._view.sidebarControl.addEventListener("score", (e) => + this._evtScorePost(e) + ); + this._view.sidebarControl.addEventListener( + "fitModeChange", + (e) => this._evtFitModeChange(e) + ); + this._view.sidebarControl.addEventListener("change", (e) => + this._evtPostChange(e) + ); + this._view.sidebarControl.addEventListener("submit", (e) => + this._evtUpdatePost(e) + ); + this._view.sidebarControl.addEventListener( + "feature", + (e) => this._evtFeaturePost(e) + ); + this._view.sidebarControl.addEventListener("delete", (e) => + this._evtDeletePost(e) + ); + this._view.sidebarControl.addEventListener("merge", (e) => + this._evtMergePost(e) + ); + } - if (this._view.commentControl) { - this._view.commentControl.addEventListener( - 'change', e => this._evtCommentChange(e)); - this._view.commentControl.addEventListener( - 'submit', e => this._evtCreateComment(e)); - } + if (this._view.commentControl) { + this._view.commentControl.addEventListener("change", (e) => + this._evtCommentChange(e) + ); + this._view.commentControl.addEventListener("submit", (e) => + this._evtCreateComment(e) + ); + } - if (this._view.commentListControl) { - this._view.commentListControl.addEventListener( - 'submit', e => this._evtUpdateComment(e)); - this._view.commentListControl.addEventListener( - 'score', e => this._evtScoreComment(e)); - this._view.commentListControl.addEventListener( - 'delete', e => this._evtDeleteComment(e)); + if (this._view.commentListControl) { + this._view.commentListControl.addEventListener( + "submit", + (e) => this._evtUpdateComment(e) + ); + this._view.commentListControl.addEventListener( + "score", + (e) => this._evtScoreComment(e) + ); + this._view.commentListControl.addEventListener( + "delete", + (e) => this._evtDeleteComment(e) + ); + } + }, + (error) => { + this._view = new EmptyView(); + this._view.showError(error.message); } - }, error => { - this._view = new EmptyView(); - this._view.showError(error.message); - }); + ); } _evtFitModeChange(e) { @@ -100,32 +133,36 @@ class PostMainController extends BasePostController { _evtFeaturePost(e) { this._view.sidebarControl.disableForm(); this._view.sidebarControl.clearMessages(); - e.detail.post.feature() - .then(() => { - this._view.sidebarControl.showSuccess('Post featured.'); + e.detail.post.feature().then( + () => { + this._view.sidebarControl.showSuccess("Post featured."); this._view.sidebarControl.enableForm(); - }, error => { + }, + (error) => { this._view.sidebarControl.showError(error.message); this._view.sidebarControl.enableForm(); - }); + } + ); } _evtMergePost(e) { - router.show(uri.formatClientLink('post', e.detail.post.id, 'merge')); + router.show(uri.formatClientLink("post", e.detail.post.id, "merge")); } _evtDeletePost(e) { this._view.sidebarControl.disableForm(); this._view.sidebarControl.clearMessages(); - e.detail.post.delete() - .then(() => { + e.detail.post.delete().then( + () => { misc.disableExitConfirmation(); - const ctx = router.show(uri.formatClientLink('posts')); - ctx.controller.showSuccess('Post deleted.'); - }, error => { + const ctx = router.show(uri.formatClientLink("posts")); + ctx.controller.showSuccess("Post deleted."); + }, + (error) => { this._view.sidebarControl.showError(error.message); this._view.sidebarControl.enableForm(); - }); + } + ); } _evtUpdatePost(e) { @@ -150,15 +187,17 @@ class PostMainController extends BasePostController { if (e.detail.source !== undefined) { post.source = e.detail.source; } - post.save() - .then(() => { - this._view.sidebarControl.showSuccess('Post saved.'); + post.save().then( + () => { + this._view.sidebarControl.showSuccess("Post saved."); this._view.sidebarControl.enableForm(); misc.disableExitConfirmation(); - }, error => { + }, + (error) => { this._view.sidebarControl.showError(error.message); this._view.sidebarControl.enableForm(); - }); + } + ); } _evtPostChange(e) { @@ -173,79 +212,82 @@ class PostMainController extends BasePostController { this._view.commentControl.disableForm(); const comment = Comment.create(this._post.id); comment.text = e.detail.text; - comment.save() - .then(() => { + comment.save().then( + () => { this._post.comments.add(comment); this._view.commentControl.exitEditMode(); this._view.commentControl.enableForm(); misc.disableExitConfirmation(); - }, error => { + }, + (error) => { this._view.commentControl.showError(error.message); this._view.commentControl.enableForm(); - }); + } + ); } _evtUpdateComment(e) { // TODO: disable form e.detail.comment.text = e.detail.text; - e.detail.comment.save() - .catch(error => { - e.detail.target.showError(error.message); - // TODO: enable form - }); + e.detail.comment.save().catch((error) => { + e.detail.target.showError(error.message); + // TODO: enable form + }); } _evtScoreComment(e) { - e.detail.comment.setScore(e.detail.score) - .catch(error => window.alert(error.message)); + e.detail.comment + .setScore(e.detail.score) + .catch((error) => window.alert(error.message)); } _evtDeleteComment(e) { - e.detail.comment.delete() - .catch(error => window.alert(error.message)); + e.detail.comment + .delete() + .catch((error) => window.alert(error.message)); } _evtScorePost(e) { - if (!api.hasPrivilege('posts:score')) { + if (!api.hasPrivilege("posts:score")) { return; } - e.detail.post.setScore(e.detail.score) - .catch(error => window.alert(error.message)); + e.detail.post + .setScore(e.detail.score) + .catch((error) => window.alert(error.message)); } _evtFavoritePost(e) { - if (!api.hasPrivilege('posts:favorite')) { + if (!api.hasPrivilege("posts:favorite")) { return; } - e.detail.post.addToFavorites() - .catch(error => window.alert(error.message)); + e.detail.post + .addToFavorites() + .catch((error) => window.alert(error.message)); } _evtUnfavoritePost(e) { - if (!api.hasPrivilege('posts:favorite')) { + if (!api.hasPrivilege("posts:favorite")) { return; } - e.detail.post.removeFromFavorites() - .catch(error => window.alert(error.message)); + e.detail.post + .removeFromFavorites() + .catch((error) => window.alert(error.message)); } } -module.exports = router => { - router.enter(['post', ':id', 'edit'], - (ctx, next) => { - // restore parameters from history state - if (ctx.state.parameters) { - Object.assign(ctx.parameters, ctx.state.parameters); - } - ctx.controller = new PostMainController(ctx, true); - }); - router.enter( - ['post', ':id'], - (ctx, next) => { - // restore parameters from history state - if (ctx.state.parameters) { - Object.assign(ctx.parameters, ctx.state.parameters); - } - ctx.controller = new PostMainController(ctx, false); - }); +module.exports = (router) => { + router.enter(["post", ":id", "edit"], (ctx, next) => { + // restore parameters from history state + if (ctx.state.parameters) { + Object.assign(ctx.parameters, ctx.state.parameters); + } + ctx.controller = new PostMainController(ctx, true); + }); + router.enter(["post", ":id"], (ctx, next) => { + // restore parameters from history state + if (ctx.state.parameters) { + Object.assign(ctx.parameters, ctx.state.parameters); + } + ctx.controller = new PostMainController(ctx, false); + }); }; diff --git a/client/js/controllers/post_upload_controller.js b/client/js/controllers/post_upload_controller.js index f16aaf0..d317be5 100644 --- a/client/js/controllers/post_upload_controller.js +++ b/client/js/controllers/post_upload_controller.js @@ -1,40 +1,40 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const router = require('../router.js'); -const uri = require('../util/uri.js'); -const misc = require('../util/misc.js'); -const progress = require('../util/progress.js'); -const topNavigation = require('../models/top_navigation.js'); -const Post = require('../models/post.js'); -const Tag = require('../models/tag.js'); -const PostUploadView = require('../views/post_upload_view.js'); -const EmptyView = require('../views/empty_view.js'); +const api = require("../api.js"); +const router = require("../router.js"); +const uri = require("../util/uri.js"); +const misc = require("../util/misc.js"); +const progress = require("../util/progress.js"); +const topNavigation = require("../models/top_navigation.js"); +const Post = require("../models/post.js"); +const Tag = require("../models/tag.js"); +const PostUploadView = require("../views/post_upload_view.js"); +const EmptyView = require("../views/empty_view.js"); const genericErrorMessage = - 'One of the posts needs your attention; ' + + "One of the posts needs your attention; " + 'click "resume upload" when you\'re ready.'; class PostUploadController { constructor() { this._lastCancellablePromise = null; - if (!api.hasPrivilege('posts:create')) { + if (!api.hasPrivilege("posts:create")) { this._view = new EmptyView(); - this._view.showError('You don\'t have privileges to upload posts.'); + this._view.showError("You don't have privileges to upload posts."); return; } - topNavigation.activate('upload'); - topNavigation.setTitle('Upload'); + topNavigation.activate("upload"); + topNavigation.setTitle("Upload"); this._view = new PostUploadView({ - canUploadAnonymously: api.hasPrivilege('posts:create:anonymous'), - canViewPosts: api.hasPrivilege('posts:view'), + canUploadAnonymously: api.hasPrivilege("posts:create:anonymous"), + canViewPosts: api.hasPrivilege("posts:view"), enableSafety: api.safetyEnabled(), }); - this._view.addEventListener('change', e => this._evtChange(e)); - this._view.addEventListener('submit', e => this._evtSubmit(e)); - this._view.addEventListener('cancel', e => this._evtCancel(e)); + this._view.addEventListener("change", (e) => this._evtChange(e)); + this._view.addEventListener("submit", (e) => this._evtSubmit(e)); + this._view.addEventListener("cancel", (e) => this._evtCancel(e)); } _evtChange(e) { @@ -56,87 +56,109 @@ class PostUploadController { this._view.disableForm(); this._view.clearMessages(); - e.detail.uploadables.reduce( - (promise, uploadable) => promise.then(() => this._uploadSinglePost( - uploadable, e.detail.skipDuplicates)), - Promise.resolve()) - .then(() => { - this._view.clearMessages(); - misc.disableExitConfirmation(); - const ctx = router.show(uri.formatClientLink('posts')); - ctx.controller.showSuccess('Posts uploaded.'); - }, error => { - if (error.uploadable) { - if (error.similarPosts) { - error.uploadable.lookalikes = error.similarPosts; - this._view.updateUploadable(error.uploadable); - this._view.showInfo(genericErrorMessage); - this._view.showInfo( - error.message, error.uploadable); + e.detail.uploadables + .reduce( + (promise, uploadable) => + promise.then(() => + this._uploadSinglePost( + uploadable, + e.detail.skipDuplicates + ) + ), + Promise.resolve() + ) + .then( + () => { + this._view.clearMessages(); + misc.disableExitConfirmation(); + const ctx = router.show(uri.formatClientLink("posts")); + ctx.controller.showSuccess("Posts uploaded."); + }, + (error) => { + if (error.uploadable) { + if (error.similarPosts) { + error.uploadable.lookalikes = error.similarPosts; + this._view.updateUploadable(error.uploadable); + this._view.showInfo(genericErrorMessage); + this._view.showInfo( + error.message, + error.uploadable + ); + } else { + this._view.showError(genericErrorMessage); + this._view.showError( + error.message, + error.uploadable + ); + } } else { - this._view.showError(genericErrorMessage); - this._view.showError( - error.message, error.uploadable); + this._view.showError(error.message); } - } else { - this._view.showError(error.message); + this._view.enableForm(); } - this._view.enableForm(); - }); + ); } _uploadSinglePost(uploadable, skipDuplicates) { progress.start(); let reverseSearchPromise = Promise.resolve(); if (!uploadable.lookalikesConfirmed) { - reverseSearchPromise = - Post.reverseSearch(uploadable.url || uploadable.file); + reverseSearchPromise = Post.reverseSearch( + uploadable.url || uploadable.file + ); } this._lastCancellablePromise = reverseSearchPromise; - return reverseSearchPromise.then(searchResult => { - if (searchResult) { - // notify about exact duplicate - if (searchResult.exactPost) { - if (skipDuplicates) { - this._view.removeUploadable(uploadable); - return Promise.resolve(); - } else { - let error = new Error('Post already uploaded ' + - `(@${searchResult.exactPost.id})`); + return reverseSearchPromise + .then((searchResult) => { + if (searchResult) { + // notify about exact duplicate + if (searchResult.exactPost) { + if (skipDuplicates) { + this._view.removeUploadable(uploadable); + return Promise.resolve(); + } else { + let error = new Error( + "Post already uploaded " + + `(@${searchResult.exactPost.id})` + ); + error.uploadable = uploadable; + return Promise.reject(error); + } + } + + // notify about similar posts + if (searchResult.similarPosts.length) { + let error = new Error( + `Found ${searchResult.similarPosts.length} similar ` + + "posts.\nYou can resume or discard this upload." + ); error.uploadable = uploadable; + error.similarPosts = searchResult.similarPosts; return Promise.reject(error); } } - // notify about similar posts - if (searchResult.similarPosts.length) { - let error = new Error( - `Found ${searchResult.similarPosts.length} similar ` + - 'posts.\nYou can resume or discard this upload.'); - error.uploadable = uploadable; - error.similarPosts = searchResult.similarPosts; - return Promise.reject(error); - } - } - - // no duplicates, proceed with saving - let post = this._uploadableToPost(uploadable); - let savePromise = post.save(uploadable.anonymous) - .then(() => { + // no duplicates, proceed with saving + let post = this._uploadableToPost(uploadable); + let savePromise = post.save(uploadable.anonymous).then(() => { this._view.removeUploadable(uploadable); return Promise.resolve(); }); - this._lastCancellablePromise = savePromise; - return savePromise; - }).then(result => { - progress.done(); - return Promise.resolve(result); - }, error => { - error.uploadable = uploadable; - progress.done(); - return Promise.reject(error); - }); + this._lastCancellablePromise = savePromise; + return savePromise; + }) + .then( + (result) => { + progress.done(); + return Promise.resolve(result); + }, + (error) => { + error.uploadable = uploadable; + progress.done(); + return Promise.reject(error); + } + ); } _uploadableToPost(uploadable) { @@ -159,8 +181,8 @@ class PostUploadController { } } -module.exports = router => { - router.enter(['upload'], (ctx, next) => { +module.exports = (router) => { + router.enter(["upload"], (ctx, next) => { ctx.controller = new PostUploadController(); }); }; diff --git a/client/js/controllers/settings_controller.js b/client/js/controllers/settings_controller.js index 7765143..8cf2c1c 100644 --- a/client/js/controllers/settings_controller.js +++ b/client/js/controllers/settings_controller.js @@ -1,28 +1,28 @@ -'use strict'; +"use strict"; -const settings = require('../models/settings.js'); -const topNavigation = require('../models/top_navigation.js'); -const SettingsView = require('../views/settings_view.js'); +const settings = require("../models/settings.js"); +const topNavigation = require("../models/top_navigation.js"); +const SettingsView = require("../views/settings_view.js"); class SettingsController { constructor() { - topNavigation.activate('settings'); - topNavigation.setTitle('Browsing settings'); + topNavigation.activate("settings"); + topNavigation.setTitle("Browsing settings"); this._view = new SettingsView({ settings: settings.get(), }); - this._view.addEventListener('submit', e => this._evtSubmit(e)); + this._view.addEventListener("submit", (e) => this._evtSubmit(e)); } _evtSubmit(e) { this._view.clearMessages(); settings.save(e.detail); - this._view.showSuccess('Settings saved.'); + this._view.showSuccess("Settings saved."); } } -module.exports = router => { - router.enter(['settings'], (ctx, next) => { +module.exports = (router) => { + router.enter(["settings"], (ctx, next) => { ctx.controller = new SettingsController(); }); }; diff --git a/client/js/controllers/snapshots_controller.js b/client/js/controllers/snapshots_controller.js index 0e9b773..120b6cb 100644 --- a/client/js/controllers/snapshots_controller.js +++ b/client/js/controllers/snapshots_controller.js @@ -1,41 +1,43 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const SnapshotList = require('../models/snapshot_list.js'); -const PageController = require('../controllers/page_controller.js'); -const topNavigation = require('../models/top_navigation.js'); -const SnapshotsPageView = require('../views/snapshots_page_view.js'); -const EmptyView = require('../views/empty_view.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const SnapshotList = require("../models/snapshot_list.js"); +const PageController = require("../controllers/page_controller.js"); +const topNavigation = require("../models/top_navigation.js"); +const SnapshotsPageView = require("../views/snapshots_page_view.js"); +const EmptyView = require("../views/empty_view.js"); class SnapshotsController { constructor(ctx) { - if (!api.hasPrivilege('snapshots:list')) { + if (!api.hasPrivilege("snapshots:list")) { this._view = new EmptyView(); - this._view.showError('You don\'t have privileges to view history.'); + this._view.showError("You don't have privileges to view history."); return; } - topNavigation.activate(''); - topNavigation.setTitle('History'); + topNavigation.activate(""); + topNavigation.setTitle("History"); this._pageController = new PageController(); this._pageController.run({ parameters: ctx.parameters, defaultLimit: 25, getClientUrlForPage: (offset, limit) => { - const parameters = Object.assign( - {}, ctx.parameters, {offset: offset, limit: limit}); - return uri.formatClientLink('history', parameters); + const parameters = Object.assign({}, ctx.parameters, { + offset: offset, + limit: limit, + }); + return uri.formatClientLink("history", parameters); }, requestPage: (offset, limit) => { - return SnapshotList.search('', offset, limit); + return SnapshotList.search("", offset, limit); }, - pageRenderer: pageCtx => { + pageRenderer: (pageCtx) => { Object.assign(pageCtx, { - canViewPosts: api.hasPrivilege('posts:view'), - canViewUsers: api.hasPrivilege('users:view'), - canViewTags: api.hasPrivilege('tags:view'), + canViewPosts: api.hasPrivilege("posts:view"), + canViewUsers: api.hasPrivilege("users:view"), + canViewTags: api.hasPrivilege("tags:view"), }); return new SnapshotsPageView(pageCtx); }, @@ -43,9 +45,8 @@ class SnapshotsController { } } -module.exports = router => { - router.enter(['history'], - (ctx, next) => { - ctx.controller = new SnapshotsController(ctx); - }); +module.exports = (router) => { + router.enter(["history"], (ctx, next) => { + ctx.controller = new SnapshotsController(ctx); + }); }; diff --git a/client/js/controllers/tag_categories_controller.js b/client/js/controllers/tag_categories_controller.js index 49600cf..2470edb 100644 --- a/client/js/controllers/tag_categories_controller.js +++ b/client/js/controllers/tag_categories_controller.js @@ -1,57 +1,67 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const tags = require('../tags.js'); -const TagCategoryList = require('../models/tag_category_list.js'); -const topNavigation = require('../models/top_navigation.js'); -const TagCategoriesView = require('../views/tag_categories_view.js'); -const EmptyView = require('../views/empty_view.js'); +const api = require("../api.js"); +const tags = require("../tags.js"); +const TagCategoryList = require("../models/tag_category_list.js"); +const topNavigation = require("../models/top_navigation.js"); +const TagCategoriesView = require("../views/tag_categories_view.js"); +const EmptyView = require("../views/empty_view.js"); class TagCategoriesController { constructor() { - if (!api.hasPrivilege('tagCategories:list')) { + if (!api.hasPrivilege("tagCategories:list")) { this._view = new EmptyView(); this._view.showError( - 'You don\'t have privileges to view tag categories.'); + "You don't have privileges to view tag categories." + ); return; } - topNavigation.activate('tags'); - topNavigation.setTitle('Listing tags'); - TagCategoryList.get().then(response => { - this._tagCategories = response.results; - this._view = new TagCategoriesView({ - tagCategories: this._tagCategories, - canEditName: api.hasPrivilege('tagCategories:edit:name'), - canEditColor: api.hasPrivilege('tagCategories:edit:color'), - canDelete: api.hasPrivilege('tagCategories:delete'), - canCreate: api.hasPrivilege('tagCategories:create'), - canSetDefault: api.hasPrivilege('tagCategories:setDefault'), - }); - this._view.addEventListener('submit', e => this._evtSubmit(e)); - }, error => { - this._view = new EmptyView(); - this._view.showError(error.message); - }); + topNavigation.activate("tags"); + topNavigation.setTitle("Listing tags"); + TagCategoryList.get().then( + (response) => { + this._tagCategories = response.results; + this._view = new TagCategoriesView({ + tagCategories: this._tagCategories, + canEditName: api.hasPrivilege("tagCategories:edit:name"), + canEditColor: api.hasPrivilege("tagCategories:edit:color"), + canDelete: api.hasPrivilege("tagCategories:delete"), + canCreate: api.hasPrivilege("tagCategories:create"), + canSetDefault: api.hasPrivilege( + "tagCategories:setDefault" + ), + }); + this._view.addEventListener("submit", (e) => + this._evtSubmit(e) + ); + }, + (error) => { + this._view = new EmptyView(); + this._view.showError(error.message); + } + ); } _evtSubmit(e) { this._view.clearMessages(); this._view.disableForm(); - this._tagCategories.save() - .then(() => { + this._tagCategories.save().then( + () => { tags.refreshCategoryColorMap(); this._view.enableForm(); - this._view.showSuccess('Changes saved.'); - }, error => { + this._view.showSuccess("Changes saved."); + }, + (error) => { this._view.enableForm(); this._view.showError(error.message); - }); + } + ); } } -module.exports = router => { - router.enter(['tag-categories'], (ctx, next) => { +module.exports = (router) => { + router.enter(["tag-categories"], (ctx, next) => { ctx.controller = new TagCategoriesController(ctx, next); }); }; diff --git a/client/js/controllers/tag_controller.js b/client/js/controllers/tag_controller.js index 71405dc..0928b3f 100644 --- a/client/js/controllers/tag_controller.js +++ b/client/js/controllers/tag_controller.js @@ -1,63 +1,80 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const api = require('../api.js'); -const misc = require('../util/misc.js'); -const uri = require('../util/uri.js'); -const Tag = require('../models/tag.js'); -const TagCategoryList = require('../models/tag_category_list.js'); -const topNavigation = require('../models/top_navigation.js'); -const TagView = require('../views/tag_view.js'); -const EmptyView = require('../views/empty_view.js'); +const router = require("../router.js"); +const api = require("../api.js"); +const misc = require("../util/misc.js"); +const uri = require("../util/uri.js"); +const Tag = require("../models/tag.js"); +const TagCategoryList = require("../models/tag_category_list.js"); +const topNavigation = require("../models/top_navigation.js"); +const TagView = require("../views/tag_view.js"); +const EmptyView = require("../views/empty_view.js"); class TagController { constructor(ctx, section) { - if (!api.hasPrivilege('tags:view')) { + if (!api.hasPrivilege("tags:view")) { this._view = new EmptyView(); - this._view.showError('You don\'t have privileges to view tags.'); + this._view.showError("You don't have privileges to view tags."); return; } Promise.all([ TagCategoryList.get(), Tag.get(ctx.parameters.name), - ]).then(responses => { - const [tagCategoriesResponse, tag] = responses; + ]).then( + (responses) => { + const [tagCategoriesResponse, tag] = responses; - topNavigation.activate('tags'); - topNavigation.setTitle('Tag #' + tag.names[0]); + topNavigation.activate("tags"); + topNavigation.setTitle("Tag #" + tag.names[0]); - this._name = ctx.parameters.name; - tag.addEventListener('change', e => this._evtSaved(e, section)); + this._name = ctx.parameters.name; + tag.addEventListener("change", (e) => + this._evtSaved(e, section) + ); - const categories = {}; - for (let category of tagCategoriesResponse.results) { - categories[category.name] = category.name; - } + const categories = {}; + for (let category of tagCategoriesResponse.results) { + categories[category.name] = category.name; + } - this._view = new TagView({ - tag: tag, - section: section, - canEditAnything: api.hasPrivilege('tags:edit'), - canEditNames: api.hasPrivilege('tags:edit:names'), - canEditCategory: api.hasPrivilege('tags:edit:category'), - canEditImplications: api.hasPrivilege('tags:edit:implications'), - canEditSuggestions: api.hasPrivilege('tags:edit:suggestions'), - canEditDescription: api.hasPrivilege('tags:edit:description'), - canMerge: api.hasPrivilege('tags:merge'), - canDelete: api.hasPrivilege('tags:delete'), - categories: categories, - escapeColons: uri.escapeColons, - }); + this._view = new TagView({ + tag: tag, + section: section, + canEditAnything: api.hasPrivilege("tags:edit"), + canEditNames: api.hasPrivilege("tags:edit:names"), + canEditCategory: api.hasPrivilege("tags:edit:category"), + canEditImplications: api.hasPrivilege( + "tags:edit:implications" + ), + canEditSuggestions: api.hasPrivilege( + "tags:edit:suggestions" + ), + canEditDescription: api.hasPrivilege( + "tags:edit:description" + ), + canMerge: api.hasPrivilege("tags:merge"), + canDelete: api.hasPrivilege("tags:delete"), + categories: categories, + escapeColons: uri.escapeColons, + }); - this._view.addEventListener('change', e => this._evtChange(e)); - this._view.addEventListener('submit', e => this._evtUpdate(e)); - this._view.addEventListener('merge', e => this._evtMerge(e)); - this._view.addEventListener('delete', e => this._evtDelete(e)); - }, error => { - this._view = new EmptyView(); - this._view.showError(error.message); - }); + this._view.addEventListener("change", (e) => + this._evtChange(e) + ); + this._view.addEventListener("submit", (e) => + this._evtUpdate(e) + ); + this._view.addEventListener("merge", (e) => this._evtMerge(e)); + this._view.addEventListener("delete", (e) => + this._evtDelete(e) + ); + }, + (error) => { + this._view = new EmptyView(); + this._view.showError(error.message); + } + ); } _evtChange(e) { @@ -68,9 +85,10 @@ class TagController { misc.disableExitConfirmation(); if (this._name !== e.detail.tag.names[0]) { router.replace( - uri.formatClientLink('tag', e.detail.tag.names[0], section), + uri.formatClientLink("tag", e.detail.tag.names[0], section), null, - false); + false + ); } } @@ -86,59 +104,69 @@ class TagController { if (e.detail.description !== undefined) { e.detail.tag.description = e.detail.description; } - e.detail.tag.save().then(() => { - this._view.showSuccess('Tag saved.'); - this._view.enableForm(); - }, error => { - this._view.showError(error.message); - this._view.enableForm(); - }); + e.detail.tag.save().then( + () => { + this._view.showSuccess("Tag saved."); + this._view.enableForm(); + }, + (error) => { + this._view.showError(error.message); + this._view.enableForm(); + } + ); } _evtMerge(e) { this._view.clearMessages(); this._view.disableForm(); - e.detail.tag - .merge(e.detail.targetTagName, e.detail.addAlias) - .then(() => { - this._view.showSuccess('Tag merged.'); + e.detail.tag.merge(e.detail.targetTagName, e.detail.addAlias).then( + () => { + this._view.showSuccess("Tag merged."); this._view.enableForm(); router.replace( uri.formatClientLink( - 'tag', e.detail.targetTagName, 'merge'), + "tag", + e.detail.targetTagName, + "merge" + ), null, - false); - }, error => { + false + ); + }, + (error) => { this._view.showError(error.message); this._view.enableForm(); - }); + } + ); } _evtDelete(e) { this._view.clearMessages(); this._view.disableForm(); - e.detail.tag.delete() - .then(() => { - const ctx = router.show(uri.formatClientLink('tags')); - ctx.controller.showSuccess('Tag deleted.'); - }, error => { + e.detail.tag.delete().then( + () => { + const ctx = router.show(uri.formatClientLink("tags")); + ctx.controller.showSuccess("Tag deleted."); + }, + (error) => { this._view.showError(error.message); this._view.enableForm(); - }); + } + ); } } -module.exports = router => { - router.enter(['tag', ':name', 'edit'], (ctx, next) => { - ctx.controller = new TagController(ctx, 'edit'); +module.exports = (router) => { + router.enter(["tag", ":name", "edit"], (ctx, next) => { + ctx.controller = new TagController(ctx, "edit"); }); - router.enter(['tag', ':name', 'merge'], (ctx, next) => { - ctx.controller = new TagController(ctx, 'merge'); + router.enter(["tag", ":name", "merge"], (ctx, next) => { + ctx.controller = new TagController(ctx, "merge"); }); - router.enter(['tag', ':name', 'delete'], (ctx, next) => { - ctx.controller = new TagController(ctx, 'delete'); + router.enter(["tag", ":name", "delete"], (ctx, next) => { + ctx.controller = new TagController(ctx, "delete"); }); - router.enter(['tag', ':name'], (ctx, next) => { - ctx.controller = new TagController(ctx, 'summary'); + router.enter(["tag", ":name"], (ctx, next) => { + ctx.controller = new TagController(ctx, "summary"); }); }; diff --git a/client/js/controllers/tag_list_controller.js b/client/js/controllers/tag_list_controller.js index 34ff8b2..010c556 100644 --- a/client/js/controllers/tag_list_controller.js +++ b/client/js/controllers/tag_list_controller.js @@ -1,46 +1,47 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const TagList = require('../models/tag_list.js'); -const topNavigation = require('../models/top_navigation.js'); -const PageController = require('../controllers/page_controller.js'); -const TagsHeaderView = require('../views/tags_header_view.js'); -const TagsPageView = require('../views/tags_page_view.js'); -const EmptyView = require('../views/empty_view.js'); +const router = require("../router.js"); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const TagList = require("../models/tag_list.js"); +const topNavigation = require("../models/top_navigation.js"); +const PageController = require("../controllers/page_controller.js"); +const TagsHeaderView = require("../views/tags_header_view.js"); +const TagsPageView = require("../views/tags_page_view.js"); +const EmptyView = require("../views/empty_view.js"); const fields = [ - 'names', - 'suggestions', - 'implications', - 'creationTime', - 'usages', - 'category' + "names", + "suggestions", + "implications", + "creationTime", + "usages", + "category", ]; class TagListController { constructor(ctx) { this._pageController = new PageController(); - if (!api.hasPrivilege('tags:list')) { + if (!api.hasPrivilege("tags:list")) { this._view = new EmptyView(); - this._view.showError('You don\'t have privileges to view tags.'); + this._view.showError("You don't have privileges to view tags."); return; } this._ctx = ctx; - topNavigation.activate('tags'); - topNavigation.setTitle('Listing tags'); + topNavigation.activate("tags"); + topNavigation.setTitle("Listing tags"); this._headerView = new TagsHeaderView({ hostNode: this._pageController.view.pageHeaderHolderNode, parameters: ctx.parameters, - canEditTagCategories: api.hasPrivilege('tagCategories:edit'), + canEditTagCategories: api.hasPrivilege("tagCategories:edit"), }); - this._headerView.addEventListener( - 'navigate', e => this._evtNavigate(e)); + this._headerView.addEventListener("navigate", (e) => + this._evtNavigate(e) + ); this._syncPageController(); } @@ -55,7 +56,8 @@ class TagListController { _evtNavigate(e) { router.showNoDispatch( - uri.formatClientLink('tags', e.detail.parameters)); + uri.formatClientLink("tags", e.detail.parameters) + ); Object.assign(this._ctx.parameters, e.detail.parameters); this._syncPageController(); } @@ -65,25 +67,29 @@ class TagListController { parameters: this._ctx.parameters, defaultLimit: 50, getClientUrlForPage: (offset, limit) => { - const parameters = Object.assign( - {}, this._ctx.parameters, {offset: offset, limit: limit}); - return uri.formatClientLink('tags', parameters); + const parameters = Object.assign({}, this._ctx.parameters, { + offset: offset, + limit: limit, + }); + return uri.formatClientLink("tags", parameters); }, requestPage: (offset, limit) => { return TagList.search( - this._ctx.parameters.query, offset, limit, fields); + this._ctx.parameters.query, + offset, + limit, + fields + ); }, - pageRenderer: pageCtx => { + pageRenderer: (pageCtx) => { return new TagsPageView(pageCtx); }, }); } } -module.exports = router => { - router.enter( - ['tags'], - (ctx, next) => { - ctx.controller = new TagListController(ctx); - }); +module.exports = (router) => { + router.enter(["tags"], (ctx, next) => { + ctx.controller = new TagListController(ctx); + }); }; diff --git a/client/js/controllers/top_navigation_controller.js b/client/js/controllers/top_navigation_controller.js index 9cd70c1..d9bb276 100644 --- a/client/js/controllers/top_navigation_controller.js +++ b/client/js/controllers/top_navigation_controller.js @@ -1,19 +1,20 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const topNavigation = require('../models/top_navigation.js'); -const TopNavigationView = require('../views/top_navigation_view.js'); +const api = require("../api.js"); +const topNavigation = require("../models/top_navigation.js"); +const TopNavigationView = require("../views/top_navigation_view.js"); class TopNavigationController { constructor() { api.fetchConfig().then(() => { this._topNavigationView = new TopNavigationView(); - topNavigation.addEventListener( - 'activate', e => this._evtActivate(e)); + topNavigation.addEventListener("activate", (e) => + this._evtActivate(e) + ); - api.addEventListener('login', e => this._evtAuthChange(e)); - api.addEventListener('logout', e => this._evtAuthChange(e)); + api.addEventListener("login", (e) => this._evtAuthChange(e)); + api.addEventListener("logout", (e) => this._evtAuthChange(e)); this._render(); }); @@ -28,37 +29,38 @@ class TopNavigationController { } _updateNavigationFromPrivileges() { - topNavigation.get('account').url = 'user/' + api.userName; - topNavigation.get('account').imageUrl = - api.user ? api.user.avatarUrl : null; + topNavigation.get("account").url = "user/" + api.userName; + topNavigation.get("account").imageUrl = api.user + ? api.user.avatarUrl + : null; topNavigation.showAll(); - if (!api.hasPrivilege('posts:list')) { - topNavigation.hide('posts'); + if (!api.hasPrivilege("posts:list")) { + topNavigation.hide("posts"); } - if (!api.hasPrivilege('posts:create')) { - topNavigation.hide('upload'); + if (!api.hasPrivilege("posts:create")) { + topNavigation.hide("upload"); } - if (!api.hasPrivilege('comments:list')) { - topNavigation.hide('comments'); + if (!api.hasPrivilege("comments:list")) { + topNavigation.hide("comments"); } - if (!api.hasPrivilege('tags:list')) { - topNavigation.hide('tags'); + if (!api.hasPrivilege("tags:list")) { + topNavigation.hide("tags"); } - if (!api.hasPrivilege('users:list')) { - topNavigation.hide('users'); + if (!api.hasPrivilege("users:list")) { + topNavigation.hide("users"); } if (api.isLoggedIn()) { - if (!api.hasPrivilege('users:create:any')) { - topNavigation.hide('register'); + if (!api.hasPrivilege("users:create:any")) { + topNavigation.hide("register"); } - topNavigation.hide('login'); + topNavigation.hide("login"); } else { - if (!api.hasPrivilege('users:create:self')) { - topNavigation.hide('register'); + if (!api.hasPrivilege("users:create:self")) { + topNavigation.hide("register"); } - topNavigation.hide('account'); - topNavigation.hide('logout'); + topNavigation.hide("account"); + topNavigation.hide("logout"); } } @@ -66,10 +68,11 @@ class TopNavigationController { this._updateNavigationFromPrivileges(); this._topNavigationView.render({ items: topNavigation.getAll(), - name: api.getName() + name: api.getName(), }); this._topNavigationView.activate( - topNavigation.activeItem ? topNavigation.activeItem.key : ''); + topNavigation.activeItem ? topNavigation.activeItem.key : "" + ); } } diff --git a/client/js/controllers/user_controller.js b/client/js/controllers/user_controller.js index e192d87..326736b 100644 --- a/client/js/controllers/user_controller.js +++ b/client/js/controllers/user_controller.js @@ -1,23 +1,25 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const misc = require('../util/misc.js'); -const views = require('../util/views.js'); -const User = require('../models/user.js'); -const UserToken = require('../models/user_token.js'); -const topNavigation = require('../models/top_navigation.js'); -const UserView = require('../views/user_view.js'); -const EmptyView = require('../views/empty_view.js'); +const router = require("../router.js"); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const misc = require("../util/misc.js"); +const views = require("../util/views.js"); +const User = require("../models/user.js"); +const UserToken = require("../models/user_token.js"); +const topNavigation = require("../models/top_navigation.js"); +const UserView = require("../views/user_view.js"); +const EmptyView = require("../views/empty_view.js"); class UserController { constructor(ctx, section) { const userName = ctx.parameters.name; - if (!api.hasPrivilege('users:view') && - !api.isLoggedIn({name: userName})) { + if ( + !api.hasPrivilege("users:view") && + !api.isLoggedIn({ name: userName }) + ) { this._view = new EmptyView(); - this._view.showError('You don\'t have privileges to view users.'); + this._view.showError("You don't have privileges to view users."); return; } @@ -25,100 +27,129 @@ class UserController { this._errorMessages = []; let userTokenPromise = Promise.resolve([]); - if (section === 'list-tokens') { - userTokenPromise = UserToken.get(userName) - .then(userTokens => { - return userTokens.map(token => { - token.isCurrentAuthToken = api.isCurrentAuthToken(token); + if (section === "list-tokens") { + userTokenPromise = UserToken.get(userName).then( + (userTokens) => { + return userTokens.map((token) => { + token.isCurrentAuthToken = api.isCurrentAuthToken( + token + ); return token; }); - }, error => { + }, + (error) => { return []; - }); + } + ); } - topNavigation.setTitle('User ' + userName); - Promise.all([ - userTokenPromise, - User.get(userName) - ]).then(responses => { - const [userTokens, user] = responses; - const isLoggedIn = api.isLoggedIn(user); - const infix = isLoggedIn ? 'self' : 'any'; + topNavigation.setTitle("User " + userName); + Promise.all([userTokenPromise, User.get(userName)]).then( + (responses) => { + const [userTokens, user] = responses; + const isLoggedIn = api.isLoggedIn(user); + const infix = isLoggedIn ? "self" : "any"; - this._name = userName; - user.addEventListener('change', e => this._evtSaved(e, section)); + this._name = userName; + user.addEventListener("change", (e) => + this._evtSaved(e, section) + ); - const myRankIndex = api.user ? - api.allRanks.indexOf(api.user.rank) : - 0; - let ranks = {}; - for (let [rankIdx, rankIdentifier] of api.allRanks.entries()) { - if (rankIdentifier === 'anonymous') { - continue; + const myRankIndex = api.user + ? api.allRanks.indexOf(api.user.rank) + : 0; + let ranks = {}; + for (let [rankIdx, rankIdentifier] of api.allRanks.entries()) { + if (rankIdentifier === "anonymous") { + continue; + } + if (rankIdx > myRankIndex) { + continue; + } + ranks[rankIdentifier] = api.rankNames.get(rankIdentifier); } - if (rankIdx > myRankIndex) { - continue; - } - ranks[rankIdentifier] = api.rankNames.get(rankIdentifier); - } - if (isLoggedIn) { - topNavigation.activate('account'); - } else { - topNavigation.activate('users'); - } + if (isLoggedIn) { + topNavigation.activate("account"); + } else { + topNavigation.activate("users"); + } - this._view = new UserView({ - user: user, - section: section, - isLoggedIn: isLoggedIn, - canEditName: api.hasPrivilege(`users:edit:${infix}:name`), - canEditPassword: api.hasPrivilege(`users:edit:${infix}:pass`), - canEditEmail: api.hasPrivilege(`users:edit:${infix}:email`), - canEditRank: api.hasPrivilege(`users:edit:${infix}:rank`), - canEditAvatar: api.hasPrivilege(`users:edit:${infix}:avatar`), - canEditAnything: api.hasPrivilege(`users:edit:${infix}`), - canListTokens: api.hasPrivilege(`userTokens:list:${infix}`), - canCreateToken: api.hasPrivilege(`userTokens:create:${infix}`), - canEditToken: api.hasPrivilege(`userTokens:edit:${infix}`), - canDeleteToken: api.hasPrivilege(`userTokens:delete:${infix}`), - canDelete: api.hasPrivilege(`users:delete:${infix}`), - ranks: ranks, - tokens: userTokens, - }); - this._view.addEventListener('change', e => this._evtChange(e)); - this._view.addEventListener('submit', e => this._evtUpdate(e)); - this._view.addEventListener('delete', e => this._evtDelete(e)); - this._view.addEventListener('create-token', e => this._evtCreateToken(e)); - this._view.addEventListener('delete-token', e => this._evtDeleteToken(e)); - this._view.addEventListener('update-token', e => this._evtUpdateToken(e)); + this._view = new UserView({ + user: user, + section: section, + isLoggedIn: isLoggedIn, + canEditName: api.hasPrivilege(`users:edit:${infix}:name`), + canEditPassword: api.hasPrivilege( + `users:edit:${infix}:pass` + ), + canEditEmail: api.hasPrivilege( + `users:edit:${infix}:email` + ), + canEditRank: api.hasPrivilege(`users:edit:${infix}:rank`), + canEditAvatar: api.hasPrivilege( + `users:edit:${infix}:avatar` + ), + canEditAnything: api.hasPrivilege(`users:edit:${infix}`), + canListTokens: api.hasPrivilege( + `userTokens:list:${infix}` + ), + canCreateToken: api.hasPrivilege( + `userTokens:create:${infix}` + ), + canEditToken: api.hasPrivilege(`userTokens:edit:${infix}`), + canDeleteToken: api.hasPrivilege( + `userTokens:delete:${infix}` + ), + canDelete: api.hasPrivilege(`users:delete:${infix}`), + ranks: ranks, + tokens: userTokens, + }); + this._view.addEventListener("change", (e) => + this._evtChange(e) + ); + this._view.addEventListener("submit", (e) => + this._evtUpdate(e) + ); + this._view.addEventListener("delete", (e) => + this._evtDelete(e) + ); + this._view.addEventListener("create-token", (e) => + this._evtCreateToken(e) + ); + this._view.addEventListener("delete-token", (e) => + this._evtDeleteToken(e) + ); + this._view.addEventListener("update-token", (e) => + this._evtUpdateToken(e) + ); - for (let message of this._successMessages) { - this.showSuccess(message); - } + for (let message of this._successMessages) { + this.showSuccess(message); + } - for (let message of this._errorMessages) { - this.showError(message); + for (let message of this._errorMessages) { + this.showError(message); + } + }, + (error) => { + this._view = new EmptyView(); + this._view.showError(error.message); } - - }, error => { - this._view = new EmptyView(); - this._view.showError(error.message); - }); + ); } showSuccess(message) { - if (typeof this._view === 'undefined') { - this._successMessages.push(message) + if (typeof this._view === "undefined") { + this._successMessages.push(message); } else { this._view.showSuccess(message); } } showError(message) { - if (typeof this._view === 'undefined') { - this._errorMessages.push(message) + if (typeof this._view === "undefined") { + this._errorMessages.push(message); } else { this._view.showError(message); } @@ -132,9 +163,10 @@ class UserController { misc.disableExitConfirmation(); if (this._name !== e.detail.user.name) { router.replace( - uri.formatClientLink('user', e.detail.user.name, section), + uri.formatClientLink("user", e.detail.user.name, section), null, - false); + false + ); } } @@ -142,7 +174,7 @@ class UserController { this._view.clearMessages(); this._view.disableForm(); const isLoggedIn = api.isLoggedIn(e.detail.user); - const infix = isLoggedIn ? 'self' : 'any'; + const infix = isLoggedIn ? "self" : "any"; if (e.detail.name !== undefined) { e.detail.user.name = e.detail.name; @@ -165,72 +197,105 @@ class UserController { } } - e.detail.user.save().then(() => { - return isLoggedIn ? - api.login( - e.detail.name || api.userName, - e.detail.password || api.userPassword, - false) : - Promise.resolve(); - }).then(() => { - this._view.showSuccess('Settings updated.'); - this._view.enableForm(); - }, error => { - this._view.showError(error.message); - this._view.enableForm(); - }); + e.detail.user + .save() + .then(() => { + return isLoggedIn + ? api.login( + e.detail.name || api.userName, + e.detail.password || api.userPassword, + false + ) + : Promise.resolve(); + }) + .then( + () => { + this._view.showSuccess("Settings updated."); + this._view.enableForm(); + }, + (error) => { + this._view.showError(error.message); + this._view.enableForm(); + } + ); } _evtDelete(e) { this._view.clearMessages(); this._view.disableForm(); const isLoggedIn = api.isLoggedIn(e.detail.user); - e.detail.user.delete() - .then(() => { + e.detail.user.delete().then( + () => { if (isLoggedIn) { api.forget(); api.logout(); } - if (api.hasPrivilege('users:list')) { - const ctx = router.show(uri.formatClientLink('users')); - ctx.controller.showSuccess('Account deleted.'); + if (api.hasPrivilege("users:list")) { + const ctx = router.show(uri.formatClientLink("users")); + ctx.controller.showSuccess("Account deleted."); } else { const ctx = router.show(uri.formatClientLink()); - ctx.controller.showSuccess('Account deleted.'); + ctx.controller.showSuccess("Account deleted."); } - }, error => { + }, + (error) => { this._view.showError(error.message); this._view.enableForm(); - }); + } + ); } _evtCreateToken(e) { this._view.clearMessages(); this._view.disableForm(); - UserToken.create(e.detail.user.name, e.detail.note, e.detail.expirationTime) - .then(response => { - const ctx = router.show(uri.formatClientLink('user', e.detail.user.name, 'list-tokens')); - ctx.controller.showSuccess('Token ' + response.token + ' created.'); - }, error => { + UserToken.create( + e.detail.user.name, + e.detail.note, + e.detail.expirationTime + ).then( + (response) => { + const ctx = router.show( + uri.formatClientLink( + "user", + e.detail.user.name, + "list-tokens" + ) + ); + ctx.controller.showSuccess( + "Token " + response.token + " created." + ); + }, + (error) => { this._view.showError(error.message); this._view.enableForm(); - }); + } + ); } _evtDeleteToken(e) { this._view.clearMessages(); this._view.disableForm(); if (api.isCurrentAuthToken(e.detail.userToken)) { - router.show(uri.formatClientLink('logout')); + router.show(uri.formatClientLink("logout")); } else { - e.detail.userToken.delete(e.detail.user.name) - .then(() => { - const ctx = router.show(uri.formatClientLink('user', e.detail.user.name, 'list-tokens')); - ctx.controller.showSuccess('Token ' + e.detail.userToken.token + ' deleted.'); - }, error => { + e.detail.userToken.delete(e.detail.user.name).then( + () => { + const ctx = router.show( + uri.formatClientLink( + "user", + e.detail.user.name, + "list-tokens" + ) + ); + ctx.controller.showSuccess( + "Token " + e.detail.userToken.token + " deleted." + ); + }, + (error) => { this._view.showError(error.message); this._view.enableForm(); - }); + } + ); } } @@ -242,27 +307,38 @@ class UserController { e.detail.userToken.note = e.detail.note; } - e.detail.userToken.save(e.detail.user.name).then(response => { - const ctx = router.show(uri.formatClientLink('user', e.detail.user.name, 'list-tokens')); - ctx.controller.showSuccess('Token ' + response.token + ' updated.'); - }, error => { - this._view.showError(error.message); - this._view.enableForm(); - }); + e.detail.userToken.save(e.detail.user.name).then( + (response) => { + const ctx = router.show( + uri.formatClientLink( + "user", + e.detail.user.name, + "list-tokens" + ) + ); + ctx.controller.showSuccess( + "Token " + response.token + " updated." + ); + }, + (error) => { + this._view.showError(error.message); + this._view.enableForm(); + } + ); } } -module.exports = router => { - router.enter(['user', ':name'], (ctx, next) => { - ctx.controller = new UserController(ctx, 'summary'); +module.exports = (router) => { + router.enter(["user", ":name"], (ctx, next) => { + ctx.controller = new UserController(ctx, "summary"); }); - router.enter(['user', ':name', 'edit'], (ctx, next) => { - ctx.controller = new UserController(ctx, 'edit'); + router.enter(["user", ":name", "edit"], (ctx, next) => { + ctx.controller = new UserController(ctx, "edit"); }); - router.enter(['user', ':name', 'list-tokens'], (ctx, next) => { - ctx.controller = new UserController(ctx, 'list-tokens'); + router.enter(["user", ":name", "list-tokens"], (ctx, next) => { + ctx.controller = new UserController(ctx, "list-tokens"); }); - router.enter(['user', ':name', 'delete'], (ctx, next) => { - ctx.controller = new UserController(ctx, 'delete'); + router.enter(["user", ":name", "delete"], (ctx, next) => { + ctx.controller = new UserController(ctx, "delete"); }); }; diff --git a/client/js/controllers/user_list_controller.js b/client/js/controllers/user_list_controller.js index 9860de4..d6c4fe5 100644 --- a/client/js/controllers/user_list_controller.js +++ b/client/js/controllers/user_list_controller.js @@ -1,27 +1,27 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const router = require('../router.js'); -const uri = require('../util/uri.js'); -const UserList = require('../models/user_list.js'); -const topNavigation = require('../models/top_navigation.js'); -const PageController = require('../controllers/page_controller.js'); -const UsersHeaderView = require('../views/users_header_view.js'); -const UsersPageView = require('../views/users_page_view.js'); -const EmptyView = require('../views/empty_view.js'); +const api = require("../api.js"); +const router = require("../router.js"); +const uri = require("../util/uri.js"); +const UserList = require("../models/user_list.js"); +const topNavigation = require("../models/top_navigation.js"); +const PageController = require("../controllers/page_controller.js"); +const UsersHeaderView = require("../views/users_header_view.js"); +const UsersPageView = require("../views/users_page_view.js"); +const EmptyView = require("../views/empty_view.js"); class UserListController { constructor(ctx) { this._pageController = new PageController(); - if (!api.hasPrivilege('users:list')) { + if (!api.hasPrivilege("users:list")) { this._view = new EmptyView(); - this._view.showError('You don\'t have privileges to view users.'); + this._view.showError("You don't have privileges to view users."); return; } - topNavigation.activate('users'); - topNavigation.setTitle('Listing users'); + topNavigation.activate("users"); + topNavigation.setTitle("Listing users"); this._ctx = ctx; @@ -29,8 +29,9 @@ class UserListController { hostNode: this._pageController.view.pageHeaderHolderNode, parameters: ctx.parameters, }); - this._headerView.addEventListener( - 'navigate', e => this._evtNavigate(e)); + this._headerView.addEventListener("navigate", (e) => + this._evtNavigate(e) + ); this._syncPageController(); } @@ -41,7 +42,8 @@ class UserListController { _evtNavigate(e) { router.showNoDispatch( - uri.formatClientLink('users', e.detail.parameters)); + uri.formatClientLink("users", e.detail.parameters) + ); Object.assign(this._ctx.parameters, e.detail.parameters); this._syncPageController(); } @@ -51,17 +53,22 @@ class UserListController { parameters: this._ctx.parameters, defaultLimit: 30, getClientUrlForPage: (offset, limit) => { - const parameters = Object.assign( - {}, this._ctx.parameters, {offset: offset, limit: limit}); - return uri.formatClientLink('users', parameters); + const parameters = Object.assign({}, this._ctx.parameters, { + offset: offset, + limit: limit, + }); + return uri.formatClientLink("users", parameters); }, requestPage: (offset, limit) => { return UserList.search( - this._ctx.parameters.query, offset, limit); + this._ctx.parameters.query, + offset, + limit + ); }, - pageRenderer: pageCtx => { + pageRenderer: (pageCtx) => { Object.assign(pageCtx, { - canViewUsers: api.hasPrivilege('users:view'), + canViewUsers: api.hasPrivilege("users:view"), }); return new UsersPageView(pageCtx); }, @@ -69,10 +76,8 @@ class UserListController { } } -module.exports = router => { - router.enter( - ['users'], - (ctx, next) => { - ctx.controller = new UserListController(ctx); - }); +module.exports = (router) => { + router.enter(["users"], (ctx, next) => { + ctx.controller = new UserListController(ctx); + }); }; diff --git a/client/js/controllers/user_registration_controller.js b/client/js/controllers/user_registration_controller.js index 78b9402..89cfd8c 100644 --- a/client/js/controllers/user_registration_controller.js +++ b/client/js/controllers/user_registration_controller.js @@ -1,25 +1,25 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const User = require('../models/user.js'); -const topNavigation = require('../models/top_navigation.js'); -const RegistrationView = require('../views/registration_view.js'); -const EmptyView = require('../views/empty_view.js'); +const router = require("../router.js"); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const User = require("../models/user.js"); +const topNavigation = require("../models/top_navigation.js"); +const RegistrationView = require("../views/registration_view.js"); +const EmptyView = require("../views/empty_view.js"); class UserRegistrationController { constructor() { - if (!api.hasPrivilege('users:create:self')) { + if (!api.hasPrivilege("users:create:self")) { this._view = new EmptyView(); - this._view.showError('Registration is closed.'); + this._view.showError("Registration is closed."); return; } - topNavigation.activate('register'); - topNavigation.setTitle('Registration'); + topNavigation.activate("register"); + topNavigation.setTitle("Registration"); this._view = new RegistrationView(); - this._view.addEventListener('submit', e => this._evtRegister(e)); + this._view.addEventListener("submit", (e) => this._evtRegister(e)); } _evtRegister(e) { @@ -30,30 +30,35 @@ class UserRegistrationController { user.email = e.detail.email; user.password = e.detail.password; const isLoggedIn = api.isLoggedIn(); - user.save().then(() => { - if (isLoggedIn) { - return Promise.resolve(); - } else { - api.forget(); - return api.login(e.detail.name, e.detail.password, false); - } - }).then(() => { - if (isLoggedIn) { - const ctx = router.show(uri.formatClientLink('users')); - ctx.controller.showSuccess('User added!'); - } else { - const ctx = router.show(uri.formatClientLink()); - ctx.controller.showSuccess('Welcome aboard!'); - } - }, error => { - this._view.showError(error.message); - this._view.enableForm(); - }); + user.save() + .then(() => { + if (isLoggedIn) { + return Promise.resolve(); + } else { + api.forget(); + return api.login(e.detail.name, e.detail.password, false); + } + }) + .then( + () => { + if (isLoggedIn) { + const ctx = router.show(uri.formatClientLink("users")); + ctx.controller.showSuccess("User added!"); + } else { + const ctx = router.show(uri.formatClientLink()); + ctx.controller.showSuccess("Welcome aboard!"); + } + }, + (error) => { + this._view.showError(error.message); + this._view.enableForm(); + } + ); } } -module.exports = router => { - router.enter(['register'], (ctx, next) => { +module.exports = (router) => { + router.enter(["register"], (ctx, next) => { new UserRegistrationController(); }); }; diff --git a/client/js/controls/auto_complete_control.js b/client/js/controls/auto_complete_control.js index a802209..6e9bf3d 100644 --- a/client/js/controls/auto_complete_control.js +++ b/client/js/controls/auto_complete_control.js @@ -1,6 +1,6 @@ -'use strict'; +"use strict"; -const views = require('../util/views.js'); +const views = require("../util/views.js"); const KEY_TAB = 9; const KEY_RETURN = 13; @@ -10,14 +10,14 @@ const KEY_UP = 38; const KEY_DOWN = 40; function _getSelectionStart(input) { - if ('selectionStart' in input) { + if ("selectionStart" in input) { return input.selectionStart; } if (document.selection) { input.focus(); const sel = document.selection.createRange(); const selLen = document.selection.createRange().text.length; - sel.moveStart('character', -input.value.length); + sel.moveStart("character", -input.value.length); return sel.text.length - selLen; } return 0; @@ -27,18 +27,22 @@ class AutoCompleteControl { constructor(sourceInputNode, options) { this._sourceInputNode = sourceInputNode; this._options = {}; - Object.assign(this._options, { - verticalShift: 2, - maxResults: 15, - getTextToFind: () => { - const value = sourceInputNode.value; - const start = _getSelectionStart(sourceInputNode); - return value.substring(0, start).replace(/.*\s+/, ''); + Object.assign( + this._options, + { + verticalShift: 2, + maxResults: 15, + getTextToFind: () => { + const value = sourceInputNode.value; + const start = _getSelectionStart(sourceInputNode); + return value.substring(0, start).replace(/.*\s+/, ""); + }, + confirm: null, + delete: null, + getMatches: null, }, - confirm: null, - delete: null, - getMatches: null, - }, options); + options + ); this._showTimeout = null; this._results = []; @@ -49,22 +53,22 @@ class AutoCompleteControl { hide() { window.clearTimeout(this._showTimeout); - this._suggestionDiv.style.display = 'none'; + this._suggestionDiv.style.display = "none"; this._isVisible = false; } replaceSelectedText(result, addSpace) { const start = _getSelectionStart(this._sourceInputNode); - let prefix = ''; + let prefix = ""; let suffix = this._sourceInputNode.value.substring(start); let middle = this._sourceInputNode.value.substring(0, start); - const index = middle.lastIndexOf(' '); + const index = middle.lastIndexOf(" "); if (index !== -1) { prefix = this._sourceInputNode.value.substring(0, index + 1); middle = this._sourceInputNode.value.substring(index + 1); } - this._sourceInputNode.value = ( - prefix + result.toString() + ' ' + suffix.trimLeft()); + this._sourceInputNode.value = + prefix + result.toString() + " " + suffix.trimLeft(); if (!addSpace) { this._sourceInputNode.value = this._sourceInputNode.value.trim(); } @@ -86,7 +90,7 @@ class AutoCompleteControl { } _show() { - this._suggestionDiv.style.display = 'block'; + this._suggestionDiv.style.display = "block"; this._isVisible = true; } @@ -101,29 +105,32 @@ class AutoCompleteControl { _install() { if (!this._sourceInputNode) { - throw new Error('Input element was not found'); + throw new Error("Input element was not found"); } - if (this._sourceInputNode.getAttribute('data-autocomplete')) { + if (this._sourceInputNode.getAttribute("data-autocomplete")) { throw new Error( - 'Autocompletion was already added for this element'); + "Autocompletion was already added for this element" + ); } - this._sourceInputNode.setAttribute('data-autocomplete', true); - this._sourceInputNode.setAttribute('autocomplete', 'off'); + this._sourceInputNode.setAttribute("data-autocomplete", true); + this._sourceInputNode.setAttribute("autocomplete", "off"); - this._sourceInputNode.addEventListener( - 'keydown', e => this._evtKeyDown(e)); - this._sourceInputNode.addEventListener( - 'blur', e => this._evtBlur(e)); + this._sourceInputNode.addEventListener("keydown", (e) => + this._evtKeyDown(e) + ); + this._sourceInputNode.addEventListener("blur", (e) => + this._evtBlur(e) + ); this._suggestionDiv = views.htmlToDom( - '<div class="autocomplete"><ul></ul></div>'); - this._suggestionList = this._suggestionDiv.querySelector('ul'); + '<div class="autocomplete"><ul></ul></div>' + ); + this._suggestionList = this._suggestionDiv.querySelector("ul"); document.body.appendChild(this._suggestionDiv); - views.monitorNodeRemoval( - this._sourceInputNode, () => { - this._uninstall(); - }); + views.monitorNodeRemoval(this._sourceInputNode, () => { + this._uninstall(); + }); } _uninstall() { @@ -174,10 +181,9 @@ class AutoCompleteControl { func(); } else { window.clearTimeout(this._showTimeout); - this._showTimeout = window.setTimeout( - () => { - this._showOrHide(); - }, 250); + this._showTimeout = window.setTimeout(() => { + this._showOrHide(); + }, 250); } } @@ -196,9 +202,11 @@ class AutoCompleteControl { } _selectPrevious() { - this._select(this._activeResult === -1 ? - this._results.length - 1 : - this._activeResult - 1); + this._select( + this._activeResult === -1 + ? this._results.length - 1 + : this._activeResult - 1 + ); } _selectNext() { @@ -206,15 +214,18 @@ class AutoCompleteControl { } _select(newActiveResult) { - this._activeResult = - newActiveResult.between(0, this._results.length - 1, true) ? - newActiveResult : - -1; + this._activeResult = newActiveResult.between( + 0, + this._results.length - 1, + true + ) + ? newActiveResult + : -1; this._refreshActiveResult(); } _updateResults(textToFind) { - this._options.getMatches(textToFind).then(matches => { + this._options.getMatches(textToFind).then((matches) => { const oldResults = this._results.slice(); this._results = matches.slice(0, this._options.maxResults); const oldResultsHash = JSON.stringify(oldResults); @@ -237,34 +248,30 @@ class AutoCompleteControl { } for (let [resultIndex, resultItem] of this._results.entries()) { let resultIndexWorkaround = resultIndex; - const listItem = document.createElement('li'); - const link = document.createElement('a'); + const listItem = document.createElement("li"); + const link = document.createElement("a"); link.innerHTML = resultItem.caption; - link.setAttribute('href', ''); - link.setAttribute('data-key', resultItem.value); - link.addEventListener( - 'mouseenter', - e => { - e.preventDefault(); - this._activeResult = resultIndexWorkaround; - this._refreshActiveResult(); - }); - link.addEventListener( - 'mousedown', - e => { - e.preventDefault(); - this._activeResult = resultIndexWorkaround; - this._confirm(this._getActiveSuggestion()); - this.hide(); - }); + link.setAttribute("href", ""); + link.setAttribute("data-key", resultItem.value); + link.addEventListener("mouseenter", (e) => { + e.preventDefault(); + this._activeResult = resultIndexWorkaround; + this._refreshActiveResult(); + }); + link.addEventListener("mousedown", (e) => { + e.preventDefault(); + this._activeResult = resultIndexWorkaround; + this._confirm(this._getActiveSuggestion()); + this.hide(); + }); listItem.appendChild(link); this._suggestionList.appendChild(listItem); } this._refreshActiveResult(); // display the suggestions offscreen to get the height - this._suggestionDiv.style.left = '-9999px'; - this._suggestionDiv.style.top = '-9999px'; + this._suggestionDiv.style.left = "-9999px"; + this._suggestionDiv.style.top = "-9999px"; this._show(); const verticalShift = this._options.verticalShift; const inputRect = this._sourceInputNode.getBoundingClientRect(); @@ -275,17 +282,23 @@ class AutoCompleteControl { // choose where to view the suggestions: if there's more space above // the input - draw the suggestions above it, otherwise below const direction = - inputRect.top + (inputRect.height / 2) < viewPortHeight / 2 ? 1 : -1; + inputRect.top + inputRect.height / 2 < viewPortHeight / 2 ? 1 : -1; let x = inputRect.left - bodyRect.left; - let y = direction === 1 ? - inputRect.bottom - bodyRect.top - verticalShift : - inputRect.top - bodyRect.top - listRect.height + verticalShift; + let y = + direction === 1 + ? inputRect.bottom - bodyRect.top - verticalShift + : inputRect.top - + bodyRect.top - + listRect.height + + verticalShift; // remove offscreen items until whole suggestion list can fit on the // screen - while ((y < 0 || y + listRect.height > viewPortHeight) && - this._suggestionList.childNodes.length) { + while ( + (y < 0 || y + listRect.height > viewPortHeight) && + this._suggestionList.childNodes.length + ) { this._suggestionList.removeChild(this._suggestionList.lastChild); const prevHeight = listRect.height; listRect = this._suggestionDiv.getBoundingClientRect(); @@ -295,19 +308,19 @@ class AutoCompleteControl { } } - this._suggestionDiv.style.left = x + 'px'; - this._suggestionDiv.style.top = y + 'px'; + this._suggestionDiv.style.left = x + "px"; + this._suggestionDiv.style.top = y + "px"; } _refreshActiveResult() { - let activeItem = this._suggestionList.querySelector('li.active'); + let activeItem = this._suggestionList.querySelector("li.active"); if (activeItem) { - activeItem.classList.remove('active'); + activeItem.classList.remove("active"); } if (this._activeResult >= 0) { - const allItems = this._suggestionList.querySelectorAll('li'); + const allItems = this._suggestionList.querySelectorAll("li"); activeItem = allItems[this._activeResult]; - activeItem.classList.add('active'); + activeItem.classList.add("active"); } } } diff --git a/client/js/controls/comment_control.js b/client/js/controls/comment_control.js index c105bc0..cdcfd53 100644 --- a/client/js/controls/comment_control.js +++ b/client/js/controls/comment_control.js @@ -1,12 +1,12 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const misc = require('../util/misc.js'); -const events = require('../events.js'); -const views = require('../util/views.js'); +const api = require("../api.js"); +const misc = require("../util/misc.js"); +const events = require("../events.js"); +const views = require("../util/views.js"); -const template = views.getTemplate('comment'); -const scoreTemplate = views.getTemplate('score'); +const template = views.getTemplate("comment"); +const scoreTemplate = views.getTemplate("score"); class CommentControl extends events.EventTarget { constructor(hostNode, comment, onlyEditing) { @@ -16,104 +16,111 @@ class CommentControl extends events.EventTarget { this._onlyEditing = onlyEditing; if (comment) { - comment.addEventListener( - 'change', e => this._evtChange(e)); - comment.addEventListener( - 'changeScore', e => this._evtChangeScore(e)); + comment.addEventListener("change", (e) => this._evtChange(e)); + comment.addEventListener("changeScore", (e) => + this._evtChangeScore(e) + ); } const isLoggedIn = comment && api.isLoggedIn(comment.user); - const infix = isLoggedIn ? 'own' : 'any'; - views.replaceContent(this._hostNode, template({ - comment: comment, - user: comment ? comment.user : api.user, - canViewUsers: api.hasPrivilege('users:view'), - canEditComment: api.hasPrivilege(`comments:edit:${infix}`), - canDeleteComment: api.hasPrivilege(`comments:delete:${infix}`), - onlyEditing: onlyEditing, - })); + const infix = isLoggedIn ? "own" : "any"; + views.replaceContent( + this._hostNode, + template({ + comment: comment, + user: comment ? comment.user : api.user, + canViewUsers: api.hasPrivilege("users:view"), + canEditComment: api.hasPrivilege(`comments:edit:${infix}`), + canDeleteComment: api.hasPrivilege(`comments:delete:${infix}`), + onlyEditing: onlyEditing, + }) + ); if (this._editButtonNodes) { for (let node of this._editButtonNodes) { - node.addEventListener('click', e => this._evtEditClick(e)); + node.addEventListener("click", (e) => this._evtEditClick(e)); } } if (this._deleteButtonNode) { - this._deleteButtonNode.addEventListener( - 'click', e => this._evtDeleteClick(e)); + this._deleteButtonNode.addEventListener("click", (e) => + this._evtDeleteClick(e) + ); } if (this._previewEditingButtonNode) { - this._previewEditingButtonNode.addEventListener( - 'click', e => this._evtPreviewEditingClick(e)); + this._previewEditingButtonNode.addEventListener("click", (e) => + this._evtPreviewEditingClick(e) + ); } if (this._saveChangesButtonNode) { - this._saveChangesButtonNode.addEventListener( - 'click', e => this._evtSaveChangesClick(e)); + this._saveChangesButtonNode.addEventListener("click", (e) => + this._evtSaveChangesClick(e) + ); } if (this._cancelEditingButtonNode) { - this._cancelEditingButtonNode.addEventListener( - 'click', e => this._evtCancelEditingClick(e)); + this._cancelEditingButtonNode.addEventListener("click", (e) => + this._evtCancelEditingClick(e) + ); } this._installScore(); if (onlyEditing) { - this._selectNav('edit'); - this._selectTab('edit'); + this._selectNav("edit"); + this._selectTab("edit"); } else { - this._selectNav('readonly'); - this._selectTab('preview'); + this._selectNav("readonly"); + this._selectTab("preview"); } } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _scoreContainerNode() { - return this._hostNode.querySelector('.score-container'); + return this._hostNode.querySelector(".score-container"); } get _editButtonNodes() { - return this._hostNode.querySelectorAll('li.edit>a, a.edit'); + return this._hostNode.querySelectorAll("li.edit>a, a.edit"); } get _previewEditingButtonNode() { - return this._hostNode.querySelector('li.preview>a'); + return this._hostNode.querySelector("li.preview>a"); } get _deleteButtonNode() { - return this._hostNode.querySelector('.delete'); + return this._hostNode.querySelector(".delete"); } get _upvoteButtonNode() { - return this._hostNode.querySelector('.upvote'); + return this._hostNode.querySelector(".upvote"); } get _downvoteButtonNode() { - return this._hostNode.querySelector('.downvote'); + return this._hostNode.querySelector(".downvote"); } get _saveChangesButtonNode() { - return this._hostNode.querySelector('.save-changes'); + return this._hostNode.querySelector(".save-changes"); } get _cancelEditingButtonNode() { - return this._hostNode.querySelector('.cancel-editing'); + return this._hostNode.querySelector(".cancel-editing"); } get _textareaNode() { - return this._hostNode.querySelector('.tab.edit textarea'); + return this._hostNode.querySelector(".tab.edit textarea"); } get _contentNode() { - return this._hostNode.querySelector('.tab.preview .comment-content'); + return this._hostNode.querySelector(".tab.preview .comment-content"); } get _heightKeeperNode() { - return this._hostNode.querySelector('.keep-height'); + return this._hostNode.querySelector(".keep-height"); } _installScore() { @@ -122,32 +129,35 @@ class CommentControl extends events.EventTarget { scoreTemplate({ score: this._comment ? this._comment.score : 0, ownScore: this._comment ? this._comment.ownScore : 0, - canScore: api.hasPrivilege('comments:score'), - })); + canScore: api.hasPrivilege("comments:score"), + }) + ); if (this._upvoteButtonNode) { - this._upvoteButtonNode.addEventListener( - 'click', e => this._evtScoreClick(e, 1)); + this._upvoteButtonNode.addEventListener("click", (e) => + this._evtScoreClick(e, 1) + ); } if (this._downvoteButtonNode) { - this._downvoteButtonNode.addEventListener( - 'click', e => this._evtScoreClick(e, -1)); + this._downvoteButtonNode.addEventListener("click", (e) => + this._evtScoreClick(e, -1) + ); } } enterEditMode() { - this._selectNav('edit'); - this._selectTab('edit'); + this._selectNav("edit"); + this._selectTab("edit"); } exitEditMode() { if (this._onlyEditing) { - this._selectNav('edit'); - this._selectTab('edit'); - this._setText(''); + this._selectNav("edit"); + this._selectTab("edit"); + this._setText(""); } else { - this._selectNav('readonly'); - this._selectTab('preview'); + this._selectNav("readonly"); + this._selectTab("preview"); this._setText(this._comment.text); } this._forgetHeight(); @@ -173,27 +183,31 @@ class CommentControl extends events.EventTarget { _evtScoreClick(e, score) { e.preventDefault(); - if (!api.hasPrivilege('comments:score')) { + if (!api.hasPrivilege("comments:score")) { return; } - this.dispatchEvent(new CustomEvent('score', { - detail: { - comment: this._comment, - score: this._comment.ownScore === score ? 0 : score, - }, - })); + this.dispatchEvent( + new CustomEvent("score", { + detail: { + comment: this._comment, + score: this._comment.ownScore === score ? 0 : score, + }, + }) + ); } _evtDeleteClick(e) { e.preventDefault(); - if (!window.confirm('Are you sure you want to delete this comment?')) { + if (!window.confirm("Are you sure you want to delete this comment?")) { return; } - this.dispatchEvent(new CustomEvent('delete', { - detail: { - comment: this._comment, - }, - })); + this.dispatchEvent( + new CustomEvent("delete", { + detail: { + comment: this._comment, + }, + }) + ); } _evtChange(e) { @@ -206,21 +220,24 @@ class CommentControl extends events.EventTarget { _evtPreviewEditingClick(e) { e.preventDefault(); - this._contentNode.innerHTML = - misc.formatMarkdown(this._textareaNode.value); - this._selectTab('edit'); - this._selectTab('preview'); + this._contentNode.innerHTML = misc.formatMarkdown( + this._textareaNode.value + ); + this._selectTab("edit"); + this._selectTab("preview"); } _evtSaveChangesClick(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - target: this, - comment: this._comment, - text: this._textareaNode.value, - }, - })); + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + target: this, + comment: this._comment, + text: this._textareaNode.value, + }, + }) + ); } _evtCancelEditingClick(e) { @@ -234,22 +251,22 @@ class CommentControl extends events.EventTarget { } _selectNav(modeName) { - for (let node of this._hostNode.querySelectorAll('nav')) { - node.classList.toggle('active', node.classList.contains(modeName)); + for (let node of this._hostNode.querySelectorAll("nav")) { + node.classList.toggle("active", node.classList.contains(modeName)); } } _selectTab(tabName) { this._ensureHeight(); - for (let node of this._hostNode.querySelectorAll('.tab, .tabs li')) { - node.classList.toggle('active', node.classList.contains(tabName)); + for (let node of this._hostNode.querySelectorAll(".tab, .tabs li")) { + node.classList.toggle("active", node.classList.contains(tabName)); } } _ensureHeight() { this._heightKeeperNode.style.minHeight = - this._heightKeeperNode.getBoundingClientRect().height + 'px'; + this._heightKeeperNode.getBoundingClientRect().height + "px"; } _forgetHeight() { diff --git a/client/js/controls/comment_list_control.js b/client/js/controls/comment_list_control.js index 4f57fd2..7717ee5 100644 --- a/client/js/controls/comment_list_control.js +++ b/client/js/controls/comment_list_control.js @@ -1,10 +1,10 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); -const CommentControl = require('../controls/comment_control.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); +const CommentControl = require("../controls/comment_control.js"); -const template = views.getTemplate('comment-list'); +const template = views.getTemplate("comment-list"); class CommentListControl extends events.EventTarget { constructor(hostNode, comments, reversed) { @@ -13,8 +13,8 @@ class CommentListControl extends events.EventTarget { this._comments = comments; this._commentIdToNode = {}; - comments.addEventListener('add', e => this._evtAdd(e)); - comments.addEventListener('remove', e => this._evtRemove(e)); + comments.addEventListener("add", (e) => this._evtAdd(e)); + comments.addEventListener("remove", (e) => this._evtRemove(e)); views.replaceContent(this._hostNode, template()); @@ -28,16 +28,19 @@ class CommentListControl extends events.EventTarget { } get _commentListNode() { - return this._hostNode.querySelector('ul'); + return this._hostNode.querySelector("ul"); } _installCommentNode(comment) { - const commentListItemNode = document.createElement('li'); + const commentListItemNode = document.createElement("li"); const commentControl = new CommentControl( - commentListItemNode, comment, false); - events.proxyEvent(commentControl, this, 'submit'); - events.proxyEvent(commentControl, this, 'score'); - events.proxyEvent(commentControl, this, 'delete'); + commentListItemNode, + comment, + false + ); + events.proxyEvent(commentControl, this, "submit"); + events.proxyEvent(commentControl, this, "score"); + events.proxyEvent(commentControl, this, "delete"); this._commentIdToNode[comment.id] = commentListItemNode; this._commentListNode.appendChild(commentListItemNode); } diff --git a/client/js/controls/expander_control.js b/client/js/controls/expander_control.js index 28a4ffb..11ad3ef 100644 --- a/client/js/controls/expander_control.js +++ b/client/js/controls/expander_control.js @@ -1,26 +1,28 @@ -'use strict'; +"use strict"; -const ICON_CLASS_OPENED = 'fa-chevron-down'; -const ICON_CLASS_CLOSED = 'fa-chevron-up'; +const ICON_CLASS_OPENED = "fa-chevron-down"; +const ICON_CLASS_CLOSED = "fa-chevron-up"; -const views = require('../util/views.js'); +const views = require("../util/views.js"); -const template = views.getTemplate('expander'); +const template = views.getTemplate("expander"); class ExpanderControl { constructor(name, title, nodes) { this._name = name; - nodes = Array.from(nodes).filter(n => n); + nodes = Array.from(nodes).filter((n) => n); if (!nodes.length) { return; } - const expanderNode = template({title: title}); - const toggleLinkNode = expanderNode.querySelector('a'); - const toggleIconNode = expanderNode.querySelector('i'); - const expanderContentNode = expanderNode.querySelector('div'); - toggleLinkNode.addEventListener('click', e => this._evtToggleClick(e)); + const expanderNode = template({ title: title }); + const toggleLinkNode = expanderNode.querySelector("a"); + const toggleIconNode = expanderNode.querySelector("i"); + const expanderContentNode = expanderNode.querySelector("div"); + toggleLinkNode.addEventListener("click", (e) => + this._evtToggleClick(e) + ); nodes[0].parentNode.insertBefore(expanderNode, nodes[0]); @@ -32,29 +34,30 @@ class ExpanderControl { this._toggleIconNode = toggleIconNode; expanderNode.classList.toggle( - 'collapsed', - this._allStates[this._name] === undefined ? - false : - !this._allStates[this._name]); + "collapsed", + this._allStates[this._name] === undefined + ? false + : !this._allStates[this._name] + ); this._syncIcon(); } // eslint-disable-next-line accessor-pairs set title(newTitle) { if (this._expanderNode) { - this._expanderNode - .querySelector('header span') - .textContent = newTitle; + this._expanderNode.querySelector( + "header span" + ).textContent = newTitle; } } get _isOpened() { - return !this._expanderNode.classList.contains('collapsed'); + return !this._expanderNode.classList.contains("collapsed"); } get _allStates() { try { - return JSON.parse(localStorage.getItem('expander')) || {}; + return JSON.parse(localStorage.getItem("expander")) || {}; } catch (e) { return {}; } @@ -63,12 +66,12 @@ class ExpanderControl { _save() { const newStates = Object.assign({}, this._allStates); newStates[this._name] = this._isOpened; - localStorage.setItem('expander', JSON.stringify(newStates)); + localStorage.setItem("expander", JSON.stringify(newStates)); } _evtToggleClick(e) { e.preventDefault(); - this._expanderNode.classList.toggle('collapsed'); + this._expanderNode.classList.toggle("collapsed"); this._save(); this._syncIcon(); } diff --git a/client/js/controls/file_dropper_control.js b/client/js/controls/file_dropper_control.js index a618720..2dfb492 100644 --- a/client/js/controls/file_dropper_control.js +++ b/client/js/controls/file_dropper_control.js @@ -1,9 +1,9 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); -const template = views.getTemplate('file-dropper'); +const template = views.getTemplate("file-dropper"); const KEY_RETURN = 13; @@ -17,37 +17,42 @@ class FileDropperControl extends events.EventTarget { allowMultiple: options.allowMultiple, allowUrls: options.allowUrls, lock: options.lock, - id: 'file-' + Math.random().toString(36).substring(7), + id: "file-" + Math.random().toString(36).substring(7), urlPlaceholder: - options.urlPlaceholder || 'Alternatively, paste an URL here.', + options.urlPlaceholder || "Alternatively, paste an URL here.", }); - this._dropperNode = source.querySelector('.file-dropper'); - this._urlInputNode = source.querySelector('input[type=text]'); - this._urlConfirmButtonNode = source.querySelector('button'); - this._fileInputNode = source.querySelector('input[type=file]'); - this._fileInputNode.style.display = 'none'; + this._dropperNode = source.querySelector(".file-dropper"); + this._urlInputNode = source.querySelector("input[type=text]"); + this._urlConfirmButtonNode = source.querySelector("button"); + this._fileInputNode = source.querySelector("input[type=file]"); + this._fileInputNode.style.display = "none"; this._fileInputNode.multiple = options.allowMultiple || false; this._counter = 0; - this._dropperNode.addEventListener( - 'dragenter', e => this._evtDragEnter(e)); - this._dropperNode.addEventListener( - 'dragleave', e => this._evtDragLeave(e)); - this._dropperNode.addEventListener( - 'dragover', e => this._evtDragOver(e)); - this._dropperNode.addEventListener( - 'drop', e => this._evtDrop(e)); - this._fileInputNode.addEventListener( - 'change', e => this._evtFileChange(e)); + this._dropperNode.addEventListener("dragenter", (e) => + this._evtDragEnter(e) + ); + this._dropperNode.addEventListener("dragleave", (e) => + this._evtDragLeave(e) + ); + this._dropperNode.addEventListener("dragover", (e) => + this._evtDragOver(e) + ); + this._dropperNode.addEventListener("drop", (e) => this._evtDrop(e)); + this._fileInputNode.addEventListener("change", (e) => + this._evtFileChange(e) + ); if (this._urlInputNode) { - this._urlInputNode.addEventListener( - 'keydown', e => this._evtUrlInputKeyDown(e)); + this._urlInputNode.addEventListener("keydown", (e) => + this._evtUrlInputKeyDown(e) + ); } if (this._urlConfirmButtonNode) { - this._urlConfirmButtonNode.addEventListener( - 'click', e => this._evtUrlConfirmButtonClick(e)); + this._urlConfirmButtonNode.addEventListener("click", (e) => + this._evtUrlConfirmButtonClick(e) + ); } this._originalHtml = this._dropperNode.innerHTML; @@ -56,24 +61,27 @@ class FileDropperControl extends events.EventTarget { reset() { this._dropperNode.innerHTML = this._originalHtml; - this.dispatchEvent(new CustomEvent('reset')); + this.dispatchEvent(new CustomEvent("reset")); } _emitFiles(files) { files = Array.from(files); if (this._options.lock) { - this._dropperNode.innerText = - files.map(file => file.name).join(', '); + this._dropperNode.innerText = files + .map((file) => file.name) + .join(", "); } this.dispatchEvent( - new CustomEvent('fileadd', {detail: {files: files}})); + new CustomEvent("fileadd", { detail: { files: files } }) + ); } _emitUrls(urls) { - urls = Array.from(urls).map(url => url.trim()); + urls = Array.from(urls).map((url) => url.trim()); if (this._options.lock) { - this._dropperNode.innerText = - urls.map(url => url.split(/\//).reverse()[0]).join(', '); + this._dropperNode.innerText = urls + .map((url) => url.split(/\//).reverse()[0]) + .join(", "); } for (let url of urls) { if (!url) { @@ -84,18 +92,20 @@ class FileDropperControl extends events.EventTarget { return; } } - this.dispatchEvent(new CustomEvent('urladd', {detail: {urls: urls}})); + this.dispatchEvent( + new CustomEvent("urladd", { detail: { urls: urls } }) + ); } _evtDragEnter(e) { - this._dropperNode.classList.add('active'); + this._dropperNode.classList.add("active"); this._counter++; } _evtDragLeave(e) { this._counter--; if (this._counter === 0) { - this._dropperNode.classList.remove('active'); + this._dropperNode.classList.remove("active"); } } @@ -109,12 +119,12 @@ class FileDropperControl extends events.EventTarget { _evtDrop(e) { e.preventDefault(); - this._dropperNode.classList.remove('active'); + this._dropperNode.classList.remove("active"); if (!e.dataTransfer.files.length) { - window.alert('Only files are supported.'); + window.alert("Only files are supported."); } if (!this._options.allowMultiple && e.dataTransfer.files.length > 1) { - window.alert('Cannot select multiple files.'); + window.alert("Cannot select multiple files."); } this._emitFiles(e.dataTransfer.files); } @@ -124,16 +134,16 @@ class FileDropperControl extends events.EventTarget { return; } e.preventDefault(); - this._dropperNode.classList.remove('active'); + this._dropperNode.classList.remove("active"); this._emitUrls(this._urlInputNode.value.split(/[\r\n]/)); - this._urlInputNode.value = ''; + this._urlInputNode.value = ""; } _evtUrlConfirmButtonClick(e) { e.preventDefault(); - this._dropperNode.classList.remove('active'); + this._dropperNode.classList.remove("active"); this._emitUrls(this._urlInputNode.value.split(/[\r\n]/)); - this._urlInputNode.value = ''; + this._urlInputNode.value = ""; } } diff --git a/client/js/controls/pool_auto_complete_control.js b/client/js/controls/pool_auto_complete_control.js index f720833..97794a8 100644 --- a/client/js/controls/pool_auto_complete_control.js +++ b/client/js/controls/pool_auto_complete_control.js @@ -1,43 +1,54 @@ -'use strict'; +"use strict"; -const misc = require('../util/misc.js'); -const PoolList = require('../models/pool_list.js'); -const AutoCompleteControl = require('./auto_complete_control.js'); +const misc = require("../util/misc.js"); +const PoolList = require("../models/pool_list.js"); +const AutoCompleteControl = require("./auto_complete_control.js"); function _poolListToMatches(pools, options) { - return [...pools].sort((pool1, pool2) => { - return pool2.postCount - pool1.postCount; - }).map(pool => { - let cssName = misc.makeCssName(pool.category, 'pool'); - const caption = ( - '<span class="' + cssName + '">' - + misc.escapeHtml(pool.names[0] + ' (' + pool.postCount + ')') - + '</span>'); - return { - caption: caption, - value: pool, - }; - }); + return [...pools] + .sort((pool1, pool2) => { + return pool2.postCount - pool1.postCount; + }) + .map((pool) => { + let cssName = misc.makeCssName(pool.category, "pool"); + const caption = + '<span class="' + + cssName + + '">' + + misc.escapeHtml(pool.names[0] + " (" + pool.postCount + ")") + + "</span>"; + return { + caption: caption, + value: pool, + }; + }); } class PoolAutoCompleteControl extends AutoCompleteControl { constructor(input, options) { const minLengthForPartialSearch = 3; - options.getMatches = text => { + options.getMatches = (text) => { const term = misc.escapeSearchTerm(text); - const query = ( - text.length < minLengthForPartialSearch - ? term + '*' - : '*' + term + '*') + ' sort:post-count'; + const query = + (text.length < minLengthForPartialSearch + ? term + "*" + : "*" + term + "*") + " sort:post-count"; return new Promise((resolve, reject) => { - PoolList.search( - query, 0, this._options.maxResults, ['id', 'names', 'category', 'postCount', 'version']) - .then( - response => resolve( - _poolListToMatches(response.results, this._options)), - reject); + PoolList.search(query, 0, this._options.maxResults, [ + "id", + "names", + "category", + "postCount", + "version", + ]).then( + (response) => + resolve( + _poolListToMatches(response.results, this._options) + ), + reject + ); }); }; diff --git a/client/js/controls/pool_input_control.js b/client/js/controls/pool_input_control.js index af1c744..c8995da 100644 --- a/client/js/controls/pool_input_control.js +++ b/client/js/controls/pool_input_control.js @@ -1,24 +1,24 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const pools = require('../pools.js'); -const misc = require('../util/misc.js'); -const uri = require('../util/uri.js'); -const Pool = require('../models/pool.js'); -const settings = require('../models/settings.js'); -const events = require('../events.js'); -const views = require('../util/views.js'); -const PoolAutoCompleteControl = require('./pool_auto_complete_control.js'); +const api = require("../api.js"); +const pools = require("../pools.js"); +const misc = require("../util/misc.js"); +const uri = require("../util/uri.js"); +const Pool = require("../models/pool.js"); +const settings = require("../models/settings.js"); +const events = require("../events.js"); +const views = require("../util/views.js"); +const PoolAutoCompleteControl = require("./pool_auto_complete_control.js"); const KEY_SPACE = 32; const KEY_RETURN = 13; -const SOURCE_INIT = 'init'; -const SOURCE_IMPLICATION = 'implication'; -const SOURCE_USER_INPUT = 'user-input'; -const SOURCE_CLIPBOARD = 'clipboard'; +const SOURCE_INIT = "init"; +const SOURCE_IMPLICATION = "implication"; +const SOURCE_USER_INPUT = "user-input"; +const SOURCE_CLIPBOARD = "clipboard"; -const template = views.getTemplate('pool-input'); +const template = views.getTemplate("pool-input"); function _fadeOutListItemNodeStatus(listItemNode) { if (listItemNode.classList.length) { @@ -27,8 +27,7 @@ function _fadeOutListItemNodeStatus(listItemNode) { } listItemNode.fadeTimeout = window.setTimeout(() => { while (listItemNode.classList.length) { - listItemNode.classList.remove( - listItemNode.classList.item(0)); + listItemNode.classList.remove(listItemNode.classList.item(0)); } listItemNode.fadeTimeout = null; }, 2500); @@ -45,29 +44,33 @@ class PoolInputControl extends events.EventTarget { // dom const editAreaNode = template(); this._editAreaNode = editAreaNode; - this._poolInputNode = editAreaNode.querySelector('input'); - this._poolListNode = editAreaNode.querySelector('ul.compact-pools'); + this._poolInputNode = editAreaNode.querySelector("input"); + this._poolListNode = editAreaNode.querySelector("ul.compact-pools"); this._autoCompleteControl = new PoolAutoCompleteControl( - this._poolInputNode, { + this._poolInputNode, + { getTextToFind: () => { return this._poolInputNode.value; }, - confirm: pool => { - this._poolInputNode.value = ''; + confirm: (pool) => { + this._poolInputNode.value = ""; this.addPool(pool, SOURCE_USER_INPUT); }, - delete: pool => { - this._poolInputNode.value = ''; + delete: (pool) => { + this._poolInputNode.value = ""; this.deletePool(pool); }, - verticalShift: -2 - }); + verticalShift: -2, + } + ); // show - this._hostNode.style.display = 'none'; + this._hostNode.style.display = "none"; this._hostNode.parentNode.insertBefore( - this._editAreaNode, hostNode.nextSibling); + this._editAreaNode, + hostNode.nextSibling + ); // add existing pools for (let pool of [...this.pools]) { @@ -81,19 +84,21 @@ class PoolInputControl extends events.EventTarget { return Promise.resolve(); } - this.pools.add(pool, false) + this.pools.add(pool, false); const listItemNode = this._createListItemNode(pool); if (!pool.category) { - listItemNode.classList.add('new'); + listItemNode.classList.add("new"); } this._poolListNode.prependChild(listItemNode); _fadeOutListItemNodeStatus(listItemNode); - this.dispatchEvent(new CustomEvent('add', { - detail: {pool: pool, source: source}, - })); - this.dispatchEvent(new CustomEvent('change')); + this.dispatchEvent( + new CustomEvent("add", { + detail: { pool: pool, source: source }, + }) + ); + this.dispatchEvent(new CustomEvent("change")); return Promise.resolve(); } @@ -107,52 +112,57 @@ class PoolInputControl extends events.EventTarget { this._deleteListItemNode(pool); - this.dispatchEvent(new CustomEvent('remove', { - detail: {pool: pool}, - })); - this.dispatchEvent(new CustomEvent('change')); + this.dispatchEvent( + new CustomEvent("remove", { + detail: { pool: pool }, + }) + ); + this.dispatchEvent(new CustomEvent("change")); } _createListItemNode(pool) { - const className = pool.category ? - misc.makeCssName(pool.category, 'pool') : - null; + const className = pool.category + ? misc.makeCssName(pool.category, "pool") + : null; - const poolLinkNode = document.createElement('a'); + const poolLinkNode = document.createElement("a"); if (className) { poolLinkNode.classList.add(className); } poolLinkNode.setAttribute( - 'href', uri.formatClientLink('pool', pool.names[0])); + "href", + uri.formatClientLink("pool", pool.names[0]) + ); - const poolIconNode = document.createElement('i'); - poolIconNode.classList.add('fa'); - poolIconNode.classList.add('fa-pool'); + const poolIconNode = document.createElement("i"); + poolIconNode.classList.add("fa"); + poolIconNode.classList.add("fa-pool"); poolLinkNode.appendChild(poolIconNode); - const searchLinkNode = document.createElement('a'); + const searchLinkNode = document.createElement("a"); if (className) { searchLinkNode.classList.add(className); } searchLinkNode.setAttribute( - 'href', uri.formatClientLink( - 'posts', {query: "pool:" + pool.id})); - searchLinkNode.textContent = pool.names[0] + ' '; + "href", + uri.formatClientLink("posts", { query: "pool:" + pool.id }) + ); + searchLinkNode.textContent = pool.names[0] + " "; - const usagesNode = document.createElement('span'); - usagesNode.classList.add('pool-usages'); - usagesNode.setAttribute('data-pseudo-content', pool.postCount); + const usagesNode = document.createElement("span"); + usagesNode.classList.add("pool-usages"); + usagesNode.setAttribute("data-pseudo-content", pool.postCount); - const removalLinkNode = document.createElement('a'); - removalLinkNode.classList.add('remove-pool'); - removalLinkNode.setAttribute('href', ''); - removalLinkNode.setAttribute('data-pseudo-content', '×'); - removalLinkNode.addEventListener('click', e => { + const removalLinkNode = document.createElement("a"); + removalLinkNode.classList.add("remove-pool"); + removalLinkNode.setAttribute("href", ""); + removalLinkNode.setAttribute("data-pseudo-content", "×"); + removalLinkNode.addEventListener("click", (e) => { e.preventDefault(); this.deletePool(pool); }); - const listItemNode = document.createElement('li'); + const listItemNode = document.createElement("li"); listItemNode.appendChild(removalLinkNode); listItemNode.appendChild(poolLinkNode); listItemNode.appendChild(searchLinkNode); diff --git a/client/js/controls/post_content_control.js b/client/js/controls/post_content_control.js index d1848b3..55daca7 100644 --- a/client/js/controls/post_content_control.js +++ b/client/js/controls/post_content_control.js @@ -1,36 +1,38 @@ -'use strict'; +"use strict"; -const settings = require('../models/settings.js'); -const views = require('../util/views.js'); -const optimizedResize = require('../util/optimized_resize.js'); +const settings = require("../models/settings.js"); +const views = require("../util/views.js"); +const optimizedResize = require("../util/optimized_resize.js"); class PostContentControl { constructor(hostNode, post, viewportSizeCalculator, fitFunctionOverride) { this._post = post; this._viewportSizeCalculator = viewportSizeCalculator; this._hostNode = hostNode; - this._template = views.getTemplate('post-content'); + this._template = views.getTemplate("post-content"); let fitMode = settings.get().fitMode; - if (typeof fitFunctionOverride !== 'undefined') { + if (typeof fitFunctionOverride !== "undefined") { fitMode = fitFunctionOverride; } - this._currentFitFunction = { - 'fit-both': this.fitBoth, - 'fit-original': this.fitOriginal, - 'fit-width': this.fitWidth, - 'fit-height': this.fitHeight, - }[fitMode] || this.fitBoth; + this._currentFitFunction = + { + "fit-both": this.fitBoth, + "fit-original": this.fitOriginal, + "fit-width": this.fitWidth, + "fit-height": this.fitHeight, + }[fitMode] || this.fitBoth; this._install(); - this._post.addEventListener( - 'changeContent', e => this._evtPostContentChange(e)); + this._post.addEventListener("changeContent", (e) => + this._evtPostContentChange(e) + ); } disableOverlay() { - this._hostNode.querySelector('.post-overlay').style.display = 'none'; + this._hostNode.querySelector(".post-overlay").style.display = "none"; } fitWidth() { @@ -92,10 +94,11 @@ class PostContentControl { _resize(width, height) { const resizeListenerNodes = [this._postContentNode].concat( - ...this._postContentNode.querySelectorAll('.resize-listener')); + ...this._postContentNode.querySelectorAll(".resize-listener") + ); for (let node of resizeListenerNodes) { - node.style.width = width + 'px'; - node.style.height = height + 'px'; + node.style.width = width + "px"; + node.style.height = height + "px"; } } @@ -106,10 +109,9 @@ class PostContentControl { _install() { this._reinstall(); optimizedResize.add(() => this._refreshSize()); - views.monitorNodeRemoval( - this._hostNode, () => { - this._uninstall(); - }); + views.monitorNodeRemoval(this._hostNode, () => { + this._uninstall(); + }); } _reinstall() { @@ -118,7 +120,7 @@ class PostContentControl { autoplay: settings.get().autoplayVideos, }); if (settings.get().transparencyGrid) { - newNode.classList.add('transparency-grid'); + newNode.classList.add("transparency-grid"); } if (this._postContentNode) { this._hostNode.replaceChild(newNode, this._postContentNode); diff --git a/client/js/controls/post_edit_sidebar_control.js b/client/js/controls/post_edit_sidebar_control.js index 495dcdb..c6b7c22 100644 --- a/client/js/controls/post_edit_sidebar_control.js +++ b/client/js/controls/post_edit_sidebar_control.js @@ -1,17 +1,17 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const events = require('../events.js'); -const misc = require('../util/misc.js'); -const views = require('../util/views.js'); -const Note = require('../models/note.js'); -const Point = require('../models/point.js'); -const TagInputControl = require('./tag_input_control.js'); -const PoolInputControl = require('./pool_input_control.js'); -const ExpanderControl = require('../controls/expander_control.js'); -const FileDropperControl = require('../controls/file_dropper_control.js'); +const api = require("../api.js"); +const events = require("../events.js"); +const misc = require("../util/misc.js"); +const views = require("../util/views.js"); +const Note = require("../models/note.js"); +const Point = require("../models/point.js"); +const TagInputControl = require("./tag_input_control.js"); +const PoolInputControl = require("./pool_input_control.js"); +const ExpanderControl = require("../controls/expander_control.js"); +const FileDropperControl = require("../controls/file_dropper_control.js"); -const template = views.getTemplate('post-edit-sidebar'); +const template = views.getTemplate("post-edit-sidebar"); class PostEditSidebarControl extends events.EventTarget { constructor(hostNode, post, postContentControl, postNotesOverlayControl) { @@ -24,182 +24,222 @@ class PostEditSidebarControl extends events.EventTarget { this._postNotesOverlayControl.switchToPassiveEdit(); - views.replaceContent(this._hostNode, template({ - post: this._post, - enableSafety: api.safetyEnabled(), - hasClipboard: document.queryCommandSupported('copy'), - canEditPostSafety: api.hasPrivilege('posts:edit:safety'), - canEditPostSource: api.hasPrivilege('posts:edit:source'), - canEditPostTags: api.hasPrivilege('posts:edit:tags'), - canEditPostRelations: api.hasPrivilege('posts:edit:relations'), - canEditPostNotes: api.hasPrivilege('posts:edit:notes') && - post.type !== 'video' && - post.type !== 'flash', - canEditPostFlags: api.hasPrivilege('posts:edit:flags'), - canEditPostContent: api.hasPrivilege('posts:edit:content'), - canEditPostThumbnail: api.hasPrivilege('posts:edit:thumbnail'), - canEditPoolPosts: api.hasPrivilege('pools:edit:posts'), - canCreateAnonymousPosts: api.hasPrivilege('posts:create:anonymous'), - canDeletePosts: api.hasPrivilege('posts:delete'), - canFeaturePosts: api.hasPrivilege('posts:feature'), - canMergePosts: api.hasPrivilege('posts:merge'), - })); + views.replaceContent( + this._hostNode, + template({ + post: this._post, + enableSafety: api.safetyEnabled(), + hasClipboard: document.queryCommandSupported("copy"), + canEditPostSafety: api.hasPrivilege("posts:edit:safety"), + canEditPostSource: api.hasPrivilege("posts:edit:source"), + canEditPostTags: api.hasPrivilege("posts:edit:tags"), + canEditPostRelations: api.hasPrivilege("posts:edit:relations"), + canEditPostNotes: + api.hasPrivilege("posts:edit:notes") && + post.type !== "video" && + post.type !== "flash", + canEditPostFlags: api.hasPrivilege("posts:edit:flags"), + canEditPostContent: api.hasPrivilege("posts:edit:content"), + canEditPostThumbnail: api.hasPrivilege("posts:edit:thumbnail"), + canEditPoolPosts: api.hasPrivilege("pools:edit:posts"), + canCreateAnonymousPosts: api.hasPrivilege( + "posts:create:anonymous" + ), + canDeletePosts: api.hasPrivilege("posts:delete"), + canFeaturePosts: api.hasPrivilege("posts:feature"), + canMergePosts: api.hasPrivilege("posts:merge"), + }) + ); new ExpanderControl( - 'post-info', - 'Basic info', - this._hostNode.querySelectorAll('.safety, .relations, .flags, .post-source')); + "post-info", + "Basic info", + this._hostNode.querySelectorAll( + ".safety, .relations, .flags, .post-source" + ) + ); this._tagsExpander = new ExpanderControl( - 'post-tags', + "post-tags", `Tags (${this._post.tags.length})`, - this._hostNode.querySelectorAll('.tags')); + this._hostNode.querySelectorAll(".tags") + ); this._notesExpander = new ExpanderControl( - 'post-notes', - 'Notes', - this._hostNode.querySelectorAll('.notes')); + "post-notes", + "Notes", + this._hostNode.querySelectorAll(".notes") + ); this._poolsExpander = new ExpanderControl( - 'post-pools', + "post-pools", `Pools (${this._post.pools.length})`, - this._hostNode.querySelectorAll('.pools')); + this._hostNode.querySelectorAll(".pools") + ); new ExpanderControl( - 'post-content', - 'Content', - this._hostNode.querySelectorAll('.post-content, .post-thumbnail')); + "post-content", + "Content", + this._hostNode.querySelectorAll(".post-content, .post-thumbnail") + ); new ExpanderControl( - 'post-management', - 'Management', - this._hostNode.querySelectorAll('.management')); + "post-management", + "Management", + this._hostNode.querySelectorAll(".management") + ); this._syncExpanderTitles(); if (this._formNode) { - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => + this._evtSubmit(e) + ); } if (this._tagInputNode) { this._tagControl = new TagInputControl( - this._tagInputNode, post.tags); + this._tagInputNode, + post.tags + ); } if (this._poolInputNode) { this._poolControl = new PoolInputControl( - this._poolInputNode, post.pools); + this._poolInputNode, + post.pools + ); } if (this._contentInputNode) { this._contentFileDropper = new FileDropperControl( - this._contentInputNode, {allowUrls: true, + this._contentInputNode, + { + allowUrls: true, lock: true, - urlPlaceholder: '...or paste an URL here.'}); - this._contentFileDropper.addEventListener('fileadd', e => { + urlPlaceholder: "...or paste an URL here.", + } + ); + this._contentFileDropper.addEventListener("fileadd", (e) => { this._newPostContent = e.detail.files[0]; }); - this._contentFileDropper.addEventListener('urladd', e => { + this._contentFileDropper.addEventListener("urladd", (e) => { this._newPostContent = e.detail.urls[0]; }); } if (this._thumbnailInputNode) { this._thumbnailFileDropper = new FileDropperControl( - this._thumbnailInputNode, {lock: true}); - this._thumbnailFileDropper.addEventListener('fileadd', e => { + this._thumbnailInputNode, + { lock: true } + ); + this._thumbnailFileDropper.addEventListener("fileadd", (e) => { this._newPostThumbnail = e.detail.files[0]; - this._thumbnailRemovalLinkNode.style.display = 'block'; + this._thumbnailRemovalLinkNode.style.display = "block"; }); } if (this._thumbnailRemovalLinkNode) { - this._thumbnailRemovalLinkNode.addEventListener( - 'click', e => this._evtRemoveThumbnailClick(e)); - this._thumbnailRemovalLinkNode.style.display = - this._post.hasCustomThumbnail ? 'block' : 'none'; + this._thumbnailRemovalLinkNode.addEventListener("click", (e) => + this._evtRemoveThumbnailClick(e) + ); + this._thumbnailRemovalLinkNode.style.display = this._post + .hasCustomThumbnail + ? "block" + : "none"; } if (this._addNoteLinkNode) { - this._addNoteLinkNode.addEventListener( - 'click', e => this._evtAddNoteClick(e)); + this._addNoteLinkNode.addEventListener("click", (e) => + this._evtAddNoteClick(e) + ); } if (this._copyNotesLinkNode) { - this._copyNotesLinkNode.addEventListener( - 'click', e => this._evtCopyNotesClick(e)); + this._copyNotesLinkNode.addEventListener("click", (e) => + this._evtCopyNotesClick(e) + ); } if (this._pasteNotesLinkNode) { - this._pasteNotesLinkNode.addEventListener( - 'click', e => this._evtPasteNotesClick(e)); + this._pasteNotesLinkNode.addEventListener("click", (e) => + this._evtPasteNotesClick(e) + ); } if (this._deleteNoteLinkNode) { - this._deleteNoteLinkNode.addEventListener( - 'click', e => this._evtDeleteNoteClick(e)); + this._deleteNoteLinkNode.addEventListener("click", (e) => + this._evtDeleteNoteClick(e) + ); } if (this._featureLinkNode) { - this._featureLinkNode.addEventListener( - 'click', e => this._evtFeatureClick(e)); + this._featureLinkNode.addEventListener("click", (e) => + this._evtFeatureClick(e) + ); } if (this._mergeLinkNode) { - this._mergeLinkNode.addEventListener( - 'click', e => this._evtMergeClick(e)); + this._mergeLinkNode.addEventListener("click", (e) => + this._evtMergeClick(e) + ); } if (this._deleteLinkNode) { - this._deleteLinkNode.addEventListener( - 'click', e => this._evtDeleteClick(e)); + this._deleteLinkNode.addEventListener("click", (e) => + this._evtDeleteClick(e) + ); } - this._postNotesOverlayControl.addEventListener( - 'blur', e => this._evtNoteBlur(e)); + this._postNotesOverlayControl.addEventListener("blur", (e) => + this._evtNoteBlur(e) + ); - this._postNotesOverlayControl.addEventListener( - 'focus', e => this._evtNoteFocus(e)); + this._postNotesOverlayControl.addEventListener("focus", (e) => + this._evtNoteFocus(e) + ); - this._post.addEventListener( - 'changeContent', e => this._evtPostContentChange(e)); + this._post.addEventListener("changeContent", (e) => + this._evtPostContentChange(e) + ); - this._post.addEventListener( - 'changeThumbnail', e => this._evtPostThumbnailChange(e)); + this._post.addEventListener("changeThumbnail", (e) => + this._evtPostThumbnailChange(e) + ); if (this._formNode) { const inputNodes = this._formNode.querySelectorAll( - 'input, textarea'); + "input, textarea" + ); for (let node of inputNodes) { - node.addEventListener( - 'change', - e => this.dispatchEvent(new CustomEvent('change'))); + node.addEventListener("change", (e) => + this.dispatchEvent(new CustomEvent("change")) + ); } - this._postNotesOverlayControl.addEventListener( - 'change', - e => this.dispatchEvent(new CustomEvent('change'))); + this._postNotesOverlayControl.addEventListener("change", (e) => + this.dispatchEvent(new CustomEvent("change")) + ); } - for (let eventType of ['add', 'remove']) { - this._post.notes.addEventListener(eventType, e => { + for (let eventType of ["add", "remove"]) { + this._post.notes.addEventListener(eventType, (e) => { this._syncExpanderTitles(); }); - this._post.pools.addEventListener(eventType, e => { + this._post.pools.addEventListener(eventType, (e) => { this._syncExpanderTitles(); }); } - this._tagControl.addEventListener( - 'change', e => { - this.dispatchEvent(new CustomEvent('change')); - this._syncExpanderTitles(); - }); + this._tagControl.addEventListener("change", (e) => { + this.dispatchEvent(new CustomEvent("change")); + this._syncExpanderTitles(); + }); if (this._noteTextareaNode) { - this._noteTextareaNode.addEventListener( - 'change', e => this._evtNoteTextChangeRequest(e)); + this._noteTextareaNode.addEventListener("change", (e) => + this._evtNoteTextChangeRequest(e) + ); } - this._poolControl.addEventListener( - 'change', e => { - this.dispatchEvent(new CustomEvent('change')); - this._syncExpanderTitles(); - }); + this._poolControl.addEventListener("change", (e) => { + this.dispatchEvent(new CustomEvent("change")); + this._syncExpanderTitles(); + }); } _syncExpanderTitles() { @@ -220,37 +260,43 @@ class PostEditSidebarControl extends events.EventTarget { e.preventDefault(); this._thumbnailFileDropper.reset(); this._newPostThumbnail = null; - this._thumbnailRemovalLinkNode.style.display = 'none'; + this._thumbnailRemovalLinkNode.style.display = "none"; } _evtFeatureClick(e) { e.preventDefault(); - if (confirm('Are you sure you want to feature this post?')) { - this.dispatchEvent(new CustomEvent('feature', { - detail: { - post: this._post, - }, - })); + if (confirm("Are you sure you want to feature this post?")) { + this.dispatchEvent( + new CustomEvent("feature", { + detail: { + post: this._post, + }, + }) + ); } } _evtMergeClick(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('merge', { - detail: { - post: this._post, - }, - })); + this.dispatchEvent( + new CustomEvent("merge", { + detail: { + post: this._post, + }, + }) + ); } _evtDeleteClick(e) { e.preventDefault(); - if (confirm('Are you sure you want to delete this post?')) { - this.dispatchEvent(new CustomEvent('delete', { - detail: { - post: this._post, - }, - })); + if (confirm("Are you sure you want to delete this post?")) { + this.dispatchEvent( + new CustomEvent("delete", { + detail: { + post: this._post, + }, + }) + ); } } @@ -262,60 +308,64 @@ class PostEditSidebarControl extends events.EventTarget { _evtNoteFocus(e) { this._editedNote = e.detail.note; - this._addNoteLinkNode.classList.remove('inactive'); - this._deleteNoteLinkNode.classList.remove('inactive'); - this._noteTextareaNode.removeAttribute('disabled'); + this._addNoteLinkNode.classList.remove("inactive"); + this._deleteNoteLinkNode.classList.remove("inactive"); + this._noteTextareaNode.removeAttribute("disabled"); this._noteTextareaNode.value = e.detail.note.text; } _evtNoteBlur(e) { this._evtNoteTextChangeRequest(null); - this._addNoteLinkNode.classList.remove('inactive'); - this._deleteNoteLinkNode.classList.add('inactive'); + this._addNoteLinkNode.classList.remove("inactive"); + this._deleteNoteLinkNode.classList.add("inactive"); this._noteTextareaNode.blur(); - this._noteTextareaNode.setAttribute('disabled', 'disabled'); - this._noteTextareaNode.value = ''; + this._noteTextareaNode.setAttribute("disabled", "disabled"); + this._noteTextareaNode.value = ""; } _evtAddNoteClick(e) { e.preventDefault(); - if (e.target.classList.contains('inactive')) { + if (e.target.classList.contains("inactive")) { return; } - this._addNoteLinkNode.classList.add('inactive'); + this._addNoteLinkNode.classList.add("inactive"); this._postNotesOverlayControl.switchToDrawing(); } _evtCopyNotesClick(e) { e.preventDefault(); - let textarea = document.createElement('textarea'); - textarea.style.position = 'fixed'; - textarea.style.opacity = '0'; - textarea.value = JSON.stringify([...this._post.notes].map(note => ({ - polygon: [...note.polygon].map( - point => [point.x, point.y]), - text: note.text, - }))); + let textarea = document.createElement("textarea"); + textarea.style.position = "fixed"; + textarea.style.opacity = "0"; + textarea.value = JSON.stringify( + [...this._post.notes].map((note) => ({ + polygon: [...note.polygon].map((point) => [point.x, point.y]), + text: note.text, + })) + ); document.body.appendChild(textarea); textarea.select(); let success = false; try { - success = document.execCommand('copy'); + success = document.execCommand("copy"); } catch (err) { // continue regardless of error } textarea.blur(); document.body.removeChild(textarea); - alert(success - ? 'Notes copied to clipboard.' - : 'Failed to copy the text to clipboard. Sorry.'); + alert( + success + ? "Notes copied to clipboard." + : "Failed to copy the text to clipboard. Sorry." + ); } _evtPasteNotesClick(e) { e.preventDefault(); const text = window.prompt( - 'Please enter the exported notes snapshot:'); + "Please enter the exported notes snapshot:" + ); if (!text) { return; } @@ -333,7 +383,7 @@ class PostEditSidebarControl extends events.EventTarget { _evtDeleteNoteClick(e) { e.preventDefault(); - if (e.target.classList.contains('inactive')) { + if (e.target.classList.contains("inactive")) { return; } this._post.notes.remove(this._editedNote); @@ -342,72 +392,78 @@ class PostEditSidebarControl extends events.EventTarget { _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - post: this._post, + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + post: this._post, - safety: this._safetyButtonNodes.length ? - Array.from(this._safetyButtonNodes) - .filter(node => node.checked)[0] - .value.toLowerCase() : - undefined, + safety: this._safetyButtonNodes.length + ? Array.from(this._safetyButtonNodes) + .filter((node) => node.checked)[0] + .value.toLowerCase() + : undefined, - flags: this._videoFlags, + flags: this._videoFlags, - tags: this._tagInputNode ? - misc.splitByWhitespace(this._tagInputNode.value) : - undefined, + tags: this._tagInputNode + ? misc.splitByWhitespace(this._tagInputNode.value) + : undefined, - pools: this._poolInputNode ? - misc.splitByWhitespace(this._poolInputNode.value) : - undefined, + pools: this._poolInputNode + ? misc.splitByWhitespace(this._poolInputNode.value) + : undefined, - relations: this._relationsInputNode ? - misc.splitByWhitespace(this._relationsInputNode.value) - .map(x => parseInt(x)) : - undefined, + relations: this._relationsInputNode + ? misc + .splitByWhitespace( + this._relationsInputNode.value + ) + .map((x) => parseInt(x)) + : undefined, - content: this._newPostContent ? - this._newPostContent : - undefined, + content: this._newPostContent + ? this._newPostContent + : undefined, - thumbnail: this._newPostThumbnail !== undefined ? - this._newPostThumbnail : - undefined, + thumbnail: + this._newPostThumbnail !== undefined + ? this._newPostThumbnail + : undefined, - source: this._sourceInputNode ? - this._sourceInputNode.value : - undefined, - }, - })); + source: this._sourceInputNode + ? this._sourceInputNode.value + : undefined, + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _submitButtonNode() { - return this._hostNode.querySelector('.submit'); + return this._hostNode.querySelector(".submit"); } get _safetyButtonNodes() { - return this._formNode.querySelectorAll('.safety input'); + return this._formNode.querySelectorAll(".safety input"); } get _tagInputNode() { - return this._formNode.querySelector('.tags input'); + return this._formNode.querySelector(".tags input"); } get _poolInputNode() { - return this._formNode.querySelector('.pools input'); + return this._formNode.querySelector(".pools input"); } get _loopVideoInputNode() { - return this._formNode.querySelector('.flags input[name=loop]'); + return this._formNode.querySelector(".flags input[name=loop]"); } get _soundVideoInputNode() { - return this._formNode.querySelector('.flags input[name=sound]'); + return this._formNode.querySelector(".flags input[name=sound]"); } get _videoFlags() { @@ -416,65 +472,68 @@ class PostEditSidebarControl extends events.EventTarget { } let ret = []; if (this._loopVideoInputNode.checked) { - ret.push('loop'); + ret.push("loop"); } if (this._soundVideoInputNode.checked) { - ret.push('sound'); + ret.push("sound"); } return ret; } get _relationsInputNode() { - return this._formNode.querySelector('.relations input'); + return this._formNode.querySelector(".relations input"); } get _contentInputNode() { - return this._formNode.querySelector('.post-content .dropper-container'); + return this._formNode.querySelector( + ".post-content .dropper-container" + ); } get _thumbnailInputNode() { return this._formNode.querySelector( - '.post-thumbnail .dropper-container'); + ".post-thumbnail .dropper-container" + ); } get _thumbnailRemovalLinkNode() { - return this._formNode.querySelector('.post-thumbnail a'); + return this._formNode.querySelector(".post-thumbnail a"); } get _sourceInputNode() { - return this._formNode.querySelector('.post-source textarea'); + return this._formNode.querySelector(".post-source textarea"); } get _featureLinkNode() { - return this._formNode.querySelector('.management .feature'); + return this._formNode.querySelector(".management .feature"); } get _mergeLinkNode() { - return this._formNode.querySelector('.management .merge'); + return this._formNode.querySelector(".management .merge"); } get _deleteLinkNode() { - return this._formNode.querySelector('.management .delete'); + return this._formNode.querySelector(".management .delete"); } get _addNoteLinkNode() { - return this._formNode.querySelector('.notes .add'); + return this._formNode.querySelector(".notes .add"); } get _copyNotesLinkNode() { - return this._formNode.querySelector('.notes .copy'); + return this._formNode.querySelector(".notes .copy"); } get _pasteNotesLinkNode() { - return this._formNode.querySelector('.notes .paste'); + return this._formNode.querySelector(".notes .paste"); } get _deleteNoteLinkNode() { - return this._formNode.querySelector('.notes .delete'); + return this._formNode.querySelector(".notes .delete"); } get _noteTextareaNode() { - return this._formNode.querySelector('.notes textarea'); + return this._formNode.querySelector(".notes textarea"); } enableForm() { diff --git a/client/js/controls/post_notes_overlay_control.js b/client/js/controls/post_notes_overlay_control.js index 7bf69b2..030f7f2 100644 --- a/client/js/controls/post_notes_overlay_control.js +++ b/client/js/controls/post_notes_overlay_control.js @@ -1,13 +1,13 @@ -'use strict'; +"use strict"; -const keyboard = require('../util/keyboard.js'); -const views = require('../util/views.js'); -const events = require('../events.js'); -const misc = require('../util/misc.js'); -const Note = require('../models/note.js'); -const Point = require('../models/point.js'); +const keyboard = require("../util/keyboard.js"); +const views = require("../util/views.js"); +const events = require("../events.js"); +const misc = require("../util/misc.js"); +const Note = require("../models/note.js"); +const Point = require("../models/point.js"); -const svgNS = 'http://www.w3.org/2000/svg'; +const svgNS = "http://www.w3.org/2000/svg"; const snapThreshold = 10; const circleSize = 10; @@ -22,19 +22,19 @@ const KEY_RETURN = 13; function _getDistance(point1, point2) { return Math.sqrt( - Math.pow(point1.x - point2.x, 2) + - Math.pow(point1.y - point2.y, 2)); + Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2) + ); } function _setNodeState(node, stateName) { if (node === null) { return; } - node.setAttribute('data-state', stateName); + node.setAttribute("data-state", stateName); } function _clearEditedNote(hostNode) { - const node = hostNode.querySelector('[data-state=\'editing\']'); + const node = hostNode.querySelector("[data-state='editing']"); _setNodeState(node, null); return node !== null; } @@ -48,7 +48,7 @@ function _getNoteCentroid(note) { const y0 = note.polygon.at(i).y; const x1 = note.polygon.at((i + 1) % vertexCount).x; const y1 = note.polygon.at((i + 1) % vertexCount).y; - const a = (x0 * y1) - (x1 * y0); + const a = x0 * y1 - x1 * y0; signedArea += a; centroid.x += (x0 + x1) * a; centroid.y += (y0 + y1) * a; @@ -82,32 +82,30 @@ class State { return false; } - evtCanvasKeyDown(e) { - } + evtCanvasKeyDown(e) {} - evtNoteMouseDown(e, hoveredNote) { - } + evtNoteMouseDown(e, hoveredNote) {} - evtCanvasMouseDown(e) { - } + evtCanvasMouseDown(e) {} - evtCanvasMouseMove(e) { - } + evtCanvasMouseMove(e) {} - evtCanvasMouseUp(e) { - } + evtCanvasMouseUp(e) {} _getScreenPoint(point) { return new Point( point.x * this._control.boundingBox.width, - point.y * this._control.boundingBox.height); + point.y * this._control.boundingBox.height + ); } _snapPoints(targetPoint, referencePoint) { const targetScreenPoint = this._getScreenPoint(targetPoint); const referenceScreenPoint = this._getScreenPoint(referencePoint); - if (_getDistance(targetScreenPoint, referenceScreenPoint) < - snapThreshold) { + if ( + _getDistance(targetScreenPoint, referenceScreenPoint) < + snapThreshold + ) { targetPoint.x = referencePoint.x; targetPoint.y = referencePoint.y; } @@ -124,15 +122,16 @@ class State { (e.clientX - this._control.boundingBox.left) / this._control.boundingBox.width, (e.clientY - this._control.boundingBox.top) / - this._control.boundingBox.height); + this._control.boundingBox.height + ); } } class ReadOnlyState extends State { constructor(control) { - super(control, 'read-only'); + super(control, "read-only"); if (_clearEditedNote(control._hostNode)) { - this._control.dispatchEvent(new CustomEvent('blur')); + this._control.dispatchEvent(new CustomEvent("blur")); } keyboard.unpause(); } @@ -144,9 +143,9 @@ class ReadOnlyState extends State { class PassiveState extends State { constructor(control) { - super(control, 'passive'); + super(control, "passive"); if (_clearEditedNote(control._hostNode)) { - this._control.dispatchEvent(new CustomEvent('blur')); + this._control.dispatchEvent(new CustomEvent("blur")); } keyboard.unpause(); } @@ -164,23 +163,24 @@ class ActiveState extends State { constructor(control, note, stateName) { super(control, stateName); if (_clearEditedNote(control._hostNode)) { - this._control.dispatchEvent(new CustomEvent('blur')); + this._control.dispatchEvent(new CustomEvent("blur")); } keyboard.pause(); if (note !== null) { this._note = note; this._control.dispatchEvent( - new CustomEvent('focus', { - detail: {note: note}, - })); - _setNodeState(this._note.groupNode, 'editing'); + new CustomEvent("focus", { + detail: { note: note }, + }) + ); + _setNodeState(this._note.groupNode, "editing"); } } } class SelectedState extends ActiveState { constructor(control, note) { - super(control, note, 'selected'); + super(control, note, "selected"); this._clickTimeout = null; this._control._hideNoteText(); } @@ -211,27 +211,40 @@ class SelectedState extends ActiveState { const mouseScreenPoint = this._getScreenPoint(mousePoint); if (e.shiftKey) { this._control._state = new ScalingNoteState( - this._control, this._note, mousePoint); + this._control, + this._note, + mousePoint + ); return; } if (this._note !== hoveredNote) { - this._control._state = - new SelectedState(this._control, hoveredNote); + this._control._state = new SelectedState( + this._control, + hoveredNote + ); return; } this._clickTimeout = window.setTimeout(() => { for (let polygonPoint of this._note.polygon) { const distance = _getDistance( mouseScreenPoint, - this._getScreenPoint(polygonPoint)); + this._getScreenPoint(polygonPoint) + ); if (distance < circleSize) { this._control._state = new MovingPointState( - this._control, this._note, polygonPoint, mousePoint); + this._control, + this._note, + polygonPoint, + mousePoint + ); return; } } this._control._state = new MovingNoteState( - this._control, this._note, mousePoint); + this._control, + this._note, + mousePoint + ); }, 100); } @@ -241,9 +254,12 @@ class SelectedState extends ActiveState { for (let polygonPoint of this._note.polygon) { const distance = _getDistance( mouseScreenPoint, - this._getScreenPoint(polygonPoint)); + this._getScreenPoint(polygonPoint) + ); polygonPoint.edgeNode.classList.toggle( - 'nearby', distance < circleSize); + "nearby", + distance < circleSize + ); } } @@ -252,16 +268,24 @@ class SelectedState extends ActiveState { const mouseScreenPoint = this._getScreenPoint(mousePoint); if (e.shiftKey) { this._control._state = new ScalingNoteState( - this._control, this._note, mousePoint); + this._control, + this._note, + mousePoint + ); return; } for (let polygonPoint of this._note.polygon) { const distance = _getDistance( mouseScreenPoint, - this._getScreenPoint(polygonPoint)); + this._getScreenPoint(polygonPoint) + ); if (distance < circleSize) { this._control._state = new MovingPointState( - this._control, this._note, polygonPoint, mousePoint); + this._control, + this._note, + polygonPoint, + mousePoint + ); return; } } @@ -283,32 +307,37 @@ class SelectedState extends ActiveState { const origin = _getNoteCentroid(this._note); const originalSize = _getNoteSize(this._note); const targetSize = new Point( - originalSize.x + (x / this._control.boundingBox.width), - originalSize.y + (y / this._control.boundingBox.height)); + originalSize.x + x / this._control.boundingBox.width, + originalSize.y + y / this._control.boundingBox.height + ); const scale = new Point( targetSize.x / originalSize.x, - targetSize.y / originalSize.y); + targetSize.y / originalSize.y + ); for (let point of this._note.polygon) { - point.x = origin.x + ((point.x - origin.x) * scale.x); - point.y = origin.y + ((point.y - origin.y) * scale.y); + point.x = origin.x + (point.x - origin.x) * scale.x; + point.y = origin.y + (point.y - origin.y) * scale.y; } } } class MovingPointState extends ActiveState { constructor(control, note, notePoint, mousePoint) { - super(control, note, 'moving-point'); + super(control, note, "moving-point"); this._notePoint = notePoint; - this._originalNotePoint = {x: notePoint.x, y: notePoint.y}; + this._originalNotePoint = { x: notePoint.x, y: notePoint.y }; this._originalPosition = mousePoint; - _setNodeState(this._note.groupNode, 'editing'); + _setNodeState(this._note.groupNode, "editing"); } evtCanvasKeyDown(e) { if (e.which === KEY_ESCAPE) { this._notePoint.x = this._originalNotePoint.x; this._notePoint.y = this._originalNotePoint.y; - this._control._state = new SelectedState(this._control, this._note); + this._control._state = new SelectedState( + this._control, + this._note + ); } } @@ -326,9 +355,11 @@ class MovingPointState extends ActiveState { class MovingNoteState extends ActiveState { constructor(control, note, mousePoint) { - super(control, note, 'moving-note'); - this._originalPolygon = [...note.polygon].map( - point => ({x: point.x, y: point.y})); + super(control, note, "moving-note"); + this._originalPolygon = [...note.polygon].map((point) => ({ + x: point.x, + y: point.y, + })); this._originalPosition = mousePoint; } @@ -338,7 +369,10 @@ class MovingNoteState extends ActiveState { this._note.polygon.at(i).x = this._originalPolygon[i].x; this._note.polygon.at(i).y = this._originalPolygon[i].y; } - this._control._state = new SelectedState(this._control, this._note); + this._control._state = new SelectedState( + this._control, + this._note + ); } } @@ -358,9 +392,11 @@ class MovingNoteState extends ActiveState { class ScalingNoteState extends ActiveState { constructor(control, note, mousePoint) { - super(control, note, 'scaling-note'); - this._originalPolygon = [...note.polygon].map( - point => ({x: point.x, y: point.y})); + super(control, note, "scaling-note"); + this._originalPolygon = [...note.polygon].map((point) => ({ + x: point.x, + y: point.y, + })); this._originalMousePoint = mousePoint; this._originalSize = _getNoteSize(note); } @@ -371,7 +407,10 @@ class ScalingNoteState extends ActiveState { this._note.polygon.at(i).x = this._originalPolygon[i].x; this._note.polygon.at(i).y = this._originalPolygon[i].y; } - this._control._state = new SelectedState(this._control, this._note); + this._control._state = new SelectedState( + this._control, + this._note + ); } } @@ -384,12 +423,16 @@ class ScalingNoteState extends ActiveState { const originalPolygonPoint = this._originalPolygon[i]; polygonPoint.x = originalMousePoint.x + - ((originalPolygonPoint.x - originalMousePoint.x) * - (1 + ((mousePoint.x - originalMousePoint.x) / originalSize.x))); + (originalPolygonPoint.x - originalMousePoint.x) * + (1 + + (mousePoint.x - originalMousePoint.x) / + originalSize.x); polygonPoint.y = originalMousePoint.y + - ((originalPolygonPoint.y - originalMousePoint.y) * - (1 + ((mousePoint.y - originalMousePoint.y) / originalSize.y))); + (originalPolygonPoint.y - originalMousePoint.y) * + (1 + + (mousePoint.y - originalMousePoint.y) / + originalSize.y); } } @@ -400,7 +443,7 @@ class ScalingNoteState extends ActiveState { class ReadyToDrawState extends ActiveState { constructor(control) { - super(control, null, 'ready-to-draw'); + super(control, null, "ready-to-draw"); } evtNoteMouseDown(e, hoveredNote) { @@ -411,23 +454,27 @@ class ReadyToDrawState extends ActiveState { const mousePoint = this._getPointFromEvent(e); if (e.shiftKey) { this._control._state = new DrawingRectangleState( - this._control, mousePoint); + this._control, + mousePoint + ); } else { this._control._state = new DrawingPolygonState( - this._control, mousePoint); + this._control, + mousePoint + ); } } } class DrawingRectangleState extends ActiveState { constructor(control, mousePoint) { - super(control, null, 'drawing-rectangle'); + super(control, null, "drawing-rectangle"); this._note = this._createNote(); this._note.polygon.add(new Point(mousePoint.x, mousePoint.y)); this._note.polygon.add(new Point(mousePoint.x, mousePoint.y)); this._note.polygon.add(new Point(mousePoint.x, mousePoint.y)); this._note.polygon.add(new Point(mousePoint.x, mousePoint.y)); - _setNodeState(this._note.groupNode, 'drawing'); + _setNodeState(this._note.groupNode, "drawing"); } evtCanvasMouseUp(e) { @@ -443,7 +490,10 @@ class DrawingRectangleState extends ActiveState { this._control._state = new ReadyToDrawState(this._control); } else { this._control._post.notes.add(this._note); - this._control._state = new SelectedState(this._control, this._note); + this._control._state = new SelectedState( + this._control, + this._note + ); } } @@ -458,11 +508,11 @@ class DrawingRectangleState extends ActiveState { class DrawingPolygonState extends ActiveState { constructor(control, mousePoint) { - super(control, null, 'drawing-polygon'); + super(control, null, "drawing-polygon"); this._note = this._createNote(); this._note.polygon.add(new Point(mousePoint.x, mousePoint.y)); this._note.polygon.add(new Point(mousePoint.x, mousePoint.y)); - _setNodeState(this._note.groupNode, 'drawing'); + _setNodeState(this._note.groupNode, "drawing"); } evtCanvasKeyDown(e) { @@ -502,11 +552,16 @@ class DrawingPolygonState extends ActiveState { } if (e.shiftKey && secondLastPoint) { - const direction = (Math.round( - Math.atan2( - secondLastPoint.y - mousePoint.y, - secondLastPoint.x - mousePoint.x) / - (2 * Math.PI / 4)) + 4) % 4; + const direction = + (Math.round( + Math.atan2( + secondLastPoint.y - mousePoint.y, + secondLastPoint.x - mousePoint.x + ) / + ((2 * Math.PI) / 4) + ) + + 4) % + 4; if (direction === 0 || direction === 2) { lastPoint.x = mousePoint.x; lastPoint.y = secondLastPoint.y; @@ -533,7 +588,10 @@ class DrawingPolygonState extends ActiveState { } else { this._control._deleteDomNode(this._note); this._control._post.notes.add(this._note); - this._control._state = new SelectedState(this._control, this._note); + this._control._state = new SelectedState( + this._control, + this._note + ); } } } @@ -544,48 +602,51 @@ class PostNotesOverlayControl extends events.EventTarget { this._post = post; this._hostNode = hostNode; - this._svgNode = document.createElementNS(svgNS, 'svg'); - this._svgNode.classList.add('resize-listener'); - this._svgNode.classList.add('notes-overlay'); - this._svgNode.setAttribute('preserveAspectRatio', 'none'); - this._svgNode.setAttribute('viewBox', '0 0 1 1'); + this._svgNode = document.createElementNS(svgNS, "svg"); + this._svgNode.classList.add("resize-listener"); + this._svgNode.classList.add("notes-overlay"); + this._svgNode.setAttribute("preserveAspectRatio", "none"); + this._svgNode.setAttribute("viewBox", "0 0 1 1"); for (let note of this._post.notes) { this._createPolygonNode(note); } this._hostNode.appendChild(this._svgNode); - this._post.addEventListener('change', e => this._evtPostChange(e)); - this._post.notes.addEventListener('remove', e => { + this._post.addEventListener("change", (e) => this._evtPostChange(e)); + this._post.notes.addEventListener("remove", (e) => { this._deleteDomNode(e.detail.note); }); - this._post.notes.addEventListener('add', e => { + this._post.notes.addEventListener("add", (e) => { this._createPolygonNode(e.detail.note); }); - const keyHandler = e => this._evtCanvasKeyDown(e); - document.addEventListener('keydown', keyHandler); - this._svgNode.addEventListener( - 'mousedown', e => this._evtCanvasMouseDown(e)); - this._svgNode.addEventListener( - 'mouseup', e => this._evtCanvasMouseUp(e)); - this._svgNode.addEventListener( - 'mousemove', e => this._evtCanvasMouseMove(e)); + const keyHandler = (e) => this._evtCanvasKeyDown(e); + document.addEventListener("keydown", keyHandler); + this._svgNode.addEventListener("mousedown", (e) => + this._evtCanvasMouseDown(e) + ); + this._svgNode.addEventListener("mouseup", (e) => + this._evtCanvasMouseUp(e) + ); + this._svgNode.addEventListener("mousemove", (e) => + this._evtCanvasMouseMove(e) + ); - const wrapperNode = document.createElement('div'); - wrapperNode.classList.add('wrapper'); - this._textNode = document.createElement('div'); - this._textNode.classList.add('note-text'); + const wrapperNode = document.createElement("div"); + wrapperNode.classList.add("wrapper"); + this._textNode = document.createElement("div"); + this._textNode.classList.add("note-text"); this._textNode.appendChild(wrapperNode); - this._textNode.addEventListener( - 'mouseleave', e => this._evtNoteMouseLeave(e)); + this._textNode.addEventListener("mouseleave", (e) => + this._evtNoteMouseLeave(e) + ); document.body.appendChild(this._textNode); - views.monitorNodeRemoval( - this._hostNode, () => { - this._hostNode.removeChild(this._svgNode); - document.removeEventListener('keydown', keyHandler); - document.body.removeChild(this._textNode); - this._state = new ReadOnlyState(this); - }); + views.monitorNodeRemoval(this._hostNode, () => { + this._hostNode.removeChild(this._svgNode); + document.removeEventListener("keydown", keyHandler); + document.body.removeChild(this._textNode); + this._state = new ReadOnlyState(this); + }); this._state = new ReadOnlyState(this); } @@ -613,7 +674,7 @@ class PostNotesOverlayControl extends events.EventTarget { } _evtCanvasKeyDown(e) { - const illegalNodeNames = ['textarea', 'input', 'select']; + const illegalNodeNames = ["textarea", "input", "select"]; if (illegalNodeNames.includes(e.target.nodeName.toLowerCase())) { return; } @@ -655,53 +716,58 @@ class PostNotesOverlayControl extends events.EventTarget { _evtNoteMouseLeave(e) { const newElement = e.relatedTarget; - if (newElement === this._svgNode || - (!this._svgNode.contains(newElement) && + if ( + newElement === this._svgNode || + (!this._svgNode.contains(newElement) && !this._textNode.contains(newElement) && - newElement !== this._textNode)) { + newElement !== this._textNode) + ) { this._hideNoteText(); } } _showNoteText(note) { - this._textNode.querySelector('.wrapper').innerHTML = - misc.formatMarkdown(note.text); - this._textNode.style.display = 'block'; + this._textNode.querySelector( + ".wrapper" + ).innerHTML = misc.formatMarkdown(note.text); + this._textNode.style.display = "block"; const bodyRect = document.body.getBoundingClientRect(); const noteRect = this._textNode.getBoundingClientRect(); const svgRect = this.boundingBox; const centroid = _getNoteCentroid(note); - const x = ( + const x = -bodyRect.left + svgRect.left + - (svgRect.width * centroid.x) - - (noteRect.width / 2)); - const y = ( + svgRect.width * centroid.x - + noteRect.width / 2; + const y = -bodyRect.top + svgRect.top + - (svgRect.height * centroid.y) - - (noteRect.height / 2)); - this._textNode.style.left = x + 'px'; - this._textNode.style.top = y + 'px'; + svgRect.height * centroid.y - + noteRect.height / 2; + this._textNode.style.left = x + "px"; + this._textNode.style.top = y + "px"; } _hideNoteText() { - this._textNode.style.display = 'none'; + this._textNode.style.display = "none"; } _updatePolygonNotePoints(note) { note.polygonNode.setAttribute( - 'points', - [...note.polygon].map( - point => [point.x, point.y].join(',')).join(' ')); + "points", + [...note.polygon] + .map((point) => [point.x, point.y].join(",")) + .join(" ") + ); } _createEdgeNode(point, groupNode) { - const node = document.createElementNS(svgNS, 'ellipse'); - node.setAttribute('cx', point.x); - node.setAttribute('cy', point.y); - node.setAttribute('rx', circleSize / 2 / this.boundingBox.width); - node.setAttribute('ry', circleSize / 2 / this.boundingBox.height); + const node = document.createElementNS(svgNS, "ellipse"); + node.setAttribute("cx", point.x); + node.setAttribute("cy", point.y); + node.setAttribute("rx", circleSize / 2 / this.boundingBox.width); + node.setAttribute("ry", circleSize / 2 / this.boundingBox.height); point.edgeNode = node; groupNode.appendChild(node); } @@ -713,8 +779,8 @@ class PostNotesOverlayControl extends events.EventTarget { _updateEdgeNode(point, note) { this._updatePolygonNotePoints(note); - point.edgeNode.setAttribute('cx', point.x); - point.edgeNode.setAttribute('cy', point.y); + point.edgeNode.setAttribute("cx", point.x); + point.edgeNode.setAttribute("cy", point.y); } _deleteDomNode(note) { @@ -722,17 +788,19 @@ class PostNotesOverlayControl extends events.EventTarget { } _createPolygonNode(note) { - const groupNode = document.createElementNS(svgNS, 'g'); + const groupNode = document.createElementNS(svgNS, "g"); note.groupNode = groupNode; { - const node = document.createElementNS(svgNS, 'polygon'); + const node = document.createElementNS(svgNS, "polygon"); note.polygonNode = node; - node.setAttribute('vector-effect', 'non-scaling-stroke'); - node.setAttribute('stroke-alignment', 'inside'); - node.addEventListener( - 'mouseenter', e => this._evtNoteMouseEnter(e, note)); - node.addEventListener( - 'mouseleave', e => this._evtNoteMouseLeave(e)); + node.setAttribute("vector-effect", "non-scaling-stroke"); + node.setAttribute("stroke-alignment", "inside"); + node.addEventListener("mouseenter", (e) => + this._evtNoteMouseEnter(e, note) + ); + node.addEventListener("mouseleave", (e) => + this._evtNoteMouseLeave(e) + ); this._updatePolygonNotePoints(note); groupNode.appendChild(node); } @@ -740,17 +808,17 @@ class PostNotesOverlayControl extends events.EventTarget { this._createEdgeNode(point, groupNode); } - note.polygon.addEventListener('change', e => { + note.polygon.addEventListener("change", (e) => { this._updateEdgeNode(e.detail.point, note); - this.dispatchEvent(new CustomEvent('change')); + this.dispatchEvent(new CustomEvent("change")); }); - note.polygon.addEventListener('remove', e => { + note.polygon.addEventListener("remove", (e) => { this._deleteEdgeNode(e.detail.point, note); - this.dispatchEvent(new CustomEvent('change')); + this.dispatchEvent(new CustomEvent("change")); }); - note.polygon.addEventListener('add', e => { + note.polygon.addEventListener("add", (e) => { this._createEdgeNode(e.detail.point, groupNode); - this.dispatchEvent(new CustomEvent('change')); + this.dispatchEvent(new CustomEvent("change")); }); this._svgNode.appendChild(groupNode); diff --git a/client/js/controls/post_readonly_sidebar_control.js b/client/js/controls/post_readonly_sidebar_control.js index 388c238..580ef43 100644 --- a/client/js/controls/post_readonly_sidebar_control.js +++ b/client/js/controls/post_readonly_sidebar_control.js @@ -1,14 +1,14 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const events = require('../events.js'); -const views = require('../util/views.js'); -const uri = require('../util/uri.js'); -const misc = require('../util/misc.js'); +const api = require("../api.js"); +const events = require("../events.js"); +const views = require("../util/views.js"); +const uri = require("../util/uri.js"); +const misc = require("../util/misc.js"); -const template = views.getTemplate('post-readonly-sidebar'); -const scoreTemplate = views.getTemplate('score'); -const favTemplate = views.getTemplate('fav'); +const template = views.getTemplate("post-readonly-sidebar"); +const scoreTemplate = views.getTemplate("score"); +const favTemplate = views.getTemplate("fav"); class PostReadonlySidebarControl extends events.EventTarget { constructor(hostNode, post, postContentControl) { @@ -17,19 +17,22 @@ class PostReadonlySidebarControl extends events.EventTarget { this._post = post; this._postContentControl = postContentControl; - post.addEventListener('changeFavorite', e => this._evtChangeFav(e)); - post.addEventListener('changeScore', e => this._evtChangeScore(e)); + post.addEventListener("changeFavorite", (e) => this._evtChangeFav(e)); + post.addEventListener("changeScore", (e) => this._evtChangeScore(e)); - views.replaceContent(this._hostNode, template({ - post: this._post, - enableSafety: api.safetyEnabled(), - canListPosts: api.hasPrivilege('posts:list'), - canEditPosts: api.hasPrivilege('posts:edit'), - canViewTags: api.hasPrivilege('tags:view'), - escapeColons: uri.escapeColons, - extractRootDomain: uri.extractRootDomain, - getPrettyTagName: misc.getPrettyTagName, - })); + views.replaceContent( + this._hostNode, + template({ + post: this._post, + enableSafety: api.safetyEnabled(), + canListPosts: api.hasPrivilege("posts:list"), + canEditPosts: api.hasPrivilege("posts:edit"), + canViewTags: api.hasPrivilege("tags:view"), + escapeColons: uri.escapeColons, + extractRootDomain: uri.extractRootDomain, + getPrettyTagName: misc.getPrettyTagName, + }) + ); this._installFav(); this._installScore(); @@ -38,58 +41,62 @@ class PostReadonlySidebarControl extends events.EventTarget { } get _scoreContainerNode() { - return this._hostNode.querySelector('.score-container'); + return this._hostNode.querySelector(".score-container"); } get _favContainerNode() { - return this._hostNode.querySelector('.fav-container'); + return this._hostNode.querySelector(".fav-container"); } get _upvoteButtonNode() { - return this._hostNode.querySelector('.upvote'); + return this._hostNode.querySelector(".upvote"); } get _downvoteButtonNode() { - return this._hostNode.querySelector('.downvote'); + return this._hostNode.querySelector(".downvote"); } get _addFavButtonNode() { - return this._hostNode.querySelector('.add-favorite'); + return this._hostNode.querySelector(".add-favorite"); } get _remFavButtonNode() { - return this._hostNode.querySelector('.remove-favorite'); + return this._hostNode.querySelector(".remove-favorite"); } get _fitBothButtonNode() { - return this._hostNode.querySelector('.fit-both'); + return this._hostNode.querySelector(".fit-both"); } get _fitOriginalButtonNode() { - return this._hostNode.querySelector('.fit-original'); + return this._hostNode.querySelector(".fit-original"); } get _fitWidthButtonNode() { - return this._hostNode.querySelector('.fit-width'); + return this._hostNode.querySelector(".fit-width"); } get _fitHeightButtonNode() { - return this._hostNode.querySelector('.fit-height'); + return this._hostNode.querySelector(".fit-height"); } _installFitButtons() { this._fitBothButtonNode.addEventListener( - 'click', this._eventZoomProxy( - () => this._postContentControl.fitBoth())); + "click", + this._eventZoomProxy(() => this._postContentControl.fitBoth()) + ); this._fitOriginalButtonNode.addEventListener( - 'click', this._eventZoomProxy( - () => this._postContentControl.fitOriginal())); + "click", + this._eventZoomProxy(() => this._postContentControl.fitOriginal()) + ); this._fitWidthButtonNode.addEventListener( - 'click', this._eventZoomProxy( - () => this._postContentControl.fitWidth())); + "click", + this._eventZoomProxy(() => this._postContentControl.fitWidth()) + ); this._fitHeightButtonNode.addEventListener( - 'click', this._eventZoomProxy( - () => this._postContentControl.fitHeight())); + "click", + this._eventZoomProxy(() => this._postContentControl.fitHeight()) + ); } _installFav() { @@ -98,16 +105,19 @@ class PostReadonlySidebarControl extends events.EventTarget { favTemplate({ favoriteCount: this._post.favoriteCount, ownFavorite: this._post.ownFavorite, - canFavorite: api.hasPrivilege('posts:favorite'), - })); + canFavorite: api.hasPrivilege("posts:favorite"), + }) + ); if (this._addFavButtonNode) { - this._addFavButtonNode.addEventListener( - 'click', e => this._evtAddToFavoritesClick(e)); + this._addFavButtonNode.addEventListener("click", (e) => + this._evtAddToFavoritesClick(e) + ); } if (this._remFavButtonNode) { - this._remFavButtonNode.addEventListener( - 'click', e => this._evtRemoveFromFavoritesClick(e)); + this._remFavButtonNode.addEventListener("click", (e) => + this._evtRemoveFromFavoritesClick(e) + ); } } @@ -117,77 +127,88 @@ class PostReadonlySidebarControl extends events.EventTarget { scoreTemplate({ score: this._post.score, ownScore: this._post.ownScore, - canScore: api.hasPrivilege('posts:score'), - })); + canScore: api.hasPrivilege("posts:score"), + }) + ); if (this._upvoteButtonNode) { - this._upvoteButtonNode.addEventListener( - 'click', e => this._evtScoreClick(e, 1)); + this._upvoteButtonNode.addEventListener("click", (e) => + this._evtScoreClick(e, 1) + ); } if (this._downvoteButtonNode) { - this._downvoteButtonNode.addEventListener( - 'click', e => this._evtScoreClick(e, -1)); + this._downvoteButtonNode.addEventListener("click", (e) => + this._evtScoreClick(e, -1) + ); } } _eventZoomProxy(func) { - return e => { + return (e) => { e.preventDefault(); e.target.blur(); func(); this._syncFitButton(); - this.dispatchEvent(new CustomEvent('fitModeChange', { - detail: { - mode: this._getFitMode(), - }, - })); + this.dispatchEvent( + new CustomEvent("fitModeChange", { + detail: { + mode: this._getFitMode(), + }, + }) + ); }; } _getFitMode() { const funcToName = {}; - funcToName[this._postContentControl.fitBoth] = 'fit-both'; - funcToName[this._postContentControl.fitOriginal] = 'fit-original'; - funcToName[this._postContentControl.fitWidth] = 'fit-width'; - funcToName[this._postContentControl.fitHeight] = 'fit-height'; + funcToName[this._postContentControl.fitBoth] = "fit-both"; + funcToName[this._postContentControl.fitOriginal] = "fit-original"; + funcToName[this._postContentControl.fitWidth] = "fit-width"; + funcToName[this._postContentControl.fitHeight] = "fit-height"; return funcToName[this._postContentControl._currentFitFunction]; } _syncFitButton() { const className = this._getFitMode(); - const oldNode = this._hostNode.querySelector('.zoom a.active'); + const oldNode = this._hostNode.querySelector(".zoom a.active"); const newNode = this._hostNode.querySelector(`.zoom a.${className}`); if (oldNode) { - oldNode.classList.remove('active'); + oldNode.classList.remove("active"); } - newNode.classList.add('active'); + newNode.classList.add("active"); } _evtAddToFavoritesClick(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('favorite', { - detail: { - post: this._post, - }, - })); + this.dispatchEvent( + new CustomEvent("favorite", { + detail: { + post: this._post, + }, + }) + ); } _evtRemoveFromFavoritesClick(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('unfavorite', { - detail: { - post: this._post, - }, - })); + this.dispatchEvent( + new CustomEvent("unfavorite", { + detail: { + post: this._post, + }, + }) + ); } _evtScoreClick(e, score) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('score', { - detail: { - post: this._post, - score: this._post.ownScore === score ? 0 : score, - }, - })); + this.dispatchEvent( + new CustomEvent("score", { + detail: { + post: this._post, + score: this._post.ownScore === score ? 0 : score, + }, + }) + ); } _evtChangeFav(e) { diff --git a/client/js/controls/tag_auto_complete_control.js b/client/js/controls/tag_auto_complete_control.js index f8d3f6c..8ee6aa8 100644 --- a/client/js/controls/tag_auto_complete_control.js +++ b/client/js/controls/tag_auto_complete_control.js @@ -1,51 +1,63 @@ -'use strict'; +"use strict"; -const misc = require('../util/misc.js'); -const views = require('../util/views.js'); -const TagList = require('../models/tag_list.js'); -const AutoCompleteControl = require('./auto_complete_control.js'); +const misc = require("../util/misc.js"); +const views = require("../util/views.js"); +const TagList = require("../models/tag_list.js"); +const AutoCompleteControl = require("./auto_complete_control.js"); function _tagListToMatches(tags, options) { - return [...tags].sort((tag1, tag2) => { - return tag2.usages - tag1.usages; - }).map(tag => { - let cssName = misc.makeCssName(tag.category, 'tag'); - if (options.isTaggedWith(tag.names[0])) { - cssName += ' disabled'; - } - const caption = ( - '<span class="' + cssName + '">' - + misc.escapeHtml(tag.names[0] + ' (' + tag.postCount + ')') - + '</span>'); - return { - caption: caption, - value: tag, - }; - }); + return [...tags] + .sort((tag1, tag2) => { + return tag2.usages - tag1.usages; + }) + .map((tag) => { + let cssName = misc.makeCssName(tag.category, "tag"); + if (options.isTaggedWith(tag.names[0])) { + cssName += " disabled"; + } + const caption = + '<span class="' + + cssName + + '">' + + misc.escapeHtml(tag.names[0] + " (" + tag.postCount + ")") + + "</span>"; + return { + caption: caption, + value: tag, + }; + }); } class TagAutoCompleteControl extends AutoCompleteControl { constructor(input, options) { const minLengthForPartialSearch = 3; - options = Object.assign({ - isTaggedWith: tag => false, - }, options); + options = Object.assign( + { + isTaggedWith: (tag) => false, + }, + options + ); - options.getMatches = text => { + options.getMatches = (text) => { const term = misc.escapeSearchTerm(text); - const query = ( - text.length < minLengthForPartialSearch - ? term + '*' - : '*' + term + '*') + ' sort:usages'; + const query = + (text.length < minLengthForPartialSearch + ? term + "*" + : "*" + term + "*") + " sort:usages"; return new Promise((resolve, reject) => { - TagList.search( - query, 0, this._options.maxResults, ['names', 'category', 'usages']) - .then( - response => resolve( - _tagListToMatches(response.results, this._options)), - reject); + TagList.search(query, 0, this._options.maxResults, [ + "names", + "category", + "usages", + ]).then( + (response) => + resolve( + _tagListToMatches(response.results, this._options) + ), + reject + ); }); }; diff --git a/client/js/controls/tag_input_control.js b/client/js/controls/tag_input_control.js index e0b1339..7d069f9 100644 --- a/client/js/controls/tag_input_control.js +++ b/client/js/controls/tag_input_control.js @@ -1,25 +1,25 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const tags = require('../tags.js'); -const misc = require('../util/misc.js'); -const uri = require('../util/uri.js'); -const Tag = require('../models/tag.js'); -const settings = require('../models/settings.js'); -const events = require('../events.js'); -const views = require('../util/views.js'); -const TagAutoCompleteControl = require('./tag_auto_complete_control.js'); +const api = require("../api.js"); +const tags = require("../tags.js"); +const misc = require("../util/misc.js"); +const uri = require("../util/uri.js"); +const Tag = require("../models/tag.js"); +const settings = require("../models/settings.js"); +const events = require("../events.js"); +const views = require("../util/views.js"); +const TagAutoCompleteControl = require("./tag_auto_complete_control.js"); const KEY_SPACE = 32; const KEY_RETURN = 13; -const SOURCE_INIT = 'init'; -const SOURCE_IMPLICATION = 'implication'; -const SOURCE_USER_INPUT = 'user-input'; -const SOURCE_SUGGESTION = 'suggestions'; -const SOURCE_CLIPBOARD = 'clipboard'; +const SOURCE_INIT = "init"; +const SOURCE_IMPLICATION = "implication"; +const SOURCE_USER_INPUT = "user-input"; +const SOURCE_SUGGESTION = "suggestions"; +const SOURCE_CLIPBOARD = "clipboard"; -const template = views.getTemplate('tag-input'); +const template = views.getTemplate("tag-input"); function _fadeOutListItemNodeStatus(listItemNode) { if (listItemNode.classList.length) { @@ -28,8 +28,7 @@ function _fadeOutListItemNodeStatus(listItemNode) { } listItemNode.fadeTimeout = window.setTimeout(() => { while (listItemNode.classList.length) { - listItemNode.classList.remove( - listItemNode.classList.item(0)); + listItemNode.classList.remove(listItemNode.classList.item(0)); } listItemNode.fadeTimeout = null; }, 2500); @@ -51,7 +50,9 @@ class SuggestionList { } set(suggestion, weight) { - if (Object.prototype.hasOwnProperty.call(this._suggestions, suggestion)) { + if ( + Object.prototype.hasOwnProperty.call(this._suggestions, suggestion) + ) { weight = Math.max(weight, this._suggestions[suggestion]); } this._suggestions[suggestion] = weight; @@ -74,8 +75,8 @@ class SuggestionList { let nameDiff = a[0].localeCompare(b[0]); return weightDiff === 0 ? nameDiff : weightDiff; }); - return tuples.map(tuple => { - return {tagName: tuple[0], weight: tuple[1]}; + return tuples.map((tuple) => { + return { tagName: tuple[0], weight: tuple[1] }; }); } } @@ -91,45 +92,58 @@ class TagInputControl extends events.EventTarget { // dom const editAreaNode = template(); this._editAreaNode = editAreaNode; - this._tagInputNode = editAreaNode.querySelector('input'); - this._suggestionsNode = editAreaNode.querySelector('.tag-suggestions'); - this._tagListNode = editAreaNode.querySelector('ul.compact-tags'); + this._tagInputNode = editAreaNode.querySelector("input"); + this._suggestionsNode = editAreaNode.querySelector(".tag-suggestions"); + this._tagListNode = editAreaNode.querySelector("ul.compact-tags"); this._autoCompleteControl = new TagAutoCompleteControl( - this._tagInputNode, { + this._tagInputNode, + { getTextToFind: () => { return this._tagInputNode.value; }, - confirm: tag => { - this._tagInputNode.value = ''; + confirm: (tag) => { + this._tagInputNode.value = ""; // note: tags from autocomplete don't contain implications // so they need to be looked up in API this.addTagByName(tag.names[0], SOURCE_USER_INPUT); }, - delete: tag => { - this._tagInputNode.value = ''; + delete: (tag) => { + this._tagInputNode.value = ""; this.deleteTag(tag); }, verticalShift: -2, - isTaggedWith: tagName => this.tags.isTaggedWith(tagName), - }); + isTaggedWith: (tagName) => this.tags.isTaggedWith(tagName), + } + ); // dom events - this._tagInputNode.addEventListener( - 'keydown', e => this._evtInputKeyDown(e)); - this._tagInputNode.addEventListener( - 'paste', e => this._evtInputPaste(e)); - this._editAreaNode.querySelector('a.opacity').addEventListener( - 'click', e => this._evtToggleSuggestionsPopupOpacityClick(e)); - this._editAreaNode.querySelector('a.close').addEventListener( - 'click', e => this._evtCloseSuggestionsPopupClick(e)); - this._editAreaNode.querySelector('button').addEventListener( - 'click', e => this._evtAddTagButtonClick(e)); + this._tagInputNode.addEventListener("keydown", (e) => + this._evtInputKeyDown(e) + ); + this._tagInputNode.addEventListener("paste", (e) => + this._evtInputPaste(e) + ); + this._editAreaNode + .querySelector("a.opacity") + .addEventListener("click", (e) => + this._evtToggleSuggestionsPopupOpacityClick(e) + ); + this._editAreaNode + .querySelector("a.close") + .addEventListener("click", (e) => + this._evtCloseSuggestionsPopupClick(e) + ); + this._editAreaNode + .querySelector("button") + .addEventListener("click", (e) => this._evtAddTagButtonClick(e)); // show - this._hostNode.style.display = 'none'; + this._hostNode.style.display = "none"; this._hostNode.parentNode.insertBefore( - this._editAreaNode, hostNode.nextSibling); + this._editAreaNode, + hostNode.nextSibling + ); // add existing tags for (let tag of [...this.tags]) { @@ -139,7 +153,10 @@ class TagInputControl extends events.EventTarget { } addTagByText(text, source) { - for (let tagName of text.split(/\s+/).filter(word => word).reverse()) { + for (let tagName of text + .split(/\s+/) + .filter((word) => word) + .reverse()) { this.addTagByName(tagName, source); } } @@ -149,48 +166,60 @@ class TagInputControl extends events.EventTarget { if (!name) { return; } - return Tag.get(name).then(tag => { - return this.addTag(tag, source); - }, () => { - const tag = new Tag(); - tag.names = [name]; - tag.category = null; - return this.addTag(tag, source); - }); + return Tag.get(name).then( + (tag) => { + return this.addTag(tag, source); + }, + () => { + const tag = new Tag(); + tag.names = [name]; + tag.category = null; + return this.addTag(tag, source); + } + ); } addTag(tag, source) { if (source !== SOURCE_INIT && this.tags.isTaggedWith(tag.names[0])) { const listItemNode = this._getListItemNode(tag); if (source !== SOURCE_IMPLICATION) { - listItemNode.classList.add('duplicate'); + listItemNode.classList.add("duplicate"); _fadeOutListItemNodeStatus(listItemNode); } return Promise.resolve(); } - return this.tags.addByName(tag.names[0], false).then(() => { - const listItemNode = this._createListItemNode(tag); - if (!tag.category) { - listItemNode.classList.add('new'); - } - if (source === SOURCE_IMPLICATION) { - listItemNode.classList.add('implication'); - } - this._tagListNode.prependChild(listItemNode); - _fadeOutListItemNodeStatus(listItemNode); + return this.tags + .addByName(tag.names[0], false) + .then(() => { + const listItemNode = this._createListItemNode(tag); + if (!tag.category) { + listItemNode.classList.add("new"); + } + if (source === SOURCE_IMPLICATION) { + listItemNode.classList.add("implication"); + } + this._tagListNode.prependChild(listItemNode); + _fadeOutListItemNodeStatus(listItemNode); - return Promise.all( - tag.implications.map( - implication => this.addTagByName( - implication.names[0], SOURCE_IMPLICATION))); - }).then(() => { - this.dispatchEvent(new CustomEvent('add', { - detail: {tag: tag, source: source}, - })); - this.dispatchEvent(new CustomEvent('change')); - return Promise.resolve(); - }); + return Promise.all( + tag.implications.map((implication) => + this.addTagByName( + implication.names[0], + SOURCE_IMPLICATION + ) + ) + ); + }) + .then(() => { + this.dispatchEvent( + new CustomEvent("add", { + detail: { tag: tag, source: source }, + }) + ); + this.dispatchEvent(new CustomEvent("change")); + return Promise.resolve(); + }); } deleteTag(tag) { @@ -202,25 +231,27 @@ class TagInputControl extends events.EventTarget { this._deleteListItemNode(tag); - this.dispatchEvent(new CustomEvent('remove', { - detail: {tag: tag}, - })); - this.dispatchEvent(new CustomEvent('change')); + this.dispatchEvent( + new CustomEvent("remove", { + detail: { tag: tag }, + }) + ); + this.dispatchEvent(new CustomEvent("change")); } _evtInputPaste(e) { e.preventDefault(); - const pastedText = window.clipboardData ? - window.clipboardData.getData('Text') : - (e.originalEvent || e).clipboardData.getData('text/plain'); + const pastedText = window.clipboardData + ? window.clipboardData.getData("Text") + : (e.originalEvent || e).clipboardData.getData("text/plain"); if (pastedText.length > 2000) { - window.alert('Pasted text is too long.'); + window.alert("Pasted text is too long."); return; } this._hideAutoComplete(); this.addTagByText(pastedText, SOURCE_CLIPBOARD); - this._tagInputNode.value = ''; + this._tagInputNode.value = ""; } _evtCloseSuggestionsPopupClick(e) { @@ -231,7 +262,7 @@ class TagInputControl extends events.EventTarget { _evtAddTagButtonClick(e) { e.preventDefault(); this.addTagByName(this._tagInputNode.value, SOURCE_USER_INPUT); - this._tagInputNode.value = ''; + this._tagInputNode.value = ""; } _evtToggleSuggestionsPopupOpacityClick(e) { @@ -244,36 +275,41 @@ class TagInputControl extends events.EventTarget { e.preventDefault(); this._hideAutoComplete(); this.addTagByText(this._tagInputNode.value, SOURCE_USER_INPUT); - this._tagInputNode.value = ''; + this._tagInputNode.value = ""; } } _createListItemNode(tag) { - const className = tag.category ? - misc.makeCssName(tag.category, 'tag') : - null; + const className = tag.category + ? misc.makeCssName(tag.category, "tag") + : null; - const tagLinkNode = document.createElement('a'); + const tagLinkNode = document.createElement("a"); if (className) { tagLinkNode.classList.add(className); } tagLinkNode.setAttribute( - 'href', uri.formatClientLink('tag', tag.names[0])); + "href", + uri.formatClientLink("tag", tag.names[0]) + ); - const tagIconNode = document.createElement('i'); - tagIconNode.classList.add('fa'); - tagIconNode.classList.add('fa-tag'); + const tagIconNode = document.createElement("i"); + tagIconNode.classList.add("fa"); + tagIconNode.classList.add("fa-tag"); tagLinkNode.appendChild(tagIconNode); - const searchLinkNode = document.createElement('a'); + const searchLinkNode = document.createElement("a"); if (className) { searchLinkNode.classList.add(className); } searchLinkNode.setAttribute( - 'href', uri.formatClientLink( - 'posts', {query: uri.escapeColons(tag.names[0])})); - searchLinkNode.textContent = tag.names[0] + ' '; - searchLinkNode.addEventListener('click', e => { + "href", + uri.formatClientLink("posts", { + query: uri.escapeColons(tag.names[0]), + }) + ); + searchLinkNode.textContent = tag.names[0] + " "; + searchLinkNode.addEventListener("click", (e) => { e.preventDefault(); this._suggestions.clear(); if (tag.postCount > 0) { @@ -284,20 +320,20 @@ class TagInputControl extends events.EventTarget { } }); - const usagesNode = document.createElement('span'); - usagesNode.classList.add('tag-usages'); - usagesNode.setAttribute('data-pseudo-content', tag.postCount); + const usagesNode = document.createElement("span"); + usagesNode.classList.add("tag-usages"); + usagesNode.setAttribute("data-pseudo-content", tag.postCount); - const removalLinkNode = document.createElement('a'); - removalLinkNode.classList.add('remove-tag'); - removalLinkNode.setAttribute('href', ''); - removalLinkNode.setAttribute('data-pseudo-content', '×'); - removalLinkNode.addEventListener('click', e => { + const removalLinkNode = document.createElement("a"); + removalLinkNode.classList.add("remove-tag"); + removalLinkNode.setAttribute("href", ""); + removalLinkNode.setAttribute("data-pseudo-content", "×"); + removalLinkNode.addEventListener("click", (e) => { e.preventDefault(); this.deleteTag(tag); }); - const listItemNode = document.createElement('li'); + const listItemNode = document.createElement("li"); listItemNode.appendChild(removalLinkNode); listItemNode.appendChild(tagLinkNode); listItemNode.appendChild(searchLinkNode); @@ -327,20 +363,25 @@ class TagInputControl extends events.EventTarget { if (!browsingSettings.tagSuggestions) { return; } - api.get( - uri.formatApiLink('tag-siblings', tag.names[0]), - {noProgress: true}) - .then(response => { - return Promise.resolve(response.results); - }, response => { - return Promise.resolve([]); - }).then(siblings => { - const args = siblings.map(s => s.occurrences); + api.get(uri.formatApiLink("tag-siblings", tag.names[0]), { + noProgress: true, + }) + .then( + (response) => { + return Promise.resolve(response.results); + }, + (response) => { + return Promise.resolve([]); + } + ) + .then((siblings) => { + const args = siblings.map((s) => s.occurrences); let maxSiblingOccurrences = Math.max(1, ...args); for (let sibling of siblings) { this._suggestions.set( sibling.tag.names[0], - sibling.occurrences * 4.9 / maxSiblingOccurrences); + (sibling.occurrences * 4.9) / maxSiblingOccurrences + ); } for (let suggestion of tag.suggestions || []) { this._suggestions.set(suggestion, 5); @@ -354,10 +395,10 @@ class TagInputControl extends events.EventTarget { } _refreshSuggestionsPopup() { - if (!this._suggestionsNode.classList.contains('shown')) { + if (!this._suggestionsNode.classList.contains("shown")) { return; } - const listNode = this._suggestionsNode.querySelector('ul'); + const listNode = this._suggestionsNode.querySelector("ul"); listNode.scrollTop = 0; while (listNode.firstChild) { listNode.removeChild(listNode.firstChild); @@ -369,35 +410,36 @@ class TagInputControl extends events.EventTarget { continue; } - const addLinkNode = document.createElement('a'); + const addLinkNode = document.createElement("a"); addLinkNode.textContent = tagName; - addLinkNode.classList.add('add-tag'); - addLinkNode.setAttribute('href', ''); - Tag.get(tagName).then(tag => { + addLinkNode.classList.add("add-tag"); + addLinkNode.setAttribute("href", ""); + Tag.get(tagName).then((tag) => { addLinkNode.classList.add( - misc.makeCssName(tag.category, 'tag')); + misc.makeCssName(tag.category, "tag") + ); }); - addLinkNode.addEventListener('click', e => { + addLinkNode.addEventListener("click", (e) => { e.preventDefault(); listNode.removeChild(listItemNode); this.addTagByName(tagName, SOURCE_SUGGESTION); }); - const weightNode = document.createElement('span'); - weightNode.classList.add('tag-weight'); - weightNode.setAttribute('data-pseudo-content', weight); + const weightNode = document.createElement("span"); + weightNode.classList.add("tag-weight"); + weightNode.setAttribute("data-pseudo-content", weight); - const removalLinkNode = document.createElement('a'); - removalLinkNode.classList.add('remove-tag'); - removalLinkNode.setAttribute('href', ''); - removalLinkNode.setAttribute('data-pseudo-content', '×'); - removalLinkNode.addEventListener('click', e => { + const removalLinkNode = document.createElement("a"); + removalLinkNode.classList.add("remove-tag"); + removalLinkNode.setAttribute("href", ""); + removalLinkNode.setAttribute("data-pseudo-content", "×"); + removalLinkNode.addEventListener("click", (e) => { e.preventDefault(); listNode.removeChild(listItemNode); this._suggestions.ban(tagName); }); - const listItemNode = document.createElement('li'); + const listItemNode = document.createElement("li"); listItemNode.appendChild(removalLinkNode); listItemNode.appendChild(weightNode); listItemNode.appendChild(addLinkNode); @@ -407,19 +449,19 @@ class TagInputControl extends events.EventTarget { _closeSuggestionsPopup() { this._suggestions.clear(); - this._suggestionsNode.classList.remove('shown'); + this._suggestionsNode.classList.remove("shown"); } _removeSuggestionsPopupOpacity() { - this._suggestionsNode.classList.remove('translucent'); + this._suggestionsNode.classList.remove("translucent"); } _toggleSuggestionsPopupOpacity() { - this._suggestionsNode.classList.toggle('translucent'); + this._suggestionsNode.classList.toggle("translucent"); } _openSuggestionsPopup() { - this._suggestionsNode.classList.add('shown'); + this._suggestionsNode.classList.add("shown"); this._refreshSuggestionsPopup(); } diff --git a/client/js/events.js b/client/js/events.js index 945771a..7d6ffa6 100644 --- a/client/js/events.js +++ b/client/js/events.js @@ -1,12 +1,12 @@ -'use strict'; +"use strict"; class EventTarget { constructor() { this.eventTarget = document.createDocumentFragment(); for (let method of [ - 'addEventListener', - 'dispatchEvent', - 'removeEventListener' + "addEventListener", + "dispatchEvent", + "removeEventListener", ]) { this[method] = this.eventTarget[method].bind(this.eventTarget); } @@ -20,17 +20,19 @@ function proxyEvent(source, target, sourceEventType, targetEventType) { if (!targetEventType) { targetEventType = sourceEventType; } - source.addEventListener(sourceEventType, e => { - target.dispatchEvent(new CustomEvent(targetEventType, { - detail: e.detail, - })); + source.addEventListener(sourceEventType, (e) => { + target.dispatchEvent( + new CustomEvent(targetEventType, { + detail: e.detail, + }) + ); }); } module.exports = { - Success: 'success', - Error: 'error', - Info: 'info', + Success: "success", + Error: "error", + Info: "info", proxyEvent: proxyEvent, EventTarget: EventTarget, diff --git a/client/js/main.js b/client/js/main.js index 8406c79..a6e6d6a 100644 --- a/client/js/main.js +++ b/client/js/main.js @@ -1,82 +1,101 @@ -'use strict'; +"use strict"; -require('./util/polyfill.js'); -const misc = require('./util/misc.js'); -const views = require('./util/views.js'); -const router = require('./router.js'); +require("./util/polyfill.js"); +const misc = require("./util/misc.js"); +const views = require("./util/views.js"); +const router = require("./router.js"); -history.scrollRestoration = 'manual'; +history.scrollRestoration = "manual"; -router.exit( - null, - (ctx, next) => { - ctx.state.scrollX = window.scrollX; - ctx.state.scrollY = window.scrollY; - router.replace(router.url, ctx.state); - if (misc.confirmPageExit()) { - next(); - } - }); - -const mousetrap = require('mousetrap'); -router.enter( - null, - (ctx, next) => { - mousetrap.reset(); +router.exit(null, (ctx, next) => { + ctx.state.scrollX = window.scrollX; + ctx.state.scrollY = window.scrollY; + router.replace(router.url, ctx.state); + if (misc.confirmPageExit()) { next(); - }); + } +}); -const tags = require('./tags.js'); -const pools = require('./pools.js'); -const api = require('./api.js'); +const mousetrap = require("mousetrap"); +router.enter(null, (ctx, next) => { + mousetrap.reset(); + next(); +}); -api.fetchConfig().then(() => { - // register controller routes - let controllers = []; - controllers.push(require('./controllers/home_controller.js')); - controllers.push(require('./controllers/help_controller.js')); - controllers.push(require('./controllers/auth_controller.js')); - controllers.push(require('./controllers/password_reset_controller.js')); - controllers.push(require('./controllers/comments_controller.js')); - controllers.push(require('./controllers/snapshots_controller.js')); - controllers.push(require('./controllers/post_detail_controller.js')); - controllers.push(require('./controllers/post_main_controller.js')); - controllers.push(require('./controllers/post_list_controller.js')); - controllers.push(require('./controllers/post_upload_controller.js')); - controllers.push(require('./controllers/tag_controller.js')); - controllers.push(require('./controllers/tag_list_controller.js')); - controllers.push(require('./controllers/tag_categories_controller.js')); - controllers.push(require('./controllers/pool_create_controller.js')); - controllers.push(require('./controllers/pool_controller.js')); - controllers.push(require('./controllers/pool_list_controller.js')); - controllers.push(require('./controllers/pool_categories_controller.js')); - controllers.push(require('./controllers/settings_controller.js')); - controllers.push(require('./controllers/user_controller.js')); - controllers.push(require('./controllers/user_list_controller.js')); - controllers.push(require('./controllers/user_registration_controller.js')); +const tags = require("./tags.js"); +const pools = require("./pools.js"); +const api = require("./api.js"); - // 404 controller needs to be registered last - controllers.push(require('./controllers/not_found_controller.js')); +api.fetchConfig() + .then( + () => { + // register controller routes + let controllers = []; + controllers.push(require("./controllers/home_controller.js")); + controllers.push(require("./controllers/help_controller.js")); + controllers.push(require("./controllers/auth_controller.js")); + controllers.push( + require("./controllers/password_reset_controller.js") + ); + controllers.push(require("./controllers/comments_controller.js")); + controllers.push(require("./controllers/snapshots_controller.js")); + controllers.push( + require("./controllers/post_detail_controller.js") + ); + controllers.push(require("./controllers/post_main_controller.js")); + controllers.push(require("./controllers/post_list_controller.js")); + controllers.push( + require("./controllers/post_upload_controller.js") + ); + controllers.push(require("./controllers/tag_controller.js")); + controllers.push(require("./controllers/tag_list_controller.js")); + controllers.push( + require("./controllers/tag_categories_controller.js") + ); + controllers.push( + require("./controllers/pool_create_controller.js") + ); + controllers.push(require("./controllers/pool_controller.js")); + controllers.push(require("./controllers/pool_list_controller.js")); + controllers.push( + require("./controllers/pool_categories_controller.js") + ); + controllers.push(require("./controllers/settings_controller.js")); + controllers.push(require("./controllers/user_controller.js")); + controllers.push(require("./controllers/user_list_controller.js")); + controllers.push( + require("./controllers/user_registration_controller.js") + ); - for (let controller of controllers) { - controller(router); - } -}, error => { - window.alert('Could not fetch basic configuration from server'); -}).then(() => { - api.loginFromCookies().then(() => { - tags.refreshCategoryColorMap(); - pools.refreshCategoryColorMap(); - router.start(); - }, error => { - if (window.location.href.indexOf('login') !== -1) { - api.forget(); - router.start(); - } else { - const ctx = router.start('/'); - ctx.controller.showError( - 'An error happened while trying to log you in: ' + - error.message); + // 404 controller needs to be registered last + controllers.push(require("./controllers/not_found_controller.js")); + + for (let controller of controllers) { + controller(router); + } + }, + (error) => { + window.alert("Could not fetch basic configuration from server"); } + ) + .then(() => { + api.loginFromCookies().then( + () => { + tags.refreshCategoryColorMap(); + pools.refreshCategoryColorMap(); + router.start(); + }, + (error) => { + if (window.location.href.indexOf("login") !== -1) { + api.forget(); + router.start(); + } else { + const ctx = router.start("/"); + ctx.controller.showError( + "An error happened while trying to log you in: " + + error.message + ); + } + } + ); }); -}); diff --git a/client/js/models/abstract_list.js b/client/js/models/abstract_list.js index fb4dec8..7cf518c 100644 --- a/client/js/models/abstract_list.js +++ b/client/js/models/abstract_list.js @@ -1,6 +1,6 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); +const events = require("../events.js"); class AbstractList extends events.EventTarget { constructor() { @@ -13,13 +13,15 @@ class AbstractList extends events.EventTarget { for (let item of response) { const addedItem = this._itemClass.fromResponse(item); if (addedItem.addEventListener) { - addedItem.addEventListener('delete', e => { + addedItem.addEventListener("delete", (e) => { ret.remove(addedItem); }); - addedItem.addEventListener('change', e => { - ret.dispatchEvent(new CustomEvent('change', { - detail: e.detail, - })); + addedItem.addEventListener("change", (e) => { + ret.dispatchEvent( + new CustomEvent("change", { + detail: e.detail, + }) + ); }); } ret._list.push(addedItem); @@ -29,28 +31,32 @@ class AbstractList extends events.EventTarget { sync(plainList) { this.clear(); - for (let item of (plainList || [])) { + for (let item of plainList || []) { this.add(this.constructor._itemClass.fromResponse(item)); } } add(item) { if (item.addEventListener) { - item.addEventListener('delete', e => { + item.addEventListener("delete", (e) => { this.remove(item); }); - item.addEventListener('change', e => { - this.dispatchEvent(new CustomEvent('change', { - detail: e.detail, - })); + item.addEventListener("change", (e) => { + this.dispatchEvent( + new CustomEvent("change", { + detail: e.detail, + }) + ); }); } this._list.push(item); const detail = {}; detail[this.constructor._itemName] = item; - this.dispatchEvent(new CustomEvent('add', { - detail: detail, - })); + this.dispatchEvent( + new CustomEvent("add", { + detail: detail, + }) + ); } clear() { @@ -67,9 +73,11 @@ class AbstractList extends events.EventTarget { this._list.splice(index, 1); const detail = {}; detail[this.constructor._itemName] = itemToRemove; - this.dispatchEvent(new CustomEvent('remove', { - detail: detail, - })); + this.dispatchEvent( + new CustomEvent("remove", { + detail: detail, + }) + ); return; } } diff --git a/client/js/models/comment.js b/client/js/models/comment.js index e292ec4..4707a3a 100644 --- a/client/js/models/comment.js +++ b/client/js/models/comment.js @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const events = require('../events.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const events = require("../events.js"); class Comment extends events.EventTarget { constructor() { @@ -31,7 +31,7 @@ class Comment extends events.EventTarget { } get text() { - return this._text || ''; + return this._text || ""; } get user() { @@ -63,47 +63,57 @@ class Comment extends events.EventTarget { version: this._version, text: this._text, }; - let promise = this._id ? - api.put(uri.formatApiLink('comment', this.id), detail) : - api.post(uri.formatApiLink('comments'), - Object.assign({postId: this._postId}, detail)); + let promise = this._id + ? api.put(uri.formatApiLink("comment", this.id), detail) + : api.post( + uri.formatApiLink("comments"), + Object.assign({ postId: this._postId }, detail) + ); - return promise.then(response => { + return promise.then((response) => { this._updateFromResponse(response); - this.dispatchEvent(new CustomEvent('change', { - detail: { - comment: this, - }, - })); + this.dispatchEvent( + new CustomEvent("change", { + detail: { + comment: this, + }, + }) + ); return Promise.resolve(); }); } delete() { - return api.delete( - uri.formatApiLink('comment', this.id), - {version: this._version}) - .then(response => { - this.dispatchEvent(new CustomEvent('delete', { - detail: { - comment: this, - }, - })); + return api + .delete(uri.formatApiLink("comment", this.id), { + version: this._version, + }) + .then((response) => { + this.dispatchEvent( + new CustomEvent("delete", { + detail: { + comment: this, + }, + }) + ); return Promise.resolve(); }); } setScore(score) { - return api.put( - uri.formatApiLink('comment', this.id, 'score'), - {score: score}) - .then(response => { + return api + .put(uri.formatApiLink("comment", this.id, "score"), { + score: score, + }) + .then((response) => { this._updateFromResponse(response); - this.dispatchEvent(new CustomEvent('changeScore', { - detail: { - comment: this, - }, - })); + this.dispatchEvent( + new CustomEvent("changeScore", { + detail: { + comment: this, + }, + }) + ); return Promise.resolve(); }); } diff --git a/client/js/models/comment_list.js b/client/js/models/comment_list.js index a8e1150..bae2d7a 100644 --- a/client/js/models/comment_list.js +++ b/client/js/models/comment_list.js @@ -1,12 +1,11 @@ -'use strict'; +"use strict"; -const AbstractList = require('./abstract_list.js'); -const Comment = require('./comment.js'); +const AbstractList = require("./abstract_list.js"); +const Comment = require("./comment.js"); -class CommentList extends AbstractList { -} +class CommentList extends AbstractList {} CommentList._itemClass = Comment; -CommentList._itemName = 'comment'; +CommentList._itemName = "comment"; module.exports = CommentList; diff --git a/client/js/models/info.js b/client/js/models/info.js index 35ba867..6b03b38 100644 --- a/client/js/models/info.js +++ b/client/js/models/info.js @@ -1,22 +1,20 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const Post = require('./post.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const Post = require("./post.js"); class Info { static get() { - return api.get(uri.formatApiLink('info')) - .then(response => { - return Promise.resolve(Object.assign( - {}, - response, - { - featuredPost: response.featuredPost ? - Post.fromResponse(response.featuredPost) : - undefined - })); - }); + return api.get(uri.formatApiLink("info")).then((response) => { + return Promise.resolve( + Object.assign({}, response, { + featuredPost: response.featuredPost + ? Post.fromResponse(response.featuredPost) + : undefined, + }) + ); + }); } } diff --git a/client/js/models/note.js b/client/js/models/note.js index 1709d44..87c8b3b 100644 --- a/client/js/models/note.js +++ b/client/js/models/note.js @@ -1,13 +1,13 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const Point = require('./point.js'); -const PointList = require('./point_list.js'); +const events = require("../events.js"); +const Point = require("./point.js"); +const PointList = require("./point_list.js"); class Note extends events.EventTarget { constructor() { super(); - this._text = '…'; + this._text = "…"; this._polygon = new PointList(); } diff --git a/client/js/models/note_list.js b/client/js/models/note_list.js index b54d7f3..10db435 100644 --- a/client/js/models/note_list.js +++ b/client/js/models/note_list.js @@ -1,12 +1,11 @@ -'use strict'; +"use strict"; -const AbstractList = require('./abstract_list.js'); -const Note = require('./note.js'); +const AbstractList = require("./abstract_list.js"); +const Note = require("./note.js"); -class NoteList extends AbstractList { -} +class NoteList extends AbstractList {} NoteList._itemClass = Note; -NoteList._itemName = 'note'; +NoteList._itemName = "note"; module.exports = NoteList; diff --git a/client/js/models/point.js b/client/js/models/point.js index bbb05ec..70b4961 100644 --- a/client/js/models/point.js +++ b/client/js/models/point.js @@ -1,6 +1,6 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); +const events = require("../events.js"); class Point extends events.EventTarget { constructor(x, y) { @@ -19,12 +19,16 @@ class Point extends events.EventTarget { set x(value) { this._x = value; - this.dispatchEvent(new CustomEvent('change', {detail: {point: this}})); + this.dispatchEvent( + new CustomEvent("change", { detail: { point: this } }) + ); } set y(value) { this._y = value; - this.dispatchEvent(new CustomEvent('change', {detail: {point: this}})); + this.dispatchEvent( + new CustomEvent("change", { detail: { point: this } }) + ); } } diff --git a/client/js/models/point_list.js b/client/js/models/point_list.js index 166e664..3ecd7d7 100644 --- a/client/js/models/point_list.js +++ b/client/js/models/point_list.js @@ -1,7 +1,7 @@ -'use strict'; +"use strict"; -const AbstractList = require('./abstract_list.js'); -const Point = require('./point.js'); +const AbstractList = require("./abstract_list.js"); +const Point = require("./point.js"); class PointList extends AbstractList { get firstPoint() { @@ -18,6 +18,6 @@ class PointList extends AbstractList { } PointList._itemClass = Point; -PointList._itemName = 'point'; +PointList._itemName = "point"; module.exports = PointList; diff --git a/client/js/models/pool.js b/client/js/models/pool.js index 8a86e12..51fa8a0 100644 --- a/client/js/models/pool.js +++ b/client/js/models/pool.js @@ -1,13 +1,13 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const events = require('../events.js'); -const misc = require('../util/misc.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const events = require("../events.js"); +const misc = require("../util/misc.js"); class Pool extends events.EventTarget { constructor() { - const PostList = require('./post_list.js'); + const PostList = require("./post_list.js"); super(); this._orig = {}; @@ -70,14 +70,13 @@ class Pool extends events.EventTarget { } static get(id) { - return api.get(uri.formatApiLink('pool', id)) - .then(response => { - return Promise.resolve(Pool.fromResponse(response)); - }); + return api.get(uri.formatApiLink("pool", id)).then((response) => { + return Promise.resolve(Pool.fromResponse(response)); + }); } save() { - const detail = {version: this._version}; + const detail = { version: this._version }; // send only changed fields to avoid user privilege violation if (misc.arraysDiffer(this._names, this._orig._names, true)) { @@ -90,62 +89,71 @@ class Pool extends events.EventTarget { detail.description = this._description; } if (misc.arraysDiffer(this._posts, this._orig._posts)) { - detail.posts = this._posts.map(post => post.id); + detail.posts = this._posts.map((post) => post.id); } - let promise = this._id ? - api.put(uri.formatApiLink('pool', this._id), detail) : - api.post(uri.formatApiLink('pools'), detail); - return promise - .then(response => { - this._updateFromResponse(response); - this.dispatchEvent(new CustomEvent('change', { + let promise = this._id + ? api.put(uri.formatApiLink("pool", this._id), detail) + : api.post(uri.formatApiLink("pools"), detail); + return promise.then((response) => { + this._updateFromResponse(response); + this.dispatchEvent( + new CustomEvent("change", { detail: { pool: this, }, - })); - return Promise.resolve(); - }); + }) + ); + return Promise.resolve(); + }); } merge(targetId, addAlias) { - return api.get(uri.formatApiLink('pool', targetId)) - .then(response => { - return api.post(uri.formatApiLink('pool-merge'), { + return api + .get(uri.formatApiLink("pool", targetId)) + .then((response) => { + return api.post(uri.formatApiLink("pool-merge"), { removeVersion: this._version, remove: this._id, mergeToVersion: response.version, mergeTo: targetId, }); - }).then(response => { + }) + .then((response) => { if (!addAlias) { return Promise.resolve(response); } - return api.put(uri.formatApiLink('pool', targetId), { + return api.put(uri.formatApiLink("pool", targetId), { version: response.version, names: response.names.concat(this._names), }); - }).then(response => { + }) + .then((response) => { this._updateFromResponse(response); - this.dispatchEvent(new CustomEvent('change', { - detail: { - pool: this, - }, - })); + this.dispatchEvent( + new CustomEvent("change", { + detail: { + pool: this, + }, + }) + ); return Promise.resolve(); }); } delete() { - return api.delete( - uri.formatApiLink('pool', this._id), - {version: this._version}) - .then(response => { - this.dispatchEvent(new CustomEvent('delete', { - detail: { - pool: this, - }, - })); + return api + .delete(uri.formatApiLink("pool", this._id), { + version: this._version, + }) + .then((response) => { + this.dispatchEvent( + new CustomEvent("delete", { + detail: { + pool: this, + }, + }) + ); return Promise.resolve(); }); } diff --git a/client/js/models/pool_category.js b/client/js/models/pool_category.js index 1ce4b24..8c7df46 100644 --- a/client/js/models/pool_category.js +++ b/client/js/models/pool_category.js @@ -1,14 +1,14 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const events = require('../events.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const events = require("../events.js"); class PoolCategory extends events.EventTarget { constructor() { super(); - this._name = ''; - this._color = '#000000'; + this._name = ""; + this._color = "#000000"; this._poolCount = 0; this._isDefault = false; this._origName = null; @@ -50,7 +50,7 @@ class PoolCategory extends events.EventTarget { } save() { - const detail = {version: this._version}; + const detail = { version: this._version }; if (this.name !== this._origName) { detail.name = this.name; @@ -63,34 +63,39 @@ class PoolCategory extends events.EventTarget { return Promise.resolve(); } - let promise = this._origName ? - api.put( - uri.formatApiLink('pool-category', this._origName), - detail) : - api.post(uri.formatApiLink('pool-categories'), detail); + let promise = this._origName + ? api.put( + uri.formatApiLink("pool-category", this._origName), + detail + ) + : api.post(uri.formatApiLink("pool-categories"), detail); - return promise - .then(response => { - this._updateFromResponse(response); - this.dispatchEvent(new CustomEvent('change', { + return promise.then((response) => { + this._updateFromResponse(response); + this.dispatchEvent( + new CustomEvent("change", { detail: { poolCategory: this, }, - })); - return Promise.resolve(); - }); + }) + ); + return Promise.resolve(); + }); } delete() { - return api.delete( - uri.formatApiLink('pool-category', this._origName), - {version: this._version}) - .then(response => { - this.dispatchEvent(new CustomEvent('delete', { - detail: { - poolCategory: this, - }, - })); + return api + .delete(uri.formatApiLink("pool-category", this._origName), { + version: this._version, + }) + .then((response) => { + this.dispatchEvent( + new CustomEvent("delete", { + detail: { + poolCategory: this, + }, + }) + ); return Promise.resolve(); }); } diff --git a/client/js/models/pool_category_list.js b/client/js/models/pool_category_list.js index 2699620..46b7838 100644 --- a/client/js/models/pool_category_list.js +++ b/client/js/models/pool_category_list.js @@ -1,9 +1,9 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const AbstractList = require('./abstract_list.js'); -const PoolCategory = require('./pool_category.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const AbstractList = require("./abstract_list.js"); +const PoolCategory = require("./pool_category.js"); class PoolCategoryList extends AbstractList { constructor() { @@ -11,7 +11,7 @@ class PoolCategoryList extends AbstractList { this._defaultCategory = null; this._origDefaultCategory = null; this._deletedCategories = []; - this.addEventListener('remove', e => this._evtCategoryDeleted(e)); + this.addEventListener("remove", (e) => this._evtCategoryDeleted(e)); } static fromResponse(response) { @@ -27,12 +27,16 @@ class PoolCategoryList extends AbstractList { } static get() { - return api.get(uri.formatApiLink('pool-categories')) - .then(response => { - return Promise.resolve(Object.assign( - {}, - response, - {results: PoolCategoryList.fromResponse(response.results)})); + return api + .get(uri.formatApiLink("pool-categories")) + .then((response) => { + return Promise.resolve( + Object.assign({}, response, { + results: PoolCategoryList.fromResponse( + response.results + ), + }) + ); }); } @@ -57,16 +61,18 @@ class PoolCategoryList extends AbstractList { promises.push( api.put( uri.formatApiLink( - 'pool-category', + "pool-category", this._defaultCategory.name, - 'default'))); + "default" + ) + ) + ); } - return Promise.all(promises) - .then(response => { - this._deletedCategories = []; - return Promise.resolve(); - }); + return Promise.all(promises).then((response) => { + this._deletedCategories = []; + return Promise.resolve(); + }); } _evtCategoryDeleted(e) { @@ -77,6 +83,6 @@ class PoolCategoryList extends AbstractList { } PoolCategoryList._itemClass = PoolCategory; -PoolCategoryList._itemName = 'poolCategory'; +PoolCategoryList._itemName = "poolCategory"; module.exports = PoolCategoryList; diff --git a/client/js/models/pool_list.js b/client/js/models/pool_list.js index 8a3b858..a8839bb 100644 --- a/client/js/models/pool_list.js +++ b/client/js/models/pool_list.js @@ -1,25 +1,27 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const AbstractList = require('./abstract_list.js'); -const Pool = require('./pool.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const AbstractList = require("./abstract_list.js"); +const Pool = require("./pool.js"); class PoolList extends AbstractList { static search(text, offset, limit, fields) { - return api.get( - uri.formatApiLink( - 'pools', { + return api + .get( + uri.formatApiLink("pools", { query: text, offset: offset, limit: limit, - fields: fields.join(','), - })) - .then(response => { - return Promise.resolve(Object.assign( - {}, - response, - {results: PoolList.fromResponse(response.results)})); + fields: fields.join(","), + }) + ) + .then((response) => { + return Promise.resolve( + Object.assign({}, response, { + results: PoolList.fromResponse(response.results), + }) + ); }); } @@ -42,6 +44,6 @@ class PoolList extends AbstractList { } PoolList._itemClass = Pool; -PoolList._itemName = 'pool'; +PoolList._itemName = "pool"; module.exports = PoolList; diff --git a/client/js/models/post.js b/client/js/models/post.js index a11d3cb..e90d0b2 100644 --- a/client/js/models/post.js +++ b/client/js/models/post.js @@ -1,15 +1,15 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const tags = require('../tags.js'); -const events = require('../events.js'); -const TagList = require('./tag_list.js'); -const NoteList = require('./note_list.js'); -const CommentList = require('./comment_list.js'); -const PoolList = require('./pool_list.js'); -const Pool = require('./pool.js'); -const misc = require('../util/misc.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const tags = require("../tags.js"); +const events = require("../events.js"); +const TagList = require("./tag_list.js"); +const NoteList = require("./note_list.js"); +const CommentList = require("./comment_list.js"); +const PoolList = require("./pool_list.js"); +const Pool = require("./pool.js"); +const misc = require("../util/misc.js"); class Post extends events.EventTarget { constructor() { @@ -67,7 +67,7 @@ class Post extends events.EventTarget { } get sourceSplit() { - return this._source.split('\n'); + return this._source.split("\n"); } get canvasWidth() { @@ -83,11 +83,11 @@ class Post extends events.EventTarget { } get newContent() { - throw 'Invalid operation'; + throw "Invalid operation"; } get newThumbnail() { - throw 'Invalid operation'; + throw "Invalid operation"; } get flags() { @@ -99,7 +99,7 @@ class Post extends events.EventTarget { } get tagNames() { - return this._tags.map(tag => tag.names[0]); + return this._tags.map((tag) => tag.names[0]); } get notes() { @@ -174,32 +174,31 @@ class Post extends events.EventTarget { static reverseSearch(content) { let apiPromise = api.post( - uri.formatApiLink('posts', 'reverse-search'), + uri.formatApiLink("posts", "reverse-search"), {}, - {content: content}); - let returnedPromise = apiPromise - .then(response => { - if (response.exactPost) { - response.exactPost = Post.fromResponse(response.exactPost); - } - for (let item of response.similarPosts) { - item.post = Post.fromResponse(item.post); - } - return Promise.resolve(response); - }); + { content: content } + ); + let returnedPromise = apiPromise.then((response) => { + if (response.exactPost) { + response.exactPost = Post.fromResponse(response.exactPost); + } + for (let item of response.similarPosts) { + item.post = Post.fromResponse(item.post); + } + return Promise.resolve(response); + }); returnedPromise.abort = () => apiPromise.abort(); return returnedPromise; } static get(id) { - return api.get(uri.formatApiLink('post', id)) - .then(response => { - return Promise.resolve(Post.fromResponse(response)); - }); + return api.get(uri.formatApiLink("post", id)).then((response) => { + return Promise.resolve(Post.fromResponse(response)); + }); } _savePoolPosts() { - const difference = (a, b) => a.filter(post => !b.hasPoolId(post.id)); + const difference = (a, b) => a.filter((post) => !b.hasPoolId(post.id)); // find the pools where the post was added or removed const added = difference(this.pools, this._orig._pools); @@ -209,7 +208,7 @@ class Post extends events.EventTarget { // update each pool's list of posts for (let pool of added) { - let op = Pool.get(pool.id).then(response => { + let op = Pool.get(pool.id).then((response) => { if (!response.posts.hasPostId(this._id)) { response.posts.addById(this._id); return response.save(); @@ -221,7 +220,7 @@ class Post extends events.EventTarget { } for (let pool of removed) { - let op = Pool.get(pool.id).then(response => { + let op = Pool.get(pool.id).then((response) => { if (response.posts.hasPostId(this._id)) { response.posts.removeById(this._id); return response.save(); @@ -237,7 +236,7 @@ class Post extends events.EventTarget { save(anonymous) { const files = {}; - const detail = {version: this._version}; + const detail = { version: this._version }; // send only changed fields to avoid user privilege violation if (anonymous === true) { @@ -250,14 +249,14 @@ class Post extends events.EventTarget { detail.flags = this._flags; } if (misc.arraysDiffer(this._tags, this._orig._tags)) { - detail.tags = this._tags.map(tag => tag.names[0]); + detail.tags = this._tags.map((tag) => tag.names[0]); } if (misc.arraysDiffer(this._relations, this._orig._relations)) { detail.relations = this._relations; } if (misc.arraysDiffer(this._notes, this._orig._notes)) { - detail.notes = this._notes.map(note => ({ - polygon: note.polygon.map(point => [point.x, point.y]), + detail.notes = this._notes.map((note) => ({ + polygon: note.polygon.map((point) => [point.x, point.y]), text: note.text, })); } @@ -271,154 +270,187 @@ class Post extends events.EventTarget { detail.source = this._source; } - let apiPromise = this._id ? - api.put(uri.formatApiLink('post', this.id), detail, files) : - api.post(uri.formatApiLink('posts'), detail, files); + let apiPromise = this._id + ? api.put(uri.formatApiLink("post", this.id), detail, files) + : api.post(uri.formatApiLink("posts"), detail, files); - return apiPromise.then(response => { - if (misc.arraysDiffer(this._pools, this._orig._pools)) { - return this._savePoolPosts() - .then(() => Promise.resolve(response)); - } - return Promise.resolve(response); - }).then(response => { - this._updateFromResponse(response); - this.dispatchEvent( - new CustomEvent('change', {detail: {post: this}})); - if (this._newContent) { - this.dispatchEvent( - new CustomEvent('changeContent', {detail: {post: this}})); - } - if (this._newThumbnail) { - this.dispatchEvent( - new CustomEvent('changeThumbnail', {detail: {post: this}})); - } + return apiPromise + .then((response) => { + if (misc.arraysDiffer(this._pools, this._orig._pools)) { + return this._savePoolPosts().then(() => + Promise.resolve(response) + ); + } + return Promise.resolve(response); + }) + .then( + (response) => { + this._updateFromResponse(response); + this.dispatchEvent( + new CustomEvent("change", { detail: { post: this } }) + ); + if (this._newContent) { + this.dispatchEvent( + new CustomEvent("changeContent", { + detail: { post: this }, + }) + ); + } + if (this._newThumbnail) { + this.dispatchEvent( + new CustomEvent("changeThumbnail", { + detail: { post: this }, + }) + ); + } - return Promise.resolve(); - }, error => { - if (error.response && - error.response.name === 'PostAlreadyUploadedError') { - error.message = - `Post already uploaded (@${error.response.otherPostId})`; - } - return Promise.reject(error); - }); + return Promise.resolve(); + }, + (error) => { + if ( + error.response && + error.response.name === "PostAlreadyUploadedError" + ) { + error.message = `Post already uploaded (@${error.response.otherPostId})`; + } + return Promise.reject(error); + } + ); } feature() { - return api.post( - uri.formatApiLink('featured-post'), - {id: this._id}) - .then(response => { + return api + .post(uri.formatApiLink("featured-post"), { id: this._id }) + .then((response) => { return Promise.resolve(); }); } delete() { - return api.delete( - uri.formatApiLink('post', this.id), - {version: this._version}) - .then(response => { - this.dispatchEvent(new CustomEvent('delete', { - detail: { - post: this, - }, - })); + return api + .delete(uri.formatApiLink("post", this.id), { + version: this._version, + }) + .then((response) => { + this.dispatchEvent( + new CustomEvent("delete", { + detail: { + post: this, + }, + }) + ); return Promise.resolve(); }); } merge(targetId, useOldContent) { - return api.get(uri.formatApiLink('post', targetId)) - .then(response => { - return api.post(uri.formatApiLink('post-merge'), { + return api + .get(uri.formatApiLink("post", targetId)) + .then((response) => { + return api.post(uri.formatApiLink("post-merge"), { removeVersion: this._version, remove: this._id, mergeToVersion: response.version, mergeTo: targetId, replaceContent: useOldContent, }); - }).then(response => { + }) + .then((response) => { this._updateFromResponse(response); - this.dispatchEvent(new CustomEvent('change', { - detail: { - post: this, - }, - })); + this.dispatchEvent( + new CustomEvent("change", { + detail: { + post: this, + }, + }) + ); return Promise.resolve(); }); } setScore(score) { - return api.put( - uri.formatApiLink('post', this.id, 'score'), - {score: score}) - .then(response => { + return api + .put(uri.formatApiLink("post", this.id, "score"), { score: score }) + .then((response) => { const prevFavorite = this._ownFavorite; this._updateFromResponse(response); if (this._ownFavorite !== prevFavorite) { - this.dispatchEvent(new CustomEvent('changeFavorite', { + this.dispatchEvent( + new CustomEvent("changeFavorite", { + detail: { + post: this, + }, + }) + ); + } + this.dispatchEvent( + new CustomEvent("changeScore", { detail: { post: this, }, - })); - } - this.dispatchEvent(new CustomEvent('changeScore', { - detail: { - post: this, - }, - })); + }) + ); return Promise.resolve(); }); } addToFavorites() { - return api.post(uri.formatApiLink('post', this.id, 'favorite')) - .then(response => { + return api + .post(uri.formatApiLink("post", this.id, "favorite")) + .then((response) => { const prevScore = this._ownScore; this._updateFromResponse(response); if (this._ownScore !== prevScore) { - this.dispatchEvent(new CustomEvent('changeScore', { + this.dispatchEvent( + new CustomEvent("changeScore", { + detail: { + post: this, + }, + }) + ); + } + this.dispatchEvent( + new CustomEvent("changeFavorite", { detail: { post: this, }, - })); - } - this.dispatchEvent(new CustomEvent('changeFavorite', { - detail: { - post: this, - }, - })); + }) + ); return Promise.resolve(); }); } removeFromFavorites() { - return api.delete(uri.formatApiLink('post', this.id, 'favorite')) - .then(response => { + return api + .delete(uri.formatApiLink("post", this.id, "favorite")) + .then((response) => { const prevScore = this._ownScore; this._updateFromResponse(response); if (this._ownScore !== prevScore) { - this.dispatchEvent(new CustomEvent('changeScore', { + this.dispatchEvent( + new CustomEvent("changeScore", { + detail: { + post: this, + }, + }) + ); + } + this.dispatchEvent( + new CustomEvent("changeFavorite", { detail: { post: this, }, - })); - } - this.dispatchEvent(new CustomEvent('changeFavorite', { - detail: { - post: this, - }, - })); + }) + ); return Promise.resolve(); }); } mutateContentUrl() { this._contentUrl = - this._orig._contentUrl + - '?bypass-cache=' + - Math.round(Math.random() * 1000); + this._orig._contentUrl + + "?bypass-cache=" + + Math.round(Math.random() * 1000); } _updateFromResponse(response) { @@ -431,15 +463,18 @@ class Post extends events.EventTarget { _user: response.user, _safety: response.safety, _contentUrl: response.contentUrl, - _fullContentUrl: new URL(response.contentUrl, document.getElementsByTagName('base')[0].href).href, + _fullContentUrl: new URL( + response.contentUrl, + document.getElementsByTagName("base")[0].href + ).href, _thumbnailUrl: response.thumbnailUrl, _source: response.source, _canvasWidth: response.canvasWidth, _canvasHeight: response.canvasHeight, _fileSize: response.fileSize, - _flags: [...response.flags || []], - _relations: [...response.relations || []], + _flags: [...(response.flags || [])], + _relations: [...(response.relations || [])], _score: response.score, _commentCount: response.commentCount, diff --git a/client/js/models/post_list.js b/client/js/models/post_list.js index cd94e40..8c2c9d4 100644 --- a/client/js/models/post_list.js +++ b/client/js/models/post_list.js @@ -1,35 +1,37 @@ -'use strict'; +"use strict"; -const settings = require('../models/settings.js'); -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const AbstractList = require('./abstract_list.js'); -const Post = require('./post.js'); +const settings = require("../models/settings.js"); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const AbstractList = require("./abstract_list.js"); +const Post = require("./post.js"); class PostList extends AbstractList { static getAround(id, searchQuery) { return api.get( - uri.formatApiLink( - 'post', id, 'around', { - query: PostList._decorateSearchQuery(searchQuery || ''), - fields: 'id', - })); + uri.formatApiLink("post", id, "around", { + query: PostList._decorateSearchQuery(searchQuery || ""), + fields: "id", + }) + ); } static search(text, offset, limit, fields) { - return api.get( - uri.formatApiLink( - 'posts', { - query: PostList._decorateSearchQuery(text || ''), + return api + .get( + uri.formatApiLink("posts", { + query: PostList._decorateSearchQuery(text || ""), offset: offset, limit: limit, - fields: fields.join(','), - })) - .then(response => { - return Promise.resolve(Object.assign( - {}, - response, - {results: PostList.fromResponse(response.results)})); + fields: fields.join(","), + }) + ) + .then((response) => { + return Promise.resolve( + Object.assign({}, response, { + results: PostList.fromResponse(response.results), + }) + ); }); } @@ -43,7 +45,7 @@ class PostList extends AbstractList { } } if (disabledSafety.length) { - text = `-rating:${disabledSafety.join(',')} ${text}`; + text = `-rating:${disabledSafety.join(",")} ${text}`; } } return text.trim(); @@ -63,7 +65,7 @@ class PostList extends AbstractList { return; } - let post = Post.fromResponse({id: id}); + let post = Post.fromResponse({ id: id }); this.add(post); } @@ -77,6 +79,6 @@ class PostList extends AbstractList { } PostList._itemClass = Post; -PostList._itemName = 'post'; +PostList._itemName = "post"; module.exports = PostList; diff --git a/client/js/models/settings.js b/client/js/models/settings.js index 774aa0b..bd6ac5b 100644 --- a/client/js/models/settings.js +++ b/client/js/models/settings.js @@ -1,6 +1,6 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); +const events = require("../events.js"); const defaultSettings = { listPosts: { @@ -12,7 +12,7 @@ const defaultSettings = { endlessScroll: false, keyboardShortcuts: true, transparencyGrid: true, - fitMode: 'fit-both', + fitMode: "fit-both", tagSuggestions: true, autoplayVideos: false, postsPerPage: 42, @@ -28,7 +28,7 @@ class Settings extends events.EventTarget { _getFromLocalStorage() { let ret = Object.assign({}, defaultSettings); try { - Object.assign(ret, JSON.parse(localStorage.getItem('settings'))); + Object.assign(ret, JSON.parse(localStorage.getItem("settings"))); } catch (e) { // continue regardless of error } @@ -37,14 +37,16 @@ class Settings extends events.EventTarget { save(newSettings, silent) { newSettings = Object.assign(this.cache, newSettings); - localStorage.setItem('settings', JSON.stringify(newSettings)); + localStorage.setItem("settings", JSON.stringify(newSettings)); this.cache = this._getFromLocalStorage(); if (silent !== true) { - this.dispatchEvent(new CustomEvent('change', { - detail: { - settings: this.cache, - }, - })); + this.dispatchEvent( + new CustomEvent("change", { + detail: { + settings: this.cache, + }, + }) + ); } } diff --git a/client/js/models/snapshot.js b/client/js/models/snapshot.js index cf5a308..5f8e2ae 100644 --- a/client/js/models/snapshot.js +++ b/client/js/models/snapshot.js @@ -1,7 +1,7 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const events = require('../events.js'); +const api = require("../api.js"); +const events = require("../events.js"); class Snapshot extends events.EventTarget { constructor() { diff --git a/client/js/models/snapshot_list.js b/client/js/models/snapshot_list.js index 475b89a..9ea1bdd 100644 --- a/client/js/models/snapshot_list.js +++ b/client/js/models/snapshot_list.js @@ -1,24 +1,31 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const AbstractList = require('./abstract_list.js'); -const Snapshot = require('./snapshot.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const AbstractList = require("./abstract_list.js"); +const Snapshot = require("./snapshot.js"); class SnapshotList extends AbstractList { static search(text, offset, limit) { - return api.get(uri.formatApiLink( - 'snapshots', {query: text, offset: offset, limit: limit})) - .then(response => { - return Promise.resolve(Object.assign( - {}, - response, - {results: SnapshotList.fromResponse(response.results)})); + return api + .get( + uri.formatApiLink("snapshots", { + query: text, + offset: offset, + limit: limit, + }) + ) + .then((response) => { + return Promise.resolve( + Object.assign({}, response, { + results: SnapshotList.fromResponse(response.results), + }) + ); }); } } SnapshotList._itemClass = Snapshot; -SnapshotList._itemName = 'snapshot'; +SnapshotList._itemName = "snapshot"; module.exports = SnapshotList; diff --git a/client/js/models/tag.js b/client/js/models/tag.js index b99c63f..a5632c8 100644 --- a/client/js/models/tag.js +++ b/client/js/models/tag.js @@ -1,13 +1,13 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const events = require('../events.js'); -const misc = require('../util/misc.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const events = require("../events.js"); +const misc = require("../util/misc.js"); class Tag extends events.EventTarget { constructor() { - const TagList = require('./tag_list.js'); + const TagList = require("./tag_list.js"); super(); this._orig = {}; @@ -71,14 +71,13 @@ class Tag extends events.EventTarget { } static get(name) { - return api.get(uri.formatApiLink('tag', name)) - .then(response => { - return Promise.resolve(Tag.fromResponse(response)); - }); + return api.get(uri.formatApiLink("tag", name)).then((response) => { + return Promise.resolve(Tag.fromResponse(response)); + }); } save() { - const detail = {version: this._version}; + const detail = { version: this._version }; // send only changed fields to avoid user privilege violation if (misc.arraysDiffer(this._names, this._orig._names, true)) { @@ -92,66 +91,77 @@ class Tag extends events.EventTarget { } if (misc.arraysDiffer(this._implications, this._orig._implications)) { detail.implications = this._implications.map( - relation => relation.names[0]); + (relation) => relation.names[0] + ); } if (misc.arraysDiffer(this._suggestions, this._orig._suggestions)) { detail.suggestions = this._suggestions.map( - relation => relation.names[0]); + (relation) => relation.names[0] + ); } - let promise = this._origName ? - api.put(uri.formatApiLink('tag', this._origName), detail) : - api.post(uri.formatApiLink('tags'), detail); - return promise - .then(response => { - this._updateFromResponse(response); - this.dispatchEvent(new CustomEvent('change', { + let promise = this._origName + ? api.put(uri.formatApiLink("tag", this._origName), detail) + : api.post(uri.formatApiLink("tags"), detail); + return promise.then((response) => { + this._updateFromResponse(response); + this.dispatchEvent( + new CustomEvent("change", { detail: { tag: this, }, - })); - return Promise.resolve(); - }); + }) + ); + return Promise.resolve(); + }); } merge(targetName, addAlias) { - return api.get(uri.formatApiLink('tag', targetName)) - .then(response => { - return api.post(uri.formatApiLink('tag-merge'), { + return api + .get(uri.formatApiLink("tag", targetName)) + .then((response) => { + return api.post(uri.formatApiLink("tag-merge"), { removeVersion: this._version, remove: this._origName, mergeToVersion: response.version, mergeTo: targetName, }); - }).then(response => { + }) + .then((response) => { if (!addAlias) { return Promise.resolve(response); } - return api.put(uri.formatApiLink('tag', targetName), { + return api.put(uri.formatApiLink("tag", targetName), { version: response.version, names: response.names.concat(this._names), }); - }).then(response => { + }) + .then((response) => { this._updateFromResponse(response); - this.dispatchEvent(new CustomEvent('change', { - detail: { - tag: this, - }, - })); + this.dispatchEvent( + new CustomEvent("change", { + detail: { + tag: this, + }, + }) + ); return Promise.resolve(); }); } delete() { - return api.delete( - uri.formatApiLink('tag', this._origName), - {version: this._version}) - .then(response => { - this.dispatchEvent(new CustomEvent('delete', { - detail: { - tag: this, - }, - })); + return api + .delete(uri.formatApiLink("tag", this._origName), { + version: this._version, + }) + .then((response) => { + this.dispatchEvent( + new CustomEvent("delete", { + detail: { + tag: this, + }, + }) + ); return Promise.resolve(); }); } diff --git a/client/js/models/tag_category.js b/client/js/models/tag_category.js index 42e54d8..a8d0e64 100644 --- a/client/js/models/tag_category.js +++ b/client/js/models/tag_category.js @@ -1,14 +1,14 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const events = require('../events.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const events = require("../events.js"); class TagCategory extends events.EventTarget { constructor() { super(); - this._name = ''; - this._color = '#000000'; + this._name = ""; + this._color = "#000000"; this._tagCount = 0; this._isDefault = false; this._origName = null; @@ -50,7 +50,7 @@ class TagCategory extends events.EventTarget { } save() { - const detail = {version: this._version}; + const detail = { version: this._version }; if (this.name !== this._origName) { detail.name = this.name; @@ -63,34 +63,39 @@ class TagCategory extends events.EventTarget { return Promise.resolve(); } - let promise = this._origName ? - api.put( - uri.formatApiLink('tag-category', this._origName), - detail) : - api.post(uri.formatApiLink('tag-categories'), detail); + let promise = this._origName + ? api.put( + uri.formatApiLink("tag-category", this._origName), + detail + ) + : api.post(uri.formatApiLink("tag-categories"), detail); - return promise - .then(response => { - this._updateFromResponse(response); - this.dispatchEvent(new CustomEvent('change', { + return promise.then((response) => { + this._updateFromResponse(response); + this.dispatchEvent( + new CustomEvent("change", { detail: { tagCategory: this, }, - })); - return Promise.resolve(); - }); + }) + ); + return Promise.resolve(); + }); } delete() { - return api.delete( - uri.formatApiLink('tag-category', this._origName), - {version: this._version}) - .then(response => { - this.dispatchEvent(new CustomEvent('delete', { - detail: { - tagCategory: this, - }, - })); + return api + .delete(uri.formatApiLink("tag-category", this._origName), { + version: this._version, + }) + .then((response) => { + this.dispatchEvent( + new CustomEvent("delete", { + detail: { + tagCategory: this, + }, + }) + ); return Promise.resolve(); }); } diff --git a/client/js/models/tag_category_list.js b/client/js/models/tag_category_list.js index 6c1182f..2fc1522 100644 --- a/client/js/models/tag_category_list.js +++ b/client/js/models/tag_category_list.js @@ -1,9 +1,9 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const AbstractList = require('./abstract_list.js'); -const TagCategory = require('./tag_category.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const AbstractList = require("./abstract_list.js"); +const TagCategory = require("./tag_category.js"); class TagCategoryList extends AbstractList { constructor() { @@ -11,7 +11,7 @@ class TagCategoryList extends AbstractList { this._defaultCategory = null; this._origDefaultCategory = null; this._deletedCategories = []; - this.addEventListener('remove', e => this._evtCategoryDeleted(e)); + this.addEventListener("remove", (e) => this._evtCategoryDeleted(e)); } static fromResponse(response) { @@ -27,12 +27,16 @@ class TagCategoryList extends AbstractList { } static get() { - return api.get(uri.formatApiLink('tag-categories')) - .then(response => { - return Promise.resolve(Object.assign( - {}, - response, - {results: TagCategoryList.fromResponse(response.results)})); + return api + .get(uri.formatApiLink("tag-categories")) + .then((response) => { + return Promise.resolve( + Object.assign({}, response, { + results: TagCategoryList.fromResponse( + response.results + ), + }) + ); }); } @@ -57,16 +61,18 @@ class TagCategoryList extends AbstractList { promises.push( api.put( uri.formatApiLink( - 'tag-category', + "tag-category", this._defaultCategory.name, - 'default'))); + "default" + ) + ) + ); } - return Promise.all(promises) - .then(response => { - this._deletedCategories = []; - return Promise.resolve(); - }); + return Promise.all(promises).then((response) => { + this._deletedCategories = []; + return Promise.resolve(); + }); } _evtCategoryDeleted(e) { @@ -77,6 +83,6 @@ class TagCategoryList extends AbstractList { } TagCategoryList._itemClass = TagCategory; -TagCategoryList._itemName = 'tagCategory'; +TagCategoryList._itemName = "tagCategory"; module.exports = TagCategoryList; diff --git a/client/js/models/tag_list.js b/client/js/models/tag_list.js index 31a34fa..7e6b643 100644 --- a/client/js/models/tag_list.js +++ b/client/js/models/tag_list.js @@ -1,25 +1,27 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const AbstractList = require('./abstract_list.js'); -const Tag = require('./tag.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const AbstractList = require("./abstract_list.js"); +const Tag = require("./tag.js"); class TagList extends AbstractList { static search(text, offset, limit, fields) { - return api.get( - uri.formatApiLink( - 'tags', { + return api + .get( + uri.formatApiLink("tags", { query: text, offset: offset, limit: limit, - fields: fields.join(','), - })) - .then(response => { - return Promise.resolve(Object.assign( - {}, - response, - {results: TagList.fromResponse(response.results)})); + fields: fields.join(","), + }) + ) + .then((response) => { + return Promise.resolve( + Object.assign({}, response, { + results: TagList.fromResponse(response.results), + }) + ); }); } @@ -45,10 +47,12 @@ class TagList extends AbstractList { this.add(tag); if (addImplications !== false) { - return Tag.get(tagName).then(actualTag => { + return Tag.get(tagName).then((actualTag) => { return Promise.all( - actualTag.implications.map( - relation => this.addByName(relation.names[0], true))); + actualTag.implications.map((relation) => + this.addByName(relation.names[0], true) + ) + ); }); } @@ -67,6 +71,6 @@ class TagList extends AbstractList { } TagList._itemClass = Tag; -TagList._itemName = 'tag'; +TagList._itemName = "tag"; module.exports = TagList; diff --git a/client/js/models/top_navigation.js b/client/js/models/top_navigation.js index 91b8976..a469a03 100644 --- a/client/js/models/top_navigation.js +++ b/client/js/models/top_navigation.js @@ -1,7 +1,7 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const api = require('../api.js'); +const events = require("../events.js"); +const api = require("../api.js"); class TopNavigationItem { constructor(accessKey, title, url, available, imageUrl) { @@ -44,18 +44,20 @@ class TopNavigation extends events.EventTarget { activate(key) { this.activeItem = null; - this.dispatchEvent(new CustomEvent('activate', { - detail: { - key: key, - item: key ? this.get(key) : null, - }, - })); + this.dispatchEvent( + new CustomEvent("activate", { + detail: { + key: key, + item: key ? this.get(key) : null, + }, + }) + ); } setTitle(title) { api.fetchConfig().then(() => { document.oldTitle = null; - document.title = api.getName() + (title ? (' – ' + title) : ''); + document.title = api.getName() + (title ? " – " + title : ""); }); } @@ -76,24 +78,22 @@ class TopNavigation extends events.EventTarget { function _makeTopNavigation() { const ret = new TopNavigation(); - ret.add('home', new TopNavigationItem('H', 'Home', '')); - ret.add('posts', new TopNavigationItem('P', 'Posts', 'posts')); - ret.add('upload', new TopNavigationItem('U', 'Upload', 'upload')); - ret.add('comments', new TopNavigationItem('C', 'Comments', 'comments')); - ret.add('tags', new TopNavigationItem('T', 'Tags', 'tags')); - ret.add('pools', new TopNavigationItem('O', 'Pools', 'pools')); - ret.add('users', new TopNavigationItem('S', 'Users', 'users')); - ret.add('account', new TopNavigationItem('A', 'Account', 'user/{me}')); - ret.add('register', new TopNavigationItem('R', 'Register', 'register')); - ret.add('login', new TopNavigationItem('L', 'Log in', 'login')); - ret.add('logout', new TopNavigationItem('O', 'Logout', 'logout')); - ret.add('help', new TopNavigationItem('E', 'Help', 'help')); + ret.add("home", new TopNavigationItem("H", "Home", "")); + ret.add("posts", new TopNavigationItem("P", "Posts", "posts")); + ret.add("upload", new TopNavigationItem("U", "Upload", "upload")); + ret.add("comments", new TopNavigationItem("C", "Comments", "comments")); + ret.add("tags", new TopNavigationItem("T", "Tags", "tags")); + ret.add("pools", new TopNavigationItem("O", "Pools", "pools")); + ret.add("users", new TopNavigationItem("S", "Users", "users")); + ret.add("account", new TopNavigationItem("A", "Account", "user/{me}")); + ret.add("register", new TopNavigationItem("R", "Register", "register")); + ret.add("login", new TopNavigationItem("L", "Log in", "login")); + ret.add("logout", new TopNavigationItem("O", "Logout", "logout")); + ret.add("help", new TopNavigationItem("E", "Help", "help")); ret.add( - 'settings', - new TopNavigationItem( - null, - '<i class=\'fa fa-cog\'></i>', - 'settings')); + "settings", + new TopNavigationItem(null, "<i class='fa fa-cog'></i>", "settings") + ); return ret; } diff --git a/client/js/models/user.js b/client/js/models/user.js index 40fbb14..28dc3ef 100644 --- a/client/js/models/user.js +++ b/client/js/models/user.js @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const events = require('../events.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const events = require("../events.js"); class User extends events.EventTarget { constructor() { @@ -64,11 +64,11 @@ class User extends events.EventTarget { } get avatarContent() { - throw 'Invalid operation'; + throw "Invalid operation"; } get password() { - throw 'Invalid operation'; + throw "Invalid operation"; } set name(value) { @@ -102,15 +102,14 @@ class User extends events.EventTarget { } static get(name) { - return api.get(uri.formatApiLink('user', name)) - .then(response => { - return Promise.resolve(User.fromResponse(response)); - }); + return api.get(uri.formatApiLink("user", name)).then((response) => { + return Promise.resolve(User.fromResponse(response)); + }); } save() { const files = []; - const detail = {version: this._version}; + const detail = { version: this._version }; const transient = this._orig._name; if (this._name !== this._orig._name) { @@ -133,33 +132,40 @@ class User extends events.EventTarget { detail.password = this._password; } - let promise = this._orig._name ? - api.put( - uri.formatApiLink('user', this._orig._name), detail, files) : - api.post(uri.formatApiLink('users'), detail, files); + let promise = this._orig._name + ? api.put( + uri.formatApiLink("user", this._orig._name), + detail, + files + ) + : api.post(uri.formatApiLink("users"), detail, files); - return promise - .then(response => { - this._updateFromResponse(response); - this.dispatchEvent(new CustomEvent('change', { + return promise.then((response) => { + this._updateFromResponse(response); + this.dispatchEvent( + new CustomEvent("change", { detail: { user: this, }, - })); - return Promise.resolve(); - }); + }) + ); + return Promise.resolve(); + }); } delete() { - return api.delete( - uri.formatApiLink('user', this._orig._name), - {version: this._version}) - .then(response => { - this.dispatchEvent(new CustomEvent('delete', { - detail: { - user: this, - }, - })); + return api + .delete(uri.formatApiLink("user", this._orig._name), { + version: this._version, + }) + .then((response) => { + this.dispatchEvent( + new CustomEvent("delete", { + detail: { + user: this, + }, + }) + ); return Promise.resolve(); }); } diff --git a/client/js/models/user_list.js b/client/js/models/user_list.js index d44622c..c537f8f 100644 --- a/client/js/models/user_list.js +++ b/client/js/models/user_list.js @@ -1,25 +1,31 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const AbstractList = require('./abstract_list.js'); -const User = require('./user.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const AbstractList = require("./abstract_list.js"); +const User = require("./user.js"); class UserList extends AbstractList { static search(text, offset, limit) { - return api.get( - uri.formatApiLink( - 'users', {query: text, offset: offset, limit: limit})) - .then(response => { - return Promise.resolve(Object.assign( - {}, - response, - {results: UserList.fromResponse(response.results)})); + return api + .get( + uri.formatApiLink("users", { + query: text, + offset: offset, + limit: limit, + }) + ) + .then((response) => { + return Promise.resolve( + Object.assign({}, response, { + results: UserList.fromResponse(response.results), + }) + ); }); } } UserList._itemClass = User; -UserList._itemName = 'user'; +UserList._itemName = "user"; module.exports = UserList; diff --git a/client/js/models/user_token.js b/client/js/models/user_token.js index e49f5aa..c9d28a2 100644 --- a/client/js/models/user_token.js +++ b/client/js/models/user_token.js @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const uri = require('../util/uri.js'); -const events = require('../events.js'); +const api = require("../api.js"); +const uri = require("../util/uri.js"); +const events = require("../events.js"); class UserToken extends events.EventTarget { constructor() { @@ -48,12 +48,12 @@ class UserToken extends events.EventTarget { } static fromResponse(response) { - if (typeof response.results !== 'undefined') { + if (typeof response.results !== "undefined") { let tokenList = []; for (let responseToken of response.results) { const token = new UserToken(); token._updateFromResponse(responseToken); - tokenList.push(token) + tokenList.push(token); } return tokenList; } else { @@ -64,15 +64,16 @@ class UserToken extends events.EventTarget { } static get(userName) { - return api.get(uri.formatApiLink('user-tokens', userName)) - .then(response => { + return api + .get(uri.formatApiLink("user-tokens", userName)) + .then((response) => { return Promise.resolve(UserToken.fromResponse(response)); }); } static create(userName, note, expirationTime) { let userTokenRequest = { - enabled: true + enabled: true, }; if (note) { userTokenRequest.note = note; @@ -80,43 +81,54 @@ class UserToken extends events.EventTarget { if (expirationTime) { userTokenRequest.expirationTime = expirationTime; } - return api.post(uri.formatApiLink('user-token', userName), userTokenRequest) - .then(response => { - return Promise.resolve(UserToken.fromResponse(response)) + return api + .post(uri.formatApiLink("user-token", userName), userTokenRequest) + .then((response) => { + return Promise.resolve(UserToken.fromResponse(response)); }); } save(userName) { - const detail = {version: this._version}; + const detail = { version: this._version }; if (this._note !== this._orig._note) { detail.note = this._note; } - return api.put( - uri.formatApiLink('user-token', userName, this._orig._token), - detail) - .then(response => { + return api + .put( + uri.formatApiLink("user-token", userName, this._orig._token), + detail + ) + .then((response) => { this._updateFromResponse(response); - this.dispatchEvent(new CustomEvent('change', { - detail: { - userToken: this, - }, - })); + this.dispatchEvent( + new CustomEvent("change", { + detail: { + userToken: this, + }, + }) + ); return Promise.resolve(this); }); } delete(userName) { - return api.delete( - uri.formatApiLink('user-token', userName, this._orig._token), - {version: this._version}) - .then(response => { - this.dispatchEvent(new CustomEvent('delete', { - detail: { - userToken: this, - }, - })); + return api + .delete( + uri.formatApiLink("user-token", userName, this._orig._token), + { + version: this._version, + } + ) + .then((response) => { + this.dispatchEvent( + new CustomEvent("delete", { + detail: { + userToken: this, + }, + }) + ); return Promise.resolve(); }); } diff --git a/client/js/pools.js b/client/js/pools.js index 8484f0b..aa58d5e 100644 --- a/client/js/pools.js +++ b/client/js/pools.js @@ -1,22 +1,23 @@ -'use strict'; +"use strict"; -const misc = require('./util/misc.js'); -const PoolCategoryList = require('./models/pool_category_list.js'); +const misc = require("./util/misc.js"); +const PoolCategoryList = require("./models/pool_category_list.js"); let _stylesheet = null; function refreshCategoryColorMap() { - return PoolCategoryList.get().then(response => { + return PoolCategoryList.get().then((response) => { if (_stylesheet) { document.head.removeChild(_stylesheet); } - _stylesheet = document.createElement('style'); + _stylesheet = document.createElement("style"); document.head.appendChild(_stylesheet); for (let category of response.results) { - const ruleName = misc.makeCssName(category.name, 'pool'); + const ruleName = misc.makeCssName(category.name, "pool"); _stylesheet.sheet.insertRule( `.${ruleName} { color: ${category.color} }`, - _stylesheet.sheet.cssRules.length); + _stylesheet.sheet.cssRules.length + ); } }); } diff --git a/client/js/router.js b/client/js/router.js index b5b4855..460a641 100644 --- a/client/js/router.js +++ b/client/js/router.js @@ -1,4 +1,4 @@ -'use strict'; +"use strict"; // modified page.js by visionmedia // - changed regexes to components @@ -10,13 +10,17 @@ // - rename .save() to .replaceState() // - offer .url -const clickEvent = document.ontouchstart ? 'touchstart' : 'click'; -const uri = require('./util/uri.js'); +const clickEvent = document.ontouchstart ? "touchstart" : "click"; +const uri = require("./util/uri.js"); let location = window.history.location || window.location; function _getOrigin() { - return location.protocol + '//' + location.hostname - + (location.port ? (':' + location.port) : ''); + return ( + location.protocol + + "//" + + location.hostname + + (location.port ? ":" + location.port : "") + ); } function _isSameOrigin(href) { @@ -24,15 +28,16 @@ function _isSameOrigin(href) { } function _getBaseHref() { - const bases = document.getElementsByTagName('base'); - return bases.length > 0 ? - bases[0].href.replace(_getOrigin(), '').replace(/\/+$/, '') : ''; + const bases = document.getElementsByTagName("base"); + return bases.length > 0 + ? bases[0].href.replace(_getOrigin(), "").replace(/\/+$/, "") + : ""; } class Context { constructor(path, state) { const base = _getBaseHref(); - path = path.indexOf('/') !== 0 ? '/' + path : path; + path = path.indexOf("/") !== 0 ? "/" + path : path; path = path.indexOf(base) !== 0 ? base + path : path; this.canonicalPath = path; @@ -55,7 +60,7 @@ class Context { class Route { constructor(path) { - this.method = 'GET'; + this.method = "GET"; this.path = path; this.parameterNames = []; @@ -64,16 +69,17 @@ class Route { } else { let parts = []; for (let component of this.path) { - if (component[0] === ':') { - parts.push('([^/]+)'); + if (component[0] === ":") { + parts.push("([^/]+)"); this.parameterNames.push(component.substr(1)); - } else { // assert [a-z]+ + } else { + // assert [a-z]+ parts.push(component); } } - let regexString = '^/' + parts.join('/'); - regexString += '(?:/*|/((?:(?:[a-z]+=[^/]+);)*(?:[a-z]+=[^/]+)))$'; - this.parameterNames.push('variable'); + let regexString = "^/" + parts.join("/"); + regexString += "(?:/*|/((?:(?:[a-z]+=[^/]+);)*(?:[a-z]+=[^/]+)))$"; + this.parameterNames.push("variable"); this.regex = new RegExp(regexString); } } @@ -88,7 +94,7 @@ class Route { } match(path, parameters) { - const qsIndex = path.indexOf('?'); + const qsIndex = path.indexOf("?"); const pathname = ~qsIndex ? path.slice(0, qsIndex) : path; const match = this.regex.exec(pathname); @@ -104,8 +110,8 @@ class Route { continue; } - if (name === 'variable') { - for (let word of (value || '').split(/;/)) { + if (name === "variable") { + for (let word of (value || "").split(/;/)) { const [key, subvalue] = word.split(/=/, 2); parameters[key] = uri.unescapeParam(subvalue); } @@ -148,7 +154,7 @@ class Router { this._running = true; this._onPopState = _onPopState(this); this._onClick = _onClick(this); - window.addEventListener('popstate', this._onPopState, false); + window.addEventListener("popstate", this._onPopState, false); document.addEventListener(clickEvent, this._onClick, false); const url = location.pathname + location.search + location.hash; return this.replace(url, history.state, true); @@ -160,7 +166,7 @@ class Router { } this._running = false; document.removeEventListener(clickEvent, this._onClick, false); - window.removeEventListener('popstate', this._onPopState, false); + window.removeEventListener("popstate", this._onPopState, false); } showNoDispatch(path, state) { @@ -199,11 +205,11 @@ class Router { middle(); next(); }; - const callChain = (this.ctx ? this._exits : []) - .concat( - [swap], - this._callbacks, - [this._unhandled, (ctx, next) => {}]); + const callChain = (this.ctx ? this._exits : []).concat( + [swap], + this._callbacks, + [this._unhandled, (ctx, next) => {}] + ); let i = 0; let fn = () => { @@ -226,20 +232,18 @@ class Router { } } -const _onPopState = router => { +const _onPopState = (router) => { let loaded = false; - if (document.readyState === 'complete') { + if (document.readyState === "complete") { loaded = true; } else { - window.addEventListener( - 'load', - () => { - setTimeout(() => { - loaded = true; - }, 0); - }); + window.addEventListener("load", () => { + setTimeout(() => { + loaded = true; + }, 0); + }); } - return e => { + return (e) => { if (!loaded) { return; } @@ -247,16 +251,13 @@ const _onPopState = router => { const path = e.state.path; router.replace(path, e.state, true); } else { - router.show( - location.pathname + location.hash, - undefined, - false); + router.show(location.pathname + location.hash, undefined, false); } }; }; -const _onClick = router => { - return e => { +const _onClick = (router) => { + return (e) => { if (1 !== _which(e)) { return; } @@ -268,23 +269,25 @@ const _onClick = router => { } let el = e.path ? e.path[0] : e.target; - while (el && el.nodeName !== 'A') { + while (el && el.nodeName !== "A") { el = el.parentNode; } - if (!el || el.nodeName !== 'A') { + if (!el || el.nodeName !== "A") { return; } - if (el.hasAttribute('download') || - el.getAttribute('rel') === 'external') { + if ( + el.hasAttribute("download") || + el.getAttribute("rel") === "external" + ) { return; } - const link = el.getAttribute('href'); - if (el.pathname === location.pathname && (el.hash || '#' === link)) { + const link = el.getAttribute("href"); + if (el.pathname === location.pathname && (el.hash || "#" === link)) { return; } - if (link && link.indexOf('mailto:') > -1) { + if (link && link.indexOf("mailto:") > -1) { return; } if (el.target) { @@ -295,7 +298,7 @@ const _onClick = router => { } const base = _getBaseHref(); - const orig = el.pathname + el.search + (el.hash || ''); + const orig = el.pathname + el.search + (el.hash || ""); const path = !orig.indexOf(base) ? orig.slice(base.length) : orig; if (base && orig === path) { diff --git a/client/js/tags.js b/client/js/tags.js index c037f6f..ecf9b66 100644 --- a/client/js/tags.js +++ b/client/js/tags.js @@ -1,22 +1,23 @@ -'use strict'; +"use strict"; -const misc = require('./util/misc.js'); -const TagCategoryList = require('./models/tag_category_list.js'); +const misc = require("./util/misc.js"); +const TagCategoryList = require("./models/tag_category_list.js"); let _stylesheet = null; function refreshCategoryColorMap() { - return TagCategoryList.get().then(response => { + return TagCategoryList.get().then((response) => { if (_stylesheet) { document.head.removeChild(_stylesheet); } - _stylesheet = document.createElement('style'); + _stylesheet = document.createElement("style"); document.head.appendChild(_stylesheet); for (let category of response.results) { - const ruleName = misc.makeCssName(category.name, 'tag'); + const ruleName = misc.makeCssName(category.name, "tag"); _stylesheet.sheet.insertRule( `.${ruleName} { color: ${category.color} }`, - _stylesheet.sheet.cssRules.length); + _stylesheet.sheet.cssRules.length + ); } }); } diff --git a/client/js/templates.js b/client/js/templates.js index f378cbc..88bd568 100644 --- a/client/js/templates.js +++ b/client/js/templates.js @@ -1,3 +1,3 @@ -'use strict'; +"use strict"; -module.exports = require('./.templates.autogen.js'); +module.exports = require("./.templates.autogen.js"); 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(); } }); diff --git a/client/js/views/comments_page_view.js b/client/js/views/comments_page_view.js index d5bb294..5648b3a 100644 --- a/client/js/views/comments_page_view.js +++ b/client/js/views/comments_page_view.js @@ -1,10 +1,10 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); -const CommentListControl = require('../controls/comment_list_control.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); +const CommentListControl = require("../controls/comment_list_control.js"); -const template = views.getTemplate('comments-page'); +const template = views.getTemplate("comments-page"); class CommentsPageView extends events.EventTarget { constructor(ctx) { @@ -16,12 +16,14 @@ class CommentsPageView extends events.EventTarget { for (let post of ctx.response.results) { const commentListControl = new CommentListControl( sourceNode.querySelector( - `.comments-container[data-for="${post.id}"]`), + `.comments-container[data-for="${post.id}"]` + ), post.comments, - true); - events.proxyEvent(commentListControl, this, 'submit'); - events.proxyEvent(commentListControl, this, 'score'); - events.proxyEvent(commentListControl, this, 'delete'); + true + ); + events.proxyEvent(commentListControl, this, "submit"); + events.proxyEvent(commentListControl, this, "score"); + events.proxyEvent(commentListControl, this, "delete"); } views.replaceContent(this._hostNode, sourceNode); diff --git a/client/js/views/empty_view.js b/client/js/views/empty_view.js index 21843d7..59d336d 100644 --- a/client/js/views/empty_view.js +++ b/client/js/views/empty_view.js @@ -1,15 +1,16 @@ -'use strict'; +"use strict"; -const views = require('../util/views.js'); +const views = require("../util/views.js"); const template = () => { return views.htmlToDom( - '<div class="wrapper"><div class="messages"></div></div>'); + '<div class="wrapper"><div class="messages"></div></div>' + ); }; class EmptyView { constructor() { - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); views.replaceContent(this._hostNode, template()); views.syncScrollPosition(); } diff --git a/client/js/views/endless_page_view.js b/client/js/views/endless_page_view.js index f30b8fb..f94c371 100644 --- a/client/js/views/endless_page_view.js +++ b/client/js/views/endless_page_view.js @@ -1,10 +1,10 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const views = require('../util/views.js'); +const router = require("../router.js"); +const views = require("../util/views.js"); -const holderTemplate = views.getTemplate('endless-pager'); -const pageTemplate = views.getTemplate('endless-pager-page'); +const holderTemplate = views.getTemplate("endless-pager"); +const pageTemplate = views.getTemplate("endless-pager-page"); function isScrolledIntoView(element) { let top = 0; @@ -12,14 +12,12 @@ function isScrolledIntoView(element) { top += element.offsetTop || 0; element = element.offsetParent; } while (element); - return ( - (top >= window.scrollY) && - (top <= window.scrollY + window.innerHeight)); + return top >= window.scrollY && top <= window.scrollY + window.innerHeight; } class EndlessPageView { constructor(ctx) { - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); views.replaceContent(this._hostNode, holderTemplate()); } @@ -40,12 +38,13 @@ class EndlessPageView { this.defaultLimit = parseInt(ctx.parameters.limit || ctx.defaultLimit); const initialOffset = parseInt(ctx.parameters.offset || 0); - this._loadPage(ctx, initialOffset, this.defaultLimit, true) - .then(pageNode => { + this._loadPage(ctx, initialOffset, this.defaultLimit, true).then( + (pageNode) => { if (initialOffset !== 0) { pageNode.scrollIntoView(); } - }); + } + ); this._timeout = window.setInterval(() => { window.requestAnimationFrame(() => { @@ -58,19 +57,19 @@ class EndlessPageView { } get pageHeaderHolderNode() { - return this._hostNode.querySelector('.page-header-holder'); + return this._hostNode.querySelector(".page-header-holder"); } get topPageGuardNode() { - return this._hostNode.querySelector('.page-guard.top'); + return this._hostNode.querySelector(".page-guard.top"); } get bottomPageGuardNode() { - return this._hostNode.querySelector('.page-guard.bottom'); + return this._hostNode.querySelector(".page-guard.bottom"); } get _pagesHolderNode() { - return this._hostNode.querySelector('.pages-holder'); + return this._hostNode.querySelector(".pages-holder"); } _destroy() { @@ -82,9 +81,10 @@ class EndlessPageView { let topPageNode = null; let element = document.elementFromPoint( window.innerWidth / 2, - window.innerHeight / 2); + window.innerHeight / 2 + ); while (element.parentNode !== null) { - if (element.classList.contains('page')) { + if (element.classList.contains("page")) { topPageNode = element; break; } @@ -93,15 +93,17 @@ class EndlessPageView { if (!topPageNode) { return; } - let topOffset = parseInt(topPageNode.getAttribute('data-offset')); - let topLimit = parseInt(topPageNode.getAttribute('data-limit')); + let topOffset = parseInt(topPageNode.getAttribute("data-offset")); + let topLimit = parseInt(topPageNode.getAttribute("data-limit")); if (topOffset !== this.currentOffset) { router.replace( ctx.getClientUrlForPage( topOffset, - topLimit === ctx.defaultLimit ? null : topLimit), + topLimit === ctx.defaultLimit ? null : topLimit + ), ctx.state, - false); + false + ); this.currentOffset = topOffset; } } @@ -115,43 +117,47 @@ class EndlessPageView { return; } - if (this.minOffsetShown > 0 && - isScrolledIntoView(this.topPageGuardNode)) { + if ( + this.minOffsetShown > 0 && + isScrolledIntoView(this.topPageGuardNode) + ) { this._loadPage( ctx, this.minOffsetShown - this.defaultLimit, this.defaultLimit, - false); + false + ); } - if (this.maxOffsetShown < this.totalRecords && - isScrolledIntoView(this.bottomPageGuardNode)) { - this._loadPage( - ctx, - this.maxOffsetShown, - this.defaultLimit, - true); + if ( + this.maxOffsetShown < this.totalRecords && + isScrolledIntoView(this.bottomPageGuardNode) + ) { + this._loadPage(ctx, this.maxOffsetShown, this.defaultLimit, true); } } _loadPage(ctx, offset, limit, append) { this._runningRequests++; return new Promise((resolve, reject) => { - ctx.requestPage(offset, limit).then(response => { - if (!this._active) { + ctx.requestPage(offset, limit).then( + (response) => { + if (!this._active) { + this._runningRequests--; + return Promise.reject(); + } + window.requestAnimationFrame(() => { + let pageNode = this._renderPage(ctx, append, response); + this._runningRequests--; + resolve(pageNode); + }); + }, + (error) => { + this.showError(error.message); this._runningRequests--; - return Promise.reject(); + reject(); } - window.requestAnimationFrame(() => { - let pageNode = this._renderPage(ctx, append, response); - this._runningRequests--; - resolve(pageNode); - }); - }, error => { - this.showError(error.message); - this._runningRequests--; - reject(); - }); + ); }); } @@ -162,30 +168,35 @@ class EndlessPageView { pageNode = pageTemplate({ totalPages: Math.ceil(response.total / response.limit), page: Math.ceil( - (response.offset + response.limit) / response.limit), + (response.offset + response.limit) / response.limit + ), }); - pageNode.setAttribute('data-offset', response.offset); - pageNode.setAttribute('data-limit', response.limit); + pageNode.setAttribute("data-offset", response.offset); + pageNode.setAttribute("data-limit", response.limit); ctx.pageRenderer({ parameters: ctx.parameters, response: response, - hostNode: pageNode.querySelector('.page-content-holder'), + hostNode: pageNode.querySelector(".page-content-holder"), }); this.totalRecords = response.total; - if (response.offset < this.minOffsetShown || - this.minOffsetShown === null) { + if ( + response.offset < this.minOffsetShown || + this.minOffsetShown === null + ) { this.minOffsetShown = response.offset; } - if (response.offset + response.results.length - > this.maxOffsetShown || - this.maxOffsetShown === null) { + if ( + response.offset + response.results.length > + this.maxOffsetShown || + this.maxOffsetShown === null + ) { this.maxOffsetShown = response.offset + response.results.length; } - response.results.addEventListener('remove', e => { + response.results.addEventListener("remove", (e) => { this.maxOffsetShown--; this.totalRecords--; }); @@ -200,10 +211,11 @@ class EndlessPageView { window.scroll( window.scrollX, - window.scrollY + pageNode.offsetHeight); + window.scrollY + pageNode.offsetHeight + ); } } else if (!response.results.length) { - this.showInfo('No data to show'); + this.showInfo("No data to show"); } this._initialPageLoad = false; diff --git a/client/js/views/help_view.js b/client/js/views/help_view.js index 1ab016d..a88b016 100644 --- a/client/js/views/help_view.js +++ b/client/js/views/help_view.js @@ -1,73 +1,81 @@ -'use strict'; +"use strict"; -const api = require('../api.js'); -const views = require('../util/views.js'); +const api = require("../api.js"); +const views = require("../util/views.js"); -const template = views.getTemplate('help'); +const template = views.getTemplate("help"); const sectionTemplates = { - 'about': views.getTemplate('help-about'), - 'keyboard': views.getTemplate('help-keyboard'), - 'search': views.getTemplate('help-search'), - 'comments': views.getTemplate('help-comments'), - 'tos': views.getTemplate('help-tos'), + about: views.getTemplate("help-about"), + keyboard: views.getTemplate("help-keyboard"), + search: views.getTemplate("help-search"), + comments: views.getTemplate("help-comments"), + tos: views.getTemplate("help-tos"), }; const subsectionTemplates = { - 'search': { - 'default': views.getTemplate('help-search-general'), - 'posts': views.getTemplate('help-search-posts'), - 'users': views.getTemplate('help-search-users'), - 'tags': views.getTemplate('help-search-tags'), - 'pools': views.getTemplate('help-search-pools'), + search: { + default: views.getTemplate("help-search-general"), + posts: views.getTemplate("help-search-posts"), + users: views.getTemplate("help-search-users"), + tags: views.getTemplate("help-search-tags"), + pools: views.getTemplate("help-search-pools"), }, }; class HelpView { constructor(section, subsection) { - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); const sourceNode = template(); const ctx = { name: api.getName(), }; - section = section || 'about'; + section = section || "about"; if (section in sectionTemplates) { views.replaceContent( - sourceNode.querySelector('.content'), - sectionTemplates[section](ctx)); + sourceNode.querySelector(".content"), + sectionTemplates[section](ctx) + ); } - subsection = subsection || 'default'; - if (section in subsectionTemplates && - subsection in subsectionTemplates[section]) { + subsection = subsection || "default"; + if ( + section in subsectionTemplates && + subsection in subsectionTemplates[section] + ) { views.replaceContent( - sourceNode.querySelector('.subcontent'), - subsectionTemplates[section][subsection](ctx)); + sourceNode.querySelector(".subcontent"), + subsectionTemplates[section][subsection](ctx) + ); } views.replaceContent(this._hostNode, sourceNode); - for (let itemNode of - sourceNode.querySelectorAll('.primary [data-name]')) { + for (let itemNode of sourceNode.querySelectorAll( + ".primary [data-name]" + )) { itemNode.classList.toggle( - 'active', - itemNode.getAttribute('data-name') === section); - if (itemNode.getAttribute('data-name') === section) { + "active", + itemNode.getAttribute("data-name") === section + ); + if (itemNode.getAttribute("data-name") === section) { itemNode.parentNode.scrollLeft = itemNode.getBoundingClientRect().left - - itemNode.parentNode.getBoundingClientRect().left + itemNode.parentNode.getBoundingClientRect().left; } } - for (let itemNode of - sourceNode.querySelectorAll('.secondary [data-name]')) { + for (let itemNode of sourceNode.querySelectorAll( + ".secondary [data-name]" + )) { itemNode.classList.toggle( - 'active', - itemNode.getAttribute('data-name') === subsection); - if (itemNode.getAttribute('data-name') === subsection) { + "active", + itemNode.getAttribute("data-name") === subsection + ); + if (itemNode.getAttribute("data-name") === subsection) { itemNode.parentNode.scrollLeft = itemNode.getBoundingClientRect().left - - itemNode.parentNode.getBoundingClientRect().left + itemNode.parentNode.getBoundingClientRect().left; } } diff --git a/client/js/views/home_view.js b/client/js/views/home_view.js index 35d4742..c91363b 100644 --- a/client/js/views/home_view.js +++ b/client/js/views/home_view.js @@ -1,22 +1,20 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const uri = require('../util/uri.js'); -const misc = require('../util/misc.js'); -const views = require('../util/views.js'); -const PostContentControl = require('../controls/post_content_control.js'); -const PostNotesOverlayControl - = require('../controls/post_notes_overlay_control.js'); -const TagAutoCompleteControl = - require('../controls/tag_auto_complete_control.js'); +const router = require("../router.js"); +const uri = require("../util/uri.js"); +const misc = require("../util/misc.js"); +const views = require("../util/views.js"); +const PostContentControl = require("../controls/post_content_control.js"); +const PostNotesOverlayControl = require("../controls/post_notes_overlay_control.js"); +const TagAutoCompleteControl = require("../controls/tag_auto_complete_control.js"); -const template = views.getTemplate('home'); -const footerTemplate = views.getTemplate('home-footer'); -const featuredPostTemplate = views.getTemplate('home-featured-post'); +const template = views.getTemplate("home"); +const footerTemplate = views.getTemplate("home-footer"); +const featuredPostTemplate = views.getTemplate("home-featured-post"); class HomeView { constructor(ctx) { - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); this._ctx = ctx; const sourceNode = template(ctx); @@ -27,11 +25,16 @@ class HomeView { this._autoCompleteControl = new TagAutoCompleteControl( this._searchInputNode, { - confirm: tag => this._autoCompleteControl.replaceSelectedText( - misc.escapeSearchTerm(tag.names[0]), true), - }); - this._formNode.addEventListener( - 'submit', e => this._evtFormSubmit(e)); + confirm: (tag) => + this._autoCompleteControl.replaceSelectedText( + misc.escapeSearchTerm(tag.names[0]), + true + ), + } + ); + this._formNode.addEventListener("submit", (e) => + this._evtFormSubmit(e) + ); } } @@ -46,59 +49,67 @@ class HomeView { setStats(stats) { views.replaceContent( this._footerContainerNode, - footerTemplate(Object.assign({}, stats, this._ctx))); + footerTemplate(Object.assign({}, stats, this._ctx)) + ); } setFeaturedPost(postInfo) { views.replaceContent( - this._postInfoContainerNode, featuredPostTemplate(postInfo)); + this._postInfoContainerNode, + featuredPostTemplate(postInfo) + ); if (this._postContainerNode && postInfo.featuredPost) { this._postContentControl = new PostContentControl( this._postContainerNode, postInfo.featuredPost, () => { - return [ - window.innerWidth * 0.8, - window.innerHeight * 0.7, - ]; + return [window.innerWidth * 0.8, window.innerHeight * 0.7]; }, - 'fit-both'); + "fit-both" + ); this._postNotesOverlay = new PostNotesOverlayControl( - this._postContainerNode.querySelector('.post-overlay'), - postInfo.featuredPost); + this._postContainerNode.querySelector(".post-overlay"), + postInfo.featuredPost + ); - if (postInfo.featuredPost.type === 'video' - || postInfo.featuredPost.type === 'flash') { + if ( + postInfo.featuredPost.type === "video" || + postInfo.featuredPost.type === "flash" + ) { this._postContentControl.disableOverlay(); } } } get _footerContainerNode() { - return this._hostNode.querySelector('.footer-container'); + return this._hostNode.querySelector(".footer-container"); } get _postInfoContainerNode() { - return this._hostNode.querySelector('.post-info-container'); + return this._hostNode.querySelector(".post-info-container"); } get _postContainerNode() { - return this._hostNode.querySelector('.post-container'); + return this._hostNode.querySelector(".post-container"); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _searchInputNode() { - return this._formNode.querySelector('input[name=search-text]'); + return this._formNode.querySelector("input[name=search-text]"); } _evtFormSubmit(e) { e.preventDefault(); this._searchInputNode.blur(); - router.show(uri.formatClientLink('posts', {query: this._searchInputNode.value})); + router.show( + uri.formatClientLink("posts", { + query: this._searchInputNode.value, + }) + ); } } diff --git a/client/js/views/login_view.js b/client/js/views/login_view.js index 2c05332..64d49f9 100644 --- a/client/js/views/login_view.js +++ b/client/js/views/login_view.js @@ -1,52 +1,63 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const api = require('../api.js'); -const views = require('../util/views.js'); +const events = require("../events.js"); +const api = require("../api.js"); +const views = require("../util/views.js"); -const template = views.getTemplate('login'); +const template = views.getTemplate("login"); class LoginView extends events.EventTarget { constructor() { super(); - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); - views.replaceContent(this._hostNode, template({ - userNamePattern: api.getUserNameRegex(), - passwordPattern: api.getPasswordRegex(), - canSendMails: api.canSendMails(), - })); + views.replaceContent( + this._hostNode, + template({ + userNamePattern: api.getUserNameRegex(), + passwordPattern: api.getPasswordRegex(), + canSendMails: api.canSendMails(), + }) + ); views.syncScrollPosition(); views.decorateValidator(this._formNode); - this._userNameInputNode.setAttribute('pattern', api.getUserNameRegex()); - this._passwordInputNode.setAttribute('pattern', api.getPasswordRegex()); - this._formNode.addEventListener('submit', e => { + this._userNameInputNode.setAttribute( + "pattern", + api.getUserNameRegex() + ); + this._passwordInputNode.setAttribute( + "pattern", + api.getPasswordRegex() + ); + this._formNode.addEventListener("submit", (e) => { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - name: this._userNameInputNode.value, - password: this._passwordInputNode.value, - remember: this._rememberInputNode.checked, - }, - })); + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + name: this._userNameInputNode.value, + password: this._passwordInputNode.value, + remember: this._rememberInputNode.checked, + }, + }) + ); }); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _userNameInputNode() { - return this._formNode.querySelector('[name=name]'); + return this._formNode.querySelector("[name=name]"); } get _passwordInputNode() { - return this._formNode.querySelector('[name=password]'); + return this._formNode.querySelector("[name=password]"); } get _rememberInputNode() { - return this._formNode.querySelector('[name=remember-user]'); + return this._formNode.querySelector("[name=remember-user]"); } disableForm() { diff --git a/client/js/views/manual_page_view.js b/client/js/views/manual_page_view.js index 6798e9b..390994d 100644 --- a/client/js/views/manual_page_view.js +++ b/client/js/views/manual_page_view.js @@ -1,11 +1,11 @@ -'use strict'; +"use strict"; -const router = require('../router.js'); -const keyboard = require('../util/keyboard.js'); -const views = require('../util/views.js'); +const router = require("../router.js"); +const keyboard = require("../util/keyboard.js"); +const views = require("../util/views.js"); -const holderTemplate = views.getTemplate('manual-pager'); -const navTemplate = views.getTemplate('manual-pager-nav'); +const holderTemplate = views.getTemplate("manual-pager"); +const navTemplate = views.getTemplate("manual-pager-nav"); function _removeConsecutiveDuplicates(a) { return a.filter((item, pos, ary) => { @@ -22,9 +22,7 @@ function _getVisiblePageNumbers(currentPage, totalPages) { for (let i = totalPages - threshold; i <= totalPages; i++) { pagesVisible.push(i); } - for (let i = currentPage - threshold; - i <= currentPage + threshold; - i++) { + for (let i = currentPage - threshold; i <= currentPage + threshold; i++) { pagesVisible.push(i); } pagesVisible = pagesVisible.filter((item, pos, ary) => { @@ -38,18 +36,22 @@ function _getVisiblePageNumbers(currentPage, totalPages) { } function _getPages( - currentPage, pageNumbers, limit, defaultLimit, removedItems) { + currentPage, + pageNumbers, + limit, + defaultLimit, + removedItems +) { const pages = new Map(); let prevPage = 0; for (let page of pageNumbers) { if (page !== prevPage + 1) { - pages.set(page - 1, {ellipsis: true}); + pages.set(page - 1, { ellipsis: true }); } pages.set(page, { number: page, offset: - ((page - 1) * limit) - - (page > currentPage ? removedItems : 0), + (page - 1) * limit - (page > currentPage ? removedItems : 0), limit: limit === defaultLimit ? null : limit, active: currentPage === page, }); @@ -60,7 +62,7 @@ function _getPages( class ManualPageView { constructor(ctx) { - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); views.replaceContent(this._hostNode, holderTemplate()); } @@ -70,52 +72,65 @@ class ManualPageView { this.clearMessages(); views.emptyContent(this._pageNavNode); - ctx.requestPage(offset, limit).then(response => { - ctx.pageRenderer({ - parameters: ctx.parameters, - response: response, - hostNode: this._pageContentHolderNode, - }); + ctx.requestPage(offset, limit).then( + (response) => { + ctx.pageRenderer({ + parameters: ctx.parameters, + response: response, + hostNode: this._pageContentHolderNode, + }); - keyboard.bind(['a', 'left'], () => { - this._navigateToPrevNextPage('prev'); - }); - keyboard.bind(['d', 'right'], () => { - this._navigateToPrevNextPage('next'); - }); + keyboard.bind(["a", "left"], () => { + this._navigateToPrevNextPage("prev"); + }); + keyboard.bind(["d", "right"], () => { + this._navigateToPrevNextPage("next"); + }); - let removedItems = 0; - if (response.total) { - this._refreshNav( - offset, limit, response.total, removedItems, ctx); - } + let removedItems = 0; + if (response.total) { + this._refreshNav( + offset, + limit, + response.total, + removedItems, + ctx + ); + } - if (!response.results.length) { - this.showInfo('No data to show'); - } + if (!response.results.length) { + this.showInfo("No data to show"); + } - response.results.addEventListener('remove', e => { - removedItems++; - this._refreshNav( - offset, limit, response.total, removedItems, ctx); - }); + response.results.addEventListener("remove", (e) => { + removedItems++; + this._refreshNav( + offset, + limit, + response.total, + removedItems, + ctx + ); + }); - views.syncScrollPosition(); - }, response => { - this.showError(response.message); - }); + views.syncScrollPosition(); + }, + (response) => { + this.showError(response.message); + } + ); } get pageHeaderHolderNode() { - return this._hostNode.querySelector('.page-header-holder'); + return this._hostNode.querySelector(".page-header-holder"); } get _pageContentHolderNode() { - return this._hostNode.querySelector('.page-content-holder'); + return this._hostNode.querySelector(".page-content-holder"); } get _pageNavNode() { - return this._hostNode.querySelector('.page-nav'); + return this._hostNode.querySelector(".page-nav"); } clearMessages() { @@ -135,11 +150,11 @@ class ManualPageView { } _navigateToPrevNextPage(className) { - const linkNode = this._hostNode.querySelector('a.' + className); - if (linkNode.classList.contains('disabled')) { + const linkNode = this._hostNode.querySelector("a." + className); + if (linkNode.classList.contains("disabled")) { return; } - router.show(linkNode.getAttribute('href')); + router.show(linkNode.getAttribute("href")); } _refreshNav(offset, limit, total, removedItems, ctx) { @@ -147,7 +162,12 @@ class ManualPageView { const totalPages = Math.ceil((total - removedItems) / limit); const pageNumbers = _getVisiblePageNumbers(currentPage, totalPages); const pages = _getPages( - currentPage, pageNumbers, limit, ctx.defaultLimit, removedItems); + currentPage, + pageNumbers, + limit, + ctx.defaultLimit, + removedItems + ); views.replaceContent( this._pageNavNode, @@ -158,7 +178,8 @@ class ManualPageView { currentPage: currentPage, totalPages: totalPages, pages: pages, - })); + }) + ); } } diff --git a/client/js/views/not_found_view.js b/client/js/views/not_found_view.js index 487613b..c930b09 100644 --- a/client/js/views/not_found_view.js +++ b/client/js/views/not_found_view.js @@ -1,14 +1,14 @@ -'use strict'; +"use strict"; -const views = require('../util/views.js'); +const views = require("../util/views.js"); -const template = views.getTemplate('not-found'); +const template = views.getTemplate("not-found"); class NotFoundView { constructor(path) { - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); - const sourceNode = template({path: path}); + const sourceNode = template({ path: path }); views.replaceContent(this._hostNode, sourceNode); views.syncScrollPosition(); } diff --git a/client/js/views/password_reset_view.js b/client/js/views/password_reset_view.js index 685fe5a..82a7d50 100644 --- a/client/js/views/password_reset_view.js +++ b/client/js/views/password_reset_view.js @@ -1,30 +1,35 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const api = require('../api.js'); -const views = require('../util/views.js'); +const events = require("../events.js"); +const api = require("../api.js"); +const views = require("../util/views.js"); -const template = views.getTemplate('password-reset'); +const template = views.getTemplate("password-reset"); class PasswordResetView extends events.EventTarget { constructor() { super(); - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); - views.replaceContent(this._hostNode, template({ - canSendMails: api.canSendMails(), - contactEmail: api.getContactEmail(), - })); + views.replaceContent( + this._hostNode, + template({ + canSendMails: api.canSendMails(), + contactEmail: api.getContactEmail(), + }) + ); views.syncScrollPosition(); views.decorateValidator(this._formNode); - this._formNode.addEventListener('submit', e => { + this._formNode.addEventListener("submit", (e) => { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - userNameOrEmail: this._userNameOrEmailFieldNode.value, - }, - })); + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + userNameOrEmail: this._userNameOrEmailFieldNode.value, + }, + }) + ); }); } @@ -49,11 +54,11 @@ class PasswordResetView extends events.EventTarget { } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _userNameOrEmailFieldNode() { - return this._formNode.querySelector('[name=user-name]'); + return this._formNode.querySelector("[name=user-name]"); } } diff --git a/client/js/views/pool_categories_view.js b/client/js/views/pool_categories_view.js index 1928358..ac381d5 100644 --- a/client/js/views/pool_categories_view.js +++ b/client/js/views/pool_categories_view.js @@ -1,17 +1,17 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); -const PoolCategory = require('../models/pool_category.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); +const PoolCategory = require("../models/pool_category.js"); -const template = views.getTemplate('pool-categories'); -const rowTemplate = views.getTemplate('pool-category-row'); +const template = views.getTemplate("pool-categories"); +const rowTemplate = views.getTemplate("pool-category-row"); class PoolCategoriesView extends events.EventTarget { constructor(ctx) { super(); this._ctx = ctx; - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); views.replaceContent(this._hostNode, template(ctx)); views.syncScrollPosition(); @@ -31,18 +31,22 @@ class PoolCategoriesView extends events.EventTarget { } if (this._addLinkNode) { - this._addLinkNode.addEventListener( - 'click', e => this._evtAddButtonClick(e)); + this._addLinkNode.addEventListener("click", (e) => + this._evtAddButtonClick(e) + ); } - ctx.poolCategories.addEventListener( - 'add', e => this._evtPoolCategoryAdded(e)); + ctx.poolCategories.addEventListener("add", (e) => + this._evtPoolCategoryAdded(e) + ); - ctx.poolCategories.addEventListener( - 'remove', e => this._evtPoolCategoryDeleted(e)); + ctx.poolCategories.addEventListener("remove", (e) => + this._evtPoolCategoryDeleted(e) + ); - this._formNode.addEventListener( - 'submit', e => this._evtSaveButtonClick(e, ctx)); + this._formNode.addEventListener("submit", (e) => + this._evtSaveButtonClick(e, ctx) + ); } enableForm() { @@ -66,44 +70,48 @@ class PoolCategoriesView extends events.EventTarget { } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _tableBodyNode() { - return this._hostNode.querySelector('tbody'); + return this._hostNode.querySelector("tbody"); } get _addLinkNode() { - return this._hostNode.querySelector('a.add'); + return this._hostNode.querySelector("a.add"); } _addPoolCategoryRowNode(poolCategory) { const rowNode = rowTemplate( - Object.assign( - {}, this._ctx, {poolCategory: poolCategory})); + Object.assign({}, this._ctx, { poolCategory: poolCategory }) + ); - const nameInput = rowNode.querySelector('.name input'); + const nameInput = rowNode.querySelector(".name input"); if (nameInput) { - nameInput.addEventListener( - 'change', e => this._evtNameChange(e, rowNode)); + nameInput.addEventListener("change", (e) => + this._evtNameChange(e, rowNode) + ); } - const colorInput = rowNode.querySelector('.color input'); + const colorInput = rowNode.querySelector(".color input"); if (colorInput) { - colorInput.addEventListener( - 'change', e => this._evtColorChange(e, rowNode)); + colorInput.addEventListener("change", (e) => + this._evtColorChange(e, rowNode) + ); } - const removeLinkNode = rowNode.querySelector('.remove a'); + const removeLinkNode = rowNode.querySelector(".remove a"); if (removeLinkNode) { - removeLinkNode.addEventListener( - 'click', e => this._evtDeleteButtonClick(e, rowNode)); + removeLinkNode.addEventListener("click", (e) => + this._evtDeleteButtonClick(e, rowNode) + ); } - const defaultLinkNode = rowNode.querySelector('.set-default a'); + const defaultLinkNode = rowNode.querySelector(".set-default a"); if (defaultLinkNode) { - defaultLinkNode.addEventListener( - 'click', e => this._evtSetDefaultButtonClick(e, rowNode)); + defaultLinkNode.addEventListener("click", (e) => + this._evtSetDefaultButtonClick(e, rowNode) + ); } this._tableBodyNode.appendChild(rowNode); @@ -141,7 +149,7 @@ class PoolCategoriesView extends events.EventTarget { _evtDeleteButtonClick(e, rowNode, link) { e.preventDefault(); - if (e.target.classList.contains('inactive')) { + if (e.target.classList.contains("inactive")) { return; } this._ctx.poolCategories.remove(rowNode._poolCategory); @@ -150,16 +158,16 @@ class PoolCategoriesView extends events.EventTarget { _evtSetDefaultButtonClick(e, rowNode) { e.preventDefault(); this._ctx.poolCategories.defaultCategory = rowNode._poolCategory; - const oldRowNode = rowNode.parentNode.querySelector('tr.default'); + const oldRowNode = rowNode.parentNode.querySelector("tr.default"); if (oldRowNode) { - oldRowNode.classList.remove('default'); + oldRowNode.classList.remove("default"); } - rowNode.classList.add('default'); + rowNode.classList.add("default"); } _evtSaveButtonClick(e, ctx) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit')); + this.dispatchEvent(new CustomEvent("submit")); } } diff --git a/client/js/views/pool_create_view.js b/client/js/views/pool_create_view.js index f22cf71..fc75f45 100644 --- a/client/js/views/pool_create_view.js +++ b/client/js/views/pool_create_view.js @@ -1,41 +1,43 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const api = require('../api.js'); -const misc = require('../util/misc.js'); -const views = require('../util/views.js'); -const Pool = require('../models/pool.js') +const events = require("../events.js"); +const api = require("../api.js"); +const misc = require("../util/misc.js"); +const views = require("../util/views.js"); +const Pool = require("../models/pool.js"); -const template = views.getTemplate('pool-create'); +const template = views.getTemplate("pool-create"); class PoolCreateView extends events.EventTarget { constructor(ctx) { super(); - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); views.replaceContent(this._hostNode, template(ctx)); views.decorateValidator(this._formNode); if (this._namesFieldNode) { - this._namesFieldNode.addEventListener( - 'input', e => this._evtNameInput(e)); + this._namesFieldNode.addEventListener("input", (e) => + this._evtNameInput(e) + ); } if (this._postsFieldNode) { - this._postsFieldNode.addEventListener( - 'input', e => this._evtPostsInput(e)); + this._postsFieldNode.addEventListener("input", (e) => + this._evtPostsInput(e) + ); } for (let node of this._formNode.querySelectorAll( - 'input, select, textarea, posts')) { - node.addEventListener( - 'change', e => { - this.dispatchEvent(new CustomEvent('change')); - }); + "input, select, textarea, posts" + )) { + node.addEventListener("change", (e) => { + this.dispatchEvent(new CustomEvent("change")); + }); } - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } clearMessages() { @@ -64,19 +66,21 @@ class PoolCreateView extends events.EventTarget { if (!list.length) { this._namesFieldNode.setCustomValidity( - 'Pools must have at least one name.'); + "Pools must have at least one name." + ); return; } for (let item of list) { if (!regex.test(item)) { this._namesFieldNode.setCustomValidity( - `Pool name "${item}" contains invalid symbols.`); + `Pool name "${item}" contains invalid symbols.` + ); return; } } - this._namesFieldNode.setCustomValidity(''); + this._namesFieldNode.setCustomValidity(""); } _evtPostsInput(e) { @@ -86,46 +90,50 @@ class PoolCreateView extends events.EventTarget { for (let item of list) { if (!regex.test(item)) { this._postsFieldNode.setCustomValidity( - `Pool ID "${item}" is not an integer.`); + `Pool ID "${item}" is not an integer.` + ); return; } } - this._postsFieldNode.setCustomValidity(''); + this._postsFieldNode.setCustomValidity(""); } _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - names: misc.splitByWhitespace(this._namesFieldNode.value), - category: this._categoryFieldNode.value, - description: this._descriptionFieldNode.value, - posts: misc.splitByWhitespace(this._postsFieldNode.value) - .map(i => parseInt(i)) - }, - })); + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + names: misc.splitByWhitespace(this._namesFieldNode.value), + category: this._categoryFieldNode.value, + description: this._descriptionFieldNode.value, + posts: misc + .splitByWhitespace(this._postsFieldNode.value) + .map((i) => parseInt(i)), + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _namesFieldNode() { - return this._formNode.querySelector('.names input'); + return this._formNode.querySelector(".names input"); } get _categoryFieldNode() { - return this._formNode.querySelector('.category select'); + return this._formNode.querySelector(".category select"); } get _descriptionFieldNode() { - return this._formNode.querySelector('.description textarea'); + return this._formNode.querySelector(".description textarea"); } get _postsFieldNode() { - return this._formNode.querySelector('.posts input'); + return this._formNode.querySelector(".posts input"); } } diff --git a/client/js/views/pool_delete_view.js b/client/js/views/pool_delete_view.js index d3707db..fa92a49 100644 --- a/client/js/views/pool_delete_view.js +++ b/client/js/views/pool_delete_view.js @@ -1,9 +1,9 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); -const template = views.getTemplate('pool-delete'); +const template = views.getTemplate("pool-delete"); class PoolDeleteView extends events.EventTarget { constructor(ctx) { @@ -13,7 +13,7 @@ class PoolDeleteView extends events.EventTarget { this._pool = ctx.pool; views.replaceContent(this._hostNode, template(ctx)); views.decorateValidator(this._formNode); - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } clearMessages() { @@ -38,15 +38,17 @@ class PoolDeleteView extends events.EventTarget { _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - pool: this._pool, - }, - })); + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + pool: this._pool, + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } } diff --git a/client/js/views/pool_edit_view.js b/client/js/views/pool_edit_view.js index 9c118b3..b30ab9b 100644 --- a/client/js/views/pool_edit_view.js +++ b/client/js/views/pool_edit_view.js @@ -1,12 +1,12 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const api = require('../api.js'); -const misc = require('../util/misc.js'); -const views = require('../util/views.js'); -const Post = require('../models/post.js'); +const events = require("../events.js"); +const api = require("../api.js"); +const misc = require("../util/misc.js"); +const views = require("../util/views.js"); +const Post = require("../models/post.js"); -const template = views.getTemplate('pool-edit'); +const template = views.getTemplate("pool-edit"); class PoolEditView extends events.EventTarget { constructor(ctx) { @@ -19,24 +19,26 @@ class PoolEditView extends events.EventTarget { views.decorateValidator(this._formNode); if (this._namesFieldNode) { - this._namesFieldNode.addEventListener( - 'input', e => this._evtNameInput(e)); + this._namesFieldNode.addEventListener("input", (e) => + this._evtNameInput(e) + ); } if (this._postsFieldNode) { - this._postsFieldNode.addEventListener( - 'input', e => this._evtPostsInput(e)); + this._postsFieldNode.addEventListener("input", (e) => + this._evtPostsInput(e) + ); } for (let node of this._formNode.querySelectorAll( - 'input, select, textarea, posts')) { - node.addEventListener( - 'change', e => { - this.dispatchEvent(new CustomEvent('change')); - }); + "input, select, textarea, posts" + )) { + node.addEventListener("change", (e) => { + this.dispatchEvent(new CustomEvent("change")); + }); } - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } clearMessages() { @@ -65,19 +67,21 @@ class PoolEditView extends events.EventTarget { if (!list.length) { this._namesFieldNode.setCustomValidity( - 'Pools must have at least one name.'); + "Pools must have at least one name." + ); return; } for (let item of list) { if (!regex.test(item)) { this._namesFieldNode.setCustomValidity( - `Pool name "${item}" contains invalid symbols.`); + `Pool name "${item}" contains invalid symbols.` + ); return; } } - this._namesFieldNode.setCustomValidity(''); + this._namesFieldNode.setCustomValidity(""); } _evtPostsInput(e) { @@ -87,57 +91,60 @@ class PoolEditView extends events.EventTarget { for (let item of list) { if (!regex.test(item)) { this._postsFieldNode.setCustomValidity( - `Pool ID "${item}" is not an integer.`); + `Pool ID "${item}" is not an integer.` + ); return; } } - this._postsFieldNode.setCustomValidity(''); + this._postsFieldNode.setCustomValidity(""); } _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - pool: this._pool, + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + pool: this._pool, - names: this._namesFieldNode ? - misc.splitByWhitespace(this._namesFieldNode.value) : - undefined, + names: this._namesFieldNode + ? misc.splitByWhitespace(this._namesFieldNode.value) + : undefined, - category: this._categoryFieldNode ? - this._categoryFieldNode.value : - undefined, + category: this._categoryFieldNode + ? this._categoryFieldNode.value + : undefined, - description: this._descriptionFieldNode ? - this._descriptionFieldNode.value : - undefined, + description: this._descriptionFieldNode + ? this._descriptionFieldNode.value + : undefined, - posts: this._postsFieldNode ? - misc.splitByWhitespace(this._postsFieldNode.value) : - undefined, - }, - })); + posts: this._postsFieldNode + ? misc.splitByWhitespace(this._postsFieldNode.value) + : undefined, + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _namesFieldNode() { - return this._formNode.querySelector('.names input'); + return this._formNode.querySelector(".names input"); } get _categoryFieldNode() { - return this._formNode.querySelector('.category select'); + return this._formNode.querySelector(".category select"); } get _descriptionFieldNode() { - return this._formNode.querySelector('.description textarea'); + return this._formNode.querySelector(".description textarea"); } get _postsFieldNode() { - return this._formNode.querySelector('.posts input'); + return this._formNode.querySelector(".posts input"); } } diff --git a/client/js/views/pool_merge_view.js b/client/js/views/pool_merge_view.js index 09c4e8d..f0ca15a 100644 --- a/client/js/views/pool_merge_view.js +++ b/client/js/views/pool_merge_view.js @@ -1,12 +1,11 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const api = require('../api.js'); -const views = require('../util/views.js'); -const PoolAutoCompleteControl = - require('../controls/pool_auto_complete_control.js'); +const events = require("../events.js"); +const api = require("../api.js"); +const views = require("../util/views.js"); +const PoolAutoCompleteControl = require("../controls/pool_auto_complete_control.js"); -const template = views.getTemplate('pool-merge'); +const template = views.getTemplate("pool-merge"); class PoolMergeView extends events.EventTarget { constructor(ctx) { @@ -23,15 +22,18 @@ class PoolMergeView extends events.EventTarget { this._autoCompleteControl = new PoolAutoCompleteControl( this._targetPoolFieldNode, { - confirm: pool => { + confirm: (pool) => { this._targetPoolId = pool.id; this._autoCompleteControl.replaceSelectedText( - pool.names[0], false); - } - }); + pool.names[0], + false + ); + }, + } + ); } - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } clearMessages() { @@ -56,24 +58,26 @@ class PoolMergeView extends events.EventTarget { _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - pool: this._pool, - targetPoolId: this._targetPoolId - }, - })); + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + pool: this._pool, + targetPoolId: this._targetPoolId, + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _targetPoolFieldNode() { - return this._formNode.querySelector('input[name=target-pool]'); + return this._formNode.querySelector("input[name=target-pool]"); } get _addAliasCheckboxNode() { - return this._formNode.querySelector('input[name=alias]'); + return this._formNode.querySelector("input[name=alias]"); } } diff --git a/client/js/views/pool_summary_view.js b/client/js/views/pool_summary_view.js index a5808cc..37ce1c2 100644 --- a/client/js/views/pool_summary_view.js +++ b/client/js/views/pool_summary_view.js @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; -const views = require('../util/views.js'); +const views = require("../util/views.js"); -const template = views.getTemplate('pool-summary'); +const template = views.getTemplate("pool-summary"); class PoolSummaryView { constructor(ctx) { diff --git a/client/js/views/pool_view.js b/client/js/views/pool_view.js index f296e90..12b7704 100644 --- a/client/js/views/pool_view.js +++ b/client/js/views/pool_view.js @@ -1,26 +1,26 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); -const misc = require('../util/misc.js'); -const PoolSummaryView = require('./pool_summary_view.js'); -const PoolEditView = require('./pool_edit_view.js'); -const PoolMergeView = require('./pool_merge_view.js'); -const PoolDeleteView = require('./pool_delete_view.js'); -const EmptyView = require('../views/empty_view.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); +const misc = require("../util/misc.js"); +const PoolSummaryView = require("./pool_summary_view.js"); +const PoolEditView = require("./pool_edit_view.js"); +const PoolMergeView = require("./pool_merge_view.js"); +const PoolDeleteView = require("./pool_delete_view.js"); +const EmptyView = require("../views/empty_view.js"); -const template = views.getTemplate('pool'); +const template = views.getTemplate("pool"); class PoolView extends events.EventTarget { constructor(ctx) { super(); this._ctx = ctx; - ctx.pool.addEventListener('change', e => this._evtChange(e)); - ctx.section = ctx.section || 'summary'; + ctx.pool.addEventListener("change", (e) => this._evtChange(e)); + ctx.section = ctx.section || "summary"; ctx.getPrettyPoolName = misc.getPrettyPoolName; - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); this._install(); } @@ -28,52 +28,54 @@ class PoolView extends events.EventTarget { const ctx = this._ctx; views.replaceContent(this._hostNode, template(ctx)); - for (let item of this._hostNode.querySelectorAll('[data-name]')) { + for (let item of this._hostNode.querySelectorAll("[data-name]")) { item.classList.toggle( - 'active', item.getAttribute('data-name') === ctx.section); - if (item.getAttribute('data-name') === ctx.section) { + "active", + item.getAttribute("data-name") === ctx.section + ); + if (item.getAttribute("data-name") === ctx.section) { item.parentNode.scrollLeft = item.getBoundingClientRect().left - - item.parentNode.getBoundingClientRect().left + item.parentNode.getBoundingClientRect().left; } } - ctx.hostNode = this._hostNode.querySelector('.pool-content-holder'); - if (ctx.section === 'edit') { + ctx.hostNode = this._hostNode.querySelector(".pool-content-holder"); + if (ctx.section === "edit") { if (!this._ctx.canEditAnything) { this._view = new EmptyView(); this._view.showError( - 'You don\'t have privileges to edit pools.'); + "You don't have privileges to edit pools." + ); } else { this._view = new PoolEditView(ctx); - events.proxyEvent(this._view, this, 'submit'); + events.proxyEvent(this._view, this, "submit"); } - - } else if (ctx.section === 'merge') { + } else if (ctx.section === "merge") { if (!this._ctx.canMerge) { this._view = new EmptyView(); this._view.showError( - 'You don\'t have privileges to merge pools.'); + "You don't have privileges to merge pools." + ); } else { this._view = new PoolMergeView(ctx); - events.proxyEvent(this._view, this, 'submit', 'merge'); + events.proxyEvent(this._view, this, "submit", "merge"); } - - } else if (ctx.section === 'delete') { + } else if (ctx.section === "delete") { if (!this._ctx.canDelete) { this._view = new EmptyView(); this._view.showError( - 'You don\'t have privileges to delete pools.'); + "You don't have privileges to delete pools." + ); } else { this._view = new PoolDeleteView(ctx); - events.proxyEvent(this._view, this, 'submit', 'delete'); + events.proxyEvent(this._view, this, "submit", "delete"); } - } else { this._view = new PoolSummaryView(ctx); } - events.proxyEvent(this._view, this, 'change'); + events.proxyEvent(this._view, this, "change"); views.syncScrollPosition(); } diff --git a/client/js/views/pools_header_view.js b/client/js/views/pools_header_view.js index 65bbe1b..cfc6c8b 100644 --- a/client/js/views/pools_header_view.js +++ b/client/js/views/pools_header_view.js @@ -1,13 +1,12 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const misc = require('../util/misc.js'); -const search = require('../util/search.js'); -const views = require('../util/views.js'); -const PoolAutoCompleteControl = - require('../controls/pool_auto_complete_control.js'); +const events = require("../events.js"); +const misc = require("../util/misc.js"); +const search = require("../util/search.js"); +const views = require("../util/views.js"); +const PoolAutoCompleteControl = require("../controls/pool_auto_complete_control.js"); -const template = views.getTemplate('pools-header'); +const template = views.getTemplate("pools-header"); class PoolsHeaderView extends events.EventTarget { constructor(ctx) { @@ -20,31 +19,41 @@ class PoolsHeaderView extends events.EventTarget { this._autoCompleteControl = new PoolAutoCompleteControl( this._queryInputNode, { - confirm: pool => this._autoCompleteControl.replaceSelectedText( - misc.escapeSearchTerm(pool.names[0]), true), - }); + confirm: (pool) => + this._autoCompleteControl.replaceSelectedText( + misc.escapeSearchTerm(pool.names[0]), + true + ), + } + ); } search.searchInputNodeFocusHelper(this._queryInputNode); - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _queryInputNode() { - return this._hostNode.querySelector('[name=search-text]'); + return this._hostNode.querySelector("[name=search-text]"); } _evtSubmit(e) { e.preventDefault(); this._queryInputNode.blur(); - this.dispatchEvent(new CustomEvent('navigate', {detail: {parameters: { - query: this._queryInputNode.value, - page: 1, - }}})); + this.dispatchEvent( + new CustomEvent("navigate", { + detail: { + parameters: { + query: this._queryInputNode.value, + page: 1, + }, + }, + }) + ); } } diff --git a/client/js/views/pools_page_view.js b/client/js/views/pools_page_view.js index 234d446..6230ef6 100644 --- a/client/js/views/pools_page_view.js +++ b/client/js/views/pools_page_view.js @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; -const views = require('../util/views.js'); +const views = require("../util/views.js"); -const template = views.getTemplate('pools-page'); +const template = views.getTemplate("pools-page"); class PoolsPageView { constructor(ctx) { diff --git a/client/js/views/post_detail_view.js b/client/js/views/post_detail_view.js index 14786d3..587c41f 100644 --- a/client/js/views/post_detail_view.js +++ b/client/js/views/post_detail_view.js @@ -1,21 +1,21 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); -const PostMergeView = require('./post_merge_view.js'); -const EmptyView = require('../views/empty_view.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); +const PostMergeView = require("./post_merge_view.js"); +const EmptyView = require("../views/empty_view.js"); -const template = views.getTemplate('post-detail'); +const template = views.getTemplate("post-detail"); class PostDetailView extends events.EventTarget { constructor(ctx) { super(); this._ctx = ctx; - ctx.post.addEventListener('change', e => this._evtChange(e)); - ctx.section = ctx.section || 'summary'; + ctx.post.addEventListener("change", (e) => this._evtChange(e)); + ctx.section = ctx.section || "summary"; - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); this._install(); } @@ -23,28 +23,30 @@ class PostDetailView extends events.EventTarget { const ctx = this._ctx; views.replaceContent(this._hostNode, template(ctx)); - for (let item of this._hostNode.querySelectorAll('[data-name]')) { + for (let item of this._hostNode.querySelectorAll("[data-name]")) { item.classList.toggle( - 'active', item.getAttribute('data-name') === ctx.section); - if (item.getAttribute('data-name') === ctx.section) { + "active", + item.getAttribute("data-name") === ctx.section + ); + if (item.getAttribute("data-name") === ctx.section) { item.parentNode.scrollLeft = item.getBoundingClientRect().left - - item.parentNode.getBoundingClientRect().left + item.parentNode.getBoundingClientRect().left; } } - ctx.hostNode = this._hostNode.querySelector('.post-content-holder'); - if (ctx.section === 'merge') { + ctx.hostNode = this._hostNode.querySelector(".post-content-holder"); + if (ctx.section === "merge") { if (!this._ctx.canMerge) { this._view = new EmptyView(); this._view.showError( - 'You don\'t have privileges to merge posts.'); + "You don't have privileges to merge posts." + ); } else { this._view = new PostMergeView(ctx); - events.proxyEvent(this._view, this, 'select'); - events.proxyEvent(this._view, this, 'submit', 'merge'); + events.proxyEvent(this._view, this, "select"); + events.proxyEvent(this._view, this, "submit", "merge"); } - } else { // this._view = new PostSummaryView(ctx); } diff --git a/client/js/views/post_main_view.js b/client/js/views/post_main_view.js index 6a7472c..73bb324 100644 --- a/client/js/views/post_main_view.js +++ b/client/js/views/post_main_view.js @@ -1,35 +1,33 @@ -'use strict'; +"use strict"; -const iosCorrectedInnerHeight = require('ios-inner-height'); -const router = require('../router.js'); -const views = require('../util/views.js'); -const uri = require('../util/uri.js'); -const keyboard = require('../util/keyboard.js'); -const Touch = require('../util/touch.js'); -const PostContentControl = require('../controls/post_content_control.js'); -const PostNotesOverlayControl = - require('../controls/post_notes_overlay_control.js'); -const PostReadonlySidebarControl = - require('../controls/post_readonly_sidebar_control.js'); -const PostEditSidebarControl = - require('../controls/post_edit_sidebar_control.js'); -const CommentControl = require('../controls/comment_control.js'); -const CommentListControl = require('../controls/comment_list_control.js'); +const iosCorrectedInnerHeight = require("ios-inner-height"); +const router = require("../router.js"); +const views = require("../util/views.js"); +const uri = require("../util/uri.js"); +const keyboard = require("../util/keyboard.js"); +const Touch = require("../util/touch.js"); +const PostContentControl = require("../controls/post_content_control.js"); +const PostNotesOverlayControl = require("../controls/post_notes_overlay_control.js"); +const PostReadonlySidebarControl = require("../controls/post_readonly_sidebar_control.js"); +const PostEditSidebarControl = require("../controls/post_edit_sidebar_control.js"); +const CommentControl = require("../controls/comment_control.js"); +const CommentListControl = require("../controls/comment_list_control.js"); -const template = views.getTemplate('post-main'); +const template = views.getTemplate("post-main"); class PostMainView { constructor(ctx) { - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); const sourceNode = template(ctx); - const postContainerNode = sourceNode.querySelector('.post-container'); - const sidebarNode = sourceNode.querySelector('.sidebar'); + const postContainerNode = sourceNode.querySelector(".post-container"); + const sidebarNode = sourceNode.querySelector(".sidebar"); views.replaceContent(this._hostNode, sourceNode); views.syncScrollPosition(); - const topNavigationNode = - document.body.querySelector('#top-navigation'); + const topNavigationNode = document.body.querySelector( + "#top-navigation" + ); this._postContentControl = new PostContentControl( postContainerNode, @@ -43,15 +41,17 @@ class PostMainView { margin, iosCorrectedInnerHeight() - topNavigationNode.getBoundingClientRect().height - - (margin * 2), + margin * 2, ]; - }); + } + ); this._postNotesOverlayControl = new PostNotesOverlayControl( - postContainerNode.querySelector('.post-overlay'), - ctx.post); + postContainerNode.querySelector(".post-overlay"), + ctx.post + ); - if (ctx.post.type === 'video' || ctx.post.type === 'flash') { + if (ctx.post.type === "video" || ctx.post.type === "flash") { this._postContentControl.disableOverlay(); } @@ -71,16 +71,16 @@ class PostMainView { } }; - keyboard.bind('e', () => { + keyboard.bind("e", () => { if (ctx.editMode) { - router.show(uri.formatClientLink('post', ctx.post.id)); + router.show(uri.formatClientLink("post", ctx.post.id)); } else { - router.show(uri.formatClientLink('post', ctx.post.id, 'edit')); + router.show(uri.formatClientLink("post", ctx.post.id, "edit")); } }); - keyboard.bind(['a', 'left'], showPreviousImage); - keyboard.bind(['d', 'right'], showNextImage); - keyboard.bind('del', e => { + keyboard.bind(["a", "left"], showPreviousImage); + keyboard.bind(["d", "right"], showNextImage); + keyboard.bind("del", (e) => { if (ctx.editMode) { this.sidebarControl._evtDeleteClick(e); } @@ -90,53 +90,65 @@ class PostMainView { postContainerNode, () => { if (!ctx.editMode) { - showPreviousImage() + showPreviousImage(); } }, () => { if (!ctx.editMode) { - showNextImage() + showNextImage(); } } - ) + ); } _installSidebar(ctx) { const sidebarContainerNode = document.querySelector( - '#content-holder .sidebar-container'); + "#content-holder .sidebar-container" + ); if (ctx.editMode) { this.sidebarControl = new PostEditSidebarControl( sidebarContainerNode, ctx.post, this._postContentControl, - this._postNotesOverlayControl); + this._postNotesOverlayControl + ); } else { this.sidebarControl = new PostReadonlySidebarControl( - sidebarContainerNode, ctx.post, this._postContentControl); + sidebarContainerNode, + ctx.post, + this._postContentControl + ); } } _installCommentForm() { const commentFormContainer = document.querySelector( - '#content-holder .comment-form-container'); + "#content-holder .comment-form-container" + ); if (!commentFormContainer) { return; } this.commentControl = new CommentControl( - commentFormContainer, null, true); + commentFormContainer, + null, + true + ); } _installComments(comments) { const commentsContainerNode = document.querySelector( - '#content-holder .comments-container'); + "#content-holder .comments-container" + ); if (!commentsContainerNode) { return; } this.commentListControl = new CommentListControl( - commentsContainerNode, comments); + commentsContainerNode, + comments + ); } } diff --git a/client/js/views/post_merge_view.js b/client/js/views/post_merge_view.js index 952e3a3..20924d3 100644 --- a/client/js/views/post_merge_view.js +++ b/client/js/views/post_merge_view.js @@ -1,11 +1,11 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); const KEY_RETURN = 13; -const template = views.getTemplate('post-merge'); -const sideTemplate = views.getTemplate('post-merge-side'); +const template = views.getTemplate("post-merge"); +const sideTemplate = views.getTemplate("post-merge-side"); class PostMergeView extends events.EventTarget { constructor(ctx) { @@ -23,7 +23,7 @@ class PostMergeView extends events.EventTarget { this._refreshLeftSide(); this._refreshRightSide(); - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } clearMessages() { @@ -52,47 +52,61 @@ class PostMergeView extends events.EventTarget { } _refreshLeftSide() { - this._refreshSide(this._leftPost, this._leftSideNode, 'left', false); + this._refreshSide(this._leftPost, this._leftSideNode, "left", false); } _refreshRightSide() { - this._refreshSide(this._rightPost, this._rightSideNode, 'right', true); + this._refreshSide(this._rightPost, this._rightSideNode, "right", true); } _refreshSide(post, sideNode, sideName, isEditable) { views.replaceContent( sideNode, - sideTemplate(Object.assign({}, this._ctx, {post: post, - name: sideName, - editable: isEditable}))); + sideTemplate( + Object.assign({}, this._ctx, { + post: post, + name: sideName, + editable: isEditable, + }) + ) + ); - let postIdNode = sideNode.querySelector('input[type=text]'); - let searchButtonNode = sideNode.querySelector('input[type=button]'); + let postIdNode = sideNode.querySelector("input[type=text]"); + let searchButtonNode = sideNode.querySelector("input[type=button]"); if (isEditable) { - postIdNode.addEventListener( - 'keydown', e => this._evtPostSearchFieldKeyDown(e)); - searchButtonNode.addEventListener( - 'click', e => this._evtPostSearchButtonClick(e, postIdNode)); + postIdNode.addEventListener("keydown", (e) => + this._evtPostSearchFieldKeyDown(e) + ); + searchButtonNode.addEventListener("click", (e) => + this._evtPostSearchButtonClick(e, postIdNode) + ); } } _evtSubmit(e) { e.preventDefault(); const checkedTargetPost = this._formNode.querySelector( - '.target-post :checked').value; + ".target-post :checked" + ).value; const checkedTargetPostContent = this._formNode.querySelector( - '.target-post-content :checked').value; - this.dispatchEvent(new CustomEvent('submit', { - detail: { - post: checkedTargetPost === 'left' ? - this._rightPost : - this._leftPost, - targetPost: checkedTargetPost === 'left' ? - this._leftPost : - this._rightPost, - useOldContent: checkedTargetPostContent !== checkedTargetPost, - }, - })); + ".target-post-content :checked" + ).value; + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + post: + checkedTargetPost === "left" + ? this._rightPost + : this._leftPost, + targetPost: + checkedTargetPost === "left" + ? this._leftPost + : this._rightPost, + useOldContent: + checkedTargetPostContent !== checkedTargetPost, + }, + }) + ); } _evtPostSearchFieldKeyDown(e) { @@ -102,33 +116,37 @@ class PostMergeView extends events.EventTarget { } e.target.blur(); e.preventDefault(); - this.dispatchEvent(new CustomEvent('select', { - detail: { - postId: e.target.value, - }, - })); + this.dispatchEvent( + new CustomEvent("select", { + detail: { + postId: e.target.value, + }, + }) + ); } _evtPostSearchButtonClick(e, textNode) { e.target.blur(); e.preventDefault(); - this.dispatchEvent(new CustomEvent('select', { - detail: { - postId: textNode.value, - }, - })); + this.dispatchEvent( + new CustomEvent("select", { + detail: { + postId: textNode.value, + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _leftSideNode() { - return this._hostNode.querySelector('.left-post-container'); + return this._hostNode.querySelector(".left-post-container"); } get _rightSideNode() { - return this._hostNode.querySelector('.right-post-container'); + return this._hostNode.querySelector(".right-post-container"); } } diff --git a/client/js/views/post_upload_view.js b/client/js/views/post_upload_view.js index f1d465f..7daf4fb 100644 --- a/client/js/views/post_upload_view.js +++ b/client/js/views/post_upload_view.js @@ -1,22 +1,24 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); -const FileDropperControl = require('../controls/file_dropper_control.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); +const FileDropperControl = require("../controls/file_dropper_control.js"); -const template = views.getTemplate('post-upload'); -const rowTemplate = views.getTemplate('post-upload-row'); +const template = views.getTemplate("post-upload"); +const rowTemplate = views.getTemplate("post-upload-row"); function _mimeTypeToPostType(mimeType) { - return { - 'application/x-shockwave-flash': 'flash', - 'image/gif': 'image', - 'image/jpeg': 'image', - 'image/png': 'image', - 'image/webp': 'image', - 'video/mp4': 'video', - 'video/webm': 'video', - }[mimeType] || 'unknown'; + return ( + { + "application/x-shockwave-flash": "flash", + "image/gif": "image", + "image/jpeg": "image", + "image/png": "image", + "image/webp": "image", + "video/mp4": "video", + "video/webm": "video", + }[mimeType] || "unknown" + ); } class Uploadable extends events.EventTarget { @@ -24,18 +26,17 @@ class Uploadable extends events.EventTarget { super(); this.lookalikes = []; this.lookalikesConfirmed = false; - this.safety = 'safe'; + this.safety = "safe"; this.flags = []; this.tags = []; this.relations = []; this.anonymous = false; } - destroy() { - } + destroy() {} get mimeType() { - return 'application/octet-stream'; + return "application/octet-stream"; } get type() { @@ -43,11 +44,11 @@ class Uploadable extends events.EventTarget { } get key() { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } get name() { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } } @@ -62,10 +63,11 @@ class File extends Uploadable { } else { let reader = new FileReader(); reader.readAsDataURL(file); - reader.addEventListener('load', e => { + reader.addEventListener("load", (e) => { this._previewUrl = e.target.result; this.dispatchEvent( - new CustomEvent('finish', {detail: {uploadable: this}})); + new CustomEvent("finish", { detail: { uploadable: this } }) + ); }); } } @@ -97,25 +99,25 @@ class Url extends Uploadable { constructor(url) { super(); this.url = url; - this.dispatchEvent(new CustomEvent('finish')); + this.dispatchEvent(new CustomEvent("finish")); } get mimeType() { let mime = { - 'swf': 'application/x-shockwave-flash', - 'jpg': 'image/jpeg', - 'png': 'image/png', - 'gif': 'image/gif', - 'webp': 'image/webp', - 'mp4': 'video/mp4', - 'webm': 'video/webm', + swf: "application/x-shockwave-flash", + jpg: "image/jpeg", + png: "image/png", + gif: "image/gif", + webp: "image/webp", + mp4: "video/mp4", + webm: "video/webm", }; for (let extension of Object.keys(mime)) { - if (this.url.toLowerCase().indexOf('.' + extension) !== -1) { + if (this.url.toLowerCase().indexOf("." + extension) !== -1) { return mime[extension]; } } - return 'unknown'; + return "unknown"; } get previewUrl() { @@ -135,7 +137,7 @@ class PostUploadView extends events.EventTarget { constructor(ctx) { super(); this._ctx = ctx; - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); views.replaceContent(this._hostNode, template()); views.syncScrollPosition(); @@ -143,40 +145,46 @@ class PostUploadView extends events.EventTarget { this._cancelButtonNode.disabled = true; this._uploadables = []; - this._uploadables.find = u => { - return this._uploadables.findIndex(u2 => u.key === u2.key); + this._uploadables.find = (u) => { + return this._uploadables.findIndex((u2) => u.key === u2.key); }; this._contentFileDropper = new FileDropperControl( this._contentInputNode, { extraText: - 'Allowed extensions: .jpg, .png, .gif, .webm, .mp4, .swf', + "Allowed extensions: .jpg, .png, .gif, .webm, .mp4, .swf", allowUrls: true, allowMultiple: true, lock: false, - }); - this._contentFileDropper.addEventListener( - 'fileadd', e => this._evtFilesAdded(e)); - this._contentFileDropper.addEventListener( - 'urladd', e => this._evtUrlsAdded(e)); + } + ); + this._contentFileDropper.addEventListener("fileadd", (e) => + this._evtFilesAdded(e) + ); + this._contentFileDropper.addEventListener("urladd", (e) => + this._evtUrlsAdded(e) + ); - this._cancelButtonNode.addEventListener( - 'click', e => this._evtCancelButtonClick(e)); - this._formNode.addEventListener('submit', e => this._evtFormSubmit(e)); - this._formNode.classList.add('inactive'); + this._cancelButtonNode.addEventListener("click", (e) => + this._evtCancelButtonClick(e) + ); + this._formNode.addEventListener("submit", (e) => + this._evtFormSubmit(e) + ); + this._formNode.classList.add("inactive"); } enableForm() { views.enableForm(this._formNode); this._cancelButtonNode.disabled = true; - this._formNode.classList.remove('uploading'); + this._formNode.classList.remove("uploading"); } disableForm() { views.disableForm(this._formNode); this._cancelButtonNode.disabled = false; - this._formNode.classList.add('uploading'); + this._formNode.classList.add("uploading"); } clearMessages() { @@ -201,7 +209,7 @@ class PostUploadView extends events.EventTarget { } addUploadables(uploadables) { - this._formNode.classList.remove('inactive'); + this._formNode.classList.remove("inactive"); let duplicatesFound = 0; for (let uploadable of uploadables) { if (this._uploadables.find(uploadable) !== -1) { @@ -209,20 +217,22 @@ class PostUploadView extends events.EventTarget { continue; } this._uploadables.push(uploadable); - this._emit('change'); + this._emit("change"); this._renderRowNode(uploadable); - uploadable.addEventListener( - 'finish', e => this._updateThumbnailNode(e.detail.uploadable)); + uploadable.addEventListener("finish", (e) => + this._updateThumbnailNode(e.detail.uploadable) + ); } if (duplicatesFound) { let message = null; if (duplicatesFound < uploadables.length) { - message = 'Some of the files were already added ' + - 'and have been skipped.'; + message = + "Some of the files were already added " + + "and have been skipped."; } else if (duplicatesFound === 1) { - message = 'This file was already added.'; + message = "This file was already added."; } else { - message = 'These files were already added.'; + message = "These files were already added."; } alert(message); } @@ -235,10 +245,10 @@ class PostUploadView extends events.EventTarget { uploadable.destroy(); uploadable.rowNode.parentNode.removeChild(uploadable.rowNode); this._uploadables.splice(this._uploadables.find(uploadable), 1); - this._emit('change'); + this._emit("change"); if (!this._uploadables.length) { - this._formNode.classList.add('inactive'); - this._submitButtonNode.value = 'Upload all'; + this._formNode.classList.add("inactive"); + this._submitButtonNode.value = "Upload all"; } } @@ -248,16 +258,16 @@ class PostUploadView extends events.EventTarget { } _evtFilesAdded(e) { - this.addUploadables(e.detail.files.map(file => new File(file))); + this.addUploadables(e.detail.files.map((file) => new File(file))); } _evtUrlsAdded(e) { - this.addUploadables(e.detail.urls.map(url => new Url(url))); + this.addUploadables(e.detail.urls.map((url) => new Url(url))); } _evtCancelButtonClick(e) { e.preventDefault(); - this._emit('cancel'); + this._emit("cancel"); } _evtFormSubmit(e) { @@ -265,19 +275,21 @@ class PostUploadView extends events.EventTarget { for (let uploadable of this._uploadables) { this._updateUploadableFromDom(uploadable); } - this._submitButtonNode.value = 'Resume upload'; - this._emit('submit'); + this._submitButtonNode.value = "Resume upload"; + this._emit("submit"); } _updateUploadableFromDom(uploadable) { const rowNode = uploadable.rowNode; - const safetyNode = rowNode.querySelector('.safety input:checked'); + const safetyNode = rowNode.querySelector(".safety input:checked"); if (safetyNode) { uploadable.safety = safetyNode.value; } - const anonymousNode = rowNode.querySelector('.anonymous input:checked'); + const anonymousNode = rowNode.querySelector( + ".anonymous input:checked" + ); if (anonymousNode) { uploadable.anonymous = true; } @@ -286,11 +298,14 @@ class PostUploadView extends events.EventTarget { uploadable.relations = []; for (let [i, lookalike] of uploadable.lookalikes.entries()) { let lookalikeNode = rowNode.querySelector( - `.lookalikes li:nth-child(${i + 1})`); - if (lookalikeNode.querySelector('[name=copy-tags]').checked) { - uploadable.tags = uploadable.tags.concat(lookalike.post.tagNames); + `.lookalikes li:nth-child(${i + 1})` + ); + if (lookalikeNode.querySelector("[name=copy-tags]").checked) { + uploadable.tags = uploadable.tags.concat( + lookalike.post.tagNames + ); } - if (lookalikeNode.querySelector('[name=add-relation]').checked) { + if (lookalikeNode.querySelector("[name=add-relation]").checked) { uploadable.relations.push(lookalike.post.id); } } @@ -317,78 +332,97 @@ class PostUploadView extends events.EventTarget { this._uploadables[index + delta] = uploadable1; if (delta === 1) { this._listNode.insertBefore( - uploadable2.rowNode, uploadable1.rowNode); + uploadable2.rowNode, + uploadable1.rowNode + ); } else { this._listNode.insertBefore( - uploadable1.rowNode, uploadable2.rowNode); + uploadable1.rowNode, + uploadable2.rowNode + ); } } } _emit(eventType) { this.dispatchEvent( - new CustomEvent( - eventType, - {detail: { + new CustomEvent(eventType, { + detail: { uploadables: this._uploadables, skipDuplicates: this._skipDuplicatesCheckboxNode.checked, - }})); + }, + }) + ); } _renderRowNode(uploadable) { - const rowNode = rowTemplate(Object.assign( - {}, this._ctx, {uploadable: uploadable})); + const rowNode = rowTemplate( + Object.assign({}, this._ctx, { uploadable: uploadable }) + ); if (uploadable.rowNode) { uploadable.rowNode.parentNode.replaceChild( - rowNode, uploadable.rowNode); + rowNode, + uploadable.rowNode + ); } else { this._listNode.appendChild(rowNode); } uploadable.rowNode = rowNode; - rowNode.querySelector('a.remove').addEventListener('click', - e => this._evtRemoveClick(e, uploadable)); - rowNode.querySelector('a.move-up').addEventListener('click', - e => this._evtMoveClick(e, uploadable, -1)); - rowNode.querySelector('a.move-down').addEventListener('click', - e => this._evtMoveClick(e, uploadable, 1)); + rowNode + .querySelector("a.remove") + .addEventListener("click", (e) => + this._evtRemoveClick(e, uploadable) + ); + rowNode + .querySelector("a.move-up") + .addEventListener("click", (e) => + this._evtMoveClick(e, uploadable, -1) + ); + rowNode + .querySelector("a.move-down") + .addEventListener("click", (e) => + this._evtMoveClick(e, uploadable, 1) + ); } _updateThumbnailNode(uploadable) { - const rowNode = rowTemplate(Object.assign( - {}, this._ctx, {uploadable: uploadable})); + const rowNode = rowTemplate( + Object.assign({}, this._ctx, { uploadable: uploadable }) + ); views.replaceContent( - uploadable.rowNode.querySelector('.thumbnail'), - rowNode.querySelector('.thumbnail').childNodes); + uploadable.rowNode.querySelector(".thumbnail"), + rowNode.querySelector(".thumbnail").childNodes + ); } get _uploading() { - return this._formNode.classList.contains('uploading'); + return this._formNode.classList.contains("uploading"); } get _listNode() { - return this._hostNode.querySelector('.uploadables-container'); + return this._hostNode.querySelector(".uploadables-container"); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _skipDuplicatesCheckboxNode() { - return this._hostNode.querySelector('form [name=skip-duplicates]'); + return this._hostNode.querySelector("form [name=skip-duplicates]"); } get _submitButtonNode() { - return this._hostNode.querySelector('form [type=submit]'); + return this._hostNode.querySelector("form [type=submit]"); } get _cancelButtonNode() { - return this._hostNode.querySelector('form .cancel'); + return this._hostNode.querySelector("form .cancel"); } get _contentInputNode() { - return this._formNode.querySelector('.dropper-container'); + return this._formNode.querySelector(".dropper-container"); } } diff --git a/client/js/views/posts_header_view.js b/client/js/views/posts_header_view.js index 0797fff..81546a7 100644 --- a/client/js/views/posts_header_view.js +++ b/client/js/views/posts_header_view.js @@ -1,53 +1,56 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const settings = require('../models/settings.js'); -const keyboard = require('../util/keyboard.js'); -const misc = require('../util/misc.js'); -const search = require('../util/search.js'); -const views = require('../util/views.js'); -const TagAutoCompleteControl = - require('../controls/tag_auto_complete_control.js'); +const events = require("../events.js"); +const settings = require("../models/settings.js"); +const keyboard = require("../util/keyboard.js"); +const misc = require("../util/misc.js"); +const search = require("../util/search.js"); +const views = require("../util/views.js"); +const TagAutoCompleteControl = require("../controls/tag_auto_complete_control.js"); -const template = views.getTemplate('posts-header'); +const template = views.getTemplate("posts-header"); class BulkEditor extends events.EventTarget { constructor(hostNode) { super(); this._hostNode = hostNode; - this._openLinkNode.addEventListener( - 'click', e => this._evtOpenLinkClick(e)); - this._closeLinkNode.addEventListener( - 'click', e => this._evtCloseLinkClick(e)); + this._openLinkNode.addEventListener("click", (e) => + this._evtOpenLinkClick(e) + ); + this._closeLinkNode.addEventListener("click", (e) => + this._evtCloseLinkClick(e) + ); } get opened() { - return this._hostNode.classList.contains('opened') && - !this._hostNode.classList.contains('hidden'); + return ( + this._hostNode.classList.contains("opened") && + !this._hostNode.classList.contains("hidden") + ); } get _openLinkNode() { - return this._hostNode.querySelector('.open'); + return this._hostNode.querySelector(".open"); } get _closeLinkNode() { - return this._hostNode.querySelector('.close'); + return this._hostNode.querySelector(".close"); } toggleOpen(state) { - this._hostNode.classList.toggle('opened', state); + this._hostNode.classList.toggle("opened", state); } toggleHide(state) { - this._hostNode.classList.toggle('hidden', state); + this._hostNode.classList.toggle("hidden", state); } _evtOpenLinkClick(e) { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } _evtCloseLinkClick(e) { - throw new Error('Not implemented'); + throw new Error("Not implemented"); } } @@ -55,13 +58,13 @@ class BulkSafetyEditor extends BulkEditor { _evtOpenLinkClick(e) { e.preventDefault(); this.toggleOpen(true); - this.dispatchEvent(new CustomEvent('open', {detail: {}})); + this.dispatchEvent(new CustomEvent("open", { detail: {} })); } _evtCloseLinkClick(e) { e.preventDefault(); this.toggleOpen(false); - this.dispatchEvent(new CustomEvent('close', {detail: {}})); + this.dispatchEvent(new CustomEvent("close", { detail: {} })); } } @@ -71,10 +74,16 @@ class BulkTagEditor extends BulkEditor { this._autoCompleteControl = new TagAutoCompleteControl( this._inputNode, { - confirm: tag => this._autoCompleteControl.replaceSelectedText( - tag.names[0], false), - }); - this._hostNode.addEventListener('submit', e => this._evtFormSubmit(e)); + confirm: (tag) => + this._autoCompleteControl.replaceSelectedText( + tag.names[0], + false + ), + } + ); + this._hostNode.addEventListener("submit", (e) => + this._evtFormSubmit(e) + ); } get value() { @@ -82,7 +91,7 @@ class BulkTagEditor extends BulkEditor { } get _inputNode() { - return this._hostNode.querySelector('input[name=tag]'); + return this._hostNode.querySelector("input[name=tag]"); } focus() { @@ -96,22 +105,22 @@ class BulkTagEditor extends BulkEditor { _evtFormSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', {detail: {}})); + this.dispatchEvent(new CustomEvent("submit", { detail: {} })); } _evtOpenLinkClick(e) { e.preventDefault(); this.toggleOpen(true); this.focus(); - this.dispatchEvent(new CustomEvent('open', {detail: {}})); + this.dispatchEvent(new CustomEvent("open", { detail: {} })); } _evtCloseLinkClick(e) { e.preventDefault(); - this._inputNode.value = ''; + this._inputNode.value = ""; this.toggleOpen(false); this.blur(); - this.dispatchEvent(new CustomEvent('close', {detail: {}})); + this.dispatchEvent(new CustomEvent("close", { detail: {} })); } } @@ -127,18 +136,25 @@ class PostsHeaderView extends events.EventTarget { this._autoCompleteControl = new TagAutoCompleteControl( this._queryInputNode, { - confirm: tag => this._autoCompleteControl.replaceSelectedText( - misc.escapeSearchTerm(tag.names[0]), true), - }); + confirm: (tag) => + this._autoCompleteControl.replaceSelectedText( + misc.escapeSearchTerm(tag.names[0]), + true + ), + } + ); - keyboard.bind('p', () => this._focusFirstPostNode()); + keyboard.bind("p", () => this._focusFirstPostNode()); search.searchInputNodeFocusHelper(this._queryInputNode); for (let safetyButtonNode of this._safetyButtonNodes) { - safetyButtonNode.addEventListener( - 'click', e => this._evtSafetyButtonClick(e)); + safetyButtonNode.addEventListener("click", (e) => + this._evtSafetyButtonClick(e) + ); } - this._formNode.addEventListener('submit', e => this._evtFormSubmit(e)); + this._formNode.addEventListener("submit", (e) => + this._evtFormSubmit(e) + ); this._bulkEditors = []; if (this._bulkEditTagsNode) { @@ -148,19 +164,20 @@ class PostsHeaderView extends events.EventTarget { if (this._bulkEditSafetyNode) { this._bulkSafetyEditor = new BulkSafetyEditor( - this._bulkEditSafetyNode); + this._bulkEditSafetyNode + ); this._bulkEditors.push(this._bulkSafetyEditor); } for (let editor of this._bulkEditors) { - editor.addEventListener('submit', e => { + editor.addEventListener("submit", (e) => { this._navigate(); }); - editor.addEventListener('open', e => { + editor.addEventListener("open", (e) => { this._hideBulkEditorsExcept(editor); this._navigate(); }); - editor.addEventListener('close', e => { + editor.addEventListener("close", (e) => { this._closeAndShowAllBulkEditors(); this._navigate(); }); @@ -174,23 +191,23 @@ class PostsHeaderView extends events.EventTarget { } get _formNode() { - return this._hostNode.querySelector('form.search'); + return this._hostNode.querySelector("form.search"); } get _safetyButtonNodes() { - return this._hostNode.querySelectorAll('form .safety'); + return this._hostNode.querySelectorAll("form .safety"); } get _queryInputNode() { - return this._hostNode.querySelector('form [name=search-text]'); + return this._hostNode.querySelector("form [name=search-text]"); } get _bulkEditTagsNode() { - return this._hostNode.querySelector('.bulk-edit-tags'); + return this._hostNode.querySelector(".bulk-edit-tags"); } get _bulkEditSafetyNode() { - return this._hostNode.querySelector('.bulk-edit-safety'); + return this._hostNode.querySelector(".bulk-edit-safety"); } _openBulkEditor(editor) { @@ -216,20 +233,23 @@ class PostsHeaderView extends events.EventTarget { _evtSafetyButtonClick(e, url) { e.preventDefault(); - e.target.classList.toggle('disabled'); - const safety = e.target.getAttribute('data-safety'); + e.target.classList.toggle("disabled"); + const safety = e.target.getAttribute("data-safety"); let browsingSettings = settings.get(); - browsingSettings.listPosts[safety] = - !browsingSettings.listPosts[safety]; + browsingSettings.listPosts[safety] = !browsingSettings.listPosts[ + safety + ]; settings.save(browsingSettings, true); this.dispatchEvent( - new CustomEvent( - 'navigate', { - detail: { - parameters: Object.assign( - {}, this._ctx.parameters, {tag: null, offset: 0}), - }, - })); + new CustomEvent("navigate", { + detail: { + parameters: Object.assign({}, this._ctx.parameters, { + tag: null, + offset: 0, + }), + }, + }) + ); } _evtFormSubmit(e) { @@ -239,27 +259,33 @@ class PostsHeaderView extends events.EventTarget { _navigate() { this._autoCompleteControl.hide(); - let parameters = {query: this._queryInputNode.value}; + let parameters = { query: this._queryInputNode.value }; // convert falsy values to an empty string "" so that we can correctly compare with the current query - const prevQuery = this._ctx.parameters.query ? this._ctx.parameters.query : ""; - parameters.offset = parameters.query === prevQuery ? this._ctx.parameters.offset : 0; + const prevQuery = this._ctx.parameters.query + ? this._ctx.parameters.query + : ""; + parameters.offset = + parameters.query === prevQuery ? this._ctx.parameters.offset : 0; if (this._bulkTagEditor && this._bulkTagEditor.opened) { parameters.tag = this._bulkTagEditor.value; this._bulkTagEditor.blur(); } else { parameters.tag = null; } - parameters.safety = ( - this._bulkSafetyEditor && - this._bulkSafetyEditor.opened ? '1' : null); + parameters.safety = + this._bulkSafetyEditor && this._bulkSafetyEditor.opened + ? "1" + : null; this.dispatchEvent( - new CustomEvent('navigate', {detail: {parameters: parameters}})); + new CustomEvent("navigate", { detail: { parameters: parameters } }) + ); } _focusFirstPostNode() { - const firstPostNode = - document.body.querySelector('.post-list li:first-child a'); + const firstPostNode = document.body.querySelector( + ".post-list li:first-child a" + ); if (firstPostNode) { firstPostNode.focus(); } diff --git a/client/js/views/posts_page_view.js b/client/js/views/posts_page_view.js index 84521e3..ba07a63 100644 --- a/client/js/views/posts_page_view.js +++ b/client/js/views/posts_page_view.js @@ -1,9 +1,9 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); -const template = views.getTemplate('posts-page'); +const template = views.getTemplate("posts-page"); class PostsPageView extends events.EventTarget { constructor(ctx) { @@ -15,26 +15,28 @@ class PostsPageView extends events.EventTarget { this._postIdToPost = {}; for (let post of ctx.response.results) { this._postIdToPost[post.id] = post; - post.addEventListener('change', e => this._evtPostChange(e)); + post.addEventListener("change", (e) => this._evtPostChange(e)); } this._postIdToListItemNode = {}; for (let listItemNode of this._listItemNodes) { - const postId = listItemNode.getAttribute('data-post-id'); + const postId = listItemNode.getAttribute("data-post-id"); const post = this._postIdToPost[postId]; this._postIdToListItemNode[postId] = listItemNode; const tagFlipperNode = this._getTagFlipperNode(listItemNode); if (tagFlipperNode) { - tagFlipperNode.addEventListener( - 'click', e => this._evtBulkEditTagsClick(e, post)); + tagFlipperNode.addEventListener("click", (e) => + this._evtBulkEditTagsClick(e, post) + ); } const safetyFlipperNode = this._getSafetyFlipperNode(listItemNode); if (safetyFlipperNode) { - for (let linkNode of safetyFlipperNode.querySelectorAll('a')) { - linkNode.addEventListener( - 'click', e => this._evtBulkEditSafetyClick(e, post)); + for (let linkNode of safetyFlipperNode.querySelectorAll("a")) { + linkNode.addEventListener("click", (e) => + this._evtBulkEditSafetyClick(e, post) + ); } } } @@ -43,21 +45,21 @@ class PostsPageView extends events.EventTarget { } get _listItemNodes() { - return this._hostNode.querySelectorAll('li'); + return this._hostNode.querySelectorAll("li"); } _getTagFlipperNode(listItemNode) { - return listItemNode.querySelector('.tag-flipper'); + return listItemNode.querySelector(".tag-flipper"); } _getSafetyFlipperNode(listItemNode) { - return listItemNode.querySelector('.safety-flipper'); + return listItemNode.querySelector(".safety-flipper"); } _evtPostChange(e) { const listItemNode = this._postIdToListItemNode[e.detail.post.id]; - for (let node of listItemNode.querySelectorAll('[data-disabled]')) { - node.removeAttribute('data-disabled'); + for (let node of listItemNode.querySelectorAll("[data-disabled]")) { + node.removeAttribute("data-disabled"); } this._syncBulkEditorsHighlights(); } @@ -65,35 +67,41 @@ class PostsPageView extends events.EventTarget { _evtBulkEditTagsClick(e, post) { e.preventDefault(); const linkNode = e.target; - if (linkNode.getAttribute('data-disabled')) { + if (linkNode.getAttribute("data-disabled")) { return; } - linkNode.setAttribute('data-disabled', true); + linkNode.setAttribute("data-disabled", true); this.dispatchEvent( new CustomEvent( - linkNode.classList.contains('tagged') ? 'untag' : 'tag', - {detail: {post: post}})); + linkNode.classList.contains("tagged") ? "untag" : "tag", + { + detail: { post: post }, + } + ) + ); } _evtBulkEditSafetyClick(e, post) { e.preventDefault(); const linkNode = e.target; - if (linkNode.getAttribute('data-disabled')) { + if (linkNode.getAttribute("data-disabled")) { return; } - const newSafety = linkNode.getAttribute('data-safety'); + const newSafety = linkNode.getAttribute("data-safety"); if (post.safety === newSafety) { return; } - linkNode.setAttribute('data-disabled', true); + linkNode.setAttribute("data-disabled", true); this.dispatchEvent( - new CustomEvent( - 'changeSafety', {detail: {post: post, safety: newSafety}})); + new CustomEvent("changeSafety", { + detail: { post: post, safety: newSafety }, + }) + ); } _syncBulkEditorsHighlights() { for (let listItemNode of this._listItemNodes) { - const postId = listItemNode.getAttribute('data-post-id'); + const postId = listItemNode.getAttribute("data-post-id"); const post = this._postIdToPost[postId]; const tagFlipperNode = this._getTagFlipperNode(listItemNode); @@ -102,14 +110,17 @@ class PostsPageView extends events.EventTarget { for (let tag of this._ctx.bulkEdit.tags) { tagged &= post.tags.isTaggedWith(tag); } - tagFlipperNode.classList.toggle('tagged', tagged); + tagFlipperNode.classList.toggle("tagged", tagged); } const safetyFlipperNode = this._getSafetyFlipperNode(listItemNode); if (safetyFlipperNode) { - for (let linkNode of safetyFlipperNode.querySelectorAll('a')) { - const safety = linkNode.getAttribute('data-safety'); - linkNode.classList.toggle('active', post.safety === safety); + for (let linkNode of safetyFlipperNode.querySelectorAll("a")) { + const safety = linkNode.getAttribute("data-safety"); + linkNode.classList.toggle( + "active", + post.safety === safety + ); } } } diff --git a/client/js/views/registration_view.js b/client/js/views/registration_view.js index 1db5fcb..0a08de2 100644 --- a/client/js/views/registration_view.js +++ b/client/js/views/registration_view.js @@ -1,22 +1,25 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const api = require('../api.js'); -const views = require('../util/views.js'); +const events = require("../events.js"); +const api = require("../api.js"); +const views = require("../util/views.js"); -const template = views.getTemplate('user-registration'); +const template = views.getTemplate("user-registration"); class RegistrationView extends events.EventTarget { constructor() { super(); - this._hostNode = document.getElementById('content-holder'); - views.replaceContent(this._hostNode, template({ - userNamePattern: api.getUserNameRegex(), - passwordPattern: api.getPasswordRegex(), - })); + this._hostNode = document.getElementById("content-holder"); + views.replaceContent( + this._hostNode, + template({ + userNamePattern: api.getUserNameRegex(), + passwordPattern: api.getPasswordRegex(), + }) + ); views.syncScrollPosition(); views.decorateValidator(this._formNode); - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } clearMessages() { @@ -37,29 +40,31 @@ class RegistrationView extends events.EventTarget { _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - name: this._userNameFieldNode.value, - password: this._passwordFieldNode.value, - email: this._emailFieldNode.value, - }, - })); + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + name: this._userNameFieldNode.value, + password: this._passwordFieldNode.value, + email: this._emailFieldNode.value, + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _userNameFieldNode() { - return this._formNode.querySelector('[name=name]'); + return this._formNode.querySelector("[name=name]"); } get _passwordFieldNode() { - return this._formNode.querySelector('[name=password]'); + return this._formNode.querySelector("[name=password]"); } get _emailFieldNode() { - return this._formNode.querySelector('[name=email]'); + return this._formNode.querySelector("[name=email]"); } } diff --git a/client/js/views/settings_view.js b/client/js/views/settings_view.js index 5ab7316..184028e 100644 --- a/client/js/views/settings_view.js +++ b/client/js/views/settings_view.js @@ -1,21 +1,23 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); -const template = views.getTemplate('settings'); +const template = views.getTemplate("settings"); class SettingsView extends events.EventTarget { constructor(ctx) { super(); - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); views.replaceContent( - this._hostNode, template({browsingSettings: ctx.settings})); + this._hostNode, + template({ browsingSettings: ctx.settings }) + ); views.syncScrollPosition(); views.decorateValidator(this._formNode); - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } clearMessages() { @@ -28,26 +30,32 @@ class SettingsView extends events.EventTarget { _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - upscaleSmallPosts: this._find('upscale-small-posts').checked, - endlessScroll: this._find('endless-scroll').checked, - keyboardShortcuts: this._find('keyboard-shortcuts').checked, - transparencyGrid: this._find('transparency-grid').checked, - tagSuggestions: this._find('tag-suggestions').checked, - autoplayVideos: this._find('autoplay-videos').checked, - postsPerPage: this._find('posts-per-page').value, - tagUnderscoresAsSpaces: this._find('tag-underscores-as-spaces').checked, - }, - })); + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + upscaleSmallPosts: this._find("upscale-small-posts") + .checked, + endlessScroll: this._find("endless-scroll").checked, + keyboardShortcuts: this._find("keyboard-shortcuts") + .checked, + transparencyGrid: this._find("transparency-grid").checked, + tagSuggestions: this._find("tag-suggestions").checked, + autoplayVideos: this._find("autoplay-videos").checked, + postsPerPage: this._find("posts-per-page").value, + tagUnderscoresAsSpaces: this._find( + "tag-underscores-as-spaces" + ).checked, + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } _find(nodeName) { - return this._formNode.querySelector('[name=' + nodeName + ']'); + return this._formNode.querySelector("[name=" + nodeName + "]"); } } diff --git a/client/js/views/snapshots_page_view.js b/client/js/views/snapshots_page_view.js index e7ea264..a665ea0 100644 --- a/client/js/views/snapshots_page_view.js +++ b/client/js/views/snapshots_page_view.js @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; -const views = require('../util/views.js'); +const views = require("../util/views.js"); -const template = views.getTemplate('snapshots-page'); +const template = views.getTemplate("snapshots-page"); function _extend(target, source) { target.push.apply(target, source); @@ -10,18 +10,18 @@ function _extend(target, source) { function _formatBasicChange(diff, text) { const lines = []; - if (diff.type === 'list change') { + if (diff.type === "list change") { const addedItems = diff.added; const removedItems = diff.removed; if (addedItems && addedItems.length) { - lines.push(`Added ${text} (${addedItems.join(', ')})`); + lines.push(`Added ${text} (${addedItems.join(", ")})`); } if (removedItems && removedItems.length) { - lines.push(`Removed ${text} (${removedItems.join(', ')})`); + lines.push(`Removed ${text} (${removedItems.join(", ")})`); } - } else if (diff.type === 'primitive change') { - const oldValue = diff['old-value']; - const newValue = diff['new-value']; + } else if (diff.type === "primitive change") { + const oldValue = diff["old-value"]; + const newValue = diff["new-value"]; lines.push(`Changed ${text} (${oldValue} → ${newValue})`); } else { lines.push(`Changed ${text}`); @@ -30,13 +30,13 @@ function _formatBasicChange(diff, text) { } function _makeResourceLink(type, id) { - if (type === 'post') { + if (type === "post") { return views.makePostLink(id, true); - } else if (type === 'tag') { + } else if (type === "tag") { return views.makeTagLink(id, true); - } else if (type === 'tag_category') { + } else if (type === "tag_category") { return 'category "' + id + '"'; - } else if (type === 'pool') { + } else if (type === "pool") { return views.makePoolLink(id, true); } } @@ -50,96 +50,102 @@ function _makeItemCreation(type, data) { let text = key[0].toUpperCase() + key.substr(1).toLowerCase(); if (Array.isArray(data[key])) { if (data[key].length) { - lines.push(`${text}: ${data[key].join(', ')}`); + lines.push(`${text}: ${data[key].join(", ")}`); } } else { lines.push(`${text}: ${data[key]}`); } } - return lines.join('<br/>'); + return lines.join("<br/>"); } function _makeItemModification(type, data) { const lines = []; const diff = data.value; - if (type === 'tag_category') { + if (type === "tag_category") { if (diff.name) { - _extend(lines, _formatBasicChange(diff.name, 'name')); + _extend(lines, _formatBasicChange(diff.name, "name")); } if (diff.color) { - _extend(lines, _formatBasicChange(diff.color, 'color')); + _extend(lines, _formatBasicChange(diff.color, "color")); } if (diff.default) { - _extend(lines, ['Made into default category']); + _extend(lines, ["Made into default category"]); } - - } else if (type === 'tag') { + } else if (type === "tag") { if (diff.names) { - _extend(lines, _formatBasicChange(diff.names, 'names')); + _extend(lines, _formatBasicChange(diff.names, "names")); } if (diff.category) { - _extend( - lines, _formatBasicChange(diff.category, 'category')); + _extend(lines, _formatBasicChange(diff.category, "category")); } if (diff.suggestions) { _extend( - lines, _formatBasicChange(diff.suggestions, 'suggestions')); + lines, + _formatBasicChange(diff.suggestions, "suggestions") + ); } if (diff.implications) { _extend( - lines, _formatBasicChange(diff.implications, 'implications')); + lines, + _formatBasicChange(diff.implications, "implications") + ); } - - } else if (type === 'post') { + } else if (type === "post") { if (diff.checksum) { - _extend(lines, ['Changed content']); + _extend(lines, ["Changed content"]); } if (diff.featured) { - _extend(lines, ['Featured on front page']); + _extend(lines, ["Featured on front page"]); } if (diff.source) { - _extend(lines, _formatBasicChange(diff.source, 'source')); + _extend(lines, _formatBasicChange(diff.source, "source")); } if (diff.safety) { - _extend(lines, _formatBasicChange(diff.safety, 'safety')); + _extend(lines, _formatBasicChange(diff.safety, "safety")); } if (diff.tags) { - _extend(lines, _formatBasicChange(diff.tags, 'tags')); + _extend(lines, _formatBasicChange(diff.tags, "tags")); } if (diff.relations) { - _extend(lines, _formatBasicChange(diff.relations, 'relations')); + _extend(lines, _formatBasicChange(diff.relations, "relations")); } if (diff.notes) { - _extend(lines, ['Changed notes']); + _extend(lines, ["Changed notes"]); } if (diff.flags) { - _extend(lines, ['Changed flags']); + _extend(lines, ["Changed flags"]); } - - } else if (type === 'pool') { + } else if (type === "pool") { if (diff.names) { - _extend(lines, _formatBasicChange(diff.names, 'names')); + _extend(lines, _formatBasicChange(diff.names, "names")); } if (diff.category) { - _extend( - lines, _formatBasicChange(diff.category, 'category')); + _extend(lines, _formatBasicChange(diff.category, "category")); } if (diff.posts) { - _extend( - lines, _formatBasicChange(diff.posts, 'posts')); + _extend(lines, _formatBasicChange(diff.posts, "posts")); } } - return lines.join('<br/>'); + return lines.join("<br/>"); } class SnapshotsPageView { constructor(ctx) { - views.replaceContent(ctx.hostNode, template(Object.assign({ - makeResourceLink: _makeResourceLink, - makeItemCreation: _makeItemCreation, - makeItemModification: _makeItemModification, - }, ctx))); + views.replaceContent( + ctx.hostNode, + template( + Object.assign( + { + makeResourceLink: _makeResourceLink, + makeItemCreation: _makeItemCreation, + makeItemModification: _makeItemModification, + }, + ctx + ) + ) + ); } } diff --git a/client/js/views/tag_categories_view.js b/client/js/views/tag_categories_view.js index 7e1000d..1f1a4da 100644 --- a/client/js/views/tag_categories_view.js +++ b/client/js/views/tag_categories_view.js @@ -1,17 +1,17 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); -const TagCategory = require('../models/tag_category.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); +const TagCategory = require("../models/tag_category.js"); -const template = views.getTemplate('tag-categories'); -const rowTemplate = views.getTemplate('tag-category-row'); +const template = views.getTemplate("tag-categories"); +const rowTemplate = views.getTemplate("tag-category-row"); class TagCategoriesView extends events.EventTarget { constructor(ctx) { super(); this._ctx = ctx; - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); views.replaceContent(this._hostNode, template(ctx)); views.syncScrollPosition(); @@ -31,18 +31,22 @@ class TagCategoriesView extends events.EventTarget { } if (this._addLinkNode) { - this._addLinkNode.addEventListener( - 'click', e => this._evtAddButtonClick(e)); + this._addLinkNode.addEventListener("click", (e) => + this._evtAddButtonClick(e) + ); } - ctx.tagCategories.addEventListener( - 'add', e => this._evtTagCategoryAdded(e)); + ctx.tagCategories.addEventListener("add", (e) => + this._evtTagCategoryAdded(e) + ); - ctx.tagCategories.addEventListener( - 'remove', e => this._evtTagCategoryDeleted(e)); + ctx.tagCategories.addEventListener("remove", (e) => + this._evtTagCategoryDeleted(e) + ); - this._formNode.addEventListener( - 'submit', e => this._evtSaveButtonClick(e, ctx)); + this._formNode.addEventListener("submit", (e) => + this._evtSaveButtonClick(e, ctx) + ); } enableForm() { @@ -66,44 +70,48 @@ class TagCategoriesView extends events.EventTarget { } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _tableBodyNode() { - return this._hostNode.querySelector('tbody'); + return this._hostNode.querySelector("tbody"); } get _addLinkNode() { - return this._hostNode.querySelector('a.add'); + return this._hostNode.querySelector("a.add"); } _addTagCategoryRowNode(tagCategory) { const rowNode = rowTemplate( - Object.assign( - {}, this._ctx, {tagCategory: tagCategory})); + Object.assign({}, this._ctx, { tagCategory: tagCategory }) + ); - const nameInput = rowNode.querySelector('.name input'); + const nameInput = rowNode.querySelector(".name input"); if (nameInput) { - nameInput.addEventListener( - 'change', e => this._evtNameChange(e, rowNode)); + nameInput.addEventListener("change", (e) => + this._evtNameChange(e, rowNode) + ); } - const colorInput = rowNode.querySelector('.color input'); + const colorInput = rowNode.querySelector(".color input"); if (colorInput) { - colorInput.addEventListener( - 'change', e => this._evtColorChange(e, rowNode)); + colorInput.addEventListener("change", (e) => + this._evtColorChange(e, rowNode) + ); } - const removeLinkNode = rowNode.querySelector('.remove a'); + const removeLinkNode = rowNode.querySelector(".remove a"); if (removeLinkNode) { - removeLinkNode.addEventListener( - 'click', e => this._evtDeleteButtonClick(e, rowNode)); + removeLinkNode.addEventListener("click", (e) => + this._evtDeleteButtonClick(e, rowNode) + ); } - const defaultLinkNode = rowNode.querySelector('.set-default a'); + const defaultLinkNode = rowNode.querySelector(".set-default a"); if (defaultLinkNode) { - defaultLinkNode.addEventListener( - 'click', e => this._evtSetDefaultButtonClick(e, rowNode)); + defaultLinkNode.addEventListener("click", (e) => + this._evtSetDefaultButtonClick(e, rowNode) + ); } this._tableBodyNode.appendChild(rowNode); @@ -141,7 +149,7 @@ class TagCategoriesView extends events.EventTarget { _evtDeleteButtonClick(e, rowNode, link) { e.preventDefault(); - if (e.target.classList.contains('inactive')) { + if (e.target.classList.contains("inactive")) { return; } this._ctx.tagCategories.remove(rowNode._tagCategory); @@ -150,16 +158,16 @@ class TagCategoriesView extends events.EventTarget { _evtSetDefaultButtonClick(e, rowNode) { e.preventDefault(); this._ctx.tagCategories.defaultCategory = rowNode._tagCategory; - const oldRowNode = rowNode.parentNode.querySelector('tr.default'); + const oldRowNode = rowNode.parentNode.querySelector("tr.default"); if (oldRowNode) { - oldRowNode.classList.remove('default'); + oldRowNode.classList.remove("default"); } - rowNode.classList.add('default'); + rowNode.classList.add("default"); } _evtSaveButtonClick(e, ctx) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit')); + this.dispatchEvent(new CustomEvent("submit")); } } diff --git a/client/js/views/tag_delete_view.js b/client/js/views/tag_delete_view.js index 4d27f15..4246ec3 100644 --- a/client/js/views/tag_delete_view.js +++ b/client/js/views/tag_delete_view.js @@ -1,9 +1,9 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); -const template = views.getTemplate('tag-delete'); +const template = views.getTemplate("tag-delete"); class TagDeleteView extends events.EventTarget { constructor(ctx) { @@ -13,7 +13,7 @@ class TagDeleteView extends events.EventTarget { this._tag = ctx.tag; views.replaceContent(this._hostNode, template(ctx)); views.decorateValidator(this._formNode); - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } clearMessages() { @@ -38,15 +38,17 @@ class TagDeleteView extends events.EventTarget { _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - tag: this._tag, - }, - })); + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + tag: this._tag, + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } } diff --git a/client/js/views/tag_edit_view.js b/client/js/views/tag_edit_view.js index bb4a382..58c1bc4 100644 --- a/client/js/views/tag_edit_view.js +++ b/client/js/views/tag_edit_view.js @@ -1,12 +1,12 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const api = require('../api.js'); -const misc = require('../util/misc.js'); -const views = require('../util/views.js'); -const TagInputControl = require('../controls/tag_input_control.js'); +const events = require("../events.js"); +const api = require("../api.js"); +const misc = require("../util/misc.js"); +const views = require("../util/views.js"); +const TagInputControl = require("../controls/tag_input_control.js"); -const template = views.getTemplate('tag-edit'); +const template = views.getTemplate("tag-edit"); class TagEditView extends events.EventTarget { constructor(ctx) { @@ -19,28 +19,33 @@ class TagEditView extends events.EventTarget { views.decorateValidator(this._formNode); if (this._namesFieldNode) { - this._namesFieldNode.addEventListener( - 'input', e => this._evtNameInput(e)); + this._namesFieldNode.addEventListener("input", (e) => + this._evtNameInput(e) + ); } if (this._implicationsFieldNode) { new TagInputControl( - this._implicationsFieldNode, this._tag.implications); + this._implicationsFieldNode, + this._tag.implications + ); } if (this._suggestionsFieldNode) { new TagInputControl( - this._suggestionsFieldNode, this._tag.suggestions); + this._suggestionsFieldNode, + this._tag.suggestions + ); } for (let node of this._formNode.querySelectorAll( - 'input, select, textarea')) { - node.addEventListener( - 'change', e => { - this.dispatchEvent(new CustomEvent('change')); - }); + "input, select, textarea" + )) { + node.addEventListener("change", (e) => { + this.dispatchEvent(new CustomEvent("change")); + }); } - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } clearMessages() { @@ -69,72 +74,80 @@ class TagEditView extends events.EventTarget { if (!list.length) { this._namesFieldNode.setCustomValidity( - 'Tags must have at least one name.'); + "Tags must have at least one name." + ); return; } for (let item of list) { if (!regex.test(item)) { this._namesFieldNode.setCustomValidity( - `Tag name "${item}" contains invalid symbols.`); + `Tag name "${item}" contains invalid symbols.` + ); return; } } - this._namesFieldNode.setCustomValidity(''); + this._namesFieldNode.setCustomValidity(""); } _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - tag: this._tag, + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + tag: this._tag, - names: this._namesFieldNode ? - misc.splitByWhitespace(this._namesFieldNode.value) : - undefined, + names: this._namesFieldNode + ? misc.splitByWhitespace(this._namesFieldNode.value) + : undefined, - category: this._categoryFieldNode ? - this._categoryFieldNode.value : - undefined, + category: this._categoryFieldNode + ? this._categoryFieldNode.value + : undefined, - implications: this._implicationsFieldNode ? - misc.splitByWhitespace(this._implicationsFieldNode.value) : - undefined, + implications: this._implicationsFieldNode + ? misc.splitByWhitespace( + this._implicationsFieldNode.value + ) + : undefined, - suggestions: this._suggestionsFieldNode ? - misc.splitByWhitespace(this._suggestionsFieldNode.value) : - undefined, + suggestions: this._suggestionsFieldNode + ? misc.splitByWhitespace( + this._suggestionsFieldNode.value + ) + : undefined, - description: this._descriptionFieldNode ? - this._descriptionFieldNode.value : - undefined, - }, - })); + description: this._descriptionFieldNode + ? this._descriptionFieldNode.value + : undefined, + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _namesFieldNode() { - return this._formNode.querySelector('.names input'); + return this._formNode.querySelector(".names input"); } get _categoryFieldNode() { - return this._formNode.querySelector('.category select'); + return this._formNode.querySelector(".category select"); } get _implicationsFieldNode() { - return this._formNode.querySelector('.implications input'); + return this._formNode.querySelector(".implications input"); } get _suggestionsFieldNode() { - return this._formNode.querySelector('.suggestions input'); + return this._formNode.querySelector(".suggestions input"); } get _descriptionFieldNode() { - return this._formNode.querySelector('.description textarea'); + return this._formNode.querySelector(".description textarea"); } } diff --git a/client/js/views/tag_merge_view.js b/client/js/views/tag_merge_view.js index e29183a..f823ca4 100644 --- a/client/js/views/tag_merge_view.js +++ b/client/js/views/tag_merge_view.js @@ -1,12 +1,11 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const api = require('../api.js'); -const views = require('../util/views.js'); -const TagAutoCompleteControl = - require('../controls/tag_auto_complete_control.js'); +const events = require("../events.js"); +const api = require("../api.js"); +const views = require("../util/views.js"); +const TagAutoCompleteControl = require("../controls/tag_auto_complete_control.js"); -const template = views.getTemplate('tag-merge'); +const template = views.getTemplate("tag-merge"); class TagMergeView extends events.EventTarget { constructor(ctx) { @@ -22,12 +21,16 @@ class TagMergeView extends events.EventTarget { this._autoCompleteControl = new TagAutoCompleteControl( this._targetTagFieldNode, { - confirm: tag => this._autoCompleteControl.replaceSelectedText( - tag.names[0], false), - }); + confirm: (tag) => + this._autoCompleteControl.replaceSelectedText( + tag.names[0], + false + ), + } + ); } - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } clearMessages() { @@ -52,25 +55,27 @@ class TagMergeView extends events.EventTarget { _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - tag: this._tag, - targetTagName: this._targetTagFieldNode.value, - addAlias: this._addAliasCheckboxNode.checked, - }, - })); + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + tag: this._tag, + targetTagName: this._targetTagFieldNode.value, + addAlias: this._addAliasCheckboxNode.checked, + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _targetTagFieldNode() { - return this._formNode.querySelector('input[name=target-tag]'); + return this._formNode.querySelector("input[name=target-tag]"); } get _addAliasCheckboxNode() { - return this._formNode.querySelector('input[name=alias]'); + return this._formNode.querySelector("input[name=alias]"); } } diff --git a/client/js/views/tag_summary_view.js b/client/js/views/tag_summary_view.js index 019e72b..11c6191 100644 --- a/client/js/views/tag_summary_view.js +++ b/client/js/views/tag_summary_view.js @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; -const views = require('../util/views.js'); +const views = require("../util/views.js"); -const template = views.getTemplate('tag-summary'); +const template = views.getTemplate("tag-summary"); class TagSummaryView { constructor(ctx) { diff --git a/client/js/views/tag_view.js b/client/js/views/tag_view.js index 2aaa999..e6c3776 100644 --- a/client/js/views/tag_view.js +++ b/client/js/views/tag_view.js @@ -1,26 +1,26 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); -const misc = require('../util/misc.js'); -const TagSummaryView = require('./tag_summary_view.js'); -const TagEditView = require('./tag_edit_view.js'); -const TagMergeView = require('./tag_merge_view.js'); -const TagDeleteView = require('./tag_delete_view.js'); -const EmptyView = require('../views/empty_view.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); +const misc = require("../util/misc.js"); +const TagSummaryView = require("./tag_summary_view.js"); +const TagEditView = require("./tag_edit_view.js"); +const TagMergeView = require("./tag_merge_view.js"); +const TagDeleteView = require("./tag_delete_view.js"); +const EmptyView = require("../views/empty_view.js"); -const template = views.getTemplate('tag'); +const template = views.getTemplate("tag"); class TagView extends events.EventTarget { constructor(ctx) { super(); this._ctx = ctx; - ctx.tag.addEventListener('change', e => this._evtChange(e)); - ctx.section = ctx.section || 'summary'; + ctx.tag.addEventListener("change", (e) => this._evtChange(e)); + ctx.section = ctx.section || "summary"; ctx.getPrettyTagName = misc.getPrettyTagName; - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); this._install(); } @@ -28,52 +28,54 @@ class TagView extends events.EventTarget { const ctx = this._ctx; views.replaceContent(this._hostNode, template(ctx)); - for (let item of this._hostNode.querySelectorAll('[data-name]')) { + for (let item of this._hostNode.querySelectorAll("[data-name]")) { item.classList.toggle( - 'active', item.getAttribute('data-name') === ctx.section); - if (item.getAttribute('data-name') === ctx.section) { + "active", + item.getAttribute("data-name") === ctx.section + ); + if (item.getAttribute("data-name") === ctx.section) { item.parentNode.scrollLeft = item.getBoundingClientRect().left - - item.parentNode.getBoundingClientRect().left + item.parentNode.getBoundingClientRect().left; } } - ctx.hostNode = this._hostNode.querySelector('.tag-content-holder'); - if (ctx.section === 'edit') { + ctx.hostNode = this._hostNode.querySelector(".tag-content-holder"); + if (ctx.section === "edit") { if (!this._ctx.canEditAnything) { this._view = new EmptyView(); this._view.showError( - 'You don\'t have privileges to edit tags.'); + "You don't have privileges to edit tags." + ); } else { this._view = new TagEditView(ctx); - events.proxyEvent(this._view, this, 'submit'); + events.proxyEvent(this._view, this, "submit"); } - - } else if (ctx.section === 'merge') { + } else if (ctx.section === "merge") { if (!this._ctx.canMerge) { this._view = new EmptyView(); this._view.showError( - 'You don\'t have privileges to merge tags.'); + "You don't have privileges to merge tags." + ); } else { this._view = new TagMergeView(ctx); - events.proxyEvent(this._view, this, 'submit', 'merge'); + events.proxyEvent(this._view, this, "submit", "merge"); } - - } else if (ctx.section === 'delete') { + } else if (ctx.section === "delete") { if (!this._ctx.canDelete) { this._view = new EmptyView(); this._view.showError( - 'You don\'t have privileges to delete tags.'); + "You don't have privileges to delete tags." + ); } else { this._view = new TagDeleteView(ctx); - events.proxyEvent(this._view, this, 'submit', 'delete'); + events.proxyEvent(this._view, this, "submit", "delete"); } - } else { this._view = new TagSummaryView(ctx); } - events.proxyEvent(this._view, this, 'change'); + events.proxyEvent(this._view, this, "change"); views.syncScrollPosition(); } diff --git a/client/js/views/tags_header_view.js b/client/js/views/tags_header_view.js index 425c811..6dd7626 100644 --- a/client/js/views/tags_header_view.js +++ b/client/js/views/tags_header_view.js @@ -1,13 +1,12 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const misc = require('../util/misc.js'); -const search = require('../util/search.js'); -const views = require('../util/views.js'); -const TagAutoCompleteControl = - require('../controls/tag_auto_complete_control.js'); +const events = require("../events.js"); +const misc = require("../util/misc.js"); +const search = require("../util/search.js"); +const views = require("../util/views.js"); +const TagAutoCompleteControl = require("../controls/tag_auto_complete_control.js"); -const template = views.getTemplate('tags-header'); +const template = views.getTemplate("tags-header"); class TagsHeaderView extends events.EventTarget { constructor(ctx) { @@ -20,31 +19,41 @@ class TagsHeaderView extends events.EventTarget { this._autoCompleteControl = new TagAutoCompleteControl( this._queryInputNode, { - confirm: tag => this._autoCompleteControl.replaceSelectedText( - misc.escapeSearchTerm(tag.names[0]), true), - }); + confirm: (tag) => + this._autoCompleteControl.replaceSelectedText( + misc.escapeSearchTerm(tag.names[0]), + true + ), + } + ); } search.searchInputNodeFocusHelper(this._queryInputNode); - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _queryInputNode() { - return this._hostNode.querySelector('[name=search-text]'); + return this._hostNode.querySelector("[name=search-text]"); } _evtSubmit(e) { e.preventDefault(); this._queryInputNode.blur(); - this.dispatchEvent(new CustomEvent('navigate', {detail: {parameters: { - query: this._queryInputNode.value, - page: 1, - }}})); + this.dispatchEvent( + new CustomEvent("navigate", { + detail: { + parameters: { + query: this._queryInputNode.value, + page: 1, + }, + }, + }) + ); } } diff --git a/client/js/views/tags_page_view.js b/client/js/views/tags_page_view.js index cd335a4..e2b2820 100644 --- a/client/js/views/tags_page_view.js +++ b/client/js/views/tags_page_view.js @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; -const views = require('../util/views.js'); +const views = require("../util/views.js"); -const template = views.getTemplate('tags-page'); +const template = views.getTemplate("tags-page"); class TagsPageView { constructor(ctx) { diff --git a/client/js/views/top_navigation_view.js b/client/js/views/top_navigation_view.js index 2f99187..efe75a9 100644 --- a/client/js/views/top_navigation_view.js +++ b/client/js/views/top_navigation_view.js @@ -1,24 +1,24 @@ -'use strict'; +"use strict"; -const views = require('../util/views.js'); +const views = require("../util/views.js"); -const template = views.getTemplate('top-navigation'); +const template = views.getTemplate("top-navigation"); class TopNavigationView { constructor() { - this._hostNode = document.getElementById('top-navigation-holder'); + this._hostNode = document.getElementById("top-navigation-holder"); } get _mobileNavigationToggleNode() { - return this._hostNode.querySelector('#mobile-navigation-toggle'); + return this._hostNode.querySelector("#mobile-navigation-toggle"); } get _navigationListNode() { - return this._hostNode.querySelector('nav > ul'); + return this._hostNode.querySelector("nav > ul"); } get _navigationLinkNodes() { - return this._navigationListNode.querySelectorAll('li > a'); + return this._navigationListNode.querySelectorAll("li > a"); } render(ctx) { @@ -28,28 +28,32 @@ class TopNavigationView { } activate(key) { - for (let itemNode of this._hostNode.querySelectorAll('[data-name]')) { + for (let itemNode of this._hostNode.querySelectorAll("[data-name]")) { itemNode.classList.toggle( - 'active', itemNode.getAttribute('data-name') === key); + "active", + itemNode.getAttribute("data-name") === key + ); } } _bindMobileNavigationEvents() { - this._mobileNavigationToggleNode.addEventListener( - 'click', e => this._mobileNavigationToggleClick(e)); + this._mobileNavigationToggleNode.addEventListener("click", (e) => + this._mobileNavigationToggleClick(e) + ); for (let navigationLinkNode of this._navigationLinkNodes) { - navigationLinkNode.addEventListener( - 'click', e => this._navigationLinkClick(e)); + navigationLinkNode.addEventListener("click", (e) => + this._navigationLinkClick(e) + ); } } _mobileNavigationToggleClick(e) { - this._navigationListNode.classList.toggle('opened'); + this._navigationListNode.classList.toggle("opened"); } _navigationLinkClick(e) { - this._navigationListNode.classList.remove('opened'); + this._navigationListNode.classList.remove("opened"); } } diff --git a/client/js/views/user_delete_view.js b/client/js/views/user_delete_view.js index bdaf9e6..37de52f 100644 --- a/client/js/views/user_delete_view.js +++ b/client/js/views/user_delete_view.js @@ -1,9 +1,9 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); -const template = views.getTemplate('user-delete'); +const template = views.getTemplate("user-delete"); class UserDeleteView extends events.EventTarget { constructor(ctx) { @@ -14,7 +14,7 @@ class UserDeleteView extends events.EventTarget { views.replaceContent(this._hostNode, template(ctx)); views.decorateValidator(this._formNode); - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } clearMessages() { @@ -39,16 +39,17 @@ class UserDeleteView extends events.EventTarget { _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - user: this._user, - }, - })); + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + user: this._user, + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('form'); - + return this._hostNode.querySelector("form"); } } diff --git a/client/js/views/user_edit_view.js b/client/js/views/user_edit_view.js index 317d8e1..4886726 100644 --- a/client/js/views/user_edit_view.js +++ b/client/js/views/user_edit_view.js @@ -1,18 +1,18 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const api = require('../api.js'); -const views = require('../util/views.js'); -const FileDropperControl = require('../controls/file_dropper_control.js'); +const events = require("../events.js"); +const api = require("../api.js"); +const views = require("../util/views.js"); +const FileDropperControl = require("../controls/file_dropper_control.js"); -const template = views.getTemplate('user-edit'); +const template = views.getTemplate("user-edit"); class UserEditView extends events.EventTarget { constructor(ctx) { super(); - ctx.userNamePattern = api.getUserNameRegex() + (/|^$/).source; - ctx.passwordPattern = api.getPasswordRegex() + (/|^$/).source; + ctx.userNamePattern = api.getUserNameRegex() + /|^$/.source; + ctx.passwordPattern = api.getPasswordRegex() + /|^$/.source; this._user = ctx.user; this._hostNode = ctx.hostNode; @@ -22,24 +22,26 @@ class UserEditView extends events.EventTarget { this._avatarContent = null; if (this._avatarContentInputNode) { this._avatarFileDropper = new FileDropperControl( - this._avatarContentInputNode, {lock: true}); - this._avatarFileDropper.addEventListener('fileadd', e => { + this._avatarContentInputNode, + { lock: true } + ); + this._avatarFileDropper.addEventListener("fileadd", (e) => { this._hostNode.querySelector( - '[name=avatar-style][value=manual]').checked = true; + "[name=avatar-style][value=manual]" + ).checked = true; this._avatarContent = e.detail.files[0]; }); } - for (let node of this._formNode.querySelectorAll('input, select')) { - node.addEventListener( - 'change', e => { - if (!e.target.classList.contains('anticomplete')) { - this.dispatchEvent(new CustomEvent('change')); - } - }); + for (let node of this._formNode.querySelectorAll("input, select")) { + node.addEventListener("change", (e) => { + if (!e.target.classList.contains("anticomplete")) { + this.dispatchEvent(new CustomEvent("change")); + } + }); } - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } clearMessages() { @@ -64,61 +66,63 @@ class UserEditView extends events.EventTarget { _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - user: this._user, + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + user: this._user, - name: this._userNameInputNode ? - this._userNameInputNode.value : - undefined, + name: this._userNameInputNode + ? this._userNameInputNode.value + : undefined, - email: this._emailInputNode ? - this._emailInputNode.value : - undefined, + email: this._emailInputNode + ? this._emailInputNode.value + : undefined, - rank: this._rankInputNode ? - this._rankInputNode.value : - undefined, + rank: this._rankInputNode + ? this._rankInputNode.value + : undefined, - avatarStyle: this._avatarStyleInputNode ? - this._avatarStyleInputNode.value : - undefined, + avatarStyle: this._avatarStyleInputNode + ? this._avatarStyleInputNode.value + : undefined, - password: this._passwordInputNode ? - this._passwordInputNode.value : - undefined, + password: this._passwordInputNode + ? this._passwordInputNode.value + : undefined, - avatarContent: this._avatarContent, - }, - })); + avatarContent: this._avatarContent, + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _rankInputNode() { - return this._formNode.querySelector('[name=rank]'); + return this._formNode.querySelector("[name=rank]"); } get _emailInputNode() { - return this._formNode.querySelector('[name=email]'); + return this._formNode.querySelector("[name=email]"); } get _userNameInputNode() { - return this._formNode.querySelector('[name=name]'); + return this._formNode.querySelector("[name=name]"); } get _passwordInputNode() { - return this._formNode.querySelector('[name=password]'); + return this._formNode.querySelector("[name=password]"); } get _avatarContentInputNode() { - return this._formNode.querySelector('#avatar-content'); + return this._formNode.querySelector("#avatar-content"); } get _avatarStyleInputNode() { - return this._formNode.querySelector('[name=avatar-style]:checked'); + return this._formNode.querySelector("[name=avatar-style]:checked"); } } diff --git a/client/js/views/user_summary_view.js b/client/js/views/user_summary_view.js index d8463f3..eab827f 100644 --- a/client/js/views/user_summary_view.js +++ b/client/js/views/user_summary_view.js @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; -const views = require('../util/views.js'); +const views = require("../util/views.js"); -const template = views.getTemplate('user-summary'); +const template = views.getTemplate("user-summary"); class UserSummaryView { constructor(ctx) { diff --git a/client/js/views/user_tokens_view.js b/client/js/views/user_tokens_view.js index 8e283bf..68707f0 100644 --- a/client/js/views/user_tokens_view.js +++ b/client/js/views/user_tokens_view.js @@ -1,9 +1,9 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); -const template = views.getTemplate('user-tokens'); +const template = views.getTemplate("user-tokens"); class UserTokenView extends events.EventTarget { constructor(ctx) { @@ -16,7 +16,7 @@ class UserTokenView extends events.EventTarget { views.replaceContent(this._hostNode, template(ctx)); views.decorateValidator(this._formNode); - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); this._decorateTokenForms(); this._decorateTokenNoteChangeLinks(); @@ -26,8 +26,9 @@ class UserTokenView extends events.EventTarget { this._tokenFormNodes = []; for (let i = 0; i < this._tokens.length; i++) { let formNode = this._hostNode.querySelector( - '.token[data-token-id=\"' + i + '\"]'); - formNode.addEventListener('submit', e => this._evtDelete(e)); + '.token[data-token-id="' + i + '"]' + ); + formNode.addEventListener("submit", (e) => this._evtDelete(e)); this._tokenFormNodes.push(formNode); } } @@ -35,9 +36,11 @@ class UserTokenView extends events.EventTarget { _decorateTokenNoteChangeLinks() { for (let i = 0; i < this._tokens.length; i++) { let linkNode = this._hostNode.querySelector( - '.token-change-note[data-token-id=\"' + i + '\"]'); - linkNode.addEventListener( - 'click', e => this._evtChangeNoteClick(e)); + '.token-change-note[data-token-id="' + i + '"]' + ); + linkNode.addEventListener("click", (e) => + this._evtChangeNoteClick(e) + ); } } @@ -69,65 +72,75 @@ class UserTokenView extends events.EventTarget { _evtDelete(e) { e.preventDefault(); - const userToken = this._tokens[parseInt( - e.target.getAttribute('data-token-id'))]; - this.dispatchEvent(new CustomEvent('delete', { - detail: { - user: this._user, - userToken: userToken, - }, - })); + const userToken = this._tokens[ + parseInt(e.target.getAttribute("data-token-id")) + ]; + this.dispatchEvent( + new CustomEvent("delete", { + detail: { + user: this._user, + userToken: userToken, + }, + }) + ); } _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('submit', { - detail: { - user: this._user, + this.dispatchEvent( + new CustomEvent("submit", { + detail: { + user: this._user, - note: this._userTokenNoteInputNode ? - this._userTokenNoteInputNode.value : - undefined, + note: this._userTokenNoteInputNode + ? this._userTokenNoteInputNode.value + : undefined, - expirationTime: - (this._userTokenExpirationTimeInputNode - && this._userTokenExpirationTimeInputNode.value) ? - new Date(this._userTokenExpirationTimeInputNode.value) - .toISOString() : - undefined, - }, - })); + expirationTime: + this._userTokenExpirationTimeInputNode && + this._userTokenExpirationTimeInputNode.value + ? new Date( + this._userTokenExpirationTimeInputNode.value + ).toISOString() + : undefined, + }, + }) + ); } _evtChangeNoteClick(e) { e.preventDefault(); const userToken = this._tokens[ - parseInt(e.target.getAttribute('data-token-id'))]; + parseInt(e.target.getAttribute("data-token-id")) + ]; const text = window.prompt( - 'Please enter the new name:', - userToken.note !== null ? userToken.note : undefined); + "Please enter the new name:", + userToken.note !== null ? userToken.note : undefined + ); if (!text) { return; } - this.dispatchEvent(new CustomEvent('update', { - detail: { - user: this._user, - userToken: userToken, - note: text ? text : undefined, - }, - })); + this.dispatchEvent( + new CustomEvent("update", { + detail: { + user: this._user, + userToken: userToken, + note: text ? text : undefined, + }, + }) + ); } get _formNode() { - return this._hostNode.querySelector('#create-token-form'); + return this._hostNode.querySelector("#create-token-form"); } get _userTokenNoteInputNode() { - return this._formNode.querySelector('.note input'); + return this._formNode.querySelector(".note input"); } get _userTokenExpirationTimeInputNode() { - return this._formNode.querySelector('.expirationTime input'); + return this._formNode.querySelector(".expirationTime input"); } } diff --git a/client/js/views/user_view.js b/client/js/views/user_view.js index d96ebe1..2eebfe9 100644 --- a/client/js/views/user_view.js +++ b/client/js/views/user_view.js @@ -1,24 +1,24 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const views = require('../util/views.js'); -const UserDeleteView = require('./user_delete_view.js'); -const UserTokensView = require('./user_tokens_view.js'); -const UserSummaryView = require('./user_summary_view.js'); -const UserEditView = require('./user_edit_view.js'); -const EmptyView = require('../views/empty_view.js'); +const events = require("../events.js"); +const views = require("../util/views.js"); +const UserDeleteView = require("./user_delete_view.js"); +const UserTokensView = require("./user_tokens_view.js"); +const UserSummaryView = require("./user_summary_view.js"); +const UserEditView = require("./user_edit_view.js"); +const EmptyView = require("../views/empty_view.js"); -const template = views.getTemplate('user'); +const template = views.getTemplate("user"); class UserView extends events.EventTarget { constructor(ctx) { super(); this._ctx = ctx; - ctx.user.addEventListener('change', e => this._evtChange(e)); - ctx.section = ctx.section || 'summary'; + ctx.user.addEventListener("change", (e) => this._evtChange(e)); + ctx.section = ctx.section || "summary"; - this._hostNode = document.getElementById('content-holder'); + this._hostNode = document.getElementById("content-holder"); this._install(); } @@ -26,52 +26,56 @@ class UserView extends events.EventTarget { const ctx = this._ctx; views.replaceContent(this._hostNode, template(ctx)); - for (let item of this._hostNode.querySelectorAll('[data-name]')) { + for (let item of this._hostNode.querySelectorAll("[data-name]")) { item.classList.toggle( - 'active', item.getAttribute('data-name') === ctx.section); - if (item.getAttribute('data-name') === ctx.section) { + "active", + item.getAttribute("data-name") === ctx.section + ); + if (item.getAttribute("data-name") === ctx.section) { item.parentNode.scrollLeft = item.getBoundingClientRect().left - - item.parentNode.getBoundingClientRect().left + item.parentNode.getBoundingClientRect().left; } } - ctx.hostNode = this._hostNode.querySelector('#user-content-holder'); - if (ctx.section === 'edit') { + ctx.hostNode = this._hostNode.querySelector("#user-content-holder"); + if (ctx.section === "edit") { if (!this._ctx.canEditAnything) { this._view = new EmptyView(); this._view.showError( - 'You don\'t have privileges to edit users.'); + "You don't have privileges to edit users." + ); } else { this._view = new UserEditView(ctx); - events.proxyEvent(this._view, this, 'submit'); + events.proxyEvent(this._view, this, "submit"); } - } else if (ctx.section === 'list-tokens') { + } else if (ctx.section === "list-tokens") { if (!this._ctx.canListTokens) { this._view = new EmptyView(); this._view.showError( - 'You don\'t have privileges to view user tokens.'); + "You don't have privileges to view user tokens." + ); } else { this._view = new UserTokensView(ctx); - events.proxyEvent(this._view, this, 'delete', 'delete-token'); - events.proxyEvent(this._view, this, 'submit', 'create-token'); - events.proxyEvent(this._view, this, 'update', 'update-token'); + events.proxyEvent(this._view, this, "delete", "delete-token"); + events.proxyEvent(this._view, this, "submit", "create-token"); + events.proxyEvent(this._view, this, "update", "update-token"); } - } else if (ctx.section === 'delete') { + } else if (ctx.section === "delete") { if (!this._ctx.canDelete) { this._view = new EmptyView(); this._view.showError( - 'You don\'t have privileges to delete users.'); + "You don't have privileges to delete users." + ); } else { this._view = new UserDeleteView(ctx); - events.proxyEvent(this._view, this, 'submit', 'delete'); + events.proxyEvent(this._view, this, "submit", "delete"); } - } else { this._view = new UserSummaryView(ctx); } - events.proxyEvent(this._view, this, 'change'); + events.proxyEvent(this._view, this, "change"); views.syncScrollPosition(); } diff --git a/client/js/views/users_header_view.js b/client/js/views/users_header_view.js index 08b7620..d6b7c6a 100644 --- a/client/js/views/users_header_view.js +++ b/client/js/views/users_header_view.js @@ -1,10 +1,10 @@ -'use strict'; +"use strict"; -const events = require('../events.js'); -const search = require('../util/search.js'); -const views = require('../util/views.js'); +const events = require("../events.js"); +const search = require("../util/search.js"); +const views = require("../util/views.js"); -const template = views.getTemplate('users-header'); +const template = views.getTemplate("users-header"); class UsersHeaderView extends events.EventTarget { constructor(ctx) { @@ -15,23 +15,29 @@ class UsersHeaderView extends events.EventTarget { search.searchInputNodeFocusHelper(this._queryInputNode); - this._formNode.addEventListener('submit', e => this._evtSubmit(e)); + this._formNode.addEventListener("submit", (e) => this._evtSubmit(e)); } get _formNode() { - return this._hostNode.querySelector('form'); + return this._hostNode.querySelector("form"); } get _queryInputNode() { - return this._formNode.querySelector('[name=search-text]'); + return this._formNode.querySelector("[name=search-text]"); } _evtSubmit(e) { e.preventDefault(); - this.dispatchEvent(new CustomEvent('navigate', {detail: {parameters: { - query: this._queryInputNode.value, - page: 1, - }}})); + this.dispatchEvent( + new CustomEvent("navigate", { + detail: { + parameters: { + query: this._queryInputNode.value, + page: 1, + }, + }, + }) + ); } } diff --git a/client/js/views/users_page_view.js b/client/js/views/users_page_view.js index f772d4e..2689433 100644 --- a/client/js/views/users_page_view.js +++ b/client/js/views/users_page_view.js @@ -1,8 +1,8 @@ -'use strict'; +"use strict"; -const views = require('../util/views.js'); +const views = require("../util/views.js"); -const template = views.getTemplate('users-page'); +const template = views.getTemplate("users-page"); class UsersPageView { constructor(ctx) { |