]> git.wh0rd.org Git - tt-rss.git/blob - js/tt-rss.js
add context menu to Special category
[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                         if (typeof themeAfterLayout == 'function') {
340                                 themeAfterLayout();
341                         }
342
343                 });
344
345                 delCookie("ttrss_test");
346
347                 var toolbar = document.forms["main_toolbar_form"];
348
349                 dijit.getEnclosingWidget(toolbar.view_mode).attr('value',
350                         getInitParam("default_view_mode"));
351
352                 dijit.getEnclosingWidget(toolbar.order_by).attr('value',
353                         getInitParam("default_view_order_by"));
354
355                 feeds_sort_by_unread = getInitParam("feeds_sort_by_unread") == 1;
356
357                 loading_set_progress(30);
358
359                 // can't use cache_clear() here because viewfeed might not have initialized yet
360                 if ('sessionStorage' in window && window['sessionStorage'] !== null)
361                         sessionStorage.clear();
362
363                 console.log("second stage ok");
364
365         } catch (e) {
366                 exception_error("init_second_stage", e);
367         }
368 }
369
370 function quickMenuGo(opid) {
371         try {
372                 if (opid == "qmcPrefs") {
373                         gotoPreferences();
374                 }
375
376                 if (opid == "qmcTagCloud") {
377                         displayDlg("printTagCloud");
378                 }
379
380                 if (opid == "qmcTagSelect") {
381                         displayDlg("printTagSelect");
382                 }
383
384                 if (opid == "qmcSearch") {
385                         search();
386                         return;
387                 }
388
389                 if (opid == "qmcAddFeed") {
390                         quickAddFeed();
391                         return;
392                 }
393
394                 if (opid == "qmcDigest") {
395                         window.location.href = "backend.php?op=digest";
396                         return;
397                 }
398
399                 if (opid == "qmcEditFeed") {
400                         if (activeFeedIsCat())
401                                 alert(__("You can't edit this kind of feed."));
402                         else
403                                 editFeed(getActiveFeedId());
404                         return;
405                 }
406
407                 if (opid == "qmcRemoveFeed") {
408                         var actid = getActiveFeedId();
409
410                         if (activeFeedIsCat()) {
411                                 alert(__("You can't unsubscribe from the category."));
412                                 return;
413                         }
414
415                         if (!actid) {
416                                 alert(__("Please select some feed first."));
417                                 return;
418                         }
419
420                         var fn = getFeedName(actid);
421
422                         var pr = __("Unsubscribe from %s?").replace("%s", fn);
423
424                         if (confirm(pr)) {
425                                 unsubscribeFeed(actid);
426                         }
427
428                         return;
429                 }
430
431                 if (opid == "qmcCatchupAll") {
432                         catchupAllFeeds();
433                         return;
434                 }
435
436                 if (opid == "qmcShowOnlyUnread") {
437                         toggleDispRead();
438                         return;
439                 }
440
441                 if (opid == "qmcAddFilter") {
442                         quickAddFilter();
443                         return;
444                 }
445
446                 if (opid == "qmcAddLabel") {
447                         addLabel();
448                         return;
449                 }
450
451                 if (opid == "qmcRescoreFeed") {
452                         rescoreCurrentFeed();
453                         return;
454                 }
455
456                 if (opid == "qmcToggleWidescreen") {
457                         if (!isCdmMode()) {
458                                 _widescreen_mode = !_widescreen_mode;
459
460                                 switchPanelMode(_widescreen_mode);
461                         }
462                         return;
463                 }
464
465                 if (opid == "qmcHKhelp") {
466                         helpDialog("main");
467                 }
468
469         } catch (e) {
470                 exception_error("quickMenuGo", e);
471         }
472 }
473
474 function toggleDispRead() {
475         try {
476
477                 var hide = !(getInitParam("hide_read_feeds") == "1");
478
479                 hideOrShowFeeds(hide);
480
481                 var query = "?op=rpc&method=setpref&key=HIDE_READ_FEEDS&value=" +
482                         param_escape(hide);
483
484                 setInitParam("hide_read_feeds", hide);
485
486                 new Ajax.Request("backend.php", {
487                         parameters: query,
488                         onComplete: function(transport) {
489                         } });
490
491         } catch (e) {
492                 exception_error("toggleDispRead", e);
493         }
494 }
495
496 function parse_runtime_info(data) {
497
498         //console.log("parsing runtime info...");
499
500         for (k in data) {
501                 var v = data[k];
502
503 //              console.log("RI: " + k + " => " + v);
504
505                 if (k == "new_version_available") {
506                         var icon = $("newVersionIcon");
507                         if (icon) {
508                                 if (v == "1") {
509                                         icon.style.display = "inline";
510                                 } else {
511                                         icon.style.display = "none";
512                                 }
513                         }
514                         return;
515                 }
516
517                 if (k == "daemon_is_running" && v != 1) {
518                         notify_error("<span onclick=\"javascript:explainError(1)\">Update daemon is not running.</span>", true);
519                         return;
520                 }
521
522                 if (k == "daemon_stamp_ok" && v != 1) {
523                         notify_error("<span onclick=\"javascript:explainError(3)\">Update daemon is not updating feeds.</span>", true);
524                         return;
525                 }
526
527                 if (k == "max_feed_id" || k == "num_feeds") {
528                         if (init_params[k] != v) {
529                                 console.log("feed count changed, need to reload feedlist.");
530                                 updateFeedList();
531                         }
532                 }
533
534                 init_params[k] = v;
535                 notify('');
536         }
537 }
538
539 function collapse_feedlist() {
540         try {
541
542                 if (!Element.visible('feeds-holder')) {
543                         Element.show('feeds-holder');
544                         Element.show('feeds-holder_splitter');
545                         $("collapse_feeds_btn").innerHTML = "&lt;&lt;";
546                 } else {
547                         Element.hide('feeds-holder');
548                         Element.hide('feeds-holder_splitter');
549                         $("collapse_feeds_btn").innerHTML = "&gt;&gt;";
550                 }
551
552                 dijit.byId("main").resize();
553
554                 query = "?op=rpc&method=setpref&key=_COLLAPSED_FEEDLIST&value=true";
555                 new Ajax.Request("backend.php", { parameters: query });
556
557         } catch (e) {
558                 exception_error("collapse_feedlist", e);
559         }
560 }
561
562 function viewModeChanged() {
563         cache_clear();
564         return viewCurrentFeed('');
565 }
566
567 function viewLimitChanged() {
568         return viewCurrentFeed('');
569 }
570
571 /* function adjustArticleScore(id, score) {
572         try {
573
574                 var pr = prompt(__("Assign score to article:"), score);
575
576                 if (pr != undefined) {
577                         var query = "?op=rpc&method=setScore&id=" + id + "&score=" + pr;
578
579                         new Ajax.Request("backend.php", {
580                         parameters: query,
581                         onComplete: function(transport) {
582                                         viewCurrentFeed();
583                                 } });
584
585                 }
586         } catch (e) {
587                 exception_error("adjustArticleScore", e);
588         }
589 } */
590
591 function rescoreCurrentFeed() {
592
593         var actid = getActiveFeedId();
594
595         if (activeFeedIsCat() || actid < 0) {
596                 alert(__("You can't rescore this kind of feed."));
597                 return;
598         }
599
600         if (!actid) {
601                 alert(__("Please select some feed first."));
602                 return;
603         }
604
605         var fn = getFeedName(actid);
606         var pr = __("Rescore articles in %s?").replace("%s", fn);
607
608         if (confirm(pr)) {
609                 notify_progress("Rescoring articles...");
610
611                 var query = "?op=pref-feeds&method=rescore&quiet=1&ids=" + actid;
612
613                 new Ajax.Request("backend.php", {
614                         parameters: query,
615                         onComplete: function(transport) {
616                                 viewCurrentFeed();
617                         } });
618         }
619 }
620
621 function hotkey_handler(e) {
622         try {
623
624                 if (e.target.nodeName == "INPUT" || e.target.nodeName == "TEXTAREA") return;
625
626                 var keycode = false;
627                 var shift_key = false;
628
629                 var cmdline = $('cmdline');
630
631                 try {
632                         shift_key = e.shiftKey;
633                 } catch (e) {
634
635                 }
636
637                 if (window.event) {
638                         keycode = window.event.keyCode;
639                 } else if (e) {
640                         keycode = e.which;
641                 }
642
643                 var keychar = String.fromCharCode(keycode);
644
645                 if (keycode == 27) { // escape
646                         hotkey_prefix = false;
647                 }
648
649                 if (keycode == 16) return; // ignore lone shift
650                 if (keycode == 17) return; // ignore lone ctrl
651
652                 if (!shift_key) keychar = keychar.toLowerCase();
653
654                 var hotkeys = getInitParam("hotkeys");
655
656                 if (!hotkey_prefix && hotkeys[0].indexOf(keychar) != -1) {
657
658                         var date = new Date();
659                         var ts = Math.round(date.getTime() / 1000);
660
661                         hotkey_prefix = keychar;
662                         hotkey_prefix_pressed = ts;
663
664                         cmdline.innerHTML = keychar;
665                         Element.show(cmdline);
666
667                         return true;
668                 }
669
670                 Element.hide(cmdline);
671
672                 var hotkey = keychar.search(/[a-zA-Z0-9]/) != -1 ? keychar : "(" + keycode + ")";
673                 hotkey = hotkey_prefix ? hotkey_prefix + " " + hotkey : hotkey;
674                 hotkey_prefix = false;
675
676                 var hotkey_action = false;
677                 var hotkeys = getInitParam("hotkeys");
678
679                 for (sequence in hotkeys[1]) {
680                         if (sequence == hotkey) {
681                                 hotkey_action = hotkeys[1][sequence];
682                                 break;
683                         }
684                 }
685
686                 switch (hotkey_action) {
687                 case "next_feed":
688                         var rv = dijit.byId("feedTree").getNextFeed(
689                                         getActiveFeedId(), activeFeedIsCat());
690
691                         if (rv) viewfeed(rv[0], '', rv[1]);
692                         return false;
693                 case "prev_feed":
694                         var rv = dijit.byId("feedTree").getPreviousFeed(
695                                         getActiveFeedId(), activeFeedIsCat());
696
697                         if (rv) viewfeed(rv[0], '', rv[1]);
698                         return false;
699                 case "next_article":
700                         moveToPost('next');
701                         return false;
702                 case "prev_article":
703                         moveToPost('prev');
704                         return false;
705                 case "search_dialog":
706                         search();
707                         return ;
708                 case "toggle_mark":
709                         selectionToggleMarked(undefined, false, true);
710                         return false;
711                 case "toggle_publ":
712                         selectionTogglePublished(undefined, false, true);
713                         return false;
714                 case "toggle_unread":
715                         selectionToggleUnread(undefined, false, true);
716                         return false;
717                 case "edit_tags":
718                         var id = getActiveArticleId();
719                         if (id) {
720                                 editArticleTags(id, getActiveFeedId(), isCdmMode());
721                                 return;
722                         }
723                         return false;
724                 case "dismiss_selected":
725                         dismissSelectedArticles();
726                         return false;
727                 case "dismiss_read":
728                         return false;
729                 case "open_in_new_window":
730                         if (getActiveArticleId()) {
731                                 openArticleInNewWindow(getActiveArticleId());
732                                 return;
733                         }
734                         return false;
735                 case "catchup_below":
736                         catchupRelativeToArticle(1);
737                         return false;
738                 case "catchup_above":
739                         catchupRelativeToArticle(0);
740                         return false;
741                 case "article_scroll_down":
742                         scrollArticle(50);
743                         return false;
744                 case "article_scroll_up":
745                         scrollArticle(-50);
746                         return false;
747                 case "email_article":
748                         if (typeof emailArticle != "undefined") {
749                                 emailArticle();
750                         } else {
751                                 alert(__("Please enable mail plugin first."));
752                         }
753                         return false;
754                 case "select_all":
755                         selectArticles('all');
756                         return false;
757                 case "select_unread":
758                         selectArticles('unread');
759                         return false;
760                 case "select_marked":
761                         selectArticles('marked');
762                         return false;
763                 case "select_published":
764                         selectArticles('published');
765                         return false;
766                 case "select_invert":
767                         selectArticles('invert');
768                         return false;
769                 case "select_none":
770                         selectArticles('none');
771                         return false;
772                 case "feed_refresh":
773                         if (getActiveFeedId() != undefined) {
774                                 viewfeed(getActiveFeedId(), '', activeFeedIsCat());
775                                 return;
776                         }
777                         return false;
778                 case "feed_unhide_read":
779                         toggleDispRead();
780                         return false;
781                 case "feed_subscribe":
782                         quickAddFeed();
783                         return false;
784                 case "feed_debug_update":
785                         window.open("backend.php?op=feeds&method=view&feed=" + getActiveFeedId() +
786                                 "&view_mode=adaptive&order_by=default&update=&m=ForceUpdate&cat=" +
787                                 activeFeedIsCat() + "&DevForceUpdate=1&debug=1&xdebug=1&csrf_token=" +
788                                 getInitParam("csrf_token"));
789                         return false;
790                 case "feed_edit":
791                         if (activeFeedIsCat())
792                                 alert(__("You can't edit this kind of feed."));
793                         else
794                                 editFeed(getActiveFeedId());
795                         return false;
796                 case "feed_catchup":
797                         if (getActiveFeedId() != undefined) {
798                                 catchupCurrentFeed();
799                                 return;
800                         }
801                         return false;
802                 case "feed_reverse":
803                         reverseHeadlineOrder();
804                         return false;
805                 case "catchup_all":
806                         catchupAllFeeds();
807                         return false;
808                 case "cat_toggle_collapse":
809                         if (activeFeedIsCat()) {
810                                 dijit.byId("feedTree").collapseCat(getActiveFeedId());
811                                 return;
812                         }
813                         return false;
814                 case "goto_all":
815                         viewfeed(-4);
816                         return false;
817                 case "goto_fresh":
818                         viewfeed(-3);
819                         return false;
820                 case "goto_marked":
821                         viewfeed(-1);
822                         return false;
823                 case "goto_published":
824                         viewfeed(-2);
825                         return false;
826                 case "goto_tagcloud":
827                         displayDlg("printTagCloud");
828                         return false;
829                 case "goto_prefs":
830                         gotoPreferences();
831                         return false;
832                 case "select_article_cursor":
833                         var id = getArticleUnderPointer();
834                         if (id) {
835                                 var cb = dijit.byId("RCHK-" + id);
836                                 if (cb) {
837                                         cb.attr("checked", !cb.attr("checked"));
838                                         toggleSelectRowById(cb, "RROW-" + id);
839                                         return false;
840                                 }
841                         }
842                         return false;
843                 case "create_label":
844                         addLabel();
845                         return false;
846                 case "create_filter":
847                         quickAddFilter();
848                         return false;
849                 case "collapse_sidebar":
850                         collapse_feedlist();
851                         return false;
852                 case "toggle_widescreen":
853                         if (!isCdmMode()) {
854                                 _widescreen_mode = !_widescreen_mode;
855
856                                 switchPanelMode(_widescreen_mode);
857                         }
858                         return false;
859                 case "help_dialog":
860                         helpDialog("main");
861                         return false;
862                 default:
863                         console.log("unhandled action: " + hotkey_action + "; hotkey: " + hotkey);
864                 }
865
866         } catch (e) {
867                 exception_error("hotkey_handler", e);
868         }
869 }
870
871 function inPreferences() {
872         return false;
873 }
874
875 function reverseHeadlineOrder() {
876         try {
877
878                 var query_str = "?op=rpc&method=togglepref&key=REVERSE_HEADLINES";
879
880                 new Ajax.Request("backend.php", {
881                         parameters: query_str,
882                         onComplete: function(transport) {
883                                         viewCurrentFeed();
884                                 } });
885
886         } catch (e) {
887                 exception_error("reverseHeadlineOrder", e);
888         }
889 }
890
891 function newVersionDlg() {
892         try {
893                 var query = "backend.php?op=dlg&method=newVersion";
894
895                 if (dijit.byId("newVersionDlg"))
896                         dijit.byId("newVersionDlg").destroyRecursive();
897
898                 dialog = new dijit.Dialog({
899                         id: "newVersionDlg",
900                         title: __("New version available!"),
901                         style: "width: 600px",
902                         href: query,
903                 });
904
905                 dialog.show();
906
907         } catch (e) {
908                 exception_error("newVersionDlg", e);
909         }
910 }
911
912 function handle_rpc_json(transport, scheduled_call) {
913         try {
914                 var reply = JSON.parse(transport.responseText);
915
916                 if (reply) {
917
918                         var error = reply['error'];
919
920                         if (error) {
921                                 var code = error['code'];
922                                 var msg = error['msg'];
923
924                                 console.warn("[handle_rpc_json] received fatal error " + code + "/" + msg);
925
926                                 if (code != 0) {
927                                         fatalError(code, msg);
928                                         return false;
929                                 }
930                         }
931
932                         var seq = reply['seq'];
933
934                         if (seq) {
935                                 if (get_seq() != seq) {
936                                         console.log("[handle_rpc_json] sequence mismatch: " + seq +
937                                                 " (want: " + get_seq() + ")");
938                                         return true;
939                                 }
940                         }
941
942                         var message = reply['message'];
943
944                         if (message) {
945                                 if (message == "UPDATE_COUNTERS") {
946                                         console.log("need to refresh counters...");
947                                         setInitParam("last_article_id", -1);
948                                         _force_scheduled_update = true;
949                                 }
950                         }
951
952                         var counters = reply['counters'];
953
954                         if (counters)
955                                 parse_counters(counters, scheduled_call);
956
957                         var runtime_info = reply['runtime-info'];;
958
959                         if (runtime_info)
960                                 parse_runtime_info(runtime_info);
961
962                         hideOrShowFeeds(getInitParam("hide_read_feeds") == 1);
963
964                         Element.hide("net-alert");
965
966                 } else {
967                         //notify_error("Error communicating with server.");
968                         Element.show("net-alert");
969                 }
970
971         } catch (e) {
972                 Element.show("net-alert");
973                 //notify_error("Error communicating with server.");
974                 console.log(e);
975                 //exception_error("handle_rpc_json", e, transport);
976         }
977
978         return true;
979 }
980
981 function switchPanelMode(wide) {
982         try {
983                 article_id = getActiveArticleId();
984
985                 if (wide) {
986                         dijit.byId("headlines-wrap-inner").attr("design", 'sidebar');
987                         dijit.byId("content-insert").attr("region", "trailing");
988
989                         dijit.byId("content-insert").domNode.setStyle({width: '50%',
990                                 height: 'auto',
991                                 'border-top-width': '0px' });
992
993                         $("headlines-toolbar").setStyle({ 'border-bottom-width': '0px' });
994
995                 } else {
996
997                         dijit.byId("content-insert").attr("region", "bottom");
998
999                         dijit.byId("content-insert").domNode.setStyle({width: 'auto',
1000                                 height: '50%',
1001                                 'border-top-width': '1px'});
1002
1003                         $("headlines-toolbar").setStyle({ 'border-bottom-width': '1px' });
1004                 }
1005
1006                 closeArticlePanel();
1007
1008                 if (article_id) view(article_id);
1009
1010         } catch (e) {
1011                 exception_error("switchPanelMode", e);
1012         }
1013 }