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