| 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     // Help tab |   819     // Help tab | 
|   829     getDocLink("faq", function(link) |   820     getDocLink("faq", (link) => | 
|   830     { |   821     { | 
|   831       E("link-faq").setAttribute("href", link); |   822       E("link-faq").setAttribute("href", link); | 
|   832     }); |   823     }); | 
|   833     getDocLink("social_twitter", function(link) |   824     getDocLink("social_twitter", (link) => | 
|   834     { |   825     { | 
|   835       E("link-twitter").setAttribute("href", link); |   826       E("link-twitter").setAttribute("href", link); | 
|   836     }); |   827     }); | 
|   837     getDocLink("social_facebook", function(link) |   828     getDocLink("social_facebook", (link) => | 
|   838     { |   829     { | 
|   839       E("link-facebook").setAttribute("href", link); |   830       E("link-facebook").setAttribute("href", link); | 
|   840     }); |   831     }); | 
|   841     getDocLink("social_gplus", function(link) |   832     getDocLink("social_gplus", (link) => | 
|   842     { |   833     { | 
|   843       E("link-gplus").setAttribute("href", link); |   834       E("link-gplus").setAttribute("href", link); | 
|   844     }); |   835     }); | 
|   845     getDocLink("social_renren", function(link) |   836     getDocLink("social_renren", (link) => | 
|   846     { |   837     { | 
|   847       E("link-renren").setAttribute("href", link); |   838       E("link-renren").setAttribute("href", link); | 
|   848     }); |   839     }); | 
|   849     getDocLink("social_weibo", function(link) |   840     getDocLink("social_weibo", (link) => | 
|   850     { |   841     { | 
|   851       E("link-weibo").setAttribute("href", link); |   842       E("link-weibo").setAttribute("href", link); | 
|   852     }); |   843     }); | 
|   853  |   844  | 
|   854     // Set forum link |   845     // Set forum link | 
|   855     ext.backgroundPage.sendMessage( |   846     ext.backgroundPage.sendMessage({ | 
|   856     { |  | 
|   857       type: "app.get", |   847       type: "app.get", | 
|   858       what: "platform" |   848       what: "platform" | 
|   859     }, |   849     }, | 
|   860     function(platform) |   850     (platform) => | 
|   861     { |   851     { | 
|   862       ext.backgroundPage.sendMessage( |   852       ext.backgroundPage.sendMessage({ | 
|   863       { |  | 
|   864         type: "app.get", |   853         type: "app.get", | 
|   865         what: "application" |   854         what: "application" | 
|   866       }, |   855       }, | 
|   867       function(application) |   856       (application) => | 
|   868       { |   857       { | 
|   869         if (platform == "chromium" && application != "opera") |   858         if (platform == "chromium" && application != "opera") | 
|   870           application = "chrome"; |   859           application = "chrome"; | 
|   871  |   860  | 
|   872         getDocLink(application + "_support", function(link) |   861         getDocLink(application + "_support", (link) => | 
|   873         { |   862         { | 
|   874           E("link-forum").setAttribute("href", link); |   863           E("link-forum").setAttribute("href", link); | 
|   875         }); |   864         }); | 
|   876       }); |   865       }); | 
|   877     }); |   866     }); | 
|   878  |   867  | 
|   879     E("dialog").addEventListener("keydown", function(e) |   868     E("dialog").addEventListener("keydown", function(e) | 
|   880     { |   869     { | 
|   881       switch (getKey(e)) |   870       switch (getKey(e)) | 
|   882       { |   871       { | 
| (...skipping 14 matching lines...) Expand all  Loading... | 
|   897             e.preventDefault(); |   886             e.preventDefault(); | 
|   898             this.querySelector(".focus-first").focus(); |   887             this.querySelector(".focus-first").focus(); | 
|   899           } |   888           } | 
|   900           break; |   889           break; | 
|   901       } |   890       } | 
|   902     }, false); |   891     }, false); | 
|   903  |   892  | 
|   904     onHashChange(); |   893     onHashChange(); | 
|   905   } |   894   } | 
|   906  |   895  | 
|   907   var focusedBeforeDialog = null; |   896   let focusedBeforeDialog = null; | 
|   908   function openDialog(name) |   897   function openDialog(name) | 
|   909   { |   898   { | 
|   910     var dialog = E("dialog"); |   899     let dialog = E("dialog"); | 
|   911     dialog.setAttribute("aria-hidden", false); |   900     dialog.setAttribute("aria-hidden", false); | 
|   912     dialog.setAttribute("aria-labelledby", "dialog-title-" + name); |   901     dialog.setAttribute("aria-labelledby", "dialog-title-" + name); | 
|   913     document.body.setAttribute("data-dialog", name); |   902     document.body.setAttribute("data-dialog", name); | 
|   914  |   903  | 
|   915     var defaultFocus = document.querySelector("#dialog-content-" + name |   904     let defaultFocus = document.querySelector( | 
|   916       + " .default-focus"); |   905       "#dialog-content-" + name + " .default-focus" | 
 |   906     ); | 
|   917     if (!defaultFocus) |   907     if (!defaultFocus) | 
|   918       defaultFocus = dialog.querySelector(".focus-first"); |   908       defaultFocus = dialog.querySelector(".focus-first"); | 
|   919     focusedBeforeDialog = document.activeElement; |   909     focusedBeforeDialog = document.activeElement; | 
|   920     defaultFocus.focus(); |   910     defaultFocus.focus(); | 
|   921   } |   911   } | 
|   922  |   912  | 
|   923   function closeDialog() |   913   function closeDialog() | 
|   924   { |   914   { | 
|   925     var dialog = E("dialog"); |   915     let dialog = E("dialog"); | 
|   926     dialog.setAttribute("aria-hidden", true); |   916     dialog.setAttribute("aria-hidden", true); | 
|   927     dialog.removeAttribute("aria-labelledby"); |   917     dialog.removeAttribute("aria-labelledby"); | 
|   928     document.body.removeAttribute("data-dialog"); |   918     document.body.removeAttribute("data-dialog"); | 
|   929     focusedBeforeDialog.focus(); |   919     focusedBeforeDialog.focus(); | 
|   930   } |   920   } | 
|   931  |   921  | 
|   932   function populateLists() |   922   function populateLists() | 
|   933   { |   923   { | 
|   934     subscriptionsMap = Object.create(null); |   924     subscriptionsMap = Object.create(null); | 
|   935     filtersMap = Object.create(null); |   925     filtersMap = Object.create(null); | 
|   936  |   926  | 
|   937     // Empty collections and lists |   927     // Empty collections and lists | 
|   938     for (var property in collections) |   928     for (let property in collections) | 
|   939       collections[property].clearAll(); |   929       collections[property].clearAll(); | 
|   940  |   930  | 
|   941     ext.backgroundPage.sendMessage( |   931     ext.backgroundPage.sendMessage({ | 
|   942     { |  | 
|   943       type: "subscriptions.get", |   932       type: "subscriptions.get", | 
|   944       special: true |   933       special: true | 
|   945     }, |   934     }, | 
|   946     function(subscriptions) |   935     (subscriptions) => | 
|   947     { |   936     { | 
|   948       // Load filters |   937       // Load filters | 
|   949       for (var i = 0; i < subscriptions.length; i++) |   938       for (let subscription of subscriptions) | 
|   950       { |   939       { | 
|   951         ext.backgroundPage.sendMessage( |   940         ext.backgroundPage.sendMessage({ | 
 |   941           type: "filters.get", | 
 |   942           subscriptionUrl: subscription.url | 
 |   943         }, | 
 |   944         (filters) => | 
|   952         { |   945         { | 
|   953           type: "filters.get", |   946           for (let filter of filters) | 
|   954           subscriptionUrl: subscriptions[i].url |   947             updateFilter(filter); | 
|   955         }, |  | 
|   956         function(filters) |  | 
|   957         { |  | 
|   958           for (var i = 0; i < filters.length; i++) |  | 
|   959             updateFilter(filters[i]); |  | 
|   960         }); |   948         }); | 
|   961       } |   949       } | 
|   962     }); |   950     }); | 
|   963     loadRecommendations(); |   951     loadRecommendations(); | 
|   964     ext.backgroundPage.sendMessage( |   952     ext.backgroundPage.sendMessage({ | 
|   965     { |  | 
|   966       type: "prefs.get", |   953       type: "prefs.get", | 
|   967       key: "subscriptions_exceptionsurl" |   954       key: "subscriptions_exceptionsurl" | 
|   968     }, |   955     }, | 
|   969     function(url) |   956     (url) => | 
|   970     { |   957     { | 
|   971       acceptableAdsUrl = url; |   958       acceptableAdsUrl = url; | 
|   972       addSubscription({ |   959       addSubscription({ | 
|   973         url: acceptableAdsUrl, |   960         url: acceptableAdsUrl, | 
|   974         disabled: true |   961         disabled: true | 
|   975       }); |   962       }); | 
|   976  |   963  | 
|   977       // Load user subscriptions |   964       // Load user subscriptions | 
|   978       ext.backgroundPage.sendMessage( |   965       ext.backgroundPage.sendMessage({ | 
|   979       { |  | 
|   980         type: "subscriptions.get", |   966         type: "subscriptions.get", | 
|   981         downloadable: true |   967         downloadable: true | 
|   982       }, |   968       }, | 
|   983       function(subscriptions) |   969       (subscriptions) => | 
|   984       { |   970       { | 
|   985         for (var i = 0; i < subscriptions.length; i++) |   971         for (let subscription of subscriptions) | 
|   986           onSubscriptionMessage("added", subscriptions[i]); |   972           onSubscriptionMessage("added", subscription); | 
|   987       }); |   973       }); | 
|   988     }); |   974     }); | 
|   989   } |   975   } | 
|   990  |   976  | 
|   991   function addWhitelistedDomain() |   977   function addWhitelistedDomain() | 
|   992   { |   978   { | 
|   993     var domain = E("whitelisting-textbox"); |   979     let domain = E("whitelisting-textbox"); | 
|   994     if (domain.value) |   980     if (domain.value) | 
|   995     { |   981     { | 
|   996       sendMessageHandleErrors( |   982       sendMessageHandleErrors({ | 
|   997       { |  | 
|   998         type: "filters.add", |   983         type: "filters.add", | 
|   999         text: "@@||" + domain.value.toLowerCase() + "^$document" |   984         text: "@@||" + domain.value.toLowerCase() + "^$document" | 
|  1000       }); |   985       }); | 
|  1001     } |   986     } | 
|  1002  |   987  | 
|  1003     domain.value = ""; |   988     domain.value = ""; | 
|  1004     document.querySelector("#whitelisting .controls").classList.remove("mode-edi
      t"); |   989     document.querySelector("#whitelisting .controls") | 
 |   990       .classList.remove("mode-edit"); | 
|  1005   } |   991   } | 
|  1006  |   992  | 
|  1007   function editCustomFilters() |   993   function editCustomFilters() | 
|  1008   { |   994   { | 
|  1009     var customFilterItems = collections.customFilters.items; |   995     let filterTexts = []; | 
|  1010     var filterTexts = []; |   996     for (let customFilterItem of collections.customFilters.items) | 
|  1011     for (var i = 0; i < customFilterItems.length; i++) |   997       filterTexts.push(customFilterItem.text); | 
|  1012       filterTexts.push(customFilterItems[i].text); |  | 
|  1013     E("custom-filters-raw").value = filterTexts.join("\n"); |   998     E("custom-filters-raw").value = filterTexts.join("\n"); | 
|  1014   } |   999   } | 
|  1015  |  1000  | 
|  1016   function addEnableSubscription(url, title, homepage) |  1001   function addEnableSubscription(url, title, homepage) | 
|  1017   { |  1002   { | 
|  1018     var messageType = null; |  1003     let messageType = null; | 
|  1019     var knownSubscription = subscriptionsMap[url]; |  1004     let knownSubscription = subscriptionsMap[url]; | 
|  1020     if (knownSubscription && knownSubscription.disabled == true) |  1005     if (knownSubscription && knownSubscription.disabled == true) | 
|  1021       messageType = "subscriptions.toggle"; |  1006       messageType = "subscriptions.toggle"; | 
|  1022     else |  1007     else | 
|  1023       messageType = "subscriptions.add"; |  1008       messageType = "subscriptions.add"; | 
|  1024  |  1009  | 
|  1025     var message = { |  1010     let message = { | 
|  1026       type: messageType, |  1011       type: messageType, | 
|  1027       url: url |  1012       url | 
|  1028     }; |  1013     }; | 
|  1029     if (title) |  1014     if (title) | 
|  1030       message.title = title; |  1015       message.title = title; | 
|  1031     if (homepage) |  1016     if (homepage) | 
|  1032       message.homepage = homepage; |  1017       message.homepage = homepage; | 
|  1033  |  1018  | 
|  1034     ext.backgroundPage.sendMessage(message); |  1019     ext.backgroundPage.sendMessage(message); | 
|  1035   } |  1020   } | 
|  1036  |  1021  | 
|  1037   function onFilterMessage(action, filter) |  1022   function onFilterMessage(action, filter) | 
|  1038   { |  1023   { | 
|  1039     switch (action) |  1024     switch (action) | 
|  1040     { |  1025     { | 
|  1041       case "added": |  1026       case "added": | 
|  1042         updateFilter(filter); |  1027         updateFilter(filter); | 
|  1043         updateShareLink(); |  1028         updateShareLink(); | 
|  1044         break; |  1029         break; | 
|  1045       case "loaded": |  1030       case "loaded": | 
|  1046         populateLists(); |  1031         populateLists(); | 
|  1047         break; |  1032         break; | 
|  1048       case "removed": |  1033       case "removed": | 
|  1049         var knownFilter = filtersMap[filter.text]; |  1034         let knownFilter = filtersMap[filter.text]; | 
|  1050         collections.whitelist.removeItem(knownFilter); |  1035         collections.whitelist.removeItem(knownFilter); | 
|  1051         collections.customFilters.removeItem(knownFilter); |  1036         collections.customFilters.removeItem(knownFilter); | 
|  1052         delete filtersMap[filter.text]; |  1037         delete filtersMap[filter.text]; | 
|  1053         updateShareLink(); |  1038         updateShareLink(); | 
|  1054         break; |  1039         break; | 
|  1055     } |  1040     } | 
|  1056   } |  1041   } | 
|  1057  |  1042  | 
|  1058   function onSubscriptionMessage(action, subscription) |  1043   function onSubscriptionMessage(action, subscription) | 
|  1059   { |  1044   { | 
|  1060     if (subscription.url in subscriptionsMap) |  1045     if (subscription.url in subscriptionsMap) | 
|  1061     { |  1046     { | 
|  1062       var knownSubscription = subscriptionsMap[subscription.url]; |  1047       let knownSubscription = subscriptionsMap[subscription.url]; | 
|  1063       for (var property in subscription) |  1048       for (let property in subscription) | 
|  1064       { |  1049       { | 
|  1065         if (property == "title" && knownSubscription.recommended) |  1050         if (property == "title" && knownSubscription.recommended) | 
|  1066           knownSubscription.originalTitle = subscription.title; |  1051           knownSubscription.originalTitle = subscription.title; | 
|  1067         else |  1052         else | 
|  1068           knownSubscription[property] = subscription[property]; |  1053           knownSubscription[property] = subscription[property]; | 
|  1069       } |  1054       } | 
|  1070       subscription = knownSubscription; |  1055       subscription = knownSubscription; | 
|  1071     } |  1056     } | 
|  1072     switch (action) |  1057     switch (action) | 
|  1073     { |  1058     { | 
| (...skipping 28 matching lines...) Expand all  Loading... | 
|  1102         } |  1087         } | 
|  1103         collections.filterLists.removeItem(subscription); |  1088         collections.filterLists.removeItem(subscription); | 
|  1104         break; |  1089         break; | 
|  1105     } |  1090     } | 
|  1106  |  1091  | 
|  1107     updateShareLink(); |  1092     updateShareLink(); | 
|  1108   } |  1093   } | 
|  1109  |  1094  | 
|  1110   function hidePref(key, value) |  1095   function hidePref(key, value) | 
|  1111   { |  1096   { | 
|  1112     var element = document.querySelector("[data-pref='" + key + "']"); |  1097     let element = document.querySelector("[data-pref='" + key + "']"); | 
|  1113     if (element) |  1098     if (element) | 
|  1114       element.setAttribute("aria-hidden", value); |  1099       element.setAttribute("aria-hidden", value); | 
|  1115   } |  1100   } | 
|  1116  |  1101  | 
|  1117   function getPref(key, callback) |  1102   function getPref(key, callback) | 
|  1118   { |  1103   { | 
|  1119     var checkPref = getPref.checks[key] || getPref.checkNone; |  1104     let checkPref = getPref.checks[key] || getPref.checkNone; | 
|  1120     checkPref(function(isActive) |  1105     checkPref((isActive) => | 
|  1121     { |  1106     { | 
|  1122       if (!isActive) |  1107       if (!isActive) | 
|  1123       { |  1108       { | 
|  1124         hidePref(key, !isActive); |  1109         hidePref(key, !isActive); | 
|  1125         return; |  1110         return; | 
|  1126       } |  1111       } | 
|  1127  |  1112  | 
|  1128       ext.backgroundPage.sendMessage( |  1113       ext.backgroundPage.sendMessage({ | 
|  1129       { |  | 
|  1130         type: "prefs.get", |  1114         type: "prefs.get", | 
|  1131         key: key |  1115         key | 
|  1132       }, callback); |  1116       }, callback); | 
|  1133     }); |  1117     }); | 
|  1134   } |  1118   } | 
|  1135  |  1119  | 
|  1136   getPref.checkNone = function(callback) |  1120   getPref.checkNone = function(callback) | 
|  1137   { |  1121   { | 
|  1138     callback(true); |  1122     callback(true); | 
|  1139   }; |  1123   }; | 
|  1140  |  1124  | 
|  1141   getPref.checks = |  1125   getPref.checks = | 
|  1142   { |  1126   { | 
|  1143     notifications_ignoredcategories: function(callback) |  1127     notifications_ignoredcategories(callback) | 
|  1144     { |  1128     { | 
|  1145       getPref("notifications_showui", callback); |  1129       getPref("notifications_showui", callback); | 
|  1146     } |  1130     } | 
|  1147   }; |  1131   }; | 
|  1148  |  1132  | 
|  1149   function onPrefMessage(key, value, initial) |  1133   function onPrefMessage(key, value, initial) | 
|  1150   { |  1134   { | 
|  1151     switch (key) |  1135     switch (key) | 
|  1152     { |  1136     { | 
|  1153       case "notifications_ignoredcategories": |  1137       case "notifications_ignoredcategories": | 
|  1154         value = value.indexOf("*") == -1; |  1138         value = value.indexOf("*") == -1; | 
|  1155         break; |  1139         break; | 
|  1156  |  1140  | 
|  1157       case "notifications_showui": |  1141       case "notifications_showui": | 
|  1158         hidePref("notifications_ignoredcategories", !value); |  1142         hidePref("notifications_ignoredcategories", !value); | 
|  1159         break; |  1143         break; | 
|  1160     } |  1144     } | 
|  1161  |  1145  | 
|  1162     var checkbox = document.querySelector("[data-pref='" + key + "'] button[role
      ='checkbox']"); |  1146     let checkbox = document.querySelector( | 
 |  1147       "[data-pref='" + key + "'] button[role='checkbox']" | 
 |  1148     ); | 
|  1163     if (checkbox) |  1149     if (checkbox) | 
|  1164       checkbox.setAttribute("aria-checked", value); |  1150       checkbox.setAttribute("aria-checked", value); | 
|  1165   } |  1151   } | 
|  1166  |  1152  | 
|  1167   function updateShareLink() |  1153   function updateShareLink() | 
|  1168   { |  1154   { | 
|  1169     var shareResources = [ |  1155     let shareResources = [ | 
|  1170       "https://facebook.com/plugins/like.php?", |  1156       "https://facebook.com/plugins/like.php?", | 
|  1171       "https://platform.twitter.com/widgets/", |  1157       "https://platform.twitter.com/widgets/", | 
|  1172       "https://apis.google.com/se/0/_/+1/fastbutton?" |  1158       "https://apis.google.com/se/0/_/+1/fastbutton?" | 
|  1173     ]; |  1159     ]; | 
|  1174     var isAnyBlocked = false; |  1160     let isAnyBlocked = false; | 
|  1175     var checksRemaining = shareResources.length; |  1161     let checksRemaining = shareResources.length; | 
|  1176  |  1162  | 
|  1177     function onResult(isBlocked) |  1163     function onResult(isBlocked) | 
|  1178     { |  1164     { | 
|  1179       isAnyBlocked |= isBlocked; |  1165       isAnyBlocked |= isBlocked; | 
|  1180       if (!--checksRemaining) |  1166       if (!--checksRemaining) | 
|  1181       { |  1167       { | 
|  1182         // Hide the share tab if a script on the share page would be blocked |  1168         // Hide the share tab if a script on the share page would be blocked | 
|  1183         E("tab-share").hidden = isAnyBlocked; |  1169         E("tab-share").hidden = isAnyBlocked; | 
|  1184       } |  1170       } | 
|  1185     } |  1171     } | 
|  1186  |  1172  | 
|  1187     for (var i = 0; i < shareResources.length; i++) |  1173     for (let sharedResource of shareResources) | 
|  1188       checkShareResource(shareResources[i], onResult); |  1174       checkShareResource(sharedResource, onResult); | 
|  1189   } |  1175   } | 
|  1190  |  1176  | 
|  1191   function getMessages(id) |  1177   function getMessages(id) | 
|  1192   { |  1178   { | 
|  1193     var messages = []; |  1179     let messages = []; | 
|  1194     for (var i = 1; true; i++) |  1180     for (let i = 1; true; i++) | 
|  1195     { |  1181     { | 
|  1196       var message = ext.i18n.getMessage(id + "_" + i); |  1182       let message = ext.i18n.getMessage(id + "_" + i); | 
|  1197       if (!message) |  1183       if (!message) | 
|  1198         break; |  1184         break; | 
|  1199  |  1185  | 
|  1200       messages.push(message); |  1186       messages.push(message); | 
|  1201     } |  1187     } | 
|  1202     return messages; |  1188     return messages; | 
|  1203   } |  1189   } | 
|  1204  |  1190  | 
|  1205   function updateTooltips() |  1191   function updateTooltips() | 
|  1206   { |  1192   { | 
|  1207     var anchors = document.querySelectorAll(":not(.tooltip) > [data-tooltip]"); |  1193     let anchors = document.querySelectorAll(":not(.tooltip) > [data-tooltip]"); | 
|  1208     for (var i = 0; i < anchors.length; i++) |  1194     for (let anchor of anchors) | 
|  1209     { |  1195     { | 
|  1210       var anchor = anchors[i]; |  1196       let id = anchor.getAttribute("data-tooltip"); | 
|  1211       var id = anchor.getAttribute("data-tooltip"); |  | 
|  1212  |  1197  | 
|  1213       var wrapper = document.createElement("div"); |  1198       let wrapper = document.createElement("div"); | 
|  1214       wrapper.className = "tooltip"; |  1199       wrapper.className = "tooltip"; | 
|  1215       anchor.parentNode.replaceChild(wrapper, anchor); |  1200       anchor.parentNode.replaceChild(wrapper, anchor); | 
|  1216       wrapper.appendChild(anchor); |  1201       wrapper.appendChild(anchor); | 
|  1217  |  1202  | 
|  1218       var topTexts = getMessages(id); |  1203       let topTexts = getMessages(id); | 
|  1219       var bottomTexts = getMessages(id + "_notes"); |  1204       let bottomTexts = getMessages(id + "_notes"); | 
|  1220  |  1205  | 
|  1221       // We have to use native tooltips to avoid issues when attaching a tooltip |  1206       // We have to use native tooltips to avoid issues when attaching a tooltip | 
|  1222       // to an element in a scrollable list or otherwise it might get cut off |  1207       // to an element in a scrollable list or otherwise it might get cut off | 
|  1223       if (anchor.hasAttribute("data-tooltip-native")) |  1208       if (anchor.hasAttribute("data-tooltip-native")) | 
|  1224       { |  1209       { | 
|  1225         var title = topTexts.concat(bottomTexts).join("\n\n"); |  1210         let title = topTexts.concat(bottomTexts).join("\n\n"); | 
|  1226         anchor.setAttribute("title", title); |  1211         anchor.setAttribute("title", title); | 
|  1227         continue; |  1212         continue; | 
|  1228       } |  1213       } | 
|  1229  |  1214  | 
|  1230       var tooltip = document.createElement("div"); |  1215       let tooltip = document.createElement("div"); | 
|  1231       tooltip.setAttribute("role", "tooltip"); |  1216       tooltip.setAttribute("role", "tooltip"); | 
|  1232  |  1217  | 
|  1233       var flip = anchor.getAttribute("data-tooltip-flip"); |  1218       let flip = anchor.getAttribute("data-tooltip-flip"); | 
|  1234       if (flip) |  1219       if (flip) | 
|  1235         tooltip.className = "flip-" + flip; |  1220         tooltip.className = "flip-" + flip; | 
|  1236  |  1221  | 
|  1237       var imageSource = anchor.getAttribute("data-tooltip-image"); |  1222       let imageSource = anchor.getAttribute("data-tooltip-image"); | 
|  1238       if (imageSource) |  1223       if (imageSource) | 
|  1239       { |  1224       { | 
|  1240         var image = document.createElement("img"); |  1225         let image = document.createElement("img"); | 
|  1241         image.src = imageSource; |  1226         image.src = imageSource; | 
|  1242         image.alt = ""; |  1227         image.alt = ""; | 
|  1243         tooltip.appendChild(image); |  1228         tooltip.appendChild(image); | 
|  1244       } |  1229       } | 
|  1245  |  1230  | 
|  1246       for (var j = 0; j < topTexts.length; j++) |  1231       for (let topText of topTexts) | 
|  1247       { |  1232       { | 
|  1248         var paragraph = document.createElement("p"); |  1233         let paragraph = document.createElement("p"); | 
|  1249         paragraph.innerHTML = topTexts[j]; |  1234         paragraph.innerHTML = topText; | 
|  1250         tooltip.appendChild(paragraph); |  1235         tooltip.appendChild(paragraph); | 
|  1251       } |  1236       } | 
|  1252       if (bottomTexts.length > 0) |  1237       if (bottomTexts.length > 0) | 
|  1253       { |  1238       { | 
|  1254         var notes = document.createElement("div"); |  1239         let notes = document.createElement("div"); | 
|  1255         notes.className = "notes"; |  1240         notes.className = "notes"; | 
|  1256         for (var j = 0; j < bottomTexts.length; j++) |  1241         for (let bottomText of bottomTexts) | 
|  1257         { |  1242         { | 
|  1258           var paragraph = document.createElement("p"); |  1243           let paragraph = document.createElement("p"); | 
|  1259           paragraph.innerHTML = bottomTexts[j]; |  1244           paragraph.innerHTML = bottomText; | 
|  1260           notes.appendChild(paragraph); |  1245           notes.appendChild(paragraph); | 
|  1261         } |  1246         } | 
|  1262         tooltip.appendChild(notes); |  1247         tooltip.appendChild(notes); | 
|  1263       } |  1248       } | 
|  1264  |  1249  | 
|  1265       wrapper.appendChild(tooltip); |  1250       wrapper.appendChild(tooltip); | 
|  1266     } |  1251     } | 
|  1267   } |  1252   } | 
|  1268  |  1253  | 
|  1269   ext.onMessage.addListener(function(message) |  1254   ext.onMessage.addListener((message) => | 
|  1270   { |  1255   { | 
|  1271     switch (message.type) |  1256     switch (message.type) | 
|  1272     { |  1257     { | 
|  1273       case "app.respond": |  1258       case "app.respond": | 
|  1274         switch (message.action) |  1259         switch (message.action) | 
|  1275         { |  1260         { | 
|  1276           case "addSubscription": |  1261           case "addSubscription": | 
|  1277             var subscription = message.args[0]; |  1262             let subscription = message.args[0]; | 
|  1278             var dialog = E("dialog-content-predefined"); |  1263             let dialog = E("dialog-content-predefined"); | 
|  1279             dialog.querySelector("h3").textContent = subscription.title || ""; |  1264             dialog.querySelector("h3").textContent = subscription.title || ""; | 
|  1280             dialog.querySelector(".url").textContent = subscription.url; |  1265             dialog.querySelector(".url").textContent = subscription.url; | 
|  1281             openDialog("predefined"); |  1266             openDialog("predefined"); | 
|  1282             break; |  1267             break; | 
|  1283           case "focusSection": |  1268           case "focusSection": | 
|  1284             document.body.setAttribute("data-tab", message.args[0]); |  1269             document.body.setAttribute("data-tab", message.args[0]); | 
|  1285             break; |  1270             break; | 
|  1286         } |  1271         } | 
|  1287         break; |  1272         break; | 
|  1288       case "filters.respond": |  1273       case "filters.respond": | 
|  1289         onFilterMessage(message.action, message.args[0]); |  1274         onFilterMessage(message.action, message.args[0]); | 
|  1290         break; |  1275         break; | 
|  1291       case "prefs.respond": |  1276       case "prefs.respond": | 
|  1292         onPrefMessage(message.action, message.args[0], false); |  1277         onPrefMessage(message.action, message.args[0], false); | 
|  1293         break; |  1278         break; | 
|  1294       case "subscriptions.respond": |  1279       case "subscriptions.respond": | 
|  1295         onSubscriptionMessage(message.action, message.args[0]); |  1280         onSubscriptionMessage(message.action, message.args[0]); | 
|  1296         break; |  1281         break; | 
|  1297     } |  1282     } | 
|  1298   }); |  1283   }); | 
|  1299  |  1284  | 
|  1300   ext.backgroundPage.sendMessage( |  1285   ext.backgroundPage.sendMessage({ | 
|  1301   { |  | 
|  1302     type: "app.listen", |  1286     type: "app.listen", | 
|  1303     filter: ["addSubscription", "focusSection"] |  1287     filter: ["addSubscription", "focusSection"] | 
|  1304   }); |  1288   }); | 
|  1305   ext.backgroundPage.sendMessage( |  1289   ext.backgroundPage.sendMessage({ | 
|  1306   { |  | 
|  1307     type: "filters.listen", |  1290     type: "filters.listen", | 
|  1308     filter: ["added", "loaded", "removed"] |  1291     filter: ["added", "loaded", "removed"] | 
|  1309   }); |  1292   }); | 
|  1310   ext.backgroundPage.sendMessage( |  1293   ext.backgroundPage.sendMessage({ | 
|  1311   { |  | 
|  1312     type: "prefs.listen", |  1294     type: "prefs.listen", | 
|  1313     filter: ["notifications_ignoredcategories", "notifications_showui", |  1295     filter: ["notifications_ignoredcategories", "notifications_showui", | 
|  1314         "show_devtools_panel", "shouldShowBlockElementMenu"] |  1296              "show_devtools_panel", "shouldShowBlockElementMenu"] | 
|  1315   }); |  1297   }); | 
|  1316   ext.backgroundPage.sendMessage( |  1298   ext.backgroundPage.sendMessage({ | 
|  1317   { |  | 
|  1318     type: "subscriptions.listen", |  1299     type: "subscriptions.listen", | 
|  1319     filter: ["added", "disabled", "homepage", "lastDownload", "removed", |  1300     filter: ["added", "disabled", "homepage", "lastDownload", "removed", | 
|  1320         "title", "downloadStatus", "downloading"] |  1301              "title", "downloadStatus", "downloading"] | 
|  1321   }); |  1302   }); | 
|  1322  |  1303  | 
|  1323   window.addEventListener("DOMContentLoaded", onDOMLoaded, false); |  1304   window.addEventListener("DOMContentLoaded", onDOMLoaded, false); | 
|  1324   window.addEventListener("hashchange", onHashChange, false); |  1305   window.addEventListener("hashchange", onHashChange, false); | 
|  1325 })(); |  1306 } | 
| OLD | NEW |