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