summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
Diffstat (limited to 'js')
-rw-r--r--js/AppBase.js69
-rw-r--r--js/Article.js57
-rw-r--r--js/CommonDialogs.js2
-rw-r--r--js/Feeds.js20
-rwxr-xr-xjs/Headlines.js147
-rw-r--r--js/PrefHelpers.js44
-rwxr-xr-xjs/prefs.js29
-rw-r--r--js/tt-rss.js127
8 files changed, 324 insertions, 171 deletions
diff --git a/js/AppBase.js b/js/AppBase.js
index a5e20b8f9..8a710d685 100644
--- a/js/AppBase.js
+++ b/js/AppBase.js
@@ -16,6 +16,51 @@ define(["dojo/_base/declare"], function (declare) {
setInitParam: function(k, v) {
this._initParams[k] = v;
},
+ nightModeChanged: function(is_night, link) {
+ console.log("night mode changed to", is_night);
+
+ if (link) {
+ const css_override = is_night ? "themes/night.css" : "css/default.css";
+ link.setAttribute("href", css_override + "?" + Date.now());
+ }
+ },
+ setupNightModeDetection: function(callback) {
+ if (!$("theme_css")) {
+ const mql = window.matchMedia('(prefers-color-scheme: dark)');
+
+ try {
+ mql.addEventListener("change", () => {
+ this.nightModeChanged(mql.matches, $("theme_auto_css"));
+ });
+ } catch (e) {
+ console.warn("exception while trying to set MQL event listener");
+ }
+
+ const link = new Element("link", {
+ rel: "stylesheet",
+ id: "theme_auto_css"
+ });
+
+ if (callback) {
+ link.onload = function () {
+ document.querySelector("body").removeClassName("css_loading");
+ callback();
+ };
+
+ link.onerror = function(event) {
+ alert("Fatal error while loading application stylesheet: " + link.getAttribute("href"));
+ }
+ }
+
+ this.nightModeChanged(mql.matches, link);
+
+ document.querySelector("head").appendChild(link);
+ } else {
+ document.querySelector("body").removeClassName("css_loading");
+
+ if (callback) callback();
+ }
+ },
enableCsrfSupport: function() {
Ajax.Base.prototype.initialize = Ajax.Base.prototype.initialize.wrap(
function (callOriginal, options) {
@@ -358,30 +403,6 @@ define(["dojo/_base/declare"], function (declare) {
this.initSecondStage();
},
- toggleNightMode: function() {
- const link = $("theme_css");
-
- if (link) {
-
- let user_theme = "";
- let user_css = "";
-
- if (link.getAttribute("href").indexOf("themes/night.css") == -1) {
- user_css = "themes/night.css?" + Date.now();
- user_theme = "night.css";
- } else {
- user_theme = "default.php";
- user_css = "css/default.css?" + Date.now();
- }
-
- $("main").fade({duration: 0.5, afterFinish: () => {
- link.setAttribute("href", user_css);
- $("main").appear({duration: 0.5});
- xhrPost("backend.php", {op: "rpc", method: "setpref", key: "USER_CSS_THEME", value: user_theme});
- }});
-
- }
- },
explainError: function(code) {
return this.displayDlg(__("Error explained"), "explainError", code);
},
diff --git a/js/Article.js b/js/Article.js
index b933ed716..08b565695 100644
--- a/js/Article.js
+++ b/js/Article.js
@@ -2,6 +2,7 @@
/* global __, ngettext */
define(["dojo/_base/declare"], function (declare) {
Article = {
+ _scroll_reset_timeout: false,
getScoreClass: function (score) {
if (score > 500) {
return "score-high";
@@ -32,7 +33,7 @@ define(["dojo/_base/declare"], function (declare) {
if (ids.length > 0) {
const score = prompt(__("Please enter new score for selected articles:"));
- if (parseInt(score) != undefined) {
+ if (!isNaN(parseInt(score))) {
ids.each((id) => {
const row = $("RROW-" + id);
@@ -66,7 +67,7 @@ define(["dojo/_base/declare"], function (declare) {
const score_old = row.getAttribute("data-score");
const score = prompt(__("Please enter new score for this article:"), score_old);
- if (parseInt(score) != undefined) {
+ if (!isNaN(parseInt(score))) {
row.setAttribute("data-score", score);
const pic = row.select(".icon-score")[0];
@@ -274,15 +275,28 @@ define(["dojo/_base/declare"], function (declare) {
dialog.show();
},
- cdmScrollToId: function (id, force) {
+ cdmScrollToId: function (id, force, event) {
const ctr = $("headlines-frame");
const e = $("RROW-" + id);
+ const is_expanded = App.getInitParam("cdm_expanded");
if (!e || !ctr) return;
- if (force || e.offsetTop + e.offsetHeight > (ctr.scrollTop + ctr.offsetHeight) ||
+ if (force || is_expanded || e.offsetTop + e.offsetHeight > (ctr.scrollTop + ctr.offsetHeight) ||
e.offsetTop < ctr.scrollTop) {
+ if (event && event.repeat || !is_expanded) {
+ ctr.addClassName("forbid-smooth-scroll");
+ window.clearTimeout(this._scroll_reset_timeout);
+
+ this._scroll_reset_timeout = window.setTimeout(() => {
+ if (ctr) ctr.removeClassName("forbid-smooth-scroll");
+ }, 250)
+
+ } else {
+ ctr.removeClassName("forbid-smooth-scroll");
+ }
+
ctr.scrollTop = e.offsetTop;
Element.hide("floatingTitle");
@@ -314,19 +328,30 @@ define(["dojo/_base/declare"], function (declare) {
else
return 0;
},
- 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;
- }
+ scrollByPages: function (page_offset, event) {
+ const elem = App.isCombinedMode() ? $("headlines-frame") : $("content-insert");
+
+ const offset = elem.offsetHeight * page_offset * 0.99;
+ this.scroll(offset, event);
+ },
+ scroll: function (offset, event) {
+
+ const elem = App.isCombinedMode() ? $("headlines-frame") : $("content-insert");
+
+ if (event && event.repeat) {
+ elem.addClassName("forbid-smooth-scroll");
+ window.clearTimeout(this._scroll_reset_timeout);
+
+ this._scroll_reset_timeout = window.setTimeout(() => {
+ if (elem) elem.removeClassName("forbid-smooth-scroll");
+ }, 250)
+
+ } else {
+ elem.removeClassName("forbid-smooth-scroll");
}
+
+ elem.scrollTop += offset;
},
mouseIn: function (id) {
this.post_under_pointer = id;
@@ -340,4 +365,4 @@ define(["dojo/_base/declare"], function (declare) {
}
return Article;
-}); \ No newline at end of file
+});
diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js
index e0338a97c..c6d476de0 100644
--- a/js/CommonDialogs.js
+++ b/js/CommonDialogs.js
@@ -385,7 +385,7 @@ define(["dojo/_base/declare"], function (declare) {
const msg = __("Unsubscribe from %s?").replace("%s", title);
- if (title == undefined || confirm(msg)) {
+ if (typeof title == "undefined" || confirm(msg)) {
Notify.progress("Removing feed...");
const query = {op: "pref-feeds", quiet: 1, method: "remove", ids: feed_id};
diff --git a/js/Feeds.js b/js/Feeds.js
index 07ec89452..42ab6fe7e 100644
--- a/js/Feeds.js
+++ b/js/Feeds.js
@@ -93,7 +93,9 @@ define(["dojo/_base/declare"], function (declare) {
}
}
- this.hideOrShowFeeds(App.getInitParam("hide_read_feeds") == 1);
+ Headlines.updateCurrentUnread();
+
+ this.hideOrShowFeeds(App.getInitParam("hide_read_feeds"));
this._counters_prev = elems;
PluginHost.run(PluginHost.HOOK_COUNTERS_PROCESSED);
@@ -119,6 +121,8 @@ define(["dojo/_base/declare"], function (declare) {
Element.visible("feeds-holder") ? splitter.show() : splitter.hide();
dijit.byId("main").resize();
+
+ Headlines.updateCurrentUnread();
},
cancelSearch: function() {
this._search_query = "";
@@ -147,7 +151,7 @@ define(["dojo/_base/declare"], function (declare) {
const treeModel = new fox.FeedStoreModel({
store: store,
query: {
- "type": App.getInitParam('enable_feed_cats') == 1 ? "category" : "feed"
+ "type": App.getInitParam('enable_feed_cats') ? "category" : "feed"
},
rootId: "root",
rootLabel: "Feeds",
@@ -212,7 +216,7 @@ define(["dojo/_base/declare"], function (declare) {
this.open({feed: -3});
}
- this.hideOrShowFeeds(App.getInitParam("hide_read_feeds") == 1);
+ this.hideOrShowFeeds(App.getInitParam("hide_read_feeds"));
if (App.getInitParam("is_default_pw")) {
console.warn("user password is at default value");
@@ -237,7 +241,7 @@ define(["dojo/_base/declare"], function (declare) {
}
// bw_limit disables timeout() so we request initial counters separately
- if (App.getInitParam("bw_limit") == "1") {
+ if (App.getInitParam("bw_limit")) {
this.requestCounters(true);
} else {
setTimeout(() => {
@@ -274,7 +278,7 @@ define(["dojo/_base/declare"], function (declare) {
if (tree) return tree.selectFeed(feed, is_cat);
},
toggleUnread: function() {
- const hide = !(App.getInitParam("hide_read_feeds") == "1");
+ const hide = !App.getInitParam("hide_read_feeds");
xhrPost("backend.php", {op: "rpc", method: "setpref", key: "HIDE_READ_FEEDS", value: hide}, () => {
this.hideOrShowFeeds(hide);
@@ -385,7 +389,7 @@ define(["dojo/_base/declare"], function (declare) {
}
},
catchupFeed: function(feed, is_cat, mode) {
- if (is_cat == undefined) is_cat = false;
+ is_cat = is_cat || false;
let str = false;
@@ -409,7 +413,7 @@ define(["dojo/_base/declare"], function (declare) {
str = str.replace("%s", fn)
.replace("%w", mark_what);
- if (App.getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
+ if (App.getInitParam("confirm_feed_catchup") && !confirm(str)) {
return;
}
@@ -424,7 +428,7 @@ define(["dojo/_base/declare"], function (declare) {
xhrPost("backend.php", catchup_query, (transport) => {
App.handleRpcJson(transport);
- const show_next_feed = App.getInitParam("on_catchup_show_next_feed") == "1";
+ const show_next_feed = App.getInitParam("on_catchup_show_next_feed");
if (show_next_feed) {
const nuf = this.getNextUnread(feed, is_cat);
diff --git a/js/Headlines.js b/js/Headlines.js
index 3c98bef6c..5b7aac0b0 100755
--- a/js/Headlines.js
+++ b/js/Headlines.js
@@ -7,6 +7,7 @@ define(["dojo/_base/declare"], function (declare) {
_observer_counters_timeout: 0,
headlines: [],
current_first_id: 0,
+ _scroll_reset_timeout: false,
row_observer: new MutationObserver((mutations) => {
const modified = [];
@@ -212,7 +213,7 @@ define(["dojo/_base/declare"], function (declare) {
clearTimeout(this._headlines_scroll_timeout);
this._headlines_scroll_timeout = window.setTimeout(function () {
//console.log('done scrolling', event);
- Headlines.scrollHandler();
+ Headlines.scrollHandler(event);
}, 50);
}
},
@@ -244,33 +245,35 @@ define(["dojo/_base/declare"], function (declare) {
Feeds.open({feed: Feeds.getActive(), is_cat: Feeds.activeIsCat(), offset: offset, append: true});
},
- scrollHandler: function () {
- try {
- Headlines.unpackVisible();
-
- if (App.isCombinedMode()) {
- Headlines.updateFloatingTitle();
+ isChildVisible: function (elem, ctr) {
+ const ctop = ctr.scrollTop;
+ const cbottom = ctop + ctr.offsetHeight;
- // 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 etop = elem.offsetTop;
+ const ebottom = etop + elem.offsetHeight;
- const rows = $$("#headlines-frame > div[id*=RROW]");
+ return etop >= ctop && ebottom <= cbottom ||
+ etop < ctop && ebottom > ctop || ebottom > cbottom && etop < cbottom
- for (let i = 0; i < rows.length; i++) {
- const row = rows[i];
+ },
+ firstVisible: function() {
+ const rows = $$("#headlines-frame > div[id*=RROW]");
+ const ctr = $("headlines-frame");
- if ($("headlines-frame").scrollTop <= row.offsetTop &&
- row.offsetTop - $("headlines-frame").scrollTop < 100 &&
- row.getAttribute("data-article-id") != Article.getActive()) {
+ for (let i = 0; i < rows.length; i++) {
+ const row = rows[i];
- Article.setActive(row.getAttribute("data-article-id"));
- break;
- }
- }
- }
+ if (this.isChildVisible(row, ctr)) {
+ return row.getAttribute("data-article-id");
}
+ }
+ },
+ scrollHandler: function (/*event*/) {
+ try {
+ Headlines.unpackVisible();
+
+ if (App.isCombinedMode())
+ Headlines.updateFloatingTitle();
if (!Feeds.infscroll_disabled && !Feeds.infscroll_in_progress) {
const hsp = $("headlines-spacer");
@@ -290,7 +293,7 @@ define(["dojo/_base/declare"], function (declare) {
}
}
- if (App.getInitParam("cdm_auto_catchup") == 1) {
+ if (App.getInitParam("cdm_auto_catchup")) {
let rows = $$("#headlines-frame > div[id*=RROW][class*=Unread]");
@@ -442,7 +445,7 @@ define(["dojo/_base/declare"], function (declare) {
const originally_from = Article.formatOriginallyFrom(hl);
row = `<div class="cdm ${row_class} ${Article.getScoreClass(hl.score)}" id="RROW-${hl.id}" data-article-id="${hl.id}" data-orig-feed-id="${hl.feed_id}"
- data-content="${escapeHtml(hl.content)}" data-score="${hl.score}"
+ data-content="${escapeHtml(hl.content)}" data-score="${hl.score}" data-article-title="${hl.title}"
onmouseover="Article.mouseIn(${hl.id})" onmouseout="Article.mouseOut(${hl.id})">
<div class="header">
@@ -543,6 +546,18 @@ define(["dojo/_base/declare"], function (declare) {
return tmp.firstChild;
},
+ updateCurrentUnread: function() {
+ if ($("feed_current_unread")) {
+ const feed_unread = Feeds.getUnread(Feeds.getActive(), Feeds.activeIsCat());
+
+ if (feed_unread > 0 && !Element.visible("feeds-holder")) {
+ $("feed_current_unread").innerText = feed_unread;
+ Element.show("feed_current_unread");
+ } else {
+ Element.hide("feed_current_unread");
+ }
+ }
+ },
onLoaded: function (transport, offset, append) {
const reply = App.handleRpcJson(transport);
@@ -592,7 +607,9 @@ define(["dojo/_base/declare"], function (declare) {
Article.setActive(0);
try {
+ $("headlines-frame").removeClassName("smooth-scroll");
$("headlines-frame").scrollTop = 0;
+ $("headlines-frame").addClassName("smooth-scroll");
Element.hide("floatingTitle");
$("floatingTitle").setAttribute("data-article-id", 0);
@@ -643,6 +660,8 @@ define(["dojo/_base/declare"], function (declare) {
"</span>";
}
+ Headlines.updateCurrentUnread();
+
} else if (headlines_count > 0 && feed_id == Feeds.getActive() && is_cat == Feeds.activeIsCat()) {
const c = dijit.byId("headlines-frame");
@@ -803,19 +822,27 @@ define(["dojo/_base/declare"], function (declare) {
if (row)
row.toggleClassName("published");
},
- move: function (mode, noscroll, noexpand) {
+ move: function (mode, params) {
+ params = params || {};
+
+ const noscroll = params.noscroll || false;
+ const noexpand = params.noexpand || false;
+ const event = params.event;
+
const rows = Headlines.getLoaded();
let prev_id = false;
let next_id = false;
- if (!$('RROW-' + Article.getActive())) {
+ const active_row = $("RROW-" + Article.getActive());
+
+ if (!active_row) {
Article.setActive(0);
}
- if (!Article.getActive()) {
- next_id = rows[0];
- prev_id = rows[rows.length - 1]
+ if (!Article.getActive() || (active_row && !Headlines.isChildVisible(active_row, $("headlines-frame")))) {
+ next_id = Headlines.firstVisible();
+ prev_id = next_id;
} else {
for (let i = 0; i < rows.length; i++) {
if (rows[i] == Article.getActive()) {
@@ -836,21 +863,18 @@ define(["dojo/_base/declare"], function (declare) {
console.log("cur: " + Article.getActive() + " next: " + next_id);
- if (mode == "next") {
+ if (mode === "next") {
if (next_id || Article.getActive()) {
if (App.isCombinedMode()) {
- const article = $("RROW-" + Article.getActive());
+ //const row = $("RROW-" + Article.getActive());
const ctr = $("headlines-frame");
- if (!noscroll && article && article.offsetTop + article.offsetHeight >
- ctr.scrollTop + ctr.offsetHeight) {
-
- Article.scroll(ctr.offsetHeight / 4);
-
+ if (!noscroll) {
+ Article.scroll(ctr.offsetHeight / 2, event);
} else if (next_id) {
Article.setActive(next_id);
- Article.cdmScrollToId(next_id, true);
+ Article.cdmScrollToId(next_id, true, event);
}
} else if (next_id) {
@@ -860,22 +884,23 @@ define(["dojo/_base/declare"], function (declare) {
}
}
- if (mode == "prev") {
+ if (mode === "prev") {
if (prev_id || Article.getActive()) {
if (App.isCombinedMode()) {
- const article = $("RROW-" + Article.getActive());
- const prev_article = $("RROW-" + prev_id);
+ const row = $("RROW-" + Article.getActive());
+ //const prev_row = $("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);
+ if (!noscroll) {
+ Article.scroll(-ctr.offsetHeight / 2, event);
+ } else {
+ if (row && row.offsetTop < ctr.scrollTop) {
+ Article.cdmScrollToId(Article.getActive(), noscroll, event);
+ } else if (prev_id) {
+ Article.setActive(prev_id);
+ Article.cdmScrollToId(prev_id, noscroll, event);
+ }
}
} else if (prev_id) {
@@ -900,9 +925,7 @@ define(["dojo/_base/declare"], function (declare) {
const row = $("RROW-" + id);
if (row) {
- //const origClassName = row.className;
-
- if (cmode == undefined) cmode = 2;
+ if (typeof cmode == "undefined") cmode = 2;
switch (cmode) {
case 0:
@@ -973,7 +996,7 @@ define(["dojo/_base/declare"], function (declare) {
str = str.replace("%d", rows.length);
str = str.replace("%s", fn);
- if (App.getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
+ if (App.getInitParam("confirm_feed_catchup") && !confirm(str)) {
return;
}
@@ -1119,7 +1142,7 @@ define(["dojo/_base/declare"], function (declare) {
str = str.replace("%d", rows.length);
str = str.replace("%s", fn);
- if (App.getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
+ if (App.getInitParam("confirm_feed_catchup") && !confirm(str)) {
return;
}
@@ -1145,7 +1168,7 @@ define(["dojo/_base/declare"], function (declare) {
str = str.replace("%d", rows.length);
str = str.replace("%s", fn);
- if (App.getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
+ if (App.getInitParam("confirm_feed_catchup") && !confirm(str)) {
return;
}
@@ -1381,6 +1404,22 @@ define(["dojo/_base/declare"], function (declare) {
}
},
+ scrollByPages: function (offset, event) {
+ const elem = $("headlines-frame");
+
+ if (event && event.repeat) {
+ elem.addClassName("forbid-smooth-scroll");
+ window.clearTimeout(this._scroll_reset_timeout);
+
+ this._scroll_reset_timeout = window.setTimeout(() => {
+ if (elem) elem.removeClassName("forbid-smooth-scroll");
+ }, 250)
+ } else {
+ elem.removeClassName("forbid-smooth-scroll");
+ }
+
+ elem.scrollTop += elem.offsetHeight * offset * 0.99;
+ },
initHeadlinesMenu: function () {
if (!dijit.byId("headlinesMenu")) {
diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js
index a3d122029..4b908204c 100644
--- a/js/PrefHelpers.js
+++ b/js/PrefHelpers.js
@@ -1,5 +1,43 @@
define(["dojo/_base/declare"], function (declare) {
Helpers = {
+ AppPasswords: {
+ getSelected: function() {
+ return Tables.getSelected("app-password-list");
+ },
+ updateContent: function(data) {
+ $("app_passwords_holder").innerHTML = data;
+ dojo.parser.parse("app_passwords_holder");
+ },
+ removeSelected: function() {
+ const rows = this.getSelected();
+
+ if (rows.length == 0) {
+ alert("No passwords selected.");
+ } else {
+ if (confirm(__("Remove selected app passwords?"))) {
+
+ xhrPost("backend.php", {op: "pref-prefs", method: "deleteAppPassword", ids: rows.toString()}, (transport) => {
+ this.updateContent(transport.responseText);
+ Notify.close();
+ });
+
+ Notify.progress("Loading, please wait...");
+ }
+ }
+ },
+ generate: function() {
+ const title = prompt("Password description:")
+
+ if (title) {
+ xhrPost("backend.php", {op: "pref-prefs", method: "generateAppPassword", title: title}, (transport) => {
+ this.updateContent(transport.responseText);
+ Notify.close();
+ });
+
+ Notify.progress("Loading, please wait...");
+ }
+ },
+ },
clearFeedAccessKeys: function() {
if (confirm(__("This will invalidate all previously generated feed URLs. Continue?"))) {
Notify.progress("Clearing URLs...");
@@ -112,6 +150,12 @@ define(["dojo/_base/declare"], function (declare) {
id: "cssEditDlg",
title: __("Customize stylesheet"),
style: "width: 600px",
+ apply: function() {
+ xhrPost("backend.php", this.attr('value'), () => {
+ new Effect.Appear("css_edit_apply_msg");
+ $("user_css_style").innerText = this.attr('value');
+ });
+ },
execute: function () {
Notify.progress('Saving data...', true);
diff --git a/js/prefs.js b/js/prefs.js
index 844ce8c8a..944e49258 100755
--- a/js/prefs.js
+++ b/js/prefs.js
@@ -63,19 +63,21 @@ require(["dojo/_base/kernel",
try {
const _App = declare("fox.App", AppBase, {
constructor: function() {
- parser.parse();
+ this.setupNightModeDetection(() => {
+ parser.parse();
- this.setLoadingProgress(50);
+ this.setLoadingProgress(50);
- const clientTzOffset = new Date().getTimezoneOffset() * 60;
- const params = {op: "rpc", method: "sanityCheck", clientTzOffset: clientTzOffset};
+ 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);
- }
+ xhrPost("backend.php", params, (transport) => {
+ try {
+ this.backendSanityCallback(transport);
+ } catch (e) {
+ this.Error.report(e);
+ }
+ });
});
},
initSecondStage: function() {
@@ -142,8 +144,6 @@ require(["dojo/_base/kernel",
case "help_dialog":
App.helpDialog("main");
return false;
- case "toggle_night_mode":
- App.toggleNightMode();
default:
console.log("unhandled action: " + action_name + "; keycode: " + event.which);
}
@@ -157,7 +157,10 @@ require(["dojo/_base/kernel",
App = new _App();
} catch (e) {
- this.Error.report(e);
+ if (App && App.Error)
+ App.Error.report(e);
+ else
+ alert(e + "\n\n" + e.stack);
}
});
});
diff --git a/js/tt-rss.js b/js/tt-rss.js
index a31404426..84e42bf85 100644
--- a/js/tt-rss.js
+++ b/js/tt-rss.js
@@ -67,33 +67,35 @@ require(["dojo/_base/kernel",
_widescreen_mode: false,
hotkey_actions: {},
constructor: function () {
- parser.parse();
-
- if (!this.checkBrowserFeatures())
- return;
-
- this.setLoadingProgress(30);
- this.initHotkeyActions();
-
- const a = document.createElement('audio');
- const hasAudio = !!a.canPlayType;
- const hasSandbox = "sandbox" in document.createElement("iframe");
- const hasMp3 = !!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, ''));
- const clientTzOffset = new Date().getTimezoneOffset() * 60;
-
- const params = {
- op: "rpc", method: "sanityCheck", hasAudio: hasAudio,
- hasMp3: hasMp3,
- clientTzOffset: clientTzOffset,
- hasSandbox: hasSandbox
- };
-
- xhrPost("backend.php", params, (transport) => {
- try {
- App.backendSanityCallback(transport);
- } catch (e) {
- App.Error.report(e);
- }
+ this.setupNightModeDetection(() => {
+ parser.parse();
+
+ if (!this.checkBrowserFeatures())
+ return;
+
+ this.setLoadingProgress(30);
+ this.initHotkeyActions();
+
+ const a = document.createElement('audio');
+ const hasAudio = !!a.canPlayType;
+ const hasSandbox = "sandbox" in document.createElement("iframe");
+ const hasMp3 = !!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, ''));
+ const clientTzOffset = new Date().getTimezoneOffset() * 60;
+
+ const params = {
+ op: "rpc", method: "sanityCheck", hasAudio: hasAudio,
+ hasMp3: hasMp3,
+ clientTzOffset: clientTzOffset,
+ hasSandbox: hasSandbox
+ };
+
+ xhrPost("backend.php", params, (transport) => {
+ try {
+ App.backendSanityCallback(transport);
+ } catch (e) {
+ App.Error.report(e);
+ }
+ });
});
},
checkBrowserFeatures: function() {
@@ -204,8 +206,8 @@ require(["dojo/_base/kernel",
if (event.target.nodeName == "INPUT" || event.target.nodeName == "TEXTAREA") return;
// Arrow buttons and escape are not reported via keypress, handle them via keydown.
- // escape = 27, left = 37, up = 38, right = 39, down = 40
- if (event.type == "keydown" && event.which != 27 && (event.which < 37 || event.which > 40)) return;
+ // escape = 27, left = 37, up = 38, right = 39, down = 40, pgup = 33, pgdn = 34
+ if (event.type == "keydown" && event.which != 27 && (event.which < 33 || event.which > 40)) return;
const action_name = App.keyeventToAction(event);
@@ -213,7 +215,7 @@ require(["dojo/_base/kernel",
const action_func = this.hotkey_actions[action_name];
if (action_func != null) {
- action_func();
+ action_func(event);
event.stopPropagation();
return false;
}
@@ -277,23 +279,23 @@ require(["dojo/_base/kernel",
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["next_article_or_scroll"] = function (event) {
+ Headlines.move('next', {event: event});
};
- this.hotkey_actions["prev_article"] = function () {
- Headlines.move('prev');
+ this.hotkey_actions["prev_article_or_scroll"] = function (event) {
+ Headlines.move('prev', {event: event});
};
- this.hotkey_actions["next_article_noscroll"] = function () {
- Headlines.move('next', true);
+ this.hotkey_actions["next_article_noscroll"] = function (event) {
+ Headlines.move('next', {noscroll: true, event: event});
};
- this.hotkey_actions["prev_article_noscroll"] = function () {
- Headlines.move('prev', true);
+ this.hotkey_actions["prev_article_noscroll"] = function (event) {
+ Headlines.move('prev', {noscroll: true, event: event});
};
- this.hotkey_actions["next_article_noexpand"] = function () {
- Headlines.move('next', true, true);
+ this.hotkey_actions["next_article_noexpand"] = function (event) {
+ Headlines.move('next', {noscroll: true, noexpand: true, event: event});
};
- this.hotkey_actions["prev_article_noexpand"] = function () {
- Headlines.move('prev', true, true);
+ this.hotkey_actions["prev_article_noexpand"] = function (event) {
+ Headlines.move('prev', {noscroll: true, noexpand: true, event: event});
};
this.hotkey_actions["search_dialog"] = function () {
Feeds.search();
@@ -324,11 +326,29 @@ require(["dojo/_base/kernel",
this.hotkey_actions["catchup_above"] = function () {
Headlines.catchupRelativeTo(0);
};
- this.hotkey_actions["article_scroll_down"] = function () {
- Article.scroll(40);
+ this.hotkey_actions["article_scroll_down"] = function (event) {
+ const ctr = App.isCombinedMode() ? $("headlines-frame") : $("content-insert");
+
+ if (ctr)
+ Article.scroll(ctr.offsetHeight / 2, event);
+ };
+ this.hotkey_actions["article_scroll_up"] = function (event) {
+ const ctr = App.isCombinedMode() ? $("headlines-frame") : $("content-insert");
+
+ if (ctr)
+ Article.scroll(-ctr.offsetHeight / 2, event);
+ };
+ this.hotkey_actions["next_article_page"] = function (event) {
+ Headlines.scrollByPages(1, event);
};
- this.hotkey_actions["article_scroll_up"] = function () {
- Article.scroll(-40);
+ this.hotkey_actions["prev_article_page"] = function (event) {
+ Headlines.scrollByPages(-1, event);
+ };
+ this.hotkey_actions["article_page_down"] = function (event) {
+ Article.scrollByPages(1, event);
+ };
+ this.hotkey_actions["article_page_up"] = function (event) {
+ Article.scrollByPages(-1, event);
};
this.hotkey_actions["close_article"] = function () {
if (App.isCombinedMode()) {
@@ -363,7 +383,7 @@ require(["dojo/_base/kernel",
Headlines.select('none');
};
this.hotkey_actions["feed_refresh"] = function () {
- if (Feeds.getActive() != undefined) {
+ if (typeof Feeds.getActive() != "undefined") {
Feeds.open({feed: Feeds.getActive(), is_cat: Feeds.activeIsCat()});
}
};
@@ -393,7 +413,7 @@ require(["dojo/_base/kernel",
CommonDialogs.editFeed(Feeds.getActive());
};
this.hotkey_actions["feed_catchup"] = function () {
- if (Feeds.getActive() != undefined) {
+ if (typeof Feeds.getActive() != "undefined") {
Feeds.catchupCurrent();
}
};
@@ -495,9 +515,6 @@ require(["dojo/_base/kernel",
Headlines.renderAgain();
});
};
- this.hotkey_actions["toggle_night_mode"] = function () {
- App.toggleNightMode();
- };
},
onActionSelected: function(opid) {
switch (opid) {
@@ -563,9 +580,6 @@ require(["dojo/_base/kernel",
alert(__("Widescreen is not available in combined mode."));
}
break;
- case "qmcToggleNightMode":
- App.toggleNightMode();
- break;
case "qmcHKhelp":
App.helpDialog("main");
break;
@@ -580,7 +594,10 @@ require(["dojo/_base/kernel",
App = new _App();
} catch (e) {
- App.Error.report(e);
+ if (App && App.Error)
+ App.Error.report(e);
+ else
+ alert(e + "\n\n" + e.stack);
}
});
});