]> git.wh0rd.org - tt-rss.git/blob - viewfeed.js
add experimental headline caching
[tt-rss.git] / viewfeed.js
1 var active_post_id = false;
2 var last_article_view = false;
3 var active_real_feed_id = false;
4
5 var _tag_active_post_id = false;
6 var _tag_active_feed_id = false;
7 var _tag_active_cdm = false;
8
9 // FIXME: kludge, to restore scrollTop after tag editor terminates
10 var _tag_cdm_scroll = false;
11
12 // FIXME: kludges, needs proper implementation
13 var _reload_feedlist_after_view = false;
14
15 var _cdm_wd_timeout = false;
16 var _cdm_wd_vishist = new Array();
17
18 var article_cache = new Array();
19
20 function catchup_callback() {
21 if (xmlhttp_rpc.readyState == 4) {
22 try {
23 debug("catchup_callback");
24 notify("");
25 all_counters_callback2(xmlhttp_rpc);
26 if (_catchup_callback_func) {
27 setTimeout(_catchup_callback_func, 10);
28 }
29 } catch (e) {
30 exception_error("catchup_callback", e);
31 }
32 }
33 }
34
35 function catchup_callback2(transport, callback) {
36 try {
37 debug("catchup_callback2 " + transport + ", " + callback);
38 notify("");
39 all_counters_callback2(transport);
40 if (callback) {
41 setTimeout(callback, 10);
42 }
43 } catch (e) {
44 exception_error("catchup_callback2", e);
45 }
46 }
47
48 function clean_feed_selections() {
49 try {
50 var feeds = document.getElementById("feedList").getElementsByTagName("LI");
51
52 for (var i = 0; i < feeds.length; i++) {
53 if (feeds[i].id && feeds[i].id.match("FEEDR-")) {
54 feeds[i].className = feeds[i].className.replace("Selected", "");
55 }
56 }
57 } catch (e) {
58 exception_error("clean_feed_selections", e);
59 }
60 }
61
62 function headlines_callback2(transport, active_feed_id, is_cat, feed_cur_page) {
63 try {
64
65 debug("headlines_callback2 [page=" + feed_cur_page + "]");
66
67 clean_feed_selections();
68
69 setActiveFeedId(active_feed_id);
70
71 if (is_cat != undefined) {
72 active_feed_is_cat = is_cat;
73 }
74
75 if (!is_cat) {
76 var feedr = document.getElementById("FEEDR-" + active_feed_id);
77 if (feedr && !feedr.className.match("Selected")) {
78 feedr.className = feedr.className + "Selected";
79 }
80 }
81
82 var f = document.getElementById("headlines-frame");
83 try {
84 if (feed_cur_page == 0) {
85 debug("resetting headlines scrollTop");
86 f.scrollTop = 0;
87 }
88 } catch (e) { };
89
90 if (transport.responseXML) {
91 var headlines = transport.responseXML.getElementsByTagName("headlines")[0];
92 var headlines_count_obj = transport.responseXML.getElementsByTagName("headlines-count")[0];
93 var headlines_unread_obj = transport.responseXML.getElementsByTagName("headlines-unread")[0];
94
95 var headlines_count = headlines_count_obj.getAttribute("value");
96 var headlines_unread = headlines_unread_obj.getAttribute("value");
97
98 if (headlines_count == 0) _infscroll_disable = 1;
99
100 var counters = transport.responseXML.getElementsByTagName("counters")[0];
101 var articles = transport.responseXML.getElementsByTagName("article");
102 var runtime_info = transport.responseXML.getElementsByTagName("runtime-info");
103
104 if (feed_cur_page == 0) {
105 if (headlines) {
106 f.innerHTML = headlines.firstChild.nodeValue;
107
108 cache_inject("F:" + active_feed_id,
109 headlines.firstChild.nodeValue, headlines_unread);
110
111 } else {
112 debug("headlines_callback: returned no data");
113 f.innerHTML = "<div class='whiteBox'>" + __('Could not update headlines (missing XML data)') + "</div>";
114
115 }
116 } else {
117 if (headlines) {
118 if (headlines_count > 0) {
119 debug("adding some more headlines...");
120
121 var c = document.getElementById("headlinesList");
122
123 if (!c) {
124 c = document.getElementById("headlinesInnerContainer");
125 }
126
127 var ids = getSelectedArticleIds2();
128
129 c.innerHTML = c.innerHTML + headlines.firstChild.nodeValue;
130
131 debug("restore selected ids: " + ids);
132
133 for (var i = 0; i < ids.length; i++) {
134 markHeadline(ids[i]);
135 }
136
137 } else {
138 debug("no new headlines received");
139 }
140 } else {
141 debug("headlines_callback: returned no data");
142 notify_error("Error while trying to load more headlines");
143 }
144
145 }
146
147 if (articles) {
148 for (var i = 0; i < articles.length; i++) {
149 var a_id = articles[i].getAttribute("id");
150 debug("found id: " + a_id);
151 cache_inject(a_id, articles[i].firstChild.nodeValue);
152 }
153 } else {
154 debug("no cached articles received");
155 }
156
157 if (counters) {
158 debug("parsing piggybacked counters: " + counters);
159 parse_counters(counters, false);
160 } else {
161 debug("counters container not found in reply");
162 }
163
164 if (runtime_info) {
165 debug("parsing runtime info: " + runtime_info[0]);
166 parse_runtime_info(runtime_info[0]);
167 } else {
168 debug("counters container not found in reply");
169 }
170
171 } else {
172 debug("headlines_callback: returned no XML object");
173 f.innerHTML = "<div class='whiteBox'>" + __('Could not update headlines (missing XML object)') + "</div>";
174 }
175
176 if (typeof correctPNG != 'undefined') {
177 correctPNG();
178 }
179
180 if (_cdm_wd_timeout) window.clearTimeout(_cdm_wd_timeout);
181
182 if (!document.getElementById("headlinesList") &&
183 getInitParam("cdm_auto_catchup") == 1) {
184 debug("starting CDM watchdog");
185 _cdm_wd_timeout = window.setTimeout("cdmWatchdog()", 5000);
186 _cdm_wd_vishist = new Array();
187 } else {
188 debug("not in CDM mode or watchdog disabled");
189 }
190
191 if (_tag_cdm_scroll) {
192 try {
193 document.getElementById("headlinesInnerContainer").scrollTop = _tag_cdm_scroll;
194 _tag_cdm_scroll = false;
195 debug("resetting headlinesInner scrollTop");
196
197 } catch (e) { }
198 }
199
200 _feed_cur_page = feed_cur_page;
201 _infscroll_request_sent = 0;
202
203 notify("");
204 } catch (e) {
205 exception_error("headlines_callback2", e);
206 }
207 }
208
209 function render_article(article) {
210 try {
211 var f = document.getElementById("content-frame");
212 try {
213 f.scrollTop = 0;
214 } catch (e) { };
215
216 f.innerHTML = article;
217
218 } catch (e) {
219 exception_error("render_article", e);
220 }
221 }
222
223 function showArticleInHeadlines(id) {
224
225 try {
226
227 cleanSelected("headlinesList");
228
229 var crow = document.getElementById("RROW-" + id);
230
231 if (!crow) return;
232
233 var article_is_unread = crow.className.match("Unread");
234
235 crow.className = crow.className.replace("Unread", "");
236
237 var upd_img_pic = document.getElementById("FUPDPIC-" + id);
238
239 if (upd_img_pic) {
240 upd_img_pic.src = "images/blank_icon.gif";
241 }
242
243 selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', false);
244 markHeadline(id);
245
246 } catch (e) {
247 exception_error("showArticleInHeadlines", e);
248 }
249 }
250
251 function article_callback2(transport, id, feed_id) {
252 try {
253 debug("article_callback2 " + id);
254
255 if (transport.responseXML) {
256
257 active_real_feed_id = feed_id;
258 active_post_id = id;
259
260 showArticleInHeadlines(id);
261
262 var reply = transport.responseXML.firstChild.firstChild;
263
264 var articles = transport.responseXML.getElementsByTagName("article");
265
266 for (var i = 0; i < articles.length; i++) {
267 var a_id = articles[i].getAttribute("id");
268
269 debug("found id: " + a_id);
270
271 if (a_id == active_post_id) {
272 debug("active article, rendering...");
273 render_article(articles[i].firstChild.nodeValue);
274 }
275
276 cache_inject(a_id, articles[i].firstChild.nodeValue);
277 }
278
279 } else {
280 debug("article_callback: returned no XML object");
281 var f = document.getElementById("content-frame");
282 f.innerHTML = "<div class='whiteBox'>" + __('Could not display article (missing XML object)') + "</div>";
283 }
284
285 var date = new Date();
286 last_article_view = date.getTime() / 1000;
287
288 if (typeof correctPNG != 'undefined') {
289 correctPNG();
290 }
291
292 if (_reload_feedlist_after_view) {
293 setTimeout('updateFeedList(false, false)', 50);
294 _reload_feedlist_after_view = false;
295 } else {
296 var counters = transport.responseXML.getElementsByTagName("counters")[0];
297
298 if (counters) {
299 debug("parsing piggybacked counters: " + counters);
300 parse_counters(counters, false);
301 } else {
302 debug("counters container not found in reply");
303 }
304 }
305
306 notify("");
307 } catch (e) {
308 exception_error("article_callback2", e);
309 }
310 }
311
312 function view(id, feed_id, skip_history) {
313
314 try {
315 debug("loading article: " + id + "/" + feed_id);
316
317 var cached_article = cache_find(id);
318
319 debug("cache check result: " + (cached_article != false));
320
321 enableHotkeys();
322
323 //setActiveFeedId(feed_id);
324
325 var query = "backend.php?op=view&id=" + param_escape(id) +
326 "&feed=" + param_escape(feed_id);
327
328 var date = new Date();
329
330 var neighbor_ids = getRelativePostIds(active_post_id);
331
332 /* only request uncached articles */
333
334 var cids_to_request = Array();
335
336 for (var i = 0; i < neighbor_ids.length; i++) {
337 if (!cache_check(neighbor_ids[i])) {
338 cids_to_request.push(neighbor_ids[i]);
339 }
340 }
341
342 debug("additional ids: " + cids_to_request.toString());
343
344 /* additional info for piggyback counters */
345
346 if (tagsAreDisplayed()) {
347 query = query + "&omode=lt";
348 } else {
349 query = query + "&omode=flc";
350 }
351
352 var date = new Date();
353 var timestamp = Math.round(date.getTime() / 1000);
354 query = query + "&ts=" + timestamp;
355
356 query = query + "&cids=" + cids_to_request.toString();
357
358 var crow = document.getElementById("RROW-" + id);
359 var article_is_unread = crow.className.match("Unread");
360
361 showArticleInHeadlines(id);
362
363 if (!cached_article) {
364
365 notify_progress("Loading, please wait...");
366
367 } else if (cached_article && article_is_unread) {
368
369 query = query + "&mode=prefetch";
370
371 render_article(cached_article);
372
373 } else if (cached_article) {
374
375 query = query + "&mode=prefetch_old";
376 render_article(cached_article);
377 }
378
379 cache_expire();
380
381 new Ajax.Request(query, {
382 onComplete: function(transport) {
383 article_callback2(transport, id, feed_id);
384 } });
385
386 } catch (e) {
387 exception_error("view", e);
388 }
389 }
390
391 function tMark(id) {
392 return toggleMark(id);
393 }
394
395 function tPub(id) {
396 return togglePub(id);
397 }
398
399 function tMark_afh_off(effect) {
400 try {
401 var elem = effect.effects[0].element;
402
403 debug("tMark_afh_off : " + elem.id);
404
405 if (elem) {
406 elem.src = elem.src.replace("mark_set", "mark_unset");
407 elem.alt = __("Star article");
408 Element.show(elem);
409 }
410
411 } catch (e) {
412 exception_error("tMark_afh_off", e);
413 }
414 }
415
416 function tPub_afh_off(effect) {
417 try {
418 var elem = effect.effects[0].element;
419
420 debug("tPub_afh_off : " + elem.id);
421
422 if (elem) {
423 elem.src = elem.src.replace("pub_set", "pub_unset");
424 elem.alt = __("Publish article");
425 Element.show(elem);
426 }
427
428 } catch (e) {
429 exception_error("tPub_afh_off", e);
430 }
431 }
432
433 function toggleMark(id, client_only, no_effects) {
434
435 try {
436
437 var query = "backend.php?op=rpc&id=" + id + "&subop=mark";
438
439 query = query + "&afid=" + getActiveFeedId();
440
441 if (tagsAreDisplayed()) {
442 query = query + "&omode=tl";
443 } else {
444 query = query + "&omode=flc";
445 }
446
447 var mark_img = document.getElementById("FMPIC-" + id);
448 var vfeedu = document.getElementById("FEEDU--1");
449 var crow = document.getElementById("RROW-" + id);
450
451 if (mark_img.src.match("mark_unset")) {
452 mark_img.src = mark_img.src.replace("mark_unset", "mark_set");
453 mark_img.alt = __("Unstar article");
454 query = query + "&mark=1";
455
456 /* if (vfeedu && crow.className.match("Unread")) {
457 vfeedu.innerHTML = (+vfeedu.innerHTML) + 1;
458 } */
459
460 } else {
461 //mark_img.src = "images/mark_unset.png";
462 mark_img.alt = __("Please wait...");
463 query = query + "&mark=0";
464
465 /* if (vfeedu && crow.className.match("Unread")) {
466 vfeedu.innerHTML = (+vfeedu.innerHTML) - 1;
467 } */
468
469 if (document.getElementById("headlinesList") && !no_effects) {
470 Effect.Puff(mark_img, {duration : 0.25, afterFinish: tMark_afh_off});
471 } else {
472 mark_img.src = mark_img.src.replace("mark_set", "mark_unset");
473 mark_img.alt = __("Star article");
474 }
475 }
476
477 /* var vfeedctr = document.getElementById("FEEDCTR--1");
478 var vfeedr = document.getElementById("FEEDR--1");
479
480 if (vfeedu && vfeedctr) {
481 if ((+vfeedu.innerHTML) > 0) {
482 if (crow.className.match("Unread") && !vfeedr.className.match("Unread")) {
483 vfeedr.className = vfeedr.className + "Unread";
484 vfeedctr.className = "odd";
485 }
486 } else {
487 vfeedctr.className = "invisible";
488 vfeedr.className = vfeedr.className.replace("Unread", "");
489 }
490 }
491
492 debug("toggle starred for aid " + id);
493
494 //new Ajax.Request(query); */
495
496 if (!client_only) {
497 debug(query);
498
499 new Ajax.Request(query, {
500 onComplete: function(transport) {
501 all_counters_callback2(transport);
502 } });
503
504 }
505
506 } catch (e) {
507 exception_error("toggleMark", e);
508 }
509 }
510
511 function togglePub(id, client_only, no_effects) {
512
513 try {
514
515 var query = "backend.php?op=rpc&id=" + id + "&subop=publ";
516
517 query = query + "&afid=" + getActiveFeedId();
518
519 if (tagsAreDisplayed()) {
520 query = query + "&omode=tl";
521 } else {
522 query = query + "&omode=flc";
523 }
524
525 var mark_img = document.getElementById("FPPIC-" + id);
526 var vfeedu = document.getElementById("FEEDU--2");
527 var crow = document.getElementById("RROW-" + id);
528
529 if (mark_img.src.match("pub_unset")) {
530 mark_img.src = mark_img.src.replace("pub_unset", "pub_set");
531 mark_img.alt = __("Unpublish article");
532 query = query + "&pub=1";
533
534 /* if (vfeedu && crow.className.match("Unread")) {
535 vfeedu.innerHTML = (+vfeedu.innerHTML) + 1;
536 } */
537
538 } else {
539 //mark_img.src = "images/pub_unset.png";
540 mark_img.alt = __("Please wait...");
541 query = query + "&pub=0";
542
543 /* if (vfeedu && crow.className.match("Unread")) {
544 vfeedu.innerHTML = (+vfeedu.innerHTML) - 1;
545 } */
546
547 if (document.getElementById("headlinesList") && !no_effects) {
548 Effect.Puff(mark_img, {duration : 0.25, afterFinish: tPub_afh_off});
549 } else {
550 mark_img.src = mark_img.src.replace("pub_set", "pub_unset");
551 mark_img.alt = __("Publish article");
552 }
553 }
554
555 /* var vfeedctr = document.getElementById("FEEDCTR--2");
556 var vfeedr = document.getElementById("FEEDR--2");
557
558 if (vfeedu && vfeedctr) {
559 if ((+vfeedu.innerHTML) > 0) {
560 if (crow.className.match("Unread") && !vfeedr.className.match("Unread")) {
561 vfeedr.className = vfeedr.className + "Unread";
562 vfeedctr.className = "odd";
563 }
564 } else {
565 vfeedctr.className = "invisible";
566 vfeedr.className = vfeedr.className.replace("Unread", "");
567 }
568 }
569
570 debug("toggle published for aid " + id);
571
572 new Ajax.Request(query); */
573
574 if (!client_only) {
575 new Ajax.Request(query, {
576 onComplete: function(transport) {
577 all_counters_callback2(transport);
578 } });
579 }
580
581 } catch (e) {
582
583 exception_error("togglePub", e);
584 }
585 }
586
587 function correctHeadlinesOffset(id) {
588
589 try {
590
591 var hlist = document.getElementById("headlinesList");
592 var container = document.getElementById("headlinesInnerContainer");
593 var row = document.getElementById("RROW-" + id);
594
595 var viewport = container.offsetHeight;
596
597 var rel_offset_top = row.offsetTop - container.scrollTop;
598 var rel_offset_bottom = row.offsetTop + row.offsetHeight - container.scrollTop;
599
600 debug("Rtop: " + rel_offset_top + " Rbtm: " + rel_offset_bottom);
601 debug("Vport: " + viewport);
602
603 if (rel_offset_top <= 0 || rel_offset_top > viewport) {
604 container.scrollTop = row.offsetTop;
605 } else if (rel_offset_bottom > viewport) {
606
607 /* doesn't properly work with Opera in some cases because
608 Opera fucks up element scrolling */
609
610 container.scrollTop = row.offsetTop + row.offsetHeight - viewport;
611 }
612
613 } catch (e) {
614 exception_error("correctHeadlinesOffset", e);
615 }
616
617 }
618
619 function moveToPost(mode) {
620
621 // check for combined mode
622 if (!document.getElementById("headlinesList"))
623 return;
624
625 var rows = getVisibleHeadlineIds();
626
627 var prev_id = false;
628 var next_id = false;
629
630 if (!document.getElementById('RROW-' + active_post_id)) {
631 active_post_id = false;
632 }
633
634 if (active_post_id == false) {
635 next_id = getFirstVisibleHeadlineId();
636 prev_id = getLastVisibleHeadlineId();
637 } else {
638 for (var i = 0; i < rows.length; i++) {
639 if (rows[i] == active_post_id) {
640 prev_id = rows[i-1];
641 next_id = rows[i+1];
642 }
643 }
644 }
645
646 if (mode == "next") {
647 if (next_id) {
648 correctHeadlinesOffset(next_id);
649 view(next_id, getActiveFeedId());
650 }
651 }
652
653 if (mode == "prev") {
654 if (prev_id) {
655 correctHeadlinesOffset(prev_id);
656 view(prev_id, getActiveFeedId());
657 }
658 }
659 }
660
661 function toggleUnread(id, cmode) {
662 try {
663
664 var row = document.getElementById("RROW-" + id);
665 if (row) {
666 var nc = row.className;
667 nc = nc.replace("Unread", "");
668 nc = nc.replace("Selected", "");
669
670 if (cmode == undefined || cmode == 2) {
671 if (row.className.match("Unread")) {
672 row.className = nc;
673 } else {
674 row.className = nc + "Unread";
675 }
676 } else if (cmode == 0) {
677 row.className = nc;
678 } else if (cmode == 1) {
679 row.className = nc + "Unread";
680 }
681
682 if (cmode == undefined) cmode = 2;
683
684 var query = "backend.php?op=rpc&subop=catchupSelected&ids=" +
685 param_escape(id) + "&cmode=" + param_escape(cmode);
686
687 // notify_progress("Loading, please wait...");
688
689 new Ajax.Request(query, {
690 onComplete: function(transport) {
691 all_counters_callback2(transport);
692 } });
693
694 }
695
696
697 } catch (e) {
698 exception_error("toggleUnread", e);
699 }
700 }
701
702 function selectionToggleUnread(cdm_mode, set_state, callback_func, no_error) {
703 try {
704 var rows;
705
706 if (cdm_mode) {
707 rows = cdmGetSelectedArticles();
708 } else {
709 rows = getSelectedTableRowIds("headlinesList", "RROW", "RCHK");
710 }
711
712 if (rows.length == 0 && !no_error) {
713 alert(__("No articles are selected."));
714 return;
715 }
716
717 for (i = 0; i < rows.length; i++) {
718 var row = document.getElementById("RROW-" + rows[i]);
719 if (row) {
720 var nc = row.className;
721 nc = nc.replace("Unread", "");
722 nc = nc.replace("Selected", "");
723
724 if (set_state == undefined) {
725 if (row.className.match("Unread")) {
726 row.className = nc + "Selected";
727 } else {
728 row.className = nc + "UnreadSelected";
729 }
730 }
731
732 if (set_state == false) {
733 row.className = nc + "Selected";
734 }
735
736 if (set_state == true) {
737 row.className = nc + "UnreadSelected";
738 }
739 }
740 }
741
742 if (rows.length > 0) {
743
744 var cmode = "";
745
746 if (set_state == undefined) {
747 cmode = "2";
748 } else if (set_state == true) {
749 cmode = "1";
750 } else if (set_state == false) {
751 cmode = "0";
752 }
753
754 var query = "backend.php?op=rpc&subop=catchupSelected&ids=" +
755 param_escape(rows.toString()) + "&cmode=" + cmode;
756
757 notify_progress("Loading, please wait...");
758
759 new Ajax.Request(query, {
760 onComplete: function(transport) {
761 catchup_callback2(transport, callback_func);
762 } });
763
764 }
765
766 } catch (e) {
767 exception_error("selectionToggleUnread", e);
768 }
769 }
770
771 function selectionToggleMarked(cdm_mode) {
772 try {
773
774 var rows;
775
776 if (cdm_mode) {
777 rows = cdmGetSelectedArticles();
778 } else {
779 rows = getSelectedTableRowIds("headlinesList", "RROW", "RCHK");
780 }
781
782 if (rows.length == 0) {
783 alert(__("No articles are selected."));
784 return;
785 }
786
787 for (i = 0; i < rows.length; i++) {
788 toggleMark(rows[i], true, true);
789 }
790
791 if (rows.length > 0) {
792
793 var query = "backend.php?op=rpc&subop=markSelected&ids=" +
794 param_escape(rows.toString()) + "&cmode=2";
795
796 query = query + "&afid=" + getActiveFeedId();
797
798 /* if (tagsAreDisplayed()) {
799 query = query + "&omode=tl";
800 } else {
801 query = query + "&omode=flc";
802 } */
803
804 query = query + "&omode=lc";
805
806 new Ajax.Request(query, {
807 onComplete: function(transport) {
808 all_counters_callback2(transport);
809 } });
810
811 }
812
813 } catch (e) {
814 exception_error("selectionToggleMarked", e);
815 }
816 }
817
818 function selectionTogglePublished(cdm_mode) {
819 try {
820
821 var rows;
822
823 if (cdm_mode) {
824 rows = cdmGetSelectedArticles();
825 } else {
826 rows = getSelectedTableRowIds("headlinesList", "RROW", "RCHK");
827 }
828
829 if (rows.length == 0) {
830 alert(__("No articles are selected."));
831 return;
832 }
833
834 for (i = 0; i < rows.length; i++) {
835 togglePub(rows[i], true, true);
836 }
837
838 if (rows.length > 0) {
839
840 var query = "backend.php?op=rpc&subop=publishSelected&ids=" +
841 param_escape(rows.toString()) + "&cmode=2";
842
843 query = query + "&afid=" + getActiveFeedId();
844
845 /* if (tagsAreDisplayed()) {
846 query = query + "&omode=tl";
847 } else {
848 query = query + "&omode=flc";
849 } */
850
851 query = query + "&omode=lc";
852
853 new Ajax.Request(query, {
854 onComplete: function(transport) {
855 all_counters_callback2(transport);
856 } });
857
858 }
859
860 } catch (e) {
861 exception_error("selectionToggleMarked", e);
862 }
863 }
864
865 function cdmGetSelectedArticles() {
866 var sel_articles = new Array();
867 var container = document.getElementById("headlinesInnerContainer");
868
869 for (i = 0; i < container.childNodes.length; i++) {
870 var child = container.childNodes[i];
871
872 if (child.id.match("RROW-") && child.className.match("Selected")) {
873 var c_id = child.id.replace("RROW-", "");
874 sel_articles.push(c_id);
875 }
876 }
877
878 return sel_articles;
879 }
880
881 function cdmGetVisibleArticles() {
882 var sel_articles = new Array();
883 var container = document.getElementById("headlinesInnerContainer");
884
885 for (i = 0; i < container.childNodes.length; i++) {
886 var child = container.childNodes[i];
887
888 if (child.id.match("RROW-")) {
889 var c_id = child.id.replace("RROW-", "");
890 sel_articles.push(c_id);
891 }
892 }
893
894 return sel_articles;
895 }
896
897 function cdmGetUnreadArticles() {
898 var sel_articles = new Array();
899 var container = document.getElementById("headlinesInnerContainer");
900
901 for (i = 0; i < container.childNodes.length; i++) {
902 var child = container.childNodes[i];
903
904 if (child.id.match("RROW-") && child.className.match("Unread")) {
905 var c_id = child.id.replace("RROW-", "");
906 sel_articles.push(c_id);
907 }
908 }
909
910 return sel_articles;
911 }
912
913
914 // mode = all,none,unread
915 function cdmSelectArticles(mode) {
916 var container = document.getElementById("headlinesInnerContainer");
917
918 for (i = 0; i < container.childNodes.length; i++) {
919 var child = container.childNodes[i];
920
921 if (child.id.match("RROW-")) {
922 var aid = child.id.replace("RROW-", "");
923
924 var cb = document.getElementById("RCHK-" + aid);
925
926 if (mode == "all") {
927 if (!child.className.match("Selected")) {
928 child.className = child.className + "Selected";
929 cb.checked = true;
930 }
931 } else if (mode == "unread") {
932 if (child.className.match("Unread") && !child.className.match("Selected")) {
933 child.className = child.className + "Selected";
934 cb.checked = true;
935 }
936 } else {
937 child.className = child.className.replace("Selected", "");
938 cb.checked = false;
939 }
940 }
941 }
942 }
943
944 function catchupPage() {
945
946 var fn = getFeedName(getActiveFeedId(), active_feed_is_cat);
947
948 var str = __("Mark all visible articles in %s as read?");
949
950 str = str.replace("%s", fn);
951
952 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
953 return;
954 }
955
956 if (document.getElementById("headlinesList")) {
957 selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', true, 'Unread', true);
958 selectionToggleUnread(false, false, 'viewCurrentFeed()', true);
959 selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', false);
960 } else {
961 cdmSelectArticles('all');
962 selectionToggleUnread(true, false, 'viewCurrentFeed()', true)
963 cdmSelectArticles('none');
964 }
965 }
966
967 function catchupSelection() {
968
969 try {
970
971 var rows;
972
973 if (document.getElementById("headlinesList")) {
974 rows = getSelectedTableRowIds("headlinesList", "RROW", "RCHK");
975 } else {
976 rows = cdmGetSelectedArticles();
977 }
978
979 if (rows.length == 0) {
980 alert(__("No articles are selected."));
981 return;
982 }
983
984
985 var fn = getFeedName(getActiveFeedId(), active_feed_is_cat);
986
987 var str = __("Mark %d selected articles in %s as read?");
988
989 str = str.replace("%d", rows.length);
990 str = str.replace("%s", fn);
991
992 if (getInitParam("confirm_feed_catchup") == 1 && !confirm(str)) {
993 return;
994 }
995
996 if (document.getElementById("headlinesList")) {
997 selectionToggleUnread(false, false, 'viewCurrentFeed()', true);
998 // selectTableRowsByIdPrefix('headlinesList', 'RROW-', 'RCHK-', false);
999 } else {
1000 selectionToggleUnread(true, false, 'viewCurrentFeed()', true)
1001 // cdmSelectArticles('none');
1002 }
1003
1004 } catch (e) {
1005 exception_error("catchupSelection", e);
1006 }
1007 }
1008
1009
1010 function labelFromSearch(search, search_mode, match_on, feed_id, is_cat) {
1011
1012 if (!xmlhttp_ready(xmlhttp_rpc)) {
1013 printLockingError();
1014 }
1015
1016 var title = prompt(__("Please enter label title:"), "");
1017
1018 if (title) {
1019
1020 var query = "backend.php?op=labelFromSearch&search=" + param_escape(search) +
1021 "&smode=" + param_escape(search_mode) + "&match=" + param_escape(match_on) +
1022 "&feed=" + param_escape(feed_id) + "&is_cat=" + param_escape(is_cat) +
1023 "&title=" + param_escape(title);
1024
1025 debug("LFS: " + query);
1026
1027 new Ajax.Request(query, {
1028 onComplete: function(transport) {
1029 dlg_frefresh_callback(transport);
1030 } });
1031 }
1032 }
1033
1034 function editArticleTags(id, feed_id, cdm_enabled) {
1035 _tag_active_post_id = id;
1036 _tag_active_feed_id = feed_id;
1037 _tag_active_cdm = cdm_enabled;
1038
1039 cache_invalidate(id);
1040
1041 try {
1042 _tag_cdm_scroll = document.getElementById("headlinesInnerContainer").scrollTop;
1043 } catch (e) { }
1044 displayDlg('editArticleTags', id);
1045 }
1046
1047
1048 function tag_saved_callback(transport) {
1049 try {
1050 debug("in tag_saved_callback");
1051
1052 closeInfoBox();
1053 notify("");
1054
1055 if (tagsAreDisplayed()) {
1056 _reload_feedlist_after_view = true;
1057 }
1058
1059 if (!_tag_active_cdm) {
1060 if (active_post_id == _tag_active_post_id) {
1061 debug("reloading current article");
1062 view(_tag_active_post_id, _tag_active_feed_id);
1063 }
1064 } else {
1065 debug("reloading current feed");
1066 viewCurrentFeed();
1067 }
1068
1069 } catch (e) {
1070 exception_error("catchup_callback", e);
1071 }
1072 }
1073
1074 function editTagsSave() {
1075
1076 notify_progress("Saving article tags...");
1077
1078 var form = document.forms["tag_edit_form"];
1079
1080 var query = Form.serialize("tag_edit_form");
1081
1082 query = "backend.php?op=rpc&subop=setArticleTags&" + query;
1083
1084 debug(query);
1085
1086 new Ajax.Request(query, {
1087 onComplete: function(transport) {
1088 tag_saved_callback(transport);
1089 } });
1090
1091 }
1092
1093 function editTagsInsert() {
1094 try {
1095
1096 var form = document.forms["tag_edit_form"];
1097
1098 var found_tags = form.found_tags;
1099 var tags_str = form.tags_str;
1100
1101 var tag = found_tags[found_tags.selectedIndex].value;
1102
1103 if (tags_str.value.length > 0 &&
1104 tags_str.value.lastIndexOf(", ") != tags_str.value.length - 2) {
1105
1106 tags_str.value = tags_str.value + ", ";
1107 }
1108
1109 tags_str.value = tags_str.value + tag + ", ";
1110
1111 found_tags.selectedIndex = 0;
1112
1113 } catch (e) {
1114 exception_error(e, "editTagsInsert");
1115 }
1116 }
1117
1118 function cdmWatchdog() {
1119
1120 try {
1121
1122 var ctr = document.getElementById("headlinesInnerContainer");
1123
1124 if (!ctr) return;
1125
1126 var ids = new Array();
1127
1128 var e = ctr.firstChild;
1129
1130 while (e) {
1131 if (e.className && e.className == "cdmArticleUnread" && e.id &&
1132 e.id.match("RROW-")) {
1133
1134 // article fits in viewport OR article is longer than viewport and
1135 // its bottom is visible
1136
1137 if (ctr.scrollTop <= e.offsetTop && e.offsetTop + e.offsetHeight <=
1138 ctr.scrollTop + ctr.offsetHeight) {
1139
1140 // debug(e.id + " is visible " + e.offsetTop + "." +
1141 // (e.offsetTop + e.offsetHeight) + " vs " + ctr.scrollTop + "." +
1142 // (ctr.scrollTop + ctr.offsetHeight));
1143
1144 ids.push(e.id.replace("RROW-", ""));
1145
1146 } else if (e.offsetHeight > ctr.offsetHeight &&
1147 e.offsetTop + e.offsetHeight >= ctr.scrollTop &&
1148 e.offsetTop + e.offsetHeight <= ctr.scrollTop + ctr.offsetHeight) {
1149
1150 ids.push(e.id.replace("RROW-", ""));
1151
1152 }
1153
1154 // method 2: article bottom is visible and is in upper 1/2 of the viewport
1155
1156 /* if (e.offsetTop + e.offsetHeight >= ctr.scrollTop &&
1157 e.offsetTop + e.offsetHeight <= ctr.scrollTop + ctr.offsetHeight/2) {
1158
1159 ids.push(e.id.replace("RROW-", ""));
1160
1161 } */
1162
1163 }
1164
1165 e = e.nextSibling;
1166 }
1167
1168 debug("cdmWatchdog, ids= " + ids.toString());
1169
1170 if (ids.length > 0) {
1171
1172 for (var i = 0; i < ids.length; i++) {
1173 var e = document.getElementById("RROW-" + ids[i]);
1174 if (e) {
1175 e.className = e.className.replace("Unread", "");
1176 }
1177 }
1178
1179 var query = "backend.php?op=rpc&subop=catchupSelected&ids=" +
1180 param_escape(ids.toString()) + "&cmode=0";
1181
1182 new Ajax.Request(query, {
1183 onComplete: function(transport) {
1184 all_counters_callback2(transport);
1185 } });
1186
1187 }
1188
1189 _cdm_wd_timeout = window.setTimeout("cdmWatchdog()", 4000);
1190
1191 } catch (e) {
1192 exception_error(e, "cdmWatchdog");
1193 }
1194
1195 }
1196
1197
1198 function cache_inject(id, article, param) {
1199 if (!cache_check(id, param)) {
1200 debug("cache_article: miss: " + id + " [p=" + param + "]");
1201
1202 var cache_obj = new Array();
1203
1204 cache_obj["id"] = id;
1205 cache_obj["data"] = article;
1206 cache_obj["param"] = param;
1207
1208 article_cache.push(cache_obj);
1209
1210 } else {
1211 debug("cache_article: hit: " + id + " [p=" + param + "]");
1212 }
1213 }
1214
1215 function cache_find(id) {
1216 for (var i = 0; i < article_cache.length; i++) {
1217 if (article_cache[i]["id"] == id) {
1218 return article_cache[i]["data"];
1219 }
1220 }
1221 return false;
1222 }
1223
1224 function cache_get_param(id) {
1225 for (var i = 0; i < article_cache.length; i++) {
1226 if (article_cache[i]["id"] == id) {
1227 return article_cache[i]["param"];
1228 }
1229 }
1230 return false;
1231 }
1232
1233 function cache_check(id) {
1234 for (var i = 0; i < article_cache.length; i++) {
1235 if (article_cache[i]["id"] == id) {
1236 return true;
1237 }
1238 }
1239 return false;
1240 }
1241
1242 function cache_check_param(id, param) {
1243 for (var i = 0; i < article_cache.length; i++) {
1244 if (article_cache[i]["id"] == id && article_cache[i]["param"] == param) {
1245 return true;
1246 }
1247 }
1248 return false;
1249 }
1250
1251 function cache_expire() {
1252 while (article_cache.length > 20) {
1253 article_cache.shift();
1254 }
1255 }
1256
1257 function cache_invalidate(id) {
1258 var i = 0
1259
1260 try {
1261
1262 while (i < article_cache.length) {
1263 if (article_cache[i]["id"] == id) {
1264 debug("cache_invalidate: removed id " + id);
1265 article_cache.splice(i, 1);
1266 return true;
1267 }
1268 i++;
1269 }
1270 debug("cache_invalidate: id not found: " + id);
1271 return false;
1272 } catch (e) {
1273 exception_error("cache_invalidate", e);
1274 }
1275 }
1276
1277 function getActiveArticleId() {
1278 return active_post_id;
1279 }
1280
1281 function cdmMouseIn(elem) {
1282 try {
1283 if (elem.id && elem.id.match("RROW-")) {
1284 var id = elem.id.replace("RROW-", "");
1285 active_post_id = id;
1286 }
1287 } catch (e) {
1288 exception_error("cdmMouseIn", e);
1289 }
1290
1291 }
1292
1293 function cdmMouseOut(elem) {
1294 active_post_id = false;
1295 }
1296
1297 function headlines_scroll_handler() {
1298 try {
1299
1300 var e = document.getElementById("headlinesInnerContainer");
1301
1302 // don't do infinite scrolling when Limit == All
1303
1304 var toolbar_form = document.forms["main_toolbar_form"];
1305
1306 var limit = toolbar_form.limit[toolbar_form.limit.selectedIndex];
1307 if (limit.value != 0) {
1308 if (e.scrollTop + e.offsetHeight > e.scrollHeight - 50) {
1309 if (!_infscroll_disable) {
1310 debug("more cowbell!");
1311 viewNextFeedPage();
1312 }
1313 }
1314 }
1315
1316 } catch (e) {
1317 exception_error("headlines_scroll_handler", e);
1318 }
1319 }
1320
1321 function catchupRelativeToArticle(below) {
1322
1323 try {
1324
1325 if (!xmlhttp_ready(xmlhttp_rpc)) {
1326 printLockingError();
1327 }
1328
1329 if (!getActiveArticleId()) {
1330 alert(__("No article is selected."));
1331 return;
1332 }
1333
1334 var visible_ids;
1335
1336 if (document.getElementById("headlinesList")) {
1337 visible_ids = getVisibleHeadlineIds();
1338 } else {
1339 visible_ids = cdmGetVisibleArticles();
1340 }
1341
1342 var ids_to_mark = new Array();
1343
1344 if (!below) {
1345 for (var i = 0; i < visible_ids.length; i++) {
1346 if (visible_ids[i] != getActiveArticleId()) {
1347 var e = document.getElementById("RROW-" + visible_ids[i]);
1348
1349 if (e && e.className.match("Unread")) {
1350 ids_to_mark.push(visible_ids[i]);
1351 }
1352 } else {
1353 break;
1354 }
1355 }
1356 } else {
1357 for (var i = visible_ids.length-1; i >= 0; i--) {
1358 if (visible_ids[i] != getActiveArticleId()) {
1359 var e = document.getElementById("RROW-" + visible_ids[i]);
1360
1361 if (e && e.className.match("Unread")) {
1362 ids_to_mark.push(visible_ids[i]);
1363 }
1364 } else {
1365 break;
1366 }
1367 }
1368 }
1369
1370 if (ids_to_mark.length == 0) {
1371 alert(__("No articles found to mark"));
1372 } else {
1373 var msg = __("Mark %d article(s) as read?").replace("%d", ids_to_mark.length);
1374
1375 if (confirm(msg)) {
1376
1377 for (var i = 0; i < ids_to_mark.length; i++) {
1378 var e = document.getElementById("RROW-" + ids_to_mark[i]);
1379 e.className = e.className.replace("Unread", "");
1380 }
1381
1382 var query = "backend.php?op=rpc&subop=catchupSelected&ids=" +
1383 param_escape(ids_to_mark.toString()) + "&cmode=0";
1384
1385 new Ajax.Request(query, {
1386 onComplete: function(transport) {
1387 catchup_callback2(transport);
1388 } });
1389
1390 }
1391 }
1392
1393 } catch (e) {
1394 exception_error("catchupRelativeToArticle", e);
1395 }
1396 }
1397
1398 function cdmExpandArticle(a_id) {
1399 try {
1400 var id = 'CICD-' + a_id;
1401
1402 Effect.Appear(id, {duration : 0.5,
1403 beforeStart: function(effect) {
1404 var h_id = 'CICH-' + a_id;
1405 var h_elem = document.getElementById(h_id);
1406 if (h_elem) { h_elem.style.display = "none"; }
1407
1408 toggleUnread(a_id, 0);
1409 }});
1410
1411
1412 } catch (e) {
1413 exception_error("appearBlockElementF", e);
1414 }
1415
1416 }
1417
1418