| Index: options.js | 
| =================================================================== | 
| --- a/options.js | 
| +++ b/options.js | 
| @@ -10,738 +10,27 @@ | 
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| * GNU General Public License for more details. | 
| * | 
| * You should have received a copy of the GNU General Public License | 
| * along with Adblock Plus.  If not, see <http://www.gnu.org/licenses/>. | 
| */ | 
|  | 
| -/* global $, i18n, i18nTimeDateStrings */ | 
| - | 
| "use strict"; | 
|  | 
| -/** | 
| - * Creates a wrapping function used to conveniently send a type of message. | 
| - * | 
| - * @param {Object} baseMessage The part of the message that's always sent | 
| - * @param {...string} paramKeys Any message keys that have dynamic values. The | 
| - *                              returned function will take the corresponding | 
| - *                              values as arguments. | 
| - * @return {function} The generated messaging function, optionally | 
| - *                    taking any values as specified by the paramKeys | 
| - *                    and finally an optional callback.  (Although the | 
| - *                    value arguments are optional their index must be | 
| - *                    maintained. E.g. if you omit the first value you | 
| - *                    must omit the second too.) | 
| - */ | 
| -function wrapper(baseMessage, ...paramKeys) | 
| -{ | 
| -  return function(...paramValues /* , callback */) | 
| -  { | 
| -    let message = Object.assign(Object.create(null), baseMessage); | 
| -    let callback; | 
| - | 
| -    if (paramValues.length > 0) | 
| -    { | 
| -      let lastArg = paramValues[paramValues.length - 1]; | 
| -      if (typeof lastArg == "function") | 
| -        callback = lastArg; | 
| - | 
| -      for (let i = 0; i < paramValues.length - (callback ? 1 : 0); i++) | 
| -        message[paramKeys[i]] = paramValues[i]; | 
| -    } | 
| - | 
| -    ext.backgroundPage.sendMessage(message, callback); | 
| -  }; | 
| -} | 
| - | 
| -const getDocLink = wrapper({type: "app.get", what: "doclink"}, "link"); | 
| -const getInfo = wrapper({type: "app.get"}, "what"); | 
| -const getPref = wrapper({type: "prefs.get"}, "key"); | 
| -const togglePref = wrapper({type: "prefs.toggle"}, "key"); | 
| -const getSubscriptions = wrapper({type: "subscriptions.get"}, | 
| -                                 "downloadable", "special"); | 
| -const removeSubscription = wrapper({type: "subscriptions.remove"}, "url"); | 
| -const addSubscription = wrapper({type: "subscriptions.add"}, | 
| -                                "url", "title", "homepage"); | 
| -const toggleSubscription = wrapper({type: "subscriptions.toggle"}, | 
| -                                   "url", "keepInstalled"); | 
| -const updateSubscription = wrapper({type: "subscriptions.update"}, "url"); | 
| -const importRawFilters = wrapper({type: "filters.importRaw"}, | 
| -                                 "text", "removeExisting"); | 
| -const addFilter = wrapper({type: "filters.add"}, "text"); | 
| -const removeFilter = wrapper({type: "filters.remove"}, "text"); | 
| -const quoteCSS = wrapper({type: "composer.quoteCSS"}, "CSS"); | 
| - | 
| -const whitelistedDomainRegexp = /^@@\|\|([^/:]+)\^\$document$/; | 
| -const statusMessages = new Map([ | 
| -  ["synchronize_invalid_url", | 
| -   "filters_subscription_lastDownload_invalidURL"], | 
| -  ["synchronize_connection_error", | 
| -   "filters_subscription_lastDownload_connectionError"], | 
| -  ["synchronize_invalid_data", | 
| -   "filters_subscription_lastDownload_invalidData"], | 
| -  ["synchronize_checksum_mismatch", | 
| -   "filters_subscription_lastDownload_checksumMismatch"] | 
| -]); | 
| - | 
| -let delayedSubscriptionSelection = null; | 
| -let acceptableAdsUrl; | 
| - | 
| -// Loads options from localStorage and sets UI elements accordingly | 
| -function loadOptions() | 
| -{ | 
| -  // Set page title to i18n version of "Adblock Plus Options" | 
| -  document.title = i18n.getMessage("options"); | 
| +let iframe = document.getElementById("content"); | 
|  | 
| -  // Set links | 
| -  getPref("subscriptions_exceptionsurl", url => | 
| -  { | 
| -    acceptableAdsUrl = url; | 
| -    $("#acceptableAdsLink").attr("href", acceptableAdsUrl); | 
| -  }); | 
| -  getDocLink("acceptable_ads", url => | 
| -  { | 
| -    $("#acceptableAdsDocs").attr("href", url); | 
| -  }); | 
| -  getDocLink("filterdoc", url => | 
| -  { | 
| -    setLinks("filter-must-follow-syntax", url); | 
| -  }); | 
| -  getInfo("application", application => | 
| -  { | 
| -    getInfo("platform", platform => | 
| -    { | 
| -      if (platform == "chromium" && application != "opera") | 
| -        application = "chrome"; | 
| - | 
| -      getDocLink(application + "_support", url => | 
| -      { | 
| -        setLinks("found-a-bug", url); | 
| -      }); | 
| - | 
| -      if (platform == "gecko") | 
| -        $("#firefox-warning").removeAttr("hidden"); | 
| -    }); | 
| -  }); | 
| - | 
| -  // Add event listeners | 
| -  $("#updateFilterLists").click(updateFilterLists); | 
| -  $("#startSubscriptionSelection").click(startSubscriptionSelection); | 
| -  $("#subscriptionSelector").change(updateSubscriptionSelection); | 
| -  $("#addSubscription").click(addSubscriptionClicked); | 
| -  $("#acceptableAds").click(toggleAcceptableAds); | 
| -  $("#whitelistForm").submit(addWhitelistDomain); | 
| -  $("#removeWhitelist").click(removeSelectedExcludedDomain); | 
| -  $("#customFilterForm").submit(addTypedFilter); | 
| -  $("#removeCustomFilter").click(removeSelectedFilters); | 
| -  $("#rawFiltersButton").click(toggleFiltersInRawFormat); | 
| -  $("#importRawFilters").click(importRawFiltersText); | 
| - | 
| -  // Display jQuery UI elements | 
| -  $("#tabs").tabs(); | 
| -  $("button:not(.subscriptionRemoveButton)").button(); | 
| -  $(".refreshButton").button("option", "icons", {primary: "ui-icon-refresh"}); | 
| -  $(".addButton").button("option", "icons", {primary: "ui-icon-plus"}); | 
| -  $(".removeButton").button("option", "icons", {primary: "ui-icon-minus"}); | 
| - | 
| -  // Popuplate option checkboxes | 
| -  initCheckbox("shouldShowBlockElementMenu"); | 
| -  initCheckbox("show_devtools_panel"); | 
| -  initCheckbox("shouldShowNotifications", "notifications_ignoredcategories"); | 
| - | 
| -  getInfo("features", features => | 
| -  { | 
| -    if (!features.devToolsPanel) | 
| -      document.getElementById("showDevtoolsPanelContainer").hidden = true; | 
| -  }); | 
| -  getPref("notifications_showui", showNotificationsUI => | 
| -  { | 
| -    if (!showNotificationsUI) | 
| -      document.getElementById("shouldShowNotificationsContainer").hidden = true; | 
| -  }); | 
| - | 
| -  // Register listeners in the background message responder | 
| -  ext.backgroundPage.sendMessage({ | 
| -    type: "app.listen", | 
| -    filter: ["addSubscription", "focusSection"] | 
| -  }); | 
| -  ext.backgroundPage.sendMessage({ | 
| -    type: "filters.listen", | 
| -    filter: ["added", "loaded", "removed"] | 
| -  }); | 
| -  ext.backgroundPage.sendMessage({ | 
| -    type: "prefs.listen", | 
| -    filter: ["notifications_ignoredcategories", "notifications_showui", | 
| -             "show_devtools_panel", "shouldShowBlockElementMenu"] | 
| -  }); | 
| -  ext.backgroundPage.sendMessage({ | 
| -    type: "subscriptions.listen", | 
| -    filter: ["added", "disabled", "homepage", "lastDownload", "removed", | 
| -             "title", "downloadStatus", "downloading"] | 
| -  }); | 
| - | 
| -  // Load recommended subscriptions | 
| -  loadRecommendations(); | 
| - | 
| -  // Show user's filters | 
| -  reloadFilters(); | 
| -} | 
| -$(loadOptions); | 
| - | 
| -function convertSpecialSubscription(subscription) | 
| +iframe.onload = () => | 
| { | 
| -  for (let filter of subscription.filters) | 
| -  { | 
| -    if (whitelistedDomainRegexp.test(filter.text)) | 
| -      appendToListBox("excludedDomainsBox", RegExp.$1); | 
| -    else | 
| -      appendToListBox("userFiltersBox", filter.text); | 
| -  } | 
| -} | 
| - | 
| -// Reloads the displayed subscriptions and filters | 
| -function reloadFilters() | 
| -{ | 
| -  // Load user filter URLs | 
| -  let container = document.getElementById("filterLists"); | 
| -  while (container.lastChild) | 
| -    container.removeChild(container.lastChild); | 
| - | 
| -  getSubscriptions(true, false, subscriptions => | 
| -  { | 
| -    for (let subscription of subscriptions) | 
| -    { | 
| -      if (subscription.url == acceptableAdsUrl) | 
| -        $("#acceptableAds").prop("checked", !subscription.disabled); | 
| -      else | 
| -        addSubscriptionEntry(subscription); | 
| -    } | 
| -  }); | 
| - | 
| -  // User-entered filters | 
| -  getSubscriptions(false, true, subscriptions => | 
| -  { | 
| -    document.getElementById("userFiltersBox").innerHTML = ""; | 
| -    document.getElementById("excludedDomainsBox").innerHTML = ""; | 
| - | 
| -    for (let subscription of subscriptions) | 
| -      convertSpecialSubscription(subscription); | 
| -  }); | 
| -} | 
| - | 
| -function initCheckbox(id, key) | 
| -{ | 
| -  key = key || id; | 
| -  let checkbox = document.getElementById(id); | 
| - | 
| -  getPref(key, value => | 
| -  { | 
| -    onPrefMessage(key, value); | 
| -  }); | 
| - | 
| -  checkbox.addEventListener("click", () => | 
| -  { | 
| -    togglePref(key); | 
| -  }, false); | 
| -} | 
| - | 
| -function loadRecommendations() | 
| -{ | 
| -  fetch("subscriptions.xml") | 
| -    .then(response => | 
| -    { | 
| -      return response.text(); | 
| -    }) | 
| -    .then(text => | 
| -    { | 
| -      let selectedIndex = 0; | 
| -      let selectedPrefix = null; | 
| -      let matchCount = 0; | 
| - | 
| -      let list = document.getElementById("subscriptionSelector"); | 
| -      let doc = new DOMParser().parseFromString(text, "application/xml"); | 
| -      let elements = doc.documentElement.getElementsByTagName("subscription"); | 
| - | 
| -      for (let i = 0; i < elements.length; i++) | 
| -      { | 
| -        let element = elements[i]; | 
| -        let option = new Option(); | 
| -        option.text = element.getAttribute("title") + " (" + | 
| -                      element.getAttribute("specialization") + ")"; | 
| -        option._data = { | 
| -          title: element.getAttribute("title"), | 
| -          url: element.getAttribute("url"), | 
| -          homepage: element.getAttribute("homepage") | 
| -        }; | 
| - | 
| -        let prefix = element.getAttribute("prefixes"); | 
| -        if (prefix) | 
| -        { | 
| -          prefix = prefix.replace(/\W/g, "_"); | 
| -          option.style.fontWeight = "bold"; | 
| -          option.style.backgroundColor = "#E0FFE0"; | 
| -          option.style.color = "#000000"; | 
| -          if (!selectedPrefix || selectedPrefix.length < prefix.length) | 
| -          { | 
| -            selectedIndex = i; | 
| -            selectedPrefix = prefix; | 
| -            matchCount = 1; | 
| -          } | 
| -          else if (selectedPrefix && selectedPrefix.length == prefix.length) | 
| -          { | 
| -            matchCount++; | 
| - | 
| -            // If multiple items have a matching prefix of the same length: | 
| -            // Select one of the items randomly, probability should be the same | 
| -            // for all items. So we replace the previous match here with | 
| -            // probability 1/N (N being the number of matches). | 
| -            if (Math.random() * matchCount < 1) | 
| -            { | 
| -              selectedIndex = i; | 
| -              selectedPrefix = prefix; | 
| -            } | 
| -          } | 
| -        } | 
| -        list.appendChild(option); | 
| -      } | 
| - | 
| -      let option = new Option(); | 
| -      let label = i18n.getMessage("filters_addSubscriptionOther_label"); | 
| -      option.text = label + "\u2026"; | 
| -      option._data = null; | 
| -      list.appendChild(option); | 
| - | 
| -      list.selectedIndex = selectedIndex; | 
| - | 
| -      if (delayedSubscriptionSelection) | 
| -        startSubscriptionSelection(...delayedSubscriptionSelection); | 
| -    }); | 
| -} | 
| - | 
| -function startSubscriptionSelection(title, url) | 
| -{ | 
| -  let list = document.getElementById("subscriptionSelector"); | 
| -  if (list.length == 0) | 
| -  { | 
| -    delayedSubscriptionSelection = [title, url]; | 
| -    return; | 
| -  } | 
| - | 
| -  $("#tabs").tabs("option", "active", 0); | 
| -  $("#addSubscriptionContainer").show(); | 
| -  $("#addSubscriptionButton").hide(); | 
| -  $("#subscriptionSelector").focus(); | 
| -  if (typeof url != "undefined") | 
| -  { | 
| -    list.selectedIndex = list.length - 1; | 
| -    document.getElementById("customSubscriptionTitle").value = title; | 
| -    document.getElementById("customSubscriptionLocation").value = url; | 
| -  } | 
| -  updateSubscriptionSelection(); | 
| -  document.getElementById("addSubscriptionContainer").scrollIntoView(true); | 
| -} | 
| - | 
| -function updateSubscriptionSelection() | 
| -{ | 
| -  let list = document.getElementById("subscriptionSelector"); | 
| -  let data = list.options[list.selectedIndex]._data; | 
| -  if (data) | 
| -    $("#customSubscriptionContainer").hide(); | 
| -  else | 
| -  { | 
| -    $("#customSubscriptionContainer").show(); | 
| -    $("#customSubscriptionTitle").focus(); | 
| -  } | 
| -} | 
| - | 
| -function addSubscriptionClicked() | 
| -{ | 
| -  let list = document.getElementById("subscriptionSelector"); | 
| -  let data = list.options[list.selectedIndex]._data; | 
| -  if (data) | 
| -    addSubscription(data.url, data.title, data.homepage); | 
| -  else | 
| -  { | 
| -    let url = document.getElementById("customSubscriptionLocation") | 
| -                      .value.trim(); | 
| -    if (!/^https?:/i.test(url)) | 
| -    { | 
| -      alert(i18n.getMessage("global_subscription_invalid_location")); | 
| -      $("#customSubscriptionLocation").focus(); | 
| -      return; | 
| -    } | 
| - | 
| -    let title = document.getElementById("customSubscriptionTitle").value.trim(); | 
| -    if (!title) | 
| -      title = url; | 
| - | 
| -    addSubscription(url, title, null); | 
| -  } | 
| +  document.title = iframe.contentDocument.title; | 
| +}; | 
|  | 
| -  $("#addSubscriptionContainer").hide(); | 
| -  $("#customSubscriptionContainer").hide(); | 
| -  $("#addSubscriptionButton").show(); | 
| -} | 
| - | 
| -function toggleAcceptableAds() | 
| -{ | 
| -  toggleSubscription(acceptableAdsUrl, true); | 
| -} | 
| - | 
| -function findSubscriptionElement(subscription) | 
| -{ | 
| -  for (let child of document.getElementById("filterLists").childNodes) | 
| -  { | 
| -    if (child._subscription.url == subscription.url) | 
| -      return child; | 
| -  } | 
| -  return null; | 
| -} | 
| - | 
| -function updateSubscriptionInfo(element, subscription) | 
| -{ | 
| -  if (subscription) | 
| -    element._subscription = subscription; | 
| -  else | 
| -    subscription = element._subscription; | 
| - | 
| -  let title = element.getElementsByClassName("subscriptionTitle")[0]; | 
| -  title.textContent = subscription.title; | 
| -  title.setAttribute("title", subscription.url); | 
| -  if (subscription.homepage) | 
| -    title.href = subscription.homepage; | 
| -  else | 
| -    title.href = subscription.url; | 
| - | 
| -  let enabled = element.getElementsByClassName("subscriptionEnabled")[0]; | 
| -  enabled.checked = !subscription.disabled; | 
| - | 
| -  let lastUpdate = element.getElementsByClassName("subscriptionUpdate")[0]; | 
| -  lastUpdate.classList.remove("error"); | 
| - | 
| -  let {downloadStatus} = subscription; | 
| -  if (subscription.isDownloading) | 
| -  { | 
| -    lastUpdate.textContent = i18n.getMessage( | 
| -      "filters_subscription_lastDownload_inProgress" | 
| -    ); | 
| -  } | 
| -  else if (downloadStatus && downloadStatus != "synchronize_ok") | 
| -  { | 
| -    if (statusMessages.has(downloadStatus)) | 
| -    { | 
| -      lastUpdate.textContent = i18n.getMessage( | 
| -        statusMessages.get(downloadStatus) | 
| -      ); | 
| -    } | 
| -    else | 
| -      lastUpdate.textContent = downloadStatus; | 
| -    lastUpdate.classList.add("error"); | 
| -  } | 
| -  else if (subscription.lastDownload > 0) | 
| -  { | 
| -    let timeDate = i18nTimeDateStrings(subscription.lastDownload * 1000); | 
| -    let messageID = (timeDate[1] ? "last_updated_at" : "last_updated_at_today"); | 
| -    lastUpdate.textContent = i18n.getMessage(messageID, timeDate); | 
| -  } | 
| -} | 
| - | 
| -function onSubscriptionMessage(action, subscription) | 
| -{ | 
| -  let element = findSubscriptionElement(subscription); | 
| - | 
| -  switch (action) | 
| -  { | 
| -    case "disabled": | 
| -    case "downloading": | 
| -    case "downloadStatus": | 
| -    case "homepage": | 
| -    case "lastDownload": | 
| -    case "title": | 
| -      if (element) | 
| -        updateSubscriptionInfo(element, subscription); | 
| -      break; | 
| -    case "added": | 
| -      if (subscription.url.indexOf("~user") == 0) | 
| -        convertSpecialSubscription(subscription); | 
| -      else if (subscription.url == acceptableAdsUrl) | 
| -        $("#acceptableAds").prop("checked", true); | 
| -      else if (!element) | 
| -        addSubscriptionEntry(subscription); | 
| -      break; | 
| -    case "removed": | 
| -      if (subscription.url == acceptableAdsUrl) | 
| -        $("#acceptableAds").prop("checked", false); | 
| -      else if (element) | 
| -        element.parentNode.removeChild(element); | 
| -      break; | 
| -  } | 
| -} | 
| - | 
| -function onPrefMessage(key, value) | 
| -{ | 
| -  switch (key) | 
| -  { | 
| -    case "notifications_showui": | 
| -      document.getElementById( | 
| -        "shouldShowNotificationsContainer" | 
| -      ).hidden = !value; | 
| -      return; | 
| -    case "notifications_ignoredcategories": | 
| -      key = "shouldShowNotifications"; | 
| -      value = value.indexOf("*") == -1; | 
| -      break; | 
| -  } | 
| -  let checkbox = document.getElementById(key); | 
| -  if (checkbox) | 
| -    checkbox.checked = value; | 
| -} | 
| - | 
| -function onFilterMessage(action, filter) | 
| -{ | 
| -  switch (action) | 
| -  { | 
| -    case "loaded": | 
| -      reloadFilters(); | 
| -      break; | 
| -    case "added": | 
| -      if (whitelistedDomainRegexp.test(filter.text)) | 
| -        appendToListBox("excludedDomainsBox", RegExp.$1); | 
| -      else | 
| -        appendToListBox("userFiltersBox", filter.text); | 
| -      break; | 
| -    case "removed": | 
| -      if (whitelistedDomainRegexp.test(filter.text)) | 
| -        removeFromListBox("excludedDomainsBox", RegExp.$1); | 
| -      else | 
| -        removeFromListBox("userFiltersBox", filter.text); | 
| -      break; | 
| -  } | 
| -} | 
| - | 
| -// Add a filter string to the list box. | 
| -function appendToListBox(boxId, text) | 
| -{ | 
| -  // Note: document.createElement("option") is unreliable in Opera | 
| -  let elt = new Option(); | 
| -  elt.text = text; | 
| -  elt.value = text; | 
| -  document.getElementById(boxId).appendChild(elt); | 
| -} | 
| - | 
| -// Remove a filter string from a list box. | 
| -function removeFromListBox(boxId, text) | 
| -{ | 
| -  let list = document.getElementById(boxId); | 
| -  // Edge does not support CSS.escape yet: | 
| -  // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/101410/ | 
| -  quoteCSS(text, escapedCSS => | 
| -  { | 
| -    let selector = "option[value=" + escapedCSS + "]"; | 
| -    for (let option of list.querySelectorAll(selector)) | 
| -      list.removeChild(option); | 
| -  }); | 
| -} | 
| - | 
| -function addWhitelistDomain(event) | 
| -{ | 
| -  event.preventDefault(); | 
| - | 
| -  let domain = document.getElementById( | 
| -    "newWhitelistDomain" | 
| -  ).value.replace(/\s/g, ""); | 
| -  document.getElementById("newWhitelistDomain").value = ""; | 
| -  if (!domain) | 
| -    return; | 
| - | 
| -  let filterText = "@@||" + domain + "^$document"; | 
| -  addFilter(filterText); | 
| -} | 
| - | 
| -// Adds filter text that user typed to the selection box | 
| -function addTypedFilter(event) | 
| +chrome.runtime.sendMessage({ | 
| +  type: "app.get", | 
| +  what: "application" | 
| +}, | 
| +application => | 
| { | 
| -  event.preventDefault(); | 
| - | 
| -  let element = document.getElementById("newFilter"); | 
| -  addFilter(element.value, errors => | 
| -  { | 
| -    if (errors.length > 0) | 
| -      alert(errors.join("\n")); | 
| -    else | 
| -      element.value = ""; | 
| -  }); | 
| -} | 
| - | 
| -// Removes currently selected whitelisted domains | 
| -function removeSelectedExcludedDomain(event) | 
| -{ | 
| -  event.preventDefault(); | 
| -  let remove = []; | 
| -  for (let option of document.getElementById("excludedDomainsBox").options) | 
| -  { | 
| -    if (option.selected) | 
| -      remove.push(option.value); | 
| -  } | 
| -  if (!remove.length) | 
| -    return; | 
| - | 
| -  for (let domain of remove) | 
| -    removeFilter("@@||" + domain + "^$document"); | 
| -} | 
| - | 
| -// Removes all currently selected filters | 
| -function removeSelectedFilters(event) | 
| -{ | 
| -  event.preventDefault(); | 
| -  let options = document.querySelectorAll("#userFiltersBox > option:checked"); | 
| -  for (let option of options) | 
| -    removeFilter(option.value); | 
| -} | 
| - | 
| -// Shows raw filters box and fills it with the current user filters | 
| -function toggleFiltersInRawFormat(event) | 
| -{ | 
| -  event.preventDefault(); | 
| - | 
| -  let rawFilters = document.getElementById("rawFilters"); | 
| -  let filters = []; | 
| - | 
| -  if (rawFilters.style.display != "table-row") | 
| -  { | 
| -    rawFilters.style.display = "table-row"; | 
| -    for (let option of document.getElementById("userFiltersBox").options) | 
| -      filters.push(option.value); | 
| -  } | 
| -  else | 
| -  { | 
| -    rawFilters.style.display = "none"; | 
| -  } | 
| - | 
| -  document.getElementById("rawFiltersText").value = filters.join("\n"); | 
| -} | 
| - | 
| -// Imports filters in the raw text box | 
| -function importRawFiltersText() | 
| -{ | 
| -  let text = document.getElementById("rawFiltersText").value; | 
| - | 
| -  importRawFilters(text, true, errors => | 
| -  { | 
| -    if (errors.length > 0) | 
| -      alert(errors.join("\n")); | 
| -    else | 
| -      $("#rawFilters").hide(); | 
| -  }); | 
| -} | 
| - | 
| -// Called when user explicitly requests filter list updates | 
| -function updateFilterLists() | 
| -{ | 
| -  // Without the URL parameter this will update all subscriptions | 
| -  updateSubscription(); | 
| -} | 
| - | 
| -// Adds a subscription entry to the UI. | 
| -function addSubscriptionEntry(subscription) | 
| -{ | 
| -  let template = document.getElementById("subscriptionTemplate"); | 
| -  let element = template.cloneNode(true); | 
| -  element.removeAttribute("id"); | 
| -  element._subscription = subscription; | 
| - | 
| -  let removeButton = element.getElementsByClassName( | 
| -    "subscriptionRemoveButton" | 
| -  )[0]; | 
| -  removeButton.setAttribute("title", removeButton.textContent); | 
| -  removeButton.textContent = "\xD7"; | 
| -  removeButton.addEventListener("click", () => | 
| -  { | 
| -    if (!confirm(i18n.getMessage("global_remove_subscription_warning"))) | 
| -      return; | 
| - | 
| -    removeSubscription(subscription.url); | 
| -  }, false); | 
| - | 
| -  getPref("additional_subscriptions", additionalSubscriptions => | 
| -  { | 
| -    if (additionalSubscriptions.includes(subscription.url)) | 
| -      removeButton.style.visibility = "hidden"; | 
| -  }); | 
| - | 
| -  let enabled = element.getElementsByClassName("subscriptionEnabled")[0]; | 
| -  enabled.addEventListener("click", () => | 
| -  { | 
| -    subscription.disabled = !subscription.disabled; | 
| -    toggleSubscription(subscription.url, true); | 
| -  }, false); | 
| - | 
| -  updateSubscriptionInfo(element); | 
| - | 
| -  document.getElementById("filterLists").appendChild(element); | 
| -} | 
| - | 
| -function setLinks(id, ...args) | 
| -{ | 
| -  let element = document.getElementById(id); | 
| -  if (!element) | 
| -    return; | 
| - | 
| -  let links = element.getElementsByTagName("a"); | 
| -  for (let i = 0; i < links.length; i++) | 
| -  { | 
| -    if (typeof args[i] == "string") | 
| -    { | 
| -      links[i].href = args[i]; | 
| -      links[i].setAttribute("target", "_blank"); | 
| -    } | 
| -    else if (typeof args[i] == "function") | 
| -    { | 
| -      links[i].href = "javascript:void(0);"; | 
| -      links[i].addEventListener("click", args[i], false); | 
| -    } | 
| -  } | 
| -} | 
| - | 
| -ext.onMessage.addListener(message => | 
| -{ | 
| -  switch (message.type) | 
| -  { | 
| -    case "app.respond": | 
| -      switch (message.action) | 
| -      { | 
| -        case "addSubscription": | 
| -          let subscription = message.args[0]; | 
| -          startSubscriptionSelection(subscription.title, subscription.url); | 
| -          break; | 
| -        case "focusSection": | 
| -          for (let tab of document.getElementsByClassName("ui-tabs-panel")) | 
| -          { | 
| -            let found = tab.querySelector( | 
| -              "[data-section='" + message.args[0] + "']" | 
| -            ); | 
| -            if (!found) | 
| -              continue; | 
| - | 
| -            let previous = document.getElementsByClassName("focused"); | 
| -            if (previous.length > 0) | 
| -              previous[0].classList.remove("focused"); | 
| - | 
| -            let index = $("[href='#" + tab.id + "']").parent().index(); | 
| -            $("#tabs").tabs("option", "active", index); | 
| -            found.classList.add("focused"); | 
| -          } | 
| -          break; | 
| -      } | 
| -      break; | 
| -    case "filters.respond": | 
| -      onFilterMessage(message.action, message.args[0]); | 
| -      break; | 
| -    case "prefs.respond": | 
| -      onPrefMessage(message.action, message.args[0]); | 
| -      break; | 
| -    case "subscriptions.respond": | 
| -      onSubscriptionMessage(message.action, message.args[0]); | 
| -      break; | 
| -  } | 
| +  // Load the mobile version of the options page on Firefox for Android. | 
| +  iframe.src = iframe.getAttribute("data-src-" + application) || | 
| +               iframe.getAttribute("data-src"); | 
| }); | 
|  |