3 let _active_article_id
= 0;
5 let vgroup_last_feed
= false;
6 let post_under_pointer
= false;
8 let last_requested_article
= 0;
10 let catchup_id_batch
= [];
11 let catchup_timeout_id
= false;
13 let cids_requested
= [];
14 let loaded_article_ids
= [];
15 let _last_headlines_update
= 0;
16 let _headlines_scroll_offset
= 0;
17 let current_first_id
= 0;
18 let last_search_query
;
20 let _catchup_request_sent
= false;
22 let has_storage
= 'sessionStorage' in window
&& window
['sessionStorage'] !== null;
24 function headlines_callback2(transport
, offset
, background
, infscroll_req
) {
25 const reply
= handle_rpc_json(transport
);
27 console
.log("headlines_callback2 [offset=" + offset
+ "] B:" + background
+ " I:" + infscroll_req
);
37 is_cat
= reply
['headlines']['is_cat'];
38 feed_id
= reply
['headlines']['id'];
39 last_search_query
= reply
['headlines']['search_query'];
41 console
.log(feed_id
, getActiveFeedId(), is_cat
, activeFeedIsCat());
43 if (feed_id
!= -7 && (feed_id
!= getActiveFeedId() || is_cat
!= activeFeedIsCat()))
47 if (infscroll_req
== false) {
48 $("headlines-frame").scrollTop
= 0;
50 $("floatingTitle").style
.visibility
= "hidden";
51 $("floatingTitle").setAttribute("data-article-id", 0);
52 $("floatingTitle").innerHTML
= "";
56 $("headlines-frame").removeClassName("cdm");
57 $("headlines-frame").removeClassName("normal");
59 $("headlines-frame").addClassName(isCdmMode() ? "cdm" : "normal");
61 const headlines_count
= reply
['headlines-info']['count'];
63 vgroup_last_feed
= reply
['headlines-info']['vgroup_last_feed'];
65 if (parseInt(headlines_count
) < 30) {
66 _infscroll_disable
= 1;
68 _infscroll_disable
= 0;
71 current_first_id
= reply
['headlines']['first_id'];
72 const counters
= reply
['counters'];
73 const articles
= reply
['articles'];
75 if (infscroll_req
== false) {
76 loaded_article_ids
= [];
78 dojo
.html
.set($("headlines-toolbar"),
79 reply
['headlines']['toolbar'],
80 {parseContent
: true});
82 $("headlines-frame").innerHTML
= '';
84 let tmp
= document
.createElement("div");
85 tmp
.innerHTML
= reply
['headlines']['content'];
86 dojo
.parser
.parse(tmp
);
88 while (tmp
.hasChildNodes()) {
89 var row
= tmp
.removeChild(tmp
.firstChild
);
91 if (loaded_article_ids
.indexOf(row
.id
) == -1 || row
.hasClassName("cdmFeedTitle")) {
92 dijit
.byId("headlines-frame").domNode
.appendChild(row
);
94 loaded_article_ids
.push(row
.id
);
98 let hsp
= $("headlines-spacer");
99 if (!hsp
) hsp
= new Element("DIV", {"id": "headlines-spacer"});
100 dijit
.byId('headlines-frame').domNode
.appendChild(hsp
);
104 if (_infscroll_disable
)
105 hsp
.innerHTML
= "<a href='#' onclick='openNextUnreadFeed()'>" +
106 __("Click to open next unread feed.") + "</a>";
109 $("feed_title").innerHTML
+= "<span id='cancel_search'>" +
110 " (<a href='#' onclick='cancelSearch()'>" + __("Cancel search") + "</a>)" +
114 } else if (headlines_count
> 0 && feed_id
== getActiveFeedId() && is_cat
== activeFeedIsCat()) {
115 console
.log("adding some more headlines: " + headlines_count
);
117 const c
= dijit
.byId("headlines-frame");
118 const ids
= getSelectedArticleIds2();
120 let hsp
= $("headlines-spacer");
123 c
.domNode
.removeChild(hsp
);
125 let tmp
= document
.createElement("div");
126 tmp
.innerHTML
= reply
['headlines']['content'];
127 dojo
.parser
.parse(tmp
);
129 while (tmp
.hasChildNodes()) {
130 let row
= tmp
.removeChild(tmp
.firstChild
);
132 if (loaded_article_ids
.indexOf(row
.id
) == -1 || row
.hasClassName("cdmFeedTitle")) {
133 dijit
.byId("headlines-frame").domNode
.appendChild(row
);
135 loaded_article_ids
.push(row
.id
);
139 if (!hsp
) hsp
= new Element("DIV", {"id": "headlines-spacer"});
140 c
.domNode
.appendChild(hsp
);
142 if (headlines_count
< 30) _infscroll_disable
= true;
144 console
.log("restore selected ids: " + ids
);
146 for (let i
= 0; i
< ids
.length
; i
++) {
147 markHeadline(ids
[i
]);
152 if (_infscroll_disable
) {
153 hsp
.innerHTML
= "<a href='#' onclick='openNextUnreadFeed()'>" +
154 __("Click to open next unread feed.") + "</a>";
158 console
.log("no new headlines received");
160 const first_id_changed
= reply
['headlines']['first_id_changed'];
161 console
.log("first id changed:" + first_id_changed
);
163 let hsp
= $("headlines-spacer");
166 if (first_id_changed
) {
167 hsp
.innerHTML
= "<a href='#' onclick='viewCurrentFeed()'>" +
168 __("New articles found, reload feed to continue.") + "</a>";
170 hsp
.innerHTML
= "<a href='#' onclick='openNextUnreadFeed()'>" +
171 __("Click to open next unread feed.") + "</a>";
179 for (let i
= 0; i
< articles
.length
; i
++) {
180 const a_id
= articles
[i
]['id'];
181 cache_set("article:" + a_id
, articles
[i
]['content']);
184 console
.log("no cached articles received");
188 parse_counters(counters
);
193 console
.error("Invalid object received: " + transport
.responseText
);
194 dijit
.byId("headlines-frame").attr('content', "<div class='whiteBox'>" +
195 __('Could not update headlines (invalid object received - see error console for details)') +
199 _infscroll_request_sent
= 0;
200 _last_headlines_update
= new Date().getTime();
202 unpackVisibleHeadlines();
204 // if we have some more space in the buffer, why not try to fill it
206 if (!_infscroll_disable
&& $("headlines-spacer") &&
207 $("headlines-spacer").offsetTop
< $("headlines-frame").offsetHeight
) {
209 window
.setTimeout(function() {
217 function render_article(article
) {
218 cleanup_memory("content-insert");
220 dijit
.byId("headlines-wrap-inner").addChild(
221 dijit
.byId("content-insert"));
223 const c
= dijit
.byId("content-insert");
226 c
.domNode
.scrollTop
= 0;
229 c
.attr('content', article
);
230 PluginHost
.run(PluginHost
.HOOK_ARTICLE_RENDERED
, c
.domNode
);
232 correctHeadlinesOffset(getActiveArticleId());
239 function showArticleInHeadlines(id
, noexpand
) {
240 const row
= $("RROW-" + id
);
244 row
.removeClassName("Unread");
246 row
.addClassName("active");
248 selectArticles('none');
253 function article_callback2(transport
, id
) {
254 console
.log("article_callback2 " + id
);
256 handle_rpc_json(transport
);
261 reply
= JSON
.parse(transport
.responseText
);
268 reply
.each(function(article
) {
269 if (getActiveArticleId() == article
['id']) {
270 render_article(article
['content']);
272 cids_requested
.remove(article
['id']);
274 cache_set("article:" + article
['id'], article
['content']);
277 // if (id != last_requested_article) {
278 // console.log("requested article id is out of sequence, aborting");
283 console
.error("Invalid object received: " + transport
.responseText
);
285 render_article("<div class='whiteBox'>" +
286 __('Could not display article (invalid object received - see error console for details)') + "</div>");
289 const unread_in_buffer
= $$("#headlines-frame > div[id*=RROW][class*=Unread]").length
290 request_counters(unread_in_buffer
== 0);
295 function view(id
, activefeed
, noexpand
) {
296 const oldrow
= $("RROW-" + getActiveArticleId());
297 if (oldrow
) oldrow
.removeClassName("active");
299 const crow
= $("RROW-" + id
);
303 setActiveArticleId(id
);
304 showArticleInHeadlines(id
, noexpand
);
308 console
.log("loading article: " + id
);
310 const cached_article
= cache_get("article:" + id
);
312 console
.log("cache check result: " + (cached_article
!= false));
314 let query
= "?op=article&method=view&id=" + param_escape(id
);
316 const neighbor_ids
= getRelativePostIds(id
);
318 /* only request uncached articles */
320 const cids_to_request
= [];
322 for (let i
= 0; i
< neighbor_ids
.length
; i
++) {
323 if (cids_requested
.indexOf(neighbor_ids
[i
]) == -1)
324 if (!cache_get("article:" + neighbor_ids
[i
])) {
325 cids_to_request
.push(neighbor_ids
[i
]);
326 cids_requested
.push(neighbor_ids
[i
]);
330 console
.log("additional ids: " + cids_to_request
.toString());
332 query
= query
+ "&cids=" + cids_to_request
.toString();
334 const article_is_unread
= crow
.hasClassName("Unread");
336 setActiveArticleId(id
);
337 showArticleInHeadlines(id
);
339 if (cached_article
&& article_is_unread
) {
341 query
= query
+ "&mode=prefetch";
343 render_article(cached_article
);
345 } else if (cached_article
) {
347 query
= query
+ "&mode=prefetch_old";
348 render_article(cached_article
);
350 // if we don't need to request any relative ids, we might as well skip
351 // the server roundtrip altogether
352 if (cids_to_request
.length
== 0) {
357 last_requested_article
= id
;
361 if (article_is_unread
) {
362 decrementFeedCounter(getActiveFeedId(), activeFeedIsCat());
365 new Ajax
.Request("backend.php", {
367 onComplete: function(transport
) {
368 article_callback2(transport
, id
);
375 function toggleMark(id
, client_only
) {
376 let query
= "?op=rpc&id=" + id
+ "&method=mark";
378 const row
= $("RROW-" + id
);
383 const row_imgs
= row
.getElementsByClassName("markedPic");
385 for (var i
= 0; i
< row_imgs
.length
; i
++)
386 imgs
.push(row_imgs
[i
]);
388 const ft
= $("floatingTitle");
390 if (ft
&& ft
.getAttribute("data-article-id") == id
) {
391 const fte
= ft
.getElementsByClassName("markedPic");
393 for (var i
= 0; i
< fte
.length
; i
++)
397 for (i
= 0; i
< imgs
.length
; i
++) {
400 if (!row
.hasClassName("marked")) {
401 img
.src
= img
.src
.replace("mark_unset", "mark_set");
402 query
= query
+ "&mark=1";
404 img
.src
= img
.src
.replace("mark_set", "mark_unset");
405 query
= query
+ "&mark=0";
409 row
.toggleClassName("marked");
412 new Ajax
.Request("backend.php", {
414 onComplete: function (transport
) {
415 handle_rpc_json(transport
);
421 function togglePub(id
, client_only
, no_effects
, note
) {
422 let query
= "?op=rpc&id=" + id
+ "&method=publ";
424 if (note
!= undefined) {
425 query
= query
+ "¬e=" + param_escape(note
);
427 query
= query
+ "¬e=undefined";
430 const row
= $("RROW-" + id
);
435 const row_imgs
= row
.getElementsByClassName("pubPic");
437 for (var i
= 0; i
< row_imgs
.length
; i
++)
438 imgs
.push(row_imgs
[i
]);
440 const ft
= $("floatingTitle");
442 if (ft
&& ft
.getAttribute("data-article-id") == id
) {
443 const fte
= ft
.getElementsByClassName("pubPic");
445 for (var i
= 0; i
< fte
.length
; i
++)
449 for (var i
= 0; i
< imgs
.length
; i
++) {
452 if (!row
.hasClassName("published") || note
!= undefined) {
453 img
.src
= img
.src
.replace("pub_unset", "pub_set");
454 query
= query
+ "&pub=1";
456 img
.src
= img
.src
.replace("pub_set", "pub_unset");
457 query
= query
+ "&pub=0";
461 if (note
!= undefined)
462 row
.addClassName("published");
464 row
.toggleClassName("published");
467 new Ajax
.Request("backend.php", {
469 onComplete: function(transport
) {
470 handle_rpc_json(transport
);
476 function moveToPost(mode
, noscroll
, noexpand
) {
477 const rows
= getLoadedArticleIds();
482 if (!$('RROW-' + getActiveArticleId())) {
483 setActiveArticleId(0);
486 if (!getActiveArticleId()) {
488 prev_id
= rows
[rows
.length
-1]
490 for (let i
= 0; i
< rows
.length
; i
++) {
491 if (rows
[i
] == getActiveArticleId()) {
493 // Account for adjacent identical article ids.
494 if (i
> 0) prev_id
= rows
[i
-1];
496 for (let j
= i
+1; j
< rows
.length
; j
++) {
497 if (rows
[j
] != getActiveArticleId()) {
507 console
.log("cur: " + getActiveArticleId() + " next: " + next_id
);
509 if (mode
== "next") {
510 if (next_id
|| getActiveArticleId()) {
513 var article
= $("RROW-" + getActiveArticleId());
514 var ctr
= $("headlines-frame");
516 if (!noscroll
&& article
&& article
.offsetTop
+ article
.offsetHeight
>
517 ctr
.scrollTop
+ ctr
.offsetHeight
) {
519 scrollArticle(ctr
.offsetHeight
/4);
521 } else if (next_id
) {
522 cdmExpandArticle(next_id
, noexpand
);
523 cdmScrollToArticleId(next_id
, true);
526 } else if (next_id
) {
527 correctHeadlinesOffset(next_id
);
528 view(next_id
, getActiveFeedId(), noexpand
);
533 if (mode
== "prev") {
534 if (prev_id
|| getActiveArticleId()) {
537 var article
= $("RROW-" + getActiveArticleId());
538 const prev_article
= $("RROW-" + prev_id
);
539 var ctr
= $("headlines-frame");
541 if (!getInitParam("cdm_expanded")) {
543 if (!noscroll
&& article
&& article
.offsetTop
< ctr
.scrollTop
) {
544 scrollArticle(-ctr
.offsetHeight
/4);
546 cdmExpandArticle(prev_id
, noexpand
);
547 cdmScrollToArticleId(prev_id
, true);
549 } else if (!noscroll
&& article
&& article
.offsetTop
< ctr
.scrollTop
) {
550 scrollArticle(-ctr
.offsetHeight
/3);
551 } else if (!noscroll
&& prev_article
&&
552 prev_article
.offsetTop
< ctr
.scrollTop
) {
553 cdmExpandArticle(prev_id
, noexpand
);
554 scrollArticle(-ctr
.offsetHeight
/4);
555 } else if (prev_id
) {
556 cdmExpandArticle(prev_id
, noexpand
);
557 cdmScrollToArticleId(prev_id
, noscroll
);
560 } else if (prev_id
) {
561 correctHeadlinesOffset(prev_id
);
562 view(prev_id
, getActiveFeedId(), noexpand
);
569 function toggleSelected(id
, force_on
) {
570 const row
= $("RROW-" + id
);
573 const cb
= dijit
.getEnclosingWidget(
574 row
.getElementsByClassName("rchk")[0]);
576 if (row
.hasClassName('Selected') && !force_on
) {
577 row
.removeClassName('Selected');
578 if (cb
) cb
.attr("checked", false);
580 row
.addClassName('Selected');
581 if (cb
) cb
.attr("checked", true);
585 updateSelectedPrompt();
588 function updateSelectedPrompt() {
589 const count
= getSelectedArticleIds2().length
;
590 const elem
= $("selected_prompt");
593 elem
.innerHTML
= ngettext("%d article selected",
594 "%d articles selected", count
).replace("%d", count
);
604 function toggleUnread(id
, cmode
, effect
) {
605 const row
= $("RROW-" + id
);
607 const tmpClassName
= row
.className
;
609 if (cmode
== undefined || cmode
== 2) {
610 if (row
.hasClassName("Unread")) {
611 row
.removeClassName("Unread");
614 row
.addClassName("Unread");
617 } else if (cmode
== 0) {
619 row
.removeClassName("Unread");
621 } else if (cmode
== 1) {
622 row
.addClassName("Unread");
625 if (cmode
== undefined) cmode
= 2;
627 const query
= "?op=rpc&method=catchupSelected" +
628 "&cmode=" + param_escape(cmode
) + "&ids=" + param_escape(id
);
630 // notify_progress("Loading, please wait...");
632 if (tmpClassName
!= row
.className
) {
633 new Ajax
.Request("backend.php", {
635 onComplete: function (transport
) {
636 handle_rpc_json(transport
);
644 function selectionRemoveLabel(id
, ids
) {
645 if (!ids
) ids
= getSelectedArticleIds2();
647 if (ids
.length
== 0) {
648 alert(__("No articles are selected."));
652 const query
= "?op=article&method=removeFromLabel&ids=" +
653 param_escape(ids
.toString()) + "&lid=" + param_escape(id
);
657 new Ajax
.Request("backend.php", {
659 onComplete: function(transport
) {
660 handle_rpc_json(transport
);
661 show_labels_in_headlines(transport
);
666 function selectionAssignLabel(id
, ids
) {
667 if (!ids
) ids
= getSelectedArticleIds2();
669 if (ids
.length
== 0) {
670 alert(__("No articles are selected."));
674 const query
= "?op=article&method=assignToLabel&ids=" +
675 param_escape(ids
.toString()) + "&lid=" + param_escape(id
);
679 new Ajax
.Request("backend.php", {
681 onComplete: function(transport
) {
682 handle_rpc_json(transport
);
683 show_labels_in_headlines(transport
);
687 function selectionToggleUnread(set_state
, callback
, no_error
, ids
) {
688 const rows
= ids
? ids
: getSelectedArticleIds2();
690 if (rows
.length
== 0 && !no_error
) {
691 alert(__("No articles are selected."));
695 for (let i
= 0; i
< rows
.length
; i
++) {
696 const row
= $("RROW-" + rows
[i
]);
698 if (set_state
== undefined) {
699 if (row
.hasClassName("Unread")) {
700 row
.removeClassName("Unread");
702 row
.addClassName("Unread");
706 if (set_state
== false) {
707 row
.removeClassName("Unread");
710 if (set_state
== true) {
711 row
.addClassName("Unread");
716 updateFloatingTitle(true);
718 if (rows
.length
> 0) {
722 if (set_state
== undefined) {
724 } else if (set_state
== true) {
726 } else if (set_state
== false) {
730 const query
= "?op=rpc&method=catchupSelected" +
731 "&cmode=" + cmode
+ "&ids=" + param_escape(rows
.toString());
733 notify_progress("Loading, please wait...");
735 new Ajax
.Request("backend.php", {
737 onComplete: function(transport
) {
738 handle_rpc_json(transport
);
739 if (callback
) callback(transport
);
746 function selectionToggleMarked(sel_state
, callback
, no_error
, ids
) {
747 const rows
= ids
? ids
: getSelectedArticleIds2();
749 if (rows
.length
== 0 && !no_error
) {
750 alert(__("No articles are selected."));
754 for (let i
= 0; i
< rows
.length
; i
++) {
755 toggleMark(rows
[i
], true, true);
758 if (rows
.length
> 0) {
760 const query
= "?op=rpc&method=markSelected&ids=" +
761 param_escape(rows
.toString()) + "&cmode=2";
763 new Ajax
.Request("backend.php", {
765 onComplete: function(transport
) {
766 handle_rpc_json(transport
);
767 if (callback
) callback(transport
);
774 function selectionTogglePublished(sel_state
, callback
, no_error
, ids
) {
775 const rows
= ids
? ids
: getSelectedArticleIds2();
777 if (rows
.length
== 0 && !no_error
) {
778 alert(__("No articles are selected."));
782 for (let i
= 0; i
< rows
.length
; i
++) {
783 togglePub(rows
[i
], true, true);
786 if (rows
.length
> 0) {
788 const query
= "?op=rpc&method=publishSelected&ids=" +
789 param_escape(rows
.toString()) + "&cmode=2";
791 new Ajax
.Request("backend.php", {
793 onComplete: function(transport
) {
794 handle_rpc_json(transport
);
800 function getSelectedArticleIds2() {
804 $$("#headlines-frame > div[id*=RROW][class*=Selected]").each(
806 rv
.push(child
.getAttribute("data-article-id"));
812 function getLoadedArticleIds() {
815 const children
= $$("#headlines-frame > div[id*=RROW-]");
817 children
.each(function(child
) {
818 if (Element
.visible(child
)) {
819 rv
.push(child
.getAttribute("data-article-id"));
827 // mode = all,none,unread,invert,marked,published
828 function selectArticles(mode
, query
) {
829 if (!query
) query
= "#headlines-frame > div[id*=RROW]";
831 const children
= $$(query
);
833 children
.each(function(child
) {
834 const id
= child
.getAttribute("data-article-id");
836 const cb
= dijit
.getEnclosingWidget(
837 child
.getElementsByClassName("rchk")[0]);
840 child
.addClassName("Selected");
841 if (cb
) cb
.attr("checked", true);
842 } else if (mode
== "unread") {
843 if (child
.hasClassName("Unread")) {
844 child
.addClassName("Selected");
845 if (cb
) cb
.attr("checked", true);
847 child
.removeClassName("Selected");
848 if (cb
) cb
.attr("checked", false);
850 } else if (mode
== "marked") {
851 if (child
.hasClassName("marked")) {
852 child
.addClassName("Selected");
853 if (cb
) cb
.attr("checked", true);
855 child
.removeClassName("Selected");
856 if (cb
) cb
.attr("checked", false);
858 } else if (mode
== "published") {
859 if (child
.hasClassName("published")) {
860 child
.addClassName("Selected");
861 if (cb
) cb
.attr("checked", true);
863 child
.removeClassName("Selected");
864 if (cb
) cb
.attr("checked", false);
867 } else if (mode
== "invert") {
868 if (child
.hasClassName("Selected")) {
869 child
.removeClassName("Selected");
870 if (cb
) cb
.attr("checked", false);
872 child
.addClassName("Selected");
873 if (cb
) cb
.attr("checked", true);
877 child
.removeClassName("Selected");
878 if (cb
) cb
.attr("checked", false);
882 updateSelectedPrompt();
885 function deleteSelection() {
887 const rows
= getSelectedArticleIds2();
889 if (rows
.length
== 0) {
890 alert(__("No articles are selected."));
894 const fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
897 if (getActiveFeedId() != 0) {
898 str
= ngettext("Delete %d selected article in %s?", "Delete %d selected articles in %s?", rows
.length
);
900 str
= ngettext("Delete %d selected article?", "Delete %d selected articles?", rows
.length
);
903 str
= str
.replace("%d", rows
.length
);
904 str
= str
.replace("%s", fn
);
906 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
910 const query
= "?op=rpc&method=delete&ids=" + param_escape(rows
);
914 new Ajax
.Request("backend.php", {
916 onComplete: function (transport
) {
917 handle_rpc_json(transport
);
923 function archiveSelection() {
925 const rows
= getSelectedArticleIds2();
927 if (rows
.length
== 0) {
928 alert(__("No articles are selected."));
932 const fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
936 if (getActiveFeedId() != 0) {
937 str
= ngettext("Archive %d selected article in %s?", "Archive %d selected articles in %s?", rows
.length
);
940 str
= ngettext("Move %d archived article back?", "Move %d archived articles back?", rows
.length
);
942 str
+= " " + __("Please note that unstarred articles might get purged on next feed update.");
947 str
= str
.replace("%d", rows
.length
);
948 str
= str
.replace("%s", fn
);
950 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
954 const query
= "?op=rpc&method="+op
+"&ids=" + param_escape(rows
);
958 for (let i
= 0; i
< rows
.length
; i
++) {
959 cache_delete("article:" + rows
[i
]);
962 new Ajax
.Request("backend.php", {
964 onComplete: function(transport
) {
965 handle_rpc_json(transport
);
971 function catchupSelection() {
973 const rows
= getSelectedArticleIds2();
975 if (rows
.length
== 0) {
976 alert(__("No articles are selected."));
980 const fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
982 let str
= ngettext("Mark %d selected article in %s as read?", "Mark %d selected articles in %s as read?", rows
.length
);
984 str
= str
.replace("%d", rows
.length
);
985 str
= str
.replace("%s", fn
);
987 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
991 selectionToggleUnread(false, 'viewCurrentFeed()', true);
994 function editArticleTags(id
) {
995 const query
= "backend.php?op=article&method=editArticleTags¶m=" + param_escape(id
);
997 if (dijit
.byId("editTagsDlg"))
998 dijit
.byId("editTagsDlg").destroyRecursive();
1000 const dialog
= new dijit
.Dialog({
1002 title
: __("Edit article Tags"),
1003 style
: "width: 600px",
1004 execute: function() {
1005 if (this.validate()) {
1006 const query
= dojo
.objectToQuery(this.attr('value'));
1008 notify_progress("Saving article tags...", true);
1010 new Ajax
.Request("backend.php", {
1012 onComplete: function(transport
) {
1017 const data
= JSON
.parse(transport
.responseText
);
1024 const tags
= $("ATSTR-" + id
);
1025 const tooltip
= dijit
.byId("ATSTRTIP-" + id
);
1027 if (tags
) tags
.innerHTML
= data
.content
;
1028 if (tooltip
) tooltip
.attr('label', data
.content_full
);
1040 var tmph
= dojo
.connect(dialog
, 'onLoad', function() {
1041 dojo
.disconnect(tmph
);
1043 new Ajax
.Autocompleter('tags_str', 'tags_choices',
1044 "backend.php?op=article&method=completeTags",
1045 { tokens
: ',', paramName
: "search" });
1052 function cdmScrollToArticleId(id
, force
) {
1053 const ctr
= $("headlines-frame");
1054 const e
= $("RROW-" + id
);
1056 if (!e
|| !ctr
) return;
1058 if (force
|| e
.offsetTop
+e
.offsetHeight
> (ctr
.scrollTop
+ctr
.offsetHeight
) ||
1059 e
.offsetTop
< ctr
.scrollTop
) {
1061 // expanded cdm has a 4px margin now
1062 ctr
.scrollTop
= parseInt(e
.offsetTop
) - 4;
1066 function setActiveArticleId(id
) {
1067 console
.log("setActiveArticleId:" + id
);
1069 _active_article_id
= id
;
1070 PluginHost
.run(PluginHost
.HOOK_ARTICLE_SET_ACTIVE
, _active_article_id
);
1073 function getActiveArticleId() {
1074 return _active_article_id
;
1077 function postMouseIn(e
, id
) {
1078 post_under_pointer
= id
;
1081 function postMouseOut(id
) {
1082 post_under_pointer
= false;
1085 function unpackVisibleHeadlines() {
1086 if (!isCdmMode() || !getInitParam("cdm_expanded")) return;
1088 $$("#headlines-frame span.cencw[id]").each(
1090 const row
= $("RROW-" + child
.id
.replace("CENCW-", ""));
1092 if (row
&& row
.offsetTop
<= $("headlines-frame").scrollTop
+
1093 $("headlines-frame").offsetHeight
) {
1095 //console.log("unpacking: " + child.id);
1097 child
.innerHTML
= htmlspecialchars_decode(child
.innerHTML
);
1098 child
.removeAttribute('id');
1100 PluginHost
.run(PluginHost
.HOOK_ARTICLE_RENDERED_CDM
, row
);
1102 Element
.show(child
);
1108 function headlines_scroll_handler(e
) {
1111 // rate-limit in case of smooth scrolling and similar abominations
1112 if (Math
.max(e
.scrollTop
, _headlines_scroll_offset
) - Math
.min(e
.scrollTop
, _headlines_scroll_offset
) < 25) {
1116 _headlines_scroll_offset
= e
.scrollTop
;
1118 const hsp
= $("headlines-spacer");
1120 unpackVisibleHeadlines();
1122 // set topmost child in the buffer as active
1123 if (isCdmMode() && getInitParam("cdm_auto_catchup") == 1 &&
1124 getSelectedArticleIds2().length
<= 1 &&
1125 getInitParam("cdm_expanded")) {
1127 const rows
= $$("#headlines-frame > div[id*=RROW]");
1129 for (let i
= 0; i
< rows
.length
; i
++) {
1130 const child
= rows
[i
];
1132 if ($("headlines-frame").scrollTop
<= child
.offsetTop
&&
1133 child
.offsetTop
- $("headlines-frame").scrollTop
< 100 &&
1134 child
.getAttribute("data-article-id") != _active_article_id
) {
1136 if (_active_article_id
) {
1137 const row
= $("RROW-" + _active_article_id
);
1138 if (row
) row
.removeClassName("active");
1141 _active_article_id
= child
.getAttribute("data-article-id");
1142 showArticleInHeadlines(_active_article_id
, true);
1143 updateSelectedPrompt();
1149 if (!_infscroll_disable
) {
1150 if (hsp
&& hsp
.offsetTop
- 250 <= e
.scrollTop
+ e
.offsetHeight
) {
1152 hsp
.innerHTML
= "<span class='loading'><img src='images/indicator_tiny.gif'> " +
1153 __("Loading, please wait...") + "</span>";
1155 loadMoreHeadlines();
1162 updateFloatingTitle();
1165 catchupCurrentBatchIfNeeded();
1167 if (getInitParam("cdm_auto_catchup") == 1) {
1169 // let's get DOM some time to settle down
1170 const ts
= new Date().getTime();
1171 if (ts
- _last_headlines_update
< 100) return;
1173 $$("#headlines-frame > div[id*=RROW][class*=Unread]").each(
1175 if ($("headlines-frame").scrollTop
> (child
.offsetTop
+ child
.offsetHeight
/2)) {
1177 const id
= child
.getAttribute("data-article-id")
1179 if (catchup_id_batch
.indexOf(id
) == -1)
1180 catchup_id_batch
.push(id
);
1182 //console.log("auto_catchup_batch: " + catchup_id_batch.toString());
1187 if (_infscroll_disable
) {
1188 const child
= $$("#headlines-frame div[id*=RROW]").last();
1190 if (child
&& $("headlines-frame").scrollTop
>
1191 (child
.offsetTop
+ child
.offsetHeight
- 50)) {
1193 console
.log("we seem to be at an end");
1195 if (getInitParam("on_catchup_show_next_feed") == "1") {
1196 openNextUnreadFeed();
1203 console
.warn("headlines_scroll_handler: " + e
);
1207 function openNextUnreadFeed() {
1208 const is_cat
= activeFeedIsCat();
1209 const nuf
= getNextUnreadFeed(getActiveFeedId(), is_cat
);
1210 if (nuf
) viewfeed({feed
: nuf
, is_cat
: is_cat
});
1213 function catchupBatchedArticles() {
1214 if (catchup_id_batch
.length
> 0 && !_infscroll_request_sent
&& !_catchup_request_sent
) {
1216 console
.log("catchupBatchedArticles: working");
1218 // make a copy of the array
1219 const batch
= catchup_id_batch
.slice();
1220 const query
= "?op=rpc&method=catchupSelected" +
1221 "&cmode=0&ids=" + param_escape(batch
.toString());
1225 _catchup_request_sent
= true;
1227 new Ajax
.Request("backend.php", {
1229 onComplete: function (transport
) {
1230 handle_rpc_json(transport
);
1232 _catchup_request_sent
= false;
1234 const reply
= JSON
.parse(transport
.responseText
);
1235 const batch
= reply
.ids
;
1237 batch
.each(function (id
) {
1239 const elem
= $("RROW-" + id
);
1240 if (elem
) elem
.removeClassName("Unread");
1241 catchup_id_batch
.remove(id
);
1244 updateFloatingTitle(true);
1251 function catchupRelativeToArticle(below
, id
) {
1253 if (!id
) id
= getActiveArticleId();
1256 alert(__("No article is selected."));
1260 const visible_ids
= getLoadedArticleIds();
1262 const ids_to_mark
= [];
1265 for (var i
= 0; i
< visible_ids
.length
; i
++) {
1266 if (visible_ids
[i
] != id
) {
1267 var e
= $("RROW-" + visible_ids
[i
]);
1269 if (e
&& e
.hasClassName("Unread")) {
1270 ids_to_mark
.push(visible_ids
[i
]);
1277 for (var i
= visible_ids
.length
- 1; i
>= 0; i
--) {
1278 if (visible_ids
[i
] != id
) {
1279 var e
= $("RROW-" + visible_ids
[i
]);
1281 if (e
&& e
.hasClassName("Unread")) {
1282 ids_to_mark
.push(visible_ids
[i
]);
1290 if (ids_to_mark
.length
== 0) {
1291 alert(__("No articles found to mark"));
1293 const msg
= ngettext("Mark %d article as read?", "Mark %d articles as read?", ids_to_mark
.length
).replace("%d", ids_to_mark
.length
);
1295 if (getInitParam("confirm_feed_catchup") != 1 || confirm(msg
)) {
1297 for (var i
= 0; i
< ids_to_mark
.length
; i
++) {
1298 var e
= $("RROW-" + ids_to_mark
[i
]);
1299 e
.removeClassName("Unread");
1302 const query
= "?op=rpc&method=catchupSelected" +
1303 "&cmode=0" + "&ids=" + param_escape(ids_to_mark
.toString());
1305 new Ajax
.Request("backend.php", {
1307 onComplete: function (transport
) {
1308 handle_rpc_json(transport
);
1316 function cdmCollapseArticle(event
, id
, unmark
) {
1317 if (unmark
== undefined) unmark
= true;
1319 const row
= $("RROW-" + id
);
1320 const elem
= $("CICD-" + id
);
1323 const collapse
= row
.select("span[class='collapseBtn']")[0];
1326 Element
.show("CEXC-" + id
);
1327 Element
.hide(collapse
);
1330 row
.removeClassName("active");
1332 markHeadline(id
, false);
1334 if (id
== getActiveArticleId()) {
1335 setActiveArticleId(0);
1338 updateSelectedPrompt();
1341 if (event
) Event
.stop(event
);
1343 PluginHost
.run(PluginHost
.HOOK_ARTICLE_COLLAPSED
, id
);
1345 if (row
.offsetTop
< $("headlines-frame").scrollTop
)
1346 scrollToRowId(row
.id
);
1348 $("floatingTitle").style
.visibility
= "hidden";
1349 $("floatingTitle").setAttribute("data-article-id", 0);
1353 function cdmExpandArticle(id
, noexpand
) {
1354 console
.log("cdmExpandArticle " + id
);
1356 const row
= $("RROW-" + id
);
1358 if (!row
) return false;
1360 const oldrow
= $("RROW-" + getActiveArticleId());
1362 let elem
= $("CICD-" + getActiveArticleId());
1364 if (id
== getActiveArticleId() && Element
.visible(elem
))
1367 selectArticles("none");
1369 const old_offset
= row
.offsetTop
;
1371 if (getActiveArticleId() && elem
&& !getInitParam("cdm_expanded")) {
1372 let collapse
= oldrow
.select("span[class='collapseBtn']")[0];
1375 Element
.show("CEXC-" + getActiveArticleId());
1376 Element
.hide(collapse
);
1379 if (oldrow
) oldrow
.removeClassName("active");
1381 setActiveArticleId(id
);
1383 elem
= $("CICD-" + id
);
1385 let collapse
= row
.select("span[class='collapseBtn']")[0];
1387 const cencw
= $("CENCW-" + id
);
1389 if (!Element
.visible(elem
) && !noexpand
) {
1391 cencw
.innerHTML
= htmlspecialchars_decode(cencw
.innerHTML
);
1392 cencw
.setAttribute('id', '');
1393 Element
.show(cencw
);
1397 Element
.hide("CEXC-" + id
);
1398 Element
.show(collapse
);
1401 const new_offset
= row
.offsetTop
;
1403 if (old_offset
> new_offset
)
1404 $("headlines-frame").scrollTop
-= (old_offset
- new_offset
);
1407 if (catchup_id_batch
.indexOf(id
) == -1)
1408 catchup_id_batch
.push(id
);
1410 catchupCurrentBatchIfNeeded();
1414 row
.addClassName("active");
1416 PluginHost
.run(PluginHost
.HOOK_ARTICLE_EXPANDED
, id
);
1421 function getArticleUnderPointer() {
1422 return post_under_pointer
;
1425 function scrollArticle(offset
) {
1427 const ci
= $("content-insert");
1429 ci
.scrollTop
+= offset
;
1432 const hi
= $("headlines-frame");
1434 hi
.scrollTop
+= offset
;
1440 function show_labels_in_headlines(transport
) {
1441 const data
= JSON
.parse(transport
.responseText
);
1444 data
['info-for-headlines'].each(function (elem
) {
1445 $$(".HLLCTR-" + elem
.id
).each(function (ctr
) {
1446 ctr
.innerHTML
= elem
.labels
;
1452 function cdmClicked(event
, id
, in_body
) {
1453 //var shift_key = event.shiftKey;
1455 if (!event
.ctrlKey
&& !event
.metaKey
) {
1457 if (!getInitParam("cdm_expanded")) {
1458 return cdmExpandArticle(id
);
1461 let elem
= $("RROW-" + getActiveArticleId());
1463 if (elem
) elem
.removeClassName("active");
1465 selectArticles("none");
1468 elem
= $("RROW-" + id
);
1469 const article_is_unread
= elem
.hasClassName("Unread");
1471 elem
.removeClassName("Unread");
1472 elem
.addClassName("active");
1474 setActiveArticleId(id
);
1476 if (article_is_unread
) {
1477 decrementFeedCounter(getActiveFeedId(), activeFeedIsCat());
1478 updateFloatingTitle(true);
1481 const query
= "?op=rpc&method=catchupSelected" +
1482 "&cmode=0&ids=" + param_escape(id
);
1484 new Ajax
.Request("backend.php", {
1486 onComplete: function (transport
) {
1487 handle_rpc_json(transport
);
1491 return !event
.shiftKey
;
1494 } else if (!in_body
) {
1496 toggleSelected(id
, true);
1498 let elem
= $("RROW-" + id
);
1499 const article_is_unread
= elem
.hasClassName("Unread");
1501 if (article_is_unread
) {
1502 decrementFeedCounter(getActiveFeedId(), activeFeedIsCat());
1505 toggleUnread(id
, 0, false);
1507 openArticleInNewWindow(id
);
1512 const unread_in_buffer
= $$("#headlines-frame > div[id*=RROW][class*=Unread]").length
1513 request_counters(unread_in_buffer
== 0);
1518 function hlClicked(event
, id
) {
1519 if (event
.which
== 2) {
1522 } else if (event
.ctrlKey
|| event
.metaKey
) {
1523 openArticleInNewWindow(id
);
1531 function openArticleInNewWindow(id
) {
1532 toggleUnread(id
, 0, false);
1534 const w
= window
.open("");
1536 w
.location
= "backend.php?op=article&method=redirect&id=" + id
;
1539 function isCdmMode() {
1540 return getInitParam("combined_display_mode");
1543 function markHeadline(id
, marked
) {
1544 if (marked
== undefined) marked
= true;
1546 const row
= $("RROW-" + id
);
1548 const check
= dijit
.getEnclosingWidget(
1549 row
.getElementsByClassName("rchk")[0]);
1552 check
.attr("checked", marked
);
1556 row
.addClassName("Selected");
1558 row
.removeClassName("Selected");
1562 function getRelativePostIds(id
, limit
) {
1566 if (!limit
) limit
= 6; //3
1568 const ids
= getLoadedArticleIds();
1570 for (let i
= 0; i
< ids
.length
; i
++) {
1572 for (let k
= 1; k
<= limit
; k
++) {
1573 //if (i > k-1) tmp.push(ids[i-k]);
1574 if (i
< ids
.length
- k
) tmp
.push(ids
[i
+ k
]);
1583 function correctHeadlinesOffset(id
) {
1585 const container
= $("headlines-frame");
1586 const row
= $("RROW-" + id
);
1588 if (!container
|| !row
) return;
1590 const viewport
= container
.offsetHeight
;
1592 const rel_offset_top
= row
.offsetTop
- container
.scrollTop
;
1593 const rel_offset_bottom
= row
.offsetTop
+ row
.offsetHeight
- container
.scrollTop
;
1595 //console.log("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom);
1596 //console.log("Vport: " + viewport);
1598 if (rel_offset_top
<= 0 || rel_offset_top
> viewport
) {
1599 container
.scrollTop
= row
.offsetTop
;
1600 } else if (rel_offset_bottom
> viewport
) {
1602 /* doesn't properly work with Opera in some cases because
1603 Opera fucks up element scrolling */
1605 container
.scrollTop
= row
.offsetTop
+ row
.offsetHeight
- viewport
;
1609 function headlineActionsChange(elem
) {
1611 elem
.attr('value', 'false');
1614 function closeArticlePanel() {
1616 if (dijit
.byId("content-insert"))
1617 dijit
.byId("headlines-wrap-inner").removeChild(
1618 dijit
.byId("content-insert"));
1621 function initFloatingMenu() {
1622 if (!dijit
.byId("floatingMenu")) {
1624 const menu
= new dijit
.Menu({
1626 targetNodeIds
: ["floatingTitle"]
1629 headlinesMenuCommon(menu
);
1635 function headlinesMenuCommon(menu
) {
1637 menu
.addChild(new dijit
.MenuItem({
1638 label
: __("Open original article"),
1639 onClick: function (event
) {
1640 openArticleInNewWindow(this.getParent().currentTarget
.getAttribute("data-article-id"));
1644 menu
.addChild(new dijit
.MenuItem({
1645 label
: __("Display article URL"),
1646 onClick: function (event
) {
1647 displayArticleUrl(this.getParent().currentTarget
.getAttribute("data-article-id"));
1651 menu
.addChild(new dijit
.MenuSeparator());
1653 menu
.addChild(new dijit
.MenuItem({
1654 label
: __("Toggle unread"),
1655 onClick: function () {
1657 let ids
= getSelectedArticleIds2();
1659 const id
= (this.getParent().currentTarget
.getAttribute("data-article-id")) + "";
1660 ids
= ids
.length
!= 0 && ids
.indexOf(id
) != -1 ? ids
: [id
];
1662 selectionToggleUnread(undefined, false, true, ids
);
1666 menu
.addChild(new dijit
.MenuItem({
1667 label
: __("Toggle starred"),
1668 onClick: function () {
1669 let ids
= getSelectedArticleIds2();
1671 const id
= (this.getParent().currentTarget
.getAttribute("data-article-id")) + "";
1672 ids
= ids
.length
!= 0 && ids
.indexOf(id
) != -1 ? ids
: [id
];
1674 selectionToggleMarked(undefined, false, true, ids
);
1678 menu
.addChild(new dijit
.MenuItem({
1679 label
: __("Toggle published"),
1680 onClick: function () {
1681 let ids
= getSelectedArticleIds2();
1683 const id
= (this.getParent().currentTarget
.getAttribute("data-article-id")) + "";
1684 ids
= ids
.length
!= 0 && ids
.indexOf(id
) != -1 ? ids
: [id
];
1686 selectionTogglePublished(undefined, false, true, ids
);
1690 menu
.addChild(new dijit
.MenuSeparator());
1692 menu
.addChild(new dijit
.MenuItem({
1693 label
: __("Mark above as read"),
1694 onClick: function () {
1695 catchupRelativeToArticle(0, this.getParent().currentTarget
.getAttribute("data-article-id"));
1699 menu
.addChild(new dijit
.MenuItem({
1700 label
: __("Mark below as read"),
1701 onClick: function () {
1702 catchupRelativeToArticle(1, this.getParent().currentTarget
.getAttribute("data-article-id"));
1707 const labels
= getInitParam("labels");
1709 if (labels
&& labels
.length
) {
1711 menu
.addChild(new dijit
.MenuSeparator());
1713 const labelAddMenu
= new dijit
.Menu({ownerMenu
: menu
});
1714 const labelDelMenu
= new dijit
.Menu({ownerMenu
: menu
});
1716 labels
.each(function (label
) {
1717 const bare_id
= label
.id
;
1718 const name
= label
.caption
;
1720 labelAddMenu
.addChild(new dijit
.MenuItem({
1723 onClick: function () {
1725 let ids
= getSelectedArticleIds2();
1727 const id
= (this.getParent().ownerMenu
.currentTarget
.getAttribute("data-article-id")) + "";
1729 ids
= ids
.length
!= 0 && ids
.indexOf(id
) != -1 ? ids
: [id
];
1731 selectionAssignLabel(this.labelId
, ids
);
1735 labelDelMenu
.addChild(new dijit
.MenuItem({
1738 onClick: function () {
1739 let ids
= getSelectedArticleIds2();
1741 const id
= (this.getParent().ownerMenu
.currentTarget
.getAttribute("data-article-id")) + "";
1743 ids
= ids
.length
!= 0 && ids
.indexOf(id
) != -1 ? ids
: [id
];
1745 selectionRemoveLabel(this.labelId
, ids
);
1751 menu
.addChild(new dijit
.PopupMenuItem({
1752 label
: __("Assign label"),
1756 menu
.addChild(new dijit
.PopupMenuItem({
1757 label
: __("Remove label"),
1764 function initHeadlinesMenu() {
1765 if (!dijit
.byId("headlinesMenu")) {
1767 const menu
= new dijit
.Menu({
1768 id
: "headlinesMenu",
1769 targetNodeIds
: ["headlines-frame"],
1770 selector
: ".hlMenuAttach"
1773 headlinesMenuCommon(menu
);
1778 /* vgroup feed title menu */
1780 if (!dijit
.byId("headlinesFeedTitleMenu")) {
1782 const menu
= new dijit
.Menu({
1783 id
: "headlinesFeedTitleMenu",
1784 targetNodeIds
: ["headlines-frame"],
1785 selector
: "div.cdmFeedTitle"
1788 menu
.addChild(new dijit
.MenuItem({
1789 label
: __("Select articles in group"),
1790 onClick: function (event
) {
1791 selectArticles("all",
1792 "#headlines-frame > div[id*=RROW]" +
1793 "[data-orig-feed-id='" + this.getParent().currentTarget
.getAttribute("data-feed-id") + "']");
1798 menu
.addChild(new dijit
.MenuItem({
1799 label
: __("Mark group as read"),
1800 onClick: function () {
1801 selectArticles("none");
1802 selectArticles("all",
1803 "#headlines-frame > div[id*=RROW]" +
1804 "[data-orig-feed-id='" + this.getParent().currentTarget
.getAttribute("data-feed-id") + "']");
1810 menu
.addChild(new dijit
.MenuItem({
1811 label
: __("Mark feed as read"),
1812 onClick: function () {
1813 catchupFeedInGroup(this.getParent().currentTarget
.getAttribute("data-feed-id"));
1817 menu
.addChild(new dijit
.MenuItem({
1818 label
: __("Edit feed"),
1819 onClick: function () {
1820 editFeed(this.getParent().currentTarget
.getAttribute("data-feed-id"));
1828 function cache_set(id
, obj
) {
1829 //console.log("cache_set: " + id);
1832 sessionStorage
[id
] = obj
;
1834 sessionStorage
.clear();
1838 function cache_get(id
) {
1840 return sessionStorage
[id
];
1843 function cache_clear() {
1845 sessionStorage
.clear();
1848 function cache_delete(id
) {
1850 sessionStorage
.removeItem(id
);
1853 function cancelSearch() {
1858 function setSelectionScore() {
1859 const ids
= getSelectedArticleIds2();
1861 if (ids
.length
> 0) {
1864 const score
= prompt(__("Please enter new score for selected articles:"));
1866 if (score
!= undefined) {
1867 const query
= "op=article&method=setScore&id=" + param_escape(ids
.toString()) +
1868 "&score=" + param_escape(score
);
1870 new Ajax
.Request("backend.php", {
1872 onComplete: function (transport
) {
1873 const reply
= JSON
.parse(transport
.responseText
);
1877 ids
.each(function (id
) {
1878 const row
= $("RROW-" + id
);
1881 const pic
= row
.getElementsByClassName("hlScorePic")[0];
1884 pic
.src
= pic
.src
.replace(/score_.*?\.png/,
1885 reply
["score_pic"]);
1886 pic
.setAttribute("score", score
);
1896 alert(__("No articles are selected."));
1900 function updateScore(id
) {
1901 const pic
= $$("#RROW-" + id
+ " .hlScorePic")[0];
1905 const query
= "op=article&method=getScore&id=" + param_escape(id
);
1907 new Ajax
.Request("backend.php", {
1909 onComplete: function (transport
) {
1910 console
.log(transport
.responseText
);
1912 const reply
= JSON
.parse(transport
.responseText
);
1915 pic
.src
= pic
.src
.replace(/score_.*?\.png/, reply
["score_pic"]);
1916 pic
.setAttribute("score", reply
["score"]);
1917 pic
.setAttribute("title", reply
["score"]);
1924 function changeScore(id
, pic
) {
1925 const score
= pic
.getAttribute("score");
1927 const new_score
= prompt(__("Please enter new score for this article:"), score
);
1929 if (new_score
!= undefined) {
1931 const query
= "op=article&method=setScore&id=" + param_escape(id
) +
1932 "&score=" + param_escape(new_score
);
1934 new Ajax
.Request("backend.php", {
1936 onComplete: function (transport
) {
1937 const reply
= JSON
.parse(transport
.responseText
);
1940 pic
.src
= pic
.src
.replace(/score_.*?\.png/, reply
["score_pic"]);
1941 pic
.setAttribute("score", new_score
);
1942 pic
.setAttribute("title", new_score
);
1949 function displayArticleUrl(id
) {
1950 const query
= "op=rpc&method=getlinktitlebyid&id=" + param_escape(id
);
1952 new Ajax
.Request("backend.php", {
1954 onComplete: function (transport
) {
1955 const reply
= JSON
.parse(transport
.responseText
);
1957 if (reply
&& reply
.link
) {
1958 prompt(__("Article URL:"), reply
.link
);
1964 function scrollToRowId(id
) {
1968 $("headlines-frame").scrollTop
= row
.offsetTop
- 4;
1971 function updateFloatingTitle(unread_only
) {
1972 if (!isCdmMode()) return;
1974 const hf
= $("headlines-frame");
1976 const elems
= $$("#headlines-frame > div[id*=RROW]");
1978 for (let i
= 0; i
< elems
.length
; i
++) {
1980 const child
= elems
[i
];
1982 if (child
&& child
.offsetTop
+ child
.offsetHeight
> hf
.scrollTop
) {
1984 const header
= child
.getElementsByClassName("cdmHeader")[0];
1986 if (unread_only
|| child
.getAttribute("data-article-id") != $("floatingTitle").getAttribute("data-article-id")) {
1987 if (child
.getAttribute("data-article-id") != $("floatingTitle").getAttribute("data-article-id")) {
1989 $("floatingTitle").setAttribute("data-article-id", child
.getAttribute("data-article-id"));
1990 $("floatingTitle").innerHTML
= header
.innerHTML
;
1991 $("floatingTitle").firstChild
.innerHTML
= "<img class='anchor markedPic' src='images/page_white_go.png' onclick=\"scrollToRowId('" + child
.id
+ "')\">" + $("floatingTitle").firstChild
.innerHTML
;
1995 const cb
= $$("#floatingTitle .dijitCheckBox")[0];
1998 cb
.parentNode
.removeChild(cb
);
2001 if (child
.hasClassName("Unread"))
2002 $("floatingTitle").addClassName("Unread");
2004 $("floatingTitle").removeClassName("Unread");
2006 PluginHost
.run(PluginHost
.HOOK_FLOATING_TITLE
, child
);
2009 $("floatingTitle").style
.marginRight
= hf
.offsetWidth
- child
.offsetWidth
+ "px";
2010 if (header
.offsetTop
+ header
.offsetHeight
< hf
.scrollTop
+ $("floatingTitle").offsetHeight
- 5 &&
2011 child
.offsetTop
+ child
.offsetHeight
>= hf
.scrollTop
+ $("floatingTitle").offsetHeight
- 5)
2012 $("floatingTitle").style
.visibility
= "visible";
2014 $("floatingTitle").style
.visibility
= "hidden";
2022 function catchupCurrentBatchIfNeeded() {
2023 if (catchup_id_batch
.length
> 0) {
2024 window
.clearTimeout(catchup_timeout_id
);
2025 catchup_timeout_id
= window
.setTimeout(catchupBatchedArticles
, 1000);
2027 if (catchup_id_batch
.length
>= 10) {
2028 catchupBatchedArticles();