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