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