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