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