]> git.wh0rd.org Git - tt-rss.git/blob - js/prefs.js
bd3866991950c63eb25aef75f447044a1d659772
[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(sort_key) {
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 updateInstanceList(sort_key) {
28         new Ajax.Request("backend.php", {
29                 parameters: "?op=pref-instances&sort=" + param_escape(sort_key),
30                 onComplete: function(transport) {
31                         dijit.byId('instanceConfigTab').attr('content', transport.responseText);
32                         selectTab("instanceConfig", true);
33                         notify("");
34                 } });
35 }
36
37 function updateUsersList(sort_key) {
38         try {
39                 var user_search = $("user_search");
40                 var search = "";
41                 if (user_search) { search = user_search.value; }
42
43                 var query = "?op=pref-users&sort="
44                         + param_escape(sort_key) +
45                         "&search=" + param_escape(search);
46
47                 new Ajax.Request("backend.php", {
48                         parameters: query,
49                         onComplete: function(transport) {
50                                 dijit.byId('userConfigTab').attr('content', transport.responseText);
51                                 selectTab("userConfig", true)
52                                 notify("");
53                         } });
54
55         } catch (e) {
56                 exception_error("updateUsersList", e);
57         }
58 }
59
60 function addUser() {
61
62         try {
63
64                 var login = prompt(__("Please enter login:"), "");
65
66                 if (login == null) {
67                         return false;
68                 }
69
70                 if (login == "") {
71                         alert(__("Can't create user: no login specified."));
72                         return false;
73                 }
74
75                 notify_progress("Adding user...");
76
77                 var query = "?op=pref-users&method=add&login=" +
78                         param_escape(login);
79
80                 new Ajax.Request("backend.php", {
81                         parameters: query,
82                         onComplete: function(transport) {
83                                 notify_callback2(transport);
84                                 updateUsersList();
85                         } });
86
87         } catch (e) {
88                 exception_error("addUser", e);
89         }
90 }
91
92 function editUser(id, event) {
93
94         try {
95                 if (!event || !event.ctrlKey) {
96
97                 notify_progress("Loading, please wait...");
98
99                 selectTableRows('prefUserList', 'none');
100                 selectTableRowById('UMRR-'+id, 'UMCHK-'+id, true);
101
102                 var query = "?op=pref-users&method=edit&id=" +
103                         param_escape(id);
104
105                 new Ajax.Request("backend.php", {
106                         parameters: query,
107                         onComplete: function(transport) {
108                                         infobox_callback2(transport);
109                                         document.forms['user_edit_form'].login.focus();
110                                 } });
111
112                 } else if (event.ctrlKey) {
113                         var cb = $('UMCHK-' + id);
114                         cb.checked = !cb.checked;
115                         toggleSelectRow(cb);
116                 }
117
118         } catch (e) {
119                 exception_error("editUser", e);
120         }
121
122 }
123
124 function editFilter(id) {
125         try {
126
127                 var query = "backend.php?op=pref-filters&method=edit&id=" + param_escape(id);
128
129                 if (dijit.byId("filterEditDlg"))
130                         dijit.byId("filterEditDlg").destroyRecursive();
131
132                 dialog = new dijit.Dialog({
133                         id: "filterEditDlg",
134                         title: __("Edit Filter"),
135                         style: "width: 600px",
136                         removeFilter: function() {
137                                 var title = this.attr('value').reg_exp;
138                                 var msg = __("Remove filter %s?").replace("%s", title);
139
140                                 if (confirm(msg)) {
141                                         this.hide();
142
143                                         notify_progress("Removing filter...");
144
145                                         var id = this.attr('value').id;
146
147                                         var query = "?op=pref-filters&method=remove&ids="+
148                                                 param_escape(id);
149
150                                         new Ajax.Request("backend.php", {
151                                                 parameters: query,
152                                                 onComplete: function(transport) {
153                                                         updateFilterList();
154                                                 } });
155                                 }
156                         },
157                         test: function() {
158                                 if (this.validate()) {
159
160                                         if (dijit.byId("filterTestDlg"))
161                                                 dijit.byId("filterTestDlg").destroyRecursive();
162
163                                         tdialog = new dijit.Dialog({
164                                                 id: "filterTestDlg",
165                                                 title: __("Filter Test Results"),
166                                                 style: "width: 600px",
167                                                 href: "backend.php?savemode=test&" +
168                                                         dojo.objectToQuery(dialog.attr('value')),
169                                                 });
170
171                                         tdialog.show();
172
173                                 }
174                         },
175                         execute: function() {
176                                 if (this.validate()) {
177
178                                         var query = "?op=rpc&method=verifyRegexp&reg_exp=" +
179                                                 param_escape(dialog.attr('value').reg_exp);
180
181                                         notify_progress("Verifying regular expression...");
182
183                                         new Ajax.Request("backend.php", {
184                                                 parameters: query,
185                                                 onComplete: function(transport) {
186                                                         var reply = JSON.parse(transport.responseText);
187
188                                                         if (reply) {
189                                                                 notify('');
190
191                                                                 if (!reply['status']) {
192                                                                         alert("Match regular expression seems to be invalid.");
193                                                                         return;
194                                                                 } else {
195                                                                         notify_progress("Saving data...", true);
196
197                                                                         console.log(dojo.objectToQuery(dialog.attr('value')));
198
199                                                                         new Ajax.Request("backend.php", {
200                                                                                 parameters: dojo.objectToQuery(dialog.attr('value')),
201                                                                                 onComplete: function(transport) {
202                                                                                         dialog.hide();
203                                                                                         updateFilterList();
204                                                                         }});
205                                                                 }
206                                                         }
207                                         }});
208                                 }
209                         },
210                         href: query});
211
212                 dialog.show();
213
214
215         } catch (e) {
216                 exception_error("editFilter", e);
217         }
218 }
219
220 function getSelectedLabels() {
221         var tree = dijit.byId("labelTree");
222         var items = tree.model.getCheckedItems();
223         var rv = [];
224
225         items.each(function(item) {
226                 rv.push(tree.model.store.getValue(item, 'bare_id'));
227         });
228
229         return rv;
230 }
231
232 function getSelectedUsers() {
233         return getSelectedTableRowIds("prefUserList");
234 }
235
236 function getSelectedFeeds() {
237         var tree = dijit.byId("feedTree");
238         var items = tree.model.getCheckedItems();
239         var rv = [];
240
241         items.each(function(item) {
242                 if (item.id[0].match("FEED:"))
243                         rv.push(tree.model.store.getValue(item, 'bare_id'));
244         });
245
246         return rv;
247 }
248
249 function getSelectedFilters() {
250         var tree = dijit.byId("filterTree");
251         var items = tree.model.getCheckedItems();
252         var rv = [];
253
254         items.each(function(item) {
255                 rv.push(tree.model.store.getValue(item, 'bare_id'));
256         });
257
258         return rv;
259
260 }
261
262 /* function getSelectedFeedCats() {
263         return getSelectedTableRowIds("prefFeedCatList");
264 } */
265
266 function removeSelectedLabels() {
267
268         var sel_rows = getSelectedLabels();
269
270         if (sel_rows.length > 0) {
271
272                 var ok = confirm(__("Remove selected labels?"));
273
274                 if (ok) {
275                         notify_progress("Removing selected labels...");
276
277                         var query = "?op=pref-labels&method=remove&ids="+
278                                 param_escape(sel_rows.toString());
279
280                         new Ajax.Request("backend.php", {
281                                 parameters: query,
282                                 onComplete: function(transport) {
283                                                 updateLabelList();
284                                         } });
285
286                 }
287         } else {
288                 alert(__("No labels are selected."));
289         }
290
291         return false;
292 }
293
294 function removeSelectedUsers() {
295
296         try {
297
298                 var sel_rows = getSelectedUsers();
299
300                 if (sel_rows.length > 0) {
301
302                         var ok = confirm(__("Remove selected users? Neither default admin nor your account will be removed."));
303
304                         if (ok) {
305                                 notify_progress("Removing selected users...");
306
307                                 var query = "?op=pref-users&method=remove&ids="+
308                                         param_escape(sel_rows.toString());
309
310                                 new Ajax.Request("backend.php", {
311                                         parameters: query,
312                                         onComplete: function(transport) {
313                                                 updateUsersList();
314                                         } });
315
316                         }
317
318                 } else {
319                         alert(__("No users are selected."));
320                 }
321
322         } catch (e) {
323                 exception_error("removeSelectedUsers", e);
324         }
325
326         return false;
327 }
328
329 function removeSelectedFilters() {
330
331         try {
332
333                 var sel_rows = getSelectedFilters();
334
335                 if (sel_rows.length > 0) {
336
337                         var ok = confirm(__("Remove selected filters?"));
338
339                         if (ok) {
340                                 notify_progress("Removing selected filters...");
341
342                                 var query = "?op=pref-filters&method=remove&ids="+
343                                         param_escape(sel_rows.toString());
344
345                                 new Ajax.Request("backend.php", {
346                                                 parameters: query,
347                                                 onComplete: function(transport) {
348                                                         updateFilterList();
349                                                 } });
350                         }
351                 } else {
352                         alert(__("No filters are selected."));
353                 }
354
355         } catch (e) {
356                 exception_error("removeSelectedFilters", e);
357         }
358
359         return false;
360 }
361
362
363 function removeSelectedFeeds() {
364
365         try {
366
367                 var sel_rows = getSelectedFeeds();
368
369                 if (sel_rows.length > 0) {
370
371                         var ok = confirm(__("Unsubscribe from selected feeds?"));
372
373                         if (ok) {
374
375                                 notify_progress("Unsubscribing from selected feeds...", true);
376
377                                 var query = "?op=pref-feeds&method=remove&ids="+
378                                         param_escape(sel_rows.toString());
379
380                                 console.log(query);
381
382                                 new Ajax.Request("backend.php", {
383                                         parameters: query,
384                                         onComplete: function(transport) {
385                                                 updateFeedList();
386                                                 } });
387                         }
388
389                 } else {
390                         alert(__("No feeds are selected."));
391                 }
392
393         } catch (e) {
394                 exception_error("removeSelectedFeeds", e);
395         }
396
397         return false;
398 }
399
400 function clearSelectedFeeds() {
401
402         var sel_rows = getSelectedFeeds();
403
404         if (sel_rows.length > 1) {
405                 alert(__("Please select only one feed."));
406                 return;
407         }
408
409         if (sel_rows.length > 0) {
410
411                 var ok = confirm(__("Erase all non-starred articles in selected feed?"));
412
413                 if (ok) {
414                         notify_progress("Clearing selected feed...");
415                         clearFeedArticles(sel_rows[0]);
416                 }
417
418         } else {
419
420                 alert(__("No feeds are selected."));
421
422         }
423
424         return false;
425 }
426
427 function purgeSelectedFeeds() {
428
429         var sel_rows = getSelectedFeeds();
430
431         if (sel_rows.length > 0) {
432
433                 var pr = prompt(__("How many days of articles to keep (0 - use default)?"), "0");
434
435                 if (pr != undefined) {
436                         notify_progress("Purging selected feed...");
437
438                         var query = "?op=rpc&method=purge&ids="+
439                                 param_escape(sel_rows.toString()) + "&days=" + pr;
440
441                         console.log(query);
442
443                         new Ajax.Request("prefs.php",   {
444                                 parameters: query,
445                                 onComplete: function(transport) {
446                                         notify('');
447                                 } });
448                 }
449
450         } else {
451
452                 alert(__("No feeds are selected."));
453
454         }
455
456         return false;
457 }
458
459 function userEditCancel() {
460         closeInfoBox();
461         return false;
462 }
463
464 function userEditSave() {
465
466         try {
467
468                 var login = document.forms["user_edit_form"].login.value;
469
470                 if (login.length == 0) {
471                         alert(__("Login field cannot be blank."));
472                         return;
473                 }
474
475                 notify_progress("Saving user...");
476
477                 closeInfoBox();
478
479                 var query = Form.serialize("user_edit_form");
480
481                 new Ajax.Request("backend.php", {
482                         parameters: query,
483                         onComplete: function(transport) {
484                                 updateUsersList();
485                         } });
486
487         } catch (e) {
488                 exception_error("userEditSave", e);
489         }
490
491         return false;
492
493 }
494
495
496 function editSelectedUser() {
497         var rows = getSelectedUsers();
498
499         if (rows.length == 0) {
500                 alert(__("No users are selected."));
501                 return;
502         }
503
504         if (rows.length > 1) {
505                 alert(__("Please select only one user."));
506                 return;
507         }
508
509         notify("");
510
511         editUser(rows[0]);
512 }
513
514 function resetSelectedUserPass() {
515
516         try {
517
518                 var rows = getSelectedUsers();
519
520                 if (rows.length == 0) {
521                         alert(__("No users are selected."));
522                         return;
523                 }
524
525                 if (rows.length > 1) {
526                         alert(__("Please select only one user."));
527                         return;
528                 }
529
530                 var ok = confirm(__("Reset password of selected user?"));
531
532                 if (ok) {
533                         notify_progress("Resetting password for selected user...");
534
535                         var id = rows[0];
536
537                         var query = "?op=pref-users&method=resetPass&id=" +
538                                 param_escape(id);
539
540                         new Ajax.Request("backend.php", {
541                                 parameters: query,
542                                 onComplete: function(transport) {
543                                         notify_info(transport.responseText);
544                                 } });
545
546                 }
547
548         } catch (e) {
549                 exception_error("resetSelectedUserPass", e);
550         }
551 }
552
553 function selectedUserDetails() {
554
555         try {
556
557                 var rows = getSelectedUsers();
558
559                 if (rows.length == 0) {
560                         alert(__("No users are selected."));
561                         return;
562                 }
563
564                 if (rows.length > 1) {
565                         alert(__("Please select only one user."));
566                         return;
567                 }
568
569                 notify_progress("Loading, please wait...");
570
571                 var id = rows[0];
572
573                 var query = "?op=pref-users&method=userdetails&id=" + id;
574
575                 new Ajax.Request("backend.php", {
576                         parameters: query,
577                         onComplete: function(transport) {
578                                         infobox_callback2(transport);
579                                 } });
580         } catch (e) {
581                 exception_error("selectedUserDetails", e);
582         }
583 }
584
585
586 function editSelectedFilter() {
587         var rows = getSelectedFilters();
588
589         if (rows.length == 0) {
590                 alert(__("No filters are selected."));
591                 return;
592         }
593
594         if (rows.length > 1) {
595                 alert(__("Please select only one filter."));
596                 return;
597         }
598
599         notify("");
600
601         editFilter(rows[0]);
602
603 }
604
605
606 function editSelectedFeed() {
607         var rows = getSelectedFeeds();
608
609         if (rows.length == 0) {
610                 alert(__("No feeds are selected."));
611                 return;
612         }
613
614         if (rows.length > 1) {
615                 return editSelectedFeeds();
616         }
617
618         notify("");
619
620         editFeed(rows[0], {});
621
622 }
623
624 function editSelectedFeeds() {
625
626         try {
627                 var rows = getSelectedFeeds();
628
629                 if (rows.length == 0) {
630                         alert(__("No feeds are selected."));
631                         return;
632                 }
633
634                 notify_progress("Loading, please wait...");
635
636                 var query = "backend.php?op=pref-feeds&method=editfeeds&ids=" +
637                         param_escape(rows.toString());
638
639                 console.log(query);
640
641                 if (dijit.byId("feedEditDlg"))
642                         dijit.byId("feedEditDlg").destroyRecursive();
643
644                 new Ajax.Request("backend.php", {
645                         parameters: query,
646                         onComplete: function(transport) {
647
648                                 notify("");
649
650                                 var dialog = new dijit.Dialog({
651                                         id: "feedEditDlg",
652                                         title: __("Edit Multiple Feeds"),
653                                         style: "width: 600px",
654                                         getChildByName: function (name) {
655                                                 var rv = null;
656                                                 this.getChildren().each(
657                                                         function(child) {
658                                                                 if (child.name == name) {
659                                                                         rv = child;
660                                                                         return;
661                                                                 }
662                                                         });
663                                                 return rv;
664                                         },
665                                         toggleField: function (checkbox, elem, label) {
666                                                 this.getChildByName(elem).attr('disabled', !checkbox.checked);
667
668                                                 if ($(label))
669                                                         if (checkbox.checked)
670                                                                 $(label).removeClassName('insensitive');
671                                                         else
672                                                                 $(label).addClassName('insensitive');
673
674                                         },
675                                         execute: function() {
676                                                 if (this.validate() && confirm(__("Save changes to selected feeds?"))) {
677                                                         var query = dojo.objectToQuery(this.attr('value'));
678
679                                                         /* Form.serialize ignores unchecked checkboxes */
680
681                                                         if (!query.match("&rtl_content=") &&
682                                                                         this.getChildByName('rtl_content').attr('disabled') == false) {
683                                                                 query = query + "&rtl_content=false";
684                                                         }
685
686                                                         if (!query.match("&private=") &&
687                                                                         this.getChildByName('private').attr('disabled') == false) {
688                                                                 query = query + "&private=false";
689                                                         }
690
691                                                         try {
692                                                                 if (!query.match("&cache_images=") &&
693                                                                                 this.getChildByName('cache_images').attr('disabled') == false) {
694                                                                         query = query + "&cache_images=false";
695                                                                 }
696                                                         } catch (e) { }
697
698                                                         if (!query.match("&include_in_digest=") &&
699                                                                         this.getChildByName('include_in_digest').attr('disabled') == false) {
700                                                                 query = query + "&include_in_digest=false";
701                                                         }
702
703                                                         if (!query.match("&always_display_enclosures=") &&
704                                                                         this.getChildByName('always_display_enclosures').attr('disabled') == false) {
705                                                                 query = query + "&always_display_enclosures=false";
706                                                         }
707
708                                                         if (!query.match("&mark_unread_on_update=") &&
709                                                                         this.getChildByName('mark_unread_on_update').attr('disabled') == false) {
710                                                                 query = query + "&mark_unread_on_update=false";
711                                                         }
712
713                                                         if (!query.match("&update_on_checksum_change=") &&
714                                                                         this.getChildByName('update_on_checksum_change').attr('disabled') == false) {
715                                                                 query = query + "&update_on_checksum_change=false";
716                                                         }
717
718                                                         console.log(query);
719
720                                                         notify_progress("Saving data...", true);
721
722                                                         new Ajax.Request("backend.php", {
723                                                                 parameters: query,
724                                                                 onComplete: function(transport) {
725                                                                         dialog.hide();
726                                                                         updateFeedList();
727                                                         }});
728                                                 }
729                                         },
730                                         content: transport.responseText});
731
732                                         dialog.show();
733
734                         } });
735
736         } catch (e) {
737                 exception_error("editSelectedFeeds", e);
738         }
739 }
740
741 function piggie(enable) {
742         if (enable) {
743                 console.log("I LOVEDED IT!");
744                 var piggie = $("piggie");
745
746                 Element.show(piggie);
747                 Position.Center(piggie);
748                 Effect.Puff(piggie);
749
750         }
751 }
752
753 function opmlImportComplete(iframe) {
754         try {
755                 if (!iframe.contentDocument.body.innerHTML) return false;
756
757                 notify('');
758
759                 if (dijit.byId('opmlImportDlg'))
760                         dijit.byId('opmlImportDlg').destroyRecursive();
761
762                 var content = iframe.contentDocument.body.innerHTML;
763
764                 dialog = new dijit.Dialog({
765                         id: "opmlImportDlg",
766                         title: __("OPML Import"),
767                         style: "width: 600px",
768                         onCancel: function() {
769                                 updateFeedList();
770                         },
771                         content: content});
772
773                 dialog.show();
774
775         } catch (e) {
776                 exception_error("opmlImportComplete", e);
777         }
778 }
779
780 function opmlImport() {
781
782         var opml_file = $("opml_file");
783
784         if (opml_file.value.length == 0) {
785                 alert(__("Please choose an OPML file first."));
786                 return false;
787         } else {
788                 notify_progress("Importing, please wait...", true);
789                 return true;
790         }
791 }
792
793 function updateFilterList() {
794         new Ajax.Request("backend.php", {
795                 parameters: "?op=pref-filters",
796                 onComplete: function(transport) {
797                         dijit.byId('filterConfigTab').attr('content', transport.responseText);
798                         notify("");
799                 } });
800 }
801
802 function updateLabelList() {
803         new Ajax.Request("backend.php", {
804                 parameters: "?op=pref-labels",
805                 onComplete: function(transport) {
806                         dijit.byId('labelConfigTab').attr('content', transport.responseText);
807                         notify("");
808                 } });
809 }
810
811 function updatePrefsList() {
812         new Ajax.Request("backend.php", {
813                 parameters: "?op=pref-prefs",
814                 onComplete: function(transport) {
815                         dijit.byId('genConfigTab').attr('content', transport.responseText);
816                         notify("");
817                 } });
818 }
819
820 function selectTab(id, noupdate, method) {
821         try {
822                 if (!noupdate) {
823                         notify_progress("Loading, please wait...");
824
825                         if (id == "feedConfig") {
826                                 updateFeedList();
827                         } else if (id == "filterConfig") {
828                                 updateFilterList();
829                         } else if (id == "labelConfig") {
830                                 updateLabelList();
831                         } else if (id == "genConfig") {
832                                 updatePrefsList();
833                         } else if (id == "userConfig") {
834                                 updateUsersList();
835                         }
836
837                         var tab = dijit.byId(id + "Tab");
838                         dijit.byId("pref-tabs").selectChild(tab);
839
840                 }
841
842         } catch (e) {
843                 exception_error("selectTab", e);
844         }
845 }
846
847 function init_second_stage() {
848         try {
849
850                 document.onkeydown = pref_hotkey_handler;
851                 loading_set_progress(50);
852                 notify("");
853
854                 dojo.addOnLoad(function() {
855                         var tab = getURLParam('tab');
856
857                         if (tab) {
858                                 tab = dijit.byId(tab + "Tab");
859                                 if (tab) dijit.byId("pref-tabs").selectChild(tab);
860                         }
861
862                         var method = getURLParam('method');
863
864                         if (method == 'editFeed') {
865                                 var param = getURLParam('methodparam');
866
867                                 window.setTimeout('editFeed(' + param + ')', 100);
868                         }
869                 });
870
871                 setTimeout("hotkey_prefix_timeout()", 5*1000);
872
873         } catch (e) {
874                 exception_error("init_second_stage", e);
875         }
876 }
877
878 function init() {
879
880         try {
881                 dojo.registerModulePath("lib", "..");
882                 dojo.registerModulePath("fox", "../../js/");
883
884                 dojo.require("lib.CheckBoxTree");
885                 dojo.require("fox.PrefFeedTree");
886                 dojo.require("fox.PrefFilterTree");
887                 dojo.require("fox.PrefLabelTree");
888
889                 dojo.parser.parse();
890
891                 dojo.addOnLoad(function() {
892                         loading_set_progress(50);
893
894                         new Ajax.Request("backend.php", {
895                                 parameters: {op: "rpc", method: "sanityCheck"},
896                                         onComplete: function(transport) {
897                                         backend_sanity_check_callback(transport);
898                                 } });
899                 });
900
901         } catch (e) {
902                 exception_error("init", e);
903         }
904 }
905
906 function validatePrefsReset() {
907         try {
908                 var ok = confirm(__("Reset to defaults?"));
909
910                 if (ok) {
911
912                         query = "?op=pref-prefs&method=resetconfig";
913                         console.log(query);
914
915                         new Ajax.Request("backend.php", {
916                                 parameters: query,
917                                 onComplete: function(transport) {
918                                         var msg = transport.responseText;
919                                         if (msg.match("PREFS_THEME_CHANGED")) {
920                                                 window.location.reload();
921                                         } else {
922                                                 notify_info(msg);
923                                                 selectTab();
924                                         }
925                                 } });
926
927                 }
928
929         } catch (e) {
930                 exception_error("validatePrefsReset", e);
931         }
932
933         return false;
934
935 }
936
937
938 function pref_hotkey_handler(e) {
939         try {
940                 if (e.target.nodeName == "INPUT") return;
941
942                 var keycode = false;
943                 var shift_key = false;
944
945                 var cmdline = $('cmdline');
946
947                 try {
948                         shift_key = e.shiftKey;
949                 } catch (e) {
950
951                 }
952
953                 if (window.event) {
954                         keycode = window.event.keyCode;
955                 } else if (e) {
956                         keycode = e.which;
957                 }
958
959                 var keychar = String.fromCharCode(keycode);
960
961                 if (keycode == 27) { // escape
962                         if (Element.visible("hotkey_help_overlay")) {
963                                 Element.hide("hotkey_help_overlay");
964                         }
965                         hotkey_prefix = false;
966                         closeInfoBox();
967                 }
968
969                 if (keycode == 16) return; // ignore lone shift
970                 if (keycode == 17) return; // ignore lone ctrl
971
972                 if ((keycode == 67 || keycode == 71) && !hotkey_prefix) {
973                         hotkey_prefix = keycode;
974
975                         var date = new Date();
976                         var ts = Math.round(date.getTime() / 1000);
977
978                         hotkey_prefix_pressed = ts;
979
980                         cmdline.innerHTML = keychar;
981                         Element.show(cmdline);
982
983                         console.log("KP: PREFIX=" + keycode + " CHAR=" + keychar);
984                         return;
985                 }
986
987                 if (Element.visible("hotkey_help_overlay")) {
988                         Element.hide("hotkey_help_overlay");
989                 }
990
991                 if (keycode == 13 || keycode == 27) {
992                         seq = "";
993                 } else {
994                         seq = seq + "" + keycode;
995                 }
996
997                 /* Global hotkeys */
998
999                 Element.hide(cmdline);
1000
1001                 if (!hotkey_prefix) {
1002
1003                         if ((keycode == 191 || keychar == '?') && shift_key) { // ?
1004                                 showHelp();
1005                                 return false;
1006                         }
1007
1008                         if (keycode == 191 || keychar == '/') { // /
1009                                 var search_boxes = new Array("label_search",
1010                                         "feed_search", "filter_search", "user_search", "feed_browser_search");
1011
1012                                 for (var i = 0; i < search_boxes.length; i++) {
1013                                         var elem = $(search_boxes[i]);
1014                                         if (elem) {
1015                                                 $(search_boxes[i]).focus();
1016                                                 return false;
1017                                         }
1018                                 }
1019                         }
1020                 }
1021
1022                 /* Prefix c */
1023
1024                 if (hotkey_prefix == 67) { // c
1025                         hotkey_prefix = false;
1026
1027                         if (keycode == 70) { // f
1028                                 quickAddFilter();
1029                                 return false;
1030                         }
1031
1032                         if (keycode == 83) { // s
1033                                 quickAddFeed();
1034                                 return false;
1035                         }
1036
1037                         if (keycode == 85) { // u
1038                                 // no-op
1039                         }
1040
1041                         if (keycode == 67) { // c
1042                                 editFeedCats();
1043                                 return false;
1044                         }
1045
1046                         if (keycode == 84 && shift_key) { // T
1047                                 feedBrowser();
1048                                 return false;
1049                         }
1050
1051                 }
1052
1053                 /* Prefix g */
1054
1055                 if (hotkey_prefix == 71) { // g
1056
1057                         hotkey_prefix = false;
1058
1059                         if (keycode == 49 && $("genConfigTab")) { // 1
1060                                 selectTab("genConfig");
1061                                 return false;
1062                         }
1063
1064                         if (keycode == 50 && $("feedConfigTab")) { // 2
1065                                 selectTab("feedConfig");
1066                                 return false;
1067                         }
1068
1069                         if (keycode == 51 && $("filterConfigTab")) { // 4
1070                                 selectTab("filterConfig");
1071                                 return false;
1072                         }
1073
1074                         if (keycode == 52 && $("labelConfigTab")) { // 5
1075                                 selectTab("labelConfig");
1076                                 return false;
1077                         }
1078
1079                         if (keycode == 53 && $("userConfigTab")) { // 6
1080                                 selectTab("userConfig");
1081                                 return false;
1082                         }
1083
1084                         if (keycode == 88) { // x
1085                                 return gotoMain();
1086                         }
1087
1088                 }
1089
1090                 if ($("piggie")) {
1091                         if (seq.match("8073717369")) {
1092                                 seq = "";
1093                                 piggie(true);
1094                         } else {
1095                                 piggie(false);
1096                         }
1097                 }
1098
1099                 if (hotkey_prefix) {
1100                         console.log("KP: PREFIX=" + hotkey_prefix + " CODE=" + keycode + " CHAR=" + keychar);
1101                 } else {
1102                         console.log("KP: CODE=" + keycode + " CHAR=" + keychar);
1103                 }
1104
1105         } catch (e) {
1106                 exception_error("pref_hotkey_handler", e);
1107         }
1108 }
1109
1110 function editFeedCats() {
1111         try {
1112                 var query = "backend.php?op=pref-feeds&method=editCats";
1113
1114                 if (dijit.byId("feedCatEditDlg"))
1115                         dijit.byId("feedCatEditDlg").destroyRecursive();
1116
1117                 dialog = new dijit.Dialog({
1118                         id: "feedCatEditDlg",
1119                         title: __("Feed Categories"),
1120                         style: "width: 600px",
1121                         getSelectedCategories: function() {
1122                                 return getSelectedTableRowIds("prefFeedCatList");
1123                         },
1124                         removeSelected: function() {
1125                                 var sel_rows = this.getSelectedCategories();
1126
1127                                 if (sel_rows.length > 0) {
1128                                         var ok = confirm(__("Remove selected categories?"));
1129
1130                                         if (ok) {
1131                                                 notify_progress("Removing selected categories...", true);
1132
1133                                                 var query = "?op=pref-feeds&method=editCats&action=remove&ids="+
1134                                                         param_escape(sel_rows.toString());
1135
1136                                                 new Ajax.Request("backend.php", {
1137                                                         parameters: query,
1138                                                         onComplete: function(transport) {
1139                                                                 notify('');
1140                                                                 dialog.attr('content', transport.responseText);
1141                                                                 updateFeedList();
1142                                                         } });
1143
1144                                         }
1145
1146                                 } else {
1147                                         alert(__("No categories are selected."));
1148                                 }
1149                         },
1150                         addCategory: function() {
1151                                 if (this.validate()) {
1152                                         notify_progress("Creating category...");
1153
1154                                         var query = "?op=pref-feeds&method=editCats&action=add&cat=" +
1155                                                 param_escape(this.attr('value').newcat);
1156
1157                                         new Ajax.Request("backend.php", {
1158                                                 parameters: query,
1159                                                 onComplete: function(transport) {
1160                                                         notify('');
1161                                                         dialog.attr('content', transport.responseText);
1162                                                         updateFeedList();
1163                                                 } });
1164                                 }
1165                         },
1166                         execute: function() {
1167                                 if (this.validate()) {
1168                                 }
1169                         },
1170                         href: query});
1171
1172                 dialog.show();
1173
1174         } catch (e) {
1175                 exception_error("editFeedCats", e);
1176         }
1177 }
1178
1179 function showInactiveFeeds() {
1180         try {
1181                 var query = "backend.php?op=dlg&method=inactiveFeeds";
1182
1183                 if (dijit.byId("inactiveFeedsDlg"))
1184                         dijit.byId("inactiveFeedsDlg").destroyRecursive();
1185
1186                 dialog = new dijit.Dialog({
1187                         id: "inactiveFeedsDlg",
1188                         title: __("Feeds without recent updates"),
1189                         style: "width: 600px",
1190                         getSelectedFeeds: function() {
1191                                 return getSelectedTableRowIds("prefInactiveFeedList");
1192                         },
1193                         removeSelected: function() {
1194                                 var sel_rows = this.getSelectedFeeds();
1195
1196                                 console.log(sel_rows);
1197
1198                                 if (sel_rows.length > 0) {
1199                                         var ok = confirm(__("Remove selected feeds?"));
1200
1201                                         if (ok) {
1202                                                 notify_progress("Removing selected feeds...", true);
1203
1204                                                 var query = "?op=pref-feeds&method=remove&ids="+
1205                                                         param_escape(sel_rows.toString());
1206
1207                                                 new Ajax.Request("backend.php", {
1208                                                         parameters: query,
1209                                                         onComplete: function(transport) {
1210                                                                 notify('');
1211                                                                 dialog.hide();
1212                                                                 updateFeedList();
1213                                                         } });
1214                                         }
1215
1216                                 } else {
1217                                         alert(__("No feeds are selected."));
1218                                 }
1219                         },
1220                         execute: function() {
1221                                 if (this.validate()) {
1222                                 }
1223                         },
1224                         href: query});
1225
1226                 dialog.show();
1227
1228         } catch (e) {
1229                 exception_error("showInactiveFeeds", e);
1230         }
1231
1232 }
1233
1234 function opmlRegenKey() {
1235
1236         try {
1237                 var ok = confirm(__("Replace current OPML publishing address with a new one?"));
1238
1239                 if (ok) {
1240
1241                         notify_progress("Trying to change address...", true);
1242
1243                         var query = "?op=rpc&method=regenOPMLKey";
1244
1245                         new Ajax.Request("backend.php", {
1246                                 parameters: query,
1247                                 onComplete: function(transport) {
1248                                                 var reply = JSON.parse(transport.responseText);
1249
1250                                                 var new_link = reply.link;
1251
1252                                                 var e = $('pub_opml_url');
1253
1254                                                 if (new_link) {
1255                                                         e.href = new_link;
1256                                                         e.innerHTML = new_link;
1257
1258                                                         new Effect.Highlight(e);
1259
1260                                                         notify('');
1261
1262                                                 } else {
1263                                                         notify_error("Could not change feed URL.");
1264                                                 }
1265                                 } });
1266                 }
1267         } catch (e) {
1268                 exception_error("opmlRegenKey", e);
1269         }
1270         return false;
1271 }
1272
1273 function feedActionChange() {
1274         try {
1275                 var chooser = $("feedActionChooser");
1276                 var opid = chooser[chooser.selectedIndex].value;
1277
1278                 chooser.selectedIndex = 0;
1279                 feedActionGo(opid);
1280         } catch (e) {
1281                 exception_error("feedActionChange", e);
1282         }
1283 }
1284
1285 function feedActionGo(op) {
1286         try {
1287                 if (op == "facEdit") {
1288
1289                         var rows = getSelectedFeeds();
1290
1291                         if (rows.length > 1) {
1292                                 editSelectedFeeds();
1293                         } else {
1294                                 editSelectedFeed();
1295                         }
1296                 }
1297
1298                 if (op == "facClear") {
1299                         clearSelectedFeeds();
1300                 }
1301
1302                 if (op == "facPurge") {
1303                         purgeSelectedFeeds();
1304                 }
1305
1306                 if (op == "facEditCats") {
1307                         editFeedCats();
1308                 }
1309
1310                 if (op == "facRescore") {
1311                         rescoreSelectedFeeds();
1312                 }
1313
1314                 if (op == "facUnsubscribe") {
1315                         removeSelectedFeeds();
1316                 }
1317
1318         } catch (e) {
1319                 exception_error("feedActionGo", e);
1320
1321         }
1322 }
1323
1324 function clearFeedArticles(feed_id) {
1325
1326         notify_progress("Clearing feed...");
1327
1328         var query = "?op=pref-feeds&quiet=1&method=clear&id=" + feed_id;
1329
1330         new Ajax.Request("backend.php", {
1331                 parameters: query,
1332                 onComplete: function(transport) {
1333                                 notify('');
1334                         } });
1335
1336         return false;
1337 }
1338
1339 function rescoreSelectedFeeds() {
1340
1341         var sel_rows = getSelectedFeeds();
1342
1343         if (sel_rows.length > 0) {
1344
1345                 //var ok = confirm(__("Rescore last 100 articles in selected feeds?"));
1346                 var ok = confirm(__("Rescore articles in selected feeds?"));
1347
1348                 if (ok) {
1349                         notify_progress("Rescoring selected feeds...", true);
1350
1351                         var query = "?op=pref-feeds&method=rescore&quiet=1&ids="+
1352                                 param_escape(sel_rows.toString());
1353
1354                         new Ajax.Request("backend.php", {
1355                                 parameters: query,
1356                                 onComplete: function(transport) {
1357                                                 notify_callback2(transport);
1358                         } });
1359
1360                 }
1361         } else {
1362                 alert(__("No feeds are selected."));
1363         }
1364
1365         return false;
1366 }
1367
1368 function rescore_all_feeds() {
1369         var ok = confirm(__("Rescore all articles? This operation may take a lot of time."));
1370
1371         if (ok) {
1372                 notify_progress("Rescoring feeds...", true);
1373
1374                 var query = "?op=pref-feeds&method=rescoreAll&quiet=1";
1375
1376                 new Ajax.Request("backend.php", {
1377                         parameters: query,
1378                         onComplete: function(transport) {
1379                                         notify_callback2(transport);
1380                 } });
1381         }
1382 }
1383
1384 function labelColorReset() {
1385         try {
1386                 var labels = getSelectedLabels();
1387
1388                 if (labels.length > 0) {
1389                         var ok = confirm(__("Reset selected labels to default colors?"));
1390
1391                         if (ok) {
1392                                 var query = "?op=pref-labels&method=colorreset&ids="+
1393                                         param_escape(labels.toString());
1394
1395                                 new Ajax.Request("backend.php", {
1396                                         parameters: query,
1397                                         onComplete: function(transport) {
1398                                                 updateLabelList();
1399                                         } });
1400                         }
1401
1402                 } else {
1403                         alert(__("No labels are selected."));
1404                 }
1405
1406         } catch (e) {
1407                 exception_error("labelColorReset", e);
1408         }
1409 }
1410
1411
1412 function inPreferences() {
1413         return true;
1414 }
1415
1416 function editProfiles() {
1417         try {
1418
1419                 if (dijit.byId("profileEditDlg"))
1420                         dijit.byId("profileEditDlg").destroyRecursive();
1421
1422                 var query = "backend.php?op=dlg&method=editPrefProfiles";
1423
1424                 dialog = new dijit.Dialog({
1425                         id: "profileEditDlg",
1426                         title: __("Settings Profiles"),
1427                         style: "width: 600px",
1428                         getSelectedProfiles: function() {
1429                                 return getSelectedTableRowIds("prefFeedProfileList");
1430                         },
1431                         removeSelected: function() {
1432                                 var sel_rows = this.getSelectedProfiles();
1433
1434                                 if (sel_rows.length > 0) {
1435                                         var ok = confirm(__("Remove selected profiles? Active and default profiles will not be removed."));
1436
1437                                         if (ok) {
1438                                                 notify_progress("Removing selected profiles...", true);
1439
1440                                                 var query = "?op=rpc&method=remprofiles&ids="+
1441                                                         param_escape(sel_rows.toString());
1442
1443                                                 new Ajax.Request("backend.php", {
1444                                                         parameters: query,
1445                                                         onComplete: function(transport) {
1446                                                                 notify('');
1447                                                                 editProfiles();
1448                                                         } });
1449
1450                                         }
1451
1452                                 } else {
1453                                         alert(__("No profiles are selected."));
1454                                 }
1455                         },
1456                         activateProfile: function() {
1457                                 var sel_rows = this.getSelectedProfiles();
1458
1459                                 if (sel_rows.length == 1) {
1460
1461                                         var ok = confirm(__("Activate selected profile?"));
1462
1463                                         if (ok) {
1464                                                 notify_progress("Loading, please wait...");
1465
1466                                                 var query = "?op=rpc&method=setprofile&id="+
1467                                                         param_escape(sel_rows.toString());
1468
1469                                                 new Ajax.Request("backend.php", {
1470                                                         parameters: query,
1471                                                         onComplete: function(transport) {
1472                                                                 window.location.reload();
1473                                                         } });
1474                                         }
1475
1476                                 } else {
1477                                         alert(__("Please choose a profile to activate."));
1478                                 }
1479                         },
1480                         addProfile: function() {
1481                                 if (this.validate()) {
1482                                         notify_progress("Creating profile...", true);
1483
1484                                         var query = "?op=rpc&method=addprofile&title=" +
1485                                                 param_escape(dialog.attr('value').newprofile);
1486
1487                                         new Ajax.Request("backend.php", {
1488                                                 parameters: query,
1489                                                 onComplete: function(transport) {
1490                                                         notify('');
1491                                                         editProfiles();
1492                                                 } });
1493
1494                                 }
1495                         },
1496                         execute: function() {
1497                                 if (this.validate()) {
1498                                 }
1499                         },
1500                         href: query});
1501
1502                 dialog.show();
1503         } catch (e) {
1504                 exception_error("editProfiles", e);
1505         }
1506 }
1507
1508 function activatePrefProfile() {
1509
1510         var sel_rows = getSelectedFeedCats();
1511
1512         if (sel_rows.length == 1) {
1513
1514                 var ok = confirm(__("Activate selected profile?"));
1515
1516                 if (ok) {
1517                         notify_progress("Loading, please wait...");
1518
1519                         var query = "?op=rpc&method=setprofile&id="+
1520                                 param_escape(sel_rows.toString());
1521
1522                         new Ajax.Request("backend.php", {
1523                                 parameters: query,
1524                                 onComplete: function(transport) {
1525                                         window.location.reload();
1526                                 } });
1527                 }
1528
1529         } else {
1530                 alert(__("Please choose a profile to activate."));
1531         }
1532
1533         return false;
1534 }
1535
1536 function clearFeedAccessKeys() {
1537
1538         var ok = confirm(__("This will invalidate all previously generated feed URLs. Continue?"));
1539
1540         if (ok) {
1541                 notify_progress("Clearing URLs...");
1542
1543                 var query = "?op=rpc&method=clearKeys";
1544
1545                 new Ajax.Request("backend.php", {
1546                         parameters: query,
1547                         onComplete: function(transport) {
1548                                 notify_info("Generated URLs cleared.");
1549                         } });
1550         }
1551
1552         return false;
1553 }
1554
1555 function clearArticleAccessKeys() {
1556
1557         var ok = confirm(__("This will invalidate all previously shared article URLs. Continue?"));
1558
1559         if (ok) {
1560                 notify_progress("Clearing URLs...");
1561
1562                 var query = "?op=rpc&method=clearArticleKeys";
1563
1564                 new Ajax.Request("backend.php", {
1565                         parameters: query,
1566                         onComplete: function(transport) {
1567                                 notify_info("Shared URLs cleared.");
1568                         } });
1569         }
1570
1571         return false;
1572 }
1573 function resetFeedOrder() {
1574         try {
1575                 notify_progress("Loading, please wait...");
1576
1577                 new Ajax.Request("backend.php", {
1578                         parameters: "?op=pref-feeds&method=feedsortreset",
1579                         onComplete: function(transport) {
1580                                 updateFeedList();
1581                         } });
1582
1583
1584         } catch (e) {
1585                 exception_error("resetFeedOrder");
1586         }
1587 }
1588
1589 function resetCatOrder() {
1590         try {
1591                 notify_progress("Loading, please wait...");
1592
1593                 new Ajax.Request("backend.php", {
1594                         parameters: "?op=pref-feeds&method=catsortreset",
1595                         onComplete: function(transport) {
1596                                 updateFeedList();
1597                         } });
1598
1599
1600         } catch (e) {
1601                 exception_error("resetCatOrder");
1602         }
1603 }
1604
1605 function editCat(id, item, event) {
1606         try {
1607                 var new_name = prompt(__('Rename category to:'), item.name);
1608
1609                 if (new_name && new_name != item.name) {
1610
1611                         notify_progress("Loading, please wait...");
1612
1613                         new Ajax.Request("backend.php", {
1614                         parameters: {
1615                                 op: 'pref-feeds',
1616                                 method: 'renamecat',
1617                                 id: id,
1618                                 title: new_name,
1619                         },
1620                         onComplete: function(transport) {
1621                                 updateFeedList();
1622                         } });
1623                 }
1624
1625         } catch (e) {
1626                 exception_error("editCat", e);
1627         }
1628 }
1629
1630 function editLabel(id, event) {
1631         try {
1632                 var query = "backend.php?op=pref-labels&method=edit&id=" +
1633                         param_escape(id);
1634
1635                 if (dijit.byId("labelEditDlg"))
1636                         dijit.byId("labelEditDlg").destroyRecursive();
1637
1638                 dialog = new dijit.Dialog({
1639                         id: "labelEditDlg",
1640                         title: __("Label Editor"),
1641                         style: "width: 600px",
1642                         setLabelColor: function(id, fg, bg) {
1643
1644                                 var kind = '';
1645                                 var color = '';
1646
1647                                 if (fg && bg) {
1648                                         kind = 'both';
1649                                 } else if (fg) {
1650                                         kind = 'fg';
1651                                         color = fg;
1652                                 } else if (bg) {
1653                                         kind = 'bg';
1654                                         color = bg;
1655                                 }
1656
1657                                 var query = "?op=pref-labels&method=colorset&kind="+kind+
1658                                         "&ids=" + param_escape(id) + "&fg=" + param_escape(fg) +
1659                                         "&bg=" + param_escape(bg) + "&color=" + param_escape(color);
1660
1661                 //              console.log(query);
1662
1663                                 var e = $("LICID-" + id);
1664
1665                                 if (e) {
1666                                         if (fg) e.style.color = fg;
1667                                         if (bg) e.style.backgroundColor = bg;
1668                                 }
1669
1670                                 new Ajax.Request("backend.php", { parameters: query });
1671
1672                                 updateFilterList();
1673                         },
1674                         execute: function() {
1675                                 if (this.validate()) {
1676                                         var caption = this.attr('value').caption;
1677                                         var fg_color = this.attr('value').fg_color;
1678                                         var bg_color = this.attr('value').bg_color;
1679                                         var query = dojo.objectToQuery(this.attr('value'));
1680
1681                                         dijit.byId('labelTree').setNameById(id, caption);
1682                                         this.setLabelColor(id, fg_color, bg_color);
1683                                         this.hide();
1684
1685                                         new Ajax.Request("backend.php", {
1686                                                 parameters: query,
1687                                                 onComplete: function(transport) {
1688                                                         updateFilterList();
1689                                         } });
1690                                 }
1691                         },
1692                         href: query});
1693
1694                 dialog.show();
1695
1696         } catch (e) {
1697                 exception_error("editLabel", e);
1698         }
1699 }
1700
1701 function clearTwitterCredentials() {
1702         try {
1703                 var ok = confirm(__("This will clear your stored authentication information for Twitter. Continue?"));
1704
1705                 if (ok) {
1706                         notify_progress("Clearing credentials...");
1707
1708                         var query = "?op=pref-feeds&method=remtwitterinfo";
1709
1710                         new Ajax.Request("backend.php", {
1711                                 parameters: query,
1712                                 onComplete: function(transport) {
1713                                         notify_info("Twitter credentials have been cleared.");
1714                                         updateFeedList();
1715                                 } });
1716                 }
1717
1718         } catch (e) {
1719                 exception_error("clearTwitterCredentials", e);
1720         }
1721 }
1722
1723 function customizeCSS() {
1724         try {
1725                 var query = "backend.php?op=dlg&method=customizeCSS";
1726
1727                 if (dijit.byId("cssEditDlg"))
1728                         dijit.byId("cssEditDlg").destroyRecursive();
1729
1730                 dialog = new dijit.Dialog({
1731                         id: "cssEditDlg",
1732                         title: __("Customize stylesheet"),
1733                         style: "width: 600px",
1734                         execute: function() {
1735                                 notify_progress('Saving data...', true);
1736                                 new Ajax.Request("backend.php", {
1737                                         parameters: dojo.objectToQuery(this.attr('value')),
1738                                         onComplete: function(transport) {
1739                                                 notify('');
1740                                                 window.location.reload();
1741                                 } });
1742
1743                         },
1744                         href: query});
1745
1746                 dialog.show();
1747
1748         } catch (e) {
1749                 exception_error("customizeCSS", e);
1750         }
1751 }
1752
1753 function insertSSLserial(value) {
1754         try {
1755                 dijit.byId("SSL_CERT_SERIAL").attr('value', value);
1756         } catch (e) {
1757                 exception_error("insertSSLcerial", e);
1758         }
1759 }
1760
1761 function getSelectedInstances() {
1762         return getSelectedTableRowIds("prefInstanceList");
1763 }
1764
1765 function addInstance() {
1766         try {
1767                 var query = "backend.php?op=dlg&method=addInstance";
1768
1769                 if (dijit.byId("instanceAddDlg"))
1770                         dijit.byId("instanceAddDlg").destroyRecursive();
1771
1772                 dialog = new dijit.Dialog({
1773                         id: "instanceAddDlg",
1774                         title: __("Link Instance"),
1775                         style: "width: 600px",
1776                         regenKey: function() {
1777                                 new Ajax.Request("backend.php", {
1778                                         parameters: "?op=rpc&method=genHash",
1779                                         onComplete: function(transport) {
1780                                                 var reply = JSON.parse(transport.responseText);
1781                                                 if (reply)
1782                                                         dijit.byId('instance_add_key').attr('value', reply.hash);
1783
1784                                         } });
1785                         },
1786                         execute: function() {
1787                                 if (this.validate()) {
1788                                         console.warn(dojo.objectToQuery(this.attr('value')));
1789
1790                                         notify_progress('Saving data...', true);
1791                                         new Ajax.Request("backend.php", {
1792                                                 parameters: dojo.objectToQuery(this.attr('value')),
1793                                                 onComplete: function(transport) {
1794                                                         dialog.hide();
1795                                                         notify('');
1796                                                         updateInstanceList();
1797                                         } });
1798                                 }
1799                         },
1800                         href: query,
1801                 });
1802
1803                 dialog.show();
1804
1805         } catch (e) {
1806                 exception_error("addInstance", e);
1807         }
1808 }
1809
1810 function editInstance(id, event) {
1811         try {
1812                 if (!event || !event.ctrlKey) {
1813
1814                 selectTableRows('prefInstanceList', 'none');
1815                 selectTableRowById('LIRR-'+id, 'LICHK-'+id, true);
1816
1817                 var query = "backend.php?op=pref-instances&method=edit&id=" +
1818                         param_escape(id);
1819
1820                 if (dijit.byId("instanceEditDlg"))
1821                         dijit.byId("instanceEditDlg").destroyRecursive();
1822
1823                 dialog = new dijit.Dialog({
1824                         id: "instanceEditDlg",
1825                         title: __("Edit Instance"),
1826                         style: "width: 600px",
1827                         regenKey: function() {
1828                                 new Ajax.Request("backend.php", {
1829                                         parameters: "?op=rpc&method=genHash",
1830                                         onComplete: function(transport) {
1831                                                 var reply = JSON.parse(transport.responseText);
1832                                                 if (reply)
1833                                                         dijit.byId('instance_edit_key').attr('value', reply.hash);
1834
1835                                         } });
1836                         },
1837                         execute: function() {
1838                                 if (this.validate()) {
1839 //                                      console.warn(dojo.objectToQuery(this.attr('value')));
1840
1841                                         notify_progress('Saving data...', true);
1842                                         new Ajax.Request("backend.php", {
1843                                                 parameters: dojo.objectToQuery(this.attr('value')),
1844                                                 onComplete: function(transport) {
1845                                                         dialog.hide();
1846                                                         notify('');
1847                                                         updateInstanceList();
1848                                         } });
1849                                 }
1850                         },
1851                         href: query,
1852                 });
1853
1854                 dialog.show();
1855
1856                 } else if (event.ctrlKey) {
1857                         var cb = $('LICHK-' + id);
1858                         cb.checked = !cb.checked;
1859                         toggleSelectRow(cb);
1860                 }
1861
1862
1863         } catch (e) {
1864                 exception_error("editInstance", e);
1865         }
1866 }
1867
1868 function removeSelectedInstances() {
1869         try {
1870                 var sel_rows = getSelectedInstances();
1871
1872                 if (sel_rows.length > 0) {
1873
1874                         var ok = confirm(__("Remove selected instances?"));
1875
1876                         if (ok) {
1877                                 notify_progress("Removing selected instances...");
1878
1879                                 var query = "?op=pref-instances&method=remove&ids="+
1880                                         param_escape(sel_rows.toString());
1881
1882                                 new Ajax.Request("backend.php", {
1883                                         parameters: query,
1884                                         onComplete: function(transport) {
1885                                                 notify('');
1886                                                 updateInstanceList();
1887                                         } });
1888                         }
1889
1890                 } else {
1891                         alert(__("No instances are selected."));
1892                 }
1893
1894         } catch (e) {
1895                 exception_error("removeInstance", e);
1896         }
1897 }
1898
1899 function editSelectedInstance() {
1900         var rows = getSelectedInstances();
1901
1902         if (rows.length == 0) {
1903                 alert(__("No instances are selected."));
1904                 return;
1905         }
1906
1907         if (rows.length > 1) {
1908                 alert(__("Please select only one instance."));
1909                 return;
1910         }
1911
1912         notify("");
1913
1914         editInstance(rows[0]);
1915 }
1916
1917 function showHelp() {
1918         try {
1919                 new Ajax.Request("backend.php", {
1920                         parameters: "?op=backend&method=help&topic=prefs",
1921                         onComplete: function(transport) {
1922                                 $("hotkey_help_overlay").innerHTML = transport.responseText;
1923                                 Effect.Appear("hotkey_help_overlay", {duration : 0.3});
1924                         } });
1925
1926         } catch (e) {
1927                 exception_error("showHelp", e);
1928         }
1929 }