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