From 1299d632bdc8b676d3fc2114a1788a2f8f63597c Mon Sep 17 00:00:00 2001 From: supahgreg Date: Sun, 12 Oct 2025 19:38:08 +0000 Subject: Get ESLint working and tweak the config (stricter). --- .eslintrc.js | 300 ------------------------------------------------------- eslint.config.js | 80 +++++++++++++++ package.json | 3 +- 3 files changed, 82 insertions(+), 301 deletions(-) delete mode 100644 .eslintrc.js create mode 100644 eslint.config.js diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 53184783a..000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,300 +0,0 @@ -module.exports = { - "env": { - "browser": true, - "es6": true, - "jquery": false, - "webextensions": false - }, - "extends": "eslint:recommended", - "parserOptions": { - "ecmaVersion": 2020 - }, - "rules": { - "accessor-pairs": "error", - "array-bracket-newline": "off", - "array-bracket-spacing": "off", - "array-callback-return": "error", - "array-element-newline": "off", - "arrow-body-style": "error", - "arrow-parens": "error", - "arrow-spacing": "error", - "block-scoped-var": "off", - "block-spacing": [ - "error", - "always" - ], - "brace-style": "off", - "callback-return": "off", - "camelcase": "off", - "capitalized-comments": "off", - "class-methods-use-this": "error", - "comma-dangle": "off", - "comma-spacing": "off", - "comma-style": [ - "error", - "last" - ], - "complexity": "off", - "computed-property-spacing": [ - "error", - "never" - ], - "consistent-return": "off", - "consistent-this": "off", - "curly": "off", - "default-case": "off", - "dot-location": "off", - "dot-notation": "off", - "eol-last": "error", - "eqeqeq": "off", - "func-call-spacing": "error", - "func-name-matching": "error", - "func-names": "off", - "func-style": "off", - "function-paren-newline": "off", - "generator-star-spacing": "error", - "global-require": "error", - "guard-for-in": "off", - "handle-callback-err": "off", - "id-blacklist": "error", - "id-length": "off", - "id-match": "error", - "implicit-arrow-linebreak": "off", - "indent": "off", - "indent-legacy": "off", - "init-declarations": "off", - "jsx-quotes": "error", - "key-spacing": "off", - "keyword-spacing": [ - "error", - { - "after": true, - "before": true - } - ], - "line-comment-position": "off", - "linebreak-style": [ - "error", - "unix" - ], - "lines-around-comment": "off", - "lines-around-directive": "error", - "lines-between-class-members": "error", - "max-classes-per-file": "off", - "max-depth": "off", - "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": [ "warn", { "max" : 2 } ], - "multiline-comment-style": "off", - "multiline-ternary": "off", - "new-cap": "warn", - "new-parens": "error", - "newline-after-var": "off", - "newline-before-return": "off", - "newline-per-chained-call": "off", - "no-alert": "off", - "no-array-constructor": "error", - "no-async-promise-executor": "off", - "no-await-in-loop": "warn", - "no-bitwise": "off", - "no-buffer-constructor": "error", - "no-caller": "error", - "no-catch-shadow": "off", - "no-confusing-arrow": "error", - "no-continue": "off", - "no-console": "off", - "no-div-regex": "error", - "no-duplicate-imports": "error", - "no-else-return": "off", - "no-empty": [ - "error", - { - "allowEmptyCatch": true - } - ], - "no-empty-function": "error", - "no-eq-null": "off", - "no-eval": "error", - "no-extend-native": "off", - "no-extra-bind": "error", - "no-extra-label": "error", - "no-extra-parens": "off", - "no-floating-decimal": "error", - "no-implicit-globals": "off", - "no-implied-eval": "off", - "no-inline-comments": "off", - "no-inner-declarations": [ - "error", - "functions" - ], - "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-magic-numbers": "off", - "no-misleading-character-class": "off", - "no-mixed-operators": "off", - "no-mixed-requires": "error", - "no-multi-assign": "error", - "no-multi-spaces": "off", - "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": "warn", - "no-new-func": "error", - "no-new-object": "off", - "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-prototype-builtins": "warn", - "no-restricted-globals": "error", - "no-restricted-imports": "error", - "no-restricted-modules": "error", - "no-restricted-properties": "error", - "no-restricted-syntax": "error", - "no-return-assign": [ - "error", - "except-parens" - ], - "no-return-await": "error", - "no-script-url": "error", - "no-self-compare": "error", - "no-sequences": "error", - "no-shadow": "off", - "no-shadow-restricted-names": "error", - "no-spaced-func": "error", - "no-sync": "error", - "no-tabs": "off", - "no-template-curly-in-string": "error", - "no-ternary": "off", - "no-throw-literal": "error", - "no-trailing-spaces": "error", - "no-undef-init": "error", - "no-undefined": "off", - "no-undef": "warn", - "no-underscore-dangle": "off", - "no-unmodified-loop-condition": "error", - "no-unneeded-ternary": [ - "error", - { - "defaultAssignment": true - } - ], - "no-unused-expressions": "off", - "no-unused-vars": "warn", - "no-use-before-define": "off", - "no-useless-call": "error", - "no-useless-computed-key": "error", - "no-useless-concat": "error", - "no-useless-constructor": "error", - "no-useless-rename": "error", - "no-useless-return": "off", - "no-var": "warn", - "no-void": "error", - "no-warning-comments": "off", - "no-whitespace-before-property": "error", - "no-with": "error", - "nonblock-statement-body-position": [ - "error", - "any" - ], - "object-curly-newline": "off", - "object-curly-spacing": "off", - "object-property-newline": "off", - "object-shorthand": "off", - "one-var": "off", - "one-var-declaration-per-line": "error", - "operator-assignment": "off", - "operator-linebreak": [ - "error", - "after" - ], - "padded-blocks": "off", - "padding-line-between-statements": "error", - "prefer-arrow-callback": "off", - "prefer-const": "error", - "prefer-destructuring": "off", - "prefer-numeric-literals": "error", - "prefer-object-spread": "off", - "prefer-promise-reject-errors": "error", - "prefer-reflect": "off", - "prefer-rest-params": "error", - "prefer-spread": "error", - "prefer-template": "off", - "quote-props": "off", - "quotes": "off", - "radix": [ - "error", - "as-needed" - ], - "require-atomic-updates": "off", - "require-await": "warn", - "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": "off", - "space-before-blocks": "off", - "space-before-function-paren": "off", - "space-in-parens": "off", - "space-infix-ops": "off", - "space-unary-ops": [ - "error", - { - "nonwords": false, - "words": false - } - ], - "spaced-comment": "off", - "strict": [ - "off", - "never" - ], - "switch-colon-spacing": "error", - "symbol-description": "error", - "template-curly-spacing": "error", - "template-tag-spacing": "error", - "unicode-bom": [ - "error", - "never" - ], - "valid-jsdoc": "error", - "vars-on-top": "off", - "wrap-iife": "error", - "wrap-regex": "error", - "yield-star-spacing": "error", - "yoda": [ - "error", - "never" - ] - } -}; diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..f1fc76d28 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,80 @@ +import js from '@eslint/js'; + +export default [ + js.configs.recommended, + + { + files: ['js/**/*.js', 'plugins/**/*.js'], + languageOptions: { + ecmaVersion: 2022, + sourceType: 'script', + globals: { + // Browser + window: 'readonly', + document: 'readonly', + console: 'readonly', + alert: 'readonly', + confirm: 'readonly', + setTimeout: 'readonly', + setInterval: 'readonly', + clearTimeout: 'readonly', + clearInterval: 'readonly', + fetch: 'readonly', + XMLHttpRequest: 'readonly', + localStorage: 'readonly', + location: 'readonly', + history: 'readonly', + + // tt-rss + App: 'writable', + Article: 'writable', + CommonDialogs: 'writable', + CommonFilters: 'writable', + Feeds: 'writable', + FeedStoreModel: 'writable', + FeedTree: 'writable', + Headlines: 'writable', + PluginHost: 'writable', + PrefFeedStore: 'writable', + PrefFeedTree: 'writable', + PrefFilterStore: 'writable', + PrefFilterTree: 'writable', + PrefHelpers: 'writable', + PrefLabelTree: 'writable', + PrefUsers: 'writable', + SingleUseDialog: 'writable', + Toolbar: 'writable', + + // Dojo + dojo: 'readonly', + dijit: 'readonly' + } + }, + + rules: { + 'no-undef': 'warn', + 'no-unused-vars': 'warn', + 'no-console': 'off', + + 'prefer-const': 'error', + 'no-var': 'warn', + + 'eqeqeq': ['error', 'always'], + 'no-caller': 'error', + 'no-proto': 'error', + + 'linebreak-style': ['error', 'unix'], + 'eol-last': 'error', + 'no-trailing-spaces': 'error', + 'no-multiple-empty-lines': ['error', { 'max': 2 }], + + 'keyword-spacing': ['error', { 'after': true, 'before': true }], + 'block-spacing': ['error', 'always'], + 'computed-property-spacing': ['error', 'never'], + + 'no-empty': ['error', { 'allowEmptyCatch': true }], + + 'max-statements-per-line': ['warn', { 'max': 2 }] + } + } +]; diff --git a/package.json b/package.json index 3eb8d6851..31dcfbd7e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "name": "tt-rss", + "description": "A free, flexible, open-source, web-based news feed (RSS/Atom/other) reader and aggregator.", "version": "1.0.0", - "description": "", + "type": "module", "devDependencies": { "eslint": "^9.37.0", "gulp": "^5.0.1", -- cgit v1.2.3-54-g00ecf From 39182a76e4070d3b92af10bf609fef8151bce6e4 Mon Sep 17 00:00:00 2001 From: supahgreg Date: Sun, 12 Oct 2025 21:02:39 +0000 Subject: Address ESLint rule 'eqeqeq'. https://eslint.org/docs/latest/rules/eqeqeq --- eslint.config.js | 26 +++-------- js/App.js | 59 ++++++++++++------------ js/Article.js | 12 ++--- js/CommonDialogs.js | 12 ++--- js/CommonFilters.js | 14 +++--- js/FeedStoreModel.js | 2 +- js/FeedTree.js | 26 +++++------ js/Feeds.js | 36 ++++++++------- js/Headlines.js | 98 ++++++++++++++++++++-------------------- js/PluginHost.js | 10 ++-- js/PrefFeedTree.js | 24 +++++----- js/PrefFilterTree.js | 6 +-- js/PrefHelpers.js | 32 ++++++------- js/PrefLabelTree.js | 2 +- js/PrefUsers.js | 4 +- js/common.js | 66 +++++++++++++-------------- js/form/Select.js | 4 +- js/form/ValidationMultiSelect.js | 2 +- js/prefs.js | 2 +- js/tt-rss.js | 2 +- js/utility.js | 6 +-- 21 files changed, 216 insertions(+), 229 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index f1fc76d28..a42b6d0e3 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -21,29 +21,15 @@ export default [ clearInterval: 'readonly', fetch: 'readonly', XMLHttpRequest: 'readonly', + FormData: 'readonly', + URLSearchParams: 'readonly', localStorage: 'readonly', + sessionStorage: 'readonly', location: 'readonly', history: 'readonly', - - // tt-rss - App: 'writable', - Article: 'writable', - CommonDialogs: 'writable', - CommonFilters: 'writable', - Feeds: 'writable', - FeedStoreModel: 'writable', - FeedTree: 'writable', - Headlines: 'writable', - PluginHost: 'writable', - PrefFeedStore: 'writable', - PrefFeedTree: 'writable', - PrefFilterStore: 'writable', - PrefFilterTree: 'writable', - PrefHelpers: 'writable', - PrefLabelTree: 'writable', - PrefUsers: 'writable', - SingleUseDialog: 'writable', - Toolbar: 'writable', + navigator: 'readonly', + Event: 'readonly', + CustomEvent: 'readonly', // Dojo dojo: 'readonly', diff --git a/js/App.js b/js/App.js index 33bd81d9a..c727cc947 100644 --- a/js/App.js +++ b/js/App.js @@ -2,7 +2,7 @@ /* eslint-disable new-cap */ /* global __, Article, Headlines, Filters, fox */ -/* global xhr, dojo, dijit, PluginHost, Notify, Feeds, Cookie */ +/* global xhr, PluginHost, Notify, Feeds, Cookie */ /* global CommonDialogs, Plugins */ const App = { @@ -75,7 +75,7 @@ const App = { return ` ` @@ -89,7 +89,7 @@ const App = { return ` ` @@ -333,7 +333,7 @@ const App = { for (const seq in hotkeys_map[1]) { if (hotkeys_map[1].hasOwnProperty(seq)) { - if (seq == sequence) { + if (seq === sequence) { return hotkeys_map[1][seq]; } } @@ -345,11 +345,11 @@ const App = { const keycode = event.which; const keychar = String.fromCharCode(keycode); - if (keycode == 27) { // escape and drop prefix + if (keycode === 27) { // escape and drop prefix this.hotkey_prefix = false; } - if (!this.hotkey_prefix && hotkeys_map[0].indexOf(keychar) != -1) { + if (!this.hotkey_prefix && hotkeys_map[0].indexOf(keychar) !== -1) { this.hotkey_prefix = keychar; App.byId("cmdline").innerHTML = keychar; @@ -370,7 +370,7 @@ const App = { let hotkey_name = ""; - if (event.type == "keydown") { + if (event.type === 'keydown') { hotkey_name = "(" + keycode + ")"; // ensure ^*char notation @@ -473,20 +473,20 @@ const App = { const counters = reply['counters']; const runtime_info = reply['runtime-info']; - if (error && error.code && error.code != App.Error.E_SUCCESS) { + if (error && error.code && error.code !== App.Error.E_SUCCESS) { console.warn("handleRpcJson: fatal error", error); this.Error.fatal(error.code, error.params); return false; } - if (seq && this.get_seq() != seq) { + if (seq && this.get_seq() !== seq) { console.warn("handleRpcJson: sequence mismatch: ", seq, '!=', this.get_seq()); return false; } // not in preferences - if (typeof Feeds != "undefined") { - if (message == "UPDATE_COUNTERS") { + if (typeof Feeds !== 'undefined') { + if (message === 'UPDATE_COUNTERS') { console.log("need to refresh counters for", reply.feeds); Feeds.requestCounters(reply.feeds); } @@ -515,12 +515,12 @@ const App = { console.log("RI:", k, "=>", v); - if (k == "daemon_is_running" && v != 1) { + if (k === "daemon_is_running" && v !== 1) { Notify.error("Update daemon is not running.", true); return; } - if (k == "recent_log_events") { + if (k === "recent_log_events") { const alert = App.find(".log-alert"); if (alert) { @@ -528,14 +528,14 @@ const App = { } } - if (k == "daemon_stamp_ok" && v != 1) { + if (k === "daemon_stamp_ok" && v !== 1) { Notify.error("Update daemon is not updating feeds.", true); return; } - if (typeof Feeds != "undefined") { - if (k == "max_feed_id" || k == "num_feeds") { - if (this.getInitParam(k) && this.getInitParam(k) != v) { + if (typeof Feeds !== 'undefined') { + if (k === "max_feed_id" || k === "num_feeds") { + if (this.getInitParam(k) && this.getInitParam(k) !== v) { console.log("feed count changed, need to reload feedlist:", this.getInitParam(k), v); Feeds.reload(); } @@ -609,13 +609,13 @@ const App = { E_SCHEMA_MISMATCH: "E_SCHEMA_MISMATCH", E_URL_SCHEME_MISMATCH: "E_URL_SCHEME_MISMATCH", fatal: function (error, params = {}) { - if (error == App.Error.E_UNAUTHORIZED) { + if (error === App.Error.E_UNAUTHORIZED) { window.location.href = "index.php"; return; - } else if (error == App.Error.E_SCHEMA_MISMATCH) { + } else if (error === App.Error.E_SCHEMA_MISMATCH) { window.location.href = "public.php?op=dbupdate"; return; - } else if (error == App.Error.E_URL_SCHEME_MISMATCH) { + } else if (error === App.Error.E_URL_SCHEME_MISMATCH) { params.description = __("URL scheme reported by your browser (%a) doesn't match server-configured SELF_URL_PATH (%b), check X-Forwarded-Proto.") .replace("%a", params.client_scheme) .replace("%b", params.server_scheme); @@ -744,12 +744,12 @@ const App = { } }); - if (typeof Promise.allSettled == "undefined") { + if (typeof Promise.allSettled === "undefined") { errorMsg = `Browser check failed: Promise.allSettled is not defined.`; throw new Error(errorMsg); } - return errorMsg == ""; + return errorMsg === ""; }, updateRuntimeInfo: function() { xhr.json("backend.php", {op: "RPC", method: "getruntimeinfo"}, () => { @@ -902,18 +902,17 @@ const App = { document.title = tmp; }, hotkeyHandler: function(event) { - if (event.target.nodeName == "INPUT" || event.target.nodeName == "TEXTAREA") return; + if (event.target.nodeName === "INPUT" || event.target.nodeName === "TEXTAREA") return; // Arrow buttons and escape are not reported via keypress, handle them via keydown. // escape = 27, left = 37, up = 38, right = 39, down = 40, pgup = 33, pgdn = 34, insert = 45, delete = 46 - if (event.type == "keydown" && event.which != 27 && (event.which < 33 || event.which > 46)) return; - + if (event.type === "keydown" && event.which !== 27 && (event.which < 33 || event.which > 46)) return; const action_name = this.keyeventToAction(event); if (action_name) { const action_func = this.hotkey_actions[action_name]; - if (action_func != null) { + if (action_func !== null) { action_func(event); event.stopPropagation(); return false; @@ -1120,7 +1119,7 @@ const App = { } }; this.hotkey_actions["email_article"] = () => { - if (typeof Plugins.Mail != "undefined") { + if (typeof Plugins.Mail !== "undefined") { Plugins.Mail.onHotkey(Headlines.getSelected()); } else { alert(__("Please enable mail or mailto plugin first.")); @@ -1145,7 +1144,7 @@ const App = { Headlines.select('none'); }; this.hotkey_actions["feed_refresh"] = () => { - if (typeof Feeds.getActive() != "undefined") { + if (typeof Feeds.getActive() !== "undefined") { Feeds.open({feed: Feeds.getActive(), is_cat: Feeds.activeIsCat()}); } }; @@ -1191,7 +1190,7 @@ const App = { CommonDialogs.editFeed(Feeds.getActive()); }; this.hotkey_actions["feed_catchup"] = () => { - if (typeof Feeds.getActive() != "undefined") { + if (typeof Feeds.getActive() !== "undefined") { Feeds.catchupCurrent(); } }; @@ -1254,7 +1253,7 @@ const App = { Feeds.toggle(); }; this.hotkey_actions["toggle_full_text"] = () => { - if (typeof Plugins.Af_Readability != "undefined") { + if (typeof Plugins.Af_Readability !== "undefined") { if (Article.getActive()) Plugins.Af_Readability.embed(Article.getActive()); } else { diff --git a/js/Article.js b/js/Article.js index a57454dec..b38cb50c6 100644 --- a/js/Article.js +++ b/js/Article.js @@ -166,7 +166,7 @@ const Article = { `
${enclosures.entries.map((enc) => { if (!enclosures.inline_text_only) { - if (enc.content_type && enc.content_type.indexOf("image/") != -1) { + if (enc.content_type && enc.content_type.indexOf("image/") !== -1) { return `

` - } else if (enc.content_type && enc.content_type.indexOf("audio/") != -1 && App.audioCanPlay(enc.content_type)) { + } else if (enc.content_type && enc.content_type.indexOf("audio/") !== -1 && App.audioCanPlay(enc.content_type)) { return `

  • ${__('No recent articles matching this filter have been found.')}
  • `; @@ -132,9 +132,9 @@ const Filters = { insertAction: function(parentNode, replaceNode) { const form = document.forms["filter_new_action_form"]; - if (form.action_id.value == 7) { + if (form.action_id.value === '7') { form.action_param.value = form.action_param_label.value; - } else if (form.action_id.value == 9) { + } else if (form.action_id.value === '9') { form.action_param.value = form.action_param_plugin.value; } @@ -254,11 +254,11 @@ const Filters = { dijit.byId("filterDlg_actionParamPlugin").domNode.hide(); // if selected action supports parameters, enable params field - if (action == dialog.ACTION_LABEL) { + if (action === dialog.ACTION_LABEL) { dijit.byId("filterDlg_actionParamLabel").domNode.show(); - } else if (action == dialog.ACTION_PLUGIN) { + } else if (action === dialog.ACTION_PLUGIN) { dijit.byId("filterDlg_actionParamPlugin").domNode.show(); - } else if (dialog.PARAM_ACTIONS.indexOf(action) != -1) { + } else if (dialog.PARAM_ACTIONS.indexOf(action) !== -1) { dijit.byId("filterDlg_actionParam").domNode.show(); } }, @@ -531,7 +531,7 @@ const Filters = { // `selectedText` is always empty at this point (tested by selecting some article text). const selectedText = App.getSelectedText(); - if (selectedText != "") { + if (selectedText !== '') { const feed_id = Feeds.activeIsCat() ? 'CAT:' + parseInt(Feeds.getActive()) : Feeds.getActive(); const rule = {reg_exp: selectedText, feed_id: [feed_id], filter_type: 1}; diff --git a/js/FeedStoreModel.js b/js/FeedStoreModel.js index befc441af..fa8f730d6 100644 --- a/js/FeedStoreModel.js +++ b/js/FeedStoreModel.js @@ -56,7 +56,7 @@ define(["dojo/_base/declare", "dijit/tree/ForestStoreModel"], function (declare) }, hasCats: function () { if (this.store && this.store._itemsByIdentity) - return this.store._itemsByIdentity['CAT:-1'] != undefined; + return this.store._itemsByIdentity['CAT:-1'] !== undefined; else return false; }, diff --git a/js/FeedTree.js b/js/FeedTree.js index 683205579..cd7e1f311 100755 --- a/js/FeedTree.js +++ b/js/FeedTree.js @@ -26,7 +26,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co // var oreo = cookie(this.cookieName); let oreo = localStorage.getItem(this.cookieName); // migrate old data if nothing in localStorage - if (oreo == null || oreo === '') { + if (oreo === null || oreo === '') { oreo = cookie(this.cookieName); cookie(this.cookieName, null, { expires: -1 }); } @@ -50,7 +50,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co let iconNode; if (iconName) { - if (iconName.indexOf("/") == -1) { + if (iconName.indexOf("/") === -1) { iconNode = dojo.create("i", { className: "material-icons icon icon-" + iconName, innerHTML: iconName }); } else { iconNode = dojo.create('img', { className: 'icon' }); @@ -156,7 +156,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co domConstruct.place(tnode.loadingNode, tnode.expandoNode, 'only'); } - if (id.match("CAT:") && bare_id == -1) { + if (id.match("CAT:") && bare_id === -1) { const menu = new dijit.Menu(); menu.row_id = bare_id; @@ -205,7 +205,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co } }, getTooltip: function (item) { - return [item.updated, item.error].filter((x) => x && x != "").join(" - "); + return [item.updated, item.error].filter((x) => x && x !== "").join(" - "); }, getIconClass: function (item, opened) { // eslint-disable-next-line no-nested-ternary @@ -217,21 +217,21 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co getRowClass: function (item/*, opened */) { let rc = "dijitTreeRow dijitTreeRowFlex"; - const is_cat = String(item.id).indexOf('CAT:') != -1; + const is_cat = String(item.id).indexOf('CAT:') !== -1; if (is_cat) rc += " Is_Cat"; else rc += " Is_Feed"; - if (!is_cat && item.error != '') rc += " Error"; + if (!is_cat && item.error !== '') rc += " Error"; if (item.unread > 0) rc += " Unread"; if (item.auxcounter > 0) rc += " Has_Aux"; if (item.markedcounter > 0) rc += " Has_Marked"; if (item.publishedcounter > 0) rc += " Has_Published"; if (item.updates_disabled > 0) rc += " UpdatesDisabled"; - if (item.bare_id >= App.LABEL_BASE_INDEX && item.bare_id < 0 && !is_cat || item.bare_id == Feeds.FEED_ARCHIVED && !is_cat) rc += " Special"; - if (item.bare_id == Feeds.CATEGORY_SPECIAL && is_cat) rc += " AlwaysVisible"; + if (item.bare_id >= App.LABEL_BASE_INDEX && item.bare_id < 0 && !is_cat || item.bare_id === Feeds.FEED_ARCHIVED && !is_cat) rc += " Special"; + if (item.bare_id === Feeds.CATEGORY_SPECIAL && is_cat) rc += " AlwaysVisible"; if (item.bare_id < App.LABEL_BASE_INDEX) rc += " Label"; return rc; @@ -266,7 +266,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co const items = this.model.store._arrayOfTopLevelItems; for (let i = 0; i < items.length; i++) { - if (String(items[i].id) == test_id) { + if (String(items[i].id) === test_id) { this.expandParentNodes(feed, is_cat, parents); } else { this.findNodeParentsAndExpandThem(feed, is_cat, items[i], []); @@ -276,13 +276,13 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co parents.push(root); for (let i = 0; i < root.items.length; i++) { - if (String(root.items[i].id) == test_id) { + if (String(root.items[i].id) === test_id) { this.expandParentNodes(feed, is_cat, parents); } else { this.findNodeParentsAndExpandThem(feed, is_cat, root.items[i], parents.slice(0)); } } - } else if (String(root.id) == test_id) { + } else if (String(root.id) === test_id) { this.expandParentNodes(feed, is_cat, parents.slice(0)); } } catch (e) { @@ -423,7 +423,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co const items = this.model.store._arrayOfAllItems; const start = items.indexOf(treeItem); - if (start != -1) { + if (start !== -1) { let item = this._nextTreeItemFromIndex(start, unread_only); // let's try again from the top @@ -469,7 +469,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co const items = this.model.store._arrayOfAllItems; const start = items.indexOf(treeItem); - if (start != -1) { + if (start !== -1) { let item = this._prevTreeItemFromIndex(start, unread_only); // wrap from the bottom diff --git a/js/Feeds.js b/js/Feeds.js index 71d9c29d4..825f6f8ef 100644 --- a/js/Feeds.js +++ b/js/Feeds.js @@ -31,7 +31,7 @@ const Feeds = { entries.forEach((entry) => { //console.log('feeds',entry.target, entry.intersectionRatio); - if (entry.intersectionRatio == 0) + if (entry.intersectionRatio === 0) Feeds.onHide(entry); else Feeds.onShow(entry); @@ -50,7 +50,7 @@ const Feeds = { // If number of properties is different, // objects are not equivalent - if (aProps.length != bProps.length) { + if (aProps.length !== bProps.length) { return false; } @@ -87,13 +87,13 @@ const Feeds = { const ts = elems[l].ts; const updated = elems[l].updated; - if (id == "global-unread") { + if (id === "global-unread") { App.global_unread = ctr; App.updateTitle(); continue; } - if (id == "subscribed-feeds") { + if (id === "subscribed-feeds") { /* feeds_found = ctr; */ continue; } @@ -102,12 +102,14 @@ const Feeds = { (kind == "cat")) { }*/ - this.setUnread(id, (kind == "cat"), ctr); - this.setValue(id, (kind == "cat"), 'auxcounter', parseInt(elems[l].auxcounter)); - this.setValue(id, (kind == "cat"), 'markedcounter', parseInt(elems[l].markedcounter)); - this.setValue(id, (kind == "cat"), 'publishedcounter', parseInt(elems[l].publishedcounter)); + const is_cat = (kind === 'cat'); - if (kind != "cat") { + this.setUnread(id, is_cat, ctr); + this.setValue(id, is_cat, 'auxcounter', parseInt(elems[l].auxcounter)); + this.setValue(id, is_cat, 'markedcounter', parseInt(elems[l].markedcounter)); + this.setValue(id, is_cat, 'publishedcounter', parseInt(elems[l].publishedcounter)); + + if (!is_cat) { this.setValue(id, false, 'error', error); this.setValue(id, false, 'updated', updated); @@ -130,7 +132,7 @@ const Feeds = { PluginHost.run(PluginHost.HOOK_COUNTERS_PROCESSED, elems); }, reloadCurrent: function(method) { - if (this.getActive() != undefined) { + if (this.getActive() !== undefined) { console.log("reloadCurrent", this.getActive(), this.activeIsCat(), method); this.open({feed: this.getActive(), is_cat: this.activeIsCat(), method: method}); @@ -274,7 +276,7 @@ const Feeds = { if (hash.query) this._search_query = {query: hash.query, search_language: hash.search_language}; - if (hash.f != undefined) { + if (hash.f !== undefined) { this.open({feed: parseInt(hash.f), is_cat: parseInt(hash.c)}); } else { this.openDefaultFeed(); @@ -389,7 +391,7 @@ const Feeds = { // this is used to quickly switch between feeds, sets active but xhr is on a timeout const delayed = params.delayed || false; - if (offset != 0) { + if (offset !== 0) { if (this.infscroll_in_progress) return; @@ -419,9 +421,9 @@ const Feeds = { query = Object.assign(query, this._search_query); } - if (offset != 0) { + if (offset !== 0) { query.skip = offset; - } else if (!is_cat && feed == this.getActive() && !params.method) { + } else if (!is_cat && feed === this.getActive() && !params.method) { query.m = "ForceUpdate"; } @@ -450,7 +452,7 @@ const Feeds = { catchupAll: function() { const str = __("Mark all articles as read?"); - if (App.getInitParam("confirm_feed_catchup") != 1 || confirm(str)) { + if (App.getInitParam("confirm_feed_catchup") !== 1 || confirm(str)) { Notify.progress("Marking all feeds as read..."); @@ -509,7 +511,7 @@ const Feeds = { if (next_feed !== false) { this.open({feed: next_feed, is_cat: next_is_cat}); } - } else if (feed == this.getActive() && is_cat == this.activeIsCat()) { + } else if (feed === this.getActive() && is_cat === this.activeIsCat()) { this.reloadCurrent(); } @@ -524,7 +526,7 @@ const Feeds = { const str = __("Mark all articles in %s as read?").replace("%s", title); - if (App.getInitParam("confirm_feed_catchup") != 1 || confirm(str)) { + if (App.getInitParam("confirm_feed_catchup") !== 1 || confirm(str)) { const rows = App.findAll("#headlines-frame > div[id*=RROW][class*=Unread][data-orig-feed-id='" + id + "']"); diff --git a/js/Headlines.js b/js/Headlines.js index 74b8ca00d..1547781c4 100755 --- a/js/Headlines.js +++ b/js/Headlines.js @@ -56,7 +56,7 @@ const Headlines = { const modified = []; mutations.forEach((m) => { - if (m.type == 'attributes' && ['class', 'data-score'].indexOf(m.attributeName) != -1) { + if (m.type === 'attributes' && ['class', 'data-score'].indexOf(m.attributeName) !== -1) { const row = m.target; const id = row.getAttribute("data-article-id"); @@ -105,22 +105,22 @@ const Headlines = { }; modified.forEach(function (m) { - if (m.old.marked != m.new.marked) + if (m.old.marked !== m.new.marked) ops.tmark.push(m.id); - if (m.old.published != m.new.published) + if (m.old.published !== m.new.published) ops.tpub.push(m.id); - if (m.old.unread != m.new.unread) + if (m.old.unread !== m.new.unread) m.new.unread ? ops.unread.push(m.id) : ops.read.push(m.id); - if (m.old.selected != m.new.selected) + if (m.old.selected !== m.new.selected) m.new.selected ? ops.select.push(m.row) : ops.deselect.push(m.row); - if (m.old.active != m.new.active) + if (m.old.active !== m.new.active) m.new.active ? ops.activate.push(m.row) : ops.deactivate.push(m.row); - if (m.old.score != m.new.score) { + if (m.old.score !== m.new.score) { const score = m.new.score; ops.rescore[score] = ops.rescore[score] || []; @@ -158,25 +158,25 @@ const Headlines = { const promises = []; - if (ops.tmark.length != 0) + if (ops.tmark.length !== 0) promises.push(xhr.post("backend.php", {op: "RPC", method: "markSelected", "ids[]": ops.tmark, cmode: 2})); - if (ops.tpub.length != 0) + if (ops.tpub.length !== 0) promises.push(xhr.post("backend.php", {op: "RPC", method: "publishSelected", "ids[]": ops.tpub, cmode: 2})); - if (ops.read.length != 0) + if (ops.read.length !== 0) promises.push(xhr.post("backend.php", {op: "RPC", method: "catchupSelected", "ids[]": ops.read, cmode: 0})); - if (ops.unread.length != 0) + if (ops.unread.length !== 0) promises.push(xhr.post("backend.php", {op: "RPC", method: "catchupSelected", "ids[]": ops.unread, cmode: 1})); const scores = Object.keys(ops.rescore); - if (scores.length != 0) { + if (scores.length !== 0) { scores.forEach((score) => { promises.push(xhr.post("backend.php", {op: "Article", method: "setScore", "ids[]": ops.rescore[score], score: score})); @@ -236,7 +236,7 @@ const Headlines = { Article.openInNewWindow(id); Headlines.toggleUnread(id, 0); - } else if (Article.getActive() != id) { + } else if (Article.getActive() !== id) { Headlines.select('none'); @@ -309,7 +309,7 @@ const Headlines = { offset = unread_in_buffer; break; case "adaptive": - if (!(Feeds.getActive() == Feeds.FEED_STARRED && !Feeds.activeIsCat())) + if (!(Feeds.getActive() === Feeds.FEED_STARRED && !Feeds.activeIsCat())) offset = num_unread > 0 ? unread_in_buffer : num_all; break; } @@ -352,7 +352,7 @@ const Headlines = { const last_row = hsp.previousSibling; // invoke lazy load if last article in buffer is nearly visible OR is active - if (Article.getActive() == last_row.getAttribute("data-article-id") || last_row.offsetTop - 250 <= container.scrollTop + container.offsetHeight) { + if (Article.getActive() === last_row.getAttribute("data-article-id") || last_row.offsetTop - 250 <= container.scrollTop + container.offsetHeight) { hsp.innerHTML = ` ${__("Loading, please wait...")}`; Headlines.loadMore(); @@ -466,7 +466,7 @@ const Headlines = { if (hl.unread) row_class += " Unread"; if (headlines.vfeed_group_enabled) row_class += " vgrlf"; - if (headlines.vfeed_group_enabled && hl.feed_title && this.vgroup_last_feed != hl.feed_id) { + if (headlines.vfeed_group_enabled && hl.feed_title && this.vgroup_last_feed !== hl.feed_id) { const vgrhdr = `
    ${Feeds.renderIcon(hl.feed_id, hl.has_icon)}
    @@ -635,7 +635,7 @@ const Headlines = { target.destroyDescendants(); - if (tb && typeof tb == 'object') { + if (tb && typeof tb === 'object') { target.attr('innerHTML', ` @@ -672,7 +672,7 @@ const Headlines = { - ${tb.plugin_menu_items != '' ? + ${tb.plugin_menu_items !== '' ? ` ${tb.plugin_menu_items} @@ -747,7 +747,7 @@ const Headlines = { feed_id = reply['headlines']['id']; Feeds.last_search_query = reply['headlines']['search_query']; - if (feed_id != Feeds.FEED_ERROR && (feed_id != Feeds.getActive() || is_cat != Feeds.activeIsCat())) + if (feed_id !== Feeds.FEED_ERROR && (feed_id !== Feeds.getActive() || is_cat !== Feeds.activeIsCat())) return; const headlines_count = reply['headlines-info']['count']; @@ -758,7 +758,7 @@ const Headlines = { console.log('received', headlines_count, 'headlines'); if (!append) { - Feeds.infscroll_disabled = parseInt(headlines_count) != 30; + Feeds.infscroll_disabled = parseInt(headlines_count) !== 30; console.log('infscroll_disabled=', Feeds.infscroll_disabled); // also called in renderAgain() after view mode switch @@ -790,7 +790,7 @@ const Headlines = { Headlines.renderToolbar(reply['headlines']); - if (typeof reply['headlines']['content'] == 'string') { + if (typeof reply['headlines']['content'] === 'string') { App.byId("headlines-frame").innerHTML = reply['headlines']['content']; } else { App.byId("headlines-frame").innerHTML = ''; @@ -831,7 +831,7 @@ const Headlines = { Headlines.updateCurrentUnread(); - } else if (headlines_count > 0 && feed_id == Feeds.getActive() && is_cat == Feeds.activeIsCat()) { + } else if (headlines_count > 0 && feed_id === Feeds.getActive() && is_cat === Feeds.activeIsCat()) { const c = dijit.byId("headlines-frame"); let hsp = App.byId("headlines-spacer"); @@ -841,7 +841,7 @@ const Headlines = { let headlines_appended = 0; - if (typeof reply['headlines']['content'] == 'string') { + if (typeof reply['headlines']['content'] === 'string') { App.byId("headlines-frame").innerHTML = reply['headlines']['content']; } else { for (let i = 0; i < reply['headlines']['content'].length; i++) { @@ -856,7 +856,7 @@ const Headlines = { } } - Feeds.infscroll_disabled = headlines_appended == 0; + Feeds.infscroll_disabled = headlines_appended === 0; console.log('appended', headlines_appended, 'headlines, infscroll_disabled=', Feeds.infscroll_disabled); @@ -931,7 +931,7 @@ const Headlines = { const toolbar = dijit.byId("toolbar-main"); let order_by = toolbar.getValues().order_by; - if (order_by != "date_reverse") + if (order_by !== "date_reverse") order_by = "date_reverse"; else order_by = App.getInitParam("default_view_order_by"); @@ -939,11 +939,11 @@ const Headlines = { toolbar.setValues({order_by: order_by}); }, selectionToggleUnread: function (params = {}) { - const cmode = params.cmode != undefined ? params.cmode : 2; + const cmode = params.cmode !== undefined ? params.cmode : 2; const no_error = params.no_error || false; const ids = params.ids || Headlines.getSelected(); - if (ids.length == 0) { + if (ids.length === 0) { if (!no_error) alert(__("No articles selected.")); @@ -970,7 +970,7 @@ const Headlines = { selectionToggleMarked: function (ids) { ids = ids || Headlines.getSelected(); - if (ids.length == 0) { + if (ids.length === 0) { alert(__("No articles selected.")); return; } @@ -982,7 +982,7 @@ const Headlines = { selectionTogglePublished: function (ids) { ids = ids || Headlines.getSelected(); - if (ids.length == 0) { + if (ids.length === 0) { alert(__("No articles selected.")); return; } @@ -1022,13 +1022,13 @@ const Headlines = { const rows = Headlines.getLoaded(); for (let i = 0; i < rows.length; i++) { - if (rows[i] == current_id) { + if (rows[i] === current_id) { // Account for adjacent identical article ids. if (i > 0) prev_id = rows[i - 1]; for (let j = i + 1; j < rows.length; j++) { - if (rows[j] != current_id) { + if (rows[j] !== current_id) { next_id = rows[j]; break; } @@ -1059,7 +1059,7 @@ const Headlines = { const next = row.nextSibling; // hsp has half-screen height in auto catchup mode therefore we use its first child (normally A element) - if (next && Element.visible(next) && next.id == "headlines-spacer" && next.firstChild) { + if (next && Element.visible(next) && next.id === "headlines-spacer" && next.firstChild) { const offset = App.byId("headlines-spacer").offsetTop - App.byId("headlines-frame").offsetHeight + next.firstChild.offsetHeight; // don't jump back either @@ -1107,7 +1107,7 @@ const Headlines = { const row = App.byId(`RROW-${id}`); if (row) { - if (typeof cmode == "undefined") cmode = 2; + if (typeof cmode === "undefined") cmode = 2; switch (cmode) { case 0: @@ -1125,7 +1125,7 @@ const Headlines = { selectionRemoveLabel: function (id, ids) { if (!ids) ids = Headlines.getSelected(); - if (ids.length == 0) { + if (ids.length === 0) { alert(__("No articles selected.")); return; } @@ -1142,7 +1142,7 @@ const Headlines = { selectionAssignLabel: function (id, ids) { if (!ids) ids = Headlines.getSelected(); - if (ids.length == 0) { + if (ids.length === 0) { alert(__("No articles selected.")); return; } @@ -1159,7 +1159,7 @@ const Headlines = { deleteSelection: function () { const rows = Headlines.getSelected(); - if (rows.length == 0) { + if (rows.length === 0) { alert(__("No articles selected.")); return; } @@ -1167,7 +1167,7 @@ const Headlines = { const fn = Feeds.getName(Feeds.getActive(), Feeds.activeIsCat()); let str; - if (Feeds.getActive() != 0) { + if (Feeds.getActive() !== 0) { str = ngettext("Delete %d selected article in %s?", "Delete %d selected articles in %s?", rows.length); } else { str = ngettext("Delete %d selected article?", "Delete %d selected articles?", rows.length); @@ -1229,7 +1229,7 @@ const Headlines = { } }, getRange: function (start, stop) { - if (start == stop) + if (start === stop) return [start]; const rows = App.findAll("#headlines-frame > div[id*=RROW]"); @@ -1240,7 +1240,7 @@ const Headlines = { const row = rows[i]; const id = row.getAttribute('data-article-id'); - if (id == start || id == stop) { + if (id === start || id === stop) { if (!collecting) { collecting = true; } else { @@ -1296,7 +1296,7 @@ const Headlines = { catchupSelection: function () { const rows = Headlines.getSelected(); - if (rows.length == 0) { + if (rows.length === 0) { alert(__("No articles selected.")); return; } @@ -1329,7 +1329,7 @@ const Headlines = { if (!below) { for (let i = 0; i < visible_ids.length; i++) { - if (visible_ids[i] != id) { + if (visible_ids[i] !== id) { const e = App.byId(`RROW-${visible_ids[i]}`); if (e && e.hasClassName("Unread")) { @@ -1341,7 +1341,7 @@ const Headlines = { } } else { for (let i = visible_ids.length - 1; i >= 0; i--) { - if (visible_ids[i] != id) { + if (visible_ids[i] !== id) { const e = App.byId(`RROW-${visible_ids[i]}`); if (e && e.hasClassName("Unread")) { @@ -1353,12 +1353,12 @@ const Headlines = { } } - if (ids_to_mark.length == 0) { + if (ids_to_mark.length === 0) { alert(__("No articles found to mark")); } else { const msg = ngettext("Mark %d article as read?", "Mark %d articles as read?", ids_to_mark.length).replace("%d", ids_to_mark.length); - if (App.getInitParam("confirm_feed_catchup") != 1 || confirm(msg)) { + if (App.getInitParam("confirm_feed_catchup") !== 1 || confirm(msg)) { for (let i = 0; i < ids_to_mark.length; i++) { const e = App.byId(`RROW-${ids_to_mark[i]}`); @@ -1437,7 +1437,7 @@ const Headlines = { let ids = Headlines.getSelected(); // cast to string const id = (this.getParent().currentTarget.getAttribute("data-article-id")) + ""; - ids = ids.length != 0 && ids.indexOf(id) != -1 ? ids : [id]; + ids = ids.length !== 0 && ids.indexOf(id) !== -1 ? ids : [id]; Headlines.selectionToggleUnread({ids: ids, no_error: 1}); } @@ -1449,7 +1449,7 @@ const Headlines = { let ids = Headlines.getSelected(); // cast to string const id = (this.getParent().currentTarget.getAttribute("data-article-id")) + ""; - ids = ids.length != 0 && ids.indexOf(id) != -1 ? ids : [id]; + ids = ids.length !== 0 && ids.indexOf(id) !== -1 ? ids : [id]; Headlines.selectionToggleMarked(ids); } @@ -1461,7 +1461,7 @@ const Headlines = { let ids = Headlines.getSelected(); // cast to string const id = (this.getParent().currentTarget.getAttribute("data-article-id")) + ""; - ids = ids.length != 0 && ids.indexOf(id) != -1 ? ids : [id]; + ids = ids.length !== 0 && ids.indexOf(id) !== -1 ? ids : [id]; Headlines.selectionTogglePublished(ids); } @@ -1506,7 +1506,7 @@ const Headlines = { // cast to string const id = (this.getParent().ownerMenu.currentTarget.getAttribute("data-article-id")) + ""; - ids = ids.length != 0 && ids.indexOf(id) != -1 ? ids : [id]; + ids = ids.length !== 0 && ids.indexOf(id) !== -1 ? ids : [id]; Headlines.selectionAssignLabel(this.labelId, ids); } @@ -1520,7 +1520,7 @@ const Headlines = { // cast to string const id = (this.getParent().ownerMenu.currentTarget.getAttribute("data-article-id")) + ""; - ids = ids.length != 0 && ids.indexOf(id) != -1 ? ids : [id]; + ids = ids.length !== 0 && ids.indexOf(id) !== -1 ? ids : [id]; Headlines.selectionRemoveLabel(this.labelId, ids); } diff --git a/js/PluginHost.js b/js/PluginHost.js index 513429e4a..e770c7050 100644 --- a/js/PluginHost.js +++ b/js/PluginHost.js @@ -24,7 +24,7 @@ const PluginHost = { HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2: 19, hooks: [], register: function (name, callback) { - if (typeof(this.hooks[name]) == 'undefined') + if (typeof(this.hooks[name]) === 'undefined') this.hooks[name] = []; this.hooks[name].push(callback); @@ -32,7 +32,7 @@ const PluginHost = { run: function (name, args) { //console.warn('PluginHost.run', name); - if (typeof(this.hooks[name]) != 'undefined') + if (typeof(this.hooks[name]) !== 'undefined') for (let i = 0; i < this.hooks[name].length; i++) { this.hooks[name][i](args); } @@ -40,9 +40,9 @@ const PluginHost = { run_until: function (name, check, ...args) { //console.warn('PluginHost.run_until', name, check, args); - if (typeof(this.hooks[name]) != 'undefined') + if (typeof(this.hooks[name]) !== 'undefined') for (let i = 0; i < this.hooks[name].length; i++) { - if (this.hooks[name][i](args) == check) + if (this.hooks[name][i](args) === check) return true; } @@ -50,7 +50,7 @@ const PluginHost = { }, unregister: function (name, callback) { for (let i = 0; i < this.hooks[name].length; i++) - if (this.hooks[name][i] == callback) + if (this.hooks[name][i] === callback) this.hooks[name].splice(i, 1); } }; diff --git a/js/PrefFeedTree.js b/js/PrefFeedTree.js index f1729382c..7a7c5b551 100644 --- a/js/PrefFeedTree.js +++ b/js/PrefFeedTree.js @@ -49,7 +49,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b // var oreo = cookie(this.cookieName); let oreo = localStorage.getItem(this.cookieName); // migrate old data if nothing in localStorage - if (oreo == null || oreo === '') { + if (oreo === null || oreo === '') { oreo = cookie(this.cookieName); cookie(this.cookieName, null, { expires: -1 }); } @@ -135,7 +135,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b }, // eslint-disable-next-line no-unused-vars getRowClass: function (item, opened) { - let rc = (!item.error || item.error == '') ? "dijitTreeRow" : + let rc = (!item.error || item.error === '') ? "dijitTreeRow" : "dijitTreeRow Error"; if (item.updates_disabled > 0) rc += " UpdatesDisabled"; @@ -144,7 +144,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b }, getIconClass: function (item, opened) { // eslint-disable-next-line no-nested-ternary - return (!item || this.model.store.getValue(item, 'type') == 'category') ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feed-icon"; + return (!item || this.model.store.getValue(item, 'type') === 'category') ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feed-icon"; }, reload: function() { const searchElem = App.byId("feed_search"); @@ -175,11 +175,11 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b //console.log(id + " " + position + " " + source_id); if (source_id.match("FEED:")) { - return ((id.match("CAT:") && position == "over") || - (id.match("FEED:") && position != "over")); + return ((id.match("CAT:") && position === "over") || + (id.match("FEED:") && position !== "over")); } else if (source_id.match("CAT:")) { return ((id.match("CAT:") && !id.match("CAT:0")) || - (id.match("root") && position == "over")); + (id.match("root") && position === "over")); } }, resetFeedOrder: function() { @@ -293,7 +293,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b editSelectedFeed: function() { const rows = this.getSelectedFeeds(); - if (rows.length == 0) { + if (rows.length === 0) { alert(__("No feeds selected.")); return; } @@ -309,7 +309,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b editMultiple: function() { const rows = this.getSelectedFeeds(); - if (rows.length == 0) { + if (rows.length === 0) { alert(__("No feeds selected.")); return; } @@ -340,7 +340,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b target.attr('disabled', !checkbox.attr('checked')); console.log(target, target.attr('type')); - if (target.attr('type') == "checkbox") { + if (target.attr('type') === "checkbox") { const label = checkbox.domNode.closest("label"); if (checkbox.attr('checked')) @@ -358,7 +358,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b Object.keys(query).forEach((key) => { const val = query[key]; - if (typeof val == "object" && val.length == 0) + if (typeof val === "object" && val.length === 0) query[key] = ["off"]; }); @@ -384,12 +384,12 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b }, editCategory: function(id, item) { // uncategorized - if (String(item.id) == "CAT:0") + if (String(item.id) === "CAT:0") return; const new_name = prompt(__('Rename category to:'), item.name); - if (new_name && new_name != item.name) { + if (new_name && new_name !== item.name) { Notify.progress("Loading, please wait..."); diff --git a/js/PrefFilterTree.js b/js/PrefFilterTree.js index e63dd5797..9a9728c08 100644 --- a/js/PrefFilterTree.js +++ b/js/PrefFilterTree.js @@ -29,7 +29,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree"], functio if (param) { param = dojo.doc.createElement('ul'); - param.className = (enabled != false) ? 'actions_summary' : 'actions_summary filterDisabled'; + param.className = (enabled !== false) ? 'actions_summary' : 'actions_summary filterDisabled'; param.innerHTML = args.item.param[0]; domConstruct.place(param, tnode.rowNode, 'first'); } @@ -85,7 +85,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree"], functio // disable copying items source.copyState = function() { return false; }; - return position != 'over'; + return position !== 'over'; }, onDndDrop: function() { this.inherited(arguments); @@ -132,7 +132,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree"], functio joinSelectedFilters: function() { const rows = this.getSelectedFilters(); - if (rows.length == 0) { + if (rows.length === 0) { alert(__("No filters selected.")); return; } diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index cf822d987..b0b0d9c97 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -15,7 +15,7 @@ const Helpers = { removeSelected: function() { const rows = this.getSelected(); - if (rows.length == 0) { + if (rows.length === 0) { alert("No passwords selected."); } else if (confirm(__("Remove selected app passwords?"))) { @@ -131,7 +131,7 @@ const Helpers = { cloneSelected: function() { const sel_rows = this.getSelectedProfiles(); - if (sel_rows.length == 1) { + if (sel_rows.length === 1) { const new_title = prompt(__("Name for cloned profile:")); if (new_title) { @@ -238,7 +238,7 @@ const Helpers = { execute: function () { const sel_rows = this.getSelectedProfiles(); - if (sel_rows.length == 1) { + if (sel_rows.length === 1) { if (confirm(__("Activate selected profile?"))) { Notify.progress("Loading, please wait..."); @@ -386,11 +386,11 @@ const Helpers = { this._list_of_plugins.plugins.forEach((plugin) => { - if (search_tokens.length == 0 || + if (search_tokens.length === 0 || Object.values(plugin).filter((pval) => search_tokens.filter((stoken) => - (pval.toString().indexOf(stoken) != -1 ? stoken : null) - ).length == search_tokens.length).length > 0) { + (pval.toString().indexOf(stoken) !== -1 ? stoken : null) + ).length === search_tokens.length).length > 0) { ++results_rendered; @@ -433,7 +433,7 @@ const Helpers = { } }); - if (results_rendered == 0) { + if (results_rendered === 0) { container.innerHTML += `
  • ${__("Could not find any plugins for this search query.")}
  • `; } @@ -456,7 +456,7 @@ const Helpers = { Notify.progress("Loading, please wait..."); xhr.json("backend.php", {op: "Pref_Prefs", method: "uninstallPlugin", plugin: plugin}, (reply) => { - if (reply && reply.status == 1) + if (reply && reply.status === 1) Helpers.Plugins.reload(); else { Notify.error("Plugin uninstallation failed."); @@ -562,11 +562,11 @@ const Helpers = { const is_installed = (dialog.installed_plugins .filter((p) => plugin.topics.map((t) => t.replace(/-/g, "_")).includes(p))).length > 0; - if (search_tokens.length == 0 || + if (search_tokens.length === 0 || Object.values(plugin).filter((pval) => search_tokens.filter((stoken) => - (pval.indexOf(stoken) != -1 ? stoken : null) - ).length == search_tokens.length).length > 0) { + (pval.indexOf(stoken) !== -1 ? stoken : null) + ).length === search_tokens.length).length > 0) { ++results_rendered; @@ -592,7 +592,7 @@ const Helpers = { } }); - if (results_rendered == 0) { + if (results_rendered === 0) { container.innerHTML = `
  • ${__("Could not find any plugins for this search query.")}
  • `; } @@ -664,7 +664,7 @@ const Helpers = { container.innerHTML = ""; reply.forEach((p) => { - if (p.rv.git_status == 0) + if (p.rv.git_status === 0) dialog.need_refresh = true; else enable_update_btn = true; @@ -704,7 +704,7 @@ const Helpers = { dialog.attr('title', __("No updates available")); dijit.getEnclosingWidget(dialog.domNode.querySelector(".update-btn")) - .attr('disabled', num_updated == 0); + .attr('disabled', num_updated === 0); } }, @@ -735,7 +735,7 @@ const Helpers = { update_button.domNode.show(); } - if (p.rv.need_update || p.rv.git_status != 0) { + if (p.rv.need_update || p.rv.git_status !== 0) { container.innerHTML += `
  • ${p.plugin}

    @@ -787,7 +787,7 @@ const Helpers = { import: function() { const opml_file = App.byId("opml_file"); - if (opml_file.value.length == 0) { + if (opml_file.value.length === 0) { alert(__("Please choose an OPML file first.")); return false; } else { diff --git a/js/PrefLabelTree.js b/js/PrefLabelTree.js index 582e5a9b9..c73a3ac46 100644 --- a/js/PrefLabelTree.js +++ b/js/PrefLabelTree.js @@ -19,7 +19,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dijit/f const type = this.model.store.getValue(args.item, 'type'); //const bare_id = this.model.store.getValue(args.item, 'bare_id'); - if (type == 'label') { + if (type === 'label') { const label = dojo.doc.createElement('i'); //const fg_color = args.item.fg_color[0]; const bg_color = String(args.item.bg_color); diff --git a/js/PrefUsers.js b/js/PrefUsers.js index e8f4a7489..362e62662 100644 --- a/js/PrefUsers.js +++ b/js/PrefUsers.js @@ -32,7 +32,7 @@ const Users = { edit: function(id) { xhr.json('backend.php', {op: 'Pref_Users', method: 'edit', id: id}, (reply) => { const user = reply.user; - const admin_disabled = (user.id == 1); + const admin_disabled = (user.id === 1); const dialog = new fox.SingleUseDialog({ id: "userEditDlg", @@ -132,7 +132,7 @@ const Users = { resetSelected: function() { const rows = this.getSelection(); - if (rows.length == 0) { + if (rows.length === 0) { alert(__("No users selected.")); return; } diff --git a/js/common.js b/js/common.js index 99cf52fa1..cdc6cd6cc 100755 --- a/js/common.js +++ b/js/common.js @@ -5,7 +5,7 @@ /* exported __ */ function __(msg) { - if (typeof App != "undefined") { + if (typeof App !== "undefined") { return App.l10n.__(msg); } else { return msg; @@ -106,7 +106,7 @@ Element.prototype.fadeOut = function() { Element.prototype.fadeIn = function(display = undefined){ this.style.opacity = 0; - this.style.display = display == undefined ? "block" : display; + this.style.display = display === undefined ? "block" : display; const self = this; (function fade() { @@ -119,39 +119,39 @@ Element.prototype.fadeIn = function(display = undefined){ }; Element.prototype.visible = function() { - return window.getComputedStyle(this).display != "none"; //&& this.offsetHeight != 0 && this.offsetWidth != 0; + return window.getComputedStyle(this).display !== "none"; //&& this.offsetHeight !== 0 && this.offsetWidth !== 0; } Element.visible = function(elem) { - if (typeof elem == "string") + if (typeof elem === "string") elem = document.getElementById(elem); return elem.visible(); } Element.show = function(elem) { - if (typeof elem == "string") + if (typeof elem === "string") elem = document.getElementById(elem); return elem.show(); } Element.hide = function(elem) { - if (typeof elem == "string") + if (typeof elem === "string") elem = document.getElementById(elem); return elem.hide(); } Element.toggle = function(elem) { - if (typeof elem == "string") + if (typeof elem === "string") elem = document.getElementById(elem); return elem.toggle(); } Element.hasClassName = function (elem, className) { - if (typeof elem == "string") + if (typeof elem === "string") elem = document.getElementById(elem); return elem.hasClassName(className); @@ -159,7 +159,7 @@ Element.hasClassName = function (elem, className) { Array.prototype.remove = function(s) { for (let i=0; i < this.length; i++) { - if (s == this[i]) this.splice(i, 1); + if (s === this[i]) this.splice(i, 1); } }; @@ -176,14 +176,14 @@ const xhr = { console.log('xhr.post', '>>>', params); return new Promise((resolve, reject) => { - if (typeof __csrf_token != "undefined") + if (typeof __csrf_token !== "undefined") params = {...params, ...{csrf_token: __csrf_token}}; dojo.xhrPost({url: url, postData: dojo.objectToQuery(params), handleAs: "text", error: function(error) { - if (failed != undefined) + if (failed !== undefined) failed(error); reject(error); @@ -191,7 +191,7 @@ const xhr = { load: function(data, ioargs) { console.log('xhr.post', '<<<', ioargs.xhr, (new Date().getTime() - xhr._ts) + " ms"); - if (complete != undefined) + if (typeof complete === 'function') complete(data, ioargs.xhr); resolve(data) @@ -209,7 +209,7 @@ const xhr = { } catch (e) { console.error("xhr.json", e, xhr); - if (failed != undefined) + if (typeof failed === 'function') failed(e); reject(e); @@ -218,21 +218,21 @@ const xhr = { console.log('xhr.json', '<<<', obj, (new Date().getTime() - xhr._ts) + " ms"); - if (obj && typeof App != "undefined") - if (!App.handleRpcJson(obj)) { + if (obj && typeof App !== "undefined") + if (!App.handleRpcJson(obj)) { - if (failed != undefined) - failed(obj); + if (typeof failed === 'function') + failed(obj); - reject(obj); - return; - } + reject(obj); + return; + } - if (complete != undefined) complete(obj); + if (typeof complete === 'function') + complete(obj); - resolve(obj); - } - )); + resolve(obj); + })); } }; @@ -241,7 +241,7 @@ function xhrPost(url, params = {}, complete = undefined) { console.log("xhrPost:", params); return new Promise((resolve, reject) => { - if (typeof __csrf_token != "undefined") + if (typeof __csrf_token !== "undefined") params = {...params, ...{csrf_token: __csrf_token}}; dojo.xhrPost({url: url, @@ -251,7 +251,7 @@ function xhrPost(url, params = {}, complete = undefined) { reject(error); }, load: function(data, ioargs) { - if (complete != undefined) + if (complete !== undefined) complete(ioargs.xhr); resolve(ioargs.xhr) @@ -279,7 +279,7 @@ const Lists = { checked ? row.addClassName("Selected") : row.removeClassName("Selected"); }, select: function(elem, selected) { - if (typeof elem == "string") + if (typeof elem === "string") elem = document.getElementById(elem); elem.querySelectorAll("li").forEach((row) => { @@ -300,7 +300,7 @@ const Lists = { getSelected: function(elem) { const rv = []; - if (typeof elem == "string") + if (typeof elem === "string") elem = document.getElementById(elem); elem.querySelectorAll("li").forEach((row) => { @@ -337,7 +337,7 @@ const Tables = { }, select: function(elem, selected) { - if (typeof elem == "string") + if (typeof elem === "string") elem = document.getElementById(elem); elem.querySelectorAll("tr").forEach((row) => { @@ -358,7 +358,7 @@ const Tables = { getSelected: function(elem) { const rv = []; - if (typeof elem == "string") + if (typeof elem === "string") elem = document.getElementById(elem); elem.querySelectorAll("tr").forEach((row) => { @@ -394,8 +394,8 @@ const Cookie = { const ca = document.cookie.split(';'); for (let i=0; i < ca.length; i++) { let c = ca[i]; - while (c.charAt(0) == ' ') c = c.substring(1); - if (c.indexOf(name) == 0) return decodeURIComponent(c.substring(name.length, c.length)); + while (c.charAt(0) === ' ') c = c.substring(1); + if (c.indexOf(name) === 0) return decodeURIComponent(c.substring(name.length, c.length)); } return ""; }, @@ -455,7 +455,7 @@ const Notify = { } if (icon) - if (icon.indexOf("data:image") != -1) + if (icon.indexOf("data:image") !== -1) msgfmt = "".replace("%s", icon) + msgfmt; else msgfmt = "%s".replace("%s", icon) + msgfmt; diff --git a/js/form/Select.js b/js/form/Select.js index 0c73cd52c..2259fd762 100755 --- a/js/form/Select.js +++ b/js/form/Select.js @@ -15,7 +15,7 @@ define(["dojo/_base/declare", startup: function() { this.inherited(arguments); - if (this.attr('data-dropdown-skip-first') == 'true') { + if (this.attr('data-dropdown-skip-first') === 'true') { aspect.before(this, "_loadChildren", () => { this.options = this.options.splice(1); }); @@ -26,7 +26,7 @@ define(["dojo/_base/declare", // }, _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){ - if (this.attr('data-prevent-value-change') == 'true' && newValue != '') + if (this.attr('data-prevent-value-change') === 'true' && newValue !== '') return; this.inherited(arguments); diff --git a/js/form/ValidationMultiSelect.js b/js/form/ValidationMultiSelect.js index 4e7263c61..a0199c9c6 100644 --- a/js/form/ValidationMultiSelect.js +++ b/js/form/ValidationMultiSelect.js @@ -11,7 +11,7 @@ define(["dojo/_base/declare", "dojo/_base/lang", "dijit/form/MultiSelect", ], this.baseClass += ' dijitValidationMultiSelect'; }, validate: function(/*Boolean*/ isFocused){ - if (this.required && this.attr('value').length == 0) + if (this.required && this.attr('value').length === 0) return false; return true; diff --git a/js/prefs.js b/js/prefs.js index 8f4f45700..0fb70f710 100755 --- a/js/prefs.js +++ b/js/prefs.js @@ -63,7 +63,7 @@ require(["dojo/_base/kernel", try { App.init(parser, true); } catch (e) { - if (typeof App != "undefined" && App.Error) + if (typeof App !== "undefined" && App.Error) App.Error.report(e); else alert(e + "\n\n" + e.stack); diff --git a/js/tt-rss.js b/js/tt-rss.js index 10fafc447..8e0401d80 100644 --- a/js/tt-rss.js +++ b/js/tt-rss.js @@ -61,7 +61,7 @@ require(["dojo/_base/kernel", try { App.init(parser, false); } catch (e) { - if (typeof App != "undefined" && App.Error) + if (typeof App !== "undefined" && App.Error) App.Error.report(e); else alert(e + "\n\n" + e.stack); diff --git a/js/utility.js b/js/utility.js index 2e27c4fe6..e5de5f300 100644 --- a/js/utility.js +++ b/js/utility.js @@ -7,8 +7,8 @@ window.addEventListener("load", function() { apply_night_mode: function (is_night, link) { console.log("night mode changed to", is_night); - const light_theme = typeof __default_light_theme != 'undefined' ? __default_light_theme : 'themes/light.css'; - const dark_theme = typeof __default_dark_theme != 'undefined' ? __default_dark_theme : 'themes/night.css'; + const light_theme = typeof __default_light_theme !== 'undefined' ? __default_light_theme : 'themes/light.css'; + const dark_theme = typeof __default_dark_theme !== 'undefined' ? __default_dark_theme : 'themes/night.css'; if (link) { const css_override = is_night ? dark_theme : light_theme; @@ -27,7 +27,7 @@ window.addEventListener("load", function() { link.onload = function() { document.querySelector("body").removeClassName("css_loading"); - if (typeof UtilityApp != "undefined") + if (typeof UtilityApp !== "undefined") UtilityApp.init(); }; -- cgit v1.2.3-54-g00ecf From d442079a72dd575704a5eff6cafe325eabca71c2 Mon Sep 17 00:00:00 2001 From: supahgreg Date: Sun, 12 Oct 2025 21:38:59 +0000 Subject: Address ESLint rule 'no-prototype-builtins'. https://eslint.org/docs/latest/rules/no-prototype-builtins --- js/App.js | 2 +- js/CommonDialogs.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/App.js b/js/App.js index c727cc947..97323a9bc 100644 --- a/js/App.js +++ b/js/App.js @@ -332,7 +332,7 @@ const App = { const hotkeys_map = this.getInitParam("hotkeys"); for (const seq in hotkeys_map[1]) { - if (hotkeys_map[1].hasOwnProperty(seq)) { + if (Object.prototype.hasOwnProperty.call(hotkeys_map[1], seq)) { if (seq === sequence) { return hotkeys_map[1][seq]; } diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js index a9395d62e..9c6d1338b 100644 --- a/js/CommonDialogs.js +++ b/js/CommonDialogs.js @@ -184,7 +184,7 @@ const CommonDialogs = { select.addOption({value: '', label: __("Expand to select feed")}); for (const feedUrl in feeds) { - if (feeds.hasOwnProperty(feedUrl)) { + if (Object.prototype.hasOwnProperty.call(feeds, feedUrl)) { select.addOption({value: feedUrl, label: feeds[feedUrl]}); } } -- cgit v1.2.3-54-g00ecf From 360c552da41b08d92c8d5e0d01968ffe1e8e6323 Mon Sep 17 00:00:00 2001 From: supahgreg Date: Sun, 12 Oct 2025 21:48:10 +0000 Subject: Address rule 'no-redeclare' for 'dojo' and 'dijit' (defined as globals in 'eslint.config.js'). Also take care of 2 'no-prototype-builtins' and a 'no-useless-escape'. * https://eslint.org/docs/latest/rules/no-redeclare * https://eslint.org/docs/latest/rules/no-prototype-builtins * https://eslint.org/docs/latest/rules/no-useless-escape --- js/App.js | 2 +- js/Article.js | 2 +- js/CommonDialogs.js | 2 +- js/CommonFilters.js | 2 +- js/FeedStoreModel.js | 2 +- js/FeedTree.js | 2 +- js/Feeds.js | 2 +- js/Headlines.js | 2 +- js/PrefFeedStore.js | 2 +- js/PrefFeedTree.js | 2 +- js/PrefFilterStore.js | 2 +- js/PrefFilterTree.js | 2 +- js/PrefHelpers.js | 2 +- js/PrefLabelTree.js | 2 +- js/PrefUsers.js | 2 +- js/SingleUseDialog.js | 2 +- js/Toolbar.js | 2 +- js/common.js | 2 +- js/form/ComboButton.js | 2 +- js/form/DropDownButton.js | 2 +- js/tt-rss.js | 2 +- plugins/af_psql_trgm/init.js | 2 +- plugins/share/share.js | 2 +- plugins/shorten_expanded/init.js | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/js/App.js b/js/App.js index 97323a9bc..80fbabe82 100644 --- a/js/App.js +++ b/js/App.js @@ -423,7 +423,7 @@ const App = { '/': '/', }; - return p.replace(/[&<>"'\/]/g, m => map[m]); + return p.replace(/[&<>"'/]/g, m => map[m]); }, unescapeHtml: function(p) { if (typeof p !== 'string' || p.indexOf('&') === -1) diff --git a/js/Article.js b/js/Article.js index b38cb50c6..1f4cf931e 100644 --- a/js/Article.js +++ b/js/Article.js @@ -1,7 +1,7 @@ 'use strict' /* eslint-disable no-new */ -/* global __, ngettext, App, Headlines, xhr, dojo, dijit, PluginHost, Notify, fox */ +/* global __, ngettext, App, Headlines, xhr, PluginHost, Notify, fox */ const Article = { _scroll_reset_timeout: false, diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js index 9c6d1338b..43c22b5f6 100644 --- a/js/CommonDialogs.js +++ b/js/CommonDialogs.js @@ -3,7 +3,7 @@ /* eslint-disable new-cap */ /* eslint-disable no-new */ -/* global __, dojo, dijit, Notify, App, Feeds, xhr, Tables, fox */ +/* global __, Notify, App, Feeds, xhr, Tables, fox */ /* exported CommonDialogs */ const CommonDialogs = { diff --git a/js/CommonFilters.js b/js/CommonFilters.js index e27d3c40c..3e68168fe 100644 --- a/js/CommonFilters.js +++ b/js/CommonFilters.js @@ -3,7 +3,7 @@ /* eslint-disable no-new */ /* global __, App, Article, Lists, fox */ -/* global xhr, dojo, dijit, Notify, Feeds */ +/* global xhr, Notify, Feeds */ /* exported Filters */ const Filters = { diff --git a/js/FeedStoreModel.js b/js/FeedStoreModel.js index fa8f730d6..ef43357d0 100644 --- a/js/FeedStoreModel.js +++ b/js/FeedStoreModel.js @@ -1,4 +1,4 @@ -/* global define, dijit */ +/* global define */ define(["dojo/_base/declare", "dijit/tree/ForestStoreModel"], function (declare) { diff --git a/js/FeedTree.js b/js/FeedTree.js index cd7e1f311..09288845c 100755 --- a/js/FeedTree.js +++ b/js/FeedTree.js @@ -1,5 +1,5 @@ /* eslint-disable prefer-rest-params */ -/* global __, dojo, dijit, define, App, Feeds, CommonDialogs */ +/* global __, define, App, Feeds, CommonDialogs */ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/cookie", "dijit/Tree", "dijit/Menu"], function (declare, domConstruct, array, cookie) { diff --git a/js/Feeds.js b/js/Feeds.js index 825f6f8ef..38e87b384 100644 --- a/js/Feeds.js +++ b/js/Feeds.js @@ -1,6 +1,6 @@ 'use strict' -/* global __, App, Headlines, xhr, dojo, dijit, fox, PluginHost, Notify, fox */ +/* global __, App, Headlines, xhr, fox, PluginHost, Notify, fox */ const Feeds = { FEED_ARCHIVED: 0, diff --git a/js/Headlines.js b/js/Headlines.js index 1547781c4..1a59ffda0 100755 --- a/js/Headlines.js +++ b/js/Headlines.js @@ -1,7 +1,7 @@ 'use strict'; /* global __, ngettext, Article, App */ -/* global dojo, dijit, PluginHost, Notify, xhr, Feeds */ +/* global PluginHost, Notify, xhr, Feeds */ /* global CommonDialogs */ const Headlines = { diff --git a/js/PrefFeedStore.js b/js/PrefFeedStore.js index 348cbd995..d03169acb 100644 --- a/js/PrefFeedStore.js +++ b/js/PrefFeedStore.js @@ -1,4 +1,4 @@ -/* global define, dojo */ +/* global define */ define(["dojo/_base/declare", "dojo/data/ItemFileWriteStore"], function (declare) { diff --git a/js/PrefFeedTree.js b/js/PrefFeedTree.js index 7a7c5b551..92e748903 100644 --- a/js/PrefFeedTree.js +++ b/js/PrefFeedTree.js @@ -1,5 +1,5 @@ /* eslint-disable prefer-rest-params */ -/* global __, lib, dijit, define, dojo, CommonDialogs, Notify, Tables, xhr, fox, App */ +/* global __, lib, define, CommonDialogs, Notify, Tables, xhr, fox, App */ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_base/array", "dojo/cookie"], function (declare, domConstruct, checkBoxTree, array, cookie) { diff --git a/js/PrefFilterStore.js b/js/PrefFilterStore.js index f1192374a..f4cb8d0dd 100644 --- a/js/PrefFilterStore.js +++ b/js/PrefFilterStore.js @@ -1,4 +1,4 @@ -/* global define, dojo */ +/* global define */ define(["dojo/_base/declare", "dojo/data/ItemFileWriteStore"], function (declare) { diff --git a/js/PrefFilterTree.js b/js/PrefFilterTree.js index 9a9728c08..64984b107 100644 --- a/js/PrefFilterTree.js +++ b/js/PrefFilterTree.js @@ -1,5 +1,5 @@ /* eslint-disable prefer-rest-params */ -/* global __, define, lib, dijit, dojo, xhr, App, Notify, Filters */ +/* global __, define, lib, xhr, App, Notify, Filters */ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree"], function (declare, domConstruct) { diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index b0b0d9c97..38fa7e243 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -1,7 +1,7 @@ 'use strict'; /* eslint-disable no-new */ -/* global __, dijit, dojo, Tables, Notify, xhr, App, fox */ +/* global __, Tables, Notify, xhr, App, fox */ const Helpers = { AppPasswords: { diff --git a/js/PrefLabelTree.js b/js/PrefLabelTree.js index c73a3ac46..1859906ee 100644 --- a/js/PrefLabelTree.js +++ b/js/PrefLabelTree.js @@ -1,5 +1,5 @@ /* eslint-disable prefer-rest-params */ -/* global __, define, lib, dijit, dojo, xhr, Notify, fox, App */ +/* global __, define, lib, xhr, Notify, fox, App */ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dijit/form/DropDownButton"], function (declare, domConstruct) { diff --git a/js/PrefUsers.js b/js/PrefUsers.js index 362e62662..73e48b52f 100644 --- a/js/PrefUsers.js +++ b/js/PrefUsers.js @@ -1,6 +1,6 @@ 'use strict' -/* global __, xhr, dijit, Notify, Tables, App, fox */ +/* global __, xhr, Notify, Tables, App, fox */ const Users = { reload: function(sort) { diff --git a/js/SingleUseDialog.js b/js/SingleUseDialog.js index 2de6f83ff..b38b3f969 100644 --- a/js/SingleUseDialog.js +++ b/js/SingleUseDialog.js @@ -1,5 +1,5 @@ /* eslint-disable prefer-rest-params */ -/* global dijit, define */ +/* global define */ define(["dojo/_base/declare", "dijit/Dialog"], function (declare) { return declare("fox.SingleUseDialog", dijit.Dialog, { create: function(params) { diff --git a/js/Toolbar.js b/js/Toolbar.js index d4993e713..b7c2a0898 100755 --- a/js/Toolbar.js +++ b/js/Toolbar.js @@ -1,4 +1,4 @@ -/* global dijit, define */ +/* global define */ define(["dojo/_base/declare", "dijit/Toolbar"], function (declare) { return declare("fox.Toolbar", dijit.Toolbar, { diff --git a/js/common.js b/js/common.js index cdc6cd6cc..bccd0dcbb 100755 --- a/js/common.js +++ b/js/common.js @@ -1,6 +1,6 @@ 'use strict'; -/* global dijit, App, dojo, __csrf_token */ +/* global App, __csrf_token */ /* eslint-disable no-new */ /* exported __ */ diff --git a/js/form/ComboButton.js b/js/form/ComboButton.js index 2ad4bf123..4c97809e4 100755 --- a/js/form/ComboButton.js +++ b/js/form/ComboButton.js @@ -1,5 +1,5 @@ /* eslint-disable prefer-rest-params */ -/* global dijit, define */ +/* global define */ define(["dojo/_base/declare", "dijit/form/ComboButton"], function (declare) { return declare("fox.form.ComboButton", dijit.form.ComboButton, { startup: function() { diff --git a/js/form/DropDownButton.js b/js/form/DropDownButton.js index d5ea39726..357d5de91 100755 --- a/js/form/DropDownButton.js +++ b/js/form/DropDownButton.js @@ -1,5 +1,5 @@ /* eslint-disable prefer-rest-params */ -/* global dijit, define */ +/* global define */ define(["dojo/_base/declare", "dijit/form/DropDownButton"], function (declare) { return declare("fox.form.DropDownButton", dijit.form.DropDownButton, { startup: function() { diff --git a/js/tt-rss.js b/js/tt-rss.js index 8e0401d80..2af9d32ff 100644 --- a/js/tt-rss.js +++ b/js/tt-rss.js @@ -1,6 +1,6 @@ 'use strict' -/* global require, App, dojo */ +/* global require, App */ /* exported Plugins */ const Plugins = {}; diff --git a/plugins/af_psql_trgm/init.js b/plugins/af_psql_trgm/init.js index f3662a389..e57f11564 100644 --- a/plugins/af_psql_trgm/init.js +++ b/plugins/af_psql_trgm/init.js @@ -1,4 +1,4 @@ -/* global dijit, dojo, Plugins, xhr, __ */ +/* global Plugins, xhr, __ */ Plugins.Psql_Trgm = { showRelated: function (id) { diff --git a/plugins/share/share.js b/plugins/share/share.js index 1be9db682..f27e06678 100644 --- a/plugins/share/share.js +++ b/plugins/share/share.js @@ -1,4 +1,4 @@ -/* global dojo, Plugins, App, Notify, fox, xhr, __ */ +/* global Plugins, App, Notify, fox, xhr, __ */ Plugins.Share = { shareArticle: function(id) { diff --git a/plugins/shorten_expanded/init.js b/plugins/shorten_expanded/init.js index bc3e35ff6..a89fe8e92 100644 --- a/plugins/shorten_expanded/init.js +++ b/plugins/shorten_expanded/init.js @@ -1,4 +1,4 @@ -/* global Plugins, __, require, PluginHost, App, dojo */ +/* global Plugins, __, require, PluginHost, App */ Plugins.Shorten_Expanded = { threshold: 1.5, // of window height -- cgit v1.2.3-54-g00ecf From 7211d3468456bc770f227432e98be13bf7d7d42a Mon Sep 17 00:00:00 2001 From: supahgreg Date: Sun, 12 Oct 2025 22:20:13 +0000 Subject: Add a workaround+note for a FeedTree feed item's 'error' being '[]'. The backend is sending 'error' as a string (or undefined for cats). --- js/FeedTree.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/FeedTree.js b/js/FeedTree.js index 09288845c..86a396068 100755 --- a/js/FeedTree.js +++ b/js/FeedTree.js @@ -224,7 +224,9 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co else rc += " Is_Feed"; - if (!is_cat && item.error !== '') rc += " Error"; + // TODO: item.error is `[""]` for feeds. Need to look into what's happening on the frontend to cause that-- the backend sends a string. + // For now, just adding a check for `[""]`. + if (!is_cat && item.error !== '' && !(Array.isArray(item.error) && item.error.length === 1 && item.error[0] === '')) rc += ' Error'; if (item.unread > 0) rc += " Unread"; if (item.auxcounter > 0) rc += " Has_Aux"; if (item.markedcounter > 0) rc += " Has_Marked"; -- cgit v1.2.3-54-g00ecf From cde120b3d730dfb56fa59759bb9a7369584f551c Mon Sep 17 00:00:00 2001 From: supahgreg Date: Sun, 12 Oct 2025 22:26:51 +0000 Subject: Clean up unnecessary eslint-disable directives (per ESLint). --- js/App.js | 1 - js/Article.js | 1 - js/CommonDialogs.js | 3 --- js/CommonFilters.js | 2 -- js/FeedTree.js | 2 -- js/Headlines.js | 2 -- js/PrefFeedTree.js | 2 -- js/PrefFilterTree.js | 2 -- js/PrefHelpers.js | 1 - js/PrefLabelTree.js | 2 -- js/SingleUseDialog.js | 1 - js/common.js | 1 - js/form/ComboButton.js | 1 - js/form/DropDownButton.js | 1 - js/form/Select.js | 1 - js/form/ValidationTextArea.js | 2 -- 16 files changed, 25 deletions(-) diff --git a/js/App.js b/js/App.js index 80fbabe82..0adf291c2 100644 --- a/js/App.js +++ b/js/App.js @@ -1,6 +1,5 @@ 'use strict'; -/* eslint-disable new-cap */ /* global __, Article, Headlines, Filters, fox */ /* global xhr, PluginHost, Notify, Feeds, Cookie */ /* global CommonDialogs, Plugins */ diff --git a/js/Article.js b/js/Article.js index 1f4cf931e..f7672033c 100644 --- a/js/Article.js +++ b/js/Article.js @@ -1,6 +1,5 @@ 'use strict' -/* eslint-disable no-new */ /* global __, ngettext, App, Headlines, xhr, PluginHost, Notify, fox */ const Article = { diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js index 43c22b5f6..16c1f5f46 100644 --- a/js/CommonDialogs.js +++ b/js/CommonDialogs.js @@ -1,8 +1,5 @@ 'use strict' -/* eslint-disable new-cap */ -/* eslint-disable no-new */ - /* global __, Notify, App, Feeds, xhr, Tables, fox */ /* exported CommonDialogs */ diff --git a/js/CommonFilters.js b/js/CommonFilters.js index 3e68168fe..54df70266 100644 --- a/js/CommonFilters.js +++ b/js/CommonFilters.js @@ -1,7 +1,5 @@ 'use strict' -/* eslint-disable no-new */ - /* global __, App, Article, Lists, fox */ /* global xhr, Notify, Feeds */ diff --git a/js/FeedTree.js b/js/FeedTree.js index 86a396068..f0041640d 100755 --- a/js/FeedTree.js +++ b/js/FeedTree.js @@ -1,4 +1,3 @@ -/* eslint-disable prefer-rest-params */ /* global __, define, App, Feeds, CommonDialogs */ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/cookie", "dijit/Tree", "dijit/Menu"], function (declare, domConstruct, array, cookie) { @@ -208,7 +207,6 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co return [item.updated, item.error].filter((x) => x && x !== "").join(" - "); }, getIconClass: function (item, opened) { - // eslint-disable-next-line no-nested-ternary return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feed-icon"; }, getLabelClass: function (item/* , opened */) { diff --git a/js/Headlines.js b/js/Headlines.js index 1a59ffda0..1725998bc 100755 --- a/js/Headlines.js +++ b/js/Headlines.js @@ -228,7 +228,6 @@ const Headlines = { } else if (event.ctrlKey) { Headlines.select('invert', id); } else { - // eslint-disable-next-line no-lonely-if if (App.isCombinedMode()) { if (event.altKey && !in_body) { @@ -268,7 +267,6 @@ const Headlines = { return in_body; } else { - // eslint-disable-next-line no-lonely-if if (event.altKey) { Article.openInNewWindow(id); Headlines.toggleUnread(id, 0); diff --git a/js/PrefFeedTree.js b/js/PrefFeedTree.js index 92e748903..142060488 100644 --- a/js/PrefFeedTree.js +++ b/js/PrefFeedTree.js @@ -1,4 +1,3 @@ -/* eslint-disable prefer-rest-params */ /* global __, lib, define, CommonDialogs, Notify, Tables, xhr, fox, App */ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_base/array", "dojo/cookie"], @@ -143,7 +142,6 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b return rc; }, getIconClass: function (item, opened) { - // eslint-disable-next-line no-nested-ternary return (!item || this.model.store.getValue(item, 'type') === 'category') ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feed-icon"; }, reload: function() { diff --git a/js/PrefFilterTree.js b/js/PrefFilterTree.js index 64984b107..1f20da581 100644 --- a/js/PrefFilterTree.js +++ b/js/PrefFilterTree.js @@ -1,4 +1,3 @@ -/* eslint-disable prefer-rest-params */ /* global __, define, lib, xhr, App, Notify, Filters */ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree"], function (declare, domConstruct) { @@ -71,7 +70,6 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree"], functio return label; }, getIconClass: function (item, opened) { - // eslint-disable-next-line no-nested-ternary return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "invisible"; }, getRowClass: function (item, opened) { diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index 38fa7e243..fbe1d37f5 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -1,6 +1,5 @@ 'use strict'; -/* eslint-disable no-new */ /* global __, Tables, Notify, xhr, App, fox */ const Helpers = { diff --git a/js/PrefLabelTree.js b/js/PrefLabelTree.js index 1859906ee..a4540deec 100644 --- a/js/PrefLabelTree.js +++ b/js/PrefLabelTree.js @@ -1,4 +1,3 @@ -/* eslint-disable prefer-rest-params */ /* global __, define, lib, xhr, Notify, fox, App */ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dijit/form/DropDownButton"], function (declare, domConstruct) { @@ -40,7 +39,6 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dijit/f return tnode; }, getIconClass: function (item, opened) { - // eslint-disable-next-line no-nested-ternary return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "invisible"; }, getSelectedLabels: function() { diff --git a/js/SingleUseDialog.js b/js/SingleUseDialog.js index b38b3f969..a207c0c44 100644 --- a/js/SingleUseDialog.js +++ b/js/SingleUseDialog.js @@ -1,4 +1,3 @@ -/* eslint-disable prefer-rest-params */ /* global define */ define(["dojo/_base/declare", "dijit/Dialog"], function (declare) { return declare("fox.SingleUseDialog", dijit.Dialog, { diff --git a/js/common.js b/js/common.js index bccd0dcbb..3e8a66e5b 100755 --- a/js/common.js +++ b/js/common.js @@ -1,7 +1,6 @@ 'use strict'; /* global App, __csrf_token */ -/* eslint-disable no-new */ /* exported __ */ function __(msg) { diff --git a/js/form/ComboButton.js b/js/form/ComboButton.js index 4c97809e4..98386eead 100755 --- a/js/form/ComboButton.js +++ b/js/form/ComboButton.js @@ -1,4 +1,3 @@ -/* eslint-disable prefer-rest-params */ /* global define */ define(["dojo/_base/declare", "dijit/form/ComboButton"], function (declare) { return declare("fox.form.ComboButton", dijit.form.ComboButton, { diff --git a/js/form/DropDownButton.js b/js/form/DropDownButton.js index 357d5de91..60d8e86ef 100755 --- a/js/form/DropDownButton.js +++ b/js/form/DropDownButton.js @@ -1,4 +1,3 @@ -/* eslint-disable prefer-rest-params */ /* global define */ define(["dojo/_base/declare", "dijit/form/DropDownButton"], function (declare) { return declare("fox.form.DropDownButton", dijit.form.DropDownButton, { diff --git a/js/form/Select.js b/js/form/Select.js index 2259fd762..bc352e612 100755 --- a/js/form/Select.js +++ b/js/form/Select.js @@ -1,4 +1,3 @@ -/* eslint-disable prefer-rest-params */ /* global define */ // FIXME: there probably is a better, more dojo-like notation for custom data- properties define(["dojo/_base/declare", diff --git a/js/form/ValidationTextArea.js b/js/form/ValidationTextArea.js index c53260f40..ced4f8fe7 100644 --- a/js/form/ValidationTextArea.js +++ b/js/form/ValidationTextArea.js @@ -1,5 +1,4 @@ // https://stackoverflow.com/questions/19317258/how-to-use-dijit-textarea-validation-dojo-1-9 -/* eslint-disable no-new */ /* global define */ define(["dojo/_base/declare", "dojo/_base/lang", "dijit/form/SimpleTextarea", "dijit/form/ValidationTextBox"], @@ -10,7 +9,6 @@ define(["dojo/_base/declare", "dojo/_base/lang", "dijit/form/SimpleTextarea", "d this.constraints = {}; this.baseClass += ' dijitValidationTextArea'; }, - // eslint-disable-next-line no-template-curly-in-string templateString: "", validator: function(value, constraints) { //console.log(this, value, constraints); -- cgit v1.2.3-54-g00ecf From 47848d0198cb13a718fd507bed98b03b13846b15 Mon Sep 17 00:00:00 2001 From: supahgreg Date: Sun, 12 Oct 2025 22:32:55 +0000 Subject: Fix various ESLint 'no-undef' rule instances. https://eslint.org/docs/latest/rules/no-undef --- eslint.config.js | 3 +++ js/common.js | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index a42b6d0e3..9b54e36af 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -30,6 +30,9 @@ export default [ navigator: 'readonly', Event: 'readonly', CustomEvent: 'readonly', + Element: 'readonly', + IntersectionObserver: 'readonly', + MutationObserver: 'readonly', // Dojo dojo: 'readonly', diff --git a/js/common.js b/js/common.js index 3e8a66e5b..60b2f4c9c 100755 --- a/js/common.js +++ b/js/common.js @@ -98,7 +98,7 @@ Element.prototype.fadeOut = function() { if ((self.style.opacity -= 0.1) < 0) { self.style.display = "none"; } else { - requestAnimationFrame(fade); + window.requestAnimationFrame(fade); } }()); }; @@ -112,7 +112,7 @@ Element.prototype.fadeIn = function(display = undefined){ let val = parseFloat(self.style.opacity); if (!((val += 0.1) > 1)) { self.style.opacity = val; - requestAnimationFrame(fade); + window.requestAnimationFrame(fade); } }()); }; -- cgit v1.2.3-54-g00ecf From 54b69728d0440922cb7bf83b2102506231c85ba3 Mon Sep 17 00:00:00 2001 From: supahgreg Date: Sun, 12 Oct 2025 22:55:43 +0000 Subject: Fix 'Feed.setActive()' allowing a string 'id'. This was causing an issue with comparisons elsewhere (e.g. 'Headlines.onLoaded()'). --- js/Feeds.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/Feeds.js b/js/Feeds.js index 38e87b384..bfe901737 100644 --- a/js/Feeds.js +++ b/js/Feeds.js @@ -334,6 +334,8 @@ const Feeds = { setActive: function(id, is_cat) { console.log('setActive', id, is_cat); + id = parseInt(id); + window.requestIdleCallback(() => { App.Hash.set({ f: id, -- cgit v1.2.3-54-g00ecf From 20d2ddabdbc70c3f8f15807ace5bd226082f6a50 Mon Sep 17 00:00:00 2001 From: supahgreg Date: Mon, 13 Oct 2025 02:22:33 +0000 Subject: Handle browser globals in a much better way for ESLint. --- eslint.config.js | 26 ++------------------------ package-lock.json | 20 +++++++++++++++++--- package.json | 1 + 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 9b54e36af..4902b2f0d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,3 +1,4 @@ +import globals from 'globals'; import js from '@eslint/js'; export default [ @@ -9,30 +10,7 @@ export default [ ecmaVersion: 2022, sourceType: 'script', globals: { - // Browser - window: 'readonly', - document: 'readonly', - console: 'readonly', - alert: 'readonly', - confirm: 'readonly', - setTimeout: 'readonly', - setInterval: 'readonly', - clearTimeout: 'readonly', - clearInterval: 'readonly', - fetch: 'readonly', - XMLHttpRequest: 'readonly', - FormData: 'readonly', - URLSearchParams: 'readonly', - localStorage: 'readonly', - sessionStorage: 'readonly', - location: 'readonly', - history: 'readonly', - navigator: 'readonly', - Event: 'readonly', - CustomEvent: 'readonly', - Element: 'readonly', - IntersectionObserver: 'readonly', - MutationObserver: 'readonly', + ...globals.browser, // Dojo dojo: 'readonly', diff --git a/package-lock.json b/package-lock.json index 3ac0644fd..e33ceb57a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "devDependencies": { "eslint": "^9.37.0", + "globals": "^16.4.0", "gulp": "^5.0.1", "gulp-less": "^5.0.0", "gulp-touch-fd": "^2.0.0", @@ -122,6 +123,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/js": { "version": "9.37.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz", @@ -1360,9 +1374,9 @@ } }, "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 31dcfbd7e..7c0815d09 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "type": "module", "devDependencies": { "eslint": "^9.37.0", + "globals": "^16.4.0", "gulp": "^5.0.1", "gulp-less": "^5.0.0", "gulp-touch-fd": "^2.0.0", -- cgit v1.2.3-54-g00ecf From 7d6d32144177ce8c516092a114435988864ff6fa Mon Sep 17 00:00:00 2001 From: supahgreg Date: Mon, 13 Oct 2025 02:54:36 +0000 Subject: Address remaining ESLint 'no-undef' and 'no-unused-vars' occurrences. * https://eslint.org/docs/latest/rules/no-undef * https://eslint.org/docs/latest/rules/no-unused-vars --- js/App.js | 5 +++-- js/Article.js | 4 ++-- js/FeedTree.js | 6 +++--- js/Feeds.js | 8 ++++---- js/Headlines.js | 6 +++--- js/PrefFeedTree.js | 3 +-- js/PrefFilterTree.js | 2 +- js/form/Select.js | 2 +- js/form/ValidationMultiSelect.js | 4 ++-- js/form/ValidationTextArea.js | 4 ++-- js/utility.js | 3 ++- plugins/hotkeys_force_top/init.js | 2 ++ plugins/share/share_prefs.js | 2 +- plugins/toggle_sidebar/init.js | 2 ++ 14 files changed, 29 insertions(+), 24 deletions(-) diff --git a/js/App.js b/js/App.js index 0adf291c2..571e3373f 100644 --- a/js/App.js +++ b/js/App.js @@ -196,7 +196,7 @@ const App = { mql.addEventListener("change", () => { this.nightModeChanged(mql.matches, App.byId("theme_auto_css")); }); - } catch (e) { + } catch { console.warn("exception while trying to set MQL event listener"); } @@ -276,7 +276,7 @@ const App = { try { const results = new RegExp('[?&]' + name + '=([^&#]*)').exec(window.location.href); return decodeURIComponent(results[1].replace(/\+/g, " ")) || 0; - } catch (e) { + } catch { return 0; } }, @@ -701,6 +701,7 @@ const App = { this.is_prefs = is_prefs; window.onerror = this.Error.onWindowError; + /* global __default_dark_theme, __default_light_theme */ this.setInitParam("csrf_token", __csrf_token); this.setInitParam("default_light_theme", __default_light_theme); this.setInitParam("default_dark_theme", __default_dark_theme); diff --git a/js/Article.js b/js/Article.js index f7672033c..88c1cb0e0 100644 --- a/js/Article.js +++ b/js/Article.js @@ -220,7 +220,7 @@ const Article = { try { c.domNode.scrollTop = 0; - } catch (e) { + } catch { } c.attr('content', article); @@ -230,7 +230,7 @@ const Article = { try { c.focus(); - } catch (e) { + } catch { } }, formatComments: function(hl) { diff --git a/js/FeedTree.js b/js/FeedTree.js index f0041640d..970efe335 100755 --- a/js/FeedTree.js +++ b/js/FeedTree.js @@ -209,10 +209,10 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co getIconClass: function (item, opened) { return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feed-icon"; }, - getLabelClass: function (item/* , opened */) { + getLabelClass: function (item /*, opened */) { return (item.unread <= 0) ? "dijitTreeLabel" : "dijitTreeLabel Unread"; }, - getRowClass: function (item/*, opened */) { + getRowClass: function (item /*, opened */) { let rc = "dijitTreeRow dijitTreeRowFlex"; const is_cat = String(item.id).indexOf('CAT:') !== -1; @@ -490,7 +490,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co _itemsByIdentity["FEED:" + feed])[0]. getParent().item.bare_id[0]; - } catch (e) { + } catch { return false; } }, diff --git a/js/Feeds.js b/js/Feeds.js index bfe901737..f99d86943 100644 --- a/js/Feeds.js +++ b/js/Feeds.js @@ -544,7 +544,7 @@ const Feeds = { if (tree && tree.model) return tree.model.getFeedUnread(feed, is_cat); - } catch (e) { + } catch { // } @@ -557,7 +557,7 @@ const Feeds = { if (tree && tree.model) return tree.getFeedCategory(feed); - } catch (e) { + } catch { // } @@ -584,7 +584,7 @@ const Feeds = { if (tree && tree.model) return tree.model.setFeedValue(feed, is_cat, key, value); - } catch (e) { + } catch { // } }, @@ -595,7 +595,7 @@ const Feeds = { if (tree && tree.model) return tree.model.getFeedValue(feed, is_cat, key); - } catch (e) { + } catch { // } return ''; diff --git a/js/Headlines.js b/js/Headlines.js index 1725998bc..c27b214dc 100755 --- a/js/Headlines.js +++ b/js/Headlines.js @@ -16,7 +16,7 @@ const Headlines = { default_move_on_expand: true, line_scroll_offset: 120, /* px */ sticky_header_observer: new IntersectionObserver( - (entries, observer) => { + (entries) => { entries.forEach((entry) => { const header = entry.target.closest('.cdm').querySelector(".header"); @@ -32,7 +32,7 @@ const Headlines = { {threshold: [0, 1], root: document.querySelector("#headlines-frame")} ), sticky_content_observer: new IntersectionObserver( - (entries, observer) => { + (entries) => { entries.forEach((entry) => { const header = entry.target.closest('.cdm').querySelector(".header"); @@ -44,7 +44,7 @@ const Headlines = { {threshold: [0, 1], root: document.querySelector("#headlines-frame")} ), unpack_observer: new IntersectionObserver( - (entries, observer) => { + (entries) => { entries.forEach((entry) => { if (entry.intersectionRatio > 0) Article.unpack(entry.target); diff --git a/js/PrefFeedTree.js b/js/PrefFeedTree.js index 142060488..4e9cb58a5 100644 --- a/js/PrefFeedTree.js +++ b/js/PrefFeedTree.js @@ -132,8 +132,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b this.inherited(arguments); this.tree.model.store.save(); }, - // eslint-disable-next-line no-unused-vars - getRowClass: function (item, opened) { + getRowClass: function (item /*, opened */) { let rc = (!item.error || item.error === '') ? "dijitTreeRow" : "dijitTreeRow Error"; diff --git a/js/PrefFilterTree.js b/js/PrefFilterTree.js index 1f20da581..4b077259a 100644 --- a/js/PrefFilterTree.js +++ b/js/PrefFilterTree.js @@ -72,7 +72,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree"], functio getIconClass: function (item, opened) { return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "invisible"; }, - getRowClass: function (item, opened) { + getRowClass: function (item /*, opened */) { const enabled = this.model.store.getValue(item, 'enabled'); return enabled ? "dijitTreeRow" : "dijitTreeRow filterDisabled"; diff --git a/js/form/Select.js b/js/form/Select.js index bc352e612..c44674e2a 100755 --- a/js/form/Select.js +++ b/js/form/Select.js @@ -24,7 +24,7 @@ define(["dojo/_base/declare", onItemClick: function(/*item, menu*/) { // }, - _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){ + _setValueAttr: function(/*anything*/ newValue, /*Boolean? priorityChange */){ if (this.attr('data-prevent-value-change') === 'true' && newValue !== '') return; diff --git a/js/form/ValidationMultiSelect.js b/js/form/ValidationMultiSelect.js index a0199c9c6..5a7918271 100644 --- a/js/form/ValidationMultiSelect.js +++ b/js/form/ValidationMultiSelect.js @@ -6,11 +6,11 @@ define(["dojo/_base/declare", "dojo/_base/lang", "dijit/form/MultiSelect", ], function(declare, lang, MultiSelect) { return declare('fox.form.ValidationMultiSelect', [MultiSelect], { - constructor: function(params){ + constructor: function(/* params */) { this.constraints = {}; this.baseClass += ' dijitValidationMultiSelect'; }, - validate: function(/*Boolean*/ isFocused){ + validate: function(/*Boolean isFocused */) { if (this.required && this.attr('value').length === 0) return false; diff --git a/js/form/ValidationTextArea.js b/js/form/ValidationTextArea.js index ced4f8fe7..a71a490c1 100644 --- a/js/form/ValidationTextArea.js +++ b/js/form/ValidationTextArea.js @@ -5,7 +5,7 @@ define(["dojo/_base/declare", "dojo/_base/lang", "dijit/form/SimpleTextarea", "d function(declare, lang, SimpleTextarea, ValidationTextBox) { return declare('fox.form.ValidationTextArea', [SimpleTextarea, ValidationTextBox], { - constructor: function(params){ + constructor: function(/* params */) { this.constraints = {}; this.baseClass += ' dijitValidationTextArea'; }, @@ -19,7 +19,7 @@ define(["dojo/_base/declare", "dojo/_base/lang", "dijit/form/SimpleTextarea", "d if (this.validregexp) { try { new RegExp("/" + value + "/"); - } catch (e) { + } catch { return false; } } diff --git a/js/utility.js b/js/utility.js index e5de5f300..0a9b59454 100644 --- a/js/utility.js +++ b/js/utility.js @@ -7,6 +7,7 @@ window.addEventListener("load", function() { apply_night_mode: function (is_night, link) { console.log("night mode changed to", is_night); + /* global __default_dark_theme, __default_light_theme */ const light_theme = typeof __default_light_theme !== 'undefined' ? __default_light_theme : 'themes/light.css'; const dark_theme = typeof __default_dark_theme !== 'undefined' ? __default_dark_theme : 'themes/night.css'; @@ -35,7 +36,7 @@ window.addEventListener("load", function() { mql.addEventListener("change", () => { UtilityJS.apply_night_mode(mql.matches, link); }); - } catch (e) { + } catch { console.warn("exception while trying to set MQL event listener"); } diff --git a/plugins/hotkeys_force_top/init.js b/plugins/hotkeys_force_top/init.js index 8d6280fc9..242d14ecf 100644 --- a/plugins/hotkeys_force_top/init.js +++ b/plugins/hotkeys_force_top/init.js @@ -1,3 +1,5 @@ +/* global Headlines, require */ + require(['dojo/_base/kernel', 'dojo/ready'], function (dojo, ready) { ready(function () { Headlines.default_force_to_top = true; diff --git a/plugins/share/share_prefs.js b/plugins/share/share_prefs.js index d974af618..e2151065f 100644 --- a/plugins/share/share_prefs.js +++ b/plugins/share/share_prefs.js @@ -1,4 +1,4 @@ -/* global Plugins, Notify, xhr, App */ +/* global __, Plugins, Notify, xhr, App */ Plugins.Share = { clearKeys: function() { diff --git a/plugins/toggle_sidebar/init.js b/plugins/toggle_sidebar/init.js index 9efb06193..6ac74d3c3 100644 --- a/plugins/toggle_sidebar/init.js +++ b/plugins/toggle_sidebar/init.js @@ -1,3 +1,5 @@ +/* global Plugins, Feeds */ + Plugins.Toggle_Sidebar = { toggle: function() { Feeds.toggle(); -- cgit v1.2.3-54-g00ecf From 94924f37b61157d962eb4653ee2635a25e8546ed Mon Sep 17 00:00:00 2001 From: supahgreg Date: Mon, 13 Oct 2025 03:13:36 +0000 Subject: Replace deprecated ESLint styling rules. --- eslint.config.js | 24 ++++++++++++++---------- package-lock.json | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 4902b2f0d..faa97e718 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,5 +1,6 @@ import globals from 'globals'; import js from '@eslint/js'; +import stylistic from '@stylistic/eslint-plugin'; export default [ js.configs.recommended, @@ -18,6 +19,10 @@ export default [ } }, + plugins: { + '@stylistic/js': stylistic + }, + rules: { 'no-undef': 'warn', 'no-unused-vars': 'warn', @@ -30,18 +35,17 @@ export default [ 'no-caller': 'error', 'no-proto': 'error', - 'linebreak-style': ['error', 'unix'], - 'eol-last': 'error', - 'no-trailing-spaces': 'error', - 'no-multiple-empty-lines': ['error', { 'max': 2 }], - - 'keyword-spacing': ['error', { 'after': true, 'before': true }], - 'block-spacing': ['error', 'always'], - 'computed-property-spacing': ['error', 'never'], - 'no-empty': ['error', { 'allowEmptyCatch': true }], - 'max-statements-per-line': ['warn', { 'max': 2 }] + // Stylistic rules (replacing those deprecated in ESLint) + '@stylistic/js/linebreak-style': ['error', 'unix'], + '@stylistic/js/eol-last': 'error', + '@stylistic/js/no-trailing-spaces': 'error', + '@stylistic/js/no-multiple-empty-lines': ['error', { 'max': 2 }], + '@stylistic/js/keyword-spacing': ['error', { 'after': true, 'before': true }], + '@stylistic/js/block-spacing': ['error', 'always'], + '@stylistic/js/computed-property-spacing': ['error', 'never'], + '@stylistic/js/max-statements-per-line': ['warn', { 'max': 2 }] } } ]; diff --git a/package-lock.json b/package-lock.json index e33ceb57a..017cac879 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "tt-rss", "version": "1.0.0", "devDependencies": { + "@stylistic/eslint-plugin": "^5.4.0", "eslint": "^9.37.0", "globals": "^16.4.0", "gulp": "^5.0.1", @@ -248,6 +249,40 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@stylistic/eslint-plugin": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.4.0.tgz", + "integrity": "sha512-UG8hdElzuBDzIbjG1QDwnYH0MQ73YLXDFHgZzB4Zh/YJfnw8XNsloVtytqzx0I2Qky9THSdpTmi8Vjn/pf/Lew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.0", + "@typescript-eslint/types": "^8.44.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -262,6 +297,20 @@ "dev": true, "license": "MIT" }, + "node_modules/@typescript-eslint/types": { + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.0.tgz", + "integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", diff --git a/package.json b/package.json index 7c0815d09..a2d6daefe 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "version": "1.0.0", "type": "module", "devDependencies": { + "@stylistic/eslint-plugin": "^5.4.0", "eslint": "^9.37.0", "globals": "^16.4.0", "gulp": "^5.0.1", -- cgit v1.2.3-54-g00ecf From afc26a8721a58de5c0093ec5521895f0f08f1cf4 Mon Sep 17 00:00:00 2001 From: supahgreg Date: Mon, 13 Oct 2025 04:03:01 +0000 Subject: Remove some unnecessary and/or redundant ESLint rules. --- eslint.config.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index faa97e718..172f0ce21 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -24,17 +24,9 @@ export default [ }, rules: { - 'no-undef': 'warn', - 'no-unused-vars': 'warn', 'no-console': 'off', - 'prefer-const': 'error', - 'no-var': 'warn', - 'eqeqeq': ['error', 'always'], - 'no-caller': 'error', - 'no-proto': 'error', - 'no-empty': ['error', { 'allowEmptyCatch': true }], // Stylistic rules (replacing those deprecated in ESLint) -- cgit v1.2.3-54-g00ecf From 6d893a74ce65ae6a34d68944bb37028e60ee3119 Mon Sep 17 00:00:00 2001 From: supahgreg Date: Mon, 13 Oct 2025 16:44:21 +0000 Subject: Minor workflow config cleanup. --- .github/workflows/javascipt-code-quality.yml | 3 +-- .github/workflows/publish.yml | 12 ++++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/javascipt-code-quality.yml b/.github/workflows/javascipt-code-quality.yml index a6a9e10eb..5ff6cf80e 100644 --- a/.github/workflows/javascipt-code-quality.yml +++ b/.github/workflows/javascipt-code-quality.yml @@ -5,10 +5,9 @@ on: paths: - 'js/**.js' - 'plugins/**.js' + - 'eslint.config.js' - 'package.json' - 'package-lock.json' - - '.eslintrc.*' - - 'eslint.config.*' - '.github/workflows/javascript-code-quality.yml' # Allow manual triggering workflow_dispatch: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1a5e19e22..ce4bb81e9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -24,17 +24,21 @@ permissions: jobs: - test-docker: + docker-code-quality: uses: ./.github/workflows/docker-code-quality.yml - test-php: + javascript-code-quality: + uses: ./.github/workflows/javascript-code-quality.yml + + php-code-quality: uses: ./.github/workflows/php-code-quality.yml publish: name: Publish Docker image ${{ matrix.image.name }} needs: - - test-docker - - test-php + - docker-code-quality + - javascript-code-quality + - php-code-quality runs-on: ubuntu-latest permissions: contents: read -- cgit v1.2.3-54-g00ecf From 602d79c87ee65a4907656a4c2bbcd02cb13e17a0 Mon Sep 17 00:00:00 2001 From: supahgreg Date: Mon, 13 Oct 2025 20:37:12 +0000 Subject: Don't trigger workflows on their own YAML changes. --- .github/workflows/javascipt-code-quality.yml | 1 - .github/workflows/validate-sql.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/.github/workflows/javascipt-code-quality.yml b/.github/workflows/javascipt-code-quality.yml index 5ff6cf80e..531693250 100644 --- a/.github/workflows/javascipt-code-quality.yml +++ b/.github/workflows/javascipt-code-quality.yml @@ -8,7 +8,6 @@ on: - 'eslint.config.js' - 'package.json' - 'package-lock.json' - - '.github/workflows/javascript-code-quality.yml' # Allow manual triggering workflow_dispatch: # Allow other workflows (e.g. Publish) to invoke this one. diff --git a/.github/workflows/validate-sql.yml b/.github/workflows/validate-sql.yml index 470781385..f446c4e25 100644 --- a/.github/workflows/validate-sql.yml +++ b/.github/workflows/validate-sql.yml @@ -5,7 +5,6 @@ on: paths: - 'sql/pgsql/schema.sql' - 'sql/pgsql/migrations/*.sql' - - '.github/workflows/validate-sql.yml' # Allow manual triggering workflow_dispatch: # Allow other workflows (e.g. Publish) to invoke this one. -- cgit v1.2.3-54-g00ecf From 362e2dc28c9d28bc7644df6694b4de78a116da37 Mon Sep 17 00:00:00 2001 From: supahgreg Date: Tue, 14 Oct 2025 03:29:37 +0000 Subject: Fix some more issues related to ESLint 'eqeqeq' changes. --- js/Article.js | 8 ++------ js/FeedTree.js | 4 +++- js/Feeds.js | 4 +++- js/Headlines.js | 55 +++++++++++++++++++++++++++---------------------------- 4 files changed, 35 insertions(+), 36 deletions(-) diff --git a/js/Article.js b/js/Article.js index 88c1cb0e0..67de59aea 100644 --- a/js/Article.js +++ b/js/Article.js @@ -445,17 +445,13 @@ const Article = { row.removeClassName("Unread"); row.addClassName("active"); - PluginHost.run(PluginHost.HOOK_ARTICLE_SET_ACTIVE, row.getAttribute("data-article-id")); + PluginHost.run(PluginHost.HOOK_ARTICLE_SET_ACTIVE, parseInt(row.getAttribute('data-article-id'))); } } }, getActive: function () { const row = document.querySelector("#headlines-frame > div[id*=RROW][class*=active]"); - - if (row) - return row.getAttribute("data-article-id"); - else - return 0; + return row ? parseInt(row.getAttribute('data-article-id')) : 0; }, scrollByPages: function (page_offset) { App.Scrollable.scrollByPages(App.byId("content-insert"), page_offset); diff --git a/js/FeedTree.js b/js/FeedTree.js index 970efe335..01755e7b4 100755 --- a/js/FeedTree.js +++ b/js/FeedTree.js @@ -204,7 +204,9 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co } }, getTooltip: function (item) { - return [item.updated, item.error].filter((x) => x && x !== "").join(" - "); + // TODO: item.error is `[""]` for feeds. Need to look into what's happening on the frontend to cause that-- the backend sends a string. + // For now, just adding a check for `[""]`. + return [item.updated, item.error].filter((x) => x && x !== '' && !(Array.isArray(x) && x.length === 1 && x[0] === '')).join(' - '); }, getIconClass: function (item, opened) { return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feed-icon"; diff --git a/js/Feeds.js b/js/Feeds.js index f99d86943..790b64dd4 100644 --- a/js/Feeds.js +++ b/js/Feeds.js @@ -334,7 +334,9 @@ const Feeds = { setActive: function(id, is_cat) { console.log('setActive', id, is_cat); - id = parseInt(id); + // id might be a tag string, so check if we have something int-ish + if (Number.isInteger(Number(id))) + id = parseInt(id); window.requestIdleCallback(() => { App.Hash.set({ diff --git a/js/Headlines.js b/js/Headlines.js index c27b214dc..9ce575a84 100755 --- a/js/Headlines.js +++ b/js/Headlines.js @@ -59,7 +59,7 @@ const Headlines = { if (m.type === 'attributes' && ['class', 'data-score'].indexOf(m.attributeName) !== -1) { const row = m.target; - const id = row.getAttribute("data-article-id"); + const id = parseInt(row.getAttribute('data-article-id')); if (Headlines.headlines[id]) { const hl = Headlines.headlines[id]; @@ -326,7 +326,7 @@ const Headlines = { const row = rows[i]; if (this.isChildVisible(row)) { - return row.getAttribute("data-article-id"); + return parseInt(row.getAttribute('data-article-id')); } } }, @@ -350,7 +350,8 @@ const Headlines = { const last_row = hsp.previousSibling; // invoke lazy load if last article in buffer is nearly visible OR is active - if (Article.getActive() === last_row.getAttribute("data-article-id") || last_row.offsetTop - 250 <= container.scrollTop + container.offsetHeight) { + if (Article.getActive() === parseInt(last_row.getAttribute('data-article-id')) + || last_row.offsetTop - 250 <= container.scrollTop + container.offsetHeight) { hsp.innerHTML = ` ${__("Loading, please wait...")}`; Headlines.loadMore(); @@ -415,7 +416,7 @@ const Headlines = { Headlines.setCommonClasses(this.headlines.filter((h) => h.id).length); App.findAll("#headlines-frame > div[id*=RROW]").forEach((row) => { - const id = row.getAttribute("data-article-id"); + const id = parseInt(row.getAttribute('data-article-id')); const hl = this.headlines[id]; if (hl) { @@ -1189,7 +1190,7 @@ const Headlines = { App.findAll("#headlines-frame > div[id*=RROW][class*=Selected]").forEach( function (child) { - rv.push(child.getAttribute("data-article-id")); + rv.push(parseInt(child.getAttribute('data-article-id'))); }); // consider active article a honorary member of selected articles @@ -1205,7 +1206,7 @@ const Headlines = { children.forEach(function (child) { if (Element.visible(child)) { - rv.push(child.getAttribute("data-article-id")); + rv.push(parseInt(child.getAttribute('data-article-id'))); } }); @@ -1236,7 +1237,7 @@ const Headlines = { for (let i = 0; i < rows.length; i++) { const row = rows[i]; - const id = row.getAttribute('data-article-id'); + const id = parseInt(row.getAttribute('data-article-id')); if (id === start || id === stop) { if (!collecting) { @@ -1415,14 +1416,16 @@ const Headlines = { menu.addChild(new dijit.MenuItem({ label: __("Open original article"), onClick: function (/* event */) { - Article.openInNewWindow(this.getParent().currentTarget.getAttribute("data-article-id")); + const id = parseInt(this.getParent().currentTarget.getAttribute('data-article-id')); + Article.openInNewWindow(id); } })); menu.addChild(new dijit.MenuItem({ label: __("Display article URL"), onClick: function (/* event */) { - Article.displayUrl(this.getParent().currentTarget.getAttribute("data-article-id")); + const id = parseInt(this.getParent().currentTarget.getAttribute('data-article-id')); + Article.displayUrl(id); } })); @@ -1431,11 +1434,10 @@ const Headlines = { menu.addChild(new dijit.MenuItem({ label: __("Toggle unread"), onClick: function () { + const id = parseInt(this.getParent().currentTarget.getAttribute('data-article-id')); let ids = Headlines.getSelected(); - // cast to string - const id = (this.getParent().currentTarget.getAttribute("data-article-id")) + ""; - ids = ids.length !== 0 && ids.indexOf(id) !== -1 ? ids : [id]; + ids = ids.includes(id) ? ids : [id]; Headlines.selectionToggleUnread({ids: ids, no_error: 1}); } @@ -1444,10 +1446,10 @@ const Headlines = { menu.addChild(new dijit.MenuItem({ label: __("Toggle starred"), onClick: function () { + const id = parseInt(this.getParent().currentTarget.getAttribute('data-article-id')); + let ids = Headlines.getSelected(); - // cast to string - const id = (this.getParent().currentTarget.getAttribute("data-article-id")) + ""; - ids = ids.length !== 0 && ids.indexOf(id) !== -1 ? ids : [id]; + ids = ids.includes(id) ? ids : [id]; Headlines.selectionToggleMarked(ids); } @@ -1456,10 +1458,10 @@ const Headlines = { menu.addChild(new dijit.MenuItem({ label: __("Toggle published"), onClick: function () { + const id = parseInt(this.getParent().currentTarget.getAttribute('data-article-id')); + let ids = Headlines.getSelected(); - // cast to string - const id = (this.getParent().currentTarget.getAttribute("data-article-id")) + ""; - ids = ids.length !== 0 && ids.indexOf(id) !== -1 ? ids : [id]; + ids = ids.includes(id) ? ids : [id]; Headlines.selectionTogglePublished(ids); } @@ -1470,14 +1472,14 @@ const Headlines = { menu.addChild(new dijit.MenuItem({ label: __("Mark above as read"), onClick: function () { - Headlines.catchupRelativeTo(0, this.getParent().currentTarget.getAttribute("data-article-id")); + Headlines.catchupRelativeTo(0, parseInt(this.getParent().currentTarget.getAttribute('data-article-id'))); } })); menu.addChild(new dijit.MenuItem({ label: __("Mark below as read"), onClick: function () { - Headlines.catchupRelativeTo(1, this.getParent().currentTarget.getAttribute("data-article-id")); + Headlines.catchupRelativeTo(1, parseInt(this.getParent().currentTarget.getAttribute('data-article-id'))); } })); @@ -1499,12 +1501,10 @@ const Headlines = { label: name, labelId: bare_id, onClick: function () { + const id = parseInt(this.ownerMenu.currentTarget.getAttribute('data-article-id')); let ids = Headlines.getSelected(); - // cast to string - const id = (this.getParent().ownerMenu.currentTarget.getAttribute("data-article-id")) + ""; - - ids = ids.length !== 0 && ids.indexOf(id) !== -1 ? ids : [id]; + ids = ids.includes(id) ? ids : [id]; Headlines.selectionAssignLabel(this.labelId, ids); } @@ -1514,11 +1514,10 @@ const Headlines = { label: name, labelId: bare_id, onClick: function () { - let ids = Headlines.getSelected(); - // cast to string - const id = (this.getParent().ownerMenu.currentTarget.getAttribute("data-article-id")) + ""; + const id = parseInt(this.ownerMenu.currentTarget.getAttribute('data-article-id')); - ids = ids.length !== 0 && ids.indexOf(id) !== -1 ? ids : [id]; + let ids = Headlines.getSelected(); + ids = ids.includes(id) ? ids : [id]; Headlines.selectionRemoveLabel(this.labelId, ids); } -- cgit v1.2.3-54-g00ecf From 98f0035a9598ba2d50e00b9d7302ecb501a84900 Mon Sep 17 00:00:00 2001 From: supahgreg Date: Tue, 14 Oct 2025 04:03:24 +0000 Subject: minor: fix a labels issue introduced in 362e2dc28c9d28bc7644df6694b4de78a116da37 --- js/Headlines.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/Headlines.js b/js/Headlines.js index 9ce575a84..7ce1e63ba 100755 --- a/js/Headlines.js +++ b/js/Headlines.js @@ -1501,7 +1501,7 @@ const Headlines = { label: name, labelId: bare_id, onClick: function () { - const id = parseInt(this.ownerMenu.currentTarget.getAttribute('data-article-id')); + const id = parseInt(this.getParent().ownerMenu.currentTarget.getAttribute('data-article-id')); let ids = Headlines.getSelected(); ids = ids.includes(id) ? ids : [id]; @@ -1514,7 +1514,7 @@ const Headlines = { label: name, labelId: bare_id, onClick: function () { - const id = parseInt(this.ownerMenu.currentTarget.getAttribute('data-article-id')); + const id = parseInt(this.getParent().ownerMenu.currentTarget.getAttribute('data-article-id')); let ids = Headlines.getSelected(); ids = ids.includes(id) ? ids : [id]; -- cgit v1.2.3-54-g00ecf From 8b46ab31a96b6b6129f624719ac33ddf761805f1 Mon Sep 17 00:00:00 2001 From: supahgreg Date: Tue, 14 Oct 2025 04:36:36 +0000 Subject: Fix some more issues related to ESLint 'eqeqeq' changes (again). --- js/App.js | 2 +- js/PrefHelpers.js | 2 +- js/common.js | 21 +++++++++++---------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/js/App.js b/js/App.js index 571e3373f..e305ce998 100644 --- a/js/App.js +++ b/js/App.js @@ -912,7 +912,7 @@ const App = { if (action_name) { const action_func = this.hotkey_actions[action_name]; - if (action_func !== null) { + if (typeof action_func === 'function') { action_func(event); event.stopPropagation(); return false; diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index fbe1d37f5..cf3f03bf2 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -455,7 +455,7 @@ const Helpers = { Notify.progress("Loading, please wait..."); xhr.json("backend.php", {op: "Pref_Prefs", method: "uninstallPlugin", plugin: plugin}, (reply) => { - if (reply && reply.status === 1) + if (reply?.status === true) Helpers.Plugins.reload(); else { Notify.error("Plugin uninstallation failed."); diff --git a/js/common.js b/js/common.js index 60b2f4c9c..48a42b95a 100755 --- a/js/common.js +++ b/js/common.js @@ -217,21 +217,22 @@ const xhr = { console.log('xhr.json', '<<<', obj, (new Date().getTime() - xhr._ts) + " ms"); - if (obj && typeof App !== "undefined") - if (!App.handleRpcJson(obj)) { + if (obj && typeof App !== 'undefined') { + if (!App.handleRpcJson(obj)) { - if (typeof failed === 'function') - failed(obj); + if (typeof failed === 'function') + failed(obj); - reject(obj); - return; + reject(obj); + return; + } } - if (typeof complete === 'function') - complete(obj); + if (typeof complete === 'function') + complete(obj); - resolve(obj); - })); + resolve(obj); + })); } }; -- cgit v1.2.3-54-g00ecf