]> git.wh0rd.org Git - tt-rss.git/blob - viewfeed.js
b4941aed4243dc39c1e91bfd12d4d50d1dd09967
[tt-rss.git] / viewfeed.js
1 var active_post_id = false;
2 var last_article_view = false;
3 var active_real_feed_id = false;
4
5 var _tag_active_post_id = false;
6 var _tag_active_feed_id = false;
7 var _tag_active_cdm = false;
8
9 // FIXME: kludge, to restore scrollTop after tag editor terminates
10 var _tag_cdm_scroll = false;
11
12 // FIXME: kludges, needs proper implementation
13 var _reload_feedlist_after_view = false;
14
15 var _cdm_wd_timeout = false;
16 var _cdm_wd_vishist = new Array();
17
18 var article_cache = new Array();
19
20 function catchup_callback() {
21         if (xmlhttp_rpc.readyState == 4) {
22                 try {
23                         debug("catchup_callback");
24                         notify("");                     
25                         all_counters_callback2(xmlhttp_rpc);
26                         if (_catchup_callback_func) {
27                                 setTimeout(_catchup_callback_func, 10); 
28                         }
29                 } catch (e) {
30                         exception_error("catchup_callback", e);
31                 }
32         }
33 }
34
35 function catchup_callback2(transport, callback) {
36         try {
37                 debug("catchup_callback2 " + transport + ", " + callback);
38                 notify("");                     
39                 all_counters_callback2(transport);
40                 if (callback) {
41                         setTimeout(callback, 10);       
42                 }
43         } catch (e) {
44                 exception_error("catchup_callback2", e);
45         }
46 }
47
48 function clean_feed_selections() {
49         try {
50                 var feeds = document.getElementById("feedList").getElementsByTagName("LI");
51
52                 for (var i = 0; i < feeds.length; i++) {
53                         if (feeds[i].id && feeds[i].id.match("FEEDR-")) {
54                                 feeds[i].className = feeds[i].className.replace("Selected", "");
55                         }                       
56                 }
57         } catch (e) {
58                 exception_error("clean_feed_selections", e);
59         }
60 }
61
62 function headlines_callback2(transport, active_feed_id, is_cat, feed_cur_page) {
63         try {
64
65                 debug("headlines_callback2 [page=" + feed_cur_page + "]");
66
67                 clean_feed_selections();
68
69                 setActiveFeedId(active_feed_id);
70                 
71                 if (is_cat != undefined) {
72                         active_feed_is_cat = is_cat;
73                 }
74         
75                 if (!is_cat) {
76                         var feedr = document.getElementById("FEEDR-" + active_feed_id);
77                         if (feedr && !feedr.className.match("Selected")) {      
78                                 feedr.className = feedr.className + "Selected";
79                         } 
80                 }
81         
82                 var f = document.getElementById("headlines-frame");
83                 try {
84                         if (feed_cur_page == 0) { 
85                                 debug("resetting headlines scrollTop");
86                                 f.scrollTop = 0; 
87                         }
88                 } catch (e) { };
89         
90                 if (transport.responseXML) {
91                         var headlines = transport.responseXML.getElementsByTagName("headlines")[0];
92                         var headlines_count_obj = transport.responseXML.getElementsByTagName("headlines-count")[0];
93                         var headlines_unread_obj = transport.responseXML.getElementsByTagName("headlines-unread")[0];
94                         var disable_cache_obj = transport.responseXML.getElementsByTagName("disable-cache")[0];
95
96                         var headlines_count = headlines_count_obj.getAttribute("value");
97                         var headlines_unread = headlines_unread_obj.getAttribute("value");
98                         var disable_cache = disable_cache_obj.getAttribute("value") != "0";
99
100                         if (headlines_count == 0) {
101                                 _infscroll_disable = 1;
102                         } else {
103                                 _infscroll_disable = 0;
104                         }
105
106                         var counters = transport.responseXML.getElementsByTagName("counters")[0];
107                         var articles = transport.responseXML.getElementsByTagName("article");
108                         var runtime_info = transport.responseXML.getElementsByTagName("runtime-info");
109         
110                         if (feed_cur_page == 0) {
111                                 if (headlines) {
112                                         f.innerHTML = headlines.firstChild.nodeValue;
113
114                                         var cache_prefix = "";
115
116                                         if (is_cat) {
117                                                 cache_prefix = "C:";
118                                         } else {
119                                                 cache_prefix = "F:";
120                                         }
121
122                                         cache_invalidate(cache_prefix + active_feed_id);
123
124                                         if (!disable_cache) {
125                                                 cache_inject(cache_prefix + active_feed_id,
126                                                         headlines.firstChild.nodeValue, headlines_unread);
127                                         }
128
129                                 } else {
130                                         debug("headlines_callback: returned no data");
131                                 f.innerHTML = "<div class='whiteBox'>" + __('Could not update headlines (missing XML data)') + "</div>";
132         
133                                 }
134                         } else {
135                                 if (headlines) {
136                                         if (headlines_count > 0) {
137                                                 debug("adding some more headlines...");
138         
139                                                 var c = document.getElementById("headlinesList");
140                 
141                                                 if (!c) {
142                                                         c = document.getElementById("headlinesInnerContainer");
143                                                 }
144
145                                                 var ids = getSelectedArticleIds2();
146         
147                                                 c.innerHTML = c.innerHTML + headlines.firstChild.nodeValue;
148
149                                                 debug("restore selected ids: " + ids);
150
151                                                 for (var i = 0; i < ids.length; i++) {
152                                                         markHeadline(ids[i]);
153                                                 }
154
155                                         } else {
156                                                 debug("no new headlines received");
157                                         }
158                                 } else {
159                                         debug("headlines_callback: returned no data");
160                                         notify_error("Error while trying to load more headlines");      
161                                 }
162         
163                         }
164         
165                         if (articles) {
166                                 for (var i = 0; i < articles.length; i++) {
167                                         var a_id = articles[i].getAttribute("id");
168                                         debug("found id: " + a_id);
169                                         cache_inject(a_id, articles[i].firstChild.nodeValue);
170                                 }
171                         } else {
172                                 debug("no cached articles received");
173                         }
174         
175                         if (counters) {
176                                 debug("parsing piggybacked counters: " + counters);
177                                 parse_counters(counters, false);
178                         } else {
179                                 debug("counters container not found in reply");
180                         }
181         
182                         if (runtime_info) {
183                                 debug("parsing runtime info: " + runtime_info[0]);
184                                 parse_runtime_info(runtime_info[0]);
185                         } else {
186                                 debug("counters container not found in reply");
187                         }
188         
189                 } else {
190                         debug("headlines_callback: returned no XML object");
191                         f.innerHTML = "<div class='whiteBox'>" + __('Could not update headlines (missing XML object)') + "</div>";
192                 }
193         
194                 if (typeof correctPNG != 'undefined') {
195                         correctPNG();
196                 }
197         
198                 if (_cdm_wd_timeout) window.clearTimeout(_cdm_wd_timeout);
199         
200                 if (!document.getElementById("headlinesList") && 
201                                 getInitParam("cdm_auto_catchup") == 1) {
202                         debug("starting CDM watchdog");
203                         _cdm_wd_timeout = window.setTimeout("cdmWatchdog()", 5000);
204                         _cdm_wd_vishist = new Array();
205                 } else {
206                         debug("not in CDM mode or watchdog disabled");
207                 }
208         
209                 if (_tag_cdm_scroll) {
210                         try {
211                                 document.getElementById("headlinesInnerContainer").scrollTop = _tag_cdm_scroll;
212                                 _tag_cdm_scroll = false;
213                                 debug("resetting headlinesInner scrollTop");
214         
215                         } catch (e) { }
216                 }
217         
218                 _feed_cur_page = feed_cur_page;
219                 _infscroll_request_sent = 0;
220
221                 notify("");
222         } catch (e) {
223                 exception_error("headlines_callback2", e);
224         }
225 }
226
227 function render_article(article) {
228         try {
229                 var f = document.getElementById("content-frame");
230                 try {
231                         f.scrollTop = 0;
232                 } catch (e) { };
233
234                 f.innerHTML = article;
235
236         } catch (e) {
237                 exception_error("render_article", e);
238         }
239 }
240
241 function showArticleInHeadlines(id) {
242
243         try {
244
245                 cleanSelected("headlinesList");
246         
247                 var crow = document.getElementById("RROW-" + id);
248
249                 if (!crow) return;
250
251                 var article_is_unread = crow.className.match("Unread");
252                         
253                 crow.className = crow.className.replace("Unread", "");
254
255                 selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', false);
256         
257                 var upd_img_pic = document.getElementById("FUPDPIC-" + id);
258
259                 var cache_prefix = "";
260                                 
261                 if (activeFeedIsCat()) {
262                         cache_prefix = "C:";
263                 } else {
264                         cache_prefix = "F:";
265                 }
266
267                 var view_mode = false;
268
269                 try {
270                         view_mode = document.forms['main_toolbar_form'].view_mode;      
271                         view_mode = view_mode[view_mode.selectedIndex].value;
272                 } catch (e) {
273                         exception_error("showArticleInHeadlines/viewmode", e, true);
274                 }
275
276                 if (upd_img_pic && upd_img_pic.src.match("updated.png")) {
277                         upd_img_pic.src = "images/blank_icon.gif";
278
279                         cache_invalidate(cache_prefix + getActiveFeedId());
280
281                         cache_inject(cache_prefix + getActiveFeedId(),
282                                 document.getElementById("headlines-frame").innerHTML,
283                                 get_feed_unread(getActiveFeedId()));
284
285                 } else if (article_is_unread && view_mode == "all_articles") {
286
287                         cache_invalidate(cache_prefix + getActiveFeedId());
288
289                         cache_inject(cache_prefix + getActiveFeedId(),
290                                 document.getElementById("headlines-frame").innerHTML,
291                                 get_feed_unread(getActiveFeedId())-1);
292
293                 } else if (article_is_unread) {
294                         cache_invalidate(cache_prefix + getActiveFeedId());
295                 }
296
297                 markHeadline(id);
298
299         } catch (e) {
300                 exception_error("showArticleInHeadlines", e);
301         }
302 }
303
304 function article_callback2(transport, id, feed_id) {
305         try {
306                 debug("article_callback2 " + id);
307
308                 if (transport.responseXML) {
309
310                         active_real_feed_id = feed_id;
311                         active_post_id = id; 
312
313                         showArticleInHeadlines(id);     
314
315                         var reply = transport.responseXML.firstChild.firstChild;
316
317                         var articles = transport.responseXML.getElementsByTagName("article");
318
319                         for (var i = 0; i < articles.length; i++) {
320                                 var a_id = articles[i].getAttribute("id");
321
322                                 debug("found id: " + a_id);
323
324                                 if (a_id == active_post_id) {
325                                         debug("active article, rendering...");                                  
326                                         render_article(articles[i].firstChild.nodeValue);
327                                 }
328
329                                 cache_inject(a_id, articles[i].firstChild.nodeValue);
330                         }
331                 
332                 } else {
333                         debug("article_callback: returned no XML object");
334                         var f = document.getElementById("content-frame");
335                         f.innerHTML = "<div class='whiteBox'>" + __('Could not display article (missing XML object)') + "</div>";
336                 }
337
338                 var date = new Date();
339                 last_article_view = date.getTime() / 1000;
340
341                 if (typeof correctPNG != 'undefined') {
342                         correctPNG();
343                 }
344
345                 if (_reload_feedlist_after_view) {
346                         setTimeout('updateFeedList(false, false)', 50);                 
347                         _reload_feedlist_after_view = false;
348                 } else {
349                         var counters = transport.responseXML.getElementsByTagName("counters")[0];
350
351                         if (counters) {
352                                 debug("parsing piggybacked counters: " + counters);
353                                 parse_counters(counters, false);
354                         } else {
355                                 debug("counters container not found in reply");
356                         }
357                 }
358
359                 notify("");
360         } catch (e) {
361                 exception_error("article_callback2", e);
362         }
363 }
364
365 function view(id, feed_id, skip_history) {
366         
367         try {
368                 debug("loading article: " + id + "/" + feed_id);
369         
370                 var cached_article = cache_find(id);
371
372                 debug("cache check result: " + (cached_article != false));
373         
374                 enableHotkeys();
375         
376                 //setActiveFeedId(feed_id);
377
378                 var query = "backend.php?op=view&id=" + param_escape(id) +
379                         "&feed=" + param_escape(feed_id);
380
381                 var date = new Date();
382
383                 var neighbor_ids = getRelativePostIds(active_post_id);
384
385                 /* only request uncached articles */
386
387                 var cids_to_request = Array();
388
389                 for (var i = 0; i < neighbor_ids.length; i++) {
390                         if (!cache_check(neighbor_ids[i])) {
391                                 cids_to_request.push(neighbor_ids[i]);
392                         }
393                 }
394
395                 debug("additional ids: " + cids_to_request.toString());                 
396
397                 /* additional info for piggyback counters */
398
399                 if (tagsAreDisplayed()) {
400                         query = query + "&omode=lt";
401                 } else {
402                         query = query + "&omode=flc";
403                 }
404
405                 var date = new Date();
406                 var timestamp = Math.round(date.getTime() / 1000);
407                 query = query + "&ts=" + timestamp;
408
409                 query = query + "&cids=" + cids_to_request.toString();
410
411                 var crow = document.getElementById("RROW-" + id);
412                 var article_is_unread = crow.className.match("Unread");
413
414                 showArticleInHeadlines(id);
415
416                 if (!cached_article) {
417
418                         notify_progress("Loading, please wait...");
419
420                 } else if (cached_article && article_is_unread) {
421
422                         query = query + "&mode=prefetch";
423
424                         render_article(cached_article);
425
426                 } else if (cached_article) {
427
428                         query = query + "&mode=prefetch_old";
429                         render_article(cached_article);
430                 }
431
432                 cache_expire();
433
434                 new Ajax.Request(query, {
435                         onComplete: function(transport) { 
436                                 article_callback2(transport, id, feed_id); 
437                         } });
438
439         } catch (e) {
440                 exception_error("view", e);
441         }
442 }
443
444 function tMark(id) {
445         return toggleMark(id);
446 }
447
448 function tPub(id) {
449         return togglePub(id);
450 }
451
452 function tMark_afh_off(effect) {
453         try {
454                 var elem = effect.effects[0].element;
455
456                 debug("tMark_afh_off : " + elem.id);
457
458                 if (elem) {
459                         elem.src = elem.src.replace("mark_set", "mark_unset");
460                         elem.alt = __("Star article");
461                         Element.show(elem);
462                 }
463
464         } catch (e) {
465                 exception_error("tMark_afh_off", e);
466         }
467 }
468
469 function tPub_afh_off(effect) {
470         try {
471                 var elem = effect.effects[0].element;
472
473                 debug("tPub_afh_off : " + elem.id);
474
475                 if (elem) {
476                         elem.src = elem.src.replace("pub_set", "pub_unset");
477                         elem.alt = __("Publish article");
478                         Element.show(elem);
479                 }
480
481         } catch (e) {
482                 exception_error("tPub_afh_off", e);
483         }
484 }
485
486 function toggleMark(id, client_only, no_effects) {
487
488         try {
489
490                 var query = "backend.php?op=rpc&id=" + id + "&subop=mark";
491         
492                 query = query + "&afid=" + getActiveFeedId();
493         
494                 if (tagsAreDisplayed()) {
495                         query = query + "&omode=tl";
496                 } else {
497                         query = query + "&omode=flc";
498                 }
499         
500                 var mark_img = document.getElementById("FMPIC-" + id);
501                 var vfeedu = document.getElementById("FEEDU--1");
502                 var crow = document.getElementById("RROW-" + id);
503         
504                 if (mark_img.src.match("mark_unset")) {
505                         mark_img.src = mark_img.src.replace("mark_unset", "mark_set");
506                         mark_img.alt = __("Unstar article");
507                         query = query + "&mark=1";
508         
509 /*                      if (vfeedu && crow.className.match("Unread")) {
510                                 vfeedu.innerHTML = (+vfeedu.innerHTML) + 1;
511                         } */
512         
513                 } else {
514                         //mark_img.src = "images/mark_unset.png";
515                         mark_img.alt = __("Please wait...");
516                         query = query + "&mark=0";
517         
518 /*                      if (vfeedu && crow.className.match("Unread")) {
519                                 vfeedu.innerHTML = (+vfeedu.innerHTML) - 1;
520                         } */
521         
522                         if (document.getElementById("headlinesList") && !no_effects) {
523                                 Effect.Puff(mark_img, {duration : 0.25, afterFinish: tMark_afh_off});
524                         } else { 
525                                 mark_img.src = mark_img.src.replace("mark_set", "mark_unset");
526                                 mark_img.alt = __("Star article");
527                         }
528                 }
529         
530 /*              var vfeedctr = document.getElementById("FEEDCTR--1");
531                 var vfeedr = document.getElementById("FEEDR--1");
532         
533                 if (vfeedu && vfeedctr) {
534                         if ((+vfeedu.innerHTML) > 0) {
535                                 if (crow.className.match("Unread") && !vfeedr.className.match("Unread")) {
536                                         vfeedr.className = vfeedr.className + "Unread";
537                                         vfeedctr.className = "odd";
538                                 }
539                         } else {
540                                 vfeedctr.className = "invisible";
541                                 vfeedr.className = vfeedr.className.replace("Unread", "");
542                         }
543                 }
544         
545                 debug("toggle starred for aid " + id);
546         
547                 //new Ajax.Request(query); */
548
549                 if (!client_only) {
550                         debug(query);
551
552                         new Ajax.Request(query, {
553                                 onComplete: function(transport) { 
554                                         all_counters_callback2(transport); 
555                                 } });
556
557                 }
558
559         } catch (e) {
560                 exception_error("toggleMark", e);
561         }
562 }
563
564 function togglePub(id, client_only, no_effects) {
565
566         try {
567
568                 var query = "backend.php?op=rpc&id=" + id + "&subop=publ";
569         
570                 query = query + "&afid=" + getActiveFeedId();
571         
572                 if (tagsAreDisplayed()) {
573                         query = query + "&omode=tl";
574                 } else {
575                         query = query + "&omode=flc";
576                 }
577         
578                 var mark_img = document.getElementById("FPPIC-" + id);
579                 var vfeedu = document.getElementById("FEEDU--2");
580                 var crow = document.getElementById("RROW-" + id);
581         
582                 if (mark_img.src.match("pub_unset")) {
583                         mark_img.src = mark_img.src.replace("pub_unset", "pub_set");
584                         mark_img.alt = __("Unpublish article");
585                         query = query + "&pub=1";
586         
587 /*                      if (vfeedu && crow.className.match("Unread")) {
588                                 vfeedu.innerHTML = (+vfeedu.innerHTML) + 1;
589                         } */
590         
591                 } else {
592                         //mark_img.src = "images/pub_unset.png";
593                         mark_img.alt = __("Please wait...");
594                         query = query + "&pub=0";
595         
596 /*                      if (vfeedu && crow.className.match("Unread")) {
597                                 vfeedu.innerHTML = (+vfeedu.innerHTML) - 1;
598                         } */
599
600                         if (document.getElementById("headlinesList") && !no_effects) {
601                                 Effect.Puff(mark_img, {duration : 0.25, afterFinish: tPub_afh_off});
602                         } else { 
603                                 mark_img.src = mark_img.src.replace("pub_set", "pub_unset");
604                                 mark_img.alt = __("Publish article");
605                         }
606                 }
607         
608 /*              var vfeedctr = document.getElementById("FEEDCTR--2");
609                 var vfeedr = document.getElementById("FEEDR--2");
610         
611                 if (vfeedu && vfeedctr) {
612                         if ((+vfeedu.innerHTML) > 0) {
613                                 if (crow.className.match("Unread") && !vfeedr.className.match("Unread")) {
614                                         vfeedr.className = vfeedr.className + "Unread";
615                                         vfeedctr.className = "odd";
616                                 }
617                         } else {
618                                 vfeedctr.className = "invisible";
619                                 vfeedr.className = vfeedr.className.replace("Unread", "");
620                         }
621                 }
622         
623                 debug("toggle published for aid " + id);
624         
625                 new Ajax.Request(query); */
626
627                 if (!client_only) {
628                         new Ajax.Request(query, {
629                                 onComplete: function(transport) { 
630                                         all_counters_callback2(transport); 
631                                 } });
632                 }
633
634         } catch (e) {
635
636                 exception_error("togglePub", e);
637         }
638 }
639
640 function correctHeadlinesOffset(id) {
641         
642         try {
643
644                 var hlist = document.getElementById("headlinesList");
645                 var container = document.getElementById("headlinesInnerContainer");
646                 var row = document.getElementById("RROW-" + id);
647         
648                 var viewport = container.offsetHeight;
649         
650                 var rel_offset_top = row.offsetTop - container.scrollTop;
651                 var rel_offset_bottom = row.offsetTop + row.offsetHeight - container.scrollTop;
652         
653                 debug("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom);
654                 debug("Vport: " + viewport);
655
656                 if (rel_offset_top <= 0 || rel_offset_top > viewport) {
657                         container.scrollTop = row.offsetTop;
658                 } else if (rel_offset_bottom > viewport) {
659
660                         /* doesn't properly work with Opera in some cases because
661                                 Opera fucks up element scrolling */
662
663                         container.scrollTop = row.offsetTop + row.offsetHeight - viewport;              
664                 } 
665
666         } catch (e) {
667                 exception_error("correctHeadlinesOffset", e);
668         }
669
670 }
671
672 function moveToPost(mode) {
673
674         try {
675
676         // check for combined mode
677                 if (document.getElementById("headlinesList")) {
678         
679                         var rows = getVisibleHeadlineIds();
680                 
681                         var prev_id = false;
682                         var next_id = false;
683                 
684                         if (!document.getElementById('RROW-' + active_post_id)) {
685                                 active_post_id = false;
686                         }
687                 
688                         if (active_post_id == false) {
689                                 next_id = getFirstVisibleHeadlineId();
690                                 prev_id = getLastVisibleHeadlineId();
691                         } else {        
692                                 for (var i = 0; i < rows.length; i++) {
693                                         if (rows[i] == active_post_id) {
694                                                 prev_id = rows[i-1];
695                                                 next_id = rows[i+1];                    
696                                         }
697                                 }
698                         }
699                 
700                         if (mode == "next") {
701                                 if (next_id) {
702                                         correctHeadlinesOffset(next_id);
703                                         view(next_id, getActiveFeedId());
704                                 }
705                         }
706                 
707                         if (mode == "prev") {
708                                 if (prev_id) {
709                                         correctHeadlinesOffset(prev_id);
710                                         view(prev_id, getActiveFeedId());
711                                 }
712                         } 
713                 } else {
714                         var rows = cdmGetUnreadArticles();
715
716                         if (mode == "next") {
717
718                                 for (var i = 0; i < rows.length; i++) {
719
720                                         if (cdmArticleIsActuallyVisible(rows[i]) ||
721                                                         cdmArticleIsBelowViewport(rows[i])) {
722
723                                                 cdmScrollToArticleId(rows[i]);
724                                                 setTimeout("toggleUnread(" + rows[i] + ", undefined, true)", 500);
725                                                 //toggleUnread(rows[i], undefined, true);
726
727                                                 return;
728                                         }
729                                 }
730
731                                 cdmScrollViewport('bottom');
732
733                         } else if (mode == "prev") {
734
735                                 for (var i = 0; i < rows.length; i++) {
736
737                                         if (cdmArticleIsActuallyVisible(rows[i]) ||
738                                                         (cdmArticleIsAboveViewport(rows[i]) &&
739                                                                 !cdmArticleIsAboveViewport(rows[i+1]))) {
740
741                                                 cdmScrollToArticleId(rows[i]);
742                                                 setTimeout("toggleUnread(" + rows[i] + ", undefined, true)", 500);
743                                                 //toggleUnread(rows[i], undefined, true);
744
745                                                 break;
746                                         } 
747                                 }
748
749                         }
750         
751                 }
752
753         } catch (e) {
754                 exception_error(e, "moveToPost");
755         }
756 }
757
758 function toggleSelected(id) {
759         try {
760         
761                 var row = document.getElementById("RROW-" + id);
762                 if (row) {
763                         var nc = row.className;
764                         
765                         if (!nc.match("Selected")) {
766                                 nc = nc + "Selected";
767                         } else {
768                                 nc = nc.replace("Selected", "");
769                         }
770
771                         row.className = nc;
772                 }
773         } catch (e) {
774                 exception_error(e, "toggleSelected");
775         }
776 }
777
778 /*function toggleUnread_afh(effect) {
779         try {
780
781                 var elem = effect.element;
782                 //elem.style.backgroundColor = "transparent";
783
784                 alert(elem.className);
785         } catch (e) {
786                 exception_error(e, "toggleUnread_afh");
787         }
788 } */
789
790 function toggleUnread(id, cmode, effect) {
791         try {
792         
793                 var row = document.getElementById("RROW-" + id);
794                 if (row) {
795                         var nc = row.className;
796                         nc = nc.replace("Unread", "");
797                         nc = nc.replace("Selected", "");
798
799                         // since we are removing selection from the object, uncheck
800                         // corresponding checkbox
801
802                         var cb = document.getElementById("RCHK-" + id);
803                         if (cb) {
804                                 cb.checked = false;
805                         }
806
807                         // NOTE: I'm not sure that resetting selection here is a feature -fox
808
809                         if (cmode == undefined || cmode == 2) {
810                                 if (row.className.match("Unread")) {
811                                         row.className = nc;
812
813 /*                                      if (effect) {
814                                                 new Effect.Highlight(row, {duration: 1, startcolor: "#fff7d5",
815                                                         afterFinish: toggleUnread_afh,
816                                                         queue: { position:'end', scope: 'TMRQ-' + id, limit: 1 } } );
817                                         } */
818
819                                 } else {
820                                         row.className = nc + "Unread";
821                                 }
822                         } else if (cmode == 0) {
823                                 row.className = nc;
824
825 /*                              if (effect) {
826                                         new Effect.Highlight(row, {duration: 1, startcolor: "#fff7d5",
827                                                 queue: { position:'end', scope: 'TMRQ-' + id, limit: 1 } } );
828                                 } */
829                         } else if (cmode == 1) {
830                                 row.className = nc + "Unread";
831                         }
832
833                         if (cmode == undefined) cmode = 2;
834
835                         var query = "backend.php?op=rpc&subop=catchupSelected&ids=" +
836                                 param_escape(id) + "&cmode=" + param_escape(cmode);
837
838 //                      notify_progress("Loading, please wait...");
839
840                         new Ajax.Request(query, {
841                                 onComplete: function(transport) { 
842                                         all_counters_callback2(transport); 
843                                 } });
844
845                 }
846
847
848         } catch (e) {
849                 exception_error("toggleUnread", e);
850         }
851 }
852
853 function selectionToggleUnread(cdm_mode, set_state, callback_func, no_error) {
854         try {
855                 var rows;
856
857                 if (cdm_mode) {
858                         rows = cdmGetSelectedArticles();
859                 } else {        
860                         rows = getSelectedTableRowIds("headlinesList", "RROW", "RCHK");
861                 }
862
863                 if (rows.length == 0 && !no_error) {
864                         alert(__("No articles are selected."));
865                         return;
866                 }
867
868                 for (i = 0; i < rows.length; i++) {
869                         var row = document.getElementById("RROW-" + rows[i]);
870                         if (row) {
871                                 var nc = row.className;
872                                 nc = nc.replace("Unread", "");
873                                 nc = nc.replace("Selected", "");
874
875                                 if (set_state == undefined) {
876                                         if (row.className.match("Unread")) {
877                                                 row.className = nc + "Selected";
878                                         } else {
879                                                 row.className = nc + "UnreadSelected";
880                                         }
881                                 }
882
883                                 if (set_state == false) {
884                                         row.className = nc + "Selected";
885                                 }
886
887                                 if (set_state == true) {
888                                         row.className = nc + "UnreadSelected";
889                                 }
890                         }
891                 }
892
893                 if (rows.length > 0) {
894
895                         var cmode = "";
896
897                         if (set_state == undefined) {
898                                 cmode = "2";
899                         } else if (set_state == true) {
900                                 cmode = "1";
901                         } else if (set_state == false) {
902                                 cmode = "0";
903                         }
904
905                         var query = "backend.php?op=rpc&subop=catchupSelected&ids=" +
906                                 param_escape(rows.toString()) + "&cmode=" + cmode;
907
908                         notify_progress("Loading, please wait...");
909
910                         new Ajax.Request(query, {
911                                 onComplete: function(transport) { 
912                                         catchup_callback2(transport, callback_func); 
913                                 } });
914
915                 }
916
917         } catch (e) {
918                 exception_error("selectionToggleUnread", e);
919         }
920 }
921
922 function selectionToggleMarked(cdm_mode) {
923         try {
924         
925                 var rows;
926                 
927                 if (cdm_mode) {
928                         rows = cdmGetSelectedArticles();
929                 } else {        
930                         rows = getSelectedTableRowIds("headlinesList", "RROW", "RCHK");
931                 }       
932
933                 if (rows.length == 0) {
934                         alert(__("No articles are selected."));
935                         return;
936                 }
937
938                 for (i = 0; i < rows.length; i++) {
939                         toggleMark(rows[i], true, true);
940                 }
941
942                 if (rows.length > 0) {
943
944                         var query = "backend.php?op=rpc&subop=markSelected&ids=" +
945                                 param_escape(rows.toString()) + "&cmode=2";
946
947                         query = query + "&afid=" + getActiveFeedId();
948
949 /*                      if (tagsAreDisplayed()) {
950                                 query = query + "&omode=tl";
951                         } else {
952                                 query = query + "&omode=flc";
953                         } */
954
955                         query = query + "&omode=lc";
956
957                         new Ajax.Request(query, {
958                                 onComplete: function(transport) { 
959                                         all_counters_callback2(transport); 
960                                 } });
961
962                 }
963
964         } catch (e) {
965                 exception_error("selectionToggleMarked", e);
966         }
967 }
968
969 function selectionTogglePublished(cdm_mode) {
970         try {
971         
972                 var rows;
973                 
974                 if (cdm_mode) {
975                         rows = cdmGetSelectedArticles();
976                 } else {        
977                         rows = getSelectedTableRowIds("headlinesList", "RROW", "RCHK");
978                 }       
979
980                 if (rows.length == 0) {
981                         alert(__("No articles are selected."));
982                         return;
983                 }
984
985                 for (i = 0; i < rows.length; i++) {
986                         togglePub(rows[i], true, true);
987                 }
988
989                 if (rows.length > 0) {
990
991                         var query = "backend.php?op=rpc&subop=publishSelected&ids=" +
992                                 param_escape(rows.toString()) + "&cmode=2";
993
994                         query = query + "&afid=" + getActiveFeedId();
995
996 /*                      if (tagsAreDisplayed()) {
997                                 query = query + "&omode=tl";
998                         } else {
999                                 query = query + "&omode=flc";
1000                         } */
1001
1002                         query = query + "&omode=lc";
1003
1004                         new Ajax.Request(query, {
1005                                 onComplete: function(transport) { 
1006                                         all_counters_callback2(transport); 
1007                                 } });
1008
1009                 }
1010
1011         } catch (e) {
1012                 exception_error("selectionToggleMarked", e);
1013         }
1014 }
1015
1016 function cdmGetSelectedArticles() {
1017         var sel_articles = new Array();
1018         var container = document.getElementById("headlinesInnerContainer");
1019
1020         for (i = 0; i < container.childNodes.length; i++) {
1021                 var child = container.childNodes[i];
1022
1023                 if (child.id.match("RROW-") && child.className.match("Selected")) {
1024                         var c_id = child.id.replace("RROW-", "");
1025                         sel_articles.push(c_id);
1026                 }
1027         }
1028
1029         return sel_articles;
1030 }
1031
1032 function cdmGetVisibleArticles() {
1033         var sel_articles = new Array();
1034         var container = document.getElementById("headlinesInnerContainer");
1035
1036         for (i = 0; i < container.childNodes.length; i++) {
1037                 var child = container.childNodes[i];
1038
1039                 if (child.id.match("RROW-")) {
1040                         var c_id = child.id.replace("RROW-", "");
1041                         sel_articles.push(c_id);
1042                 }
1043         }
1044
1045         return sel_articles;
1046 }
1047
1048 function cdmGetUnreadArticles() {
1049         var sel_articles = new Array();
1050         var container = document.getElementById("headlinesInnerContainer");
1051
1052         for (i = 0; i < container.childNodes.length; i++) {
1053                 var child = container.childNodes[i];
1054
1055                 if (child.id.match("RROW-") && child.className.match("Unread")) {
1056                         var c_id = child.id.replace("RROW-", "");
1057                         sel_articles.push(c_id);
1058                 }
1059         }
1060
1061         return sel_articles;
1062 }
1063
1064
1065 // mode = all,none,unread
1066 function cdmSelectArticles(mode) {
1067         var container = document.getElementById("headlinesInnerContainer");
1068
1069         for (i = 0; i < container.childNodes.length; i++) {
1070                 var child = container.childNodes[i];
1071
1072                 if (child.id.match("RROW-")) {
1073                         var aid = child.id.replace("RROW-", "");
1074
1075                         var cb = document.getElementById("RCHK-" + aid);
1076
1077                         if (mode == "all") {
1078                                 if (!child.className.match("Selected")) {
1079                                         child.className = child.className + "Selected";
1080                                         cb.checked = true;
1081                                 }
1082                         } else if (mode == "unread") {
1083                                 if (child.className.match("Unread") && !child.className.match("Selected")) {
1084                                         child.className = child.className + "Selected";
1085                                         cb.checked = true;
1086                                 }
1087                         } else {
1088                                 child.className = child.className.replace("Selected", "");
1089                                 cb.checked = false;
1090                         }
1091                 }               
1092         }
1093 }
1094
1095 function catchupPage() {
1096
1097         var fn = getFeedName(getActiveFeedId(), active_feed_is_cat);
1098         
1099         var str = __("Mark all visible articles in %s as read?");
1100
1101         str = str.replace("%s", fn);
1102
1103         if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
1104                 return;
1105         }
1106
1107         if (document.getElementById("headlinesList")) {
1108                 selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', true, 'Unread', true);
1109                 selectionToggleUnread(false, false, 'viewCurrentFeed()', true);
1110                 selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', false);
1111         } else {
1112                 cdmSelectArticles('all');
1113                 selectionToggleUnread(true, false, 'viewCurrentFeed()', true)
1114                 cdmSelectArticles('none');
1115         }
1116 }
1117
1118 function catchupSelection() {
1119
1120         try {
1121
1122                 var rows;
1123         
1124                 if (document.getElementById("headlinesList")) {
1125                         rows = getSelectedTableRowIds("headlinesList", "RROW", "RCHK");
1126                 } else {        
1127                         rows = cdmGetSelectedArticles();
1128                 }
1129         
1130                 if (rows.length == 0) {
1131                         alert(__("No articles are selected."));
1132                         return;
1133                 }
1134         
1135         
1136                 var fn = getFeedName(getActiveFeedId(), active_feed_is_cat);
1137                 
1138                 var str = __("Mark %d selected articles in %s as read?");
1139         
1140                 str = str.replace("%d", rows.length);
1141                 str = str.replace("%s", fn);
1142         
1143                 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
1144                         return;
1145                 }
1146         
1147                 if (document.getElementById("headlinesList")) {
1148                         selectionToggleUnread(false, false, 'viewCurrentFeed()', true);
1149         //              selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', false);
1150                 } else {
1151                         selectionToggleUnread(true, false, 'viewCurrentFeed()', true)
1152         //              cdmSelectArticles('none');
1153                 }
1154
1155         } catch (e) {
1156                 exception_error("catchupSelection", e);
1157         }
1158 }
1159
1160
1161 function labelFromSearch(search, search_mode, match_on, feed_id, is_cat) {
1162
1163         if (!xmlhttp_ready(xmlhttp_rpc)) {
1164                 printLockingError();
1165         }
1166
1167         var title = prompt(__("Please enter label title:"), "");
1168
1169         if (title) {
1170
1171                 var query = "backend.php?op=labelFromSearch&search=" + param_escape(search) +
1172                         "&smode=" + param_escape(search_mode) + "&match=" + param_escape(match_on) +
1173                         "&feed=" + param_escape(feed_id) + "&is_cat=" + param_escape(is_cat) + 
1174                         "&title=" + param_escape(title);
1175
1176                 debug("LFS: " + query);
1177
1178                 new Ajax.Request(query, {
1179                         onComplete: function(transport) {
1180                                         dlg_frefresh_callback(transport);
1181                                 } });
1182         }
1183 }
1184
1185 function editArticleTags(id, feed_id, cdm_enabled) {
1186         _tag_active_post_id = id;
1187         _tag_active_feed_id = feed_id;
1188         _tag_active_cdm = cdm_enabled;
1189
1190         cache_invalidate(id);
1191
1192         try {
1193                 _tag_cdm_scroll = document.getElementById("headlinesInnerContainer").scrollTop;
1194         } catch (e) { }
1195         displayDlg('editArticleTags', id);
1196 }
1197
1198
1199 function tag_saved_callback(transport) {
1200         try {
1201                 debug("in tag_saved_callback");
1202
1203                 closeInfoBox();
1204                 notify("");
1205
1206                 if (tagsAreDisplayed()) {
1207                         _reload_feedlist_after_view = true;
1208                 }
1209
1210                 if (!_tag_active_cdm) {
1211                         if (active_post_id == _tag_active_post_id) {
1212                                 debug("reloading current article");
1213                                 view(_tag_active_post_id, _tag_active_feed_id);                 
1214                         }
1215                 } else {
1216                         debug("reloading current feed");
1217                         viewCurrentFeed();
1218                 }
1219
1220         } catch (e) {
1221                 exception_error("catchup_callback", e);
1222         }
1223 }
1224
1225 function editTagsSave() {
1226
1227         notify_progress("Saving article tags...");
1228
1229         var form = document.forms["tag_edit_form"];
1230
1231         var query = Form.serialize("tag_edit_form");
1232
1233         query = "backend.php?op=rpc&subop=setArticleTags&" + query;
1234
1235         debug(query);
1236
1237         new Ajax.Request(query, {
1238                 onComplete: function(transport) {
1239                                 tag_saved_callback(transport);
1240                         } });
1241
1242 }
1243
1244 function editTagsInsert() {
1245         try {
1246
1247                 var form = document.forms["tag_edit_form"];
1248
1249                 var found_tags = form.found_tags;
1250                 var tags_str = form.tags_str;
1251
1252                 var tag = found_tags[found_tags.selectedIndex].value;
1253
1254                 if (tags_str.value.length > 0 && 
1255                                 tags_str.value.lastIndexOf(", ") != tags_str.value.length - 2) {
1256
1257                         tags_str.value = tags_str.value + ", ";
1258                 }
1259
1260                 tags_str.value = tags_str.value + tag + ", ";
1261
1262                 found_tags.selectedIndex = 0;
1263                 
1264         } catch (e) {
1265                 exception_error(e, "editTagsInsert");
1266         }
1267 }
1268
1269 function cdmScrollViewport(where) {
1270         debug("cdmScrollViewport: " + where);
1271
1272         var ctr = document.getElementById("headlinesInnerContainer");
1273
1274         if (!ctr) return;
1275
1276         if (where == "bottom") {
1277                 ctr.scrollTop = ctr.scrollHeight;
1278         } else {
1279                 ctr.scrollTop = where;
1280         }
1281 }
1282
1283 function cdmArticleIsBelowViewport(id) {
1284         try {
1285                 var ctr = document.getElementById("headlinesInnerContainer");
1286                 var e = document.getElementById("RROW-" + id);
1287
1288                 if (!e || !ctr) return;
1289
1290                 // article starts below viewport
1291
1292                 if (ctr.scrollTop < e.offsetTop) {
1293                         return true;
1294                 } else {        
1295                         return false;
1296                 }
1297
1298         } catch (e) {
1299                 exception_error(e, "cdmArticleIsVisible");
1300         }
1301 }
1302
1303 function cdmArticleIsAboveViewport(id) {
1304         try {
1305                 var ctr = document.getElementById("headlinesInnerContainer");
1306                 var e = document.getElementById("RROW-" + id);
1307
1308                 if (!e || !ctr) return;
1309
1310                 // article starts above viewport
1311
1312                 if (ctr.scrollTop > e.offsetTop + e.offsetHeight) {
1313                         return true;
1314                 } else {        
1315                         return false;
1316                 }
1317
1318         } catch (e) {
1319                 exception_error(e, "cdmArticleIsVisible");
1320         }
1321 }
1322
1323 function cdmScrollToArticleId(id) {
1324         try {
1325                 var ctr = document.getElementById("headlinesInnerContainer");
1326                 var e = document.getElementById("RROW-" + id);
1327
1328                 if (!e || !ctr) return;
1329
1330                 ctr.scrollTop = e.offsetTop;
1331
1332         } catch (e) {
1333                 exception_error(e, "cdmScrollToArticleId");
1334         }
1335 }
1336
1337 function cdmArticleIsActuallyVisible(id) {
1338         try {
1339                 var ctr = document.getElementById("headlinesInnerContainer");
1340                 var e = document.getElementById("RROW-" + id);
1341
1342                 if (!e || !ctr) return;
1343
1344                 // article fits in viewport OR article is longer than viewport and
1345                 // its bottom is visible
1346
1347                 if (ctr.scrollTop <= e.offsetTop && e.offsetTop + e.offsetHeight <=
1348                                 ctr.scrollTop + ctr.offsetHeight) {
1349
1350                         return true;
1351                 
1352                 } else if (e.offsetHeight > ctr.offsetHeight &&
1353                                 e.offsetTop + e.offsetHeight >= ctr.scrollTop &&
1354                                 e.offsetTop + e.offsetHeight <= ctr.scrollTop + ctr.offsetHeight) {
1355
1356                         return true;
1357
1358                 }
1359
1360                 return false;
1361
1362         } catch (e) {
1363                 exception_error(e, "cdmArticleIsVisible");
1364         }
1365 }
1366
1367 function cdmWatchdog() {
1368
1369         try {
1370
1371                 var ctr = document.getElementById("headlinesInnerContainer");
1372
1373                 if (!ctr) return;
1374
1375                 var ids = new Array();
1376
1377                 var e = ctr.firstChild;
1378
1379                 while (e) {
1380                         if (e.className && e.className == "cdmArticleUnread" && e.id &&
1381                                         e.id.match("RROW-")) {
1382
1383                                 // article fits in viewport OR article is longer than viewport and
1384                                 // its bottom is visible
1385
1386                                 if (ctr.scrollTop <= e.offsetTop && e.offsetTop + e.offsetHeight <=
1387                                                 ctr.scrollTop + ctr.offsetHeight) {
1388
1389 //                                      debug(e.id + " is visible " + e.offsetTop + "." + 
1390 //                                              (e.offsetTop + e.offsetHeight) + " vs " + ctr.scrollTop + "." +
1391 //                                              (ctr.scrollTop + ctr.offsetHeight));
1392
1393                                         ids.push(e.id.replace("RROW-", ""));
1394
1395                                 } else if (e.offsetHeight > ctr.offsetHeight &&
1396                                                 e.offsetTop + e.offsetHeight >= ctr.scrollTop &&
1397                                                 e.offsetTop + e.offsetHeight <= ctr.scrollTop + ctr.offsetHeight) {
1398
1399                                         ids.push(e.id.replace("RROW-", "")); 
1400
1401                                 }
1402
1403                                 // method 2: article bottom is visible and is in upper 1/2 of the viewport
1404
1405 /*                              if (e.offsetTop + e.offsetHeight >= ctr.scrollTop &&
1406                                                 e.offsetTop + e.offsetHeight <= ctr.scrollTop + ctr.offsetHeight/2) {
1407
1408                                         ids.push(e.id.replace("RROW-", "")); 
1409
1410                                 } */
1411
1412                         }
1413
1414                         e = e.nextSibling;
1415                 }
1416
1417                 debug("cdmWatchdog, ids= " + ids.toString());
1418
1419                 if (ids.length > 0) {
1420
1421                         for (var i = 0; i < ids.length; i++) {
1422                                 var e = document.getElementById("RROW-" + ids[i]);
1423                                 if (e) {
1424                                         e.className = e.className.replace("Unread", "");
1425                                 }
1426                         }
1427
1428                         var query = "backend.php?op=rpc&subop=catchupSelected&ids=" +
1429                                 param_escape(ids.toString()) + "&cmode=0";
1430
1431                         new Ajax.Request(query, {
1432                                 onComplete: function(transport) { 
1433                                         all_counters_callback2(transport); 
1434                                 } });
1435
1436                 }
1437
1438                 _cdm_wd_timeout = window.setTimeout("cdmWatchdog()", 4000);
1439
1440         } catch (e) {
1441                 exception_error(e, "cdmWatchdog");
1442         }
1443
1444 }
1445
1446
1447 function cache_inject(id, article, param) {
1448         if (!cache_check_param(id, param)) {
1449                 debug("cache_article: miss: " + id + " [p=" + param + "]");
1450
1451                 var cache_obj = new Array();
1452
1453                 cache_obj["id"] = id;
1454                 cache_obj["data"] = article;
1455                 cache_obj["param"] = param;
1456
1457                 article_cache.push(cache_obj);
1458
1459         } else {
1460                 debug("cache_article: hit: " + id + " [p=" + param + "]");
1461         }
1462 }
1463
1464 function cache_find(id) {
1465         for (var i = 0; i < article_cache.length; i++) {
1466                 if (article_cache[i]["id"] == id) {
1467                         return article_cache[i]["data"];
1468                 }
1469         }
1470         return false;
1471 }
1472
1473 function cache_find_param(id, param) {
1474         for (var i = 0; i < article_cache.length; i++) {
1475                 if (article_cache[i]["id"] == id && article_cache[i]["param"] == param) {
1476                         return article_cache[i]["data"];
1477                 }
1478         }
1479         return false;
1480 }
1481
1482 function cache_check(id) {
1483         for (var i = 0; i < article_cache.length; i++) {
1484                 if (article_cache[i]["id"] == id) {
1485                         return true;
1486                 }
1487         }
1488         return false;
1489 }
1490
1491 function cache_check_param(id, param) {
1492         for (var i = 0; i < article_cache.length; i++) {
1493
1494 //              debug("cache_check_param " + article_cache[i]["id"] + ":" + 
1495 //                      article_cache[i]["param"] + " vs " + id + ":" + param);
1496
1497                 if (article_cache[i]["id"] == id && article_cache[i]["param"] == param) {
1498                         return true;
1499                 }
1500         }
1501         return false;
1502 }
1503
1504 function cache_expire() {
1505         while (article_cache.length > 20) {
1506                 article_cache.shift();
1507         }
1508 }
1509
1510 function cache_invalidate(id) {
1511         var i = 0
1512
1513         try {   
1514
1515                 while (i < article_cache.length) {
1516                         if (article_cache[i]["id"] == id) {
1517                                 debug("cache_invalidate: removed id " + id);
1518                                 article_cache.splice(i, 1);
1519                                 return true;
1520                         }
1521                         i++;
1522                 }
1523                 debug("cache_invalidate: id not found: " + id);
1524                 return false;
1525         } catch (e) {
1526                 exception_error("cache_invalidate", e);
1527         }
1528 }
1529
1530 function getActiveArticleId() {
1531         return active_post_id;
1532 }
1533
1534 function cdmMouseIn(elem) {
1535         try {
1536                 if (elem.id && elem.id.match("RROW-")) {
1537                         var id = elem.id.replace("RROW-", "");
1538                         active_post_id = id;
1539                 }
1540         } catch (e) {
1541                 exception_error("cdmMouseIn", e);
1542         }
1543
1544 }
1545
1546 function cdmMouseOut(elem) {
1547         active_post_id = false;
1548 }
1549
1550 function headlines_scroll_handler() {
1551         try {
1552
1553                 var e = document.getElementById("headlinesInnerContainer");
1554
1555                 // don't do infinite scrolling when Limit == All
1556
1557                 var toolbar_form = document.forms["main_toolbar_form"];
1558
1559                 var limit = toolbar_form.limit[toolbar_form.limit.selectedIndex];
1560                 if (limit.value != 0) {
1561                 
1562                         debug((e.scrollTop + e.offsetHeight) + " vs " + e.scrollHeight + " dis? " +
1563                                 _infscroll_disable);
1564
1565                         if (e.scrollTop + e.offsetHeight > e.scrollHeight - 100) {
1566                                 if (!_infscroll_disable) {
1567                                         debug("more cowbell!");
1568                                         viewNextFeedPage();
1569                                 }
1570                         }
1571                 }
1572
1573         } catch (e) {
1574                 exception_error("headlines_scroll_handler", e);
1575         }
1576 }
1577
1578 function catchupRelativeToArticle(below) {
1579
1580         try {
1581
1582                 if (!xmlhttp_ready(xmlhttp_rpc)) {
1583                         printLockingError();
1584                 }
1585         
1586                 if (!getActiveArticleId()) {
1587                         alert(__("No article is selected."));
1588                         return;
1589                 }
1590
1591                 var visible_ids;
1592
1593                 if (document.getElementById("headlinesList")) {
1594                         visible_ids = getVisibleHeadlineIds();
1595                 } else {
1596                         visible_ids = cdmGetVisibleArticles();
1597                 }
1598
1599                 var ids_to_mark = new Array();
1600
1601                 if (!below) {
1602                         for (var i = 0; i < visible_ids.length; i++) {
1603                                 if (visible_ids[i] != getActiveArticleId()) {
1604                                         var e = document.getElementById("RROW-" + visible_ids[i]);
1605
1606                                         if (e && e.className.match("Unread")) {
1607                                                 ids_to_mark.push(visible_ids[i]);
1608                                         }
1609                                 } else {
1610                                         break;
1611                                 }
1612                         }
1613                 } else {
1614                         for (var i = visible_ids.length-1; i >= 0; i--) {
1615                                 if (visible_ids[i] != getActiveArticleId()) {
1616                                         var e = document.getElementById("RROW-" + visible_ids[i]);
1617
1618                                         if (e && e.className.match("Unread")) {
1619                                                 ids_to_mark.push(visible_ids[i]);
1620                                         }
1621                                 } else {
1622                                         break;
1623                                 }
1624                         }
1625                 }
1626
1627                 if (ids_to_mark.length == 0) {
1628                         alert(__("No articles found to mark"));
1629                 } else {
1630                         var msg = __("Mark %d article(s) as read?").replace("%d", ids_to_mark.length);
1631
1632                         if (confirm(msg)) {
1633
1634                                 for (var i = 0; i < ids_to_mark.length; i++) {
1635                                         var e = document.getElementById("RROW-" + ids_to_mark[i]);
1636                                         e.className = e.className.replace("Unread", "");
1637                                 }
1638
1639                                 var query = "backend.php?op=rpc&subop=catchupSelected&ids=" +
1640                                         param_escape(ids_to_mark.toString()) + "&cmode=0";
1641
1642                                 new Ajax.Request(query, {
1643                                         onComplete: function(transport) { 
1644                                                 catchup_callback2(transport); 
1645                                         } });
1646
1647                         }
1648                 }
1649
1650         } catch (e) {
1651                 exception_error("catchupRelativeToArticle", e);
1652         }
1653 }
1654
1655 function cdmExpandArticle(a_id) {
1656         try {
1657                 var id = 'CICD-' + a_id;
1658
1659                 Effect.Appear(id, {duration : 0.5, 
1660                         beforeStart: function(effect) { 
1661                                 var h_id = 'CICH-' + a_id;
1662                                 var h_elem = document.getElementById(h_id);
1663                                 if (h_elem) { h_elem.style.display = "none"; }
1664
1665                                 toggleUnread(a_id, 0);
1666                         }});
1667
1668
1669         } catch (e) {
1670                 exception_error("appearBlockElementF", e);
1671         }
1672
1673 }
1674
1675