]> git.wh0rd.org Git - tt-rss.git/blob - js/viewfeed.js
3b647e08085b9f757e4ef70ceb870b6c8c552125
[tt-rss.git] / js / viewfeed.js
1 var article_cache = new Array();
2
3 var _active_article_id = 0;
4
5 var vgroup_last_feed = false;
6 var post_under_pointer = false;
7
8 var last_requested_article = false;
9
10 var catchup_id_batch = [];
11 var catchup_timeout_id = false;
12
13 var cids_requested = [];
14 var loaded_article_ids = [];
15 var _last_headlines_update = 0;
16 var current_first_id = 0;
17
18 var _catchup_request_sent = false;
19
20 var has_storage = 'sessionStorage' in window && window['sessionStorage'] !== null;
21
22 function headlines_callback2(transport, offset, background, infscroll_req) {
23         try {
24                 handle_rpc_json(transport);
25
26                 console.log("headlines_callback2 [offset=" + offset + "] B:" + background + " I:" + infscroll_req);
27
28                 var is_cat = false;
29                 var feed_id = false;
30
31                 var reply = false;
32
33                 try {
34                         reply = JSON.parse(transport.responseText);
35                 } catch (e) {
36                         console.error(e);
37                 }
38
39                 if (reply) {
40
41                         is_cat = reply['headlines']['is_cat'];
42                         feed_id = reply['headlines']['id'];
43
44                         if (background) {
45                                 var content = reply['headlines']['content'];
46
47                                 content = content + "<div id='headlines-spacer'></div>";
48                                 return;
49                         }
50
51                         if (feed_id != -7 && (feed_id != getActiveFeedId() || is_cat != activeFeedIsCat()))
52                                 return;
53
54                         /* dijit.getEnclosingWidget(
55                                 document.forms["main_toolbar_form"].update).attr('disabled',
56                                         is_cat || feed_id <= 0); */
57
58                         try {
59                                 if (infscroll_req == false) {
60                                         $("headlines-frame").scrollTop = 0;
61
62                                         $("floatingTitle").style.visibility = "hidden";
63                                         $("floatingTitle").setAttribute("rowid", 0);
64                                         $("floatingTitle").innerHTML = "";
65                                 }
66                         } catch (e) { };
67
68                         $("headlines-frame").removeClassName("cdm");
69                         $("headlines-frame").removeClassName("normal");
70
71                         $("headlines-frame").addClassName(isCdmMode() ? "cdm" : "normal");
72
73                         var headlines_count = reply['headlines-info']['count'];
74
75                         vgroup_last_feed = reply['headlines-info']['vgroup_last_feed'];
76
77                         if (parseInt(headlines_count) < 30) {
78                                 _infscroll_disable = 1;
79                         } else {
80                                 _infscroll_disable = 0;
81                         }
82
83                         current_first_id = reply['headlines']['first_id'];
84                         var counters = reply['counters'];
85                         var articles = reply['articles'];
86                         //var runtime_info = reply['runtime-info'];
87
88                         if (infscroll_req == false) {
89                                 loaded_article_ids = [];
90
91                                 dojo.html.set($("headlines-toolbar"),
92                                                 reply['headlines']['toolbar'],
93                                                 {parseContent: true});
94
95                                 /*dojo.html.set($("headlines-frame"),
96                                         reply['headlines']['content'],
97                                         {parseContent: true});
98
99                                 $$("#headlines-frame div[id*='RROW']").each(function(row) {
100                                         loaded_article_ids.push(row.id);
101                                 });*/
102
103                                 $("headlines-frame").innerHTML = '';
104
105                                 var tmp = new Element("div");
106                                 tmp.innerHTML = reply['headlines']['content'];
107                                 dojo.parser.parse(tmp);
108
109                                 while (tmp.hasChildNodes()) {
110                                         var row = tmp.removeChild(tmp.firstChild);
111
112                                         if (loaded_article_ids.indexOf(row.id) == -1 || row.hasClassName("cdmFeedTitle")) {
113                                                 dijit.byId("headlines-frame").domNode.appendChild(row);
114
115                                                 loaded_article_ids.push(row.id);
116                                         }
117                                 }
118
119                                 var hsp = $("headlines-spacer");
120                                 if (!hsp) hsp = new Element("DIV", {"id": "headlines-spacer"});
121                                 dijit.byId('headlines-frame').domNode.appendChild(hsp);
122
123                                 initHeadlinesMenu();
124
125                                 if (_infscroll_disable)
126                                         hsp.innerHTML = "<a href='#' onclick='openNextUnreadFeed()'>" +
127                                                 __("Click to open next unread feed.") + "</a>";
128
129                                 if (_search_query) {
130                                         $("feed_title").innerHTML += "<span id='cancel_search'>" +
131                                                 " (<a href='#' onclick='cancelSearch()'>" + __("Cancel search") + "</a>)" +
132                                                 "</span>";
133                                 }
134
135                         } else {
136
137                                 if (headlines_count > 0 && feed_id == getActiveFeedId() && is_cat == activeFeedIsCat()) {
138                                         console.log("adding some more headlines: " + headlines_count);
139
140                                         var c = dijit.byId("headlines-frame");
141                                         var ids = getSelectedArticleIds2();
142
143                                         var hsp = $("headlines-spacer");
144
145                                         if (hsp)
146                                                 c.domNode.removeChild(hsp);
147
148                                         var tmp = new Element("div");
149                                         tmp.innerHTML = reply['headlines']['content'];
150                                         dojo.parser.parse(tmp);
151
152                                         while (tmp.hasChildNodes()) {
153                                                 var row = tmp.removeChild(tmp.firstChild);
154
155                                                 if (loaded_article_ids.indexOf(row.id) == -1 || row.hasClassName("cdmFeedTitle")) {
156                                                         dijit.byId("headlines-frame").domNode.appendChild(row);
157
158                                                         loaded_article_ids.push(row.id);
159                                                 }
160                                         }
161
162                                         if (!hsp) hsp = new Element("DIV", {"id": "headlines-spacer"});
163                                         c.domNode.appendChild(hsp);
164
165                                         if (headlines_count < 30) _infscroll_disable = true;
166
167                                         console.log("restore selected ids: " + ids);
168
169                                         for (var i = 0; i < ids.length; i++) {
170                                                 markHeadline(ids[i]);
171                                         }
172
173                                         initHeadlinesMenu();
174
175                                         if (_infscroll_disable) {
176                                                 hsp.innerHTML = "<a href='#' onclick='openNextUnreadFeed()'>" +
177                                                 __("Click to open next unread feed.") + "</a>";
178                                         }
179
180                                 } else {
181                                         console.log("no new headlines received");
182
183                                         var first_id_changed = reply['headlines']['first_id_changed'];
184                                         console.log("first id changed:" + first_id_changed);
185
186                                         var hsp = $("headlines-spacer");
187
188                                         if (hsp) {
189                                                 if (first_id_changed) {
190                                                         hsp.innerHTML = "<a href='#' onclick='viewCurrentFeed()'>" +
191                                                         __("New articles found, reload feed to continue.") + "</a>";
192                                                 } else {
193                                                         hsp.innerHTML = "<a href='#' onclick='openNextUnreadFeed()'>" +
194                                                         __("Click to open next unread feed.") + "</a>";
195                                                 }
196
197                                         }
198
199                                 }
200                         }
201
202                         if (articles) {
203                                 for (var i = 0; i < articles.length; i++) {
204                                         var a_id = articles[i]['id'];
205                                         cache_set("article:" + a_id, articles[i]['content']);
206                                 }
207                         } else {
208                                 console.log("no cached articles received");
209                         }
210
211                         if (counters)
212                                 parse_counters(counters);
213                         else
214                                 request_counters(true);
215
216                 } else {
217                         console.error("Invalid object received: " + transport.responseText);
218                         dijit.byId("headlines-frame").attr('content', "<div class='whiteBox'>" +
219                                         __('Could not update headlines (invalid object received - see error console for details)') +
220                                         "</div>");
221                 }
222
223                 _infscroll_request_sent = 0;
224                 _last_headlines_update = new Date().getTime();
225
226                 unpackVisibleHeadlines();
227
228                 // if we have some more space in the buffer, why not try to fill it
229
230                 if (!_infscroll_disable && $("headlines-spacer") &&
231                                 $("headlines-spacer").offsetTop < $("headlines-frame").offsetHeight) {
232
233                         window.setTimeout(function() {
234                                 loadMoreHeadlines();
235                         }, 250);
236                 }
237
238                 notify("");
239
240         } catch (e) {
241                 exception_error("headlines_callback2", e, transport);
242         }
243 }
244
245 function render_article(article) {
246         try {
247                 dijit.byId("headlines-wrap-inner").addChild(
248                                 dijit.byId("content-insert"));
249
250                 var c = dijit.byId("content-insert");
251
252                 try {
253                         c.domNode.scrollTop = 0;
254                 } catch (e) { };
255
256                 c.attr('content', article);
257                 PluginHost.run(PluginHost.HOOK_ARTICLE_RENDERED, c.domNode);
258
259                 correctHeadlinesOffset(getActiveArticleId());
260
261                 try {
262                         c.focus();
263                 } catch (e) { };
264
265         } catch (e) {
266                 exception_error("render_article", e);
267         }
268 }
269
270 function showArticleInHeadlines(id, noexpand) {
271
272         try {
273                 selectArticles("none");
274
275                 var crow = $("RROW-" + id);
276
277                 if (!crow) return;
278
279                 var article_is_unread = crow.hasClassName("Unread");
280
281                 if (!noexpand)
282                         crow.removeClassName("Unread");
283                 crow.addClassName("active");
284
285                 selectArticles('none');
286
287                 var view_mode = false;
288
289                 try {
290                         view_mode = document.forms['main_toolbar_form'].view_mode;
291                         view_mode = view_mode[view_mode.selectedIndex].value;
292                 } catch (e) {
293                         //
294                 }
295
296                 markHeadline(id);
297
298                 if (article_is_unread && !noexpand)
299                         _force_scheduled_update = true;
300
301         } catch (e) {
302                 exception_error("showArticleInHeadlines", e);
303         }
304 }
305
306 function article_callback2(transport, id) {
307         try {
308                 console.log("article_callback2 " + id);
309
310                 handle_rpc_json(transport);
311
312                 var reply = false;
313
314                 try {
315                         reply = JSON.parse(transport.responseText);
316                 } catch (e) {
317                         console.error(e);
318                 }
319
320                 if (reply) {
321
322                         reply.each(function(article) {
323                                 if (getActiveArticleId() == article['id']) {
324                                         render_article(article['content']);
325                                 }
326                                 cids_requested.remove(article['id']);
327
328                                 cache_set("article:" + article['id'], article['content']);
329                         });
330
331 //                      if (id != last_requested_article) {
332 //                              console.log("requested article id is out of sequence, aborting");
333 //                              return;
334 //                      }
335
336                 } else {
337                         console.error("Invalid object received: " + transport.responseText);
338
339                         render_article("<div class='whiteBox'>" +
340                                         __('Could not display article (invalid object received - see error console for details)') + "</div>");
341                 }
342
343                 var unread_in_buffer = $$("#headlines-frame > div[id*=RROW][class*=Unread]").length
344                 request_counters(unread_in_buffer == 0);
345
346                 notify("");
347         } catch (e) {
348                 exception_error("article_callback2", e, transport);
349         }
350 }
351
352 function view(id, activefeed, noexpand) {
353         try {
354                 var oldrow = $("RROW-" + getActiveArticleId());
355                 if (oldrow) oldrow.removeClassName("active");
356
357                 var crow = $("RROW-" + id);
358
359                 if (!crow) return;
360                 if (noexpand) {
361                         setActiveArticleId(id);
362                         showArticleInHeadlines(id, noexpand);
363                         return;
364                 }
365
366                 console.log("loading article: " + id);
367
368                 var cached_article = cache_get("article:" + id);
369
370                 console.log("cache check result: " + (cached_article != false));
371
372                 var query = "?op=article&method=view&id=" + param_escape(id);
373
374                 var neighbor_ids = getRelativePostIds(id);
375
376                 /* only request uncached articles */
377
378                 var cids_to_request = [];
379
380                 for (var i = 0; i < neighbor_ids.length; i++) {
381                         if (cids_requested.indexOf(neighbor_ids[i]) == -1)
382                                 if (!cache_get("article:" + neighbor_ids[i])) {
383                                         cids_to_request.push(neighbor_ids[i]);
384                                         cids_requested.push(neighbor_ids[i]);
385                                 }
386                 }
387
388                 console.log("additional ids: " + cids_to_request.toString());
389
390                 query = query + "&cids=" + cids_to_request.toString();
391
392                 var article_is_unread = crow.hasClassName("Unread");
393
394                 setActiveArticleId(id);
395                 showArticleInHeadlines(id);
396
397                 if (cached_article && article_is_unread) {
398
399                         query = query + "&mode=prefetch";
400
401                         render_article(cached_article);
402
403                 } else if (cached_article) {
404
405                         query = query + "&mode=prefetch_old";
406                         render_article(cached_article);
407
408                         // if we don't need to request any relative ids, we might as well skip
409                         // the server roundtrip altogether
410                         if (cids_to_request.length == 0) {
411                                 return;
412                         }
413                 }
414
415                 last_requested_article = id;
416
417                 console.log(query);
418
419                 if (article_is_unread) {
420                         decrementFeedCounter(getActiveFeedId(), activeFeedIsCat());
421                 }
422
423                 new Ajax.Request("backend.php", {
424                         parameters: query,
425                         onComplete: function(transport) {
426                                 article_callback2(transport, id);
427                         } });
428
429                 return false;
430
431         } catch (e) {
432                 exception_error("view", e);
433         }
434 }
435
436 function toggleMark(id, client_only) {
437         try {
438                 var query = "?op=rpc&id=" + id + "&method=mark";
439
440                 var row = $("RROW-" + id);
441                 if (!row) return;
442
443                 var imgs = [];
444
445                 var row_imgs = row.getElementsByClassName("markedPic");
446
447                 for (var i = 0; i < row_imgs.length; i++)
448                         imgs.push(row_imgs[i]);
449
450                 var ft = $("floatingTitle");
451
452                 if (ft && ft.getAttribute("rowid") == "RROW-" + id) {
453                         var fte = ft.getElementsByClassName("markedPic");
454
455                         for (var i = 0; i < fte.length; i++)
456                                 imgs.push(fte[i]);
457                 }
458
459                 for (i = 0; i < imgs.length; i++) {
460                         var img = imgs[i];
461
462                         if (!row.hasClassName("marked")) {
463                                 img.src = img.src.replace("mark_unset", "mark_set");
464                                 img.alt = __("Unstar article");
465                                 query = query + "&mark=1";
466                         } else {
467                                 img.src = img.src.replace("mark_set", "mark_unset");
468                                 img.alt = __("Star article");
469                                 query = query + "&mark=0";
470                         }
471                 }
472
473                 row.toggleClassName("marked");
474
475                 if (!client_only) {
476                         new Ajax.Request("backend.php", {
477                                 parameters: query,
478                                 onComplete: function(transport) {
479                                         handle_rpc_json(transport);
480                                 } });
481                 }
482
483         } catch (e) {
484                 exception_error("toggleMark", e);
485         }
486 }
487
488 function togglePub(id, client_only, no_effects, note) {
489         try {
490                 var query = "?op=rpc&id=" + id + "&method=publ";
491
492                 if (note != undefined) {
493                         query = query + "&note=" + param_escape(note);
494                 } else {
495                         query = query + "&note=undefined";
496                 }
497
498                 var row = $("RROW-" + id);
499                 if (!row) return;
500
501                 var imgs = [];
502
503                 var row_imgs = row.getElementsByClassName("pubPic");
504
505                 for (var i = 0; i < row_imgs.length; i++)
506                         imgs.push(row_imgs[i]);
507
508                 var ft = $("floatingTitle");
509
510                 if (ft && ft.getAttribute("rowid") == "RROW-" + id) {
511                         var fte = ft.getElementsByClassName("pubPic");
512
513                         for (var i = 0; i < fte.length; i++)
514                                 imgs.push(fte[i]);
515                 }
516
517                 for (i = 0; i < imgs.length; i++) {
518                         var img = imgs[i];
519
520                         if (!row.hasClassName("published") || note != undefined) {
521                                 img.src = img.src.replace("pub_unset", "pub_set");
522                                 img.alt = __("Unpublish article");
523                                 query = query + "&pub=1";
524                         } else {
525                                 img.src = img.src.replace("pub_set", "pub_unset");
526                                 img.alt = __("Publish article");
527                                 query = query + "&pub=0";
528                         }
529                 }
530
531                 if (note != undefined)
532                         row.addClassName("published");
533                 else
534                         row.toggleClassName("published");
535
536                 if (!client_only) {
537                         new Ajax.Request("backend.php", {
538                                 parameters: query,
539                                 onComplete: function(transport) {
540                                         handle_rpc_json(transport);
541                                 } });
542                 }
543
544         } catch (e) {
545                 exception_error("togglePub", e);
546         }
547 }
548
549 function moveToPost(mode, noscroll, noexpand) {
550
551         try {
552
553                 var rows = getVisibleArticleIds();
554
555                 var prev_id = false;
556                 var next_id = false;
557
558                 if (!$('RROW-' + getActiveArticleId())) {
559                         setActiveArticleId(0);
560                 }
561
562                 if (!getActiveArticleId()) {
563                         next_id = rows[0];
564                         prev_id = rows[rows.length-1]
565                 } else {
566                         for (var i = 0; i < rows.length; i++) {
567                                 if (rows[i] == getActiveArticleId()) {
568
569                                         // Account for adjacent identical article ids.
570                                         if (i > 0) prev_id = rows[i-1];
571
572                                         for (var j = i+1; j < rows.length; j++) {
573                                                 if (rows[j] != getActiveArticleId()) {
574                                                         next_id = rows[j];
575                                                         break;
576                                                 }
577                                         }
578                                         break;
579                                 }
580                         }
581                 }
582
583                 console.log("cur: " + getActiveArticleId() + " next: " + next_id);
584
585                 if (mode == "next") {
586                         if (next_id || getActiveArticleId()) {
587                                 if (isCdmMode()) {
588
589                                         var article = $("RROW-" + getActiveArticleId());
590                                         var ctr = $("headlines-frame");
591
592                                         if (!noscroll && article && article.offsetTop + article.offsetHeight >
593                                                         ctr.scrollTop + ctr.offsetHeight) {
594
595                                                 scrollArticle(ctr.offsetHeight/4);
596
597                                         } else if (next_id) {
598                                                 cdmExpandArticle(next_id, noexpand);
599                                                 cdmScrollToArticleId(next_id, true);
600                                         }
601
602                                 } else if (next_id) {
603                                         correctHeadlinesOffset(next_id);
604                                         view(next_id, getActiveFeedId(), noexpand);
605                                 }
606                         }
607                 }
608
609                 if (mode == "prev") {
610                         if (prev_id || getActiveArticleId()) {
611                                 if (isCdmMode()) {
612
613                                         var article = $("RROW-" + getActiveArticleId());
614                                         var prev_article = $("RROW-" + prev_id);
615                                         var ctr = $("headlines-frame");
616
617                                         if (!getInitParam("cdm_expanded")) {
618
619                                                 if (!noscroll && article && article.offsetTop < ctr.scrollTop) {
620                                                         scrollArticle(-ctr.offsetHeight/4);
621                                                 } else {
622                                                         cdmExpandArticle(prev_id, noexpand);
623                                                         cdmScrollToArticleId(prev_id, true);
624                                                 }
625                                         } else {
626
627                                                 if (!noscroll && article && article.offsetTop < ctr.scrollTop) {
628                                                         scrollArticle(-ctr.offsetHeight/3);
629                                                 } else if (!noscroll && prev_article &&
630                                                                 prev_article.offsetTop < ctr.scrollTop) {
631                                                         cdmExpandArticle(prev_id, noexpand);
632                                                         scrollArticle(-ctr.offsetHeight/4);
633                                                 } else if (prev_id) {
634                                                         cdmExpandArticle(prev_id, noexpand);
635                                                         cdmScrollToArticleId(prev_id, noscroll);
636                                                 }
637                                         }
638
639                                 } else if (prev_id) {
640                                         correctHeadlinesOffset(prev_id);
641                                         view(prev_id, getActiveFeedId(), noexpand);
642                                 }
643                         }
644                 }
645
646         } catch (e) {
647                 exception_error("moveToPost", e);
648         }
649 }
650
651 function toggleSelected(id, force_on) {
652         try {
653                 var row = $("RROW-" + id);
654
655                 if (row) {
656                         var cb = dijit.getEnclosingWidget(
657                                         row.getElementsByClassName("rchk")[0]);
658
659                         if (row.hasClassName('Selected') && !force_on) {
660                                 row.removeClassName('Selected');
661                                 if (cb) cb.attr("checked", false);
662                         } else {
663                                 row.addClassName('Selected');
664                                 if (cb) cb.attr("checked", true);
665                         }
666                 }
667
668                 updateSelectedPrompt();
669         } catch (e) {
670                 exception_error("toggleSelected", e);
671         }
672 }
673
674 function updateSelectedPrompt() {
675         try {
676                 var count = getSelectedArticleIds2().size();
677                 var elem = $("selected_prompt");
678
679                 if (elem) {
680                         elem.innerHTML = ngettext("%d article selected",
681                                         "%d articles selected", count).replace("%d", count);
682
683                         if (count > 0)
684                                 Element.show(elem);
685                         else
686                                 Element.hide(elem);
687                 }
688
689         } catch (e) {
690                 exception_error("updateSelectedPrompt", e);
691         }
692 }
693
694 function toggleUnread_afh(effect) {
695         try {
696
697                 var elem = effect.element;
698                 elem.style.backgroundColor = "";
699
700         } catch (e) {
701                 exception_error("toggleUnread_afh", e);
702         }
703 }
704
705 function toggleUnread(id, cmode, effect) {
706         try {
707
708                 var row = $("RROW-" + id);
709                 if (row) {
710                         var tmpClassName = row.className;
711
712                         if (cmode == undefined || cmode == 2) {
713                                 if (row.hasClassName("Unread")) {
714                                         row.removeClassName("Unread");
715
716                                 } else {
717                                         row.addClassName("Unread");
718                                 }
719
720                         } else if (cmode == 0) {
721
722                                 row.removeClassName("Unread");
723
724                         } else if (cmode == 1) {
725                                 row.addClassName("Unread");
726                         }
727
728                         if (cmode == undefined) cmode = 2;
729
730                         var query = "?op=rpc&method=catchupSelected" +
731                                 "&cmode=" + param_escape(cmode) + "&ids=" + param_escape(id);
732
733 //                      notify_progress("Loading, please wait...");
734
735                         if (tmpClassName != row.className) {
736                                 new Ajax.Request("backend.php", {
737                                         parameters: query,
738                                         onComplete: function (transport) {
739                                                 handle_rpc_json(transport);
740                                         }
741                                 });
742                         }
743
744                 }
745
746         } catch (e) {
747                 exception_error("toggleUnread", e);
748         }
749 }
750
751 function selectionRemoveLabel(id, ids) {
752         try {
753
754                 if (!ids) ids = getSelectedArticleIds2();
755
756                 if (ids.length == 0) {
757                         alert(__("No articles are selected."));
758                         return;
759                 }
760
761                 var query = "?op=article&method=removeFromLabel&ids=" +
762                         param_escape(ids.toString()) + "&lid=" + param_escape(id);
763
764                 console.log(query);
765
766                 new Ajax.Request("backend.php", {
767                         parameters: query,
768                         onComplete: function(transport) {
769                                 handle_rpc_json(transport);
770                                 show_labels_in_headlines(transport);
771                         } });
772
773         } catch (e) {
774                 exception_error("selectionAssignLabel", e);
775
776         }
777 }
778
779 function selectionAssignLabel(id, ids) {
780         try {
781
782                 if (!ids) ids = getSelectedArticleIds2();
783
784                 if (ids.length == 0) {
785                         alert(__("No articles are selected."));
786                         return;
787                 }
788
789                 var query = "?op=article&method=assignToLabel&ids=" +
790                         param_escape(ids.toString()) + "&lid=" + param_escape(id);
791
792                 console.log(query);
793
794                 new Ajax.Request("backend.php", {
795                         parameters: query,
796                         onComplete: function(transport) {
797                                 handle_rpc_json(transport);
798                                 show_labels_in_headlines(transport);
799                         } });
800
801         } catch (e) {
802                 exception_error("selectionAssignLabel", e);
803
804         }
805 }
806
807 function selectionToggleUnread(set_state, callback, no_error, ids) {
808         try {
809                 var rows = ids ? ids : getSelectedArticleIds2();
810
811                 if (rows.length == 0 && !no_error) {
812                         alert(__("No articles are selected."));
813                         return;
814                 }
815
816                 for (var i = 0; i < rows.length; i++) {
817                         var row = $("RROW-" + rows[i]);
818                         if (row) {
819                                 if (set_state == undefined) {
820                                         if (row.hasClassName("Unread")) {
821                                                 row.removeClassName("Unread");
822                                         } else {
823                                                 row.addClassName("Unread");
824                                         }
825                                 }
826
827                                 if (set_state == false) {
828                                         row.removeClassName("Unread");
829                                 }
830
831                                 if (set_state == true) {
832                                         row.addClassName("Unread");
833                                 }
834                         }
835                 }
836
837                 updateFloatingTitle(true);
838
839                 if (rows.length > 0) {
840
841                         var cmode = "";
842
843                         if (set_state == undefined) {
844                                 cmode = "2";
845                         } else if (set_state == true) {
846                                 cmode = "1";
847                         } else if (set_state == false) {
848                                 cmode = "0";
849                         }
850
851                         var query = "?op=rpc&method=catchupSelected" +
852                                 "&cmode=" + cmode + "&ids=" + param_escape(rows.toString());
853
854                         notify_progress("Loading, please wait...");
855
856                         new Ajax.Request("backend.php", {
857                                 parameters: query,
858                                 onComplete: function(transport) {
859                                         handle_rpc_json(transport);
860                                         if (callback) callback(transport);
861                                 } });
862
863                 }
864
865         } catch (e) {
866                 exception_error("selectionToggleUnread", e);
867         }
868 }
869
870 // sel_state ignored
871 function selectionToggleMarked(sel_state, callback, no_error, ids) {
872         try {
873
874                 var rows = ids ? ids : getSelectedArticleIds2();
875
876                 if (rows.length == 0 && !no_error) {
877                         alert(__("No articles are selected."));
878                         return;
879                 }
880
881                 for (var i = 0; i < rows.length; i++) {
882                         toggleMark(rows[i], true, true);
883                 }
884
885                 if (rows.length > 0) {
886
887                         var query = "?op=rpc&method=markSelected&ids=" +
888                                 param_escape(rows.toString()) + "&cmode=2";
889
890                         new Ajax.Request("backend.php", {
891                                 parameters: query,
892                                 onComplete: function(transport) {
893                                         handle_rpc_json(transport);
894                                         if (callback) callback(transport);
895                                 } });
896
897                 }
898
899         } catch (e) {
900                 exception_error("selectionToggleMarked", e);
901         }
902 }
903
904 // sel_state ignored
905 function selectionTogglePublished(sel_state, callback, no_error, ids) {
906         try {
907
908                 var rows = ids ? ids : getSelectedArticleIds2();
909
910                 if (rows.length == 0 && !no_error) {
911                         alert(__("No articles are selected."));
912                         return;
913                 }
914
915                 for (var i = 0; i < rows.length; i++) {
916                         togglePub(rows[i], true, true);
917                 }
918
919                 if (rows.length > 0) {
920
921                         var query = "?op=rpc&method=publishSelected&ids=" +
922                                 param_escape(rows.toString()) + "&cmode=2";
923
924                         new Ajax.Request("backend.php", {
925                                 parameters: query,
926                                 onComplete: function(transport) {
927                                         handle_rpc_json(transport);
928                                 } });
929
930                 }
931
932         } catch (e) {
933                 exception_error("selectionToggleMarked", e);
934         }
935 }
936
937 function getSelectedArticleIds2() {
938
939         var rv = [];
940
941         $$("#headlines-frame > div[id*=RROW][class*=Selected]").each(
942                 function(child) {
943                         rv.push(child.id.replace("RROW-", ""));
944                 });
945
946         return rv;
947 }
948
949 function getLoadedArticleIds() {
950         var rv = [];
951
952         var children = $$("#headlines-frame > div[id*=RROW-]");
953
954         children.each(function(child) {
955                         rv.push(child.id.replace("RROW-", ""));
956                 });
957
958         return rv;
959
960 }
961
962 // mode = all,none,unread,invert,marked,published
963 function selectArticles(mode, query) {
964         try {
965
966                 if (!query) query = "#headlines-frame > div[id*=RROW]";
967
968                 var children = $$(query);
969
970                 children.each(function(child) {
971                         var id = child.id.replace("RROW-", "");
972
973                         var cb = dijit.getEnclosingWidget(
974                                         child.getElementsByClassName("rchk")[0]);
975
976                         if (mode == "all") {
977                                 child.addClassName("Selected");
978                                 if (cb) cb.attr("checked", true);
979                         } else if (mode == "unread") {
980                                 if (child.hasClassName("Unread")) {
981                                         child.addClassName("Selected");
982                                         if (cb) cb.attr("checked", true);
983                                 } else {
984                                         child.removeClassName("Selected");
985                                         if (cb) cb.attr("checked", false);
986                                 }
987                         } else if (mode == "marked") {
988                                 if (child.hasClassName("marked")) {
989                                         child.addClassName("Selected");
990                                         if (cb) cb.attr("checked", true);
991                                 } else {
992                                         child.removeClassName("Selected");
993                                         if (cb) cb.attr("checked", false);
994                                 }
995                         } else if (mode == "published") {
996                                 if (child.hasClassName("published")) {
997                                         child.addClassName("Selected");
998                                         if (cb) cb.attr("checked", true);
999                                 } else {
1000                                         child.removeClassName("Selected");
1001                                         if (cb) cb.attr("checked", false);
1002                                 }
1003
1004                         } else if (mode == "invert") {
1005                                 if (child.hasClassName("Selected")) {
1006                                         child.removeClassName("Selected");
1007                                         if (cb) cb.attr("checked", false);
1008                                 } else {
1009                                         child.addClassName("Selected");
1010                                         if (cb) cb.attr("checked", true);
1011                                 }
1012
1013                         } else {
1014                                 child.removeClassName("Selected");
1015                                 if (cb) cb.attr("checked", false);
1016                         }
1017                 });
1018
1019                 updateSelectedPrompt();
1020
1021         } catch (e) {
1022                 exception_error("selectArticles", e);
1023         }
1024 }
1025
1026 function deleteSelection() {
1027
1028         try {
1029
1030                 var rows = getSelectedArticleIds2();
1031
1032                 if (rows.length == 0) {
1033                         alert(__("No articles are selected."));
1034                         return;
1035                 }
1036
1037                 var fn = getFeedName(getActiveFeedId(), activeFeedIsCat());
1038                 var str;
1039
1040                 if (getActiveFeedId() != 0) {
1041                         str = ngettext("Delete %d selected article in %s?", "Delete %d selected articles in %s?" , rows.length);
1042                 } else {
1043                         str = ngettext("Delete %d selected article?", "Delete %d selected articles?", rows.length);
1044                 }
1045
1046                 str = str.replace("%d", rows.length);
1047                 str = str.replace("%s", fn);
1048
1049                 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
1050                         return;
1051                 }
1052
1053                 query = "?op=rpc&method=delete&ids=" + param_escape(rows);
1054
1055                 console.log(query);
1056
1057                 new Ajax.Request("backend.php", {
1058                         parameters: query,
1059                         onComplete: function(transport) {
1060                                         handle_rpc_json(transport);
1061                                         viewCurrentFeed();
1062                                 } });
1063
1064         } catch (e) {
1065                 exception_error("deleteSelection", e);
1066         }
1067 }
1068
1069 function archiveSelection() {
1070
1071         try {
1072
1073                 var rows = getSelectedArticleIds2();
1074
1075                 if (rows.length == 0) {
1076                         alert(__("No articles are selected."));
1077                         return;
1078                 }
1079
1080                 var fn = getFeedName(getActiveFeedId(), activeFeedIsCat());
1081                 var str;
1082                 var op;
1083
1084                 if (getActiveFeedId() != 0) {
1085                         str = ngettext("Archive %d selected article in %s?", "Archive %d selected articles in %s?", rows.length);
1086                         op = "archive";
1087                 } else {
1088                         str = ngettext("Move %d archived article back?", "Move %d archived articles back?", rows.length);
1089
1090                         str += " " + __("Please note that unstarred articles might get purged on next feed update.");
1091
1092                         op = "unarchive";
1093                 }
1094
1095                 str = str.replace("%d", rows.length);
1096                 str = str.replace("%s", fn);
1097
1098                 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
1099                         return;
1100                 }
1101
1102                 query = "?op=rpc&method="+op+"&ids=" + param_escape(rows);
1103
1104                 console.log(query);
1105
1106                 for (var i = 0; i < rows.length; i++) {
1107                         cache_delete("article:" + rows[i]);
1108                 }
1109
1110                 new Ajax.Request("backend.php", {
1111                         parameters: query,
1112                         onComplete: function(transport) {
1113                                         handle_rpc_json(transport);
1114                                         viewCurrentFeed();
1115                                 } });
1116
1117         } catch (e) {
1118                 exception_error("archiveSelection", e);
1119         }
1120 }
1121
1122 function catchupSelection() {
1123
1124         try {
1125
1126                 var rows = getSelectedArticleIds2();
1127
1128                 if (rows.length == 0) {
1129                         alert(__("No articles are selected."));
1130                         return;
1131                 }
1132
1133                 var fn = getFeedName(getActiveFeedId(), activeFeedIsCat());
1134
1135                 var str = ngettext("Mark %d selected article in %s as read?", "Mark %d selected articles in %s as read?", rows.length);
1136
1137                 str = str.replace("%d", rows.length);
1138                 str = str.replace("%s", fn);
1139
1140                 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
1141                         return;
1142                 }
1143
1144                 selectionToggleUnread(false, 'viewCurrentFeed()', true);
1145
1146         } catch (e) {
1147                 exception_error("catchupSelection", e);
1148         }
1149 }
1150
1151 function editArticleTags(id) {
1152                 var query = "backend.php?op=article&method=editArticleTags&param=" + param_escape(id);
1153
1154                 if (dijit.byId("editTagsDlg"))
1155                         dijit.byId("editTagsDlg").destroyRecursive();
1156
1157                 dialog = new dijit.Dialog({
1158                         id: "editTagsDlg",
1159                         title: __("Edit article Tags"),
1160                         style: "width: 600px",
1161                         execute: function() {
1162                                 if (this.validate()) {
1163                                         var query = dojo.objectToQuery(this.attr('value'));
1164
1165                                         notify_progress("Saving article tags...", true);
1166
1167                                         new Ajax.Request("backend.php", {
1168                                         parameters: query,
1169                                         onComplete: function(transport) {
1170                                                 try {
1171                                                         notify('');
1172                                                         dialog.hide();
1173
1174                                                         var data = JSON.parse(transport.responseText);
1175
1176                                                         if (data) {
1177                                                                 var id = data.id;
1178
1179                                                                 console.log(id);
1180
1181                                                                 var tags = $("ATSTR-" + id);
1182                                                                 var tooltip = dijit.byId("ATSTRTIP-" + id);
1183
1184                                                                 if (tags) tags.innerHTML = data.content;
1185                                                                 if (tooltip) tooltip.attr('label', data.content_full);
1186                                                         }
1187                                                 } catch (e) {
1188                                                         exception_error("editArticleTags/inner", e);
1189                                                 }
1190
1191                                         }});
1192                                 }
1193                         },
1194                         href: query
1195                 });
1196
1197                 var tmph = dojo.connect(dialog, 'onLoad', function() {
1198                 dojo.disconnect(tmph);
1199
1200                         new Ajax.Autocompleter('tags_str', 'tags_choices',
1201                            "backend.php?op=article&method=completeTags",
1202                            { tokens: ',', paramName: "search" });
1203                 });
1204
1205                 dialog.show();
1206
1207 }
1208
1209 function cdmScrollToArticleId(id, force) {
1210         try {
1211                 var ctr = $("headlines-frame");
1212                 var e = $("RROW-" + id);
1213
1214                 if (!e || !ctr) return;
1215
1216                 if (force || e.offsetTop+e.offsetHeight > (ctr.scrollTop+ctr.offsetHeight) ||
1217                                 e.offsetTop < ctr.scrollTop) {
1218
1219                         // expanded cdm has a 4px margin now
1220                         ctr.scrollTop = parseInt(e.offsetTop) - 4;
1221                 }
1222
1223         } catch (e) {
1224                 exception_error("cdmScrollToArticleId", e);
1225         }
1226 }
1227
1228 function setActiveArticleId(id) {
1229         console.log("setActiveArticleId:" + id);
1230
1231         _active_article_id = id;
1232         PluginHost.run(PluginHost.HOOK_ARTICLE_SET_ACTIVE, _active_article_id);
1233 }
1234
1235 function getActiveArticleId() {
1236         return _active_article_id;
1237 }
1238
1239 function postMouseIn(e, id) {
1240         post_under_pointer = id;
1241 }
1242
1243 function postMouseOut(id) {
1244         post_under_pointer = false;
1245 }
1246
1247 function unpackVisibleHeadlines() {
1248         try {
1249                 if (!isCdmMode() || !getInitParam("cdm_expanded")) return;
1250
1251                 $$("#headlines-frame > div[id*=RROW]").each(
1252                         function(child) {
1253                                 if (child.offsetTop <= $("headlines-frame").scrollTop +
1254                                         $("headlines-frame").offsetHeight) {
1255
1256                                         var cencw = $("CENCW-" + child.id.replace("RROW-", ""));
1257
1258                                         if (cencw) {
1259                                                 cencw.innerHTML = htmlspecialchars_decode(cencw.innerHTML);
1260                                                 cencw.setAttribute('id', '');
1261
1262                                                 PluginHost.run(PluginHost.HOOK_ARTICLE_RENDERED_CDM, child);
1263
1264                                                 Element.show(cencw);
1265                                         }
1266                                 }
1267                         }
1268                 );
1269
1270         } catch (e) {
1271                 exception_error("unpackVisibleHeadlines", e);
1272         }
1273 }
1274
1275 function headlines_scroll_handler(e) {
1276         try {
1277                 var hsp = $("headlines-spacer");
1278
1279                 unpackVisibleHeadlines();
1280
1281                 // set topmost child in the buffer as active
1282                 if (isCdmMode() && getInitParam("cdm_auto_catchup") == 1 &&
1283                                 getSelectedArticleIds2().length <= 1 &&
1284                                 getInitParam("cdm_expanded")) {
1285                         var rows = $$("#headlines-frame > div[id*=RROW]");
1286
1287                         for (var i = 0; i < rows.length; i++) {
1288                                 var child = rows[i];
1289
1290                                 if ($("headlines-frame").scrollTop <= child.offsetTop &&
1291                                         child.offsetTop - $("headlines-frame").scrollTop < 100 &&
1292                                         child.id.replace("RROW-", "") != _active_article_id) {
1293
1294                                         if (_active_article_id) {
1295                                                 var row = $("RROW-" + _active_article_id);
1296                                                 if (row) row.removeClassName("active");
1297                                         }
1298
1299                                         _active_article_id = child.id.replace("RROW-", "");
1300                                         showArticleInHeadlines(_active_article_id, true);
1301                                         updateSelectedPrompt();
1302                                         break;
1303                                 }
1304                         }
1305                 }
1306
1307                 if (!_infscroll_disable) {
1308                         if (hsp && hsp.offsetTop - 250 <= e.scrollTop + e.offsetHeight) {
1309
1310                                 hsp.innerHTML = "<span class='loading'><img src='images/indicator_tiny.gif'> " +
1311                                         __("Loading, please wait...") + "</span>";
1312
1313                                 loadMoreHeadlines();
1314                                 return;
1315
1316                         }
1317                 }
1318
1319                 if (isCdmMode()) {
1320                         updateFloatingTitle();
1321                 }
1322
1323                 catchupCurrentBatchIfNeeded();
1324
1325                 if (getInitParam("cdm_auto_catchup") == 1) {
1326
1327                         // let's get DOM some time to settle down
1328                         var ts = new Date().getTime();
1329                         if (ts - _last_headlines_update < 100) return;
1330
1331                         $$("#headlines-frame > div[id*=RROW][class*=Unread]").each(
1332                                 function(child) {
1333                                         if (child.hasClassName("Unread") && $("headlines-frame").scrollTop >
1334                                                         (child.offsetTop + child.offsetHeight/2)) {
1335
1336                                                 var id = child.id.replace("RROW-", "");
1337
1338                                                 if (catchup_id_batch.indexOf(id) == -1)
1339                                                         catchup_id_batch.push(id);
1340
1341                                                 //console.log("auto_catchup_batch: " + catchup_id_batch.toString());
1342                                         }
1343
1344                                 });
1345
1346                         if (_infscroll_disable) {
1347                                 var child = $$("#headlines-frame div[id*=RROW]").last();
1348
1349                                 if (child && $("headlines-frame").scrollTop >
1350                                                 (child.offsetTop + child.offsetHeight - 50)) {
1351
1352                                         console.log("we seem to be at an end");
1353
1354                                         if (getInitParam("on_catchup_show_next_feed") == "1") {
1355                                                 openNextUnreadFeed();
1356                                         }
1357                                 }
1358                         }
1359                 }
1360
1361         } catch (e) {
1362                 console.warn("headlines_scroll_handler: " + e);
1363         }
1364 }
1365
1366 function openNextUnreadFeed() {
1367         try {
1368                 var is_cat = activeFeedIsCat();
1369                 var nuf = getNextUnreadFeed(getActiveFeedId(), is_cat);
1370                 if (nuf) viewfeed({feed: nuf, is_cat: is_cat});
1371         } catch (e) {
1372                 exception_error("openNextUnreadFeed", e);
1373         }
1374 }
1375
1376 function catchupBatchedArticles() {
1377         try {
1378                 if (catchup_id_batch.length > 0 && !_infscroll_request_sent && !_catchup_request_sent) {
1379
1380                         console.log("catchupBatchedArticles: working");
1381
1382                         // make a copy of the array
1383                         var batch = catchup_id_batch.slice();
1384                         var query = "?op=rpc&method=catchupSelected" +
1385                                 "&cmode=0&ids=" + param_escape(batch.toString());
1386
1387                         console.log(query);
1388
1389                         _catchup_request_sent = true;
1390
1391                         new Ajax.Request("backend.php", {
1392                                 parameters: query,
1393                                 onComplete: function(transport) {
1394                                         handle_rpc_json(transport);
1395
1396                                         _catchup_request_sent = false;
1397
1398                                         reply = JSON.parse(transport.responseText);
1399                                         var batch = reply.ids;
1400
1401                                         batch.each(function(id) {
1402                                                 console.log(id);
1403                                                 var elem = $("RROW-" + id);
1404                                                 if (elem) elem.removeClassName("Unread");
1405                                                 catchup_id_batch.remove(id);
1406                                         });
1407
1408                                         updateFloatingTitle(true);
1409
1410                                 } });
1411                 }
1412
1413         } catch (e) {
1414                 exception_error("catchupBatchedArticles", e);
1415         }
1416 }
1417
1418 function catchupRelativeToArticle(below, id) {
1419
1420         try {
1421
1422                 if (!id) id = getActiveArticleId();
1423
1424                 if (!id) {
1425                         alert(__("No article is selected."));
1426                         return;
1427                 }
1428
1429                 var visible_ids = getVisibleArticleIds();
1430
1431                 var ids_to_mark = new Array();
1432
1433                 if (!below) {
1434                         for (var i = 0; i < visible_ids.length; i++) {
1435                                 if (visible_ids[i] != id) {
1436                                         var e = $("RROW-" + visible_ids[i]);
1437
1438                                         if (e && e.hasClassName("Unread")) {
1439                                                 ids_to_mark.push(visible_ids[i]);
1440                                         }
1441                                 } else {
1442                                         break;
1443                                 }
1444                         }
1445                 } else {
1446                         for (var i = visible_ids.length-1; i >= 0; i--) {
1447                                 if (visible_ids[i] != id) {
1448                                         var e = $("RROW-" + visible_ids[i]);
1449
1450                                         if (e && e.hasClassName("Unread")) {
1451                                                 ids_to_mark.push(visible_ids[i]);
1452                                         }
1453                                 } else {
1454                                         break;
1455                                 }
1456                         }
1457                 }
1458
1459                 if (ids_to_mark.length == 0) {
1460                         alert(__("No articles found to mark"));
1461                 } else {
1462                         var msg = ngettext("Mark %d article as read?", "Mark %d articles as read?", ids_to_mark.length).replace("%d", ids_to_mark.length);
1463
1464                         if (getInitParam("confirm_feed_catchup") != 1 || confirm(msg)) {
1465
1466                                 for (var i = 0; i < ids_to_mark.length; i++) {
1467                                         var e = $("RROW-" + ids_to_mark[i]);
1468                                         e.removeClassName("Unread");
1469                                 }
1470
1471                                 var query = "?op=rpc&method=catchupSelected" +
1472                                         "&cmode=0" + "&ids=" + param_escape(ids_to_mark.toString());
1473
1474                                 new Ajax.Request("backend.php", {
1475                                         parameters: query,
1476                                         onComplete: function(transport) {
1477                                                 handle_rpc_json(transport);
1478                                         } });
1479
1480                         }
1481                 }
1482
1483         } catch (e) {
1484                 exception_error("catchupRelativeToArticle", e);
1485         }
1486 }
1487
1488 function cdmCollapseArticle(event, id, unmark) {
1489         try {
1490                 if (unmark == undefined) unmark = true;
1491
1492                 var row = $("RROW-" + id);
1493                 var elem = $("CICD-" + id);
1494
1495                 if (elem && row) {
1496                         var collapse = $$("div#RROW-" + id +
1497                                 " span[class='collapseBtn']")[0];
1498
1499                         Element.hide(elem);
1500                         Element.show("CEXC-" + id);
1501                         Element.hide(collapse);
1502
1503                         if (unmark) {
1504                                 row.removeClassName("active");
1505
1506                                 markHeadline(id, false);
1507
1508                                 if (id == getActiveArticleId()) {
1509                                         setActiveArticleId(0);
1510                                 }
1511
1512                                 updateSelectedPrompt();
1513                         }
1514
1515                         if (event) Event.stop(event);
1516
1517                         PluginHost.run(PluginHost.HOOK_ARTICLE_COLLAPSED, id);
1518
1519                         if (row.offsetTop < $("headlines-frame").scrollTop)
1520                                 scrollToRowId(row.id);
1521
1522                         $("floatingTitle").style.visibility = "hidden";
1523                         $("floatingTitle").setAttribute("rowid", false);
1524                 }
1525
1526         } catch (e) {
1527                 exception_error("cdmCollapseArticle", e);
1528         }
1529 }
1530
1531 function cdmExpandArticle(id, noexpand) {
1532         try {
1533                 console.log("cdmExpandArticle " + id);
1534
1535                 if (!$("RROW-" + id)) return false;
1536
1537                 var oldrow = $("RROW-" + getActiveArticleId());
1538
1539                 var elem = $("CICD-" + getActiveArticleId());
1540
1541                 if (id == getActiveArticleId() && Element.visible(elem))
1542                         return true;
1543
1544                 selectArticles("none");
1545
1546                 var old_offset = $("RROW-" + id).offsetTop;
1547
1548                 if (getActiveArticleId() && elem && !getInitParam("cdm_expanded")) {
1549                         var collapse = $$("div#RROW-" + getActiveArticleId() +
1550                                 " span[class='collapseBtn']")[0];
1551
1552                         Element.hide(elem);
1553                         Element.show("CEXC-" + getActiveArticleId());
1554                         Element.hide(collapse);
1555                 }
1556
1557                 if (oldrow) oldrow.removeClassName("active");
1558
1559                 setActiveArticleId(id);
1560
1561                 elem = $("CICD-" + id);
1562
1563                 var collapse = $$("div#RROW-" + id +
1564                                 " span[class='collapseBtn']")[0];
1565
1566                 var cencw = $("CENCW-" + id);
1567
1568                 if (!Element.visible(elem) && !noexpand) {
1569                         if (cencw) {
1570                                 cencw.innerHTML = htmlspecialchars_decode(cencw.innerHTML);
1571                                 cencw.setAttribute('id', '');
1572                                 Element.show(cencw);
1573                         }
1574
1575                         Element.show(elem);
1576                         Element.hide("CEXC-" + id);
1577                         Element.show(collapse);
1578                 }
1579
1580                 var new_offset = $("RROW-" + id).offsetTop;
1581
1582                 if (old_offset > new_offset)
1583                         $("headlines-frame").scrollTop -= (old_offset-new_offset);
1584
1585                 if (!noexpand) {
1586                         if (catchup_id_batch.indexOf(id) == -1)
1587                                 catchup_id_batch.push(id);
1588
1589                         catchupCurrentBatchIfNeeded();
1590                 }
1591
1592                 toggleSelected(id);
1593                 $("RROW-" + id).addClassName("active");
1594
1595                 PluginHost.run(PluginHost.HOOK_ARTICLE_EXPANDED, id);
1596
1597         } catch (e) {
1598                 exception_error("cdmExpandArticle", e);
1599         }
1600
1601         return false;
1602 }
1603
1604 function getArticleUnderPointer() {
1605         return post_under_pointer;
1606 }
1607
1608 function scrollArticle(offset) {
1609         try {
1610                 if (!isCdmMode()) {
1611                         var ci = $("content-insert");
1612                         if (ci) {
1613                                 ci.scrollTop += offset;
1614                         }
1615                 } else {
1616                         var hi = $("headlines-frame");
1617                         if (hi) {
1618                                 hi.scrollTop += offset;
1619                         }
1620
1621                 }
1622         } catch (e) {
1623                 exception_error("scrollArticle", e);
1624         }
1625 }
1626
1627 function show_labels_in_headlines(transport) {
1628         try {
1629                 var data = JSON.parse(transport.responseText);
1630
1631                 if (data) {
1632                         data['info-for-headlines'].each(function(elem) {
1633                                 $$(".HLLCTR-" + elem.id).each(function(ctr) {
1634                                         ctr.innerHTML = elem.labels;
1635                                 });
1636                         });
1637                 }
1638         } catch (e) {
1639                 exception_error("show_labels_in_headlines", e);
1640         }
1641 }
1642
1643 function dismissArticle(id) {
1644         try {
1645                 var elem = $("RROW-" + id);
1646
1647                 if (!elem) return;
1648
1649                 toggleUnread(id, 0, true);
1650
1651                 new Effect.Fade(elem, {duration : 0.5});
1652
1653                 // Remove the content, too
1654                 var elem_content = $("CICD-" + id);
1655                 if (elem_content) {
1656                         Element.remove(elem_content);
1657                 }
1658
1659                 if (id == getActiveArticleId()) {
1660                         setActiveArticleId(0);
1661                 }
1662
1663         } catch (e) {
1664                 exception_error("dismissArticle", e);
1665         }
1666 }
1667
1668 function dismissSelectedArticles() {
1669         try {
1670
1671                 var ids = getVisibleArticleIds();
1672                 var tmp = [];
1673                 var sel = [];
1674
1675                 for (var i = 0; i < ids.length; i++) {
1676                         var elem = $("RROW-" + ids[i]);
1677
1678                         if (elem.className && elem.hasClassName("Selected") &&
1679                                         ids[i] != getActiveArticleId()) {
1680                                 new Effect.Fade(elem, {duration : 0.5});
1681                                 sel.push(ids[i]);
1682
1683                                 // Remove the content, too
1684                                 var elem_content = $("CICD-" + ids[i]);
1685                                 if (elem_content) {
1686                                         Element.remove(elem_content);
1687                                 }
1688                         } else {
1689                                 tmp.push(ids[i]);
1690                         }
1691                 }
1692
1693                 if (sel.length > 0)
1694                         selectionToggleUnread(false);
1695
1696
1697         } catch (e) {
1698                 exception_error("dismissSelectedArticles", e);
1699         }
1700 }
1701
1702 function dismissReadArticles() {
1703         try {
1704
1705                 var ids = getVisibleArticleIds();
1706                 var tmp = [];
1707
1708                 for (var i = 0; i < ids.length; i++) {
1709                         var elem = $("RROW-" + ids[i]);
1710
1711                         if (elem.className && !elem.hasClassName("Unread") &&
1712                                         !elem.hasClassName("Selected")) {
1713
1714                                 new Effect.Fade(elem, {duration : 0.5});
1715
1716                                 // Remove the content, too
1717                                 var elem_content = $("CICD-" + ids[i]);
1718                                 if (elem_content) {
1719                                         Element.remove(elem_content);
1720                                 }
1721                         } else {
1722                                 tmp.push(ids[i]);
1723                         }
1724                 }
1725
1726         } catch (e) {
1727                 exception_error("dismissReadArticles", e);
1728         }
1729 }
1730
1731 // we don't really hide rows anymore
1732 function getVisibleArticleIds() {
1733         return getLoadedArticleIds();
1734
1735         /*var ids = [];
1736
1737         try {
1738
1739                 getLoadedArticleIds().each(function(id) {
1740                         var elem = $("RROW-" + id);
1741                         if (elem && Element.visible(elem))
1742                                 ids.push(id);
1743                         });
1744
1745         } catch (e) {
1746                 exception_error("getVisibleArticleIds", e);
1747         }
1748
1749         return ids; */
1750 }
1751
1752 function cdmClicked(event, id) {
1753         try {
1754                 //var shift_key = event.shiftKey;
1755
1756                 if (!event.ctrlKey) {
1757
1758                         if (!getInitParam("cdm_expanded")) {
1759                                 return cdmExpandArticle(id);
1760                         } else {
1761
1762                                 var elem = $("RROW-" + getActiveArticleId());
1763
1764                                 if (elem) elem.removeClassName("active");
1765
1766                                 selectArticles("none");
1767                                 toggleSelected(id);
1768
1769                                 var elem = $("RROW-" + id);
1770                                 var article_is_unread = elem.hasClassName("Unread");
1771
1772                                 elem.removeClassName("Unread");
1773                                 elem.addClassName("active");
1774
1775                                 setActiveArticleId(id);
1776
1777                                 if (article_is_unread) {
1778                                         decrementFeedCounter(getActiveFeedId(), activeFeedIsCat());
1779                                         updateFloatingTitle(true);
1780                                 }
1781
1782                                 var query = "?op=rpc&method=catchupSelected" +
1783                                         "&cmode=0&ids=" + param_escape(id);
1784
1785                                 new Ajax.Request("backend.php", {
1786                                         parameters: query,
1787                                         onComplete: function(transport) {
1788                                                 handle_rpc_json(transport);
1789                                         } });
1790
1791                                 return !event.shiftKey;
1792                         }
1793
1794                 } else if (event.target.parents(".cdmHeader").length > 0) {
1795
1796                         toggleSelected(id, true);
1797
1798                         var elem = $("RROW-" + id);
1799                         var article_is_unread = elem.hasClassName("Unread");
1800
1801                         if (article_is_unread) {
1802                                 decrementFeedCounter(getActiveFeedId(), activeFeedIsCat());
1803                         }
1804
1805                         toggleUnread(id, 0, false);
1806
1807                         openArticleInNewWindow(id);
1808                 }
1809
1810                 var unread_in_buffer = $$("#headlines-frame > div[id*=RROW][class*=Unread]").length
1811                 request_counters(unread_in_buffer == 0);
1812
1813         } catch (e) {
1814                 exception_error("cdmClicked");
1815         }
1816
1817         return false;
1818 }
1819
1820 function hlClicked(event, id) {
1821         try {
1822                 if (event.which == 2) {
1823                         view(id);
1824                         return true;
1825                 } else if (event.ctrlKey) {
1826                         toggleSelected(id, true);
1827                         toggleUnread(id, 0, false);
1828                         openArticleInNewWindow(id);
1829                         return false;
1830                 } else {
1831                         view(id);
1832                         return false;
1833                 }
1834
1835         } catch (e) {
1836                 exception_error("hlClicked");
1837         }
1838 }
1839
1840 function getFirstVisibleHeadlineId() {
1841         var rows = getVisibleArticleIds();
1842         return rows[0];
1843
1844 }
1845
1846 function getLastVisibleHeadlineId() {
1847         var rows = getVisibleArticleIds();
1848         return rows[rows.length-1];
1849 }
1850
1851 function openArticleInNewWindow(id) {
1852         toggleUnread(id, 0, false);
1853         window.open("backend.php?op=article&method=redirect&id=" + id);
1854 }
1855
1856 function isCdmMode() {
1857         return getInitParam("combined_display_mode");
1858 }
1859
1860 function markHeadline(id, marked) {
1861         if (marked == undefined) marked = true;
1862
1863         var row = $("RROW-" + id);
1864         if (row) {
1865                 var check = dijit.getEnclosingWidget(
1866                                 row.getElementsByClassName("rchk")[0]);
1867
1868                 if (check) {
1869                         check.attr("checked", marked);
1870                 }
1871
1872                 if (marked)
1873                         row.addClassName("Selected");
1874                 else
1875                         row.removeClassName("Selected");
1876         }
1877 }
1878
1879 function getRelativePostIds(id, limit) {
1880
1881         var tmp = [];
1882
1883         try {
1884
1885                 if (!limit) limit = 6; //3
1886
1887                 var ids = getVisibleArticleIds();
1888
1889                 for (var i = 0; i < ids.length; i++) {
1890                         if (ids[i] == id) {
1891                                 for (var k = 1; k <= limit; k++) {
1892                                         //if (i > k-1) tmp.push(ids[i-k]);
1893                                         if (i < ids.length-k) tmp.push(ids[i+k]);
1894                                 }
1895                                 break;
1896                         }
1897                 }
1898
1899         } catch (e) {
1900                 exception_error("getRelativePostIds", e);
1901         }
1902
1903         return tmp;
1904 }
1905
1906 function correctHeadlinesOffset(id) {
1907
1908         try {
1909
1910                 var container = $("headlines-frame");
1911                 var row = $("RROW-" + id);
1912
1913                 if (!container || !row) return;
1914
1915                 var viewport = container.offsetHeight;
1916
1917                 var rel_offset_top = row.offsetTop - container.scrollTop;
1918                 var rel_offset_bottom = row.offsetTop + row.offsetHeight - container.scrollTop;
1919
1920                 //console.log("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom);
1921                 //console.log("Vport: " + viewport);
1922
1923                 if (rel_offset_top <= 0 || rel_offset_top > viewport) {
1924                         container.scrollTop = row.offsetTop;
1925                 } else if (rel_offset_bottom > viewport) {
1926
1927                         /* doesn't properly work with Opera in some cases because
1928                                 Opera fucks up element scrolling */
1929
1930                         container.scrollTop = row.offsetTop + row.offsetHeight - viewport;
1931                 }
1932
1933         } catch (e) {
1934                 exception_error("correctHeadlinesOffset", e);
1935         }
1936
1937 }
1938
1939 function headlineActionsChange(elem) {
1940         try {
1941                 eval(elem.value);
1942                 elem.attr('value', 'false');
1943         } catch (e) {
1944                 exception_error("headlineActionsChange", e);
1945         }
1946 }
1947
1948 function closeArticlePanel() {
1949
1950         if (dijit.byId("content-insert"))
1951                 dijit.byId("headlines-wrap-inner").removeChild(
1952                         dijit.byId("content-insert"));
1953 }
1954
1955 function initFloatingMenu() {
1956         try {
1957                 if (dijit.byId("floatingMenu"))
1958                         dijit.byId("floatingMenu").destroyRecursive();
1959
1960                         var menu = new dijit.Menu({
1961                                 id: "floatingMenu",
1962                                 targetNodeIds: ["floatingTitle"]
1963                         });
1964
1965                         var id = $("floatingTitle").getAttribute("rowid").replace("RROW-", "");
1966
1967                         headlinesMenuCommon(menu, id);
1968
1969                         menu.startup();
1970         } catch (e) {
1971                 exception_error("initFloatingMenu", e);
1972         }
1973 }
1974
1975 function headlinesMenuCommon(menu, base_id) {
1976         try {
1977
1978                 menu.addChild(new dijit.MenuItem({
1979                         label: __("Open original article"),
1980                         onClick: function(event) {
1981                                 openArticleInNewWindow(base_id ? base_id : this.getParent().callerRowId);
1982                         }}));
1983
1984                 menu.addChild(new dijit.MenuItem({
1985                         label: __("Display article URL"),
1986                         onClick: function(event) {
1987                                 displayArticleUrl(base_id ? base_id : this.getParent().callerRowId);
1988                         }}));
1989
1990                 menu.addChild(new dijit.MenuSeparator());
1991
1992                 menu.addChild(new dijit.MenuItem({
1993                         label: __("Toggle unread"),
1994                         onClick: function(event) {
1995                                 var ids = getSelectedArticleIds2();
1996                                 // cast to string
1997                                 var id = (base_id ? base_id : this.getParent().callerRowId) + "";
1998                                 ids = ids.size() != 0 && ids.indexOf(id) != -1 ? ids : [id];
1999
2000                                 selectionToggleUnread(undefined, false, true, ids);
2001                                 }}));
2002
2003                 menu.addChild(new dijit.MenuItem({
2004                         label: __("Toggle starred"),
2005                         onClick: function(event) {
2006                                 var ids = getSelectedArticleIds2();
2007                                 // cast to string
2008                                 var id = (base_id ? base_id : this.getParent().callerRowId) + "";
2009                                 ids = ids.size() != 0 && ids.indexOf(id) != -1 ? ids : [id];
2010
2011                                 selectionToggleMarked(undefined, false, true, ids);
2012                                 }}));
2013
2014                 menu.addChild(new dijit.MenuItem({
2015                         label: __("Toggle published"),
2016                         onClick: function(event) {
2017                                 var ids = getSelectedArticleIds2();
2018                                 // cast to string
2019                                 var id = (base_id ? base_id : this.getParent().callerRowId) + "";
2020                                 ids = ids.size() != 0 && ids.indexOf(id) != -1 ? ids : [id];
2021
2022                                 selectionTogglePublished(undefined, false, true, ids);
2023                                 }}));
2024
2025                 menu.addChild(new dijit.MenuSeparator());
2026
2027                 menu.addChild(new dijit.MenuItem({
2028                         label: __("Mark above as read"),
2029                         onClick: function(event) {
2030                                 catchupRelativeToArticle(0, base_id ? base_id : this.getParent().callerRowId);
2031                                 }}));
2032
2033                 menu.addChild(new dijit.MenuItem({
2034                         label: __("Mark below as read"),
2035                         onClick: function(event) {
2036                                 catchupRelativeToArticle(1, base_id ? base_id : this.getParent().callerRowId);
2037                                 }}));
2038
2039
2040                 var labels = dijit.byId("feedTree").model.getItemsInCategory(-2);
2041
2042                 if (labels) {
2043
2044                         menu.addChild(new dijit.MenuSeparator());
2045
2046                         var labelAddMenu = new dijit.Menu({ownerMenu: menu});
2047                         var labelDelMenu = new dijit.Menu({ownerMenu: menu});
2048
2049                         labels.each(function(label) {
2050                                 var id = label.id[0];
2051                                 var bare_id = id.substr(id.indexOf(":")+1);
2052                                 var name = label.name[0];
2053
2054                                 bare_id = feed_to_label_id(bare_id);
2055
2056                                 labelAddMenu.addChild(new dijit.MenuItem({
2057                                         label: name,
2058                                         labelId: bare_id,
2059                                         onClick: function(event) {
2060                                                 var ids = getSelectedArticleIds2();
2061                                                 // cast to string
2062                                                 var id = (base_id ? base_id : this.getParent().ownerMenu.callerRowId) + "";
2063
2064                                                 ids = ids.size() != 0 && ids.indexOf(id) != -1 ? ids : [id];
2065
2066                                                 selectionAssignLabel(this.labelId, ids);
2067                                 }}));
2068
2069                                 labelDelMenu.addChild(new dijit.MenuItem({
2070                                         label: name,
2071                                         labelId: bare_id,
2072                                         onClick: function(event) {
2073                                                 var ids = getSelectedArticleIds2();
2074                                                 // cast to string
2075                                                 var id = (base_id ? base_id : this.getParent().ownerMenu.callerRowId) + "";
2076
2077                                                 ids = ids.size() != 0 && ids.indexOf(id) != -1 ? ids : [id];
2078
2079                                                 selectionRemoveLabel(this.labelId, ids);
2080                                 }}));
2081
2082                         });
2083
2084                         menu.addChild(new dijit.PopupMenuItem({
2085                                 label: __("Assign label"),
2086                                 popup: labelAddMenu
2087                         }));
2088
2089                         menu.addChild(new dijit.PopupMenuItem({
2090                                 label: __("Remove label"),
2091                                 popup: labelDelMenu
2092                         }));
2093
2094                 }
2095
2096
2097         } catch (e) {
2098                 exception_error("headlinesMenuCommon", e);
2099         }
2100 }
2101
2102 function initHeadlinesMenu() {
2103         try {
2104                 if (dijit.byId("headlinesMenu"))
2105                         dijit.byId("headlinesMenu").destroyRecursive();
2106
2107                 var ids = [];
2108
2109                 if (!isCdmMode()) {
2110                         nodes = $$("#headlines-frame > div[id*=RROW]");
2111                 } else {
2112                         nodes = $$("#headlines-frame span[id*=RTITLE]");
2113                 }
2114
2115                 nodes.each(function(node) {
2116                         ids.push(node.id);
2117                 });
2118
2119                 var menu = new dijit.Menu({
2120                         id: "headlinesMenu",
2121                         targetNodeIds: ids
2122                 });
2123
2124                 var tmph = dojo.connect(menu, '_openMyself', function (event) {
2125                         var callerNode = event.target, match = null, tries = 0;
2126
2127                         while (match == null && callerNode && tries <= 3) {
2128                                 match = callerNode.id.match("^[A-Z]+[-]([0-9]+)$");
2129                                 callerNode = callerNode.parentNode;
2130                                 ++tries;
2131                         }
2132
2133                         if (match) this.callerRowId = parseInt(match[1]);
2134
2135                 });
2136
2137                 headlinesMenuCommon(menu, false);
2138
2139                 menu.startup();
2140
2141                 /* vgroup feed title menu */
2142
2143                 var nodes = $$("#headlines-frame > div[class='cdmFeedTitle']");
2144                 var ids = [];
2145
2146                 nodes.each(function(node) {
2147                         ids.push(node.id);
2148                 });
2149
2150                 if (ids.length > 0) {
2151                         if (dijit.byId("headlinesFeedTitleMenu"))
2152                                 dijit.byId("headlinesFeedTitleMenu").destroyRecursive();
2153
2154                         var menu = new dijit.Menu({
2155                                 id: "headlinesFeedTitleMenu",
2156                                 targetNodeIds: ids
2157                         });
2158
2159                         var tmph = dojo.connect(menu, '_openMyself', function (event) {
2160                                 var callerNode = event.target, match = null, tries = 0;
2161
2162                                 while (match == null && callerNode && tries <= 3) {
2163                                         console.log(callerNode.id);
2164
2165                                         match = callerNode.id.match("^[A-Z]+[-]([0-9]+)$");
2166                                         callerNode = callerNode.parentNode;
2167                                         ++tries;
2168
2169                                         console.log(match[1]);
2170                                 }
2171
2172                                 if (match) this.callerRowId = parseInt(match[1]);
2173
2174                         });
2175
2176                         menu.addChild(new dijit.MenuItem({
2177                                 label: __("Select articles in group"),
2178                                 onClick: function(event) {
2179                                         selectArticles("all",
2180                                                 "#headlines-frame > div[id*=RROW]"+
2181                                                 "[orig-feed-id='"+menu.callerRowId+"']");
2182
2183                                 }}));
2184
2185                         menu.addChild(new dijit.MenuItem({
2186                                 label: __("Mark group as read"),
2187                                 onClick: function(event) {
2188                                         selectArticles("none");
2189                                         selectArticles("all",
2190                                                 "#headlines-frame > div[id*=RROW]"+
2191                                                 "[orig-feed-id='"+menu.callerRowId+"']");
2192
2193                                         catchupSelection();
2194                                 }}));
2195
2196
2197                         menu.addChild(new dijit.MenuItem({
2198                                 label: __("Mark feed as read"),
2199                                 onClick: function(event) {
2200                                         catchupFeedInGroup(menu.callerRowId);
2201                                 }}));
2202
2203                         menu.addChild(new dijit.MenuItem({
2204                                 label: __("Edit feed"),
2205                                 onClick: function(event) {
2206                                         editFeed(menu.callerRowId);
2207                                 }}));
2208
2209                         menu.startup();
2210
2211                 }
2212
2213         } catch (e) {
2214                 exception_error("initHeadlinesMenu", e);
2215         }
2216 }
2217
2218 function cache_set(id, obj) {
2219         //console.log("cache_set: " + id);
2220         if (has_storage)
2221                 try {
2222                         sessionStorage[id] = obj;
2223                 } catch (e) {
2224                         sessionStorage.clear();
2225                 }
2226 }
2227
2228 function cache_get(id) {
2229         if (has_storage)
2230                 return sessionStorage[id];
2231 }
2232
2233 function cache_clear() {
2234         if (has_storage)
2235                 sessionStorage.clear();
2236 }
2237
2238 function cache_delete(id) {
2239         if (has_storage)
2240                 sessionStorage.removeItem(id);
2241 }
2242
2243 function cancelSearch() {
2244         try {
2245                 _search_query = "";
2246                 viewCurrentFeed();
2247         } catch (e) {
2248                 exception_error("cancelSearch", e);
2249         }
2250 }
2251
2252 function setSelectionScore() {
2253         try {
2254                 var ids = getSelectedArticleIds2();
2255
2256                 if (ids.length > 0) {
2257                         console.log(ids);
2258
2259                         var score = prompt(__("Please enter new score for selected articles:"), score);
2260
2261                         if (score != undefined) {
2262                                 var query = "op=article&method=setScore&id=" + param_escape(ids.toString()) +
2263                                         "&score=" + param_escape(score);
2264
2265                                 new Ajax.Request("backend.php", {
2266                                         parameters: query,
2267                                         onComplete: function(transport) {
2268                                                 var reply = JSON.parse(transport.responseText);
2269                                                 if (reply) {
2270                                                         console.log(ids);
2271
2272                                                         ids.each(function(id) {
2273                                                                 var row = $("RROW-" + id);
2274
2275                                                                 if (row) {
2276                                                                         var pic = row.getElementsByClassName("hlScorePic")[0];
2277
2278                                                                         if (pic) {
2279                                                                                 pic.src = pic.src.replace(/score_.*?\.png/,
2280                                                                                         reply["score_pic"]);
2281                                                                                 pic.setAttribute("score", score);
2282                                                                         }
2283                                                                 }
2284                                                         });
2285                                                 }
2286                                         } });
2287                         }
2288
2289                 } else {
2290                         alert(__("No articles are selected."));
2291                 }
2292         } catch (e) {
2293                 exception_error("setSelectionScore", e);
2294         }
2295 }
2296
2297 function updateScore(id) {
2298         try {
2299                 var pic = $$("#RROW-" + id + " .hlScorePic")[0];
2300
2301                 if (pic) {
2302
2303                         var query = "op=article&method=getScore&id=" + param_escape(id);
2304
2305                         new Ajax.Request("backend.php", {
2306                                 parameters: query,
2307                                 onComplete: function(transport) {
2308                                         console.log(transport.responseText);
2309
2310                                         var reply = JSON.parse(transport.responseText);
2311
2312                                         if (reply) {
2313                                                 pic.src = pic.src.replace(/score_.*?\.png/, reply["score_pic"]);
2314                                                 pic.setAttribute("score", reply["score"]);
2315                                                 pic.setAttribute("title", reply["score"]);
2316                                         }
2317                                 } });
2318                 }
2319
2320         } catch (e) {
2321                 exception_error("updateScore", e);
2322         }
2323 }
2324
2325 function changeScore(id, pic) {
2326         try {
2327                 var score = pic.getAttribute("score");
2328
2329                 var new_score = prompt(__("Please enter new score for this article:"), score);
2330
2331                 if (new_score != undefined) {
2332
2333                         var query = "op=article&method=setScore&id=" + param_escape(id) +
2334                                 "&score=" + param_escape(new_score);
2335
2336                         new Ajax.Request("backend.php", {
2337                                 parameters: query,
2338                                 onComplete: function(transport) {
2339                                         var reply = JSON.parse(transport.responseText);
2340
2341                                         if (reply) {
2342                                                 pic.src = pic.src.replace(/score_.*?\.png/, reply["score_pic"]);
2343                                                 pic.setAttribute("score", new_score);
2344                                                 pic.setAttribute("title", new_score);
2345                                         }
2346                                 } });
2347                 }
2348         } catch (e) {
2349                 exception_error("changeScore", e);
2350         }
2351 }
2352
2353 function displayArticleUrl(id) {
2354         try {
2355                 var query = "op=rpc&method=getlinktitlebyid&id=" + param_escape(id);
2356
2357                         new Ajax.Request("backend.php", {
2358                                 parameters: query,
2359                                 onComplete: function(transport) {
2360                                         var reply = JSON.parse(transport.responseText);
2361
2362                                         if (reply && reply.link) {
2363                                                 prompt(__("Article URL:"), reply.link);
2364                                         }
2365                                 } });
2366         } catch (e) {
2367                 exception_error("changeScore", e);
2368         }
2369 }
2370
2371 function scrollToRowId(id) {
2372         try {
2373                 var row = $(id);
2374
2375                 if (row)
2376                         $("headlines-frame").scrollTop = row.offsetTop - 4;
2377
2378         } catch (e) {
2379                 exception_error("scrollToRowId", e);
2380         }
2381 }
2382
2383 function updateFloatingTitle(unread_only) {
2384         try {
2385                 if (!isCdmMode()) return;
2386
2387                 var hf = $("headlines-frame");
2388
2389                 var elems = $$("#headlines-frame > div[id*=RROW]");
2390
2391                 for (var i = 0; i < elems.length; i++) {
2392
2393                         var child = elems[i];
2394
2395                         if (child && child.offsetTop + child.offsetHeight > hf.scrollTop) {
2396
2397                                 var header = child.getElementsByClassName("cdmHeader")[0];
2398
2399                                 if (unread_only || child.id != $("floatingTitle").getAttribute("rowid")) {
2400                                         if (child.id != $("floatingTitle").getAttribute("rowid")) {
2401                                                 $("floatingTitle").setAttribute("rowid", child.id);
2402                                                 $("floatingTitle").innerHTML = header.innerHTML;
2403                                                 $("floatingTitle").firstChild.innerHTML = "<img class='anchor markedPic' src='images/page_white_go.png' onclick=\"scrollToRowId('"+child.id+"')\">" + $("floatingTitle").firstChild.innerHTML;
2404
2405                                                 initFloatingMenu();
2406
2407                                                 var cb = $$("#floatingTitle .dijitCheckBox")[0];
2408
2409                                                 if (cb)
2410                                                         cb.parentNode.removeChild(cb);
2411                                         }
2412
2413                                         if (child.hasClassName("Unread"))
2414                                                 $("floatingTitle").addClassName("Unread");
2415                                         else
2416                                                 $("floatingTitle").removeClassName("Unread");
2417
2418                                         PluginHost.run(PluginHost.HOOK_FLOATING_TITLE, child);
2419                                 }
2420
2421                                 $("floatingTitle").style.marginRight = hf.offsetWidth - child.offsetWidth + "px";
2422                                 if (header.offsetTop + header.offsetHeight < hf.scrollTop + $("floatingTitle").offsetHeight - 5 &&
2423                                     child.offsetTop + child.offsetHeight >= hf.scrollTop + $("floatingTitle").offsetHeight - 5)
2424                                         $("floatingTitle").style.visibility = "visible";
2425                                 else
2426                                         $("floatingTitle").style.visibility = "hidden";
2427
2428                                 return;
2429
2430                         }
2431                 }
2432
2433         } catch (e) {
2434                 exception_error("updateFloatingTitle", e);
2435         }
2436 }
2437
2438 function catchupCurrentBatchIfNeeded() {
2439         if (catchup_id_batch.length > 0) {
2440                 window.clearTimeout(catchup_timeout_id);
2441                 catchup_timeout_id = window.setTimeout('catchupBatchedArticles()', 1000);
2442
2443                 if (catchup_id_batch.length >= 10) {
2444                         catchupBatchedArticles();
2445                 }
2446         }
2447 }
2448
2449 function cdmFooterClick(event) {
2450         event.stopPropagation();
2451 }