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