OLD | NEW |
(Empty) | |
| 1 var backgroundPage = opera.extension.bgProcess; |
| 2 var imports = ["FilterStorage", "FilterNotifier", "Subscription", "SpecialSubscr
iption", |
| 3 "DownloadableSubscription", "Filter", "WhitelistFilter", |
| 4 "Synchronizer", "Prefs", "Utils", "require"]; |
| 5 for (var i = 0; i < imports.length; i++) |
| 6 window[imports[i]] = backgroundPage[imports[i]]; |
| 7 |
| 8 // Loads options from localStorage and sets UI elements accordingly |
| 9 function loadOptions() |
| 10 { |
| 11 // Set page title to i18n version of "Adblock Plus Options" |
| 12 document.title = i18n.getMessage("options"); |
| 13 |
| 14 // Set links |
| 15 $("#acceptableAdsLink").attr("href", Prefs.subscriptions_exceptionsurl); |
| 16 $("#acceptableAdsDocs").attr("href", Prefs.documentation_link.replace(/%LINK%/
g, "acceptable_ads").replace(/%LANG%/g, Utils.appLocale)); |
| 17 |
| 18 // Add event listeners |
| 19 window.addEventListener("unload", unloadOptions, false); |
| 20 $("#updateFilterLists").click(updateFilterLists); |
| 21 $("#startSubscriptionSelection").click(startSubscriptionSelection); |
| 22 $("#subscriptionSelector").change(updateSubscriptionSelection); |
| 23 $("#addSubscription").click(addSubscription); |
| 24 $("#acceptableAds").click(allowAcceptableAds); |
| 25 $("#whitelistForm").submit(addWhitelistDomain); |
| 26 $("#removeWhitelist").click(removeSelectedExcludedDomain); |
| 27 $("#customFilterForm").submit(addTypedFilter); |
| 28 $("#removeCustomFilter").click(removeSelectedFilters); |
| 29 $("#rawFiltersButton").click(toggleFiltersInRawFormat); |
| 30 $("#importRawFilters").click(importRawFiltersText); |
| 31 FilterNotifier.addListener(onFilterChange); |
| 32 |
| 33 // Display jQuery UI elements |
| 34 $("#tabs").tabs(); |
| 35 $("button").button(); |
| 36 $(".refreshButton").button("option", "icons", {primary: "ui-icon-refresh"}); |
| 37 $(".addButton").button("option", "icons", {primary: "ui-icon-plus"}); |
| 38 $(".removeButton").button("option", "icons", {primary: "ui-icon-minus"}); |
| 39 |
| 40 // Popuplate option checkboxes |
| 41 initCheckbox("shouldShowIcon"); |
| 42 initCheckbox("shouldShowBlockElementMenu"); |
| 43 initCheckbox("disableInlineTextAds"); |
| 44 initCheckbox("hidePlaceholders"); |
| 45 |
| 46 // Load recommended subscriptions |
| 47 loadRecommendations(); |
| 48 |
| 49 // Show user's filters |
| 50 reloadFilters(); |
| 51 } |
| 52 $(loadOptions); |
| 53 |
| 54 // Reloads the displayed subscriptions and filters |
| 55 function reloadFilters() |
| 56 { |
| 57 // Load user filter URLs |
| 58 var container = document.getElementById("filterLists"); |
| 59 while (container.lastChild) |
| 60 container.removeChild(container.lastChild); |
| 61 |
| 62 var hasAcceptable = false; |
| 63 for (var i = 0; i < FilterStorage.subscriptions.length; i++) |
| 64 { |
| 65 var subscription = FilterStorage.subscriptions[i]; |
| 66 if (subscription instanceof SpecialSubscription) |
| 67 continue; |
| 68 |
| 69 if (subscription.url == Prefs.subscriptions_exceptionsurl) |
| 70 { |
| 71 hasAcceptable = true; |
| 72 continue; |
| 73 } |
| 74 |
| 75 addSubscriptionEntry(subscription); |
| 76 } |
| 77 |
| 78 $("#acceptableAds").prop("checked", hasAcceptable); |
| 79 |
| 80 // User-entered filters |
| 81 showUserFilters(); |
| 82 } |
| 83 |
| 84 // Cleans up when the options window is closed |
| 85 function unloadOptions() |
| 86 { |
| 87 FilterNotifier.removeListener(onFilterChange); |
| 88 } |
| 89 |
| 90 function initCheckbox(id) |
| 91 { |
| 92 var checkbox = document.getElementById(id); |
| 93 checkbox.checked = typeof localStorage[id] == "undefined" ? true : localStorag
e[id] == "true"; |
| 94 checkbox.addEventListener("click", function() |
| 95 { |
| 96 localStorage[id] = checkbox.checked; |
| 97 }, false); |
| 98 } |
| 99 |
| 100 function showUserFilters() |
| 101 { |
| 102 var filters = []; |
| 103 var exceptions = []; |
| 104 for (var i = 0; i < FilterStorage.subscriptions.length; i++) |
| 105 { |
| 106 var subscription = FilterStorage.subscriptions[i]; |
| 107 if (!(subscription instanceof SpecialSubscription)) |
| 108 continue; |
| 109 |
| 110 for (var j = 0; j < subscription.filters.length; j++) |
| 111 { |
| 112 var filter = subscription.filters[j]; |
| 113 if (filter instanceof WhitelistFilter && /^@@\|\|([^\/:]+)\^\$document$/.t
est(filter.text)) |
| 114 exceptions.push(RegExp.$1) |
| 115 else |
| 116 filters.push(filter.text); |
| 117 } |
| 118 } |
| 119 |
| 120 populateList("userFiltersBox", filters); |
| 121 populateList("excludedDomainsBox", exceptions); |
| 122 } |
| 123 |
| 124 var delayedSubscriptionSelection = null; |
| 125 |
| 126 function loadRecommendations() |
| 127 { |
| 128 var request = new XMLHttpRequest(); |
| 129 request.open("GET", "subscriptions.xml"); |
| 130 request.onload = function() |
| 131 { |
| 132 var selectedIndex = 0; |
| 133 var selectedPrefix = null; |
| 134 var matchCount = 0; |
| 135 |
| 136 var list = document.getElementById("subscriptionSelector"); |
| 137 var elements = request.responseXML.documentElement.getElementsByTagName("sub
scription"); |
| 138 for (var i = 0; i < elements.length; i++) |
| 139 { |
| 140 var element = elements[i]; |
| 141 var option = document.createElement("option"); |
| 142 option.text = element.getAttribute("title") + " (" + element.getAttribute(
"specialization") + ")"; |
| 143 option._data = { |
| 144 title: element.getAttribute("title"), |
| 145 url: element.getAttribute("url"), |
| 146 homepage: element.getAttribute("homepage") |
| 147 }; |
| 148 |
| 149 var prefix = require("utils").Utils.checkLocalePrefixMatch(element.getAttr
ibute("prefixes")); |
| 150 if (prefix) |
| 151 { |
| 152 option.style.fontWeight = "bold"; |
| 153 option.style.backgroundColor = "#E0FFE0"; |
| 154 option.style.color = "#000000"; |
| 155 if (!selectedPrefix || selectedPrefix.length < prefix.length) |
| 156 { |
| 157 selectedIndex = i; |
| 158 selectedPrefix = prefix; |
| 159 matchCount = 1; |
| 160 } |
| 161 else if (selectedPrefix && selectedPrefix.length == prefix.length) |
| 162 { |
| 163 matchCount++; |
| 164 |
| 165 // If multiple items have a matching prefix of the same length: |
| 166 // Select one of the items randomly, probability should be the same |
| 167 // for all items. So we replace the previous match here with |
| 168 // probability 1/N (N being the number of matches). |
| 169 if (Math.random() * matchCount < 1) |
| 170 { |
| 171 selectedIndex = i; |
| 172 selectedPrefix = prefix; |
| 173 } |
| 174 } |
| 175 } |
| 176 list.appendChild(option); |
| 177 } |
| 178 |
| 179 var option = document.createElement("option"); |
| 180 option.text = i18n.getMessage("filters_addSubscriptionOther_label") + "\u202
6"; |
| 181 option._data = null; |
| 182 list.appendChild(option); |
| 183 |
| 184 list.selectedIndex = selectedIndex; |
| 185 |
| 186 if (delayedSubscriptionSelection) |
| 187 startSubscriptionSelection.apply(null, delayedSubscriptionSelection); |
| 188 }; |
| 189 request.send(null); |
| 190 } |
| 191 |
| 192 function startSubscriptionSelection(title, url) |
| 193 { |
| 194 var list = document.getElementById("subscriptionSelector"); |
| 195 if (list.length == 0) |
| 196 { |
| 197 delayedSubscriptionSelection = [title, url]; |
| 198 return; |
| 199 } |
| 200 |
| 201 $('#tabs').tabs('select', 0); |
| 202 $("#addSubscriptionContainer").show(); |
| 203 $("#addSubscriptionButton").hide(); |
| 204 $("#subscriptionSelector").focus(); |
| 205 if (typeof url != "undefined") |
| 206 { |
| 207 list.selectedIndex = list.length - 1; |
| 208 document.getElementById("customSubscriptionTitle").value = title; |
| 209 document.getElementById("customSubscriptionLocation").value = url; |
| 210 } |
| 211 updateSubscriptionSelection(); |
| 212 document.getElementById("addSubscriptionContainer").scrollIntoView(true); |
| 213 } |
| 214 |
| 215 function updateSubscriptionSelection() |
| 216 { |
| 217 var list = document.getElementById("subscriptionSelector"); |
| 218 var data = list.options[list.selectedIndex]._data; |
| 219 if (data) |
| 220 $("#customSubscriptionContainer").hide(); |
| 221 else |
| 222 { |
| 223 $("#customSubscriptionContainer").show(); |
| 224 $("#customSubscriptionTitle").focus(); |
| 225 } |
| 226 } |
| 227 |
| 228 function addSubscription() |
| 229 { |
| 230 var list = document.getElementById("subscriptionSelector"); |
| 231 var data = list.options[list.selectedIndex]._data; |
| 232 if (data) |
| 233 doAddSubscription(data.url, data.title, data.homepage); |
| 234 else |
| 235 { |
| 236 var url = document.getElementById("customSubscriptionLocation").value.replac
e(/^\s+/, "").replace(/\s+$/, ""); |
| 237 if (!/^https?:/i.test(url)) |
| 238 { |
| 239 alert(i18n.getMessage("global_subscription_invalid_location")); |
| 240 $("#customSubscriptionLocation").focus(); |
| 241 return; |
| 242 } |
| 243 |
| 244 var title = document.getElementById("customSubscriptionTitle").value.replace
(/^\s+/, "").replace(/\s+$/, ""); |
| 245 if (!title) |
| 246 title = url; |
| 247 |
| 248 doAddSubscription(url, title, null); |
| 249 } |
| 250 |
| 251 $("#addSubscriptionContainer").hide(); |
| 252 $("#customSubscriptionContainer").hide(); |
| 253 $("#addSubscriptionButton").show(); |
| 254 } |
| 255 |
| 256 function doAddSubscription(url, title, homepage) |
| 257 { |
| 258 if (url in FilterStorage.knownSubscriptions) |
| 259 return; |
| 260 |
| 261 var subscription = Subscription.fromURL(url); |
| 262 if (!subscription) |
| 263 return; |
| 264 |
| 265 subscription.title = title; |
| 266 if (homepage) |
| 267 subscription.homepage = homepage; |
| 268 FilterStorage.addSubscription(subscription); |
| 269 |
| 270 if (subscription instanceof DownloadableSubscription && !subscription.lastDown
load) |
| 271 Synchronizer.execute(subscription); |
| 272 } |
| 273 |
| 274 function allowAcceptableAds(event) |
| 275 { |
| 276 var subscription = Subscription.fromURL(Prefs.subscriptions_exceptionsurl); |
| 277 if (!subscription) |
| 278 return; |
| 279 |
| 280 subscription.disabled = false; |
| 281 subscription.title = "Allow non-intrusive advertising"; |
| 282 if ($("#acceptableAds").prop("checked")) |
| 283 { |
| 284 FilterStorage.addSubscription(subscription); |
| 285 if (subscription instanceof DownloadableSubscription && !subscription.lastDo
wnload) |
| 286 Synchronizer.execute(subscription); |
| 287 } |
| 288 else |
| 289 FilterStorage.removeSubscription(subscription); |
| 290 } |
| 291 |
| 292 function findSubscriptionElement(subscription) |
| 293 { |
| 294 var children = document.getElementById("filterLists").childNodes; |
| 295 for (var i = 0; i < children.length; i++) |
| 296 if (children[i]._subscription == subscription) |
| 297 return children[i]; |
| 298 return null; |
| 299 } |
| 300 |
| 301 function updateSubscriptionInfo(element) |
| 302 { |
| 303 var subscription = element._subscription; |
| 304 |
| 305 var title = element.getElementsByClassName("subscriptionTitle")[0]; |
| 306 title.textContent = subscription.title; |
| 307 title.setAttribute("title", subscription.url); |
| 308 if (subscription.homepage) |
| 309 title.href = subscription.homepage; |
| 310 else |
| 311 title.href = subscription.url; |
| 312 |
| 313 var enabled = element.getElementsByClassName("subscriptionEnabled")[0]; |
| 314 enabled.checked = !subscription.disabled; |
| 315 |
| 316 var lastUpdate = element.getElementsByClassName("subscriptionUpdate")[0]; |
| 317 lastUpdate.classList.remove("error"); |
| 318 if (Synchronizer.isExecuting(subscription.url)) |
| 319 lastUpdate.textContent = i18n.getMessage("filters_subscription_lastDownload_
inProgress"); |
| 320 else if (subscription.downloadStatus && subscription.downloadStatus != "synchr
onize_ok") |
| 321 { |
| 322 var map = |
| 323 { |
| 324 "synchronize_invalid_url": "filters_subscription_lastDownload_invalidURL", |
| 325 "synchronize_connection_error": "filters_subscription_lastDownload_connect
ionError", |
| 326 "synchronize_invalid_data": "filters_subscription_lastDownload_invalidData
", |
| 327 "synchronize_checksum_mismatch": "filters_subscription_lastDownload_checks
umMismatch" |
| 328 }; |
| 329 if (subscription.downloadStatus in map) |
| 330 lastUpdate.textContent = i18n.getMessage(map[subscription.downloadStatus])
; |
| 331 else |
| 332 lastUpdate.textContent = subscription.downloadStatus; |
| 333 lastUpdate.classList.add("error"); |
| 334 } |
| 335 else if (subscription.lastDownload > 0) |
| 336 { |
| 337 var timeDate = i18n_timeDateStrings(subscription.lastDownload * 1000); |
| 338 var messageID = (timeDate[1] ? "last_updated_at" : "last_updated_at_today"); |
| 339 lastUpdate.textContent = i18n.getMessage(messageID, timeDate); |
| 340 } |
| 341 } |
| 342 |
| 343 function onFilterChange(action, item, param1, param2) |
| 344 { |
| 345 switch (action) |
| 346 { |
| 347 case "load": |
| 348 reloadFilters(); |
| 349 break; |
| 350 case "subscription.title": |
| 351 case "subscription.disabled": |
| 352 case "subscription.homepage": |
| 353 case "subscription.lastDownload": |
| 354 case "subscription.downloadStatus": |
| 355 var element = findSubscriptionElement(item); |
| 356 if (element) |
| 357 updateSubscriptionInfo(element); |
| 358 break; |
| 359 case "subscription.added": |
| 360 if (!(item instanceof SpecialSubscription) && !findSubscriptionElement(ite
m)) |
| 361 { |
| 362 if (item.url == Prefs.subscriptions_exceptionsurl) |
| 363 $("#acceptableAds").prop("checked", true); |
| 364 else |
| 365 addSubscriptionEntry(item); |
| 366 } |
| 367 break; |
| 368 case "subscription.removed": |
| 369 if (item.url == Prefs.subscriptions_exceptionsurl) |
| 370 $("#acceptableAds").prop("checked", false); |
| 371 else |
| 372 { |
| 373 var element = findSubscriptionElement(item); |
| 374 if (element) |
| 375 element.parentNode.removeChild(element); |
| 376 } |
| 377 break; |
| 378 case "filter.added": |
| 379 if (item instanceof WhitelistFilter && /^@@\|\|([^\/:]+)\^\$document$/.tes
t(item.text)) |
| 380 appendToListBox("excludedDomainsBox", RegExp.$1); |
| 381 else |
| 382 appendToListBox("userFiltersBox", item.text); |
| 383 break; |
| 384 case "filter.removed": |
| 385 if (item instanceof WhitelistFilter && /^@@\|\|([^\/:]+)\^\$document$/.tes
t(item.text)) |
| 386 removeFromListBox("excludedDomainsBox", RegExp.$1); |
| 387 else |
| 388 removeFromListBox("userFiltersBox", item.text); |
| 389 break; |
| 390 } |
| 391 } |
| 392 |
| 393 // Populates a list box with a number of entries |
| 394 function populateList(id, entries) |
| 395 { |
| 396 var list = document.getElementById(id); |
| 397 while (list.lastChild) |
| 398 list.removeChild(list.lastChild); |
| 399 |
| 400 entries.sort(); |
| 401 for (var i = 0; i < entries.length; i++) |
| 402 { |
| 403 var option = document.createElement("option"); |
| 404 option.text = entries[i]; |
| 405 option.value = entries[i]; |
| 406 list.appendChild(option); |
| 407 } |
| 408 } |
| 409 |
| 410 // Add a filter string to the list box. |
| 411 function appendToListBox(boxId, text) |
| 412 { |
| 413 var elt = document.createElement("option"); |
| 414 elt.text = text; |
| 415 elt.value = text; |
| 416 document.getElementById(boxId).appendChild(elt); |
| 417 } |
| 418 |
| 419 // Remove a filter string from a list box. |
| 420 function removeFromListBox(boxId, text) |
| 421 { |
| 422 var elt = document.createElement("option"); |
| 423 elt.text = text; |
| 424 elt.value = text; |
| 425 var list = document.getElementById(boxId); |
| 426 for (var i = 0; i < list.length; i++) |
| 427 if (list.options[i].value == text) |
| 428 list.remove(i--); |
| 429 } |
| 430 |
| 431 function addWhitelistDomain(event) |
| 432 { |
| 433 event.preventDefault(); |
| 434 |
| 435 var domain = document.getElementById("newWhitelistDomain").value.replace(/\s/g
, ""); |
| 436 document.getElementById("newWhitelistDomain").value = ""; |
| 437 if (!domain) |
| 438 return; |
| 439 |
| 440 var filterText = "@@||" + domain + "^$document"; |
| 441 FilterStorage.addFilter(Filter.fromText(filterText)); |
| 442 } |
| 443 |
| 444 // Adds filter text that user typed to the selection box |
| 445 function addTypedFilter(event) |
| 446 { |
| 447 event.preventDefault(); |
| 448 |
| 449 var filterText = Filter.normalize(document.getElementById("newFilter").value); |
| 450 document.getElementById("newFilter").value = ""; |
| 451 if (!filterText) |
| 452 return; |
| 453 |
| 454 FilterStorage.addFilter(Filter.fromText(filterText)); |
| 455 } |
| 456 |
| 457 // Removes currently selected whitelisted domains |
| 458 function removeSelectedExcludedDomain() |
| 459 { |
| 460 var excludedDomainsBox = document.getElementById("excludedDomainsBox"); |
| 461 var remove = []; |
| 462 for (var i = 0; i < excludedDomainsBox.length; i++) |
| 463 if (excludedDomainsBox.options[i].selected) |
| 464 remove.push(excludedDomainsBox.options[i].value); |
| 465 if (!remove.length) |
| 466 return; |
| 467 |
| 468 for (var i = 0; i < remove.length; i++) |
| 469 FilterStorage.removeFilter(Filter.fromText("@@||" + remove[i] + "^$document"
)); |
| 470 } |
| 471 |
| 472 // Removes all currently selected filters |
| 473 function removeSelectedFilters() |
| 474 { |
| 475 var userFiltersBox = document.getElementById("userFiltersBox"); |
| 476 var remove = []; |
| 477 for (var i = 0; i < userFiltersBox.length; i++) |
| 478 if (userFiltersBox.options[i].selected) |
| 479 remove.push(userFiltersBox.options[i].value); |
| 480 if (!remove.length) |
| 481 return; |
| 482 |
| 483 for (var i = 0; i < remove.length; i++) |
| 484 FilterStorage.removeFilter(Filter.fromText(remove[i])); |
| 485 } |
| 486 |
| 487 // Shows raw filters box and fills it with the current user filters |
| 488 function toggleFiltersInRawFormat(event) |
| 489 { |
| 490 event.preventDefault(); |
| 491 |
| 492 $("#rawFilters").toggle(); |
| 493 if ($("#rawFilters").is(":visible")) |
| 494 { |
| 495 var userFiltersBox = document.getElementById("userFiltersBox"); |
| 496 var text = ""; |
| 497 for (var i = 0; i < userFiltersBox.length; i++) |
| 498 text += userFiltersBox.options[i].value + "\n"; |
| 499 document.getElementById("rawFiltersText").value = text; |
| 500 } |
| 501 } |
| 502 |
| 503 // Imports filters in the raw text box |
| 504 function importRawFiltersText() |
| 505 { |
| 506 $("#rawFilters").hide(); |
| 507 var filters = document.getElementById("rawFiltersText").value.split("\n"); |
| 508 var seenFilter = {__proto__: null}; |
| 509 for (var i = 0; i < filters.length; i++) |
| 510 { |
| 511 var text = Filter.normalize(filters[i]); |
| 512 if (!text) |
| 513 continue; |
| 514 |
| 515 // Don't import filter list header |
| 516 if (/^\[/.test(text)) |
| 517 continue; |
| 518 |
| 519 FilterStorage.addFilter(Filter.fromText(text)); |
| 520 seenFilter[text] = true; |
| 521 } |
| 522 |
| 523 var remove = []; |
| 524 for (var i = 0; i < FilterStorage.subscriptions.length; i++) |
| 525 { |
| 526 var subscription = FilterStorage.subscriptions[i]; |
| 527 if (!(subscription instanceof SpecialSubscription)) |
| 528 continue; |
| 529 |
| 530 for (var j = 0; j < subscription.filters.length; j++) |
| 531 { |
| 532 var filter = subscription.filters[j]; |
| 533 if (filter instanceof WhitelistFilter && /^@@\|\|([^\/:]+)\^\$document$/.t
est(filter.text)) |
| 534 continue; |
| 535 |
| 536 if (!(filter.text in seenFilter)) |
| 537 remove.push(filter); |
| 538 } |
| 539 } |
| 540 for (var i = 0; i < remove.length; i++) |
| 541 FilterStorage.removeFilter(remove[i]); |
| 542 } |
| 543 |
| 544 // Called when user explicitly requests filter list updates |
| 545 function updateFilterLists() |
| 546 { |
| 547 for (var i = 0; i < FilterStorage.subscriptions.length; i++) |
| 548 { |
| 549 var subscription = FilterStorage.subscriptions[i]; |
| 550 if (subscription instanceof DownloadableSubscription) |
| 551 Synchronizer.execute(subscription, true, true); |
| 552 } |
| 553 } |
| 554 |
| 555 // Adds a subscription entry to the UI. |
| 556 function addSubscriptionEntry(subscription) |
| 557 { |
| 558 var template = document.getElementById("subscriptionTemplate"); |
| 559 var element = template.cloneNode(true); |
| 560 element.removeAttribute("id"); |
| 561 element._subscription = subscription; |
| 562 |
| 563 var removeButton = element.getElementsByClassName("subscriptionRemoveButton")[
0]; |
| 564 removeButton.setAttribute("title", removeButton.textContent); |
| 565 removeButton.textContent = "\xD7"; |
| 566 removeButton.addEventListener("click", function() |
| 567 { |
| 568 if (!confirm(i18n.getMessage("global_remove_subscription_warning"))) |
| 569 return; |
| 570 |
| 571 FilterStorage.removeSubscription(subscription); |
| 572 }, false); |
| 573 |
| 574 var enabled = element.getElementsByClassName("subscriptionEnabled")[0]; |
| 575 enabled.addEventListener("click", function() |
| 576 { |
| 577 if (subscription.disabled == !enabled.checked) |
| 578 return; |
| 579 |
| 580 subscription.disabled = !enabled.checked; |
| 581 }, false); |
| 582 |
| 583 updateSubscriptionInfo(element); |
| 584 |
| 585 document.getElementById("filterLists").appendChild(element); |
| 586 } |
OLD | NEW |