| LEFT | RIGHT | 
|    1 /* |    1 /* | 
|    2  * This file is part of Adblock Plus <http://adblockplus.org/>, |    2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
|    3  * Copyright (C) 2006-2015 Eyeo GmbH |    3  * Copyright (C) 2006-2015 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 "use strict"; |   18 "use strict"; | 
|   19  |   19  | 
|   20 (function() |   20 (function() | 
|   21 { |   21 { | 
|   22   var optionSubscriptions = {}; |   22   var subscriptionsMap = Object.create(null); | 
|   23    |   23   var recommendationsMap = Object.create(null); | 
 |   24   var filtersMap = Object.create(null); | 
 |   25   var collections = Object.create(null); | 
 |   26  | 
 |   27   function Collection(details) | 
 |   28   { | 
 |   29     this.details = details; | 
 |   30     this.items = []; | 
 |   31   } | 
 |   32  | 
 |   33   Collection.prototype.addItems = function()  | 
 |   34   { | 
 |   35     var length = Array.prototype.push.apply(this.items, arguments); | 
 |   36     if (length == 0) | 
 |   37       return; | 
 |   38  | 
 |   39     this.items.sort(function(a, b) | 
 |   40     { | 
 |   41       var aValue = (a.title || a.url || a.text).toLowerCase(); | 
 |   42       var bValue = (b.title || b.url || a.text).toLowerCase(); | 
 |   43       return aValue.localeCompare(bValue); | 
 |   44     }); | 
 |   45  | 
 |   46     for (var j = 0; j < this.details.length; j++)  | 
 |   47     { | 
 |   48       var table = E(this.details[j].id); | 
 |   49       var template = table.querySelector("template"); | 
 |   50       for (var i = 0; i < arguments.length; i++)  | 
 |   51       { | 
 |   52         var item = arguments[i]; | 
 |   53         var text = item.title || item.url || item.text; | 
 |   54         var listItem = document.createElement("li"); | 
 |   55         listItem.appendChild(document.importNode(template.content, true)); | 
 |   56         listItem.dataset.access = item.url || item.text; | 
 |   57         listItem.querySelector(".display").textContent = text; | 
 |   58         if (text) | 
 |   59           listItem.dataset.search = text.toLowerCase(); | 
 |   60  | 
 |   61         var control = listItem.querySelector(".control"); | 
 |   62         if (control) | 
 |   63         { | 
 |   64           control.addEventListener("click", this.details[j].onClick, false); | 
 |   65           control.checked = item.disabled == false; | 
 |   66         } | 
 |   67  | 
 |   68         if (table.hasChildNodes()) | 
 |   69           table.insertBefore(listItem, table.childNodes[this.items.indexOf(item)
     ]); | 
 |   70         else | 
 |   71           table.appendChild(listItem); | 
 |   72       } | 
 |   73     } | 
 |   74     return length; | 
 |   75   }; | 
 |   76  | 
 |   77   Collection.prototype.removeItem = function(item) | 
 |   78   { | 
 |   79     var index = this.items.indexOf(item); | 
 |   80     if (index == -1) | 
 |   81       return; | 
 |   82  | 
 |   83     this.items.splice(index, 1); | 
 |   84     var access = (item.url || item.text).replace(/'/g, "\\'"); | 
 |   85     for (var i = 0; i < this.details.length; i++) | 
 |   86     { | 
 |   87       var table = E(this.details[i].id); | 
 |   88       var element = table.querySelector("[data-access='" + access + "']"); | 
 |   89       element.parentElement.removeChild(element); | 
 |   90     } | 
 |   91   }; | 
 |   92  | 
 |   93   Collection.prototype.clearAll = function() | 
 |   94   { | 
 |   95     for (var i = 0; i < this.details.length; i++) | 
 |   96     { | 
 |   97       var table = E(this.details[i].id); | 
 |   98       var template = table.querySelector("template"); | 
 |   99       table.innerHTML = ""; | 
 |  100       table.appendChild(template); | 
 |  101     } | 
 |  102     this.items.length = 0; | 
 |  103   }; | 
 |  104  | 
 |  105   function onToggleSubscriptionClick(e) | 
 |  106   { | 
 |  107     e.preventDefault(); | 
 |  108     var subscriptionUrl = e.target.parentNode.dataset.access; | 
 |  109     if (!e.target.checked) | 
 |  110       removeSubscription(subscriptionUrl); | 
 |  111     else | 
 |  112       addEnableSubscription(subscriptionUrl); | 
 |  113   } | 
 |  114  | 
 |  115   function onAddLanguageSubscriptionClick(e) | 
 |  116   { | 
 |  117     e.preventDefault(); | 
 |  118     var url = this.parentNode.dataset.access; | 
 |  119     addEnableSubscription(url); | 
 |  120   } | 
 |  121  | 
 |  122   function onRemoveFilterClick() | 
 |  123   { | 
 |  124     var filter = this.parentNode.dataset.access; | 
 |  125     removeFilter(filter); | 
 |  126   } | 
 |  127  | 
 |  128   collections.popular = new Collection( | 
 |  129   [ | 
 |  130     { | 
 |  131       id: "recommend-list-table", | 
 |  132       onClick: onToggleSubscriptionClick | 
 |  133     } | 
 |  134   ]); | 
 |  135   collections.langs = new Collection( | 
 |  136   [ | 
 |  137     { | 
 |  138       id: "blocking-languages-table", | 
 |  139       onClick: onToggleSubscriptionClick | 
 |  140     }, | 
 |  141     { | 
 |  142       id: "blocking-languages-dialog-table" | 
 |  143     } | 
 |  144   ]); | 
 |  145   collections.allLangs = new Collection( | 
 |  146   [ | 
 |  147     { | 
 |  148       id: "all-lang-table",  | 
 |  149       onClick: onAddLanguageSubscriptionClick | 
 |  150     } | 
 |  151   ]); | 
 |  152   collections.acceptableAds = new Collection( | 
 |  153   [ | 
 |  154     { | 
 |  155       id: "acceptableads-table",  | 
 |  156       onClick: onToggleSubscriptionClick | 
 |  157     } | 
 |  158   ]); | 
 |  159   collections.custom = new Collection( | 
 |  160   [ | 
 |  161     { | 
 |  162       id: "custom-list-table",  | 
 |  163       onClick: onToggleSubscriptionClick | 
 |  164     } | 
 |  165   ]); | 
 |  166   collections.whitelist = new Collection( | 
 |  167   [ | 
 |  168     { | 
 |  169       id: "whitelisting-table",  | 
 |  170       onClick: onRemoveFilterClick | 
 |  171     } | 
 |  172   ]); | 
 |  173  | 
 |  174   function updateSubscription(subscription) | 
 |  175   { | 
 |  176     var subscriptionUrl = subscription.url; | 
 |  177     var knownSubscription = subscriptionsMap[subscriptionUrl]; | 
 |  178     if (knownSubscription) | 
 |  179       knownSubscription.disabled = subscription.disabled; | 
 |  180     else | 
 |  181     { | 
 |  182       getAcceptableAdsURL(function(acceptableAdsUrl) | 
 |  183       { | 
 |  184         function onObjectChanged() | 
 |  185         { | 
 |  186           var access = (subscriptionUrl || subscription.text).replace(/'/g, "\\'
     "); | 
 |  187           var elements = document.querySelectorAll("[data-access='" + access + "
     ']"); | 
 |  188           for (var i = 0; i < elements.length; i++) | 
 |  189           { | 
 |  190             var element = elements[i]; | 
 |  191             var control = element.querySelector(".control"); | 
 |  192             if (control.localName == "input") | 
 |  193               control.checked = subscription.disabled == false; | 
 |  194             if (subscriptionUrl in recommendationsMap) | 
 |  195             { | 
 |  196               var recommendation = recommendationsMap[subscriptionUrl]; | 
 |  197               if (recommendation.isAdsType) | 
 |  198               { | 
 |  199                 if (subscription.disabled == false) | 
 |  200                 { | 
 |  201                   collections.allLangs.removeItem(subscription); | 
 |  202                   collections.langs.addItems(subscription); | 
 |  203                 } | 
 |  204                 else | 
 |  205                 { | 
 |  206                   collections.allLangs.addItems(subscription); | 
 |  207                   collections.langs.removeItem(subscription); | 
 |  208                 } | 
 |  209               } | 
 |  210             } | 
 |  211           } | 
 |  212         } | 
 |  213  | 
 |  214         if (!Object.observe) | 
 |  215         { | 
 |  216           // Currently only "disabled" property of subscription used for observa
     tion | 
 |  217           // but with Advanced tab implementation we should also add more proper
     ties. | 
 |  218           ["disabled"].forEach(function(property) | 
 |  219           { | 
 |  220             subscription["$" + property] = subscription[property]; | 
 |  221             Object.defineProperty(subscription, property, | 
 |  222             { | 
 |  223               get: function() | 
 |  224               { | 
 |  225                 return this["$" + property]; | 
 |  226               }, | 
 |  227               set: function(value) | 
 |  228               { | 
 |  229                 this["$" + property] = value; | 
 |  230                 onObjectChanged(); | 
 |  231               } | 
 |  232             }); | 
 |  233           }); | 
 |  234         } | 
 |  235         else | 
 |  236         { | 
 |  237           Object.observe(subscription, onObjectChanged); | 
 |  238         } | 
 |  239  | 
 |  240         var collection = null; | 
 |  241         if (subscriptionUrl in recommendationsMap) | 
 |  242         { | 
 |  243           var recommendation = recommendationsMap[subscriptionUrl]; | 
 |  244           if (recommendation.isPopular) | 
 |  245             collection = collections.popular; | 
 |  246           else if (recommendation.isAdsType && subscription.disabled == false) | 
 |  247             collection = collections.langs; | 
 |  248           else | 
 |  249             collection = collections.allLangs; | 
 |  250         } | 
 |  251         else if (subscriptionUrl == acceptableAdsUrl) | 
 |  252           collection = collections.acceptableAds; | 
 |  253         else | 
 |  254           collection = collections.custom; | 
 |  255  | 
 |  256         collection.addItems(subscription); | 
 |  257         subscriptionsMap[subscriptionUrl] = subscription; | 
 |  258       }); | 
 |  259     } | 
 |  260   } | 
 |  261  | 
 |  262   function updateFilter(filter) | 
 |  263   { | 
 |  264     var match = filter.text.match(/^@@\|\|([^\/:]+)\^\$document$/); | 
 |  265     if (match && !filtersMap[filter.text]) | 
 |  266     { | 
 |  267       filter.title = match[1]; | 
 |  268       collections.whitelist.addItems(filter); | 
 |  269       filtersMap[filter.text] = filter | 
 |  270     } | 
 |  271     else | 
 |  272     { | 
 |  273       // TODO: add `filters[i].text` to list of custom filters | 
 |  274     } | 
 |  275   } | 
 |  276  | 
 |  277   function loadRecommendations() | 
 |  278   { | 
 |  279     var request = new XMLHttpRequest(); | 
 |  280     request.open("GET", "subscriptions.xml", false); | 
 |  281     request.addEventListener("load", function() | 
 |  282     { | 
 |  283       var list = document.getElementById("subscriptionSelector"); | 
 |  284       var docElem = request.responseXML.documentElement; | 
 |  285       var elements = docElem.getElementsByTagName("subscription"); | 
 |  286       for (var i = 0; i < elements.length; i++) | 
 |  287       { | 
 |  288         var element = elements[i]; | 
 |  289         var subscription = Object.create(null); | 
 |  290         subscription.title = element.getAttribute("title"); | 
 |  291         subscription.url = element.getAttribute("url"); | 
 |  292         subscription.disabled = null; | 
 |  293         subscription.downloadStatus = null; | 
 |  294         subscription.homepage = null; | 
 |  295         subscription.lastSuccess = null; | 
 |  296         var recommendation = Object.create(null); | 
 |  297         recommendation.isAdsType = false; | 
 |  298         recommendation.isPopular = false; | 
 |  299         var prefix = element.getAttribute("prefixes"); | 
 |  300         if (prefix) | 
 |  301         { | 
 |  302           var prefix = element.getAttribute("prefixes").replace(/,/g, "_"); | 
 |  303           subscription.title = ext.i18n.getMessage("options_language_" + prefix)
     ; | 
 |  304           recommendation.isAdsType = true; | 
 |  305         } | 
 |  306         else | 
 |  307           subscription.title = element.getAttribute("specialization"); | 
 |  308          | 
 |  309         if (element.getAttribute("popular")) | 
 |  310           recommendation.isPopular = true; | 
 |  311  | 
 |  312         recommendationsMap[subscription.url] = recommendation; | 
 |  313         updateSubscription(subscription); | 
 |  314       } | 
 |  315     }, false); | 
 |  316     request.send(null); | 
 |  317   } | 
 |  318  | 
|   24   function onDOMLoaded() |  319   function onDOMLoaded() | 
|   25   { |  320   { | 
|   26     initTabs(); |  321     var recommendationTemplate = document.querySelector("#recommend-list-table t
     emplate"); | 
 |  322     var popularText = ext.i18n.getMessage("options_popular"); | 
 |  323     recommendationTemplate.content.querySelector(".popular").textContent = popul
     arText; | 
 |  324     var languagesTemplate = document.querySelector("#all-lang-table template"); | 
 |  325     var buttonText = ext.i18n.getMessage("options_button_add"); | 
 |  326     languagesTemplate.content.querySelector(".button-add span").textContent = bu
     ttonText; | 
 |  327  | 
|   27     updateShareLink(); |  328     updateShareLink(); | 
|   28     populateLists(); |  329     populateLists(); | 
|   29  |  330  | 
|   30     E("add-blocking-list").addEventListener("click", Modal.open, false); |  331     var tabList = document.querySelectorAll("#main-navigation-tabs li"); | 
|   31     E("add-website-language").addEventListener("click", Modal.open, false); |  332     for (var i = 0; i < tabList.length; i++) | 
|   32     E("modal-close").addEventListener("click", Modal.close, false); |  333     { | 
|   33     E("whitelisting-add-icon").addEventListener("click", whitelistDomainBtnClick
     , false); |  334       tabList[i].addEventListener("click", function(e) | 
|   34     E("whitelisting-add-btn").addEventListener("click", whitelistDomainBtnClick,
      false); |  335       { | 
|   35     E("whitelisting-enter-icon").addEventListener("click", whitelistDomainBtnCli
     ck, false); |  336         document.body.dataset.tab = e.currentTarget.dataset.show; | 
|   36     E("whitelisting-textbox").addEventListener("keypress", function(e) { |  337       }, false); | 
|   37       if (e.keyCode == 13) |  338     } | 
|   38         whitelistDomainBtnClick(); |  339  | 
 |  340     function onFindLanguageKeyUp() | 
 |  341     { | 
 |  342       var searchStyle = E("search-style"); | 
 |  343       if (!this.value) | 
 |  344         searchStyle.innerHTML = ""; | 
 |  345       else | 
 |  346         searchStyle.innerHTML = "#all-lang-table li:not([data-search*=\"" + this
     .value.toLowerCase() + "\"]) { display: none; }"; | 
 |  347     } | 
 |  348  | 
 |  349     // Update version number in navigation sidebar | 
 |  350     ext.backgroundPage.sendMessage( | 
 |  351     { | 
 |  352       method: "app.get", | 
 |  353       what: "addonVersion" | 
 |  354     }, | 
 |  355     function(addonVersion) | 
 |  356     { | 
 |  357       E("abp-version").textContent = addonVersion; | 
 |  358     }); | 
 |  359      | 
 |  360     var placeholderValue  = ext.i18n.getMessage("options_dialog_language_find"); | 
 |  361     E("find-language").setAttribute("placeholder", placeholderValue); | 
 |  362     E("add-blocking-list").addEventListener("click", function() | 
 |  363     { | 
 |  364       openDialog("customlist"); | 
|   39     }, false); |  365     }, false); | 
|   40     E("whitelisting-cancel-btn").addEventListener("click", function(){ |  366     E("add-website-language").addEventListener("click", function() | 
|   41       E("whitelisting-textbox").value = ""; |  367     { | 
 |  368       openDialog("language"); | 
|   42     }, false); |  369     }, false); | 
|   43     E("allow-whitelist-cb").addEventListener("click", toggleAcceptableAds, false
     ); |  370     E("dialog-close").addEventListener("click", function() | 
|   44     E("import-blockingList-btn").addEventListener("click", importListBtnCLick, f
     alse); |  371     { | 
|   45     E("edit-ownBlockingList-btn").addEventListener("click", editOwnRulsBtnClick,
      false); |  372       delete document.body.dataset.dialog; | 
|   46     E("find-language").addEventListener("keyup", searchLanguage, false); |  373     }, false); | 
|   47   } |  374     E("edit-ownBlockingList-button").addEventListener("click", editCustomFilters
     , false); | 
|   48    |  375     E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false); | 
|   49   function initTabs() |  376     E("whitelisting").addEventListener("click", function(e) | 
|   50   { |  377     { | 
|   51     var showContent = function(tab) |  378       var id = e.target.id; | 
|   52     { |  379       if (id == "whitelisting-add-icon" || id == "whitelisting-enter-icon") | 
|   53       var tab = tab.querySelector(".tabs li.active"); |  380         addWhitelistedDomain(); | 
|   54       if (tab.dataset.show) |  381       else if (id == "whitelisting-cancel-button") | 
|   55         E(tab.dataset.show).style.display = "block"; |  382         E("whitelisting-textbox").value = ""; | 
|   56     }; |  383     }, false); | 
|   57     var optionList = document.querySelectorAll('.tabs li[data-show]'); |  384     E("whitelisting-add-button").addEventListener("click", addWhitelistedDomain,
      false); | 
|   58     for (var i = 0; i < optionList.length; ++i) |  385     E("whitelisting-textbox").addEventListener("keypress", function(e) | 
|   59     { |  386     { | 
|   60       optionList[i].addEventListener("click", function(ev) |  387       // e.keyCode has been deprecated so we attempt to use e.key | 
|   61       { |  388       // keyCode "13" corresponds to "Enter" | 
|   62         var tab = this.parentNode.querySelector(".active"); |  389       if ((e.key && e.key == "Enter") || (!e.key && e.keyCode == 13)) | 
|   63         tab.classList.remove("active"); |  390         addWhitelistedDomain(); | 
|   64         this.classList.add("active"); |  391     }, false); | 
|   65         E(tab.dataset.show).style.display = "none";; |  392     E("import-blockingList-button").addEventListener("click", function() | 
|   66         showContent(this.parentNode); |  393     { | 
|   67       }, false); |  394       var url = E("blockingList-textbox").value; | 
|   68     } |  395       addEnableSubscription(url); | 
|   69     showContent(E("main-navigation-tabs")); |  396       delete document.body.dataset.dialog; | 
|   70     showContent(E("blocking-list-tabs")); |  397     }, false); | 
|   71   } |  398   } | 
|   72    |  399  | 
|   73   var Modal = |  400   function openDialog(name) | 
|   74   { |  401   { | 
|   75     open: function (content) |  402     document.body.dataset.dialog = name; | 
|   76     { |  403   } | 
|   77       var modal = E("modal"); |  404  | 
|   78       var content = E(this && this.dataset ? this.dataset.show : content); |  | 
|   79       content.style.display = "block"; |  | 
|   80       modal.style.visibility = "visible"; |  | 
|   81       E("modal-background").style.display = "block"; |  | 
|   82       if (content.dataset.title) |  | 
|   83         E("modal-title").innerHTML = ext.i18n.getMessage(content.dataset.title); |  | 
|   84       modal.style.marginTop = -(modal.clientHeight/2)+"px"; |  | 
|   85     }, |  | 
|   86     close: function () |  | 
|   87     { |  | 
|   88       var contents = E("modal-content").childNodes; |  | 
|   89       for (var i = 0; i < contents.length; ++i) |  | 
|   90       { |  | 
|   91         if (contents[i].style) |  | 
|   92           contents[i].style.display = "none"; |  | 
|   93       } |  | 
|   94       E("modal-background").style.display = "none"; |  | 
|   95       E("modal").style.visibility = "hidden"; |  | 
|   96     } |  | 
|   97   } |  | 
|   98    |  | 
|   99   function populateLists() |  405   function populateLists() | 
|  100   { |  406   { | 
|  101     ext.backgroundPage.sendMessage({ |  407     subscriptionsMap = Object.create(null); | 
 |  408     filtersMap = Object.create(null); | 
 |  409     recommendationsMap = Object.create(null); | 
 |  410  | 
 |  411     // Empty collections and lists | 
 |  412     for (var property in collections) | 
 |  413       collections[property].clearAll(); | 
 |  414  | 
 |  415     ext.backgroundPage.sendMessage( | 
 |  416     { | 
|  102       type: "subscriptions.get", |  417       type: "subscriptions.get", | 
|  103       special: true |  418       special: true | 
|  104     }, function(subscriptions) |  419     }, | 
|  105     { |  420     function(subscriptions) | 
 |  421     { | 
 |  422       // Load filters | 
|  106       for (var i = 0; i < subscriptions.length; i++) |  423       for (var i = 0; i < subscriptions.length; i++) | 
|  107       { |  424       { | 
|  108         ext.backgroundPage.sendMessage({ |  425         ext.backgroundPage.sendMessage( | 
 |  426         { | 
|  109           type: "filters.get", |  427           type: "filters.get", | 
|  110           subscriptionUrl: subscriptions[i].url |  428           subscriptionUrl: subscriptions[i].url | 
|  111         }, function(filters) |  429         },  | 
|  112         { |  430         function(filters) | 
|  113           var whitelistArray = []; |  431         { | 
|  114           for (var i = 0; i < filters.length; i++) |  432           for (var i = 0; i < filters.length; i++) | 
|  115           { |  433             updateFilter(filters[i]); | 
|  116             var match = filters[i].text.match(/^@@\|\|([^\/:]+)\^\$document$/); |  | 
|  117             if (match[1]) |  | 
|  118             { |  | 
|  119               whitelistArray.push(match[1]); |  | 
|  120             } |  | 
|  121             else |  | 
|  122             { |  | 
|  123               // TODO: add `filters[i].text` to list of custom filters |  | 
|  124             } |  | 
|  125           } |  | 
|  126            |  | 
|  127           if (whitelistArray.length > 0) |  | 
|  128           { |  | 
|  129             whitelistArray.sort(); |  | 
|  130             for (var i = 0; i < whitelistArray.length; i++) |  | 
|  131             { |  | 
|  132               var domain = whitelistArray[i]; |  | 
|  133               E("whitelisting-table").appendChild(createWhitelistElem(domain)); |  | 
|  134             } |  | 
|  135           } |  | 
|  136         }); |  434         }); | 
|  137       } |  435       } | 
|  138     }); |  436     }); | 
|  139      |  437     loadRecommendations(); | 
|  140     loadRecommendations(function(recommends) |  438     getAcceptableAdsURL(function(acceptableAdsUrl) | 
|  141     { |  439     { | 
|  142       ext.backgroundPage.sendMessage({ |  440       var subscription = Object.create(null); | 
 |  441       subscription.url = acceptableAdsUrl; | 
 |  442       subscription.disabled = true; | 
 |  443       subscription.title = ext.i18n.getMessage("options_acceptableAds_descriptio
     n"); | 
 |  444       updateSubscription(subscription); | 
 |  445  | 
 |  446       // Load user subscriptions | 
 |  447       ext.backgroundPage.sendMessage( | 
 |  448       { | 
|  143         type: "subscriptions.get", |  449         type: "subscriptions.get", | 
|  144         downloadable: true |  450         downloadable: true | 
|  145       }, function(subscriptions) |  451       },  | 
|  146       { |  452       function(subscriptions) | 
|  147         getAcceptableAdsURL(function(exceptionUrl) |  453       { | 
|  148         { |  454         for (var i = 0; i < subscriptions.length; i++) | 
|  149           for (var i = 0; i < subscriptions.length; i++) |  455           onSubscriptionMessage("added", subscriptions[i]); | 
|  150           { |  | 
|  151             if (subscriptions[i].url == exceptionUrl) |  | 
|  152             { |  | 
|  153               E("allow-whitelist-cb").previousSibling.checked = !subscriptions[i
     ].disabled; |  | 
|  154               continue; |  | 
|  155             } |  | 
|  156   |  | 
|  157             var subscription = recommends[subscriptions[i].url]; |  | 
|  158             if (!subscription) |  | 
|  159               recommends[subscriptions[i].url] = subscriptions[i]; |  | 
|  160             else |  | 
|  161             { |  | 
|  162               subscription.disabled = subscriptions[i].disabled; |  | 
|  163               if (subscription.type == "ads") |  | 
|  164                 subscription.isAdded = true; |  | 
|  165             } |  | 
|  166           } |  | 
|  167           for (var key in recommends) |  | 
|  168             addOptionItem(recommends[key]); |  | 
|  169         }); |  | 
|  170       }); |  456       }); | 
|  171     }); |  457     }); | 
|  172   } |  458   } | 
|  173    |  459  | 
|  174   function loadRecommendations(callback) |  460   function addWhitelistedDomain() | 
|  175   { |  461   { | 
|  176     var recommendations = {}; |  462     var domain = E("whitelisting-textbox"); | 
|  177     var request = new XMLHttpRequest(); |  463     if (domain.value) | 
|  178     request.open("GET", "subscriptions.xml"); |  464     { | 
|  179     request.onload = function() |  465       ext.backgroundPage.sendMessage( | 
|  180     { |  466       { | 
|  181       var list = document.getElementById("subscriptionSelector"); |  467         type: "filters.add", | 
|  182       var elements = request.responseXML.documentElement.getElementsByTagName("s
     ubscription"); |  468         text: "@@||" + domain.value.toLowerCase() + "^$document" | 
|  183       for (var i = 0; i < elements.length; i++) |  | 
|  184       { |  | 
|  185         var element = elements[i]; |  | 
|  186         var subscription = {}; |  | 
|  187         subscription.title = element.getAttribute("title"); |  | 
|  188         subscription.url = element.getAttribute("url"); |  | 
|  189         subscription.disabled = true; |  | 
|  190         var prefix = element.getAttribute("prefixes"); |  | 
|  191         if (prefix) |  | 
|  192         { |  | 
|  193           subscription.prefixes = element.getAttribute("prefixes"); |  | 
|  194           subscription.type = "ads"; |  | 
|  195           subscription.display = ext.i18n.getMessage("options_language_"+subscri
     ption.prefixes.replace(/,/g, '_')); |  | 
|  196         } |  | 
|  197         else |  | 
|  198           subscription.display = element.getAttribute("specialization"); |  | 
|  199          |  | 
|  200         var popular = element.getAttribute("popular"); |  | 
|  201         if (popular) |  | 
|  202           subscription.popular = element.getAttribute("popular"); |  | 
|  203          |  | 
|  204         recommendations[subscription.url] = subscription; |  | 
|  205       } |  | 
|  206       optionSubscriptions = recommendations; |  | 
|  207       callback(recommendations); |  | 
|  208     } |  | 
|  209     request.send(); |  | 
|  210   } |  | 
|  211    |  | 
|  212   function searchLanguage() |  | 
|  213   { |  | 
|  214     var searchVal = this.value; |  | 
|  215     var items = E("all-lang-table").childNodes; |  | 
|  216     for (i = 0; i < items.length; ++i) |  | 
|  217     { |  | 
|  218       var item = items[i]; |  | 
|  219       var language = item.getElementsByTagName("span")[1].innerHTML; |  | 
|  220       if (language.toLowerCase().indexOf(searchVal.toLowerCase()) > -1) |  | 
|  221         item.style.display = "block"; |  | 
|  222       else |  | 
|  223         item.style.display = "none"; |  | 
|  224     } |  | 
|  225   } |  | 
|  226    |  | 
|  227   function addOptionItem(subscription) |  | 
|  228   { |  | 
|  229     var display = subscription.display ? subscription.display : subscription.tit
     le; |  | 
|  230     var getPossition = function(elements, subscription) |  | 
|  231     { |  | 
|  232       var localArray = []; |  | 
|  233       for (var i = 0; i < elements.length; i++) |  | 
|  234       { |  | 
|  235         var elem = elements[i]; |  | 
|  236         localArray.push(elem); |  | 
|  237       } |  | 
|  238        |  | 
|  239       localArray.push(subscription); |  | 
|  240       return localArray.sort(function(a, b) { |  | 
|  241         var aPopular = a.getElementsByClassName("popular").length > 0; |  | 
|  242         var bPopular = b.getElementsByClassName("popular").length > 0; |  | 
|  243         if(aPopular == bPopular) |  | 
|  244         { |  | 
|  245           var aValue = a.getElementsByClassName("display")[0].innerHTML.toLowerC
     ase(); |  | 
|  246           var bValue = b.getElementsByClassName("display")[0].innerHTML.toLowerC
     ase(); |  | 
|  247           if (aValue < bValue) |  | 
|  248             return -1; |  | 
|  249           if (aValue > bValue) |  | 
|  250             return 1; |  | 
|  251           return 0; |  | 
|  252         } |  | 
|  253         if (aPopular == "true") |  | 
|  254           return 1; |  | 
|  255         else |  | 
|  256           return -1; |  | 
|  257       }).indexOf(subscription); |  | 
|  258     }; |  | 
|  259      |  | 
|  260     var checkBoxClick = function() |  | 
|  261     { |  | 
|  262       toggleSubscription(subscription); |  | 
|  263     }; |  | 
|  264      |  | 
|  265     var appendToTable = function(table, elem) |  | 
|  266     { |  | 
|  267       var elements = table.getElementsByTagName("li"); |  | 
|  268       if (elements.length == 0) |  | 
|  269         table.appendChild(elem); |  | 
|  270       else |  | 
|  271       { |  | 
|  272         var possition = getPossition(elements, elem); |  | 
|  273         table.insertBefore(elem, table.childNodes[possition]); |  | 
|  274       } |  | 
|  275     }; |  | 
|  276      |  | 
|  277     if (subscription.type && subscription.type == "ads") |  | 
|  278     { |  | 
|  279       if (!subscription.isAdded) |  | 
|  280       { |  | 
|  281         var listElem = generateListElement(subscription, subscription.display, "
     add"); |  | 
|  282         listElem.dataset.url = subscription.url; |  | 
|  283         listElem._subscription = subscription; |  | 
|  284         listElem.getElementsByTagName("button")[0].addEventListener("click", fun
     ction() |  | 
|  285         { |  | 
|  286           addSubscription(this.dataset.url); |  | 
|  287         }.bind(listElem), false); |  | 
|  288         appendToTable(E("all-lang-table"), listElem); |  | 
|  289       } |  | 
|  290       else |  | 
|  291       { |  | 
|  292         var listElem = generateListElement(subscription, display, "checkbox"); |  | 
|  293         listElem.dataset.url = subscription.url; |  | 
|  294         listElem._subscription = subscription; |  | 
|  295         listElem.getElementsByTagName("label")[0].addEventListener("click", chec
     kBoxClick, false); |  | 
|  296         appendToTable(E("blocking-languages-table"), listElem); |  | 
|  297         var listElem = generateListElement(subscription, display); |  | 
|  298         listElem.dataset.url = subscription.url; |  | 
|  299         listElem._subscription = subscription; |  | 
|  300         appendToTable(E("blocking-languages-modal-table"), listElem); |  | 
|  301       } |  | 
|  302     } |  | 
|  303     else |  | 
|  304     { |  | 
|  305       var listElem = generateListElement(subscription, display, "checkbox"); |  | 
|  306       listElem.dataset.url = subscription.url; |  | 
|  307       listElem._subscription = subscription; |  | 
|  308       listElem.getElementsByTagName("label")[0].addEventListener("click", checkB
     oxClick, false); |  | 
|  309       appendToTable(E("further-list-table"), listElem); |  | 
|  310     } |  | 
|  311   } |  | 
|  312    |  | 
|  313   function addLanguageSubscription(subscription) |  | 
|  314   { |  | 
|  315     var optionSubscription = getOptionSubscription(subscription.url); |  | 
|  316     var elems = getElementsByUrl(subscription.url); |  | 
|  317     for (var i = 0; i < elems.length; i++) |  | 
|  318       elems[i].parentNode.removeChild(elems[i]); |  | 
|  319     optionSubscription.isAdded = true; |  | 
|  320     optionSubscription.disabled = false; |  | 
|  321     addOptionItem(optionSubscription); |  | 
|  322   } |  | 
|  323    |  | 
|  324   function createWhitelistElem(domain) |  | 
|  325   { |  | 
|  326     var listElem = generateListElement(null, domain, "delete"); |  | 
|  327     listElem.dataset.domain = domain; |  | 
|  328     listElem.getElementsByTagName("button")[0].addEventListener("click", removeW
     hitelistBtnClick.bind(listElem), false); |  | 
|  329     return listElem; |  | 
|  330   } |  | 
|  331    |  | 
|  332   function addFurtherList(subscription) |  | 
|  333   { |  | 
|  334     var optionSubscription = getOptionSubscription(subscription.url); |  | 
|  335     if (optionSubscription) |  | 
|  336     { |  | 
|  337       optionSubscription.disabled = false; |  | 
|  338       addOptionItem(optionSubscription); |  | 
|  339     } |  | 
|  340     else |  | 
|  341     { |  | 
|  342       optionSubscriptions[subscription.url] = subscription; |  | 
|  343       addOptionItem(subscription); |  | 
|  344     } |  | 
|  345   } |  | 
|  346    |  | 
|  347   function updateSubscriptionState(subscription, state) |  | 
|  348   { |  | 
|  349     var elem = getElementsByUrl(subscription.url); |  | 
|  350     if (elem.length > 0) |  | 
|  351     { |  | 
|  352       for (var i = 0; i < elem.length; i++) |  | 
|  353       { |  | 
|  354         var checkbox = elem[i].getElementsByTagName("input")[0]; |  | 
|  355         if (checkbox) |  | 
|  356           checkbox.checked = state; |  | 
|  357       } |  | 
|  358     } |  | 
|  359     else |  | 
|  360     { |  | 
|  361       getAcceptableAdsURL(function(exceptionUrl) |  | 
|  362       { |  | 
|  363         if (subscription.url == exceptionUrl) |  | 
|  364           E("allow-whitelist-cb").previousSibling.checked = !state; |  | 
|  365         else |  | 
|  366           addFurtherList(subscription); |  | 
|  367       }); |  469       }); | 
|  368     } |  470     } | 
|  369   } |  471  | 
|  370    |  472     domain.value = ""; | 
|  371   function getElementsByUrl(url) |  473   } | 
|  372   { |  474  | 
|  373     return document.querySelectorAll("[data-url='"+url+"']"); |  475   function editCustomFilters() | 
|  374   } |  476   { | 
|  375    |  477     //TODO: NYI | 
|  376   function generateListElement(subscription, text, type) |  478   } | 
|  377   { |  479  | 
|  378     var list = document.createElement("li"); |  | 
|  379     if (type == "checkbox") |  | 
|  380     { |  | 
|  381       var input = document.createElement("input"); |  | 
|  382       input.setAttribute("type", "checkbox"); |  | 
|  383       if (subscription.disabled == false) |  | 
|  384         input.checked = true; |  | 
|  385       list.appendChild(input); |  | 
|  386       var label = document.createElement("label"); |  | 
|  387       list.appendChild(label); |  | 
|  388     } |  | 
|  389     else if (type == "delete") |  | 
|  390     { |  | 
|  391       var button = document.createElement("button"); |  | 
|  392       button.setAttribute("class", "delete"); |  | 
|  393       list.appendChild(button); |  | 
|  394     } |  | 
|  395     else if (type == "add") |  | 
|  396     { |  | 
|  397       var button = document.createElement("button"); |  | 
|  398       button.setAttribute("class", "addbtn"); |  | 
|  399       var span = document.createElement("span"); |  | 
|  400       span.innerHTML = "+" + ext.i18n.getMessage("options_btn_add"); |  | 
|  401       button.appendChild(span); |  | 
|  402       list.appendChild(button); |  | 
|  403     } |  | 
|  404     var span = document.createElement("span"); |  | 
|  405     span.setAttribute("class", "display"); |  | 
|  406     span.innerHTML = text; |  | 
|  407     list.appendChild(span); |  | 
|  408      |  | 
|  409     if (subscription && subscription.popular == "true") |  | 
|  410     { |  | 
|  411       var popular = document.createElement("span"); |  | 
|  412       popular.setAttribute("class", "popular"); |  | 
|  413       popular.innerHTML = "popular"; |  | 
|  414       list.appendChild(popular); |  | 
|  415     } |  | 
|  416      |  | 
|  417     return list; |  | 
|  418   } |  | 
|  419    |  | 
|  420   function getOptionSubscription(url) |  | 
|  421   { |  | 
|  422     return optionSubscriptions[url]; |  | 
|  423   } |  | 
|  424  |  | 
|  425   function importListBtnCLick() |  | 
|  426   { |  | 
|  427     var url = E("blockingList-textbox").value; |  | 
|  428     addSubscription(url); |  | 
|  429     Modal.close(); |  | 
|  430   } |  | 
|  431    |  | 
|  432   function whitelistDomainBtnClick() |  | 
|  433   { |  | 
|  434     var domain = E("whitelisting-textbox").value; |  | 
|  435     if (domain) |  | 
|  436       addWhitelistedDomain(domain); |  | 
|  437   } |  | 
|  438    |  | 
|  439   function removeWhitelistBtnClick() |  | 
|  440   { |  | 
|  441     removeWhitelistedDomain(this.dataset.domain); |  | 
|  442   } |  | 
|  443    |  | 
|  444   function editOwnRulsBtnClick() |  | 
|  445   { |  | 
|  446      |  | 
|  447   } |  | 
|  448    |  | 
|  449   function showAddSubscriptionDialog(action, subscription) |  | 
|  450   { |  | 
|  451     E("blockingList-textbox").value = subscription.url; |  | 
|  452     Modal.open("further-blocking-modal"); |  | 
|  453   } |  | 
|  454    |  | 
|  455   function getAcceptableAdsURL(callback) |  480   function getAcceptableAdsURL(callback) | 
|  456   { |  481   { | 
|  457     ext.backgroundPage.sendMessage({ |  482     ext.backgroundPage.sendMessage( | 
 |  483     { | 
|  458       type: "prefs.get", |  484       type: "prefs.get", | 
|  459       key: "subscriptions_exceptionsurl" |  485       key: "subscriptions_exceptionsurl" | 
|  460     }, function(value) |  486     }, | 
 |  487     function(value) | 
|  461     { |  488     { | 
|  462       getAcceptableAdsURL = function(callback) |  489       getAcceptableAdsURL = function(callback) | 
|  463       { |  490       { | 
|  464         callback(value); |  491         callback(value); | 
|  465       } |  492       } | 
|  466       getAcceptableAdsURL(callback); |  493       getAcceptableAdsURL(callback); | 
|  467     }); |  494     }); | 
|  468   } |  495   } | 
|  469  |  496  | 
|  470   function toggleSubscription(subscription) |  497   function addEnableSubscription(url, title, homepage) | 
|  471   { |  498   { | 
|  472     ext.backgroundPage.sendMessage({ |  499     var messageType = null; | 
|  473       type: "subscriptions.toggle", |  500     var knownSubscription = subscriptionsMap[url]; | 
|  474       url: subscription.url, |  501     if (knownSubscription && knownSubscription.disabled == true) | 
|  475       title: subscription.title, |  502       messageType = "subscriptions.toggle" | 
|  476       homepage: subscription.homepage |  503     else | 
|  477     }); |  504       messageType = "subscriptions.add" | 
|  478   } |  505  | 
|  479  |  | 
|  480   function toggleAcceptableAds() |  | 
|  481   { |  | 
|  482     var acceptableCheckbox = this.previousSibling; |  | 
|  483     getAcceptableAdsURL(function(url) |  | 
|  484     { |  | 
|  485       var isChecked = acceptableCheckbox.checked; |  | 
|  486       var title = "Allow non-intrusive advertising"; |  | 
|  487       if (isChecked) |  | 
|  488         addSubscription(url, title); |  | 
|  489       else |  | 
|  490         removeSubscription(url); |  | 
|  491       acceptableCheckbox.checked = !isChecked; |  | 
|  492     }); |  | 
|  493   } |  | 
|  494  |  | 
|  495   function addSubscription(url, title, homepage) |  | 
|  496   { |  | 
|  497     var message = { |  506     var message = { | 
|  498       type: "subscriptions.add", |  507       type: messageType, | 
|  499       url: url |  508       url: url | 
|  500     }; |  509     }; | 
|  501     if (title) |  510     if (title) | 
|  502       message.title = title; |  511       message.title = title; | 
|  503     if (homepage) |  512     if (homepage) | 
|  504       message.homepage = homepage; |  513       message.homepage = homepage; | 
|  505  |  514  | 
|  506     ext.backgroundPage.sendMessage(message); |  515     ext.backgroundPage.sendMessage(message); | 
|  507   } |  516   } | 
|  508  |  517  | 
|  509   function removeSubscription(url) |  518   function removeSubscription(url) | 
|  510   { |  519   { | 
|  511     ext.backgroundPage.sendMessage({ |  520     ext.backgroundPage.sendMessage( | 
 |  521     { | 
|  512       type: "subscriptions.remove", |  522       type: "subscriptions.remove", | 
|  513       url: url |  523       url: url | 
|  514     }); |  524     }); | 
|  515   } |  525   } | 
|  516  |  526  | 
|  517   function addWhitelistedDomain(domain) |  527   function removeFilter(filter) | 
|  518   { |  528   { | 
|  519     ext.backgroundPage.sendMessage({ |  529     ext.backgroundPage.sendMessage( | 
|  520       type: "filters.add", |  530     { | 
|  521       text: "@@||" + domain.toLowerCase() + "^$document" |  | 
|  522     }); |  | 
|  523   } |  | 
|  524  |  | 
|  525   function removeWhitelistedDomain(domain) |  | 
|  526   { |  | 
|  527     ext.backgroundPage.sendMessage({ |  | 
|  528       type: "filters.remove", |  531       type: "filters.remove", | 
|  529       text: "@@||" + domain.toLowerCase() + "^$document" |  532       text: filter | 
|  530     }); |  533     }); | 
|  531   } |  534   } | 
|  532  |  535  | 
|  533   function onFilterMessage(action, filter) |  536   function onFilterMessage(action, filter) | 
|  534   { |  537   { | 
|  535     switch (action) |  538     switch (action) | 
|  536     { |  539     { | 
|  537       case "added": |  540       case "added": | 
|  538         var match = filter.text.match(/^@@\|\|([^\/:]+)\^\$document$/); |  541         updateFilter(filter); | 
|  539         if (match[1]) |  542         updateShareLink(); | 
|  540         { |  | 
|  541           var whitelistTbl =  E("whitelisting-table"); |  | 
|  542           var items = whitelistTbl.getElementsByClassName("display"); |  | 
|  543           var domains = []; |  | 
|  544           for (var i = 0; i < items.length; i++) |  | 
|  545           { |  | 
|  546             domains.push(items[i].innerHTML); |  | 
|  547           } |  | 
|  548           var domain = match[1]; |  | 
|  549           domains.push(domain); |  | 
|  550           domains.sort(); |  | 
|  551            |  | 
|  552           whitelistTbl.insertBefore(createWhitelistElem(domain), whitelistTbl.ch
     ildNodes[domains.indexOf(domain)]); |  | 
|  553           E("whitelisting-textbox").value = ""; |  | 
|  554         } |  | 
|  555         else |  | 
|  556         { |  | 
|  557           // TODO: add `filters[i].text` to list of custom filters |  | 
|  558         } |  | 
|  559         break; |  543         break; | 
|  560       case "loaded": |  544       case "loaded": | 
|  561         populateLists(); |  545         populateLists(); | 
|  562         break; |  546         break; | 
|  563       case "removed": |  547       case "removed": | 
|  564         var match = filter.text.match(/^@@\|\|([^\/:]+)\^\$document$/); |  548         var knownFilter = filtersMap[filter.text]; | 
|  565         if (match[1]) |  549         collections.whitelist.removeItem(knownFilter); | 
|  566         { |  550         delete filtersMap[filter.text]; | 
|  567           var elem = document.querySelector("[data-domain='"+match[1]+"']"); |  551         updateShareLink(); | 
|  568           elem.parentNode.removeChild(elem); |  552         break; | 
|  569         } |  553     } | 
|  570         break; |  554   } | 
|  571     } |  555  | 
|  572   } |  | 
|  573    |  | 
|  574   function onSubscriptionMessage(action, subscription) |  556   function onSubscriptionMessage(action, subscription) | 
|  575   { |  557   { | 
|  576     switch (action) |  558     switch (action) | 
|  577     { |  559     { | 
|  578       case "added": |  560       case "added": | 
|  579         var optionSubscription = getOptionSubscription(subscription.url); |  | 
|  580         if (optionSubscription) |  | 
|  581         { |  | 
|  582           var isAdsType = optionSubscription.type && optionSubscription.type == 
     "ads"; |  | 
|  583           if (isAdsType && !optionSubscription.isAdded) |  | 
|  584             addLanguageSubscription(subscription); |  | 
|  585           else |  | 
|  586             updateSubscriptionState(subscription, true); |  | 
|  587         } |  | 
|  588         else |  | 
|  589           addFurtherList(subscription); |  | 
|  590         break; |  | 
|  591       case "disabled": |  561       case "disabled": | 
|  592         updateSubscriptionState(subscription, false); |  562         updateSubscription(subscription); | 
 |  563         updateShareLink(); | 
|  593         break; |  564         break; | 
|  594       case "homepage": |  565       case "homepage": | 
|  595         // TODO: NYI |  566         // TODO: NYI | 
|  596         break; |  567         break; | 
|  597       case "removed": |  568       case "removed": | 
|  598         updateSubscriptionState(subscription, false); |  569         getAcceptableAdsURL(function(acceptableAdsUrl) | 
 |  570         { | 
 |  571           if (subscription.url == acceptableAdsUrl) | 
 |  572           { | 
 |  573             subscription.disabled = true; | 
 |  574             updateSubscription(subscription); | 
 |  575           } | 
 |  576           else | 
 |  577           { | 
 |  578             var knownSubscription = subscriptionsMap[subscription.url]; | 
 |  579             if (subscription.url in recommendationsMap) | 
 |  580               knownSubscription.disabled = true; | 
 |  581             else | 
 |  582             { | 
 |  583               collections.custom.removeItem(knownSubscription); | 
 |  584               delete subscriptionsMap[subscription.url]; | 
 |  585             } | 
 |  586           } | 
 |  587           updateShareLink(); | 
 |  588         }); | 
|  599         break; |  589         break; | 
|  600       case "title": |  590       case "title": | 
|  601         // TODO: NYI |  591         // TODO: NYI | 
|  602         break; |  592         break; | 
|  603     } |  593     } | 
|  604   } |  594   } | 
|  605    |  595  | 
 |  596   function showAddSubscriptionDialog(subscription) | 
 |  597   { | 
 |  598     E("blockingList-textbox").value = subscription.url; | 
 |  599     openDialog("customlist"); | 
 |  600   } | 
 |  601  | 
|  606   function updateShareLink() |  602   function updateShareLink() | 
|  607   { |  603   { | 
|  608     ext.backgroundPage.sendMessage({ |  604     ext.backgroundPage.sendMessage( | 
 |  605     { | 
|  609       type: "filters.blocked", |  606       type: "filters.blocked", | 
|  610       url: "https://platform.twitter.com/widgets/", |  607       url: "https://platform.twitter.com/widgets/", | 
|  611       requestType: "SCRIPT", |  608       requestType: "SCRIPT", | 
|  612       docDomain: "adblockplus.org", |  609       docDomain: "adblockplus.org", | 
|  613       thirdParty: true |  610       thirdParty: true | 
|  614     }, function(blocked) |  611     }, | 
 |  612     function(blocked) | 
|  615     { |  613     { | 
|  616       // TODO: modify "share" link accordingly |  614       // TODO: modify "share" link accordingly | 
|  617     }); |  615     }); | 
|  618   } |  616   } | 
|  619    |  617  | 
|  620   function getDocLink(link, callback) |  | 
|  621   { |  | 
|  622     ext.backgroundPage.sendMessage({ |  | 
|  623       type: "app.get", |  | 
|  624       what: "doclink", |  | 
|  625       link: link |  | 
|  626     }, callback); |  | 
|  627   } |  | 
|  628    |  | 
|  629   function E(id) |  618   function E(id) | 
|  630   { |  619   { | 
|  631     return document.getElementById(id); |  620     return document.getElementById(id); | 
|  632   } |  621   } | 
|  633  |  622  | 
|  634   ext.onMessage.addListener(function(message) |  623   ext.onMessage.addListener(function(message) | 
|  635   { |  624   { | 
|  636     switch (message.type) |  625     switch (message.type) | 
|  637     { |  626     { | 
|  638       case "app.listen": |  627       case "app.listen": | 
|  639         if (message.action == "addSubscription") |  628         if (message.action == "addSubscription") | 
|  640         { |  629           showAddSubscriptionDialog(message.args[0]); | 
|  641           message.args.unshift(message.action); |  | 
|  642           showAddSubscriptionDialog.apply(null, message.args); |  | 
|  643         } |  | 
|  644         break; |  630         break; | 
|  645       case "filters.listen": |  631       case "filters.listen": | 
|  646         message.args.unshift(message.action); |  632         onFilterMessage(message.action, message.args[0]); | 
|  647         onFilterMessage.apply(null, message.args); |  | 
|  648         break; |  633         break; | 
|  649       case "subscriptions.listen": |  634       case "subscriptions.listen": | 
|  650         message.args.unshift(message.action); |  635         onSubscriptionMessage(message.action, message.args[0]); | 
|  651         onSubscriptionMessage.apply(null, message.args); |  | 
|  652         break; |  636         break; | 
|  653     } |  637     } | 
|  654   }); |  638   }); | 
|  655    |  639  | 
|  656   ext.backgroundPage.sendMessage({ |  640   ext.backgroundPage.sendMessage( | 
 |  641   { | 
|  657     type: "app.listen", |  642     type: "app.listen", | 
|  658     filter: ["addSubscription"] |  643     filter: ["addSubscription"] | 
|  659   }); |  644   }); | 
|  660   ext.backgroundPage.sendMessage({ |  645   ext.backgroundPage.sendMessage( | 
 |  646   { | 
|  661     type: "filters.listen", |  647     type: "filters.listen", | 
|  662     filter: ["added", "loaded", "removed"] |  648     filter: ["added", "loaded", "removed"] | 
|  663   }); |  649   }); | 
|  664   ext.backgroundPage.sendMessage({ |  650   ext.backgroundPage.sendMessage( | 
 |  651   { | 
|  665     type: "subscriptions.listen", |  652     type: "subscriptions.listen", | 
|  666     filter: ["added", "disabled", "homepage", "removed", "title"] |  653     filter: ["added", "disabled", "homepage", "removed", "title"] | 
|  667   }); |  654   }); | 
|  668    |  655  | 
|  669   window.addEventListener("DOMContentLoaded", onDOMLoaded, false); |  656   window.addEventListener("DOMContentLoaded", onDOMLoaded, false); | 
|  670 })(); |  657 })(); | 
| LEFT | RIGHT |