3 let _active_article_id
= 0;
5 let vgroup_last_feed
= false;
6 let post_under_pointer
= false;
8 let last_requested_article
= false;
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 handle_rpc_json(transport
);
27 console
.log("headlines_callback2 [offset=" + offset
+ "] B:" + background
+ " I:" + infscroll_req
);
35 reply
= JSON
.parse(transport
.responseText
);
42 is_cat
= reply
['headlines']['is_cat'];
43 feed_id
= reply
['headlines']['id'];
44 last_search_query
= reply
['headlines']['search_query'];
47 let content
= reply
['headlines']['content'];
49 content
= content
+ "<div id='headlines-spacer'></div>";
53 if (feed_id
!= -7 && (feed_id
!= getActiveFeedId() || is_cat
!= activeFeedIsCat()))
56 /* dijit.getEnclosingWidget(
57 document.forms["main_toolbar_form"].update).attr('disabled',
58 is_cat || feed_id <= 0); */
61 if (infscroll_req
== false) {
62 $("headlines-frame").scrollTop
= 0;
64 $("floatingTitle").style
.visibility
= "hidden";
65 $("floatingTitle").setAttribute("data-article-id", 0);
66 $("floatingTitle").innerHTML
= "";
70 $("headlines-frame").removeClassName("cdm");
71 $("headlines-frame").removeClassName("normal");
73 $("headlines-frame").addClassName(isCdmMode() ? "cdm" : "normal");
75 const headlines_count
= reply
['headlines-info']['count'];
77 vgroup_last_feed
= reply
['headlines-info']['vgroup_last_feed'];
79 if (parseInt(headlines_count
) < 30) {
80 _infscroll_disable
= 1;
82 _infscroll_disable
= 0;
85 current_first_id
= reply
['headlines']['first_id'];
86 const counters
= reply
['counters'];
87 const articles
= reply
['articles'];
88 //var runtime_info = reply['runtime-info'];
90 if (infscroll_req
== false) {
91 loaded_article_ids
= [];
93 dojo
.html
.set($("headlines-toolbar"),
94 reply
['headlines']['toolbar'],
95 {parseContent
: true});
97 /*dojo.html.set($("headlines-frame"),
98 reply['headlines']['content'],
99 {parseContent: true});
101 $$("#headlines-frame div[id*='RROW']").each(function(row) {
102 loaded_article_ids.push(row.id);
105 $("headlines-frame").innerHTML
= '';
107 var tmp
= new Element("div");
108 tmp
.innerHTML
= reply
['headlines']['content'];
109 dojo
.parser
.parse(tmp
);
111 while (tmp
.hasChildNodes()) {
112 var row
= tmp
.removeChild(tmp
.firstChild
);
114 if (loaded_article_ids
.indexOf(row
.id
) == -1 || row
.hasClassName("cdmFeedTitle")) {
115 dijit
.byId("headlines-frame").domNode
.appendChild(row
);
117 loaded_article_ids
.push(row
.id
);
121 var hsp
= $("headlines-spacer");
122 if (!hsp
) hsp
= new Element("DIV", {"id": "headlines-spacer"});
123 dijit
.byId('headlines-frame').domNode
.appendChild(hsp
);
127 if (_infscroll_disable
)
128 hsp
.innerHTML
= "<a href='#' onclick='openNextUnreadFeed()'>" +
129 __("Click to open next unread feed.") + "</a>";
132 $("feed_title").innerHTML
+= "<span id='cancel_search'>" +
133 " (<a href='#' onclick='cancelSearch()'>" + __("Cancel search") + "</a>)" +
137 } else if (headlines_count
> 0 && feed_id
== getActiveFeedId() && is_cat
== activeFeedIsCat()) {
138 console
.log("adding some more headlines: " + headlines_count
);
140 const c
= dijit
.byId("headlines-frame");
141 const ids
= getSelectedArticleIds2();
143 var hsp
= $("headlines-spacer");
146 c
.domNode
.removeChild(hsp
);
148 var tmp
= new Element("div");
149 tmp
.innerHTML
= reply
['headlines']['content'];
150 dojo
.parser
.parse(tmp
);
152 while (tmp
.hasChildNodes()) {
153 var row
= tmp
.removeChild(tmp
.firstChild
);
155 if (loaded_article_ids
.indexOf(row
.id
) == -1 || row
.hasClassName("cdmFeedTitle")) {
156 dijit
.byId("headlines-frame").domNode
.appendChild(row
);
158 loaded_article_ids
.push(row
.id
);
162 if (!hsp
) hsp
= new Element("DIV", {"id": "headlines-spacer"});
163 c
.domNode
.appendChild(hsp
);
165 if (headlines_count
< 30) _infscroll_disable
= true;
167 console
.log("restore selected ids: " + ids
);
169 for (var i
= 0; i
< ids
.length
; i
++) {
170 markHeadline(ids
[i
]);
175 if (_infscroll_disable
) {
176 hsp
.innerHTML
= "<a href='#' onclick='openNextUnreadFeed()'>" +
177 __("Click to open next unread feed.") + "</a>";
181 console
.log("no new headlines received");
183 const first_id_changed
= reply
['headlines']['first_id_changed'];
184 console
.log("first id changed:" + first_id_changed
);
186 var hsp
= $("headlines-spacer");
189 if (first_id_changed
) {
190 hsp
.innerHTML
= "<a href='#' onclick='viewCurrentFeed()'>" +
191 __("New articles found, reload feed to continue.") + "</a>";
193 hsp
.innerHTML
= "<a href='#' onclick='openNextUnreadFeed()'>" +
194 __("Click to open next unread feed.") + "</a>";
202 for (var i
= 0; i
< articles
.length
; i
++) {
203 const a_id
= articles
[i
]['id'];
204 cache_set("article:" + a_id
, articles
[i
]['content']);
207 console
.log("no cached articles received");
211 parse_counters(counters
);
216 console
.error("Invalid object received: " + transport
.responseText
);
217 dijit
.byId("headlines-frame").attr('content', "<div class='whiteBox'>" +
218 __('Could not update headlines (invalid object received - see error console for details)') +
222 _infscroll_request_sent
= 0;
223 _last_headlines_update
= new Date().getTime();
225 unpackVisibleHeadlines();
227 // if we have some more space in the buffer, why not try to fill it
229 if (!_infscroll_disable
&& $("headlines-spacer") &&
230 $("headlines-spacer").offsetTop
< $("headlines-frame").offsetHeight
) {
232 window
.setTimeout(function() {
240 function render_article(article
) {
241 cleanup_memory("content-insert");
243 dijit
.byId("headlines-wrap-inner").addChild(
244 dijit
.byId("content-insert"));
246 const c
= dijit
.byId("content-insert");
249 c
.domNode
.scrollTop
= 0;
252 c
.attr('content', article
);
253 PluginHost
.run(PluginHost
.HOOK_ARTICLE_RENDERED
, c
.domNode
);
255 correctHeadlinesOffset(getActiveArticleId());
262 function showArticleInHeadlines(id
, noexpand
) {
263 const row
= $("RROW-" + id
);
267 row
.removeClassName("Unread");
269 row
.addClassName("active");
271 selectArticles('none');
276 function article_callback2(transport
, id
) {
277 console
.log("article_callback2 " + id
);
279 handle_rpc_json(transport
);
284 reply
= JSON
.parse(transport
.responseText
);
291 reply
.each(function(article
) {
292 if (getActiveArticleId() == article
['id']) {
293 render_article(article
['content']);
295 cids_requested
.remove(article
['id']);
297 cache_set("article:" + article
['id'], article
['content']);
300 // if (id != last_requested_article) {
301 // console.log("requested article id is out of sequence, aborting");
306 console
.error("Invalid object received: " + transport
.responseText
);
308 render_article("<div class='whiteBox'>" +
309 __('Could not display article (invalid object received - see error console for details)') + "</div>");
312 const unread_in_buffer
= $$("#headlines-frame > div[id*=RROW][class*=Unread]").length
313 request_counters(unread_in_buffer
== 0);
318 function view(id
, activefeed
, noexpand
) {
319 const oldrow
= $("RROW-" + getActiveArticleId());
320 if (oldrow
) oldrow
.removeClassName("active");
322 const crow
= $("RROW-" + id
);
326 setActiveArticleId(id
);
327 showArticleInHeadlines(id
, noexpand
);
331 console
.log("loading article: " + id
);
333 const cached_article
= cache_get("article:" + id
);
335 console
.log("cache check result: " + (cached_article
!= false));
337 let query
= "?op=article&method=view&id=" + param_escape(id
);
339 const neighbor_ids
= getRelativePostIds(id
);
341 /* only request uncached articles */
343 const cids_to_request
= [];
345 for (let i
= 0; i
< neighbor_ids
.length
; i
++) {
346 if (cids_requested
.indexOf(neighbor_ids
[i
]) == -1)
347 if (!cache_get("article:" + neighbor_ids
[i
])) {
348 cids_to_request
.push(neighbor_ids
[i
]);
349 cids_requested
.push(neighbor_ids
[i
]);
353 console
.log("additional ids: " + cids_to_request
.toString());
355 query
= query
+ "&cids=" + cids_to_request
.toString();
357 const article_is_unread
= crow
.hasClassName("Unread");
359 setActiveArticleId(id
);
360 showArticleInHeadlines(id
);
362 if (cached_article
&& article_is_unread
) {
364 query
= query
+ "&mode=prefetch";
366 render_article(cached_article
);
368 } else if (cached_article
) {
370 query
= query
+ "&mode=prefetch_old";
371 render_article(cached_article
);
373 // if we don't need to request any relative ids, we might as well skip
374 // the server roundtrip altogether
375 if (cids_to_request
.length
== 0) {
380 last_requested_article
= id
;
384 if (article_is_unread
) {
385 decrementFeedCounter(getActiveFeedId(), activeFeedIsCat());
388 new Ajax
.Request("backend.php", {
390 onComplete: function(transport
) {
391 article_callback2(transport
, id
);
398 function toggleMark(id
, client_only
) {
399 let query
= "?op=rpc&id=" + id
+ "&method=mark";
401 const row
= $("RROW-" + id
);
406 const row_imgs
= row
.getElementsByClassName("markedPic");
408 for (var i
= 0; i
< row_imgs
.length
; i
++)
409 imgs
.push(row_imgs
[i
]);
411 const ft
= $("floatingTitle");
413 if (ft
&& ft
.getAttribute("data-article-id") == id
) {
414 const fte
= ft
.getElementsByClassName("markedPic");
416 for (var i
= 0; i
< fte
.length
; i
++)
420 for (i
= 0; i
< imgs
.length
; i
++) {
423 if (!row
.hasClassName("marked")) {
424 img
.src
= img
.src
.replace("mark_unset", "mark_set");
425 query
= query
+ "&mark=1";
427 img
.src
= img
.src
.replace("mark_set", "mark_unset");
428 query
= query
+ "&mark=0";
432 row
.toggleClassName("marked");
435 new Ajax
.Request("backend.php", {
437 onComplete: function (transport
) {
438 handle_rpc_json(transport
);
444 function togglePub(id
, client_only
, no_effects
, note
) {
445 let query
= "?op=rpc&id=" + id
+ "&method=publ";
447 if (note
!= undefined) {
448 query
= query
+ "¬e=" + param_escape(note
);
450 query
= query
+ "¬e=undefined";
453 const row
= $("RROW-" + id
);
458 const row_imgs
= row
.getElementsByClassName("pubPic");
460 for (var i
= 0; i
< row_imgs
.length
; i
++)
461 imgs
.push(row_imgs
[i
]);
463 const ft
= $("floatingTitle");
465 if (ft
&& ft
.getAttribute("data-article-id") == id
) {
466 const fte
= ft
.getElementsByClassName("pubPic");
468 for (var i
= 0; i
< fte
.length
; i
++)
472 for (var i
= 0; i
< imgs
.length
; i
++) {
475 if (!row
.hasClassName("published") || note
!= undefined) {
476 img
.src
= img
.src
.replace("pub_unset", "pub_set");
477 query
= query
+ "&pub=1";
479 img
.src
= img
.src
.replace("pub_set", "pub_unset");
480 query
= query
+ "&pub=0";
484 if (note
!= undefined)
485 row
.addClassName("published");
487 row
.toggleClassName("published");
490 new Ajax
.Request("backend.php", {
492 onComplete: function(transport
) {
493 handle_rpc_json(transport
);
499 function moveToPost(mode
, noscroll
, noexpand
) {
500 const rows
= getLoadedArticleIds();
505 if (!$('RROW-' + getActiveArticleId())) {
506 setActiveArticleId(0);
509 if (!getActiveArticleId()) {
511 prev_id
= rows
[rows
.length
-1]
513 for (let i
= 0; i
< rows
.length
; i
++) {
514 if (rows
[i
] == getActiveArticleId()) {
516 // Account for adjacent identical article ids.
517 if (i
> 0) prev_id
= rows
[i
-1];
519 for (let j
= i
+1; j
< rows
.length
; j
++) {
520 if (rows
[j
] != getActiveArticleId()) {
530 console
.log("cur: " + getActiveArticleId() + " next: " + next_id
);
532 if (mode
== "next") {
533 if (next_id
|| getActiveArticleId()) {
536 var article
= $("RROW-" + getActiveArticleId());
537 var ctr
= $("headlines-frame");
539 if (!noscroll
&& article
&& article
.offsetTop
+ article
.offsetHeight
>
540 ctr
.scrollTop
+ ctr
.offsetHeight
) {
542 scrollArticle(ctr
.offsetHeight
/4);
544 } else if (next_id
) {
545 cdmExpandArticle(next_id
, noexpand
);
546 cdmScrollToArticleId(next_id
, true);
549 } else if (next_id
) {
550 correctHeadlinesOffset(next_id
);
551 view(next_id
, getActiveFeedId(), noexpand
);
556 if (mode
== "prev") {
557 if (prev_id
|| getActiveArticleId()) {
560 var article
= $("RROW-" + getActiveArticleId());
561 const prev_article
= $("RROW-" + prev_id
);
562 var ctr
= $("headlines-frame");
564 if (!getInitParam("cdm_expanded")) {
566 if (!noscroll
&& article
&& article
.offsetTop
< ctr
.scrollTop
) {
567 scrollArticle(-ctr
.offsetHeight
/4);
569 cdmExpandArticle(prev_id
, noexpand
);
570 cdmScrollToArticleId(prev_id
, true);
572 } else if (!noscroll
&& article
&& article
.offsetTop
< ctr
.scrollTop
) {
573 scrollArticle(-ctr
.offsetHeight
/3);
574 } else if (!noscroll
&& prev_article
&&
575 prev_article
.offsetTop
< ctr
.scrollTop
) {
576 cdmExpandArticle(prev_id
, noexpand
);
577 scrollArticle(-ctr
.offsetHeight
/4);
578 } else if (prev_id
) {
579 cdmExpandArticle(prev_id
, noexpand
);
580 cdmScrollToArticleId(prev_id
, noscroll
);
583 } else if (prev_id
) {
584 correctHeadlinesOffset(prev_id
);
585 view(prev_id
, getActiveFeedId(), noexpand
);
592 function toggleSelected(id
, force_on
) {
593 const row
= $("RROW-" + id
);
596 const cb
= dijit
.getEnclosingWidget(
597 row
.getElementsByClassName("rchk")[0]);
599 if (row
.hasClassName('Selected') && !force_on
) {
600 row
.removeClassName('Selected');
601 if (cb
) cb
.attr("checked", false);
603 row
.addClassName('Selected');
604 if (cb
) cb
.attr("checked", true);
608 updateSelectedPrompt();
611 function updateSelectedPrompt() {
612 const count
= getSelectedArticleIds2().length
;
613 const elem
= $("selected_prompt");
616 elem
.innerHTML
= ngettext("%d article selected",
617 "%d articles selected", count
).replace("%d", count
);
627 function toggleUnread(id
, cmode
, effect
) {
628 const row
= $("RROW-" + id
);
630 const tmpClassName
= row
.className
;
632 if (cmode
== undefined || cmode
== 2) {
633 if (row
.hasClassName("Unread")) {
634 row
.removeClassName("Unread");
637 row
.addClassName("Unread");
640 } else if (cmode
== 0) {
642 row
.removeClassName("Unread");
644 } else if (cmode
== 1) {
645 row
.addClassName("Unread");
648 if (cmode
== undefined) cmode
= 2;
650 const query
= "?op=rpc&method=catchupSelected" +
651 "&cmode=" + param_escape(cmode
) + "&ids=" + param_escape(id
);
653 // notify_progress("Loading, please wait...");
655 if (tmpClassName
!= row
.className
) {
656 new Ajax
.Request("backend.php", {
658 onComplete: function (transport
) {
659 handle_rpc_json(transport
);
667 function selectionRemoveLabel(id
, ids
) {
668 if (!ids
) ids
= getSelectedArticleIds2();
670 if (ids
.length
== 0) {
671 alert(__("No articles are selected."));
675 const query
= "?op=article&method=removeFromLabel&ids=" +
676 param_escape(ids
.toString()) + "&lid=" + param_escape(id
);
680 new Ajax
.Request("backend.php", {
682 onComplete: function(transport
) {
683 handle_rpc_json(transport
);
684 show_labels_in_headlines(transport
);
689 function selectionAssignLabel(id
, ids
) {
690 if (!ids
) ids
= getSelectedArticleIds2();
692 if (ids
.length
== 0) {
693 alert(__("No articles are selected."));
697 const query
= "?op=article&method=assignToLabel&ids=" +
698 param_escape(ids
.toString()) + "&lid=" + param_escape(id
);
702 new Ajax
.Request("backend.php", {
704 onComplete: function(transport
) {
705 handle_rpc_json(transport
);
706 show_labels_in_headlines(transport
);
710 function selectionToggleUnread(set_state
, callback
, no_error
, ids
) {
711 const rows
= ids
? ids
: getSelectedArticleIds2();
713 if (rows
.length
== 0 && !no_error
) {
714 alert(__("No articles are selected."));
718 for (let i
= 0; i
< rows
.length
; i
++) {
719 const row
= $("RROW-" + rows
[i
]);
721 if (set_state
== undefined) {
722 if (row
.hasClassName("Unread")) {
723 row
.removeClassName("Unread");
725 row
.addClassName("Unread");
729 if (set_state
== false) {
730 row
.removeClassName("Unread");
733 if (set_state
== true) {
734 row
.addClassName("Unread");
739 updateFloatingTitle(true);
741 if (rows
.length
> 0) {
745 if (set_state
== undefined) {
747 } else if (set_state
== true) {
749 } else if (set_state
== false) {
753 const query
= "?op=rpc&method=catchupSelected" +
754 "&cmode=" + cmode
+ "&ids=" + param_escape(rows
.toString());
756 notify_progress("Loading, please wait...");
758 new Ajax
.Request("backend.php", {
760 onComplete: function(transport
) {
761 handle_rpc_json(transport
);
762 if (callback
) callback(transport
);
769 function selectionToggleMarked(sel_state
, callback
, no_error
, ids
) {
770 const rows
= ids
? ids
: getSelectedArticleIds2();
772 if (rows
.length
== 0 && !no_error
) {
773 alert(__("No articles are selected."));
777 for (let i
= 0; i
< rows
.length
; i
++) {
778 toggleMark(rows
[i
], true, true);
781 if (rows
.length
> 0) {
783 const query
= "?op=rpc&method=markSelected&ids=" +
784 param_escape(rows
.toString()) + "&cmode=2";
786 new Ajax
.Request("backend.php", {
788 onComplete: function(transport
) {
789 handle_rpc_json(transport
);
790 if (callback
) callback(transport
);
797 function selectionTogglePublished(sel_state
, callback
, no_error
, ids
) {
798 const rows
= ids
? ids
: getSelectedArticleIds2();
800 if (rows
.length
== 0 && !no_error
) {
801 alert(__("No articles are selected."));
805 for (let i
= 0; i
< rows
.length
; i
++) {
806 togglePub(rows
[i
], true, true);
809 if (rows
.length
> 0) {
811 const query
= "?op=rpc&method=publishSelected&ids=" +
812 param_escape(rows
.toString()) + "&cmode=2";
814 new Ajax
.Request("backend.php", {
816 onComplete: function(transport
) {
817 handle_rpc_json(transport
);
823 function getSelectedArticleIds2() {
827 $$("#headlines-frame > div[id*=RROW][class*=Selected]").each(
829 rv
.push(child
.getAttribute("data-article-id"));
835 function getLoadedArticleIds() {
838 const children
= $$("#headlines-frame > div[id*=RROW-]");
840 children
.each(function(child
) {
841 if (Element
.visible(child
)) {
842 rv
.push(child
.getAttribute("data-article-id"));
850 // mode = all,none,unread,invert,marked,published
851 function selectArticles(mode
, query
) {
852 if (!query
) query
= "#headlines-frame > div[id*=RROW]";
854 const children
= $$(query
);
856 children
.each(function(child
) {
857 const id
= child
.getAttribute("data-article-id");
859 const cb
= dijit
.getEnclosingWidget(
860 child
.getElementsByClassName("rchk")[0]);
863 child
.addClassName("Selected");
864 if (cb
) cb
.attr("checked", true);
865 } else if (mode
== "unread") {
866 if (child
.hasClassName("Unread")) {
867 child
.addClassName("Selected");
868 if (cb
) cb
.attr("checked", true);
870 child
.removeClassName("Selected");
871 if (cb
) cb
.attr("checked", false);
873 } else if (mode
== "marked") {
874 if (child
.hasClassName("marked")) {
875 child
.addClassName("Selected");
876 if (cb
) cb
.attr("checked", true);
878 child
.removeClassName("Selected");
879 if (cb
) cb
.attr("checked", false);
881 } else if (mode
== "published") {
882 if (child
.hasClassName("published")) {
883 child
.addClassName("Selected");
884 if (cb
) cb
.attr("checked", true);
886 child
.removeClassName("Selected");
887 if (cb
) cb
.attr("checked", false);
890 } else if (mode
== "invert") {
891 if (child
.hasClassName("Selected")) {
892 child
.removeClassName("Selected");
893 if (cb
) cb
.attr("checked", false);
895 child
.addClassName("Selected");
896 if (cb
) cb
.attr("checked", true);
900 child
.removeClassName("Selected");
901 if (cb
) cb
.attr("checked", false);
905 updateSelectedPrompt();
908 function deleteSelection() {
910 const rows
= getSelectedArticleIds2();
912 if (rows
.length
== 0) {
913 alert(__("No articles are selected."));
917 const fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
920 if (getActiveFeedId() != 0) {
921 str
= ngettext("Delete %d selected article in %s?", "Delete %d selected articles in %s?", rows
.length
);
923 str
= ngettext("Delete %d selected article?", "Delete %d selected articles?", rows
.length
);
926 str
= str
.replace("%d", rows
.length
);
927 str
= str
.replace("%s", fn
);
929 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
933 const query
= "?op=rpc&method=delete&ids=" + param_escape(rows
);
937 new Ajax
.Request("backend.php", {
939 onComplete: function (transport
) {
940 handle_rpc_json(transport
);
946 function archiveSelection() {
948 const rows
= getSelectedArticleIds2();
950 if (rows
.length
== 0) {
951 alert(__("No articles are selected."));
955 const fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
959 if (getActiveFeedId() != 0) {
960 str
= ngettext("Archive %d selected article in %s?", "Archive %d selected articles in %s?", rows
.length
);
963 str
= ngettext("Move %d archived article back?", "Move %d archived articles back?", rows
.length
);
965 str
+= " " + __("Please note that unstarred articles might get purged on next feed update.");
970 str
= str
.replace("%d", rows
.length
);
971 str
= str
.replace("%s", fn
);
973 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
977 const query
= "?op=rpc&method="+op
+"&ids=" + param_escape(rows
);
981 for (let i
= 0; i
< rows
.length
; i
++) {
982 cache_delete("article:" + rows
[i
]);
985 new Ajax
.Request("backend.php", {
987 onComplete: function(transport
) {
988 handle_rpc_json(transport
);
994 function catchupSelection() {
996 const rows
= getSelectedArticleIds2();
998 if (rows
.length
== 0) {
999 alert(__("No articles are selected."));
1003 const fn
= getFeedName(getActiveFeedId(), activeFeedIsCat());
1005 let str
= ngettext("Mark %d selected article in %s as read?", "Mark %d selected articles in %s as read?", rows
.length
);
1007 str
= str
.replace("%d", rows
.length
);
1008 str
= str
.replace("%s", fn
);
1010 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str
)) {
1014 selectionToggleUnread(false, 'viewCurrentFeed()', true);
1017 function editArticleTags(id
) {
1018 const query
= "backend.php?op=article&method=editArticleTags¶m=" + param_escape(id
);
1020 if (dijit
.byId("editTagsDlg"))
1021 dijit
.byId("editTagsDlg").destroyRecursive();
1023 const dialog
= new dijit
.Dialog({
1025 title
: __("Edit article Tags"),
1026 style
: "width: 600px",
1027 execute: function() {
1028 if (this.validate()) {
1029 const query
= dojo
.objectToQuery(this.attr('value'));
1031 notify_progress("Saving article tags...", true);
1033 new Ajax
.Request("backend.php", {
1035 onComplete: function(transport
) {
1040 const data
= JSON
.parse(transport
.responseText
);
1047 const tags
= $("ATSTR-" + id
);
1048 const tooltip
= dijit
.byId("ATSTRTIP-" + id
);
1050 if (tags
) tags
.innerHTML
= data
.content
;
1051 if (tooltip
) tooltip
.attr('label', data
.content_full
);
1063 var tmph
= dojo
.connect(dialog
, 'onLoad', function() {
1064 dojo
.disconnect(tmph
);
1066 new Ajax
.Autocompleter('tags_str', 'tags_choices',
1067 "backend.php?op=article&method=completeTags",
1068 { tokens
: ',', paramName
: "search" });
1075 function cdmScrollToArticleId(id
, force
) {
1076 const ctr
= $("headlines-frame");
1077 const e
= $("RROW-" + id
);
1079 if (!e
|| !ctr
) return;
1081 if (force
|| e
.offsetTop
+e
.offsetHeight
> (ctr
.scrollTop
+ctr
.offsetHeight
) ||
1082 e
.offsetTop
< ctr
.scrollTop
) {
1084 // expanded cdm has a 4px margin now
1085 ctr
.scrollTop
= parseInt(e
.offsetTop
) - 4;
1089 function setActiveArticleId(id
) {
1090 console
.log("setActiveArticleId:" + id
);
1092 _active_article_id
= id
;
1093 PluginHost
.run(PluginHost
.HOOK_ARTICLE_SET_ACTIVE
, _active_article_id
);
1096 function getActiveArticleId() {
1097 return _active_article_id
;
1100 function postMouseIn(e
, id
) {
1101 post_under_pointer
= id
;
1104 function postMouseOut(id
) {
1105 post_under_pointer
= false;
1108 function unpackVisibleHeadlines() {
1109 if (!isCdmMode() || !getInitParam("cdm_expanded")) return;
1111 $$("#headlines-frame span.cencw[id]").each(
1113 const row
= $("RROW-" + child
.id
.replace("CENCW-", ""));
1115 if (row
&& row
.offsetTop
<= $("headlines-frame").scrollTop
+
1116 $("headlines-frame").offsetHeight
) {
1118 //console.log("unpacking: " + child.id);
1120 child
.innerHTML
= htmlspecialchars_decode(child
.innerHTML
);
1121 child
.removeAttribute('id');
1123 PluginHost
.run(PluginHost
.HOOK_ARTICLE_RENDERED_CDM
, row
);
1125 Element
.show(child
);
1131 function headlines_scroll_handler(e
) {
1134 // rate-limit in case of smooth scrolling and similar abominations
1135 if (Math
.max(e
.scrollTop
, _headlines_scroll_offset
) - Math
.min(e
.scrollTop
, _headlines_scroll_offset
) < 25) {
1139 _headlines_scroll_offset
= e
.scrollTop
;
1141 const hsp
= $("headlines-spacer");
1143 unpackVisibleHeadlines();
1145 // set topmost child in the buffer as active
1146 if (isCdmMode() && getInitParam("cdm_auto_catchup") == 1 &&
1147 getSelectedArticleIds2().length
<= 1 &&
1148 getInitParam("cdm_expanded")) {
1150 const rows
= $$("#headlines-frame > div[id*=RROW]");
1152 for (let i
= 0; i
< rows
.length
; i
++) {
1153 const child
= rows
[i
];
1155 if ($("headlines-frame").scrollTop
<= child
.offsetTop
&&
1156 child
.offsetTop
- $("headlines-frame").scrollTop
< 100 &&
1157 child
.getAttribute("data-article-id") != _active_article_id
) {
1159 if (_active_article_id
) {
1160 const row
= $("RROW-" + _active_article_id
);
1161 if (row
) row
.removeClassName("active");
1164 _active_article_id
= child
.getAttribute("data-article-id");
1165 showArticleInHeadlines(_active_article_id
, true);
1166 updateSelectedPrompt();
1172 if (!_infscroll_disable
) {
1173 if (hsp
&& hsp
.offsetTop
- 250 <= e
.scrollTop
+ e
.offsetHeight
) {
1175 hsp
.innerHTML
= "<span class='loading'><img src='images/indicator_tiny.gif'> " +
1176 __("Loading, please wait...") + "</span>";
1178 loadMoreHeadlines();
1185 updateFloatingTitle();
1188 catchupCurrentBatchIfNeeded();
1190 if (getInitParam("cdm_auto_catchup") == 1) {
1192 // let's get DOM some time to settle down
1193 const ts
= new Date().getTime();
1194 if (ts
- _last_headlines_update
< 100) return;
1196 $$("#headlines-frame > div[id*=RROW][class*=Unread]").each(
1198 if ($("headlines-frame").scrollTop
> (child
.offsetTop
+ child
.offsetHeight
/2)) {
1200 const id
= child
.getAttribute("data-article-id")
1202 if (catchup_id_batch
.indexOf(id
) == -1)
1203 catchup_id_batch
.push(id
);
1205 //console.log("auto_catchup_batch: " + catchup_id_batch.toString());
1210 if (_infscroll_disable
) {
1211 const child
= $$("#headlines-frame div[id*=RROW]").last();
1213 if (child
&& $("headlines-frame").scrollTop
>
1214 (child
.offsetTop
+ child
.offsetHeight
- 50)) {
1216 console
.log("we seem to be at an end");
1218 if (getInitParam("on_catchup_show_next_feed") == "1") {
1219 openNextUnreadFeed();
1226 console
.warn("headlines_scroll_handler: " + e
);
1230 function openNextUnreadFeed() {
1231 const is_cat
= activeFeedIsCat();
1232 const nuf
= getNextUnreadFeed(getActiveFeedId(), is_cat
);
1233 if (nuf
) viewfeed({feed
: nuf
, is_cat
: is_cat
});
1236 function catchupBatchedArticles() {
1237 if (catchup_id_batch
.length
> 0 && !_infscroll_request_sent
&& !_catchup_request_sent
) {
1239 console
.log("catchupBatchedArticles: working");
1241 // make a copy of the array
1242 const batch
= catchup_id_batch
.slice();
1243 const query
= "?op=rpc&method=catchupSelected" +
1244 "&cmode=0&ids=" + param_escape(batch
.toString());
1248 _catchup_request_sent
= true;
1250 new Ajax
.Request("backend.php", {
1252 onComplete: function (transport
) {
1253 handle_rpc_json(transport
);
1255 _catchup_request_sent
= false;
1257 const reply
= JSON
.parse(transport
.responseText
);
1258 const batch
= reply
.ids
;
1260 batch
.each(function (id
) {
1262 const elem
= $("RROW-" + id
);
1263 if (elem
) elem
.removeClassName("Unread");
1264 catchup_id_batch
.remove(id
);
1267 updateFloatingTitle(true);
1274 function catchupRelativeToArticle(below
, id
) {
1276 if (!id
) id
= getActiveArticleId();
1279 alert(__("No article is selected."));
1283 const visible_ids
= getLoadedArticleIds();
1285 const ids_to_mark
= [];
1288 for (var i
= 0; i
< visible_ids
.length
; i
++) {
1289 if (visible_ids
[i
] != id
) {
1290 var e
= $("RROW-" + visible_ids
[i
]);
1292 if (e
&& e
.hasClassName("Unread")) {
1293 ids_to_mark
.push(visible_ids
[i
]);
1300 for (var i
= visible_ids
.length
- 1; i
>= 0; i
--) {
1301 if (visible_ids
[i
] != id
) {
1302 var e
= $("RROW-" + visible_ids
[i
]);
1304 if (e
&& e
.hasClassName("Unread")) {
1305 ids_to_mark
.push(visible_ids
[i
]);
1313 if (ids_to_mark
.length
== 0) {
1314 alert(__("No articles found to mark"));
1316 const msg
= ngettext("Mark %d article as read?", "Mark %d articles as read?", ids_to_mark
.length
).replace("%d", ids_to_mark
.length
);
1318 if (getInitParam("confirm_feed_catchup") != 1 || confirm(msg
)) {
1320 for (var i
= 0; i
< ids_to_mark
.length
; i
++) {
1321 var e
= $("RROW-" + ids_to_mark
[i
]);
1322 e
.removeClassName("Unread");
1325 const query
= "?op=rpc&method=catchupSelected" +
1326 "&cmode=0" + "&ids=" + param_escape(ids_to_mark
.toString());
1328 new Ajax
.Request("backend.php", {
1330 onComplete: function (transport
) {
1331 handle_rpc_json(transport
);
1339 function cdmCollapseArticle(event
, id
, unmark
) {
1340 if (unmark
== undefined) unmark
= true;
1342 const row
= $("RROW-" + id
);
1343 const elem
= $("CICD-" + id
);
1346 const collapse
= row
.select("span[class='collapseBtn']")[0];
1349 Element
.show("CEXC-" + id
);
1350 Element
.hide(collapse
);
1353 row
.removeClassName("active");
1355 markHeadline(id
, false);
1357 if (id
== getActiveArticleId()) {
1358 setActiveArticleId(0);
1361 updateSelectedPrompt();
1364 if (event
) Event
.stop(event
);
1366 PluginHost
.run(PluginHost
.HOOK_ARTICLE_COLLAPSED
, id
);
1368 if (row
.offsetTop
< $("headlines-frame").scrollTop
)
1369 scrollToRowId(row
.id
);
1371 $("floatingTitle").style
.visibility
= "hidden";
1372 $("floatingTitle").setAttribute("data-article-id", 0);
1376 function cdmExpandArticle(id
, noexpand
) {
1377 console
.log("cdmExpandArticle " + id
);
1379 const row
= $("RROW-" + id
);
1381 if (!row
) return false;
1383 const oldrow
= $("RROW-" + getActiveArticleId());
1385 let elem
= $("CICD-" + getActiveArticleId());
1387 if (id
== getActiveArticleId() && Element
.visible(elem
))
1390 selectArticles("none");
1392 const old_offset
= row
.offsetTop
;
1394 if (getActiveArticleId() && elem
&& !getInitParam("cdm_expanded")) {
1395 let collapse
= oldrow
.select("span[class='collapseBtn']")[0];
1398 Element
.show("CEXC-" + getActiveArticleId());
1399 Element
.hide(collapse
);
1402 if (oldrow
) oldrow
.removeClassName("active");
1404 setActiveArticleId(id
);
1406 elem
= $("CICD-" + id
);
1408 let collapse
= row
.select("span[class='collapseBtn']")[0];
1410 const cencw
= $("CENCW-" + id
);
1412 if (!Element
.visible(elem
) && !noexpand
) {
1414 cencw
.innerHTML
= htmlspecialchars_decode(cencw
.innerHTML
);
1415 cencw
.setAttribute('id', '');
1416 Element
.show(cencw
);
1420 Element
.hide("CEXC-" + id
);
1421 Element
.show(collapse
);
1424 const new_offset
= row
.offsetTop
;
1426 if (old_offset
> new_offset
)
1427 $("headlines-frame").scrollTop
-= (old_offset
- new_offset
);
1430 if (catchup_id_batch
.indexOf(id
) == -1)
1431 catchup_id_batch
.push(id
);
1433 catchupCurrentBatchIfNeeded();
1437 row
.addClassName("active");
1439 PluginHost
.run(PluginHost
.HOOK_ARTICLE_EXPANDED
, id
);
1444 function getArticleUnderPointer() {
1445 return post_under_pointer
;
1448 function scrollArticle(offset
) {
1450 const ci
= $("content-insert");
1452 ci
.scrollTop
+= offset
;
1455 const hi
= $("headlines-frame");
1457 hi
.scrollTop
+= offset
;
1463 function show_labels_in_headlines(transport
) {
1464 const data
= JSON
.parse(transport
.responseText
);
1467 data
['info-for-headlines'].each(function (elem
) {
1468 $$(".HLLCTR-" + elem
.id
).each(function (ctr
) {
1469 ctr
.innerHTML
= elem
.labels
;
1475 function cdmClicked(event
, id
, in_body
) {
1476 //var shift_key = event.shiftKey;
1478 if (!event
.ctrlKey
&& !event
.metaKey
) {
1480 if (!getInitParam("cdm_expanded")) {
1481 return cdmExpandArticle(id
);
1484 let elem
= $("RROW-" + getActiveArticleId());
1486 if (elem
) elem
.removeClassName("active");
1488 selectArticles("none");
1491 elem
= $("RROW-" + id
);
1492 const article_is_unread
= elem
.hasClassName("Unread");
1494 elem
.removeClassName("Unread");
1495 elem
.addClassName("active");
1497 setActiveArticleId(id
);
1499 if (article_is_unread
) {
1500 decrementFeedCounter(getActiveFeedId(), activeFeedIsCat());
1501 updateFloatingTitle(true);
1504 const query
= "?op=rpc&method=catchupSelected" +
1505 "&cmode=0&ids=" + param_escape(id
);
1507 new Ajax
.Request("backend.php", {
1509 onComplete: function (transport
) {
1510 handle_rpc_json(transport
);
1514 return !event
.shiftKey
;
1517 } else if (!in_body
) {
1519 toggleSelected(id
, true);
1521 let elem
= $("RROW-" + id
);
1522 const article_is_unread
= elem
.hasClassName("Unread");
1524 if (article_is_unread
) {
1525 decrementFeedCounter(getActiveFeedId(), activeFeedIsCat());
1528 toggleUnread(id
, 0, false);
1530 openArticleInNewWindow(id
);
1535 const unread_in_buffer
= $$("#headlines-frame > div[id*=RROW][class*=Unread]").length
1536 request_counters(unread_in_buffer
== 0);
1541 function hlClicked(event
, id
) {
1542 if (event
.which
== 2) {
1545 } else if (event
.ctrlKey
|| event
.metaKey
) {
1546 openArticleInNewWindow(id
);
1554 function openArticleInNewWindow(id
) {
1555 toggleUnread(id
, 0, false);
1557 const w
= window
.open("");
1559 w
.location
= "backend.php?op=article&method=redirect&id=" + id
;
1562 function isCdmMode() {
1563 return getInitParam("combined_display_mode");
1566 function markHeadline(id
, marked
) {
1567 if (marked
== undefined) marked
= true;
1569 const row
= $("RROW-" + id
);
1571 const check
= dijit
.getEnclosingWidget(
1572 row
.getElementsByClassName("rchk")[0]);
1575 check
.attr("checked", marked
);
1579 row
.addClassName("Selected");
1581 row
.removeClassName("Selected");
1585 function getRelativePostIds(id
, limit
) {
1589 if (!limit
) limit
= 6; //3
1591 const ids
= getLoadedArticleIds();
1593 for (let i
= 0; i
< ids
.length
; i
++) {
1595 for (let k
= 1; k
<= limit
; k
++) {
1596 //if (i > k-1) tmp.push(ids[i-k]);
1597 if (i
< ids
.length
- k
) tmp
.push(ids
[i
+ k
]);
1606 function correctHeadlinesOffset(id
) {
1608 const container
= $("headlines-frame");
1609 const row
= $("RROW-" + id
);
1611 if (!container
|| !row
) return;
1613 const viewport
= container
.offsetHeight
;
1615 const rel_offset_top
= row
.offsetTop
- container
.scrollTop
;
1616 const rel_offset_bottom
= row
.offsetTop
+ row
.offsetHeight
- container
.scrollTop
;
1618 //console.log("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom);
1619 //console.log("Vport: " + viewport);
1621 if (rel_offset_top
<= 0 || rel_offset_top
> viewport
) {
1622 container
.scrollTop
= row
.offsetTop
;
1623 } else if (rel_offset_bottom
> viewport
) {
1625 /* doesn't properly work with Opera in some cases because
1626 Opera fucks up element scrolling */
1628 container
.scrollTop
= row
.offsetTop
+ row
.offsetHeight
- viewport
;
1632 function headlineActionsChange(elem
) {
1634 elem
.attr('value', 'false');
1637 function closeArticlePanel() {
1639 if (dijit
.byId("content-insert"))
1640 dijit
.byId("headlines-wrap-inner").removeChild(
1641 dijit
.byId("content-insert"));
1644 function initFloatingMenu() {
1645 if (!dijit
.byId("floatingMenu")) {
1647 const menu
= new dijit
.Menu({
1649 targetNodeIds
: ["floatingTitle"]
1652 headlinesMenuCommon(menu
);
1658 function headlinesMenuCommon(menu
) {
1660 menu
.addChild(new dijit
.MenuItem({
1661 label
: __("Open original article"),
1662 onClick: function (event
) {
1663 openArticleInNewWindow(this.getParent().currentTarget
.getAttribute("data-article-id"));
1667 menu
.addChild(new dijit
.MenuItem({
1668 label
: __("Display article URL"),
1669 onClick: function (event
) {
1670 displayArticleUrl(this.getParent().currentTarget
.getAttribute("data-article-id"));
1674 menu
.addChild(new dijit
.MenuSeparator());
1676 menu
.addChild(new dijit
.MenuItem({
1677 label
: __("Toggle unread"),
1678 onClick: function () {
1680 let ids
= getSelectedArticleIds2();
1682 const id
= (this.getParent().currentTarget
.getAttribute("data-article-id")) + "";
1683 ids
= ids
.length
!= 0 && ids
.indexOf(id
) != -1 ? ids
: [id
];
1685 selectionToggleUnread(undefined, false, true, ids
);
1689 menu
.addChild(new dijit
.MenuItem({
1690 label
: __("Toggle starred"),
1691 onClick: function () {
1692 let ids
= getSelectedArticleIds2();
1694 const id
= (this.getParent().currentTarget
.getAttribute("data-article-id")) + "";
1695 ids
= ids
.length
!= 0 && ids
.indexOf(id
) != -1 ? ids
: [id
];
1697 selectionToggleMarked(undefined, false, true, ids
);
1701 menu
.addChild(new dijit
.MenuItem({
1702 label
: __("Toggle published"),
1703 onClick: function () {
1704 let ids
= getSelectedArticleIds2();
1706 const id
= (this.getParent().currentTarget
.getAttribute("data-article-id")) + "";
1707 ids
= ids
.length
!= 0 && ids
.indexOf(id
) != -1 ? ids
: [id
];
1709 selectionTogglePublished(undefined, false, true, ids
);
1713 menu
.addChild(new dijit
.MenuSeparator());
1715 menu
.addChild(new dijit
.MenuItem({
1716 label
: __("Mark above as read"),
1717 onClick: function () {
1718 catchupRelativeToArticle(0, this.getParent().currentTarget
.getAttribute("data-article-id"));
1722 menu
.addChild(new dijit
.MenuItem({
1723 label
: __("Mark below as read"),
1724 onClick: function () {
1725 catchupRelativeToArticle(1, this.getParent().currentTarget
.getAttribute("data-article-id"));
1730 const labels
= getInitParam("labels");
1732 if (labels
&& labels
.length
) {
1734 menu
.addChild(new dijit
.MenuSeparator());
1736 const labelAddMenu
= new dijit
.Menu({ownerMenu
: menu
});
1737 const labelDelMenu
= new dijit
.Menu({ownerMenu
: menu
});
1739 labels
.each(function (label
) {
1740 const bare_id
= label
.id
;
1741 const name
= label
.caption
;
1743 labelAddMenu
.addChild(new dijit
.MenuItem({
1746 onClick: function () {
1748 let ids
= getSelectedArticleIds2();
1750 const id
= (this.getParent().ownerMenu
.currentTarget
.getAttribute("data-article-id")) + "";
1752 ids
= ids
.length
!= 0 && ids
.indexOf(id
) != -1 ? ids
: [id
];
1754 selectionAssignLabel(this.labelId
, ids
);
1758 labelDelMenu
.addChild(new dijit
.MenuItem({
1761 onClick: function () {
1762 let ids
= getSelectedArticleIds2();
1764 const id
= (this.getParent().ownerMenu
.currentTarget
.getAttribute("data-article-id")) + "";
1766 ids
= ids
.length
!= 0 && ids
.indexOf(id
) != -1 ? ids
: [id
];
1768 selectionRemoveLabel(this.labelId
, ids
);
1774 menu
.addChild(new dijit
.PopupMenuItem({
1775 label
: __("Assign label"),
1779 menu
.addChild(new dijit
.PopupMenuItem({
1780 label
: __("Remove label"),
1787 function initHeadlinesMenu() {
1788 if (!dijit
.byId("headlinesMenu")) {
1790 const menu
= new dijit
.Menu({
1791 id
: "headlinesMenu",
1792 targetNodeIds
: ["headlines-frame"],
1793 selector
: ".hlMenuAttach"
1796 headlinesMenuCommon(menu
);
1801 /* vgroup feed title menu */
1803 if (!dijit
.byId("headlinesFeedTitleMenu")) {
1805 const menu
= new dijit
.Menu({
1806 id
: "headlinesFeedTitleMenu",
1807 targetNodeIds
: ["headlines-frame"],
1808 selector
: "div.cdmFeedTitle"
1811 menu
.addChild(new dijit
.MenuItem({
1812 label
: __("Select articles in group"),
1813 onClick: function (event
) {
1814 selectArticles("all",
1815 "#headlines-frame > div[id*=RROW]" +
1816 "[data-orig-feed-id='" + this.getParent().currentTarget
.getAttribute("data-feed-id") + "']");
1821 menu
.addChild(new dijit
.MenuItem({
1822 label
: __("Mark group as read"),
1823 onClick: function () {
1824 selectArticles("none");
1825 selectArticles("all",
1826 "#headlines-frame > div[id*=RROW]" +
1827 "[data-orig-feed-id='" + this.getParent().currentTarget
.getAttribute("data-feed-id") + "']");
1833 menu
.addChild(new dijit
.MenuItem({
1834 label
: __("Mark feed as read"),
1835 onClick: function () {
1836 catchupFeedInGroup(this.getParent().currentTarget
.getAttribute("data-feed-id"));
1840 menu
.addChild(new dijit
.MenuItem({
1841 label
: __("Edit feed"),
1842 onClick: function () {
1843 editFeed(this.getParent().currentTarget
.getAttribute("data-feed-id"));
1851 function cache_set(id
, obj
) {
1852 //console.log("cache_set: " + id);
1855 sessionStorage
[id
] = obj
;
1857 sessionStorage
.clear();
1861 function cache_get(id
) {
1863 return sessionStorage
[id
];
1866 function cache_clear() {
1868 sessionStorage
.clear();
1871 function cache_delete(id
) {
1873 sessionStorage
.removeItem(id
);
1876 function cancelSearch() {
1881 function setSelectionScore() {
1882 const ids
= getSelectedArticleIds2();
1884 if (ids
.length
> 0) {
1887 const score
= prompt(__("Please enter new score for selected articles:"));
1889 if (score
!= undefined) {
1890 const query
= "op=article&method=setScore&id=" + param_escape(ids
.toString()) +
1891 "&score=" + param_escape(score
);
1893 new Ajax
.Request("backend.php", {
1895 onComplete: function (transport
) {
1896 const reply
= JSON
.parse(transport
.responseText
);
1900 ids
.each(function (id
) {
1901 const row
= $("RROW-" + id
);
1904 const pic
= row
.getElementsByClassName("hlScorePic")[0];
1907 pic
.src
= pic
.src
.replace(/score_.*?\.png/,
1908 reply
["score_pic"]);
1909 pic
.setAttribute("score", score
);
1919 alert(__("No articles are selected."));
1923 function updateScore(id
) {
1924 const pic
= $$("#RROW-" + id
+ " .hlScorePic")[0];
1928 const query
= "op=article&method=getScore&id=" + param_escape(id
);
1930 new Ajax
.Request("backend.php", {
1932 onComplete: function (transport
) {
1933 console
.log(transport
.responseText
);
1935 const reply
= JSON
.parse(transport
.responseText
);
1938 pic
.src
= pic
.src
.replace(/score_.*?\.png/, reply
["score_pic"]);
1939 pic
.setAttribute("score", reply
["score"]);
1940 pic
.setAttribute("title", reply
["score"]);
1947 function changeScore(id
, pic
) {
1948 const score
= pic
.getAttribute("score");
1950 const new_score
= prompt(__("Please enter new score for this article:"), score
);
1952 if (new_score
!= undefined) {
1954 const query
= "op=article&method=setScore&id=" + param_escape(id
) +
1955 "&score=" + param_escape(new_score
);
1957 new Ajax
.Request("backend.php", {
1959 onComplete: function (transport
) {
1960 const reply
= JSON
.parse(transport
.responseText
);
1963 pic
.src
= pic
.src
.replace(/score_.*?\.png/, reply
["score_pic"]);
1964 pic
.setAttribute("score", new_score
);
1965 pic
.setAttribute("title", new_score
);
1972 function displayArticleUrl(id
) {
1973 const query
= "op=rpc&method=getlinktitlebyid&id=" + param_escape(id
);
1975 new Ajax
.Request("backend.php", {
1977 onComplete: function (transport
) {
1978 const reply
= JSON
.parse(transport
.responseText
);
1980 if (reply
&& reply
.link
) {
1981 prompt(__("Article URL:"), reply
.link
);
1987 function scrollToRowId(id
) {
1991 $("headlines-frame").scrollTop
= row
.offsetTop
- 4;
1994 function updateFloatingTitle(unread_only
) {
1995 if (!isCdmMode()) return;
1997 const hf
= $("headlines-frame");
1999 const elems
= $$("#headlines-frame > div[id*=RROW]");
2001 for (let i
= 0; i
< elems
.length
; i
++) {
2003 const child
= elems
[i
];
2005 if (child
&& child
.offsetTop
+ child
.offsetHeight
> hf
.scrollTop
) {
2007 const header
= child
.getElementsByClassName("cdmHeader")[0];
2009 if (unread_only
|| child
.getAttribute("data-article-id") != $("floatingTitle").getAttribute("data-article-id")) {
2010 if (child
.getAttribute("data-article-id") != $("floatingTitle").getAttribute("data-article-id")) {
2012 $("floatingTitle").setAttribute("data-article-id", child
.getAttribute("data-article-id"));
2013 $("floatingTitle").innerHTML
= header
.innerHTML
;
2014 $("floatingTitle").firstChild
.innerHTML
= "<img class='anchor markedPic' src='images/page_white_go.png' onclick=\"scrollToRowId('" + child
.id
+ "')\">" + $("floatingTitle").firstChild
.innerHTML
;
2018 const cb
= $$("#floatingTitle .dijitCheckBox")[0];
2021 cb
.parentNode
.removeChild(cb
);
2024 if (child
.hasClassName("Unread"))
2025 $("floatingTitle").addClassName("Unread");
2027 $("floatingTitle").removeClassName("Unread");
2029 PluginHost
.run(PluginHost
.HOOK_FLOATING_TITLE
, child
);
2032 $("floatingTitle").style
.marginRight
= hf
.offsetWidth
- child
.offsetWidth
+ "px";
2033 if (header
.offsetTop
+ header
.offsetHeight
< hf
.scrollTop
+ $("floatingTitle").offsetHeight
- 5 &&
2034 child
.offsetTop
+ child
.offsetHeight
>= hf
.scrollTop
+ $("floatingTitle").offsetHeight
- 5)
2035 $("floatingTitle").style
.visibility
= "visible";
2037 $("floatingTitle").style
.visibility
= "hidden";
2045 function catchupCurrentBatchIfNeeded() {
2046 if (catchup_id_batch
.length
> 0) {
2047 window
.clearTimeout(catchup_timeout_id
);
2048 catchup_timeout_id
= window
.setTimeout(catchupBatchedArticles
, 1000);
2050 if (catchup_id_batch
.length
>= 10) {
2051 catchupBatchedArticles();