diff options
Diffstat (limited to 'js')
-rw-r--r-- | js/AppBase.js | 69 | ||||
-rw-r--r-- | js/Article.js | 57 | ||||
-rw-r--r-- | js/CommonDialogs.js | 2 | ||||
-rw-r--r-- | js/Feeds.js | 20 | ||||
-rwxr-xr-x | js/Headlines.js | 147 | ||||
-rw-r--r-- | js/PrefHelpers.js | 44 | ||||
-rwxr-xr-x | js/prefs.js | 29 | ||||
-rw-r--r-- | js/tt-rss.js | 127 |
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); } }); }); |