| Index: new-options.js | 
| =================================================================== | 
| --- a/new-options.js | 
| +++ b/new-options.js | 
| @@ -25,6 +25,7 @@ | 
| let filtersMap = Object.create(null); | 
| let collections = Object.create(null); | 
| let acceptableAdsUrl = null; | 
| +  let acceptableAdsPrivacyUrl = null; | 
| let isCustomFiltersLoaded = false; | 
| let {getMessage} = ext.i18n; | 
| let customFilters = []; | 
| @@ -83,8 +84,6 @@ | 
|  | 
| Collection.prototype._getItemTitle = function(item, i) | 
| { | 
| -    if (item.url == acceptableAdsUrl) | 
| -      return getMessage("options_acceptableAds_description"); | 
| if (this.details[i].useOriginalTitle && item.originalTitle) | 
| return item.originalTitle; | 
| return item.title || item.url || item.text; | 
| @@ -98,9 +97,9 @@ | 
| // disabled, but only be removed. That way it's grouped together with | 
| // the "Own filter list" which cannot be disabled either at the bottom | 
| // of the filter lists in the Advanced tab. | 
| -      if (a.url == acceptableAdsUrl) | 
| +      if (isAcceptableAds(a.url)) | 
| return 1; | 
| -      if (b.url == acceptableAdsUrl) | 
| +      if (isAcceptableAds(b.url)) | 
| return -1; | 
|  | 
| // Make sure that newly added entries always appear on top in descending | 
| @@ -134,12 +133,19 @@ | 
| listItem.setAttribute("data-access", item.url || item.text); | 
| listItem.setAttribute("role", "section"); | 
|  | 
| -      let label = listItem.querySelector(".display"); | 
| -      if (item.recommended && label.hasAttribute("data-tooltip")) | 
| +      let tooltip = listItem.querySelector("[data-tooltip]"); | 
| +      if (tooltip) | 
| { | 
| -        let tooltipId = label.getAttribute("data-tooltip"); | 
| +        let tooltipId = tooltip.getAttribute("data-tooltip"); | 
| tooltipId = tooltipId.replace("%value%", item.recommended); | 
| -        label.setAttribute("data-tooltip", tooltipId); | 
| +        if (getMessage(tooltipId)) | 
| +        { | 
| +          tooltip.setAttribute("data-tooltip", tooltipId); | 
| +        } | 
| +        else | 
| +        { | 
| +          tooltip.parentNode.removeChild(tooltip); | 
| +        } | 
| } | 
|  | 
| for (let control of listItem.querySelectorAll(".control")) | 
| @@ -214,7 +220,10 @@ | 
| continue; | 
|  | 
| let title = this._getItemTitle(item, i); | 
| -      element.querySelector(".display").textContent = title; | 
| +      let displays = element.querySelectorAll(".display"); | 
| +      for (let j = 0; j < displays.length; j++) | 
| +        displays[j].textContent = title; | 
| + | 
| element.setAttribute("aria-label", title); | 
| if (this.details[i].searchable) | 
| element.setAttribute("data-search", title.toLowerCase()); | 
| @@ -222,7 +231,7 @@ | 
| if (control) | 
| { | 
| control.setAttribute("aria-checked", item.disabled == false); | 
| -        if (item.url == acceptableAdsUrl && this == collections.filterLists) | 
| +        if (isAcceptableAds(item.url) && this == collections.filterLists) | 
| control.disabled = true; | 
| } | 
|  | 
| @@ -330,31 +339,21 @@ | 
| return true; | 
| } | 
|  | 
| -  collections.popular = new Collection([ | 
| +  collections.protection = new Collection([ | 
| { | 
| -      id: "recommend-list-table" | 
| +      id: "recommend-protection-list-table" | 
| } | 
| ]); | 
| collections.langs = new Collection([ | 
| { | 
| id: "blocking-languages-table", | 
| -      emptyText: ["options_dialog_language_added_empty"] | 
| -    }, | 
| -    { | 
| -      id: "blocking-languages-dialog-table", | 
| -      emptyText: ["options_dialog_language_added_empty"] | 
| +      emptyText: ["options_language_empty"] | 
| } | 
| ]); | 
| collections.allLangs = new Collection([ | 
| { | 
| -      id: "all-lang-table", | 
| -      emptyText: ["options_dialog_language_other_empty"], | 
| -      searchable: true | 
| -    } | 
| -  ]); | 
| -  collections.acceptableAds = new Collection([ | 
| -    { | 
| -      id: "acceptableads-table" | 
| +      id: "all-lang-table-add", | 
| +      emptyText: ["options_dialog_language_other_empty"] | 
| } | 
| ]); | 
| collections.custom = new Collection([ | 
| @@ -375,43 +374,32 @@ | 
| } | 
| ]); | 
|  | 
| -  function toggleShowLanguage(subscription) | 
| +  function addSubscription(subscription) | 
| { | 
| -    if (subscription.recommended == "ads") | 
| +    let collection = null; | 
| +    if (subscription.recommended) | 
| { | 
| -      if (subscription.disabled) | 
| +      if (subscription.recommended == "ads") | 
| { | 
| +        if (subscription.disabled == false) | 
| +          collection = collections.langs; | 
| + | 
| collections.allLangs.addItem(subscription); | 
| -        collections.langs.removeItem(subscription); | 
| } | 
| else | 
| { | 
| -        collections.allLangs.removeItem(subscription); | 
| -        collections.langs.addItem(subscription); | 
| +        collection = collections.protection; | 
| } | 
| } | 
| -  } | 
| +    else if (!isAcceptableAds(subscription.url)) | 
| +    { | 
| +      collection = collections.custom; | 
| +    } | 
|  | 
| -  function addSubscription(subscription) | 
| -  { | 
| -    let collection; | 
| -    if (subscription.recommended) | 
| -    { | 
| -      if (subscription.recommended != "ads") | 
| -        collection = collections.popular; | 
| -      else if (subscription.disabled == false) | 
| -        collection = collections.langs; | 
| -      else | 
| -        collection = collections.allLangs; | 
| -    } | 
| -    else if (subscription.url == acceptableAdsUrl) | 
| -      collection = collections.acceptableAds; | 
| -    else | 
| -      collection = collections.custom; | 
| +    if (collection) | 
| +      collection.addItem(subscription); | 
|  | 
| -    collection.addItem(subscription); | 
| subscriptionsMap[subscription.url] = subscription; | 
| -    toggleShowLanguage(subscription); | 
| updateTooltips(); | 
| } | 
|  | 
| @@ -420,7 +408,25 @@ | 
| for (let name in collections) | 
| collections[name].updateItem(subscription); | 
|  | 
| -    toggleShowLanguage(subscription); | 
| +    if (subscription.recommended == "ads") | 
| +    { | 
| +      if (subscription.disabled) | 
| +        collections.langs.removeItem(subscription); | 
| +      else | 
| +        collections.langs.addItem(subscription); | 
| +    } | 
| +    else if (!subscription.recommended && !isAcceptableAds(subscription.url)) | 
| +    { | 
| +      if (subscription.disabled == false) | 
| +      { | 
| +        collections.custom.addItem(subscription); | 
| +        updateTooltips(); | 
| +      } | 
| +      else | 
| +      { | 
| +        collections.custom.removeItem(subscription); | 
| +      } | 
| +    } | 
| } | 
|  | 
| function updateFilter(filter) | 
| @@ -561,6 +567,25 @@ | 
| case "cancel-custom-filters": | 
| setCustomFiltersView("read"); | 
| break; | 
| +      case "change-language-subscription": | 
| +        for (let key in subscriptionsMap) | 
| +        { | 
| +          let subscription = subscriptionsMap[key]; | 
| +          let subscriptionType = subscription.recommended; | 
| +          if (subscriptionType == "ads" && subscription.disabled == false) | 
| +          { | 
| +            ext.backgroundPage.sendMessage({ | 
| +              type: "subscriptions.remove", | 
| +              url: subscription.url | 
| +            }); | 
| +            ext.backgroundPage.sendMessage({ | 
| +              type: "subscriptions.add", | 
| +              url: findParentData(element, "access", false) | 
| +            }); | 
| +            break; | 
| +          } | 
| +        } | 
| +        break; | 
| case "close-dialog": | 
| closeDialog(); | 
| break; | 
| @@ -612,6 +637,18 @@ | 
| setCustomFiltersView("read"); | 
| }); | 
| break; | 
| +      case "switch-acceptable-ads": | 
| +        let {value} = element; | 
| +        ext.backgroundPage.sendMessage({ | 
| +          type: value == "privacy" ? "subscriptions.add" : | 
| +            "subscriptions.remove", | 
| +          url: acceptableAdsPrivacyUrl | 
| +        }); | 
| +        ext.backgroundPage.sendMessage({ | 
| +          type: value == "ads" ? "subscriptions.add" : "subscriptions.remove", | 
| +          url: acceptableAdsUrl | 
| +        }); | 
| +        break; | 
| case "switch-tab": | 
| let tabId = findParentData(element, "tab", false); | 
| switchTab(tabId); | 
| @@ -788,17 +825,6 @@ | 
| function onDOMLoaded() | 
| { | 
| populateLists(); | 
| -    function onFindLanguageKeyUp() | 
| -    { | 
| -      let searchStyle = E("search-style"); | 
| -      if (!this.value) | 
| -        searchStyle.innerHTML = ""; | 
| -      else | 
| -      { | 
| -        searchStyle.innerHTML = "#all-lang-table li:not([data-search*=\"" + | 
| -          this.value.toLowerCase() + "\"]) { display: none; }"; | 
| -      } | 
| -    } | 
|  | 
| // Initialize navigation sidebar | 
| ext.backgroundPage.sendMessage({ | 
| @@ -820,9 +846,6 @@ | 
| // Initialize interactive UI elements | 
| document.body.addEventListener("click", onClick, false); | 
| document.body.addEventListener("keyup", onKeyUp, false); | 
| -    let placeholderValue = getMessage("options_dialog_language_find"); | 
| -    E("find-language").setAttribute("placeholder", placeholderValue); | 
| -    E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false); | 
| let exampleValue = getMessage("options_whitelist_placeholder_example", | 
| ["www.example.com"]); | 
| E("whitelisting-textbox").setAttribute("placeholder", exampleValue); | 
| @@ -831,6 +854,11 @@ | 
| E("whitelisting-add-button").disabled = !e.target.value; | 
| }, false); | 
|  | 
| +    getDocLink("acceptable_ads_criteria", (link) => | 
| +    { | 
| +      setLinks("enable-aa-description", link); | 
| +    }); | 
| + | 
| // Advanced tab | 
| let customize = document.querySelectorAll("#customize li[data-pref]"); | 
| customize = Array.prototype.map.call(customize, (checkbox) => | 
| @@ -969,6 +997,29 @@ | 
| focusedBeforeDialog.focus(); | 
| } | 
|  | 
| +  function setAcceptableAds() | 
| +  { | 
| +    let option = "none"; | 
| +    document.forms["acceptable-ads"].classList.remove("show-dnt-notification"); | 
| +    if (acceptableAdsUrl in subscriptionsMap) | 
| +    { | 
| +      option = "ads"; | 
| +    } | 
| +    else if (acceptableAdsPrivacyUrl in subscriptionsMap) | 
| +    { | 
| +      option = "privacy"; | 
| + | 
| +      if (!navigator.doNotTrack) | 
| +        document.forms["acceptable-ads"].classList.add("show-dnt-notification"); | 
| +    } | 
| +    document.forms["acceptable-ads"]["acceptable-ads"].value = option; | 
| +  } | 
| + | 
| +  function isAcceptableAds(url) | 
| +  { | 
| +    return url == acceptableAdsUrl || url == acceptableAdsPrivacyUrl; | 
| +  } | 
| + | 
| function populateLists() | 
| { | 
| subscriptionsMap = Object.create(null); | 
| @@ -1014,15 +1065,24 @@ | 
| disabled: true | 
| }); | 
|  | 
| -      // Load user subscriptions | 
| ext.backgroundPage.sendMessage({ | 
| -        type: "subscriptions.get", | 
| -        downloadable: true | 
| +        type: "prefs.get", | 
| +        key: "subscriptions_exceptionsurl_privacy" | 
| }, | 
| -      (subscriptions) => | 
| +      (urlPrivacy) => | 
| { | 
| -        for (let subscription of subscriptions) | 
| -          onSubscriptionMessage("added", subscription); | 
| +        acceptableAdsPrivacyUrl = urlPrivacy; | 
| + | 
| +        // Load user subscriptions | 
| +        ext.backgroundPage.sendMessage({ | 
| +          type: "subscriptions.get", | 
| +          downloadable: true | 
| +        }, | 
| +        (subscriptions) => | 
| +        { | 
| +          for (let subscription of subscriptions) | 
| +            onSubscriptionMessage("added", subscription); | 
| +        }); | 
| }); | 
| }); | 
| } | 
| @@ -1130,18 +1190,28 @@ | 
| else | 
| addSubscription(subscription); | 
|  | 
| +        if (isAcceptableAds(subscription.url)) | 
| +          setAcceptableAds(); | 
| + | 
| collections.filterLists.addItem(subscription); | 
| break; | 
| case "removed": | 
| -        if (subscription.url == acceptableAdsUrl || subscription.recommended) | 
| +        if (subscription.recommended) | 
| { | 
| subscription.disabled = true; | 
| onSubscriptionMessage("disabled", subscription); | 
| } | 
| else | 
| { | 
| -          collections.custom.removeItem(subscription); | 
| delete subscriptionsMap[subscription.url]; | 
| +          if (isAcceptableAds(subscription.url)) | 
| +          { | 
| +            setAcceptableAds(); | 
| +          } | 
| +          else | 
| +          { | 
| +            collections.custom.removeItem(subscription); | 
| +          } | 
| } | 
| collections.filterLists.removeItem(subscription); | 
| break; | 
| @@ -1232,20 +1302,6 @@ | 
| checkShareResource(sharedResource, onResult); | 
| } | 
|  | 
| -  function getMessages(id) | 
| -  { | 
| -    let messages = []; | 
| -    for (let i = 1; true; i++) | 
| -    { | 
| -      let message = ext.i18n.getMessage(id + "_" + i); | 
| -      if (!message) | 
| -        break; | 
| - | 
| -      messages.push(message); | 
| -    } | 
| -    return messages; | 
| -  } | 
| - | 
| function updateTooltips() | 
| { | 
| let anchors = document.querySelectorAll(":not(.tooltip) > [data-tooltip]"); | 
| @@ -1258,52 +1314,12 @@ | 
| anchor.parentNode.replaceChild(wrapper, anchor); | 
| wrapper.appendChild(anchor); | 
|  | 
| -      let topTexts = getMessages(id); | 
| -      let bottomTexts = getMessages(id + "_notes"); | 
| - | 
| -      // We have to use native tooltips to avoid issues when attaching a tooltip | 
| -      // to an element in a scrollable list or otherwise it might get cut off | 
| -      if (anchor.hasAttribute("data-tooltip-native")) | 
| -      { | 
| -        let title = topTexts.concat(bottomTexts).join("\n\n"); | 
| -        anchor.setAttribute("title", title); | 
| -        continue; | 
| -      } | 
| - | 
| let tooltip = document.createElement("div"); | 
| tooltip.setAttribute("role", "tooltip"); | 
|  | 
| -      let flip = anchor.getAttribute("data-tooltip-flip"); | 
| -      if (flip) | 
| -        tooltip.className = "flip-" + flip; | 
| - | 
| -      let imageSource = anchor.getAttribute("data-tooltip-image"); | 
| -      if (imageSource) | 
| -      { | 
| -        let image = document.createElement("img"); | 
| -        image.src = imageSource; | 
| -        image.alt = ""; | 
| -        tooltip.appendChild(image); | 
| -      } | 
| - | 
| -      for (let topText of topTexts) | 
| -      { | 
| -        let paragraph = document.createElement("p"); | 
| -        paragraph.innerHTML = topText; | 
| -        tooltip.appendChild(paragraph); | 
| -      } | 
| -      if (bottomTexts.length > 0) | 
| -      { | 
| -        let notes = document.createElement("div"); | 
| -        notes.className = "notes"; | 
| -        for (let bottomText of bottomTexts) | 
| -        { | 
| -          let paragraph = document.createElement("p"); | 
| -          paragraph.innerHTML = bottomText; | 
| -          notes.appendChild(paragraph); | 
| -        } | 
| -        tooltip.appendChild(notes); | 
| -      } | 
| +      let paragraph = document.createElement("p"); | 
| +      paragraph.textContent = getMessage(id); | 
| +      tooltip.appendChild(paragraph); | 
|  | 
| wrapper.appendChild(tooltip); | 
| } | 
|  |