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