OLD | NEW |
| (Empty) |
1 /* | |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | |
3 * Copyright (C) 2006-present 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 /* global $, i18nTimeDateStrings */ | |
19 | |
20 "use strict"; | |
21 | |
22 /** | |
23 * Creates a wrapping function used to conveniently send a type of message. | |
24 * | |
25 * @param {Object} baseMessage The part of the message that's always sent | |
26 * @param {...string} paramKeys Any message keys that have dynamic values. The | |
27 * returned function will take the corresponding | |
28 * values as arguments. | |
29 * @return {function} The generated messaging function, optionally | |
30 * taking any values as specified by the paramKeys | |
31 * and finally an optional callback. (Although the | |
32 * value arguments are optional their index must be | |
33 * maintained. E.g. if you omit the first value you | |
34 * must omit the second too.) | |
35 */ | |
36 function wrapper(baseMessage, ...paramKeys) | |
37 { | |
38 return function(...paramValues /* , callback */) | |
39 { | |
40 let message = Object.assign(Object.create(null), baseMessage); | |
41 let callback; | |
42 | |
43 if (paramValues.length > 0) | |
44 { | |
45 let lastArg = paramValues[paramValues.length - 1]; | |
46 if (typeof lastArg == "function") | |
47 callback = lastArg; | |
48 | |
49 for (let i = 0; i < paramValues.length - (callback ? 1 : 0); i++) | |
50 message[paramKeys[i]] = paramValues[i]; | |
51 } | |
52 | |
53 browser.runtime.sendMessage(message, callback); | |
54 }; | |
55 } | |
56 | |
57 const getDocLink = wrapper({type: "app.get", what: "doclink"}, "link"); | |
58 const getInfo = wrapper({type: "app.get"}, "what"); | |
59 const getPref = wrapper({type: "prefs.get"}, "key"); | |
60 const togglePref = wrapper({type: "prefs.toggle"}, "key"); | |
61 const getSubscriptions = wrapper({type: "subscriptions.get"}, | |
62 "downloadable", "special"); | |
63 const removeSubscription = wrapper({type: "subscriptions.remove"}, "url"); | |
64 const addSubscription = wrapper({type: "subscriptions.add"}, | |
65 "url", "title", "homepage"); | |
66 const toggleSubscription = wrapper({type: "subscriptions.toggle"}, | |
67 "url", "keepInstalled"); | |
68 const updateSubscription = wrapper({type: "subscriptions.update"}, "url"); | |
69 const importRawFilters = wrapper({type: "filters.importRaw"}, | |
70 "text", "removeExisting"); | |
71 const addFilter = wrapper({type: "filters.add"}, "text"); | |
72 const removeFilter = wrapper({type: "filters.remove"}, "text"); | |
73 const quoteCSS = wrapper({type: "composer.quoteCSS"}, "CSS"); | |
74 | |
75 const whitelistedDomainRegexp = /^@@\|\|([^/:]+)\^\$document$/; | |
76 const statusMessages = new Map([ | |
77 ["synchronize_invalid_url", | |
78 "filters_subscription_lastDownload_invalidURL"], | |
79 ["synchronize_connection_error", | |
80 "filters_subscription_lastDownload_connectionError"], | |
81 ["synchronize_invalid_data", | |
82 "filters_subscription_lastDownload_invalidData"], | |
83 ["synchronize_checksum_mismatch", | |
84 "filters_subscription_lastDownload_checksumMismatch"] | |
85 ]); | |
86 | |
87 let delayedSubscriptionSelection = null; | |
88 let acceptableAdsUrl; | |
89 | |
90 // Loads options from localStorage and sets UI elements accordingly | |
91 function loadOptions() | |
92 { | |
93 // Set page title to i18n version of "Adblock Plus Options" | |
94 document.title = browser.i18n.getMessage("options"); | |
95 | |
96 // Set links | |
97 getPref("subscriptions_exceptionsurl", url => | |
98 { | |
99 acceptableAdsUrl = url; | |
100 $("#acceptableAdsLink").attr("href", acceptableAdsUrl); | |
101 }); | |
102 getDocLink("acceptable_ads", url => | |
103 { | |
104 $("#acceptableAdsDocs").attr("href", url); | |
105 }); | |
106 getDocLink("filterdoc", url => | |
107 { | |
108 setLinks("filter-must-follow-syntax", url); | |
109 }); | |
110 getInfo("application", application => | |
111 { | |
112 getInfo("platform", platform => | |
113 { | |
114 if (platform == "chromium" && application != "opera") | |
115 application = "chrome"; | |
116 | |
117 getDocLink(application + "_support", url => | |
118 { | |
119 setLinks("found-a-bug", url); | |
120 }); | |
121 | |
122 if (platform == "gecko") | |
123 $("#firefox-warning").removeAttr("hidden"); | |
124 }); | |
125 }); | |
126 | |
127 // Add event listeners | |
128 $("#updateFilterLists").click(updateFilterLists); | |
129 $("#startSubscriptionSelection").click(startSubscriptionSelection); | |
130 $("#subscriptionSelector").change(updateSubscriptionSelection); | |
131 $("#addSubscription").click(addSubscriptionClicked); | |
132 $("#acceptableAds").click(toggleAcceptableAds); | |
133 $("#whitelistForm").submit(addWhitelistDomain); | |
134 $("#removeWhitelist").click(removeSelectedExcludedDomain); | |
135 $("#customFilterForm").submit(addTypedFilter); | |
136 $("#removeCustomFilter").click(removeSelectedFilters); | |
137 $("#rawFiltersButton").click(toggleFiltersInRawFormat); | |
138 $("#importRawFilters").click(importRawFiltersText); | |
139 | |
140 // Display jQuery UI elements | |
141 $("#tabs").tabs(); | |
142 $("button:not(.subscriptionRemoveButton)").button(); | |
143 $(".refreshButton").button("option", "icons", {primary: "ui-icon-refresh"}); | |
144 $(".addButton").button("option", "icons", {primary: "ui-icon-plus"}); | |
145 $(".removeButton").button("option", "icons", {primary: "ui-icon-minus"}); | |
146 | |
147 // Popuplate option checkboxes | |
148 initCheckbox("shouldShowBlockElementMenu"); | |
149 initCheckbox("show_devtools_panel"); | |
150 initCheckbox("shouldShowNotifications", "notifications_ignoredcategories"); | |
151 | |
152 getInfo("features", features => | |
153 { | |
154 if (!features.devToolsPanel) | |
155 document.getElementById("showDevtoolsPanelContainer").hidden = true; | |
156 }); | |
157 getPref("notifications_showui", showNotificationsUI => | |
158 { | |
159 if (!showNotificationsUI) | |
160 document.getElementById("shouldShowNotificationsContainer").hidden = true; | |
161 }); | |
162 | |
163 // Register listeners in the background message responder | |
164 browser.runtime.sendMessage({ | |
165 type: "app.listen", | |
166 filter: ["addSubscription", "focusSection"] | |
167 }); | |
168 browser.runtime.sendMessage({ | |
169 type: "filters.listen", | |
170 filter: ["added", "loaded", "removed"] | |
171 }); | |
172 browser.runtime.sendMessage({ | |
173 type: "prefs.listen", | |
174 filter: ["notifications_ignoredcategories", "notifications_showui", | |
175 "show_devtools_panel", "shouldShowBlockElementMenu"] | |
176 }); | |
177 browser.runtime.sendMessage({ | |
178 type: "subscriptions.listen", | |
179 filter: ["added", "disabled", "homepage", "lastDownload", "removed", | |
180 "title", "downloadStatus", "downloading"] | |
181 }); | |
182 | |
183 // Load recommended subscriptions | |
184 loadRecommendations(); | |
185 | |
186 // Show user's filters | |
187 reloadFilters(); | |
188 } | |
189 | |
190 function convertSpecialSubscription(subscription) | |
191 { | |
192 for (let filter of subscription.filters) | |
193 { | |
194 if (whitelistedDomainRegexp.test(filter.text)) | |
195 appendToListBox("excludedDomainsBox", RegExp.$1); | |
196 else | |
197 appendToListBox("userFiltersBox", filter.text); | |
198 } | |
199 } | |
200 | |
201 // Reloads the displayed subscriptions and filters | |
202 function reloadFilters() | |
203 { | |
204 // Load user filter URLs | |
205 let container = document.getElementById("filterLists"); | |
206 while (container.lastChild) | |
207 container.removeChild(container.lastChild); | |
208 | |
209 getSubscriptions(true, false, subscriptions => | |
210 { | |
211 for (let subscription of subscriptions) | |
212 { | |
213 if (subscription.url == acceptableAdsUrl) | |
214 $("#acceptableAds").prop("checked", !subscription.disabled); | |
215 else | |
216 addSubscriptionEntry(subscription); | |
217 } | |
218 }); | |
219 | |
220 // User-entered filters | |
221 getSubscriptions(false, true, subscriptions => | |
222 { | |
223 document.getElementById("userFiltersBox").innerHTML = ""; | |
224 document.getElementById("excludedDomainsBox").innerHTML = ""; | |
225 | |
226 for (let subscription of subscriptions) | |
227 convertSpecialSubscription(subscription); | |
228 }); | |
229 } | |
230 | |
231 function initCheckbox(id, key) | |
232 { | |
233 key = key || id; | |
234 let checkbox = document.getElementById(id); | |
235 | |
236 getPref(key, value => | |
237 { | |
238 onPrefMessage(key, value); | |
239 }); | |
240 | |
241 checkbox.addEventListener("click", () => | |
242 { | |
243 togglePref(key); | |
244 }, false); | |
245 } | |
246 | |
247 function loadRecommendations() | |
248 { | |
249 fetch("subscriptions.xml") | |
250 .then(response => | |
251 { | |
252 return response.text(); | |
253 }) | |
254 .then(text => | |
255 { | |
256 let selectedIndex = 0; | |
257 let selectedPrefix = null; | |
258 let matchCount = 0; | |
259 | |
260 let list = document.getElementById("subscriptionSelector"); | |
261 let doc = new DOMParser().parseFromString(text, "application/xml"); | |
262 let elements = doc.documentElement.getElementsByTagName("subscription"); | |
263 | |
264 for (let i = 0; i < elements.length; i++) | |
265 { | |
266 let element = elements[i]; | |
267 let option = new Option(); | |
268 option.text = element.getAttribute("title") + " (" + | |
269 element.getAttribute("specialization") + ")"; | |
270 option._data = { | |
271 title: element.getAttribute("title"), | |
272 url: element.getAttribute("url"), | |
273 homepage: element.getAttribute("homepage") | |
274 }; | |
275 | |
276 let prefix = element.getAttribute("prefixes"); | |
277 if (prefix) | |
278 { | |
279 prefix = prefix.replace(/\W/g, "_"); | |
280 option.style.fontWeight = "bold"; | |
281 option.style.backgroundColor = "#E0FFE0"; | |
282 option.style.color = "#000000"; | |
283 if (!selectedPrefix || selectedPrefix.length < prefix.length) | |
284 { | |
285 selectedIndex = i; | |
286 selectedPrefix = prefix; | |
287 matchCount = 1; | |
288 } | |
289 else if (selectedPrefix && selectedPrefix.length == prefix.length) | |
290 { | |
291 matchCount++; | |
292 | |
293 // If multiple items have a matching prefix of the same length: | |
294 // Select one of the items randomly, probability should be the same | |
295 // for all items. So we replace the previous match here with | |
296 // probability 1/N (N being the number of matches). | |
297 if (Math.random() * matchCount < 1) | |
298 { | |
299 selectedIndex = i; | |
300 selectedPrefix = prefix; | |
301 } | |
302 } | |
303 } | |
304 list.appendChild(option); | |
305 } | |
306 | |
307 let option = new Option(); | |
308 let label = browser.i18n.getMessage("filters_addSubscriptionOther_label"); | |
309 option.text = label + "\u2026"; | |
310 option._data = null; | |
311 list.appendChild(option); | |
312 | |
313 list.selectedIndex = selectedIndex; | |
314 | |
315 if (delayedSubscriptionSelection) | |
316 startSubscriptionSelection(...delayedSubscriptionSelection); | |
317 }); | |
318 } | |
319 | |
320 function startSubscriptionSelection(title, url) | |
321 { | |
322 let list = document.getElementById("subscriptionSelector"); | |
323 if (list.length == 0) | |
324 { | |
325 delayedSubscriptionSelection = [title, url]; | |
326 return; | |
327 } | |
328 | |
329 $("#tabs").tabs("option", "active", 0); | |
330 $("#addSubscriptionContainer").show(); | |
331 $("#addSubscriptionButton").hide(); | |
332 $("#subscriptionSelector").focus(); | |
333 if (typeof url != "undefined") | |
334 { | |
335 list.selectedIndex = list.length - 1; | |
336 document.getElementById("customSubscriptionTitle").value = title; | |
337 document.getElementById("customSubscriptionLocation").value = url; | |
338 } | |
339 updateSubscriptionSelection(); | |
340 document.getElementById("addSubscriptionContainer").scrollIntoView(true); | |
341 } | |
342 | |
343 function updateSubscriptionSelection() | |
344 { | |
345 let list = document.getElementById("subscriptionSelector"); | |
346 let data = list.options[list.selectedIndex]._data; | |
347 if (data) | |
348 $("#customSubscriptionContainer").hide(); | |
349 else | |
350 { | |
351 $("#customSubscriptionContainer").show(); | |
352 $("#customSubscriptionTitle").focus(); | |
353 } | |
354 } | |
355 | |
356 function addSubscriptionClicked() | |
357 { | |
358 let list = document.getElementById("subscriptionSelector"); | |
359 let data = list.options[list.selectedIndex]._data; | |
360 if (data) | |
361 addSubscription(data.url, data.title, data.homepage); | |
362 else | |
363 { | |
364 let url = document.getElementById("customSubscriptionLocation") | |
365 .value.trim(); | |
366 if (!/^https?:/i.test(url)) | |
367 { | |
368 alert(browser.i18n.getMessage("global_subscription_invalid_location")); | |
369 $("#customSubscriptionLocation").focus(); | |
370 return; | |
371 } | |
372 | |
373 let title = document.getElementById("customSubscriptionTitle").value.trim(); | |
374 if (!title) | |
375 title = url; | |
376 | |
377 addSubscription(url, title, null); | |
378 } | |
379 | |
380 $("#addSubscriptionContainer").hide(); | |
381 $("#customSubscriptionContainer").hide(); | |
382 $("#addSubscriptionButton").show(); | |
383 } | |
384 | |
385 function toggleAcceptableAds() | |
386 { | |
387 toggleSubscription(acceptableAdsUrl, true); | |
388 } | |
389 | |
390 function findSubscriptionElement(subscription) | |
391 { | |
392 for (let child of document.getElementById("filterLists").childNodes) | |
393 { | |
394 if (child._subscription.url == subscription.url) | |
395 return child; | |
396 } | |
397 return null; | |
398 } | |
399 | |
400 function updateSubscriptionInfo(element, subscription) | |
401 { | |
402 if (subscription) | |
403 element._subscription = subscription; | |
404 else | |
405 subscription = element._subscription; | |
406 | |
407 let title = element.getElementsByClassName("subscriptionTitle")[0]; | |
408 title.textContent = subscription.title; | |
409 title.setAttribute("title", subscription.url); | |
410 if (subscription.homepage) | |
411 title.href = subscription.homepage; | |
412 else | |
413 title.href = subscription.url; | |
414 | |
415 let enabled = element.getElementsByClassName("subscriptionEnabled")[0]; | |
416 enabled.checked = !subscription.disabled; | |
417 | |
418 let lastUpdate = element.getElementsByClassName("subscriptionUpdate")[0]; | |
419 lastUpdate.classList.remove("error"); | |
420 | |
421 let {downloadStatus} = subscription; | |
422 if (subscription.isDownloading) | |
423 { | |
424 lastUpdate.textContent = browser.i18n.getMessage( | |
425 "filters_subscription_lastDownload_inProgress" | |
426 ); | |
427 } | |
428 else if (downloadStatus && downloadStatus != "synchronize_ok") | |
429 { | |
430 if (statusMessages.has(downloadStatus)) | |
431 { | |
432 lastUpdate.textContent = browser.i18n.getMessage( | |
433 statusMessages.get(downloadStatus) | |
434 ); | |
435 } | |
436 else | |
437 lastUpdate.textContent = downloadStatus; | |
438 lastUpdate.classList.add("error"); | |
439 } | |
440 else if (subscription.lastDownload > 0) | |
441 { | |
442 let timeDate = i18nTimeDateStrings(subscription.lastDownload * 1000); | |
443 let messageID = (timeDate[1] ? "last_updated_at" : "last_updated_at_today"); | |
444 lastUpdate.textContent = browser.i18n.getMessage(messageID, timeDate); | |
445 } | |
446 } | |
447 | |
448 function onSubscriptionMessage(action, subscription) | |
449 { | |
450 let element = findSubscriptionElement(subscription); | |
451 | |
452 switch (action) | |
453 { | |
454 case "disabled": | |
455 case "downloading": | |
456 case "downloadStatus": | |
457 case "homepage": | |
458 case "lastDownload": | |
459 case "title": | |
460 if (element) | |
461 updateSubscriptionInfo(element, subscription); | |
462 break; | |
463 case "added": | |
464 if (subscription.url.indexOf("~user") == 0) | |
465 convertSpecialSubscription(subscription); | |
466 else if (subscription.url == acceptableAdsUrl) | |
467 $("#acceptableAds").prop("checked", true); | |
468 else if (!element) | |
469 addSubscriptionEntry(subscription); | |
470 break; | |
471 case "removed": | |
472 if (subscription.url == acceptableAdsUrl) | |
473 $("#acceptableAds").prop("checked", false); | |
474 else if (element) | |
475 element.parentNode.removeChild(element); | |
476 break; | |
477 } | |
478 } | |
479 | |
480 function onPrefMessage(key, value) | |
481 { | |
482 switch (key) | |
483 { | |
484 case "notifications_showui": | |
485 document.getElementById( | |
486 "shouldShowNotificationsContainer" | |
487 ).hidden = !value; | |
488 return; | |
489 case "notifications_ignoredcategories": | |
490 key = "shouldShowNotifications"; | |
491 value = value.indexOf("*") == -1; | |
492 break; | |
493 } | |
494 let checkbox = document.getElementById(key); | |
495 if (checkbox) | |
496 checkbox.checked = value; | |
497 } | |
498 | |
499 function onFilterMessage(action, filter) | |
500 { | |
501 switch (action) | |
502 { | |
503 case "loaded": | |
504 reloadFilters(); | |
505 break; | |
506 case "added": | |
507 if (whitelistedDomainRegexp.test(filter.text)) | |
508 appendToListBox("excludedDomainsBox", RegExp.$1); | |
509 else | |
510 appendToListBox("userFiltersBox", filter.text); | |
511 break; | |
512 case "removed": | |
513 if (whitelistedDomainRegexp.test(filter.text)) | |
514 removeFromListBox("excludedDomainsBox", RegExp.$1); | |
515 else | |
516 removeFromListBox("userFiltersBox", filter.text); | |
517 break; | |
518 } | |
519 } | |
520 | |
521 // Add a filter string to the list box. | |
522 function appendToListBox(boxId, text) | |
523 { | |
524 // Note: document.createElement("option") is unreliable in Opera | |
525 let elt = new Option(); | |
526 elt.text = text; | |
527 elt.value = text; | |
528 document.getElementById(boxId).appendChild(elt); | |
529 } | |
530 | |
531 // Remove a filter string from a list box. | |
532 function removeFromListBox(boxId, text) | |
533 { | |
534 let list = document.getElementById(boxId); | |
535 // Edge does not support CSS.escape yet: | |
536 // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/101410
/ | |
537 quoteCSS(text, escapedCSS => | |
538 { | |
539 let selector = "option[value=" + escapedCSS + "]"; | |
540 for (let option of list.querySelectorAll(selector)) | |
541 list.removeChild(option); | |
542 }); | |
543 } | |
544 | |
545 function addWhitelistDomain(event) | |
546 { | |
547 event.preventDefault(); | |
548 | |
549 let domain = document.getElementById( | |
550 "newWhitelistDomain" | |
551 ).value.replace(/\s/g, ""); | |
552 document.getElementById("newWhitelistDomain").value = ""; | |
553 if (!domain) | |
554 return; | |
555 | |
556 let filterText = "@@||" + domain + "^$document"; | |
557 addFilter(filterText); | |
558 } | |
559 | |
560 // Adds filter text that user typed to the selection box | |
561 function addTypedFilter(event) | |
562 { | |
563 event.preventDefault(); | |
564 | |
565 let element = document.getElementById("newFilter"); | |
566 addFilter(element.value, errors => | |
567 { | |
568 if (errors.length > 0) | |
569 alert(errors.join("\n")); | |
570 else | |
571 element.value = ""; | |
572 }); | |
573 } | |
574 | |
575 // Removes currently selected whitelisted domains | |
576 function removeSelectedExcludedDomain(event) | |
577 { | |
578 event.preventDefault(); | |
579 let remove = []; | |
580 for (let option of document.getElementById("excludedDomainsBox").options) | |
581 { | |
582 if (option.selected) | |
583 remove.push(option.value); | |
584 } | |
585 if (!remove.length) | |
586 return; | |
587 | |
588 for (let domain of remove) | |
589 removeFilter("@@||" + domain + "^$document"); | |
590 } | |
591 | |
592 // Removes all currently selected filters | |
593 function removeSelectedFilters(event) | |
594 { | |
595 event.preventDefault(); | |
596 let options = document.querySelectorAll("#userFiltersBox > option:checked"); | |
597 for (let option of options) | |
598 removeFilter(option.value); | |
599 } | |
600 | |
601 // Shows raw filters box and fills it with the current user filters | |
602 function toggleFiltersInRawFormat(event) | |
603 { | |
604 event.preventDefault(); | |
605 | |
606 let rawFilters = document.getElementById("rawFilters"); | |
607 let filters = []; | |
608 | |
609 if (rawFilters.style.display != "table-row") | |
610 { | |
611 rawFilters.style.display = "table-row"; | |
612 for (let option of document.getElementById("userFiltersBox").options) | |
613 filters.push(option.value); | |
614 } | |
615 else | |
616 { | |
617 rawFilters.style.display = "none"; | |
618 } | |
619 | |
620 document.getElementById("rawFiltersText").value = filters.join("\n"); | |
621 } | |
622 | |
623 // Imports filters in the raw text box | |
624 function importRawFiltersText() | |
625 { | |
626 let text = document.getElementById("rawFiltersText").value; | |
627 | |
628 importRawFilters(text, true, errors => | |
629 { | |
630 if (errors.length > 0) | |
631 alert(errors.join("\n")); | |
632 else | |
633 $("#rawFilters").hide(); | |
634 }); | |
635 } | |
636 | |
637 // Called when user explicitly requests filter list updates | |
638 function updateFilterLists() | |
639 { | |
640 // Without the URL parameter this will update all subscriptions | |
641 updateSubscription(); | |
642 } | |
643 | |
644 // Adds a subscription entry to the UI. | |
645 function addSubscriptionEntry(subscription) | |
646 { | |
647 let template = document.getElementById("subscriptionTemplate"); | |
648 let element = template.cloneNode(true); | |
649 element.removeAttribute("id"); | |
650 element._subscription = subscription; | |
651 | |
652 let removeButton = element.getElementsByClassName( | |
653 "subscriptionRemoveButton" | |
654 )[0]; | |
655 removeButton.setAttribute("title", removeButton.textContent); | |
656 removeButton.textContent = "\xD7"; | |
657 removeButton.addEventListener("click", () => | |
658 { | |
659 if (!confirm(browser.i18n.getMessage("global_remove_subscription_warning"))) | |
660 return; | |
661 | |
662 removeSubscription(subscription.url); | |
663 }, false); | |
664 | |
665 getPref("additional_subscriptions", additionalSubscriptions => | |
666 { | |
667 if (additionalSubscriptions.includes(subscription.url)) | |
668 removeButton.style.visibility = "hidden"; | |
669 }); | |
670 | |
671 let enabled = element.getElementsByClassName("subscriptionEnabled")[0]; | |
672 enabled.addEventListener("click", () => | |
673 { | |
674 subscription.disabled = !subscription.disabled; | |
675 toggleSubscription(subscription.url, true); | |
676 }, false); | |
677 | |
678 updateSubscriptionInfo(element); | |
679 | |
680 document.getElementById("filterLists").appendChild(element); | |
681 } | |
682 | |
683 function setLinks(id, ...args) | |
684 { | |
685 let element = document.getElementById(id); | |
686 if (!element) | |
687 return; | |
688 | |
689 let links = element.getElementsByTagName("a"); | |
690 for (let i = 0; i < links.length; i++) | |
691 { | |
692 if (typeof args[i] == "string") | |
693 { | |
694 links[i].href = args[i]; | |
695 links[i].setAttribute("target", "_blank"); | |
696 } | |
697 else if (typeof args[i] == "function") | |
698 { | |
699 links[i].href = "javascript:void(0);"; | |
700 links[i].addEventListener("click", args[i], false); | |
701 } | |
702 } | |
703 } | |
704 | |
705 document.addEventListener("DOMContentLoaded", loadOptions); | |
706 | |
707 ext.onMessage.addListener(message => | |
708 { | |
709 switch (message.type) | |
710 { | |
711 case "app.respond": | |
712 switch (message.action) | |
713 { | |
714 case "addSubscription": | |
715 let subscription = message.args[0]; | |
716 startSubscriptionSelection(subscription.title, subscription.url); | |
717 break; | |
718 case "focusSection": | |
719 for (let tab of document.getElementsByClassName("ui-tabs-panel")) | |
720 { | |
721 let found = tab.querySelector( | |
722 "[data-section='" + message.args[0] + "']" | |
723 ); | |
724 if (!found) | |
725 continue; | |
726 | |
727 let previous = document.getElementsByClassName("focused"); | |
728 if (previous.length > 0) | |
729 previous[0].classList.remove("focused"); | |
730 | |
731 let index = $("[href='#" + tab.id + "']").parent().index(); | |
732 $("#tabs").tabs("option", "active", index); | |
733 found.classList.add("focused"); | |
734 } | |
735 break; | |
736 } | |
737 break; | |
738 case "filters.respond": | |
739 onFilterMessage(message.action, message.args[0]); | |
740 break; | |
741 case "prefs.respond": | |
742 onPrefMessage(message.action, message.args[0]); | |
743 break; | |
744 case "subscriptions.respond": | |
745 onSubscriptionMessage(message.action, message.args[0]); | |
746 break; | |
747 } | |
748 }); | |
OLD | NEW |