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