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