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