| OLD | NEW | 
|     1 /* |     1 /* | 
|     2  * This file is part of Adblock Plus <https://adblockplus.org/>, |     2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
|     3  * Copyright (C) 2006-2016 Eyeo GmbH |     3  * Copyright (C) 2006-2016 Eyeo GmbH | 
|     4  * |     4  * | 
|     5  * Adblock Plus is free software: you can redistribute it and/or modify |     5  * Adblock Plus is free software: you can redistribute it and/or modify | 
|     6  * it under the terms of the GNU General Public License version 3 as |     6  * it under the terms of the GNU General Public License version 3 as | 
|     7  * published by the Free Software Foundation. |     7  * published by the Free Software Foundation. | 
|     8  * |     8  * | 
|     9  * Adblock Plus is distributed in the hope that it will be useful, |     9  * Adblock Plus is distributed in the hope that it will be useful, | 
|    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of |    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|    12  * GNU General Public License for more details. |    12  * GNU General Public License for more details. | 
|    13  * |    13  * | 
|    14  * You should have received a copy of the GNU General Public License |    14  * You should have received a copy of the GNU General Public License | 
|    15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. |    15  * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
|    16  */ |    16  */ | 
|    17  |    17  | 
 |    18 /* globals checkShareResource, getDocLink, i18nFormatDateTime, openSharePopup, | 
 |    19            E */ | 
 |    20  | 
|    18 "use strict"; |    21 "use strict"; | 
|    19  |    22  | 
|    20 (function() |  | 
|    21 { |    23 { | 
|    22   var subscriptionsMap = Object.create(null); |    24   let subscriptionsMap = Object.create(null); | 
|    23   var filtersMap = Object.create(null); |    25   let filtersMap = Object.create(null); | 
|    24   var collections = Object.create(null); |    26   let collections = Object.create(null); | 
|    25   var acceptableAdsUrl = null; |    27   let acceptableAdsUrl = null; | 
|    26   var getMessage = ext.i18n.getMessage; |    28   let {getMessage} = ext.i18n; | 
|    27   var filterErrors = |    29   let filterErrors = new Map([ | 
|    28   { |    30     ["synchronize_invalid_url", | 
|    29     "synchronize_invalid_url": "options_filterList_lastDownload_invalidURL", |    31      "options_filterList_lastDownload_invalidURL"], | 
|    30     "synchronize_connection_error": "options_filterList_lastDownload_connectionE
      rror", |    32     ["synchronize_connection_error", | 
|    31     "synchronize_invalid_data": "options_filterList_lastDownload_invalidData", |    33      "options_filterList_lastDownload_connectionError"], | 
|    32     "synchronize_checksum_mismatch": "options_filterList_lastDownload_checksumMi
      smatch" |    34     ["synchronize_invalid_data", | 
|    33   }; |    35      "options_filterList_lastDownload_invalidData"], | 
 |    36     ["synchronize_checksum_mismatch", | 
 |    37      "options_filterList_lastDownload_checksumMismatch"] | 
 |    38   ]); | 
|    34  |    39  | 
|    35   function Collection(details) |    40   function Collection(details) | 
|    36   { |    41   { | 
|    37     this.details = details; |    42     this.details = details; | 
|    38     this.items = []; |    43     this.items = []; | 
|    39   } |    44   } | 
|    40  |    45  | 
|    41   Collection.prototype._setEmpty = function(table, text) |    46   Collection.prototype._setEmpty = function(table, text) | 
|    42   { |    47   { | 
|    43     var placeholder = table.querySelector(".empty-placeholder"); |    48     let placeholder = table.querySelector(".empty-placeholder"); | 
|    44     if (text && !placeholder) |    49     if (text && !placeholder) | 
|    45     { |    50     { | 
|    46       placeholder = document.createElement("li"); |    51       placeholder = document.createElement("li"); | 
|    47       placeholder.className = "empty-placeholder"; |    52       placeholder.className = "empty-placeholder"; | 
|    48       placeholder.textContent = getMessage(text); |    53       placeholder.textContent = getMessage(text); | 
|    49       table.appendChild(placeholder); |    54       table.appendChild(placeholder); | 
|    50     } |    55     } | 
|    51     else if (placeholder) |    56     else if (placeholder) | 
|    52       table.removeChild(placeholder); |    57       table.removeChild(placeholder); | 
|    53   }; |    58   }; | 
|    54  |    59  | 
|    55   Collection.prototype._createElementQuery = function(item) |    60   Collection.prototype._createElementQuery = function(item) | 
|    56   { |    61   { | 
|    57     var access = (item.url || item.text).replace(/'/g, "\\'"); |    62     let access = (item.url || item.text).replace(/'/g, "\\'"); | 
|    58     return function(container) |    63     return function(container) | 
|    59     { |    64     { | 
|    60       return container.querySelector("[data-access='" + access + "']"); |    65       return container.querySelector("[data-access='" + access + "']"); | 
|    61     }; |    66     }; | 
|    62   }; |    67   }; | 
|    63  |    68  | 
|    64   Collection.prototype._getItemTitle = function(item, i) |    69   Collection.prototype._getItemTitle = function(item, i) | 
|    65   { |    70   { | 
|    66     if (item.url == acceptableAdsUrl) |    71     if (item.url == acceptableAdsUrl) | 
|    67       return getMessage("options_acceptableAds_description"); |    72       return getMessage("options_acceptableAds_description"); | 
|    68     if (this.details[i].useOriginalTitle && item.originalTitle) |    73     if (this.details[i].useOriginalTitle && item.originalTitle) | 
|    69       return item.originalTitle; |    74       return item.originalTitle; | 
|    70     return item.title || item.url || item.text; |    75     return item.title || item.url || item.text; | 
|    71   }; |    76   }; | 
|    72  |    77  | 
|    73   Collection.prototype.addItem = function(item) |    78   Collection.prototype.addItem = function(item) | 
|    74   { |    79   { | 
|    75     if (this.items.indexOf(item) >= 0) |    80     if (this.items.indexOf(item) >= 0) | 
|    76       return; |    81       return; | 
|    77  |    82  | 
|    78     this.items.push(item); |    83     this.items.push(item); | 
|    79     this.items.sort(function(a, b) |    84     this.items.sort((a, b) => | 
|    80     { |    85     { | 
|    81       // Make sure that Acceptable Ads is always last, since it cannot be |    86       // Make sure that Acceptable Ads is always last, since it cannot be | 
|    82       // disabled, but only be removed. That way it's grouped together with |    87       // disabled, but only be removed. That way it's grouped together with | 
|    83       // the "Own filter list" which cannot be disabled either at the bottom |    88       // the "Own filter list" which cannot be disabled either at the bottom | 
|    84       // of the filter lists in the Advanced tab. |    89       // of the filter lists in the Advanced tab. | 
|    85       if (a.url == acceptableAdsUrl) |    90       if (a.url == acceptableAdsUrl) | 
|    86         return 1; |    91         return 1; | 
|    87       if (b.url == acceptableAdsUrl) |    92       if (b.url == acceptableAdsUrl) | 
|    88         return -1; |    93         return -1; | 
|    89  |    94  | 
|    90       var aTitle = this._getItemTitle(a, 0).toLowerCase(); |    95       let aTitle = this._getItemTitle(a, 0).toLowerCase(); | 
|    91       var bTitle = this._getItemTitle(b, 0).toLowerCase(); |    96       let bTitle = this._getItemTitle(b, 0).toLowerCase(); | 
|    92       return aTitle.localeCompare(bTitle); |    97       return aTitle.localeCompare(bTitle); | 
|    93     }.bind(this)); |    98     }); | 
|    94  |    99  | 
|    95     for (var j = 0; j < this.details.length; j++) |   100     for (let j = 0; j < this.details.length; j++) | 
|    96     { |   101     { | 
|    97       var table = E(this.details[j].id); |   102       let table = E(this.details[j].id); | 
|    98       var template = table.querySelector("template"); |   103       let template = table.querySelector("template"); | 
|    99       var listItem = document.createElement("li"); |   104       let listItem = document.createElement("li"); | 
|   100       listItem.appendChild(document.importNode(template.content, true)); |   105       listItem.appendChild(document.importNode(template.content, true)); | 
|   101       listItem.setAttribute("aria-label", this._getItemTitle(item, j)); |   106       listItem.setAttribute("aria-label", this._getItemTitle(item, j)); | 
|   102       listItem.setAttribute("data-access", item.url || item.text); |   107       listItem.setAttribute("data-access", item.url || item.text); | 
|   103       listItem.setAttribute("role", "section"); |   108       listItem.setAttribute("role", "section"); | 
|   104  |   109  | 
|   105       var label = listItem.querySelector(".display"); |   110       let label = listItem.querySelector(".display"); | 
|   106       if (item.recommended && label.hasAttribute("data-tooltip")) |   111       if (item.recommended && label.hasAttribute("data-tooltip")) | 
|   107       { |   112       { | 
|   108         var tooltipId = label.getAttribute("data-tooltip"); |   113         let tooltipId = label.getAttribute("data-tooltip"); | 
|   109         tooltipId = tooltipId.replace("%value%", item.recommended); |   114         tooltipId = tooltipId.replace("%value%", item.recommended); | 
|   110         label.setAttribute("data-tooltip", tooltipId); |   115         label.setAttribute("data-tooltip", tooltipId); | 
|   111       } |   116       } | 
|   112  |   117  | 
|   113       var controls = listItem.querySelectorAll(".control"); |   118       for (let control of listItem.querySelectorAll(".control")) | 
|   114       for (var k = 0; k < controls.length; k++) |  | 
|   115       { |   119       { | 
|   116         if (controls[k].hasAttribute("title")) |   120         if (control.hasAttribute("title")) | 
|   117         { |   121         { | 
|   118           var titleValue = getMessage(controls[k].getAttribute("title")); |   122           let titleValue = getMessage(control.getAttribute("title")); | 
|   119           controls[k].setAttribute("title", titleValue) |   123           control.setAttribute("title", titleValue); | 
|   120         } |   124         } | 
|   121       } |   125       } | 
|   122  |   126  | 
|   123       this._setEmpty(table, null); |   127       this._setEmpty(table, null); | 
|   124       if (table.hasChildNodes()) |   128       if (table.hasChildNodes()) | 
|   125       { |   129       { | 
|   126         table.insertBefore(listItem, |   130         table.insertBefore(listItem, | 
|   127             table.childNodes[this.items.indexOf(item)]); |   131             table.childNodes[this.items.indexOf(item)]); | 
|   128       } |   132       } | 
|   129       else |   133       else | 
|   130         table.appendChild(listItem); |   134         table.appendChild(listItem); | 
|   131       this.updateItem(item); |   135       this.updateItem(item); | 
|   132     } |   136     } | 
|   133     return length; |   137     return length; | 
|   134   }; |   138   }; | 
|   135  |   139  | 
|   136   Collection.prototype.removeItem = function(item) |   140   Collection.prototype.removeItem = function(item) | 
|   137   { |   141   { | 
|   138     var index = this.items.indexOf(item); |   142     let index = this.items.indexOf(item); | 
|   139     if (index == -1) |   143     if (index == -1) | 
|   140       return; |   144       return; | 
|   141  |   145  | 
|   142     this.items.splice(index, 1); |   146     this.items.splice(index, 1); | 
|   143     var getListElement = this._createElementQuery(item); |   147     let getListElement = this._createElementQuery(item); | 
|   144     for (var i = 0; i < this.details.length; i++) |   148     for (let detail of this.details) | 
|   145     { |   149     { | 
|   146       var table = E(this.details[i].id); |   150       let table = E(detail.id); | 
|   147       var element = getListElement(table); |   151       let element = getListElement(table); | 
|   148  |   152  | 
|   149       // Element gets removed so make sure to handle focus appropriately |   153       // Element gets removed so make sure to handle focus appropriately | 
|   150       var control = element.querySelector(".control"); |   154       let control = element.querySelector(".control"); | 
|   151       if (control && control == document.activeElement) |   155       if (control && control == document.activeElement) | 
|   152       { |   156       { | 
|   153         if (!focusNextElement(element.parentElement, control)) |   157         if (!focusNextElement(element.parentElement, control)) | 
|   154         { |   158         { | 
|   155           // Fall back to next focusable element within same tab or dialog |   159           // Fall back to next focusable element within same tab or dialog | 
|   156           var focusableElement = element.parentElement; |   160           let focusableElement = element.parentElement; | 
|   157           while (focusableElement) |   161           while (focusableElement) | 
|   158           { |   162           { | 
|   159             if (focusableElement.classList.contains("tab-content") |   163             if (focusableElement.classList.contains("tab-content") || | 
|   160                 || focusableElement.classList.contains("dialog-content")) |   164                 focusableElement.classList.contains("dialog-content")) | 
|   161               break; |   165               break; | 
|   162  |   166  | 
|   163             focusableElement = focusableElement.parentElement; |   167             focusableElement = focusableElement.parentElement; | 
|   164           } |   168           } | 
|   165           focusNextElement(focusableElement || document, control); |   169           focusNextElement(focusableElement || document, control); | 
|   166         } |   170         } | 
|   167       } |   171       } | 
|   168  |   172  | 
|   169       element.parentElement.removeChild(element); |   173       element.parentElement.removeChild(element); | 
|   170       if (this.items.length == 0) |   174       if (this.items.length == 0) | 
|   171         this._setEmpty(table, this.details[i].emptyText); |   175         this._setEmpty(table, detail.emptyText); | 
|   172     } |   176     } | 
|   173   }; |   177   }; | 
|   174  |   178  | 
|   175   Collection.prototype.updateItem = function(item) |   179   Collection.prototype.updateItem = function(item) | 
|   176   { |   180   { | 
|   177     var access = (item.url || item.text).replace(/'/g, "\\'"); |   181     let access = (item.url || item.text).replace(/'/g, "\\'"); | 
|   178     for (var i = 0; i < this.details.length; i++) |   182     for (let i = 0; i < this.details.length; i++) | 
|   179     { |   183     { | 
|   180       var table = E(this.details[i].id); |   184       let table = E(this.details[i].id); | 
|   181       var element = table.querySelector("[data-access='" + access + "']"); |   185       let element = table.querySelector("[data-access='" + access + "']"); | 
|   182       if (!element) |   186       if (!element) | 
|   183         continue; |   187         continue; | 
|   184  |   188  | 
|   185       var title = this._getItemTitle(item, i); |   189       let title = this._getItemTitle(item, i); | 
|   186       element.querySelector(".display").textContent = title; |   190       element.querySelector(".display").textContent = title; | 
|   187       element.setAttribute("aria-label", title); |   191       element.setAttribute("aria-label", title); | 
|   188       if (this.details[i].searchable) |   192       if (this.details[i].searchable) | 
|   189         element.setAttribute("data-search", title.toLowerCase()); |   193         element.setAttribute("data-search", title.toLowerCase()); | 
|   190       var control = element.querySelector(".control[role='checkbox']"); |   194       let control = element.querySelector(".control[role='checkbox']"); | 
|   191       if (control) |   195       if (control) | 
|   192       { |   196       { | 
|   193         control.setAttribute("aria-checked", item.disabled == false); |   197         control.setAttribute("aria-checked", item.disabled == false); | 
|   194         if (item.url == acceptableAdsUrl && this == collections.filterLists) |   198         if (item.url == acceptableAdsUrl && this == collections.filterLists) | 
|   195           control.setAttribute("disabled", true); |   199           control.setAttribute("disabled", true); | 
|   196       } |   200       } | 
|   197  |   201  | 
|   198       var dateElement = element.querySelector(".date"); |   202       let dateElement = element.querySelector(".date"); | 
|   199       var timeElement = element.querySelector(".time"); |   203       let timeElement = element.querySelector(".time"); | 
|   200       if (dateElement && timeElement) |   204       if (dateElement && timeElement) | 
|   201       { |   205       { | 
|   202         var message = element.querySelector(".message"); |   206         let message = element.querySelector(".message"); | 
|   203         if (item.isDownloading) |   207         if (item.isDownloading) | 
|   204         { |   208         { | 
|   205           var text = getMessage("options_filterList_lastDownload_inProgress"); |   209           let text = getMessage("options_filterList_lastDownload_inProgress"); | 
|   206           message.textContent = text; |   210           message.textContent = text; | 
|   207           element.classList.add("show-message"); |   211           element.classList.add("show-message"); | 
|   208         } |   212         } | 
|   209         else if (item.downloadStatus != "synchronize_ok") |   213         else if (item.downloadStatus != "synchronize_ok") | 
|   210         { |   214         { | 
|   211           var error = filterErrors[item.downloadStatus]; |   215           let error = filterErrors.get(item.downloadStatus); | 
|   212           if (error) |   216           if (error) | 
|   213             message.textContent = getMessage(error); |   217             message.textContent = getMessage(error); | 
|   214           else |   218           else | 
|   215             message.textContent = item.downloadStatus; |   219             message.textContent = item.downloadStatus; | 
|   216           element.classList.add("show-message"); |   220           element.classList.add("show-message"); | 
|   217         } |   221         } | 
|   218         else if (item.lastDownload > 0) |   222         else if (item.lastDownload > 0) | 
|   219         { |   223         { | 
|   220           var dateTime = i18n_formatDateTime(item.lastDownload * 1000); |   224           let dateTime = i18nFormatDateTime(item.lastDownload * 1000); | 
|   221           dateElement.textContent = dateTime[0]; |   225           dateElement.textContent = dateTime[0]; | 
|   222           timeElement.textContent = dateTime[1]; |   226           timeElement.textContent = dateTime[1]; | 
|   223           element.classList.remove("show-message"); |   227           element.classList.remove("show-message"); | 
|   224         } |   228         } | 
|   225       } |   229       } | 
|   226  |   230  | 
|   227       var websiteElement = element.querySelector(".context-menu .website"); |   231       let websiteElement = element.querySelector(".context-menu .website"); | 
|   228       if (websiteElement) |   232       if (websiteElement) | 
|   229       { |   233       { | 
|   230         if (item.homepage) |   234         if (item.homepage) | 
|   231           websiteElement.setAttribute("href", item.homepage); |   235           websiteElement.setAttribute("href", item.homepage); | 
|   232         else |   236         else | 
|   233           websiteElement.setAttribute("aria-hidden", true); |   237           websiteElement.setAttribute("aria-hidden", true); | 
|   234       } |   238       } | 
|   235  |   239  | 
|   236       var sourceElement = element.querySelector(".context-menu .source"); |   240       let sourceElement = element.querySelector(".context-menu .source"); | 
|   237       if (sourceElement) |   241       if (sourceElement) | 
|   238         sourceElement.setAttribute("href", item.url); |   242         sourceElement.setAttribute("href", item.url); | 
|   239     } |   243     } | 
|   240   }; |   244   }; | 
|   241  |   245  | 
|   242   Collection.prototype.clearAll = function() |   246   Collection.prototype.clearAll = function() | 
|   243   { |   247   { | 
|   244     this.items = []; |   248     this.items = []; | 
|   245     for (var i = 0; i < this.details.length; i++) |   249     for (let detail of this.details) | 
|   246     { |   250     { | 
|   247       var table = E(this.details[i].id); |   251       let table = E(detail.id); | 
|   248       var element = table.firstChild; |   252       let element = table.firstChild; | 
|   249       while (element) |   253       while (element) | 
|   250       { |   254       { | 
|   251         if (element.tagName == "LI" && !element.classList.contains("static")) |   255         if (element.tagName == "LI" && !element.classList.contains("static")) | 
|   252           table.removeChild(element); |   256           table.removeChild(element); | 
|   253         element = element.nextElementSibling; |   257         element = element.nextElementSibling; | 
|   254       } |   258       } | 
|   255  |   259  | 
|   256       this._setEmpty(table, this.details[i].emptyText); |   260       this._setEmpty(table, detail.emptyText); | 
|   257     } |   261     } | 
|   258   }; |   262   }; | 
|   259  |   263  | 
|   260   function focusNextElement(container, currentElement) |   264   function focusNextElement(container, currentElement) | 
|   261   { |   265   { | 
|   262     var focusables = container.querySelectorAll("a, button, input, .control"); |   266     let focusables = container.querySelectorAll("a, button, input, .control"); | 
|   263     focusables = Array.prototype.slice.call(focusables); |   267     focusables = Array.prototype.slice.call(focusables); | 
|   264     var index = focusables.indexOf(currentElement); |   268     let index = focusables.indexOf(currentElement); | 
|   265     index += (index == focusables.length - 1) ? -1 : 1; |   269     index += (index == focusables.length - 1) ? -1 : 1; | 
|   266  |   270  | 
|   267     var nextElement = focusables[index]; |   271     let nextElement = focusables[index]; | 
|   268     if (!nextElement) |   272     if (!nextElement) | 
|   269       return false; |   273       return false; | 
|   270  |   274  | 
|   271     nextElement.focus(); |   275     nextElement.focus(); | 
|   272     return true; |   276     return true; | 
|   273   } |   277   } | 
|   274  |   278  | 
|   275   collections.popular = new Collection( |   279   collections.popular = new Collection([ | 
|   276   [ |  | 
|   277     { |   280     { | 
|   278       id: "recommend-list-table" |   281       id: "recommend-list-table" | 
|   279     } |   282     } | 
|   280   ]); |   283   ]); | 
|   281   collections.langs = new Collection( |   284   collections.langs = new Collection([ | 
|   282   [ |  | 
|   283     { |   285     { | 
|   284       id: "blocking-languages-table", |   286       id: "blocking-languages-table", | 
|   285       emptyText: "options_dialog_language_added_empty" |   287       emptyText: "options_dialog_language_added_empty" | 
|   286     }, |   288     }, | 
|   287     { |   289     { | 
|   288       id: "blocking-languages-dialog-table", |   290       id: "blocking-languages-dialog-table", | 
|   289       emptyText: "options_dialog_language_added_empty" |   291       emptyText: "options_dialog_language_added_empty" | 
|   290     } |   292     } | 
|   291   ]); |   293   ]); | 
|   292   collections.allLangs = new Collection( |   294   collections.allLangs = new Collection([ | 
|   293   [ |  | 
|   294     { |   295     { | 
|   295       id: "all-lang-table", |   296       id: "all-lang-table", | 
|   296       emptyText: "options_dialog_language_other_empty", |   297       emptyText: "options_dialog_language_other_empty", | 
|   297       searchable: true |   298       searchable: true | 
|   298     } |   299     } | 
|   299   ]); |   300   ]); | 
|   300   collections.acceptableAds = new Collection( |   301   collections.acceptableAds = new Collection([ | 
|   301   [ |  | 
|   302     { |   302     { | 
|   303       id: "acceptableads-table" |   303       id: "acceptableads-table" | 
|   304     } |   304     } | 
|   305   ]); |   305   ]); | 
|   306   collections.custom = new Collection( |   306   collections.custom = new Collection([ | 
|   307   [ |  | 
|   308     { |   307     { | 
|   309       id: "custom-list-table" |   308       id: "custom-list-table" | 
|   310     } |   309     } | 
|   311   ]); |   310   ]); | 
|   312   collections.whitelist = new Collection( |   311   collections.whitelist = new Collection([ | 
|   313   [ |  | 
|   314     { |   312     { | 
|   315       id: "whitelisting-table", |   313       id: "whitelisting-table", | 
|   316       emptyText: "options_whitelisted_empty" |   314       emptyText: "options_whitelisted_empty" | 
|   317     } |   315     } | 
|   318   ]); |   316   ]); | 
|   319   collections.customFilters = new Collection( |   317   collections.customFilters = new Collection([ | 
|   320   [ |  | 
|   321     { |   318     { | 
|   322       id: "custom-filters-table", |   319       id: "custom-filters-table", | 
|   323       emptyText: "options_customFilters_empty" |   320       emptyText: "options_customFilters_empty" | 
|   324     } |   321     } | 
|   325   ]); |   322   ]); | 
|   326   collections.filterLists = new Collection( |   323   collections.filterLists = new Collection([ | 
|   327   [ |  | 
|   328     { |   324     { | 
|   329       id: "all-filter-lists-table", |   325       id: "all-filter-lists-table", | 
|   330       useOriginalTitle: true |   326       useOriginalTitle: true | 
|   331     } |   327     } | 
|   332   ]); |   328   ]); | 
|   333  |   329  | 
|   334   function toggleShowLanguage(subscription) |   330   function toggleShowLanguage(subscription) | 
|   335   { |   331   { | 
|   336     if (subscription.recommended == "ads") |   332     if (subscription.recommended == "ads") | 
|   337     { |   333     { | 
|   338       if (subscription.disabled) |   334       if (subscription.disabled) | 
|   339       { |   335       { | 
|   340         collections.allLangs.addItem(subscription); |   336         collections.allLangs.addItem(subscription); | 
|   341         collections.langs.removeItem(subscription); |   337         collections.langs.removeItem(subscription); | 
|   342       } |   338       } | 
|   343       else |   339       else | 
|   344       { |   340       { | 
|   345         collections.allLangs.removeItem(subscription); |   341         collections.allLangs.removeItem(subscription); | 
|   346         collections.langs.addItem(subscription); |   342         collections.langs.addItem(subscription); | 
|   347       } |   343       } | 
|   348     } |   344     } | 
|   349   } |   345   } | 
|   350  |   346  | 
|   351   function addSubscription(subscription) |   347   function addSubscription(subscription) | 
|   352   { |   348   { | 
|   353     var collection; |   349     let collection; | 
|   354     if (subscription.recommended) |   350     if (subscription.recommended) | 
|   355     { |   351     { | 
|   356       if (subscription.recommended != "ads") |   352       if (subscription.recommended != "ads") | 
|   357         collection = collections.popular; |   353         collection = collections.popular; | 
|   358       else if (subscription.disabled == false) |   354       else if (subscription.disabled == false) | 
|   359         collection = collections.langs; |   355         collection = collections.langs; | 
|   360       else |   356       else | 
|   361         collection = collections.allLangs; |   357         collection = collections.allLangs; | 
|   362     } |   358     } | 
|   363     else if (subscription.url == acceptableAdsUrl) |   359     else if (subscription.url == acceptableAdsUrl) | 
|   364       collection = collections.acceptableAds; |   360       collection = collections.acceptableAds; | 
|   365     else |   361     else | 
|   366       collection = collections.custom; |   362       collection = collections.custom; | 
|   367  |   363  | 
|   368     collection.addItem(subscription); |   364     collection.addItem(subscription); | 
|   369     subscriptionsMap[subscription.url] = subscription; |   365     subscriptionsMap[subscription.url] = subscription; | 
|   370     toggleShowLanguage(subscription); |   366     toggleShowLanguage(subscription); | 
|   371     updateTooltips(); |   367     updateTooltips(); | 
|   372   } |   368   } | 
|   373  |   369  | 
|   374   function updateSubscription(subscription) |   370   function updateSubscription(subscription) | 
|   375   { |   371   { | 
|   376     for (var name in collections) |   372     for (let name in collections) | 
|   377       collections[name].updateItem(subscription); |   373       collections[name].updateItem(subscription); | 
|   378  |   374  | 
|   379     toggleShowLanguage(subscription); |   375     toggleShowLanguage(subscription); | 
|   380   } |   376   } | 
|   381  |   377  | 
|   382   function updateFilter(filter) |   378   function updateFilter(filter) | 
|   383   { |   379   { | 
|   384     var match = filter.text.match(/^@@\|\|([^\/:]+)\^\$document$/); |   380     let match = filter.text.match(/^@@\|\|([^/:]+)\^\$document$/); | 
|   385     if (match && !filtersMap[filter.text]) |   381     if (match && !filtersMap[filter.text]) | 
|   386     { |   382     { | 
|   387       filter.title = match[1]; |   383       filter.title = match[1]; | 
|   388       collections.whitelist.addItem(filter); |   384       collections.whitelist.addItem(filter); | 
|   389     } |   385     } | 
|   390     else |   386     else | 
|   391       collections.customFilters.addItem(filter); |   387       collections.customFilters.addItem(filter); | 
|   392  |   388  | 
|   393     filtersMap[filter.text] = filter; |   389     filtersMap[filter.text] = filter; | 
|   394   } |   390   } | 
|   395  |   391  | 
|   396   function loadRecommendations() |   392   function loadRecommendations() | 
|   397   { |   393   { | 
|   398     fetch("subscriptions.xml") |   394     fetch("subscriptions.xml") | 
|   399       .then(function(response) |   395       .then((response) => | 
|   400       { |   396       { | 
|   401         return response.text(); |   397         return response.text(); | 
|   402       }) |   398       }) | 
|   403       .then(function(text) |   399       .then((text) => | 
|   404       { |   400       { | 
|   405         var list = document.getElementById("subscriptionSelector"); |   401         let doc = new DOMParser().parseFromString(text, "application/xml"); | 
|   406         var doc = new DOMParser().parseFromString(text, "application/xml"); |   402         let elements = doc.documentElement.getElementsByTagName("subscription"); | 
|   407         var elements = doc.documentElement.getElementsByTagName("subscription"); |   403         for (let element of elements) | 
|   408         for (var i = 0; i < elements.length; i++) |  | 
|   409         { |   404         { | 
|   410           var element = elements[i]; |   405           let type = element.getAttribute("type"); | 
|   411           var type = element.getAttribute("type"); |   406           let subscription = { | 
|   412           var subscription = { |  | 
|   413             disabled: true, |   407             disabled: true, | 
|   414             downloadStatus: null, |   408             downloadStatus: null, | 
|   415             homepage: null, |   409             homepage: null, | 
|   416             originalTitle: element.getAttribute("title"), |   410             originalTitle: element.getAttribute("title"), | 
|   417             recommended: type, |   411             recommended: type, | 
|   418             url: element.getAttribute("url") |   412             url: element.getAttribute("url") | 
|   419           }; |   413           }; | 
|   420  |   414  | 
|   421           var prefix = element.getAttribute("prefixes"); |   415           let prefix = element.getAttribute("prefixes"); | 
|   422           if (prefix) |   416           if (prefix) | 
|   423           { |   417           { | 
|   424             prefix = prefix.replace(/\W/g, "_"); |   418             prefix = prefix.replace(/\W/g, "_"); | 
|   425             subscription.title = getMessage("options_language_" + prefix); |   419             subscription.title = getMessage("options_language_" + prefix); | 
|   426           } |   420           } | 
|   427           else |   421           else | 
|   428           { |   422           { | 
|   429             type = type.replace(/\W/g, "_"); |   423             type = type.replace(/\W/g, "_"); | 
|   430             subscription.title = getMessage("common_feature_" + type + "_title")
      ; |   424             subscription.title = getMessage("common_feature_" + | 
 |   425                                             type + "_title"); | 
|   431           } |   426           } | 
|   432  |   427  | 
|   433           addSubscription(subscription); |   428           addSubscription(subscription); | 
|   434         } |   429         } | 
|   435       }); |   430       }); | 
|   436   } |   431   } | 
|   437  |   432  | 
|   438   function findParentData(element, dataName, returnElement) |   433   function findParentData(element, dataName, returnElement) | 
|   439   { |   434   { | 
|   440     while (element) |   435     while (element) | 
|   441     { |   436     { | 
|   442       if (element.hasAttribute("data-" + dataName)) |   437       if (element.hasAttribute("data-" + dataName)) | 
|   443         return returnElement ? element : element.getAttribute("data-" + dataName
      ); |   438       { | 
 |   439         if (returnElement) | 
 |   440           return element; | 
 |   441         return element.getAttribute("data-" + dataName); | 
 |   442       } | 
|   444  |   443  | 
|   445       element = element.parentElement; |   444       element = element.parentElement; | 
|   446     } |   445     } | 
|   447     return null; |   446     return null; | 
|   448   } |   447   } | 
|   449  |   448  | 
|   450   function sendMessageHandleErrors(message, onSuccess) |   449   function sendMessageHandleErrors(message, onSuccess) | 
|   451   { |   450   { | 
|   452     ext.backgroundPage.sendMessage(message, function(errors) |   451     ext.backgroundPage.sendMessage(message, (errors) => | 
|   453     { |   452     { | 
|   454       if (errors.length > 0) |   453       if (errors.length > 0) | 
|   455         alert(errors.join("\n")); |   454         alert(errors.join("\n")); | 
|   456       else if (onSuccess) |   455       else if (onSuccess) | 
|   457         onSuccess(); |   456         onSuccess(); | 
|   458     }); |   457     }); | 
|   459   } |   458   } | 
|   460  |   459  | 
|   461   function openDocLink(id) |   460   function openDocLink(id) | 
|   462   { |   461   { | 
|   463     getDocLink(id, function(link) |   462     getDocLink(id, (link) => | 
|   464     { |   463     { | 
|   465       if (id == "share-general") |   464       if (id == "share-general") | 
|   466         openSharePopup(link); |   465         openSharePopup(link); | 
|   467       else |   466       else | 
|   468         location.href = link; |   467         location.href = link; | 
|   469     }); |   468     }); | 
|   470   } |   469   } | 
|   471  |   470  | 
|   472   function switchTab(id) |   471   function switchTab(id) | 
|   473   { |   472   { | 
|   474     location.hash = id; |   473     location.hash = id; | 
|   475   } |   474   } | 
|   476  |   475  | 
|   477   function onClick(e) |   476   function onClick(e) | 
|   478   { |   477   { | 
|   479     var context = document.querySelector(".show-context-menu"); |   478     let context = document.querySelector(".show-context-menu"); | 
|   480     if (context) |   479     if (context) | 
|   481       context.classList.remove("show-context-menu"); |   480       context.classList.remove("show-context-menu"); | 
|   482  |   481  | 
|   483     var element = e.target; |   482     let element = findParentData(e.target, "action", true); | 
|   484     while (true) |   483     if (!element) | 
 |   484       return; | 
 |   485  | 
 |   486     let actions = element.getAttribute("data-action").split(","); | 
 |   487     for (let action of actions) | 
|   485     { |   488     { | 
|   486       if (!element) |   489       switch (action) | 
|   487         return; |  | 
|   488  |  | 
|   489       if (element.hasAttribute("data-action")) |  | 
|   490         break; |  | 
|   491  |  | 
|   492       element = element.parentElement; |  | 
|   493     } |  | 
|   494  |  | 
|   495     var element = findParentData(e.target, "action", true); |  | 
|   496     var actions = element.getAttribute("data-action").split(","); |  | 
|   497     for (var i = 0; i < actions.length; i++) |  | 
|   498     { |  | 
|   499       switch (actions[i]) |  | 
|   500       { |   490       { | 
|   501         case "add-domain-exception": |   491         case "add-domain-exception": | 
|   502           addWhitelistedDomain(); |   492           addWhitelistedDomain(); | 
|   503           break; |   493           break; | 
|   504         case "add-predefined-subscription": |   494         case "add-predefined-subscription": { | 
|   505           var dialog = E("dialog-content-predefined"); |   495           let dialog = E("dialog-content-predefined"); | 
|   506           var title = dialog.querySelector("h3").textContent; |   496           let title = dialog.querySelector("h3").textContent; | 
|   507           var url = dialog.querySelector(".url").textContent; |   497           let url = dialog.querySelector(".url").textContent; | 
|   508           addEnableSubscription(url, title); |   498           addEnableSubscription(url, title); | 
|   509           closeDialog(); |   499           closeDialog(); | 
|   510           break; |   500           break; | 
 |   501         } | 
|   511         case "cancel-custom-filters": |   502         case "cancel-custom-filters": | 
|   512           E("custom-filters").classList.remove("mode-edit"); |   503           E("custom-filters").classList.remove("mode-edit"); | 
|   513           break; |   504           break; | 
|   514         case "cancel-domain-exception": |   505         case "cancel-domain-exception": | 
|   515           E("whitelisting-textbox").value = ""; |   506           E("whitelisting-textbox").value = ""; | 
|   516           document.querySelector("#whitelisting .controls").classList.remove("mo
      de-edit"); |   507           document.querySelector("#whitelisting .controls").classList. | 
 |   508             remove("mode-edit"); | 
|   517           break; |   509           break; | 
|   518         case "close-dialog": |   510         case "close-dialog": | 
|   519           closeDialog(); |   511           closeDialog(); | 
|   520           break; |   512           break; | 
|   521         case "edit-custom-filters": |   513         case "edit-custom-filters": | 
|   522           E("custom-filters").classList.add("mode-edit"); |   514           E("custom-filters").classList.add("mode-edit"); | 
|   523           editCustomFilters(); |   515           editCustomFilters(); | 
|   524           break; |   516           break; | 
|   525         case "edit-domain-exception": |   517         case "edit-domain-exception": | 
|   526           document.querySelector("#whitelisting .controls").classList.add("mode-
      edit"); |   518           document.querySelector("#whitelisting .controls").classList. | 
 |   519             add("mode-edit"); | 
|   527           E("whitelisting-textbox").focus(); |   520           E("whitelisting-textbox").focus(); | 
|   528           break; |   521           break; | 
|   529         case "import-subscription": |   522         case "import-subscription": { | 
|   530           var url = E("blockingList-textbox").value; |   523           let url = E("blockingList-textbox").value; | 
|   531           addEnableSubscription(url); |   524           addEnableSubscription(url); | 
|   532           closeDialog(); |   525           closeDialog(); | 
|   533           break; |   526           break; | 
|   534         case "open-dialog": |   527         } | 
|   535           var dialog = findParentData(element, "dialog", false); |   528         case "open-dialog": { | 
 |   529           let dialog = findParentData(element, "dialog", false); | 
|   536           openDialog(dialog); |   530           openDialog(dialog); | 
|   537           break; |   531           break; | 
|   538         case "open-doclink": |   532         } | 
|   539           var doclink = findParentData(element, "doclink", false); |   533         case "open-doclink": { | 
 |   534           let doclink = findParentData(element, "doclink", false); | 
|   540           openDocLink(doclink); |   535           openDocLink(doclink); | 
|   541           break; |   536           break; | 
 |   537         } | 
|   542         case "save-custom-filters": |   538         case "save-custom-filters": | 
|   543           sendMessageHandleErrors( |   539           sendMessageHandleErrors({ | 
|   544           { |  | 
|   545             type: "filters.importRaw", |   540             type: "filters.importRaw", | 
|   546             text: E("custom-filters-raw").value, |   541             text: E("custom-filters-raw").value, | 
|   547             removeExisting: true |   542             removeExisting: true | 
|   548           }, |   543           }, | 
|   549           function() |   544           () => | 
|   550           { |   545           { | 
|   551             E("custom-filters").classList.remove("mode-edit"); |   546             E("custom-filters").classList.remove("mode-edit"); | 
|   552           }); |   547           }); | 
|   553           break; |   548           break; | 
|   554         case "switch-tab": |   549         case "switch-tab": { | 
|   555           var tabId = findParentData(e.target, "tab", false); |   550           let tabId = findParentData(e.target, "tab", false); | 
|   556           switchTab(tabId); |   551           switchTab(tabId); | 
|   557           break; |   552           break; | 
 |   553         } | 
|   558         case "toggle-pref": |   554         case "toggle-pref": | 
|   559           ext.backgroundPage.sendMessage( |   555           ext.backgroundPage.sendMessage({ | 
|   560           { |  | 
|   561             type: "prefs.toggle", |   556             type: "prefs.toggle", | 
|   562             key: findParentData(element, "pref", false) |   557             key: findParentData(element, "pref", false) | 
|   563           }); |   558           }); | 
|   564           break; |   559           break; | 
|   565         case "update-all-subscriptions": |   560         case "update-all-subscriptions": | 
|   566           ext.backgroundPage.sendMessage( |   561           ext.backgroundPage.sendMessage({ | 
|   567           { |  | 
|   568             type: "subscriptions.update" |   562             type: "subscriptions.update" | 
|   569           }); |   563           }); | 
|   570           break; |   564           break; | 
|   571         case "open-context-menu": |   565         case "open-context-menu": { | 
|   572           var listItem = findParentData(element, "access", true); |   566           let listItem = findParentData(element, "access", true); | 
|   573           if (listItem != context) |   567           if (listItem != context) | 
|   574             listItem.classList.add("show-context-menu"); |   568             listItem.classList.add("show-context-menu"); | 
|   575           break; |   569           break; | 
 |   570         } | 
|   576         case "update-subscription": |   571         case "update-subscription": | 
|   577           ext.backgroundPage.sendMessage( |   572           ext.backgroundPage.sendMessage({ | 
|   578           { |  | 
|   579             type: "subscriptions.update", |   573             type: "subscriptions.update", | 
|   580             url: findParentData(element, "access", false) |   574             url: findParentData(element, "access", false) | 
|   581           }); |   575           }); | 
|   582           break; |   576           break; | 
|   583         case "remove-subscription": |   577         case "remove-subscription": | 
|   584           ext.backgroundPage.sendMessage( |   578           ext.backgroundPage.sendMessage({ | 
|   585           { |  | 
|   586             type: "subscriptions.remove", |   579             type: "subscriptions.remove", | 
|   587             url: findParentData(element, "access", false) |   580             url: findParentData(element, "access", false) | 
|   588           }); |   581           }); | 
|   589           break; |   582           break; | 
|   590         case "toggle-remove-subscription": |   583         case "toggle-remove-subscription": { | 
|   591           var subscriptionUrl = findParentData(element, "access", false); |   584           let subscriptionUrl = findParentData(element, "access", false); | 
|   592           if (element.getAttribute("aria-checked") == "true") |   585           if (element.getAttribute("aria-checked") == "true") | 
|   593           { |   586           { | 
|   594             ext.backgroundPage.sendMessage({ |   587             ext.backgroundPage.sendMessage({ | 
|   595               type: "subscriptions.remove", |   588               type: "subscriptions.remove", | 
|   596               url: subscriptionUrl |   589               url: subscriptionUrl | 
|   597             }); |   590             }); | 
|   598           } |   591           } | 
|   599           else |   592           else | 
|   600             addEnableSubscription(subscriptionUrl); |   593             addEnableSubscription(subscriptionUrl); | 
|   601           break; |   594           break; | 
 |   595         } | 
|   602         case "toggle-disable-subscription": |   596         case "toggle-disable-subscription": | 
|   603           ext.backgroundPage.sendMessage( |   597           ext.backgroundPage.sendMessage({ | 
|   604           { |  | 
|   605             type: "subscriptions.toggle", |   598             type: "subscriptions.toggle", | 
|   606             keepInstalled: true, |   599             keepInstalled: true, | 
|   607             url: findParentData(element, "access", false) |   600             url: findParentData(element, "access", false) | 
|   608           }); |   601           }); | 
|   609           break; |   602           break; | 
|   610         case "add-language-subscription": |   603         case "add-language-subscription": | 
|   611           addEnableSubscription(findParentData(element, "access", false)); |   604           addEnableSubscription(findParentData(element, "access", false)); | 
|   612           break; |   605           break; | 
|   613         case "remove-filter": |   606         case "remove-filter": | 
|   614           ext.backgroundPage.sendMessage( |   607           ext.backgroundPage.sendMessage({ | 
|   615           { |  | 
|   616             type: "filters.remove", |   608             type: "filters.remove", | 
|   617             text: findParentData(element, "access", false) |   609             text: findParentData(element, "access", false) | 
|   618           }); |   610           }); | 
|   619           break; |   611           break; | 
|   620       } |   612       } | 
|   621     } |   613     } | 
|   622   } |   614   } | 
|   623  |   615  | 
|   624   function getKey(e) |   616   function getKey(e) | 
|   625   { |   617   { | 
|   626     // e.keyCode has been deprecated so we attempt to use e.key |   618     // e.keyCode has been deprecated so we attempt to use e.key | 
|   627     if ("key" in e) |   619     if ("key" in e) | 
|   628       return e.key; |   620       return e.key; | 
|   629     return getKey.keys[e.keyCode]; |   621     return getKey.keys[e.keyCode]; | 
|   630   } |   622   } | 
|   631   getKey.keys = { |   623   getKey.keys = { | 
|   632     9: "Tab", |   624     9: "Tab", | 
|   633     13: "Enter", |   625     13: "Enter", | 
|   634     27: "Escape", |   626     27: "Escape", | 
|   635     37: "ArrowLeft", |   627     37: "ArrowLeft", | 
|   636     38: "ArrowUp", |   628     38: "ArrowUp", | 
|   637     39: "ArrowRight", |   629     39: "ArrowRight", | 
|   638     40: "ArrowDown" |   630     40: "ArrowDown" | 
|   639   }; |   631   }; | 
|   640  |   632  | 
|   641   function onKeyUp(e) |   633   function onKeyUp(e) | 
|   642   { |   634   { | 
|   643     var key = getKey(e); |   635     let key = getKey(e); | 
|   644     var element = document.activeElement; |   636     let element = document.activeElement; | 
|   645     if (!key || !element) |   637     if (!key || !element) | 
|   646       return; |   638       return; | 
|   647  |   639  | 
|   648     var container = findParentData(element, "action", true); |   640     let container = findParentData(element, "action", true); | 
|   649     if (!container || !container.hasAttribute("data-keys")) |   641     if (!container || !container.hasAttribute("data-keys")) | 
|   650       return; |   642       return; | 
|   651  |   643  | 
|   652     var keys = container.getAttribute("data-keys").split(" "); |   644     let keys = container.getAttribute("data-keys").split(" "); | 
|   653     if (keys.indexOf(key) < 0) |   645     if (keys.indexOf(key) < 0) | 
|   654       return; |   646       return; | 
|   655  |   647  | 
|   656     switch (container.getAttribute("data-action")) |   648     switch (container.getAttribute("data-action")) | 
|   657     { |   649     { | 
|   658       case "add-domain-exception": |   650       case "add-domain-exception": | 
|   659         addWhitelistedDomain(); |   651         addWhitelistedDomain(); | 
|   660         break; |   652         break; | 
|   661       case "open-doclink": |   653       case "open-doclink": | 
|   662         var doclink = findParentData(element, "doclink", false); |   654         let doclink = findParentData(element, "doclink", false); | 
|   663         openDocLink(doclink); |   655         openDocLink(doclink); | 
|   664         break; |   656         break; | 
|   665       case "switch-tab": |   657       case "switch-tab": | 
|   666         if (key == "Enter") |   658         if (key == "Enter") | 
|   667         { |   659         { | 
|   668           var tabId = findParentData(element, "tab", false); |   660           let tabId = findParentData(element, "tab", false); | 
|   669           switchTab(tabId); |   661           switchTab(tabId); | 
|   670         } |   662         } | 
|   671         else if (element.hasAttribute("aria-selected")) |   663         else if (element.hasAttribute("aria-selected")) | 
|   672         { |   664         { | 
|   673           if (key == "ArrowLeft" || key == "ArrowUp") |   665           if (key == "ArrowLeft" || key == "ArrowUp") | 
|   674           { |   666           { | 
|   675             element = element.previousElementSibling |   667             element = element.previousElementSibling || | 
|   676                 || container.lastElementChild; |   668                 container.lastElementChild; | 
|   677           } |   669           } | 
|   678           else if (key == "ArrowRight" || key == "ArrowDown") |   670           else if (key == "ArrowRight" || key == "ArrowDown") | 
|   679           { |   671           { | 
|   680             element = element.nextElementSibling |   672             element = element.nextElementSibling || | 
|   681                 || container.firstElementChild; |   673                 container.firstElementChild; | 
|   682           } |   674           } | 
|   683  |   675  | 
|   684           var tabId = findParentData(element, "tab", false); |   676           let tabId = findParentData(element, "tab", false); | 
|   685           switchTab(tabId); |   677           switchTab(tabId); | 
|   686         } |   678         } | 
|   687         break; |   679         break; | 
|   688     } |   680     } | 
|   689   } |   681   } | 
|   690  |   682  | 
|   691   function selectTabItem(tabId, container, focus) |   683   function selectTabItem(tabId, container, focus) | 
|   692   { |   684   { | 
|   693     // Show tab content |   685     // Show tab content | 
|   694     document.body.setAttribute("data-tab", tabId); |   686     document.body.setAttribute("data-tab", tabId); | 
|   695  |   687  | 
|   696     // Select tab |   688     // Select tab | 
|   697     var tabList = container.querySelector("[role='tablist']"); |   689     let tabList = container.querySelector("[role='tablist']"); | 
|   698     if (!tabList) |   690     if (!tabList) | 
|   699       return null; |   691       return null; | 
|   700  |   692  | 
|   701     var previousTab = tabList.querySelector("[aria-selected]"); |   693     let previousTab = tabList.querySelector("[aria-selected]"); | 
|   702     previousTab.removeAttribute("aria-selected"); |   694     previousTab.removeAttribute("aria-selected"); | 
|   703     previousTab.setAttribute("tabindex", -1); |   695     previousTab.setAttribute("tabindex", -1); | 
|   704  |   696  | 
|   705     var tab = tabList.querySelector("li[data-tab='" + tabId + "']"); |   697     let tab = tabList.querySelector("li[data-tab='" + tabId + "']"); | 
|   706     tab.setAttribute("aria-selected", true); |   698     tab.setAttribute("aria-selected", true); | 
|   707     tab.setAttribute("tabindex", 0); |   699     tab.setAttribute("tabindex", 0); | 
|   708  |   700  | 
|   709     var tabContentId = tab.getAttribute("aria-controls"); |   701     let tabContentId = tab.getAttribute("aria-controls"); | 
|   710     var tabContent = document.getElementById(tabContentId); |   702     let tabContent = document.getElementById(tabContentId); | 
|   711  |   703  | 
|   712     // Select sub tabs |   704     // Select sub tabs | 
|   713     if (tab.hasAttribute("data-subtab")) |   705     if (tab.hasAttribute("data-subtab")) | 
|   714       selectTabItem(tab.getAttribute("data-subtab"), tabContent, false); |   706       selectTabItem(tab.getAttribute("data-subtab"), tabContent, false); | 
|   715  |   707  | 
|   716     if (tab && focus) |   708     if (tab && focus) | 
|   717       tab.focus(); |   709       tab.focus(); | 
|   718  |   710  | 
|   719     return tabContent; |   711     return tabContent; | 
|   720   } |   712   } | 
|   721  |   713  | 
|   722   function onHashChange() |   714   function onHashChange() | 
|   723   { |   715   { | 
|   724     var hash = location.hash.substr(1); |   716     let hash = location.hash.substr(1); | 
|   725     if (!hash) |   717     if (!hash) | 
|   726       return; |   718       return; | 
|   727  |   719  | 
|   728     // Select tab and parent tabs |   720     // Select tab and parent tabs | 
|   729     var tabIds = hash.split("-"); |   721     let tabIds = hash.split("-"); | 
|   730     var tabContent = document.body; |   722     let tabContent = document.body; | 
|   731     for (var i = 0; i < tabIds.length; i++) |   723     for (let i = 0; i < tabIds.length; i++) | 
|   732     { |   724     { | 
|   733       var tabId = tabIds.slice(0, i + 1).join("-"); |   725       let tabId = tabIds.slice(0, i + 1).join("-"); | 
|   734       tabContent = selectTabItem(tabId, tabContent, true); |   726       tabContent = selectTabItem(tabId, tabContent, true); | 
|   735       if (!tabContent) |   727       if (!tabContent) | 
|   736         break; |   728         break; | 
|   737     } |   729     } | 
|   738   } |   730   } | 
|   739  |   731  | 
|   740   function onDOMLoaded() |   732   function onDOMLoaded() | 
|   741   { |   733   { | 
|   742     populateLists(); |   734     populateLists(); | 
|   743     function onFindLanguageKeyUp() |   735     function onFindLanguageKeyUp() | 
|   744     { |   736     { | 
|   745       var searchStyle = E("search-style"); |   737       let searchStyle = E("search-style"); | 
|   746       if (!this.value) |   738       if (!this.value) | 
|   747         searchStyle.innerHTML = ""; |   739         searchStyle.innerHTML = ""; | 
|   748       else |   740       else | 
|   749         searchStyle.innerHTML = "#all-lang-table li:not([data-search*=\"" + this
      .value.toLowerCase() + "\"]) { display: none; }"; |   741       { | 
 |   742         searchStyle.innerHTML = "#all-lang-table li:not([data-search*=\"" + | 
 |   743           this.value.toLowerCase() + "\"]) { display: none; }"; | 
 |   744       } | 
|   750     } |   745     } | 
|   751  |   746  | 
|   752     // Initialize navigation sidebar |   747     // Initialize navigation sidebar | 
|   753     ext.backgroundPage.sendMessage( |   748     ext.backgroundPage.sendMessage({ | 
|   754     { |  | 
|   755       type: "app.get", |   749       type: "app.get", | 
|   756       what: "addonVersion" |   750       what: "addonVersion" | 
|   757     }, |   751     }, | 
|   758     function(addonVersion) |   752     (addonVersion) => | 
|   759     { |   753     { | 
|   760       E("abp-version").textContent = addonVersion; |   754       E("abp-version").textContent = addonVersion; | 
|   761     }); |   755     }); | 
|   762     getDocLink("releases", function(link) |   756     getDocLink("releases", (link) => | 
|   763     { |   757     { | 
|   764       E("link-version").setAttribute("href", link); |   758       E("link-version").setAttribute("href", link); | 
|   765     }); |   759     }); | 
|   766  |   760  | 
|   767     updateShareLink(); |   761     updateShareLink(); | 
|   768     updateTooltips(); |   762     updateTooltips(); | 
|   769  |   763  | 
|   770     // Initialize interactive UI elements |   764     // Initialize interactive UI elements | 
|   771     document.body.addEventListener("click", onClick, false); |   765     document.body.addEventListener("click", onClick, false); | 
|   772     document.body.addEventListener("keyup", onKeyUp, false); |   766     document.body.addEventListener("keyup", onKeyUp, false); | 
|   773     var placeholderValue  = getMessage("options_dialog_language_find"); |   767     let placeholderValue = getMessage("options_dialog_language_find"); | 
|   774     E("find-language").setAttribute("placeholder", placeholderValue); |   768     E("find-language").setAttribute("placeholder", placeholderValue); | 
|   775     E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false); |   769     E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false); | 
|   776     E("whitelisting-textbox").addEventListener("keypress", function(e) |   770     E("whitelisting-textbox").addEventListener("keypress", (e) => | 
|   777     { |   771     { | 
|   778       if (getKey(e) == "Enter") |   772       if (getKey(e) == "Enter") | 
|   779         addWhitelistedDomain(); |   773         addWhitelistedDomain(); | 
|   780     }, false); |   774     }, false); | 
|   781  |   775  | 
|   782     // Advanced tab |   776     // Advanced tab | 
|   783     var tweaks = document.querySelectorAll("#tweaks li[data-pref]"); |   777     let tweaks = document.querySelectorAll("#tweaks li[data-pref]"); | 
|   784     tweaks = Array.prototype.map.call(tweaks, function(checkbox) |   778     tweaks = Array.prototype.map.call(tweaks, (checkbox) => | 
|   785     { |   779     { | 
|   786       return checkbox.getAttribute("data-pref"); |   780       return checkbox.getAttribute("data-pref"); | 
|   787     }); |   781     }); | 
|   788     tweaks.forEach(function(key) |   782     for (let key of tweaks) | 
|   789     { |   783     { | 
|   790       getPref(key, function(value) |   784       getPref(key, (value) => | 
|   791       { |   785       { | 
|   792         onPrefMessage(key, value, true); |   786         onPrefMessage(key, value, true); | 
|   793       }); |   787       }); | 
|   794     }); |   788     } | 
|   795     ext.backgroundPage.sendMessage( |   789     ext.backgroundPage.sendMessage({ | 
|   796     { |  | 
|   797       type: "app.get", |   790       type: "app.get", | 
|   798       what: "features" |   791       what: "features" | 
|   799     }, |   792     }, | 
|   800     function(features) |   793     (features) => | 
|   801     { |   794     { | 
|   802       hidePref("show_devtools_panel", !features.devToolsPanel); |   795       hidePref("show_devtools_panel", !features.devToolsPanel); | 
|   803     }); |   796     }); | 
|   804  |   797  | 
|   805     var filterTextbox = document.querySelector("#custom-filters-add input"); |   798     let filterTextbox = document.querySelector("#custom-filters-add input"); | 
|   806     placeholderValue = getMessage("options_customFilters_textbox_placeholder"); |   799     placeholderValue = getMessage("options_customFilters_textbox_placeholder"); | 
|   807     filterTextbox.setAttribute("placeholder", placeholderValue); |   800     filterTextbox.setAttribute("placeholder", placeholderValue); | 
|   808     function addCustomFilters() |   801     function addCustomFilters() | 
|   809     { |   802     { | 
|   810       var filterText = filterTextbox.value; |   803       let filterText = filterTextbox.value; | 
|   811       sendMessageHandleErrors( |   804       sendMessageHandleErrors({ | 
|   812       { |  | 
|   813         type: "filters.add", |   805         type: "filters.add", | 
|   814         text: filterText |   806         text: filterText | 
|   815       }, |   807       }, | 
|   816       function() |   808       () => | 
|   817       { |   809       { | 
|   818         filterTextbox.value = ""; |   810         filterTextbox.value = ""; | 
|   819       }); |   811       }); | 
|   820     } |   812     } | 
|   821     E("custom-filters-add").addEventListener("submit", function(e) |   813     E("custom-filters-add").addEventListener("submit", (e) => | 
|   822     { |   814     { | 
|   823       e.preventDefault(); |   815       e.preventDefault(); | 
|   824       addCustomFilters(); |   816       addCustomFilters(); | 
|   825     }, false); |   817     }, false); | 
|   826     var customFilterEditButtons = document.querySelectorAll("#custom-filters-edi
      t-wrapper button"); |  | 
|   827  |   818  | 
|   828     E("dialog").addEventListener("keydown", function(e) |   819     E("dialog").addEventListener("keydown", function(e) | 
|   829     { |   820     { | 
|   830       switch (getKey(e)) |   821       switch (getKey(e)) | 
|   831       { |   822       { | 
|   832         case "Escape": |   823         case "Escape": | 
|   833           closeDialog(); |   824           closeDialog(); | 
|   834           break; |   825           break; | 
|   835         case "Tab": |   826         case "Tab": | 
|   836           if (e.shiftKey) |   827           if (e.shiftKey) | 
|   837           { |   828           { | 
|   838             if (e.target.classList.contains("focus-first")) |   829             if (e.target.classList.contains("focus-first")) | 
|   839             { |   830             { | 
|   840               e.preventDefault(); |   831               e.preventDefault(); | 
|   841               this.querySelector(".focus-last").focus(); |   832               this.querySelector(".focus-last").focus(); | 
|   842             } |   833             } | 
|   843           } |   834           } | 
|   844           else if (e.target.classList.contains("focus-last")) |   835           else if (e.target.classList.contains("focus-last")) | 
|   845           { |   836           { | 
|   846             e.preventDefault(); |   837             e.preventDefault(); | 
|   847             this.querySelector(".focus-first").focus(); |   838             this.querySelector(".focus-first").focus(); | 
|   848           } |   839           } | 
|   849           break; |   840           break; | 
|   850       } |   841       } | 
|   851     }, false); |   842     }, false); | 
|   852  |   843  | 
|   853     onHashChange(); |   844     onHashChange(); | 
|   854   } |   845   } | 
|   855  |   846  | 
|   856   var focusedBeforeDialog = null; |   847   let focusedBeforeDialog = null; | 
|   857   function openDialog(name) |   848   function openDialog(name) | 
|   858   { |   849   { | 
|   859     var dialog = E("dialog"); |   850     let dialog = E("dialog"); | 
|   860     dialog.setAttribute("aria-hidden", false); |   851     dialog.setAttribute("aria-hidden", false); | 
|   861     dialog.setAttribute("aria-labelledby", "dialog-title-" + name); |   852     dialog.setAttribute("aria-labelledby", "dialog-title-" + name); | 
|   862     document.body.setAttribute("data-dialog", name); |   853     document.body.setAttribute("data-dialog", name); | 
|   863  |   854  | 
|   864     var defaultFocus = document.querySelector("#dialog-content-" + name |   855     let defaultFocus = document.querySelector( | 
|   865       + " .default-focus"); |   856       "#dialog-content-" + name + " .default-focus" | 
 |   857     ); | 
|   866     if (!defaultFocus) |   858     if (!defaultFocus) | 
|   867       defaultFocus = dialog.querySelector(".focus-first"); |   859       defaultFocus = dialog.querySelector(".focus-first"); | 
|   868     focusedBeforeDialog = document.activeElement; |   860     focusedBeforeDialog = document.activeElement; | 
|   869     defaultFocus.focus(); |   861     defaultFocus.focus(); | 
|   870   } |   862   } | 
|   871  |   863  | 
|   872   function closeDialog() |   864   function closeDialog() | 
|   873   { |   865   { | 
|   874     var dialog = E("dialog"); |   866     let dialog = E("dialog"); | 
|   875     dialog.setAttribute("aria-hidden", true); |   867     dialog.setAttribute("aria-hidden", true); | 
|   876     dialog.removeAttribute("aria-labelledby"); |   868     dialog.removeAttribute("aria-labelledby"); | 
|   877     document.body.removeAttribute("data-dialog"); |   869     document.body.removeAttribute("data-dialog"); | 
|   878     focusedBeforeDialog.focus(); |   870     focusedBeforeDialog.focus(); | 
|   879   } |   871   } | 
|   880  |   872  | 
|   881   function populateLists() |   873   function populateLists() | 
|   882   { |   874   { | 
|   883     subscriptionsMap = Object.create(null); |   875     subscriptionsMap = Object.create(null); | 
|   884     filtersMap = Object.create(null); |   876     filtersMap = Object.create(null); | 
|   885  |   877  | 
|   886     // Empty collections and lists |   878     // Empty collections and lists | 
|   887     for (var property in collections) |   879     for (let property in collections) | 
|   888       collections[property].clearAll(); |   880       collections[property].clearAll(); | 
|   889  |   881  | 
|   890     ext.backgroundPage.sendMessage( |   882     ext.backgroundPage.sendMessage({ | 
|   891     { |  | 
|   892       type: "subscriptions.get", |   883       type: "subscriptions.get", | 
|   893       special: true |   884       special: true | 
|   894     }, |   885     }, | 
|   895     function(subscriptions) |   886     (subscriptions) => | 
|   896     { |   887     { | 
|   897       // Load filters |   888       // Load filters | 
|   898       for (var i = 0; i < subscriptions.length; i++) |   889       for (let subscription of subscriptions) | 
|   899       { |   890       { | 
|   900         ext.backgroundPage.sendMessage( |   891         ext.backgroundPage.sendMessage({ | 
 |   892           type: "filters.get", | 
 |   893           subscriptionUrl: subscription.url | 
 |   894         }, | 
 |   895         (filters) => | 
|   901         { |   896         { | 
|   902           type: "filters.get", |   897           for (let filter of filters) | 
|   903           subscriptionUrl: subscriptions[i].url |   898             updateFilter(filter); | 
|   904         }, |  | 
|   905         function(filters) |  | 
|   906         { |  | 
|   907           for (var i = 0; i < filters.length; i++) |  | 
|   908             updateFilter(filters[i]); |  | 
|   909         }); |   899         }); | 
|   910       } |   900       } | 
|   911     }); |   901     }); | 
|   912     loadRecommendations(); |   902     loadRecommendations(); | 
|   913     ext.backgroundPage.sendMessage( |   903     ext.backgroundPage.sendMessage({ | 
|   914     { |  | 
|   915       type: "prefs.get", |   904       type: "prefs.get", | 
|   916       key: "subscriptions_exceptionsurl" |   905       key: "subscriptions_exceptionsurl" | 
|   917     }, |   906     }, | 
|   918     function(url) |   907     (url) => | 
|   919     { |   908     { | 
|   920       acceptableAdsUrl = url; |   909       acceptableAdsUrl = url; | 
|   921       addSubscription({ |   910       addSubscription({ | 
|   922         url: acceptableAdsUrl, |   911         url: acceptableAdsUrl, | 
|   923         disabled: true |   912         disabled: true | 
|   924       }); |   913       }); | 
|   925  |   914  | 
|   926       // Load user subscriptions |   915       // Load user subscriptions | 
|   927       ext.backgroundPage.sendMessage( |   916       ext.backgroundPage.sendMessage({ | 
|   928       { |  | 
|   929         type: "subscriptions.get", |   917         type: "subscriptions.get", | 
|   930         downloadable: true |   918         downloadable: true | 
|   931       }, |   919       }, | 
|   932       function(subscriptions) |   920       (subscriptions) => | 
|   933       { |   921       { | 
|   934         for (var i = 0; i < subscriptions.length; i++) |   922         for (let subscription of subscriptions) | 
|   935           onSubscriptionMessage("added", subscriptions[i]); |   923           onSubscriptionMessage("added", subscription); | 
|   936       }); |   924       }); | 
|   937     }); |   925     }); | 
|   938   } |   926   } | 
|   939  |   927  | 
|   940   function addWhitelistedDomain() |   928   function addWhitelistedDomain() | 
|   941   { |   929   { | 
|   942     var domain = E("whitelisting-textbox"); |   930     let domain = E("whitelisting-textbox"); | 
|   943     if (domain.value) |   931     if (domain.value) | 
|   944     { |   932     { | 
|   945       sendMessageHandleErrors( |   933       sendMessageHandleErrors({ | 
|   946       { |  | 
|   947         type: "filters.add", |   934         type: "filters.add", | 
|   948         text: "@@||" + domain.value.toLowerCase() + "^$document" |   935         text: "@@||" + domain.value.toLowerCase() + "^$document" | 
|   949       }); |   936       }); | 
|   950     } |   937     } | 
|   951  |   938  | 
|   952     domain.value = ""; |   939     domain.value = ""; | 
|   953     document.querySelector("#whitelisting .controls").classList.remove("mode-edi
      t"); |   940     document.querySelector("#whitelisting .controls"). | 
 |   941       classList.remove("mode-edit"); | 
|   954   } |   942   } | 
|   955  |   943  | 
|   956   function editCustomFilters() |   944   function editCustomFilters() | 
|   957   { |   945   { | 
|   958     var customFilterItems = collections.customFilters.items; |   946     let filterTexts = []; | 
|   959     var filterTexts = []; |   947     for (let customFilterItem of collections.customFilters.items) | 
|   960     for (var i = 0; i < customFilterItems.length; i++) |   948       filterTexts.push(customFilterItem.text); | 
|   961       filterTexts.push(customFilterItems[i].text); |  | 
|   962     E("custom-filters-raw").value = filterTexts.join("\n"); |   949     E("custom-filters-raw").value = filterTexts.join("\n"); | 
|   963   } |   950   } | 
|   964  |   951  | 
|   965   function addEnableSubscription(url, title, homepage) |   952   function addEnableSubscription(url, title, homepage) | 
|   966   { |   953   { | 
|   967     var messageType = null; |   954     let messageType = null; | 
|   968     var knownSubscription = subscriptionsMap[url]; |   955     let knownSubscription = subscriptionsMap[url]; | 
|   969     if (knownSubscription && knownSubscription.disabled == true) |   956     if (knownSubscription && knownSubscription.disabled == true) | 
|   970       messageType = "subscriptions.toggle"; |   957       messageType = "subscriptions.toggle"; | 
|   971     else |   958     else | 
|   972       messageType = "subscriptions.add"; |   959       messageType = "subscriptions.add"; | 
|   973  |   960  | 
|   974     var message = { |   961     let message = { | 
|   975       type: messageType, |   962       type: messageType, | 
|   976       url: url |   963       url | 
|   977     }; |   964     }; | 
|   978     if (title) |   965     if (title) | 
|   979       message.title = title; |   966       message.title = title; | 
|   980     if (homepage) |   967     if (homepage) | 
|   981       message.homepage = homepage; |   968       message.homepage = homepage; | 
|   982  |   969  | 
|   983     ext.backgroundPage.sendMessage(message); |   970     ext.backgroundPage.sendMessage(message); | 
|   984   } |   971   } | 
|   985  |   972  | 
|   986   function onFilterMessage(action, filter) |   973   function onFilterMessage(action, filter) | 
|   987   { |   974   { | 
|   988     switch (action) |   975     switch (action) | 
|   989     { |   976     { | 
|   990       case "added": |   977       case "added": | 
|   991         updateFilter(filter); |   978         updateFilter(filter); | 
|   992         updateShareLink(); |   979         updateShareLink(); | 
|   993         break; |   980         break; | 
|   994       case "loaded": |   981       case "loaded": | 
|   995         populateLists(); |   982         populateLists(); | 
|   996         break; |   983         break; | 
|   997       case "removed": |   984       case "removed": | 
|   998         var knownFilter = filtersMap[filter.text]; |   985         let knownFilter = filtersMap[filter.text]; | 
|   999         collections.whitelist.removeItem(knownFilter); |   986         collections.whitelist.removeItem(knownFilter); | 
|  1000         collections.customFilters.removeItem(knownFilter); |   987         collections.customFilters.removeItem(knownFilter); | 
|  1001         delete filtersMap[filter.text]; |   988         delete filtersMap[filter.text]; | 
|  1002         updateShareLink(); |   989         updateShareLink(); | 
|  1003         break; |   990         break; | 
|  1004     } |   991     } | 
|  1005   } |   992   } | 
|  1006  |   993  | 
|  1007   function onSubscriptionMessage(action, subscription) |   994   function onSubscriptionMessage(action, subscription) | 
|  1008   { |   995   { | 
|  1009     if (subscription.url in subscriptionsMap) |   996     if (subscription.url in subscriptionsMap) | 
|  1010     { |   997     { | 
|  1011       var knownSubscription = subscriptionsMap[subscription.url]; |   998       let knownSubscription = subscriptionsMap[subscription.url]; | 
|  1012       for (var property in subscription) |   999       for (let property in subscription) | 
|  1013       { |  1000       { | 
|  1014         if (property == "title" && knownSubscription.recommended) |  1001         if (property == "title" && knownSubscription.recommended) | 
|  1015           knownSubscription.originalTitle = subscription.title; |  1002           knownSubscription.originalTitle = subscription.title; | 
|  1016         else |  1003         else | 
|  1017           knownSubscription[property] = subscription[property]; |  1004           knownSubscription[property] = subscription[property]; | 
|  1018       } |  1005       } | 
|  1019       subscription = knownSubscription; |  1006       subscription = knownSubscription; | 
|  1020     } |  1007     } | 
|  1021     switch (action) |  1008     switch (action) | 
|  1022     { |  1009     { | 
| (...skipping 28 matching lines...) Expand all  Loading... | 
|  1051         } |  1038         } | 
|  1052         collections.filterLists.removeItem(subscription); |  1039         collections.filterLists.removeItem(subscription); | 
|  1053         break; |  1040         break; | 
|  1054     } |  1041     } | 
|  1055  |  1042  | 
|  1056     updateShareLink(); |  1043     updateShareLink(); | 
|  1057   } |  1044   } | 
|  1058  |  1045  | 
|  1059   function hidePref(key, value) |  1046   function hidePref(key, value) | 
|  1060   { |  1047   { | 
|  1061     var element = document.querySelector("[data-pref='" + key + "']"); |  1048     let element = document.querySelector("[data-pref='" + key + "']"); | 
|  1062     if (element) |  1049     if (element) | 
|  1063       element.setAttribute("aria-hidden", value); |  1050       element.setAttribute("aria-hidden", value); | 
|  1064   } |  1051   } | 
|  1065  |  1052  | 
|  1066   function getPref(key, callback) |  1053   function getPref(key, callback) | 
|  1067   { |  1054   { | 
|  1068     var checkPref = getPref.checks[key] || getPref.checkNone; |  1055     let checkPref = getPref.checks[key] || getPref.checkNone; | 
|  1069     checkPref(function(isActive) |  1056     checkPref((isActive) => | 
|  1070     { |  1057     { | 
|  1071       if (!isActive) |  1058       if (!isActive) | 
|  1072       { |  1059       { | 
|  1073         hidePref(key, !isActive); |  1060         hidePref(key, !isActive); | 
|  1074         return; |  1061         return; | 
|  1075       } |  1062       } | 
|  1076  |  1063  | 
|  1077       ext.backgroundPage.sendMessage( |  1064       ext.backgroundPage.sendMessage({ | 
|  1078       { |  | 
|  1079         type: "prefs.get", |  1065         type: "prefs.get", | 
|  1080         key: key |  1066         key | 
|  1081       }, callback); |  1067       }, callback); | 
|  1082     }); |  1068     }); | 
|  1083   } |  1069   } | 
|  1084  |  1070  | 
|  1085   getPref.checkNone = function(callback) |  1071   getPref.checkNone = function(callback) | 
|  1086   { |  1072   { | 
|  1087     callback(true); |  1073     callback(true); | 
|  1088   }; |  1074   }; | 
|  1089  |  1075  | 
|  1090   getPref.checks = |  1076   getPref.checks = | 
|  1091   { |  1077   { | 
|  1092     notifications_ignoredcategories: function(callback) |  1078     notifications_ignoredcategories(callback) | 
|  1093     { |  1079     { | 
|  1094       getPref("notifications_showui", callback); |  1080       getPref("notifications_showui", callback); | 
|  1095     } |  1081     } | 
|  1096   }; |  1082   }; | 
|  1097  |  1083  | 
|  1098   function onPrefMessage(key, value, initial) |  1084   function onPrefMessage(key, value, initial) | 
|  1099   { |  1085   { | 
|  1100     switch (key) |  1086     switch (key) | 
|  1101     { |  1087     { | 
|  1102       case "notifications_ignoredcategories": |  1088       case "notifications_ignoredcategories": | 
|  1103         value = value.indexOf("*") == -1; |  1089         value = value.indexOf("*") == -1; | 
|  1104         break; |  1090         break; | 
|  1105  |  1091  | 
|  1106       case "notifications_showui": |  1092       case "notifications_showui": | 
|  1107         hidePref("notifications_ignoredcategories", !value); |  1093         hidePref("notifications_ignoredcategories", !value); | 
|  1108         break; |  1094         break; | 
|  1109     } |  1095     } | 
|  1110  |  1096  | 
|  1111     var checkbox = document.querySelector("[data-pref='" + key + "'] button[role
      ='checkbox']"); |  1097     let checkbox = document.querySelector( | 
 |  1098       "[data-pref='" + key + "'] button[role='checkbox']" | 
 |  1099     ); | 
|  1112     if (checkbox) |  1100     if (checkbox) | 
|  1113       checkbox.setAttribute("aria-checked", value); |  1101       checkbox.setAttribute("aria-checked", value); | 
|  1114   } |  1102   } | 
|  1115  |  1103  | 
|  1116   function updateShareLink() |  1104   function updateShareLink() | 
|  1117   { |  1105   { | 
|  1118     var shareResources = [ |  1106     let shareResources = [ | 
|  1119       "https://facebook.com/plugins/like.php?", |  1107       "https://facebook.com/plugins/like.php?", | 
|  1120       "https://platform.twitter.com/widgets/", |  1108       "https://platform.twitter.com/widgets/", | 
|  1121       "https://apis.google.com/se/0/_/+1/fastbutton?" |  1109       "https://apis.google.com/se/0/_/+1/fastbutton?" | 
|  1122     ]; |  1110     ]; | 
|  1123     var isAnyBlocked = false; |  1111     let isAnyBlocked = false; | 
|  1124     var checksRemaining = shareResources.length; |  1112     let checksRemaining = shareResources.length; | 
|  1125  |  1113  | 
|  1126     function onResult(isBlocked) |  1114     function onResult(isBlocked) | 
|  1127     { |  1115     { | 
|  1128       isAnyBlocked |= isBlocked; |  1116       isAnyBlocked |= isBlocked; | 
|  1129       if (!--checksRemaining) |  1117       if (!--checksRemaining) | 
|  1130       { |  1118       { | 
|  1131         // Hide the share tab if a script on the share page would be blocked |  1119         // Hide the share tab if a script on the share page would be blocked | 
|  1132         E("tab-share").hidden = isAnyBlocked; |  1120         E("tab-share").hidden = isAnyBlocked; | 
|  1133       } |  1121       } | 
|  1134     } |  1122     } | 
|  1135  |  1123  | 
|  1136     for (var i = 0; i < shareResources.length; i++) |  1124     for (let sharedResource of shareResources) | 
|  1137       checkShareResource(shareResources[i], onResult); |  1125       checkShareResource(sharedResource, onResult); | 
|  1138   } |  1126   } | 
|  1139  |  1127  | 
|  1140   function getMessages(id) |  1128   function getMessages(id) | 
|  1141   { |  1129   { | 
|  1142     var messages = []; |  1130     let messages = []; | 
|  1143     for (var i = 1; true; i++) |  1131     for (let i = 1; true; i++) | 
|  1144     { |  1132     { | 
|  1145       var message = ext.i18n.getMessage(id + "_" + i); |  1133       let message = ext.i18n.getMessage(id + "_" + i); | 
|  1146       if (!message) |  1134       if (!message) | 
|  1147         break; |  1135         break; | 
|  1148  |  1136  | 
|  1149       messages.push(message); |  1137       messages.push(message); | 
|  1150     } |  1138     } | 
|  1151     return messages; |  1139     return messages; | 
|  1152   } |  1140   } | 
|  1153  |  1141  | 
|  1154   function updateTooltips() |  1142   function updateTooltips() | 
|  1155   { |  1143   { | 
|  1156     var anchors = document.querySelectorAll(":not(.tooltip) > [data-tooltip]"); |  1144     let anchors = document.querySelectorAll(":not(.tooltip) > [data-tooltip]"); | 
|  1157     for (var i = 0; i < anchors.length; i++) |  1145     for (let anchor of anchors) | 
|  1158     { |  1146     { | 
|  1159       var anchor = anchors[i]; |  1147       let id = anchor.getAttribute("data-tooltip"); | 
|  1160       var id = anchor.getAttribute("data-tooltip"); |  | 
|  1161  |  1148  | 
|  1162       var wrapper = document.createElement("div"); |  1149       let wrapper = document.createElement("div"); | 
|  1163       wrapper.className = "tooltip"; |  1150       wrapper.className = "tooltip"; | 
|  1164       anchor.parentNode.replaceChild(wrapper, anchor); |  1151       anchor.parentNode.replaceChild(wrapper, anchor); | 
|  1165       wrapper.appendChild(anchor); |  1152       wrapper.appendChild(anchor); | 
|  1166  |  1153  | 
|  1167       var topTexts = getMessages(id); |  1154       let topTexts = getMessages(id); | 
|  1168       var bottomTexts = getMessages(id + "_notes"); |  1155       let bottomTexts = getMessages(id + "_notes"); | 
|  1169  |  1156  | 
|  1170       // We have to use native tooltips to avoid issues when attaching a tooltip |  1157       // We have to use native tooltips to avoid issues when attaching a tooltip | 
|  1171       // to an element in a scrollable list or otherwise it might get cut off |  1158       // to an element in a scrollable list or otherwise it might get cut off | 
|  1172       if (anchor.hasAttribute("data-tooltip-native")) |  1159       if (anchor.hasAttribute("data-tooltip-native")) | 
|  1173       { |  1160       { | 
|  1174         var title = topTexts.concat(bottomTexts).join("\n\n"); |  1161         let title = topTexts.concat(bottomTexts).join("\n\n"); | 
|  1175         anchor.setAttribute("title", title); |  1162         anchor.setAttribute("title", title); | 
|  1176         continue; |  1163         continue; | 
|  1177       } |  1164       } | 
|  1178  |  1165  | 
|  1179       var tooltip = document.createElement("div"); |  1166       let tooltip = document.createElement("div"); | 
|  1180       tooltip.setAttribute("role", "tooltip"); |  1167       tooltip.setAttribute("role", "tooltip"); | 
|  1181  |  1168  | 
|  1182       var flip = anchor.getAttribute("data-tooltip-flip"); |  1169       let flip = anchor.getAttribute("data-tooltip-flip"); | 
|  1183       if (flip) |  1170       if (flip) | 
|  1184         tooltip.className = "flip-" + flip; |  1171         tooltip.className = "flip-" + flip; | 
|  1185  |  1172  | 
|  1186       var imageSource = anchor.getAttribute("data-tooltip-image"); |  1173       let imageSource = anchor.getAttribute("data-tooltip-image"); | 
|  1187       if (imageSource) |  1174       if (imageSource) | 
|  1188       { |  1175       { | 
|  1189         var image = document.createElement("img"); |  1176         let image = document.createElement("img"); | 
|  1190         image.src = imageSource; |  1177         image.src = imageSource; | 
|  1191         image.alt = ""; |  1178         image.alt = ""; | 
|  1192         tooltip.appendChild(image); |  1179         tooltip.appendChild(image); | 
|  1193       } |  1180       } | 
|  1194  |  1181  | 
|  1195       for (var j = 0; j < topTexts.length; j++) |  1182       for (let topText of topTexts) | 
|  1196       { |  1183       { | 
|  1197         var paragraph = document.createElement("p"); |  1184         let paragraph = document.createElement("p"); | 
|  1198         paragraph.innerHTML = topTexts[j]; |  1185         paragraph.innerHTML = topText; | 
|  1199         tooltip.appendChild(paragraph); |  1186         tooltip.appendChild(paragraph); | 
|  1200       } |  1187       } | 
|  1201       if (bottomTexts.length > 0) |  1188       if (bottomTexts.length > 0) | 
|  1202       { |  1189       { | 
|  1203         var notes = document.createElement("div"); |  1190         let notes = document.createElement("div"); | 
|  1204         notes.className = "notes"; |  1191         notes.className = "notes"; | 
|  1205         for (var j = 0; j < bottomTexts.length; j++) |  1192         for (let bottomText of bottomTexts) | 
|  1206         { |  1193         { | 
|  1207           var paragraph = document.createElement("p"); |  1194           let paragraph = document.createElement("p"); | 
|  1208           paragraph.innerHTML = bottomTexts[j]; |  1195           paragraph.innerHTML = bottomText; | 
|  1209           notes.appendChild(paragraph); |  1196           notes.appendChild(paragraph); | 
|  1210         } |  1197         } | 
|  1211         tooltip.appendChild(notes); |  1198         tooltip.appendChild(notes); | 
|  1212       } |  1199       } | 
|  1213  |  1200  | 
|  1214       wrapper.appendChild(tooltip); |  1201       wrapper.appendChild(tooltip); | 
|  1215     } |  1202     } | 
|  1216   } |  1203   } | 
|  1217  |  1204  | 
|  1218   ext.onMessage.addListener(function(message) |  1205   ext.onMessage.addListener((message) => | 
|  1219   { |  1206   { | 
|  1220     switch (message.type) |  1207     switch (message.type) | 
|  1221     { |  1208     { | 
|  1222       case "app.respond": |  1209       case "app.respond": | 
|  1223         switch (message.action) |  1210         switch (message.action) | 
|  1224         { |  1211         { | 
|  1225           case "addSubscription": |  1212           case "addSubscription": | 
|  1226             var subscription = message.args[0]; |  1213             let subscription = message.args[0]; | 
|  1227             var dialog = E("dialog-content-predefined"); |  1214             let dialog = E("dialog-content-predefined"); | 
|  1228             dialog.querySelector("h3").textContent = subscription.title || ""; |  1215             dialog.querySelector("h3").textContent = subscription.title || ""; | 
|  1229             dialog.querySelector(".url").textContent = subscription.url; |  1216             dialog.querySelector(".url").textContent = subscription.url; | 
|  1230             openDialog("predefined"); |  1217             openDialog("predefined"); | 
|  1231             break; |  1218             break; | 
|  1232           case "focusSection": |  1219           case "focusSection": | 
|  1233             document.body.setAttribute("data-tab", message.args[0]); |  1220             document.body.setAttribute("data-tab", message.args[0]); | 
|  1234             break; |  1221             break; | 
|  1235         } |  1222         } | 
|  1236         break; |  1223         break; | 
|  1237       case "filters.respond": |  1224       case "filters.respond": | 
|  1238         onFilterMessage(message.action, message.args[0]); |  1225         onFilterMessage(message.action, message.args[0]); | 
|  1239         break; |  1226         break; | 
|  1240       case "prefs.respond": |  1227       case "prefs.respond": | 
|  1241         onPrefMessage(message.action, message.args[0], false); |  1228         onPrefMessage(message.action, message.args[0], false); | 
|  1242         break; |  1229         break; | 
|  1243       case "subscriptions.respond": |  1230       case "subscriptions.respond": | 
|  1244         onSubscriptionMessage(message.action, message.args[0]); |  1231         onSubscriptionMessage(message.action, message.args[0]); | 
|  1245         break; |  1232         break; | 
|  1246     } |  1233     } | 
|  1247   }); |  1234   }); | 
|  1248  |  1235  | 
|  1249   ext.backgroundPage.sendMessage( |  1236   ext.backgroundPage.sendMessage({ | 
|  1250   { |  | 
|  1251     type: "app.listen", |  1237     type: "app.listen", | 
|  1252     filter: ["addSubscription", "focusSection"] |  1238     filter: ["addSubscription", "focusSection"] | 
|  1253   }); |  1239   }); | 
|  1254   ext.backgroundPage.sendMessage( |  1240   ext.backgroundPage.sendMessage({ | 
|  1255   { |  | 
|  1256     type: "filters.listen", |  1241     type: "filters.listen", | 
|  1257     filter: ["added", "loaded", "removed"] |  1242     filter: ["added", "loaded", "removed"] | 
|  1258   }); |  1243   }); | 
|  1259   ext.backgroundPage.sendMessage( |  1244   ext.backgroundPage.sendMessage({ | 
|  1260   { |  | 
|  1261     type: "prefs.listen", |  1245     type: "prefs.listen", | 
|  1262     filter: ["notifications_ignoredcategories", "notifications_showui", |  1246     filter: ["notifications_ignoredcategories", "notifications_showui", | 
|  1263         "show_devtools_panel", "shouldShowBlockElementMenu"] |  1247              "show_devtools_panel", "shouldShowBlockElementMenu"] | 
|  1264   }); |  1248   }); | 
|  1265   ext.backgroundPage.sendMessage( |  1249   ext.backgroundPage.sendMessage({ | 
|  1266   { |  | 
|  1267     type: "subscriptions.listen", |  1250     type: "subscriptions.listen", | 
|  1268     filter: ["added", "disabled", "homepage", "lastDownload", "removed", |  1251     filter: ["added", "disabled", "homepage", "lastDownload", "removed", | 
|  1269         "title", "downloadStatus", "downloading"] |  1252              "title", "downloadStatus", "downloading"] | 
|  1270   }); |  1253   }); | 
|  1271  |  1254  | 
|  1272   window.addEventListener("DOMContentLoaded", onDOMLoaded, false); |  1255   window.addEventListener("DOMContentLoaded", onDOMLoaded, false); | 
|  1273   window.addEventListener("hashchange", onHashChange, false); |  1256   window.addEventListener("hashchange", onHashChange, false); | 
|  1274 })(); |  1257 } | 
| OLD | NEW |