summaryrefslogtreecommitdiff
path: root/js/Headlines.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/Headlines.js')
-rwxr-xr-xjs/Headlines.js327
1 files changed, 128 insertions, 199 deletions
diff --git a/js/Headlines.js b/js/Headlines.js
index 540c400d3..8425dc980 100755
--- a/js/Headlines.js
+++ b/js/Headlines.js
@@ -8,6 +8,36 @@ define(["dojo/_base/declare"], function (declare) {
headlines: [],
current_first_id: 0,
_scroll_reset_timeout: false,
+ default_force_previous: false,
+ default_force_to_top: false,
+ line_scroll_offset: 120, /* px */
+ sticky_header_observer: new IntersectionObserver(
+ (entries, observer) => {
+ entries.forEach((entry) => {
+ const header = entry.target.nextElementSibling;
+
+ if (entry.intersectionRatio == 0) {
+ header.setAttribute("stuck", "1");
+
+ } else if (entry.intersectionRatio == 1) {
+ header.removeAttribute("stuck");
+ }
+
+ //console.log(entry.target, header, entry.intersectionRatio);
+
+ });
+ },
+ {threshold: [0, 1], root: document.querySelector("#headlines-frame")}
+ ),
+ unpack_observer: new IntersectionObserver(
+ (entries, observer) => {
+ entries.forEach((entry) => {
+ if (entry.intersectionRatio > 0)
+ Article.unpack(entry.target);
+ });
+ },
+ {threshold: [0], root: document.querySelector("#headlines-frame")}
+ ),
row_observer: new MutationObserver((mutations) => {
const modified = [];
@@ -40,7 +70,6 @@ define(["dojo/_base/declare"], function (declare) {
});
Headlines.updateSelectedPrompt();
- Headlines.updateFloatingTitle(true);
if ('requestIdleCallback' in window)
window.requestIdleCallback(() => {
@@ -49,7 +78,7 @@ define(["dojo/_base/declare"], function (declare) {
else
Headlines.syncModified(modified);
}),
- syncModified: function(modified) {
+ syncModified: function (modified) {
const ops = {
tmark: [],
tpub: [],
@@ -62,7 +91,7 @@ define(["dojo/_base/declare"], function (declare) {
rescore: {},
};
- modified.each(function(m) {
+ modified.each(function (m) {
if (m.old.marked != m.new.marked)
ops.tmark.push(m.id);
@@ -118,26 +147,26 @@ define(["dojo/_base/declare"], function (declare) {
if (ops.tmark.length != 0)
promises.push(xhrPost("backend.php",
- { op: "rpc", method: "markSelected", ids: ops.tmark.toString(), cmode: 2}));
+ {op: "rpc", method: "markSelected", ids: ops.tmark.toString(), cmode: 2}));
if (ops.tpub.length != 0)
promises.push(xhrPost("backend.php",
- { op: "rpc", method: "publishSelected", ids: ops.tpub.toString(), cmode: 2}));
+ {op: "rpc", method: "publishSelected", ids: ops.tpub.toString(), cmode: 2}));
if (ops.read.length != 0)
promises.push(xhrPost("backend.php",
- { op: "rpc", method: "catchupSelected", ids: ops.read.toString(), cmode: 0}));
+ {op: "rpc", method: "catchupSelected", ids: ops.read.toString(), cmode: 0}));
if (ops.unread.length != 0)
promises.push(xhrPost("backend.php",
- { op: "rpc", method: "catchupSelected", ids: ops.unread.toString(), cmode: 1}));
+ {op: "rpc", method: "catchupSelected", ids: ops.unread.toString(), cmode: 1}));
const scores = Object.keys(ops.rescore);
if (scores.length != 0) {
scores.each((score) => {
promises.push(xhrPost("backend.php",
- { op: "article", method: "setScore", id: ops.rescore[score].toString(), score: score }));
+ {op: "article", method: "setScore", id: ops.rescore[score].toString(), score: score}));
});
}
@@ -177,15 +206,24 @@ define(["dojo/_base/declare"], function (declare) {
} else if (Article.getActive() != id) {
Headlines.select('none');
+
+ const scroll_position_A = $("RROW-" + id).offsetTop - $("headlines-frame").scrollTop;
+
Article.setActive(id);
if (App.getInitParam("cdm_expanded")) {
+
if (!in_body)
Article.openInNewWindow(id);
Headlines.toggleUnread(id, 0);
} else {
- Article.cdmScrollToId(id);
+ const scroll_position_B = $("RROW-" + id).offsetTop - $("headlines-frame").scrollTop;
+
+ // this would only work if there's enough space
+ $("headlines-frame").scrollTop -= scroll_position_A-scroll_position_B;
+
+ Article.cdmMoveToId(id);
}
} else if (in_body) {
@@ -245,36 +283,22 @@ define(["dojo/_base/declare"], function (declare) {
Feeds.open({feed: Feeds.getActive(), is_cat: Feeds.activeIsCat(), offset: offset, append: true});
},
- isChildVisible: function (elem, ctr) {
- const ctop = ctr.scrollTop;
- const cbottom = ctop + ctr.offsetHeight;
-
- const etop = elem.offsetTop;
- const ebottom = etop + elem.offsetHeight;
-
- return etop >= ctop && ebottom <= cbottom ||
- etop < ctop && ebottom > ctop || ebottom > cbottom && etop < cbottom
-
+ isChildVisible: function (elem) {
+ return App.Scrollable.isChildVisible(elem, $("headlines-frame"));
},
- firstVisible: function() {
+ firstVisible: function () {
const rows = $$("#headlines-frame > div[id*=RROW]");
- const ctr = $("headlines-frame");
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
- if (this.isChildVisible(row, ctr)) {
+ if (this.isChildVisible(row)) {
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");
const container = $("headlines-frame");
@@ -311,82 +335,25 @@ define(["dojo/_base/declare"], function (declare) {
console.warn("scrollHandler", e);
}
},
- updateFloatingTitle: function (status_only) {
- if (!App.isCombinedMode()/* || !App.getInitParam("cdm_expanded")*/) return;
-
- const safety_offset = 120; /* px, needed for firefox */
- 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 + safety_offset) {
-
- const header = row.select(".header")[0];
- const id = row.getAttribute("data-article-id");
-
- if (status_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.select(".dijitCheckBox")[0].outerHTML = "<i class=\"material-icons icon-anchor\" onclick=\"Article.cdmScrollToId(" + id + ", true)\">expand_more</i>";
-
- this.initFloatingMenu();
-
- }
-
- if (row.hasClassName("Unread"))
- ft.addClassName("Unread");
- else
- ft.removeClassName("Unread");
-
- if (row.hasClassName("marked"))
- ft.addClassName("marked");
- else
- ft.removeClassName("marked");
-
- if (row.hasClassName("published"))
- ft.addClassName("published");
- else
- ft.removeClassName("published");
-
- PluginHost.run(PluginHost.HOOK_FLOATING_TITLE, row);
- }
-
- if (hf.scrollTop - row.offsetTop <= header.offsetHeight + safety_offset)
- ft.fade({duration: 0.2});
- else
- ft.appear({duration: 0.2});
-
- return;
- }
- }
+ objectById: function (id) {
+ return this.headlines[id];
},
- unpackVisible: function () {
- if (!App.isCombinedMode() || !App.getInitParam("cdm_expanded")) return;
+ setCommonClasses: function () {
+ $("headlines-frame").removeClassName("cdm");
+ $("headlines-frame").removeClassName("normal");
- const rows = $$("#headlines-frame div[id*=RROW][data-content]");
- const threshold = $("headlines-frame").scrollTop + $("headlines-frame").offsetHeight + 600;
+ $("headlines-frame").addClassName(App.isCombinedMode() ? "cdm" : "normal");
- for (let i = 0; i < rows.length; i++) {
- const row = rows[i];
+ // for floating title because it's placed outside of headlines-frame
+ $("main").removeClassName("expandable");
+ $("main").removeClassName("expanded");
- if (row.offsetTop <= threshold) {
- Article.unpack(row);
- } else {
- break;
- }
- }
- },
- objectById: function (id){
- return this.headlines[id];
+ if (App.isCombinedMode())
+ $("main").addClassName(App.getInitParam("cdm_expanded") ? " expanded" : " expandable");
},
- renderAgain: function() {
+ renderAgain: function () {
// TODO: wrap headline elements into a knockoutjs model to prevent all this stuff
+ Headlines.setCommonClasses();
$$("#headlines-frame > div[id*=RROW]").each((row) => {
const id = row.getAttribute("data-article-id");
@@ -399,19 +366,27 @@ define(["dojo/_base/declare"], function (declare) {
if (hl.active) {
new_row.addClassName("active");
+ Article.unpack(new_row);
if (App.isCombinedMode())
- Article.cdmScrollToId(id);
+ Article.cdmMoveToId(id, {noscroll: true});
else
Article.view(id);
}
if (hl.selected) this.select("all", id);
-
- Article.unpack(new_row);
-
}
});
+
+ $$(".cdm .header-sticky-guard").each((e) => {
+ this.sticky_header_observer.observe(e)
+ });
+
+ if (App.getInitParam("cdm_expanded"))
+ $$("#headlines-frame > div[id*=RROW].cdm").each((e) => {
+ this.unpack_observer.observe(e)
+ });
+
},
render: function (headlines, hl) {
let row = null;
@@ -453,7 +428,7 @@ define(["dojo/_base/declare"], function (declare) {
data-article-title="${escapeHtml(hl.title)}"
onmouseover="Article.mouseIn(${hl.id})"
onmouseout="Article.mouseOut(${hl.id})">
-
+ <div class="header-sticky-guard"></div>
<div class="header">
<div class="left">
<input dojoType="dijit.form.CheckBox" type="checkbox" onclick="Headlines.onRowChecked(this)" class='rchk'>
@@ -558,7 +533,7 @@ define(["dojo/_base/declare"], function (declare) {
return tmp.firstChild;
},
- updateCurrentUnread: function() {
+ updateCurrentUnread: function () {
if ($("feed_current_unread")) {
const feed_unread = Feeds.getUnread(Feeds.getActive(), Feeds.activeIsCat());
@@ -598,34 +573,18 @@ define(["dojo/_base/declare"], function (declare) {
Feeds.infscroll_disabled = parseInt(headlines_count) != 30;
console.log('infscroll_disabled=', Feeds.infscroll_disabled);
- // TODO: the below needs to be applied again when switching expanded/expandable on the fly
- // via hotkeys, not just on feed load
-
- $("headlines-frame").removeClassName("cdm");
- $("headlines-frame").removeClassName("normal");
-
- $("headlines-frame").addClassName(App.isCombinedMode() ? "cdm" : "normal");
+ // also called in renderAgain() after view mode switch
+ Headlines.setCommonClasses();
$("headlines-frame").setAttribute("is-vfeed",
reply['headlines']['is_vfeed'] ? 1 : 0);
- // for floating title because it's placed outside of headlines-frame
- $("main").removeClassName("expandable");
- $("main").removeClassName("expanded");
-
- if (App.isCombinedMode())
- $("main").addClassName(App.getInitParam("cdm_expanded") ? " expanded" : " expandable");
-
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);
- $("floatingTitle").innerHTML = "";
} catch (e) {
console.warn(e);
}
@@ -736,6 +695,15 @@ define(["dojo/_base/declare"], function (declare) {
}
}
+ $$(".cdm .header-sticky-guard").each((e) => {
+ this.sticky_header_observer.observe(e)
+ });
+
+ if (App.getInitParam("cdm_expanded"))
+ $$("#headlines-frame > div[id*=RROW].cdm").each((e) => {
+ this.unpack_observer.observe(e)
+ });
+
} else {
console.error("Invalid object received: " + transport.responseText);
dijit.byId("headlines-frame").attr('content', "<div class='whiteBox'>" +
@@ -837,33 +805,30 @@ define(["dojo/_base/declare"], function (declare) {
move: function (mode, params) {
params = params || {};
- const noscroll = params.noscroll || false;
- const noexpand = params.noexpand || false;
- const event = params.event;
-
- const rows = Headlines.getLoaded();
+ const no_expand = params.no_expand || false;
+ const force_previous = params.force_previous || this.default_force_previous;
+ const force_to_top = params.force_to_top || this.default_force_to_top;
let prev_id = false;
let next_id = false;
+ let current_id = Article.getActive();
- const active_row = $("RROW-" + Article.getActive());
-
- if (!active_row) {
- Article.setActive(0);
- }
-
- if (!Article.getActive() || (active_row && !Headlines.isChildVisible(active_row, $("headlines-frame")))) {
- next_id = Headlines.firstVisible();
- prev_id = next_id;
+ if (!Headlines.isChildVisible($("RROW-" + current_id))) {
+ console.log('active article is obscured, resetting to first visible...');
+ current_id = Headlines.firstVisible();
+ prev_id = current_id;
+ next_id = current_id;
} else {
+ const rows = Headlines.getLoaded();
+
for (let i = 0; i < rows.length; i++) {
- if (rows[i] == Article.getActive()) {
+ if (rows[i] == current_id) {
// Account for adjacent identical article ids.
if (i > 0) prev_id = rows[i - 1];
for (let j = i + 1; j < rows.length; j++) {
- if (rows[j] != Article.getActive()) {
+ if (rows[j] != current_id) {
next_id = rows[j];
break;
}
@@ -873,51 +838,39 @@ define(["dojo/_base/declare"], function (declare) {
}
}
- console.log("cur: " + Article.getActive() + " next: " + next_id);
+ console.log("cur: " + current_id + " next: " + next_id + " prev:" + prev_id);
if (mode === "next") {
- if (next_id || Article.getActive()) {
+ if (next_id) {
if (App.isCombinedMode()) {
-
- //const row = $("RROW-" + Article.getActive());
- const ctr = $("headlines-frame");
-
- if (!noscroll) {
- Article.scroll(ctr.offsetHeight / 2, event);
- } else if (next_id) {
+ window.requestAnimationFrame(() => {
Article.setActive(next_id);
- Article.cdmScrollToId(next_id, true, event);
- }
-
- } else if (next_id) {
- Headlines.correctHeadlinesOffset(next_id);
- Article.view(next_id, noexpand);
+ Article.cdmMoveToId(next_id, {force_to_top: force_to_top});
+ });
+ } else {
+ Article.view(next_id, no_expand);
}
}
- }
-
- if (mode === "prev") {
- if (prev_id || Article.getActive()) {
+ } else if (mode === "prev") {
+ if (prev_id || current_id) {
if (App.isCombinedMode()) {
+ window.requestAnimationFrame(() => {
+ const row = $("RROW-" + current_id);
+ const ctr = $("headlines-frame");
+ const delta_px = Math.round(row.offsetTop) - Math.round(ctr.scrollTop);
- const row = $("RROW-" + Article.getActive());
- //const prev_row = $("RROW-" + prev_id);
- const ctr = $("headlines-frame");
+ console.log('moving back, delta_px', delta_px);
- if (!noscroll) {
- Article.scroll(-ctr.offsetHeight / 2, event);
- } else {
- if (row && Math.round(row.offsetTop) < Math.round(ctr.scrollTop)) {
- Article.cdmScrollToId(Article.getActive(), noscroll, event);
+ if (!force_previous && row && delta_px < -8) {
+ Article.setActive(current_id);
+ Article.cdmMoveToId(current_id, {force_to_top: force_to_top});
} else if (prev_id) {
Article.setActive(prev_id);
- Article.cdmScrollToId(prev_id, noscroll, event);
+ Article.cdmMoveToId(prev_id, {force_to_top: force_to_top});
}
- }
-
+ });
} else if (prev_id) {
- Headlines.correctHeadlinesOffset(prev_id);
- Article.view(prev_id, noexpand);
+ Article.view(prev_id, no_expand);
}
}
}
@@ -1254,7 +1207,7 @@ define(["dojo/_base/declare"], function (declare) {
eval(elem.value);
elem.attr('value', 'false');
},
- correctHeadlinesOffset: function (id) {
+ scrollToArticleId: function (id) {
const container = $("headlines-frame");
const row = $("RROW-" + id);
@@ -1274,20 +1227,6 @@ define(["dojo/_base/declare"], function (declare) {
container.scrollTop = row.offsetTop + row.offsetHeight - viewport;
}
},
- initFloatingMenu: function () {
- if (!dijit.byId("floatingMenu")) {
-
- const menu = new dijit.Menu({
- id: "floatingMenu",
- selector: ".hlMenuAttach",
- targetNodeIds: ["floatingTitle"]
- });
-
- this.headlinesMenuCommon(menu);
-
- menu.startup();
- }
- },
headlinesMenuCommon: function (menu) {
menu.addChild(new dijit.MenuItem({
@@ -1416,21 +1355,11 @@ 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;
+ scrollByPages: function (page_offset) {
+ App.Scrollable.scrollByPages($("headlines-frame"), page_offset);
+ },
+ scroll: function (offset) {
+ App.Scrollable.scroll($("headlines-frame"), offset);
},
initHeadlinesMenu: function () {
if (!dijit.byId("headlinesMenu")) {