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