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