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