Browse Source

eslint-related fixes; move a few things from global context to App

Andrew Dolgov 2 months ago
parent
commit
d01ad09800
11 changed files with 132 additions and 138 deletions
  1. 3 3
      .eslintrc.js
  2. 4 4
      classes/article.php
  3. 8 8
      classes/feeds.php
  4. 6 6
      classes/pref/feeds.php
  5. 49 21
      js/App.js
  6. 21 5
      js/Article.js
  7. 10 8
      js/CommonDialogs.js
  8. 3 2
      js/CommonFilters.js
  9. 4 4
      js/FeedTree.js
  10. 12 11
      js/Headlines.js
  11. 12 66
      js/common.js

+ 3 - 3
.eslintrc.js

@@ -91,7 +91,7 @@ module.exports = {
         "max-statements-per-line": [ "warn", { "max" : 2 } ],
         "multiline-comment-style": "off",
         "multiline-ternary": "off",
-        "new-cap": "error",
+        "new-cap": "warn",
         "new-parens": "error",
         "newline-after-var": "off",
         "newline-before-return": "off",
@@ -150,7 +150,7 @@ module.exports = {
         "no-negated-condition": "off",
         "no-negated-in-lhs": "error",
         "no-nested-ternary": "error",
-        "no-new": "error",
+        "no-new": "warn",
         "no-new-func": "error",
         "no-new-object": "off",
         "no-new-require": "error",
@@ -162,7 +162,7 @@ module.exports = {
         "no-process-env": "error",
         "no-process-exit": "error",
         "no-proto": "error",
-        "no-prototype-builtins": "error",
+        "no-prototype-builtins": "warn",
         "no-restricted-globals": "error",
         "no-restricted-imports": "error",
         "no-restricted-modules": "error",

+ 4 - 4
classes/article.php

@@ -94,7 +94,7 @@ class Article extends Handler_Protected {
 						":id" => $ref_id];
 					$sth->execute($params);
 				}
-				
+
 				$sth = $pdo->prepare("UPDATE ttrss_user_entries SET published = true,
 						last_published = NOW() WHERE
 						int_id = ? AND owner_uid = ?");
@@ -393,7 +393,7 @@ class Article extends Handler_Protected {
 #				$entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\" rel=\"noopener noreferrer\">" .
 #					$filename . " (" . $ctype . ")" . "</a>";
 
-				$entry = "<div onclick=\"popupOpenUrl('".htmlspecialchars($url)."')\"
+				$entry = "<div onclick=\"Article.popupOpenUrl('".htmlspecialchars($url)."')\"
 					dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
 
 				array_push($entries_html, $entry);
@@ -473,7 +473,7 @@ class Article extends Handler_Protected {
 				else
 					$filename = "";
 
-				$rv .= "<div onclick='popupOpenUrl(\"".htmlspecialchars($entry["url"])."\")'
+				$rv .= "<div onclick='Article.popupOpenUrl(\"".htmlspecialchars($entry["url"])."\")'
 					dojoType=\"dijit.MenuItem\">".$filename . $title."</div>";
 
 			};
@@ -583,7 +583,7 @@ class Article extends Handler_Protected {
 
 		return "<div class='article-note $note_class'>
 			<i class='material-icons'>note</i>
-			<div $onclick class='body'>$note</div>			
+			<div $onclick class='body'>$note</div>
 			</div>";
 
 		return $str;

+ 8 - 8
classes/feeds.php

@@ -701,12 +701,12 @@ class Feeds extends Handler_Protected {
 		print "<section>";
 		print "<label>
 			<label class='checkbox'><input type='checkbox' name='need_auth' dojoType='dijit.form.CheckBox' id='feedDlg_loginCheck'
-					onclick='displayIfChecked(this, \"feedDlg_loginContainer\")'>
+					onclick='App.displayIfChecked(this, \"feedDlg_loginContainer\")'>
 				".__('This feed requires authentication.')."</label>";
 		print "</section>";
 
 		print "<footer>";
-		print "<button dojoType='dijit.form.Button' class='alt-primary' type='submit' 
+		print "<button dojoType='dijit.form.Button' class='alt-primary' type='submit'
 				onclick=\"return dijit.byId('feedAddDlg').execute()\">".__('Subscribe')."</button>";
 
 		print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('feedAddDlg').hide()\">".__('Cancel')."</button>";
@@ -1337,7 +1337,7 @@ class Feeds extends Handler_Protected {
 			return 0;
 		} else if ($cat == -2) {
 
-			$sth = $pdo->prepare("SELECT COUNT(DISTINCT article_id) AS unread 
+			$sth = $pdo->prepare("SELECT COUNT(DISTINCT article_id) AS unread
 				FROM ttrss_user_entries ue, ttrss_user_labels2 l
 				WHERE article_id = ref_id AND unread IS true AND ue.owner_uid = :uid");
 			$sth->execute(["uid" => $owner_uid]);
@@ -1373,8 +1373,8 @@ class Feeds extends Handler_Protected {
 
 		$pdo = Db::pdo();
 
-		$sth = $pdo->prepare("SELECT SUM(CASE WHEN unread THEN 1 ELSE 0 END) AS count 
-			FROM ttrss_user_entries ue 
+		$sth = $pdo->prepare("SELECT SUM(CASE WHEN unread THEN 1 ELSE 0 END) AS count
+			FROM ttrss_user_entries ue
 			WHERE ue.owner_uid = ?");
 
 		$sth->execute([$user_id]);
@@ -1468,7 +1468,7 @@ class Feeds extends Handler_Protected {
 			}
 
 			if (DB_TYPE == "pgsql") {
-				$test_sth = $pdo->prepare("select $search_query_part 
+				$test_sth = $pdo->prepare("select $search_query_part
 					FROM ttrss_entries, ttrss_user_entries WHERE id = ref_id limit 1");
 
 				try {
@@ -2272,9 +2272,9 @@ class Feeds extends Handler_Protected {
 						$label_id = Labels::find_id($commandpair[1], $_SESSION["uid"]);
 
 						if ($label_id) {
-							array_push($query_keywords, "($not 
+							array_push($query_keywords, "($not
 								(ttrss_entries.id IN (
-									SELECT article_id FROM ttrss_user_labels2 WHERE 
+									SELECT article_id FROM ttrss_user_labels2 WHERE
 										label_id = ".$pdo->quote($label_id).")))");
 						} else {
 							array_push($query_keywords, "(false)");

+ 6 - 6
classes/pref/feeds.php

@@ -554,7 +554,7 @@ class Pref_Feeds extends Handler_Protected {
 			$last_error = $row["last_error"];
 
 			if ($last_error) {
-				print "&nbsp;<i class=\"material-icons\" 
+				print "&nbsp;<i class=\"material-icons\"
 					title=\"".htmlspecialchars($last_error)."\">error</i>";
 			}
 
@@ -676,7 +676,7 @@ class Pref_Feeds extends Handler_Protected {
 			$auth_checked = $auth_enabled ? 'checked' : '';
 			print "<label class='checkbox'>
 				<input type='checkbox' $auth_checked name='need_auth' dojoType='dijit.form.CheckBox' id='feedEditDlg_loginCheck'
-						onclick='displayIfChecked(this, \"feedEditDlg_loginContainer\")'>
+						onclick='App.displayIfChecked(this, \"feedEditDlg_loginContainer\")'>
 					".__('This feed requires authentication.')."</label>";
 
 			print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Options').'">';
@@ -1172,7 +1172,7 @@ class Pref_Feeds extends Handler_Protected {
 	function index() {
 
 		print "<div dojoType='dijit.layout.AccordionContainer' region='center'>";
-		print "<div style='padding : 0px' dojoType='dijit.layout.AccordionPane' 
+		print "<div style='padding : 0px' dojoType='dijit.layout.AccordionPane'
 			title=\"<i class='material-icons'>rss_feed</i> ".__('Feeds')."\">";
 
 		$sth = $this->pdo->prepare("SELECT COUNT(id) AS num_errors
@@ -1307,7 +1307,7 @@ class Pref_Feeds extends Handler_Protected {
 
 		print "</div>"; # feeds pane
 
-		print "<div dojoType='dijit.layout.AccordionPane' 
+		print "<div dojoType='dijit.layout.AccordionPane'
 			title='<i class=\"material-icons\">import_export</i> ".__('OPML')."'>";
 
 		print "<h3>" . __("Using OPML you can export and import your feeds, filters, labels and Tiny Tiny RSS settings.") . "</h3>";
@@ -1360,7 +1360,7 @@ class Pref_Feeds extends Handler_Protected {
 
 		print "</div>"; # pane
 
-		print "<div dojoType=\"dijit.layout.AccordionPane\" 
+		print "<div dojoType=\"dijit.layout.AccordionPane\"
 			title=\"<i class='material-icons'>share</i> ".__('Published & shared articles / Generated feeds')."\">";
 
 		print "<h3>" . __('Published articles can be subscribed by anyone who knows the following URL:') . "</h3>";
@@ -1672,7 +1672,7 @@ class Pref_Feeds extends Handler_Protected {
 
 		print "<fieldset class='narrow'>
 			<label class='checkbox'><input type='checkbox' name='need_auth' dojoType='dijit.form.CheckBox'
-					onclick='displayIfChecked(this, \"feedDlg_loginContainer\")'> ".
+					onclick='App.displayIfChecked(this, \"feedDlg_loginContainer\")'> ".
 				__('Feeds require authentication.')."</label></div>";
 		print "</fieldset>";
 

+ 49 - 21
js/App.js

@@ -1,8 +1,8 @@
 'use strict';
 
-/* global __, ngettext, Article, Headlines, Filters */
+/* global __, Article, Ajax, Headlines, Filters */
 /* global xhrPost, xhrJson, dojo, dijit, PluginHost, Notify, $$, Feeds, Cookie */
-/* global CommonDialogs, CommonFilters, Plugins */
+/* global CommonDialogs, Plugins, Effect */
 
 const App = {
    _initParams: [],
@@ -12,8 +12,10 @@ const App = {
 	hotkey_prefix_timeout: 0,
    global_unread: -1,
    _widescreen_mode: false,
+   _loading_progress: 0,
    hotkey_actions: {},
    is_prefs: false,
+   LABEL_BASE_INDEX: -1024,
    Scrollable: {
 		scrollByPages: function (elem, page_offset) {
 			if (!elem) return;
@@ -46,8 +48,14 @@ const App = {
 			return elem.offsetTop + elem.offsetHeight <= ctr.scrollTop + ctr.offsetHeight &&
 				elem.offsetTop >= ctr.scrollTop;
 		}
-	},
-	getInitParam: function(k) {
+   },
+   label_to_feed_id: function(label) {
+      return this.LABEL_BASE_INDEX - 1 - Math.abs(label);
+   },
+   feed_to_label_id: function(feed) {
+      return this.LABEL_BASE_INDEX - 1 + Math.abs(feed);
+   },
+   getInitParam: function(k) {
 		return this._initParams[k];
 	},
 	setInitParam: function(k, v) {
@@ -130,12 +138,12 @@ const App = {
 		return this._rpc_seq;
 	},
 	setLoadingProgress: function(p) {
-		loading_progress += p;
+		this._loading_progress += p;
 
 		if (dijit.byId("loading_bar"))
-			dijit.byId("loading_bar").update({progress: loading_progress});
+			dijit.byId("loading_bar").update({progress: this._loading_progress});
 
-		if (loading_progress >= 90) {
+		if (this._loading_progress >= 90) {
 			$("overlay").hide();
 		}
 
@@ -223,8 +231,27 @@ const App = {
 		$$("#" + root + " *").each(function (i) {
 			i.parentNode ? i.parentNode.removeChild(i) : true;
 		});
-	},
-	helpDialog: function(topic) {
+   },
+   // htmlspecialchars()-alike for headlines data-content attribute
+   escapeHtml: function(text) {
+      const map = {
+         '&': '&amp;',
+         '<': '&lt;',
+         '>': '&gt;',
+         '"': '&quot;',
+         "'": '&#039;'
+      };
+
+      return text.replace(/[&<>"']/g, function(m) { return map[m]; });
+   },
+   displayIfChecked: function(checkbox, elemId) {
+      if (checkbox.checked) {
+         Effect.Appear(elemId, {duration : 0.5});
+      } else {
+         Effect.Fade(elemId, {duration : 0.5});
+      }
+   },
+   helpDialog: function(topic) {
 		const query = "backend.php?op=backend&method=help&topic=" + encodeURIComponent(topic);
 
 		if (dijit.byId("helpDlg"))
@@ -418,7 +445,7 @@ const App = {
 				if (params.hasOwnProperty(k)) {
 					switch (k) {
 						case "label_base_index":
-							LABEL_BASE_INDEX = parseInt(params[k]);
+							this.LABEL_BASE_INDEX = parseInt(params[k]);
 							break;
 						case "cdm_auto_catchup":
 							if (params[k] == 1) {
@@ -429,16 +456,17 @@ const App = {
 						case "hotkeys":
 							// filter mnemonic definitions (used for help panel) from hotkeys map
 							// i.e. *(191)|Ctrl-/ -> *(191)
-
-							const tmp = [];
-							for (const sequence in params[k][1]) {
-								if (params[k][1].hasOwnProperty(sequence)) {
-									const filtered = sequence.replace(/\|.*$/, "");
-									tmp[filtered] = params[k][1][sequence];
-								}
-							}
-
-							params[k][1] = tmp;
+                     {
+                        const tmp = [];
+                        for (const sequence in params[k][1]) {
+                           if (params[k][1].hasOwnProperty(sequence)) {
+                              const filtered = sequence.replace(/\|.*$/, "");
+                              tmp[filtered] = params[k][1][sequence];
+                           }
+                        }
+
+                        params[k][1] = tmp;
+                     }
 							break;
 					}
 
@@ -587,7 +615,7 @@ const App = {
       ['MutationObserver'].each(function(wf) {
          if (!(wf in window)) {
             errorMsg = `Browser feature check failed: <code>window.${wf}</code> not found.`;
-            throw $break;
+            throw new Error(errorMsg);
          }
       });
 

+ 21 - 5
js/Article.js

@@ -1,6 +1,6 @@
 'use strict'
 
-/* global __, ngettext, App, Headlines, xhrPost, xhrJson, dojo, dijit, PluginHost, Notify, $$, escapeHtml */
+/* global __, ngettext, App, Headlines, xhrPost, xhrJson, dojo, dijit, PluginHost, Notify, $$, Ajax */
 
 const Article = {
 	_scroll_reset_timeout: false,
@@ -86,6 +86,22 @@ const Article = {
 			}
 		}
 	},
+	popupOpenUrl: function(url) {
+		const w = window.open("");
+
+		w.opener = null;
+		w.location = url;
+	},
+	/* popupOpenArticle: function(id) {
+		const w = window.open("",
+			"ttrss_article_popup",
+			"height=900,width=900,resizable=yes,status=no,location=no,menubar=no,directories=no,scrollbars=yes,toolbar=no");
+
+		if (w) {
+			w.opener = null;
+			w.location = "backend.php?op=article&method=view&mode=raw&html=1&zoom=1&id=" + id + "&csrf_token=" + App.getInitParam("csrf_token");
+		}
+	}, */
 	cdmUnsetActive: function (event) {
 		const row = $("RROW-" + Article.getActive());
 
@@ -157,14 +173,14 @@ const Article = {
 				comments_msg = hl.num_comments + " " + ngettext("comment", "comments", hl.num_comments)
 			}
 
-			comments = `<a href="${escapeHtml(hl.comments ? hl.comments : hl.link)}">(${comments_msg})</a>`;
+			comments = `<a href="${App.escapeHtml(hl.comments ? hl.comments : hl.link)}">(${comments_msg})</a>`;
 		}
 
 		return comments;
 	},
 	formatOriginallyFrom: function(hl) {
 		return hl.orig_feed ? `<span>
-				${__('Originally from:')} <a target="_blank" rel="noopener noreferrer" href="${escapeHtml(hl.orig_feed[1])}">${hl.orig_feed[0]}</a>
+				${__('Originally from:')} <a target="_blank" rel="noopener noreferrer" href="${App.escapeHtml(hl.orig_feed[1])}">${hl.orig_feed[0]}</a>
 				</span>` : "";
 	},
 	unpack: function(row) {
@@ -213,8 +229,8 @@ const Article = {
 					<div class="header">
 						<div class="row">
 							<div class="title"><a target="_blank" rel="noopener noreferrer"
-								title="${escapeHtml(hl.title)}"
-								href="${escapeHtml(hl.link)}">${hl.title}</a></div>
+								title="${App.escapeHtml(hl.title)}"
+								href="${App.escapeHtml(hl.link)}">${hl.title}</a></div>
 							<div class="date">${hl.updated_long}</div>
 						</div>
 						<div class="row">

+ 10 - 8
js/CommonDialogs.js

@@ -46,18 +46,20 @@ const	CommonDialogs = {
 				xhr.onload = function () {
 					switch (parseInt(this.responseText)) {
 						case 0:
-							Notify.info("Upload complete.");
+							{
+								Notify.info("Upload complete.");
 
-							if (App.isPrefs())
-								dijit.byId("feedTree").reload();
-							else
-								Feeds.reload();
+								if (App.isPrefs())
+									dijit.byId("feedTree").reload();
+								else
+									Feeds.reload();
 
-							const icon = $$(".feed-editor-icon")[0];
+								const icon = $$(".feed-editor-icon")[0];
 
-							if (icon)
-								icon.src = icon.src.replace(/\?[0-9]+$/, "?" + new Date().getTime());
+								if (icon)
+									icon.src = icon.src.replace(/\?[0-9]+$/, "?" + new Date().getTime());
 
+							}
 							break;
 						case 1:
 							Notify.error("Upload failed: icon is too big.");

+ 3 - 2
js/CommonFilters.js

@@ -1,7 +1,7 @@
 'use strict'
 
-/* global __, ngettext, App, Article, Lists */
-/* global xhrPost, xhrJson, dojo, dijit, Notify, $$, Feeds */
+/* global __, App, Article, Lists, Effect */
+/* global xhrPost, dojo, dijit, Notify, $$, Feeds */
 
 const	Filters = {
 	filterDlgCheckAction: function(sender) {
@@ -337,6 +337,7 @@ const	Filters = {
 		});
 
 		if (!App.isPrefs()) {
+			/* global getSelectionText */
 			const selectedText = getSelectionText();
 
 			const lh = dojo.connect(dialog, "onLoad", function () {

+ 4 - 4
js/FeedTree.js

@@ -1,4 +1,4 @@
-/* global dojo, dijit, define, App, Feeds, CommonDialogs, LABEL_BASE_INDEX */
+/* global dojo, dijit, define, App, Feeds, CommonDialogs */
 
 define(["dojo/_base/declare", "dojo/dom-construct", "dijit/Tree", "dijit/Menu"], function (declare, domConstruct) {
 
@@ -34,7 +34,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dijit/Tree", "dijit/Menu"],
 			const id = args.item.id[0];
 			const bare_id = parseInt(id.substr(id.indexOf(':')+1));
 
-			if (bare_id < LABEL_BASE_INDEX) {
+			if (bare_id < App.LABEL_BASE_INDEX) {
 				const label = dojo.create('i', { className: "material-icons icon icon-label", innerHTML: "label" });
 
 				//const fg_color = args.item.fg_color[0];
@@ -164,9 +164,9 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dijit/Tree", "dijit/Menu"],
 			if (item.auxcounter > 0) rc += " Has_Aux";
 			if (item.markedcounter > 0) rc += " Has_Marked";
 			if (item.updates_disabled > 0) rc += " UpdatesDisabled";
-			if (item.bare_id >= LABEL_BASE_INDEX && item.bare_id < 0 && !is_cat || item.bare_id == 0 && !is_cat) rc += " Special";
+			if (item.bare_id >= App.LABEL_BASE_INDEX && item.bare_id < 0 && !is_cat || item.bare_id == 0 && !is_cat) rc += " Special";
 			if (item.bare_id == -1 && is_cat) rc += " AlwaysVisible";
-			if (item.bare_id < LABEL_BASE_INDEX) rc += " Label";
+			if (item.bare_id < App.LABEL_BASE_INDEX) rc += " Label";
 
 			return rc;
 		},

+ 12 - 11
js/Headlines.js

@@ -1,7 +1,7 @@
 'use strict';
 
-/* global __, ngettext, Article, App, escapeHtml */
-/* global xhrPost, xhrJson, dojo, dijit, PluginHost, Notify, $$, Feeds */
+/* global __, ngettext, Article, App */
+/* global xhrPost, dojo, dijit, PluginHost, Notify, $$, Feeds */
 /* global CommonDialogs */
 
 const Headlines = {
@@ -402,7 +402,7 @@ const Headlines = {
 		if (headlines.vfeed_group_enabled) row_class += " vgrlf";
 
 		if (headlines.vfeed_group_enabled && hl.feed_title && this.vgroup_last_feed != hl.feed_id) {
-			let vgrhdr = `<div data-feed-id='${hl.feed_id}' class='feed-title'>
+			const vgrhdr = `<div data-feed-id='${hl.feed_id}' class='feed-title'>
 				<div style='float : right'>${hl.feed_icon}</div>
 				<a class="title" href="#" onclick="Feeds.open({feed:${hl.feed_id}})">${hl.feed_title}
 				<a class="catchup" title="${__('mark feed as read')}" onclick="Feeds.catchupFeedInGroup(${hl.feed_id})" href="#"><i class="icon-done material-icons">done_all</i></a>
@@ -426,9 +426,9 @@ const Headlines = {
 						id="RROW-${hl.id}"
 						data-article-id="${hl.id}"
 						data-orig-feed-id="${hl.feed_id}"
-						data-content="${escapeHtml(hl.content)}"
+						data-content="${App.escapeHtml(hl.content)}"
 						data-score="${hl.score}"
-						data-article-title="${escapeHtml(hl.title)}"
+						data-article-title="${App.escapeHtml(hl.title)}"
 						onmouseover="Article.mouseIn(${hl.id})"
 						onmouseout="Article.mouseOut(${hl.id})">
 						<div class="header-sticky-guard"></div>
@@ -440,7 +440,7 @@ const Headlines = {
 							</div>
 
 							<span onclick="return Headlines.click(event, ${hl.id});" data-article-id="${hl.id}" class="titleWrap hlMenuAttach">
-								<a class="title" title="${escapeHtml(hl.title)}" target="_blank" rel="noopener noreferrer" href="${escapeHtml(hl.link)}">
+								<a class="title" title="${App.escapeHtml(hl.title)}" target="_blank" rel="noopener noreferrer" href="${App.escapeHtml(hl.link)}">
 									${hl.title}</a>
 								<span class="author">${hl.author}</span>
 								${hl.labels}
@@ -457,7 +457,7 @@ const Headlines = {
 							<div class="right">
 								<i class="material-icons icon-score" title="${hl.score}" onclick="Article.setScore(${hl.id}, this)">${Article.getScorePic(hl.score)}</i>
 
-								<span style="cursor : pointer" title="${escapeHtml(hl.feed_title)}" onclick="Feeds.open({feed:${hl.feed_id}})">
+								<span style="cursor : pointer" title="${App.escapeHtml(hl.feed_title)}" onclick="Feeds.open({feed:${hl.feed_id}})">
 									${hl.feed_icon}</span>
 							</div>
 
@@ -497,7 +497,7 @@ const Headlines = {
 				data-orig-feed-id="${hl.feed_id}"
 				data-article-id="${hl.id}"
 				data-score="${hl.score}"
-				data-article-title="${escapeHtml(hl.title)}"
+				data-article-title="${App.escapeHtml(hl.title)}"
 				onmouseover="Article.mouseIn(${hl.id})"
 				onmouseout="Article.mouseOut(${hl.id})">
 			<div class="left">
@@ -507,7 +507,7 @@ const Headlines = {
 			</div>
 			<div onclick="return Headlines.click(event, ${hl.id})" class="title">
 				<span data-article-id="${hl.id}" class="hl-content hlMenuAttach">
-					<a class="title" href="${escapeHtml(hl.link)}">${hl.title} <span class="preview">${hl.content_preview}</span></a>
+					<a class="title" href="${App.escapeHtml(hl.link)}">${hl.title} <span class="preview">${hl.content_preview}</span></a>
 					<span class="author">${hl.author}</span>
 					${hl.labels}
 				</span>
@@ -520,7 +520,7 @@ const Headlines = {
 			</div>
 			<div class="right">
 				<i class="material-icons icon-score" title="${hl.score}" onclick="Article.setScore(${hl.id}, this)">${Article.getScorePic(hl.score)}</i>
-				<span onclick="Feeds.open({feed:${hl.feed_id}})" style="cursor : pointer" title="${escapeHtml(hl.feed_title)}">${hl.feed_icon}</span>
+				<span onclick="Feeds.open({feed:${hl.feed_id}})" style="cursor : pointer" title="${App.escapeHtml(hl.feed_title)}">${hl.feed_icon}</span>
 			</div>
 			</div>
 		`;
@@ -685,7 +685,7 @@ const Headlines = {
 
 				console.log("no headlines received, infscroll_disabled=", Feeds.infscroll_disabled, 'first_id_changed=', first_id_changed);
 
-				let hsp = $("headlines-spacer");
+				const hsp = $("headlines-spacer");
 
 				if (hsp) {
 					if (first_id_changed) {
@@ -1207,6 +1207,7 @@ const Headlines = {
 		}
 	},
 	onActionChanged: function (elem) {
+		// eslint-disable-next-line no-eval
 		eval(elem.value);
 		elem.attr('value', 'false');
 	},

+ 12 - 66
js/common.js

@@ -1,21 +1,19 @@
-'use strict'
-/* global dijit, __ */
+'use strict';
 
-let LABEL_BASE_INDEX = -1024; /* not const because it's assigned at least once (by backend) */
-let loading_progress = 0;
+/* global dijit, __, App, Ajax */
 
 /* error reporting shim */
-
 // TODO: deprecated; remove
-function exception_error(e, e_compat, filename, lineno, colno) {
+/* function exception_error(e, e_compat, filename, lineno, colno) {
 	if (typeof e == "string")
 		e = e_compat;
 
 	App.Error.report(e, {filename: filename, lineno: lineno, colno: colno});
-}
+} */
 
 /* xhr shorthand helpers */
 
+/* exported xhrPost */
 function xhrPost(url, params, complete) {
 	console.log("xhrPost:", params);
 
@@ -31,6 +29,7 @@ function xhrPost(url, params, complete) {
 	});
 }
 
+/* exported xhrJson */
 function xhrJson(url, params, complete) {
 	return new Promise((resolve, reject) => {
 		return xhrPost(url, params).then((reply) => {
@@ -58,6 +57,7 @@ Array.prototype.remove = function(s) {
 
 /* common helpers not worthy of separate Dojo modules */
 
+/* exported Lists */
 const Lists = {
 	onRowChecked: function(elem) {
 		const checked = elem.domNode ? elem.attr("checked") : elem.checked;
@@ -87,7 +87,7 @@ const Lists = {
 	},
 };
 
-// noinspection JSUnusedGlobalSymbols
+/* exported Tables */
 const Tables = {
 	onRowChecked: function(elem) {
 		// account for dojo checkboxes
@@ -133,6 +133,7 @@ const Tables = {
 	}
 };
 
+/* exported Cookie */
 const Cookie = {
 	set: function (name, value, lifetime) {
 		const d = new Date();
@@ -152,12 +153,12 @@ const Cookie = {
 	},
 	delete: function(name) {
 		const expires = "expires=Thu, 01-Jan-1970 00:00:01 GMT";
-		document.cookie = name + "=" + "" + "; " + expires;
+		document.cookie = name + "=; " + expires;
 	}
 };
 
 /* runtime notifications */
-
+/* exported Notify */
 const Notify = {
 	KIND_GENERIC: 0,
 	KIND_INFO: 1,
@@ -237,30 +238,8 @@ const Notify = {
 	}
 };
 
-// noinspection JSUnusedGlobalSymbols
-function displayIfChecked(checkbox, elemId) {
-	if (checkbox.checked) {
-		Effect.Appear(elemId, {duration : 0.5});
-	} else {
-		Effect.Fade(elemId, {duration : 0.5});
-	}
-}
-
-/* function strip_tags(s) {
-	return s.replace(/<\/?[^>]+(>|$)/g, "");
-} */
-
-// noinspection JSUnusedGlobalSymbols
-function label_to_feed_id(label) {
-	return LABEL_BASE_INDEX - 1 - Math.abs(label);
-}
-
-// noinspection JSUnusedGlobalSymbols
-function feed_to_label_id(feed) {
-	return LABEL_BASE_INDEX - 1 + Math.abs(feed);
-}
-
 // http://stackoverflow.com/questions/6251937/how-to-get-selecteduser-highlighted-text-in-contenteditable-element-and-replac
+/* exported getSelectionText */
 function getSelectionText() {
 	let text = "";
 
@@ -281,36 +260,3 @@ function getSelectionText() {
 
 	return text.stripTags();
 }
-
-// noinspection JSUnusedGlobalSymbols
-function popupOpenUrl(url) {
-	const w = window.open("");
-
-	w.opener = null;
-	w.location = url;
-}
-
-// noinspection JSUnusedGlobalSymbols
-function popupOpenArticle(id) {
-	const w = window.open("",
-		"ttrss_article_popup",
-		"height=900,width=900,resizable=yes,status=no,location=no,menubar=no,directories=no,scrollbars=yes,toolbar=no");
-
-	if (w) {
-		w.opener = null;
-		w.location = "backend.php?op=article&method=view&mode=raw&html=1&zoom=1&id=" + id + "&csrf_token=" + App.getInitParam("csrf_token");
-	}
-}
-
-// htmlspecialchars()-alike for headlines data-content attribute
-function escapeHtml(text) {
-	const map = {
-		'&': '&amp;',
-		'<': '&lt;',
-		'>': '&gt;',
-		'"': '&quot;',
-		"'": '&#039;'
-	};
-
-	return text.replace(/[&<>"']/g, function(m) { return map[m]; });
-}