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