]> git.wh0rd.org Git - tt-rss.git/blob - js/prefs.js
Merge branch 'master' of git.tt-rss.org:git/tt-rss into pdo-experimental
[tt-rss.git] / js / prefs.js
1 var init_params = new Array();
2
3 var hotkey_prefix = false;
4 var hotkey_prefix_pressed = false;
5
6 var seq = "";
7
8 function notify_callback2(transport, sticky) {
9         notify_info(transport.responseText, sticky);
10 }
11
12 function updateFeedList() {
13
14         var user_search = $("feed_search");
15         var search = "";
16         if (user_search) { search = user_search.value; }
17
18         new Ajax.Request("backend.php", {
19                 parameters: "?op=pref-feeds&search=" + param_escape(search),
20                 onComplete: function(transport) {
21                         dijit.byId('feedConfigTab').attr('content', transport.responseText);
22                         selectTab("feedConfig", true);
23                         notify("");
24                 } });
25 }
26
27 function checkInactiveFeeds() {
28         new Ajax.Request("backend.php", {
29                 parameters: "?op=pref-feeds&method=getinactivefeeds",
30                 onComplete: function (transport) {
31                         if (parseInt(transport.responseText) > 0) {
32                                 Element.show(dijit.byId("pref_feeds_inactive_btn").domNode);
33                         }
34                 }
35         });
36 }
37
38 function updateUsersList(sort_key) {
39         var user_search = $("user_search");
40         var search = "";
41         if (user_search) {
42                 search = user_search.value;
43         }
44
45         var query = "?op=pref-users&sort="
46                 + param_escape(sort_key) +
47                 "&search=" + param_escape(search);
48
49         new Ajax.Request("backend.php", {
50                 parameters: query,
51                 onComplete: function (transport) {
52                         dijit.byId('userConfigTab').attr('content', transport.responseText);
53                         selectTab("userConfig", true)
54                         notify("");
55                 }
56         });
57 }
58
59 function addUser() {
60         var login = prompt(__("Please enter login:"), "");
61
62         if (login == null) {
63                 return false;
64         }
65
66         if (login == "") {
67                 alert(__("Can't create user: no login specified."));
68                 return false;
69         }
70
71         notify_progress("Adding user...");
72
73         var query = "?op=pref-users&method=add&login=" +
74                 param_escape(login);
75
76         new Ajax.Request("backend.php", {
77                 parameters: query,
78                 onComplete: function (transport) {
79                         notify_callback2(transport);
80                         updateUsersList();
81                 }
82         });
83
84 }
85
86 function editUser(id) {
87
88         var query = "backend.php?op=pref-users&method=edit&id=" +
89                 param_escape(id);
90
91         if (dijit.byId("userEditDlg"))
92                 dijit.byId("userEditDlg").destroyRecursive();
93
94         dialog = new dijit.Dialog({
95                 id: "userEditDlg",
96                 title: __("User Editor"),
97                 style: "width: 600px",
98                 execute: function () {
99                         if (this.validate()) {
100                                 notify_progress("Saving data...", true);
101
102                                 var query = dojo.formToQuery("user_edit_form");
103
104                                 new Ajax.Request("backend.php", {
105                                         parameters: query,
106                                         onComplete: function (transport) {
107                                                 dialog.hide();
108                                                 updateUsersList();
109                                         }
110                                 });
111                         }
112                 },
113                 href: query
114         });
115
116         dialog.show();
117 }
118
119 function editFilter(id) {
120
121         var query = "backend.php?op=pref-filters&method=edit&id=" + param_escape(id);
122
123         if (dijit.byId("feedEditDlg"))
124                 dijit.byId("feedEditDlg").destroyRecursive();
125
126         if (dijit.byId("filterEditDlg"))
127                 dijit.byId("filterEditDlg").destroyRecursive();
128
129         dialog = new dijit.Dialog({
130                 id: "filterEditDlg",
131                 title: __("Edit Filter"),
132                 style: "width: 600px",
133
134                 test: function () {
135                         var query = "backend.php?" + dojo.formToQuery("filter_edit_form") + "&savemode=test";
136
137                         editFilterTest(query);
138                 },
139                 selectRules: function (select) {
140                         $$("#filterDlg_Matches input[type=checkbox]").each(function (e) {
141                                 e.checked = select;
142                                 if (select)
143                                         e.parentNode.addClassName("Selected");
144                                 else
145                                         e.parentNode.removeClassName("Selected");
146                         });
147                 },
148                 selectActions: function (select) {
149                         $$("#filterDlg_Actions input[type=checkbox]").each(function (e) {
150                                 e.checked = select;
151
152                                 if (select)
153                                         e.parentNode.addClassName("Selected");
154                                 else
155                                         e.parentNode.removeClassName("Selected");
156
157                         });
158                 },
159                 editRule: function (e) {
160                         var li = e.parentNode;
161                         var rule = li.getElementsByTagName("INPUT")[1].value;
162                         addFilterRule(li, rule);
163                 },
164                 editAction: function (e) {
165                         var li = e.parentNode;
166                         var action = li.getElementsByTagName("INPUT")[1].value;
167                         addFilterAction(li, action);
168                 },
169                 removeFilter: function () {
170                         var msg = __("Remove filter?");
171
172                         if (confirm(msg)) {
173                                 this.hide();
174
175                                 notify_progress("Removing filter...");
176
177                                 var id = this.attr('value').id;
178
179                                 var query = "?op=pref-filters&method=remove&ids=" +
180                                         param_escape(id);
181
182                                 new Ajax.Request("backend.php", {
183                                         parameters: query,
184                                         onComplete: function (transport) {
185                                                 updateFilterList();
186                                         }
187                                 });
188                         }
189                 },
190                 addAction: function () {
191                         addFilterAction();
192                 },
193                 addRule: function () {
194                         addFilterRule();
195                 },
196                 deleteAction: function () {
197                         $$("#filterDlg_Actions li[class*=Selected]").each(function (e) {
198                                 e.parentNode.removeChild(e)
199                         });
200                 },
201                 deleteRule: function () {
202                         $$("#filterDlg_Matches li[class*=Selected]").each(function (e) {
203                                 e.parentNode.removeChild(e)
204                         });
205                 },
206                 execute: function () {
207                         if (this.validate()) {
208
209                                 notify_progress("Saving data...", true);
210
211                                 var query = dojo.formToQuery("filter_edit_form");
212
213                                 console.log(query);
214
215                                 new Ajax.Request("backend.php", {
216                                         parameters: query,
217                                         onComplete: function (transport) {
218                                                 dialog.hide();
219                                                 updateFilterList();
220                                         }
221                                 });
222                         }
223                 },
224                 href: query
225         });
226
227         dialog.show();
228 }
229
230
231 function getSelectedLabels() {
232         var tree = dijit.byId("labelTree");
233         var items = tree.model.getCheckedItems();
234         var rv = [];
235
236         items.each(function(item) {
237                 rv.push(tree.model.store.getValue(item, 'bare_id'));
238         });
239
240         return rv;
241 }
242
243 function getSelectedUsers() {
244         return getSelectedTableRowIds("prefUserList");
245 }
246
247 function getSelectedFeeds() {
248         var tree = dijit.byId("feedTree");
249         var items = tree.model.getCheckedItems();
250         var rv = [];
251
252         items.each(function(item) {
253                 if (item.id[0].match("FEED:"))
254                         rv.push(tree.model.store.getValue(item, 'bare_id'));
255         });
256
257         return rv;
258 }
259
260 function getSelectedCategories() {
261         var tree = dijit.byId("feedTree");
262         var items = tree.model.getCheckedItems();
263         var rv = [];
264
265         items.each(function(item) {
266                 if (item.id[0].match("CAT:"))
267                         rv.push(tree.model.store.getValue(item, 'bare_id'));
268         });
269
270         return rv;
271 }
272
273 function getSelectedFilters() {
274         var tree = dijit.byId("filterTree");
275         var items = tree.model.getCheckedItems();
276         var rv = [];
277
278         items.each(function(item) {
279                 rv.push(tree.model.store.getValue(item, 'bare_id'));
280         });
281
282         return rv;
283
284 }
285
286 function removeSelectedLabels() {
287
288         var sel_rows = getSelectedLabels();
289
290         if (sel_rows.length > 0) {
291
292                 var ok = confirm(__("Remove selected labels?"));
293
294                 if (ok) {
295                         notify_progress("Removing selected labels...");
296
297                         var query = "?op=pref-labels&method=remove&ids="+
298                                 param_escape(sel_rows.toString());
299
300                         new Ajax.Request("backend.php", {
301                                 parameters: query,
302                                 onComplete: function(transport) {
303                                                 updateLabelList();
304                                         } });
305
306                 }
307         } else {
308                 alert(__("No labels are selected."));
309         }
310
311         return false;
312 }
313
314 function removeSelectedUsers() {
315
316         var sel_rows = getSelectedUsers();
317
318         if (sel_rows.length > 0) {
319
320                 var ok = confirm(__("Remove selected users? Neither default admin nor your account will be removed."));
321
322                 if (ok) {
323                         notify_progress("Removing selected users...");
324
325                         var query = "?op=pref-users&method=remove&ids=" +
326                                 param_escape(sel_rows.toString());
327
328                         new Ajax.Request("backend.php", {
329                                 parameters: query,
330                                 onComplete: function (transport) {
331                                         updateUsersList();
332                                 }
333                         });
334
335                 }
336
337         } else {
338                 alert(__("No users are selected."));
339         }
340
341         return false;
342 }
343
344 function removeSelectedFilters() {
345
346         var sel_rows = getSelectedFilters();
347
348         if (sel_rows.length > 0) {
349
350                 var ok = confirm(__("Remove selected filters?"));
351
352                 if (ok) {
353                         notify_progress("Removing selected filters...");
354
355                         var query = "?op=pref-filters&method=remove&ids=" +
356                                 param_escape(sel_rows.toString());
357
358                         new Ajax.Request("backend.php", {
359                                 parameters: query,
360                                 onComplete: function (transport) {
361                                         updateFilterList();
362                                 }
363                         });
364                 }
365         } else {
366                 alert(__("No filters are selected."));
367         }
368
369         return false;
370 }
371
372 function removeSelectedFeeds() {
373
374         var sel_rows = getSelectedFeeds();
375
376         if (sel_rows.length > 0) {
377
378                 var ok = confirm(__("Unsubscribe from selected feeds?"));
379
380                 if (ok) {
381
382                         notify_progress("Unsubscribing from selected feeds...", true);
383
384                         var query = "?op=pref-feeds&method=remove&ids=" +
385                                 param_escape(sel_rows.toString());
386
387                         console.log(query);
388
389                         new Ajax.Request("backend.php", {
390                                 parameters: query,
391                                 onComplete: function (transport) {
392                                         updateFeedList();
393                                 }
394                         });
395                 }
396
397         } else {
398                 alert(__("No feeds are selected."));
399         }
400
401         return false;
402 }
403
404 function editSelectedUser() {
405         var rows = getSelectedUsers();
406
407         if (rows.length == 0) {
408                 alert(__("No users are selected."));
409                 return;
410         }
411
412         if (rows.length > 1) {
413                 alert(__("Please select only one user."));
414                 return;
415         }
416
417         notify("");
418
419         editUser(rows[0]);
420 }
421
422 function resetSelectedUserPass() {
423
424         var rows = getSelectedUsers();
425
426         if (rows.length == 0) {
427                 alert(__("No users are selected."));
428                 return;
429         }
430
431         if (rows.length > 1) {
432                 alert(__("Please select only one user."));
433                 return;
434         }
435
436         var ok = confirm(__("Reset password of selected user?"));
437
438         if (ok) {
439                 notify_progress("Resetting password for selected user...");
440
441                 var id = rows[0];
442
443                 var query = "?op=pref-users&method=resetPass&id=" +
444                         param_escape(id);
445
446                 new Ajax.Request("backend.php", {
447                         parameters: query,
448                         onComplete: function (transport) {
449                                 notify_info(transport.responseText, true);
450                         }
451                 });
452
453         }
454 }
455
456 function selectedUserDetails() {
457
458         var rows = getSelectedUsers();
459
460         if (rows.length == 0) {
461                 alert(__("No users are selected."));
462                 return;
463         }
464
465         if (rows.length > 1) {
466                 alert(__("Please select only one user."));
467                 return;
468         }
469
470         var id = rows[0];
471
472         var query = "backend.php?op=pref-users&method=userdetails&id=" + id;
473
474         if (dijit.byId("userDetailsDlg"))
475                 dijit.byId("userDetailsDlg").destroyRecursive();
476
477         dialog = new dijit.Dialog({
478                 id: "userDetailsDlg",
479                 title: __("User details"),
480                 style: "width: 600px",
481                 execute: function () {
482                         dialog.hide();
483                 },
484                 href: query
485         });
486
487         dialog.show();
488 }
489
490
491 function editSelectedFilter() {
492         var rows = getSelectedFilters();
493
494         if (rows.length == 0) {
495                 alert(__("No filters are selected."));
496                 return;
497         }
498
499         if (rows.length > 1) {
500                 alert(__("Please select only one filter."));
501                 return;
502         }
503
504         notify("");
505
506         editFilter(rows[0]);
507
508 }
509
510 function joinSelectedFilters() {
511         var rows = getSelectedFilters();
512
513         if (rows.length == 0) {
514                 alert(__("No filters are selected."));
515                 return;
516         }
517
518         var ok = confirm(__("Combine selected filters?"));
519
520         if (ok) {
521                 notify_progress("Joining filters...");
522
523                 var query = "?op=pref-filters&method=join&ids="+
524                         param_escape(rows.toString());
525
526                 console.log(query);
527
528                 new Ajax.Request("backend.php", {
529                         parameters: query,
530                         onComplete: function(transport) {
531                                         updateFilterList();
532                         } });
533         }
534 }
535
536 function editSelectedFeed() {
537         var rows = getSelectedFeeds();
538
539         if (rows.length == 0) {
540                 alert(__("No feeds are selected."));
541                 return;
542         }
543
544         if (rows.length > 1) {
545                 return editSelectedFeeds();
546         }
547
548         notify("");
549
550         editFeed(rows[0], {});
551
552 }
553
554 function editSelectedFeeds() {
555         var rows = getSelectedFeeds();
556
557         if (rows.length == 0) {
558                 alert(__("No feeds are selected."));
559                 return;
560         }
561
562         notify_progress("Loading, please wait...");
563
564         var query = "backend.php?op=pref-feeds&method=editfeeds&ids=" +
565                 param_escape(rows.toString());
566
567         console.log(query);
568
569         if (dijit.byId("feedEditDlg"))
570                 dijit.byId("feedEditDlg").destroyRecursive();
571
572         new Ajax.Request("backend.php", {
573                 parameters: query,
574                 onComplete: function (transport) {
575
576                         notify("");
577
578                         var dialog = new dijit.Dialog({
579                                 id: "feedEditDlg",
580                                 title: __("Edit Multiple Feeds"),
581                                 style: "width: 600px",
582                                 getChildByName: function (name) {
583                                         var rv = null;
584                                         this.getChildren().each(
585                                                 function (child) {
586                                                         if (child.name == name) {
587                                                                 rv = child;
588                                                                 return;
589                                                         }
590                                                 });
591                                         return rv;
592                                 },
593                                 toggleField: function (checkbox, elem, label) {
594                                         this.getChildByName(elem).attr('disabled', !checkbox.checked);
595
596                                         if ($(label))
597                                                 if (checkbox.checked)
598                                                         $(label).removeClassName('insensitive');
599                                                 else
600                                                         $(label).addClassName('insensitive');
601
602                                 },
603                                 execute: function () {
604                                         if (this.validate() && confirm(__("Save changes to selected feeds?"))) {
605                                                 var query = dojo.objectToQuery(this.attr('value'));
606
607                                                 /* Form.serialize ignores unchecked checkboxes */
608
609                                                 if (!query.match("&private=") &&
610                                                         this.getChildByName('private').attr('disabled') == false) {
611                                                         query = query + "&private=false";
612                                                 }
613
614                                                 try {
615                                                         if (!query.match("&cache_images=") &&
616                                                                 this.getChildByName('cache_images').attr('disabled') == false) {
617                                                                 query = query + "&cache_images=false";
618                                                         }
619                                                 } catch (e) {
620                                                 }
621
622                                                 try {
623                                                         if (!query.match("&hide_images=") &&
624                                                                 this.getChildByName('hide_images').attr('disabled') == false) {
625                                                                 query = query + "&hide_images=false";
626                                                         }
627                                                 } catch (e) {
628                                                 }
629
630                                                 if (!query.match("&include_in_digest=") &&
631                                                         this.getChildByName('include_in_digest').attr('disabled') == false) {
632                                                         query = query + "&include_in_digest=false";
633                                                 }
634
635                                                 if (!query.match("&always_display_enclosures=") &&
636                                                         this.getChildByName('always_display_enclosures').attr('disabled') == false) {
637                                                         query = query + "&always_display_enclosures=false";
638                                                 }
639
640                                                 if (!query.match("&mark_unread_on_update=") &&
641                                                         this.getChildByName('mark_unread_on_update').attr('disabled') == false) {
642                                                         query = query + "&mark_unread_on_update=false";
643                                                 }
644
645                                                 console.log(query);
646
647                                                 notify_progress("Saving data...", true);
648
649                                                 new Ajax.Request("backend.php", {
650                                                         parameters: query,
651                                                         onComplete: function (transport) {
652                                                                 dialog.hide();
653                                                                 updateFeedList();
654                                                         }
655                                                 });
656                                         }
657                                 },
658                                 content: transport.responseText
659                         });
660
661                         dialog.show();
662
663                 }
664         });
665 }
666
667 function opmlImportComplete(iframe) {
668         if (!iframe.contentDocument.body.innerHTML) return false;
669
670         Element.show(iframe);
671
672         notify('');
673
674         if (dijit.byId('opmlImportDlg'))
675                 dijit.byId('opmlImportDlg').destroyRecursive();
676
677         var content = iframe.contentDocument.body.innerHTML;
678
679         dialog = new dijit.Dialog({
680                 id: "opmlImportDlg",
681                 title: __("OPML Import"),
682                 style: "width: 600px",
683                 onCancel: function () {
684             window.location.reload();
685                 },
686                 execute: function () {
687                         window.location.reload();
688                 },
689                 content: content
690         });
691
692         dialog.show();
693 }
694
695 function opmlImport() {
696
697         var opml_file = $("opml_file");
698
699         if (opml_file.value.length == 0) {
700                 alert(__("Please choose an OPML file first."));
701                 return false;
702         } else {
703                 notify_progress("Importing, please wait...", true);
704
705                 Element.show("upload_iframe");
706
707                 return true;
708         }
709 }
710
711
712 function updateFilterList() {
713         var user_search = $("filter_search");
714         var search = "";
715         if (user_search) { search = user_search.value; }
716
717         new Ajax.Request("backend.php", {
718                 parameters: "?op=pref-filters&search=" + param_escape(search),
719                 onComplete: function(transport) {
720                         dijit.byId('filterConfigTab').attr('content', transport.responseText);
721                         notify("");
722                 } });
723 }
724
725 function updateLabelList() {
726         new Ajax.Request("backend.php", {
727                 parameters: "?op=pref-labels",
728                 onComplete: function(transport) {
729                         dijit.byId('labelConfigTab').attr('content', transport.responseText);
730                         notify("");
731                 } });
732 }
733
734 function updatePrefsList() {
735         new Ajax.Request("backend.php", {
736                 parameters: "?op=pref-prefs",
737                 onComplete: function(transport) {
738                         dijit.byId('genConfigTab').attr('content', transport.responseText);
739                         notify("");
740                 } });
741 }
742
743 function updateSystemList() {
744         new Ajax.Request("backend.php", {
745                 parameters: "?op=pref-system",
746                 onComplete: function(transport) {
747                         dijit.byId('systemConfigTab').attr('content', transport.responseText);
748                         notify("");
749                 } });
750 }
751
752 function selectTab(id, noupdate) {
753         if (!noupdate) {
754                 notify_progress("Loading, please wait...");
755
756                 if (id == "feedConfig") {
757                         updateFeedList();
758                 } else if (id == "filterConfig") {
759                         updateFilterList();
760                 } else if (id == "labelConfig") {
761                         updateLabelList();
762                 } else if (id == "genConfig") {
763                         updatePrefsList();
764                 } else if (id == "userConfig") {
765                         updateUsersList();
766                 } else if (id == "systemConfig") {
767                         updateSystemList();
768                 }
769
770                 var tab = dijit.byId(id + "Tab");
771                 dijit.byId("pref-tabs").selectChild(tab);
772
773         }
774 }
775
776 function init_second_stage() {
777         document.onkeydown = pref_hotkey_handler;
778         loading_set_progress(50);
779         notify("");
780
781         var tab = getURLParam('tab');
782
783         if (tab) {
784                 tab = dijit.byId(tab + "Tab");
785                 if (tab) dijit.byId("pref-tabs").selectChild(tab);
786         }
787
788         var method = getURLParam('method');
789
790         if (method == 'editFeed') {
791                 var param = getURLParam('methodparam');
792
793                 window.setTimeout(function() { editFeed(param) }, 100);
794         }
795
796         setTimeout(hotkey_prefix_timeout, 5*1000);
797 }
798
799 function init() {
800         window.onerror = function (message, filename, lineno, colno, error) {
801                 report_error(message, filename, lineno, colno, error);
802         };
803
804         require(["dojo/_base/kernel",
805                 "dojo/ready",
806                 "dojo/parser",
807                 "dojo/_base/loader",
808                 "dojo/_base/html",
809                 "dijit/ColorPalette",
810                 "dijit/Dialog",
811                 "dijit/form/Button",
812                 "dijit/form/CheckBox",
813                 "dijit/form/DropDownButton",
814                 "dijit/form/FilteringSelect",
815         "dijit/form/MultiSelect",
816                 "dijit/form/Form",
817                 "dijit/form/RadioButton",
818                 "dijit/form/ComboButton",
819                 "dijit/form/Select",
820                 "dijit/form/SimpleTextarea",
821                 "dijit/form/TextBox",
822                 "dijit/form/ValidationTextBox",
823                 "dijit/InlineEditBox",
824                 "dijit/layout/AccordionContainer",
825                 "dijit/layout/AccordionPane",
826                 "dijit/layout/BorderContainer",
827                 "dijit/layout/ContentPane",
828                 "dijit/layout/TabContainer",
829                 "dijit/Menu",
830                 "dijit/ProgressBar",
831                 "dijit/Toolbar",
832                 "dijit/Tree",
833                 "dijit/tree/dndSource",
834                 "dojo/data/ItemFileWriteStore",
835                 "fox/PrefFeedTree",
836                 "fox/PrefFilterTree",
837                 "fox/PrefLabelTree"], function (dojo, ready, parser) {
838
839                 ready(function () {
840                         try {
841                                 parser.parse();
842
843                                 loading_set_progress(50);
844
845                                 var clientTzOffset = new Date().getTimezoneOffset() * 60;
846
847                                 new Ajax.Request("backend.php", {
848                                         parameters: {
849                                                 op: "rpc", method: "sanityCheck",
850                                                 clientTzOffset: clientTzOffset
851                                         },
852                                         onComplete: function (transport) {
853                                                 backend_sanity_check_callback(transport);
854                                         }
855                                 });
856                         } catch (e) {
857                                 exception_error(e);
858                         }
859                 });
860         });
861 }
862
863
864 function validatePrefsReset() {
865         var ok = confirm(__("Reset to defaults?"));
866
867         if (ok) {
868
869                 query = "?op=pref-prefs&method=resetconfig";
870                 console.log(query);
871
872                 new Ajax.Request("backend.php", {
873                         parameters: query,
874                         onComplete: function(transport) {
875                                 updatePrefsList();
876                                 notify_info(transport.responseText);
877                         } });
878
879         }
880
881         return false;
882
883 }
884
885 function pref_hotkey_handler(e) {
886
887         if (e.target.nodeName == "INPUT" || e.target.nodeName == "TEXTAREA") return;
888
889         var keycode = false;
890         var shift_key = false;
891
892         var cmdline = $('cmdline');
893
894         try {
895                 shift_key = e.shiftKey;
896         } catch (e) {
897
898         }
899
900         if (window.event) {
901                 keycode = window.event.keyCode;
902         } else if (e) {
903                 keycode = e.which;
904         }
905
906         var keychar = String.fromCharCode(keycode);
907
908         if (keycode == 27) { // escape
909                 hotkey_prefix = false;
910         }
911
912         if (keycode == 16) return; // ignore lone shift
913         if (keycode == 17) return; // ignore lone ctrl
914
915         if (!shift_key) keychar = keychar.toLowerCase();
916
917         var hotkeys = getInitParam("hotkeys");
918
919         if (!hotkey_prefix && hotkeys[0].indexOf(keychar) != -1) {
920
921                 var date = new Date();
922                 var ts = Math.round(date.getTime() / 1000);
923
924                 hotkey_prefix = keychar;
925                 hotkey_prefix_pressed = ts;
926
927                 cmdline.innerHTML = keychar;
928                 Element.show(cmdline);
929
930                 return true;
931         }
932
933         Element.hide(cmdline);
934
935         var hotkey = keychar.search(/[a-zA-Z0-9]/) != -1 ? keychar : "(" + keycode + ")";
936         hotkey = hotkey_prefix ? hotkey_prefix + " " + hotkey : hotkey;
937         hotkey_prefix = false;
938
939         var hotkey_action = false;
940         var hotkeys = getInitParam("hotkeys");
941
942         for (sequence in hotkeys[1]) {
943                 if (sequence == hotkey) {
944                         hotkey_action = hotkeys[1][sequence];
945                         break;
946                 }
947         }
948
949         switch (hotkey_action) {
950                 case "feed_subscribe":
951                         quickAddFeed();
952                         return false;
953                 case "create_label":
954                         addLabel();
955                         return false;
956                 case "create_filter":
957                         quickAddFilter();
958                         return false;
959                 case "help_dialog":
960                         //helpDialog("prefs");
961                         return false;
962                 default:
963                         console.log("unhandled action: " + hotkey_action + "; hotkey: " + hotkey);
964         }
965 }
966
967 function removeCategory(id, item) {
968
969         var ok = confirm(__("Remove category %s? Any nested feeds would be placed into Uncategorized.").replace("%s", item.name));
970
971         if (ok) {
972                 var query = "?op=pref-feeds&method=removeCat&ids=" +
973                         param_escape(id);
974
975                 notify_progress("Removing category...");
976
977                 new Ajax.Request("backend.php", {
978                         parameters: query,
979                         onComplete: function (transport) {
980                                 notify('');
981                                 updateFeedList();
982                         }
983                 });
984         }
985 }
986
987 function removeSelectedCategories() {
988
989         var sel_rows = getSelectedCategories();
990
991         if (sel_rows.length > 0) {
992
993                 var ok = confirm(__("Remove selected categories?"));
994
995                 if (ok) {
996                         notify_progress("Removing selected categories...");
997
998                         var query = "?op=pref-feeds&method=removeCat&ids="+
999                                 param_escape(sel_rows.toString());
1000
1001                         new Ajax.Request("backend.php", {
1002                                 parameters: query,
1003                                 onComplete: function(transport) {
1004                                                 updateFeedList();
1005                                         } });
1006
1007                 }
1008         } else {
1009                 alert(__("No categories are selected."));
1010         }
1011
1012         return false;
1013 }
1014
1015 function createCategory() {
1016         var title = prompt(__("Category title:"));
1017
1018         if (title) {
1019
1020                 notify_progress("Creating category...");
1021
1022                 var query = "?op=pref-feeds&method=addCat&cat=" +
1023                         param_escape(title);
1024
1025                 new Ajax.Request("backend.php", {
1026                         parameters: query,
1027                         onComplete: function (transport) {
1028                                 notify('');
1029                                 updateFeedList();
1030                         }
1031                 });
1032         }
1033 }
1034
1035 function showInactiveFeeds() {
1036         var query = "backend.php?op=pref-feeds&method=inactiveFeeds";
1037
1038         if (dijit.byId("inactiveFeedsDlg"))
1039                 dijit.byId("inactiveFeedsDlg").destroyRecursive();
1040
1041         dialog = new dijit.Dialog({
1042                 id: "inactiveFeedsDlg",
1043                 title: __("Feeds without recent updates"),
1044                 style: "width: 600px",
1045                 getSelectedFeeds: function () {
1046                         return getSelectedTableRowIds("prefInactiveFeedList");
1047                 },
1048                 removeSelected: function () {
1049                         var sel_rows = this.getSelectedFeeds();
1050
1051                         console.log(sel_rows);
1052
1053                         if (sel_rows.length > 0) {
1054                                 var ok = confirm(__("Remove selected feeds?"));
1055
1056                                 if (ok) {
1057                                         notify_progress("Removing selected feeds...", true);
1058
1059                                         var query = "?op=pref-feeds&method=remove&ids=" +
1060                                                 param_escape(sel_rows.toString());
1061
1062                                         new Ajax.Request("backend.php", {
1063                                                 parameters: query,
1064                                                 onComplete: function (transport) {
1065                                                         notify('');
1066                                                         dialog.hide();
1067                                                         updateFeedList();
1068                                                 }
1069                                         });
1070                                 }
1071
1072                         } else {
1073                                 alert(__("No feeds are selected."));
1074                         }
1075                 },
1076                 execute: function () {
1077                         if (this.validate()) {
1078                         }
1079                 },
1080                 href: query
1081         });
1082
1083         dialog.show();
1084 }
1085
1086 function opmlRegenKey() {
1087         var ok = confirm(__("Replace current OPML publishing address with a new one?"));
1088
1089         if (ok) {
1090
1091                 notify_progress("Trying to change address...", true);
1092
1093                 var query = "?op=pref-feeds&method=regenOPMLKey";
1094
1095                 new Ajax.Request("backend.php", {
1096                         parameters: query,
1097                         onComplete: function (transport) {
1098                                 var reply = JSON.parse(transport.responseText);
1099
1100                                 var new_link = reply.link;
1101
1102                                 var e = $('pub_opml_url');
1103
1104                                 if (new_link) {
1105                                         e.href = new_link;
1106                                         e.innerHTML = new_link;
1107
1108                                         new Effect.Highlight(e);
1109
1110                                         notify('');
1111
1112                                 } else {
1113                                         notify_error("Could not change feed URL.");
1114                                 }
1115                         }
1116                 });
1117         }
1118         return false;
1119 }
1120
1121 function labelColorReset() {
1122         var labels = getSelectedLabels();
1123
1124         if (labels.length > 0) {
1125                 var ok = confirm(__("Reset selected labels to default colors?"));
1126
1127                 if (ok) {
1128                         var query = "?op=pref-labels&method=colorreset&ids=" +
1129                                 param_escape(labels.toString());
1130
1131                         new Ajax.Request("backend.php", {
1132                                 parameters: query,
1133                                 onComplete: function (transport) {
1134                                         updateLabelList();
1135                                 }
1136                         });
1137                 }
1138
1139         } else {
1140                 alert(__("No labels are selected."));
1141         }
1142 }
1143
1144 function inPreferences() {
1145         return true;
1146 }
1147
1148 function editProfiles() {
1149
1150         if (dijit.byId("profileEditDlg"))
1151                 dijit.byId("profileEditDlg").destroyRecursive();
1152
1153         var query = "backend.php?op=pref-prefs&method=editPrefProfiles";
1154
1155         dialog = new dijit.Dialog({
1156                 id: "profileEditDlg",
1157                 title: __("Settings Profiles"),
1158                 style: "width: 600px",
1159                 getSelectedProfiles: function () {
1160                         return getSelectedTableRowIds("prefFeedProfileList");
1161                 },
1162                 removeSelected: function () {
1163                         var sel_rows = this.getSelectedProfiles();
1164
1165                         if (sel_rows.length > 0) {
1166                                 var ok = confirm(__("Remove selected profiles? Active and default profiles will not be removed."));
1167
1168                                 if (ok) {
1169                                         notify_progress("Removing selected profiles...", true);
1170
1171                                         var query = "?op=rpc&method=remprofiles&ids=" +
1172                                                 param_escape(sel_rows.toString());
1173
1174                                         new Ajax.Request("backend.php", {
1175                                                 parameters: query,
1176                                                 onComplete: function (transport) {
1177                                                         notify('');
1178                                                         editProfiles();
1179                                                 }
1180                                         });
1181
1182                                 }
1183
1184                         } else {
1185                                 alert(__("No profiles are selected."));
1186                         }
1187                 },
1188                 activateProfile: function () {
1189                         var sel_rows = this.getSelectedProfiles();
1190
1191                         if (sel_rows.length == 1) {
1192
1193                                 var ok = confirm(__("Activate selected profile?"));
1194
1195                                 if (ok) {
1196                                         notify_progress("Loading, please wait...");
1197
1198                                         var query = "?op=rpc&method=setprofile&id=" +
1199                                                 param_escape(sel_rows.toString());
1200
1201                                         new Ajax.Request("backend.php", {
1202                                                 parameters: query,
1203                                                 onComplete: function (transport) {
1204                                                         window.location.reload();
1205                                                 }
1206                                         });
1207                                 }
1208
1209                         } else {
1210                                 alert(__("Please choose a profile to activate."));
1211                         }
1212                 },
1213                 addProfile: function () {
1214                         if (this.validate()) {
1215                                 notify_progress("Creating profile...", true);
1216
1217                                 var query = "?op=rpc&method=addprofile&title=" +
1218                                         param_escape(dialog.attr('value').newprofile);
1219
1220                                 new Ajax.Request("backend.php", {
1221                                         parameters: query,
1222                                         onComplete: function (transport) {
1223                                                 notify('');
1224                                                 editProfiles();
1225                                         }
1226                                 });
1227
1228                         }
1229                 },
1230                 execute: function () {
1231                         if (this.validate()) {
1232                         }
1233                 },
1234                 href: query
1235         });
1236
1237         dialog.show();
1238 }
1239
1240 function activatePrefProfile() {
1241
1242         var sel_rows = getSelectedFeedCats();
1243
1244         if (sel_rows.length == 1) {
1245
1246                 var ok = confirm(__("Activate selected profile?"));
1247
1248                 if (ok) {
1249                         notify_progress("Loading, please wait...");
1250
1251                         var query = "?op=rpc&method=setprofile&id="+
1252                                 param_escape(sel_rows.toString());
1253
1254                         new Ajax.Request("backend.php", {
1255                                 parameters: query,
1256                                 onComplete: function(transport) {
1257                                         window.location.reload();
1258                                 } });
1259                 }
1260
1261         } else {
1262                 alert(__("Please choose a profile to activate."));
1263         }
1264
1265         return false;
1266 }
1267
1268 function clearFeedAccessKeys() {
1269
1270         var ok = confirm(__("This will invalidate all previously generated feed URLs. Continue?"));
1271
1272         if (ok) {
1273                 notify_progress("Clearing URLs...");
1274
1275                 var query = "?op=pref-feeds&method=clearKeys";
1276
1277                 new Ajax.Request("backend.php", {
1278                         parameters: query,
1279                         onComplete: function(transport) {
1280                                 notify_info("Generated URLs cleared.");
1281                         } });
1282         }
1283
1284         return false;
1285 }
1286
1287 function resetFilterOrder() {
1288         notify_progress("Loading, please wait...");
1289
1290         new Ajax.Request("backend.php", {
1291                 parameters: "?op=pref-filters&method=filtersortreset",
1292                 onComplete: function (transport) {
1293                         updateFilterList();
1294                 }
1295         });
1296 }
1297
1298
1299
1300 function resetFeedOrder() {
1301         notify_progress("Loading, please wait...");
1302
1303         new Ajax.Request("backend.php", {
1304                 parameters: "?op=pref-feeds&method=feedsortreset",
1305                 onComplete: function (transport) {
1306                         updateFeedList();
1307                 }
1308         });
1309 }
1310
1311 function resetCatOrder() {
1312         notify_progress("Loading, please wait...");
1313
1314         new Ajax.Request("backend.php", {
1315                 parameters: "?op=pref-feeds&method=catsortreset",
1316                 onComplete: function (transport) {
1317                         updateFeedList();
1318                 }
1319         });
1320 }
1321
1322 function editCat(id, item) {
1323         var new_name = prompt(__('Rename category to:'), item.name);
1324
1325         if (new_name && new_name != item.name) {
1326
1327                 notify_progress("Loading, please wait...");
1328
1329                 new Ajax.Request("backend.php", {
1330                         parameters: {
1331                                 op: 'pref-feeds',
1332                                 method: 'renamecat',
1333                                 id: id,
1334                                 title: new_name,
1335                         },
1336                         onComplete: function (transport) {
1337                                 updateFeedList();
1338                         }
1339                 });
1340         }
1341 }
1342
1343 function editLabel(id) {
1344         var query = "backend.php?op=pref-labels&method=edit&id=" +
1345                 param_escape(id);
1346
1347         if (dijit.byId("labelEditDlg"))
1348                 dijit.byId("labelEditDlg").destroyRecursive();
1349
1350         dialog = new dijit.Dialog({
1351                 id: "labelEditDlg",
1352                 title: __("Label Editor"),
1353                 style: "width: 600px",
1354                 setLabelColor: function (id, fg, bg) {
1355
1356                         var kind = '';
1357                         var color = '';
1358
1359                         if (fg && bg) {
1360                                 kind = 'both';
1361                         } else if (fg) {
1362                                 kind = 'fg';
1363                                 color = fg;
1364                         } else if (bg) {
1365                                 kind = 'bg';
1366                                 color = bg;
1367                         }
1368
1369                         var query = "?op=pref-labels&method=colorset&kind=" + kind +
1370                                 "&ids=" + param_escape(id) + "&fg=" + param_escape(fg) +
1371                                 "&bg=" + param_escape(bg) + "&color=" + param_escape(color);
1372
1373                         //              console.log(query);
1374
1375                         var e = $("LICID-" + id);
1376
1377                         if (e) {
1378                                 if (fg) e.style.color = fg;
1379                                 if (bg) e.style.backgroundColor = bg;
1380                         }
1381
1382                         new Ajax.Request("backend.php", {parameters: query});
1383
1384                         updateFilterList();
1385                 },
1386                 execute: function () {
1387                         if (this.validate()) {
1388                                 var caption = this.attr('value').caption;
1389                                 var fg_color = this.attr('value').fg_color;
1390                                 var bg_color = this.attr('value').bg_color;
1391                                 var query = dojo.objectToQuery(this.attr('value'));
1392
1393                                 dijit.byId('labelTree').setNameById(id, caption);
1394                                 this.setLabelColor(id, fg_color, bg_color);
1395                                 this.hide();
1396
1397                                 new Ajax.Request("backend.php", {
1398                                         parameters: query,
1399                                         onComplete: function (transport) {
1400                                                 updateFilterList();
1401                                         }
1402                                 });
1403                         }
1404                 },
1405                 href: query
1406         });
1407
1408         dialog.show();
1409 }
1410
1411
1412 function customizeCSS() {
1413         var query = "backend.php?op=pref-prefs&method=customizeCSS";
1414
1415         if (dijit.byId("cssEditDlg"))
1416                 dijit.byId("cssEditDlg").destroyRecursive();
1417
1418         dialog = new dijit.Dialog({
1419                 id: "cssEditDlg",
1420                 title: __("Customize stylesheet"),
1421                 style: "width: 600px",
1422                 execute: function () {
1423                         notify_progress('Saving data...', true);
1424                         new Ajax.Request("backend.php", {
1425                                 parameters: dojo.objectToQuery(this.attr('value')),
1426                                 onComplete: function (transport) {
1427                                         notify('');
1428                                         window.location.reload();
1429                                 }
1430                         });
1431
1432                 },
1433                 href: query
1434         });
1435
1436         dialog.show();
1437 }
1438
1439 function insertSSLserial(value) {
1440         dijit.byId("SSL_CERT_SERIAL").attr('value', value);
1441 }
1442
1443 function gotoExportOpml(filename, settings) {
1444         tmp = settings ? 1 : 0;
1445         document.location.href = "backend.php?op=opml&method=export&filename=" + filename + "&settings=" + tmp;
1446 }
1447
1448
1449 function batchSubscribe() {
1450         var query = "backend.php?op=pref-feeds&method=batchSubscribe";
1451
1452         // overlapping widgets
1453         if (dijit.byId("batchSubDlg")) dijit.byId("batchSubDlg").destroyRecursive();
1454         if (dijit.byId("feedAddDlg"))    dijit.byId("feedAddDlg").destroyRecursive();
1455
1456         var dialog = new dijit.Dialog({
1457                 id: "batchSubDlg",
1458                 title: __("Batch subscribe"),
1459                 style: "width: 600px",
1460                 execute: function () {
1461                         if (this.validate()) {
1462                                 console.log(dojo.objectToQuery(this.attr('value')));
1463
1464                                 notify_progress(__("Subscribing to feeds..."), true);
1465
1466                                 new Ajax.Request("backend.php", {
1467                                         parameters: dojo.objectToQuery(this.attr('value')),
1468                                         onComplete: function (transport) {
1469                                                 notify("");
1470                                                 updateFeedList();
1471                                                 dialog.hide();
1472                                         }
1473                                 });
1474                         }
1475                 },
1476                 href: query
1477         });
1478
1479         dialog.show();
1480 }
1481
1482 function clearPluginData(name) {
1483         if (confirm(__("Clear stored data for this plugin?"))) {
1484                 notify_progress("Loading, please wait...");
1485
1486                 new Ajax.Request("backend.php", {
1487                         parameters: "?op=pref-prefs&method=clearplugindata&name=" + param_escape(name),
1488                         onComplete: function(transport) {
1489                                 notify('');
1490                                 updatePrefsList();
1491                         } });
1492         }
1493 }
1494
1495 function clearSqlLog() {
1496
1497         if (confirm(__("Clear all messages in the error log?"))) {
1498
1499                 notify_progress("Loading, please wait...");
1500                 var query = "?op=pref-system&method=clearLog";
1501
1502                 new Ajax.Request("backend.php", {
1503                         parameters: query,
1504                         onComplete: function(transport) {
1505                                 updateSystemList();
1506                         } });
1507
1508         }
1509 }
1510
1511 function updateSelectedPrompt() {
1512         // no-op shim for toggleSelectedRow()
1513 }
1514