]> git.wh0rd.org Git - tt-rss.git/blob - js/tt-rss.js
switchPanelMode: fix css manipulation for firefox
[tt-rss.git] / js / tt-rss.js
1 var total_unread = 0;
2 var global_unread = -1;
3 var firsttime_update = true;
4 var _active_feed_id = undefined;
5 var _active_feed_is_cat = false;
6 var hotkey_prefix = false;
7 var hotkey_prefix_pressed = false;
8 var _force_scheduled_update = false;
9 var last_scheduled_update = false;
10 var _widescreen_mode = false;
11
12 var _rpc_seq = 0;
13
14 function next_seq() {
15         _rpc_seq += 1;
16         return _rpc_seq;
17 }
18
19 function get_seq() {
20         return _rpc_seq;
21 }
22
23 function activeFeedIsCat() {
24         return _active_feed_is_cat;
25 }
26
27 function getActiveFeedId() {
28         try {
29                 //console.log("gAFID: " + _active_feed_id);
30                 return _active_feed_id;
31         } catch (e) {
32                 exception_error("getActiveFeedId", e);
33         }
34 }
35
36 function setActiveFeedId(id, is_cat) {
37         try {
38                 _active_feed_id = id;
39
40                 if (is_cat != undefined) {
41                         _active_feed_is_cat = is_cat;
42                 }
43
44                 selectFeed(id, is_cat);
45         } catch (e) {
46                 exception_error("setActiveFeedId", e);
47         }
48 }
49
50
51 function updateFeedList() {
52         try {
53
54 //              $("feeds-holder").innerHTML = "<div id=\"feedlistLoading\">" +
55 //                      __("Loading, please wait...") + "</div>";
56
57                 Element.show("feedlistLoading");
58
59                 if (dijit.byId("feedTree")) {
60                         dijit.byId("feedTree").destroyRecursive();
61                 }
62
63                 var store = new dojo.data.ItemFileWriteStore({
64          url: "backend.php?op=pref_feeds&method=getfeedtree&mode=2"});
65
66                 var treeModel = new fox.FeedStoreModel({
67                         store: store,
68                         query: {
69                                 "type": getInitParam('enable_feed_cats') == 1 ? "category" : "feed"
70                         },
71                         rootId: "root",
72                         rootLabel: "Feeds",
73                         childrenAttrs: ["items"]
74                 });
75
76                 var tree = new fox.FeedTree({
77                 persist: false,
78                 model: treeModel,
79                 onOpen: function (item, node) {
80                         var id = String(item.id);
81                         var cat_id = id.substr(id.indexOf(":")+1);
82
83                         new Ajax.Request("backend.php",
84                                 { parameters: "backend.php?op=feeds&method=collapse&cid=" +
85                                         param_escape(cat_id) + "&mode=0" } );
86            },
87                 onClose: function (item, node) {
88                         var id = String(item.id);
89                         var cat_id = id.substr(id.indexOf(":")+1);
90
91                         new Ajax.Request("backend.php",
92                                 { parameters: "backend.php?op=feeds&method=collapse&cid=" +
93                                         param_escape(cat_id) + "&mode=1" } );
94
95            },
96                 onClick: function (item, node) {
97                         var id = String(item.id);
98                         var is_cat = id.match("^CAT:");
99                         var feed = id.substr(id.indexOf(":")+1);
100                         viewfeed(feed, '', is_cat);
101                         return false;
102                 },
103                 openOnClick: false,
104                 showRoot: false,
105                 id: "feedTree",
106                 }, "feedTree");
107
108                 _force_scheduled_update = true;
109
110 /*              var menu = new dijit.Menu({id: 'feedMenu'});
111
112                 menu.addChild(new dijit.MenuItem({
113           label: "Simple menu item"
114                 }));
115
116 //              menu.bindDomNode(tree.domNode); */
117
118                 var tmph = dojo.connect(dijit.byId('feedMenu'), '_openMyself', function (event) {
119                         console.log(dijit.getEnclosingWidget(event.target));
120                         dojo.disconnect(tmph);
121                 });
122
123                 $("feeds-holder").appendChild(tree.domNode);
124
125                 var tmph = dojo.connect(tree, 'onLoad', function() {
126                 dojo.disconnect(tmph);
127                         Element.hide("feedlistLoading");
128
129                         tree.collapseHiddenCats();
130
131                         feedlist_init();
132
133 //                      var node = dijit.byId("feedTree")._itemNodesMap['FEED:-2'][0].domNode
134 //                      menu.bindDomNode(node);
135
136                         loading_set_progress(25);
137                 });
138
139                 tree.startup();
140
141         } catch (e) {
142                 exception_error("updateFeedList", e);
143         }
144 }
145
146 function catchupAllFeeds() {
147
148         var str = __("Mark all articles as read?");
149
150         if (getInitParam("confirm_feed_catchup") != 1 || confirm(str)) {
151
152                 var query_str = "backend.php?op=feeds&method=catchupAll";
153
154                 notify_progress("Marking all feeds as read...");
155
156                 //console.log("catchupAllFeeds Q=" + query_str);
157
158                 new Ajax.Request("backend.php", {
159                         parameters: query_str,
160                         onComplete: function(transport) {
161                                 feedlist_callback2(transport);
162                         } });
163
164                 global_unread = 0;
165                 updateTitle("");
166         }
167 }
168
169 function viewCurrentFeed(method) {
170         console.log("viewCurrentFeed");
171
172         if (getActiveFeedId() != undefined) {
173                 viewfeed(getActiveFeedId(), method, activeFeedIsCat());
174         }
175         return false; // block unneeded form submits
176 }
177
178 function timeout() {
179         if (getInitParam("bw_limit") == "1") return;
180
181         try {
182            var date = new Date();
183       var ts = Math.round(date.getTime() / 1000);
184
185                 if (ts - last_scheduled_update > 10 || _force_scheduled_update) {
186
187                         //console.log("timeout()");
188
189                         window.clearTimeout(counter_timeout_id);
190
191                         var query_str = "?op=rpc&method=getAllCounters&seq=" + next_seq();
192
193                         var omode;
194
195                         if (firsttime_update && !navigator.userAgent.match("Opera")) {
196                                 firsttime_update = false;
197                                 omode = "T";
198                         } else {
199                                 omode = "flc";
200                         }
201
202                         query_str = query_str + "&omode=" + omode;
203
204                         if (!_force_scheduled_update)
205                                 query_str = query_str + "&last_article_id=" + getInitParam("last_article_id");
206
207                         //console.log("[timeout]" + query_str);
208
209                         new Ajax.Request("backend.php", {
210                                 parameters: query_str,
211                                 onComplete: function(transport) {
212                                                 handle_rpc_json(transport, !_force_scheduled_update);
213                                                 _force_scheduled_update = false;
214                                         } });
215
216                         last_scheduled_update = ts;
217                 }
218
219         } catch (e) {
220                 exception_error("timeout", e);
221         }
222
223         setTimeout("timeout()", 3000);
224 }
225
226 function search() {
227         var query = "backend.php?op=dlg&method=search&param=" +
228                 param_escape(getActiveFeedId() + ":" + activeFeedIsCat());
229
230         if (dijit.byId("searchDlg"))
231                 dijit.byId("searchDlg").destroyRecursive();
232
233         dialog = new dijit.Dialog({
234                 id: "searchDlg",
235                 title: __("Search"),
236                 style: "width: 600px",
237                 execute: function() {
238                         if (this.validate()) {
239                                 _search_query = dojo.objectToQuery(this.attr('value'));
240                                 this.hide();
241                                 viewCurrentFeed();
242                         }
243                 },
244                 href: query});
245
246         dialog.show();
247 }
248
249 function updateTitle() {
250         var tmp = "Tiny Tiny RSS";
251
252         if (global_unread > 0) {
253                 tmp = tmp + " (" + global_unread + ")";
254         }
255
256         if (window.fluid) {
257                 if (global_unread > 0) {
258                         window.fluid.dockBadge = global_unread;
259                 } else {
260                         window.fluid.dockBadge = "";
261                 }
262         }
263
264         document.title = tmp;
265 }
266
267 function genericSanityCheck() {
268         setCookie("ttrss_test", "TEST");
269
270         if (getCookie("ttrss_test") != "TEST") {
271                 return fatalError(2);
272         }
273
274         return true;
275 }
276
277 function init() {
278         try {
279                 //dojo.registerModulePath("fox", "../../js/");
280
281                 dojo.require("fox.FeedTree");
282
283                 if (typeof themeBeforeLayout == 'function') {
284                         themeBeforeLayout();
285                 }
286
287                 dojo.require("dijit.ColorPalette");
288                 dojo.require("dijit.Dialog");
289                 dojo.require("dijit.form.Button");
290                 dojo.require("dijit.form.CheckBox");
291                 dojo.require("dijit.form.DropDownButton");
292                 dojo.require("dijit.form.FilteringSelect");
293                 dojo.require("dijit.form.Form");
294                 dojo.require("dijit.form.RadioButton");
295                 dojo.require("dijit.form.Select");
296                 dojo.require("dijit.form.SimpleTextarea");
297                 dojo.require("dijit.form.TextBox");
298                 dojo.require("dijit.form.ValidationTextBox");
299                 dojo.require("dijit.InlineEditBox");
300                 dojo.require("dijit.layout.AccordionContainer");
301                 dojo.require("dijit.layout.BorderContainer");
302                 dojo.require("dijit.layout.ContentPane");
303                 dojo.require("dijit.layout.TabContainer");
304                 dojo.require("dijit.Menu");
305                 dojo.require("dijit.ProgressBar");
306                 dojo.require("dijit.ProgressBar");
307                 dojo.require("dijit.Toolbar");
308                 dojo.require("dijit.Tree");
309                 dojo.require("dijit.tree.dndSource");
310                 dojo.require("dojo.data.ItemFileWriteStore");
311
312                 dojo.parser.parse();
313
314                 if (!genericSanityCheck())
315                         return false;
316
317                 loading_set_progress(20);
318
319                 var hasAudio = !!((myAudioTag = document.createElement('audio')).canPlayType);
320
321                 new Ajax.Request("backend.php", {
322                         parameters: {op: "rpc", method: "sanityCheck", hasAudio: hasAudio},
323                         onComplete: function(transport) {
324                                         backend_sanity_check_callback(transport);
325                                 } });
326
327         } catch (e) {
328                 exception_error("init", e);
329         }
330 }
331
332 function init_second_stage() {
333
334         try {
335                 dojo.addOnLoad(function() {
336                         updateFeedList();
337                         closeArticlePanel();
338
339                         _widescreen_mode = getInitParam("widescreen");
340
341                         if (_widescreen_mode) {
342                                 switchPanelMode(_widescreen_mode);
343                         }
344
345                         if (typeof themeAfterLayout == 'function') {
346                                 themeAfterLayout();
347                         }
348
349                 });
350
351                 delCookie("ttrss_test");
352
353                 var toolbar = document.forms["main_toolbar_form"];
354
355                 dijit.getEnclosingWidget(toolbar.view_mode).attr('value',
356                         getInitParam("default_view_mode"));
357
358                 dijit.getEnclosingWidget(toolbar.order_by).attr('value',
359                         getInitParam("default_view_order_by"));
360
361                 feeds_sort_by_unread = getInitParam("feeds_sort_by_unread") == 1;
362
363                 loading_set_progress(30);
364
365                 // can't use cache_clear() here because viewfeed might not have initialized yet
366                 if ('sessionStorage' in window && window['sessionStorage'] !== null)
367                         sessionStorage.clear();
368
369                 var hotkeys = getInitParam("hotkeys");
370                 var tmp = [];
371
372                 for (sequence in hotkeys[1]) {
373                         filtered = sequence.replace(/\|.*$/, "");
374                         tmp[filtered] = hotkeys[1][sequence];
375                 }
376
377                 hotkeys[1] = tmp;
378                 setInitParam("hotkeys", hotkeys);
379
380                 console.log("second stage ok");
381
382                 if (getInitParam("simple_update")) {
383                         console.log("scheduling simple feed updater...");
384                         window.setTimeout("update_random_feed()", 30*1000);
385                 }
386
387         } catch (e) {
388                 exception_error("init_second_stage", e);
389         }
390 }
391
392 function quickMenuGo(opid) {
393         try {
394                 if (opid == "qmcPrefs") {
395                         gotoPreferences();
396                 }
397
398                 if (opid == "qmcTagCloud") {
399                         displayDlg("printTagCloud");
400                 }
401
402                 if (opid == "qmcTagSelect") {
403                         displayDlg("printTagSelect");
404                 }
405
406                 if (opid == "qmcSearch") {
407                         search();
408                         return;
409                 }
410
411                 if (opid == "qmcAddFeed") {
412                         quickAddFeed();
413                         return;
414                 }
415
416                 if (opid == "qmcDigest") {
417                         window.location.href = "backend.php?op=digest";
418                         return;
419                 }
420
421                 if (opid == "qmcEditFeed") {
422                         if (activeFeedIsCat())
423                                 alert(__("You can't edit this kind of feed."));
424                         else
425                                 editFeed(getActiveFeedId());
426                         return;
427                 }
428
429                 if (opid == "qmcRemoveFeed") {
430                         var actid = getActiveFeedId();
431
432                         if (activeFeedIsCat()) {
433                                 alert(__("You can't unsubscribe from the category."));
434                                 return;
435                         }
436
437                         if (!actid) {
438                                 alert(__("Please select some feed first."));
439                                 return;
440                         }
441
442                         var fn = getFeedName(actid);
443
444                         var pr = __("Unsubscribe from %s?").replace("%s", fn);
445
446                         if (confirm(pr)) {
447                                 unsubscribeFeed(actid);
448                         }
449
450                         return;
451                 }
452
453                 if (opid == "qmcCatchupAll") {
454                         catchupAllFeeds();
455                         return;
456                 }
457
458                 if (opid == "qmcShowOnlyUnread") {
459                         toggleDispRead();
460                         return;
461                 }
462
463                 if (opid == "qmcAddFilter") {
464                         quickAddFilter();
465                         return;
466                 }
467
468                 if (opid == "qmcAddLabel") {
469                         addLabel();
470                         return;
471                 }
472
473                 if (opid == "qmcRescoreFeed") {
474                         rescoreCurrentFeed();
475                         return;
476                 }
477
478                 if (opid == "qmcToggleWidescreen") {
479                         if (!isCdmMode()) {
480                                 _widescreen_mode = !_widescreen_mode;
481
482                                 switchPanelMode(_widescreen_mode);
483                         }
484                         return;
485                 }
486
487                 if (opid == "qmcHKhelp") {
488                         helpDialog("main");
489                 }
490
491         } catch (e) {
492                 exception_error("quickMenuGo", e);
493         }
494 }
495
496 function toggleDispRead() {
497         try {
498
499                 var hide = !(getInitParam("hide_read_feeds") == "1");
500
501                 hideOrShowFeeds(hide);
502
503                 var query = "?op=rpc&method=setpref&key=HIDE_READ_FEEDS&value=" +
504                         param_escape(hide);
505
506                 setInitParam("hide_read_feeds", hide);
507
508                 new Ajax.Request("backend.php", {
509                         parameters: query,
510                         onComplete: function(transport) {
511                         } });
512
513         } catch (e) {
514                 exception_error("toggleDispRead", e);
515         }
516 }
517
518 function parse_runtime_info(data) {
519
520         //console.log("parsing runtime info...");
521
522         for (k in data) {
523                 var v = data[k];
524
525 //              console.log("RI: " + k + " => " + v);
526
527                 if (k == "new_version_available") {
528                         var icon = $("newVersionIcon");
529                         if (icon) {
530                                 if (v == "1") {
531                                         icon.style.display = "inline";
532                                 } else {
533                                         icon.style.display = "none";
534                                 }
535                         }
536                         return;
537                 }
538
539                 if (k == "daemon_is_running" && v != 1) {
540                         notify_error("<span onclick=\"javascript:explainError(1)\">Update daemon is not running.</span>", true);
541                         return;
542                 }
543
544                 if (k == "daemon_stamp_ok" && v != 1) {
545                         notify_error("<span onclick=\"javascript:explainError(3)\">Update daemon is not updating feeds.</span>", true);
546                         return;
547                 }
548
549                 if (k == "max_feed_id" || k == "num_feeds") {
550                         if (init_params[k] != v) {
551                                 console.log("feed count changed, need to reload feedlist.");
552                                 updateFeedList();
553                         }
554                 }
555
556                 init_params[k] = v;
557                 notify('');
558         }
559 }
560
561 function collapse_feedlist() {
562         try {
563
564                 if (!Element.visible('feeds-holder')) {
565                         Element.show('feeds-holder');
566                         Element.show('feeds-holder_splitter');
567                         $("collapse_feeds_btn").innerHTML = "&lt;&lt;";
568                 } else {
569                         Element.hide('feeds-holder');
570                         Element.hide('feeds-holder_splitter');
571                         $("collapse_feeds_btn").innerHTML = "&gt;&gt;";
572                 }
573
574                 dijit.byId("main").resize();
575
576                 query = "?op=rpc&method=setpref&key=_COLLAPSED_FEEDLIST&value=true";
577                 new Ajax.Request("backend.php", { parameters: query });
578
579         } catch (e) {
580                 exception_error("collapse_feedlist", e);
581         }
582 }
583
584 function viewModeChanged() {
585         cache_clear();
586         return viewCurrentFeed('');
587 }
588
589 function viewLimitChanged() {
590         return viewCurrentFeed('');
591 }
592
593 /* function adjustArticleScore(id, score) {
594         try {
595
596                 var pr = prompt(__("Assign score to article:"), score);
597
598                 if (pr != undefined) {
599                         var query = "?op=rpc&method=setScore&id=" + id + "&score=" + pr;
600
601                         new Ajax.Request("backend.php", {
602                         parameters: query,
603                         onComplete: function(transport) {
604                                         viewCurrentFeed();
605                                 } });
606
607                 }
608         } catch (e) {
609                 exception_error("adjustArticleScore", e);
610         }
611 } */
612
613 function rescoreCurrentFeed() {
614
615         var actid = getActiveFeedId();
616
617         if (activeFeedIsCat() || actid < 0) {
618                 alert(__("You can't rescore this kind of feed."));
619                 return;
620         }
621
622         if (!actid) {
623                 alert(__("Please select some feed first."));
624                 return;
625         }
626
627         var fn = getFeedName(actid);
628         var pr = __("Rescore articles in %s?").replace("%s", fn);
629
630         if (confirm(pr)) {
631                 notify_progress("Rescoring articles...");
632
633                 var query = "?op=pref-feeds&method=rescore&quiet=1&ids=" + actid;
634
635                 new Ajax.Request("backend.php", {
636                         parameters: query,
637                         onComplete: function(transport) {
638                                 viewCurrentFeed();
639                         } });
640         }
641 }
642
643 function hotkey_handler(e) {
644         try {
645
646                 if (e.target.nodeName == "INPUT" || e.target.nodeName == "TEXTAREA") return;
647
648                 var keycode = false;
649                 var shift_key = false;
650
651                 var cmdline = $('cmdline');
652
653                 shift_key = e.shiftKey;
654                 ctrl_key = e.ctrlKey;
655
656                 if (window.event) {
657                         keycode = window.event.keyCode;
658                 } else if (e) {
659                         keycode = e.which;
660                 }
661
662                 var keychar = String.fromCharCode(keycode);
663
664                 if (keycode == 27) { // escape
665                         hotkey_prefix = false;
666                 }
667
668                 if (keycode == 16) return; // ignore lone shift
669                 if (keycode == 17) return; // ignore lone ctrl
670
671                 if (!shift_key) keychar = keychar.toLowerCase();
672
673                 var hotkeys = getInitParam("hotkeys");
674
675                 if (!hotkey_prefix && hotkeys[0].indexOf(keychar) != -1) {
676
677                         var date = new Date();
678                         var ts = Math.round(date.getTime() / 1000);
679
680                         hotkey_prefix = keychar;
681                         hotkey_prefix_pressed = ts;
682
683                         cmdline.innerHTML = keychar;
684                         Element.show(cmdline);
685
686                         return true;
687                 }
688
689                 Element.hide(cmdline);
690
691                 var hotkey = keychar.search(/[a-zA-Z0-9]/) != -1 ? keychar : "(" + keycode + ")";
692                 if (ctrl_key) hotkey = "^" + hotkey;
693                 hotkey = hotkey_prefix ? hotkey_prefix + " " + hotkey : hotkey;
694                 hotkey_prefix = false;
695
696                 var hotkey_action = false;
697                 var hotkeys = getInitParam("hotkeys");
698
699                 for (sequence in hotkeys[1]) {
700                         if (sequence == hotkey) {
701                                 hotkey_action = hotkeys[1][sequence];
702                                 break;
703                         }
704                 }
705
706                 switch (hotkey_action) {
707                 case "next_feed":
708                         var rv = dijit.byId("feedTree").getNextFeed(
709                                         getActiveFeedId(), activeFeedIsCat());
710
711                         if (rv) viewfeed(rv[0], '', rv[1]);
712                         return false;
713                 case "prev_feed":
714                         var rv = dijit.byId("feedTree").getPreviousFeed(
715                                         getActiveFeedId(), activeFeedIsCat());
716
717                         if (rv) viewfeed(rv[0], '', rv[1]);
718                         return false;
719                 case "next_article":
720                         moveToPost('next');
721                         return false;
722                 case "prev_article":
723                         moveToPost('prev');
724                         return false;
725                 case "search_dialog":
726                         search();
727                         return ;
728                 case "toggle_mark":
729                         selectionToggleMarked(undefined, false, true);
730                         return false;
731                 case "toggle_publ":
732                         selectionTogglePublished(undefined, false, true);
733                         return false;
734                 case "toggle_unread":
735                         selectionToggleUnread(undefined, false, true);
736                         return false;
737                 case "edit_tags":
738                         var id = getActiveArticleId();
739                         if (id) {
740                                 editArticleTags(id, getActiveFeedId(), isCdmMode());
741                                 return;
742                         }
743                         return false;
744                 case "dismiss_selected":
745                         dismissSelectedArticles();
746                         return false;
747                 case "dismiss_read":
748                         return false;
749                 case "open_in_new_window":
750                         if (getActiveArticleId()) {
751                                 openArticleInNewWindow(getActiveArticleId());
752                                 return;
753                         }
754                         return false;
755                 case "catchup_below":
756                         catchupRelativeToArticle(1);
757                         return false;
758                 case "catchup_above":
759                         catchupRelativeToArticle(0);
760                         return false;
761                 case "article_scroll_down":
762                         scrollArticle(50);
763                         return false;
764                 case "article_scroll_up":
765                         scrollArticle(-50);
766                         return false;
767                 case "close_article":
768                         closeArticlePanel();
769                         return false;
770                 case "email_article":
771                         if (typeof emailArticle != "undefined") {
772                                 emailArticle();
773                         } else {
774                                 alert(__("Please enable mail plugin first."));
775                         }
776                         return false;
777                 case "select_all":
778                         selectArticles('all');
779                         return false;
780                 case "select_unread":
781                         selectArticles('unread');
782                         return false;
783                 case "select_marked":
784                         selectArticles('marked');
785                         return false;
786                 case "select_published":
787                         selectArticles('published');
788                         return false;
789                 case "select_invert":
790                         selectArticles('invert');
791                         return false;
792                 case "select_none":
793                         selectArticles('none');
794                         return false;
795                 case "feed_refresh":
796                         if (getActiveFeedId() != undefined) {
797                                 viewfeed(getActiveFeedId(), '', activeFeedIsCat());
798                                 return;
799                         }
800                         return false;
801                 case "feed_unhide_read":
802                         toggleDispRead();
803                         return false;
804                 case "feed_subscribe":
805                         quickAddFeed();
806                         return false;
807                 case "feed_debug_update":
808                         window.open("backend.php?op=feeds&method=view&feed=" + getActiveFeedId() +
809                                 "&view_mode=adaptive&order_by=default&update=&m=ForceUpdate&cat=" +
810                                 activeFeedIsCat() + "&DevForceUpdate=1&debug=1&xdebug=1&csrf_token=" +
811                                 getInitParam("csrf_token"));
812                         return false;
813                 case "feed_edit":
814                         if (activeFeedIsCat())
815                                 alert(__("You can't edit this kind of feed."));
816                         else
817                                 editFeed(getActiveFeedId());
818                         return false;
819                 case "feed_catchup":
820                         if (getActiveFeedId() != undefined) {
821                                 catchupCurrentFeed();
822                                 return;
823                         }
824                         return false;
825                 case "feed_reverse":
826                         reverseHeadlineOrder();
827                         return false;
828                 case "catchup_all":
829                         catchupAllFeeds();
830                         return false;
831                 case "cat_toggle_collapse":
832                         if (activeFeedIsCat()) {
833                                 dijit.byId("feedTree").collapseCat(getActiveFeedId());
834                                 return;
835                         }
836                         return false;
837                 case "goto_all":
838                         viewfeed(-4);
839                         return false;
840                 case "goto_fresh":
841                         viewfeed(-3);
842                         return false;
843                 case "goto_marked":
844                         viewfeed(-1);
845                         return false;
846                 case "goto_published":
847                         viewfeed(-2);
848                         return false;
849                 case "goto_tagcloud":
850                         displayDlg("printTagCloud");
851                         return false;
852                 case "goto_prefs":
853                         gotoPreferences();
854                         return false;
855                 case "select_article_cursor":
856                         var id = getArticleUnderPointer();
857                         if (id) {
858                                 var cb = dijit.byId("RCHK-" + id);
859                                 if (cb) {
860                                         cb.attr("checked", !cb.attr("checked"));
861                                         toggleSelectRowById(cb, "RROW-" + id);
862                                         return false;
863                                 }
864                         }
865                         return false;
866                 case "create_label":
867                         addLabel();
868                         return false;
869                 case "create_filter":
870                         quickAddFilter();
871                         return false;
872                 case "collapse_sidebar":
873                         collapse_feedlist();
874                         return false;
875                 case "toggle_widescreen":
876                         if (!isCdmMode()) {
877                                 _widescreen_mode = !_widescreen_mode;
878
879                                 switchPanelMode(_widescreen_mode);
880                         }
881                         return false;
882                 case "help_dialog":
883                         helpDialog("main");
884                         return false;
885                 default:
886                         console.log("unhandled action: " + hotkey_action + "; hotkey: " + hotkey);
887                 }
888
889         } catch (e) {
890                 exception_error("hotkey_handler", e);
891         }
892 }
893
894 function inPreferences() {
895         return false;
896 }
897
898 function reverseHeadlineOrder() {
899         try {
900
901                 var query_str = "?op=rpc&method=togglepref&key=REVERSE_HEADLINES";
902
903                 new Ajax.Request("backend.php", {
904                         parameters: query_str,
905                         onComplete: function(transport) {
906                                         viewCurrentFeed();
907                                 } });
908
909         } catch (e) {
910                 exception_error("reverseHeadlineOrder", e);
911         }
912 }
913
914 function newVersionDlg() {
915         try {
916                 var query = "backend.php?op=dlg&method=newVersion";
917
918                 if (dijit.byId("newVersionDlg"))
919                         dijit.byId("newVersionDlg").destroyRecursive();
920
921                 dialog = new dijit.Dialog({
922                         id: "newVersionDlg",
923                         title: __("New version available!"),
924                         style: "width: 600px",
925                         href: query,
926                 });
927
928                 dialog.show();
929
930         } catch (e) {
931                 exception_error("newVersionDlg", e);
932         }
933 }
934
935 function handle_rpc_json(transport, scheduled_call) {
936         try {
937                 var reply = JSON.parse(transport.responseText);
938
939                 if (reply) {
940
941                         var error = reply['error'];
942
943                         if (error) {
944                                 var code = error['code'];
945                                 var msg = error['msg'];
946
947                                 console.warn("[handle_rpc_json] received fatal error " + code + "/" + msg);
948
949                                 if (code != 0) {
950                                         fatalError(code, msg);
951                                         return false;
952                                 }
953                         }
954
955                         var seq = reply['seq'];
956
957                         if (seq) {
958                                 if (get_seq() != seq) {
959                                         console.log("[handle_rpc_json] sequence mismatch: " + seq +
960                                                 " (want: " + get_seq() + ")");
961                                         return true;
962                                 }
963                         }
964
965                         var message = reply['message'];
966
967                         if (message) {
968                                 if (message == "UPDATE_COUNTERS") {
969                                         console.log("need to refresh counters...");
970                                         setInitParam("last_article_id", -1);
971                                         _force_scheduled_update = true;
972                                 }
973                         }
974
975                         var counters = reply['counters'];
976
977                         if (counters)
978                                 parse_counters(counters, scheduled_call);
979
980                         var runtime_info = reply['runtime-info'];;
981
982                         if (runtime_info)
983                                 parse_runtime_info(runtime_info);
984
985                         hideOrShowFeeds(getInitParam("hide_read_feeds") == 1);
986
987                         Element.hide("net-alert");
988
989                 } else {
990                         //notify_error("Error communicating with server.");
991                         Element.show("net-alert");
992                 }
993
994         } catch (e) {
995                 Element.show("net-alert");
996                 //notify_error("Error communicating with server.");
997                 console.log(e);
998                 //exception_error("handle_rpc_json", e, transport);
999         }
1000
1001         return true;
1002 }
1003
1004 function switchPanelMode(wide) {
1005         try {
1006                 article_id = getActiveArticleId();
1007
1008                 if (wide) {
1009                         dijit.byId("headlines-wrap-inner").attr("design", 'sidebar');
1010                         dijit.byId("content-insert").attr("region", "trailing");
1011
1012                         dijit.byId("content-insert").domNode.setStyle({width: '50%',
1013                                 height: 'auto',
1014                                 borderTopWidth: '0px' });
1015
1016                         $("headlines-toolbar").setStyle({ borderBottomWidth: '0px' });
1017
1018                 } else {
1019
1020                         dijit.byId("content-insert").attr("region", "bottom");
1021
1022                         dijit.byId("content-insert").domNode.setStyle({width: 'auto',
1023                                 height: '50%',
1024                                 borderTopWidthidth: '1px'});
1025
1026                         $("headlines-toolbar").setStyle({ borderBottomWidth: '1px' });
1027                 }
1028
1029                 closeArticlePanel();
1030
1031                 if (article_id) view(article_id);
1032
1033                 new Ajax.Request("backend.php", {
1034                         parameters: "op=rpc&method=setpanelmode&wide=" + (wide ? 1 : 0),
1035                         onComplete: function(transport) {
1036                                 console.log(transport.responseText);
1037                         } });
1038
1039
1040         } catch (e) {
1041                 exception_error("switchPanelMode", e);
1042         }
1043 }
1044
1045 function update_random_feed() {
1046         try {
1047                 console.log("in update_random_feed");
1048
1049                 new Ajax.Request("backend.php", {
1050                         parameters: "op=rpc&method=updateRandomFeed",
1051                         onComplete: function(transport) {
1052                                 handle_rpc_json(transport, true);
1053                                 window.setTimeout("update_random_feed()", 30*1000);
1054                         } });
1055
1056         } catch (e) {
1057                 exception_error("update_random_feed", e);
1058         }
1059 }