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