diff options
-rw-r--r-- | .eslintignore | 2 | ||||
-rw-r--r-- | .eslintrc | 6 | ||||
-rw-r--r-- | package-lock.json | 136 | ||||
-rw-r--r-- | package.json | 4 | ||||
-rw-r--r-- | src/main/assets/scripts.js | 54 |
5 files changed, 175 insertions, 27 deletions
diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..e6ddcb62 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +src/main/resources/static/scripts.js +target/** @@ -2,7 +2,8 @@ // Extend existing configuration // from ESlint and eslint-plugin-react defaults. "extends": [ - "eslint:recommended" + "eslint:recommended", + "plugin:jsdoc/recommended" //, "plugin:react/recommended" ], // Enable ES6 support. If you want to use custom Babel @@ -19,7 +20,8 @@ // Enable custom plugin known as eslint-plugin-react "plugins": [ // "react" - "only-ascii" + "only-ascii", + "jsdoc" ], // http://eslint.org/docs/rules/ "rules": { diff --git a/package-lock.json b/package-lock.json index 15e09798..5d1bb662 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "cross-env": "^7.0.3", "cssnano": "^5.1.10", "eslint": "8.16.0", + "eslint-plugin-jsdoc": "^39.3.2", "eslint-plugin-only-ascii": "0.0.0", "eslint-webpack-plugin": "^3.1.1", "file-loader": "^6.2.0", @@ -1887,6 +1888,20 @@ "node": ">=10.0.0" } }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.31.0.tgz", + "integrity": "sha512-tc1/iuQcnaiSIUVad72PBierDFpsxdUHtEF/OrfqvM1CBAsIoMP51j52jTMb3dXriwhieTo289InzZj72jL3EQ==", + "dev": true, + "dependencies": { + "comment-parser": "1.3.1", + "esquery": "^1.4.0", + "jsdoc-type-pratt-parser": "~3.1.0" + }, + "engines": { + "node": "^14 || ^16 || ^17 || ^18" + } + }, "node_modules/@eslint/eslintrc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", @@ -2842,6 +2857,15 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "node_modules/comment-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", + "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "dev": true, + "engines": { + "node": ">= 12.0.0" + } + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -3715,6 +3739,54 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-plugin-jsdoc": { + "version": "39.3.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.3.2.tgz", + "integrity": "sha512-RSGN94RYzIJS/WfW3l6cXzRLfJWxvJgNQZ4w0WCaxJWDJMigtwTsILEAfKqmmPkT2rwMH/s3C7G5ChDE6cwPJg==", + "dev": true, + "dependencies": { + "@es-joy/jsdoccomment": "~0.31.0", + "comment-parser": "1.3.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.4.0", + "semver": "^7.3.7", + "spdx-expression-parse": "^3.0.1" + }, + "engines": { + "node": "^14 || ^16 || ^17 || ^18" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/eslint-plugin-only-ascii": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-only-ascii/-/eslint-plugin-only-ascii-0.0.0.tgz", @@ -4921,6 +4993,15 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz", + "integrity": "sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==", + "dev": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -9507,6 +9588,17 @@ "integrity": "sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==", "dev": true }, + "@es-joy/jsdoccomment": { + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.31.0.tgz", + "integrity": "sha512-tc1/iuQcnaiSIUVad72PBierDFpsxdUHtEF/OrfqvM1CBAsIoMP51j52jTMb3dXriwhieTo289InzZj72jL3EQ==", + "dev": true, + "requires": { + "comment-parser": "1.3.1", + "esquery": "^1.4.0", + "jsdoc-type-pratt-parser": "~3.1.0" + } + }, "@eslint/eslintrc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", @@ -10265,6 +10357,12 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "comment-parser": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", + "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", + "dev": true + }, "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -10976,6 +11074,38 @@ } } }, + "eslint-plugin-jsdoc": { + "version": "39.3.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.3.2.tgz", + "integrity": "sha512-RSGN94RYzIJS/WfW3l6cXzRLfJWxvJgNQZ4w0WCaxJWDJMigtwTsILEAfKqmmPkT2rwMH/s3C7G5ChDE6cwPJg==", + "dev": true, + "requires": { + "@es-joy/jsdoccomment": "~0.31.0", + "comment-parser": "1.3.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.4.0", + "semver": "^7.3.7", + "spdx-expression-parse": "^3.0.1" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "eslint-plugin-only-ascii": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-only-ascii/-/eslint-plugin-only-ascii-0.0.0.tgz", @@ -11743,6 +11873,12 @@ "argparse": "^2.0.1" } }, + "jsdoc-type-pratt-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz", + "integrity": "sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==", + "dev": true + }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", diff --git a/package.json b/package.json index cb36149a..d482af96 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "test": "echo \"Error: no test specified\" && exit 1", "compile:css": "postcss --verbose --map style.css.map src/main/assets/style.css -d src/main/resources/static/", "compile:js": "webpack", - "compile": "cross-env NODE_ENV=production concurrently \"npm run compile:css\" \"npm run compile:js\"" + "compile": "cross-env NODE_ENV=production concurrently \"npm run compile:css\" \"npm run compile:js\"", + "lint": "eslint ." }, "repository": { "type": "git", @@ -38,6 +39,7 @@ "cross-env": "^7.0.3", "cssnano": "^5.1.10", "eslint": "8.16.0", + "eslint-plugin-jsdoc": "^39.3.2", "eslint-plugin-only-ascii": "0.0.0", "eslint-webpack-plugin": "^3.1.1", "file-loader": "^6.2.0", diff --git a/src/main/assets/scripts.js b/src/main/assets/scripts.js index d5ceb86e..87fdf7a4 100644 --- a/src/main/assets/scripts.js +++ b/src/main/assets/scripts.js @@ -9,13 +9,14 @@ import svg4everybody from 'svg4everybody'; /** * Autosize textarea + * * @param {HTMLTextAreaElement} el textarea element */ function autosize(el) { let offset = el.offsetHeight - el.clientHeight; el.addEventListener('input', (ev) => { - const textarea = /** @type HTMLTextAreaElement */ (ev.target); + const textarea = /** @type {HTMLTextAreaElement} */ (ev.target); textarea.style.height = 'auto'; textarea.style.height = (textarea.scrollHeight + offset) + 'px'; }); @@ -23,8 +24,9 @@ function autosize(el) { /** * Display an icon from the evil-icons set + * * @param {string} name Icon name from the iconset - * @returns HTML markup for the selected icon + * @returns {string} HTML markup for the selected icon */ function evilIcon(name) { return `<div class="icon icon--${name}"><svg class="icon__cnt"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="/sprite.svg#${name}-icon"></use></svg></div>`; @@ -87,6 +89,7 @@ const translations = { /** * Detect window language + * * @returns {string} Detected language */ function getLang() { @@ -223,6 +226,7 @@ function updateRepliesCounter() { /** * Submit form on Ctrl+Enter + * * @param {HTMLElement} formEl Target form element * @param {KeyboardEvent} ev Keyboard event */ @@ -230,17 +234,18 @@ function postformListener(formEl, ev) { if (ev.ctrlKey && (ev.keyCode == 10 || ev.keyCode == 13)) { let form = formEl.closest('form'); if (!form.onsubmit || form.submit()) { - /** @type HTMLInputElement */ (form.querySelector('input[type="submit"]')).click(); + /** @type {HTMLInputElement} */ (form.querySelector('input[type="submit"]')).click(); } } } /** * Close dialog on Esc + * * @param {KeyboardEvent} ev Keyboard event */ function closeDialogListener(ev) { - ev = ev || /** @type KeyboardEvent */ (window.event); + ev = ev || /** @type {KeyboardEvent} */ (window.event); if (ev.keyCode == 27) { closeDialog(); } @@ -273,13 +278,13 @@ function showCommentForm(mid, rid) { formTarget.remove(); let form = reply.querySelector('form'); - let submitButton = /** @type HTMLInputElement */ (form.querySelector('input[type="submit"]')); + let submitButton = /** @type {HTMLInputElement} */ (form.querySelector('input[type="submit"]')); let attachButton = form.querySelector('.msg-comment .attach-photo'); - attachButton.addEventListener('click', e => attachCommentPhoto(/** @type HTMLDivElement */ (e.target))); + attachButton.addEventListener('click', e => attachCommentPhoto(/** @type {HTMLDivElement} */ (e.target))); - let textarea = /** @type HTMLTextAreaElement */ (form.querySelector('.msg-comment textarea')); - textarea.addEventListener('keypress', e => postformListener(/** @type HTMLElement */ (e.target), e)); + let textarea = /** @type {HTMLTextAreaElement} */ (form.querySelector('.msg-comment textarea')); + textarea.addEventListener('keypress', e => postformListener(/** @type {HTMLElement} */ (e.target), e)); autosize(textarea); let validateMessage = () => { @@ -318,7 +323,7 @@ function showCommentForm(mid, rid) { e.preventDefault(); }); } - /** @type HTMLTextAreaElement */ (reply.querySelector('.msg-comment textarea')).focus(); + /** @type {HTMLTextAreaElement} */ (reply.querySelector('.msg-comment textarea')).focus(); } function attachInput() { @@ -332,6 +337,7 @@ function attachInput() { /** * "Attach" button + * * @param {HTMLDivElement} div */ function attachCommentPhoto(div) { @@ -395,7 +401,7 @@ function closeDialog() { function checkUsername() { var uname = document.querySelector('#username').textContent, - style = /** @type HTMLElement */ (document.querySelector('#username')).style; + style = /** @type {HTMLElement} */ (document.querySelector('#username')).style; fetch('/api/users?uname=' + uname) .then(handleErrors) .then(function() { @@ -554,15 +560,15 @@ ready(() => { e.target.removeEventListener('click', insertPMButtons); e.preventDefault(); }; - /** @type HTMLTextAreaElement[] */ (Array.from(document.querySelectorAll('textarea.replypm'))).forEach(function(e) { + /** @type {HTMLTextAreaElement[]} */ (Array.from(document.querySelectorAll('textarea.replypm'))).forEach(function(e) { e.addEventListener('click', insertPMButtons); e.addEventListener('keypress', function(e) { - postformListener(/** @type HTMLElement */ (e.target), e); + postformListener(/** @type {HTMLElement} */ (e.target), e); }); }); - /** @type HTMLTextAreaElement[] */ (Array.from(document.querySelectorAll('#postmsg textarea'))).forEach(function(e) { + /** @type {HTMLTextAreaElement[]} */ (Array.from(document.querySelectorAll('#postmsg textarea'))).forEach(function(e) { e.addEventListener('keypress', function(e) { - postformListener(/** @type HTMLElement */ (e.target), e); + postformListener(/** @type {HTMLElement} */ (e.target), e); }); }); @@ -581,10 +587,10 @@ ready(() => { }); let opMessage = document.querySelector('.msgthread'); if (opMessage) { - let replyTextarea = /** @type HTMLTextAreaElement */ (opMessage.querySelector('textarea.reply')); + let replyTextarea = /** @type {HTMLTextAreaElement} */ (opMessage.querySelector('textarea.reply')); if (replyTextarea) { replyTextarea.addEventListener('focus', e => showCommentForm(pageMID, 0)); - replyTextarea.addEventListener('keypress', e => postformListener(/** @type HTMLElement */ (e.target), e)); + replyTextarea.addEventListener('keypress', e => postformListener(/** @type {HTMLElement} */ (e.target), e)); if (!window.location.hash) { replyTextarea.focus(); } @@ -593,7 +599,7 @@ ready(() => { } } - var postmsg = /** @type HTMLFormElement */(document.getElementById('postmsg')); + var postmsg = /** @type {HTMLFormElement} */(document.getElementById('postmsg')); if (postmsg) { Array.from(document.querySelectorAll('a')).filter(t => t.href.indexOf('?') >= 0).forEach(t => { t.addEventListener('click', e => { @@ -629,7 +635,7 @@ ready(() => { e.preventDefault(); }); } - /** @type HTMLFormElement[] */ (Array.from(document.querySelectorAll('.pmmsg'))).forEach(pmmsg => { + /** @type {HTMLFormElement[]} */ (Array.from(document.querySelectorAll('.pmmsg'))).forEach(pmmsg => { pmmsg.addEventListener('submit', e => { let formData = new FormData(pmmsg); fetch('/api/pm' + '?hash=' + document.getElementById('body').getAttribute('data-hash'), { @@ -660,13 +666,13 @@ ready(() => { e.addEventListener('click', function(e) { setPrivacy( e.target, - /** @type HTMLElement */(e.target).closest('article').getAttribute('data-mid')); + /** @type {HTMLElement} */(e.target).closest('article').getAttribute('data-mid')); e.preventDefault(); }); }); Array.from(document.querySelectorAll('.ir a[data-fname], .msg-media a[data-fname]')).forEach(function(el) { el.addEventListener('click', function(e) { - let fname = /** @type HTMLElement */ (e.target).closest('[data-fname]').getAttribute('data-fname'); + let fname = /** @type {HTMLElement} */ (e.target).closest('[data-fname]').getAttribute('data-fname'); if (!showPhotoDialog(fname)) { e.preventDefault(); } @@ -684,7 +690,7 @@ ready(() => { e.addEventListener('click', function(e) { likeMessage( e.target, - /** @type HTMLElement */(e.target).closest('article').getAttribute('data-mid')); + /** @type {HTMLElement} */(e.target).closest('article').getAttribute('data-mid')); e.preventDefault(); }); }); @@ -705,10 +711,10 @@ ready(() => { var unfoldall = document.getElementById('unfoldall'); if (unfoldall) { unfoldall.addEventListener('click', function(e) { - /** @type HTMLElement[] */ (Array.from(document.querySelectorAll('#replies>li'))).forEach(function(e) { + /** @type {HTMLElement[]} */ (Array.from(document.querySelectorAll('#replies>li'))).forEach(function(e) { e.style.display = 'block'; }); - /** @type HTMLElement[] */ (Array.from(document.querySelectorAll('#replies .msg-comments'))).forEach(function(e) { + /** @type {HTMLElement[]} */ (Array.from(document.querySelectorAll('#replies .msg-comments'))).forEach(function(e) { e.style.display = 'none'; }); e.preventDefault(); @@ -758,7 +764,7 @@ ready(() => { } }); let location = window.location.href; - /** @type HTMLLinkElement[] */ (Array.from(document.querySelectorAll('#header_wrapper a'))).forEach(el => { + /** @type {HTMLLinkElement[]} */ (Array.from(document.querySelectorAll('#header_wrapper a'))).forEach(el => { if (el.href === location) { el.classList.add('active'); el.setAttribute('disabled', 'disabled'); |