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