]> git.wh0rd.org Git - tt-rss.git/blob - tt-rss.js
shortcuts: remove blocking of some alpha characters
[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                 loading_set_progress(30);
348
349                 new Ajax.Request("backend.php?op=rpc&subop=sanityCheck" + params,       {
350                         onComplete: function(transport) {
351                                         backend_sanity_check_callback(transport);
352                                 } });
353
354         } catch (e) {
355                 exception_error("init", e);
356         }
357 }
358
359 function resize_headlines() {
360
361         var h_frame = document.getElementById("headlines-frame");
362         var c_frame = document.getElementById("content-frame");
363         var f_frame = document.getElementById("footer");
364         var feeds_frame = document.getElementById("feeds-holder");
365
366         if (!c_frame || !h_frame) return;
367
368         if (feeds_frame && getInitParam("theme") == "compat") {
369                         feeds_frame.style.bottom = f_frame.offsetHeight + "px";         
370         }
371
372         if (getInitParam("theme") == "3pane") {
373                 debug("resize_headlines: HOR-mode");
374
375                 c_frame.style.width = '35%';
376                 h_frame.style.right = c_frame.offsetWidth - 1 + "px";
377
378         } else {
379                 debug("resize_headlines: VER-mode");
380
381                 if (!is_msie()) {
382                         h_frame.style.height = 30 + "%";
383                         c_frame.style.top = h_frame.offsetTop + h_frame.offsetHeight + 1 + "px";
384                         h_frame.style.height = h_frame.offsetHeight + "px";
385                 } else {
386                         h_frame.style.height = document.documentElement.clientHeight * 0.3 + "px";
387                         c_frame.style.top = h_frame.offsetTop + h_frame.offsetHeight + 1 + "px";
388         
389                         var c_bottom = document.documentElement.clientHeight;
390         
391                         if (f_frame) {
392                                 c_bottom = f_frame.offsetTop;
393                         }
394         
395                         c_frame.style.height = c_bottom - (h_frame.offsetTop + 
396                                 h_frame.offsetHeight + 1) + "px";
397                         h_frame.style.height = h_frame.offsetHeight + "px";
398         
399                 }
400
401         }
402
403 }
404
405 function init_second_stage() {
406
407         try {
408
409                 cookie_lifetime = getCookie("ttrss_cltime");
410
411                 delCookie("ttrss_vf_test");
412
413 //              document.onresize = resize_headlines;
414                 resize_headlines();
415
416                 var toolbar = document.forms["main_toolbar_form"];
417
418                 dropboxSelect(toolbar.view_mode, getInitParam("default_view_mode"));
419                 dropboxSelect(toolbar.limit, getInitParam("default_view_limit"));
420
421                 daemon_enabled = getInitParam("daemon_enabled") == 1;
422                 daemon_refresh_only = getInitParam("daemon_refresh_only") == 1;
423
424                 setTimeout('updateFeedList(false, false)', 50);
425
426                 debug("second stage ok");
427
428                 loading_set_progress(60);
429
430         } catch (e) {
431                 exception_error("init_second_stage", e);
432         }
433 }
434
435 function quickMenuChange() {
436         var chooser = document.getElementById("quickMenuChooser");
437         var opid = chooser[chooser.selectedIndex].value;
438
439         chooser.selectedIndex = 0;
440         quickMenuGo(opid);
441 }
442
443 function quickMenuGo(opid) {
444         try {
445
446                 if (opid == "qmcPrefs") {
447                         gotoPreferences();
448                 }
449         
450                 if (opid == "qmcSearch") {
451                         displayDlg("search", getActiveFeedId() + ":" + activeFeedIsCat());
452                         return;
453                 }
454         
455                 if (opid == "qmcAddFeed") {
456                         displayDlg("quickAddFeed");
457                         return;
458                 }
459
460                 if (opid == "qmcEditFeed") {
461                         editFeedDlg(getActiveFeedId());
462                 }
463         
464                 if (opid == "qmcRemoveFeed") {
465                         var actid = getActiveFeedId();
466
467                         if (activeFeedIsCat()) {
468                                 alert(__("You can't unsubscribe from the category."));
469                                 return;
470                         }       
471
472                         if (!actid) {
473                                 alert(__("Please select some feed first."));
474                                 return;
475                         }
476
477                         var fn = getFeedName(actid);
478
479                         var pr = __("Unsubscribe from %s?").replace("%s", fn);
480
481                         if (confirm(pr)) {
482                                 unsubscribeFeed(actid);
483                         }
484                 
485                         return;
486                 }
487
488                 if (opid == "qmcClearFeed") {
489                         var actid = getActiveFeedId();
490
491                         if (!actid) {
492                                 alert(__("Please select some feed first."));
493                                 return;
494                         }
495
496                         if (activeFeedIsCat() || actid < 0) {
497                                 alert(__("You can't clear this type of feed."));
498                                 return;
499                         }       
500
501                         var fn = getFeedName(actid);
502
503                         var pr = __("Erase all non-starred articles in %s?").replace("%s", fn);
504
505                         if (confirm(pr)) {
506                                 clearFeedArticles(actid);
507                         }
508                 
509                         return;
510                 }
511         
512
513                 if (opid == "qmcUpdateFeeds") {
514                         scheduleFeedUpdate(true);
515                         return;
516                 }
517         
518                 if (opid == "qmcCatchupAll") {
519                         catchupAllFeeds();
520                         return;
521                 }
522         
523                 if (opid == "qmcShowOnlyUnread") {
524                         toggleDispRead();
525                         return;
526                 }
527         
528                 if (opid == "qmcAddFilter") {
529                         displayDlg("quickAddFilter", getActiveFeedId());
530                 }
531
532                 if (opid == "qmcRescoreFeed") {
533                         rescoreCurrentFeed();
534                 }
535
536                 if (opid == "qmcHKhelp") {
537                         //Element.show("hotkey_help_overlay");
538                         Effect.Appear("hotkey_help_overlay", {duration : 0.3});
539                 }
540
541         } catch (e) {
542                 exception_error("quickMenuGo", e);
543         }
544 }
545
546 function unsubscribeFeed(feed_id) {
547
548         notify_progress("Removing feed...");
549
550         var query = "backend.php?op=pref-feeds&quiet=1&subop=remove&ids=" + feed_id;
551
552         new Ajax.Request(query, {
553                 onComplete: function(transport) {
554                                 dlg_frefresh_callback(transport, feed_id);
555                         } });
556
557
558         return false;
559 }
560
561
562 function updateFeedTitle(t) {
563         active_title_text = t;
564         updateTitle();
565 }
566
567 function toggleDispRead() {
568         try {
569
570                 var hide_read_feeds = (getInitParam("hide_read_feeds") == "1");
571
572                 hide_read_feeds = !hide_read_feeds;
573
574                 debug("toggle_disp_read => " + hide_read_feeds);
575
576                 hideOrShowFeeds(getFeedsContext().document, hide_read_feeds);
577
578                 storeInitParam("hide_read_feeds", hide_read_feeds, true);
579                                 
580         } catch (e) {
581                 exception_error("toggleDispRead", e);
582         }
583 }
584
585 function parse_runtime_info(elem) {
586         if (!elem) {
587                 debug("parse_runtime_info: elem is null, aborting");
588                 return;
589         }
590
591         var param = elem.firstChild;
592
593         debug("parse_runtime_info: " + param);
594
595         while (param) {
596                 var k = param.getAttribute("key");
597                 var v = param.getAttribute("value");
598
599                 debug("RI: " + k + " => " + v);
600
601                 if (k == "new_version_available") {
602                         var icon = document.getElementById("newVersionIcon");
603                         if (icon) {
604                                 if (v == "1") {
605                                         icon.style.display = "inline";
606                                 } else {
607                                         icon.style.display = "none";
608                                 }
609                         }
610                 }
611
612                 var error_flag;
613
614                 if (k == "daemon_is_running" && v != 1) {
615                         notify_error("<span onclick=\"javascript:explainError(1)\">Update daemon is not running.</span>", true);
616                         error_flag = true;
617                 }
618
619                 if (k == "daemon_stamp_ok" && v != 1) {
620                         notify_error("<span onclick=\"javascript:explainError(3)\">Update daemon is not updating feeds.</span>", true);
621                         error_flag = true;
622                 }
623
624                 if (!error_flag) {
625                         notify('');
626                 }
627
628 /*              var w = document.getElementById("noDaemonWarning");
629                 
630                 if (w) {
631                         if (k == "daemon_is_running" && v != 1) {
632                                 w.style.display = "block";
633                         } else {
634                                 w.style.display = "none";
635                         }
636                 } */
637                 param = param.nextSibling;
638         }
639 }
640
641 function catchupCurrentFeed() {
642
643         var fn = getFeedName(getActiveFeedId(), active_feed_is_cat);
644         
645         var str = __("Mark all articles in %s as read?").replace("%s", fn);
646
647 /*      if (active_feed_is_cat) {
648                 str = "Mark all articles in this category as read?";
649         } */
650
651         if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
652                 return viewCurrentFeed('MarkAllRead')
653         }
654 }
655
656 function catchupFeedInGroup(id, title) {
657
658         var str = __("Mark all articles in %s as read?").replace("%s", title);
659
660         if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
661                 return viewCurrentFeed('MarkAllReadGR:' + id)
662         }
663 }
664
665 function editFeedDlg(feed) {
666         try {
667
668                 if (!feed) {
669                         alert(__("Please select some feed first."));
670                         return;
671                 }
672         
673                 if ((feed <= 0 && feed > -10) || activeFeedIsCat() || tagsAreDisplayed()) {
674                         alert(__("You can't edit this kind of feed."));
675                         return;
676                 }
677         
678                 var query = "";
679         
680                 if (feed > 0) {
681                         query = "backend.php?op=pref-feeds&subop=editfeed&id=" +        param_escape(feed);
682                 } else {
683                         query = "backend.php?op=pref-labels&subop=edit&id=" +   param_escape(-feed-11);
684                 }
685
686                 disableHotkeys();
687
688                 new Ajax.Request(query, {
689                         onComplete: function(transport) { 
690                                 infobox_callback2(transport); 
691                         } });
692
693         } catch (e) {
694                 exception_error("editFeedDlg", e);
695         }
696 }
697
698 /* this functions duplicate those of prefs.js feed editor, with
699         some differences because there is no feedlist */
700
701 function feedEditCancel() {
702         closeInfoBox();
703         return false;
704 }
705
706 function feedEditSave() {
707
708         try {
709         
710                 // FIXME: add parameter validation
711
712                 var query = Form.serialize("edit_feed_form");
713
714                 notify_progress("Saving feed...");
715
716                 new Ajax.Request("backend.php", {
717                         parameters: query,
718                         onComplete: function(transport) { 
719                                 dlg_frefresh_callback(transport); 
720                         } });
721
722
723                 closeInfoBox();
724
725                 return false;
726
727         } catch (e) {
728                 exception_error("feedEditSave (main)", e);
729         } 
730 }
731
732 function labelEditCancel() {
733         closeInfoBox();
734         return false;
735 }
736
737 function labelEditSave() {
738
739         try {
740
741                 closeInfoBox();
742         
743                 notify_progress("Saving label...");
744         
745                 query = Form.serialize("label_edit_form");
746         
747                 new Ajax.Request("backend.php?" + query, {
748                         onComplete: function(transport) { 
749                                 dlg_frefresh_callback(transport); 
750                         } });
751
752                 return false;
753
754         } catch (e) {
755                 exception_error("feedEditSave (main)", e);
756         } 
757
758 }
759
760 function clearFeedArticles(feed_id) {
761
762         notify_progress("Clearing feed...");
763
764         var query = "backend.php?op=pref-feeds&quiet=1&subop=clear&id=" + feed_id;
765
766         new Ajax.Request(query, {
767                 onComplete: function(transport) {
768                                 dlg_frefresh_callback(transport, feed_id);
769                         } });
770
771         return false;
772 }
773
774 /*
775 function toggle_feedlist() {
776         try {
777                 debug("toggle_feedlist");
778
779                 var fl = document.getElementById("feeds-holder");
780
781                 if (!Element.visible(fl)) {
782                         Element.show(fl);
783                         fl.style.zIndex = 30;
784                         fl.scrollTop = _hfd_scrolltop;
785                 } else {
786                         _hfd_scrolltop = fl.scrollTop;
787                         Element.hide(fl);                       
788 //                      Effect.Fade(fl, {duration : 0.2, 
789 //                              queue: { position: 'end', scope: 'FLFADEQ', limit: 1 }});
790                 }
791         } catch (e) {
792                 exception_error("toggle_feedlist", e);
793         }
794 } */
795
796 function collapse_feedlist() {
797         try {
798                 debug("toggle_feedlist");
799                 
800                 var theme = getInitParam("theme");
801                 if (theme != "" && theme != "compact" && theme != "graycube" &&
802                                 theme != "compat") return;
803
804                 var fl = document.getElementById("feeds-holder");
805                 var fh = document.getElementById("headlines-frame");
806                 var fc = document.getElementById("content-frame");
807                 var ft = document.getElementById("toolbar");
808                 var ff = document.getElementById("footer");
809                 var fhdr = document.getElementById("header");
810                 var fbtn = document.getElementById("collapse_feeds_btn");
811
812                 if (!Element.visible(fl)) {
813                         Element.show(fl);
814                         fbtn.value = "<<";
815
816                         if (theme != "graycube") {
817
818                                 fh.style.left = fl.offsetWidth + "px";
819                                 ft.style.left = fl.offsetWidth + "px";
820                                 if (fc) fc.style.left = fl.offsetWidth + "px";
821                                 if (ff && theme != "compat") ff.style.left = (fl.offsetWidth-1) + "px";
822
823                                 if (theme == "compact") fhdr.style.left = (fl.offsetWidth + 10) + "px";
824                         } else {
825                                 fh.style.left = fl.offsetWidth + 40 + "px";
826                                 ft.style.left = fl.offsetWidth + 40 +"px";
827                                 if (fc) fc.style.left = fl.offsetWidth + 40 + "px";
828                         }
829
830                         setCookie("ttrss_vf_fclps", "0");
831
832                 } else {
833                         Element.hide(fl);
834                         fbtn.value = ">>";
835
836                         if (theme != "graycube") {
837
838                                 fh.style.left = "0px";
839                                 ft.style.left = "0px";
840                                 if (fc) fc.style.left = "0px";
841                                 if (ff) ff.style.left = "0px";
842
843                                 if (theme == "compact") fhdr.style.left = "10px";
844
845                         } else {
846                                 fh.style.left = "20px";
847                                 ft.style.left = "20px";
848                                 if (fc) fc.style.left = "20px";
849
850                         }
851
852                         setCookie("ttrss_vf_fclps", "1");
853                 }
854         } catch (e) {
855                 exception_error("toggle_feedlist", e);
856         }
857 }
858
859 function viewModeChanged() {
860         cache_empty();
861         return viewCurrentFeed(0, '')
862 }
863
864 function viewLimitChanged() {
865         cache_empty();
866         return viewCurrentFeed(0, '')
867 }
868
869 function adjustArticleScore(id, score) {
870         try {
871
872                 var pr = prompt(__("Assign score to article:"), score);
873
874                 if (pr != undefined) {
875                         var query = "backend.php?op=rpc&subop=setScore&id=" + id + "&score=" + pr;
876
877                         new Ajax.Request(query, {
878                         onComplete: function(transport) {
879                                         viewCurrentFeed();
880                                 } });
881
882                 }
883         } catch (e) {
884                 exception_error("adjustArticleScore", e);
885         }
886 }       
887
888 function rescoreCurrentFeed() {
889
890         var actid = getActiveFeedId();
891
892         if (activeFeedIsCat() || actid < 0 || tagsAreDisplayed()) {
893                 alert(__("You can't rescore this kind of feed."));
894                 return;
895         }       
896
897         if (!actid) {
898                 alert(__("Please select some feed first."));
899                 return;
900         }
901
902         var fn = getFeedName(actid);
903         var pr = __("Rescore articles in %s?").replace("%s", fn);
904
905         if (confirm(pr)) {
906                 notify_progress("Rescoring articles...");
907
908                 var query = "backend.php?op=pref-feeds&subop=rescore&quiet=1&ids=" + actid;
909
910                 new Ajax.Request(query, {
911                 onComplete: function(transport) {
912                         viewCurrentFeed();
913                 } });
914         }
915 }
916
917 function hotkey_handler(e) {
918
919         try {
920
921                 var keycode;
922                 var shift_key = false;
923
924                 var feedlist = document.getElementById('feedList');
925
926                 try {
927                         shift_key = e.shiftKey;
928                 } catch (e) {
929
930                 }
931         
932                 if (window.event) {
933                         keycode = window.event.keyCode;
934                 } else if (e) {
935                         keycode = e.which;
936                 }
937
938                 var keychar = String.fromCharCode(keycode);
939
940                 if (keycode == 27) { // escape
941                         if (Element.visible("hotkey_help_overlay")) {
942                                 Element.hide("hotkey_help_overlay");
943                         }
944                         hotkey_prefix = false;
945                         closeInfoBox();
946                 } 
947
948                 if (!hotkeys_enabled) {
949                         debug("hotkeys disabled");
950                         return;
951                 }
952
953                 if (keycode == 16) return; // ignore lone shift
954
955                 if ((keycode == 70 || keycode == 67 || keycode == 71) && !hotkey_prefix) {
956                         hotkey_prefix = keycode;
957                         debug("KP: PREFIX=" + keycode + " CHAR=" + keychar);
958                         return;
959                 }
960
961                 if (Element.visible("hotkey_help_overlay")) {
962                         Element.hide("hotkey_help_overlay");
963                 }
964
965                 /* Global hotkeys */
966
967                 if (!hotkey_prefix) {
968
969                         if (keycode == 68 && shift_key) { // d
970                                 if (!debug_mode_enabled) {
971                                         document.getElementById('debug_output').style.display = 'block';
972                                         debug('debug mode activated');
973                                 } else {
974                                         document.getElementById('debug_output').style.display = 'none';
975                                 }
976         
977                                 return;
978                         }
979         
980                         if ((keycode == 191 || keychar == '?') && shift_key) { // ?
981                                 if (!Element.visible("hotkey_help_overlay")) {
982                                         //Element.show("hotkey_help_overlay");
983                                         Effect.Appear("hotkey_help_overlay", {duration : 0.3});
984                                 } else {
985                                         Element.hide("hotkey_help_overlay");
986                                 }
987                                 return false;
988                         }
989         
990                         if (keycode == 191 || keychar == '/') { // /
991                                 displayDlg("search", getActiveFeedId() + ":" + activeFeedIsCat());
992                                 return false;
993                         }
994
995                         if (keycode == 82 && shift_key) { // R
996                                 scheduleFeedUpdate(true);
997                                 return;
998                         }
999
1000                         if (keycode == 82) { // r
1001                                 if (getActiveFeedId()) {
1002                                         viewfeed(getActiveFeedId(), "ForceUpdate", activeFeedIsCat());
1003                                         return;
1004                                 }
1005                         }
1006
1007                         if (keycode == 74) { // j
1008                                 var feed = getActiveFeedId();
1009                                 var new_feed = getRelativeFeedId(feedlist, feed, 'prev');
1010                                 if (new_feed) viewfeed(new_feed, '');
1011                                 return;
1012                         }
1013         
1014                         if (keycode == 75) { // k
1015                                 var feed = getActiveFeedId();
1016                                 var new_feed = getRelativeFeedId(feedlist, feed, 'next');
1017                                 if (new_feed) viewfeed(new_feed, '');
1018                                 return;
1019                         }
1020
1021                         if (shift_key && (keycode == 78 || keycode == 40)) { // shift - n, down
1022                                 catchupRelativeToArticle(1);
1023                                 return;
1024                         }
1025
1026                         if (shift_key && (keycode == 80 || keycode == 38)) { // shift - p, up
1027                                 catchupRelativeToArticle(0);
1028                                 return;
1029                         }
1030
1031                         if (keycode == 78 || keycode == 40) { // n, down
1032                                 if (typeof moveToPost != 'undefined') {
1033                                         moveToPost('next');
1034                                         return;
1035                                 }
1036                         }
1037         
1038                         if (keycode == 80 || keycode == 38) { // p, up
1039                                 if (typeof moveToPost != 'undefined') {
1040                                         moveToPost('prev');
1041                                         return;
1042                                 }
1043                         }
1044
1045                         if (keycode == 83 && shift_key) { // S
1046                                 var id = getActiveArticleId();
1047                                 if (id) {                               
1048                                         togglePub(id);
1049                                 }
1050                                 return;
1051                         }
1052
1053                         if (keycode == 83) { // s
1054                                 var id = getActiveArticleId();
1055                                 if (id) {                               
1056                                         toggleMark(id);
1057                                 }
1058                                 return;
1059                         }
1060
1061
1062                         if (keycode == 85) { // u
1063                                 var id = getActiveArticleId();
1064                                 if (id) {                               
1065                                         toggleUnread(id);
1066                                 }
1067                                 return;
1068                         }
1069
1070                         if (keycode == 84 && shift_key) { // T
1071                                 var id = getActiveArticleId();
1072                                 if (id) {
1073                                         editArticleTags(id, getActiveFeedId(), isCdmMode());
1074                                         return;
1075                                 }
1076                         }
1077
1078                         if (keycode == 9) { // tab
1079                                 var id = getArticleUnderPointer();
1080                                 if (id) {                               
1081                                         var cb = document.getElementById("RCHK-" + id);
1082
1083                                         if (cb) {
1084                                                 cb.checked = !cb.checked;
1085                                                 toggleSelectRowById(cb, "RROW-" + id);
1086                                                 return false;
1087                                         }
1088                                 }
1089                         }
1090
1091                         if (keycode == 79) { // o
1092                                 if (getActiveArticleId()) {
1093                                         openArticleInNewWindow(getActiveArticleId());
1094                                         return;
1095                                 }
1096                         }
1097
1098                         if (keycode == 81 && shift_key) { // Q
1099                                 if (typeof catchupAllFeeds != 'undefined') {
1100                                         catchupAllFeeds();
1101                                         return;
1102                                 }
1103                         }
1104
1105                         if (keycode == 81) { // q
1106                                 if (getActiveFeedId()) {
1107                                         catchupCurrentFeed();
1108                                         return;
1109                                 }
1110                         }
1111
1112                         if (keycode == 220 && shift_key) { // shift + |
1113                                 if (document.getElementById("subtoolbar_search")) {
1114                                         if (Element.visible("subtoolbar_search")) {
1115                                                 Element.hide("subtoolbar_search");
1116                                                 Element.show("subtoolbar_ftitle");
1117                                                 setTimeout("Element.focus('subtoolbar_search_box')", 100);
1118                                         } else {
1119                                                         Element.show("subtoolbar_search");
1120                                                 Element.hide("subtoolbar_ftitle");
1121                                         }
1122                                 }
1123                         }
1124                 }
1125
1126                 /* Prefix f */
1127
1128                 if (hotkey_prefix == 70) { // f 
1129
1130                         hotkey_prefix = false;
1131
1132                         if (keycode == 65) { // a
1133                                 toggleDispRead();
1134                                 return false;
1135                         }
1136
1137                         if (keycode == 85 && shift_key) { // U
1138                                 scheduleFeedUpdate(true);
1139                                 return false;
1140                         }
1141
1142                         if (keycode == 85) { // u
1143                                 if (getActiveFeedId()) {
1144                                         viewfeed(getActiveFeedId(), "ForceUpdate");
1145                                         return false;
1146                                 }
1147                         }
1148
1149                         if (keycode == 69) { // e
1150                                 editFeedDlg(getActiveFeedId());
1151                                 return false;
1152                         }
1153
1154                         if (keycode == 83) { // s
1155                                 displayDlg("quickAddFeed");
1156                                 return false;
1157                         }
1158
1159                         if (keycode == 67 && shift_key) { // C
1160                                 if (typeof catchupAllFeeds != 'undefined') {
1161                                         catchupAllFeeds();
1162                                         return false;
1163                                 }
1164                         }
1165
1166                         if (keycode == 67) { // c
1167                                 if (getActiveFeedId()) {
1168                                         catchupCurrentFeed();
1169                                         return false;
1170                                 }
1171                         }
1172
1173                 }
1174
1175                 /* Prefix c */
1176
1177                 if (hotkey_prefix == 67) { // c
1178                         hotkey_prefix = false;
1179
1180                         if (keycode == 70) { // f
1181                                 displayDlg("quickAddFilter", getActiveFeedId());
1182                                 return false;
1183                         }
1184
1185                         if (keycode == 83) { // s
1186                                 if (typeof collapse_feedlist != 'undefined') {
1187                                         collapse_feedlist();
1188                                         return false;
1189                                 }
1190                         }
1191
1192                 }
1193
1194                 /* Prefix g */
1195
1196                 if (hotkey_prefix == 71) { // g
1197
1198                         hotkey_prefix = false;
1199
1200                         if (keycode == 83) { // s
1201                                 viewfeed(-1);
1202                                 return false;
1203                         }
1204
1205                         if (keycode == 80 && shift_key) { // P
1206                                 gotoPreferences();
1207                                 return false;
1208                         }
1209
1210                         if (keycode == 80) { // p
1211                                 viewfeed(-2);
1212                                 return false;
1213                         }
1214
1215                         if (keycode == 70) { // f
1216                                 viewfeed(-3);
1217                                 return false;
1218                         }
1219
1220                         if (keycode == 84 && shift_key) { // T
1221                                 toggleTags();
1222                                 return false;
1223                         }
1224
1225                 }
1226
1227 /*
1228                 if (keycode == 48) { // 0
1229                         return setHotkeyZone(0);
1230                 }
1231
1232                 if (keycode == 49) { // 1
1233                         return setHotkeyZone(1);
1234                 }
1235
1236                 if (keycode == 50) { // 2
1237                         return setHotkeyZone(2);
1238                 }
1239
1240                 if (keycode == 51) { // 3
1241                         return setHotkeyZone(3);
1242                 }
1243
1244                 if (keycode == 82) { // r
1245                         return scheduleFeedUpdate(true);
1246                 }
1247
1248                 if (keycode == 83) { // s
1249                         return displayDlg("search", getActiveFeedId());
1250                 }
1251
1252                 if (keycode == 85) { // u
1253                         if (getActiveFeedId()) {
1254                                 return viewfeed(getActiveFeedId(), "ForceUpdate");
1255                         }
1256                 }
1257         
1258                 if (keycode == 65) { // a
1259                         return toggleDispRead();
1260                 }
1261         
1262                 var feedlist = document.getElementById('feedList');
1263         
1264                 if (keycode == 74) { // j
1265                         var feed = getActiveFeedId();
1266                         var new_feed = getRelativeFeedId(feedlist, feed, 'prev');
1267                         if (new_feed) viewfeed(new_feed, '');
1268                 }
1269         
1270                 if (keycode == 75) { // k
1271                         var feed = getActiveFeedId();
1272                         var new_feed = getRelativeFeedId(feedlist, feed, 'next');
1273                         if (new_feed) viewfeed(new_feed, '');
1274                 }
1275
1276                 if (shift_key && (keycode == 78 || keycode == 40)) { // shift - n, down
1277                         return catchupRelativeToArticle(1);
1278                 }
1279
1280                 if (shift_key && (keycode == 80 || keycode == 38)) { // shift - p, up
1281                         return catchupRelativeToArticle(0);                     
1282                 }
1283
1284                 if (keycode == 78 || keycode == 40) { // n, down
1285                         if (typeof moveToPost != 'undefined') {
1286                                 return moveToPost('next');
1287                         }
1288                 }
1289         
1290                 if (keycode == 80 || keycode == 38) { // p, up
1291                         if (typeof moveToPost != 'undefined') {
1292                                 return moveToPost('prev');
1293                         }
1294                 }
1295                 
1296                 if (keycode == 68 && shift_key) { // d
1297                         if (!debug_mode_enabled) {
1298                                 document.getElementById('debug_output').style.display = 'block';
1299                                 debug('debug mode activated');
1300                         } else {
1301                                 document.getElementById('debug_output').style.display = 'none';
1302                         }
1303
1304                         debug_mode_enabled = !debug_mode_enabled;
1305                 }
1306
1307                 if (keycode == 191 && shift_key) { // ?
1308                         if (!Element.visible("hotkey_help_overlay")) {
1309                                 Element.show("hotkey_help_overlay");
1310                         } else {
1311                                 Element.hide("hotkey_help_overlay");
1312                         }
1313                 }
1314
1315                 if (keycode == 69 && shift_key) { // e
1316                         return editFeedDlg(getActiveFeedId());
1317                 }
1318
1319                 if (keycode == 70 && shift_key) { // f
1320                         if (getActiveFeedId()) {
1321                                 return catchupCurrentFeed();
1322                         }
1323                 }
1324
1325                 if (keycode == 80 && shift_key) { // p 
1326                         if (getActiveFeedId()) {
1327                                 return catchupPage();
1328                         }
1329                 }
1330
1331                 if (keycode == 86) { // v
1332                         if (getActiveArticleId()) {
1333                                 openArticleInNewWindow(getActiveArticleId());
1334                         }
1335                 }
1336
1337                 if (keycode == 84) { // t
1338
1339                         var id = getActiveArticleId();
1340
1341                         if (id) {                               
1342
1343                                 var cb = document.getElementById("RCHK-" + id);
1344
1345                                 if (cb) {
1346                                         cb.checked = !cb.checked;
1347                                         toggleSelectRowById(cb, "RROW-" + id);
1348                                 }
1349                         }
1350                 }
1351
1352                 if (keycode == 67) { // c
1353                         var id = getActiveArticleId();
1354
1355                         if (id) {                               
1356                                 toggleUnread(id, 0);
1357                         }
1358                 }
1359
1360                 if (keycode == 67 && shift_key) { // c
1361                         if (typeof collapse_feedlist != 'undefined') {
1362                                 return collapse_feedlist();
1363                         }
1364                 }
1365
1366                 if (keycode == 81 && shift_key) { // shift + q
1367                         if (typeof catchupAllFeeds != 'undefined') {
1368                                 return catchupAllFeeds();
1369                         }
1370                 }
1371
1372                 if (keycode == 73 && shift_key) { // shift + i
1373                         if (document.getElementById("subtoolbar_search")) {
1374                                 if (Element.visible("subtoolbar_search")) {
1375                                         Element.hide("subtoolbar_search");
1376                                         Element.show("subtoolbar_ftitle");
1377                                         setTimeout("Element.focus('subtoolbar_search_box')", 100);
1378                                 } else {
1379                                         Element.show("subtoolbar_search");
1380                                         Element.hide("subtoolbar_ftitle");
1381                                 }
1382                         }
1383                 }
1384
1385                 if (keycode == 27) { // escape
1386                         if (Element.visible("hotkey_help_overlay")) {
1387                                 Element.hide("hotkey_help_overlay");
1388                         }
1389                 } 
1390
1391                 if (typeof localHotkeyHandler != 'undefined') {
1392                         try {
1393                                 return localHotkeyHandler(e);
1394                         } catch (e) {
1395                                 exception_error("hotkey_handler, local:", e);
1396                         }
1397                 } */
1398
1399                 if (hotkey_prefix) {
1400                         debug("KP: PREFIX=" + hotkey_prefix + " CODE=" + keycode + " CHAR=" + keychar);
1401                 } else {
1402                         debug("KP: CODE=" + keycode + " CHAR=" + keychar);
1403                 }
1404
1405
1406         } catch (e) {
1407                 exception_error("hotkey_handler", e);
1408         }
1409 }
1410
1411 function mouse_handler(e) {
1412         try {
1413                 var r_mouse = false;
1414
1415                 if (window.event) {
1416                         r_mouse = window.event.button == 2;
1417                 } else if (e) {
1418                         r_mouse = e.which == 3;
1419                 }
1420
1421         } catch (e) {
1422                 exception_error("mouse_handler", e);
1423         }
1424 }