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