Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: options.js

Issue 6088024630755328: issue 1526 - Implement new options page design for Chrome/Opera/Safari (Closed)
Left Patch Set: Created Jan. 27, 2015, 2:52 p.m.
Right Patch Set: Comment about solution being temprorary is added to subscriptions.xml Created June 13, 2015, 12:59 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « options.html ('k') | skin/options.css » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 /* 1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>, 2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-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 var acceptableAdsUrl = null; 23 var recommendationsMap = Object.create(null);
Thomas Greiner 2015/01/30 13:55:51 Don't save that URL as a separate variable or you'
saroyanm 2015/02/26 12:18:32 Done, but without caching Recommendation, now I'm
24 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
25 function onDOMLoaded() 319 function onDOMLoaded()
26 { 320 {
27 initTabs(); 321 var recommendationTemplate = document.querySelector("#recommend-list-table t emplate");
28 updateVersionNumber(); 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
29 updateShareLink(); 328 updateShareLink();
30 populateLists(); 329 populateLists();
330
331 var tabList = document.querySelectorAll("#main-navigation-tabs li");
332 for (var i = 0; i < tabList.length; i++)
333 {
334 tabList[i].addEventListener("click", function(e)
335 {
336 document.body.dataset.tab = e.currentTarget.dataset.show;
337 }, false);
338 }
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 });
31 359
32 E("find-language").setAttribute("placeholder", ext.i18n.getMessage("options_ modal_language_find")); 360 var placeholderValue = ext.i18n.getMessage("options_dialog_language_find");
33 setLinks("block-element-explanation", "#"); 361 E("find-language").setAttribute("placeholder", placeholderValue);
Thomas Greiner 2015/01/30 13:55:51 No need to set the `src` attribute of anchor tags
saroyanm 2015/02/26 12:18:32 Not using setLinks in currently uploaded patch.
34 362 E("add-blocking-list").addEventListener("click", function()
35 E("add-blocking-list").addEventListener("click", Modal.open, false); 363 {
Thomas Greiner 2015/01/30 13:55:51 I noticed you have a ton of event listeners. Inste
saroyanm 2015/02/26 12:18:32 Most of them already not exist, let me know if it'
Thomas Greiner 2015/03/05 11:36:03 Yes, it definitely makes sense to combine the list
saroyanm 2015/03/06 11:54:32 Done.
36 E("add-website-language").addEventListener("click", Modal.open, false); 364 openDialog("customlist");
37 E("modal-close").addEventListener("click", Modal.close, false);
38 E("whitelisting-add-icon").addEventListener("click", whitelistDomainBtnClick , false);
39 E("whitelisting-add-btn").addEventListener("click", whitelistDomainBtnClick, false);
40 E("whitelisting-enter-icon").addEventListener("click", whitelistDomainBtnCli ck, false);
41 E("whitelisting-textbox").addEventListener("keypress", function(e) {
Thomas Greiner 2015/01/30 13:55:51 Nit: Opening braces should be in next line.
Thomas Greiner 2015/01/30 13:55:51 Here you're using `e` as the name for an Event whe
saroyanm 2015/02/26 12:18:32 I've changed to use `e` from some point, let me kn
saroyanm 2015/02/26 12:18:32 Done.
Thomas Greiner 2015/03/05 11:36:03 No need to as long as it's consistent.
42 if (e.keyCode == 13)
Thomas Greiner 2015/01/30 13:55:51 Please avoid using magical numbers. Unfortunately,
saroyanm 2015/02/26 12:18:32 Done.
43 whitelistDomainBtnClick();
44 }, false); 365 }, false);
45 E("whitelisting-cancel-btn").addEventListener("click", function(){ 366 E("add-website-language").addEventListener("click", function()
Thomas Greiner 2015/01/30 13:55:51 Nit: Opening braces should be in next line.
saroyanm 2015/02/26 12:18:32 Done.
46 E("whitelisting-textbox").value = ""; 367 {
368 openDialog("language");
47 }, false); 369 }, false);
48 E("allow-whitelist-cb").addEventListener("click", toggleAcceptableAds, false ); 370 E("dialog-close").addEventListener("click", function()
49 E("import-blockingList-btn").addEventListener("click", importListBtnCLick, f alse); 371 {
Thomas Greiner 2015/01/30 13:55:51 Calling this function `importList` should be more
saroyanm 2015/02/26 12:18:32 I don't have this function anymore.
50 E("edit-ownBlockingList-btn").addEventListener("click", editOwnRulsBtnClick, false); 372 delete document.body.dataset.dialog;
Thomas Greiner 2015/01/30 13:55:51 Calling this function `editCustomFilters` should b
saroyanm 2015/02/26 12:18:32 Done.
51 E("find-language").addEventListener("keyup", searchLanguage, false); 373 }, false);
52 } 374 E("edit-ownBlockingList-button").addEventListener("click", editCustomFilters , false);
53 375 E("find-language").addEventListener("keyup", onFindLanguageKeyUp, false);
54 function initTabs() 376 E("whitelisting").addEventListener("click", function(e)
55 { 377 {
56 var showContent = function(tab) 378 var id = e.target.id;
57 { 379 if (id == "whitelisting-add-icon" || id == "whitelisting-enter-icon")
58 var tab = tab.querySelector(".tabs li.active"); 380 addWhitelistedDomain();
59 if (tab.dataset.show) 381 else if (id == "whitelisting-cancel-button")
60 E(tab.dataset.show).style.display = "block"; 382 E("whitelisting-textbox").value = "";
61 }; 383 }, false);
62 var optionList = document.querySelectorAll('.tabs li[data-show]'); 384 E("whitelisting-add-button").addEventListener("click", addWhitelistedDomain, false);
63 for (var i = 0; i < optionList.length; ++i) 385 E("whitelisting-textbox").addEventListener("keypress", function(e)
64 { 386 {
65 optionList[i].addEventListener("click", function(ev) 387 // e.keyCode has been deprecated so we attempt to use e.key
66 { 388 // keyCode "13" corresponds to "Enter"
67 var tab = this.parentNode.querySelector(".active"); 389 if ((e.key && e.key == "Enter") || (!e.key && e.keyCode == 13))
68 tab.classList.remove("active"); 390 addWhitelistedDomain();
69 this.classList.add("active"); 391 }, false);
70 E(tab.dataset.show).style.display = "none";; 392 E("import-blockingList-button").addEventListener("click", function()
71 showContent(this.parentNode); 393 {
72 }, false); 394 var url = E("blockingList-textbox").value;
73 } 395 addEnableSubscription(url);
74 showContent(E("main-navigation-tabs")); 396 delete document.body.dataset.dialog;
75 showContent(E("blocking-list-tabs")); 397 }, false);
76 } 398 }
77 399
78 var Modal = 400 function openDialog(name)
79 { 401 {
80 open: function (content) 402 document.body.dataset.dialog = name;
Thomas Greiner 2015/01/30 13:55:51 This function is being called inconsistently. Some
saroyanm 2015/02/26 12:18:32 Now we are using CSS to show the modal.
81 { 403 }
82 var modal = E("modal"); 404
83 var content = E(this && this.dataset ? this.dataset.show : content);
84 content.style.display = "block";
85 modal.style.visibility = "visible";
86 E("modal-background").style.display = "block";
87 if (content.dataset.title)
88 E("modal-title").innerHTML = ext.i18n.getMessage(content.dataset.title);
89 modal.style.marginTop = -(modal.clientHeight/2)+"px";
Thomas Greiner 2015/01/30 13:55:51 Calculating layout is the job of the browser so st
saroyanm 2015/02/26 12:18:32 Done.
90 },
91 close: function ()
92 {
93 var contents = E("modal-content").childNodes;
94 for (var i = 0; i < contents.length; ++i)
95 {
96 if (contents[i].style)
97 contents[i].style.display = "none";
98 }
99 E("modal-background").style.display = "none";
100 E("modal").style.visibility = "hidden";
101 }
102 }
103
104 function populateLists() 405 function populateLists()
105 { 406 {
106 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 {
107 type: "subscriptions.get", 417 type: "subscriptions.get",
108 special: true 418 special: true
109 }, function(subscriptions) 419 },
110 { 420 function(subscriptions)
421 {
422 // Load filters
111 for (var i = 0; i < subscriptions.length; i++) 423 for (var i = 0; i < subscriptions.length; i++)
112 { 424 {
113 ext.backgroundPage.sendMessage({ 425 ext.backgroundPage.sendMessage(
426 {
114 type: "filters.get", 427 type: "filters.get",
115 subscriptionUrl: subscriptions[i].url 428 subscriptionUrl: subscriptions[i].url
116 }, function(filters) 429 },
117 { 430 function(filters)
118 var whitelistArray = []; 431 {
119 for (var i = 0; i < filters.length; i++) 432 for (var i = 0; i < filters.length; i++)
120 { 433 updateFilter(filters[i]);
121 var match = filters[i].text.match(/^@@\|\|([^\/:]+)\^\$document$/);
122 if (match[1])
123 {
124 whitelistArray.push(match[1]);
125 }
126 else
127 {
128 // TODO: add `filters[i].text` to list of custom filters
129 }
130 }
131
132 if (whitelistArray.length > 0)
133 {
134 whitelistArray.sort();
135 for (var i = 0; i < whitelistArray.length; i++)
136 {
137 var domain = whitelistArray[i];
138 E("whitelisting-table").appendChild(createWhitelistElem(domain));
139 }
140 }
141 }); 434 });
142 } 435 }
143 }); 436 });
144 437 loadRecommendations();
145 loadRecommendations(function(recommends) 438 getAcceptableAdsURL(function(acceptableAdsUrl)
Thomas Greiner 2015/01/30 13:55:51 You're calling it "recommends" here but "recommend
saroyanm 2015/02/26 12:18:32 Done.
146 { 439 {
147 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 {
148 type: "subscriptions.get", 449 type: "subscriptions.get",
149 downloadable: true 450 downloadable: true
150 }, function(subscriptions) 451 },
151 { 452 function(subscriptions)
152 getAcceptableAdsURL(function(url) 453 {
153 { 454 for (var i = 0; i < subscriptions.length; i++)
154 acceptableAdsUrl = url; 455 onSubscriptionMessage("added", subscriptions[i]);
155 for (var i = 0; i < subscriptions.length; i++)
156 {
157 if (subscriptions[i].url == acceptableAdsUrl)
158 {
159 getCheckboxSibling(E("allow-whitelist-cb")).checked = !subscriptio ns[i].disabled;
160 continue;
161 }
162
163 var subscription = recommends[subscriptions[i].url];
164 if (!subscription)
165 recommends[subscriptions[i].url] = subscriptions[i];
Thomas Greiner 2015/01/30 13:55:51 Please keep both lists separate since they contain
saroyanm 2015/02/26 12:18:32 Done.
166 else
167 {
168 subscription.disabled = subscriptions[i].disabled;
169 if (subscription.type == "ads")
170 subscription.isAdded = true;
171 }
172 }
173 for (var key in recommends)
174 addOptionItem(recommends[key]);
175 });
176 }); 456 });
177 }); 457 });
178 } 458 }
179 459
180 function loadRecommendations(callback) 460 function addWhitelistedDomain()
181 { 461 {
182 var recommendations = {}; 462 var domain = E("whitelisting-textbox");
183 var request = new XMLHttpRequest(); 463 if (domain.value)
184 request.open("GET", "subscriptions.xml"); 464 {
185 request.onload = function() 465 ext.backgroundPage.sendMessage(
186 { 466 {
187 var list = document.getElementById("subscriptionSelector"); 467 type: "filters.add",
188 var elements = request.responseXML.documentElement.getElementsByTagName("s ubscription"); 468 text: "@@||" + domain.value.toLowerCase() + "^$document"
189 for (var i = 0; i < elements.length; i++) 469 });
190 { 470 }
191 var element = elements[i]; 471
192 var subscription = {}; 472 domain.value = "";
193 subscription.title = element.getAttribute("title"); 473 }
194 subscription.url = element.getAttribute("url"); 474
195 subscription.disabled = true; 475 function editCustomFilters()
196 var prefix = element.getAttribute("prefixes"); 476 {
197 if (prefix) 477 //TODO: NYI
198 { 478 }
199 subscription.prefixes = element.getAttribute("prefixes"); 479
200 subscription.type = "ads";
201 subscription.display = ext.i18n.getMessage("options_language_"+subscri ption.prefixes.replace(/,/g, '_'));
202 }
203 else
204 subscription.display = element.getAttribute("specialization");
205
206 var popular = element.getAttribute("popular");
207 if (popular)
208 subscription.popular = element.getAttribute("popular");
209
210 recommendations[subscription.url] = subscription;
211 }
212 optionSubscriptions = recommendations;
213 callback(recommendations);
214 }
215 request.send();
216 }
217
218 function searchLanguage()
219 {
220 var searchVal = this.value;
221 var items = E("all-lang-table").childNodes;
222 for (var i = 0; i < items.length; ++i)
223 {
224 var item = items[i];
225 var language = item.getElementsByTagName("span")[1].innerHTML;
226 if (language.toLowerCase().indexOf(searchVal.toLowerCase()) > -1)
227 item.style.display = "block";
228 else
229 item.style.display = "none";
230 }
231 }
232
233 function addOptionItem(subscription)
234 {
235 var display = subscription.display ? subscription.display : subscription.tit le;
236 var getPossition = function(elements, subscription)
237 {
238 var localArray = [];
239 for (var i = 0; i < elements.length; i++)
240 {
241 var elem = elements[i];
242 localArray.push(elem);
243 }
244
245 localArray.push(subscription);
246 return localArray.sort(function(a, b) {
247 var aPopular = a.getElementsByClassName("popular").length > 0;
248 var bPopular = b.getElementsByClassName("popular").length > 0;
249 if(aPopular == bPopular)
250 {
251 var aValue = a.getElementsByClassName("display")[0].innerHTML.toLowerC ase();
252 var bValue = b.getElementsByClassName("display")[0].innerHTML.toLowerC ase();
253 if (aValue < bValue)
254 return -1;
255 if (aValue > bValue)
256 return 1;
257 return 0;
258 }
259 if (aPopular == "true")
260 return 1;
261 else
262 return -1;
263 }).indexOf(subscription);
264 };
265
266 var checkBoxClick = function()
267 {
268 toggleSubscription(subscription);
269 };
270
271 var appendToTable = function(table, elem)
272 {
273 var elements = table.getElementsByTagName("li");
274 if (elements.length == 0)
275 table.appendChild(elem);
276 else
277 {
278 var possition = getPossition(elements, elem);
279 table.insertBefore(elem, table.childNodes[possition]);
280 }
281 };
282
283 if (subscription.type && subscription.type == "ads")
284 {
285 if (!subscription.isAdded)
286 {
287 var listElem = generateListElement(subscription, subscription.display, " add");
288 listElem.dataset.url = subscription.url;
289 listElem._subscription = subscription;
290 listElem.getElementsByTagName("button")[0].addEventListener("click", fun ction()
291 {
292 addSubscription(this.dataset.url);
293 }.bind(listElem), false);
294 appendToTable(E("all-lang-table"), listElem);
295 }
296 else
297 {
298 var listElem = generateListElement(subscription, display, "checkbox");
299 listElem.dataset.url = subscription.url;
300 listElem._subscription = subscription;
301 listElem.getElementsByTagName("span")[0].addEventListener("click", check BoxClick, false);
302 appendToTable(E("blocking-languages-table"), listElem);
303 var listElem = generateListElement(subscription, display);
304 listElem.dataset.url = subscription.url;
305 listElem._subscription = subscription;
306 appendToTable(E("blocking-languages-modal-table"), listElem);
307 }
308 }
309 else
310 {
311 var listElem = generateListElement(subscription, display, "checkbox");
312 listElem.dataset.url = subscription.url;
313 listElem._subscription = subscription;
314 listElem.getElementsByTagName("span")[0].addEventListener("click", checkBo xClick, false);
315 appendToTable(E("further-list-table"), listElem);
316 }
317 }
318
319 function addLanguageSubscription(subscription)
320 {
321 var optionSubscription = getOptionSubscription(subscription.url);
322 var elems = getElementsByUrl(subscription.url);
323 for (var i = 0; i < elems.length; i++)
324 elems[i].parentNode.removeChild(elems[i]);
325 optionSubscription.isAdded = true;
326 optionSubscription.disabled = false;
327 addOptionItem(optionSubscription);
328 }
329
330 function createWhitelistElem(domain)
331 {
332 var listElem = generateListElement(null, domain, "delete");
333 listElem.dataset.domain = domain;
334 listElem.getElementsByTagName("button")[0].addEventListener("click", removeW hitelistBtnClick.bind(listElem), false);
335 return listElem;
336 }
337
338 function addFurtherList(subscription)
339 {
340 var optionSubscription = getOptionSubscription(subscription.url);
341 if (optionSubscription)
342 {
343 optionSubscription.disabled = false;
344 addOptionItem(optionSubscription);
345 }
346 else
347 {
348 optionSubscriptions[subscription.url] = subscription;
349 addOptionItem(subscription);
350 }
351 }
352
353 function updateSubscriptionState(subscription, state)
354 {
355 var elem = getElementsByUrl(subscription.url);
356 if (elem.length > 0)
357 {
358 for (var i = 0; i < elem.length; i++)
359 {
360 var checkbox = elem[i].getElementsByTagName("input")[0];
361 if (checkbox)
362 checkbox.checked = state;
363 }
364 }
365 else
366 {
367 if (subscription.url == acceptableAdsUrl)
368 getCheckboxSibling(E("allow-whitelist-cb")).checked = state;
369 else
370 addFurtherList(subscription);
371 }
372 }
373
374 function getElementsByUrl(url)
375 {
376 return document.querySelectorAll("[data-url='"+url+"']");
377 }
378
379 function generateListElement(subscription, text, type)
380 {
381 var list = document.createElement("li");
382 if (type == "checkbox")
383 {
384 var input = document.createElement("input");
385 input.setAttribute("type", "checkbox");
386 if (subscription.disabled == false)
387 input.checked = true;
388 list.appendChild(input);
389 var span = document.createElement("span");
390 list.appendChild(span);
391 }
392 else if (type == "delete")
393 {
394 var button = document.createElement("button");
395 button.setAttribute("class", "delete");
396 list.appendChild(button);
397 }
398 else if (type == "add")
399 {
400 var button = document.createElement("button");
401 button.setAttribute("class", "button-add");
402 var span = document.createElement("span");
403 span.innerHTML = "+" + ext.i18n.getMessage("options_btn_add");
404 button.appendChild(span);
405 list.appendChild(button);
406 }
407 var span = document.createElement("span");
408 span.setAttribute("class", "display");
409 span.innerHTML = text;
410 list.appendChild(span);
411
412 if (subscription && subscription.popular == "true")
413 {
414 var popular = document.createElement("span");
415 popular.setAttribute("class", "popular");
416 popular.innerHTML = "popular";
417 list.appendChild(popular);
418 }
419
420 return list;
421 }
422
423 function getOptionSubscription(url)
424 {
425 return optionSubscriptions[url];
426 }
427
428 function importListBtnCLick()
429 {
430 var url = E("blockingList-textbox").value;
431 addSubscription(url);
432 Modal.close();
433 }
434
435 function whitelistDomainBtnClick()
436 {
437 var domain = E("whitelisting-textbox").value;
438 if (domain)
439 addWhitelistedDomain(domain);
440 }
441
442 function removeWhitelistBtnClick()
443 {
444 removeWhitelistedDomain(this.dataset.domain);
445 }
446
447 function editOwnRulsBtnClick()
448 {
449
450 }
451
452 function showAddSubscriptionDialog(action, subscription)
453 {
454 E("blockingList-textbox").value = subscription.url;
455 Modal.open("further-blocking-modal");
456 }
457
458 function getAcceptableAdsURL(callback) 480 function getAcceptableAdsURL(callback)
459 { 481 {
460 ext.backgroundPage.sendMessage({ 482 ext.backgroundPage.sendMessage(
483 {
461 type: "prefs.get", 484 type: "prefs.get",
462 key: "subscriptions_exceptionsurl" 485 key: "subscriptions_exceptionsurl"
463 }, function(value) 486 },
487 function(value)
464 { 488 {
465 getAcceptableAdsURL = function(callback) 489 getAcceptableAdsURL = function(callback)
466 { 490 {
467 callback(value); 491 callback(value);
468 } 492 }
469 getAcceptableAdsURL(callback); 493 getAcceptableAdsURL(callback);
470 }); 494 });
471 } 495 }
472 496
473 function toggleSubscription(subscription) 497 function addEnableSubscription(url, title, homepage)
474 { 498 {
475 ext.backgroundPage.sendMessage({ 499 var messageType = null;
476 type: "subscriptions.toggle", 500 var knownSubscription = subscriptionsMap[url];
477 url: subscription.url, 501 if (knownSubscription && knownSubscription.disabled == true)
478 title: subscription.title, 502 messageType = "subscriptions.toggle"
479 homepage: subscription.homepage 503 else
480 }); 504 messageType = "subscriptions.add"
481 } 505
482
483 function toggleAcceptableAds()
484 {
485 var acceptableCheckbox = getCheckboxSibling(this);
486 getAcceptableAdsURL(function(url)
487 {
488 var isChecked = acceptableCheckbox.checked;
489 var title = "Allow non-intrusive advertising";
490 if (isChecked)
491 removeSubscription(url);
492 else
493 addSubscription(url, title);
494 });
495 }
496
497 function addSubscription(url, title, homepage)
498 {
499 var message = { 506 var message = {
500 type: "subscriptions.add", 507 type: messageType,
501 url: url 508 url: url
502 }; 509 };
503 if (title) 510 if (title)
504 message.title = title; 511 message.title = title;
505 if (homepage) 512 if (homepage)
506 message.homepage = homepage; 513 message.homepage = homepage;
507 514
508 ext.backgroundPage.sendMessage(message); 515 ext.backgroundPage.sendMessage(message);
509 } 516 }
510 517
511 function removeSubscription(url) 518 function removeSubscription(url)
512 { 519 {
513 ext.backgroundPage.sendMessage({ 520 ext.backgroundPage.sendMessage(
521 {
514 type: "subscriptions.remove", 522 type: "subscriptions.remove",
515 url: url 523 url: url
516 }); 524 });
517 } 525 }
518 526
519 function addWhitelistedDomain(domain) 527 function removeFilter(filter)
520 { 528 {
521 ext.backgroundPage.sendMessage({ 529 ext.backgroundPage.sendMessage(
522 type: "filters.add", 530 {
523 text: "@@||" + domain.toLowerCase() + "^$document"
524 });
525 }
526
527 function removeWhitelistedDomain(domain)
528 {
529 ext.backgroundPage.sendMessage({
530 type: "filters.remove", 531 type: "filters.remove",
531 text: "@@||" + domain.toLowerCase() + "^$document" 532 text: filter
532 }); 533 });
533 } 534 }
534 535
535 function onFilterMessage(action, filter) 536 function onFilterMessage(action, filter)
536 { 537 {
537 switch (action) 538 switch (action)
538 { 539 {
539 case "added": 540 case "added":
540 var match = filter.text.match(/^@@\|\|([^\/:]+)\^\$document$/); 541 updateFilter(filter);
541 if (match[1]) 542 updateShareLink();
542 {
543 var whitelistTbl = E("whitelisting-table");
544 var items = whitelistTbl.getElementsByClassName("display");
545 var domains = [];
546 for (var i = 0; i < items.length; i++)
547 {
548 domains.push(items[i].innerHTML);
549 }
550 var domain = match[1];
551 domains.push(domain);
552 domains.sort();
553
554 whitelistTbl.insertBefore(createWhitelistElem(domain), whitelistTbl.ch ildNodes[domains.indexOf(domain)]);
555 E("whitelisting-textbox").value = "";
556 }
557 else
558 {
559 // TODO: add `filters[i].text` to list of custom filters
560 }
561 break; 543 break;
562 case "loaded": 544 case "loaded":
563 populateLists(); 545 populateLists();
564 break; 546 break;
565 case "removed": 547 case "removed":
566 var match = filter.text.match(/^@@\|\|([^\/:]+)\^\$document$/); 548 var knownFilter = filtersMap[filter.text];
567 if (match[1]) 549 collections.whitelist.removeItem(knownFilter);
568 { 550 delete filtersMap[filter.text];
569 var elem = document.querySelector("[data-domain='"+match[1]+"']"); 551 updateShareLink();
570 elem.parentNode.removeChild(elem); 552 break;
571 } 553 }
572 break; 554 }
573 } 555
574 }
575
576 function onSubscriptionMessage(action, subscription) 556 function onSubscriptionMessage(action, subscription)
577 { 557 {
578 switch (action) 558 switch (action)
579 { 559 {
580 case "added": 560 case "added":
581 var optionSubscription = getOptionSubscription(subscription.url);
582 if (optionSubscription)
583 {
584 var isAdsType = optionSubscription.type && optionSubscription.type == "ads";
585 if (isAdsType && !optionSubscription.isAdded)
586 addLanguageSubscription(subscription);
587 else
588 updateSubscriptionState(subscription, true);
589 }
590 else if (subscription.url == acceptableAdsUrl)
591 updateSubscriptionState(subscription, true);
592 else
593 addFurtherList(subscription);
594 break;
595 case "disabled": 561 case "disabled":
596 updateSubscriptionState(subscription, false); 562 updateSubscription(subscription);
563 updateShareLink();
597 break; 564 break;
598 case "homepage": 565 case "homepage":
599 // TODO: NYI 566 // TODO: NYI
600 break; 567 break;
601 case "removed": 568 case "removed":
602 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 });
603 break; 589 break;
604 case "title": 590 case "title":
605 // TODO: NYI 591 // TODO: NYI
606 break; 592 break;
607 } 593 }
608 } 594 }
609 595
596 function showAddSubscriptionDialog(subscription)
597 {
598 E("blockingList-textbox").value = subscription.url;
599 openDialog("customlist");
600 }
601
610 function updateShareLink() 602 function updateShareLink()
611 { 603 {
612 ext.backgroundPage.sendMessage({ 604 ext.backgroundPage.sendMessage(
605 {
613 type: "filters.blocked", 606 type: "filters.blocked",
614 url: "https://platform.twitter.com/widgets/", 607 url: "https://platform.twitter.com/widgets/",
615 requestType: "SCRIPT", 608 requestType: "SCRIPT",
616 docDomain: "adblockplus.org", 609 docDomain: "adblockplus.org",
617 thirdParty: true 610 thirdParty: true
618 }, function(blocked) 611 },
612 function(blocked)
619 { 613 {
620 // TODO: modify "share" link accordingly 614 // TODO: modify "share" link accordingly
621 }); 615 });
622 } 616 }
623 617
624 function updateVersionNumber()
625 {
626 ext.backgroundPage.sendMessage({
627 method: "app.get",
628 what: "addonVersion"
629 }, function(addonVersion)
630 {
631 E("abp-version").innerHTML = addonVersion;
632 });
633 }
634
635 function getDocLink(link, callback)
636 {
637 ext.backgroundPage.sendMessage({
638 type: "app.get",
639 what: "doclink",
640 link: link
641 }, callback);
642 }
643
644 function setLinks(id)
645 {
646 var element = E(id);
647 if (!element)
648 {
649 return;
650 }
651
652 var links = element.getElementsByTagName("a");
653
654 for (var i = 0; i < links.length; i++)
655 {
656 if (typeof arguments[i + 1] == "string")
657 {
658 links[i].href = arguments[i + 1];
659 links[i].setAttribute("target", "_blank");
660 }
661 else if (typeof arguments[i + 1] == "function")
662 {
663 links[i].href = "javascript:void(0);";
664 links[i].addEventListener("click", arguments[i + 1], false);
665 }
666 }
667 }
668
669 function E(id) 618 function E(id)
670 { 619 {
671 return document.getElementById(id); 620 return document.getElementById(id);
672 }
673
674 function getCheckboxSibling(element)
675 {
676 var parent = element.parentNode;
677 for (var i=0; i < parent.childNodes.length; i++)
678 {
679 if (parent.childNodes[i] && parent.childNodes[i].type == "checkbox")
680 return parent.childNodes[i];
681 }
682 } 621 }
683 622
684 ext.onMessage.addListener(function(message) 623 ext.onMessage.addListener(function(message)
685 { 624 {
686 switch (message.type) 625 switch (message.type)
687 { 626 {
688 case "app.listen": 627 case "app.listen":
689 if (message.action == "addSubscription") 628 if (message.action == "addSubscription")
690 { 629 showAddSubscriptionDialog(message.args[0]);
691 message.args.unshift(message.action);
692 showAddSubscriptionDialog.apply(null, message.args);
693 }
694 break; 630 break;
695 case "filters.listen": 631 case "filters.listen":
696 message.args.unshift(message.action); 632 onFilterMessage(message.action, message.args[0]);
697 onFilterMessage.apply(null, message.args);
698 break; 633 break;
699 case "subscriptions.listen": 634 case "subscriptions.listen":
700 message.args.unshift(message.action); 635 onSubscriptionMessage(message.action, message.args[0]);
701 onSubscriptionMessage.apply(null, message.args);
702 break; 636 break;
703 } 637 }
704 }); 638 });
705 639
706 ext.backgroundPage.sendMessage({ 640 ext.backgroundPage.sendMessage(
641 {
707 type: "app.listen", 642 type: "app.listen",
708 filter: ["addSubscription"] 643 filter: ["addSubscription"]
709 }); 644 });
710 ext.backgroundPage.sendMessage({ 645 ext.backgroundPage.sendMessage(
646 {
711 type: "filters.listen", 647 type: "filters.listen",
712 filter: ["added", "loaded", "removed"] 648 filter: ["added", "loaded", "removed"]
713 }); 649 });
714 ext.backgroundPage.sendMessage({ 650 ext.backgroundPage.sendMessage(
651 {
715 type: "subscriptions.listen", 652 type: "subscriptions.listen",
716 filter: ["added", "disabled", "homepage", "removed", "title"] 653 filter: ["added", "disabled", "homepage", "removed", "title"]
717 }); 654 });
718 655
719 window.addEventListener("DOMContentLoaded", onDOMLoaded, false); 656 window.addEventListener("DOMContentLoaded", onDOMLoaded, false);
720 })(); 657 })();
LEFTRIGHT

Powered by Google App Engine
This is Rietveld