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