]> git.wh0rd.org - tt-rss.git/blob - tt-rss.js
fix setting scrollTop on headlines buffer
[tt-rss.git] / tt-rss.js
1 var total_unread = 0;
2 var display_tags = false;
3 var global_unread = -1;
4 var firsttime_update = true;
5 var _active_feed_id = 0;
6 var _active_feed_is_cat = false;
7 var number_of_feeds = 0;
8 var hotkey_prefix = false;
9 var hotkey_prefix_pressed = false;
10 var init_params = {};
11 var _force_scheduled_update = false;
12 var last_scheduled_update = false;
13 var treeModel;
14
15 var _rpc_seq = 0;
16
17 function next_seq() {
18 _rpc_seq += 1;
19 return _rpc_seq;
20 }
21
22 function get_seq() {
23 return _rpc_seq;
24 }
25
26 function activeFeedIsCat() {
27 return _active_feed_is_cat;
28 }
29
30 function getActiveFeedId() {
31 try {
32 //console.log("gAFID: " + _active_feed_id);
33 return _active_feed_id;
34 } catch (e) {
35 exception_error("getActiveFeedId", e);
36 }
37 }
38
39 function setActiveFeedId(id, is_cat) {
40 try {
41 _active_feed_id = id;
42
43 if (is_cat != undefined) {
44 _active_feed_is_cat = is_cat;
45 }
46
47 selectFeed(id, is_cat);
48
49 } catch (e) {
50 exception_error("setActiveFeedId", e);
51 }
52 }
53
54
55 function isFeedlistSortable() {
56 return feedlist_sortable_enabled;
57 }
58
59 function tagsAreDisplayed() {
60 return display_tags;
61 }
62
63 function toggleTags(show_all) {
64
65 try {
66
67 console.log("toggleTags: " + show_all + "; " + display_tags);
68
69 var p = $("dispSwitchPrompt");
70
71 if (!show_all && !display_tags) {
72 displayDlg("printTagCloud");
73 } else if (show_all) {
74 closeInfoBox();
75 display_tags = true;
76 p.innerHTML = __("display feeds");
77 notify_progress("Loading, please wait...", true);
78 updateFeedList();
79 } else if (display_tags) {
80 display_tags = false;
81 p.innerHTML = __("tag cloud");
82 notify_progress("Loading, please wait...", true);
83 updateFeedList();
84 }
85
86 } catch (e) {
87 exception_error("toggleTags", e);
88 }
89 }
90
91 function dlg_frefresh_callback(transport, deleted_feed) {
92 if (getActiveFeedId() == deleted_feed) {
93 setTimeout("viewfeed(-5)", 100);
94 }
95
96 setTimeout('updateFeedList()', 50);
97 closeInfoBox();
98 }
99
100 function updateFeedList() {
101 try {
102 console.warn("updateFeedList: function not implemented");
103
104 /* var query_str = "backend.php?op=feeds";
105
106 if (display_tags) {
107 query_str = query_str + "&tags=1";
108 }
109
110 if (getActiveFeedId() && !activeFeedIsCat()) {
111 query_str = query_str + "&actid=" + getActiveFeedId();
112 }
113
114 new Ajax.Request("backend.php", {
115 parameters: query_str,
116 onComplete: function(transport) {
117 render_feedlist(transport.responseText);
118 } }); */
119
120 } catch (e) {
121 exception_error("updateFeedList", e);
122 }
123 }
124
125 function catchupAllFeeds() {
126
127 var str = __("Mark all articles as read?");
128
129 if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
130
131 var query_str = "backend.php?op=feeds&subop=catchupAll";
132
133 notify_progress("Marking all feeds as read...");
134
135 //console.log("catchupAllFeeds Q=" + query_str);
136
137 new Ajax.Request("backend.php", {
138 parameters: query_str,
139 onComplete: function(transport) {
140 feedlist_callback2(transport);
141 } });
142
143 global_unread = 0;
144 updateTitle("");
145 }
146 }
147
148 function viewCurrentFeed(subop) {
149
150 if (getActiveFeedId() != undefined) {
151 viewfeed(getActiveFeedId(), subop, activeFeedIsCat());
152 }
153 return false; // block unneeded form submits
154 }
155
156 function timeout() {
157 if (getInitParam("bw_limit") == "1") return;
158
159 try {
160 var date = new Date();
161 var ts = Math.round(date.getTime() / 1000);
162
163 if (ts - last_scheduled_update > 10 || _force_scheduled_update) {
164
165 //console.log("timeout()");
166
167 window.clearTimeout(counter_timeout_id);
168
169 var query_str = "?op=rpc&subop=getAllCounters&seq=" + next_seq();
170
171 var omode;
172
173 if (firsttime_update && !navigator.userAgent.match("Opera")) {
174 firsttime_update = false;
175 omode = "T";
176 } else {
177 if (display_tags) {
178 omode = "tl";
179 } else {
180 omode = "flc";
181 }
182 }
183
184 query_str = query_str + "&omode=" + omode;
185
186 if (!_force_scheduled_update)
187 query_str = query_str + "&last_article_id=" + getInitParam("last_article_id");
188
189 //console.log("[timeout]" + query_str);
190
191 new Ajax.Request("backend.php", {
192 parameters: query_str,
193 onComplete: function(transport) {
194 handle_rpc_reply(transport, !_force_scheduled_update);
195 _force_scheduled_update = false;
196 } });
197
198 last_scheduled_update = ts;
199 }
200
201 } catch (e) {
202 exception_error("timeout", e);
203 }
204
205 setTimeout("timeout()", 3000);
206 }
207
208 function search() {
209 closeInfoBox();
210 viewCurrentFeed();
211 }
212
213 function updateTitle() {
214 var tmp = "Tiny Tiny RSS";
215
216 if (global_unread > 0) {
217 tmp = tmp + " (" + global_unread + ")";
218 }
219
220 if (window.fluid) {
221 if (global_unread > 0) {
222 window.fluid.dockBadge = global_unread;
223 } else {
224 window.fluid.dockBadge = "";
225 }
226 }
227
228 document.title = tmp;
229 }
230
231 function genericSanityCheck() {
232 setCookie("ttrss_test", "TEST");
233
234 if (getCookie("ttrss_test") != "TEST") {
235 fatalError(2);
236 }
237
238 return true;
239 }
240
241 function init() {
242 try {
243 Form.disable("main_toolbar_form");
244
245 dojo.require("dijit.layout.BorderContainer");
246 dojo.require("dijit.layout.ContentPane");
247 dojo.require("dijit.Dialog");
248 dojo.require("dijit.form.Button");
249 dojo.require("dojo.data.ItemFileWriteStore");
250 dojo.require("dijit.Tree");
251 dojo.require("dijit.form.Select");
252 dojo.require("dojo.parser");
253
254 dojo.addOnLoad(function() {
255
256 var store = new dojo.data.ItemFileWriteStore({
257 url: "backend.php?op=feeds"});
258
259 treeModel = new dijit.tree.ForestStoreModel({
260 store: store,
261 query: {
262 "type": "feed"
263 },
264 rootId: "root",
265 rootLabel: "Feeds",
266 childrenAttrs: ["items"]
267 });
268
269 var tree = new dijit.Tree({
270 model: treeModel,
271 _createTreeNode: function(args) {
272 var tnode = new dijit._TreeNode(args);
273
274 if (args.item.icon)
275 tnode.iconNode.src = args.item.icon[0];
276
277 //tnode.labelNode.innerHTML = args.label;
278 return tnode;
279 },
280 getIconClass: function (item, opened) {
281 return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "feedIcon";
282 },
283 getLabelClass: function (item, opened) {
284 return (item.unread == 0) ? "dijitTreeLabel" : "dijitTreeLabel Unread";
285 },
286 getRowClass: function (item, opened) {
287 return (!item.error || item.error == '') ? "dijitTreeRow" :
288 "dijitTreeRow Error";
289 },
290 getLabel: function(item) {
291 if (item.unread > 0) {
292 return item.name + " (" + item.unread + ")";
293 } else {
294 return item.name;
295 }
296 },
297 onOpen: function (item, node) {
298 var id = String(item.id);
299 var cat_id = id.substr(id.indexOf(":")+1);
300
301 new Ajax.Request("backend.php",
302 { parameters: "backend.php?op=feeds&subop=collapse&cid=" +
303 param_escape(cat_id) + "&mode=1" } );
304 },
305 onClose: function (item, node) {
306 var id = String(item.id);
307 var cat_id = id.substr(id.indexOf(":")+1);
308
309 new Ajax.Request("backend.php",
310 { parameters: "backend.php?op=feeds&subop=collapse&cid=" +
311 param_escape(cat_id) + "&mode=0" } );
312
313 },
314 onClick: function (item, node) {
315 var id = String(item.id);
316 var is_cat = id.match("^CAT:");
317 var feed = id.substr(id.indexOf(":")+1);
318 viewfeed(feed, '', is_cat);
319 return false;
320 },
321 openOnClick: false,
322 showRoot: false,
323 }, "feedTree");
324
325 });
326
327 if (!genericSanityCheck())
328 return;
329
330 var params = "&ua=" + param_escape(navigator.userAgent);
331
332 loading_set_progress(30);
333
334 new Ajax.Request("backend.php", {
335 parameters: "backend.php?op=rpc&subop=sanityCheck" + params,
336 onComplete: function(transport) {
337 backend_sanity_check_callback(transport);
338 } });
339
340 } catch (e) {
341 exception_error("init", e);
342 }
343 }
344
345 function init_second_stage() {
346
347 try {
348
349 delCookie("ttrss_test");
350
351 var toolbar = document.forms["main_toolbar_form"];
352
353 dropboxSelect(toolbar.view_mode, getInitParam("default_view_mode"));
354 dropboxSelect(toolbar.order_by, getInitParam("default_view_order_by"));
355
356 feeds_sort_by_unread = getInitParam("feeds_sort_by_unread") == 1;
357
358 loading_set_progress(60);
359
360 if (has_local_storage())
361 localStorage.clear();
362
363 feedlist_init();
364 setTimeout("timeout()", 3000);
365
366 console.log("second stage ok");
367
368 } catch (e) {
369 exception_error("init_second_stage", e);
370 }
371 }
372
373 function quickMenuChange() {
374 var chooser = $("quickMenuChooser");
375 var opid = chooser[chooser.selectedIndex].value;
376
377 chooser.selectedIndex = 0;
378 quickMenuGo(opid);
379 }
380
381 function quickMenuGo(opid) {
382 try {
383
384 if (opid == "qmcPrefs") {
385 gotoPreferences();
386 }
387
388 if (opid == "qmcSearch") {
389 displayDlg("search", getActiveFeedId() + ":" + activeFeedIsCat(),
390 function() {
391 document.forms['search_form'].query.focus();
392 });
393 return;
394 }
395
396 if (opid == "qmcAddFeed") {
397 quickAddFeed();
398 return;
399 }
400
401 if (opid == "qmcEditFeed") {
402 editFeedDlg(getActiveFeedId());
403 }
404
405 if (opid == "qmcRemoveFeed") {
406 var actid = getActiveFeedId();
407
408 if (activeFeedIsCat()) {
409 alert(__("You can't unsubscribe from the category."));
410 return;
411 }
412
413 if (!actid) {
414 alert(__("Please select some feed first."));
415 return;
416 }
417
418 var fn = getFeedName(actid);
419
420 var pr = __("Unsubscribe from %s?").replace("%s", fn);
421
422 if (confirm(pr)) {
423 unsubscribeFeed(actid);
424 }
425
426 return;
427 }
428
429 if (opid == "qmcCatchupAll") {
430 catchupAllFeeds();
431 return;
432 }
433
434 if (opid == "qmcShowOnlyUnread") {
435 toggleDispRead();
436 return;
437 }
438
439 if (opid == "qmcAddFilter") {
440 displayDlg('quickAddFilter', '',
441 function () {document.forms['filter_add_form'].reg_exp.focus();});
442 }
443
444 if (opid == "qmcAddLabel") {
445 addLabel();
446 }
447
448 if (opid == "qmcRescoreFeed") {
449 rescoreCurrentFeed();
450 }
451
452 if (opid == "qmcHKhelp") {
453 //Element.show("hotkey_help_overlay");
454 Effect.Appear("hotkey_help_overlay", {duration : 0.3});
455 }
456
457 if (opid == "qmcResetUI") {
458 alert("Function not implemented");
459 }
460
461 if (opid == "qmcToggleReorder") {
462 feedlist_sortable_enabled = !feedlist_sortable_enabled;
463
464 if (feedlist_sortable_enabled) {
465 notify_info("Category reordering enabled");
466 toggle_sortable_feedlist(true);
467 } else {
468 notify_info("Category reordering disabled");
469 toggle_sortable_feedlist(false);
470 }
471 }
472
473 if (opid == "qmcResetCats") {
474
475 if (confirm(__("Reset category order?"))) {
476
477 var query = "?op=feeds&subop=catsortreset";
478
479 notify_progress("Loading, please wait...", true);
480
481 new Ajax.Request("backend.php", {
482 parameters: query,
483 onComplete: function(transport) {
484 window.setTimeout('updateFeedList(false, false)', 50);
485 } });
486 }
487 }
488
489 } catch (e) {
490 exception_error("quickMenuGo", e);
491 }
492 }
493
494 function toggleDispRead() {
495 try {
496
497 var hide = !(getInitParam("hide_read_feeds") == "1");
498
499 hideOrShowFeeds(hide);
500
501 var query = "?op=rpc&subop=setpref&key=HIDE_READ_FEEDS&value=" +
502 param_escape(hide);
503
504 new Ajax.Request("backend.php", {
505 parameters: query,
506 onComplete: function(transport) {
507 setInitParam("hide_read_feeds", hide);
508 } });
509
510 } catch (e) {
511 exception_error("toggleDispRead", e);
512 }
513 }
514
515 function parse_runtime_info(elem) {
516
517 if (!elem || !elem.firstChild) {
518 console.warn("parse_runtime_info: invalid node passed");
519 return;
520 }
521
522 var data = JSON.parse(elem.firstChild.nodeValue);
523
524 //console.log("parsing runtime info...");
525
526 for (k in data) {
527 var v = data[k];
528
529 // console.log("RI: " + k + " => " + v);
530
531 if (k == "new_version_available") {
532 var icon = $("newVersionIcon");
533 if (icon) {
534 if (v == "1") {
535 icon.style.display = "inline";
536 } else {
537 icon.style.display = "none";
538 }
539 }
540 return;
541 }
542
543 var error_flag;
544
545 if (k == "daemon_is_running" && v != 1) {
546 notify_error("<span onclick=\"javascript:explainError(1)\">Update daemon is not running.</span>", true);
547 return;
548 }
549
550 if (k == "daemon_stamp_ok" && v != 1) {
551 notify_error("<span onclick=\"javascript:explainError(3)\">Update daemon is not updating feeds.</span>", true);
552 return;
553 }
554
555 init_params[k] = v;
556 notify('');
557 }
558 }
559
560 function catchupCurrentFeed() {
561
562 var fn = getFeedName(getActiveFeedId(), activeFeedIsCat());
563
564 var str = __("Mark all articles in %s as read?").replace("%s", fn);
565
566 if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
567 return viewCurrentFeed('MarkAllRead')
568 }
569 }
570
571 function catchupFeedInGroup(id) {
572
573 try {
574
575 var title = getFeedName(id);
576
577 var str = __("Mark all articles in %s as read?").replace("%s", title);
578
579 if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
580 return viewCurrentFeed('MarkAllReadGR:' + id)
581 }
582
583 } catch (e) {
584 exception_error("catchupFeedInGroup", e);
585 }
586 }
587
588 function editFeedDlg(feed) {
589 try {
590
591 if (!feed) {
592 alert(__("Please select some feed first."));
593 return;
594 }
595
596 if ((feed <= 0) || activeFeedIsCat() || tagsAreDisplayed()) {
597 alert(__("You can't edit this kind of feed."));
598 return;
599 }
600
601 var query = "";
602
603 if (feed > 0) {
604 query = "?op=pref-feeds&subop=editfeed&id=" + param_escape(feed);
605 } else {
606 query = "?op=pref-labels&subop=edit&id=" + param_escape(-feed-11);
607 }
608
609 disableHotkeys();
610
611 notify_progress("Loading, please wait...", true);
612
613 new Ajax.Request("backend.php", {
614 parameters: query,
615 onComplete: function(transport) {
616 infobox_callback2(transport);
617 document.forms["edit_feed_form"].title.focus();
618 } });
619
620 } catch (e) {
621 exception_error("editFeedDlg", e);
622 }
623 }
624
625 /* this functions duplicate those of prefs.js feed editor, with
626 some differences because there is no feedlist */
627
628 function feedEditCancel() {
629 closeInfoBox();
630 return false;
631 }
632
633 function feedEditSave() {
634
635 try {
636
637 // FIXME: add parameter validation
638
639 var query = Form.serialize("edit_feed_form");
640
641 notify_progress("Saving feed...");
642
643 new Ajax.Request("backend.php", {
644 parameters: query,
645 onComplete: function(transport) {
646 dlg_frefresh_callback(transport);
647 } });
648
649 cache_flush();
650 closeInfoBox();
651
652 return false;
653
654 } catch (e) {
655 exception_error("feedEditSave (main)", e);
656 }
657 }
658
659 function collapse_feedlist() {
660 try {
661
662 if (!Element.visible('feeds-holder')) {
663 Element.show('feeds-holder');
664 $("collapse_feeds_btn").innerHTML = "&lt;&lt;";
665 } else {
666 Element.hide('feeds-holder');
667 $("collapse_feeds_btn").innerHTML = "&gt;&gt;";
668 }
669
670 dijit.byId("main").resize();
671
672 query = "?op=rpc&subop=setpref&key=_COLLAPSED_FEEDLIST&value=true";
673 new Ajax.Request("backend.php", { parameters: query });
674
675 } catch (e) {
676 exception_error("collapse_feedlist", e);
677 }
678 }
679
680 function viewModeChanged() {
681 cache_flush();
682 return viewCurrentFeed('')
683 }
684
685 function viewLimitChanged() {
686 cache_flush();
687 return viewCurrentFeed('')
688 }
689
690 /* function adjustArticleScore(id, score) {
691 try {
692
693 var pr = prompt(__("Assign score to article:"), score);
694
695 if (pr != undefined) {
696 var query = "?op=rpc&subop=setScore&id=" + id + "&score=" + pr;
697
698 new Ajax.Request("backend.php", {
699 parameters: query,
700 onComplete: function(transport) {
701 viewCurrentFeed();
702 } });
703
704 }
705 } catch (e) {
706 exception_error("adjustArticleScore", e);
707 }
708 } */
709
710 function rescoreCurrentFeed() {
711
712 var actid = getActiveFeedId();
713
714 if (activeFeedIsCat() || actid < 0 || tagsAreDisplayed()) {
715 alert(__("You can't rescore this kind of feed."));
716 return;
717 }
718
719 if (!actid) {
720 alert(__("Please select some feed first."));
721 return;
722 }
723
724 var fn = getFeedName(actid);
725 var pr = __("Rescore articles in %s?").replace("%s", fn);
726
727 if (confirm(pr)) {
728 notify_progress("Rescoring articles...");
729
730 var query = "?op=pref-feeds&subop=rescore&quiet=1&ids=" + actid;
731
732 new Ajax.Request("backend.php", {
733 parameters: query,
734 onComplete: function(transport) {
735 viewCurrentFeed();
736 } });
737 }
738 }
739
740 function hotkey_handler(e) {
741
742 try {
743
744 var keycode;
745 var shift_key = false;
746
747 var cmdline = $('cmdline');
748
749 try {
750 shift_key = e.shiftKey;
751 } catch (e) {
752
753 }
754
755 if (window.event) {
756 keycode = window.event.keyCode;
757 } else if (e) {
758 keycode = e.which;
759 }
760
761 var keychar = String.fromCharCode(keycode);
762
763 if (keycode == 27) { // escape
764 if (Element.visible("hotkey_help_overlay")) {
765 Element.hide("hotkey_help_overlay");
766 }
767 hotkey_prefix = false;
768 closeInfoBox();
769 }
770
771 if (dialogs.length > 0 || !hotkeys_enabled) {
772 console.log("hotkeys disabled");
773 return;
774 }
775
776 if (keycode == 16) return; // ignore lone shift
777 if (keycode == 17) return; // ignore lone ctrl
778
779 if ((keycode == 70 || keycode == 67 || keycode == 71)
780 && !hotkey_prefix) {
781
782 var date = new Date();
783 var ts = Math.round(date.getTime() / 1000);
784
785 hotkey_prefix = keycode;
786 hotkey_prefix_pressed = ts;
787
788 cmdline.innerHTML = keychar;
789 Element.show(cmdline);
790
791 console.log("KP: PREFIX=" + keycode + " CHAR=" + keychar + " TS=" + ts);
792 return true;
793 }
794
795 if (Element.visible("hotkey_help_overlay")) {
796 Element.hide("hotkey_help_overlay");
797 }
798
799 /* Global hotkeys */
800
801 Element.hide(cmdline);
802
803 if (!hotkey_prefix) {
804
805 if ((keycode == 191 || keychar == '?') && shift_key) { // ?
806 if (!Element.visible("hotkey_help_overlay")) {
807 //Element.show("hotkey_help_overlay");
808 Effect.Appear("hotkey_help_overlay", {duration : 0.3});
809 } else {
810 Element.hide("hotkey_help_overlay");
811 }
812 return false;
813 }
814
815 if (keycode == 191 || keychar == '/') { // /
816 displayDlg("search", getActiveFeedId() + ":" + activeFeedIsCat(),
817 function() {
818 document.forms['search_form'].query.focus();
819 });
820 return false;
821 }
822
823 /* if (keycode == 82 && shift_key) { // R
824 scheduleFeedUpdate(true);
825 return;
826 } */
827
828 if (keycode == 74) { // j
829 var feed = getActiveFeedId();
830 var new_feed = getRelativeFeedId2(feed, activeFeedIsCat(), 'prev');
831 // alert(feed + " IC: " + activeFeedIsCat() + " => " + new_feed);
832 if (new_feed) {
833 var is_cat = new_feed.match("CAT:");
834 if (is_cat) {
835 new_feed = new_feed.replace("CAT:", "");
836 viewCategory(new_feed);
837 } else {
838 viewfeed(new_feed, '', false);
839 }
840 }
841 return;
842 }
843
844 if (keycode == 75) { // k
845 var feed = getActiveFeedId();
846 var new_feed = getRelativeFeedId2(feed, activeFeedIsCat(), 'next');
847 // alert(feed + " IC: " + activeFeedIsCat() + " => " + new_feed);
848 if (new_feed) {
849 var is_cat = new_feed.match("CAT:");
850 if (is_cat == "CAT:") {
851 new_feed = new_feed.replace("CAT:", "");
852 viewCategory(new_feed);
853 } else {
854 viewfeed(new_feed, '', false);
855 }
856 }
857 return;
858 }
859
860 if (shift_key && keycode == 40) { // shift-down
861 catchupRelativeToArticle(1);
862 return;
863 }
864
865 if (shift_key && keycode == 38) { // shift-up
866 catchupRelativeToArticle(0);
867 return;
868 }
869
870 if (shift_key && keycode == 78) { // N
871 scrollArticle(50);
872 return;
873 }
874
875 if (shift_key && keycode == 80) { // P
876 scrollArticle(-50);
877 return;
878 }
879
880 if (keycode == 68 && shift_key) { // shift-D
881 dismissSelectedArticles();
882 }
883
884 if (keycode == 88 && shift_key) { // shift-X
885 dismissReadArticles();
886 }
887
888 if (keycode == 78 || keycode == 40) { // n, down
889 if (typeof moveToPost != 'undefined') {
890 moveToPost('next');
891 return false;
892 }
893 }
894
895 if (keycode == 80 || keycode == 38) { // p, up
896 if (typeof moveToPost != 'undefined') {
897 moveToPost('prev');
898 return false;
899 }
900 }
901
902 if (keycode == 83 && shift_key) { // S
903 selectionTogglePublished(undefined, false, true);
904 return;
905 }
906
907 if (keycode == 83) { // s
908 selectionToggleMarked(undefined, false, true);
909 return;
910 }
911
912
913 if (keycode == 85) { // u
914 selectionToggleUnread(undefined, false, true)
915 return;
916 }
917
918 if (keycode == 84 && shift_key) { // T
919 var id = getActiveArticleId();
920 if (id) {
921 editArticleTags(id, getActiveFeedId(), isCdmMode());
922 return;
923 }
924 }
925
926 if (keycode == 9) { // tab
927 var id = getArticleUnderPointer();
928 if (id) {
929 var cb = $("RCHK-" + id);
930
931 if (cb) {
932 cb.checked = !cb.checked;
933 toggleSelectRowById(cb, "RROW-" + id);
934 return false;
935 }
936 }
937 }
938
939 if (keycode == 79) { // o
940 if (getActiveArticleId()) {
941 openArticleInNewWindow(getActiveArticleId());
942 return;
943 }
944 }
945
946 if (keycode == 81 && shift_key) { // Q
947 if (typeof catchupAllFeeds != 'undefined') {
948 catchupAllFeeds();
949 return;
950 }
951 }
952
953 if (keycode == 88) { // x
954 if (activeFeedIsCat()) {
955 toggleCollapseCat(getActiveFeedId());
956 }
957 }
958 }
959
960 /* Prefix f */
961
962 if (hotkey_prefix == 70) { // f
963
964 hotkey_prefix = false;
965
966 if (keycode == 81) { // q
967 if (getActiveFeedId()) {
968 catchupCurrentFeed();
969 return;
970 }
971 }
972
973 if (keycode == 82) { // r
974 if (getActiveFeedId()) {
975 viewfeed(getActiveFeedId(), "ForceUpdate", activeFeedIsCat());
976 return;
977 }
978 }
979
980 if (keycode == 65) { // a
981 toggleDispRead();
982 return false;
983 }
984
985 /* if (keycode == 85 && shift_key) { // U
986 scheduleFeedUpdate(true);
987 return false;
988 } */
989
990 if (keycode == 85) { // u
991 if (getActiveFeedId()) {
992 viewfeed(getActiveFeedId(), "ForceUpdate");
993 return false;
994 }
995 }
996
997 if (keycode == 69) { // e
998 editFeedDlg(getActiveFeedId());
999 return false;
1000 }
1001
1002 if (keycode == 83) { // s
1003 quickAddFeed();
1004 return false;
1005 }
1006
1007 if (keycode == 67 && shift_key) { // C
1008 if (typeof catchupAllFeeds != 'undefined') {
1009 catchupAllFeeds();
1010 return false;
1011 }
1012 }
1013
1014 if (keycode == 67) { // c
1015 if (getActiveFeedId()) {
1016 catchupCurrentFeed();
1017 return false;
1018 }
1019 }
1020
1021 if (keycode == 87) { // w
1022 feeds_sort_by_unread = !feeds_sort_by_unread;
1023 return resort_feedlist();
1024 }
1025
1026 if (keycode == 88) { // x
1027 reverseHeadlineOrder();
1028 return;
1029 }
1030 }
1031
1032 /* Prefix c */
1033
1034 if (hotkey_prefix == 67) { // c
1035 hotkey_prefix = false;
1036
1037 if (keycode == 70) { // f
1038 displayDlg('quickAddFilter', '',
1039 function () {document.forms['filter_add_form'].reg_exp.focus();});
1040 return false;
1041 }
1042
1043 if (keycode == 76) { // l
1044 addLabel();
1045 return false;
1046 }
1047
1048 if (keycode == 83) { // s
1049 if (typeof collapse_feedlist != 'undefined') {
1050 collapse_feedlist();
1051 return false;
1052 }
1053 }
1054
1055 if (keycode == 77) { // m
1056 feedlist_sortable_enabled = !feedlist_sortable_enabled;
1057 if (feedlist_sortable_enabled) {
1058 notify_info("Category reordering enabled");
1059 toggle_sortable_feedlist(true);
1060 } else {
1061 notify_info("Category reordering disabled");
1062 toggle_sortable_feedlist(false);
1063 }
1064 }
1065
1066 if (keycode == 78) { // n
1067 catchupRelativeToArticle(1);
1068 return;
1069 }
1070
1071 if (keycode == 80) { // p
1072 catchupRelativeToArticle(0);
1073 return;
1074 }
1075
1076
1077 }
1078
1079 /* Prefix g */
1080
1081 if (hotkey_prefix == 71) { // g
1082
1083 hotkey_prefix = false;
1084
1085
1086 if (keycode == 65) { // a
1087 viewfeed(-4);
1088 return false;
1089 }
1090
1091 if (keycode == 83) { // s
1092 viewfeed(-1);
1093 return false;
1094 }
1095
1096 if (keycode == 80 && shift_key) { // P
1097 gotoPreferences();
1098 return false;
1099 }
1100
1101 if (keycode == 80) { // p
1102 viewfeed(-2);
1103 return false;
1104 }
1105
1106 if (keycode == 70) { // f
1107 viewfeed(-3);
1108 return false;
1109 }
1110
1111 if (keycode == 84 && shift_key) { // T
1112 toggleTags();
1113 return false;
1114 }
1115 }
1116
1117 /* Cmd */
1118
1119 if (hotkey_prefix == 224 || hotkey_prefix == 91) { // f
1120 hotkey_prefix = false;
1121 return;
1122 }
1123
1124 if (hotkey_prefix) {
1125 console.log("KP: PREFIX=" + hotkey_prefix + " CODE=" + keycode + " CHAR=" + keychar);
1126 } else {
1127 console.log("KP: CODE=" + keycode + " CHAR=" + keychar);
1128 }
1129
1130
1131 } catch (e) {
1132 exception_error("hotkey_handler", e);
1133 }
1134 }
1135
1136 function inPreferences() {
1137 return false;
1138 }
1139
1140 function reverseHeadlineOrder() {
1141 try {
1142
1143 var query_str = "?op=rpc&subop=togglepref&key=REVERSE_HEADLINES";
1144
1145 new Ajax.Request("backend.php", {
1146 parameters: query_str,
1147 onComplete: function(transport) {
1148 viewCurrentFeed();
1149 } });
1150
1151 } catch (e) {
1152 exception_error("reverseHeadlineOrder", e);
1153 }
1154 }
1155
1156 function showFeedsWithErrors() {
1157 displayDlg('feedUpdateErrors');
1158 }
1159
1160 function handle_rpc_reply(transport, scheduled_call) {
1161 try {
1162 if (transport.responseXML) {
1163
1164 if (!transport_error_check(transport)) return false;
1165
1166 var seq = transport.responseXML.getElementsByTagName("seq")[0];
1167
1168 if (seq) {
1169 seq = seq.firstChild.nodeValue;
1170
1171 if (get_seq() != seq) {
1172 //console.log("[handle_rpc_reply] sequence mismatch: " + seq);
1173 return true;
1174 }
1175 }
1176
1177 var message = transport.responseXML.getElementsByTagName("message")[0];
1178
1179 if (message) {
1180 message = message.firstChild.nodeValue;
1181
1182 if (message == "UPDATE_COUNTERS") {
1183 console.log("need to refresh counters...");
1184 setInitParam("last_article_id", -1);
1185 _force_scheduled_update = true;
1186 }
1187 }
1188
1189 var counters = transport.responseXML.getElementsByTagName("counters")[0];
1190
1191 if (counters)
1192 parse_counters(counters, scheduled_call);
1193
1194 var runtime_info = transport.responseXML.getElementsByTagName("runtime-info")[0];
1195
1196 if (runtime_info)
1197 parse_runtime_info(runtime_info);
1198
1199 if (feedsSortByUnread())
1200 resort_feedlist();
1201
1202 hideOrShowFeeds(getInitParam("hide_read_feeds") == 1);
1203
1204 } else {
1205 notify_error("Error communicating with server.");
1206 }
1207
1208 } catch (e) {
1209 exception_error("handle_rpc_reply", e, transport);
1210 }
1211
1212 return true;
1213 }
1214
1215 function scheduleFeedUpdate() {
1216 try {
1217
1218 if (!getActiveFeedId()) {
1219 alert(__("Please select some feed first."));
1220 return;
1221 }
1222
1223 var query = "?op=rpc&subop=scheduleFeedUpdate&id=" +
1224 param_escape(getActiveFeedId()) +
1225 "&is_cat=" + param_escape(activeFeedIsCat());
1226
1227 console.log(query);
1228
1229 new Ajax.Request("backend.php", {
1230 parameters: query,
1231 onComplete: function(transport) {
1232
1233 if (transport.responseXML) {
1234 var message = transport.responseXML.getElementsByTagName("message")[0];
1235
1236 if (message) {
1237 notify_info(message.firstChild.nodeValue);
1238 return;
1239 }
1240 }
1241
1242 notify_error("Error communicating with server.");
1243
1244 } });
1245
1246
1247 } catch (e) {
1248 exception_error("scheduleFeedUpdate", e);
1249 }
1250 }