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