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