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