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