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 Jan. 27, 2015, 2:52 p.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 optionSubscriptions = {};
23 var acceptableAdsUrl = 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
25 function onDOMLoaded()
26 {
27 initTabs();
28 updateVersionNumber();
29 updateShareLink();
30 populateLists();
31
32 E("find-language").setAttribute("placeholder", ext.i18n.getMessage("options_ modal_language_find"));
33 setLinks("block-element-explanation", "#");
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
35 E("add-blocking-list").addEventListener("click", Modal.open, false);
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);
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);
45 E("whitelisting-cancel-btn").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 = "";
47 }, false);
48 E("allow-whitelist-cb").addEventListener("click", toggleAcceptableAds, false );
49 E("import-blockingList-btn").addEventListener("click", importListBtnCLick, f alse);
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);
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);
52 }
53
54 function initTabs()
55 {
56 var showContent = function(tab)
57 {
58 var tab = tab.querySelector(".tabs li.active");
59 if (tab.dataset.show)
60 E(tab.dataset.show).style.display = "block";
61 };
62 var optionList = document.querySelectorAll('.tabs li[data-show]');
63 for (var i = 0; i < optionList.length; ++i)
64 {
65 optionList[i].addEventListener("click", function(ev)
66 {
67 var tab = this.parentNode.querySelector(".active");
68 tab.classList.remove("active");
69 this.classList.add("active");
70 E(tab.dataset.show).style.display = "none";;
71 showContent(this.parentNode);
72 }, false);
73 }
74 showContent(E("main-navigation-tabs"));
75 showContent(E("blocking-list-tabs"));
76 }
77
78 var Modal =
79 {
80 open: function (content)
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 {
82 var modal = E("modal");
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()
105 {
106 ext.backgroundPage.sendMessage({
107 type: "subscriptions.get",
108 special: true
109 }, function(subscriptions)
110 {
111 for (var i = 0; i < subscriptions.length; i++)
112 {
113 ext.backgroundPage.sendMessage({
114 type: "filters.get",
115 subscriptionUrl: subscriptions[i].url
116 }, function(filters)
117 {
118 var whitelistArray = [];
119 for (var i = 0; i < filters.length; i++)
120 {
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 });
142 }
143 });
144
145 loadRecommendations(function(recommends)
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 {
147 ext.backgroundPage.sendMessage({
148 type: "subscriptions.get",
149 downloadable: true
150 }, function(subscriptions)
151 {
152 getAcceptableAdsURL(function(url)
153 {
154 acceptableAdsUrl = url;
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 });
177 });
178 }
179
180 function loadRecommendations(callback)
181 {
182 var recommendations = {};
183 var request = new XMLHttpRequest();
184 request.open("GET", "subscriptions.xml");
185 request.onload = function()
186 {
187 var list = document.getElementById("subscriptionSelector");
188 var elements = request.responseXML.documentElement.getElementsByTagName("s ubscription");
189 for (var i = 0; i < elements.length; i++)
190 {
191 var element = elements[i];
192 var subscription = {};
193 subscription.title = element.getAttribute("title");
194 subscription.url = element.getAttribute("url");
195 subscription.disabled = true;
196 var prefix = element.getAttribute("prefixes");
197 if (prefix)
198 {
199 subscription.prefixes = element.getAttribute("prefixes");
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)
459 {
460 ext.backgroundPage.sendMessage({
461 type: "prefs.get",
462 key: "subscriptions_exceptionsurl"
463 }, function(value)
464 {
465 getAcceptableAdsURL = function(callback)
466 {
467 callback(value);
468 }
469 getAcceptableAdsURL(callback);
470 });
471 }
472
473 function toggleSubscription(subscription)
474 {
475 ext.backgroundPage.sendMessage({
476 type: "subscriptions.toggle",
477 url: subscription.url,
478 title: subscription.title,
479 homepage: subscription.homepage
480 });
481 }
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 = {
500 type: "subscriptions.add",
501 url: url
502 };
503 if (title)
504 message.title = title;
505 if (homepage)
506 message.homepage = homepage;
507
508 ext.backgroundPage.sendMessage(message);
509 }
510
511 function removeSubscription(url)
512 {
513 ext.backgroundPage.sendMessage({
514 type: "subscriptions.remove",
515 url: url
516 });
517 }
518
519 function addWhitelistedDomain(domain)
520 {
521 ext.backgroundPage.sendMessage({
522 type: "filters.add",
523 text: "@@||" + domain.toLowerCase() + "^$document"
524 });
525 }
526
527 function removeWhitelistedDomain(domain)
528 {
529 ext.backgroundPage.sendMessage({
530 type: "filters.remove",
531 text: "@@||" + domain.toLowerCase() + "^$document"
532 });
533 }
534
535 function onFilterMessage(action, filter)
536 {
537 switch (action)
538 {
539 case "added":
540 var match = filter.text.match(/^@@\|\|([^\/:]+)\^\$document$/);
541 if (match[1])
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;
562 case "loaded":
563 populateLists();
564 break;
565 case "removed":
566 var match = filter.text.match(/^@@\|\|([^\/:]+)\^\$document$/);
567 if (match[1])
568 {
569 var elem = document.querySelector("[data-domain='"+match[1]+"']");
570 elem.parentNode.removeChild(elem);
571 }
572 break;
573 }
574 }
575
576 function onSubscriptionMessage(action, subscription)
577 {
578 switch (action)
579 {
580 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":
596 updateSubscriptionState(subscription, false);
597 break;
598 case "homepage":
599 // TODO: NYI
600 break;
601 case "removed":
602 updateSubscriptionState(subscription, false);
603 break;
604 case "title":
605 // TODO: NYI
606 break;
607 }
608 }
609
610 function updateShareLink()
611 {
612 ext.backgroundPage.sendMessage({
613 type: "filters.blocked",
614 url: "https://platform.twitter.com/widgets/",
615 requestType: "SCRIPT",
616 docDomain: "adblockplus.org",
617 thirdParty: true
618 }, function(blocked)
619 {
620 // TODO: modify "share" link accordingly
621 });
622 }
623
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)
670 {
671 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 }
683
684 ext.onMessage.addListener(function(message)
685 {
686 switch (message.type)
687 {
688 case "app.listen":
689 if (message.action == "addSubscription")
690 {
691 message.args.unshift(message.action);
692 showAddSubscriptionDialog.apply(null, message.args);
693 }
694 break;
695 case "filters.listen":
696 message.args.unshift(message.action);
697 onFilterMessage.apply(null, message.args);
698 break;
699 case "subscriptions.listen":
700 message.args.unshift(message.action);
701 onSubscriptionMessage.apply(null, message.args);
702 break;
703 }
704 });
705
706 ext.backgroundPage.sendMessage({
707 type: "app.listen",
708 filter: ["addSubscription"]
709 });
710 ext.backgroundPage.sendMessage({
711 type: "filters.listen",
712 filter: ["added", "loaded", "removed"]
713 });
714 ext.backgroundPage.sendMessage({
715 type: "subscriptions.listen",
716 filter: ["added", "disabled", "homepage", "removed", "title"]
717 });
718
719 window.addEventListener("DOMContentLoaded", onDOMLoaded, false);
720 })();
OLDNEW
« options.html ('K') | « options.html ('k') | skin/options.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld