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

Side by Side Diff: options.js

Issue 6088024630755328: issue 1526 - Implement new options page design for Chrome/Opera/Safari (Closed)
Patch Set: Created March 6, 2015, 11:44 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
(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 })();
OLDNEW
« locale/en-US/options.json ('K') | « options.html ('k') | skin/options.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld