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