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