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