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