summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
Diffstat (limited to 'js')
-rw-r--r--js/AppBase.js421
-rw-r--r--js/Article.js339
-rw-r--r--js/ArticleCache.js29
-rw-r--r--js/CommonDialogs.js449
-rw-r--r--js/CommonFilters.js393
-rwxr-xr-xjs/FeedTree.js14
-rw-r--r--js/Feeds.js642
-rwxr-xr-xjs/Headlines.js1215
-rw-r--r--js/PluginHost.js6
-rw-r--r--js/PrefFeedTree.js302
-rw-r--r--js/PrefFilterTree.js184
-rw-r--r--js/PrefHelpers.js230
-rw-r--r--js/PrefLabelTree.js124
-rw-r--r--js/PrefUsers.js122
-rwxr-xr-xjs/common.js311
-rw-r--r--js/feedlist.js532
-rwxr-xr-xjs/functions.js1521
-rwxr-xr-xjs/prefs.js1345
-rw-r--r--js/tt-rss.js1357
-rwxr-xr-xjs/viewfeed.js1656
20 files changed, 5405 insertions, 5787 deletions
diff --git a/js/AppBase.js b/js/AppBase.js
new file mode 100644
index 000000000..9ab2f507e
--- /dev/null
+++ b/js/AppBase.js
@@ -0,0 +1,421 @@
+'use strict'
+/* global __, ngettext */
+define(["dojo/_base/declare"], function (declare) {
+ return declare("fox.AppBase", null, {
+ _initParams: [],
+ _rpc_seq: 0,
+ hotkey_prefix: 0,
+ hotkey_prefix_pressed: false,
+ hotkey_prefix_timeout: 0,
+ constructor: function() {
+ window.onerror = this.Error.onWindowError;
+ },
+ getInitParam: function(k) {
+ return this._initParams[k];
+ },
+ setInitParam: function(k, v) {
+ this._initParams[k] = v;
+ },
+ enableCsrfSupport: function() {
+ Ajax.Base.prototype.initialize = Ajax.Base.prototype.initialize.wrap(
+ function (callOriginal, options) {
+
+ if (App.getInitParam("csrf_token") != undefined) {
+ Object.extend(options, options || { });
+
+ if (Object.isString(options.parameters))
+ options.parameters = options.parameters.toQueryParams();
+ else if (Object.isHash(options.parameters))
+ options.parameters = options.parameters.toObject();
+
+ options.parameters["csrf_token"] = App.getInitParam("csrf_token");
+ }
+
+ return callOriginal(options);
+ }
+ );
+ },
+ urlParam: function(param) {
+ return String(window.location.href).parseQuery()[param];
+ },
+ next_seq: function() {
+ this._rpc_seq += 1;
+ return this._rpc_seq;
+ },
+ get_seq: function() {
+ return this._rpc_seq;
+ },
+ setLoadingProgress: function(p) {
+ loading_progress += p;
+
+ if (dijit.byId("loading_bar"))
+ dijit.byId("loading_bar").update({progress: loading_progress});
+
+ if (loading_progress >= 90)
+ Element.hide("overlay");
+
+ },
+ keyeventToAction: function(event) {
+
+ const hotkeys_map = App.getInitParam("hotkeys");
+ const keycode = event.which;
+ const keychar = String.fromCharCode(keycode).toLowerCase();
+
+ if (keycode == 27) { // escape and drop prefix
+ this.hotkey_prefix = false;
+ }
+
+ if (keycode == 16 || keycode == 17) return; // ignore lone shift / ctrl
+
+ if (!this.hotkey_prefix && hotkeys_map[0].indexOf(keychar) != -1) {
+
+ this.hotkey_prefix = keychar;
+ $("cmdline").innerHTML = keychar;
+ Element.show("cmdline");
+
+ window.clearTimeout(this.hotkey_prefix_timeout);
+ this.hotkey_prefix_timeout = window.setTimeout(() => {
+ this.hotkey_prefix = false;
+ Element.hide("cmdline");
+ }, 3 * 1000);
+
+ event.stopPropagation();
+
+ return false;
+ }
+
+ Element.hide("cmdline");
+
+ let hotkey_name = keychar.search(/[a-zA-Z0-9]/) != -1 ? keychar : "(" + keycode + ")";
+
+ // ensure ^*char notation
+ if (event.shiftKey) hotkey_name = "*" + hotkey_name;
+ if (event.ctrlKey) hotkey_name = "^" + hotkey_name;
+ if (event.altKey) hotkey_name = "+" + hotkey_name;
+ if (event.metaKey) hotkey_name = "%" + hotkey_name;
+
+ const hotkey_full = this.hotkey_prefix ? this.hotkey_prefix + " " + hotkey_name : hotkey_name;
+ this.hotkey_prefix = false;
+
+ let action_name = false;
+
+ for (const sequence in hotkeys_map[1]) {
+ if (hotkeys_map[1].hasOwnProperty(sequence)) {
+ if (sequence == hotkey_full) {
+ action_name = hotkeys_map[1][sequence];
+ break;
+ }
+ }
+ }
+
+ console.log('keyeventToAction', hotkey_full, '=>', action_name);
+
+ return action_name;
+ },
+ cleanupMemory: function(root) {
+ const dijits = dojo.query("[widgetid]", dijit.byId(root).domNode).map(dijit.byNode);
+
+ dijits.each(function (d) {
+ dojo.destroy(d.domNode);
+ });
+
+ $$("#" + root + " *").each(function (i) {
+ i.parentNode ? i.parentNode.removeChild(i) : true;
+ });
+ },
+ helpDialog: function(topic) {
+ const query = "backend.php?op=backend&method=help&topic=" + encodeURIComponent(topic);
+
+ if (dijit.byId("helpDlg"))
+ dijit.byId("helpDlg").destroyRecursive();
+
+ const dialog = new dijit.Dialog({
+ id: "helpDlg",
+ title: __("Help"),
+ style: "width: 600px",
+ href: query,
+ });
+
+ dialog.show();
+ },
+ displayDlg: function(title, id, param, callback) {
+ Notify.progress("Loading, please wait...", true);
+
+ const query = {op: "dlg", method: id, param: param};
+
+ xhrPost("backend.php", query, (transport) => {
+ try {
+ const content = transport.responseText;
+
+ let dialog = dijit.byId("infoBox");
+
+ if (!dialog) {
+ dialog = new dijit.Dialog({
+ title: title,
+ id: 'infoBox',
+ style: "width: 600px",
+ onCancel: function () {
+ return true;
+ },
+ onExecute: function () {
+ return true;
+ },
+ onClose: function () {
+ return true;
+ },
+ content: content
+ });
+ } else {
+ dialog.attr('title', title);
+ dialog.attr('content', content);
+ }
+
+ dialog.show();
+
+ Notify.close();
+
+ if (callback) callback(transport);
+ } catch (e) {
+ this.Error.report(e);
+ }
+ });
+
+ return false;
+ },
+ handleRpcJson: function(transport) {
+
+ const netalert_dijit = dijit.byId("net-alert");
+ let netalert = false;
+
+ if (netalert_dijit) netalert = netalert_dijit.domNode;
+
+ try {
+ const reply = JSON.parse(transport.responseText);
+
+ if (reply) {
+
+ const error = reply['error'];
+
+ if (error) {
+ const code = error['code'];
+ const msg = error['msg'];
+
+ console.warn("[handleRpcJson] received fatal error " + code + "/" + msg);
+
+ if (code != 0) {
+ fatalError(code, msg);
+ return false;
+ }
+ }
+
+ const seq = reply['seq'];
+
+ if (seq && this.get_seq() != seq) {
+ console.log("[handleRpcJson] sequence mismatch: " + seq +
+ " (want: " + this.get_seq() + ")");
+ return true;
+ }
+
+ const message = reply['message'];
+
+ if (message == "UPDATE_COUNTERS") {
+ console.log("need to refresh counters...");
+ App.setInitParam("last_article_id", -1);
+ Feeds.requestCounters(true);
+ }
+
+ const counters = reply['counters'];
+
+ if (counters)
+ Feeds.parseCounters(counters);
+
+ const runtime_info = reply['runtime-info'];
+
+ if (runtime_info)
+ App.parseRuntimeInfo(runtime_info);
+
+ if (netalert) netalert.hide();
+
+ return reply;
+
+ } else {
+ if (netalert)
+ netalert.show();
+ else
+ Notify.error("Communication problem with server.");
+ }
+
+ } catch (e) {
+ if (netalert)
+ netalert.show();
+ else
+ Notify.error("Communication problem with server.");
+
+ console.error(e);
+ }
+
+ return false;
+ },
+ parseRuntimeInfo: function(data) {
+ for (const k in data) {
+ if (data.hasOwnProperty(k)) {
+ const v = data[k];
+
+ console.log("RI:", k, "=>", v);
+
+ if (k == "daemon_is_running" && v != 1) {
+ Notify.error("<span onclick=\"App.explainError(1)\">Update daemon is not running.</span>", true);
+ return;
+ }
+
+ if (k == "update_result") {
+ const updatesIcon = dijit.byId("updatesIcon").domNode;
+
+ if (v) {
+ Element.show(updatesIcon);
+ } else {
+ Element.hide(updatesIcon);
+ }
+ }
+
+ if (k == "daemon_stamp_ok" && v != 1) {
+ Notify.error("<span onclick=\"App.explainError(3)\">Update daemon is not updating feeds.</span>", true);
+ return;
+ }
+
+ if (k == "max_feed_id" || k == "num_feeds") {
+ if (App.getInitParam(k) != v) {
+ console.log("feed count changed, need to reload feedlist.");
+ Feeds.reload();
+ }
+ }
+
+ this.setInitParam(k, v);
+ }
+ }
+
+ PluginHost.run(PluginHost.HOOK_RUNTIME_INFO_LOADED, data);
+ },
+ backendSanityCallback: function (transport) {
+
+ const reply = JSON.parse(transport.responseText);
+
+ if (!reply) {
+ fatalError(3, "Sanity check: invalid RPC reply", transport.responseText);
+ return;
+ }
+
+ const error_code = reply['error']['code'];
+
+ if (error_code && error_code != 0) {
+ return fatalError(error_code, reply['error']['message']);
+ }
+
+ console.log("sanity check ok");
+
+ const params = reply['init-params'];
+
+ if (params) {
+ console.log('reading init-params...');
+
+ for (const k in params) {
+ if (params.hasOwnProperty(k)) {
+ switch (k) {
+ case "label_base_index":
+ _label_base_index = parseInt(params[k]);
+ break;
+ 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;
+ break;
+ }
+
+ console.log("IP:", k, "=>", params[k]);
+ this.setInitParam(k, params[k]);
+ }
+ }
+
+ // PluginHost might not be available on non-index pages
+ if (typeof PluginHost !== 'undefined')
+ PluginHost.run(PluginHost.HOOK_PARAMS_LOADED, App._initParams);
+ }
+
+ this.initSecondStage();
+ },
+ explainError: function(code) {
+ return this.displayDlg(__("Error explained"), "explainError", code);
+ },
+ Error: {
+ report: function(error, params) {
+ params = params || {};
+
+ if (!error) return;
+
+ console.error("[Error.report]", error, params);
+
+ const message = params.message ? params.message : error.toString();
+
+ try {
+ xhrPost("backend.php",
+ {op: "rpc", method: "log",
+ file: params.filename ? params.filename : error.fileName,
+ line: params.lineno ? params.lineno : error.lineNumber,
+ msg: message,
+ context: error.stack},
+ (transport) => {
+ console.warn("[Error.report] log response", transport.responseText);
+ });
+ } catch (re) {
+ console.error("[Error.report] exception while saving logging error on server", re);
+ }
+
+ try {
+ if (dijit.byId("exceptionDlg"))
+ dijit.byId("exceptionDlg").destroyRecursive();
+
+ let content = "<div class='fatalError'><p>" + message + "</p>";
+
+ if (error.stack)
+ content += "<div><b>Stack trace:</b></div>" +
+ "<textarea name=\"stack\" readonly=\"1\">" + error.stack + "</textarea>";
+
+ content += "<div style='text-align : center'>";
+
+ content += "<button dojoType=\"dijit.form.Button\" " +
+ "onclick=\"dijit.byId('exceptionDlg').hide()\">" +
+ __('Close this window') + "</button>";
+ content += "</div>";
+
+ const dialog = new dijit.Dialog({
+ id: "exceptionDlg",
+ title: "Unhandled exception",
+ style: "width: 600px",
+ content: content
+ });
+
+ dialog.show();
+ } catch (de) {
+ console.error("[Error.report] exception while showing error dialog", de);
+
+ alert(error.stack ? error.stack : message);
+ }
+
+ },
+ onWindowError: function (message, filename, lineno, colno, error) {
+ // called without context (this) from window.onerror
+ App.Error.report(error,
+ {message: message, filename: filename, lineno: lineno, colno: colno});
+ },
+ }
+ });
+});
diff --git a/js/Article.js b/js/Article.js
new file mode 100644
index 000000000..04cba8ab7
--- /dev/null
+++ b/js/Article.js
@@ -0,0 +1,339 @@
+'use strict'
+/* global __, ngettext */
+define(["dojo/_base/declare"], function (declare) {
+ Article = {
+ _active_article_id: 0,
+ selectionSetScore: function () {
+ const ids = Headlines.getSelected();
+
+ if (ids.length > 0) {
+ console.log(ids);
+
+ const score = prompt(__("Please enter new score for selected articles:"));
+
+ if (score != undefined) {
+ const query = {
+ op: "article", method: "setScore", id: ids.toString(),
+ score: score
+ };
+
+ xhrJson("backend.php", query, (reply) => {
+ if (reply) {
+ reply.id.each((id) => {
+ const row = $("RROW-" + id);
+
+ if (row) {
+ const pic = row.getElementsByClassName("score-pic")[0];
+
+ if (pic) {
+ pic.src = pic.src.replace(/score_.*?\.png/,
+ reply["score_pic"]);
+ pic.setAttribute("score", reply["score"]);
+ }
+ }
+ });
+ }
+ });
+ }
+
+ } else {
+ alert(__("No articles selected."));
+ }
+ },
+ setScore: function (id, pic) {
+ const score = pic.getAttribute("score");
+
+ const new_score = prompt(__("Please enter new score for this article:"), score);
+
+ if (new_score != undefined) {
+ const query = {op: "article", method: "setScore", id: id, score: new_score};
+
+ xhrJson("backend.php", query, (reply) => {
+ if (reply) {
+ pic.src = pic.src.replace(/score_.*?\.png/, reply["score_pic"]);
+ pic.setAttribute("score", new_score);
+ pic.setAttribute("title", new_score);
+ }
+ });
+ }
+ },
+ cdmUnsetActive: function (event) {
+ const row = $("RROW-" + Article.getActive());
+
+ if (row) {
+ row.removeClassName("active");
+ const cb = dijit.getEnclosingWidget(row.select(".rchk")[0]);
+
+ if (cb && !row.hasClassName("Selected"))
+ cb.attr("checked", false);
+
+ Article.setActive(0);
+
+ if (event)
+ event.stopPropagation();
+
+ return false;
+ }
+ },
+ close: function () {
+ if (dijit.byId("content-insert"))
+ dijit.byId("headlines-wrap-inner").removeChild(
+ dijit.byId("content-insert"));
+ },
+ displayUrl: function (id) {
+ const query = {op: "rpc", method: "getlinktitlebyid", id: id};
+
+ xhrJson("backend.php", query, (reply) => {
+ if (reply && reply.link) {
+ prompt(__("Article URL:"), reply.link);
+ }
+ });
+ },
+ openInNewWindow: function (id) {
+ const w = window.open("");
+ w.opener = null;
+ w.location = "backend.php?op=article&method=redirect&id=" + id;
+
+ Article.setActive(id);
+ },
+ render: function (article) {
+ App.cleanupMemory("content-insert");
+
+ dijit.byId("headlines-wrap-inner").addChild(
+ dijit.byId("content-insert"));
+
+ const c = dijit.byId("content-insert");
+
+ try {
+ c.domNode.scrollTop = 0;
+ } catch (e) {
+ }
+
+ c.attr('content', article);
+ PluginHost.run(PluginHost.HOOK_ARTICLE_RENDERED, c.domNode);
+
+ Headlines.correctHeadlinesOffset(Article.getActive());
+
+ try {
+ c.focus();
+ } catch (e) {
+ }
+ },
+ view: function (id, noexpand) {
+ this.setActive(id);
+
+ if (!noexpand) {
+ console.log("loading article", id);
+
+ const cids = [];
+
+ /* only request uncached articles */
+
+ this.getRelativeIds(id).each((n) => {
+ if (!ArticleCache.get(n))
+ cids.push(n);
+ });
+
+ const cached_article = ArticleCache.get(id);
+
+ if (cached_article) {
+ console.log('rendering cached', id);
+ this.render(cached_article);
+ return false;
+ }
+
+ xhrPost("backend.php", {op: "article", method: "view", id: id, cids: cids.toString()}, (transport) => {
+ try {
+ const reply = App.handleRpcJson(transport);
+
+ if (reply) {
+
+ reply.each(function (article) {
+ if (Article.getActive() == article['id']) {
+ Article.render(article['content']);
+ }
+ ArticleCache.set(article['id'], article['content']);
+ });
+
+ } else {
+ console.error("Invalid object received: " + transport.responseText);
+
+ Article.render("<div class='whiteBox'>" +
+ __('Could not display article (invalid object received - see error console for details)') + "</div>");
+ }
+
+ //const unread_in_buffer = $$("#headlines-frame > div[id*=RROW][class*=Unread]").length;
+ //request_counters(unread_in_buffer == 0);
+
+ Notify.close();
+
+ } catch (e) {
+ App.Error.report(e);
+ }
+ })
+ }
+
+ return false;
+ },
+ editTags: function (id) {
+ const query = "backend.php?op=article&method=editArticleTags&param=" + encodeURIComponent(id);
+
+ if (dijit.byId("editTagsDlg"))
+ dijit.byId("editTagsDlg").destroyRecursive();
+
+ const dialog = new dijit.Dialog({
+ id: "editTagsDlg",
+ title: __("Edit article Tags"),
+ style: "width: 600px",
+ execute: function () {
+ if (this.validate()) {
+ Notify.progress("Saving article tags...", true);
+
+ xhrPost("backend.php", this.attr('value'), (transport) => {
+ try {
+ Notify.close();
+ dialog.hide();
+
+ const data = JSON.parse(transport.responseText);
+
+ if (data) {
+ const id = data.id;
+
+ const tags = $("ATSTR-" + id);
+ const tooltip = dijit.byId("ATSTRTIP-" + id);
+
+ if (tags) tags.innerHTML = data.content;
+ if (tooltip) tooltip.attr('label', data.content_full);
+ }
+ } catch (e) {
+ App.Error.report(e);
+ }
+ });
+ }
+ },
+ href: query
+ });
+
+ const tmph = dojo.connect(dialog, 'onLoad', function () {
+ dojo.disconnect(tmph);
+
+ new Ajax.Autocompleter('tags_str', 'tags_choices',
+ "backend.php?op=article&method=completeTags",
+ {tokens: ',', paramName: "search"});
+ });
+
+ dialog.show();
+ },
+ cdmScrollToId: function (id, force) {
+ const ctr = $("headlines-frame");
+ const e = $("RROW-" + id);
+
+ if (!e || !ctr) return;
+
+ if (force || e.offsetTop + e.offsetHeight > (ctr.scrollTop + ctr.offsetHeight) ||
+ e.offsetTop < ctr.scrollTop) {
+
+ // expanded cdm has a 4px margin now
+ ctr.scrollTop = parseInt(e.offsetTop) - 4;
+
+ Element.hide("floatingTitle");
+ }
+ },
+ setActive: function (id) {
+ console.log("setActive", id);
+
+ $$("div[id*=RROW][class*=active]").each((e) => {
+ e.removeClassName("active");
+
+ if (!e.hasClassName("Selected")) {
+ const cb = dijit.getEnclosingWidget(e.select(".rchk")[0]);
+ if (cb) cb.attr("checked", false);
+ }
+ });
+
+ this._active_article_id = id;
+
+ const row = $("RROW-" + id);
+
+ if (row) {
+ if (row.hasAttribute("data-content")) {
+ console.log("unpacking: " + row.id);
+
+ row.select(".content-inner")[0].innerHTML = row.getAttribute("data-content");
+ row.removeAttribute("data-content");
+
+ PluginHost.run(PluginHost.HOOK_ARTICLE_RENDERED_CDM, row);
+ }
+
+ if (row.hasClassName("Unread")) {
+
+ Headlines.catchupBatched(() => {
+ Feeds.decrementFeedCounter(Feeds.getActive(), Feeds.activeIsCat());
+ Headlines.toggleUnread(id, 0);
+ Headlines.updateFloatingTitle(true);
+ });
+
+ }
+
+ row.addClassName("active");
+
+ if (!row.hasClassName("Selected")) {
+ const cb = dijit.getEnclosingWidget(row.select(".rchk")[0]);
+ if (cb) cb.attr("checked", true);
+ }
+
+ PluginHost.run(PluginHost.HOOK_ARTICLE_SET_ACTIVE, this._active_article_id);
+ }
+
+ Headlines.updateSelectedPrompt();
+ },
+ getActive: function () {
+ return this._active_article_id;
+ },
+ scroll: function (offset) {
+ if (!App.isCombinedMode()) {
+ const ci = $("content-insert");
+ if (ci) {
+ ci.scrollTop += offset;
+ }
+ } else {
+ const hi = $("headlines-frame");
+ if (hi) {
+ hi.scrollTop += offset;
+ }
+
+ }
+ },
+ getRelativeIds: function (id, limit) {
+
+ const tmp = [];
+
+ if (!limit) limit = 6; //3
+
+ const ids = Headlines.getLoaded();
+
+ for (let i = 0; i < ids.length; i++) {
+ if (ids[i] == id) {
+ for (let k = 1; k <= limit; k++) {
+ //if (i > k-1) tmp.push(ids[i-k]);
+ if (i < ids.length - k) tmp.push(ids[i + k]);
+ }
+ break;
+ }
+ }
+
+ return tmp;
+ },
+ mouseIn: function (id) {
+ this.post_under_pointer = id;
+ },
+ mouseOut: function (id) {
+ this.post_under_pointer = false;
+ },
+ getUnderPointer: function () {
+ return this.post_under_pointer;
+ }
+ }
+
+ return Article;
+}); \ No newline at end of file
diff --git a/js/ArticleCache.js b/js/ArticleCache.js
new file mode 100644
index 000000000..ce34d00d9
--- /dev/null
+++ b/js/ArticleCache.js
@@ -0,0 +1,29 @@
+'use strict'
+/* global __, ngettext */
+define(["dojo/_base/declare"], function (declare) {
+ ArticleCache = {
+ has_storage: 'sessionStorage' in window && window['sessionStorage'] !== null,
+ set: function (id, obj) {
+ if (this.has_storage)
+ try {
+ sessionStorage["article:" + id] = obj;
+ } catch (e) {
+ sessionStorage.clear();
+ }
+ },
+ get: function (id) {
+ if (this.has_storage)
+ return sessionStorage["article:" + id];
+ },
+ clear: function () {
+ if (this.has_storage)
+ sessionStorage.clear();
+ },
+ del: function (id) {
+ if (this.has_storage)
+ sessionStorage.removeItem("article:" + id);
+ },
+ }
+
+ return ArticleCache;
+});
diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js
new file mode 100644
index 000000000..81ad2ffce
--- /dev/null
+++ b/js/CommonDialogs.js
@@ -0,0 +1,449 @@
+'use strict'
+/* global __, ngettext */
+define(["dojo/_base/declare"], function (declare) {
+ // noinspection JSUnusedGlobalSymbols
+ CommonDialogs = {
+ closeInfoBox: function() {
+ const dialog = dijit.byId("infoBox");
+ if (dialog) dialog.hide();
+ },
+ uploadIconHandler: function(rc) {
+ switch (rc) {
+ case 0:
+ Notify.info("Upload complete.");
+ if (App.isPrefs()) {
+ Feeds.reload();
+ } else {
+ setTimeout('Feeds.reload(false, false)', 50);
+ }
+ break;
+ case 1:
+ Notify.error("Upload failed: icon is too big.");
+ break;
+ case 2:
+ Notify.error("Upload failed.");
+ break;
+ }
+ },
+ removeFeedIcon: function(id) {
+ if (confirm(__("Remove stored feed icon?"))) {
+ Notify.progress("Removing feed icon...", true);
+
+ const query = {op: "pref-feeds", method: "removeicon", feed_id: id};
+
+ xhrPost("backend.php", query, () => {
+ Notify.info("Feed icon removed.");
+ if (App.isPrefs()) {
+ Feeds.reload();
+ } else {
+ setTimeout('Feeds.reload(false, false)', 50);
+ }
+ });
+ }
+
+ return false;
+ },
+ uploadFeedIcon: function() {
+ const file = $("icon_file");
+
+ if (file.value.length == 0) {
+ alert(__("Please select an image file to upload."));
+ } else if (confirm(__("Upload new icon for this feed?"))) {
+ Notify.progress("Uploading, please wait...", true);
+ return true;
+ }
+
+ return false;
+ },
+ quickAddFeed: function() {
+ const query = "backend.php?op=feeds&method=quickAddFeed";
+
+ // overlapping widgets
+ if (dijit.byId("batchSubDlg")) dijit.byId("batchSubDlg").destroyRecursive();
+ if (dijit.byId("feedAddDlg")) dijit.byId("feedAddDlg").destroyRecursive();
+
+ const dialog = new dijit.Dialog({
+ id: "feedAddDlg",
+ title: __("Subscribe to Feed"),
+ style: "width: 600px",
+ show_error: function (msg) {
+ const elem = $("fadd_error_message");
+
+ elem.innerHTML = msg;
+
+ if (!Element.visible(elem))
+ new Effect.Appear(elem);
+
+ },
+ execute: function () {
+ if (this.validate()) {
+ console.log(dojo.objectToQuery(this.attr('value')));
+
+ const feed_url = this.attr('value').feed;
+
+ Element.show("feed_add_spinner");
+ Element.hide("fadd_error_message");
+
+ xhrPost("backend.php", this.attr('value'), (transport) => {
+ try {
+
+ try {
+ var reply = JSON.parse(transport.responseText);
+ } catch (e) {
+ Element.hide("feed_add_spinner");
+ alert(__("Failed to parse output. This can indicate server timeout and/or network issues. Backend output was logged to browser console."));
+ console.log('quickAddFeed, backend returned:' + transport.responseText);
+ return;
+ }
+
+ const rc = reply['result'];
+
+ Notify.close();
+ Element.hide("feed_add_spinner");
+
+ console.log(rc);
+
+ switch (parseInt(rc['code'])) {
+ case 1:
+ dialog.hide();
+ Notify.info(__("Subscribed to %s").replace("%s", feed_url));
+
+ Feeds.reload();
+ break;
+ case 2:
+ dialog.show_error(__("Specified URL seems to be invalid."));
+ break;
+ case 3:
+ dialog.show_error(__("Specified URL doesn't seem to contain any feeds."));
+ break;
+ case 4:
+ const feeds = rc['feeds'];
+
+ Element.show("fadd_multiple_notify");
+
+ const select = dijit.byId("feedDlg_feedContainerSelect");
+
+ while (select.getOptions().length > 0)
+ select.removeOption(0);
+
+ select.addOption({value: '', label: __("Expand to select feed")});
+
+ let count = 0;
+ for (const feedUrl in feeds) {
+ if (feeds.hasOwnProperty(feedUrl)) {
+ select.addOption({value: feedUrl, label: feeds[feedUrl]});
+ count++;
+ }
+ }
+
+ Effect.Appear('feedDlg_feedsContainer', {duration: 0.5});
+
+ break;
+ case 5:
+ dialog.show_error(__("Couldn't download the specified URL: %s").replace("%s", rc['message']));
+ break;
+ case 6:
+ dialog.show_error(__("XML validation failed: %s").replace("%s", rc['message']));
+ break;
+ case 0:
+ dialog.show_error(__("You are already subscribed to this feed."));
+ break;
+ }
+
+ } catch (e) {
+ console.error(transport.responseText);
+ App.Error.report(e);
+ }
+ });
+ }
+ },
+ href: query
+ });
+
+ dialog.show();
+ },
+ showFeedsWithErrors: function() {
+ const query = {op: "pref-feeds", method: "feedsWithErrors"};
+
+ if (dijit.byId("errorFeedsDlg"))
+ dijit.byId("errorFeedsDlg").destroyRecursive();
+
+ const dialog = new dijit.Dialog({
+ id: "errorFeedsDlg",
+ title: __("Feeds with update errors"),
+ style: "width: 600px",
+ getSelectedFeeds: function () {
+ return Tables.getSelected("prefErrorFeedList");
+ },
+ removeSelected: function () {
+ const sel_rows = this.getSelectedFeeds();
+
+ if (sel_rows.length > 0) {
+ if (confirm(__("Remove selected feeds?"))) {
+ Notify.progress("Removing selected feeds...", true);
+
+ const query = {
+ op: "pref-feeds", method: "remove",
+ ids: sel_rows.toString()
+ };
+
+ xhrPost("backend.php", query, () => {
+ Notify.close();
+ dialog.hide();
+ Feeds.reload();
+ });
+ }
+
+ } else {
+ alert(__("No feeds selected."));
+ }
+ },
+ execute: function () {
+ if (this.validate()) {
+ //
+ }
+ },
+ href: "backend.php?" + dojo.objectToQuery(query)
+ });
+
+ dialog.show();
+ },
+ feedBrowser: function() {
+ const query = {op: "feeds", method: "feedBrowser"};
+
+ if (dijit.byId("feedAddDlg"))
+ dijit.byId("feedAddDlg").hide();
+
+ if (dijit.byId("feedBrowserDlg"))
+ dijit.byId("feedBrowserDlg").destroyRecursive();
+
+ // noinspection JSUnusedGlobalSymbols
+ const dialog = new dijit.Dialog({
+ id: "feedBrowserDlg",
+ title: __("More Feeds"),
+ style: "width: 600px",
+ getSelectedFeedIds: function () {
+ const list = $$("#browseFeedList li[id*=FBROW]");
+ const selected = [];
+
+ list.each(function (child) {
+ const id = child.id.replace("FBROW-", "");
+
+ if (child.hasClassName('Selected')) {
+ selected.push(id);
+ }
+ });
+
+ return selected;
+ },
+ getSelectedFeeds: function () {
+ const list = $$("#browseFeedList li.Selected");
+ const selected = [];
+
+ list.each(function (child) {
+ const title = child.getElementsBySelector("span.fb_feedTitle")[0].innerHTML;
+ const url = child.getElementsBySelector("a.fb_feedUrl")[0].href;
+
+ selected.push([title, url]);
+
+ });
+
+ return selected;
+ },
+
+ subscribe: function () {
+ const mode = this.attr('value').mode;
+ let selected = [];
+
+ if (mode == "1")
+ selected = this.getSelectedFeeds();
+ else
+ selected = this.getSelectedFeedIds();
+
+ if (selected.length > 0) {
+ dijit.byId("feedBrowserDlg").hide();
+
+ Notify.progress("Loading, please wait...", true);
+
+ const query = {
+ op: "rpc", method: "massSubscribe",
+ payload: JSON.stringify(selected), mode: mode
+ };
+
+ xhrPost("backend.php", query, () => {
+ Notify.close();
+ Feeds.reload();
+ });
+
+ } else {
+ alert(__("No feeds selected."));
+ }
+
+ },
+ update: function () {
+ Element.show('feed_browser_spinner');
+
+ xhrPost("backend.php", dialog.attr("value"), (transport) => {
+ Notify.close();
+
+ Element.hide('feed_browser_spinner');
+
+ const reply = JSON.parse(transport.responseText);
+ const mode = reply['mode'];
+
+ if ($("browseFeedList") && reply['content']) {
+ $("browseFeedList").innerHTML = reply['content'];
+ }
+
+ dojo.parser.parse("browseFeedList");
+
+ if (mode == 2) {
+ Element.show(dijit.byId('feed_archive_remove').domNode);
+ } else {
+ Element.hide(dijit.byId('feed_archive_remove').domNode);
+ }
+ });
+ },
+ removeFromArchive: function () {
+ const selected = this.getSelectedFeedIds();
+
+ if (selected.length > 0) {
+ if (confirm(__("Remove selected feeds from the archive? Feeds with stored articles will not be removed."))) {
+ Element.show('feed_browser_spinner');
+
+ const query = {op: "rpc", method: "remarchive", ids: selected.toString()};
+
+ xhrPost("backend.php", query, () => {
+ dialog.update();
+ });
+ }
+ }
+ },
+ execute: function () {
+ if (this.validate()) {
+ this.subscribe();
+ }
+ },
+ href: "backend.php?" + dojo.objectToQuery(query)
+ });
+
+ dialog.show();
+ },
+ addLabel: function(select, callback) {
+ const caption = prompt(__("Please enter label caption:"), "");
+
+ if (caption != undefined && caption.trim().length > 0) {
+
+ const query = {op: "pref-labels", method: "add", caption: caption.trim()};
+
+ if (select)
+ Object.extend(query, {output: "select"});
+
+ Notify.progress("Loading, please wait...", true);
+
+ xhrPost("backend.php", query, (transport) => {
+ if (callback) {
+ callback(transport);
+ } else if (App.isPrefs()) {
+ dijit.byId("labelTree").reload();
+ } else {
+ Feeds.reload();
+ }
+ });
+ }
+ },
+ unsubscribeFeed: function(feed_id, title) {
+
+ const msg = __("Unsubscribe from %s?").replace("%s", title);
+
+ if (title == undefined || confirm(msg)) {
+ Notify.progress("Removing feed...");
+
+ const query = {op: "pref-feeds", quiet: 1, method: "remove", ids: feed_id};
+
+ xhrPost("backend.php", query, () => {
+ if (dijit.byId("feedEditDlg")) dijit.byId("feedEditDlg").hide();
+
+ if (App.isPrefs()) {
+ Feeds.reload();
+ } else {
+ if (feed_id == Feeds.getActive())
+ setTimeout(() => {
+ Feeds.open({feed: -5})
+ },
+ 100);
+
+ if (feed_id < 0) Feeds.reload();
+ }
+ });
+ }
+
+ return false;
+ },
+ editFeed: function (feed) {
+ if (feed <= 0)
+ return alert(__("You can't edit this kind of feed."));
+
+ const query = {op: "pref-feeds", method: "editfeed", id: feed};
+
+ console.log("editFeed", query);
+
+ if (dijit.byId("filterEditDlg"))
+ dijit.byId("filterEditDlg").destroyRecursive();
+
+ if (dijit.byId("feedEditDlg"))
+ dijit.byId("feedEditDlg").destroyRecursive();
+
+ const dialog = new dijit.Dialog({
+ id: "feedEditDlg",
+ title: __("Edit Feed"),
+ style: "width: 600px",
+ execute: function () {
+ if (this.validate()) {
+ Notify.progress("Saving data...", true);
+
+ xhrPost("backend.php", dialog.attr('value'), () => {
+ dialog.hide();
+ Notify.close();
+ Feeds.reload();
+ });
+ }
+ },
+ href: "backend.php?" + dojo.objectToQuery(query)
+ });
+
+ dialog.show();
+ },
+ genUrlChangeKey: function(feed, is_cat) {
+ if (confirm(__("Generate new syndication address for this feed?"))) {
+
+ Notify.progress("Trying to change address...", true);
+
+ const query = {op: "pref-feeds", method: "regenFeedKey", id: feed, is_cat: is_cat};
+
+ xhrJson("backend.php", query, (reply) => {
+ const new_link = reply.link;
+ const e = $('gen_feed_url');
+
+ if (new_link) {
+ e.innerHTML = e.innerHTML.replace(/&amp;key=.*$/,
+ "&amp;key=" + new_link);
+
+ e.href = e.href.replace(/&key=.*$/,
+ "&key=" + new_link);
+
+ new Effect.Highlight(e);
+
+ Notify.close();
+
+ } else {
+ Notify.error("Could not change feed URL.");
+ }
+ });
+ }
+ return false;
+ }
+ };
+
+ return CommonDialogs;
+}); \ No newline at end of file
diff --git a/js/CommonFilters.js b/js/CommonFilters.js
new file mode 100644
index 000000000..97a676c98
--- /dev/null
+++ b/js/CommonFilters.js
@@ -0,0 +1,393 @@
+'use strict'
+/* global __, ngettext */
+define(["dojo/_base/declare"], function (declare) {
+ Filters = {
+ filterDlgCheckAction: function(sender) {
+ const action = sender.value;
+
+ const action_param = $("filterDlg_paramBox");
+
+ if (!action_param) {
+ console.log("filterDlgCheckAction: can't find action param box!");
+ return;
+ }
+
+ // if selected action supports parameters, enable params field
+ if (action == 4 || action == 6 || action == 7 || action == 9) {
+ new Effect.Appear(action_param, {duration: 0.5});
+
+ Element.hide(dijit.byId("filterDlg_actionParam").domNode);
+ Element.hide(dijit.byId("filterDlg_actionParamLabel").domNode);
+ Element.hide(dijit.byId("filterDlg_actionParamPlugin").domNode);
+
+ if (action == 7) {
+ Element.show(dijit.byId("filterDlg_actionParamLabel").domNode);
+ } else if (action == 9) {
+ Element.show(dijit.byId("filterDlg_actionParamPlugin").domNode);
+ } else {
+ Element.show(dijit.byId("filterDlg_actionParam").domNode);
+ }
+
+ } else {
+ Element.hide(action_param);
+ }
+ },
+ createNewRuleElement: function(parentNode, replaceNode) {
+ const form = document.forms["filter_new_rule_form"];
+ const query = {op: "pref-filters", method: "printrulename", rule: dojo.formToJson(form)};
+
+ xhrPost("backend.php", query, (transport) => {
+ try {
+ const li = dojo.create("li");
+
+ const cb = dojo.create("input", {type: "checkbox"}, li);
+
+ new dijit.form.CheckBox({
+ onChange: function () {
+ Lists.onRowChecked(this);
+ },
+ }, cb);
+
+ dojo.create("input", {
+ type: "hidden",
+ name: "rule[]",
+ value: dojo.formToJson(form)
+ }, li);
+
+ dojo.create("span", {
+ onclick: function () {
+ dijit.byId('filterEditDlg').editRule(this);
+ },
+ innerHTML: transport.responseText
+ }, li);
+
+ if (replaceNode) {
+ parentNode.replaceChild(li, replaceNode);
+ } else {
+ parentNode.appendChild(li);
+ }
+ } catch (e) {
+ App.Error.report(e);
+ }
+ });
+ },
+ createNewActionElement: function(parentNode, replaceNode) {
+ const form = document.forms["filter_new_action_form"];
+
+ if (form.action_id.value == 7) {
+ form.action_param.value = form.action_param_label.value;
+ } else if (form.action_id.value == 9) {
+ form.action_param.value = form.action_param_plugin.value;
+ }
+
+ const query = {
+ op: "pref-filters", method: "printactionname",
+ action: dojo.formToJson(form)
+ };
+
+ xhrPost("backend.php", query, (transport) => {
+ try {
+ const li = dojo.create("li");
+
+ const cb = dojo.create("input", {type: "checkbox"}, li);
+
+ new dijit.form.CheckBox({
+ onChange: function () {
+ Lists.onRowChecked(this);
+ },
+ }, cb);
+
+ dojo.create("input", {
+ type: "hidden",
+ name: "action[]",
+ value: dojo.formToJson(form)
+ }, li);
+
+ dojo.create("span", {
+ onclick: function () {
+ dijit.byId('filterEditDlg').editAction(this);
+ },
+ innerHTML: transport.responseText
+ }, li);
+
+ if (replaceNode) {
+ parentNode.replaceChild(li, replaceNode);
+ } else {
+ parentNode.appendChild(li);
+ }
+
+ } catch (e) {
+ App.Error.report(e);
+ }
+ });
+ },
+ addFilterRule: function(replaceNode, ruleStr) {
+ if (dijit.byId("filterNewRuleDlg"))
+ dijit.byId("filterNewRuleDlg").destroyRecursive();
+
+ const query = "backend.php?op=pref-filters&method=newrule&rule=" +
+ encodeURIComponent(ruleStr);
+
+ const rule_dlg = new dijit.Dialog({
+ id: "filterNewRuleDlg",
+ title: ruleStr ? __("Edit rule") : __("Add rule"),
+ style: "width: 600px",
+ execute: function () {
+ if (this.validate()) {
+ Filters.createNewRuleElement($("filterDlg_Matches"), replaceNode);
+ this.hide();
+ }
+ },
+ href: query
+ });
+
+ rule_dlg.show();
+ },
+ addFilterAction: function(replaceNode, actionStr) {
+ if (dijit.byId("filterNewActionDlg"))
+ dijit.byId("filterNewActionDlg").destroyRecursive();
+
+ const query = "backend.php?op=pref-filters&method=newaction&action=" +
+ encodeURIComponent(actionStr);
+
+ const rule_dlg = new dijit.Dialog({
+ id: "filterNewActionDlg",
+ title: actionStr ? __("Edit action") : __("Add action"),
+ style: "width: 600px",
+ execute: function () {
+ if (this.validate()) {
+ Filters.createNewActionElement($("filterDlg_Actions"), replaceNode);
+ this.hide();
+ }
+ },
+ href: query
+ });
+
+ rule_dlg.show();
+ },
+ editFilterTest: function(query) {
+
+ if (dijit.byId("filterTestDlg"))
+ dijit.byId("filterTestDlg").destroyRecursive();
+
+ const test_dlg = new dijit.Dialog({
+ id: "filterTestDlg",
+ title: "Test Filter",
+ style: "width: 600px",
+ results: 0,
+ limit: 100,
+ max_offset: 10000,
+ getTestResults: function (query, offset) {
+ const updquery = query + "&offset=" + offset + "&limit=" + test_dlg.limit;
+
+ console.log("getTestResults:" + offset);
+
+ xhrPost("backend.php", updquery, (transport) => {
+ try {
+ const result = JSON.parse(transport.responseText);
+
+ if (result && dijit.byId("filterTestDlg") && dijit.byId("filterTestDlg").open) {
+ test_dlg.results += result.length;
+
+ console.log("got results:" + result.length);
+
+ $("prefFilterProgressMsg").innerHTML = __("Looking for articles (%d processed, %f found)...")
+ .replace("%f", test_dlg.results)
+ .replace("%d", offset);
+
+ console.log(offset + " " + test_dlg.max_offset);
+
+ for (let i = 0; i < result.length; i++) {
+ const tmp = new Element("table");
+ tmp.innerHTML = result[i];
+ dojo.parser.parse(tmp);
+
+ $("prefFilterTestResultList").innerHTML += tmp.innerHTML;
+ }
+
+ if (test_dlg.results < 30 && offset < test_dlg.max_offset) {
+
+ // get the next batch
+ window.setTimeout(function () {
+ test_dlg.getTestResults(query, offset + test_dlg.limit);
+ }, 0);
+
+ } else {
+ // all done
+
+ Element.hide("prefFilterLoadingIndicator");
+
+ if (test_dlg.results == 0) {
+ $("prefFilterTestResultList").innerHTML = "<tr><td align='center'>No recent articles matching this filter have been found.</td></tr>";
+ $("prefFilterProgressMsg").innerHTML = "Articles matching this filter:";
+ } else {
+ $("prefFilterProgressMsg").innerHTML = __("Found %d articles matching this filter:")
+ .replace("%d", test_dlg.results);
+ }
+
+ }
+
+ } else if (!result) {
+ console.log("getTestResults: can't parse results object");
+
+ Element.hide("prefFilterLoadingIndicator");
+
+ Notify.error("Error while trying to get filter test results.");
+
+ } else {
+ console.log("getTestResults: dialog closed, bailing out.");
+ }
+ } catch (e) {
+ App.Error.report(e);
+ }
+
+ });
+ },
+ href: query
+ });
+
+ dojo.connect(test_dlg, "onLoad", null, function (e) {
+ test_dlg.getTestResults(query, 0);
+ });
+
+ test_dlg.show();
+ },
+ quickAddFilter: function() {
+ let query;
+
+ if (!App.isPrefs()) {
+ query = {
+ op: "pref-filters", method: "newfilter",
+ feed: Feeds.getActive(), is_cat: Feeds.activeIsCat()
+ };
+ } else {
+ query = {op: "pref-filters", method: "newfilter"};
+ }
+
+ console.log('quickAddFilter', query);
+
+ if (dijit.byId("feedEditDlg"))
+ dijit.byId("feedEditDlg").destroyRecursive();
+
+ if (dijit.byId("filterEditDlg"))
+ dijit.byId("filterEditDlg").destroyRecursive();
+
+ const dialog = new dijit.Dialog({
+ id: "filterEditDlg",
+ title: __("Create Filter"),
+ style: "width: 600px",
+ test: function () {
+ const query = "backend.php?" + dojo.formToQuery("filter_new_form") + "&savemode=test";
+
+ Filters.editFilterTest(query);
+ },
+ selectRules: function (select) {
+ $$("#filterDlg_Matches input[type=checkbox]").each(function (e) {
+ e.checked = select;
+ if (select)
+ e.parentNode.addClassName("Selected");
+ else
+ e.parentNode.removeClassName("Selected");
+ });
+ },
+ selectActions: function (select) {
+ $$("#filterDlg_Actions input[type=checkbox]").each(function (e) {
+ e.checked = select;
+
+ if (select)
+ e.parentNode.addClassName("Selected");
+ else
+ e.parentNode.removeClassName("Selected");
+
+ });
+ },
+ editRule: function (e) {
+ const li = e.parentNode;
+ const rule = li.getElementsByTagName("INPUT")[1].value;
+ Filters.addFilterRule(li, rule);
+ },
+ editAction: function (e) {
+ const li = e.parentNode;
+ const action = li.getElementsByTagName("INPUT")[1].value;
+ Filters.addFilterAction(li, action);
+ },
+ addAction: function () {
+ Filters.addFilterAction();
+ },
+ addRule: function () {
+ Filters.addFilterRule();
+ },
+ deleteAction: function () {
+ $$("#filterDlg_Actions li[class*=Selected]").each(function (e) {
+ e.parentNode.removeChild(e)
+ });
+ },
+ deleteRule: function () {
+ $$("#filterDlg_Matches li[class*=Selected]").each(function (e) {
+ e.parentNode.removeChild(e)
+ });
+ },
+ execute: function () {
+ if (this.validate()) {
+
+ const query = dojo.formToQuery("filter_new_form");
+
+ xhrPost("backend.php", query, () => {
+ if (App.isPrefs()) {
+ dijit.byId("filterTree").reload();
+ }
+
+ dialog.hide();
+ });
+ }
+ },
+ href: "backend.php?" + dojo.objectToQuery(query)
+ });
+
+ if (!App.isPrefs()) {
+ const selectedText = getSelectionText();
+
+ const lh = dojo.connect(dialog, "onLoad", function () {
+ dojo.disconnect(lh);
+
+ 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};
+
+ Filters.addFilterRule(null, dojo.toJson(rule));
+
+ } else {
+
+ const query = {op: "rpc", method: "getlinktitlebyid", id: Article.getActive()};
+
+ xhrPost("backend.php", query, (transport) => {
+ const reply = JSON.parse(transport.responseText);
+
+ let title = false;
+
+ if (reply && reply.title) title = reply.title;
+
+ if (title || Feeds.getActive() || Feeds.activeIsCat()) {
+
+ console.log(title + " " + Feeds.getActive());
+
+ const feed_id = Feeds.activeIsCat() ? 'CAT:' + parseInt(Feeds.getActive()) :
+ Feeds.getActive();
+
+ const rule = {reg_exp: title, feed_id: [feed_id], filter_type: 1};
+
+ Filters.addFilterRule(null, dojo.toJson(rule));
+ }
+ });
+ }
+ });
+ }
+ dialog.show();
+ },
+ };
+
+ return Filters;
+});
diff --git a/js/FeedTree.js b/js/FeedTree.js
index b37d339c2..75d1c901b 100755
--- a/js/FeedTree.js
+++ b/js/FeedTree.js
@@ -41,14 +41,14 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dijit/Tree", "dijit/Menu"],
menu.addChild(new dijit.MenuItem({
label: __("Mark as read"),
onClick: function() {
- catchupFeed(this.getParent().row_id);
+ Feeds.catchupFeed(this.getParent().row_id);
}}));
if (bare_id > 0) {
menu.addChild(new dijit.MenuItem({
label: __("Edit feed"),
onClick: function() {
- editFeed(this.getParent().row_id, false);
+ CommonDialogs.editFeed(this.getParent().row_id, false);
}}));
/* menu.addChild(new dijit.MenuItem({
@@ -69,7 +69,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dijit/Tree", "dijit/Menu"],
menu.addChild(new dijit.MenuItem({
label: __("Mark as read"),
onClick: function() {
- catchupFeed(this.getParent().row_id, true);
+ Feeds.catchupFeed(this.getParent().row_id, true);
}}));
menu.addChild(new dijit.MenuItem({
@@ -97,7 +97,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dijit/Tree", "dijit/Menu"],
menu.addChild(new dijit.MenuItem({
label: __("Mark all feeds as read"),
onClick: function() {
- catchupAllFeeds();
+ Feeds.catchupAllFeeds();
}}));
menu.bindDomNode(tnode.domNode);
@@ -123,7 +123,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dijit/Tree", "dijit/Menu"],
postCreate: function() {
this.connect(this.model, "onChange", "updateCounter");
this.connect(this, "_expandNode", function() {
- this.hideRead(getInitParam("hide_read_feeds"), getInitParam("hide_read_shows_special"));
+ this.hideRead(App.getInitParam("hide_read_feeds"), App.getInitParam("hide_read_shows_special"));
});
this.inherited(arguments);
@@ -207,7 +207,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dijit/Tree", "dijit/Menu"],
}
}
} catch (e) {
- exception_error(e);
+ App.Error.report(e);
}
},
findNodeParentsAndExpandThem: function(feed, is_cat, root, parents) {
@@ -242,7 +242,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dijit/Tree", "dijit/Menu"],
this.expandParentNodes(feed, is_cat, parents.slice(0));
}
} catch (e) {
- exception_error(e);
+ App.Error.report(e);
}
},
selectFeed: function(feed, is_cat) {
diff --git a/js/Feeds.js b/js/Feeds.js
new file mode 100644
index 000000000..fbcb56150
--- /dev/null
+++ b/js/Feeds.js
@@ -0,0 +1,642 @@
+'use strict'
+/* global __, ngettext */
+define(["dojo/_base/declare"], function (declare) {
+ Feeds = {
+ counters_last_request: 0,
+ _active_feed_id: 0,
+ _active_feed_is_cat: false,
+ infscroll_in_progress: 0,
+ infscroll_disabled: 0,
+ _infscroll_timeout: false,
+ _search_query: false,
+ last_search_query: [],
+ _viewfeed_wait_timeout: false,
+ _counters_prev: [],
+ // NOTE: this implementation is incomplete
+ // for general objects but good enough for counters
+ // http://adripofjavascript.com/blog/drips/object-equality-in-javascript.html
+ counterEquals: function(a, b) {
+ // Create arrays of property names
+ const aProps = Object.getOwnPropertyNames(a);
+ const bProps = Object.getOwnPropertyNames(b);
+
+ // If number of properties is different,
+ // objects are not equivalent
+ if (aProps.length != bProps.length) {
+ return false;
+ }
+
+ for (let i = 0; i < aProps.length; i++) {
+ const propName = aProps[i];
+
+ // If values of same property are not equal,
+ // objects are not equivalent
+ if (a[propName] !== b[propName]) {
+ return false;
+ }
+ }
+
+ // If we made it this far, objects
+ // are considered equivalent
+ return true;
+ },
+ resetCounters: function () {
+ this._counters_prev = [];
+ },
+ parseCounters: function (elems) {
+ for (let l = 0; l < elems.length; l++) {
+
+ if (Feeds._counters_prev[l] && this.counterEquals(elems[l], this._counters_prev[l])) {
+ continue;
+ }
+
+ const id = elems[l].id;
+ const kind = elems[l].kind;
+ const ctr = parseInt(elems[l].counter);
+ const error = elems[l].error;
+ const has_img = elems[l].has_img;
+ const updated = elems[l].updated;
+ const auxctr = parseInt(elems[l].auxcounter);
+
+ if (id == "global-unread") {
+ App.global_unread = ctr;
+ App.updateTitle();
+ continue;
+ }
+
+ if (id == "subscribed-feeds") {
+ /* feeds_found = ctr; */
+ continue;
+ }
+
+ /*if (this.getUnread(id, (kind == "cat")) != ctr ||
+ (kind == "cat")) {
+ }*/
+
+ this.setUnread(id, (kind == "cat"), ctr);
+ this.setValue(id, (kind == "cat"), 'auxcounter', auxctr);
+
+ if (kind != "cat") {
+ this.setValue(id, false, 'error', error);
+ this.setValue(id, false, 'updated', updated);
+
+ if (id > 0) {
+ if (has_img) {
+ this.setIcon(id, false,
+ App.getInitParam("icons_url") + "/" + id + ".ico?" + has_img);
+ } else {
+ this.setIcon(id, false, 'images/blank_icon.gif');
+ }
+ }
+ }
+ }
+
+ this.hideOrShowFeeds(App.getInitParam("hide_read_feeds") == 1);
+ this._counters_prev = elems;
+ },
+ reloadCurrent: function(method) {
+ console.log("reloadCurrent: " + method);
+
+ if (this.getActive() != undefined) {
+ this.open({feed: this.getActive(), is_cat: this.activeIsCat(), method: method});
+ }
+ return false; // block unneeded form submits
+ },
+ openNextUnread: function() {
+ const is_cat = this.activeIsCat();
+ const nuf = this.getNextUnread(this.getActive(), is_cat);
+ if (nuf) this.open({feed: nuf, is_cat: is_cat});
+ },
+ toggle: function() {
+ Element.toggle("feeds-holder");
+
+ const splitter = $("feeds-holder_splitter");
+
+ Element.visible("feeds-holder") ? splitter.show() : splitter.hide();
+
+ dijit.byId("main").resize();
+ },
+ cancelSearch: function() {
+ this._search_query = "";
+ this.reloadCurrent();
+ },
+ requestCounters: function(force) {
+ const date = new Date();
+ const timestamp = Math.round(date.getTime() / 1000);
+
+ if (force || timestamp - this.counters_last_request > 5) {
+ console.log("scheduling request of counters...");
+
+ this.counters_last_request = timestamp;
+
+ let query = {op: "rpc", method: "getAllCounters", seq: App.next_seq()};
+
+ if (!force)
+ query.last_article_id = App.getInitParam("last_article_id");
+
+ xhrPost("backend.php", query, (transport) => {
+ App.handleRpcJson(transport);
+ });
+
+ } else {
+ console.log("request_counters: rate limit reached: " + (timestamp - this.counters_last_request));
+ }
+ },
+ reload: function() {
+ try {
+ Element.show("feedlistLoading");
+
+ this.resetCounters();
+
+ if (dijit.byId("feedTree")) {
+ dijit.byId("feedTree").destroyRecursive();
+ }
+
+ const store = new dojo.data.ItemFileWriteStore({
+ url: "backend.php?op=pref_feeds&method=getfeedtree&mode=2"
+ });
+
+ // noinspection JSUnresolvedFunction
+ const treeModel = new fox.FeedStoreModel({
+ store: store,
+ query: {
+ "type": App.getInitParam('enable_feed_cats') == 1 ? "category" : "feed"
+ },
+ rootId: "root",
+ rootLabel: "Feeds",
+ childrenAttrs: ["items"]
+ });
+
+ // noinspection JSUnresolvedFunction
+ const tree = new fox.FeedTree({
+ model: treeModel,
+ onClick: function (item/*, node*/) {
+ const id = String(item.id);
+ const is_cat = id.match("^CAT:");
+ const feed = id.substr(id.indexOf(":") + 1);
+ Feeds.open({feed: feed, is_cat: is_cat});
+ return false;
+ },
+ openOnClick: false,
+ showRoot: false,
+ persist: true,
+ id: "feedTree",
+ }, "feedTree");
+
+ const tmph = dojo.connect(dijit.byId('feedMenu'), '_openMyself', function (event) {
+ console.log(dijit.getEnclosingWidget(event.target));
+ dojo.disconnect(tmph);
+ });
+
+ $("feeds-holder").appendChild(tree.domNode);
+
+ const tmph2 = dojo.connect(tree, 'onLoad', function () {
+ dojo.disconnect(tmph2);
+ Element.hide("feedlistLoading");
+
+ try {
+ Feeds.init();
+ App.setLoadingProgress(25);
+ } catch (e) {
+ App.Error.report(e);
+ }
+ });
+
+ tree.startup();
+ } catch (e) {
+ App.Error.report(e);
+ }
+ },
+ init: function() {
+ console.log("in feedlist init");
+
+ App.setLoadingProgress(50);
+
+ document.onkeydown = (event) => { App.hotkeyHandler(event) };
+ window.setInterval(() => { Headlines.catchupBatched() }, 10 * 1000);
+
+ if (!this.getActive()) {
+ this.open({feed: -3});
+ } else {
+ this.open({feed: this.getActive(), is_cat: this.activeIsCat()});
+ }
+
+ this.hideOrShowFeeds(App.getInitParam("hide_read_feeds") == 1);
+
+ if (App.getInitParam("is_default_pw")) {
+ console.warn("user password is at default value");
+
+ const dialog = new dijit.Dialog({
+ title: __("Your password is at default value"),
+ href: "backend.php?op=dlg&method=defaultpasswordwarning",
+ id: 'infoBox',
+ style: "width: 600px",
+ onCancel: function () {
+ return true;
+ },
+ onExecute: function () {
+ return true;
+ },
+ onClose: function () {
+ return true;
+ }
+ });
+
+ dialog.show();
+ }
+
+ // bw_limit disables timeout() so we request initial counters separately
+ if (App.getInitParam("bw_limit") == "1") {
+ this.requestCounters(true);
+ } else {
+ setTimeout(() => {
+ this.requestCounters(true);
+ setInterval(() => { this.requestCounters(); }, 60 * 1000)
+ }, 250);
+ }
+ },
+ activeIsCat: function() {
+ return !!this._active_feed_is_cat;
+ },
+ getActive: function() {
+ return this._active_feed_id;
+ },
+ setActive: function(id, is_cat) {
+ hash_set('f', id);
+ hash_set('c', is_cat ? 1 : 0);
+
+ this._active_feed_id = id;
+ this._active_feed_is_cat = is_cat;
+
+ $("headlines-frame").setAttribute("feed-id", id);
+ $("headlines-frame").setAttribute("is-cat", is_cat ? 1 : 0);
+
+ this.select(id, is_cat);
+
+ PluginHost.run(PluginHost.HOOK_FEED_SET_ACTIVE, [this._active_feed_id, this._active_feed_is_cat]);
+ },
+ select: function(feed, is_cat) {
+ const tree = dijit.byId("feedTree");
+
+ if (tree) return tree.selectFeed(feed, is_cat);
+ },
+ toggleUnread: function() {
+ const hide = !(App.getInitParam("hide_read_feeds") == "1");
+
+ xhrPost("backend.php", {op: "rpc", method: "setpref", key: "HIDE_READ_FEEDS", value: hide}, () => {
+ this.hideOrShowFeeds(hide);
+ App.setInitParam("hide_read_feeds", hide);
+ });
+ },
+ hideOrShowFeeds: function(hide) {
+ const tree = dijit.byId("feedTree");
+
+ if (tree)
+ return tree.hideRead(hide, App.getInitParam("hide_read_shows_special"));
+ },
+ open: function(params) {
+ const feed = params.feed;
+ const is_cat = !!params.is_cat || false;
+ const offset = params.offset || 0;
+ const viewfeed_debug = params.viewfeed_debug;
+ const method = params.method;
+ // this is used to quickly switch between feeds, sets active but xhr is on a timeout
+ const delayed = params.delayed || false;
+
+ if (feed != this.getActive() || this.activeIsCat() != is_cat) {
+ this._search_query = false;
+ Article.setActive(0);
+ }
+
+ if (offset != 0) {
+ if (this.infscroll_in_progress)
+ return;
+
+ this.infscroll_in_progress = 1;
+
+ window.clearTimeout(this._infscroll_timeout);
+ this._infscroll_timeout = window.setTimeout(() => {
+ console.log('infscroll request timed out, aborting');
+ this.infscroll_in_progress = 0;
+
+ // call scroll handler to maybe repeat infscroll request
+ Headlines.scrollHandler();
+ }, 10 * 1000);
+ }
+
+ Form.enable("main_toolbar_form");
+
+ let query = Object.assign({op: "feeds", method: "view", feed: feed},
+ dojo.formToObject("main_toolbar_form"));
+
+ if (method) query.m = method;
+
+ if (offset > 0) {
+ if (Headlines.current_first_id) {
+ query.fid = Headlines.current_first_id;
+ }
+ }
+
+ if (this._search_query) {
+ query = Object.assign(query, this._search_query);
+ }
+
+ if (offset != 0) {
+ query.skip = offset;
+
+ // to prevent duplicate feed titles when showing grouped vfeeds
+ if (Headlines.vgroup_last_feed != undefined) {
+ query.vgrlf = Headlines.vgroup_last_feed;
+ }
+ } else if (!is_cat && feed == this.getActive() && !params.method) {
+ query.m = "ForceUpdate";
+ }
+
+ Form.enable("main_toolbar_form");
+
+ if (!delayed)
+ if (!this.setExpando(feed, is_cat,
+ (is_cat) ? 'images/indicator_tiny.gif' : 'images/indicator_white.gif'))
+ Notify.progress("Loading, please wait...", true);
+
+ query.cat = is_cat;
+
+ this.setActive(feed, is_cat);
+
+ if (viewfeed_debug) {
+ window.open("backend.php?" +
+ dojo.objectToQuery(
+ Object.assign({debug: 1, csrf_token: App.getInitParam("csrf_token")}, query)
+ ));
+ }
+
+ window.clearTimeout(this._viewfeed_wait_timeout);
+ this._viewfeed_wait_timeout = window.setTimeout(() => {
+ Headlines.catchupBatched(() => {
+ xhrPost("backend.php", query, (transport) => {
+ try {
+ window.clearTimeout(this._infscroll_timeout);
+ this.setExpando(feed, is_cat, 'images/blank_icon.gif');
+ Headlines.onLoaded(transport, offset);
+ PluginHost.run(PluginHost.HOOK_FEED_LOADED, [feed, is_cat]);
+ } catch (e) {
+ App.Error.report(e);
+ }
+ });
+ });
+ }, delayed ? 250 : 0);
+ },
+ catchupAll: function() {
+ const str = __("Mark all articles as read?");
+
+ if (App.getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
+
+ Notify.progress("Marking all feeds as read...");
+
+ xhrPost("backend.php", {op: "feeds", method: "catchupAll"}, () => {
+ this.requestCounters(true);
+ this.reloadCurrent();
+ });
+
+ App.global_unread = 0;
+ App.updateTitle();
+ }
+ },
+ decrementFeedCounter: function(feed, is_cat) {
+ let ctr = this.getUnread(feed, is_cat);
+
+ if (ctr > 0) {
+ this.setUnread(feed, is_cat, ctr - 1);
+ App.global_unread -= 1;
+ App.updateTitle();
+
+ if (!is_cat) {
+ const cat = parseInt(this.getCategory(feed));
+
+ if (!isNaN(cat)) {
+ ctr = this.getUnread(cat, true);
+
+ if (ctr > 0) {
+ this.setUnread(cat, true, ctr - 1);
+ }
+ }
+ }
+ }
+ },
+ catchupFeed: function(feed, is_cat, mode) {
+ if (is_cat == undefined) is_cat = false;
+
+ let str = false;
+
+ switch (mode) {
+ case "1day":
+ str = __("Mark %w in %s older than 1 day as read?");
+ break;
+ case "1week":
+ str = __("Mark %w in %s older than 1 week as read?");
+ break;
+ case "2week":
+ str = __("Mark %w in %s older than 2 weeks as read?");
+ break;
+ default:
+ str = __("Mark %w in %s as read?");
+ }
+
+ const mark_what = this.last_search_query && this.last_search_query[0] ? __("search results") : __("all articles");
+ const fn = this.getName(feed, is_cat);
+
+ str = str.replace("%s", fn)
+ .replace("%w", mark_what);
+
+ if (App.getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
+ return;
+ }
+
+ const catchup_query = {
+ op: 'rpc', method: 'catchupFeed', feed_id: feed,
+ is_cat: is_cat, mode: mode, search_query: this.last_search_query[0],
+ search_lang: this.last_search_query[1]
+ };
+
+ Notify.progress("Loading, please wait...", true);
+
+ xhrPost("backend.php", catchup_query, (transport) => {
+ App.handleRpcJson(transport);
+
+ const show_next_feed = App.getInitParam("on_catchup_show_next_feed") == "1";
+
+ if (show_next_feed) {
+ const nuf = this.getNextUnread(feed, is_cat);
+
+ if (nuf) {
+ this.open({feed: nuf, is_cat: is_cat});
+ }
+ } else if (feed == this.getActive() && is_cat == this.activeIsCat()) {
+ this.reloadCurrent();
+ }
+
+ Notify.close();
+ });
+ },
+ catchupCurrent: function(mode) {
+ this.catchupFeed(this.getActive(), this.activeIsCat(), mode);
+ },
+ catchupFeedInGroup: function(id) {
+ const title = this.getName(id);
+
+ const str = __("Mark all articles in %s as read?").replace("%s", title);
+
+ if (App.getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
+
+ const rows = $$("#headlines-frame > div[id*=RROW][data-orig-feed-id='" + id + "']");
+
+ if (rows.length > 0) {
+
+ rows.each(function (row) {
+ row.removeClassName("Unread");
+
+ if (row.getAttribute("data-article-id") != Article.getActive()) {
+ new Effect.Fade(row, {duration: 0.5});
+ }
+
+ });
+
+ const feedTitles = $$("#headlines-frame > div[class='feed-title']");
+
+ for (let i = 0; i < feedTitles.length; i++) {
+ if (feedTitles[i].getAttribute("data-feed-id") == id) {
+
+ if (i < feedTitles.length - 1) {
+ new Effect.Fade(feedTitles[i], {duration: 0.5});
+ }
+
+ break;
+ }
+ }
+
+ Headlines.updateFloatingTitle(true);
+ }
+
+ Notify.progress("Loading, please wait...", true);
+
+ xhrPost("backend.php", {op: "rpc", method: "catchupFeed", feed_id: id, is_cat: false}, (transport) => {
+ App.handleRpcJson(transport);
+ });
+ }
+ },
+ getUnread: function(feed, is_cat) {
+ try {
+ const tree = dijit.byId("feedTree");
+
+ if (tree && tree.model)
+ return tree.model.getFeedUnread(feed, is_cat);
+
+ } catch (e) {
+ //
+ }
+
+ return -1;
+ },
+ getCategory: function(feed) {
+ try {
+ const tree = dijit.byId("feedTree");
+
+ if (tree && tree.model)
+ return tree.getFeedCategory(feed);
+
+ } catch (e) {
+ //
+ }
+
+ return false;
+ },
+ getName: function(feed, is_cat) {
+ if (isNaN(feed)) return feed; // it's a tag
+
+ const tree = dijit.byId("feedTree");
+
+ if (tree && tree.model)
+ return tree.model.getFeedValue(feed, is_cat, 'name');
+ },
+ setUnread: function(feed, is_cat, unread) {
+ const tree = dijit.byId("feedTree");
+
+ if (tree && tree.model)
+ return tree.model.setFeedUnread(feed, is_cat, unread);
+ },
+ setValue: function(feed, is_cat, key, value) {
+ try {
+ const tree = dijit.byId("feedTree");
+
+ if (tree && tree.model)
+ return tree.model.setFeedValue(feed, is_cat, key, value);
+
+ } catch (e) {
+ //
+ }
+ },
+ getValue: function(feed, is_cat, key) {
+ try {
+ const tree = dijit.byId("feedTree");
+
+ if (tree && tree.model)
+ return tree.model.getFeedValue(feed, is_cat, key);
+
+ } catch (e) {
+ //
+ }
+ return '';
+ },
+ setIcon: function(feed, is_cat, src) {
+ const tree = dijit.byId("feedTree");
+
+ if (tree) return tree.setFeedIcon(feed, is_cat, src);
+ },
+ setExpando: function(feed, is_cat, src) {
+ const tree = dijit.byId("feedTree");
+
+ if (tree) return tree.setFeedExpandoIcon(feed, is_cat, src);
+
+ return false;
+ },
+ getNextUnread: function(feed, is_cat) {
+ const tree = dijit.byId("feedTree");
+ const nuf = tree.model.getNextUnreadFeed(feed, is_cat);
+
+ if (nuf)
+ return tree.model.store.getValue(nuf, 'bare_id');
+ },
+ search: function() {
+ const query = "backend.php?op=feeds&method=search&param=" +
+ encodeURIComponent(Feeds.getActive() + ":" + Feeds.activeIsCat());
+
+ if (dijit.byId("searchDlg"))
+ dijit.byId("searchDlg").destroyRecursive();
+
+ const dialog = new dijit.Dialog({
+ id: "searchDlg",
+ title: __("Search"),
+ style: "width: 600px",
+ execute: function () {
+ if (this.validate()) {
+ Feeds._search_query = this.attr('value');
+ this.hide();
+ Feeds.reloadCurrent();
+ }
+ },
+ href: query
+ });
+
+ dialog.show();
+ },
+ updateRandom: function() {
+ console.log("in update_random_feed");
+
+ xhrPost("backend.php", {op: "rpc", method: "updateRandom"}, (transport) => {
+ App.handleRpcJson(transport, true);
+ });
+ },
+ };
+
+ return Feeds;
+});
diff --git a/js/Headlines.js b/js/Headlines.js
new file mode 100755
index 000000000..fbe1bd98a
--- /dev/null
+++ b/js/Headlines.js
@@ -0,0 +1,1215 @@
+'use strict';
+/* global __, ngettext */
+define(["dojo/_base/declare"], function (declare) {
+ Headlines = {
+ vgroup_last_feed: undefined,
+ _headlines_scroll_timeout: 0,
+ loaded_article_ids: [],
+ current_first_id: 0,
+ catchup_id_batch: [],
+ click: function (event, id, in_body) {
+ in_body = in_body || false;
+
+ if (App.isCombinedMode()) {
+
+ if (!in_body && (event.ctrlKey || id == Article.getActive() || App.getInitParam("cdm_expanded"))) {
+ Article.openInNewWindow(id);
+ }
+
+ Article.setActive(id);
+
+ if (!App.getInitParam("cdm_expanded"))
+ Article.cdmScrollToId(id);
+
+ return in_body;
+
+ } else {
+ if (event.ctrlKey) {
+ Article.openInNewWindow(id);
+ Article.setActive(id);
+ } else {
+ Article.view(id);
+ }
+
+ return false;
+ }
+ },
+ initScrollHandler: function () {
+ $("headlines-frame").onscroll = (event) => {
+ clearTimeout(this._headlines_scroll_timeout);
+ this._headlines_scroll_timeout = window.setTimeout(function () {
+ //console.log('done scrolling', event);
+ Headlines.scrollHandler();
+ }, 50);
+ }
+ },
+ loadMore: function () {
+ const view_mode = document.forms["main_toolbar_form"].view_mode.value;
+ const unread_in_buffer = $$("#headlines-frame > div[id*=RROW][class*=Unread]").length;
+ const num_all = $$("#headlines-frame > div[id*=RROW]").length;
+ const num_unread = Feeds.getUnread(Feeds.getActive(), Feeds.activeIsCat());
+
+ // TODO implement marked & published
+
+ let offset = num_all;
+
+ switch (view_mode) {
+ case "marked":
+ case "published":
+ console.warn("loadMore: ", view_mode, "not implemented");
+ break;
+ case "unread":
+ offset = unread_in_buffer;
+ break;
+ case "adaptive":
+ if (!(Feeds.getActive() == -1 && !Feeds.activeIsCat()))
+ offset = num_unread > 0 ? unread_in_buffer : num_all;
+ break;
+ }
+
+ console.log("loadMore, offset=", offset);
+
+ Feeds.open({feed: Feeds.getActive(), is_cat: Feeds.activeIsCat(), offset: offset});
+ },
+ scrollHandler: function () {
+ try {
+ Headlines.unpackVisible();
+
+ if (App.isCombinedMode()) {
+ Headlines.updateFloatingTitle();
+
+ // set topmost child in the buffer as active, but not if we're at the beginning (to prevent auto marking
+ // first article as read all the time)
+ if ($("headlines-frame").scrollTop != 0 &&
+ App.getInitParam("cdm_expanded") && App.getInitParam("cdm_auto_catchup") == 1) {
+
+ const rows = $$("#headlines-frame > div[id*=RROW]");
+
+ for (let i = 0; i < rows.length; i++) {
+ const row = rows[i];
+
+ if ($("headlines-frame").scrollTop <= row.offsetTop &&
+ row.offsetTop - $("headlines-frame").scrollTop < 100 &&
+ row.getAttribute("data-article-id") != Article.getActive()) {
+
+ Article.setActive(row.getAttribute("data-article-id"));
+ break;
+ }
+ }
+ }
+ }
+
+ if (!Feeds.infscroll_disabled) {
+ const hsp = $("headlines-spacer");
+ const container = $("headlines-frame");
+
+ if (hsp && hsp.offsetTop - 250 <= container.scrollTop + container.offsetHeight) {
+
+ hsp.innerHTML = "<span class='loading'><img src='images/indicator_tiny.gif'> " +
+ __("Loading, please wait...") + "</span>";
+
+ Headlines.loadMore();
+ return;
+ }
+ }
+
+ if (App.getInitParam("cdm_auto_catchup") == 1) {
+
+ let rows = $$("#headlines-frame > div[id*=RROW][class*=Unread]");
+
+ for (let i = 0; i < rows.length; i++) {
+ const row = rows[i];
+
+ if ($("headlines-frame").scrollTop > (row.offsetTop + row.offsetHeight / 2)) {
+ const id = row.getAttribute("data-article-id")
+
+ if (this.catchup_id_batch.indexOf(id) == -1)
+ this.catchup_id_batch.push(id);
+
+ } else {
+ break;
+ }
+ }
+
+ if (Feeds.infscroll_disabled) {
+ const row = $$("#headlines-frame div[id*=RROW]").last();
+
+ if (row && $("headlines-frame").scrollTop >
+ (row.offsetTop + row.offsetHeight - 50)) {
+
+ console.log("we seem to be at an end");
+
+ if (App.getInitParam("on_catchup_show_next_feed") == "1") {
+ Feeds.openNextUnread();
+ }
+ }
+ }
+ }
+ } catch (e) {
+ console.warn("scrollHandler", e);
+ }
+ },
+ updateFloatingTitle: function (unread_only) {
+ if (!App.isCombinedMode()/* || !App.getInitParam("cdm_expanded")*/) return;
+
+ const hf = $("headlines-frame");
+ const elems = $$("#headlines-frame > div[id*=RROW]");
+ const ft = $("floatingTitle");
+
+ for (let i = 0; i < elems.length; i++) {
+ const row = elems[i];
+
+ if (row && row.offsetTop + row.offsetHeight > hf.scrollTop) {
+
+ const header = row.select(".header")[0];
+ const id = row.getAttribute("data-article-id");
+
+ if (unread_only || id != ft.getAttribute("data-article-id")) {
+ if (id != ft.getAttribute("data-article-id")) {
+
+ ft.setAttribute("data-article-id", id);
+ ft.innerHTML = header.innerHTML;
+ ft.firstChild.innerHTML = "<img class='anchor marked-pic' src='images/page_white_go.png' " +
+ "onclick=\"Article.cdmScrollToId(" + id + ", true)\">" + ft.firstChild.innerHTML;
+
+ this.initFloatingMenu();
+
+ const cb = ft.select(".rchk")[0];
+
+ if (cb)
+ cb.parentNode.removeChild(cb);
+ }
+
+ if (row.hasClassName("Unread"))
+ ft.addClassName("Unread");
+ else
+ ft.removeClassName("Unread");
+
+ PluginHost.run(PluginHost.HOOK_FLOATING_TITLE, row);
+ }
+
+ ft.style.marginRight = hf.offsetWidth - row.offsetWidth + "px";
+
+ if (header.offsetTop + header.offsetHeight < hf.scrollTop + ft.offsetHeight - 5 &&
+ row.offsetTop + row.offsetHeight >= hf.scrollTop + ft.offsetHeight - 5)
+ new Effect.Appear(ft, {duration: 0.3});
+ else
+ Element.hide(ft);
+
+ return;
+ }
+ }
+ },
+ unpackVisible: function () {
+ if (!App.isCombinedMode() || !App.getInitParam("cdm_expanded")) return;
+
+ const rows = $$("#headlines-frame div[id*=RROW][data-content]");
+ const threshold = $("headlines-frame").scrollTop + $("headlines-frame").offsetHeight + 600;
+
+ for (let i = 0; i < rows.length; i++) {
+ const row = rows[i];
+
+ if (row.offsetTop <= threshold) {
+ console.log("unpacking: " + row.id);
+
+ row.select(".content-inner")[0].innerHTML = row.getAttribute("data-content");
+ row.removeAttribute("data-content");
+
+ PluginHost.run(PluginHost.HOOK_ARTICLE_RENDERED_CDM, row);
+ } else {
+ break;
+ }
+ }
+ },
+ onLoaded: function (transport, offset) {
+ const reply = App.handleRpcJson(transport);
+
+ console.log("Headlines.onLoaded: offset=", offset);
+
+ let is_cat = false;
+ let feed_id = false;
+
+ if (reply) {
+
+ is_cat = reply['headlines']['is_cat'];
+ feed_id = reply['headlines']['id'];
+ Feeds.last_search_query = reply['headlines']['search_query'];
+
+ if (feed_id != -7 && (feed_id != Feeds.getActive() || is_cat != Feeds.activeIsCat()))
+ return;
+
+ try {
+ if (offset == 0) {
+ $("headlines-frame").scrollTop = 0;
+
+ Element.hide("floatingTitle");
+ $("floatingTitle").setAttribute("data-article-id", 0);
+ $("floatingTitle").innerHTML = "";
+ }
+ } catch (e) {
+ }
+
+ $("headlines-frame").removeClassName("cdm");
+ $("headlines-frame").removeClassName("normal");
+
+ $("headlines-frame").addClassName(App.isCombinedMode() ? "cdm" : "normal");
+
+ const headlines_count = reply['headlines-info']['count'];
+ Feeds.infscroll_disabled = parseInt(headlines_count) != 30;
+
+ console.log('received', headlines_count, 'headlines, infscroll disabled=', Feeds.infscroll_disabled);
+
+ this.vgroup_last_feed = reply['headlines-info']['vgroup_last_feed'];
+ this.current_first_id = reply['headlines']['first_id'];
+
+ if (offset == 0) {
+ this.loaded_article_ids = [];
+
+ dojo.html.set($("headlines-toolbar"),
+ reply['headlines']['toolbar'],
+ {parseContent: true});
+
+ $("headlines-frame").innerHTML = '';
+
+ let tmp = document.createElement("div");
+ tmp.innerHTML = reply['headlines']['content'];
+ dojo.parser.parse(tmp);
+
+ while (tmp.hasChildNodes()) {
+ const row = tmp.removeChild(tmp.firstChild);
+
+ if (this.loaded_article_ids.indexOf(row.id) == -1 || row.hasClassName("feed-title")) {
+ dijit.byId("headlines-frame").domNode.appendChild(row);
+
+ this.loaded_article_ids.push(row.id);
+ }
+ }
+
+ let hsp = $("headlines-spacer");
+
+ if (!hsp) {
+ hsp = document.createElement("div");
+ hsp.id = "headlines-spacer";
+ }
+
+ dijit.byId('headlines-frame').domNode.appendChild(hsp);
+
+ this.initHeadlinesMenu();
+
+ if (Feeds.infscroll_disabled)
+ hsp.innerHTML = "<a href='#' onclick='Feeds.openNextUnread()'>" +
+ __("Click to open next unread feed.") + "</a>";
+
+ if (Feeds._search_query) {
+ $("feed_title").innerHTML += "<span id='cancel_search'>" +
+ " (<a href='#' onclick='Feeds.cancelSearch()'>" + __("Cancel search") + "</a>)" +
+ "</span>";
+ }
+
+ } else if (headlines_count > 0 && feed_id == Feeds.getActive() && is_cat == Feeds.activeIsCat()) {
+ const c = dijit.byId("headlines-frame");
+ //const ids = Headlines.getSelected();
+
+ let hsp = $("headlines-spacer");
+
+ if (hsp)
+ c.domNode.removeChild(hsp);
+
+ let tmp = document.createElement("div");
+ tmp.innerHTML = reply['headlines']['content'];
+ dojo.parser.parse(tmp);
+
+ while (tmp.hasChildNodes()) {
+ let row = tmp.removeChild(tmp.firstChild);
+
+ if (this.loaded_article_ids.indexOf(row.id) == -1 || row.hasClassName("feed-title")) {
+ dijit.byId("headlines-frame").domNode.appendChild(row);
+
+ this.loaded_article_ids.push(row.id);
+ }
+ }
+
+ if (!hsp) {
+ hsp = document.createElement("div");
+ hsp.id = "headlines-spacer";
+ }
+
+ c.domNode.appendChild(hsp);
+
+ /* console.log("restore selected ids: " + ids);
+
+ for (let i = 0; i < ids.length; i++) {
+ markHeadline(ids[i]);
+ } */
+
+ this.initHeadlinesMenu();
+
+ if (Feeds.infscroll_disabled) {
+ hsp.innerHTML = "<a href='#' onclick='Feeds.openNextUnread()'>" +
+ __("Click to open next unread feed.") + "</a>";
+ }
+
+ } else {
+ console.log("no new headlines received");
+
+ const first_id_changed = reply['headlines']['first_id_changed'];
+ console.log("first id changed:" + first_id_changed);
+
+ let hsp = $("headlines-spacer");
+
+ if (hsp) {
+ if (first_id_changed) {
+ hsp.innerHTML = "<a href='#' onclick='Feeds.reloadCurrent()'>" +
+ __("New articles found, reload feed to continue.") + "</a>";
+ } else {
+ hsp.innerHTML = "<a href='#' onclick='Feeds.openNextUnread()'>" +
+ __("Click to open next unread feed.") + "</a>";
+ }
+ }
+ }
+
+ } else {
+ console.error("Invalid object received: " + transport.responseText);
+ dijit.byId("headlines-frame").attr('content', "<div class='whiteBox'>" +
+ __('Could not update headlines (invalid object received - see error console for details)') +
+ "</div>");
+ }
+
+ Feeds.infscroll_in_progress = 0;
+
+ // this is used to auto-catchup articles if needed after infscroll request has finished,
+ // unpack visible articles, fill buffer more, etc
+ this.scrollHandler();
+
+ Notify.close();
+ },
+ reverse: function () {
+ const toolbar = document.forms["main_toolbar_form"];
+ const order_by = dijit.getEnclosingWidget(toolbar.order_by);
+
+ let value = order_by.attr('value');
+
+ if (value == "date_reverse")
+ value = "default";
+ else
+ value = "date_reverse";
+
+ order_by.attr('value', value);
+
+ Feeds.reloadCurrent();
+ },
+ selectionToggleUnread: function (params) {
+ params = params || {};
+
+ const cmode = params.cmode || 2;
+ const callback = params.callback;
+ const no_error = params.no_error || false;
+ const ids = params.ids || Headlines.getSelected();
+
+ if (ids.length == 0) {
+ if (!no_error)
+ alert(__("No articles selected."));
+
+ return;
+ }
+
+ ids.each((id) => {
+ const row = $("RROW-" + id);
+
+ if (row) {
+ switch (cmode) {
+ case 0:
+ row.removeClassName("Unread");
+ break;
+ case 1:
+ row.addClassName("Unread");
+ break;
+ case 2:
+ row.toggleClassName("Unread");
+ }
+ }
+ });
+
+ const query = {
+ op: "rpc", method: "catchupSelected",
+ cmode: cmode, ids: ids.toString()
+ };
+
+ Notify.progress("Loading, please wait...");
+
+ xhrPost("backend.php", query, (transport) => {
+ App.handleRpcJson(transport);
+ if (callback) callback(transport);
+ });
+ },
+ selectionToggleMarked: function (ids) {
+ const rows = ids || Headlines.getSelected();
+
+ if (rows.length == 0) {
+ alert(__("No articles selected."));
+ return;
+ }
+
+ for (let i = 0; i < rows.length; i++) {
+ this.toggleMark(rows[i], true, true);
+ }
+
+ const query = {
+ op: "rpc", method: "markSelected",
+ ids: rows.toString(), cmode: 2
+ };
+
+ xhrPost("backend.php", query, (transport) => {
+ App.handleRpcJson(transport);
+ });
+ },
+ selectionTogglePublished: function (ids) {
+ const rows = ids || Headlines.getSelected();
+
+ if (rows.length == 0) {
+ alert(__("No articles selected."));
+ return;
+ }
+
+ for (let i = 0; i < rows.length; i++) {
+ this.togglePub(rows[i], true);
+ }
+
+ if (rows.length > 0) {
+ const query = {
+ op: "rpc", method: "publishSelected",
+ ids: rows.toString(), cmode: 2
+ };
+
+ xhrPost("backend.php", query, (transport) => {
+ App.handleRpcJson(transport);
+ });
+ }
+ },
+ toggleMark: function (id, client_only) {
+ const query = {op: "rpc", id: id, method: "mark"};
+ const row = $("RROW-" + id);
+
+ if (row) {
+ const imgs = $$("img[class*=marked-pic][class*=marked-" + id + "]");
+
+ imgs.each((img) => {
+ if (!row.hasClassName("marked")) {
+ img.src = img.src.replace("mark_unset", "mark_set");
+ query.mark = 1;
+ } else {
+ img.src = img.src.replace("mark_set", "mark_unset");
+ query.mark = 0;
+ }
+ });
+
+ row.toggleClassName("marked");
+
+ if (!client_only)
+ xhrPost("backend.php", query, (transport) => {
+ App.handleRpcJson(transport);
+ });
+ }
+ },
+ togglePub: function (id, client_only) {
+ const row = $("RROW-" + id);
+
+ if (row) {
+ const query = {op: "rpc", id: id, method: "publ"};
+
+ const imgs = $$("img[class*=pub-pic][class*=pub-" + id + "]");
+
+ imgs.each((img) => {
+ if (!row.hasClassName("published")) {
+ img.src = img.src.replace("pub_unset", "pub_set");
+ query.pub = 1;
+ } else {
+ img.src = img.src.replace("pub_set", "pub_unset");
+ query.pub = 0;
+ }
+ });
+
+ row.toggleClassName("published");
+
+ if (!client_only)
+ xhrPost("backend.php", query, (transport) => {
+ App.handleRpcJson(transport);
+ });
+
+ }
+ },
+ move: function (mode, noscroll, noexpand) {
+ const rows = Headlines.getLoaded();
+
+ let prev_id = false;
+ let next_id = false;
+
+ if (!$('RROW-' + Article.getActive())) {
+ Article.setActive(0);
+ }
+
+ if (!Article.getActive()) {
+ next_id = rows[0];
+ prev_id = rows[rows.length - 1]
+ } else {
+ for (let i = 0; i < rows.length; i++) {
+ if (rows[i] == Article.getActive()) {
+
+ // 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] != Article.getActive()) {
+ next_id = rows[j];
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ console.log("cur: " + Article.getActive() + " next: " + next_id);
+
+ if (mode == "next") {
+ if (next_id || Article.getActive()) {
+ if (App.isCombinedMode()) {
+
+ const article = $("RROW-" + Article.getActive());
+ const ctr = $("headlines-frame");
+
+ if (!noscroll && article && article.offsetTop + article.offsetHeight >
+ ctr.scrollTop + ctr.offsetHeight) {
+
+ Article.scroll(ctr.offsetHeight / 4);
+
+ } else if (next_id) {
+ Article.setActive(next_id);
+ Article.cdmScrollToId(next_id, true);
+ }
+
+ } else if (next_id) {
+ Headlines.correctHeadlinesOffset(next_id);
+ Article.view(next_id, noexpand);
+ }
+ }
+ }
+
+ if (mode == "prev") {
+ if (prev_id || Article.getActive()) {
+ if (App.isCombinedMode()) {
+
+ const article = $("RROW-" + Article.getActive());
+ const prev_article = $("RROW-" + prev_id);
+ const ctr = $("headlines-frame");
+
+ if (!noscroll && article && article.offsetTop < ctr.scrollTop) {
+ Article.scroll(-ctr.offsetHeight / 3);
+ } else if (!noscroll && prev_article &&
+ prev_article.offsetTop < ctr.scrollTop) {
+ Article.scroll(-ctr.offsetHeight / 4);
+ } else if (prev_id) {
+ Article.setActive(prev_id);
+ Article.cdmScrollToId(prev_id, noscroll);
+ }
+
+ } else if (prev_id) {
+ Headlines.correctHeadlinesOffset(prev_id);
+ Article.view(prev_id, noexpand);
+ }
+ }
+ }
+ },
+ updateSelectedPrompt: function () {
+ const count = Headlines.getSelected().length;
+ const elem = $("selected_prompt");
+
+ if (elem) {
+ elem.innerHTML = ngettext("%d article selected",
+ "%d articles selected", count).replace("%d", count);
+
+ count > 0 ? Element.show(elem) : Element.hide(elem);
+ }
+ },
+ toggleUnread: function (id, cmode) {
+ const row = $("RROW-" + id);
+
+ if (row) {
+ const origClassName = row.className;
+
+ if (cmode == undefined) cmode = 2;
+
+ switch (cmode) {
+ case 0:
+ row.removeClassName("Unread");
+ break;
+ case 1:
+ row.addClassName("Unread");
+ break;
+ case 2:
+ row.toggleClassName("Unread");
+ break;
+ }
+
+ if (row.className != origClassName)
+ xhrPost("backend.php",
+ {op: "rpc", method: "catchupSelected", cmode: cmode, ids: id}, (transport) => {
+ App.handleRpcJson(transport);
+ });
+ }
+ },
+ selectionRemoveLabel: function (id, ids) {
+ if (!ids) ids = Headlines.getSelected();
+
+ if (ids.length == 0) {
+ alert(__("No articles selected."));
+ return;
+ }
+
+ const query = {
+ op: "article", method: "removeFromLabel",
+ ids: ids.toString(), lid: id
+ };
+
+ xhrPost("backend.php", query, (transport) => {
+ App.handleRpcJson(transport);
+ this.onLabelsUpdated(transport);
+ });
+ },
+ selectionAssignLabel: function (id, ids) {
+ if (!ids) ids = Headlines.getSelected();
+
+ if (ids.length == 0) {
+ alert(__("No articles selected."));
+ return;
+ }
+
+ const query = {
+ op: "article", method: "assignToLabel",
+ ids: ids.toString(), lid: id
+ };
+
+ xhrPost("backend.php", query, (transport) => {
+ App.handleRpcJson(transport);
+ this.onLabelsUpdated(transport);
+ });
+ },
+ deleteSelection: function () {
+ const rows = Headlines.getSelected();
+
+ if (rows.length == 0) {
+ alert(__("No articles selected."));
+ return;
+ }
+
+ const fn = Feeds.getName(Feeds.getActive(), Feeds.activeIsCat());
+ let str;
+
+ 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);
+ }
+
+ str = str.replace("%d", rows.length);
+ str = str.replace("%s", fn);
+
+ if (App.getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
+ return;
+ }
+
+ const query = {op: "rpc", method: "delete", ids: rows.toString()};
+
+ xhrPost("backend.php", query, (transport) => {
+ App.handleRpcJson(transport);
+ Feeds.reloadCurrent();
+ });
+ },
+ getSelected: function () {
+ const rv = [];
+
+ $$("#headlines-frame > div[id*=RROW][class*=Selected]").each(
+ function (child) {
+ rv.push(child.getAttribute("data-article-id"));
+ });
+
+ // consider active article a honorary member of selected articles
+ if (Article.getActive())
+ rv.push(Article.getActive());
+
+ return rv.uniq();
+ },
+ getLoaded: function () {
+ const rv = [];
+
+ const children = $$("#headlines-frame > div[id*=RROW-]");
+
+ children.each(function (child) {
+ if (Element.visible(child)) {
+ rv.push(child.getAttribute("data-article-id"));
+ }
+ });
+
+ return rv;
+ },
+ onRowChecked: function (elem) {
+ // account for dojo checkboxes
+ elem = elem.domNode || elem;
+
+ elem.up("div[id*=RROW]").toggleClassName("Selected");
+
+ this.updateSelectedPrompt();
+ },
+ select: function (mode) {
+ // mode = all,none,unread,invert,marked,published
+ let query = "#headlines-frame > div[id*=RROW]";
+
+ switch (mode) {
+ case "none":
+ case "all":
+ case "invert":
+ break;
+ case "marked":
+ query += "[class*=marked]";
+ break;
+ case "published":
+ query += "[class*=published]";
+ break;
+ case "unread":
+ query += "[class*=Unread]";
+ break;
+ default:
+ console.warn("select: unknown mode", mode);
+ }
+
+ const rows = $$(query);
+
+ for (let i = 0; i < rows.length; i++) {
+ const row = rows[i];
+ const cb = dijit.getEnclosingWidget(row.select(".rchk")[0]);
+
+ switch (mode) {
+ case "none":
+ row.removeClassName("Selected");
+
+ if (!row.hasClassName("active"))
+ cb.attr("checked", false);
+ break;
+ case "invert":
+ if (row.hasClassName("Selected")) {
+ row.removeClassName("Selected");
+
+ if (!row.hasClassName("active"))
+ cb.attr("checked", false);
+ } else {
+ row.addClassName("Selected");
+ cb.attr("checked", true);
+ }
+ break;
+ default:
+ row.addClassName("Selected");
+ cb.attr("checked", true);
+ }
+
+ Headlines.updateSelectedPrompt();
+ }
+ },
+ archiveSelection: function () {
+ const rows = Headlines.getSelected();
+
+ if (rows.length == 0) {
+ alert(__("No articles selected."));
+ return;
+ }
+
+ const fn = Feeds.getName(Feeds.getActive(), Feeds.activeIsCat());
+ let str;
+ let op;
+
+ if (Feeds.getActive() != 0) {
+ str = ngettext("Archive %d selected article in %s?", "Archive %d selected articles in %s?", rows.length);
+ op = "archive";
+ } else {
+ str = ngettext("Move %d archived article back?", "Move %d archived articles back?", rows.length);
+ str += " " + __("Please note that unstarred articles might get purged on next feed update.");
+
+ op = "unarchive";
+ }
+
+ str = str.replace("%d", rows.length);
+ str = str.replace("%s", fn);
+
+ if (App.getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
+ return;
+ }
+
+ for (let i = 0; i < rows.length; i++) {
+ ArticleCache.del(rows[i]);
+ }
+
+ const query = {op: "rpc", method: op, ids: rows.toString()};
+
+ xhrPost("backend.php", query, (transport) => {
+ App.handleRpcJson(transport);
+ Feeds.reloadCurrent();
+ });
+ },
+ catchupSelection: function () {
+ const rows = Headlines.getSelected();
+
+ if (rows.length == 0) {
+ alert(__("No articles selected."));
+ return;
+ }
+
+ const fn = Feeds.getName(Feeds.getActive(), Feeds.activeIsCat());
+
+ let str = ngettext("Mark %d selected article in %s as read?", "Mark %d selected articles in %s as read?", rows.length);
+
+ str = str.replace("%d", rows.length);
+ str = str.replace("%s", fn);
+
+ if (App.getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
+ return;
+ }
+
+ Headlines.selectionToggleUnread({callback: Feeds.reloadCurrent, no_error: 1});
+ },
+ catchupBatched: function (callback) {
+ console.log("catchupBatched, size=", this.catchup_id_batch.length);
+
+ if (this.catchup_id_batch.length > 0) {
+
+ // make a copy of the array
+ const batch = this.catchup_id_batch.slice();
+ const query = {
+ op: "rpc", method: "catchupSelected",
+ cmode: 0, ids: batch.toString()
+ };
+
+ xhrPost("backend.php", query, (transport) => {
+ const reply = App.handleRpcJson(transport);
+
+ if (reply) {
+ const batch = reply.ids;
+
+ batch.each(function (id) {
+ const elem = $("RROW-" + id);
+ if (elem) elem.removeClassName("Unread");
+ Headlines.catchup_id_batch.remove(id);
+ });
+ }
+
+ Headlines.updateFloatingTitle(true);
+
+ if (callback) callback();
+ });
+ } else {
+ if (callback) callback();
+ }
+ },
+ catchupRelativeTo: function (below, id) {
+
+ if (!id) id = Article.getActive();
+
+ if (!id) {
+ alert(__("No article is selected."));
+ return;
+ }
+
+ const visible_ids = this.getLoaded();
+
+ const ids_to_mark = [];
+
+ if (!below) {
+ for (let i = 0; i < visible_ids.length; i++) {
+ if (visible_ids[i] != id) {
+ const e = $("RROW-" + visible_ids[i]);
+
+ if (e && e.hasClassName("Unread")) {
+ ids_to_mark.push(visible_ids[i]);
+ }
+ } else {
+ break;
+ }
+ }
+ } else {
+ for (let i = visible_ids.length - 1; i >= 0; i--) {
+ if (visible_ids[i] != id) {
+ const e = $("RROW-" + visible_ids[i]);
+
+ if (e && e.hasClassName("Unread")) {
+ ids_to_mark.push(visible_ids[i]);
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ 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)) {
+
+ for (var i = 0; i < ids_to_mark.length; i++) {
+ var e = $("RROW-" + ids_to_mark[i]);
+ e.removeClassName("Unread");
+ }
+
+ const query = {
+ op: "rpc", method: "catchupSelected",
+ cmode: 0, ids: ids_to_mark.toString()
+ };
+
+ xhrPost("backend.php", query, (transport) => {
+ App.handleRpcJson(transport);
+ });
+ }
+ }
+ },
+ onLabelsUpdated: function (transport) {
+ const data = JSON.parse(transport.responseText);
+
+ if (data) {
+ data['info-for-headlines'].each(function (elem) {
+ $$(".HLLCTR-" + elem.id).each(function (ctr) {
+ ctr.innerHTML = elem.labels;
+ });
+ });
+ }
+ },
+ onActionChanged: function (elem) {
+ eval(elem.value);
+ elem.attr('value', 'false');
+ },
+ correctHeadlinesOffset: function (id) {
+ const container = $("headlines-frame");
+ const row = $("RROW-" + id);
+
+ if (!container || !row) return;
+
+ const viewport = container.offsetHeight;
+
+ const rel_offset_top = row.offsetTop - container.scrollTop;
+ const rel_offset_bottom = row.offsetTop + row.offsetHeight - container.scrollTop;
+
+ //console.log("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom);
+ //console.log("Vport: " + viewport);
+
+ if (rel_offset_top <= 0 || rel_offset_top > viewport) {
+ container.scrollTop = row.offsetTop;
+ } else if (rel_offset_bottom > viewport) {
+ container.scrollTop = row.offsetTop + row.offsetHeight - viewport;
+ }
+ },
+ initFloatingMenu: function () {
+ if (!dijit.byId("floatingMenu")) {
+
+ const menu = new dijit.Menu({
+ id: "floatingMenu",
+ targetNodeIds: ["floatingTitle"]
+ });
+
+ this.headlinesMenuCommon(menu);
+
+ menu.startup();
+ }
+ },
+ headlinesMenuCommon: function (menu) {
+
+ menu.addChild(new dijit.MenuItem({
+ label: __("Open original article"),
+ onClick: function (event) {
+ Article.openInNewWindow(this.getParent().currentTarget.getAttribute("data-article-id"));
+ }
+ }));
+
+ menu.addChild(new dijit.MenuItem({
+ label: __("Display article URL"),
+ onClick: function (event) {
+ Article.displayUrl(this.getParent().currentTarget.getAttribute("data-article-id"));
+ }
+ }));
+
+ menu.addChild(new dijit.MenuSeparator());
+
+ menu.addChild(new dijit.MenuItem({
+ label: __("Toggle unread"),
+ onClick: function () {
+
+ 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];
+
+ Headlines.selectionToggleUnread({ids: ids, no_error: 1});
+ }
+ }));
+
+ menu.addChild(new dijit.MenuItem({
+ label: __("Toggle starred"),
+ onClick: function () {
+ 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];
+
+ Headlines.selectionToggleMarked(ids);
+ }
+ }));
+
+ menu.addChild(new dijit.MenuItem({
+ label: __("Toggle published"),
+ onClick: function () {
+ 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];
+
+ Headlines.selectionTogglePublished(ids);
+ }
+ }));
+
+ menu.addChild(new dijit.MenuSeparator());
+
+ menu.addChild(new dijit.MenuItem({
+ label: __("Mark above as read"),
+ onClick: function () {
+ Headlines.catchupRelativeTo(0, 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"));
+ }
+ }));
+
+
+ const labels = App.getInitParam("labels");
+
+ if (labels && labels.length) {
+
+ menu.addChild(new dijit.MenuSeparator());
+
+ const labelAddMenu = new dijit.Menu({ownerMenu: menu});
+ const labelDelMenu = new dijit.Menu({ownerMenu: menu});
+
+ labels.each(function (label) {
+ const bare_id = label.id;
+ const name = label.caption;
+
+ labelAddMenu.addChild(new dijit.MenuItem({
+ label: name,
+ labelId: bare_id,
+ onClick: function () {
+
+ 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];
+
+ Headlines.selectionAssignLabel(this.labelId, ids);
+ }
+ }));
+
+ labelDelMenu.addChild(new dijit.MenuItem({
+ label: name,
+ labelId: bare_id,
+ onClick: function () {
+ 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];
+
+ Headlines.selectionRemoveLabel(this.labelId, ids);
+ }
+ }));
+
+ });
+
+ menu.addChild(new dijit.PopupMenuItem({
+ label: __("Assign label"),
+ popup: labelAddMenu
+ }));
+
+ menu.addChild(new dijit.PopupMenuItem({
+ label: __("Remove label"),
+ popup: labelDelMenu
+ }));
+
+ }
+ },
+ initHeadlinesMenu: function () {
+ if (!dijit.byId("headlinesMenu")) {
+
+ const menu = new dijit.Menu({
+ id: "headlinesMenu",
+ targetNodeIds: ["headlines-frame"],
+ selector: ".hlMenuAttach"
+ });
+
+ this.headlinesMenuCommon(menu);
+
+ menu.startup();
+ }
+
+ /* vgroup feed title menu */
+
+ if (!dijit.byId("headlinesFeedTitleMenu")) {
+
+ const menu = new dijit.Menu({
+ id: "headlinesFeedTitleMenu",
+ targetNodeIds: ["headlines-frame"],
+ selector: "div.cdmFeedTitle"
+ });
+
+ menu.addChild(new dijit.MenuItem({
+ label: __("Select articles in group"),
+ onClick: function (event) {
+ Headlines.select("all",
+ "#headlines-frame > div[id*=RROW]" +
+ "[data-orig-feed-id='" + this.getParent().currentTarget.getAttribute("data-feed-id") + "']");
+
+ }
+ }));
+
+ menu.addChild(new dijit.MenuItem({
+ label: __("Mark group as read"),
+ onClick: function () {
+ Headlines.select("none");
+ Headlines.select("all",
+ "#headlines-frame > div[id*=RROW]" +
+ "[data-orig-feed-id='" + this.getParent().currentTarget.getAttribute("data-feed-id") + "']");
+
+ Headlines.catchupSelection();
+ }
+ }));
+
+ menu.addChild(new dijit.MenuItem({
+ label: __("Mark feed as read"),
+ onClick: function () {
+ Feeds.catchupFeedInGroup(this.getParent().currentTarget.getAttribute("data-feed-id"));
+ }
+ }));
+
+ menu.addChild(new dijit.MenuItem({
+ label: __("Edit feed"),
+ onClick: function () {
+ CommonDialogs.editFeed(this.getParent().currentTarget.getAttribute("data-feed-id"));
+ }
+ }));
+
+ menu.startup();
+ }
+ }
+ }
+
+ return Headlines;
+}); \ No newline at end of file
diff --git a/js/PluginHost.js b/js/PluginHost.js
index a41694f34..8e5ff32ec 100644
--- a/js/PluginHost.js
+++ b/js/PluginHost.js
@@ -1,6 +1,6 @@
// based on http://www.velvetcache.org/2010/08/19/a-simple-javascript-hooks-system
-const PluginHost = {
+PluginHost = {
HOOK_ARTICLE_RENDERED: 1,
HOOK_ARTICLE_RENDERED_CDM: 2,
HOOK_ARTICLE_SET_ACTIVE: 3,
@@ -19,10 +19,10 @@ const PluginHost = {
this.hooks[name].push(callback);
},
run: function (name, args) {
- console.warn('PluginHost::run ' + name);
+ //console.warn('PluginHost::run ' + name);
if (typeof(this.hooks[name]) != 'undefined')
- for (var i = 0; i < this.hooks[name].length; i++)
+ for (let i = 0; i < this.hooks[name].length; i++)
if (!this.hooks[name][i](args)) break;
}
};
diff --git a/js/PrefFeedTree.js b/js/PrefFeedTree.js
index 9d6a9e86c..accc4f0e5 100644
--- a/js/PrefFeedTree.js
+++ b/js/PrefFeedTree.js
@@ -35,14 +35,14 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree"], functio
menu.addChild(new dijit.MenuItem({
label: __("Edit category"),
onClick: function() {
- editCat(this.getParent().row_id, this.getParent().item, null);
+ dijit.byId("feedTree").editCategory(this.getParent().row_id, this.getParent().item, null);
}}));
menu.addChild(new dijit.MenuItem({
label: __("Remove category"),
onClick: function() {
- removeCategory(this.getParent().row_id, this.getParent().item);
+ dijit.byId("feedTree").removeCategory(this.getParent().row_id, this.getParent().item);
}}));
menu.bindDomNode(tnode.domNode);
@@ -55,13 +55,13 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree"], functio
menu.addChild(new dijit.MenuItem({
label: __("Edit feed"),
onClick: function() {
- editFeed(this.getParent().row_id);
+ CommonDialogs.editFeed(this.getParent().row_id);
}}));
menu.addChild(new dijit.MenuItem({
label: __("Unsubscribe"),
onClick: function() {
- unsubscribeFeed(this.getParent().row_id, this.getParent().item.name);
+ CommonDialogs.unsubscribeFeed(this.getParent().row_id, this.getParent().item.name);
}}));
menu.bindDomNode(tnode.domNode);
@@ -82,6 +82,15 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree"], functio
getIconClass: function (item, opened) {
return (!item || this.model.store.getValue(item, 'type') == 'category') ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feedIcon";
},
+ reload: function() {
+ const searchElem = $("feed_search");
+ let search = (searchElem) ? searchElem.value : "";
+
+ xhrPost("backend.php", { op: "pref-feeds", search: search }, (transport) => {
+ dijit.byId('feedConfigTab').attr('content', transport.responseText);
+ Notify.close();
+ });
+ },
checkItemAcceptance: function(target, source, position) {
const item = dijit.getEnclosingWidget(target).item;
@@ -109,6 +118,291 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree"], functio
(id.match("root") && position == "over"));
}
},
+ resetFeedOrder: function() {
+ Notify.progress("Loading, please wait...");
+
+ xhrPost("backend.php", {op: "pref-feeds", method: "feedsortreset"}, () => {
+ this.reload();
+ });
+ },
+ resetCatOrder: function() {
+ Notify.progress("Loading, please wait...");
+
+ xhrPost("backend.php", {op: "pref-feeds", method: "catsortreset"}, () => {
+ this.reload();
+ });
+ },
+ removeCategory: function(id, item) {
+ if (confirm(__("Remove category %s? Any nested feeds would be placed into Uncategorized.").replace("%s", item.name))) {
+ Notify.progress("Removing category...");
+
+ xhrPost("backend.php", {op: "pref-feeds", method: "removeCat", ids: id}, () => {
+ Notify.close();
+ this.reload();
+ });
+ }
+ },
+ removeSelectedFeeds: function() {
+ const sel_rows = this.getSelectedFeeds();
+
+ if (sel_rows.length > 0) {
+ if (confirm(__("Unsubscribe from selected feeds?"))) {
+
+ Notify.progress("Unsubscribing from selected feeds...", true);
+
+ const query = {
+ op: "pref-feeds", method: "remove",
+ ids: sel_rows.toString()
+ };
+
+ xhrPost("backend.php", query, () => {
+ this.reload();
+ });
+ }
+
+ } else {
+ alert(__("No feeds selected."));
+ }
+
+ return false;
+ },
+ checkInactiveFeeds: function() {
+ xhrPost("backend.php", {op: "pref-feeds", method: "getinactivefeeds"}, (transport) => {
+ if (parseInt(transport.responseText) > 0) {
+ Element.show(dijit.byId("pref_feeds_inactive_btn").domNode);
+ }
+ });
+ },
+ getSelectedCategories: function() {
+ const tree = this;
+ const items = tree.model.getCheckedItems();
+ const rv = [];
+
+ items.each(function (item) {
+ if (item.id[0].match("CAT:"))
+ rv.push(tree.model.store.getValue(item, 'bare_id'));
+ });
+
+ return rv;
+ },
+ removeSelectedCategories: function() {
+ const sel_rows = this.getSelectedCategories();
+
+ if (sel_rows.length > 0) {
+ if (confirm(__("Remove selected categories?"))) {
+ Notify.progress("Removing selected categories...");
+
+ const query = {
+ op: "pref-feeds", method: "removeCat",
+ ids: sel_rows.toString()
+ };
+
+ xhrPost("backend.php", query, () => {
+ this.reload();
+ });
+ }
+ } else {
+ alert(__("No categories selected."));
+ }
+
+ return false;
+ },
+ getSelectedFeeds: function() {
+ const tree = this;
+ const items = tree.model.getCheckedItems();
+ const rv = [];
+
+ items.each(function (item) {
+ if (item.id[0].match("FEED:"))
+ rv.push(tree.model.store.getValue(item, 'bare_id'));
+ });
+
+ return rv;
+ },
+ editSelectedFeed: function() {
+ const rows = this.getSelectedFeeds();
+
+ if (rows.length == 0) {
+ alert(__("No feeds selected."));
+ return;
+ }
+
+ Notify.close();
+
+ if (rows.length > 1) {
+ return this.editMultiple();
+ } else {
+ CommonDialogs.editFeed(rows[0], {});
+ }
+ },
+ editMultiple: function() {
+ const rows = this.getSelectedFeeds();
+
+ if (rows.length == 0) {
+ alert(__("No feeds selected."));
+ return;
+ }
+
+ Notify.progress("Loading, please wait...");
+
+ if (dijit.byId("feedEditDlg"))
+ dijit.byId("feedEditDlg").destroyRecursive();
+
+ xhrPost("backend.php", {op: "pref-feeds", method: "editfeeds", ids: rows.toString()}, (transport) => {
+ Notify.close();
+
+ const dialog = new dijit.Dialog({
+ id: "feedEditDlg",
+ title: __("Edit Multiple Feeds"),
+ style: "width: 600px",
+ getChildByName: function (name) {
+ let rv = null;
+ this.getChildren().each(
+ function (child) {
+ if (child.name == name) {
+ rv = child;
+ return;
+ }
+ });
+ return rv;
+ },
+ toggleField: function (checkbox, elem, label) {
+ this.getChildByName(elem).attr('disabled', !checkbox.checked);
+
+ if ($(label))
+ if (checkbox.checked)
+ $(label).removeClassName('insensitive');
+ else
+ $(label).addClassName('insensitive');
+
+ },
+ execute: function () {
+ if (this.validate() && confirm(__("Save changes to selected feeds?"))) {
+ const query = this.attr('value');
+
+ /* normalize unchecked checkboxes because [] is not serialized */
+
+ Object.keys(query).each((key) => {
+ let val = query[key];
+
+ if (typeof val == "object" && val.length == 0)
+ query[key] = ["off"];
+ });
+
+ Notify.progress("Saving data...", true);
+
+ xhrPost("backend.php", query, () => {
+ dialog.hide();
+ dijit.byId("feedTree").reload();
+ });
+ }
+ },
+ content: transport.responseText
+ });
+
+ dialog.show();
+ });
+ },
+ editCategory: function(id, item) {
+ // uncategorized
+ if (String(item.id) == "CAT:0")
+ return;
+
+ const new_name = prompt(__('Rename category to:'), item.name);
+
+ if (new_name && new_name != item.name) {
+
+ Notify.progress("Loading, please wait...");
+
+ xhrPost("backend.php", { op: 'pref-feeds', method: 'renamecat', id: id, title: new_name }, () => {
+ this.reload();
+ });
+ }
+ },
+ createCategory: function() {
+ const title = prompt(__("Category title:"));
+
+ if (title) {
+ Notify.progress("Creating category...");
+
+ xhrPost("backend.php", {op: "pref-feeds", method: "addCat", cat: title}, () => {
+ Notify.close();
+ this.reload();
+ });
+ }
+ },
+ batchSubscribe: function() {
+ const query = "backend.php?op=pref-feeds&method=batchSubscribe";
+
+ // overlapping widgets
+ if (dijit.byId("batchSubDlg")) dijit.byId("batchSubDlg").destroyRecursive();
+ if (dijit.byId("feedAddDlg")) dijit.byId("feedAddDlg").destroyRecursive();
+
+ const dialog = new dijit.Dialog({
+ id: "batchSubDlg",
+ title: __("Batch subscribe"),
+ style: "width: 600px",
+ execute: function () {
+ if (this.validate()) {
+ Notify.progress(__("Subscribing to feeds..."), true);
+
+ xhrPost("backend.php", this.attr('value'), () => {
+ Notify.close();
+ dijit.byId("feedTree").reload();
+ dialog.hide();
+ });
+ }
+ },
+ href: query
+ });
+
+ dialog.show();
+ },
+ showInactiveFeeds: function() {
+ const query = "backend.php?op=pref-feeds&method=inactiveFeeds";
+
+ if (dijit.byId("inactiveFeedsDlg"))
+ dijit.byId("inactiveFeedsDlg").destroyRecursive();
+
+ const dialog = new dijit.Dialog({
+ id: "inactiveFeedsDlg",
+ title: __("Feeds without recent updates"),
+ style: "width: 600px",
+ getSelectedFeeds: function () {
+ return Tables.getSelected("prefInactiveFeedList");
+ },
+ removeSelected: function () {
+ const sel_rows = this.getSelectedFeeds();
+
+ if (sel_rows.length > 0) {
+ if (confirm(__("Remove selected feeds?"))) {
+ Notify.progress("Removing selected feeds...", true);
+
+ const query = {
+ op: "pref-feeds", method: "remove",
+ ids: sel_rows.toString()
+ };
+
+ xhrPost("backend.php", query, () => {
+ Notify.close();
+ dijit.byId("feedTree").reload();
+ dialog.hide();
+ });
+ }
+
+ } else {
+ alert(__("No feeds selected."));
+ }
+ },
+ execute: function () {
+ if (this.validate()) {
+ }
+ },
+ href: query
+ });
+
+ dialog.show();
+ }
});
});
diff --git a/js/PrefFilterTree.js b/js/PrefFilterTree.js
index 1167365d6..4f4e02d95 100644
--- a/js/PrefFilterTree.js
+++ b/js/PrefFilterTree.js
@@ -75,8 +75,190 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree"], functio
this.inherited(arguments);
this.tree.model.store.save();
},
- });
+ getSelectedFilters: function() {
+ const tree = this;
+ const items = tree.model.getCheckedItems();
+ const rv = [];
+ items.each(function (item) {
+ rv.push(tree.model.store.getValue(item, 'bare_id'));
+ });
+
+ return rv;
+ },
+ reload: function() {
+ const user_search = $("filter_search");
+ let search = "";
+ if (user_search) { search = user_search.value; }
+
+ xhrPost("backend.php", { op: "pref-filters", search: search }, (transport) => {
+ dijit.byId('filterConfigTab').attr('content', transport.responseText);
+ Notify.close();
+ });
+ },
+ resetFilterOrder: function() {
+ Notify.progress("Loading, please wait...");
+
+ xhrPost("backend.php", {op: "pref-filters", method: "filtersortreset"}, () => {
+ this.reload();
+ });
+ },
+ joinSelectedFilters: function() {
+ const rows = getSelectedFilters();
+
+ if (rows.length == 0) {
+ alert(__("No filters selected."));
+ return;
+ }
+
+ if (confirm(__("Combine selected filters?"))) {
+ Notify.progress("Joining filters...");
+
+ xhrPost("backend.php", {op: "pref-filters", method: "join", ids: rows.toString()}, () => {
+ this.reload();
+ });
+ }
+ },
+ editSelectedFilter: function() {
+ const rows = this.getSelectedFilters();
+
+ if (rows.length == 0) {
+ alert(__("No filters selected."));
+ return;
+ }
+
+ if (rows.length > 1) {
+ alert(__("Please select only one filter."));
+ return;
+ }
+
+ Notify.close();
+
+ this.editFilter(rows[0]);
+ },
+ editFilter: function(id) {
+
+ const query = "backend.php?op=pref-filters&method=edit&id=" + encodeURIComponent(id);
+
+ if (dijit.byId("feedEditDlg"))
+ dijit.byId("feedEditDlg").destroyRecursive();
+
+ if (dijit.byId("filterEditDlg"))
+ dijit.byId("filterEditDlg").destroyRecursive();
+
+ const dialog = new dijit.Dialog({
+ id: "filterEditDlg",
+ title: __("Edit Filter"),
+ style: "width: 600px",
+
+ test: function () {
+ const query = "backend.php?" + dojo.formToQuery("filter_edit_form") + "&savemode=test";
+
+ Filters.editFilterTest(query);
+ },
+ selectRules: function (select) {
+ $$("#filterDlg_Matches input[type=checkbox]").each(function (e) {
+ e.checked = select;
+ if (select)
+ e.parentNode.addClassName("Selected");
+ else
+ e.parentNode.removeClassName("Selected");
+ });
+ },
+ selectActions: function (select) {
+ $$("#filterDlg_Actions input[type=checkbox]").each(function (e) {
+ e.checked = select;
+
+ if (select)
+ e.parentNode.addClassName("Selected");
+ else
+ e.parentNode.removeClassName("Selected");
+
+ });
+ },
+ editRule: function (e) {
+ const li = e.parentNode;
+ const rule = li.getElementsByTagName("INPUT")[1].value;
+ Filters.addFilterRule(li, rule);
+ },
+ editAction: function (e) {
+ const li = e.parentNode;
+ const action = li.getElementsByTagName("INPUT")[1].value;
+ Filters.addFilterAction(li, action);
+ },
+ removeFilter: function () {
+ const msg = __("Remove filter?");
+
+ if (confirm(msg)) {
+ this.hide();
+
+ Notify.progress("Removing filter...");
+
+ const query = {op: "pref-filters", method: "remove", ids: this.attr('value').id};
+
+ xhrPost("backend.php", query, () => {
+ dijit.byId("filterTree").reload();
+ });
+ }
+ },
+ addAction: function () {
+ Filters.addFilterAction();
+ },
+ addRule: function () {
+ Filters.addFilterRule();
+ },
+ deleteAction: function () {
+ $$("#filterDlg_Actions li[class*=Selected]").each(function (e) {
+ e.parentNode.removeChild(e)
+ });
+ },
+ deleteRule: function () {
+ $$("#filterDlg_Matches li[class*=Selected]").each(function (e) {
+ e.parentNode.removeChild(e)
+ });
+ },
+ execute: function () {
+ if (this.validate()) {
+
+ Notify.progress("Saving data...", true);
+
+ xhrPost("backend.php", dojo.formToObject("filter_edit_form"), () => {
+ dialog.hide();
+ dijit.byId("filterTree").reload();
+ });
+ }
+ },
+ href: query
+ });
+
+ dialog.show();
+ },
+ removeSelectedFilters: function() {
+ const sel_rows = this.getSelectedFilters();
+
+ if (sel_rows.length > 0) {
+ if (confirm(__("Remove selected filters?"))) {
+ Notify.progress("Removing selected filters...");
+
+ const query = {
+ op: "pref-filters", method: "remove",
+ ids: sel_rows.toString()
+ };
+
+ xhrPost("backend.php", query, () => {
+ this.reload();
+ });
+ }
+ } else {
+ alert(__("No filters selected."));
+ }
+
+ return false;
+ },
+
+
+
+});
});
diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js
new file mode 100644
index 000000000..1b23ebea3
--- /dev/null
+++ b/js/PrefHelpers.js
@@ -0,0 +1,230 @@
+define(["dojo/_base/declare"], function (declare) {
+ Helpers = {
+ clearFeedAccessKeys: function() {
+ if (confirm(__("This will invalidate all previously generated feed URLs. Continue?"))) {
+ Notify.progress("Clearing URLs...");
+
+ xhrPost("backend.php", {op: "pref-feeds", method: "clearKeys"}, () => {
+ Notify.info("Generated URLs cleared.");
+ });
+ }
+
+ return false;
+ },
+ updateEventLog: function() {
+ xhrPost("backend.php", { op: "pref-system" }, (transport) => {
+ dijit.byId('systemConfigTab').attr('content', transport.responseText);
+ Notify.close();
+ });
+ },
+ clearEventLog: function() {
+ if (confirm(__("Clear event log?"))) {
+
+ Notify.progress("Loading, please wait...");
+
+ xhrPost("backend.php", {op: "pref-system", method: "clearLog"}, () => {
+ this.updateEventLog();
+ });
+ }
+ },
+ editProfiles: function() {
+
+ if (dijit.byId("profileEditDlg"))
+ dijit.byId("profileEditDlg").destroyRecursive();
+
+ const query = "backend.php?op=pref-prefs&method=editPrefProfiles";
+
+ // noinspection JSUnusedGlobalSymbols
+ const dialog = new dijit.Dialog({
+ id: "profileEditDlg",
+ title: __("Settings Profiles"),
+ style: "width: 600px",
+ getSelectedProfiles: function () {
+ return Tables.getSelected("prefFeedProfileList");
+ },
+ removeSelected: function () {
+ const sel_rows = this.getSelectedProfiles();
+
+ if (sel_rows.length > 0) {
+ if (confirm(__("Remove selected profiles? Active and default profiles will not be removed."))) {
+ Notify.progress("Removing selected profiles...", true);
+
+ const query = {
+ op: "rpc", method: "remprofiles",
+ ids: sel_rows.toString()
+ };
+
+ xhrPost("backend.php", query, () => {
+ Notify.close();
+ Helpers.editProfiles();
+ });
+ }
+
+ } else {
+ alert(__("No profiles selected."));
+ }
+ },
+ activateProfile: function () {
+ const sel_rows = this.getSelectedProfiles();
+
+ if (sel_rows.length == 1) {
+ if (confirm(__("Activate selected profile?"))) {
+ Notify.progress("Loading, please wait...");
+
+ xhrPost("backend.php", {op: "rpc", method: "setprofile", id: sel_rows.toString()}, () => {
+ window.location.reload();
+ });
+ }
+
+ } else {
+ alert(__("Please choose a profile to activate."));
+ }
+ },
+ addProfile: function () {
+ if (this.validate()) {
+ Notify.progress("Creating profile...", true);
+
+ const query = {op: "rpc", method: "addprofile", title: dialog.attr('value').newprofile};
+
+ xhrPost("backend.php", query, () => {
+ Notify.close();
+ Helpers.editProfiles();
+ });
+
+ }
+ },
+ execute: function () {
+ if (this.validate()) {
+ }
+ },
+ href: query
+ });
+
+ dialog.show();
+ },
+ customizeCSS: function() {
+ const query = "backend.php?op=pref-prefs&method=customizeCSS";
+
+ if (dijit.byId("cssEditDlg"))
+ dijit.byId("cssEditDlg").destroyRecursive();
+
+ const dialog = new dijit.Dialog({
+ id: "cssEditDlg",
+ title: __("Customize stylesheet"),
+ style: "width: 600px",
+ execute: function () {
+ Notify.progress('Saving data...', true);
+
+ xhrPost("backend.php", this.attr('value'), () => {
+ window.location.reload();
+ });
+
+ },
+ href: query
+ });
+
+ dialog.show();
+ },
+ confirmReset: function() {
+ if (confirm(__("Reset to defaults?"))) {
+ xhrPost("backend.php", {op: "pref-prefs", method: "resetconfig"}, (transport) => {
+ Helpers.refresh();
+ Notify.info(transport.responseText);
+ });
+ }
+ },
+ clearPluginData: function(name) {
+ if (confirm(__("Clear stored data for this plugin?"))) {
+ Notify.progress("Loading, please wait...");
+
+ xhrPost("backend.php", {op: "pref-prefs", method: "clearplugindata", name: name}, () => {
+ Helpers.refresh();
+ });
+ }
+ },
+ refresh: function() {
+ xhrPost("backend.php", { op: "pref-prefs" }, (transport) => {
+ dijit.byId('genConfigTab').attr('content', transport.responseText);
+ Notify.close();
+ });
+ },
+ OPML: {
+ import: function() {
+ const opml_file = $("opml_file");
+
+ if (opml_file.value.length == 0) {
+ alert(__("Please choose an OPML file first."));
+ return false;
+ } else {
+ Notify.progress("Importing, please wait...", true);
+
+ Element.show("upload_iframe");
+
+ return true;
+ }
+ },
+ onImportComplete: function(iframe) {
+ if (!iframe.contentDocument.body.innerHTML) return false;
+
+ Element.show(iframe);
+
+ Notify.close();
+
+ if (dijit.byId('opmlImportDlg'))
+ dijit.byId('opmlImportDlg').destroyRecursive();
+
+ const content = iframe.contentDocument.body.innerHTML;
+
+ const dialog = new dijit.Dialog({
+ id: "opmlImportDlg",
+ title: __("OPML Import"),
+ style: "width: 600px",
+ onCancel: function () {
+ window.location.reload();
+ },
+ execute: function () {
+ window.location.reload();
+ },
+ content: content
+ });
+
+ dojo.connect(dialog, "onShow", function () {
+ Element.hide(iframe);
+ });
+
+ dialog.show();
+ },
+ export: function() {
+ console.log("export");
+ window.open("backend.php?op=opml&method=export&" + dojo.formToQuery("opmlExportForm"));
+ },
+ changeKey: function() {
+ if (confirm(__("Replace current OPML publishing address with a new one?"))) {
+ Notify.progress("Trying to change address...", true);
+
+ xhrJson("backend.php", {op: "pref-feeds", method: "regenOPMLKey"}, (reply) => {
+ if (reply) {
+ const new_link = reply.link;
+ const e = $('pub_opml_url');
+
+ if (new_link) {
+ e.href = new_link;
+ e.innerHTML = new_link;
+
+ new Effect.Highlight(e);
+
+ Notify.close();
+
+ } else {
+ Notify.error("Could not change feed URL.");
+ }
+ }
+ });
+ }
+ return false;
+ },
+ }
+ };
+
+ return Helpers;
+});
diff --git a/js/PrefLabelTree.js b/js/PrefLabelTree.js
index 7321b80d8..f808e987b 100644
--- a/js/PrefLabelTree.js
+++ b/js/PrefLabelTree.js
@@ -37,7 +37,129 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dijit/f
getIconClass: function (item, opened) {
return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "invisible";
},
- });
+ getSelectedLabels: function() {
+ const tree = this;
+ const items = tree.model.getCheckedItems();
+ const rv = [];
+
+ items.each(function(item) {
+ rv.push(tree.model.store.getValue(item, 'bare_id'));
+ });
+
+ return rv;
+ },
+ reload: function() {
+ xhrPost("backend.php", { op: "pref-labels" }, (transport) => {
+ dijit.byId('labelConfigTab').attr('content', transport.responseText);
+ Notify.close();
+ });
+ },
+ editLabel: function(id) {
+ const query = "backend.php?op=pref-labels&method=edit&id=" +
+ encodeURIComponent(id);
+
+ if (dijit.byId("labelEditDlg"))
+ dijit.byId("labelEditDlg").destroyRecursive();
+
+ const dialog = new dijit.Dialog({
+ id: "labelEditDlg",
+ title: __("Label Editor"),
+ style: "width: 600px",
+ setLabelColor: function (id, fg, bg) {
+
+ let kind = '';
+ let color = '';
+
+ if (fg && bg) {
+ kind = 'both';
+ } else if (fg) {
+ kind = 'fg';
+ color = fg;
+ } else if (bg) {
+ kind = 'bg';
+ color = bg;
+ }
+
+ const e = $("LICID-" + id);
+
+ if (e) {
+ if (fg) e.style.color = fg;
+ if (bg) e.style.backgroundColor = bg;
+ }
+
+ const query = {
+ op: "pref-labels", method: "colorset", kind: kind,
+ ids: id, fg: fg, bg: bg, color: color
+ };
+
+ xhrPost("backend.php", query, () => {
+ dijit.byId("filterTree").reload(); // maybe there's labels in there
+ });
+
+ },
+ execute: function () {
+ if (this.validate()) {
+ const caption = this.attr('value').caption;
+ const fg_color = this.attr('value').fg_color;
+ const bg_color = this.attr('value').bg_color;
+
+ dijit.byId('labelTree').setNameById(id, caption);
+ this.setLabelColor(id, fg_color, bg_color);
+ this.hide();
+
+ xhrPost("backend.php", this.attr('value'), () => {
+ dijit.byId("filterTree").reload(); // maybe there's labels in there
+ });
+ }
+ },
+ href: query
+ });
+
+ dialog.show();
+ },
+ resetColors: function() {
+ const labels = this.getSelectedLabels();
+
+ if (labels.length > 0) {
+ if (confirm(__("Reset selected labels to default colors?"))) {
+
+ const query = {
+ op: "pref-labels", method: "colorreset",
+ ids: labels.toString()
+ };
+
+ xhrPost("backend.php", query, () => {
+ this.reload();
+ });
+ }
+
+ } else {
+ alert(__("No labels selected."));
+ }
+ },
+ removeSelected: function() {
+ const sel_rows = this.getSelectedLabels();
+
+ if (sel_rows.length > 0) {
+ if (confirm(__("Remove selected labels?"))) {
+ Notify.progress("Removing selected labels...");
+
+ const query = {
+ op: "pref-labels", method: "remove",
+ ids: sel_rows.toString()
+ };
+
+ xhrPost("backend.php", query, () => {
+ this.reload();
+ });
+ }
+ } else {
+ alert(__("No labels selected."));
+ }
+
+ return false;
+ }
+});
});
diff --git a/js/PrefUsers.js b/js/PrefUsers.js
new file mode 100644
index 000000000..4f24f67cf
--- /dev/null
+++ b/js/PrefUsers.js
@@ -0,0 +1,122 @@
+'use strict'
+/* global __, ngettext */
+define(["dojo/_base/declare"], function (declare) {
+ Users = {
+ reload: function(sort) {
+ const user_search = $("user_search");
+ const search = user_search ? user_search.value : "";
+
+ xhrPost("backend.php", { op: "pref-users", sort: sort, search: search }, (transport) => {
+ dijit.byId('userConfigTab').attr('content', transport.responseText);
+ Notify.close();
+ });
+ },
+ add: function() {
+ const login = prompt(__("Please enter username:"), "");
+
+ if (login) {
+ Notify.progress("Adding user...");
+
+ xhrPost("backend.php", {op: "pref-users", method: "add", login: login}, (transport) => {
+ alert(transport.responseText);
+ Users.reload();
+ });
+
+ }
+ },
+ edit: function(id) {
+ const query = "backend.php?op=pref-users&method=edit&id=" +
+ encodeURIComponent(id);
+
+ if (dijit.byId("userEditDlg"))
+ dijit.byId("userEditDlg").destroyRecursive();
+
+ const dialog = new dijit.Dialog({
+ id: "userEditDlg",
+ title: __("User Editor"),
+ style: "width: 600px",
+ execute: function () {
+ if (this.validate()) {
+ Notify.progress("Saving data...", true);
+
+ xhrPost("backend.php", dojo.formToObject("user_edit_form"), (transport) => {
+ dialog.hide();
+ Users.reload();
+ });
+ }
+ },
+ href: query
+ });
+
+ dialog.show();
+ },
+ resetSelected: function() {
+ const rows = this.getSelection();
+
+ if (rows.length == 0) {
+ alert(__("No users selected."));
+ return;
+ }
+
+ if (rows.length > 1) {
+ alert(__("Please select one user."));
+ return;
+ }
+
+ if (confirm(__("Reset password of selected user?"))) {
+ Notify.progress("Resetting password for selected user...");
+
+ const id = rows[0];
+
+ xhrPost("backend.php", {op: "pref-users", method: "resetPass", id: id}, (transport) => {
+ Notify.close();
+ alert(transport.responseText);
+ });
+
+ }
+ },
+ removeSelected: function() {
+ const sel_rows = this.getSelection();
+
+ if (sel_rows.length > 0) {
+ if (confirm(__("Remove selected users? Neither default admin nor your account will be removed."))) {
+ Notify.progress("Removing selected users...");
+
+ const query = {
+ op: "pref-users", method: "remove",
+ ids: sel_rows.toString()
+ };
+
+ xhrPost("backend.php", query, () => {
+ this.reload();
+ });
+ }
+
+ } else {
+ alert(__("No users selected."));
+ }
+ },
+ editSelected: function() {
+ const rows = this.getSelection();
+
+ if (rows.length == 0) {
+ alert(__("No users selected."));
+ return;
+ }
+
+ if (rows.length > 1) {
+ alert(__("Please select one user."));
+ return;
+ }
+
+ this.edit(rows[0]);
+ },
+ getSelection :function() {
+ return Tables.getSelected("prefUserList");
+ }
+ }
+
+ return Users;
+});
+
+
diff --git a/js/common.js b/js/common.js
new file mode 100755
index 000000000..427e3034c
--- /dev/null
+++ b/js/common.js
@@ -0,0 +1,311 @@
+'use strict'
+/* global dijit, __ */
+
+let _label_base_index = -1024;
+let loading_progress = 0;
+
+/* error reporting shim */
+
+// TODO: deprecated; remove
+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 */
+
+function xhrPost(url, params, complete) {
+ console.log("xhrPost:", params);
+ return new Ajax.Request(url, {
+ parameters: params,
+ onComplete: complete
+ });
+}
+
+function xhrJson(url, params, complete) {
+ return xhrPost(url, params, (reply) => {
+ try {
+ const obj = JSON.parse(reply.responseText);
+ complete(obj);
+ } catch (e) {
+ console.error("xhrJson", e, reply);
+ complete(null);
+ }
+
+ })
+}
+
+/* add method to remove element from array */
+Array.prototype.remove = function(s) {
+ for (let i=0; i < this.length; i++) {
+ if (s == this[i]) this.splice(i, 1);
+ }
+};
+
+/* common helpers not worthy of separate Dojo modules */
+
+const Lists = {
+ onRowChecked: function(elem) {
+ const checked = elem.domNode ? elem.attr("checked") : elem.checked;
+ // account for dojo checkboxes
+ elem = elem.domNode || elem;
+
+ const row = elem.up("li");
+
+ if (row)
+ checked ? row.addClassName("Selected") : row.removeClassName("Selected");
+ }
+};
+
+// noinspection JSUnusedGlobalSymbols
+const Tables = {
+ onRowChecked: function(elem) {
+ // account for dojo checkboxes
+ const checked = elem.domNode ? elem.attr("checked") : elem.checked;
+ elem = elem.domNode || elem;
+
+ const row = elem.up("tr");
+
+ if (row)
+ checked ? row.addClassName("Selected") : row.removeClassName("Selected");
+
+ },
+ select: function(elemId, selected) {
+ $(elemId).select("tr").each((row) => {
+ const checkNode = row.select(".dijitCheckBox,input[type=checkbox]")[0];
+ if (checkNode) {
+ const widget = dijit.getEnclosingWidget(checkNode);
+
+ if (widget) {
+ widget.attr("checked", selected);
+ } else {
+ checkNode.checked = selected;
+ }
+
+ this.onRowChecked(widget);
+ }
+ });
+ },
+ getSelected: function(elemId) {
+ const rv = [];
+
+ $(elemId).select("tr").each((row) => {
+ if (row.hasClassName("Selected")) {
+ // either older prefix-XXX notation or separate attribute
+ const rowId = row.getAttribute("data-row-id") || row.id.replace(/^[A-Z]*?-/, "");
+
+ if (!isNaN(rowId))
+ rv.push(parseInt(rowId));
+ }
+ });
+
+ return rv;
+ }
+};
+
+const Cookie = {
+ set: function (name, value, lifetime) {
+ const d = new Date();
+ d.setTime(d.getTime() + lifetime * 1000);
+ const expires = "expires=" + d.toUTCString();
+ document.cookie = name + "=" + encodeURIComponent(value) + "; " + expires;
+ },
+ get: function (name) {
+ name = name + "=";
+ 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));
+ }
+ return "";
+ },
+ delete: function(name) {
+ const expires = "expires=Thu, 01-Jan-1970 00:00:01 GMT";
+ document.cookie = name + "=" + "" + "; " + expires;
+ }
+};
+
+/* runtime notifications */
+
+const Notify = {
+ KIND_GENERIC: 0,
+ KIND_INFO: 1,
+ KIND_ERROR: 2,
+ KIND_PROGRESS: 3,
+ timeout: 0,
+ default_timeout: 5 * 1000,
+ close: function() {
+ this.msg("");
+ },
+ msg: function(msg, keep, kind) {
+ kind = kind || this.KIND_GENERIC;
+ keep = keep || false;
+
+ const notify = $("notify");
+
+ window.clearTimeout(this.timeout);
+
+ if (!msg) {
+ notify.removeClassName("visible");
+ return;
+ }
+
+ let msgfmt = "<span class=\"msg\">%s</span>".replace("%s", __(msg));
+ let icon = false;
+
+
+ notify.className = "notify";
+
+ console.warn('notify', msg, kind);
+
+ switch (kind) {
+ case this.KIND_INFO:
+ notify.addClassName("notify_info")
+ icon = App.getInitParam("icon_information");
+ break;
+ case this.KIND_ERROR:
+ notify.addClassName("notify_error");
+ icon = App.getInitParam("icon_alert");
+ break;
+ case this.KIND_PROGRESS:
+ notify.addClassName("notify_progress");
+ icon = App.getInitParam("icon_indicator_white")
+ break;
+ }
+
+ if (icon) msgfmt = "<span><img src=\"%s\"></span>".replace("%s", icon) + msgfmt;
+
+ msgfmt += (" <span><img src=\"%s\" class='close' title=\"" +
+ __("Click to close") + "\" onclick=\"Notify.close()\"></span>")
+ .replace("%s", App.getInitParam("icon_cross"));
+
+ notify.innerHTML = msgfmt;
+ notify.addClassName("visible");
+
+ if (!keep)
+ this.timeout = window.setTimeout(() => {
+ notify.removeClassName("visible");
+ }, this.default_timeout);
+
+ },
+ info: function(msg, keep) {
+ keep = keep || false;
+ this.msg(msg, keep, this.KIND_INFO);
+ },
+ progress: function(msg, keep) {
+ keep = keep || true;
+ this.msg(msg, keep, this.KIND_PROGRESS);
+ },
+ error: function(msg, keep) {
+ keep = keep || true;
+ this.msg(msg, keep, this.KIND_ERROR);
+ }
+};
+
+// noinspection JSUnusedGlobalSymbols
+function displayIfChecked(checkbox, elemId) {
+ if (checkbox.checked) {
+ Effect.Appear(elemId, {duration : 0.5});
+ } else {
+ Effect.Fade(elemId, {duration : 0.5});
+ }
+}
+
+function fatalError(code, msg, ext_info) {
+ if (code == 6) {
+ window.location.href = "index.php";
+ } else if (code == 5) {
+ window.location.href = "public.php?op=dbupdate";
+ } else {
+
+ if (msg == "") msg = "Unknown error";
+
+ if (ext_info) {
+ if (ext_info.responseText) {
+ ext_info = ext_info.responseText;
+ }
+ }
+
+ /* global ERRORS */
+ if (ERRORS && ERRORS[code] && !msg) {
+ msg = ERRORS[code];
+ }
+
+ let content = "<div><b>Error code:</b> " + code + "</div>" +
+ "<p>" + msg + "</p>";
+
+ if (ext_info) {
+ content = content + "<div><b>Additional information:</b></div>" +
+ "<textarea style='width: 100%' readonly=\"1\">" +
+ ext_info + "</textarea>";
+ }
+
+ const dialog = new dijit.Dialog({
+ title: "Fatal error",
+ style: "width: 600px",
+ content: content});
+
+ dialog.show();
+
+ }
+
+ return false;
+
+}
+
+/* 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
+function getSelectionText() {
+ let text = "";
+
+ if (typeof window.getSelection != "undefined") {
+ const sel = window.getSelection();
+ if (sel.rangeCount) {
+ const container = document.createElement("div");
+ for (let i = 0, len = sel.rangeCount; i < len; ++i) {
+ container.appendChild(sel.getRangeAt(i).cloneContents());
+ }
+ text = container.innerHTML;
+ }
+ } else if (typeof document.selection != "undefined") {
+ if (document.selection.type == "Text") {
+ text = document.selection.createRange().textText;
+ }
+ }
+
+ 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");
+
+ 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");
+}
diff --git a/js/feedlist.js b/js/feedlist.js
deleted file mode 100644
index 575aee29a..000000000
--- a/js/feedlist.js
+++ /dev/null
@@ -1,532 +0,0 @@
-let infscroll_in_progress = 0;
-let infscroll_disabled = 0;
-
-let _infscroll_timeout = false;
-let _search_query = false;
-let _viewfeed_wait_timeout = false;
-
-let counters_last_request = 0;
-let _counters_prev = [];
-
-function resetCounterCache() {
- _counters_prev = [];
-}
-
-function loadMoreHeadlines() {
- const view_mode = document.forms["main_toolbar_form"].view_mode.value;
- const unread_in_buffer = $$("#headlines-frame > div[id*=RROW][class*=Unread]").length;
- const num_all = $$("#headlines-frame > div[id*=RROW]").length;
- const num_unread = getFeedUnread(getActiveFeedId(), activeFeedIsCat());
-
- // TODO implement marked & published
-
- let offset = num_all;
-
- switch (view_mode) {
- case "marked":
- case "published":
- console.warn("loadMoreHeadlines: ", view_mode, "not implemented");
- break;
- case "unread":
- offset = unread_in_buffer;
- break;
- case "adaptive":
- if (!(getActiveFeedId() == -1 && !activeFeedIsCat()))
- offset = num_unread > 0 ? unread_in_buffer : num_all;
- break;
- }
-
- console.log("loadMoreHeadlines, offset=", offset);
-
- viewfeed({feed: getActiveFeedId(), is_cat: activeFeedIsCat(), offset: offset, infscroll_req: true});
-}
-
-function cleanup_memory(root) {
- const dijits = dojo.query("[widgetid]", dijit.byId(root).domNode).map(dijit.byNode);
-
- dijits.each(function (d) {
- dojo.destroy(d.domNode);
- });
-
- $$("#" + root + " *").each(function (i) {
- i.parentNode ? i.parentNode.removeChild(i) : true;
- });
-}
-
-function viewfeed(params) {
- const feed = params.feed;
- const is_cat = !!params.is_cat || false;
- const offset = params.offset || 0;
- const viewfeed_debug = params.viewfeed_debug;
- const method = params.method;
- // this is used to quickly switch between feeds, sets active but xhr is on a timeout
- const delayed = params.delayed || false;
-
- if (feed != getActiveFeedId() || activeFeedIsCat() != is_cat) {
- _search_query = false;
- setActiveArticleId(0);
- }
-
- if (offset != 0) {
- if (infscroll_in_progress)
- return;
-
- infscroll_in_progress = 1;
-
- window.clearTimeout(_infscroll_timeout);
- _infscroll_timeout = window.setTimeout(() => {
- console.log('infscroll request timed out, aborting');
- infscroll_in_progress = 0;
-
- // call scroll handler to maybe repeat infscroll request
- headlinesScrollHandler();
- }, 10 * 1000);
- }
-
- Form.enable("main_toolbar_form");
-
- let query = Object.assign({op: "feeds", method: "view", feed: feed},
- dojo.formToObject("main_toolbar_form"));
-
- if (method) query.m = method;
-
- if (offset > 0) {
- if (current_first_id) {
- query.fid = current_first_id;
- }
- }
-
- if (_search_query) {
- query = Object.assign(query, _search_query);
- }
-
- if (offset != 0) {
- query.skip = offset;
-
- // to prevent duplicate feed titles when showing grouped vfeeds
- if (vgroup_last_feed) {
- query.vgrlf = vgroup_last_feed;
- }
- } else if (!is_cat && feed == getActiveFeedId() && !params.method) {
- query.m = "ForceUpdate";
- }
-
- Form.enable("main_toolbar_form");
-
- if (!delayed)
- if (!setFeedExpandoIcon(feed, is_cat,
- (is_cat) ? 'images/indicator_tiny.gif' : 'images/indicator_white.gif'))
- notify_progress("Loading, please wait...", true);
-
- query.cat = is_cat;
-
- setActiveFeedId(feed, is_cat);
-
- if (viewfeed_debug) {
- window.open("backend.php?" +
- dojo.objectToQuery(
- Object.assign({debug: 1, csrf_token: getInitParam("csrf_token")}, query)
- ));
- }
-
- window.clearTimeout(_viewfeed_wait_timeout);
- _viewfeed_wait_timeout = window.setTimeout(() => {
- catchupBatchedArticles(() => {
- xhrPost("backend.php", query, (transport) => {
- try {
- setFeedExpandoIcon(feed, is_cat, 'images/blank_icon.gif');
- headlines_callback2(transport, offset);
- PluginHost.run(PluginHost.HOOK_FEED_LOADED, [feed, is_cat]);
- } catch (e) {
- exception_error(e);
- }
- });
- });
- }, delayed ? 250 : 0);
-}
-
-function feedlist_init() {
- console.log("in feedlist init");
-
- setLoadingProgress(50);
-
- document.onkeydown = hotkey_handler;
- setInterval(hotkeyPrefixTimeout, 3*1000);
- setInterval(catchupBatchedArticles, 10*1000);
-
- if (!getActiveFeedId()) {
- viewfeed({feed: -3});
- } else {
- viewfeed({feed: getActiveFeedId(), is_cat: activeFeedIsCat()});
- }
-
- hideOrShowFeeds(getInitParam("hide_read_feeds") == 1);
-
- if (getInitParam("is_default_pw")) {
- console.warn("user password is at default value");
-
- const dialog = new dijit.Dialog({
- title: __("Your password is at default value"),
- href: "backend.php?op=dlg&method=defaultpasswordwarning",
- id: 'infoBox',
- style: "width: 600px",
- onCancel: function() {
- return true;
- },
- onExecute: function() {
- return true;
- },
- onClose: function() {
- return true;
- }
- });
-
- dialog.show();
- }
-
- // bw_limit disables timeout() so we request initial counters separately
- if (getInitParam("bw_limit") == "1") {
- request_counters(true);
- } else {
- setTimeout(timeout, 250);
- }
-}
-
-
-function request_counters(force) {
- const date = new Date();
- const timestamp = Math.round(date.getTime() / 1000);
-
- if (force || timestamp - counters_last_request > 5) {
- console.log("scheduling request of counters...");
-
- counters_last_request = timestamp;
-
- let query = {op: "rpc", method: "getAllCounters", seq: next_seq()};
-
- if (!force)
- query.last_article_id = getInitParam("last_article_id");
-
- xhrPost("backend.php", query, (transport) => {
- handle_rpc_json(transport);
- });
-
- } else {
- console.log("request_counters: rate limit reached: " + (timestamp - counters_last_request));
- }
-}
-
-// NOTE: this implementation is incomplete
-// for general objects but good enough for counters
-// http://adripofjavascript.com/blog/drips/object-equality-in-javascript.html
-function counter_is_equal(a, b) {
- // Create arrays of property names
- const aProps = Object.getOwnPropertyNames(a);
- const bProps = Object.getOwnPropertyNames(b);
-
- // If number of properties is different,
- // objects are not equivalent
- if (aProps.length != bProps.length) {
- return false;
- }
-
- for (let i = 0; i < aProps.length; i++) {
- const propName = aProps[i];
-
- // If values of same property are not equal,
- // objects are not equivalent
- if (a[propName] !== b[propName]) {
- return false;
- }
- }
-
- // If we made it this far, objects
- // are considered equivalent
- return true;
-}
-
-
-function parse_counters(elems) {
- for (let l = 0; l < elems.length; l++) {
-
- if (_counters_prev[l] && counter_is_equal(elems[l], _counters_prev[l])) {
- continue;
- }
-
- const id = elems[l].id;
- const kind = elems[l].kind;
- const ctr = parseInt(elems[l].counter);
- const error = elems[l].error;
- const has_img = elems[l].has_img;
- const updated = elems[l].updated;
- const auxctr = parseInt(elems[l].auxcounter);
-
- if (id == "global-unread") {
- global_unread = ctr;
- updateTitle();
- continue;
- }
-
- if (id == "subscribed-feeds") {
- /* feeds_found = ctr; */
- continue;
- }
-
- /*if (getFeedUnread(id, (kind == "cat")) != ctr ||
- (kind == "cat")) {
- }*/
-
- setFeedUnread(id, (kind == "cat"), ctr);
- setFeedValue(id, (kind == "cat"), 'auxcounter', auxctr);
-
- if (kind != "cat") {
- setFeedValue(id, false, 'error', error);
- setFeedValue(id, false, 'updated', updated);
-
- if (id > 0) {
- if (has_img) {
- setFeedIcon(id, false,
- getInitParam("icons_url") + "/" + id + ".ico?" + has_img);
- } else {
- setFeedIcon(id, false, 'images/blank_icon.gif');
- }
- }
- }
- }
-
- hideOrShowFeeds(getInitParam("hide_read_feeds") == 1);
-
- _counters_prev = elems;
-}
-
-function getFeedUnread(feed, is_cat) {
- try {
- const tree = dijit.byId("feedTree");
-
- if (tree && tree.model)
- return tree.model.getFeedUnread(feed, is_cat);
-
- } catch (e) {
- //
- }
-
- return -1;
-}
-
-function getFeedCategory(feed) {
- try {
- const tree = dijit.byId("feedTree");
-
- if (tree && tree.model)
- return tree.getFeedCategory(feed);
-
- } catch (e) {
- //
- }
-
- return false;
-}
-
-function hideOrShowFeeds(hide) {
- const tree = dijit.byId("feedTree");
-
- if (tree)
- return tree.hideRead(hide, getInitParam("hide_read_shows_special"));
-}
-
-function getFeedName(feed, is_cat) {
-
- if (isNaN(feed)) return feed; // it's a tag
-
- const tree = dijit.byId("feedTree");
-
- if (tree && tree.model)
- return tree.model.getFeedValue(feed, is_cat, 'name');
-}
-
-/* function getFeedValue(feed, is_cat, key) {
- try {
- const tree = dijit.byId("feedTree");
-
- if (tree && tree.model)
- return tree.model.getFeedValue(feed, is_cat, key);
-
- } catch (e) {
- //
- }
- return '';
-} */
-
-function setFeedUnread(feed, is_cat, unread) {
- const tree = dijit.byId("feedTree");
-
- if (tree && tree.model)
- return tree.model.setFeedUnread(feed, is_cat, unread);
-}
-
-function setFeedValue(feed, is_cat, key, value) {
- try {
- const tree = dijit.byId("feedTree");
-
- if (tree && tree.model)
- return tree.model.setFeedValue(feed, is_cat, key, value);
-
- } catch (e) {
- //
- }
-}
-
-function selectFeed(feed, is_cat) {
- const tree = dijit.byId("feedTree");
-
- if (tree) return tree.selectFeed(feed, is_cat);
-}
-
-function setFeedIcon(feed, is_cat, src) {
- const tree = dijit.byId("feedTree");
-
- if (tree) return tree.setFeedIcon(feed, is_cat, src);
-}
-
-function setFeedExpandoIcon(feed, is_cat, src) {
- const tree = dijit.byId("feedTree");
-
- if (tree) return tree.setFeedExpandoIcon(feed, is_cat, src);
-
- return false;
-}
-
-function getNextUnreadFeed(feed, is_cat) {
- const tree = dijit.byId("feedTree");
- const nuf = tree.model.getNextUnreadFeed(feed, is_cat);
-
- if (nuf)
- return tree.model.store.getValue(nuf, 'bare_id');
-}
-
-function catchupCurrentFeed(mode) {
- catchupFeed(getActiveFeedId(), activeFeedIsCat(), mode);
-}
-
-function catchupFeedInGroup(id) {
- const title = getFeedName(id);
-
- const str = __("Mark all articles in %s as read?").replace("%s", title);
-
- if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
-
- const rows = $$("#headlines-frame > div[id*=RROW][data-orig-feed-id='"+id+"']");
-
- if (rows.length > 0) {
-
- rows.each(function (row) {
- row.removeClassName("Unread");
-
- if (row.getAttribute("data-article-id") != getActiveArticleId()) {
- new Effect.Fade(row, {duration: 0.5});
- }
-
- });
-
- const feedTitles = $$("#headlines-frame > div[class='feed-title']");
-
- for (let i = 0; i < feedTitles.length; i++) {
- if (feedTitles[i].getAttribute("data-feed-id") == id) {
-
- if (i < feedTitles.length - 1) {
- new Effect.Fade(feedTitles[i], {duration: 0.5});
- }
-
- break;
- }
- }
-
- updateFloatingTitle(true);
- }
-
- notify_progress("Loading, please wait...", true);
-
- xhrPost("backend.php", { op: "rpc", method: "catchupFeed", feed_id: id, is_cat: false}, (transport) => {
- handle_rpc_json(transport);
- });
- }
-}
-
-function catchupFeed(feed, is_cat, mode) {
- if (is_cat == undefined) is_cat = false;
-
- let str = false;
-
- switch (mode) {
- case "1day":
- str = __("Mark %w in %s older than 1 day as read?");
- break;
- case "1week":
- str = __("Mark %w in %s older than 1 week as read?");
- break;
- case "2week":
- str = __("Mark %w in %s older than 2 weeks as read?");
- break;
- default:
- str = __("Mark %w in %s as read?");
- }
-
- const mark_what = last_search_query && last_search_query[0] ? __("search results") : __("all articles");
- const fn = getFeedName(feed, is_cat);
-
- str = str.replace("%s", fn)
- .replace("%w", mark_what);
-
- if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
- return;
- }
-
- const catchup_query = {op: 'rpc', method: 'catchupFeed', feed_id: feed,
- is_cat: is_cat, mode: mode, search_query: last_search_query[0],
- search_lang: last_search_query[1]};
-
- notify_progress("Loading, please wait...", true);
-
- xhrPost("backend.php", catchup_query, (transport) => {
- handle_rpc_json(transport);
-
- const show_next_feed = getInitParam("on_catchup_show_next_feed") == "1";
-
- if (show_next_feed) {
- const nuf = getNextUnreadFeed(feed, is_cat);
-
- if (nuf) {
- viewfeed({feed: nuf, is_cat: is_cat});
- }
- } else if (feed == getActiveFeedId() && is_cat == activeFeedIsCat()) {
- viewCurrentFeed();
- }
-
- notify("");
- });
-}
-
-function decrementFeedCounter(feed, is_cat) {
- let ctr = getFeedUnread(feed, is_cat);
-
- if (ctr > 0) {
- setFeedUnread(feed, is_cat, ctr - 1);
- global_unread = global_unread - 1;
- updateTitle();
-
- if (!is_cat) {
- const cat = parseInt(getFeedCategory(feed));
-
- if (!isNaN(cat)) {
- ctr = getFeedUnread(cat, true);
-
- if (ctr > 0) {
- setFeedUnread(cat, true, ctr - 1);
- }
- }
- }
- }
-
-}
-
-
diff --git a/js/functions.js b/js/functions.js
deleted file mode 100755
index 4a9785fac..000000000
--- a/js/functions.js
+++ /dev/null
@@ -1,1521 +0,0 @@
-/* global dijit, __ */
-
-let init_params = {};
-let _label_base_index = -1024;
-let loading_progress = 0;
-let notify_hide_timerid = false;
-
-let hotkey_prefix = 0;
-let hotkey_prefix_pressed = false;
-
-Ajax.Base.prototype.initialize = Ajax.Base.prototype.initialize.wrap(
- function (callOriginal, options) {
-
- if (getInitParam("csrf_token") != undefined) {
- Object.extend(options, options || { });
-
- if (Object.isString(options.parameters))
- options.parameters = options.parameters.toQueryParams();
- else if (Object.isHash(options.parameters))
- options.parameters = options.parameters.toObject();
-
- options.parameters["csrf_token"] = getInitParam("csrf_token");
- }
-
- return callOriginal(options);
- }
-);
-
-/* xhr shorthand helpers */
-
-function xhrPost(url, params, complete) {
- console.log("xhrPost:", params);
- return new Ajax.Request(url, {
- parameters: params,
- onComplete: complete
- });
-}
-
-function xhrJson(url, params, complete) {
- return xhrPost(url, params, (reply) => {
- try {
- const obj = JSON.parse(reply.responseText);
- complete(obj);
- } catch (e) {
- console.error("xhrJson", e, reply);
- complete(null);
- }
-
- })
-}
-
-/* add method to remove element from array */
-
-Array.prototype.remove = function(s) {
- for (let i=0; i < this.length; i++) {
- if (s == this[i]) this.splice(i, 1);
- }
-};
-
-function report_error(message, filename, lineno, colno, error) {
- exception_error(error, null, filename, lineno);
-}
-
-function exception_error(e, e_compat, filename, lineno, colno) {
- if (typeof e == "string") e = e_compat;
-
- if (!e) return; // no exception object, nothing to report.
-
- try {
- console.error(e);
- const msg = e.toString();
-
- try {
- xhrPost("backend.php",
- {op: "rpc", method: "log",
- file: e.fileName ? e.fileName : filename,
- line: e.lineNumber ? e.lineNumber : lineno,
- msg: msg, context: e.stack},
- (transport) => {
- console.warn(transport.responseText);
- });
-
- } catch (e) {
- console.error("Exception while trying to log the error.", e);
- }
-
- let content = "<div class='fatalError'><p>" + msg + "</p>";
-
- if (e.stack) {
- content += "<div><b>Stack trace:</b></div>" +
- "<textarea name=\"stack\" readonly=\"1\">" + e.stack + "</textarea>";
- }
-
- content += "</div>";
-
- content += "<div class='dlgButtons'>";
-
- content += "<button dojoType=\"dijit.form.Button\" "+
- "onclick=\"dijit.byId('exceptionDlg').hide()\">" +
- __('Close') + "</button>";
- content += "</div>";
-
- if (dijit.byId("exceptionDlg"))
- dijit.byId("exceptionDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "exceptionDlg",
- title: "Unhandled exception",
- style: "width: 600px",
- content: content});
-
- dialog.show();
-
- } catch (ei) {
- console.error("Exception while trying to report an exception:", ei);
- console.error("Original exception:", e);
-
- alert("Exception occured while trying to report an exception.\n" +
- ei.stack + "\n\nOriginal exception:\n" + e.stack);
- }
-
-}
-
-function param_escape(arg) {
- return encodeURIComponent(arg);
-}
-
-function notify_real(msg, no_hide, n_type) {
-
- const n = $("notify");
-
- if (!n) return;
-
- if (notify_hide_timerid) {
- window.clearTimeout(notify_hide_timerid);
- }
-
- if (msg == "") {
- if (n.hasClassName("visible")) {
- notify_hide_timerid = window.setTimeout(function() {
- n.removeClassName("visible") }, 0);
- }
- return;
- }
-
- /* types:
-
- 1 - generic
- 2 - progress
- 3 - error
- 4 - info
-
- */
-
- msg = "<span class=\"msg\"> " + __(msg) + "</span>";
-
- if (n_type == 2) {
- msg = "<span><img src=\""+getInitParam("icon_indicator_white")+"\"></span>" + msg;
- no_hide = true;
- } else if (n_type == 3) {
- msg = "<span><img src=\""+getInitParam("icon_alert")+"\"></span>" + msg;
- } else if (n_type == 4) {
- msg = "<span><img src=\""+getInitParam("icon_information")+"\"></span>" + msg;
- }
-
- msg += " <span><img src=\""+getInitParam("icon_cross")+"\" class=\"close\" title=\"" +
- __("Click to close") + "\" onclick=\"notify('')\"></span>";
-
- n.innerHTML = msg;
-
- window.setTimeout(function() {
- // goddamnit firefox
- if (n_type == 2) {
- n.className = "notify notify_progress visible";
- } else if (n_type == 3) {
- n.className = "notify notify_error visible";
- msg = "<span><img src='images/alert.png'></span>" + msg;
- } else if (n_type == 4) {
- n.className = "notify notify_info visible";
- } else {
- n.className = "notify visible";
- }
-
- if (!no_hide) {
- notify_hide_timerid = window.setTimeout(function() {
- n.removeClassName("visible") }, 5*1000);
- }
-
- }, 10);
-
-}
-
-function notify(msg, no_hide) {
- notify_real(msg, no_hide, 1);
-}
-
-function notify_progress(msg, no_hide) {
- notify_real(msg, no_hide, 2);
-}
-
-function notify_error(msg, no_hide) {
- notify_real(msg, no_hide, 3);
-
-}
-
-function notify_info(msg, no_hide) {
- notify_real(msg, no_hide, 4);
-}
-
-function setCookie(name, value, lifetime, path, domain, secure) {
-
- let d = false;
-
- if (lifetime) {
- d = new Date();
- d.setTime(d.getTime() + (lifetime * 1000));
- }
-
- console.log("setCookie: " + name + " => " + value + ": " + d);
-
- int_setCookie(name, value, d, path, domain, secure);
-
-}
-
-function int_setCookie(name, value, expires, path, domain, secure) {
- document.cookie= name + "=" + escape(value) +
- ((expires) ? "; expires=" + expires.toGMTString() : "") +
- ((path) ? "; path=" + path : "") +
- ((domain) ? "; domain=" + domain : "") +
- ((secure) ? "; secure" : "");
-}
-
-function delCookie(name, path, domain) {
- if (getCookie(name)) {
- document.cookie = name + "=" +
- ((path) ? ";path=" + path : "") +
- ((domain) ? ";domain=" + domain : "" ) +
- ";expires=Thu, 01-Jan-1970 00:00:01 GMT";
- }
-}
-
-
-function getCookie(name) {
-
- const dc = document.cookie;
- const prefix = name + "=";
- let begin = dc.indexOf("; " + prefix);
- if (begin == -1) {
- begin = dc.indexOf(prefix);
- if (begin != 0) return null;
- }
- else {
- begin += 2;
- }
- let end = document.cookie.indexOf(";", begin);
- if (end == -1) {
- end = dc.length;
- }
- return unescape(dc.substring(begin + prefix.length, end));
-}
-
-function toggleSelectRowById(sender, id) {
- const row = $(id);
- return toggleSelectRow(sender, row);
-}
-
-/* this is for dijit Checkbox */
-function toggleSelectListRow2(sender) {
- const row = sender.domNode.parentNode;
- return toggleSelectRow(sender, row);
-}
-
-/* this is for dijit Checkbox */
-function toggleSelectRow2(sender, row, is_cdm) {
-
- if (!row)
- if (!is_cdm)
- row = sender.domNode.parentNode.parentNode;
- else
- row = sender.domNode.parentNode.parentNode.parentNode; // oh ffs
-
- if (sender.checked && !row.hasClassName('Selected'))
- row.addClassName('Selected');
- else
- row.removeClassName('Selected');
-
- if (typeof updateSelectedPrompt != undefined)
- updateSelectedPrompt();
-}
-
-
-function toggleSelectRow(sender, row) {
-
- if (!row) row = sender.parentNode.parentNode;
-
- if (sender.checked && !row.hasClassName('Selected'))
- row.addClassName('Selected');
- else
- row.removeClassName('Selected');
-
- if (typeof updateSelectedPrompt != undefined)
- updateSelectedPrompt();
-}
-
-// noinspection JSUnusedGlobalSymbols
-function displayIfChecked(checkbox, elemId) {
- if (checkbox.checked) {
- Effect.Appear(elemId, {duration : 0.5});
- } else {
- Effect.Fade(elemId, {duration : 0.5});
- }
-}
-
-function getURLParam(param){
- return String(window.location.href).parseQuery()[param];
-}
-
-// noinspection JSUnusedGlobalSymbols
-function closeInfoBox() {
- const dialog = dijit.byId("infoBox");
-
- if (dialog) dialog.hide();
-
- return false;
-}
-
-function displayDlg(title, id, param, callback) {
- notify_progress("Loading, please wait...", true);
-
- const query = { op: "dlg", method: id, param: param };
-
- xhrPost("backend.php", query, (transport) => {
- try {
- const content = transport.responseText;
-
- let dialog = dijit.byId("infoBox");
-
- if (!dialog) {
- dialog = new dijit.Dialog({
- title: title,
- id: 'infoBox',
- style: "width: 600px",
- onCancel: function () {
- return true;
- },
- onExecute: function () {
- return true;
- },
- onClose: function () {
- return true;
- },
- content: content
- });
- } else {
- dialog.attr('title', title);
- dialog.attr('content', content);
- }
-
- dialog.show();
-
- notify("");
-
- if (callback) callback(transport);
- } catch (e) {
- exception_error(e);
- }
- });
-
- return false;
-}
-
-function getInitParam(key) {
- return init_params[key];
-}
-
-function setInitParam(key, value) {
- init_params[key] = value;
-}
-
-function fatalError(code, msg, ext_info) {
- if (code == 6) {
- window.location.href = "index.php";
- } else if (code == 5) {
- window.location.href = "public.php?op=dbupdate";
- } else {
-
- if (msg == "") msg = "Unknown error";
-
- if (ext_info) {
- if (ext_info.responseText) {
- ext_info = ext_info.responseText;
- }
- }
-
- /* global ERRORS */
- if (ERRORS && ERRORS[code] && !msg) {
- msg = ERRORS[code];
- }
-
- let content = "<div><b>Error code:</b> " + code + "</div>" +
- "<p>" + msg + "</p>";
-
- if (ext_info) {
- content = content + "<div><b>Additional information:</b></div>" +
- "<textarea style='width: 100%' readonly=\"1\">" +
- ext_info + "</textarea>";
- }
-
- const dialog = new dijit.Dialog({
- title: "Fatal error",
- style: "width: 600px",
- content: content});
-
- dialog.show();
-
- }
-
- return false;
-
-}
-
-// noinspection JSUnusedGlobalSymbols
-function filterDlgCheckAction(sender) {
- const action = sender.value;
-
- const action_param = $("filterDlg_paramBox");
-
- if (!action_param) {
- console.log("filterDlgCheckAction: can't find action param box!");
- return;
- }
-
- // if selected action supports parameters, enable params field
- if (action == 4 || action == 6 || action == 7 || action == 9) {
- new Effect.Appear(action_param, {duration : 0.5});
-
- Element.hide(dijit.byId("filterDlg_actionParam").domNode);
- Element.hide(dijit.byId("filterDlg_actionParamLabel").domNode);
- Element.hide(dijit.byId("filterDlg_actionParamPlugin").domNode);
-
- if (action == 7) {
- Element.show(dijit.byId("filterDlg_actionParamLabel").domNode);
- } else if (action == 9) {
- Element.show(dijit.byId("filterDlg_actionParamPlugin").domNode);
- } else {
- Element.show(dijit.byId("filterDlg_actionParam").domNode);
- }
-
- } else {
- Element.hide(action_param);
- }
-}
-
-
-function explainError(code) {
- return displayDlg(__("Error explained"), "explainError", code);
-}
-
-function setLoadingProgress(p) {
- loading_progress += p;
-
- if (dijit.byId("loading_bar"))
- dijit.byId("loading_bar").update({progress: loading_progress});
-
- if (loading_progress >= 90)
- Element.hide("overlay");
-
-}
-
-function strip_tags(s) {
- return s.replace(/<\/?[^>]+(>|$)/g, "");
-}
-
-function hotkeyPrefixTimeout() {
- const date = new Date();
- const ts = Math.round(date.getTime() / 1000);
-
- if (hotkey_prefix_pressed && ts - hotkey_prefix_pressed >= 5) {
- console.log("hotkey_prefix seems to be stuck, aborting");
- hotkey_prefix_pressed = false;
- hotkey_prefix = false;
- Element.hide('cmdline');
- }
-}
-
-// noinspection JSUnusedGlobalSymbols
-function uploadIconHandler(rc) {
- switch (rc) {
- case 0:
- notify_info("Upload complete.");
- if (inPreferences()) {
- updateFeedList();
- } else {
- setTimeout('updateFeedList(false, false)', 50);
- }
- break;
- case 1:
- notify_error("Upload failed: icon is too big.");
- break;
- case 2:
- notify_error("Upload failed.");
- break;
- }
-}
-
-// noinspection JSUnusedGlobalSymbols
-function removeFeedIcon(id) {
- if (confirm(__("Remove stored feed icon?"))) {
-
- notify_progress("Removing feed icon...", true);
-
- const query = { op: "pref-feeds", method: "removeicon", feed_id: id };
-
- xhrPost("backend.php", query, (transport) => {
- notify_info("Feed icon removed.");
- if (inPreferences()) {
- updateFeedList();
- } else {
- setTimeout('updateFeedList(false, false)', 50);
- }
- });
- }
-
- return false;
-}
-
-// noinspection JSUnusedGlobalSymbols
-function uploadFeedIcon() {
- const file = $("icon_file");
-
- if (file.value.length == 0) {
- alert(__("Please select an image file to upload."));
- } else if (confirm(__("Upload new icon for this feed?"))) {
- notify_progress("Uploading, please wait...", true);
- return true;
- }
-
- return false;
-}
-
-function addLabel(select, callback) {
- const caption = prompt(__("Please enter label caption:"), "");
-
- if (caption != undefined && caption.trim().length > 0) {
-
- const query = { op: "pref-labels", method: "add", caption: caption.trim() };
-
- if (select)
- Object.extend(query, {output: "select"});
-
- notify_progress("Loading, please wait...", true);
-
- xhrPost("backend.php", query, (transport) => {
- if (callback) {
- callback(transport);
- } else if (inPreferences()) {
- updateLabelList();
- } else {
- updateFeedList();
- }
- });
- }
-
-}
-
-function quickAddFeed() {
- const query = "backend.php?op=feeds&method=quickAddFeed";
-
- // overlapping widgets
- if (dijit.byId("batchSubDlg")) dijit.byId("batchSubDlg").destroyRecursive();
- if (dijit.byId("feedAddDlg")) dijit.byId("feedAddDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "feedAddDlg",
- title: __("Subscribe to Feed"),
- style: "width: 600px",
- show_error: function(msg) {
- const elem = $("fadd_error_message");
-
- elem.innerHTML = msg;
-
- if (!Element.visible(elem))
- new Effect.Appear(elem);
-
- },
- execute: function() {
- if (this.validate()) {
- console.log(dojo.objectToQuery(this.attr('value')));
-
- const feed_url = this.attr('value').feed;
-
- Element.show("feed_add_spinner");
- Element.hide("fadd_error_message");
-
- xhrPost("backend.php", this.attr('value'), (transport) => {
- try {
-
- try {
- var reply = JSON.parse(transport.responseText);
- } catch (e) {
- Element.hide("feed_add_spinner");
- alert(__("Failed to parse output. This can indicate server timeout and/or network issues. Backend output was logged to browser console."));
- console.log('quickAddFeed, backend returned:' + transport.responseText);
- return;
- }
-
- const rc = reply['result'];
-
- notify('');
- Element.hide("feed_add_spinner");
-
- console.log(rc);
-
- switch (parseInt(rc['code'])) {
- case 1:
- dialog.hide();
- notify_info(__("Subscribed to %s").replace("%s", feed_url));
-
- updateFeedList();
- break;
- case 2:
- dialog.show_error(__("Specified URL seems to be invalid."));
- break;
- case 3:
- dialog.show_error(__("Specified URL doesn't seem to contain any feeds."));
- break;
- case 4:
- const feeds = rc['feeds'];
-
- Element.show("fadd_multiple_notify");
-
- const select = dijit.byId("feedDlg_feedContainerSelect");
-
- while (select.getOptions().length > 0)
- select.removeOption(0);
-
- select.addOption({value: '', label: __("Expand to select feed")});
-
- let count = 0;
- for (const feedUrl in feeds) {
- select.addOption({value: feedUrl, label: feeds[feedUrl]});
- count++;
- }
-
- Effect.Appear('feedDlg_feedsContainer', {duration : 0.5});
-
- break;
- case 5:
- dialog.show_error(__("Couldn't download the specified URL: %s").
- replace("%s", rc['message']));
- break;
- case 6:
- dialog.show_error(__("XML validation failed: %s").
- replace("%s", rc['message']));
- break;
- case 0:
- dialog.show_error(__("You are already subscribed to this feed."));
- break;
- }
-
- } catch (e) {
- console.error(transport.responseText);
- exception_error(e);
- }
- });
- }
- },
- href: query});
-
- dialog.show();
-}
-
-function createNewRuleElement(parentNode, replaceNode) {
- const form = document.forms["filter_new_rule_form"];
- const query = { op: "pref-filters", method: "printrulename", rule: dojo.formToJson(form) };
-
- xhrPost("backend.php", query, (transport) => {
- try {
- const li = dojo.create("li");
-
- const cb = dojo.create("input", { type: "checkbox" }, li);
-
- new dijit.form.CheckBox({
- onChange: function() {
- toggleSelectListRow2(this) },
- }, cb);
-
- dojo.create("input", { type: "hidden",
- name: "rule[]",
- value: dojo.formToJson(form) }, li);
-
- dojo.create("span", {
- onclick: function() {
- dijit.byId('filterEditDlg').editRule(this);
- },
- innerHTML: transport.responseText }, li);
-
- if (replaceNode) {
- parentNode.replaceChild(li, replaceNode);
- } else {
- parentNode.appendChild(li);
- }
- } catch (e) {
- exception_error(e);
- }
- });
-}
-
-function createNewActionElement(parentNode, replaceNode) {
- const form = document.forms["filter_new_action_form"];
-
- if (form.action_id.value == 7) {
- form.action_param.value = form.action_param_label.value;
- } else if (form.action_id.value == 9) {
- form.action_param.value = form.action_param_plugin.value;
- }
-
- const query = { op: "pref-filters", method: "printactionname",
- action: dojo.formToJson(form) };
-
- xhrPost("backend.php", query, (transport) => {
- try {
- const li = dojo.create("li");
-
- const cb = dojo.create("input", { type: "checkbox" }, li);
-
- new dijit.form.CheckBox({
- onChange: function() {
- toggleSelectListRow2(this) },
- }, cb);
-
- dojo.create("input", { type: "hidden",
- name: "action[]",
- value: dojo.formToJson(form) }, li);
-
- dojo.create("span", {
- onclick: function() {
- dijit.byId('filterEditDlg').editAction(this);
- },
- innerHTML: transport.responseText }, li);
-
- if (replaceNode) {
- parentNode.replaceChild(li, replaceNode);
- } else {
- parentNode.appendChild(li);
- }
-
- } catch (e) {
- exception_error(e);
- }
- });
-}
-
-
-function addFilterRule(replaceNode, ruleStr) {
- if (dijit.byId("filterNewRuleDlg"))
- dijit.byId("filterNewRuleDlg").destroyRecursive();
-
- const query = "backend.php?op=pref-filters&method=newrule&rule=" +
- param_escape(ruleStr);
-
- const rule_dlg = new dijit.Dialog({
- id: "filterNewRuleDlg",
- title: ruleStr ? __("Edit rule") : __("Add rule"),
- style: "width: 600px",
- execute: function() {
- if (this.validate()) {
- createNewRuleElement($("filterDlg_Matches"), replaceNode);
- this.hide();
- }
- },
- href: query});
-
- rule_dlg.show();
-}
-
-function addFilterAction(replaceNode, actionStr) {
- if (dijit.byId("filterNewActionDlg"))
- dijit.byId("filterNewActionDlg").destroyRecursive();
-
- const query = "backend.php?op=pref-filters&method=newaction&action=" +
- param_escape(actionStr);
-
- const rule_dlg = new dijit.Dialog({
- id: "filterNewActionDlg",
- title: actionStr ? __("Edit action") : __("Add action"),
- style: "width: 600px",
- execute: function() {
- if (this.validate()) {
- createNewActionElement($("filterDlg_Actions"), replaceNode);
- this.hide();
- }
- },
- href: query});
-
- rule_dlg.show();
-}
-
-function editFilterTest(query) {
-
- if (dijit.byId("filterTestDlg"))
- dijit.byId("filterTestDlg").destroyRecursive();
-
- const test_dlg = new dijit.Dialog({
- id: "filterTestDlg",
- title: "Test Filter",
- style: "width: 600px",
- results: 0,
- limit: 100,
- max_offset: 10000,
- getTestResults: function(query, offset) {
- const updquery = query + "&offset=" + offset + "&limit=" + test_dlg.limit;
-
- console.log("getTestResults:" + offset);
-
- xhrPost("backend.php", updquery, (transport) => {
- try {
- const result = JSON.parse(transport.responseText);
-
- if (result && dijit.byId("filterTestDlg") && dijit.byId("filterTestDlg").open) {
- test_dlg.results += result.length;
-
- console.log("got results:" + result.length);
-
- $("prefFilterProgressMsg").innerHTML = __("Looking for articles (%d processed, %f found)...")
- .replace("%f", test_dlg.results)
- .replace("%d", offset);
-
- console.log(offset + " " + test_dlg.max_offset);
-
- for (let i = 0; i < result.length; i++) {
- const tmp = new Element("table");
- tmp.innerHTML = result[i];
- dojo.parser.parse(tmp);
-
- $("prefFilterTestResultList").innerHTML += tmp.innerHTML;
- }
-
- if (test_dlg.results < 30 && offset < test_dlg.max_offset) {
-
- // get the next batch
- window.setTimeout(function () {
- test_dlg.getTestResults(query, offset + test_dlg.limit);
- }, 0);
-
- } else {
- // all done
-
- Element.hide("prefFilterLoadingIndicator");
-
- if (test_dlg.results == 0) {
- $("prefFilterTestResultList").innerHTML = "<tr><td align='center'>No recent articles matching this filter have been found.</td></tr>";
- $("prefFilterProgressMsg").innerHTML = "Articles matching this filter:";
- } else {
- $("prefFilterProgressMsg").innerHTML = __("Found %d articles matching this filter:")
- .replace("%d", test_dlg.results);
- }
-
- }
-
- } else if (!result) {
- console.log("getTestResults: can't parse results object");
-
- Element.hide("prefFilterLoadingIndicator");
-
- notify_error("Error while trying to get filter test results.");
-
- } else {
- console.log("getTestResults: dialog closed, bailing out.");
- }
- } catch (e) {
- exception_error(e);
- }
-
- });
- },
- href: query});
-
- dojo.connect(test_dlg, "onLoad", null, function(e) {
- test_dlg.getTestResults(query, 0);
- });
-
- test_dlg.show();
-
-}
-
-function quickAddFilter() {
- let query;
-
- if (!inPreferences()) {
- query = { op: "pref-filters", method: "newfilter",
- feed: getActiveFeedId(), is_cat: activeFeedIsCat() };
- } else {
- query = { op: "pref-filters", method: "newfilter" };
- }
-
- console.log('quickAddFilter', query);
-
- if (dijit.byId("feedEditDlg"))
- dijit.byId("feedEditDlg").destroyRecursive();
-
- if (dijit.byId("filterEditDlg"))
- dijit.byId("filterEditDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "filterEditDlg",
- title: __("Create Filter"),
- style: "width: 600px",
- test: function() {
- const query = "backend.php?" + dojo.formToQuery("filter_new_form") + "&savemode=test";
-
- editFilterTest(query);
- },
- selectRules: function(select) {
- $$("#filterDlg_Matches input[type=checkbox]").each(function(e) {
- e.checked = select;
- if (select)
- e.parentNode.addClassName("Selected");
- else
- e.parentNode.removeClassName("Selected");
- });
- },
- selectActions: function(select) {
- $$("#filterDlg_Actions input[type=checkbox]").each(function(e) {
- e.checked = select;
-
- if (select)
- e.parentNode.addClassName("Selected");
- else
- e.parentNode.removeClassName("Selected");
-
- });
- },
- editRule: function(e) {
- const li = e.parentNode;
- const rule = li.getElementsByTagName("INPUT")[1].value;
- addFilterRule(li, rule);
- },
- editAction: function(e) {
- const li = e.parentNode;
- const action = li.getElementsByTagName("INPUT")[1].value;
- addFilterAction(li, action);
- },
- addAction: function() { addFilterAction(); },
- addRule: function() { addFilterRule(); },
- deleteAction: function() {
- $$("#filterDlg_Actions li[class*=Selected]").each(function(e) { e.parentNode.removeChild(e) });
- },
- deleteRule: function() {
- $$("#filterDlg_Matches li[class*=Selected]").each(function(e) { e.parentNode.removeChild(e) });
- },
- execute: function() {
- if (this.validate()) {
-
- const query = dojo.formToQuery("filter_new_form");
-
- xhrPost("backend.php", query, (transport) => {
- if (inPreferences()) {
- updateFilterList();
- }
-
- dialog.hide();
- });
- }
- },
- href: "backend.php?" + dojo.objectToQuery(query)});
-
- if (!inPreferences()) {
- const selectedText = getSelectionText();
-
- const lh = dojo.connect(dialog, "onLoad", function(){
- dojo.disconnect(lh);
-
- if (selectedText != "") {
-
- const feed_id = activeFeedIsCat() ? 'CAT:' + parseInt(getActiveFeedId()) :
- getActiveFeedId();
-
- const rule = { reg_exp: selectedText, feed_id: [feed_id], filter_type: 1 };
-
- addFilterRule(null, dojo.toJson(rule));
-
- } else {
-
- const query = { op: "rpc", method: "getlinktitlebyid", id: getActiveArticleId() };
-
- xhrPost("backend.php", query, (transport) => {
- const reply = JSON.parse(transport.responseText);
-
- let title = false;
-
- if (reply && reply.title) title = reply.title;
-
- if (title || getActiveFeedId() || activeFeedIsCat()) {
-
- console.log(title + " " + getActiveFeedId());
-
- const feed_id = activeFeedIsCat() ? 'CAT:' + parseInt(getActiveFeedId()) :
- getActiveFeedId();
-
- const rule = { reg_exp: title, feed_id: [feed_id], filter_type: 1 };
-
- addFilterRule(null, dojo.toJson(rule));
- }
- });
- }
- });
- }
-
- dialog.show();
-
-}
-
-function unsubscribeFeed(feed_id, title) {
-
- const msg = __("Unsubscribe from %s?").replace("%s", title);
-
- if (title == undefined || confirm(msg)) {
- notify_progress("Removing feed...");
-
- const query = { op: "pref-feeds", quiet: 1, method: "remove", ids: feed_id };
-
- xhrPost("backend.php", query, (transport) => {
- if (dijit.byId("feedEditDlg")) dijit.byId("feedEditDlg").hide();
-
- if (inPreferences()) {
- updateFeedList();
- } else {
- if (feed_id == getActiveFeedId())
- setTimeout(function() { viewfeed({feed:-5}) }, 100);
-
- if (feed_id < 0) updateFeedList();
- }
- });
- }
-
- return false;
-}
-
-
-function backend_sanity_check_callback(transport) {
-
- const reply = JSON.parse(transport.responseText);
-
- if (!reply) {
- fatalError(3, "Sanity check: invalid RPC reply", transport.responseText);
- return;
- }
-
- const error_code = reply['error']['code'];
-
- if (error_code && error_code != 0) {
- return fatalError(error_code, reply['error']['message']);
- }
-
- console.log("sanity check ok");
-
- const params = reply['init-params'];
-
- if (params) {
- console.log('reading init-params...');
-
- for (const k in params) {
- if (params.hasOwnProperty(k)) {
- switch (k) {
- case "label_base_index":
- _label_base_index = parseInt(params[k])
- break;
- 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]) {
- const filtered = sequence.replace(/\|.*$/, "");
- tmp[filtered] = params[k][1][sequence];
- }
-
- params[k][1] = tmp;
- break;
- }
-
- console.log("IP:", k, "=>", params[k]);
- }
- }
-
- init_params = params;
-
- // PluginHost might not be available on non-index pages
- window.PluginHost && PluginHost.run(PluginHost.HOOK_PARAMS_LOADED, init_params);
- }
-
- init_second_stage();
-}
-
-// noinspection JSUnusedGlobalSymbols
-function genUrlChangeKey(feed, is_cat) {
- if (confirm(__("Generate new syndication address for this feed?"))) {
-
- notify_progress("Trying to change address...", true);
-
- const query = { op: "pref-feeds", method: "regenFeedKey", id: feed, is_cat: is_cat };
-
- xhrJson("backend.php", query, (reply) => {
- const new_link = reply.link;
- const e = $('gen_feed_url');
-
- if (new_link) {
- e.innerHTML = e.innerHTML.replace(/\&amp;key=.*$/,
- "&amp;key=" + new_link);
-
- e.href = e.href.replace(/\&key=.*$/,
- "&key=" + new_link);
-
- new Effect.Highlight(e);
-
- notify('');
-
- } else {
- notify_error("Could not change feed URL.");
- }
- });
- }
- return false;
-}
-
-// mode = all, none, invert
-function selectTableRows(id, mode) {
- const rows = $(id).rows;
-
- for (let i = 0; i < rows.length; i++) {
- const row = rows[i];
- let cb = false;
- let dcb = false;
-
- if (row.id && row.className) {
- const bare_id = row.id.replace(/^[A-Z]*?-/, "");
- const inputs = rows[i].getElementsByTagName("input");
-
- for (let j = 0; j < inputs.length; j++) {
- const input = inputs[j];
-
- if (input.getAttribute("type") == "checkbox" &&
- input.id.match(bare_id)) {
-
- cb = input;
- dcb = dijit.getEnclosingWidget(cb);
- break;
- }
- }
-
- if (cb || dcb) {
- const issel = row.hasClassName("Selected");
-
- if (mode == "all" && !issel) {
- row.addClassName("Selected");
- cb.checked = true;
- if (dcb) dcb.set("checked", true);
- } else if (mode == "none" && issel) {
- row.removeClassName("Selected");
- cb.checked = false;
- if (dcb) dcb.set("checked", false);
-
- } else if (mode == "invert") {
-
- if (issel) {
- row.removeClassName("Selected");
- cb.checked = false;
- if (dcb) dcb.set("checked", false);
- } else {
- row.addClassName("Selected");
- cb.checked = true;
- if (dcb) dcb.set("checked", true);
- }
- }
- }
- }
- }
-
-}
-
-function getSelectedTableRowIds(id) {
- const rows = [];
-
- const elem_rows = $(id).rows;
-
- for (let i = 0; i < elem_rows.length; i++) {
- if (elem_rows[i].hasClassName("Selected")) {
- const bare_id = elem_rows[i].id.replace(/^[A-Z]*?-/, "");
- rows.push(bare_id);
- }
- }
-
- return rows;
-}
-
-function editFeed(feed) {
- if (feed <= 0)
- return alert(__("You can't edit this kind of feed."));
-
- const query = { op: "pref-feeds", method: "editfeed", id: feed };
-
- console.log("editFeed", query);
-
- if (dijit.byId("filterEditDlg"))
- dijit.byId("filterEditDlg").destroyRecursive();
-
- if (dijit.byId("feedEditDlg"))
- dijit.byId("feedEditDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "feedEditDlg",
- title: __("Edit Feed"),
- style: "width: 600px",
- execute: function() {
- if (this.validate()) {
- notify_progress("Saving data...", true);
-
- xhrPost("backend.php", dialog.attr('value'), () => {
- dialog.hide();
- notify('');
- updateFeedList();
- });
- }
- },
- href: "backend.php?" + dojo.objectToQuery(query)});
-
- dialog.show();
-}
-
-function feedBrowser() {
- const query = { op: "feeds", method: "feedBrowser" };
-
- if (dijit.byId("feedAddDlg"))
- dijit.byId("feedAddDlg").hide();
-
- if (dijit.byId("feedBrowserDlg"))
- dijit.byId("feedBrowserDlg").destroyRecursive();
-
- // noinspection JSUnusedGlobalSymbols
- const dialog = new dijit.Dialog({
- id: "feedBrowserDlg",
- title: __("More Feeds"),
- style: "width: 600px",
- getSelectedFeedIds: function () {
- const list = $$("#browseFeedList li[id*=FBROW]");
- const selected = [];
-
- list.each(function (child) {
- const id = child.id.replace("FBROW-", "");
-
- if (child.hasClassName('Selected')) {
- selected.push(id);
- }
- });
-
- return selected;
- },
- getSelectedFeeds: function () {
- const list = $$("#browseFeedList li.Selected");
- const selected = [];
-
- list.each(function (child) {
- const title = child.getElementsBySelector("span.fb_feedTitle")[0].innerHTML;
- const url = child.getElementsBySelector("a.fb_feedUrl")[0].href;
-
- selected.push([title, url]);
-
- });
-
- return selected;
- },
-
- subscribe: function () {
- const mode = this.attr('value').mode;
- let selected = [];
-
- if (mode == "1")
- selected = this.getSelectedFeeds();
- else
- selected = this.getSelectedFeedIds();
-
- if (selected.length > 0) {
- dijit.byId("feedBrowserDlg").hide();
-
- notify_progress("Loading, please wait...", true);
-
- const query = { op: "rpc", method: "massSubscribe",
- payload: JSON.stringify(selected), mode: mode };
-
- xhrPost("backend.php", query, () => {
- notify('');
- updateFeedList();
- });
-
- } else {
- alert(__("No feeds are selected."));
- }
-
- },
- update: function () {
- Element.show('feed_browser_spinner');
-
- xhrPost("backend.php", dialog.attr("value"), (transport) => {
- notify('');
-
- Element.hide('feed_browser_spinner');
-
- const reply = JSON.parse(transport.responseText);
- const mode = reply['mode'];
-
- if ($("browseFeedList") && reply['content']) {
- $("browseFeedList").innerHTML = reply['content'];
- }
-
- dojo.parser.parse("browseFeedList");
-
- if (mode == 2) {
- Element.show(dijit.byId('feed_archive_remove').domNode);
- } else {
- Element.hide(dijit.byId('feed_archive_remove').domNode);
- }
- });
- },
- removeFromArchive: function () {
- const selected = this.getSelectedFeedIds();
-
- if (selected.length > 0) {
- if (confirm(__("Remove selected feeds from the archive? Feeds with stored articles will not be removed."))) {
- Element.show('feed_browser_spinner');
-
- const query = { op: "rpc", method: "remarchive", ids: selected.toString() };
-
- xhrPost("backend.php", query, () => {
- dialog.update();
- });
- }
- }
- },
- execute: function () {
- if (this.validate()) {
- this.subscribe();
- }
- },
- href: "backend.php?" + dojo.objectToQuery(query)
- });
-
- dialog.show();
-}
-
-// noinspection JSUnusedGlobalSymbols
-function showFeedsWithErrors() {
- const query = { op: "pref-feeds", method: "feedsWithErrors" };
-
- if (dijit.byId("errorFeedsDlg"))
- dijit.byId("errorFeedsDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "errorFeedsDlg",
- title: __("Feeds with update errors"),
- style: "width: 600px",
- getSelectedFeeds: function() {
- return getSelectedTableRowIds("prefErrorFeedList");
- },
- removeSelected: function() {
- const sel_rows = this.getSelectedFeeds();
-
- if (sel_rows.length > 0) {
- if (confirm(__("Remove selected feeds?"))) {
- notify_progress("Removing selected feeds...", true);
-
- const query = { op: "pref-feeds", method: "remove",
- ids: sel_rows.toString() };
-
- xhrPost("backend.php", query, () => {
- notify('');
- dialog.hide();
- updateFeedList();
- });
- }
-
- } else {
- alert(__("No feeds are selected."));
- }
- },
- execute: function() {
- if (this.validate()) {
- //
- }
- },
- href: "backend.php?" + dojo.objectToQuery(query)
- });
-
- dialog.show();
-}
-
-function get_timestamp() {
- const date = new Date();
- return Math.round(date.getTime() / 1000);
-}
-
-function helpDialog(topic) {
- const query = "backend.php?op=backend&method=help&topic=" + param_escape(topic);
-
- if (dijit.byId("helpDlg"))
- dijit.byId("helpDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "helpDlg",
- title: __("Help"),
- style: "width: 600px",
- href: query,
- });
-
- dialog.show();
-}
-
-// 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
-function getSelectionText() {
- let text = "";
-
- if (typeof window.getSelection != "undefined") {
- const sel = window.getSelection();
- if (sel.rangeCount) {
- const container = document.createElement("div");
- for (let i = 0, len = sel.rangeCount; i < len; ++i) {
- container.appendChild(sel.getRangeAt(i).cloneContents());
- }
- text = container.innerHTML;
- }
- } else if (typeof document.selection != "undefined") {
- if (document.selection.type == "Text") {
- text = document.selection.createRange().textText;
- }
- }
-
- 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");
-
- w.opener = null;
- w.location = "backend.php?op=article&method=view&mode=raw&html=1&zoom=1&id=" + id + "&csrf_token=" + getInitParam("csrf_token");
-}
-
-function keyeventToAction(e) {
-
- const hotkeys_map = getInitParam("hotkeys");
- const keycode = e.which;
- const keychar = String.fromCharCode(keycode).toLowerCase();
-
- if (keycode == 27) { // escape and drop prefix
- hotkey_prefix = false;
- }
-
- if (keycode == 16 || keycode == 17) return; // ignore lone shift / ctrl
-
- if (!hotkey_prefix && hotkeys_map[0].indexOf(keychar) != -1) {
-
- const date = new Date();
- const ts = Math.round(date.getTime() / 1000);
-
- hotkey_prefix = keychar;
- hotkey_prefix_pressed = ts;
-
- $("cmdline").innerHTML = keychar;
- Element.show("cmdline");
-
- e.stopPropagation();
-
- return false;
- }
-
- Element.hide("cmdline");
-
- let hotkey_name = keychar.search(/[a-zA-Z0-9]/) != -1 ? keychar : "(" + keycode + ")";
-
- // ensure ^*char notation
- if (e.shiftKey) hotkey_name = "*" + hotkey_name;
- if (e.ctrlKey) hotkey_name = "^" + hotkey_name;
- if (e.altKey) hotkey_name = "+" + hotkey_name;
- if (e.metaKey) hotkey_name = "%" + hotkey_name;
-
- const hotkey_full = hotkey_prefix ? hotkey_prefix + " " + hotkey_name : hotkey_name;
- hotkey_prefix = false;
-
- let action_name = false;
-
- for (const sequence in hotkeys_map[1]) {
- if (sequence == hotkey_full) {
- action_name = hotkeys_map[1][sequence];
- break;
- }
- }
-
- console.log('keyeventToAction', hotkey_full, '=>', action_name);
-
- return action_name;
-} \ No newline at end of file
diff --git a/js/prefs.js b/js/prefs.js
index 50283efbf..c89c0494f 100755
--- a/js/prefs.js
+++ b/js/prefs.js
@@ -1,1220 +1,151 @@
+'use strict'
/* global dijit, __ */
-let seq = "";
-
-function notify_callback2(transport, sticky) {
- notify_info(transport.responseText, sticky);
-}
-
-function updateFeedList() {
-
- const user_search = $("feed_search");
- let search = "";
- if (user_search) { search = user_search.value; }
-
- xhrPost("backend.php", { op: "pref-feeds", search: search }, (transport) => {
- dijit.byId('feedConfigTab').attr('content', transport.responseText);
- selectTab("feedConfig", true);
- notify("");
- });
-}
-
-function checkInactiveFeeds() {
- xhrPost("backend.php", { op: "pref-feeds", method: "getinactivefeeds" }, (transport) => {
- if (parseInt(transport.responseText) > 0) {
- Element.show(dijit.byId("pref_feeds_inactive_btn").domNode);
- }
- });
-}
-
-function updateUsersList(sort_key) {
- const user_search = $("user_search");
- const search = user_search ? user_search.value : "";
-
- const query = { op: "pref-users", sort: sort_key, search: search };
-
- xhrPost("backend.php", query, (transport) => {
- dijit.byId('userConfigTab').attr('content', transport.responseText);
- selectTab("userConfig", true)
- notify("");
- });
-}
-
-function addUser() {
- const login = prompt(__("Please enter login:"), "");
-
- if (login == null) {
- return false;
- }
-
- if (login == "") {
- alert(__("Can't create user: no login specified."));
- return false;
- }
-
- notify_progress("Adding user...");
-
- xhrPost("backend.php", { op: "pref-users", method: "add", login: login }, (transport) => {
- notify_callback2(transport);
- updateUsersList();
- });
-
-}
-
-function editUser(id) {
-
- const query = "backend.php?op=pref-users&method=edit&id=" +
- param_escape(id);
-
- if (dijit.byId("userEditDlg"))
- dijit.byId("userEditDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "userEditDlg",
- title: __("User Editor"),
- style: "width: 600px",
- execute: function () {
- if (this.validate()) {
- notify_progress("Saving data...", true);
-
- xhrPost("backend.php", dojo.formToObject("user_edit_form"), (transport) => {
- dialog.hide();
- updateUsersList();
- });
- }
- },
- href: query
- });
-
- dialog.show();
-}
-
-function editFilter(id) {
-
- const query = "backend.php?op=pref-filters&method=edit&id=" + param_escape(id);
-
- if (dijit.byId("feedEditDlg"))
- dijit.byId("feedEditDlg").destroyRecursive();
-
- if (dijit.byId("filterEditDlg"))
- dijit.byId("filterEditDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "filterEditDlg",
- title: __("Edit Filter"),
- style: "width: 600px",
-
- test: function () {
- const query = "backend.php?" + dojo.formToQuery("filter_edit_form") + "&savemode=test";
-
- editFilterTest(query);
- },
- selectRules: function (select) {
- $$("#filterDlg_Matches input[type=checkbox]").each(function (e) {
- e.checked = select;
- if (select)
- e.parentNode.addClassName("Selected");
- else
- e.parentNode.removeClassName("Selected");
- });
- },
- selectActions: function (select) {
- $$("#filterDlg_Actions input[type=checkbox]").each(function (e) {
- e.checked = select;
-
- if (select)
- e.parentNode.addClassName("Selected");
- else
- e.parentNode.removeClassName("Selected");
-
- });
- },
- editRule: function (e) {
- const li = e.parentNode;
- const rule = li.getElementsByTagName("INPUT")[1].value;
- addFilterRule(li, rule);
- },
- editAction: function (e) {
- const li = e.parentNode;
- const action = li.getElementsByTagName("INPUT")[1].value;
- addFilterAction(li, action);
- },
- removeFilter: function () {
- const msg = __("Remove filter?");
-
- if (confirm(msg)) {
- this.hide();
-
- notify_progress("Removing filter...");
-
- const query = { op: "pref-filters", method: "remove", ids: this.attr('value').id };
-
- xhrPost("backend.php", query, () => {
- updateFilterList();
- });
- }
- },
- addAction: function () {
- addFilterAction();
- },
- addRule: function () {
- addFilterRule();
- },
- deleteAction: function () {
- $$("#filterDlg_Actions li[class*=Selected]").each(function (e) {
- e.parentNode.removeChild(e)
- });
- },
- deleteRule: function () {
- $$("#filterDlg_Matches li[class*=Selected]").each(function (e) {
- e.parentNode.removeChild(e)
- });
- },
- execute: function () {
- if (this.validate()) {
-
- notify_progress("Saving data...", true);
-
- xhrPost("backend.php", dojo.formToObject("filter_edit_form"), () => {
- dialog.hide();
- updateFilterList();
- });
- }
- },
- href: query
- });
-
- dialog.show();
-}
-
-
-function getSelectedLabels() {
- const tree = dijit.byId("labelTree");
- const items = tree.model.getCheckedItems();
- const rv = [];
-
- items.each(function(item) {
- rv.push(tree.model.store.getValue(item, 'bare_id'));
- });
-
- return rv;
-}
-
-function getSelectedUsers() {
- return getSelectedTableRowIds("prefUserList");
-}
-
-function getSelectedFeeds() {
- const tree = dijit.byId("feedTree");
- const items = tree.model.getCheckedItems();
- const rv = [];
-
- items.each(function(item) {
- if (item.id[0].match("FEED:"))
- rv.push(tree.model.store.getValue(item, 'bare_id'));
- });
-
- return rv;
-}
-
-function getSelectedCategories() {
- const tree = dijit.byId("feedTree");
- const items = tree.model.getCheckedItems();
- const rv = [];
-
- items.each(function(item) {
- if (item.id[0].match("CAT:"))
- rv.push(tree.model.store.getValue(item, 'bare_id'));
- });
-
- return rv;
-}
-
-function getSelectedFilters() {
- const tree = dijit.byId("filterTree");
- const items = tree.model.getCheckedItems();
- const rv = [];
-
- items.each(function(item) {
- rv.push(tree.model.store.getValue(item, 'bare_id'));
- });
-
- return rv;
-
-}
-
-function removeSelectedLabels() {
-
- const sel_rows = getSelectedLabels();
-
- if (sel_rows.length > 0) {
- if (confirm(__("Remove selected labels?"))) {
- notify_progress("Removing selected labels...");
-
- const query = { op: "pref-labels", method: "remove",
- ids: sel_rows.toString() };
-
- xhrPost("backend.php", query, () => {
- updateLabelList();
- });
- }
- } else {
- alert(__("No labels are selected."));
- }
-
- return false;
-}
-
-function removeSelectedUsers() {
-
- const sel_rows = getSelectedUsers();
-
- if (sel_rows.length > 0) {
-
- if (confirm(__("Remove selected users? Neither default admin nor your account will be removed."))) {
- notify_progress("Removing selected users...");
-
- const query = { op: "pref-users", method: "remove",
- ids: sel_rows.toString() };
-
- xhrPost("backend.php", query, () => {
- updateUsersList();
- });
- }
-
- } else {
- alert(__("No users are selected."));
- }
-
- return false;
-}
-
-function removeSelectedFilters() {
-
- const sel_rows = getSelectedFilters();
-
- if (sel_rows.length > 0) {
- if (confirm(__("Remove selected filters?"))) {
- notify_progress("Removing selected filters...");
-
- const query = { op: "pref-filters", method: "remove",
- ids: sel_rows.toString() };
-
- xhrPost("backend.php", query, () => {
- updateFilterList();
- });
- }
- } else {
- alert(__("No filters are selected."));
- }
-
- return false;
-}
-
-function removeSelectedFeeds() {
-
- const sel_rows = getSelectedFeeds();
-
- if (sel_rows.length > 0) {
- if (confirm(__("Unsubscribe from selected feeds?"))) {
-
- notify_progress("Unsubscribing from selected feeds...", true);
-
- const query = { op: "pref-feeds", method: "remove",
- ids: sel_rows.toString() };
-
- xhrPost("backend.php", query, () => {
- updateFeedList();
- });
- }
-
- } else {
- alert(__("No feeds are selected."));
- }
-
- return false;
-}
-
-function editSelectedUser() {
- const rows = getSelectedUsers();
-
- if (rows.length == 0) {
- alert(__("No users are selected."));
- return;
- }
-
- if (rows.length > 1) {
- alert(__("Please select only one user."));
- return;
- }
-
- notify("");
-
- editUser(rows[0]);
-}
-
-function resetSelectedUserPass() {
-
- const rows = getSelectedUsers();
-
- if (rows.length == 0) {
- alert(__("No users are selected."));
- return;
- }
-
- if (rows.length > 1) {
- alert(__("Please select only one user."));
- return;
- }
-
- if (confirm(__("Reset password of selected user?"))) {
- notify_progress("Resetting password for selected user...");
-
- const id = rows[0];
-
- xhrPost("backend.php", { op: "pref-users", method: "resetPass", id: id }, (transport) => {
- notify_info(transport.responseText, true);
- });
-
- }
-}
-
-function selectedUserDetails() {
-
- const rows = getSelectedUsers();
-
- if (rows.length == 0) {
- alert(__("No users are selected."));
- return;
- }
-
- if (rows.length > 1) {
- alert(__("Please select only one user."));
- return;
- }
-
- const query = "backend.php?op=pref-users&method=userdetails&id=" + param_escape(rows[0]);
-
- if (dijit.byId("userDetailsDlg"))
- dijit.byId("userDetailsDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "userDetailsDlg",
- title: __("User details"),
- style: "width: 600px",
- execute: function () {
- dialog.hide();
- },
- href: query
- });
-
- dialog.show();
-}
-
-
-function editSelectedFilter() {
- const rows = getSelectedFilters();
-
- if (rows.length == 0) {
- alert(__("No filters are selected."));
- return;
- }
-
- if (rows.length > 1) {
- alert(__("Please select only one filter."));
- return;
- }
-
- notify("");
-
- editFilter(rows[0]);
-
-}
-
-function joinSelectedFilters() {
- const rows = getSelectedFilters();
-
- if (rows.length == 0) {
- alert(__("No filters are selected."));
- return;
- }
-
- if (confirm(__("Combine selected filters?"))) {
- notify_progress("Joining filters...");
-
- xhrPost("backend.php", { op: "pref-filters", method: "join", ids: rows.toString() }, () => {
- updateFilterList();
- });
- }
-}
-
-function editSelectedFeed() {
- const rows = getSelectedFeeds();
-
- if (rows.length == 0) {
- alert(__("No feeds are selected."));
- return;
- }
-
- if (rows.length > 1) {
- return editSelectedFeeds();
- }
-
- notify("");
-
- editFeed(rows[0], {});
-
-}
-
-function editSelectedFeeds() {
- const rows = getSelectedFeeds();
-
- if (rows.length == 0) {
- alert(__("No feeds are selected."));
- return;
- }
-
- notify_progress("Loading, please wait...");
-
- if (dijit.byId("feedEditDlg"))
- dijit.byId("feedEditDlg").destroyRecursive();
-
- xhrPost("backend.php", { op: "pref-feeds", method: "editfeeds", ids: rows.toString() }, (transport) => {
- notify("");
-
- const dialog = new dijit.Dialog({
- id: "feedEditDlg",
- title: __("Edit Multiple Feeds"),
- style: "width: 600px",
- getChildByName: function (name) {
- let rv = null;
- this.getChildren().each(
- function (child) {
- if (child.name == name) {
- rv = child;
- return;
+let App;
+let CommonDialogs;
+let Filters;
+let Users;
+let Helpers;
+
+const Plugins = {};
+
+require(["dojo/_base/kernel",
+ "dojo/_base/declare",
+ "dojo/ready",
+ "dojo/parser",
+ "fox/AppBase",
+ "dojo/_base/loader",
+ "dojo/_base/html",
+ "dijit/ColorPalette",
+ "dijit/Dialog",
+ "dijit/form/Button",
+ "dijit/form/CheckBox",
+ "dijit/form/DropDownButton",
+ "dijit/form/FilteringSelect",
+ "dijit/form/MultiSelect",
+ "dijit/form/Form",
+ "dijit/form/RadioButton",
+ "dijit/form/ComboButton",
+ "dijit/form/Select",
+ "dijit/form/SimpleTextarea",
+ "dijit/form/TextBox",
+ "dijit/form/ValidationTextBox",
+ "dijit/InlineEditBox",
+ "dijit/layout/AccordionContainer",
+ "dijit/layout/AccordionPane",
+ "dijit/layout/BorderContainer",
+ "dijit/layout/ContentPane",
+ "dijit/layout/TabContainer",
+ "dijit/Menu",
+ "dijit/ProgressBar",
+ "dijit/Toolbar",
+ "dijit/Tree",
+ "dijit/tree/dndSource",
+ "dojo/data/ItemFileWriteStore",
+ "lib/CheckBoxStoreModel",
+ "lib/CheckBoxTree",
+ "fox/CommonDialogs",
+ "fox/CommonFilters",
+ "fox/PrefUsers",
+ "fox/PrefHelpers",
+ "fox/PrefFeedStore",
+ "fox/PrefFilterStore",
+ "fox/PrefFeedTree",
+ "fox/PrefFilterTree",
+ "fox/PrefLabelTree"], function (dojo, declare, ready, parser, AppBase) {
+
+ ready(function () {
+ try {
+ const _App = declare("fox.App", AppBase, {
+ constructor: function() {
+ parser.parse();
+
+ this.setLoadingProgress(50);
+
+ const clientTzOffset = new Date().getTimezoneOffset() * 60;
+ const params = {op: "rpc", method: "sanityCheck", clientTzOffset: clientTzOffset};
+
+ xhrPost("backend.php", params, (transport) => {
+ try {
+ this.backendSanityCallback(transport);
+ } catch (e) {
+ this.Error.report(e);
}
});
- return rv;
- },
- toggleField: function (checkbox, elem, label) {
- this.getChildByName(elem).attr('disabled', !checkbox.checked);
-
- if ($(label))
- if (checkbox.checked)
- $(label).removeClassName('insensitive');
- else
- $(label).addClassName('insensitive');
-
- },
- execute: function () {
- if (this.validate() && confirm(__("Save changes to selected feeds?"))) {
- const query = this.attr('value');
-
- /* normalize unchecked checkboxes because [] is not serialized */
-
- Object.keys(query).each((key) => {
- let val = query[key];
-
- if (typeof val == "object" && val.length == 0)
- query[key] = ["off"];
- });
-
- notify_progress("Saving data...", true);
-
- xhrPost("backend.php", query, () => {
- dialog.hide();
- updateFeedList();
- });
- }
- },
- content: transport.responseText
- });
-
- dialog.show();
- });
-}
-
-function opmlImportComplete(iframe) {
- if (!iframe.contentDocument.body.innerHTML) return false;
-
- Element.show(iframe);
-
- notify('');
-
- if (dijit.byId('opmlImportDlg'))
- dijit.byId('opmlImportDlg').destroyRecursive();
-
- const content = iframe.contentDocument.body.innerHTML;
-
- const dialog = new dijit.Dialog({
- id: "opmlImportDlg",
- title: __("OPML Import"),
- style: "width: 600px",
- onCancel: function () {
- window.location.reload();
- },
- execute: function () {
- window.location.reload();
- },
- content: content
- });
-
- dialog.show();
-}
-
-function opmlImport() {
-
- const opml_file = $("opml_file");
-
- if (opml_file.value.length == 0) {
- alert(__("Please choose an OPML file first."));
- return false;
- } else {
- notify_progress("Importing, please wait...", true);
-
- Element.show("upload_iframe");
-
- return true;
- }
-}
-
-
-function updateFilterList() {
- const user_search = $("filter_search");
- let search = "";
- if (user_search) { search = user_search.value; }
-
- xhrPost("backend.php", { op: "pref-filters", search: search }, (transport) => {
- dijit.byId('filterConfigTab').attr('content', transport.responseText);
- notify("");
- });
-}
-
-function updateLabelList() {
- xhrPost("backend.php", { op: "pref-labels" }, (transport) => {
- dijit.byId('labelConfigTab').attr('content', transport.responseText);
- notify("");
- });
-}
-
-function updatePrefsList() {
- xhrPost("backend.php", { op: "pref-prefs" }, (transport) => {
- dijit.byId('genConfigTab').attr('content', transport.responseText);
- notify("");
- });
-}
-
-function updateSystemList() {
- xhrPost("backend.php", { op: "pref-system" }, (transport) => {
- dijit.byId('systemConfigTab').attr('content', transport.responseText);
- notify("");
- });
-}
-
-function selectTab(id, noupdate) {
- if (!noupdate) {
- notify_progress("Loading, please wait...");
-
- switch (id) {
- case "feedConfig":
- updateFeedList();
- break;
- case "filterConfig":
- updateFilterList();
- break;
- case "labelConfig":
- updateLabelList();
- break;
- case "genConfig":
- updatePrefsList();
- break;
- case "userConfig":
- updateUsersList();
- break;
- case "systemConfig":
- updateSystemList();
- break;
- default:
- console.warn("unknown tab", id);
- }
-
- const tab = dijit.byId(id + "Tab");
- dijit.byId("pref-tabs").selectChild(tab);
-
- }
-}
-
-function init_second_stage() {
- document.onkeydown = pref_hotkey_handler;
- setLoadingProgress(50);
- notify("");
-
- let tab = getURLParam('tab');
-
- if (tab) {
- tab = dijit.byId(tab + "Tab");
- if (tab) dijit.byId("pref-tabs").selectChild(tab);
- }
-
- const method = getURLParam('method');
-
- if (method == 'editFeed') {
- const param = getURLParam('methodparam');
-
- window.setTimeout(function() { editFeed(param) }, 100);
- }
-
- setInterval(hotkeyPrefixTimeout, 5*1000);
-}
-
-function init() {
- window.onerror = function (message, filename, lineno, colno, error) {
- report_error(message, filename, lineno, colno, error);
- };
-
- require(["dojo/_base/kernel",
- "dojo/ready",
- "dojo/parser",
- "dojo/_base/loader",
- "dojo/_base/html",
- "dijit/ColorPalette",
- "dijit/Dialog",
- "dijit/form/Button",
- "dijit/form/CheckBox",
- "dijit/form/DropDownButton",
- "dijit/form/FilteringSelect",
- "dijit/form/MultiSelect",
- "dijit/form/Form",
- "dijit/form/RadioButton",
- "dijit/form/ComboButton",
- "dijit/form/Select",
- "dijit/form/SimpleTextarea",
- "dijit/form/TextBox",
- "dijit/form/ValidationTextBox",
- "dijit/InlineEditBox",
- "dijit/layout/AccordionContainer",
- "dijit/layout/AccordionPane",
- "dijit/layout/BorderContainer",
- "dijit/layout/ContentPane",
- "dijit/layout/TabContainer",
- "dijit/Menu",
- "dijit/ProgressBar",
- "dijit/Toolbar",
- "dijit/Tree",
- "dijit/tree/dndSource",
- "dojo/data/ItemFileWriteStore",
- "lib/CheckBoxStoreModel",
- "lib/CheckBoxTree",
- "fox/PrefFeedStore",
- "fox/PrefFilterStore",
- "fox/PrefFeedTree",
- "fox/PrefFilterTree",
- "fox/PrefLabelTree"], function (dojo, ready, parser) {
-
- ready(function () {
- try {
- parser.parse();
-
- setLoadingProgress(50);
-
- const clientTzOffset = new Date().getTimezoneOffset() * 60;
- const params = { op: "rpc", method: "sanityCheck", clientTzOffset: clientTzOffset };
-
- xhrPost("backend.php", params, (transport) => {
- backend_sanity_check_callback(transport);
- });
-
- } catch (e) {
- exception_error(e);
- }
- });
- });
-}
-
-
-function validatePrefsReset() {
- if (confirm(__("Reset to defaults?"))) {
-
- const query = "?op=pref-prefs&method=resetconfig";
-
- xhrPost("backend.php", { op: "pref-prefs", method: "resetconfig" }, (transport) => {
- updatePrefsList();
- notify_info(transport.responseText);
- });
- }
-
- return false;
-}
-
-function pref_hotkey_handler(e) {
- if (e.target.nodeName == "INPUT" || e.target.nodeName == "TEXTAREA") return;
-
- const action_name = keyeventToAction(e);
-
- if (action_name) {
- switch (action_name) {
- case "feed_subscribe":
- quickAddFeed();
- return false;
- case "create_label":
- addLabel();
- return false;
- case "create_filter":
- quickAddFilter();
- return false;
- case "help_dialog":
- helpDialog("main");
- return false;
- default:
- console.log("unhandled action: " + action_name + "; keycode: " + e.which);
- }
- }
-}
-
-function removeCategory(id, item) {
-
- if (confirm(__("Remove category %s? Any nested feeds would be placed into Uncategorized.").replace("%s", item.name))) {
- notify_progress("Removing category...");
-
- const query = { op: "pref-feeds", method: "removeCat",
- ids: id };
-
- xhrPost("backend.php", query, () => {
- notify('');
- updateFeedList();
- });
- }
-}
-
-function removeSelectedCategories() {
- const sel_rows = getSelectedCategories();
-
- if (sel_rows.length > 0) {
- if (confirm(__("Remove selected categories?"))) {
- notify_progress("Removing selected categories...");
-
- const query = { op: "pref-feeds", method: "removeCat",
- ids: sel_rows.toString() };
-
- xhrPost("backend.php", query, () => {
- updateFeedList();
- });
- }
- } else {
- alert(__("No categories are selected."));
- }
-
- return false;
-}
-
-function createCategory() {
- const title = prompt(__("Category title:"));
-
- if (title) {
- notify_progress("Creating category...");
-
- xhrPost("backend.php", { op: "pref-feeds", method: "addCat", cat: title }, () => {
- notify('');
- updateFeedList();
- });
- }
-}
-
-function showInactiveFeeds() {
- const query = "backend.php?op=pref-feeds&method=inactiveFeeds";
-
- if (dijit.byId("inactiveFeedsDlg"))
- dijit.byId("inactiveFeedsDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "inactiveFeedsDlg",
- title: __("Feeds without recent updates"),
- style: "width: 600px",
- getSelectedFeeds: function () {
- return getSelectedTableRowIds("prefInactiveFeedList");
- },
- removeSelected: function () {
- const sel_rows = this.getSelectedFeeds();
-
- if (sel_rows.length > 0) {
- if (confirm(__("Remove selected feeds?"))) {
- notify_progress("Removing selected feeds...", true);
-
- const query = { op: "pref-feeds", method: "remove",
- ids: sel_rows.toString() };
+ },
+ initSecondStage: function() {
+ this.enableCsrfSupport();
+
+ document.onkeydown = (event) => { App.hotkeyHandler(event) };
+ App.setLoadingProgress(50);
+ Notify.close();
+
+ let tab = App.urlParam('tab');
+
+ if (tab) {
+ tab = dijit.byId(tab + "Tab");
+ if (tab) {
+ dijit.byId("pref-tabs").selectChild(tab);
+
+ switch (App.urlParam('method')) {
+ case "editfeed":
+ window.setTimeout(function () {
+ CommonDialogs.editFeed(App.urlParam('methodparam'))
+ }, 100);
+ break;
+ default:
+ console.warn("initSecondStage, unknown method:", App.urlParam("method"));
+ }
+ }
+ } else {
+ let tab = localStorage.getItem("ttrss:prefs-tab");
+
+ if (tab) {
+ tab = dijit.byId(tab);
+ if (tab) {
+ dijit.byId("pref-tabs").selectChild(tab);
+ }
+ }
+ }
- xhrPost("backend.php", query, () => {
- notify('');
- dialog.hide();
- updateFeedList();
+ dojo.connect(dijit.byId("pref-tabs"), "selectChild", function (elem) {
+ localStorage.setItem("ttrss:prefs-tab", elem.id);
});
- }
-
- } else {
- alert(__("No feeds are selected."));
- }
- },
- execute: function () {
- if (this.validate()) {
- }
- },
- href: query
- });
-
- dialog.show();
-}
-
-function opmlRegenKey() {
- if (confirm(__("Replace current OPML publishing address with a new one?"))) {
- notify_progress("Trying to change address...", true);
- xhrJson("backend.php", { op: "pref-feeds", method: "regenOPMLKey" }, (reply) => {
- if (reply) {
- const new_link = reply.link;
- const e = $('pub_opml_url');
-
- if (new_link) {
- e.href = new_link;
- e.innerHTML = new_link;
-
- new Effect.Highlight(e);
-
- notify('');
-
- } else {
- notify_error("Could not change feed URL.");
+ },
+ hotkeyHandler: function (event) {
+ if (event.target.nodeName == "INPUT" || event.target.nodeName == "TEXTAREA") return;
+
+ const action_name = App.keyeventToAction(event);
+
+ if (action_name) {
+ switch (action_name) {
+ case "feed_subscribe":
+ CommonDialogs.quickAddFeed();
+ return false;
+ case "create_label":
+ CommonDialogs.addLabel();
+ return false;
+ case "create_filter":
+ Filters.quickAddFilter();
+ return false;
+ case "help_dialog":
+ App.helpDialog("main");
+ return false;
+ default:
+ console.log("unhandled action: " + action_name + "; keycode: " + event.which);
+ }
+ }
+ },
+ isPrefs: function() {
+ return true;
}
- }
- });
- }
- return false;
-}
-
-function labelColorReset() {
- const labels = getSelectedLabels();
-
- if (labels.length > 0) {
- if (confirm(__("Reset selected labels to default colors?"))) {
-
- const query = { op: "pref-labels", method: "colorreset",
- ids: labels.toString() };
-
- xhrPost("backend.php", query, () => {
- updateLabelList();
});
- }
-
- } else {
- alert(__("No labels are selected."));
- }
-}
-
-function inPreferences() {
- return true;
-}
-
-function editProfiles() {
-
- if (dijit.byId("profileEditDlg"))
- dijit.byId("profileEditDlg").destroyRecursive();
-
- const query = "backend.php?op=pref-prefs&method=editPrefProfiles";
- const dialog = new dijit.Dialog({
- id: "profileEditDlg",
- title: __("Settings Profiles"),
- style: "width: 600px",
- getSelectedProfiles: function () {
- return getSelectedTableRowIds("prefFeedProfileList");
- },
- removeSelected: function () {
- const sel_rows = this.getSelectedProfiles();
+ App = new _App();
- if (sel_rows.length > 0) {
- if (confirm(__("Remove selected profiles? Active and default profiles will not be removed."))) {
- notify_progress("Removing selected profiles...", true);
-
- const query = { op: "rpc", method: "remprofiles",
- ids: sel_rows.toString() };
-
- xhrPost("backend.php", query, () => {
- notify('');
- editProfiles();
- });
- }
-
- } else {
- alert(__("No profiles are selected."));
- }
- },
- activateProfile: function () {
- const sel_rows = this.getSelectedProfiles();
-
- if (sel_rows.length == 1) {
- if (confirm(__("Activate selected profile?"))) {
- notify_progress("Loading, please wait...");
-
- xhrPost("backend.php", { op: "rpc", method: "setprofile", id: sel_rows.toString() }, () => {
- window.location.reload();
- });
- }
-
- } else {
- alert(__("Please choose a profile to activate."));
- }
- },
- addProfile: function () {
- if (this.validate()) {
- notify_progress("Creating profile...", true);
-
- const query = { op: "rpc", method: "addprofile", title: dialog.attr('value').newprofile };
-
- xhrPost("backend.php", query, () => {
- notify('');
- editProfiles();
- });
-
- }
- },
- execute: function () {
- if (this.validate()) {
- }
- },
- href: query
- });
-
- dialog.show();
-}
-
-/*
-function activatePrefProfile() {
-
- const sel_rows = getSelectedFeedCats();
-
- if (sel_rows.length == 1) {
-
- const ok = confirm(__("Activate selected profile?"));
-
- if (ok) {
- notify_progress("Loading, please wait...");
-
- xhrPost("backend.php", { op: "rpc", method: "setprofile", id: sel_rows.toString() }, () => {
- window.location.reload();
- });
+ } catch (e) {
+ this.Error.report(e);
}
-
- } else {
- alert(__("Please choose a profile to activate."));
- }
-
- return false;
-} */
-
-function clearFeedAccessKeys() {
-
- if (confirm(__("This will invalidate all previously generated feed URLs. Continue?"))) {
- notify_progress("Clearing URLs...");
-
- xhrPost("backend.php", { op: "pref-feeds", method: "clearKeys" }, () => {
- notify_info("Generated URLs cleared.");
- });
- }
-
- return false;
-}
-
-function resetFilterOrder() {
- notify_progress("Loading, please wait...");
-
- xhrPost("backend.php", { op: "pref-filters", method: "filtersortreset" }, () => {
- updateFilterList();
});
-}
-
-
-function resetFeedOrder() {
- notify_progress("Loading, please wait...");
-
- xhrPost("backend.php", { op: "pref-feeds", method: "feedsortreset" }, () => {
- updateFeedList();
- });
-}
-
-function resetCatOrder() {
- notify_progress("Loading, please wait...");
-
- xhrPost("backend.php", { op: "pref-feeds", method: "catsortreset" }, () => {
- updateFeedList();
- });
-}
-
-function editCat(id, item) {
- const new_name = prompt(__('Rename category to:'), item.name);
-
- if (new_name && new_name != item.name) {
-
- notify_progress("Loading, please wait...");
-
- xhrPost("backend.php", { op: 'pref-feeds', method: 'renamecat', id: id, title: new_name }, () => {
- updateFeedList();
- });
- }
-}
-
-function editLabel(id) {
- const query = "backend.php?op=pref-labels&method=edit&id=" +
- param_escape(id);
-
- if (dijit.byId("labelEditDlg"))
- dijit.byId("labelEditDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "labelEditDlg",
- title: __("Label Editor"),
- style: "width: 600px",
- setLabelColor: function (id, fg, bg) {
-
- let kind = '';
- let color = '';
-
- if (fg && bg) {
- kind = 'both';
- } else if (fg) {
- kind = 'fg';
- color = fg;
- } else if (bg) {
- kind = 'bg';
- color = bg;
- }
-
- const e = $("LICID-" + id);
-
- if (e) {
- if (fg) e.style.color = fg;
- if (bg) e.style.backgroundColor = bg;
- }
-
- const query = { op: "pref-labels", method: "colorset", kind: kind,
- ids: id, fg: fg, bg: bg, color: color };
-
- xhrPost("backend.php", query, () => {
- updateFilterList(); // maybe there's labels in there
- });
-
- },
- execute: function () {
- if (this.validate()) {
- const caption = this.attr('value').caption;
- const fg_color = this.attr('value').fg_color;
- const bg_color = this.attr('value').bg_color;
-
- dijit.byId('labelTree').setNameById(id, caption);
- this.setLabelColor(id, fg_color, bg_color);
- this.hide();
-
- xhrPost("backend.php", this.attr('value'), () => {
- updateFilterList(); // maybe there's labels in there
- });
- }
- },
- href: query
- });
-
- dialog.show();
-}
-
-
-function customizeCSS() {
- const query = "backend.php?op=pref-prefs&method=customizeCSS";
-
- if (dijit.byId("cssEditDlg"))
- dijit.byId("cssEditDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "cssEditDlg",
- title: __("Customize stylesheet"),
- style: "width: 600px",
- execute: function () {
- notify_progress('Saving data...', true);
-
- xhrPost("backend.php", this.attr('value'), () => {
- window.location.reload();
- });
-
- },
- href: query
- });
-
- dialog.show();
-}
-
-function insertSSLserial(value) {
- dijit.byId("SSL_CERT_SERIAL").attr('value', value);
-}
-
-function gotoExportOpml(filename, settings) {
- const tmp = settings ? 1 : 0;
- document.location.href = "backend.php?op=opml&method=export&filename=" + filename + "&settings=" + tmp;
-}
-
-
-function batchSubscribe() {
- const query = "backend.php?op=pref-feeds&method=batchSubscribe";
-
- // overlapping widgets
- if (dijit.byId("batchSubDlg")) dijit.byId("batchSubDlg").destroyRecursive();
- if (dijit.byId("feedAddDlg")) dijit.byId("feedAddDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "batchSubDlg",
- title: __("Batch subscribe"),
- style: "width: 600px",
- execute: function () {
- if (this.validate()) {
- notify_progress(__("Subscribing to feeds..."), true);
-
- xhrPost("backend.php", this.attr('value'), () => {
- notify("");
- updateFeedList();
- dialog.hide();
- });
- }
- },
- href: query
- });
-
- dialog.show();
-}
-
-function clearPluginData(name) {
- if (confirm(__("Clear stored data for this plugin?"))) {
- notify_progress("Loading, please wait...");
-
- xhrPost("backend.php", { op: "pref-prefs", method: "clearplugindata", name: name }, () => {
- notify('');
- updatePrefsList();
- });
- }
-}
-
-function clearSqlLog() {
-
- if (confirm(__("Clear all messages in the error log?"))) {
-
- notify_progress("Loading, please wait...");
-
- xhrPost("backend.php", { op: "pref-system", method: "clearLog" }, () => {
- updateSystemList();
- });
-
- }
-}
-
-function updateSelectedPrompt() {
- // no-op shim for toggleSelectedRow()
-}
-
-function gotoMain() {
- document.location.href = "index.php";
-}
+}); \ No newline at end of file
diff --git a/js/tt-rss.js b/js/tt-rss.js
index 99a484e9a..fc87ae24d 100644
--- a/js/tt-rss.js
+++ b/js/tt-rss.js
@@ -1,242 +1,74 @@
-/* global dijit, __ */
-
-let global_unread = -1;
-let _widescreen_mode = false;
-let _rpc_seq = 0;
-let _active_feed_id = 0;
-let _active_feed_is_cat = false;
-let hotkey_actions = {};
-let _headlines_scroll_timeout = false;
-
-function next_seq() {
- _rpc_seq += 1;
- return _rpc_seq;
-}
-
-function get_seq() {
- return _rpc_seq;
-}
-
-function activeFeedIsCat() {
- return !!_active_feed_is_cat;
-}
-
-function getActiveFeedId() {
- return _active_feed_id;
-}
-
-function setActiveFeedId(id, is_cat) {
- hash_set('f', id);
- hash_set('c', is_cat ? 1 : 0);
-
- _active_feed_id = id;
- _active_feed_is_cat = is_cat;
-
- $("headlines-frame").setAttribute("feed-id", id);
- $("headlines-frame").setAttribute("is-cat", is_cat ? 1 : 0);
-
- selectFeed(id, is_cat);
-
- PluginHost.run(PluginHost.HOOK_FEED_SET_ACTIVE, _active_article_id);
-}
-
-
-function updateFeedList() {
- try {
- Element.show("feedlistLoading");
-
- resetCounterCache();
-
- if (dijit.byId("feedTree")) {
- dijit.byId("feedTree").destroyRecursive();
- }
-
- const store = new dojo.data.ItemFileWriteStore({
- url: "backend.php?op=pref_feeds&method=getfeedtree&mode=2"
- });
-
- const treeModel = new fox.FeedStoreModel({
- store: store,
- query: {
- "type": getInitParam('enable_feed_cats') == 1 ? "category" : "feed"
- },
- rootId: "root",
- rootLabel: "Feeds",
- childrenAttrs: ["items"]
- });
-
- const tree = new fox.FeedTree({
- model: treeModel,
- onClick: function (item, node) {
- const id = String(item.id);
- const is_cat = id.match("^CAT:");
- const feed = id.substr(id.indexOf(":") + 1);
- viewfeed({feed: feed, is_cat: is_cat});
- return false;
- },
- openOnClick: false,
- showRoot: false,
- persist: true,
- id: "feedTree",
- }, "feedTree");
-
- var tmph = dojo.connect(dijit.byId('feedMenu'), '_openMyself', function (event) {
- console.log(dijit.getEnclosingWidget(event.target));
- dojo.disconnect(tmph);
- });
-
- $("feeds-holder").appendChild(tree.domNode);
-
- var tmph = dojo.connect(tree, 'onLoad', function () {
- dojo.disconnect(tmph);
- Element.hide("feedlistLoading");
-
- try {
- feedlist_init();
-
- setLoadingProgress(25);
- } catch (e) {
- exception_error(e);
- }
- });
-
- tree.startup();
- } catch (e) {
- exception_error(e);
- }
-}
-
-function catchupAllFeeds() {
-
- const str = __("Mark all articles as read?");
-
- if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
-
- notify_progress("Marking all feeds as read...");
-
- xhrPost("backend.php", {op: "feeds", method: "catchupAll"}, () => {
- request_counters(true);
- viewCurrentFeed();
- });
-
- global_unread = 0;
- updateTitle("");
- }
-}
-
-function viewCurrentFeed(method) {
- console.log("viewCurrentFeed: " + method);
-
- if (getActiveFeedId() != undefined) {
- viewfeed({feed: getActiveFeedId(), is_cat: activeFeedIsCat(), method: method});
- }
- return false; // block unneeded form submits
-}
-
-function timeout() {
- if (getInitParam("bw_limit") != "1") {
- request_counters(true);
- setTimeout(timeout, 60*1000);
- }
-}
-
-function search() {
- const query = "backend.php?op=feeds&method=search&param=" +
- param_escape(getActiveFeedId() + ":" + activeFeedIsCat());
-
- if (dijit.byId("searchDlg"))
- dijit.byId("searchDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "searchDlg",
- title: __("Search"),
- style: "width: 600px",
- execute: function() {
- if (this.validate()) {
- _search_query = this.attr('value');
- this.hide();
- viewCurrentFeed();
- }
- },
- href: query});
-
- dialog.show();
-}
-
-function updateTitle() {
- let tmp = "Tiny Tiny RSS";
-
- if (global_unread > 0) {
- tmp = "(" + global_unread + ") " + tmp;
- }
-
- document.title = tmp;
-}
-
-function genericSanityCheck() {
- setCookie("ttrss_test", "TEST");
-
- if (getCookie("ttrss_test") != "TEST") {
- return fatalError(2);
- }
-
- return true;
-}
-
-
-function init() {
-
- window.onerror = function(message, filename, lineno, colno, error) {
- report_error(message, filename, lineno, colno, error);
- };
-
- require(["dojo/_base/kernel",
- "dojo/ready",
- "dojo/parser",
- "dojo/_base/loader",
- "dojo/_base/html",
- "dojo/query",
- "dijit/ProgressBar",
- "dijit/ColorPalette",
- "dijit/Dialog",
- "dijit/form/Button",
- "dijit/form/ComboButton",
- "dijit/form/CheckBox",
- "dijit/form/DropDownButton",
- "dijit/form/FilteringSelect",
- "dijit/form/Form",
- "dijit/form/RadioButton",
- "dijit/form/Select",
- "dijit/form/MultiSelect",
- "dijit/form/SimpleTextarea",
- "dijit/form/TextBox",
- "dijit/form/ComboBox",
- "dijit/form/ValidationTextBox",
- "dijit/InlineEditBox",
- "dijit/layout/AccordionContainer",
- "dijit/layout/BorderContainer",
- "dijit/layout/ContentPane",
- "dijit/layout/TabContainer",
- "dijit/PopupMenuItem",
- "dijit/Menu",
- "dijit/Toolbar",
- "dijit/Tree",
- "dijit/tree/dndSource",
- "dijit/tree/ForestStoreModel",
- "dojo/data/ItemFileWriteStore",
- "fox/FeedStoreModel",
- "fox/FeedTree" ], function (dojo, ready, parser) {
-
- ready(function() {
-
- try {
+'use strict'
+/* global dijit,__ */
+
+let App;
+let CommonDialogs;
+let Filters;
+let Feeds;
+let Headlines;
+let Article;
+let ArticleCache;
+let PluginHost;
+
+const Plugins = {};
+
+require(["dojo/_base/kernel",
+ "dojo/_base/declare",
+ "dojo/ready",
+ "dojo/parser",
+ "fox/AppBase",
+ "dojo/_base/loader",
+ "dojo/_base/html",
+ "dojo/query",
+ "dijit/ProgressBar",
+ "dijit/ColorPalette",
+ "dijit/Dialog",
+ "dijit/form/Button",
+ "dijit/form/ComboButton",
+ "dijit/form/CheckBox",
+ "dijit/form/DropDownButton",
+ "dijit/form/FilteringSelect",
+ "dijit/form/Form",
+ "dijit/form/RadioButton",
+ "dijit/form/Select",
+ "dijit/form/MultiSelect",
+ "dijit/form/SimpleTextarea",
+ "dijit/form/TextBox",
+ "dijit/form/ComboBox",
+ "dijit/form/ValidationTextBox",
+ "dijit/InlineEditBox",
+ "dijit/layout/AccordionContainer",
+ "dijit/layout/BorderContainer",
+ "dijit/layout/ContentPane",
+ "dijit/layout/TabContainer",
+ "dijit/PopupMenuItem",
+ "dijit/Menu",
+ "dijit/Toolbar",
+ "dijit/Tree",
+ "dijit/tree/dndSource",
+ "dijit/tree/ForestStoreModel",
+ "dojo/data/ItemFileWriteStore",
+ "fox/PluginHost",
+ "fox/CommonFilters",
+ "fox/CommonDialogs",
+ "fox/Feeds",
+ "fox/Headlines",
+ "fox/Article",
+ "fox/ArticleCache",
+ "fox/FeedStoreModel",
+ "fox/FeedTree"], function (dojo, declare, ready, parser, AppBase) {
+
+ ready(function () {
+ try {
+ const _App = declare("fox.App", AppBase, {
+ global_unread: -1,
+ _widescreen_mode: false,
+ hotkey_actions: {},
+ constructor: function () {
parser.parse();
- if (!genericSanityCheck())
- return false;
-
- setLoadingProgress(30);
- init_hotkey_actions();
+ this.setLoadingProgress(30);
+ this.initHotkeyActions();
const a = document.createElement('audio');
const hasAudio = !!a.canPlayType;
@@ -245,663 +77,482 @@ function init() {
const clientTzOffset = new Date().getTimezoneOffset() * 60;
const params = {
- op: "rpc", method: "sanityCheck", hasAudio: hasAudio,
- hasMp3: hasMp3,
- clientTzOffset: clientTzOffset,
- hasSandbox: hasSandbox
- };
+ op: "rpc", method: "sanityCheck", hasAudio: hasAudio,
+ hasMp3: hasMp3,
+ clientTzOffset: clientTzOffset,
+ hasSandbox: hasSandbox
+ };
xhrPost("backend.php", params, (transport) => {
try {
- backend_sanity_check_callback(transport);
+ App.backendSanityCallback(transport);
} catch (e) {
- console.error(e);
+ App.Error.report(e);
}
});
+ },
+ initSecondStage: function () {
+ this.enableCsrfSupport();
- } catch (e) {
- exception_error(e);
- }
-
- });
-
-
- });
-}
-
-function init_hotkey_actions() {
- hotkey_actions["next_feed"] = function() {
- const rv = dijit.byId("feedTree").getNextFeed(
- getActiveFeedId(), activeFeedIsCat());
-
- if (rv) viewfeed({feed: rv[0], is_cat: rv[1], delayed: true})
- };
- hotkey_actions["prev_feed"] = function() {
- const rv = dijit.byId("feedTree").getPreviousFeed(
- getActiveFeedId(), activeFeedIsCat());
-
- if (rv) viewfeed({feed: rv[0], is_cat: rv[1], delayed: true})
- };
- hotkey_actions["next_article"] = function() {
- moveToPost('next');
- };
- hotkey_actions["prev_article"] = function() {
- moveToPost('prev');
- };
- hotkey_actions["next_article_noscroll"] = function() {
- moveToPost('next', true);
- };
- hotkey_actions["prev_article_noscroll"] = function() {
- moveToPost('prev', true);
- };
- hotkey_actions["next_article_noexpand"] = function() {
- moveToPost('next', true, true);
- };
- hotkey_actions["prev_article_noexpand"] = function() {
- moveToPost('prev', true, true);
- };
- hotkey_actions["search_dialog"] = function() {
- search();
- };
- hotkey_actions["toggle_mark"] = function() {
- selectionToggleMarked();
- };
- hotkey_actions["toggle_publ"] = function() {
- selectionTogglePublished();
- };
- hotkey_actions["toggle_unread"] = function() {
- selectionToggleUnread({no_error: 1});
- };
- hotkey_actions["edit_tags"] = function() {
- const id = getActiveArticleId();
- if (id) {
- editArticleTags(id);
- }
- }
- hotkey_actions["open_in_new_window"] = function() {
- if (getActiveArticleId()) {
- openArticleInNewWindow(getActiveArticleId());
- }
- };
- hotkey_actions["catchup_below"] = function() {
- catchupRelativeToArticle(1);
- };
- hotkey_actions["catchup_above"] = function() {
- catchupRelativeToArticle(0);
- };
- hotkey_actions["article_scroll_down"] = function() {
- scrollArticle(40);
- };
- hotkey_actions["article_scroll_up"] = function() {
- scrollArticle(-40);
- };
- hotkey_actions["close_article"] = function() {
- if (isCombinedMode()) {
- cdmCollapseActive();
- } else {
- closeArticlePanel();
- }
- };
- hotkey_actions["email_article"] = function() {
- if (typeof emailArticle != "undefined") {
- emailArticle();
- } else if (typeof mailtoArticle != "undefined") {
- mailtoArticle();
- } else {
- alert(__("Please enable mail plugin first."));
- }
- };
- hotkey_actions["select_all"] = function() {
- selectArticles('all');
- };
- hotkey_actions["select_unread"] = function() {
- selectArticles('unread');
- };
- hotkey_actions["select_marked"] = function() {
- selectArticles('marked');
- };
- hotkey_actions["select_published"] = function() {
- selectArticles('published');
- };
- hotkey_actions["select_invert"] = function() {
- selectArticles('invert');
- };
- hotkey_actions["select_none"] = function() {
- selectArticles('none');
- };
- hotkey_actions["feed_refresh"] = function() {
- if (getActiveFeedId() != undefined) {
- viewfeed({feed: getActiveFeedId(), is_cat: activeFeedIsCat()});
- return;
- }
- };
- hotkey_actions["feed_unhide_read"] = function() {
- toggleDispRead();
- };
- hotkey_actions["feed_subscribe"] = function() {
- quickAddFeed();
- };
- hotkey_actions["feed_debug_update"] = function() {
- if (!activeFeedIsCat() && parseInt(getActiveFeedId()) > 0) {
- window.open("backend.php?op=feeds&method=update_debugger&feed_id=" + getActiveFeedId() +
- "&csrf_token=" + getInitParam("csrf_token"));
- } else {
- alert("You can't debug this kind of feed.");
- }
- };
-
- hotkey_actions["feed_debug_viewfeed"] = function() {
- viewfeed({feed: getActiveFeedId(), is_cat: activeFeedIsCat(), viewfeed_debug: true});
- };
-
- hotkey_actions["feed_edit"] = function() {
- if (activeFeedIsCat())
- alert(__("You can't edit this kind of feed."));
- else
- editFeed(getActiveFeedId());
- };
- hotkey_actions["feed_catchup"] = function() {
- if (getActiveFeedId() != undefined) {
- catchupCurrentFeed();
- return;
- }
- };
- hotkey_actions["feed_reverse"] = function() {
- reverseHeadlineOrder();
- };
- hotkey_actions["feed_toggle_vgroup"] = function() {
- xhrPost("backend.php", {op: "rpc", method: "togglepref", key: "VFEED_GROUP_BY_FEED"}, () => {
- viewCurrentFeed();
- })
- };
- hotkey_actions["catchup_all"] = function() {
- catchupAllFeeds();
- };
- hotkey_actions["cat_toggle_collapse"] = function() {
- if (activeFeedIsCat()) {
- dijit.byId("feedTree").collapseCat(getActiveFeedId());
- return;
- }
- };
- hotkey_actions["goto_all"] = function() {
- viewfeed({feed: -4});
- };
- hotkey_actions["goto_fresh"] = function() {
- viewfeed({feed: -3});
- };
- hotkey_actions["goto_marked"] = function() {
- viewfeed({feed: -1});
- };
- hotkey_actions["goto_published"] = function() {
- viewfeed({feed: -2});
- };
- hotkey_actions["goto_tagcloud"] = function() {
- displayDlg(__("Tag cloud"), "printTagCloud");
- };
- hotkey_actions["goto_prefs"] = function() {
- gotoPreferences();
- };
- hotkey_actions["select_article_cursor"] = function() {
- const id = getArticleUnderPointer();
- if (id) {
- const row = $("RROW-" + id);
-
- if (row) {
- const cb = dijit.getEnclosingWidget(
- row.select(".rchk")[0]);
-
- if (cb) {
- if (!row.hasClassName("active"))
- cb.attr("checked", !cb.attr("checked"));
-
- toggleSelectRowById(cb, "RROW-" + id);
- return false;
- }
- }
- }
- };
- hotkey_actions["create_label"] = function() {
- addLabel();
- };
- hotkey_actions["create_filter"] = function() {
- quickAddFilter();
- };
- hotkey_actions["collapse_sidebar"] = function() {
- collapse_feedlist();
- };
- hotkey_actions["toggle_embed_original"] = function() {
- if (typeof embedOriginalArticle != "undefined") {
- if (getActiveArticleId())
- embedOriginalArticle(getActiveArticleId());
- } else {
- alert(__("Please enable embed_original plugin first."));
- }
- };
- hotkey_actions["toggle_widescreen"] = function() {
- if (!isCombinedMode()) {
- _widescreen_mode = !_widescreen_mode;
-
- // reset stored sizes because geometry changed
- setCookie("ttrss_ci_width", 0);
- setCookie("ttrss_ci_height", 0);
-
- switchPanelMode(_widescreen_mode);
- } else {
- alert(__("Widescreen is not available in combined mode."));
- }
- };
- hotkey_actions["help_dialog"] = function() {
- helpDialog("main");
- };
- hotkey_actions["toggle_combined_mode"] = function() {
- notify_progress("Loading, please wait...");
-
- const value = isCombinedMode() ? "false" : "true";
-
- xhrPost("backend.php", {op: "rpc", method: "setpref", key: "COMBINED_DISPLAY_MODE", value: value}, () => {
- setInitParam("combined_display_mode",
- !getInitParam("combined_display_mode"));
-
- closeArticlePanel();
- viewCurrentFeed();
- })
- };
- hotkey_actions["toggle_cdm_expanded"] = function() {
- notify_progress("Loading, please wait...");
-
- const value = getInitParam("cdm_expanded") ? "false" : "true";
-
- xhrPost("backend.php", { op: "rpc", method: "setpref", key: "CDM_EXPANDED", value: value }, () => {
- setInitParam("cdm_expanded", !getInitParam("cdm_expanded"));
- viewCurrentFeed();
- });
- };
-
-}
-
-function init_second_stage() {
- updateFeedList();
- closeArticlePanel();
-
- if (parseInt(getCookie("ttrss_fh_width")) > 0) {
- dijit.byId("feeds-holder").domNode.setStyle(
- {width: getCookie("ttrss_fh_width") + "px" });
- }
-
- dijit.byId("main").resize();
-
- var tmph = dojo.connect(dijit.byId('feeds-holder'), 'resize',
- function (args) {
- if (args && args.w >= 0) {
- setCookie("ttrss_fh_width", args.w, getInitParam("cookie_lifetime"));
- }
- });
-
- var tmph = dojo.connect(dijit.byId('content-insert'), 'resize',
- function (args) {
- if (args && args.w >= 0 && args.h >= 0) {
- setCookie("ttrss_ci_width", args.w, getInitParam("cookie_lifetime"));
- setCookie("ttrss_ci_height", args.h, getInitParam("cookie_lifetime"));
- }
- });
-
- delCookie("ttrss_test");
-
- const toolbar = document.forms["main_toolbar_form"];
-
- dijit.getEnclosingWidget(toolbar.view_mode).attr('value',
- getInitParam("default_view_mode"));
-
- dijit.getEnclosingWidget(toolbar.order_by).attr('value',
- getInitParam("default_view_order_by"));
-
- const hash_feed_id = hash_get('f');
- const hash_feed_is_cat = hash_get('c') == "1";
-
- if (hash_feed_id != undefined) {
- setActiveFeedId(hash_feed_id, hash_feed_is_cat);
- }
-
- setLoadingProgress(50);
-
- // can't use cache_clear() here because viewfeed might not have initialized yet
- if ('sessionStorage' in window && window['sessionStorage'] !== null)
- sessionStorage.clear();
-
- _widescreen_mode = getInitParam("widescreen");
- switchPanelMode(_widescreen_mode);
-
- $("headlines-frame").onscroll = (event) => {
- clearTimeout(_headlines_scroll_timeout);
- _headlines_scroll_timeout = window.setTimeout(function() {
- //console.log('done scrolling', event);
- headlinesScrollHandler(event);
- }, 50);
- }
-
- console.log("second stage ok");
-
- if (getInitParam("simple_update")) {
- console.log("scheduling simple feed updater...");
- window.setTimeout(update_random_feed, 30*1000);
- }
-}
-
-function quickMenuGo(opid) {
- switch (opid) {
- case "qmcPrefs":
- gotoPreferences();
- break;
- case "qmcLogout":
- document.location.href = "backend.php?op=logout";
- break;
- case "qmcTagCloud":
- displayDlg(__("Tag cloud"), "printTagCloud");
- break;
- case "qmcSearch":
- search();
- break;
- case "qmcAddFeed":
- quickAddFeed();
- break;
- case "qmcDigest":
- window.location.href = "backend.php?op=digest";
- break;
- case "qmcEditFeed":
- if (activeFeedIsCat())
- alert(__("You can't edit this kind of feed."));
- else
- editFeed(getActiveFeedId());
- break;
- case "qmcRemoveFeed":
- var actid = getActiveFeedId();
-
- if (activeFeedIsCat()) {
- alert(__("You can't unsubscribe from the category."));
- return;
- }
-
- if (!actid) {
- alert(__("Please select some feed first."));
- return;
- }
-
- var fn = getFeedName(actid);
-
- var pr = __("Unsubscribe from %s?").replace("%s", fn);
-
- if (confirm(pr)) {
- unsubscribeFeed(actid);
- }
- break;
- case "qmcCatchupAll":
- catchupAllFeeds();
- break;
- case "qmcShowOnlyUnread":
- toggleDispRead();
- break;
- case "qmcToggleWidescreen":
- if (!isCombinedMode()) {
- _widescreen_mode = !_widescreen_mode;
-
- // reset stored sizes because geometry changed
- setCookie("ttrss_ci_width", 0);
- setCookie("ttrss_ci_height", 0);
-
- switchPanelMode(_widescreen_mode);
- } else {
- alert(__("Widescreen is not available in combined mode."));
- }
- break;
- case "qmcHKhelp":
- helpDialog("main");
- break;
- default:
- console.log("quickMenuGo: unknown action: " + opid);
- }
-}
-
-function toggleDispRead() {
-
- const hide = !(getInitParam("hide_read_feeds") == "1");
-
- xhrPost("backend.php", {op: "rpc", method: "setpref", key: "HIDE_READ_FEEDS", value: hide}, () => {
- hideOrShowFeeds(hide);
- setInitParam("hide_read_feeds", hide);
- });
-}
-
-function parse_runtime_info(data) {
-
- //console.log("parsing runtime info...");
-
- for (const k in data) {
- const v = data[k];
-
-// console.log("RI: " + k + " => " + v);
-
- if (k == "dep_ts" && parseInt(getInitParam("dep_ts")) > 0) {
- if (parseInt(getInitParam("dep_ts")) < parseInt(v) && getInitParam("reload_on_ts_change")) {
- window.location.reload();
- }
- }
+ Feeds.reload();
+ Article.close();
- if (k == "daemon_is_running" && v != 1) {
- notify_error("<span onclick=\"explainError(1)\">Update daemon is not running.</span>", true);
- return;
- }
+ if (parseInt(Cookie.get("ttrss_fh_width")) > 0) {
+ dijit.byId("feeds-holder").domNode.setStyle(
+ {width: Cookie.get("ttrss_fh_width") + "px"});
+ }
- if (k == "update_result") {
- const updatesIcon = dijit.byId("updatesIcon").domNode;
+ dijit.byId("main").resize();
- if (v) {
- Element.show(updatesIcon);
- } else {
- Element.hide(updatesIcon);
- }
- }
+ dojo.connect(dijit.byId('feeds-holder'), 'resize',
+ function (args) {
+ if (args && args.w >= 0) {
+ Cookie.set("ttrss_fh_width", args.w, App.getInitParam("cookie_lifetime"));
+ }
+ });
- if (k == "daemon_stamp_ok" && v != 1) {
- notify_error("<span onclick=\"explainError(3)\">Update daemon is not updating feeds.</span>", true);
- return;
- }
+ dojo.connect(dijit.byId('content-insert'), 'resize',
+ function (args) {
+ if (args && args.w >= 0 && args.h >= 0) {
+ Cookie.set("ttrss_ci_width", args.w, App.getInitParam("cookie_lifetime"));
+ Cookie.set("ttrss_ci_height", args.h, App.getInitParam("cookie_lifetime"));
+ }
+ });
- if (k == "max_feed_id" || k == "num_feeds") {
- if (init_params[k] != v) {
- console.log("feed count changed, need to reload feedlist.");
- updateFeedList();
- }
- }
+ Cookie.delete("ttrss_test");
- init_params[k] = v;
- notify('');
- }
+ const toolbar = document.forms["main_toolbar_form"];
- PluginHost.run(PluginHost.HOOK_RUNTIME_INFO_LOADED, data);
-}
+ dijit.getEnclosingWidget(toolbar.view_mode).attr('value',
+ App.getInitParam("default_view_mode"));
-function collapse_feedlist() {
- Element.toggle("feeds-holder");
+ dijit.getEnclosingWidget(toolbar.order_by).attr('value',
+ App.getInitParam("default_view_order_by"));
- const splitter = $("feeds-holder_splitter");
+ const hash_feed_id = hash_get('f');
+ const hash_feed_is_cat = hash_get('c') == "1";
- Element.visible("feeds-holder") ? splitter.show() : splitter.hide();
+ if (hash_feed_id != undefined) {
+ Feeds.setActive(hash_feed_id, hash_feed_is_cat);
+ }
- dijit.byId("main").resize();
-}
+ App.setLoadingProgress(50);
-function viewModeChanged() {
- cache_clear();
- return viewCurrentFeed('');
-}
-
-function hotkey_handler(e) {
- if (e.target.nodeName == "INPUT" || e.target.nodeName == "TEXTAREA") return;
-
- const action_name = keyeventToAction(e);
+ ArticleCache.clear();
- if (action_name) {
- const action_func = hotkey_actions[action_name];
+ this._widescreen_mode = App.getInitParam("widescreen");
+ this.switchPanelMode(this._widescreen_mode);
- if (action_func != null) {
- action_func();
- e.stopPropagation();
- return false;
- }
- }
-}
+ Headlines.initScrollHandler();
-function inPreferences() {
- return false;
-}
+ if (App.getInitParam("simple_update")) {
+ console.log("scheduling simple feed updater...");
+ window.setInterval(() => { Feeds.updateRandom() }, 30 * 1000);
+ }
-function reverseHeadlineOrder() {
+ console.log("second stage ok");
+ },
+ updateTitle: function() {
+ let tmp = "Tiny Tiny RSS";
- const toolbar = document.forms["main_toolbar_form"];
- const order_by = dijit.getEnclosingWidget(toolbar.order_by);
+ if (this.global_unread > 0) {
+ tmp = "(" + this.global_unread + ") " + tmp;
+ }
- let value = order_by.attr('value');
+ document.title = tmp;
+ },
+ onViewModeChanged: function() {
+ ArticleCache.clear();
+ return Feeds.reloadCurrent('');
+ },
+ isCombinedMode: function() {
+ return App.getInitParam("combined_display_mode");
+ },
+ hotkeyHandler(event) {
+ if (event.target.nodeName == "INPUT" || event.target.nodeName == "TEXTAREA") return;
- if (value == "date_reverse")
- value = "default";
- else
- value = "date_reverse";
+ const action_name = App.keyeventToAction(event);
- order_by.attr('value', value);
+ if (action_name) {
+ const action_func = this.hotkey_actions[action_name];
- viewCurrentFeed();
+ if (action_func != null) {
+ action_func();
+ event.stopPropagation();
+ return false;
+ }
+ }
+ },
+ switchPanelMode: function(wide) {
+ if (App.isCombinedMode()) return;
-}
+ const article_id = Article.getActive();
-function handle_rpc_json(transport, scheduled_call) {
+ if (wide) {
+ dijit.byId("headlines-wrap-inner").attr("design", 'sidebar');
+ dijit.byId("content-insert").attr("region", "trailing");
- const netalert_dijit = dijit.byId("net-alert");
- let netalert = false;
+ dijit.byId("content-insert").domNode.setStyle({width: '50%',
+ height: 'auto',
+ borderTopWidth: '0px' });
- if (netalert_dijit) netalert = netalert_dijit.domNode;
+ if (parseInt(Cookie.get("ttrss_ci_width")) > 0) {
+ dijit.byId("content-insert").domNode.setStyle(
+ {width: Cookie.get("ttrss_ci_width") + "px" });
+ }
- try {
- const reply = JSON.parse(transport.responseText);
+ $("headlines-frame").setStyle({ borderBottomWidth: '0px' });
+ $("headlines-frame").addClassName("wide");
- if (reply) {
+ } else {
- const error = reply['error'];
+ dijit.byId("content-insert").attr("region", "bottom");
- if (error) {
- const code = error['code'];
- const msg = error['msg'];
+ dijit.byId("content-insert").domNode.setStyle({width: 'auto',
+ height: '50%',
+ borderTopWidth: '0px'});
- console.warn("[handle_rpc_json] received fatal error " + code + "/" + msg);
+ if (parseInt(Cookie.get("ttrss_ci_height")) > 0) {
+ dijit.byId("content-insert").domNode.setStyle(
+ {height: Cookie.get("ttrss_ci_height") + "px" });
+ }
- if (code != 0) {
- fatalError(code, msg);
+ $("headlines-frame").setStyle({ borderBottomWidth: '1px' });
+ $("headlines-frame").removeClassName("wide");
+
+ }
+
+ Article.close();
+
+ if (article_id) Article.view(article_id);
+
+ xhrPost("backend.php", {op: "rpc", method: "setpanelmode", wide: wide ? 1 : 0});
+ },
+ initHotkeyActions: function() {
+ this.hotkey_actions["next_feed"] = function () {
+ const rv = dijit.byId("feedTree").getNextFeed(
+ Feeds.getActive(), Feeds.activeIsCat());
+
+ if (rv) Feeds.open({feed: rv[0], is_cat: rv[1], delayed: true})
+ };
+ this.hotkey_actions["prev_feed"] = function () {
+ const rv = dijit.byId("feedTree").getPreviousFeed(
+ Feeds.getActive(), Feeds.activeIsCat());
+
+ if (rv) Feeds.open({feed: rv[0], is_cat: rv[1], delayed: true})
+ };
+ this.hotkey_actions["next_article"] = function () {
+ Headlines.move('next');
+ };
+ this.hotkey_actions["prev_article"] = function () {
+ Headlines.move('prev');
+ };
+ this.hotkey_actions["next_article_noscroll"] = function () {
+ Headlines.move('next', true);
+ };
+ this.hotkey_actions["prev_article_noscroll"] = function () {
+ Headlines.move('prev', true);
+ };
+ this.hotkey_actions["next_article_noexpand"] = function () {
+ Headlines.move('next', true, true);
+ };
+ this.hotkey_actions["prev_article_noexpand"] = function () {
+ Headlines.move('prev', true, true);
+ };
+ this.hotkey_actions["search_dialog"] = function () {
+ Feeds.search();
+ };
+ this.hotkey_actions["toggle_mark"] = function () {
+ Headlines.selectionToggleMarked();
+ };
+ this.hotkey_actions["toggle_publ"] = function () {
+ Headlines.selectionTogglePublished();
+ };
+ this.hotkey_actions["toggle_unread"] = function () {
+ Headlines.selectionToggleUnread({no_error: 1});
+ };
+ this.hotkey_actions["edit_tags"] = function () {
+ const id = Article.getActive();
+ if (id) {
+ Article.editTags(id);
+ }
+ };
+ this.hotkey_actions["open_in_new_window"] = function () {
+ if (Article.getActive()) {
+ Article.openInNewWindow(Article.getActive());
+ }
+ };
+ this.hotkey_actions["catchup_below"] = function () {
+ Headlines.catchupRelativeTo(1);
+ };
+ this.hotkey_actions["catchup_above"] = function () {
+ Headlines.catchupRelativeTo(0);
+ };
+ this.hotkey_actions["article_scroll_down"] = function () {
+ Article.scroll(40);
+ };
+ this.hotkey_actions["article_scroll_up"] = function () {
+ Article.scroll(-40);
+ };
+ this.hotkey_actions["close_article"] = function () {
+ if (App.isCombinedMode()) {
+ Article.cdmUnsetActive();
+ } else {
+ Article.close();
+ }
+ };
+ this.hotkey_actions["email_article"] = function () {
+ if (typeof Plugins.Mail != "undefined") {
+ Plugins.Mail.onHotkey(Headlines.getSelected());
+ } else {
+ alert(__("Please enable mail or mailto plugin first."));
+ }
+ };
+ this.hotkey_actions["select_all"] = function () {
+ Headlines.select('all');
+ };
+ this.hotkey_actions["select_unread"] = function () {
+ Headlines.select('unread');
+ };
+ this.hotkey_actions["select_marked"] = function () {
+ Headlines.select('marked');
+ };
+ this.hotkey_actions["select_published"] = function () {
+ Headlines.select('published');
+ };
+ this.hotkey_actions["select_invert"] = function () {
+ Headlines.select('invert');
+ };
+ this.hotkey_actions["select_none"] = function () {
+ Headlines.select('none');
+ };
+ this.hotkey_actions["feed_refresh"] = function () {
+ if (Feeds.getActive() != undefined) {
+ Feeds.open({feed: Feeds.getActive(), is_cat: Feeds.activeIsCat()});
+ }
+ };
+ this.hotkey_actions["feed_unhide_read"] = function () {
+ Feeds.toggleUnread();
+ };
+ this.hotkey_actions["feed_subscribe"] = function () {
+ CommonDialogs.quickAddFeed();
+ };
+ this.hotkey_actions["feed_debug_update"] = function () {
+ if (!Feeds.activeIsCat() && parseInt(Feeds.getActive()) > 0) {
+ window.open("backend.php?op=feeds&method=update_debugger&feed_id=" + Feeds.getActive() +
+ "&csrf_token=" + App.getInitParam("csrf_token"));
+ } else {
+ alert("You can't debug this kind of feed.");
+ }
+ };
+
+ this.hotkey_actions["feed_debug_viewfeed"] = function () {
+ Feeds.open({feed: Feeds.getActive(), is_cat: Feeds.activeIsCat(), viewfeed_debug: true});
+ };
+
+ this.hotkey_actions["feed_edit"] = function () {
+ if (Feeds.activeIsCat())
+ alert(__("You can't edit this kind of feed."));
+ else
+ CommonDialogs.editFeed(Feeds.getActive());
+ };
+ this.hotkey_actions["feed_catchup"] = function () {
+ if (Feeds.getActive() != undefined) {
+ Feeds.catchupCurrent();
+ }
+ };
+ this.hotkey_actions["feed_reverse"] = function () {
+ Headlines.reverse();
+ };
+ this.hotkey_actions["feed_toggle_vgroup"] = function () {
+ xhrPost("backend.php", {op: "rpc", method: "togglepref", key: "VFEED_GROUP_BY_FEED"}, () => {
+ Feeds.reloadCurrent();
+ })
+ };
+ this.hotkey_actions["catchup_all"] = function () {
+ Feeds.catchupAll();
+ };
+ this.hotkey_actions["cat_toggle_collapse"] = function () {
+ if (Feeds.activeIsCat()) {
+ dijit.byId("feedTree").collapseCat(Feeds.getActive());
+ }
+ };
+ this.hotkey_actions["goto_all"] = function () {
+ Feeds.open({feed: -4});
+ };
+ this.hotkey_actions["goto_fresh"] = function () {
+ Feeds.open({feed: -3});
+ };
+ this.hotkey_actions["goto_marked"] = function () {
+ Feeds.open({feed: -1});
+ };
+ this.hotkey_actions["goto_published"] = function () {
+ Feeds.open({feed: -2});
+ };
+ this.hotkey_actions["goto_tagcloud"] = function () {
+ App.displayDlg(__("Tag cloud"), "printTagCloud");
+ };
+ this.hotkey_actions["goto_prefs"] = function () {
+ document.location.href = "prefs.php";
+ };
+ this.hotkey_actions["select_article_cursor"] = function () {
+ const id = Article.getUnderPointer();
+ if (id) {
+ const row = $("RROW-" + id);
+
+ if (row) {
+ const cb = dijit.getEnclosingWidget(
+ row.select(".rchk")[0]);
+
+ if (cb) {
+ if (!row.hasClassName("active"))
+ cb.attr("checked", !cb.attr("checked"));
+
+ Headlines.onRowChecked(cb);
+ return false;
+ }
+ }
+ }
+ };
+ this.hotkey_actions["create_label"] = function () {
+ CommonDialogs.addLabel();
+ };
+ this.hotkey_actions["create_filter"] = function () {
+ Filters.quickAddFilter();
+ };
+ this.hotkey_actions["collapse_sidebar"] = function () {
+ Feeds.reloadCurrent();
+ };
+ this.hotkey_actions["toggle_embed_original"] = function () {
+ if (typeof embedOriginalArticle != "undefined") {
+ if (Article.getActive())
+ embedOriginalArticle(Article.getActive());
+ } else {
+ alert(__("Please enable embed_original plugin first."));
+ }
+ };
+ this.hotkey_actions["toggle_widescreen"] = function () {
+ if (!App.isCombinedMode()) {
+ App._widescreen_mode = !App._widescreen_mode;
+
+ // reset stored sizes because geometry changed
+ Cookie.set("ttrss_ci_width", 0);
+ Cookie.set("ttrss_ci_height", 0);
+
+ App.switchPanelMode(App._widescreen_mode);
+ } else {
+ alert(__("Widescreen is not available in combined mode."));
+ }
+ };
+ this.hotkey_actions["help_dialog"] = function () {
+ App.helpDialog("main");
+ };
+ this.hotkey_actions["toggle_combined_mode"] = function () {
+ Notify.progress("Loading, please wait...");
+
+ const value = App.isCombinedMode() ? "false" : "true";
+
+ xhrPost("backend.php", {op: "rpc", method: "setpref", key: "COMBINED_DISPLAY_MODE", value: value}, () => {
+ App.setInitParam("combined_display_mode",
+ !App.getInitParam("combined_display_mode"));
+
+ Article.close();
+ Feeds.reloadCurrent();
+ })
+ };
+ this.hotkey_actions["toggle_cdm_expanded"] = function () {
+ Notify.progress("Loading, please wait...");
+
+ const value = App.getInitParam("cdm_expanded") ? "false" : "true";
+
+ xhrPost("backend.php", {op: "rpc", method: "setpref", key: "CDM_EXPANDED", value: value}, () => {
+ App.setInitParam("cdm_expanded", !App.getInitParam("cdm_expanded"));
+ Feeds.reloadCurrent();
+ });
+ };
+ },
+ onActionSelected: function(opid) {
+ switch (opid) {
+ case "qmcPrefs":
+ document.location.href = "prefs.php";
+ break;
+ case "qmcLogout":
+ document.location.href = "backend.php?op=logout";
+ break;
+ case "qmcTagCloud":
+ App.displayDlg(__("Tag cloud"), "printTagCloud");
+ break;
+ case "qmcSearch":
+ Feeds.search();
+ break;
+ case "qmcAddFeed":
+ CommonDialogs.quickAddFeed();
+ break;
+ case "qmcDigest":
+ window.location.href = "backend.php?op=digest";
+ break;
+ case "qmcEditFeed":
+ if (Feeds.activeIsCat())
+ alert(__("You can't edit this kind of feed."));
+ else
+ CommonDialogs.editFeed(Feeds.getActive());
+ break;
+ case "qmcRemoveFeed":
+ const actid = Feeds.getActive();
+
+ if (!actid) {
+ alert(__("Please select some feed first."));
+ return;
+ }
+
+ if (Feeds.activeIsCat()) {
+ alert(__("You can't unsubscribe from the category."));
+ return;
+ }
+
+ const fn = Feeds.getName(actid);
+
+ if (confirm(__("Unsubscribe from %s?").replace("%s", fn))) {
+ CommonDialogs.unsubscribeFeed(actid);
+ }
+ break;
+ case "qmcCatchupAll":
+ Feeds.catchupAll();
+ break;
+ case "qmcShowOnlyUnread":
+ Feeds.toggleUnread();
+ break;
+ case "qmcToggleWidescreen":
+ if (!App.isCombinedMode()) {
+ App._widescreen_mode = !App._widescreen_mode;
+
+ // reset stored sizes because geometry changed
+ Cookie.set("ttrss_ci_width", 0);
+ Cookie.set("ttrss_ci_height", 0);
+
+ App.switchPanelMode(App._widescreen_mode);
+ } else {
+ alert(__("Widescreen is not available in combined mode."));
+ }
+ break;
+ case "qmcHKhelp":
+ App.helpDialog("main");
+ break;
+ default:
+ console.log("quickMenuGo: unknown action: " + opid);
+ }
+ },
+ isPrefs: function() {
return false;
}
- }
-
- const seq = reply['seq'];
-
- if (seq && get_seq() != seq) {
- console.log("[handle_rpc_json] sequence mismatch: " + seq +
- " (want: " + get_seq() + ")");
- return true;
- }
-
- const message = reply['message'];
-
- if (message == "UPDATE_COUNTERS") {
- console.log("need to refresh counters...");
- setInitParam("last_article_id", -1);
- request_counters(true);
- }
-
- const counters = reply['counters'];
-
- if (counters)
- parse_counters(counters, scheduled_call);
-
- const runtime_info = reply['runtime-info'];
-
- if (runtime_info)
- parse_runtime_info(runtime_info);
-
- if (netalert) netalert.hide();
-
- return reply;
-
- } else {
- if (netalert)
- netalert.show();
- else
- notify_error("Communication problem with server.");
- }
-
- } catch (e) {
- if (netalert)
- netalert.show();
- else
- notify_error("Communication problem with server.");
-
- console.error(e);
- }
-
- return false;
-}
-
-function switchPanelMode(wide) {
- if (isCombinedMode()) return;
-
- const article_id = getActiveArticleId();
-
- if (wide) {
- dijit.byId("headlines-wrap-inner").attr("design", 'sidebar');
- dijit.byId("content-insert").attr("region", "trailing");
-
- dijit.byId("content-insert").domNode.setStyle({width: '50%',
- height: 'auto',
- borderTopWidth: '0px' });
-
- if (parseInt(getCookie("ttrss_ci_width")) > 0) {
- dijit.byId("content-insert").domNode.setStyle(
- {width: getCookie("ttrss_ci_width") + "px" });
- }
-
- $("headlines-frame").setStyle({ borderBottomWidth: '0px' });
- $("headlines-frame").addClassName("wide");
-
- } else {
-
- dijit.byId("content-insert").attr("region", "bottom");
-
- dijit.byId("content-insert").domNode.setStyle({width: 'auto',
- height: '50%',
- borderTopWidth: '0px'});
+ });
- if (parseInt(getCookie("ttrss_ci_height")) > 0) {
- dijit.byId("content-insert").domNode.setStyle(
- {height: getCookie("ttrss_ci_height") + "px" });
+ App = new _App();
+ } catch (e) {
+ App.Error.report(e);
}
-
- $("headlines-frame").setStyle({ borderBottomWidth: '1px' });
- $("headlines-frame").removeClassName("wide");
-
- }
-
- closeArticlePanel();
-
- if (article_id) view(article_id);
-
- xhrPost("backend.php", {op: "rpc", method: "setpanelmode", wide: wide ? 1 : 0});
-}
-
-function update_random_feed() {
- console.log("in update_random_feed");
-
- xhrPost("backend.php", { op: "rpc", method: "updateRandomFeed" }, (transport) => {
- handle_rpc_json(transport, true);
- window.setTimeout(update_random_feed, 30*1000);
});
-}
+});
function hash_get(key) {
const kv = window.location.hash.substring(1).toQueryParams();
@@ -913,7 +564,3 @@ function hash_set(key, value) {
kv[key] = value;
window.location.hash = $H(kv).toQueryString();
}
-
-function gotoPreferences() {
- document.location.href = "prefs.php";
-}
diff --git a/js/viewfeed.js b/js/viewfeed.js
deleted file mode 100755
index 73d135874..000000000
--- a/js/viewfeed.js
+++ /dev/null
@@ -1,1656 +0,0 @@
-/* global dijit, __, ngettext */
-
-let _active_article_id = 0;
-
-let vgroup_last_feed = false;
-let post_under_pointer = false;
-
-let catchup_id_batch = [];
-//let catchup_timeout_id = false;
-
-//let cids_requested = [];
-let loaded_article_ids = [];
-let current_first_id = 0;
-let last_search_query;
-
-let has_storage = 'sessionStorage' in window && window['sessionStorage'] !== null;
-
-function headlines_callback2(transport, offset) {
- const reply = handle_rpc_json(transport);
-
- console.log("headlines_callback2, offset=", offset);
-
- let is_cat = false;
- let feed_id = false;
-
- if (reply) {
-
- is_cat = reply['headlines']['is_cat'];
- feed_id = reply['headlines']['id'];
- last_search_query = reply['headlines']['search_query'];
-
- if (feed_id != -7 && (feed_id != getActiveFeedId() || is_cat != activeFeedIsCat()))
- return;
-
- try {
- if (offset == 0) {
- $("headlines-frame").scrollTop = 0;
-
- Element.hide("floatingTitle");
- $("floatingTitle").setAttribute("data-article-id", 0);
- $("floatingTitle").innerHTML = "";
- }
- } catch (e) { }
-
- $("headlines-frame").removeClassName("cdm");
- $("headlines-frame").removeClassName("normal");
-
- $("headlines-frame").addClassName(isCombinedMode() ? "cdm" : "normal");
-
- const headlines_count = reply['headlines-info']['count'];
- infscroll_disabled = parseInt(headlines_count) != 30;
-
- console.log('received', headlines_count, 'headlines, infscroll disabled=', infscroll_disabled);
-
- vgroup_last_feed = reply['headlines-info']['vgroup_last_feed'];
- current_first_id = reply['headlines']['first_id'];
-
- if (offset == 0) {
- loaded_article_ids = [];
-
- dojo.html.set($("headlines-toolbar"),
- reply['headlines']['toolbar'],
- {parseContent: true});
-
- $("headlines-frame").innerHTML = '';
-
- let tmp = document.createElement("div");
- tmp.innerHTML = reply['headlines']['content'];
- dojo.parser.parse(tmp);
-
- while (tmp.hasChildNodes()) {
- const row = tmp.removeChild(tmp.firstChild);
-
- if (loaded_article_ids.indexOf(row.id) == -1 || row.hasClassName("feed-title")) {
- dijit.byId("headlines-frame").domNode.appendChild(row);
-
- loaded_article_ids.push(row.id);
- }
- }
-
- let hsp = $("headlines-spacer");
- if (!hsp) hsp = new Element("DIV", {"id": "headlines-spacer"});
- dijit.byId('headlines-frame').domNode.appendChild(hsp);
-
- initHeadlinesMenu();
-
- if (infscroll_disabled)
- hsp.innerHTML = "<a href='#' onclick='openNextUnreadFeed()'>" +
- __("Click to open next unread feed.") + "</a>";
-
- if (_search_query) {
- $("feed_title").innerHTML += "<span id='cancel_search'>" +
- " (<a href='#' onclick='cancelSearch()'>" + __("Cancel search") + "</a>)" +
- "</span>";
- }
-
- } else if (headlines_count > 0 && feed_id == getActiveFeedId() && is_cat == activeFeedIsCat()) {
- const c = dijit.byId("headlines-frame");
- //const ids = getSelectedArticleIds2();
-
- let hsp = $("headlines-spacer");
-
- if (hsp)
- c.domNode.removeChild(hsp);
-
- let tmp = document.createElement("div");
- tmp.innerHTML = reply['headlines']['content'];
- dojo.parser.parse(tmp);
-
- while (tmp.hasChildNodes()) {
- let row = tmp.removeChild(tmp.firstChild);
-
- if (loaded_article_ids.indexOf(row.id) == -1 || row.hasClassName("feed-title")) {
- dijit.byId("headlines-frame").domNode.appendChild(row);
-
- loaded_article_ids.push(row.id);
- }
- }
-
- if (!hsp) hsp = new Element("DIV", {"id": "headlines-spacer"});
- c.domNode.appendChild(hsp);
-
- if (headlines_count < 30) infscroll_disabled = true;
-
- /* console.log("restore selected ids: " + ids);
-
- for (let i = 0; i < ids.length; i++) {
- markHeadline(ids[i]);
- } */
-
- initHeadlinesMenu();
-
- if (infscroll_disabled) {
- hsp.innerHTML = "<a href='#' onclick='openNextUnreadFeed()'>" +
- __("Click to open next unread feed.") + "</a>";
- }
-
- } else {
- console.log("no new headlines received");
-
- const first_id_changed = reply['headlines']['first_id_changed'];
- console.log("first id changed:" + first_id_changed);
-
- let hsp = $("headlines-spacer");
-
- if (hsp) {
- if (first_id_changed) {
- hsp.innerHTML = "<a href='#' onclick='viewCurrentFeed()'>" +
- __("New articles found, reload feed to continue.") + "</a>";
- } else {
- hsp.innerHTML = "<a href='#' onclick='openNextUnreadFeed()'>" +
- __("Click to open next unread feed.") + "</a>";
- }
- }
- }
-
- } else {
- console.error("Invalid object received: " + transport.responseText);
- dijit.byId("headlines-frame").attr('content', "<div class='whiteBox'>" +
- __('Could not update headlines (invalid object received - see error console for details)') +
- "</div>");
- }
-
- infscroll_in_progress = 0;
-
- // this is used to auto-catchup articles if needed after infscroll request has finished,
- // unpack visible articles, etc
- headlinesScrollHandler();
-
- // if we have some more space in the buffer, why not try to fill it
- if (!infscroll_disabled && $("headlines-spacer") &&
- $("headlines-spacer").offsetTop < $("headlines-frame").offsetHeight) {
-
- window.setTimeout(function() {
- loadMoreHeadlines();
- }, 500);
- }
-
- notify("");
-}
-
-function render_article(article) {
- cleanup_memory("content-insert");
-
- dijit.byId("headlines-wrap-inner").addChild(
- dijit.byId("content-insert"));
-
- const c = dijit.byId("content-insert");
-
- try {
- c.domNode.scrollTop = 0;
- } catch (e) { }
-
- c.attr('content', article);
- PluginHost.run(PluginHost.HOOK_ARTICLE_RENDERED, c.domNode);
-
- correctHeadlinesOffset(getActiveArticleId());
-
- try {
- c.focus();
- } catch (e) { }
-}
-
-function view(id, noexpand) {
- setActiveArticleId(id);
-
- if (!noexpand) {
- console.log("loading article", id);
-
- const cids = [];
-
- /* only request uncached articles */
-
- getRelativePostIds(id).each((n) => {
- if (!cache_get("article:" + n))
- cids.push(n);
- });
-
- const cached_article = cache_get("article:" + id);
-
- if (cached_article) {
- console.log('rendering cached', id);
- render_article(cached_article);
- return false;
- }
-
- xhrPost("backend.php", {op: "article", method: "view", id: id, cids: cids.toString()}, (transport) => {
- try {
- const reply = handle_rpc_json(transport);
-
- if (reply) {
-
- reply.each(function(article) {
- if (getActiveArticleId() == article['id']) {
- render_article(article['content']);
- }
- //cids_requested.remove(article['id']);
-
- cache_set("article:" + article['id'], article['content']);
- });
-
- } else {
- console.error("Invalid object received: " + transport.responseText);
-
- render_article("<div class='whiteBox'>" +
- __('Could not display article (invalid object received - see error console for details)') + "</div>");
- }
-
- //const unread_in_buffer = $$("#headlines-frame > div[id*=RROW][class*=Unread]").length;
- //request_counters(unread_in_buffer == 0);
-
- notify("");
-
- } catch (e) {
- exception_error(e);
- }
- })
- }
-
- return false;
-}
-
-function toggleMark(id, client_only) {
- const query = { op: "rpc", id: id, method: "mark" };
- const row = $("RROW-" + id);
-
- if (row) {
- const imgs = $$("img[class*=marked-pic][class*=marked-" + id + "]");
-
- imgs.each((img) => {
- if (!row.hasClassName("marked")) {
- img.src = img.src.replace("mark_unset", "mark_set");
- query.mark = 1;
- } else {
- img.src = img.src.replace("mark_set", "mark_unset");
- query.mark = 0;
- }
- });
-
- row.toggleClassName("marked");
-
- if (!client_only)
- xhrPost("backend.php", query, (transport) => {
- handle_rpc_json(transport);
- });
- }
-}
-
-function togglePub(id, client_only) {
- const row = $("RROW-" + id);
-
- if (row) {
- const query = { op: "rpc", id: id, method: "publ" };
-
- const imgs = $$("img[class*=pub-pic][class*=pub-" + id + "]");
-
- imgs.each((img) => {
- if (!row.hasClassName("published")) {
- img.src = img.src.replace("pub_unset", "pub_set");
- query.pub = 1;
- } else {
- img.src = img.src.replace("pub_set", "pub_unset");
- query.pub = 0;
- }
- });
-
- row.toggleClassName("published");
-
- if (!client_only)
- xhrPost("backend.php", query, (transport) => {
- handle_rpc_json(transport);
- });
-
- }
-}
-
-function moveToPost(mode, noscroll, noexpand) {
- const rows = getLoadedArticleIds();
-
- let prev_id = false;
- let next_id = false;
-
- if (!$('RROW-' + getActiveArticleId())) {
- setActiveArticleId(0);
- }
-
- if (!getActiveArticleId()) {
- next_id = rows[0];
- prev_id = rows[rows.length-1]
- } else {
- for (let i = 0; i < rows.length; i++) {
- if (rows[i] == getActiveArticleId()) {
-
- // 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] != getActiveArticleId()) {
- next_id = rows[j];
- break;
- }
- }
- break;
- }
- }
- }
-
- console.log("cur: " + getActiveArticleId() + " next: " + next_id);
-
- if (mode == "next") {
- if (next_id || getActiveArticleId()) {
- if (isCombinedMode()) {
-
- const article = $("RROW-" + getActiveArticleId());
- const ctr = $("headlines-frame");
-
- if (!noscroll && article && article.offsetTop + article.offsetHeight >
- ctr.scrollTop + ctr.offsetHeight) {
-
- scrollArticle(ctr.offsetHeight/4);
-
- } else if (next_id) {
- setActiveArticleId(next_id);
- cdmScrollToArticleId(next_id, true);
- }
-
- } else if (next_id) {
- correctHeadlinesOffset(next_id);
- view(next_id, noexpand);
- }
- }
- }
-
- if (mode == "prev") {
- if (prev_id || getActiveArticleId()) {
- if (isCombinedMode()) {
-
- const article = $("RROW-" + getActiveArticleId());
- const prev_article = $("RROW-" + prev_id);
- const ctr = $("headlines-frame");
-
- if (!noscroll && article && article.offsetTop < ctr.scrollTop) {
- scrollArticle(-ctr.offsetHeight/3);
- } else if (!noscroll && prev_article &&
- prev_article.offsetTop < ctr.scrollTop) {
- scrollArticle(-ctr.offsetHeight/4);
- } else if (prev_id) {
- setActiveArticleId(prev_id);
- cdmScrollToArticleId(prev_id, noscroll);
- }
-
- } else if (prev_id) {
- correctHeadlinesOffset(prev_id);
- view(prev_id, noexpand);
- }
- }
- }
-}
-
-function updateSelectedPrompt() {
- const count = getSelectedArticleIds2().length;
- const elem = $("selected_prompt");
-
- if (elem) {
- elem.innerHTML = ngettext("%d article selected",
- "%d articles selected", count).replace("%d", count);
-
- count > 0 ? Element.show(elem) : Element.hide(elem);
- }
-}
-
-function toggleUnread(id, cmode) {
- const row = $("RROW-" + id);
-
- if (row) {
- const origClassName = row.className;
-
- if (cmode == undefined) cmode = 2;
-
- switch (cmode) {
- case 0:
- row.removeClassName("Unread");
- break;
- case 1:
- row.addClassName("Unread");
- break;
- case 2:
- row.toggleClassName("Unread");
- break;
- }
-
- if (row.className != origClassName)
- xhrPost("backend.php",
- {op: "rpc", method: "catchupSelected", cmode: cmode, ids: id},(transport) => {
- handle_rpc_json(transport);
- });
- }
-}
-
-function selectionRemoveLabel(id, ids) {
- if (!ids) ids = getSelectedArticleIds2();
-
- if (ids.length == 0) {
- alert(__("No articles are selected."));
- return;
- }
-
- const query = { op: "article", method: "removeFromLabel",
- ids: ids.toString(), lid: id };
-
- xhrPost("backend.php", query, (transport) => {
- handle_rpc_json(transport);
- updateHeadlineLabels(transport);
- });
-}
-
-function selectionAssignLabel(id, ids) {
- if (!ids) ids = getSelectedArticleIds2();
-
- if (ids.length == 0) {
- alert(__("No articles are selected."));
- return;
- }
-
- const query = { op: "article", method: "assignToLabel",
- ids: ids.toString(), lid: id };
-
- xhrPost("backend.php", query, (transport) => {
- handle_rpc_json(transport);
- updateHeadlineLabels(transport);
- });
-}
-
-function selectionToggleUnread(params) {
- params = params || {};
-
- const cmode = params.cmode || 2;
- const callback = params.callback;
- const no_error = params.no_error || false;
- const ids = params.ids || getSelectedArticleIds2();
-
- if (ids.length == 0) {
- if (!no_error)
- alert(__("No articles are selected."));
-
- return;
- }
-
- ids.each((id) => {
- const row = $("RROW-" + id);
-
- if (row) {
- switch (cmode) {
- case 0:
- row.removeClassName("Unread");
- break;
- case 1:
- row.addClassName("Unread");
- break;
- case 2:
- row.toggleClassName("Unread");
- }
- }
- });
-
- const query = {op: "rpc", method: "catchupSelected",
- cmode: cmode, ids: ids.toString() };
-
- notify_progress("Loading, please wait...");
-
- xhrPost("backend.php", query, (transport) => {
- handle_rpc_json(transport);
- if (callback) callback(transport);
- });
-}
-
-function selectionToggleMarked(ids) {
- const rows = ids || getSelectedArticleIds2();
-
- if (rows.length == 0) {
- alert(__("No articles are selected."));
- return;
- }
-
- for (let i = 0; i < rows.length; i++) {
- toggleMark(rows[i], true, true);
- }
-
- const query = { op: "rpc", method: "markSelected",
- ids: rows.toString(), cmode: 2 };
-
- xhrPost("backend.php", query, (transport) => {
- handle_rpc_json(transport);
- });
-}
-
-// sel_state ignored
-function selectionTogglePublished(ids) {
- const rows = ids || getSelectedArticleIds2();
-
- if (rows.length == 0) {
- alert(__("No articles are selected."));
- return;
- }
-
- for (let i = 0; i < rows.length; i++) {
- togglePub(rows[i], true);
- }
-
- if (rows.length > 0) {
- const query = { op: "rpc", method: "publishSelected",
- ids: rows.toString(), cmode: 2 };
-
- xhrPost("backend.php", query, (transport) => {
- handle_rpc_json(transport);
- });
- }
-}
-
-function getSelectedArticleIds2() {
-
- const rv = [];
-
- $$("#headlines-frame > div[id*=RROW][class*=Selected]").each(
- function(child) {
- rv.push(child.getAttribute("data-article-id"));
- });
-
- // consider active article a honorary member of selected articles
- if (getActiveArticleId())
- rv.push(getActiveArticleId());
-
- return rv.uniq();
-}
-
-function getLoadedArticleIds() {
- const rv = [];
-
- const children = $$("#headlines-frame > div[id*=RROW-]");
-
- children.each(function(child) {
- if (Element.visible(child)) {
- rv.push(child.getAttribute("data-article-id"));
- }
- });
-
- return rv;
-}
-
-// mode = all,none,unread,invert,marked,published
-function selectArticles(mode) {
- let query = "#headlines-frame > div[id*=RROW]";
-
- switch (mode) {
- case "none":
- case "all":
- case "invert":
- break;
- case "marked":
- query += "[class*=marked]";
- break;
- case "published":
- query += "[class*=published]";
- break;
- case "unread":
- query += "[class*=Unread]";
- break;
- default:
- console.warn("selectArticles: unknown mode", mode);
- }
-
- const rows = $$(query);
-
- for (let i = 0; i < rows.length; i++) {
- const row = rows[i];
- const cb = dijit.getEnclosingWidget(row.select(".rchk")[0]);
-
- switch (mode) {
- case "none":
- row.removeClassName("Selected");
-
- if (!row.hasClassName("active"))
- cb.attr("checked", false);
- break;
- case "invert":
- if (row.hasClassName("Selected")) {
- row.removeClassName("Selected");
-
- if (!row.hasClassName("active"))
- cb.attr("checked", false);
- } else {
- row.addClassName("Selected");
- cb.attr("checked", true);
- }
- break;
- default:
- row.addClassName("Selected");
- cb.attr("checked", true);
- }
-
- updateSelectedPrompt();
- }
-}
-
-// noinspection JSUnusedGlobalSymbols
-function deleteSelection() {
-
- const rows = getSelectedArticleIds2();
-
- if (rows.length == 0) {
- alert(__("No articles are selected."));
- return;
- }
-
- const fn = getFeedName(getActiveFeedId(), activeFeedIsCat());
- let str;
-
- if (getActiveFeedId() != 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);
- }
-
- str = str.replace("%d", rows.length);
- str = str.replace("%s", fn);
-
- if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
- return;
- }
-
- const query = { op: "rpc", method: "delete", ids: rows.toString() };
-
- xhrPost("backend.php", query, (transport) => {
- handle_rpc_json(transport);
- viewCurrentFeed();
- });
-}
-
-// noinspection JSUnusedGlobalSymbols
-function archiveSelection() {
-
- const rows = getSelectedArticleIds2();
-
- if (rows.length == 0) {
- alert(__("No articles are selected."));
- return;
- }
-
- const fn = getFeedName(getActiveFeedId(), activeFeedIsCat());
- let str;
- let op;
-
- if (getActiveFeedId() != 0) {
- str = ngettext("Archive %d selected article in %s?", "Archive %d selected articles in %s?", rows.length);
- op = "archive";
- } else {
- str = ngettext("Move %d archived article back?", "Move %d archived articles back?", rows.length);
- str += " " + __("Please note that unstarred articles might get purged on next feed update.");
-
- op = "unarchive";
- }
-
- str = str.replace("%d", rows.length);
- str = str.replace("%s", fn);
-
- if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
- return;
- }
-
- for (let i = 0; i < rows.length; i++) {
- cache_delete("article:" + rows[i]);
- }
-
- const query = {op: "rpc", method: op, ids: rows.toString()};
-
- xhrPost("backend.php", query, (transport) => {
- handle_rpc_json(transport);
- viewCurrentFeed();
- });
-}
-
-function catchupSelection() {
-
- const rows = getSelectedArticleIds2();
-
- if (rows.length == 0) {
- alert(__("No articles are selected."));
- return;
- }
-
- const fn = getFeedName(getActiveFeedId(), activeFeedIsCat());
-
- let str = ngettext("Mark %d selected article in %s as read?", "Mark %d selected articles in %s as read?", rows.length);
-
- str = str.replace("%d", rows.length);
- str = str.replace("%s", fn);
-
- if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
- return;
- }
-
- selectionToggleUnread({callback: viewCurrentFeed, no_error: 1});
-}
-
-function editArticleTags(id) {
- const query = "backend.php?op=article&method=editArticleTags&param=" + param_escape(id);
-
- if (dijit.byId("editTagsDlg"))
- dijit.byId("editTagsDlg").destroyRecursive();
-
- const dialog = new dijit.Dialog({
- id: "editTagsDlg",
- title: __("Edit article Tags"),
- style: "width: 600px",
- execute: function() {
- if (this.validate()) {
- notify_progress("Saving article tags...", true);
-
- xhrPost("backend.php", this.attr('value'), (transport) => {
- try {
- notify('');
- dialog.hide();
-
- const data = JSON.parse(transport.responseText);
-
- if (data) {
- const id = data.id;
-
- const tags = $("ATSTR-" + id);
- const tooltip = dijit.byId("ATSTRTIP-" + id);
-
- if (tags) tags.innerHTML = data.content;
- if (tooltip) tooltip.attr('label', data.content_full);
- }
- } catch (e) {
- exception_error(e);
- }
- });
- }
- },
- href: query
- });
-
- const tmph = dojo.connect(dialog, 'onLoad', function() {
- dojo.disconnect(tmph);
-
- new Ajax.Autocompleter('tags_str', 'tags_choices',
- "backend.php?op=article&method=completeTags",
- { tokens: ',', paramName: "search" });
- });
-
- dialog.show();
-
-}
-
-function cdmScrollToArticleId(id, force) {
- const ctr = $("headlines-frame");
- const e = $("RROW-" + id);
-
- if (!e || !ctr) return;
-
- if (force || e.offsetTop+e.offsetHeight > (ctr.scrollTop+ctr.offsetHeight) ||
- e.offsetTop < ctr.scrollTop) {
-
- // expanded cdm has a 4px margin now
- ctr.scrollTop = parseInt(e.offsetTop) - 4;
-
- Element.hide("floatingTitle");
- }
-}
-
-function setActiveArticleId(id) {
- console.log("setActiveArticleId", id);
-
- $$("div[id*=RROW][class*=active]").each((e) => {
- e.removeClassName("active");
-
- if (!e.hasClassName("Selected")) {
- const cb = dijit.getEnclosingWidget(e.select(".rchk")[0]);
- if (cb) cb.attr("checked", false);
- }
- });
-
- _active_article_id = id;
-
- const row = $("RROW-" + id);
-
- if (row) {
- if (row.hasAttribute("data-content")) {
- console.log("unpacking: " + row.id);
-
- row.select(".content-inner")[0].innerHTML = row.getAttribute("data-content");
- row.removeAttribute("data-content");
-
- PluginHost.run(PluginHost.HOOK_ARTICLE_RENDERED_CDM, row);
- }
-
- if (row.hasClassName("Unread")) {
-
- catchupBatchedArticles(() => {
- decrementFeedCounter(getActiveFeedId(), activeFeedIsCat());
- toggleUnread(id, 0);
- updateFloatingTitle(true);
- });
-
- }
-
- row.addClassName("active");
-
- if (!row.hasClassName("Selected")) {
- const cb = dijit.getEnclosingWidget(row.select(".rchk")[0]);
- if (cb) cb.attr("checked", true);
- }
-
- PluginHost.run(PluginHost.HOOK_ARTICLE_SET_ACTIVE, _active_article_id);
- }
-
- updateSelectedPrompt();
-}
-
-function getActiveArticleId() {
- return _active_article_id;
-}
-
-function postMouseIn(e, id) {
- post_under_pointer = id;
-}
-
-function postMouseOut(id) {
- post_under_pointer = false;
-}
-
-function unpackVisibleArticles() {
- if (!isCombinedMode() || !getInitParam("cdm_expanded")) return;
-
- const rows = $$("#headlines-frame div[id*=RROW][data-content]");
- const threshold = $("headlines-frame").scrollTop + $("headlines-frame").offsetHeight + 600;
-
- for (let i = 0; i < rows.length; i++) {
- const row = rows[i];
-
- if (row.offsetTop <= threshold) {
- console.log("unpacking: " + row.id);
-
- row.select(".content-inner")[0].innerHTML = row.getAttribute("data-content");
- row.removeAttribute("data-content");
-
- PluginHost.run(PluginHost.HOOK_ARTICLE_RENDERED_CDM, row);
- } else {
- break;
- }
- }
-}
-
-function headlinesScrollHandler(/* event */) {
- try {
- unpackVisibleArticles();
-
- if (isCombinedMode()) {
- updateFloatingTitle();
-
- // set topmost child in the buffer as active
- if (getInitParam("cdm_expanded") && getInitParam("cdm_auto_catchup") == 1) {
-
- const rows = $$("#headlines-frame > div[id*=RROW]");
-
- for (let i = 0; i < rows.length; i++) {
- const row = rows[i];
-
- if ($("headlines-frame").scrollTop <= row.offsetTop &&
- row.offsetTop - $("headlines-frame").scrollTop < 100 &&
- row.getAttribute("data-article-id") != getActiveArticleId()) {
-
- setActiveArticleId(row.getAttribute("data-article-id"));
- break;
- }
- }
- }
- }
-
- if (!infscroll_disabled) {
- const hsp = $("headlines-spacer");
- const container = $("headlines-frame");
-
- if (hsp && hsp.offsetTop - 250 <= container.scrollTop + container.offsetHeight) {
-
- hsp.innerHTML = "<span class='loading'><img src='images/indicator_tiny.gif'> " +
- __("Loading, please wait...") + "</span>";
-
- loadMoreHeadlines();
- return;
- }
- }
-
- if (getInitParam("cdm_auto_catchup") == 1) {
-
- let rows = $$("#headlines-frame > div[id*=RROW][class*=Unread]");
-
- for (let i = 0; i < rows.length; i++) {
- const row = rows[i];
-
- if ($("headlines-frame").scrollTop > (row.offsetTop + row.offsetHeight/2)) {
- const id = row.getAttribute("data-article-id")
-
- if (catchup_id_batch.indexOf(id) == -1)
- catchup_id_batch.push(id);
-
- } else {
- break;
- }
- }
-
- if (infscroll_disabled) {
- const row = $$("#headlines-frame div[id*=RROW]").last();
-
- if (row && $("headlines-frame").scrollTop >
- (row.offsetTop + row.offsetHeight - 50)) {
-
- console.log("we seem to be at an end");
-
- if (getInitParam("on_catchup_show_next_feed") == "1") {
- openNextUnreadFeed();
- }
- }
- }
- }
- } catch (e) {
- console.warn("headlinesScrollHandler", e);
- }
-}
-
-function openNextUnreadFeed() {
- const is_cat = activeFeedIsCat();
- const nuf = getNextUnreadFeed(getActiveFeedId(), is_cat);
- if (nuf) viewfeed({feed: nuf, is_cat: is_cat});
-}
-
-function catchupBatchedArticles(callback) {
- console.log("catchupBatchedArticles, size=", catchup_id_batch.length);
-
- if (catchup_id_batch.length > 0) {
-
- // make a copy of the array
- const batch = catchup_id_batch.slice();
- const query = { op: "rpc", method: "catchupSelected",
- cmode: 0, ids: batch.toString() };
-
- xhrPost("backend.php", query, (transport) => {
- const reply = handle_rpc_json(transport);
-
- if (reply) {
- const batch = reply.ids;
-
- batch.each(function (id) {
- const elem = $("RROW-" + id);
- if (elem) elem.removeClassName("Unread");
- catchup_id_batch.remove(id);
- });
- }
-
- updateFloatingTitle(true);
-
- if (callback) callback();
- });
- } else {
- if (callback) callback();
- }
-}
-
-function catchupRelativeToArticle(below, id) {
-
- if (!id) id = getActiveArticleId();
-
- if (!id) {
- alert(__("No article is selected."));
- return;
- }
-
- const visible_ids = getLoadedArticleIds();
-
- const ids_to_mark = [];
-
- if (!below) {
- for (let i = 0; i < visible_ids.length; i++) {
- if (visible_ids[i] != id) {
- const e = $("RROW-" + visible_ids[i]);
-
- if (e && e.hasClassName("Unread")) {
- ids_to_mark.push(visible_ids[i]);
- }
- } else {
- break;
- }
- }
- } else {
- for (let i = visible_ids.length - 1; i >= 0; i--) {
- if (visible_ids[i] != id) {
- const e = $("RROW-" + visible_ids[i]);
-
- if (e && e.hasClassName("Unread")) {
- ids_to_mark.push(visible_ids[i]);
- }
- } else {
- break;
- }
- }
- }
-
- 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 (getInitParam("confirm_feed_catchup") != 1 || confirm(msg)) {
-
- for (var i = 0; i < ids_to_mark.length; i++) {
- var e = $("RROW-" + ids_to_mark[i]);
- e.removeClassName("Unread");
- }
-
- const query = { op: "rpc", method: "catchupSelected",
- cmode: 0, ids: ids_to_mark.toString() };
-
- xhrPost("backend.php", query, (transport) => {
- handle_rpc_json(transport);
- });
- }
- }
-}
-
-function getArticleUnderPointer() {
- return post_under_pointer;
-}
-
-function scrollArticle(offset) {
- if (!isCombinedMode()) {
- const ci = $("content-insert");
- if (ci) {
- ci.scrollTop += offset;
- }
- } else {
- const hi = $("headlines-frame");
- if (hi) {
- hi.scrollTop += offset;
- }
-
- }
-}
-
-function updateHeadlineLabels(transport) {
- const data = JSON.parse(transport.responseText);
-
- if (data) {
- data['info-for-headlines'].each(function (elem) {
- $$(".HLLCTR-" + elem.id).each(function (ctr) {
- ctr.innerHTML = elem.labels;
- });
- });
- }
-}
-
-function cdmClicked(event, id, in_body) {
- in_body = in_body || false;
-
- if (!in_body && (event.ctrlKey || id == getActiveArticleId() || getInitParam("cdm_expanded"))) {
- openArticleInNewWindow(id);
- }
-
- setActiveArticleId(id);
-
- if (!getInitParam("cdm_expanded"))
- cdmScrollToArticleId(id);
-
- //var shift_key = event.shiftKey;
-
- /* if (!event.ctrlKey && !event.metaKey) {
-
- let elem = $("RROW-" + getActiveArticleId());
-
- if (elem) elem.removeClassName("active");
-
- selectArticles("none");
- toggleSelected(id);
-
- elem = $("RROW-" + id);
- const article_is_unread = elem.hasClassName("Unread");
-
- elem.removeClassName("Unread");
- elem.addClassName("active");
-
- setActiveArticleId(id);
-
- if (article_is_unread) {
- decrementFeedCounter(getActiveFeedId(), activeFeedIsCat());
- updateFloatingTitle(true);
-
- const query = {
- op: "rpc", method: "catchupSelected",
- cmode: 0, ids: id
- };
-
- xhrPost("backend.php", query, (transport) => {
- handle_rpc_json(transport);
- });
- }
-
- return !event.shiftKey;
-
- } else if (!in_body) {
-
- toggleSelected(id, true);
-
- let elem = $("RROW-" + id);
- const article_is_unread = elem.hasClassName("Unread");
-
- if (article_is_unread) {
- decrementFeedCounter(getActiveFeedId(), activeFeedIsCat());
- }
-
- toggleUnread(id, 0, false);
-
- openArticleInNewWindow(id);
- } else {
- return true;
- }
-
- const unread_in_buffer = $$("#headlines-frame > div[id*=RROW][class*=Unread]").length
- request_counters(unread_in_buffer == 0); */
-
- return in_body;
-}
-
-function hlClicked(event, id) {
- if (event.ctrlKey) {
- openArticleInNewWindow(id);
- setActiveArticleId(id);
- } else {
- view(id);
- }
-
- return false;
-
- /* if (event.which == 2) {
- view(id);
- return true;
- } else if (event.ctrlKey || event.metaKey) {
- openArticleInNewWindow(id);
- return false;
- } else {
- view(id);
- return false;
- } */
-}
-
-function openArticleInNewWindow(id) {
- const w = window.open("");
- w.opener = null;
- w.location = "backend.php?op=article&method=redirect&id=" + id;
-}
-
-function isCombinedMode() {
- return getInitParam("combined_display_mode");
-}
-
-/* function markHeadline(id, marked) {
- if (marked == undefined) marked = true;
-
- const row = $("RROW-" + id);
- if (row) {
- const check = dijit.getEnclosingWidget(
- row.getElementsByClassName("rchk")[0]);
-
- if (check) {
- check.attr("checked", marked);
- }
-
- if (marked)
- row.addClassName("Selected");
- else
- row.removeClassName("Selected");
- }
-} */
-
-function getRelativePostIds(id, limit) {
-
- const tmp = [];
-
- if (!limit) limit = 6; //3
-
- const ids = getLoadedArticleIds();
-
- for (let i = 0; i < ids.length; i++) {
- if (ids[i] == id) {
- for (let k = 1; k <= limit; k++) {
- //if (i > k-1) tmp.push(ids[i-k]);
- if (i < ids.length - k) tmp.push(ids[i + k]);
- }
- break;
- }
- }
-
- return tmp;
-}
-
-function correctHeadlinesOffset(id) {
- const container = $("headlines-frame");
- const row = $("RROW-" + id);
-
- if (!container || !row) return;
-
- const viewport = container.offsetHeight;
-
- const rel_offset_top = row.offsetTop - container.scrollTop;
- const rel_offset_bottom = row.offsetTop + row.offsetHeight - container.scrollTop;
-
- //console.log("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom);
- //console.log("Vport: " + viewport);
-
- if (rel_offset_top <= 0 || rel_offset_top > viewport) {
- container.scrollTop = row.offsetTop;
- } else if (rel_offset_bottom > viewport) {
- container.scrollTop = row.offsetTop + row.offsetHeight - viewport;
- }
-}
-
-function headlineActionsChange(elem) {
- eval(elem.value);
- elem.attr('value', 'false');
-}
-
-function cdmCollapseActive(event) {
- const row = $("RROW-" + getActiveArticleId());
-
- if (row) {
- row.removeClassName("active");
- const cb = dijit.getEnclosingWidget(row.select(".rchk")[0]);
-
- if (cb && !row.hasClassName("Selected"))
- cb.attr("checked", false);
-
- setActiveArticleId(0);
-
- if (event)
- event.stopPropagation();
-
- return false;
- }
-}
-
-function closeArticlePanel() {
- if (dijit.byId("content-insert"))
- dijit.byId("headlines-wrap-inner").removeChild(
- dijit.byId("content-insert"));
-}
-
-function initFloatingMenu() {
- if (!dijit.byId("floatingMenu")) {
-
- const menu = new dijit.Menu({
- id: "floatingMenu",
- targetNodeIds: ["floatingTitle"]
- });
-
- headlinesMenuCommon(menu);
-
- menu.startup();
- }
-}
-
-function headlinesMenuCommon(menu) {
-
- menu.addChild(new dijit.MenuItem({
- label: __("Open original article"),
- onClick: function (event) {
- openArticleInNewWindow(this.getParent().currentTarget.getAttribute("data-article-id"));
- }
- }));
-
- menu.addChild(new dijit.MenuItem({
- label: __("Display article URL"),
- onClick: function (event) {
- displayArticleUrl(this.getParent().currentTarget.getAttribute("data-article-id"));
- }
- }));
-
- menu.addChild(new dijit.MenuSeparator());
-
- menu.addChild(new dijit.MenuItem({
- label: __("Toggle unread"),
- onClick: function () {
-
- let ids = getSelectedArticleIds2();
- // cast to string
- const id = (this.getParent().currentTarget.getAttribute("data-article-id")) + "";
- ids = ids.length != 0 && ids.indexOf(id) != -1 ? ids : [id];
-
- selectionToggleUnread({ids: ids, no_error: 1});
- }
- }));
-
- menu.addChild(new dijit.MenuItem({
- label: __("Toggle starred"),
- onClick: function () {
- let ids = getSelectedArticleIds2();
- // cast to string
- const id = (this.getParent().currentTarget.getAttribute("data-article-id")) + "";
- ids = ids.length != 0 && ids.indexOf(id) != -1 ? ids : [id];
-
- selectionToggleMarked(ids);
- }
- }));
-
- menu.addChild(new dijit.MenuItem({
- label: __("Toggle published"),
- onClick: function () {
- let ids = getSelectedArticleIds2();
- // cast to string
- const id = (this.getParent().currentTarget.getAttribute("data-article-id")) + "";
- ids = ids.length != 0 && ids.indexOf(id) != -1 ? ids : [id];
-
- selectionTogglePublished(ids);
- }
- }));
-
- menu.addChild(new dijit.MenuSeparator());
-
- menu.addChild(new dijit.MenuItem({
- label: __("Mark above as read"),
- onClick: function () {
- catchupRelativeToArticle(0, this.getParent().currentTarget.getAttribute("data-article-id"));
- }
- }));
-
- menu.addChild(new dijit.MenuItem({
- label: __("Mark below as read"),
- onClick: function () {
- catchupRelativeToArticle(1, this.getParent().currentTarget.getAttribute("data-article-id"));
- }
- }));
-
-
- const labels = getInitParam("labels");
-
- if (labels && labels.length) {
-
- menu.addChild(new dijit.MenuSeparator());
-
- const labelAddMenu = new dijit.Menu({ownerMenu: menu});
- const labelDelMenu = new dijit.Menu({ownerMenu: menu});
-
- labels.each(function (label) {
- const bare_id = label.id;
- const name = label.caption;
-
- labelAddMenu.addChild(new dijit.MenuItem({
- label: name,
- labelId: bare_id,
- onClick: function () {
-
- let ids = getSelectedArticleIds2();
- // cast to string
- const id = (this.getParent().ownerMenu.currentTarget.getAttribute("data-article-id")) + "";
-
- ids = ids.length != 0 && ids.indexOf(id) != -1 ? ids : [id];
-
- selectionAssignLabel(this.labelId, ids);
- }
- }));
-
- labelDelMenu.addChild(new dijit.MenuItem({
- label: name,
- labelId: bare_id,
- onClick: function () {
- let ids = getSelectedArticleIds2();
- // cast to string
- const id = (this.getParent().ownerMenu.currentTarget.getAttribute("data-article-id")) + "";
-
- ids = ids.length != 0 && ids.indexOf(id) != -1 ? ids : [id];
-
- selectionRemoveLabel(this.labelId, ids);
- }
- }));
-
- });
-
- menu.addChild(new dijit.PopupMenuItem({
- label: __("Assign label"),
- popup: labelAddMenu
- }));
-
- menu.addChild(new dijit.PopupMenuItem({
- label: __("Remove label"),
- popup: labelDelMenu
- }));
-
- }
-}
-
-function initHeadlinesMenu() {
- if (!dijit.byId("headlinesMenu")) {
-
- const menu = new dijit.Menu({
- id: "headlinesMenu",
- targetNodeIds: ["headlines-frame"],
- selector: ".hlMenuAttach"
- });
-
- headlinesMenuCommon(menu);
-
- menu.startup();
- }
-
- /* vgroup feed title menu */
-
- if (!dijit.byId("headlinesFeedTitleMenu")) {
-
- const menu = new dijit.Menu({
- id: "headlinesFeedTitleMenu",
- targetNodeIds: ["headlines-frame"],
- selector: "div.cdmFeedTitle"
- });
-
- menu.addChild(new dijit.MenuItem({
- label: __("Select articles in group"),
- onClick: function (event) {
- selectArticles("all",
- "#headlines-frame > div[id*=RROW]" +
- "[data-orig-feed-id='" + this.getParent().currentTarget.getAttribute("data-feed-id") + "']");
-
- }
- }));
-
- menu.addChild(new dijit.MenuItem({
- label: __("Mark group as read"),
- onClick: function () {
- selectArticles("none");
- selectArticles("all",
- "#headlines-frame > div[id*=RROW]" +
- "[data-orig-feed-id='" + this.getParent().currentTarget.getAttribute("data-feed-id") + "']");
-
- catchupSelection();
- }
- }));
-
- menu.addChild(new dijit.MenuItem({
- label: __("Mark feed as read"),
- onClick: function () {
- catchupFeedInGroup(this.getParent().currentTarget.getAttribute("data-feed-id"));
- }
- }));
-
- menu.addChild(new dijit.MenuItem({
- label: __("Edit feed"),
- onClick: function () {
- editFeed(this.getParent().currentTarget.getAttribute("data-feed-id"));
- }
- }));
-
- menu.startup();
- }
-}
-
-function cache_set(id, obj) {
- //console.log("cache_set: " + id);
- if (has_storage)
- try {
- sessionStorage[id] = obj;
- } catch (e) {
- sessionStorage.clear();
- }
-}
-
-function cache_get(id) {
- if (has_storage)
- return sessionStorage[id];
-}
-
-function cache_clear() {
- if (has_storage)
- sessionStorage.clear();
-}
-
-function cache_delete(id) {
- if (has_storage)
- sessionStorage.removeItem(id);
-}
-
-function cancelSearch() {
- _search_query = "";
- viewCurrentFeed();
-}
-
-function setSelectionScore() {
- const ids = getSelectedArticleIds2();
-
- if (ids.length > 0) {
- console.log(ids);
-
- const score = prompt(__("Please enter new score for selected articles:"));
-
- if (score != undefined) {
- const query = { op: "article", method: "setScore", id: ids.toString(),
- score: score };
-
- xhrJson("backend.php", query, (reply) => {
- if (reply) {
- reply.id.each((id) => {
- const row = $("RROW-" + id);
-
- if (row) {
- const pic = row.getElementsByClassName("score-pic")[0];
-
- if (pic) {
- pic.src = pic.src.replace(/score_.*?\.png/,
- reply["score_pic"]);
- pic.setAttribute("score", reply["score"]);
- }
- }
- });
- }
- });
- }
-
- } else {
- alert(__("No articles are selected."));
- }
-}
-
-function changeScore(id, pic) {
- const score = pic.getAttribute("score");
-
- const new_score = prompt(__("Please enter new score for this article:"), score);
-
- if (new_score != undefined) {
- const query = { op: "article", method: "setScore", id: id, score: new_score };
-
- xhrJson("backend.php", query, (reply) => {
- if (reply) {
- pic.src = pic.src.replace(/score_.*?\.png/, reply["score_pic"]);
- pic.setAttribute("score", new_score);
- pic.setAttribute("title", new_score);
- }
- });
- }
-}
-
-function displayArticleUrl(id) {
- const query = { op: "rpc", method: "getlinktitlebyid", id: id };
-
- xhrJson("backend.php", query, (reply) => {
- if (reply && reply.link) {
- prompt(__("Article URL:"), reply.link);
- }
- });
-
-}
-
-// floatingTitle goto button uses this
-/* function scrollToRowId(id) {
- const row = $(id);
-
- if (row)
- $("headlines-frame").scrollTop = row.offsetTop - 4;
-} */
-
-function updateFloatingTitle(unread_only) {
- if (!isCombinedMode()/* || !getInitParam("cdm_expanded")*/) return;
-
- const hf = $("headlines-frame");
- const elems = $$("#headlines-frame > div[id*=RROW]");
- const ft = $("floatingTitle");
-
- for (let i = 0; i < elems.length; i++) {
- const row = elems[i];
-
- if (row && row.offsetTop + row.offsetHeight > hf.scrollTop) {
-
- const header = row.select(".header")[0];
- var id = row.getAttribute("data-article-id");
-
- if (unread_only || id != ft.getAttribute("data-article-id")) {
- if (id != ft.getAttribute("data-article-id")) {
-
- ft.setAttribute("data-article-id", id);
- ft.innerHTML = header.innerHTML;
- ft.firstChild.innerHTML = "<img class='anchor marked-pic' src='images/page_white_go.png' " +
- "onclick=\"cdmScrollToArticleId("+id + ", true)\">" + ft.firstChild.innerHTML;
-
- initFloatingMenu();
-
- const cb = ft.select(".rchk")[0];
-
- if (cb)
- cb.parentNode.removeChild(cb);
- }
-
- if (row.hasClassName("Unread"))
- ft.addClassName("Unread");
- else
- ft.removeClassName("Unread");
-
- PluginHost.run(PluginHost.HOOK_FLOATING_TITLE, row);
- }
-
- ft.style.marginRight = hf.offsetWidth - row.offsetWidth + "px";
-
- if (header.offsetTop + header.offsetHeight < hf.scrollTop + ft.offsetHeight - 5 &&
- row.offsetTop + row.offsetHeight >= hf.scrollTop + ft.offsetHeight - 5)
- new Effect.Appear(ft, {duration: 0.3});
- else
- Element.hide(ft);
-
- return;
- }
- }
-}