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