]> git.wh0rd.org Git - tt-rss.git/blob - viewfeed.js
1968c37756492997c792ac5604afc005c2791b71
[tt-rss.git] / viewfeed.js
1 var active_post_id = false;
2 var last_article_view = false;
3 var active_real_feed_id = false;
4
5 var _tag_active_post_id = false;
6 var _tag_active_feed_id = false;
7 var _tag_active_cdm = false;
8
9 // FIXME: kludge, to restore scrollTop after tag editor terminates
10 var _tag_cdm_scroll = false;
11
12 // FIXME: kludges, needs proper implementation
13 var _reload_feedlist_after_view = false;
14
15 var _cdm_wd_timeout = false;
16 var _cdm_wd_vishist = new Array();
17
18 var article_cache = new Array();
19
20 function catchup_callback() {
21         if (xmlhttp_rpc.readyState == 4) {
22                 try {
23                         debug("catchup_callback");
24                         notify("");                     
25                         all_counters_callback2(xmlhttp_rpc);
26                         if (_catchup_callback_func) {
27                                 setTimeout(_catchup_callback_func, 10); 
28                         }
29                 } catch (e) {
30                         exception_error("catchup_callback", e);
31                 }
32         }
33 }
34
35 function catchup_callback2(transport, callback) {
36         try {
37                 debug("catchup_callback2 " + transport + ", " + callback);
38                 notify("");                     
39                 all_counters_callback2(transport);
40                 if (callback) {
41                         setTimeout(callback, 10);       
42                 }
43         } catch (e) {
44                 exception_error("catchup_callback2", e);
45         }
46 }
47
48 function clean_feed_selections() {
49         try {
50                 var feeds = document.getElementById("feedList").getElementsByTagName("LI");
51
52                 for (var i = 0; i < feeds.length; i++) {
53                         if (feeds[i].id && feeds[i].id.match("FEEDR-")) {
54                                 feeds[i].className = feeds[i].className.replace("Selected", "");
55                         }                       
56                 }
57         } catch (e) {
58                 exception_error("clean_feed_selections", e);
59         }
60 }
61
62 function headlines_callback2(transport, active_feed_id, is_cat, feed_cur_page) {
63         try {
64
65                 debug("headlines_callback2 [page=" + feed_cur_page + "]");
66
67                 clean_feed_selections();
68
69                 setActiveFeedId(active_feed_id);
70                 
71                 if (is_cat != undefined) {
72                         active_feed_is_cat = is_cat;
73                 }
74         
75                 if (!is_cat) {
76                         var feedr = document.getElementById("FEEDR-" + active_feed_id);
77                         if (feedr && !feedr.className.match("Selected")) {      
78                                 feedr.className = feedr.className + "Selected";
79                         } 
80                 }
81         
82                 var f = document.getElementById("headlines-frame");
83                 try {
84                         if (feed_cur_page == 0) { 
85                                 debug("resetting headlines scrollTop");
86                                 f.scrollTop = 0; 
87                         }
88                 } catch (e) { };
89         
90                 if (transport.responseXML) {
91                         var headlines = transport.responseXML.getElementsByTagName("headlines")[0];
92                         var headlines_count_obj = transport.responseXML.getElementsByTagName("headlines-count")[0];
93                         var headlines_unread_obj = transport.responseXML.getElementsByTagName("headlines-unread")[0];
94
95                         var headlines_count = headlines_count_obj.getAttribute("value");
96                         var headlines_unread = headlines_unread_obj.getAttribute("value");
97
98                         if (headlines_count == 0) _infscroll_disable = 1;
99         
100                         var counters = transport.responseXML.getElementsByTagName("counters")[0];
101                         var articles = transport.responseXML.getElementsByTagName("article");
102                         var runtime_info = transport.responseXML.getElementsByTagName("runtime-info");
103         
104                         if (feed_cur_page == 0) {
105                                 if (headlines) {
106                                         f.innerHTML = headlines.firstChild.nodeValue;
107
108                                         var cache_prefix = "";
109
110                                         if (is_cat) {
111                                                 cache_prefix = "C:";
112                                         } else {
113                                                 cache_prefix = "F:";
114                                         }
115
116                                         cache_invalidate(cache_prefix + active_feed_id);
117
118                                         cache_inject(cache_prefix + active_feed_id,
119                                                 headlines.firstChild.nodeValue, headlines_unread);
120
121                                 } else {
122                                         debug("headlines_callback: returned no data");
123                                 f.innerHTML = "<div class='whiteBox'>" + __('Could not update headlines (missing XML data)') + "</div>";
124         
125                                 }
126                         } else {
127                                 if (headlines) {
128                                         if (headlines_count > 0) {
129                                                 debug("adding some more headlines...");
130         
131                                                 var c = document.getElementById("headlinesList");
132                 
133                                                 if (!c) {
134                                                         c = document.getElementById("headlinesInnerContainer");
135                                                 }
136
137                                                 var ids = getSelectedArticleIds2();
138         
139                                                 c.innerHTML = c.innerHTML + headlines.firstChild.nodeValue;
140
141                                                 debug("restore selected ids: " + ids);
142
143                                                 for (var i = 0; i < ids.length; i++) {
144                                                         markHeadline(ids[i]);
145                                                 }
146
147                                         } else {
148                                                 debug("no new headlines received");
149                                         }
150                                 } else {
151                                         debug("headlines_callback: returned no data");
152                                         notify_error("Error while trying to load more headlines");      
153                                 }
154         
155                         }
156         
157                         if (articles) {
158                                 for (var i = 0; i < articles.length; i++) {
159                                         var a_id = articles[i].getAttribute("id");
160                                         debug("found id: " + a_id);
161                                         cache_inject(a_id, articles[i].firstChild.nodeValue);
162                                 }
163                         } else {
164                                 debug("no cached articles received");
165                         }
166         
167                         if (counters) {
168                                 debug("parsing piggybacked counters: " + counters);
169                                 parse_counters(counters, false);
170                         } else {
171                                 debug("counters container not found in reply");
172                         }
173         
174                         if (runtime_info) {
175                                 debug("parsing runtime info: " + runtime_info[0]);
176                                 parse_runtime_info(runtime_info[0]);
177                         } else {
178                                 debug("counters container not found in reply");
179                         }
180         
181                 } else {
182                         debug("headlines_callback: returned no XML object");
183                         f.innerHTML = "<div class='whiteBox'>" + __('Could not update headlines (missing XML object)') + "</div>";
184                 }
185         
186                 if (typeof correctPNG != 'undefined') {
187                         correctPNG();
188                 }
189         
190                 if (_cdm_wd_timeout) window.clearTimeout(_cdm_wd_timeout);
191         
192                 if (!document.getElementById("headlinesList") && 
193                                 getInitParam("cdm_auto_catchup") == 1) {
194                         debug("starting CDM watchdog");
195                         _cdm_wd_timeout = window.setTimeout("cdmWatchdog()", 5000);
196                         _cdm_wd_vishist = new Array();
197                 } else {
198                         debug("not in CDM mode or watchdog disabled");
199                 }
200         
201                 if (_tag_cdm_scroll) {
202                         try {
203                                 document.getElementById("headlinesInnerContainer").scrollTop = _tag_cdm_scroll;
204                                 _tag_cdm_scroll = false;
205                                 debug("resetting headlinesInner scrollTop");
206         
207                         } catch (e) { }
208                 }
209         
210                 _feed_cur_page = feed_cur_page;
211                 _infscroll_request_sent = 0;
212
213                 notify("");
214         } catch (e) {
215                 exception_error("headlines_callback2", e);
216         }
217 }
218
219 function render_article(article) {
220         try {
221                 var f = document.getElementById("content-frame");
222                 try {
223                         f.scrollTop = 0;
224                 } catch (e) { };
225
226                 f.innerHTML = article;
227
228         } catch (e) {
229                 exception_error("render_article", e);
230         }
231 }
232
233 function showArticleInHeadlines(id) {
234
235         try {
236
237                 cleanSelected("headlinesList");
238         
239                 var crow = document.getElementById("RROW-" + id);
240
241                 if (!crow) return;
242
243                 var article_is_unread = crow.className.match("Unread");
244                         
245                 crow.className = crow.className.replace("Unread", "");
246                 
247                 var upd_img_pic = document.getElementById("FUPDPIC-" + id);
248                 
249                 if (upd_img_pic) {
250                         upd_img_pic.src = "images/blank_icon.gif";
251                 }
252                 
253                 selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', false);
254                 markHeadline(id);
255
256         } catch (e) {
257                 exception_error("showArticleInHeadlines", e);
258         }
259 }
260
261 function article_callback2(transport, id, feed_id) {
262         try {
263                 debug("article_callback2 " + id);
264
265                 if (transport.responseXML) {
266
267                         active_real_feed_id = feed_id;
268                         active_post_id = id; 
269
270                         showArticleInHeadlines(id);     
271
272                         var reply = transport.responseXML.firstChild.firstChild;
273
274                         var articles = transport.responseXML.getElementsByTagName("article");
275
276                         for (var i = 0; i < articles.length; i++) {
277                                 var a_id = articles[i].getAttribute("id");
278
279                                 debug("found id: " + a_id);
280
281                                 if (a_id == active_post_id) {
282                                         debug("active article, rendering...");                                  
283                                         render_article(articles[i].firstChild.nodeValue);
284                                 }
285
286                                 cache_inject(a_id, articles[i].firstChild.nodeValue);
287                         }
288                 
289                 } else {
290                         debug("article_callback: returned no XML object");
291                         var f = document.getElementById("content-frame");
292                         f.innerHTML = "<div class='whiteBox'>" + __('Could not display article (missing XML object)') + "</div>";
293                 }
294
295                 var date = new Date();
296                 last_article_view = date.getTime() / 1000;
297
298                 if (typeof correctPNG != 'undefined') {
299                         correctPNG();
300                 }
301
302                 if (_reload_feedlist_after_view) {
303                         setTimeout('updateFeedList(false, false)', 50);                 
304                         _reload_feedlist_after_view = false;
305                 } else {
306                         var counters = transport.responseXML.getElementsByTagName("counters")[0];
307
308                         if (counters) {
309                                 debug("parsing piggybacked counters: " + counters);
310                                 parse_counters(counters, false);
311                         } else {
312                                 debug("counters container not found in reply");
313                         }
314                 }
315
316                 notify("");
317         } catch (e) {
318                 exception_error("article_callback2", e);
319         }
320 }
321
322 function view(id, feed_id, skip_history) {
323         
324         try {
325                 debug("loading article: " + id + "/" + feed_id);
326         
327                 var cached_article = cache_find(id);
328
329                 debug("cache check result: " + (cached_article != false));
330         
331                 enableHotkeys();
332         
333                 //setActiveFeedId(feed_id);
334
335                 var query = "backend.php?op=view&id=" + param_escape(id) +
336                         "&feed=" + param_escape(feed_id);
337
338                 var date = new Date();
339
340                 var neighbor_ids = getRelativePostIds(active_post_id);
341
342                 /* only request uncached articles */
343
344                 var cids_to_request = Array();
345
346                 for (var i = 0; i < neighbor_ids.length; i++) {
347                         if (!cache_check(neighbor_ids[i])) {
348                                 cids_to_request.push(neighbor_ids[i]);
349                         }
350                 }
351
352                 debug("additional ids: " + cids_to_request.toString());                 
353
354                 /* additional info for piggyback counters */
355
356                 if (tagsAreDisplayed()) {
357                         query = query + "&omode=lt";
358                 } else {
359                         query = query + "&omode=flc";
360                 }
361
362                 var date = new Date();
363                 var timestamp = Math.round(date.getTime() / 1000);
364                 query = query + "&ts=" + timestamp;
365
366                 query = query + "&cids=" + cids_to_request.toString();
367
368                 var crow = document.getElementById("RROW-" + id);
369                 var article_is_unread = crow.className.match("Unread");
370
371                 showArticleInHeadlines(id);
372
373                 if (!cached_article) {
374
375                         notify_progress("Loading, please wait...");
376
377                 } else if (cached_article && article_is_unread) {
378
379                         query = query + "&mode=prefetch";
380
381                         render_article(cached_article);
382
383                 } else if (cached_article) {
384
385                         query = query + "&mode=prefetch_old";
386                         render_article(cached_article);
387                 }
388
389                 cache_expire();
390
391                 new Ajax.Request(query, {
392                         onComplete: function(transport) { 
393                                 article_callback2(transport, id, feed_id); 
394                         } });
395
396         } catch (e) {
397                 exception_error("view", e);
398         }
399 }
400
401 function tMark(id) {
402         return toggleMark(id);
403 }
404
405 function tPub(id) {
406         return togglePub(id);
407 }
408
409 function tMark_afh_off(effect) {
410         try {
411                 var elem = effect.effects[0].element;
412
413                 debug("tMark_afh_off : " + elem.id);
414
415                 if (elem) {
416                         elem.src = elem.src.replace("mark_set", "mark_unset");
417                         elem.alt = __("Star article");
418                         Element.show(elem);
419                 }
420
421         } catch (e) {
422                 exception_error("tMark_afh_off", e);
423         }
424 }
425
426 function tPub_afh_off(effect) {
427         try {
428                 var elem = effect.effects[0].element;
429
430                 debug("tPub_afh_off : " + elem.id);
431
432                 if (elem) {
433                         elem.src = elem.src.replace("pub_set", "pub_unset");
434                         elem.alt = __("Publish article");
435                         Element.show(elem);
436                 }
437
438         } catch (e) {
439                 exception_error("tPub_afh_off", e);
440         }
441 }
442
443 function toggleMark(id, client_only, no_effects) {
444
445         try {
446
447                 var query = "backend.php?op=rpc&id=" + id + "&subop=mark";
448         
449                 query = query + "&afid=" + getActiveFeedId();
450         
451                 if (tagsAreDisplayed()) {
452                         query = query + "&omode=tl";
453                 } else {
454                         query = query + "&omode=flc";
455                 }
456         
457                 var mark_img = document.getElementById("FMPIC-" + id);
458                 var vfeedu = document.getElementById("FEEDU--1");
459                 var crow = document.getElementById("RROW-" + id);
460         
461                 if (mark_img.src.match("mark_unset")) {
462                         mark_img.src = mark_img.src.replace("mark_unset", "mark_set");
463                         mark_img.alt = __("Unstar article");
464                         query = query + "&mark=1";
465         
466 /*                      if (vfeedu && crow.className.match("Unread")) {
467                                 vfeedu.innerHTML = (+vfeedu.innerHTML) + 1;
468                         } */
469         
470                 } else {
471                         //mark_img.src = "images/mark_unset.png";
472                         mark_img.alt = __("Please wait...");
473                         query = query + "&mark=0";
474         
475 /*                      if (vfeedu && crow.className.match("Unread")) {
476                                 vfeedu.innerHTML = (+vfeedu.innerHTML) - 1;
477                         } */
478         
479                         if (document.getElementById("headlinesList") && !no_effects) {
480                                 Effect.Puff(mark_img, {duration : 0.25, afterFinish: tMark_afh_off});
481                         } else { 
482                                 mark_img.src = mark_img.src.replace("mark_set", "mark_unset");
483                                 mark_img.alt = __("Star article");
484                         }
485                 }
486         
487 /*              var vfeedctr = document.getElementById("FEEDCTR--1");
488                 var vfeedr = document.getElementById("FEEDR--1");
489         
490                 if (vfeedu && vfeedctr) {
491                         if ((+vfeedu.innerHTML) > 0) {
492                                 if (crow.className.match("Unread") && !vfeedr.className.match("Unread")) {
493                                         vfeedr.className = vfeedr.className + "Unread";
494                                         vfeedctr.className = "odd";
495                                 }
496                         } else {
497                                 vfeedctr.className = "invisible";
498                                 vfeedr.className = vfeedr.className.replace("Unread", "");
499                         }
500                 }
501         
502                 debug("toggle starred for aid " + id);
503         
504                 //new Ajax.Request(query); */
505
506                 if (!client_only) {
507                         debug(query);
508
509                         new Ajax.Request(query, {
510                                 onComplete: function(transport) { 
511                                         all_counters_callback2(transport); 
512                                 } });
513
514                 }
515
516         } catch (e) {
517                 exception_error("toggleMark", e);
518         }
519 }
520
521 function togglePub(id, client_only, no_effects) {
522
523         try {
524
525                 var query = "backend.php?op=rpc&id=" + id + "&subop=publ";
526         
527                 query = query + "&afid=" + getActiveFeedId();
528         
529                 if (tagsAreDisplayed()) {
530                         query = query + "&omode=tl";
531                 } else {
532                         query = query + "&omode=flc";
533                 }
534         
535                 var mark_img = document.getElementById("FPPIC-" + id);
536                 var vfeedu = document.getElementById("FEEDU--2");
537                 var crow = document.getElementById("RROW-" + id);
538         
539                 if (mark_img.src.match("pub_unset")) {
540                         mark_img.src = mark_img.src.replace("pub_unset", "pub_set");
541                         mark_img.alt = __("Unpublish article");
542                         query = query + "&pub=1";
543         
544 /*                      if (vfeedu && crow.className.match("Unread")) {
545                                 vfeedu.innerHTML = (+vfeedu.innerHTML) + 1;
546                         } */
547         
548                 } else {
549                         //mark_img.src = "images/pub_unset.png";
550                         mark_img.alt = __("Please wait...");
551                         query = query + "&pub=0";
552         
553 /*                      if (vfeedu && crow.className.match("Unread")) {
554                                 vfeedu.innerHTML = (+vfeedu.innerHTML) - 1;
555                         } */
556
557                         if (document.getElementById("headlinesList") && !no_effects) {
558                                 Effect.Puff(mark_img, {duration : 0.25, afterFinish: tPub_afh_off});
559                         } else { 
560                                 mark_img.src = mark_img.src.replace("pub_set", "pub_unset");
561                                 mark_img.alt = __("Publish article");
562                         }
563                 }
564         
565 /*              var vfeedctr = document.getElementById("FEEDCTR--2");
566                 var vfeedr = document.getElementById("FEEDR--2");
567         
568                 if (vfeedu && vfeedctr) {
569                         if ((+vfeedu.innerHTML) > 0) {
570                                 if (crow.className.match("Unread") && !vfeedr.className.match("Unread")) {
571                                         vfeedr.className = vfeedr.className + "Unread";
572                                         vfeedctr.className = "odd";
573                                 }
574                         } else {
575                                 vfeedctr.className = "invisible";
576                                 vfeedr.className = vfeedr.className.replace("Unread", "");
577                         }
578                 }
579         
580                 debug("toggle published for aid " + id);
581         
582                 new Ajax.Request(query); */
583
584                 if (!client_only) {
585                         new Ajax.Request(query, {
586                                 onComplete: function(transport) { 
587                                         all_counters_callback2(transport); 
588                                 } });
589                 }
590
591         } catch (e) {
592
593                 exception_error("togglePub", e);
594         }
595 }
596
597 function correctHeadlinesOffset(id) {
598         
599         try {
600
601                 var hlist = document.getElementById("headlinesList");
602                 var container = document.getElementById("headlinesInnerContainer");
603                 var row = document.getElementById("RROW-" + id);
604         
605                 var viewport = container.offsetHeight;
606         
607                 var rel_offset_top = row.offsetTop - container.scrollTop;
608                 var rel_offset_bottom = row.offsetTop + row.offsetHeight - container.scrollTop;
609         
610                 debug("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom);
611                 debug("Vport: " + viewport);
612
613                 if (rel_offset_top <= 0 || rel_offset_top > viewport) {
614                         container.scrollTop = row.offsetTop;
615                 } else if (rel_offset_bottom > viewport) {
616
617                         /* doesn't properly work with Opera in some cases because
618                                 Opera fucks up element scrolling */
619
620                         container.scrollTop = row.offsetTop + row.offsetHeight - viewport;              
621                 } 
622
623         } catch (e) {
624                 exception_error("correctHeadlinesOffset", e);
625         }
626
627 }
628
629 function moveToPost(mode) {
630
631         // check for combined mode
632         if (!document.getElementById("headlinesList"))
633                 return;
634
635         var rows = getVisibleHeadlineIds();
636
637         var prev_id = false;
638         var next_id = false;
639
640         if (!document.getElementById('RROW-' + active_post_id)) {
641                 active_post_id = false;
642         }
643
644         if (active_post_id == false) {
645                 next_id = getFirstVisibleHeadlineId();
646                 prev_id = getLastVisibleHeadlineId();
647         } else {        
648                 for (var i = 0; i < rows.length; i++) {
649                         if (rows[i] == active_post_id) {
650                                 prev_id = rows[i-1];
651                                 next_id = rows[i+1];                    
652                         }
653                 }
654         }
655
656         if (mode == "next") {
657                 if (next_id) {
658                         correctHeadlinesOffset(next_id);
659                         view(next_id, getActiveFeedId());
660                 }
661         }
662
663         if (mode == "prev") {
664                 if (prev_id) {
665                         correctHeadlinesOffset(prev_id);
666                         view(prev_id, getActiveFeedId());
667                 }
668         } 
669 }
670
671 function toggleUnread(id, cmode) {
672         try {
673         
674                 var row = document.getElementById("RROW-" + id);
675                 if (row) {
676                         var nc = row.className;
677                         nc = nc.replace("Unread", "");
678                         nc = nc.replace("Selected", "");
679
680                         if (cmode == undefined || cmode == 2) {
681                                 if (row.className.match("Unread")) {
682                                         row.className = nc;
683                                 } else {
684                                         row.className = nc + "Unread";
685                                 }
686                         } else if (cmode == 0) {
687                                 row.className = nc;
688                         } else if (cmode == 1) {
689                                 row.className = nc + "Unread";
690                         }
691
692                         if (cmode == undefined) cmode = 2;
693
694                         var query = "backend.php?op=rpc&subop=catchupSelected&ids=" +
695                                 param_escape(id) + "&cmode=" + param_escape(cmode);
696
697 //                      notify_progress("Loading, please wait...");
698
699                         new Ajax.Request(query, {
700                                 onComplete: function(transport) { 
701                                         all_counters_callback2(transport); 
702                                 } });
703
704                 }
705
706
707         } catch (e) {
708                 exception_error("toggleUnread", e);
709         }
710 }
711
712 function selectionToggleUnread(cdm_mode, set_state, callback_func, no_error) {
713         try {
714                 var rows;
715
716                 if (cdm_mode) {
717                         rows = cdmGetSelectedArticles();
718                 } else {        
719                         rows = getSelectedTableRowIds("headlinesList", "RROW", "RCHK");
720                 }
721
722                 if (rows.length == 0 && !no_error) {
723                         alert(__("No articles are selected."));
724                         return;
725                 }
726
727                 for (i = 0; i < rows.length; i++) {
728                         var row = document.getElementById("RROW-" + rows[i]);
729                         if (row) {
730                                 var nc = row.className;
731                                 nc = nc.replace("Unread", "");
732                                 nc = nc.replace("Selected", "");
733
734                                 if (set_state == undefined) {
735                                         if (row.className.match("Unread")) {
736                                                 row.className = nc + "Selected";
737                                         } else {
738                                                 row.className = nc + "UnreadSelected";
739                                         }
740                                 }
741
742                                 if (set_state == false) {
743                                         row.className = nc + "Selected";
744                                 }
745
746                                 if (set_state == true) {
747                                         row.className = nc + "UnreadSelected";
748                                 }
749                         }
750                 }
751
752                 if (rows.length > 0) {
753
754                         var cmode = "";
755
756                         if (set_state == undefined) {
757                                 cmode = "2";
758                         } else if (set_state == true) {
759                                 cmode = "1";
760                         } else if (set_state == false) {
761                                 cmode = "0";
762                         }
763
764                         var query = "backend.php?op=rpc&subop=catchupSelected&ids=" +
765                                 param_escape(rows.toString()) + "&cmode=" + cmode;
766
767                         notify_progress("Loading, please wait...");
768
769                         new Ajax.Request(query, {
770                                 onComplete: function(transport) { 
771                                         catchup_callback2(transport, callback_func); 
772                                 } });
773
774                 }
775
776         } catch (e) {
777                 exception_error("selectionToggleUnread", e);
778         }
779 }
780
781 function selectionToggleMarked(cdm_mode) {
782         try {
783         
784                 var rows;
785                 
786                 if (cdm_mode) {
787                         rows = cdmGetSelectedArticles();
788                 } else {        
789                         rows = getSelectedTableRowIds("headlinesList", "RROW", "RCHK");
790                 }       
791
792                 if (rows.length == 0) {
793                         alert(__("No articles are selected."));
794                         return;
795                 }
796
797                 for (i = 0; i < rows.length; i++) {
798                         toggleMark(rows[i], true, true);
799                 }
800
801                 if (rows.length > 0) {
802
803                         var query = "backend.php?op=rpc&subop=markSelected&ids=" +
804                                 param_escape(rows.toString()) + "&cmode=2";
805
806                         query = query + "&afid=" + getActiveFeedId();
807
808 /*                      if (tagsAreDisplayed()) {
809                                 query = query + "&omode=tl";
810                         } else {
811                                 query = query + "&omode=flc";
812                         } */
813
814                         query = query + "&omode=lc";
815
816                         new Ajax.Request(query, {
817                                 onComplete: function(transport) { 
818                                         all_counters_callback2(transport); 
819                                 } });
820
821                 }
822
823         } catch (e) {
824                 exception_error("selectionToggleMarked", e);
825         }
826 }
827
828 function selectionTogglePublished(cdm_mode) {
829         try {
830         
831                 var rows;
832                 
833                 if (cdm_mode) {
834                         rows = cdmGetSelectedArticles();
835                 } else {        
836                         rows = getSelectedTableRowIds("headlinesList", "RROW", "RCHK");
837                 }       
838
839                 if (rows.length == 0) {
840                         alert(__("No articles are selected."));
841                         return;
842                 }
843
844                 for (i = 0; i < rows.length; i++) {
845                         togglePub(rows[i], true, true);
846                 }
847
848                 if (rows.length > 0) {
849
850                         var query = "backend.php?op=rpc&subop=publishSelected&ids=" +
851                                 param_escape(rows.toString()) + "&cmode=2";
852
853                         query = query + "&afid=" + getActiveFeedId();
854
855 /*                      if (tagsAreDisplayed()) {
856                                 query = query + "&omode=tl";
857                         } else {
858                                 query = query + "&omode=flc";
859                         } */
860
861                         query = query + "&omode=lc";
862
863                         new Ajax.Request(query, {
864                                 onComplete: function(transport) { 
865                                         all_counters_callback2(transport); 
866                                 } });
867
868                 }
869
870         } catch (e) {
871                 exception_error("selectionToggleMarked", e);
872         }
873 }
874
875 function cdmGetSelectedArticles() {
876         var sel_articles = new Array();
877         var container = document.getElementById("headlinesInnerContainer");
878
879         for (i = 0; i < container.childNodes.length; i++) {
880                 var child = container.childNodes[i];
881
882                 if (child.id.match("RROW-") && child.className.match("Selected")) {
883                         var c_id = child.id.replace("RROW-", "");
884                         sel_articles.push(c_id);
885                 }
886         }
887
888         return sel_articles;
889 }
890
891 function cdmGetVisibleArticles() {
892         var sel_articles = new Array();
893         var container = document.getElementById("headlinesInnerContainer");
894
895         for (i = 0; i < container.childNodes.length; i++) {
896                 var child = container.childNodes[i];
897
898                 if (child.id.match("RROW-")) {
899                         var c_id = child.id.replace("RROW-", "");
900                         sel_articles.push(c_id);
901                 }
902         }
903
904         return sel_articles;
905 }
906
907 function cdmGetUnreadArticles() {
908         var sel_articles = new Array();
909         var container = document.getElementById("headlinesInnerContainer");
910
911         for (i = 0; i < container.childNodes.length; i++) {
912                 var child = container.childNodes[i];
913
914                 if (child.id.match("RROW-") && child.className.match("Unread")) {
915                         var c_id = child.id.replace("RROW-", "");
916                         sel_articles.push(c_id);
917                 }
918         }
919
920         return sel_articles;
921 }
922
923
924 // mode = all,none,unread
925 function cdmSelectArticles(mode) {
926         var container = document.getElementById("headlinesInnerContainer");
927
928         for (i = 0; i < container.childNodes.length; i++) {
929                 var child = container.childNodes[i];
930
931                 if (child.id.match("RROW-")) {
932                         var aid = child.id.replace("RROW-", "");
933
934                         var cb = document.getElementById("RCHK-" + aid);
935
936                         if (mode == "all") {
937                                 if (!child.className.match("Selected")) {
938                                         child.className = child.className + "Selected";
939                                         cb.checked = true;
940                                 }
941                         } else if (mode == "unread") {
942                                 if (child.className.match("Unread") && !child.className.match("Selected")) {
943                                         child.className = child.className + "Selected";
944                                         cb.checked = true;
945                                 }
946                         } else {
947                                 child.className = child.className.replace("Selected", "");
948                                 cb.checked = false;
949                         }
950                 }               
951         }
952 }
953
954 function catchupPage() {
955
956         var fn = getFeedName(getActiveFeedId(), active_feed_is_cat);
957         
958         var str = __("Mark all visible articles in %s as read?");
959
960         str = str.replace("%s", fn);
961
962         if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
963                 return;
964         }
965
966         if (document.getElementById("headlinesList")) {
967                 selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', true, 'Unread', true);
968                 selectionToggleUnread(false, false, 'viewCurrentFeed()', true);
969                 selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', false);
970         } else {
971                 cdmSelectArticles('all');
972                 selectionToggleUnread(true, false, 'viewCurrentFeed()', true)
973                 cdmSelectArticles('none');
974         }
975 }
976
977 function catchupSelection() {
978
979         try {
980
981                 var rows;
982         
983                 if (document.getElementById("headlinesList")) {
984                         rows = getSelectedTableRowIds("headlinesList", "RROW", "RCHK");
985                 } else {        
986                         rows = cdmGetSelectedArticles();
987                 }
988         
989                 if (rows.length == 0) {
990                         alert(__("No articles are selected."));
991                         return;
992                 }
993         
994         
995                 var fn = getFeedName(getActiveFeedId(), active_feed_is_cat);
996                 
997                 var str = __("Mark %d selected articles in %s as read?");
998         
999                 str = str.replace("%d", rows.length);
1000                 str = str.replace("%s", fn);
1001         
1002                 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
1003                         return;
1004                 }
1005         
1006                 if (document.getElementById("headlinesList")) {
1007                         selectionToggleUnread(false, false, 'viewCurrentFeed()', true);
1008         //              selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', false);
1009                 } else {
1010                         selectionToggleUnread(true, false, 'viewCurrentFeed()', true)
1011         //              cdmSelectArticles('none');
1012                 }
1013
1014         } catch (e) {
1015                 exception_error("catchupSelection", e);
1016         }
1017 }
1018
1019
1020 function labelFromSearch(search, search_mode, match_on, feed_id, is_cat) {
1021
1022         if (!xmlhttp_ready(xmlhttp_rpc)) {
1023                 printLockingError();
1024         }
1025
1026         var title = prompt(__("Please enter label title:"), "");
1027
1028         if (title) {
1029
1030                 var query = "backend.php?op=labelFromSearch&search=" + param_escape(search) +
1031                         "&smode=" + param_escape(search_mode) + "&match=" + param_escape(match_on) +
1032                         "&feed=" + param_escape(feed_id) + "&is_cat=" + param_escape(is_cat) + 
1033                         "&title=" + param_escape(title);
1034
1035                 debug("LFS: " + query);
1036
1037                 new Ajax.Request(query, {
1038                         onComplete: function(transport) {
1039                                         dlg_frefresh_callback(transport);
1040                                 } });
1041         }
1042 }
1043
1044 function editArticleTags(id, feed_id, cdm_enabled) {
1045         _tag_active_post_id = id;
1046         _tag_active_feed_id = feed_id;
1047         _tag_active_cdm = cdm_enabled;
1048
1049         cache_invalidate(id);
1050
1051         try {
1052                 _tag_cdm_scroll = document.getElementById("headlinesInnerContainer").scrollTop;
1053         } catch (e) { }
1054         displayDlg('editArticleTags', id);
1055 }
1056
1057
1058 function tag_saved_callback(transport) {
1059         try {
1060                 debug("in tag_saved_callback");
1061
1062                 closeInfoBox();
1063                 notify("");
1064
1065                 if (tagsAreDisplayed()) {
1066                         _reload_feedlist_after_view = true;
1067                 }
1068
1069                 if (!_tag_active_cdm) {
1070                         if (active_post_id == _tag_active_post_id) {
1071                                 debug("reloading current article");
1072                                 view(_tag_active_post_id, _tag_active_feed_id);                 
1073                         }
1074                 } else {
1075                         debug("reloading current feed");
1076                         viewCurrentFeed();
1077                 }
1078
1079         } catch (e) {
1080                 exception_error("catchup_callback", e);
1081         }
1082 }
1083
1084 function editTagsSave() {
1085
1086         notify_progress("Saving article tags...");
1087
1088         var form = document.forms["tag_edit_form"];
1089
1090         var query = Form.serialize("tag_edit_form");
1091
1092         query = "backend.php?op=rpc&subop=setArticleTags&" + query;
1093
1094         debug(query);
1095
1096         new Ajax.Request(query, {
1097                 onComplete: function(transport) {
1098                                 tag_saved_callback(transport);
1099                         } });
1100
1101 }
1102
1103 function editTagsInsert() {
1104         try {
1105
1106                 var form = document.forms["tag_edit_form"];
1107
1108                 var found_tags = form.found_tags;
1109                 var tags_str = form.tags_str;
1110
1111                 var tag = found_tags[found_tags.selectedIndex].value;
1112
1113                 if (tags_str.value.length > 0 && 
1114                                 tags_str.value.lastIndexOf(", ") != tags_str.value.length - 2) {
1115
1116                         tags_str.value = tags_str.value + ", ";
1117                 }
1118
1119                 tags_str.value = tags_str.value + tag + ", ";
1120
1121                 found_tags.selectedIndex = 0;
1122                 
1123         } catch (e) {
1124                 exception_error(e, "editTagsInsert");
1125         }
1126 }
1127
1128 function cdmWatchdog() {
1129
1130         try {
1131
1132                 var ctr = document.getElementById("headlinesInnerContainer");
1133
1134                 if (!ctr) return;
1135
1136                 var ids = new Array();
1137
1138                 var e = ctr.firstChild;
1139
1140                 while (e) {
1141                         if (e.className && e.className == "cdmArticleUnread" && e.id &&
1142                                         e.id.match("RROW-")) {
1143
1144                                 // article fits in viewport OR article is longer than viewport and
1145                                 // its bottom is visible
1146
1147                                 if (ctr.scrollTop <= e.offsetTop && e.offsetTop + e.offsetHeight <=
1148                                                 ctr.scrollTop + ctr.offsetHeight) {
1149
1150 //                                      debug(e.id + " is visible " + e.offsetTop + "." + 
1151 //                                              (e.offsetTop + e.offsetHeight) + " vs " + ctr.scrollTop + "." +
1152 //                                              (ctr.scrollTop + ctr.offsetHeight));
1153
1154                                         ids.push(e.id.replace("RROW-", ""));
1155
1156                                 } else if (e.offsetHeight > ctr.offsetHeight &&
1157                                                 e.offsetTop + e.offsetHeight >= ctr.scrollTop &&
1158                                                 e.offsetTop + e.offsetHeight <= ctr.scrollTop + ctr.offsetHeight) {
1159
1160                                         ids.push(e.id.replace("RROW-", "")); 
1161
1162                                 }
1163
1164                                 // method 2: article bottom is visible and is in upper 1/2 of the viewport
1165
1166 /*                              if (e.offsetTop + e.offsetHeight >= ctr.scrollTop &&
1167                                                 e.offsetTop + e.offsetHeight <= ctr.scrollTop + ctr.offsetHeight/2) {
1168
1169                                         ids.push(e.id.replace("RROW-", "")); 
1170
1171                                 } */
1172
1173                         }
1174
1175                         e = e.nextSibling;
1176                 }
1177
1178                 debug("cdmWatchdog, ids= " + ids.toString());
1179
1180                 if (ids.length > 0) {
1181
1182                         for (var i = 0; i < ids.length; i++) {
1183                                 var e = document.getElementById("RROW-" + ids[i]);
1184                                 if (e) {
1185                                         e.className = e.className.replace("Unread", "");
1186                                 }
1187                         }
1188
1189                         var query = "backend.php?op=rpc&subop=catchupSelected&ids=" +
1190                                 param_escape(ids.toString()) + "&cmode=0";
1191
1192                         new Ajax.Request(query, {
1193                                 onComplete: function(transport) { 
1194                                         all_counters_callback2(transport); 
1195                                 } });
1196
1197                 }
1198
1199                 _cdm_wd_timeout = window.setTimeout("cdmWatchdog()", 4000);
1200
1201         } catch (e) {
1202                 exception_error(e, "cdmWatchdog");
1203         }
1204
1205 }
1206
1207
1208 function cache_inject(id, article, param) {
1209         if (!cache_check_param(id, param)) {
1210                 debug("cache_article: miss: " + id + " [p=" + param + "]");
1211
1212                 var cache_obj = new Array();
1213
1214                 cache_obj["id"] = id;
1215                 cache_obj["data"] = article;
1216                 cache_obj["param"] = param;
1217
1218                 article_cache.push(cache_obj);
1219
1220         } else {
1221                 debug("cache_article: hit: " + id + " [p=" + param + "]");
1222         }
1223 }
1224
1225 function cache_find(id) {
1226         for (var i = 0; i < article_cache.length; i++) {
1227                 if (article_cache[i]["id"] == id) {
1228                         return article_cache[i]["data"];
1229                 }
1230         }
1231         return false;
1232 }
1233
1234 function cache_find_param(id, param) {
1235         for (var i = 0; i < article_cache.length; i++) {
1236                 if (article_cache[i]["id"] == id && article_cache[i]["param"] == param) {
1237                         return article_cache[i]["data"];
1238                 }
1239         }
1240         return false;
1241 }
1242
1243 function cache_check(id) {
1244         for (var i = 0; i < article_cache.length; i++) {
1245                 if (article_cache[i]["id"] == id) {
1246                         return true;
1247                 }
1248         }
1249         return false;
1250 }
1251
1252 function cache_check_param(id, param) {
1253         for (var i = 0; i < article_cache.length; i++) {
1254
1255 //              debug("cache_check_param " + article_cache[i]["id"] + ":" + 
1256 //                      article_cache[i]["param"] + " vs " + id + ":" + param);
1257
1258                 if (article_cache[i]["id"] == id && article_cache[i]["param"] == param) {
1259                         return true;
1260                 }
1261         }
1262         return false;
1263 }
1264
1265 function cache_expire() {
1266         while (article_cache.length > 20) {
1267                 article_cache.shift();
1268         }
1269 }
1270
1271 function cache_invalidate(id) {
1272         var i = 0
1273
1274         try {   
1275
1276                 while (i < article_cache.length) {
1277                         if (article_cache[i]["id"] == id) {
1278                                 debug("cache_invalidate: removed id " + id);
1279                                 article_cache.splice(i, 1);
1280                                 return true;
1281                         }
1282                         i++;
1283                 }
1284                 debug("cache_invalidate: id not found: " + id);
1285                 return false;
1286         } catch (e) {
1287                 exception_error("cache_invalidate", e);
1288         }
1289 }
1290
1291 function getActiveArticleId() {
1292         return active_post_id;
1293 }
1294
1295 function cdmMouseIn(elem) {
1296         try {
1297                 if (elem.id && elem.id.match("RROW-")) {
1298                         var id = elem.id.replace("RROW-", "");
1299                         active_post_id = id;
1300                 }
1301         } catch (e) {
1302                 exception_error("cdmMouseIn", e);
1303         }
1304
1305 }
1306
1307 function cdmMouseOut(elem) {
1308         active_post_id = false;
1309 }
1310
1311 function headlines_scroll_handler() {
1312         try {
1313
1314                 var e = document.getElementById("headlinesInnerContainer");
1315
1316                 // don't do infinite scrolling when Limit == All
1317
1318                 var toolbar_form = document.forms["main_toolbar_form"];
1319
1320                 var limit = toolbar_form.limit[toolbar_form.limit.selectedIndex];
1321                 if (limit.value != 0) {
1322                         if (e.scrollTop + e.offsetHeight > e.scrollHeight - 50) {
1323                                 if (!_infscroll_disable) {
1324                                         debug("more cowbell!");
1325                                         viewNextFeedPage();
1326                                 }
1327                         }
1328                 }
1329
1330         } catch (e) {
1331                 exception_error("headlines_scroll_handler", e);
1332         }
1333 }
1334
1335 function catchupRelativeToArticle(below) {
1336
1337         try {
1338
1339                 if (!xmlhttp_ready(xmlhttp_rpc)) {
1340                         printLockingError();
1341                 }
1342         
1343                 if (!getActiveArticleId()) {
1344                         alert(__("No article is selected."));
1345                         return;
1346                 }
1347
1348                 var visible_ids;
1349
1350                 if (document.getElementById("headlinesList")) {
1351                         visible_ids = getVisibleHeadlineIds();
1352                 } else {
1353                         visible_ids = cdmGetVisibleArticles();
1354                 }
1355
1356                 var ids_to_mark = new Array();
1357
1358                 if (!below) {
1359                         for (var i = 0; i < visible_ids.length; i++) {
1360                                 if (visible_ids[i] != getActiveArticleId()) {
1361                                         var e = document.getElementById("RROW-" + visible_ids[i]);
1362
1363                                         if (e && e.className.match("Unread")) {
1364                                                 ids_to_mark.push(visible_ids[i]);
1365                                         }
1366                                 } else {
1367                                         break;
1368                                 }
1369                         }
1370                 } else {
1371                         for (var i = visible_ids.length-1; i >= 0; i--) {
1372                                 if (visible_ids[i] != getActiveArticleId()) {
1373                                         var e = document.getElementById("RROW-" + visible_ids[i]);
1374
1375                                         if (e && e.className.match("Unread")) {
1376                                                 ids_to_mark.push(visible_ids[i]);
1377                                         }
1378                                 } else {
1379                                         break;
1380                                 }
1381                         }
1382                 }
1383
1384                 if (ids_to_mark.length == 0) {
1385                         alert(__("No articles found to mark"));
1386                 } else {
1387                         var msg = __("Mark %d article(s) as read?").replace("%d", ids_to_mark.length);
1388
1389                         if (confirm(msg)) {
1390
1391                                 for (var i = 0; i < ids_to_mark.length; i++) {
1392                                         var e = document.getElementById("RROW-" + ids_to_mark[i]);
1393                                         e.className = e.className.replace("Unread", "");
1394                                 }
1395
1396                                 var query = "backend.php?op=rpc&subop=catchupSelected&ids=" +
1397                                         param_escape(ids_to_mark.toString()) + "&cmode=0";
1398
1399                                 new Ajax.Request(query, {
1400                                         onComplete: function(transport) { 
1401                                                 catchup_callback2(transport); 
1402                                         } });
1403
1404                         }
1405                 }
1406
1407         } catch (e) {
1408                 exception_error("catchupRelativeToArticle", e);
1409         }
1410 }
1411
1412 function cdmExpandArticle(a_id) {
1413         try {
1414                 var id = 'CICD-' + a_id;
1415
1416                 Effect.Appear(id, {duration : 0.5, 
1417                         beforeStart: function(effect) { 
1418                                 var h_id = 'CICH-' + a_id;
1419                                 var h_elem = document.getElementById(h_id);
1420                                 if (h_elem) { h_elem.style.display = "none"; }
1421
1422                                 toggleUnread(a_id, 0);
1423                         }});
1424
1425
1426         } catch (e) {
1427                 exception_error("appearBlockElementF", e);
1428         }
1429
1430 }
1431
1432