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