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