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