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