]> git.wh0rd.org - tt-rss.git/blobdiff - js/prefs.js
remove ok = confirm() thing
[tt-rss.git] / js / prefs.js
index fdbb51ec00f9a5592630b7cb665ada884a428a12..965e12fa6ee42fb35a5b2be79e2f0793bba7a823 100755 (executable)
-var init_params = new Array();
+/* global dijit, __ */
 
-var hotkey_prefix = false;
-var hotkey_prefix_pressed = false;
-
-var seq = "";
+let seq = "";
 
 function notify_callback2(transport, sticky) {
        notify_info(transport.responseText, sticky);
 }
 
-function updateFeedList(sort_key) {
+function updateFeedList() {
 
-       var user_search = $("feed_search");
-       var search = "";
+       const user_search = $("feed_search");
+       let search = "";
        if (user_search) { search = user_search.value; }
 
-       new Ajax.Request("backend.php", {
-               parameters: "?op=pref-feeds&search=" + param_escape(search),
-               onComplete: function(transport) {
-                       dijit.byId('feedConfigTab').attr('content', transport.responseText);
-                       selectTab("feedConfig", true);
-                       notify("");
-               } });
+       xhrPost("backend.php", { op: "pref-feeds", search: search }, (transport) => {
+               dijit.byId('feedConfigTab').attr('content', transport.responseText);
+               selectTab("feedConfig", true);
+               notify("");
+       });
 }
 
 function checkInactiveFeeds() {
-       try {
-               new Ajax.Request("backend.php", {
-                       parameters: "?op=pref-feeds&method=getinactivefeeds",
-                       onComplete: function(transport) {
-                               if (parseInt(transport.responseText) > 0) {
-                                       Element.show(dijit.byId("pref_feeds_inactive_btn").domNode);
-                               }
-                       } });
-
-       } catch (e) {
-               exception_error("checkInactiveFeeds", e);
-       }
+       xhrPost("backend.php", { op: "pref-feeds", method: "getinactivefeeds" }, (transport) => {
+               if (parseInt(transport.responseText) > 0) {
+                       Element.show(dijit.byId("pref_feeds_inactive_btn").domNode);
+               }
+       });
 }
 
 function updateUsersList(sort_key) {
-       try {
-               var user_search = $("user_search");
-               var search = "";
-               if (user_search) { search = user_search.value; }
-
-               var query = "?op=pref-users&sort="
-                       + param_escape(sort_key) +
-                       "&search=" + param_escape(search);
-
-               new Ajax.Request("backend.php", {
-                       parameters: query,
-                       onComplete: function(transport) {
-                               dijit.byId('userConfigTab').attr('content', transport.responseText);
-                               selectTab("userConfig", true)
-                               notify("");
-                       } });
-
-       } catch (e) {
-               exception_error("updateUsersList", e);
-       }
-}
-
-function addUser() {
+       const user_search = $("user_search");
+       const search = user_search ? user_search.value : "";
 
-       try {
+       const query = { op: "pref-users", sort:  sort_key, search: search };
 
-               var login = prompt(__("Please enter login:"), "");
-
-               if (login == null) {
-                       return false;
-               }
-
-               if (login == "") {
-                       alert(__("Can't create user: no login specified."));
-                       return false;
-               }
-
-               notify_progress("Adding user...");
+       xhrPost("backend.php", query, (transport) => {
+               dijit.byId('userConfigTab').attr('content', transport.responseText);
+               selectTab("userConfig", true)
+               notify("");
+       });
+}
 
-               var query = "?op=pref-users&method=add&login=" +
-                       param_escape(login);
+function addUser() {
+       const login = prompt(__("Please enter login:"), "");
 
-               new Ajax.Request("backend.php", {
-                       parameters: query,
-                       onComplete: function(transport) {
-                               notify_callback2(transport);
-                               updateUsersList();
-                       } });
+       if (login == null) {
+               return false;
+       }
 
-       } catch (e) {
-               exception_error("addUser", e);
+       if (login == "") {
+               alert(__("Can't create user: no login specified."));
+               return false;
        }
-}
 
-function editUser(id, event) {
+       notify_progress("Adding user...");
 
-       try {
-               var query = "backend.php?op=pref-users&method=edit&id=" +
-                       param_escape(id);
+       xhrPost("backend.php", { op: "pref-users", method: "add", login: login }, (transport) => {
+        notify_callback2(transport);
+        updateUsersList();
+    });
 
-               if (dijit.byId("userEditDlg"))
-                       dijit.byId("userEditDlg").destroyRecursive();
+}
 
-               dialog = new dijit.Dialog({
-                       id: "userEditDlg",
-                       title: __("User Editor"),
-                       style: "width: 600px",
-                       execute: function() {
-                               if (this.validate()) {
-                                       notify_progress("Saving data...", true);
+function editUser(id) {
 
-                                       var query = dojo.formToQuery("user_edit_form");
+       const query = "backend.php?op=pref-users&method=edit&id=" +
+               param_escape(id);
 
-                                       new Ajax.Request("backend.php", {
-                                               parameters: query,
-                                               onComplete: function(transport) {
-                                                       dialog.hide();
-                                                       updateUsersList();
-                                               }});
-                               }
-                       },
-                       href: query});
+       if (dijit.byId("userEditDlg"))
+               dijit.byId("userEditDlg").destroyRecursive();
 
-               dialog.show();
+       const dialog = new dijit.Dialog({
+               id: "userEditDlg",
+               title: __("User Editor"),
+               style: "width: 600px",
+               execute: function () {
+                       if (this.validate()) {
+                               notify_progress("Saving data...", true);
 
-       } catch (e) {
-               exception_error("editUser", e);
-       }
+                               xhrPost("backend.php", dojo.formToObject("user_edit_form"), (transport) => {
+                    dialog.hide();
+                    updateUsersList();
+                });
+                       }
+               },
+               href: query
+       });
 
+       dialog.show();
 }
 
 function editFilter(id) {
-       try {
 
-               var query = "backend.php?op=pref-filters&method=edit&id=" + param_escape(id);
+       const query = "backend.php?op=pref-filters&method=edit&id=" + param_escape(id);
 
-               if (dijit.byId("feedEditDlg"))
-                       dijit.byId("feedEditDlg").destroyRecursive();
+       if (dijit.byId("feedEditDlg"))
+               dijit.byId("feedEditDlg").destroyRecursive();
 
-               if (dijit.byId("filterEditDlg"))
-                       dijit.byId("filterEditDlg").destroyRecursive();
+       if (dijit.byId("filterEditDlg"))
+               dijit.byId("filterEditDlg").destroyRecursive();
 
-               dialog = new dijit.Dialog({
-                       id: "filterEditDlg",
-                       title: __("Edit Filter"),
-                       style: "width: 600px",
-
-                       test: function() {
-                               var query = "backend.php?" + dojo.formToQuery("filter_edit_form") + "&savemode=test";
-
-                               editFilterTest(query);
-                       },
-                       selectRules: function(select) {
-                               $$("#filterDlg_Matches input[type=checkbox]").each(function(e) {
-                                       e.checked = select;
-                                       if (select)
-                                               e.parentNode.addClassName("Selected");
-                                       else
-                                               e.parentNode.removeClassName("Selected");
-                               });
-                       },
-                       selectActions: function(select) {
-                               $$("#filterDlg_Actions input[type=checkbox]").each(function(e) {
-                                       e.checked = select;
+       const dialog = new dijit.Dialog({
+               id: "filterEditDlg",
+               title: __("Edit Filter"),
+               style: "width: 600px",
 
-                                       if (select)
-                                               e.parentNode.addClassName("Selected");
-                                       else
-                                               e.parentNode.removeClassName("Selected");
+               test: function () {
+                       const query = "backend.php?" + dojo.formToQuery("filter_edit_form") + "&savemode=test";
 
-                               });
-                       },
-                       editRule: function(e) {
-                               var li = e.parentNode;
-                               var rule = li.getElementsByTagName("INPUT")[1].value;
-                               addFilterRule(li, rule);
-                       },
-                       editAction: function(e) {
-                               var li = e.parentNode;
-                               var action = li.getElementsByTagName("INPUT")[1].value;
-                               addFilterAction(li, action);
-                       },
-                       removeFilter: function() {
-                               var msg = __("Remove filter?");
-
-                               if (confirm(msg)) {
-                                       this.hide();
-
-                                       notify_progress("Removing filter...");
-
-                                       var id = this.attr('value').id;
+                       editFilterTest(query);
+               },
+               selectRules: function (select) {
+                       $$("#filterDlg_Matches input[type=checkbox]").each(function (e) {
+                               e.checked = select;
+                               if (select)
+                                       e.parentNode.addClassName("Selected");
+                               else
+                                       e.parentNode.removeClassName("Selected");
+                       });
+               },
+               selectActions: function (select) {
+                       $$("#filterDlg_Actions input[type=checkbox]").each(function (e) {
+                               e.checked = select;
 
-                                       var query = "?op=pref-filters&method=remove&ids="+
-                                               param_escape(id);
+                               if (select)
+                                       e.parentNode.addClassName("Selected");
+                               else
+                                       e.parentNode.removeClassName("Selected");
 
-                                       new Ajax.Request("backend.php", {
-                                               parameters: query,
-                                               onComplete: function(transport) {
-                                                       updateFilterList();
-                                               } });
-                               }
-                       },
-                       addAction: function() { addFilterAction(); },
-                       addRule: function() { addFilterRule(); },
-                       deleteAction: function() {
-                               $$("#filterDlg_Actions li.[class*=Selected]").each(function(e) { e.parentNode.removeChild(e) });
-                       },
-                       deleteRule: function() {
-                               $$("#filterDlg_Matches li.[class*=Selected]").each(function(e) { e.parentNode.removeChild(e) });
-                       },
-                       execute: function() {
-                               if (this.validate()) {
-
-                                       notify_progress("Saving data...", true);
+                       });
+               },
+               editRule: function (e) {
+                       const li = e.parentNode;
+                       const rule = li.getElementsByTagName("INPUT")[1].value;
+                       addFilterRule(li, rule);
+               },
+               editAction: function (e) {
+                       const li = e.parentNode;
+                       const action = li.getElementsByTagName("INPUT")[1].value;
+                       addFilterAction(li, action);
+               },
+               removeFilter: function () {
+                       const msg = __("Remove filter?");
+
+                       if (confirm(msg)) {
+                               this.hide();
 
-                                       var query = dojo.formToQuery("filter_edit_form");
+                               notify_progress("Removing filter...");
 
-                                       console.log(query);
+                               const query = { op: "pref-filters", method: "remove", ids: this.attr('value').id };
 
-                                       new Ajax.Request("backend.php", {
-                                               parameters: query,
-                                               onComplete: function(transport) {
-                                                       dialog.hide();
-                                                       updateFilterList();
-                                               }});
-                               }
-                       },
-                       href: query});
+                               xhrPost("backend.php", query, () => {
+                                       updateFilterList();
+                               });
+                       }
+               },
+               addAction: function () {
+                       addFilterAction();
+               },
+               addRule: function () {
+                       addFilterRule();
+               },
+               deleteAction: function () {
+                       $$("#filterDlg_Actions li[class*=Selected]").each(function (e) {
+                               e.parentNode.removeChild(e)
+                       });
+               },
+               deleteRule: function () {
+                       $$("#filterDlg_Matches li[class*=Selected]").each(function (e) {
+                               e.parentNode.removeChild(e)
+                       });
+               },
+               execute: function () {
+                       if (this.validate()) {
 
-               dialog.show();
+                               notify_progress("Saving data...", true);
 
+                               xhrPost("backend.php", dojo.formToObject("filter_edit_form"), () => {
+                                       dialog.hide();
+                                       updateFilterList();
+                               });
+                       }
+               },
+               href: query
+       });
 
-       } catch (e) {
-               exception_error("editFilter", e);
-       }
+       dialog.show();
 }
 
+
 function getSelectedLabels() {
-       var tree = dijit.byId("labelTree");
-       var items = tree.model.getCheckedItems();
-       var rv = [];
+       const tree = dijit.byId("labelTree");
+       const items = tree.model.getCheckedItems();
+       const rv = [];
 
        items.each(function(item) {
                rv.push(tree.model.store.getValue(item, 'bare_id'));
@@ -254,9 +205,9 @@ function getSelectedUsers() {
 }
 
 function getSelectedFeeds() {
-       var tree = dijit.byId("feedTree");
-       var items = tree.model.getCheckedItems();
-       var rv = [];
+       const tree = dijit.byId("feedTree");
+       const items = tree.model.getCheckedItems();
+       const rv = [];
 
        items.each(function(item) {
                if (item.id[0].match("FEED:"))
@@ -267,9 +218,9 @@ function getSelectedFeeds() {
 }
 
 function getSelectedCategories() {
-       var tree = dijit.byId("feedTree");
-       var items = tree.model.getCheckedItems();
-       var rv = [];
+       const tree = dijit.byId("feedTree");
+       const items = tree.model.getCheckedItems();
+       const rv = [];
 
        items.each(function(item) {
                if (item.id[0].match("CAT:"))
@@ -280,9 +231,9 @@ function getSelectedCategories() {
 }
 
 function getSelectedFilters() {
-       var tree = dijit.byId("filterTree");
-       var items = tree.model.getCheckedItems();
-       var rv = [];
+       const tree = dijit.byId("filterTree");
+       const items = tree.model.getCheckedItems();
+       const rv = [];
 
        items.each(function(item) {
                rv.push(tree.model.store.getValue(item, 'bare_id'));
@@ -294,24 +245,18 @@ function getSelectedFilters() {
 
 function removeSelectedLabels() {
 
-       var sel_rows = getSelectedLabels();
+       const sel_rows = getSelectedLabels();
 
        if (sel_rows.length > 0) {
-
-               var ok = confirm(__("Remove selected labels?"));
-
-               if (ok) {
+               if (confirm(__("Remove selected labels?"))) {
                        notify_progress("Removing selected labels...");
 
-                       var query = "?op=pref-labels&method=remove&ids="+
-                               param_escape(sel_rows.toString());
-
-                       new Ajax.Request("backend.php", {
-                               parameters: query,
-                               onComplete: function(transport) {
-                                               updateLabelList();
-                                       } });
+                       const query = { op: "pref-labels", method: "remove",
+                               ids: sel_rows.toString() };
 
+                       xhrPost("backend.php",  query, () => {
+                               updateLabelList();
+                       });
                }
        } else {
                alert(__("No labels are selected."));
@@ -322,34 +267,23 @@ function removeSelectedLabels() {
 
 function removeSelectedUsers() {
 
-       try {
-
-               var sel_rows = getSelectedUsers();
-
-               if (sel_rows.length > 0) {
-
-                       var ok = confirm(__("Remove selected users? Neither default admin nor your account will be removed."));
+       const sel_rows = getSelectedUsers();
 
-                       if (ok) {
-                               notify_progress("Removing selected users...");
-
-                               var query = "?op=pref-users&method=remove&ids="+
-                                       param_escape(sel_rows.toString());
+       if (sel_rows.length > 0) {
 
-                               new Ajax.Request("backend.php", {
-                                       parameters: query,
-                                       onComplete: function(transport) {
-                                               updateUsersList();
-                                       } });
+               if (confirm(__("Remove selected users? Neither default admin nor your account will be removed."))) {
+                       notify_progress("Removing selected users...");
 
-                       }
+                       const query = { op: "pref-users", method: "remove",
+                               ids: sel_rows.toString() };
 
-               } else {
-                       alert(__("No users are selected."));
+                       xhrPost("backend.php", query, () => {
+                               updateUsersList();
+                       });
                }
 
-       } catch (e) {
-               exception_error("removeSelectedUsers", e);
+       } else {
+               alert(__("No users are selected."));
        }
 
        return false;
@@ -357,136 +291,52 @@ function removeSelectedUsers() {
 
 function removeSelectedFilters() {
 
-       try {
-
-               var sel_rows = getSelectedFilters();
-
-               if (sel_rows.length > 0) {
-
-                       var ok = confirm(__("Remove selected filters?"));
-
-                       if (ok) {
-                               notify_progress("Removing selected filters...");
-
-                               var query = "?op=pref-filters&method=remove&ids="+
-                                       param_escape(sel_rows.toString());
-
-                               new Ajax.Request("backend.php", {
-                                               parameters: query,
-                                               onComplete: function(transport) {
-                                                       updateFilterList();
-                                               } });
-                       }
-               } else {
-                       alert(__("No filters are selected."));
-               }
-
-       } catch (e) {
-               exception_error("removeSelectedFilters", e);
-       }
-
-       return false;
-}
-
-
-function removeSelectedFeeds() {
-
-       try {
-
-               var sel_rows = getSelectedFeeds();
-
-               if (sel_rows.length > 0) {
-
-                       var ok = confirm(__("Unsubscribe from selected feeds?"));
-
-                       if (ok) {
-
-                               notify_progress("Unsubscribing from selected feeds...", true);
-
-                               var query = "?op=pref-feeds&method=remove&ids="+
-                                       param_escape(sel_rows.toString());
-
-                               console.log(query);
-
-                               new Ajax.Request("backend.php", {
-                                       parameters: query,
-                                       onComplete: function(transport) {
-                                               updateFeedList();
-                                               } });
-                       }
-
-               } else {
-                       alert(__("No feeds are selected."));
-               }
-
-       } catch (e) {
-               exception_error("removeSelectedFeeds", e);
-       }
-
-       return false;
-}
-
-function clearSelectedFeeds() {
-
-       var sel_rows = getSelectedFeeds();
-
-       if (sel_rows.length > 1) {
-               alert(__("Please select only one feed."));
-               return;
-       }
+       const sel_rows = getSelectedFilters();
 
        if (sel_rows.length > 0) {
+               if (confirm(__("Remove selected filters?"))) {
+                       notify_progress("Removing selected filters...");
 
-               var ok = confirm(__("Erase all non-starred articles in selected feed?"));
+                       const query = { op: "pref-filters", method: "remove",
+                               ids:  sel_rows.toString() };
 
-               if (ok) {
-                       notify_progress("Clearing selected feed...");
-                       clearFeedArticles(sel_rows[0]);
+                       xhrPost("backend.php", query, () => {
+                               updateFilterList();
+                       });
                }
-
        } else {
-
-               alert(__("No feeds are selected."));
-
+               alert(__("No filters are selected."));
        }
 
        return false;
 }
 
-function purgeSelectedFeeds() {
+function removeSelectedFeeds() {
 
-       var sel_rows = getSelectedFeeds();
+       const sel_rows = getSelectedFeeds();
 
        if (sel_rows.length > 0) {
+               if (confirm(__("Unsubscribe from selected feeds?"))) {
 
-               var pr = prompt(__("How many days of articles to keep (0 - use default)?"), "0");
-
-               if (pr != undefined) {
-                       notify_progress("Purging selected feed...");
-
-                       var query = "?op=rpc&method=purge&ids="+
-                               param_escape(sel_rows.toString()) + "&days=" + pr;
+                       notify_progress("Unsubscribing from selected feeds...", true);
 
-                       console.log(query);
+                       const query = { op: "pref-feeds", method: "remove",
+                               ids: sel_rows.toString() };
 
-                       new Ajax.Request("prefs.php",   {
-                               parameters: query,
-                               onComplete: function(transport) {
-                                       notify('');
-                               } });
+                       xhrPost("backend.php", query, () => {
+                               updateFeedList();
+                       });
                }
 
        } else {
-
                alert(__("No feeds are selected."));
-
        }
 
        return false;
 }
 
 function editSelectedUser() {
-       var rows = getSelectedUsers();
+       const rows = getSelectedUsers();
 
        if (rows.length == 0) {
                alert(__("No users are selected."));
@@ -505,85 +355,65 @@ function editSelectedUser() {
 
 function resetSelectedUserPass() {
 
-       try {
-
-               var rows = getSelectedUsers();
-
-               if (rows.length == 0) {
-                       alert(__("No users are selected."));
-                       return;
-               }
+       const rows = getSelectedUsers();
 
-               if (rows.length > 1) {
-                       alert(__("Please select only one user."));
-                       return;
-               }
-
-               var ok = confirm(__("Reset password of selected user?"));
-
-               if (ok) {
-                       notify_progress("Resetting password for selected user...");
+       if (rows.length == 0) {
+               alert(__("No users are selected."));
+               return;
+       }
 
-                       var id = rows[0];
+       if (rows.length > 1) {
+               alert(__("Please select only one user."));
+               return;
+       }
 
-                       var query = "?op=pref-users&method=resetPass&id=" +
-                               param_escape(id);
+       if (confirm(__("Reset password of selected user?"))) {
+               notify_progress("Resetting password for selected user...");
 
-                       new Ajax.Request("backend.php", {
-                               parameters: query,
-                               onComplete: function(transport) {
-                                       notify_info(transport.responseText, true);
-                               } });
+               const id = rows[0];
 
-               }
+               xhrPost("backend.php", { op: "pref-users", method: "resetPass", id: id }, (transport) => {
+                       notify_info(transport.responseText, true);
+               });
 
-       } catch (e) {
-               exception_error("resetSelectedUserPass", e);
        }
 }
 
 function selectedUserDetails() {
 
-       try {
+       const rows = getSelectedUsers();
 
-               var rows = getSelectedUsers();
-
-               if (rows.length == 0) {
-                       alert(__("No users are selected."));
-                       return;
-               }
-
-               if (rows.length > 1) {
-                       alert(__("Please select only one user."));
-                       return;
-               }
-
-               var id = rows[0];
+       if (rows.length == 0) {
+               alert(__("No users are selected."));
+               return;
+       }
 
-               var query = "backend.php?op=pref-users&method=userdetails&id=" + id;
+       if (rows.length > 1) {
+               alert(__("Please select only one user."));
+               return;
+       }
 
-               if (dijit.byId("userDetailsDlg"))
-                       dijit.byId("userDetailsDlg").destroyRecursive();
+       const query = "backend.php?op=pref-users&method=userdetails&id=" + param_escape(rows[0]);
 
-               dialog = new dijit.Dialog({
-                       id: "userDetailsDlg",
-                       title: __("User details"),
-                       style: "width: 600px",
-                       execute: function() {
-                               dialog.hide();
-                       },
-                       href: query});
+       if (dijit.byId("userDetailsDlg"))
+               dijit.byId("userDetailsDlg").destroyRecursive();
 
-               dialog.show();
+       const dialog = new dijit.Dialog({
+               id: "userDetailsDlg",
+               title: __("User details"),
+               style: "width: 600px",
+               execute: function () {
+                       dialog.hide();
+               },
+               href: query
+       });
 
-       } catch (e) {
-               exception_error("selectedUserDetails", e);
-       }
+       dialog.show();
 }
 
 
 function editSelectedFilter() {
-       var rows = getSelectedFilters();
+       const rows = getSelectedFilters();
 
        if (rows.length == 0) {
                alert(__("No filters are selected."));
@@ -602,33 +432,24 @@ function editSelectedFilter() {
 }
 
 function joinSelectedFilters() {
-       var rows = getSelectedFilters();
+       const rows = getSelectedFilters();
 
        if (rows.length == 0) {
                alert(__("No filters are selected."));
                return;
        }
 
-       var ok = confirm(__("Combine selected filters?"));
-
-       if (ok) {
+       if (confirm(__("Combine selected filters?"))) {
                notify_progress("Joining filters...");
 
-               var query = "?op=pref-filters&method=join&ids="+
-                       param_escape(rows.toString());
-
-               console.log(query);
-
-               new Ajax.Request("backend.php", {
-                       parameters: query,
-                       onComplete: function(transport) {
-                                       updateFilterList();
-                       } });
+               xhrPost("backend.php", { op: "pref-filters", method: "join", ids: rows.toString() }, () => {
+                       updateFilterList();
+               });
        }
 }
 
 function editSelectedFeed() {
-       var rows = getSelectedFeeds();
+       const rows = getSelectedFeeds();
 
        if (rows.length == 0) {
                alert(__("No feeds are selected."));
@@ -646,159 +467,105 @@ function editSelectedFeed() {
 }
 
 function editSelectedFeeds() {
+       const rows = getSelectedFeeds();
+
+       if (rows.length == 0) {
+               alert(__("No feeds are selected."));
+               return;
+       }
 
-       try {
-               var rows = getSelectedFeeds();
+       notify_progress("Loading, please wait...");
 
-               if (rows.length == 0) {
-                       alert(__("No feeds are selected."));
-                       return;
-               }
+       if (dijit.byId("feedEditDlg"))
+               dijit.byId("feedEditDlg").destroyRecursive();
 
-               notify_progress("Loading, please wait...");
+       xhrPost("backend.php", { op: "pref-feeds", method: "editfeeds", ids: rows.toString() }, (transport) => {
+               notify("");
 
-               var query = "backend.php?op=pref-feeds&method=editfeeds&ids=" +
-                       param_escape(rows.toString());
-
-               console.log(query);
-
-               if (dijit.byId("feedEditDlg"))
-                       dijit.byId("feedEditDlg").destroyRecursive();
-
-               new Ajax.Request("backend.php", {
-                       parameters: query,
-                       onComplete: function(transport) {
-
-                               notify("");
-
-                               var dialog = new dijit.Dialog({
-                                       id: "feedEditDlg",
-                                       title: __("Edit Multiple Feeds"),
-                                       style: "width: 600px",
-                                       getChildByName: function (name) {
-                                               var rv = null;
-                                               this.getChildren().each(
-                                                       function(child) {
-                                                               if (child.name == name) {
-                                                                       rv = child;
-                                                                       return;
-                                                               }
-                                                       });
-                                               return rv;
-                                       },
-                                       toggleField: function (checkbox, elem, label) {
-                                               this.getChildByName(elem).attr('disabled', !checkbox.checked);
-
-                                               if ($(label))
-                                                       if (checkbox.checked)
-                                                               $(label).removeClassName('insensitive');
-                                                       else
-                                                               $(label).addClassName('insensitive');
-
-                                       },
-                                       execute: function() {
-                                               if (this.validate() && confirm(__("Save changes to selected feeds?"))) {
-                                                       var query = dojo.objectToQuery(this.attr('value'));
-
-                                                       /* Form.serialize ignores unchecked checkboxes */
-
-                                                       if (!query.match("&private=") &&
-                                                                       this.getChildByName('private').attr('disabled') == false) {
-                                                               query = query + "&private=false";
-                                                       }
-
-                                                       try {
-                                                               if (!query.match("&cache_images=") &&
-                                                                               this.getChildByName('cache_images').attr('disabled') == false) {
-                                                                       query = query + "&cache_images=false";
-                                                               }
-                                                       } catch (e) { }
-
-                                                       try {
-                                                               if (!query.match("&hide_images=") &&
-                                                                               this.getChildByName('hide_images').attr('disabled') == false) {
-                                                                       query = query + "&hide_images=false";
-                                                               }
-                                                       } catch (e) { }
-
-                                                       if (!query.match("&include_in_digest=") &&
-                                                                       this.getChildByName('include_in_digest').attr('disabled') == false) {
-                                                               query = query + "&include_in_digest=false";
-                                                       }
-
-                                                       if (!query.match("&always_display_enclosures=") &&
-                                                                       this.getChildByName('always_display_enclosures').attr('disabled') == false) {
-                                                               query = query + "&always_display_enclosures=false";
-                                                       }
-
-                                                       if (!query.match("&mark_unread_on_update=") &&
-                                                                       this.getChildByName('mark_unread_on_update').attr('disabled') == false) {
-                                                               query = query + "&mark_unread_on_update=false";
-                                                       }
-
-                                                       console.log(query);
-
-                                                       notify_progress("Saving data...", true);
-
-                                                       new Ajax.Request("backend.php", {
-                                                               parameters: query,
-                                                               onComplete: function(transport) {
-                                                                       dialog.hide();
-                                                                       updateFeedList();
-                                                       }});
+               const dialog = new dijit.Dialog({
+                       id: "feedEditDlg",
+                       title: __("Edit Multiple Feeds"),
+                       style: "width: 600px",
+                       getChildByName: function (name) {
+                               let rv = null;
+                               this.getChildren().each(
+                                       function (child) {
+                                               if (child.name == name) {
+                                                       rv = child;
+                                                       return;
                                                }
-                                       },
-                                       content: transport.responseText});
+                                       });
+                               return rv;
+                       },
+                       toggleField: function (checkbox, elem, label) {
+                               this.getChildByName(elem).attr('disabled', !checkbox.checked);
 
-                                       dialog.show();
+                               if ($(label))
+                                       if (checkbox.checked)
+                                               $(label).removeClassName('insensitive');
+                                       else
+                                               $(label).addClassName('insensitive');
+
+                       },
+                       execute: function () {
+                               if (this.validate() && confirm(__("Save changes to selected feeds?"))) {
+                                       const query = this.attr('value');
 
-                       } });
+                                       /* normalize unchecked checkboxes because [] is not serialized */
 
-       } catch (e) {
-               exception_error("editSelectedFeeds", e);
-       }
+                                       Object.keys(query).each((key) => {
+                                               let val = query[key];
+
+                                               if (typeof val == "object" && val.length == 0)
+                                                       query[key] = ["off"];
+                                       });
+
+                                       notify_progress("Saving data...", true);
+
+                                       xhrPost("backend.php", query, () => {
+                                               dialog.hide();
+                                               updateFeedList();
+                                       });
+                               }
+                       },
+                       content: transport.responseText
+               });
+
+               dialog.show();
+       });
 }
 
 function opmlImportComplete(iframe) {
-       try {
-               if (!iframe.contentDocument.body.innerHTML) return false;
+       if (!iframe.contentDocument.body.innerHTML) return false;
 
-               Element.show(iframe);
+       Element.show(iframe);
 
-               notify('');
+       notify('');
 
-               if (dijit.byId('opmlImportDlg'))
-                       dijit.byId('opmlImportDlg').destroyRecursive();
+       if (dijit.byId('opmlImportDlg'))
+               dijit.byId('opmlImportDlg').destroyRecursive();
 
-               var content = iframe.contentDocument.body.innerHTML;
+       const content = iframe.contentDocument.body.innerHTML;
 
-               dialog = new dijit.Dialog({
-                       id: "opmlImportDlg",
-                       title: __("OPML Import"),
-                       style: "width: 600px",
-                       onCancel: function() {
-                               updateFeedList();
-                               updateFilterList();
-                               updateLabelList();
-                       },
-                       execute: function() {
-                               updateFeedList();
-                               updateFilterList();
-                               updateLabelList();
-                               this.hide();
-                       },
-                       content: content});
-
-               dialog.show();
+       const dialog = new dijit.Dialog({
+               id: "opmlImportDlg",
+               title: __("OPML Import"),
+               style: "width: 600px",
+               onCancel: function () {
+            window.location.reload();
+               },
+               execute: function () {
+                       window.location.reload();
+               },
+               content: content
+       });
 
-       } catch (e) {
-               exception_error("opmlImportComplete", e);
-       }
+       dialog.show();
 }
 
 function opmlImport() {
 
-       var opml_file = $("opml_file");
+       const opml_file = $("opml_file");
 
        if (opml_file.value.length == 0) {
                alert(__("Please choose an OPML file first."));
@@ -814,318 +581,225 @@ function opmlImport() {
 
 
 function updateFilterList() {
-       var user_search = $("filter_search");
-       var search = "";
+       const user_search = $("filter_search");
+       let search = "";
        if (user_search) { search = user_search.value; }
 
-       new Ajax.Request("backend.php", {
-               parameters: "?op=pref-filters&search=" + param_escape(search),
-               onComplete: function(transport) {
-                       dijit.byId('filterConfigTab').attr('content', transport.responseText);
-                       notify("");
-               } });
+       xhrPost("backend.php", { op: "pref-filters", search: search }, (transport) => {
+        dijit.byId('filterConfigTab').attr('content', transport.responseText);
+        notify("");
+    });
 }
 
 function updateLabelList() {
-       new Ajax.Request("backend.php", {
-               parameters: "?op=pref-labels",
-               onComplete: function(transport) {
-                       dijit.byId('labelConfigTab').attr('content', transport.responseText);
-                       notify("");
-               } });
+       xhrPost("backend.php", { op: "pref-labels" }, (transport) => {
+        dijit.byId('labelConfigTab').attr('content', transport.responseText);
+        notify("");
+    });
 }
 
 function updatePrefsList() {
-       new Ajax.Request("backend.php", {
-               parameters: "?op=pref-prefs",
-               onComplete: function(transport) {
-                       dijit.byId('genConfigTab').attr('content', transport.responseText);
-                       notify("");
-               } });
+    xhrPost("backend.php", { op: "pref-prefs" }, (transport) => {
+        dijit.byId('genConfigTab').attr('content', transport.responseText);
+        notify("");
+    });
 }
 
 function updateSystemList() {
-       new Ajax.Request("backend.php", {
-               parameters: "?op=pref-system",
-               onComplete: function(transport) {
-                       dijit.byId('systemConfigTab').attr('content', transport.responseText);
-                       notify("");
-               } });
+    xhrPost("backend.php", { op: "pref-system" }, (transport) => {
+        dijit.byId('systemConfigTab').attr('content', transport.responseText);
+        notify("");
+    });
 }
 
-function selectTab(id, noupdate, method) {
-       try {
-               if (!noupdate) {
-                       notify_progress("Loading, please wait...");
-
-                       if (id == "feedConfig") {
-                               updateFeedList();
-                       } else if (id == "filterConfig") {
-                               updateFilterList();
-                       } else if (id == "labelConfig") {
-                               updateLabelList();
-                       } else if (id == "genConfig") {
-                               updatePrefsList();
-                       } else if (id == "userConfig") {
-                               updateUsersList();
-                       } else if (id == "systemConfig") {
-                               updateSystemList();
-                       }
-
-                       var tab = dijit.byId(id + "Tab");
-                       dijit.byId("pref-tabs").selectChild(tab);
+function selectTab(id, noupdate) {
+       if (!noupdate) {
+               notify_progress("Loading, please wait...");
 
+               switch (id) {
+                       case "feedConfig":
+                updateFeedList();
+                               break;
+                       case "filterConfig":
+                updateFilterList();
+                               break;
+                       case "labelConfig":
+                updateLabelList();
+                               break;
+                       case "genConfig":
+                updatePrefsList();
+                               break;
+                       case "userConfig":
+                updateUsersList();
+                               break;
+                       case "systemConfig":
+                updateSystemList();
+                               break;
+                       default:
+                               console.warn("unknown tab", id);
                }
 
-       } catch (e) {
-               exception_error("selectTab", e);
+               const tab = dijit.byId(id + "Tab");
+               dijit.byId("pref-tabs").selectChild(tab);
+
        }
 }
 
 function init_second_stage() {
-       try {
-
-               document.onkeydown = pref_hotkey_handler;
-               loading_set_progress(50);
-               notify("");
-
-               var tab = getURLParam('tab');
-
-               if (tab) {
-                       tab = dijit.byId(tab + "Tab");
-                       if (tab) dijit.byId("pref-tabs").selectChild(tab);
-               }
+       document.onkeydown = pref_hotkey_handler;
+       loading_set_progress(50);
+       notify("");
 
-               var method = getURLParam('method');
+       let tab = getURLParam('tab');
 
-               if (method == 'editFeed') {
-                       var param = getURLParam('methodparam');
+       if (tab) {
+               tab = dijit.byId(tab + "Tab");
+               if (tab) dijit.byId("pref-tabs").selectChild(tab);
+       }
 
-                       window.setTimeout('editFeed(' + param + ')', 100);
-               }
+       const method = getURLParam('method');
 
-               setTimeout("hotkey_prefix_timeout()", 5*1000);
+       if (method == 'editFeed') {
+               const param = getURLParam('methodparam');
 
-       } catch (e) {
-               exception_error("init_second_stage", e);
+               window.setTimeout(function() { editFeed(param) }, 100);
        }
+
+       setInterval(hotkey_prefix_timeout, 5*1000);
 }
 
 function init() {
-
-       try {
-
-               require(["dojo/_base/kernel",
-                       "dojo/ready",
-                       "dojo/parser",
-                       "dojo/_base/loader",
-                       "dijit/ColorPalette",
-                       "dijit/Dialog",
-                       "dijit/form/Button",
-                       "dijit/form/CheckBox",
-                       "dijit/form/DropDownButton",
-                       "dijit/form/FilteringSelect",
-                       "dijit/form/Form",
-                       "dijit/form/RadioButton",
-                       "dijit/form/ComboButton",
-                       "dijit/form/Select",
-                       "dijit/form/SimpleTextarea",
-                       "dijit/form/TextBox",
-                       "dijit/form/ValidationTextBox",
-                       "dijit/InlineEditBox",
-                       "dijit/layout/AccordionContainer",
-                       "dijit/layout/AccordionPane",
-                       "dijit/layout/BorderContainer",
-                       "dijit/layout/ContentPane",
-                       "dijit/layout/TabContainer",
-                       "dijit/Menu",
-                       "dijit/ProgressBar",
-                       "dijit/Toolbar",
-                       "dijit/Tree",
-                       "dijit/tree/dndSource",
-                       "dojo/data/ItemFileWriteStore",
-                       "fox/PrefFeedTree",
-                       "fox/PrefFilterTree",
-                       "fox/PrefLabelTree" ], function (dojo, ready, parser) {
-
-                               ready(function() {
-                                       parser.parse();
-
-                                       loading_set_progress(50);
-
-                                       var clientTzOffset = new Date().getTimezoneOffset() * 60;
-
-                                       new Ajax.Request("backend.php", {
-                                               parameters: {op: "rpc", method: "sanityCheck",
-                                                       clientTzOffset: clientTzOffset },
-                                               onComplete: function(transport) {
-                                                       backend_sanity_check_callback(transport);
-                                               } });
+       window.onerror = function (message, filename, lineno, colno, error) {
+               report_error(message, filename, lineno, colno, error);
+       };
+
+       require(["dojo/_base/kernel",
+               "dojo/ready",
+               "dojo/parser",
+               "dojo/_base/loader",
+               "dojo/_base/html",
+               "dijit/ColorPalette",
+               "dijit/Dialog",
+               "dijit/form/Button",
+               "dijit/form/CheckBox",
+               "dijit/form/DropDownButton",
+               "dijit/form/FilteringSelect",
+        "dijit/form/MultiSelect",
+               "dijit/form/Form",
+               "dijit/form/RadioButton",
+               "dijit/form/ComboButton",
+               "dijit/form/Select",
+               "dijit/form/SimpleTextarea",
+               "dijit/form/TextBox",
+               "dijit/form/ValidationTextBox",
+               "dijit/InlineEditBox",
+               "dijit/layout/AccordionContainer",
+               "dijit/layout/AccordionPane",
+               "dijit/layout/BorderContainer",
+               "dijit/layout/ContentPane",
+               "dijit/layout/TabContainer",
+               "dijit/Menu",
+               "dijit/ProgressBar",
+               "dijit/Toolbar",
+               "dijit/Tree",
+               "dijit/tree/dndSource",
+               "dojo/data/ItemFileWriteStore",
+               "lib/CheckBoxStoreModel",
+               "lib/CheckBoxTree",
+               "fox/PrefFeedStore",
+               "fox/PrefFilterStore",
+               "fox/PrefFeedTree",
+               "fox/PrefFilterTree",
+               "fox/PrefLabelTree"], function (dojo, ready, parser) {
+
+               ready(function () {
+                       try {
+                               parser.parse();
+
+                               loading_set_progress(50);
+
+                               const clientTzOffset = new Date().getTimezoneOffset() * 60;
+                               const params = { op: "rpc", method: "sanityCheck", clientTzOffset: clientTzOffset };
+
+                               xhrPost("backend.php", params, (transport) => {
+                                       backend_sanity_check_callback(transport);
                                });
-                       });
 
-       } catch (e) {
-               exception_error("init", e);
-       }
+                       } catch (e) {
+                               exception_error(e);
+                       }
+               });
+       });
 }
 
-function validatePrefsReset() {
-       try {
-               var ok = confirm(__("Reset to defaults?"));
-
-               if (ok) {
-
-                       query = "?op=pref-prefs&method=resetconfig";
-                       console.log(query);
 
-                       new Ajax.Request("backend.php", {
-                               parameters: query,
-                               onComplete: function(transport) {
-                                       updatePrefsList();
-                                       notify_info(transport.responseText);
-                               } });
+function validatePrefsReset() {
+       if (confirm(__("Reset to defaults?"))) {
 
-               }
+               const query = "?op=pref-prefs&method=resetconfig";
 
-       } catch (e) {
-               exception_error("validatePrefsReset", e);
+               xhrPost("backend.php", { op: "pref-prefs", method: "resetconfig" }, (transport) => {
+            updatePrefsList();
+            notify_info(transport.responseText);
+        });
        }
 
        return false;
-
 }
 
 function pref_hotkey_handler(e) {
-       try {
-
-               if (e.target.nodeName == "INPUT" || e.target.nodeName == "TEXTAREA") return;
-
-               var keycode = false;
-               var shift_key = false;
-
-               var cmdline = $('cmdline');
-
-               try {
-                       shift_key = e.shiftKey;
-               } catch (e) {
-
-               }
-
-               if (window.event) {
-                       keycode = window.event.keyCode;
-               } else if (e) {
-                       keycode = e.which;
-               }
-
-               var keychar = String.fromCharCode(keycode);
-
-               if (keycode == 27) { // escape
-                       hotkey_prefix = false;
-               }
-
-               if (keycode == 16) return; // ignore lone shift
-               if (keycode == 17) return; // ignore lone ctrl
-
-               if (!shift_key) keychar = keychar.toLowerCase();
-
-               var hotkeys = getInitParam("hotkeys");
-
-               if (!hotkey_prefix && hotkeys[0].indexOf(keychar) != -1) {
-
-                       var date = new Date();
-                       var ts = Math.round(date.getTime() / 1000);
-
-                       hotkey_prefix = keychar;
-                       hotkey_prefix_pressed = ts;
-
-                       cmdline.innerHTML = keychar;
-                       Element.show(cmdline);
-
-                       return true;
-               }
-
-               Element.hide(cmdline);
-
-               var hotkey = keychar.search(/[a-zA-Z0-9]/) != -1 ? keychar : "(" + keycode + ")";
-               hotkey = hotkey_prefix ? hotkey_prefix + " " + hotkey : hotkey;
-               hotkey_prefix = false;
-
-               var hotkey_action = false;
-               var hotkeys = getInitParam("hotkeys");
-
-               for (sequence in hotkeys[1]) {
-                       if (sequence == hotkey) {
-                               hotkey_action = hotkeys[1][sequence];
-                               break;
-                       }
-               }
-
-               switch (hotkey_action) {
-               case "feed_subscribe":
-                       quickAddFeed();
-                       return false;
-               case "create_label":
-                       addLabel();
-                       return false;
-               case "create_filter":
-                       quickAddFilter();
-                       return false;
-               case "help_dialog":
-                       //helpDialog("prefs");
-                       return false;
-               default:
-                       console.log("unhandled action: " + hotkey_action + "; hotkey: " + hotkey);
-               }
-
-       } catch (e) {
-               exception_error("hotkey_handler", e);
+    if (e.target.nodeName == "INPUT" || e.target.nodeName == "TEXTAREA") return;
+
+    const action_name = keyevent_to_action(e);
+
+    if (action_name) {
+        switch (action_name) {
+            case "feed_subscribe":
+                quickAddFeed();
+                return false;
+            case "create_label":
+                addLabel();
+                return false;
+            case "create_filter":
+                quickAddFilter();
+                return false;
+            case "help_dialog":
+                helpDialog("main");
+                return false;
+            default:
+                console.log("unhandled action: " + action_name + "; keycode: " + e.which);
+        }
        }
 }
 
 function removeCategory(id, item) {
-       try {
 
-               var ok = confirm(__("Remove category %s? Any nested feeds would be placed into Uncategorized.").replace("%s", item.name));
+       if (confirm(__("Remove category %s? Any nested feeds would be placed into Uncategorized.").replace("%s", item.name))) {
+               notify_progress("Removing category...");
 
-               if (ok) {
-                       var query = "?op=pref-feeds&method=removeCat&ids="+
-                               param_escape(id);
-
-                       notify_progress("Removing category...");
-
-                       new Ajax.Request("backend.php", {
-                               parameters: query,
-                               onComplete: function(transport) {
-                                       notify('');
-                                       updateFeedList();
-                               } });
-                       }
+        const query = { op: "pref-feeds", method: "removeCat",
+            ids: id };
 
-       } catch (e) {
-               exception_error("removeCategory", e);
+               xhrPost("backend.php", query, () => {
+            notify('');
+            updateFeedList();
+        });
        }
 }
 
 function removeSelectedCategories() {
-
-       var sel_rows = getSelectedCategories();
+       const sel_rows = getSelectedCategories();
 
        if (sel_rows.length > 0) {
-
-               var ok = confirm(__("Remove selected categories?"));
-
-               if (ok) {
+               if (confirm(__("Remove selected categories?"))) {
                        notify_progress("Removing selected categories...");
 
-                       var query = "?op=pref-feeds&method=removeCat&ids="+
-                               param_escape(sel_rows.toString());
-
-                       new Ajax.Request("backend.php", {
-                               parameters: query,
-                               onComplete: function(transport) {
-                                               updateFeedList();
-                                       } });
+                       const query = { op: "pref-feeds", method: "removeCat",
+                               ids: sel_rows.toString() };
 
+                       xhrPost("backend.php", query, () => {
+                updateFeedList();
+            });
                }
        } else {
                alert(__("No categories are selected."));
@@ -1135,377 +809,199 @@ function removeSelectedCategories() {
 }
 
 function createCategory() {
-       try {
-               var title = prompt(__("Category title:"));
+       const title = prompt(__("Category title:"));
 
-               if (title) {
+       if (title) {
+               notify_progress("Creating category...");
 
-                       notify_progress("Creating category...");
-
-                       var query = "?op=pref-feeds&method=addCat&cat=" +
-                               param_escape(title);
-
-                       new Ajax.Request("backend.php", {
-                               parameters: query,
-                               onComplete: function(transport) {
-                                       notify('');
-                                       updateFeedList();
-                               } });
-               }
-
-       } catch (e) {
-               exception_error("createCategory", e);
+               xhrPost("backend.php", { op: "pref-feeds", method: "addCat", cat: title }, () => {
+            notify('');
+            updateFeedList();
+        });
        }
 }
 
 function showInactiveFeeds() {
-       try {
-               var query = "backend.php?op=pref-feeds&method=inactiveFeeds";
-
-               if (dijit.byId("inactiveFeedsDlg"))
-                       dijit.byId("inactiveFeedsDlg").destroyRecursive();
-
-               dialog = new dijit.Dialog({
-                       id: "inactiveFeedsDlg",
-                       title: __("Feeds without recent updates"),
-                       style: "width: 600px",
-                       getSelectedFeeds: function() {
-                               return getSelectedTableRowIds("prefInactiveFeedList");
-                       },
-                       removeSelected: function() {
-                               var sel_rows = this.getSelectedFeeds();
-
-                               console.log(sel_rows);
-
-                               if (sel_rows.length > 0) {
-                                       var ok = confirm(__("Remove selected feeds?"));
-
-                                       if (ok) {
-                                               notify_progress("Removing selected feeds...", true);
-
-                                               var query = "?op=pref-feeds&method=remove&ids="+
-                                                       param_escape(sel_rows.toString());
-
-                                               new Ajax.Request("backend.php", {
-                                                       parameters: query,
-                                                       onComplete: function(transport) {
-                                                               notify('');
-                                                               dialog.hide();
-                                                               updateFeedList();
-                                                       } });
-                                       }
-
-                               } else {
-                                       alert(__("No feeds are selected."));
-                               }
-                       },
-                       execute: function() {
-                               if (this.validate()) {
+       const query = "backend.php?op=pref-feeds&method=inactiveFeeds";
+
+       if (dijit.byId("inactiveFeedsDlg"))
+               dijit.byId("inactiveFeedsDlg").destroyRecursive();
+
+       const dialog = new dijit.Dialog({
+               id: "inactiveFeedsDlg",
+               title: __("Feeds without recent updates"),
+               style: "width: 600px",
+               getSelectedFeeds: function () {
+                       return getSelectedTableRowIds("prefInactiveFeedList");
+               },
+               removeSelected: function () {
+                       const sel_rows = this.getSelectedFeeds();
+
+                       if (sel_rows.length > 0) {
+                               if (confirm(__("Remove selected feeds?"))) {
+                                       notify_progress("Removing selected feeds...", true);
+
+                                       const query = { op: "pref-feeds", method: "remove",
+                                               ids: sel_rows.toString() };
+
+                                       xhrPost("backend.php", query, () => {
+                        notify('');
+                        dialog.hide();
+                        updateFeedList();
+                    });
                                }
-                       },
-                       href: query});
-
-               dialog.show();
-
-       } catch (e) {
-               exception_error("showInactiveFeeds", e);
-       }
-
-}
-
-function opmlRegenKey() {
-
-       try {
-               var ok = confirm(__("Replace current OPML publishing address with a new one?"));
-
-               if (ok) {
-
-                       notify_progress("Trying to change address...", true);
-
-                       var query = "?op=pref-feeds&method=regenOPMLKey";
-
-                       new Ajax.Request("backend.php", {
-                               parameters: query,
-                               onComplete: function(transport) {
-                                               var reply = JSON.parse(transport.responseText);
-
-                                               var new_link = reply.link;
-
-                                               var e = $('pub_opml_url');
-
-                                               if (new_link) {
-                                                       e.href = new_link;
-                                                       e.innerHTML = new_link;
-
-                                                       new Effect.Highlight(e);
-
-                                                       notify('');
 
-                                               } else {
-                                                       notify_error("Could not change feed URL.");
-                                               }
-                               } });
-               }
-       } catch (e) {
-               exception_error("opmlRegenKey", e);
-       }
-       return false;
-}
-
-function feedActionChange() {
-       try {
-               var chooser = $("feedActionChooser");
-               var opid = chooser[chooser.selectedIndex].value;
-
-               chooser.selectedIndex = 0;
-               feedActionGo(opid);
-       } catch (e) {
-               exception_error("feedActionChange", e);
-       }
-}
-
-function feedActionGo(op) {
-       try {
-               if (op == "facEdit") {
-
-                       var rows = getSelectedFeeds();
-
-                       if (rows.length > 1) {
-                               editSelectedFeeds();
                        } else {
-                               editSelectedFeed();
+                               alert(__("No feeds are selected."));
                        }
-               }
-
-               if (op == "facClear") {
-                       clearSelectedFeeds();
-               }
-
-               if (op == "facPurge") {
-                       purgeSelectedFeeds();
-               }
-
-               if (op == "facEditCats") {
-                       editFeedCats();
-               }
-
-               if (op == "facRescore") {
-                       rescoreSelectedFeeds();
-               }
-
-               if (op == "facUnsubscribe") {
-                       removeSelectedFeeds();
-               }
-
-       } catch (e) {
-               exception_error("feedActionGo", e);
-
-       }
-}
-
-function clearFeedArticles(feed_id) {
-
-       notify_progress("Clearing feed...");
-
-       var query = "?op=pref-feeds&quiet=1&method=clear&id=" + feed_id;
-
-       new Ajax.Request("backend.php", {
-               parameters: query,
-               onComplete: function(transport) {
-                               notify('');
-                       } });
+               },
+               execute: function () {
+                       if (this.validate()) {
+                       }
+               },
+               href: query
+       });
 
-       return false;
+       dialog.show();
 }
 
-function rescoreSelectedFeeds() {
-
-       var sel_rows = getSelectedFeeds();
-
-       if (sel_rows.length > 0) {
+function opmlRegenKey() {
+       if (confirm(__("Replace current OPML publishing address with a new one?"))) {
+               notify_progress("Trying to change address...", true);
 
-               //var ok = confirm(__("Rescore last 100 articles in selected feeds?"));
-               var ok = confirm(__("Rescore articles in selected feeds?"));
+               xhrJson("backend.php", { op: "pref-feeds", method: "regenOPMLKey" }, (reply) => {
+            if (reply) {
+                const new_link = reply.link;
+                const e = $('pub_opml_url');
 
-               if (ok) {
-                       notify_progress("Rescoring selected feeds...", true);
+                if (new_link) {
+                    e.href = new_link;
+                    e.innerHTML = new_link;
 
-                       var query = "?op=pref-feeds&method=rescore&quiet=1&ids="+
-                               param_escape(sel_rows.toString());
+                    new Effect.Highlight(e);
 
-                       new Ajax.Request("backend.php", {
-                               parameters: query,
-                               onComplete: function(transport) {
-                                               notify_callback2(transport);
-                       } });
+                    notify('');
 
-               }
-       } else {
-               alert(__("No feeds are selected."));
+                } else {
+                    notify_error("Could not change feed URL.");
+                }
+            }
+        });
        }
-
        return false;
 }
 
-function rescore_all_feeds() {
-       var ok = confirm(__("Rescore all articles? This operation may take a lot of time."));
-
-       if (ok) {
-               notify_progress("Rescoring feeds...", true);
-
-               var query = "?op=pref-feeds&method=rescoreAll&quiet=1";
-
-               new Ajax.Request("backend.php", {
-                       parameters: query,
-                       onComplete: function(transport) {
-                                       notify_callback2(transport);
-               } });
-       }
-}
-
 function labelColorReset() {
-       try {
-               var labels = getSelectedLabels();
-
-               if (labels.length > 0) {
-                       var ok = confirm(__("Reset selected labels to default colors?"));
+       const labels = getSelectedLabels();
 
-                       if (ok) {
-                               var query = "?op=pref-labels&method=colorreset&ids="+
-                                       param_escape(labels.toString());
+       if (labels.length > 0) {
+               if (confirm(__("Reset selected labels to default colors?"))) {
 
-                               new Ajax.Request("backend.php", {
-                                       parameters: query,
-                                       onComplete: function(transport) {
-                                               updateLabelList();
-                                       } });
-                       }
+                       const query = { op: "pref-labels", method: "colorreset",
+                               ids: labels.toString() };
 
-               } else {
-                       alert(__("No labels are selected."));
+                       xhrPost("backend.php", query, () => {
+                updateLabelList();
+            });
                }
 
-       } catch (e) {
-               exception_error("labelColorReset", e);
+       } else {
+               alert(__("No labels are selected."));
        }
 }
 
-
 function inPreferences() {
        return true;
 }
 
 function editProfiles() {
-       try {
 
-               if (dijit.byId("profileEditDlg"))
-                       dijit.byId("profileEditDlg").destroyRecursive();
+       if (dijit.byId("profileEditDlg"))
+               dijit.byId("profileEditDlg").destroyRecursive();
 
-               var query = "backend.php?op=pref-prefs&method=editPrefProfiles";
+       const query = "backend.php?op=pref-prefs&method=editPrefProfiles";
 
-               dialog = new dijit.Dialog({
-                       id: "profileEditDlg",
-                       title: __("Settings Profiles"),
-                       style: "width: 600px",
-                       getSelectedProfiles: function() {
-                               return getSelectedTableRowIds("prefFeedProfileList");
-                       },
-                       removeSelected: function() {
-                               var sel_rows = this.getSelectedProfiles();
-
-                               if (sel_rows.length > 0) {
-                                       var ok = confirm(__("Remove selected profiles? Active and default profiles will not be removed."));
-
-                                       if (ok) {
-                                               notify_progress("Removing selected profiles...", true);
+       const dialog = new dijit.Dialog({
+               id: "profileEditDlg",
+               title: __("Settings Profiles"),
+               style: "width: 600px",
+               getSelectedProfiles: function () {
+                       return getSelectedTableRowIds("prefFeedProfileList");
+               },
+               removeSelected: function () {
+                       const sel_rows = this.getSelectedProfiles();
 
-                                               var query = "?op=rpc&method=remprofiles&ids="+
-                                                       param_escape(sel_rows.toString());
+                       if (sel_rows.length > 0) {
+                               if (confirm(__("Remove selected profiles? Active and default profiles will not be removed."))) {
+                                       notify_progress("Removing selected profiles...", true);
 
-                                               new Ajax.Request("backend.php", {
-                                                       parameters: query,
-                                                       onComplete: function(transport) {
-                                                               notify('');
-                                                               editProfiles();
-                                                       } });
+                                       const query = { op: "rpc", method: "remprofiles",
+                                               ids: sel_rows.toString() };
 
-                                       }
-
-                               } else {
-                                       alert(__("No profiles are selected."));
+                                       xhrPost("backend.php", query, () => {
+                                               notify('');
+                                               editProfiles();
+                                       });
                                }
-                       },
-                       activateProfile: function() {
-                               var sel_rows = this.getSelectedProfiles();
 
-                               if (sel_rows.length == 1) {
-
-                                       var ok = confirm(__("Activate selected profile?"));
-
-                                       if (ok) {
-                                               notify_progress("Loading, please wait...");
-
-                                               var query = "?op=rpc&method=setprofile&id="+
-                                                       param_escape(sel_rows.toString());
+                       } else {
+                               alert(__("No profiles are selected."));
+                       }
+               },
+               activateProfile: function () {
+                       const sel_rows = this.getSelectedProfiles();
 
-                                               new Ajax.Request("backend.php", {
-                                                       parameters: query,
-                                                       onComplete: function(transport) {
-                                                               window.location.reload();
-                                                       } });
-                                       }
+                       if (sel_rows.length == 1) {
+                               if (confirm(__("Activate selected profile?"))) {
+                                       notify_progress("Loading, please wait...");
 
-                               } else {
-                                       alert(__("Please choose a profile to activate."));
+                    xhrPost("backend.php", { op: "rpc", method: "setprofile", id: sel_rows.toString() },  () => {
+                        window.location.reload();
+                    });
                                }
-                       },
-                       addProfile: function() {
-                               if (this.validate()) {
-                                       notify_progress("Creating profile...", true);
 
-                                       var query = "?op=rpc&method=addprofile&title=" +
-                                               param_escape(dialog.attr('value').newprofile);
+                       } else {
+                               alert(__("Please choose a profile to activate."));
+                       }
+               },
+               addProfile: function () {
+                       if (this.validate()) {
+                               notify_progress("Creating profile...", true);
 
-                                       new Ajax.Request("backend.php", {
-                                               parameters: query,
-                                               onComplete: function(transport) {
-                                                       notify('');
-                                                       editProfiles();
-                                               } });
+                               const query = { op: "rpc", method: "addprofile", title: dialog.attr('value').newprofile };
 
-                               }
-                       },
-                       execute: function() {
-                               if (this.validate()) {
-                               }
-                       },
-                       href: query});
+                               xhrPost("backend.php", query, () => {
+                                       notify('');
+                                       editProfiles();
+                               });
 
-               dialog.show();
-       } catch (e) {
-               exception_error("editProfiles", e);
-       }
+                       }
+               },
+               execute: function () {
+                       if (this.validate()) {
+                       }
+               },
+               href: query
+       });
+
+       dialog.show();
 }
 
+/*
 function activatePrefProfile() {
 
-       var sel_rows = getSelectedFeedCats();
+       const sel_rows = getSelectedFeedCats();
 
        if (sel_rows.length == 1) {
 
-               var ok = confirm(__("Activate selected profile?"));
+               const ok = confirm(__("Activate selected profile?"));
 
                if (ok) {
                        notify_progress("Loading, please wait...");
 
-                       var query = "?op=rpc&method=setprofile&id="+
-                               param_escape(sel_rows.toString());
-
-                       new Ajax.Request("backend.php", {
-                               parameters: query,
-                               onComplete: function(transport) {
-                                       window.location.reload();
-                               } });
+                       xhrPost("backend.php", { op: "rpc", method: "setprofile", id: sel_rows.toString() },  () => {
+                               window.location.reload();
+                       });
                }
 
        } else {
@@ -1513,282 +1009,192 @@ function activatePrefProfile() {
        }
 
        return false;
-}
+} */
 
 function clearFeedAccessKeys() {
 
-       var ok = confirm(__("This will invalidate all previously generated feed URLs. Continue?"));
-
-       if (ok) {
+       if (confirm(__("This will invalidate all previously generated feed URLs. Continue?"))) {
                notify_progress("Clearing URLs...");
 
-               var query = "?op=pref-feeds&method=clearKeys";
-
-               new Ajax.Request("backend.php", {
-                       parameters: query,
-                       onComplete: function(transport) {
-                               notify_info("Generated URLs cleared.");
-                       } });
+               xhrPost("backend.php", { op: "pref-feeds", method: "clearKeys" }, () => {
+            notify_info("Generated URLs cleared.");
+        });
        }
 
        return false;
 }
 
 function resetFilterOrder() {
-       try {
-               notify_progress("Loading, please wait...");
-
-               new Ajax.Request("backend.php", {
-                       parameters: "?op=pref-filters&method=filtersortreset",
-                       onComplete: function(transport) {
-                               updateFilterList();
-                       } });
+       notify_progress("Loading, please wait...");
 
-
-       } catch (e) {
-               exception_error("resetFilterOrder");
-       }
+    xhrPost("backend.php", { op: "pref-filters", method: "filtersortreset" }, () => {
+        updateFilterList();
+    });
 }
 
 
 function resetFeedOrder() {
-       try {
-               notify_progress("Loading, please wait...");
+       notify_progress("Loading, please wait...");
 
-               new Ajax.Request("backend.php", {
-                       parameters: "?op=pref-feeds&method=feedsortreset",
-                       onComplete: function(transport) {
-                               updateFeedList();
-                       } });
-
-
-       } catch (e) {
-               exception_error("resetFeedOrder");
-       }
+    xhrPost("backend.php", { op: "pref-feeds", method: "feedsortreset" }, () => {
+        updateFeedList();
+    });
 }
 
 function resetCatOrder() {
-       try {
-               notify_progress("Loading, please wait...");
+       notify_progress("Loading, please wait...");
 
-               new Ajax.Request("backend.php", {
-                       parameters: "?op=pref-feeds&method=catsortreset",
-                       onComplete: function(transport) {
-                               updateFeedList();
-                       } });
-
-
-       } catch (e) {
-               exception_error("resetCatOrder");
-       }
+       xhrPost("backend.php", { op: "pref-feeds", method: "catsortreset" }, () => {
+               updateFeedList();
+       });
 }
 
-function editCat(id, item, event) {
-       try {
-               var new_name = prompt(__('Rename category to:'), item.name);
+function editCat(id, item) {
+       const new_name = prompt(__('Rename category to:'), item.name);
 
-               if (new_name && new_name != item.name) {
+       if (new_name && new_name != item.name) {
 
-                       notify_progress("Loading, please wait...");
-
-                       new Ajax.Request("backend.php", {
-                       parameters: {
-                               op: 'pref-feeds',
-                               method: 'renamecat',
-                               id: id,
-                               title: new_name,
-                       },
-                       onComplete: function(transport) {
-                               updateFeedList();
-                       } });
-               }
+               notify_progress("Loading, please wait...");
 
-       } catch (e) {
-               exception_error("editCat", e);
+               xhrPost("backend.php", { op: 'pref-feeds', method: 'renamecat', id: id, title: new_name }, () => {
+                       updateFeedList();
+               });
        }
 }
 
-function editLabel(id, event) {
-       try {
-               var query = "backend.php?op=pref-labels&method=edit&id=" +
-                       param_escape(id);
-
-               if (dijit.byId("labelEditDlg"))
-                       dijit.byId("labelEditDlg").destroyRecursive();
-
-               dialog = new dijit.Dialog({
-                       id: "labelEditDlg",
-                       title: __("Label Editor"),
-                       style: "width: 600px",
-                       setLabelColor: function(id, fg, bg) {
-
-                               var kind = '';
-                               var color = '';
-
-                               if (fg && bg) {
-                                       kind = 'both';
-                               } else if (fg) {
-                                       kind = 'fg';
-                                       color = fg;
-                               } else if (bg) {
-                                       kind = 'bg';
-                                       color = bg;
-                               }
+function editLabel(id) {
+       const query = "backend.php?op=pref-labels&method=edit&id=" +
+               param_escape(id);
+
+       if (dijit.byId("labelEditDlg"))
+               dijit.byId("labelEditDlg").destroyRecursive();
+
+       const dialog = new dijit.Dialog({
+               id: "labelEditDlg",
+               title: __("Label Editor"),
+               style: "width: 600px",
+               setLabelColor: function (id, fg, bg) {
+
+                       let kind = '';
+                       let color = '';
+
+                       if (fg && bg) {
+                               kind = 'both';
+                       } else if (fg) {
+                               kind = 'fg';
+                               color = fg;
+                       } else if (bg) {
+                               kind = 'bg';
+                               color = bg;
+                       }
 
-                               var query = "?op=pref-labels&method=colorset&kind="+kind+
-                                       "&ids=" + param_escape(id) + "&fg=" + param_escape(fg) +
-                                       "&bg=" + param_escape(bg) + "&color=" + param_escape(color);
+                       const e = $("LICID-" + id);
 
-               //              console.log(query);
+                       if (e) {
+                               if (fg) e.style.color = fg;
+                               if (bg) e.style.backgroundColor = bg;
+                       }
 
-                               var e = $("LICID-" + id);
+            const query = { op: "pref-labels", method: "colorset", kind: kind,
+                ids: id, fg: fg, bg: bg, color: color };
 
-                               if (e) {
-                                       if (fg) e.style.color = fg;
-                                       if (bg) e.style.backgroundColor = bg;
-                               }
+            xhrPost("backend.php", query, () => {
+                updateFilterList(); // maybe there's labels in there
+                       });
 
-                               new Ajax.Request("backend.php", { parameters: query });
+               },
+               execute: function () {
+                       if (this.validate()) {
+                               const caption = this.attr('value').caption;
+                               const fg_color = this.attr('value').fg_color;
+                               const bg_color = this.attr('value').bg_color;
 
-                               updateFilterList();
-                       },
-                       execute: function() {
-                               if (this.validate()) {
-                                       var caption = this.attr('value').caption;
-                                       var fg_color = this.attr('value').fg_color;
-                                       var bg_color = this.attr('value').bg_color;
-                                       var query = dojo.objectToQuery(this.attr('value'));
-
-                                       dijit.byId('labelTree').setNameById(id, caption);
-                                       this.setLabelColor(id, fg_color, bg_color);
-                                       this.hide();
-
-                                       new Ajax.Request("backend.php", {
-                                               parameters: query,
-                                               onComplete: function(transport) {
-                                                       updateFilterList();
-                                       } });
-                               }
-                       },
-                       href: query});
+                               dijit.byId('labelTree').setNameById(id, caption);
+                               this.setLabelColor(id, fg_color, bg_color);
+                               this.hide();
 
-               dialog.show();
+                               xhrPost("backend.php", this.attr('value'), () => {
+                    updateFilterList(); // maybe there's labels in there
+                               });
+                       }
+               },
+               href: query
+       });
 
-       } catch (e) {
-               exception_error("editLabel", e);
-       }
+       dialog.show();
 }
 
 
 function customizeCSS() {
-       try {
-               var query = "backend.php?op=pref-prefs&method=customizeCSS";
+       const query = "backend.php?op=pref-prefs&method=customizeCSS";
 
-               if (dijit.byId("cssEditDlg"))
-                       dijit.byId("cssEditDlg").destroyRecursive();
+       if (dijit.byId("cssEditDlg"))
+               dijit.byId("cssEditDlg").destroyRecursive();
 
-               dialog = new dijit.Dialog({
-                       id: "cssEditDlg",
-                       title: __("Customize stylesheet"),
-                       style: "width: 600px",
-                       execute: function() {
-                               notify_progress('Saving data...', true);
-                               new Ajax.Request("backend.php", {
-                                       parameters: dojo.objectToQuery(this.attr('value')),
-                                       onComplete: function(transport) {
-                                               notify('');
-                                               window.location.reload();
-                               } });
+       const dialog = new dijit.Dialog({
+               id: "cssEditDlg",
+               title: __("Customize stylesheet"),
+               style: "width: 600px",
+               execute: function () {
+                       notify_progress('Saving data...', true);
 
-                       },
-                       href: query});
+                       xhrPost("backend.php", this.attr('value'), () => {
+                               window.location.reload();
+                       });
 
-               dialog.show();
+               },
+               href: query
+       });
 
-       } catch (e) {
-               exception_error("customizeCSS", e);
-       }
+       dialog.show();
 }
 
 function insertSSLserial(value) {
-       try {
-               dijit.byId("SSL_CERT_SERIAL").attr('value', value);
-       } catch (e) {
-               exception_error("insertSSLcerial", e);
-       }
+       dijit.byId("SSL_CERT_SERIAL").attr('value', value);
 }
 
 function gotoExportOpml(filename, settings) {
-       tmp = settings ? 1 : 0;
+       const tmp = settings ? 1 : 0;
        document.location.href = "backend.php?op=opml&method=export&filename=" + filename + "&settings=" + tmp;
 }
 
 
 function batchSubscribe() {
-       try {
-               var query = "backend.php?op=pref-feeds&method=batchSubscribe";
-
-               // overlapping widgets
-               if (dijit.byId("batchSubDlg")) dijit.byId("batchSubDlg").destroyRecursive();
-               if (dijit.byId("feedAddDlg"))   dijit.byId("feedAddDlg").destroyRecursive();
-
-               var dialog = new dijit.Dialog({
-                       id: "batchSubDlg",
-                       title: __("Batch subscribe"),
-                       style: "width: 600px",
-                       execute: function() {
-                               if (this.validate()) {
-                                       console.log(dojo.objectToQuery(this.attr('value')));
-
-                                       notify_progress(__("Subscribing to feeds..."), true);
-
-                                       new Ajax.Request("backend.php", {
-                                               parameters: dojo.objectToQuery(this.attr('value')),
-                                               onComplete: function(transport) {
-                                                       notify("");
-                                                       updateFeedList();
-                                                       dialog.hide();
-                                               } });
-                                       }
-                       },
-                       href: query});
-
-               dialog.show();
-       } catch (e) {
-               exception_error("batchSubscribe", e);
-       }
-}
-
-
-function toggleAdvancedPrefs() {
-       try {
-               notify_progress("Loading, please wait...");
-
-               new Ajax.Request("backend.php", {
-                       parameters: "?op=pref-prefs&method=toggleadvanced",
-                       onComplete: function(transport) {
-                               updatePrefsList();
-                       } });
+       const query = "backend.php?op=pref-feeds&method=batchSubscribe";
+
+       // overlapping widgets
+       if (dijit.byId("batchSubDlg")) dijit.byId("batchSubDlg").destroyRecursive();
+       if (dijit.byId("feedAddDlg"))    dijit.byId("feedAddDlg").destroyRecursive();
+
+       const dialog = new dijit.Dialog({
+               id: "batchSubDlg",
+               title: __("Batch subscribe"),
+               style: "width: 600px",
+               execute: function () {
+                       if (this.validate()) {
+                               notify_progress(__("Subscribing to feeds..."), true);
+
+                               xhrPost("backend.php", this.attr('value'), () => {
+                                       notify("");
+                                       updateFeedList();
+                                       dialog.hide();
+                               });
+                       }
+               },
+               href: query
+       });
 
-       } catch (e) {
-               exception_error("toggleAdvancedPrefs", e);
-       }
+       dialog.show();
 }
 
 function clearPluginData(name) {
-       try {
-               if (confirm(__("Clear stored data for this plugin?"))) {
-                       notify_progress("Loading, please wait...");
+       if (confirm(__("Clear stored data for this plugin?"))) {
+               notify_progress("Loading, please wait...");
 
-                       new Ajax.Request("backend.php", {
-                               parameters: "?op=pref-prefs&method=clearplugindata&name=" + param_escape(name),
-                               onComplete: function(transport) {
-                                       notify('');
-                                       updatePrefsList();
-                               } });
-               }
-       } catch (e) {
-               exception_error("clearPluginData", e);
+               xhrPost("backend.php", { op: "pref-prefs", method: "clearplugindata", name: name }, () => {
+                       notify('');
+                       updatePrefsList();
+               });
        }
 }
 
@@ -1797,15 +1203,15 @@ function clearSqlLog() {
        if (confirm(__("Clear all messages in the error log?"))) {
 
                notify_progress("Loading, please wait...");
-               var query = "?op=pref-system&method=clearLog";
 
-               new Ajax.Request("backend.php", {
-                       parameters: query,
-                       onComplete: function(transport) {
-                               updateSystemList();
-                       } });
+               xhrPost("backend.php",  { op: "pref-system", method: "clearLog" }, () => {
+                       updateSystemList();
+               });
 
        }
 }
 
+function updateSelectedPrompt() {
+       // no-op shim for toggleSelectedRow()
+}