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 Feb. 19, 2015, 4: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
« no previous file with comments | « options.html ('k') | skin/options.css » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 acceptableAdsUrl = null;
23
24 var optionObj =
25 {
26 collections:
27 {
28 push: function()
29 {
30 var length = Array.prototype.push.apply(this, arguments);
31 this.sort(function(a, b)
32 {
33 var aValue = (a.title || a.url || a.text).toLowerCase();
34 var bValue = (b.title || b.url || a.text).toLowerCase();
35 if (aValue < bValue)
36 return -1;
37 if (aValue > bValue)
38 return 1;
39 return 0;
40 });
41 for (var i = 0; i < arguments.length; i++)
42 {
43 for (var j = 0; j < this.table.length; j++)
44 {
45 var object = arguments[i];
46 var text = object.title || object.text;
47 var access = object.url || object.text;
48 var index = this.indexOf(object);
49 var table = E(this.table[j].id);
50 var template = this.table[j].template;
51 var listener = this.table[j].listener;
52
53 var listItem = document.createElement("li");
54 listItem.innerHTML = template;
55 listItem.dataset.access = access;
56 listItem.querySelector(".display").textContent = text;
57 if (object.isAdsType)
58 listItem.dataset.search = text.toLowerCase();
59
60 var control = listItem.querySelector(".control");
61 if (control)
62 {
63 control.addEventListener("click", listener, false);
64 control.checked = !object.disabled;
65 }
66 if (table.hasChildNodes)
67 table.insertBefore(listItem, table.childNodes[index]);
68 else
69 table.appendChild(listItem);
70 }
71 }
72 return length;
73 },
74 remove: function(obj)
75 {
76 var index = this.indexOf(obj);
77 this.splice(index, 1);
78 var access = obj.url || obj.text;
79 for (var i=0; i < this.table.length; i++)
80 {
81 var table = E(this.table[i].id);
82 var element = table.querySelector("[data-access='"+access+"']");
83 element.parentElement.removeChild(element);
84 }
85 }
86 },
87 subscriptions:
88 {
89 update: function(subscription)
90 {
91 var optionSubscription = this[subscription.url];
92 if (optionSubscription)
93 optionSubscription.disabled = subscription.disabled;
94 else
95 {
96 optionObj.observe(subscription, ["disabled"]);
97 var collArray = null;
98 if (subscription.isPopular)
99 collArray = collections.popular;
100 else if (subscription.isAdsType && subscription.disabled)
101 collArray = collections.allLangs;
102 else if (subscription.isAdsType && !subscription.disabled)
103 collArray = collections.langs;
104 else if (subscription.url == acceptableAdsUrl)
105 collArray = collections.acceptableAds;
106 else
107 collArray = collections.custom;
108
109 collArray.push = collections.push;
110 collArray.push(subscription);
111 this[subscription.url] = subscription;
112 }
113 },
114 remove: function(subscription)
115 {
116 var optionSubscription = this[subscription.url];
117
118 if (optionSubscription.isAdsType)
119 {
120 collections.langs.remove(optionSubscription);
121 collections.allLangs.push(optionSubscription);
122 }
123 else if(optionSubscription.isPopular)
124 {
125 optionSubscription.disabled = true;
126 }
127 else
128 {
129 collections.custom.remove(optionSubscription);
130 }
131 },
132 chboxListener: function(e)
133 {
134 e.preventDefault();
135 var isChecked = e.target.checked;
136 var url = e.target.parentNode.dataset.access;
137 if (!isChecked)
138 sendMessage.removeSubscription(url);
139 else
140 sendMessage.addSubscription(url);
141 },
142 toggleAcceptableAds: function(e)
143 {
144 e.preventDefault();
145 var target = e.target;
146 var isChecked = target.checked;
147 var url = e.target.parentNode.dataset.access;
148
149 if (isChecked)
150 sendMessage.addSubscription(url);
151 else
152 sendMessage.removeSubscription(url);
153 },
154 addBtnListener: function(e)
155 {
156 e.preventDefault();
157 var url = this.parentNode.dataset.access;
158 sendMessage.addSubscription(url);
159 }
160 },
161 filters:
162 {
163 update: function(filter)
164 {
165 var optionFilter = this[filter.text];
166 var match = filter.text.match(/^@@\|\|([^\/:]+)\^\$document$/);
167 if (match && !optionFilter)
168 {
169 filter.title = match[1];
170 collections.whitelist.push(filter);
171 this[filter.text] = filter;
172 }
173 else
174 {
175 // TODO: add `filters[i].text` to list of custom filters
176 }
177 },
178 remove: function(filter)
179 {
180 collections.whitelist.remove(filter);
181 },
182 deleteBtnClick: function()
183 {
184 var filter = this.parentNode.dataset.access;
185 sendMessage.removeFilter(filter);
186 }
187 },
188 observe: function(obj, props)
189 {
190 props.forEach(function(property)
191 {
192 obj["$"+property] = obj[property];
193 Object.defineProperty(obj, property,
194 {
195 get: function()
196 {
197 return this["$"+property];
198 },
199 set: function(value)
200 {
201 this["$"+property] = value;
202 var access = obj.url || obj.text;
203 var elements = document.querySelectorAll("[data-access='" + access + "']");
204 for (var i=0; i < elements.length; i++)
205 {
206 var element = elements[i];
207 var control = element.getElementsByClassName("control")[0];
208 if (control.localName == "input")
209 control.checked = !obj.disabled;
210 if (obj.isAdsType)
211 {
212 collections.allLangs.remove(obj);
213 collections.langs.push(obj);
214 }
215 }
216 }
217 });
218 });
219 }
220 };
221
222 function createCollArray(tables)
223 {
224 var array = [];
225 array.push = optionObj.collections.push;
226 array.remove = optionObj.collections.remove;
227 array.table = tables;
228 return array;
229 }
230
231 var collections = optionObj.collections;
232 collections.popular = createCollArray([
233 {
234 id: "recommend-list-table",
235 template: "<input type='checkbox' class='control' /><span class='display'> </span><span class='popular'>popular</span>",
236 listener: optionObj.subscriptions.chboxListener
237 }
238 ]);
239 collections.langs = createCollArray([
240 {
241 id: "blocking-languages-table",
242 template: "<input type='checkbox' class='control' /><span class='display'> </span>",
243 listener: optionObj.subscriptions.chboxListener
244 },
245 {
246 id: "blocking-languages-modal-table",
247 template: "<span class='display'></span>"
248 }
249 ]);
250 collections.allLangs = createCollArray([
251 {
252 id: "all-lang-table",
253 template: "<button class='button-add control'><span>+add</span></button><s pan class='display'></span>",
254 listener: optionObj.subscriptions.addBtnListener
255 }
256 ]);
257 collections.acceptableAds = createCollArray([
258 {
259 id: "acceptableads-table",
260 template: "<input type='checkbox' class='control' /><span class='display'> </span>",
261 listener: optionObj.subscriptions.toggleAcceptableAds
262 }
263 ]);
264 collections.custom = createCollArray([
265 {
266 id: "custom-list-table",
267 template: "<input type='checkbox' class='control' /><span class='display'> </span>",
268 listener: optionObj.subscriptions.chboxListener
269 }
270 ]);
271 collections.whitelist = createCollArray([
272 {
273 id: "whitelisting-table",
274 template: "<button class='delete control'></button><span class='display'>< /span>",
275 listener: optionObj.filters.deleteBtnClick
276 }
277 ]);
278
279 function loadRecommendations()
280 {
281 var request = new XMLHttpRequest();
282 request.open("GET", "subscriptions.xml", false);
283 request.onload = function()
284 {
285 var list = document.getElementById("subscriptionSelector");
286 var docElem = request.responseXML.documentElement;
287 var elements = docElem.getElementsByTagName("subscription");
288 for (var i = 0; i < elements.length; i++)
289 {
290 var element = elements[i];
291 var subscription = Object.create(null);
292 subscription.title = element.getAttribute("title");
293 subscription.url = element.getAttribute("url");
294 subscription.disabled = true;
295 var prefix = element.getAttribute("prefixes");
296 if (prefix)
297 {
298 subscription.isAdsType = true;
299 var prefix = element.getAttribute("prefixes").replace(/,/g, "_");
300 subscription.title = ext.i18n.getMessage("options_language_" + prefix) ;
301 }
302 else
303 subscription.title = element.getAttribute("specialization");
304
305 if (element.getAttribute("popular"))
306 subscription.isPopular = true;
307 optionObj.subscriptions.update(subscription);
308 }
309 };
310 request.send(null);
311 }
312
313 function onDOMLoaded()
314 {
315 updateShareLink();
316 populateLists();
317
318 var tabList = document.querySelectorAll("#main-navigation-tabs li");
319 for (var i = 0; i < tabList.length; i++)
320 {
321 tabList[i].addEventListener("click", function(e)
322 {
323 document.body.dataset.tab = e.currentTarget.dataset.show;
324 }, false);
325 }
326
327 var searchLanguage = function()
328 {
329 var searchStyle = E('search_style');
Thomas Greiner 2015/02/20 14:02:07 Review comments should not only be addressed for o
saroyanm 2015/02/26 12:18:33 Done.
330 if (!this.value)
331 searchStyle.innerHTML = "";
332 else
333 searchStyle.innerHTML = "#all-lang-table li:not([data-search*=\"" + this .value.toLowerCase() + "\"]) { display: none; }";
334 };
335
336 // Update version number in navigation sidebar
337 ext.backgroundPage.sendMessage(
Thomas Greiner 2015/02/20 14:02:07 It's still inconsistent. As I noted in a previous
saroyanm 2015/02/26 12:18:33 Used the one from your suggestion: https://hg.adbl
338 {
339 method: "app.get",
340 what: "addonVersion"
341 }, function(addonVersion)
342 {
343 E("abp-version").textContent = addonVersion;
344 });
345
346 var whitelistDomainBtnClick = function()
347 {
348 var domain = E("whitelisting-textbox");
349 if (domain.value)
350 SendMessage.addWhitelistedDomain(domain.value);
351 domain.value = "";
352 };
353
354 var placeholderValue = ext.i18n.getMessage("options_modal_language_find");
355 E("find-language").setAttribute("placeholder", placeholderValue);
356 setLinks("block-element-explanation", "#");
357
358 E("add-blocking-list").addEventListener("click", function()
359 {
360 openModal("customlist");
361 }, false);
362 E("add-website-language").addEventListener("click", function()
363 {
364 openModal("language");
365 }, false);
366 E("modal-close").addEventListener("click", function()
367 {
368 delete document.body.dataset.modal;
369 }, false);
370 E("edit-ownBlockingList-btn").addEventListener("click", editOwnRulsBtnClick, false);
371 E("find-language").addEventListener("keyup", searchLanguage, false);
372 E("whitelisting-add-icon").addEventListener("click", whitelistDomainBtnClick , false);
373 E("whitelisting-add-btn").addEventListener("click", whitelistDomainBtnClick, false);
374 E("whitelisting-enter-icon").addEventListener("click", whitelistDomainBtnCli ck, false);
375 E("whitelisting-textbox").addEventListener("keypress", function(e)
376 {
377 if (e.keyCode == 13)
378 whitelistDomainBtnClick();
379 }, false);
380 E("whitelisting-cancel-btn").addEventListener("click", function()
381 {
382 E("whitelisting-textbox").value = "";
383 }, false);
384 E("import-blockingList-btn").addEventListener("click", function()
385 {
386 var url = E("blockingList-textbox").value;
387 SendMessage.addSubscription(url);
388 delete document.body.dataset.modal;
389 }, false);
390 }
391
392 function openModal(name)
393 {
394 document.body.dataset.modal = name;
395 var title = "options_modal_" + name + "_title";
396 E("modal-title").textContent = ext.i18n.getMessage(title);
397 }
398
399 function populateLists()
400 {
401 ext.backgroundPage.sendMessage(
402 {
403 type: "subscriptions.get",
404 special: true
405 }, function(subscriptions)
406 {
407 // Load filters
408 for (var i = 0; i < subscriptions.length; i++)
409 {
410 ext.backgroundPage.sendMessage(
411 {
412 type: "filters.get",
413 subscriptionUrl: subscriptions[i].url
414 }, function(filters)
415 {
416 for (var i = 0; i < filters.length; i++)
417 optionObj.filters.update(filters[i]);
418 });
419 }
420 });
421 loadRecommendations();
422 sendMessage.getAcceptableAdsURL(function(url)
423 {
424 acceptableAdsUrl = url;
425 var subscription = Object.create(null);
426 subscription.url = acceptableAdsUrl;
427 subscription.disabled = true;
428 subscription.title = ext.i18n.getMessage("options_acceptableAds_descriptio n");
429 optionObj.subscriptions.update(subscription);
430
431 // Load user subscriptions
432 ext.backgroundPage.sendMessage(
433 {
434 type: "subscriptions.get",
435 downloadable: true
436 }, function(subscriptions)
437 {
438 for (var i = 0; i < subscriptions.length; i++)
439 messageListeners.onSubscriptionMessage("added", subscriptions[i]);
440 });
441 });
442 }
443
444 function editOwnRulsBtnClick()
445 {
446
447 }
448
449 var sendMessage =
450 {
451 getAcceptableAdsURL: function(callback)
452 {
453 ext.backgroundPage.sendMessage(
454 {
455 type: "prefs.get",
456 key: "subscriptions_exceptionsurl"
457 }, function(value)
458 {
459 sendMessage.getAcceptableAdsURL = function(callback)
460 {
461 callback(value);
462 }
463 sendMessage.getAcceptableAdsURL(callback);
464 });
465 },
466 addSubscription: function(url, title, homepage)
467 {
468 var message = {
469 type: "subscriptions.add",
470 url: url
471 };
472 if (title)
473 message.title = title;
474 if (homepage)
475 message.homepage = homepage;
476
477 ext.backgroundPage.sendMessage(message);
478 },
479 removeSubscription: function(url)
480 {
481 ext.backgroundPage.sendMessage(
482 {
483 type: "subscriptions.remove",
484 url: url
485 });
486 },
487 addWhitelistedDomain: function(domain)
488 {
489 ext.backgroundPage.sendMessage(
490 {
491 type: "filters.add",
492 text: "@@||" + domain.toLowerCase() + "^$document"
493 });
494 },
495 removeFilter: function(filter)
496 {
497 ext.backgroundPage.sendMessage(
498 {
499 type: "filters.remove",
500 text: filter
501 });
502 }
503 };
504
505 var messageListeners =
506 {
507 onFilterMessage: function(action, filter)
508 {
509 switch (action)
510 {
511 case "added":
512 optionObj.filters.update(filter);
513 break;
514 case "loaded":
515 populateLists();
516 break;
517 case "removed":
518 optionObj.filters.remove(filter);
519 break;
520 }
521 },
522 onSubscriptionMessage: function(action, subscription)
523 {
524 switch (action)
525 {
526 case "added":
527 optionObj.subscriptions.update(subscription);
528 break;
529 case "disabled":
530 break;
531 case "homepage":
532 // TODO: NYI
533 break;
534 case "removed":
535 if (subscription.url == acceptableAdsUrl)
536 {
537 subscription.disabled = true;
538 optionObj.subscriptions.update(subscription);
539 }
540 else
541 optionObj.subscriptions.remove(subscription);
542 break;
543 case "title":
544 // TODO: NYI
545 break;
546 }
547 }
548 };
549
550 function showAddSubscriptionDialog(subscription)
551 {
552 E("blockingList-textbox").value = subscription.url;
553 openModal("customlist");
554 }
555
556 function updateShareLink()
557 {
558 ext.backgroundPage.sendMessage(
559 {
560 type: "filters.blocked",
561 url: "https://platform.twitter.com/widgets/",
562 requestType: "SCRIPT",
563 docDomain: "adblockplus.org",
564 thirdParty: true
565 },
566 function(blocked)
567 {
568 // TODO: modify "share" link accordingly
569 });
570 }
571
572 function getDocLink(link, callback)
573 {
574 ext.backgroundPage.sendMessage(
575 {
576 type: "app.get",
577 what: "doclink",
578 link: link
579 }, callback);
580 }
581
582 function setLinks(id)
583 {
584 var element = E(id);
585 if (!element)
586 {
587 return;
588 }
589
590 var links = element.getElementsByTagName("a");
591
592 for (var i = 0; i < links.length; i++)
593 {
594 if (typeof arguments[i + 1] == "string")
595 {
596 links[i].href = arguments[i + 1];
597 links[i].setAttribute("target", "_blank");
598 }
599 else if (typeof arguments[i + 1] == "function")
600 {
601 links[i].href = "javascript:void(0);";
602 links[i].addEventListener("click", arguments[i + 1], false);
603 }
604 }
605 }
606
607 function E(id)
608 {
609 return document.getElementById(id);
610 }
611
612 ext.onMessage.addListener(function(message)
613 {
614 switch (message.type)
615 {
616 case "app.listen":
617 if (message.action == "addSubscription")
618 messageListeners.showAddSubscriptionDialog(message.args[0]);
619 break;
620 case "filters.listen":
621 messageListeners.onFilterMessage(message.action, message.args[0]);
622 break;
623 case "subscriptions.listen":
624 messageListeners.onSubscriptionMessage(message.action, message.args[0]);
625 break;
626 }
627 });
628
629 ext.backgroundPage.sendMessage(
630 {
631 type: "app.listen",
632 filter: ["addSubscription"]
633 });
634 ext.backgroundPage.sendMessage(
635 {
636 type: "filters.listen",
637 filter: ["added", "loaded", "removed"]
638 });
639 ext.backgroundPage.sendMessage(
640 {
641 type: "subscriptions.listen",
642 filter: ["added", "disabled", "homepage", "removed", "title"]
643 });
644
645 window.addEventListener("DOMContentLoaded", onDOMLoaded, false);
646 })();
OLDNEW
« no previous file with comments | « options.html ('k') | skin/options.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld