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