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