summaryrefslogtreecommitdiff
path: root/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui
diff options
context:
space:
mode:
Diffstat (limited to 'helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui')
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/composer.js412
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/composer.xul118
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/fennecSettings.xul38
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-backup.js348
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-filteractions.js561
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-filterview.js849
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-search.js269
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-subscriptionactions.js606
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-subscriptionview.js330
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters.js226
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters.xul400
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/firstRun.html141
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/firstRun.js253
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/flasher.js108
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/i18n.js150
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/overlay.xul134
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/progressBar.js158
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/progressBar.xul29
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sendReport.js1571
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sendReport.xul259
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/settings.xul38
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sidebar.js1246
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sidebar.xul139
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sidebarDetached.xul50
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/subscriptionSelection.js308
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/subscriptionSelection.xul75
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/subscriptions.xml117
-rw-r--r--helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/utils.js66
28 files changed, 8999 insertions, 0 deletions
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/composer.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/composer.js
new file mode 100644
index 0000000..f7b8087
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/composer.js
@@ -0,0 +1,412 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+let nodes = null;
+let item = null;
+let advancedMode = false;
+
+function init()
+{
+ [nodes, item] = window.arguments;
+
+ E("filterType").value = (!item.filter || item.filter.disabled || item.filter instanceof WhitelistFilter ? "filterlist" : "whitelist");
+ E("customPattern").value = item.location;
+
+ let insertionPoint = E("customPatternBox");
+ let addSuggestion = function(address)
+ {
+ // Always drop protocol and www. from the suggestion
+ address = address.replace(/^[\w\-]+:\/+(?:www\.)?/, "");
+
+ let suggestion = document.createElement("radio");
+ suggestion.setAttribute("value", address);
+ suggestion.setAttribute("label", address);
+ suggestion.setAttribute("crop", "center");
+ suggestion.setAttribute("class", "suggestion");
+ insertionPoint.parentNode.insertBefore(suggestion, insertionPoint);
+
+ return address;
+ }
+
+ let ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+ try
+ {
+ let suggestions = [""];
+
+ let url = ioService.newURI(item.location, null, null)
+ .QueryInterface(Ci.nsIURL);
+ let suffix = (url.query ? "?*" : "");
+ url.query = "";
+ url.ref = "";
+ suggestions[1] = addSuggestion(url.spec + suffix);
+
+ let parentURL = ioService.newURI(url.fileName == "" ? ".." : ".", null, url);
+ if (!parentURL.equals(url))
+ suggestions[2] = addSuggestion(parentURL.spec + "*");
+ else
+ suggestions[2] = suggestions[1];
+
+ let rootURL = ioService.newURI("/", null, url);
+ if (!rootURL.equals(parentURL) && !rootURL.equals(url))
+ suggestions[3] = addSuggestion(rootURL.spec + "*");
+ else
+ suggestions[3] = suggestions[2];
+
+ try
+ {
+ suggestions[4] = addSuggestion(url.host.replace(/^www\./, "") + "^");
+
+ // Prefer example.com^ to example.com/*
+ let undesired = suggestions[4].replace(/\^$/, "/*");
+ for (let i = 0; i < suggestions.length - 1; i++)
+ if (suggestions[i] == undesired)
+ suggestions[i] = suggestions[4];
+
+ for (let child = insertionPoint.parentNode.firstChild; child; child = child.nextSibling)
+ {
+ if (child.localName == "radio" && child.getAttribute("value") == undesired)
+ {
+ child.parentNode.removeChild(child);
+ break;
+ }
+ }
+ }
+ catch (e)
+ {
+ suggestions[4] = suggestions[3];
+ }
+
+ try
+ {
+ let effectiveTLD = Cc["@mozilla.org/network/effective-tld-service;1"].getService(Ci.nsIEffectiveTLDService);
+ let host = url.host;
+ let baseDomain = effectiveTLD.getBaseDomainFromHost(host);
+ if (baseDomain != host.replace(/^www\./, ""))
+ suggestions[5] = addSuggestion(baseDomain + "^");
+ else
+ suggestions[5] = suggestions[4];
+ }
+ catch (e)
+ {
+ suggestions[5] = suggestions[4];
+ }
+
+ E("patternGroup").value = (Prefs.composer_default in suggestions ? suggestions[Prefs.composer_default] : suggestions[1]);
+ }
+ catch (e)
+ {
+ // IOService returned nsIURI - not much we can do with it
+ addSuggestion(item.location);
+ E("patternGroup").value = "";
+ }
+ if (Prefs.composer_default == 0)
+ E("customPattern").focus();
+ else
+ E("patternGroup").focus();
+
+ let types = [];
+ for (let type in Policy.localizedDescr)
+ {
+ types.push(parseInt(type));
+ }
+ types.sort(function(a, b) {
+ if (a < b)
+ return -1;
+ else if (a > b)
+ return 1;
+ else
+ return 0;
+ });
+
+ let docDomain = item.docDomain;
+ let thirdParty = item.thirdParty;
+
+ if (docDomain)
+ docDomain = docDomain.replace(/^www\./i, "").replace(/\.+$/, "");
+ if (docDomain)
+ E("domainRestriction").value = docDomain;
+
+ E("thirdParty").hidden = !thirdParty;
+ E("firstParty").hidden = thirdParty;
+
+ let typeGroup = E("typeGroup");
+ let defaultTypes = RegExpFilter.prototype.contentType & ~RegExpFilter.typeMap.DOCUMENT;
+ let isDefaultType = (RegExpFilter.typeMap[item.typeDescr] & defaultTypes) != 0;
+ for each (let type in types)
+ {
+ if (type == Policy.type.ELEMHIDE)
+ continue;
+
+ let typeNode = document.createElement("checkbox");
+ typeNode.setAttribute("value", Policy.typeDescr[type].toLowerCase().replace(/\_/g, "-"));
+ typeNode.setAttribute("label", Policy.localizedDescr[type].toLowerCase());
+
+ let typeMask = RegExpFilter.typeMap[Policy.typeDescr[type]];
+ typeNode._defaultType = (typeMask & defaultTypes) != 0;
+ if ((isDefaultType && typeNode._defaultType) || (!isDefaultType && item.type == type))
+ typeNode.setAttribute("checked", "true");
+
+ if (item.type == type)
+ typeNode.setAttribute("disabled", "true");
+ typeNode.addEventListener("command", function() checkboxUpdated(this), false);
+ typeGroup.appendChild(typeNode);
+ }
+
+ let collapseDefault = E("collapseDefault");
+ collapseDefault.label = collapseDefault.getAttribute(Prefs.fastcollapse ? "label_no" : "label_yes");
+ E("collapse").value = "";
+ E("collapse").setAttribute("label", collapseDefault.label);
+
+ let warning = E("disabledWarning");
+ generateLinkText(warning);
+ warning.hidden = Prefs.enabled;
+
+ updatePatternSelection();
+}
+
+function checkboxUpdated(checkbox)
+{
+ checkbox._lastChange = Date.now();
+ updateFilter();
+}
+
+function updateFilter()
+{
+ let filter = "";
+
+ let type = E("filterType").value
+ if (type == "whitelist")
+ filter += "@@";
+
+ let pattern = E("patternGroup").value;
+ if (pattern == "")
+ pattern = E("customPattern").value;
+
+ if (E("anchorStart").checked)
+ filter += E("anchorStart").flexibleAnchor ? "||" : "|";
+
+ filter += pattern;
+
+ if (E("anchorEnd").checked)
+ filter += "|";
+
+ if (advancedMode)
+ {
+ let options = [];
+
+ if (E("domainRestrictionEnabled").checked)
+ {
+ let domainRestriction = E("domainRestriction").value.replace(/[,\s]/g, "").replace(/\.+$/, "");
+ if (domainRestriction)
+ options.push([E("domainRestrictionEnabled")._lastChange || 0, "domain=" + domainRestriction]);
+ }
+
+ if (E("firstParty").checked)
+ options.push([E("firstParty")._lastChange || 0, "~third-party"]);
+ if (E("thirdParty").checked)
+ options.push([E("thirdParty")._lastChange || 0, "third-party"]);
+
+ if (E("matchCase").checked)
+ options.push([E("matchCase")._lastChange || 0, "match-case"]);
+
+ let collapse = E("collapse");
+ disableElement(collapse, type == "whitelist", "value", "");
+ if (collapse.value != "")
+ options.push([collapse._lastChange, collapse.value]);
+
+ let enabledTypes = [];
+ let disabledTypes = [];
+ let forceEnabledTypes = [];
+ for (let typeNode = E("typeGroup").firstChild; typeNode; typeNode = typeNode.nextSibling)
+ {
+ let value = typeNode.getAttribute("value");
+ if (value == "document")
+ disableElement(typeNode, type != "whitelist", "checked", false);
+
+ if (!typeNode._defaultType)
+ {
+ if (typeNode.getAttribute("checked") == "true")
+ forceEnabledTypes.push([typeNode._lastChange || 0, value]);
+ }
+ else if (typeNode.getAttribute("checked") == "true")
+ enabledTypes.push([typeNode._lastChange || 0, value]);
+ else
+ disabledTypes.push([typeNode._lastChange || 0, "~" + value]);
+ }
+ if (!forceEnabledTypes.length && disabledTypes.length < enabledTypes.length)
+ options.push.apply(options, disabledTypes);
+ else
+ options.push.apply(options, enabledTypes);
+ options.push.apply(options, forceEnabledTypes);
+
+ if (options.length)
+ {
+ options.sort(function(a, b) a[0] - b[0]);
+ filter += "$" + options.map(function(o) o[1]).join(",");
+ }
+ }
+ else
+ {
+ let defaultTypes = RegExpFilter.prototype.contentType & ~RegExpFilter.typeMap.DOCUMENT;
+ let isDefaultType = (RegExpFilter.typeMap[item.typeDescr] & defaultTypes) != 0;
+ if (!isDefaultType)
+ filter += "$" + item.typeDescr.toLowerCase().replace(/\_/g, "-");
+ }
+
+ filter = Filter.normalize(filter);
+ E("regexpWarning").hidden = !Filter.regexpRegExp.test(filter);
+
+ let isSlow = false;
+ let compiledFilter = Filter.fromText(filter);
+ if (E("regexpWarning").hidden)
+ {
+ if (compiledFilter instanceof RegExpFilter && defaultMatcher.isSlowFilter(compiledFilter))
+ isSlow = true;
+ }
+ E("shortpatternWarning").hidden = !isSlow;
+
+ E("matchWarning").hidden = compiledFilter instanceof RegExpFilter && compiledFilter.matches(item.location, item.typeDescr, item.docDomain, item.thirdParty);
+
+ E("filter").value = filter;
+}
+
+function generateLinkText(element, replacement)
+{
+ let template = element.getAttribute("textTemplate");
+ if (typeof replacement != "undefined")
+ template = template.replace(/\?1\?/g, replacement)
+
+ let [, beforeLink, linkText, afterLink] = /(.*)\[link\](.*)\[\/link\](.*)/.exec(template) || [null, "", template, ""];
+ while (element.firstChild && element.firstChild.nodeType != Node.ELEMENT_NODE)
+ element.removeChild(element.firstChild);
+ while (element.lastChild && element.lastChild.nodeType != Node.ELEMENT_NODE)
+ element.removeChild(element.lastChild);
+ if (!element.firstChild)
+ return;
+
+ element.firstChild.textContent = linkText;
+ element.insertBefore(document.createTextNode(beforeLink), element.firstChild);
+ element.appendChild(document.createTextNode(afterLink));
+}
+
+function updatePatternSelection()
+{
+ let pattern = E("patternGroup").value;
+ if (pattern == "")
+ {
+ pattern = E("customPattern").value;
+ }
+ else
+ {
+ E("anchorStart").checked = true;
+ E("anchorEnd").checked = false;
+ }
+
+ function testFilter(/**String*/ filter) /**Boolean*/
+ {
+ return RegExpFilter.fromText(filter + "$" + item.typeDescr).matches(item.location, item.typeDescr, item.docDomain, item.thirdParty);
+ }
+
+ let anchorStartCheckbox = E("anchorStart");
+ if (!/^\*/.test(pattern) && testFilter("||" + pattern))
+ {
+ disableElement(anchorStartCheckbox, false, "checked", false);
+ [anchorStartCheckbox.label, anchorStartCheckbox.accessKey] = Utils.splitLabel(anchorStartCheckbox.getAttribute("labelFlexible"));
+ anchorStartCheckbox.flexibleAnchor = true;
+ }
+ else
+ {
+ disableElement(anchorStartCheckbox, /^\*/.test(pattern) || !testFilter("|" + pattern), "checked", false);
+ [anchorStartCheckbox.label, anchorStartCheckbox.accessKey] = Utils.splitLabel(anchorStartCheckbox.getAttribute("labelRegular"));
+ anchorStartCheckbox.flexibleAnchor = false;
+ }
+ disableElement(E("anchorEnd"), /[\*\^]$/.test(pattern) || !testFilter(pattern + "|"), "checked", false);
+
+ updateFilter();
+ setAdvancedMode(document.documentElement.getAttribute("advancedMode") == "true");
+}
+
+function updateCustomPattern()
+{
+ E("patternGroup").value = "";
+ updatePatternSelection();
+}
+
+function addFilter() {
+ let filter = Filter.fromText(document.getElementById("filter").value);
+ filter.disabled = false;
+
+ FilterStorage.addFilter(filter);
+
+ if (nodes)
+ Policy.refilterNodes(nodes, item);
+
+ return true;
+}
+
+function setAdvancedMode(mode) {
+ advancedMode = mode;
+
+ var dialog = document.documentElement;
+ dialog.setAttribute("advancedMode", advancedMode);
+
+ var button = dialog.getButton("disclosure");
+ button.setAttribute("label", dialog.getAttribute(advancedMode ? "buttonlabeldisclosure_off" : "buttonlabeldisclosure_on"));
+
+ updateFilter();
+}
+
+function disableElement(element, disable, valueProperty, disabledValue) {
+ if ((element.getAttribute("disabled") == "true") == disable)
+ return;
+
+ if (disable)
+ {
+ element.setAttribute("disabled", "true");
+ element._abpStoredValue = element[valueProperty];
+ element[valueProperty] = disabledValue;
+ }
+ else
+ {
+ element.removeAttribute("disabled");
+ if ("_abpStoredValue" in element)
+ element[valueProperty] = element._abpStoredValue;
+ delete element._abpStoredValue;
+ }
+}
+
+function openPreferences()
+{
+ UI.openFiltersDialog(Filter.fromText(E("filter").value));
+}
+
+function doEnable() {
+ Prefs.enabled = true;
+ E("disabledWarning").hidden = true;
+}
+
+/**
+ * Selects or unselects all type checkboxes except those
+ * that are disabled.
+ */
+function selectAllTypes(/**Boolean*/ select)
+{
+ for (let typeNode = E("typeGroup").firstChild; typeNode; typeNode = typeNode.nextSibling)
+ if (typeNode.getAttribute("disabled") != "true")
+ typeNode.checked = select;
+ updateFilter();
+}
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/composer.xul b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/composer.xul
new file mode 100644
index 0000000..8931e16
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/composer.xul
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+
+<!--
+ - This file is part of Adblock Plus <http://adblockplus.org/>,
+ - Copyright (C) 2006-2014 Eyeo GmbH
+ -
+ - Adblock Plus is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU General Public License version 3 as
+ - published by the Free Software Foundation.
+ -
+ - Adblock Plus is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU General Public License for more details.
+ -
+ - You should have received a copy of the GNU General Public License
+ - along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<!DOCTYPE overlay SYSTEM "chrome://adblockplus/locale/composer.dtd">
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://adblockplus/skin/composer.css" type="text/css"?>
+
+<dialog id="abp-composer"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&dialog.title;"
+ onload="init()"
+ ondialogaccept="return addFilter()"
+ ondialogdisclosure="setAdvancedMode(!advancedMode)"
+ buttons="accept,cancel,disclosure"
+ width="800px"
+ height="400px"
+ persist="screenX screenY width height sizemode advancedMode"
+ advancedMode="false"
+ buttonlabelaccept="&accept.label;"
+ buttonlabeldisclosure="&advanced.label;"
+ buttonlabeldisclosure_on="&advanced.label;"
+ buttonlabeldisclosure_off="&basic.label;"
+ windowtype="abp:composer">
+
+ <script type="application/x-javascript;version=1.7" src="utils.js"/>
+ <script type="application/x-javascript;version=1.7" src="composer.js"/>
+
+ <popupset>
+ <tooltip id="domainRestrictionHelp" label="&domainRestriction.help;"/>
+ </popupset>
+
+ <description id="disabledWarning" hidden="true" textTemplate="&disabled.warning;">
+ <label class="text-link" onclick="doEnable()"/>
+ </description>
+
+ <hbox id="filterBox" align="center">
+ <label control="filter" value="&filter.label;"/>
+ <textbox id="filter" flex="1" tabindex="-1" readonly="true"/>
+ <button id="preferences" label="&preferences.label;" oncommand="openPreferences()"/>
+ </hbox>
+
+ <radiogroup orient="horizontal" id="filterType" oncommand="updateFilter()">
+ <radio label="&type.filter.label;" value="filterlist" flex="1"/>
+ <radio label="&type.whitelist.label;" value="whitelist" flex="1"/>
+ </radiogroup>
+
+ <hbox flex="1">
+ <groupbox id="pattern" flex="1">
+ <caption label="&pattern.label;"/>
+ <radiogroup id="patternGroup" flex="1" oncommand="updatePatternSelection()" style="overflow: auto;">
+ <description id="patternExplanation">&pattern.explanation;</description>
+ <description id="regexpWarning" hidden="true">&regexp.warning;</description>
+ <description id="shortpatternWarning" hidden="true">&shortpattern.warning;</description>
+ <description id="matchWarning" hidden="true">&match.warning;</description>
+ <hbox id="customPatternBox">
+ <radio id="customPatternRadio" label="&custom.pattern.label;" value="" control="customPattern"/>
+ <textbox id="customPattern" flex="1" oninput="updateCustomPattern()"/>
+ </hbox>
+ </radiogroup>
+ <hbox id="anchorGroup" pack="start" align="baseline">
+ <label value="&anchors.label;"/>
+ <description flex="1" style="margin: 0; padding: 0;">
+ <checkbox id="anchorStart" labelRegular="&anchor.start.label;"
+ labelFlexible="&anchor.start.flexible.label;"
+ oncommand="updateFilter()"/>
+ <checkbox id="anchorEnd" label="&anchor.end.label;" oncommand="updateFilter()"/>
+ </description>
+ </hbox>
+ </groupbox>
+ <groupbox id="options">
+ <caption label="&options.label;"/>
+ <checkbox id="firstParty" label="&firstParty.label;" oncommand="checkboxUpdated(this);"/>
+ <checkbox id="thirdParty" label="&thirdParty.label;" oncommand="checkboxUpdated(this);"/>
+ <checkbox id="matchCase" label="&matchCase.label;" oncommand="checkboxUpdated(this);"/>
+ <hbox align="baseline">
+ <checkbox id="domainRestrictionEnabled" label="&domainRestriction.label;" oncommand="checkboxUpdated(this);"/>
+ <description class="help" value="?" tooltip="domainRestrictionHelp"/>
+ </hbox>
+ <textbox id="domainRestriction" oninput="updateFilter()"/>
+
+ <label id="typeGroupLabel" value="&types.label;"/>
+ <hbox>
+ <label id="selectAllTypes" class="text-link" value="&selectAllTypes.label;" onclick="selectAllTypes(true)"/>
+ <spacer flex="1"/>
+ <label id="unselectAllTypes" class="text-link" value="&unselectAllTypes.label;" onclick="selectAllTypes(false)"/>
+ </hbox>
+ <vbox flex="1" id="typeGroup"/>
+
+ <vbox>
+ <label control="collapse" value="&collapse.label;"/>
+ <menulist id="collapse" oncommand="updateFilter()">
+ <menupopup>
+ <menuitem id="collapseDefault" value="" label_yes="&collapse.default.yes.label;" label_no="&collapse.default.no.label;" selected="true"/>
+ <menuitem label="&collapse.yes.label;" value="collapse"/>
+ <menuitem label="&collapse.no.label;" value="~collapse"/>
+ </menupopup>
+ </menulist>
+ </vbox>
+ </groupbox>
+ </hbox>
+</dialog>
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/fennecSettings.xul b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/fennecSettings.xul
new file mode 100644
index 0000000..9063ba3
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/fennecSettings.xul
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+
+<!--
+ - This file is part of Adblock Plus <http://adblockplus.org/>,
+ - Copyright (C) 2006-2014 Eyeo GmbH
+ -
+ - Adblock Plus is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU General Public License version 3 as
+ - published by the Free Software Foundation.
+ -
+ - Adblock Plus is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU General Public License for more details.
+ -
+ - You should have received a copy of the GNU General Public License
+ - along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<!DOCTYPE vbox [
+<!ENTITY % overlayDTD SYSTEM "chrome://adblockplus/locale/overlay.dtd">
+%overlayDTD;
+<!ENTITY % filtersDTD SYSTEM "chrome://adblockplus/locale/filters.dtd">
+%filtersDTD;
+]>
+
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <setting pref="extensions.adblockplus.enabled" type="bool" inverted="true" title="&disable.label;"/>
+ <setting type="control" title="&subscriptions.tab.label;">
+ <menulist id="adblockplus-subscription-list"/>
+ </setting>
+ <setting id="adblockplus-acceptableAds" type="bool" title="&acceptableAds2.label;"
+ oncommand="/**See bug 762015*/ if (event.type == 'oncommand') {event = document.createEvent('Events'); event.initEvent('command', false, false); this.dispatchEvent(event);}"/>
+ <setting pref="extensions.adblockplus.fastcollapse" type="bool" title="&hideplaceholders.label;"
+ inverted="true"/>
+ <setting id="adblockplus-sync" type="bool" title="&sync.label;"
+ oncommand="/**See bug 762015*/ if (event.type == 'oncommand') {event = document.createEvent('Events'); event.initEvent('command', false, false); this.dispatchEvent(event);}"/>
+</vbox>
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-backup.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-backup.js
new file mode 100644
index 0000000..3ef38c6
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-backup.js
@@ -0,0 +1,348 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+
+/**
+ * Implementation of backup and restore functionality.
+ * @class
+ */
+var Backup =
+{
+ /**
+ * Template for menu items to be displayed in the Restore menu (for automated
+ * backups).
+ * @type Element
+ */
+ restoreTemplate: null,
+
+ /**
+ * Element after which restore items should be inserted.
+ * @type Element
+ */
+ restoreInsertionPoint: null,
+
+ /**
+ * Regular expression to recognize checksum comments.
+ */
+ CHECKSUM_REGEXP: /^!\s*checksum[\s\-:]+([\w\+\/]+)/i,
+
+ /**
+ * Regular expression to recognize group title comments.
+ */
+ GROUPTITLE_REGEXP: /^!\s*\[(.*)\]((?:\/\w+)*)\s*$/,
+
+
+ /**
+ * Initializes backup UI.
+ */
+ init: function()
+ {
+ this.restoreTemplate = E("restoreBackupTemplate");
+ this.restoreInsertionPoint = this.restoreTemplate.previousSibling;
+ this.restoreTemplate.parentNode.removeChild(this.restoreTemplate);
+ this.restoreTemplate.removeAttribute("id");
+ this.restoreTemplate.removeAttribute("hidden");
+ },
+
+ /**
+ * Gets the default download dir, as used by the browser itself.
+ */
+ getDefaultDir: function() /**nsIFile*/
+ {
+ try
+ {
+ return Utils.prefService.getComplexValue("browser.download.lastDir", Ci.nsILocalFile);
+ }
+ catch (e)
+ {
+ // No default download location. Default to desktop.
+ return FileUtils.getDir("Desk", [], false);
+ }
+ },
+
+ /**
+ * Saves new default download dir after the user chose a different directory to
+ * save his files to.
+ */
+ saveDefaultDir: function(/**nsIFile*/ dir)
+ {
+ try
+ {
+ Utils.prefService.setComplexValue("browser.download.lastDir", Ci.nsILocalFile, dir);
+ } catch(e) {};
+ },
+
+ /**
+ * Called when the Restore menu is being opened, fills in "Automated backup"
+ * entries.
+ */
+ fillRestorePopup: function()
+ {
+ while (this.restoreInsertionPoint.nextSibling && !this.restoreInsertionPoint.nextSibling.id)
+ this.restoreInsertionPoint.parentNode.removeChild(this.restoreInsertionPoint.nextSibling);
+
+ let files = FilterStorage.getBackupFiles().reverse();
+ for (let i = 0; i < files.length; i++)
+ {
+ let file = files[i];
+ let item = this.restoreTemplate.cloneNode(true);
+ let label = item.getAttribute("label");
+ label = label.replace(/\?1\?/, Utils.formatTime(file.lastModifiedTime));
+ item.setAttribute("label", label);
+ item.addEventListener("command", function()
+ {
+ Backup.restoreAllData(file);
+ }, false);
+ this.restoreInsertionPoint.parentNode.insertBefore(item, this.restoreInsertionPoint.nextSibling);
+ }
+ },
+
+ /**
+ * Lets the user choose a file to restore filters from.
+ */
+ restoreFromFile: function()
+ {
+ let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+ picker.init(window, E("backupButton").getAttribute("_restoreDialogTitle"), picker.modeOpen);
+ picker.defaultExtension = ".ini";
+ picker.appendFilter(E("backupButton").getAttribute("_fileFilterComplete"), "*.ini");
+ picker.appendFilter(E("backupButton").getAttribute("_fileFilterCustom"), "*.txt");
+
+ if (picker.show() != picker.returnCancel)
+ {
+ this.saveDefaultDir(picker.file.parent);
+ if (picker.filterIndex == 0)
+ this.restoreAllData(picker.file);
+ else
+ this.restoreCustomFilters(picker.file);
+ }
+ },
+
+ /**
+ * Restores patterns.ini from a file.
+ */
+ restoreAllData: function(/**nsIFile*/ file)
+ {
+ let stream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
+ stream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
+ stream.QueryInterface(Ci.nsILineInputStream);
+
+ let lines = [];
+ let line = {value: null};
+ if (stream.readLine(line))
+ lines.push(line.value);
+ if (stream.readLine(line))
+ lines.push(line.value);
+ stream.close();
+
+ let match;
+ if (lines.length < 2 || lines[0] != "# Adblock Plus preferences" || !(match = /version=(\d+)/.exec(lines[1])))
+ {
+ Utils.alert(window, E("backupButton").getAttribute("_restoreError"), E("backupButton").getAttribute("_restoreDialogTitle"));
+ return;
+ }
+
+ let warning = E("backupButton").getAttribute("_restoreCompleteWarning");
+ let minVersion = parseInt(match[1], 10);
+ if (minVersion > FilterStorage.formatVersion)
+ warning += "\n\n" + E("backupButton").getAttribute("_restoreVersionWarning");
+
+ if (!Utils.confirm(window, warning, E("backupButton").getAttribute("_restoreDialogTitle")))
+ return;
+
+ FilterStorage.loadFromDisk(file);
+ },
+
+ /**
+ * Restores custom filters from a file.
+ */
+ restoreCustomFilters: function(/**nsIFile*/ file)
+ {
+ IO.readFromFile(file, {
+ seenHeader: false,
+ subscription: null,
+ process: function(line)
+ {
+ if (!this.seenHeader)
+ {
+ // This should be a header
+ this.seenHeader = true;
+ let match = /\[Adblock(?:\s*Plus\s*([\d\.]+)?)?\]/i.exec(line);
+ if (match)
+ {
+ let warning = E("backupButton").getAttribute("_restoreCustomWarning");
+ let minVersion = match[1];
+ if (minVersion && Utils.versionComparator.compare(minVersion, Utils.addonVersion) > 0)
+ warning += "\n\n" + E("backupButton").getAttribute("_restoreVersionWarning");
+
+ if (Utils.confirm(window, warning, E("backupButton").getAttribute("_restoreDialogTitle")))
+ {
+ let subscriptions = FilterStorage.subscriptions.filter(function(s) s instanceof SpecialSubscription);
+ for (let i = 0; i < subscriptions.length; i++)
+ FilterStorage.removeSubscription(subscriptions[i]);
+
+ return;
+ }
+ else
+ throw Cr.NS_BASE_STREAM_WOULD_BLOCK;
+ }
+ else
+ throw new Error("Invalid file");
+ }
+ else if (line === null)
+ {
+ // End of file
+ if (this.subscription)
+ FilterStorage.addSubscription(this.subscription);
+ E("tabs").selectedIndex = 1;
+ }
+ else if (Backup.CHECKSUM_REGEXP.test(line))
+ {
+ // Ignore checksums
+ }
+ else if (Backup.GROUPTITLE_REGEXP.test(line))
+ {
+ // New group start
+ if (this.subscription)
+ FilterStorage.addSubscription(this.subscription);
+
+ let [, title, options] = Backup.GROUPTITLE_REGEXP.exec(line);
+ this.subscription = SpecialSubscription.create(title);
+
+ let defaults = [];
+ if (options)
+ options = options.split("/");
+ for (let j = 0; j < options.length; j++)
+ if (options[j] in SpecialSubscription.defaultsMap)
+ defaults.push(options[j]);
+ if (defaults.length)
+ this.subscription.defaults = defaults;
+ }
+ else
+ {
+ // Regular filter
+ line = Filter.normalize(line);
+ if (line)
+ {
+ if (!this.subscription)
+ this.subscription = SpecialSubscription.create(Utils.getString("newGroup_title"));
+ this.subscription.filters.push(Filter.fromText(line));
+ }
+ }
+ }
+ }, function(e)
+ {
+ if (e && e.result != Cr.NS_BASE_STREAM_WOULD_BLOCK)
+ {
+ Cu.reportError(e);
+ Utils.alert(window, E("backupButton").getAttribute("_restoreError"), E("backupButton").getAttribute("_restoreDialogTitle"));
+ }
+ });
+ },
+
+ /**
+ * Lets the user choose a file to backup filters to.
+ */
+ backupToFile: function()
+ {
+ let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+ picker.init(window, E("backupButton").getAttribute("_backupDialogTitle"), picker.modeSave);
+ picker.defaultExtension = ".ini";
+ picker.appendFilter(E("backupButton").getAttribute("_fileFilterComplete"), "*.ini");
+ picker.appendFilter(E("backupButton").getAttribute("_fileFilterCustom"), "*.txt");
+
+ if (picker.show() != picker.returnCancel)
+ {
+ this.saveDefaultDir(picker.file.parent);
+ if (picker.filterIndex == 0)
+ this.backupAllData(picker.file);
+ else
+ this.backupCustomFilters(picker.file);
+ }
+ },
+
+ /**
+ * Writes all patterns.ini data to a file.
+ */
+ backupAllData: function(/**nsIFile*/ file)
+ {
+ FilterStorage.saveToDisk(file);
+ },
+
+ /**
+ * Writes user's custom filters to a file.
+ */
+ backupCustomFilters: function(/**nsIFile*/ file)
+ {
+ let subscriptions = FilterStorage.subscriptions.filter(function(s) s instanceof SpecialSubscription);
+ let minVersion = "2.0"
+ let list = [];
+ for (let i = 0; i < subscriptions.length; i++)
+ {
+ let subscription = subscriptions[i];
+ let typeAddition = "";
+ if (subscription.defaults)
+ typeAddition = "/" + subscription.defaults.join("/");
+ list.push("! [" + subscription.title + "]" + typeAddition);
+ for (let j = 0; j < subscription.filters.length; j++)
+ {
+ let filter = subscription.filters[j];
+ // Skip checksums
+ if (filter instanceof CommentFilter && this.CHECKSUM_REGEXP.test(filter.text))
+ continue;
+ // Skip group headers
+ if (filter instanceof CommentFilter && this.GROUPTITLE_REGEXP.test(filter.text))
+ continue;
+ list.push(filter.text);
+
+ if (filter instanceof ElemHideException && Services.vc.compare(minVersion, "2.1") < 0)
+ minVersion = "2.1";
+ }
+ }
+ list.unshift("[Adblock Plus " + minVersion + "]");
+
+ // Insert checksum. Have to add an empty line to the end of the list to
+ // account for the trailing newline in the file.
+ list.push("");
+ let checksum = Utils.generateChecksum(list);
+ list.pop();
+ if (checksum)
+ list.splice(1, 0, "! Checksum: " + checksum);
+
+ function generator()
+ {
+ for (let i = 0; i < list.length; i++)
+ yield list[i];
+ }
+
+ IO.writeToFile(file, generator(), function(e)
+ {
+ if (e)
+ {
+ Cu.reportError(e);
+ Utils.alert(window, E("backupButton").getAttribute("_backupError"), E("backupButton").getAttribute("_backupDialogTitle"));
+ }
+ });
+ }
+};
+
+window.addEventListener("load", function()
+{
+ Backup.init();
+}, false);
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-filteractions.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-filteractions.js
new file mode 100644
index 0000000..5512fda
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-filteractions.js
@@ -0,0 +1,561 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Implementation of the various actions performed on the filters.
+ * @class
+ */
+var FilterActions =
+{
+ /**
+ * Initializes filter actions.
+ */
+ init: function()
+ {
+ let me = this;
+ this.treeElement.parentNode.addEventListener("keypress", function(event)
+ {
+ me.keyPress(event);
+ }, true);
+ this.treeElement.view = FilterView;
+
+ // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=777832, don't
+ // allow the tree to receive keypress/keydown events triggered by cursor
+ // keys pressed in the editor, it will call preventDefault() on them.
+ let propagationStopper = function(event)
+ {
+ if (event.keyCode >= event.DOM_VK_PAGE_UP && event.keyCode <= event.DOM_VK_DOWN)
+ event.stopPropagation();
+ };
+
+ this.treeElement.inputField.addEventListener("keypress", propagationStopper, false);
+ this.treeElement.inputField.addEventListener("keydown", propagationStopper, false);
+
+ // Create a copy of the view menu
+ function fixId(node, newId)
+ {
+ if (node.nodeType == node.ELEMENT_NODE)
+ {
+ if (node.hasAttribute("id"))
+ node.setAttribute("id", node.getAttribute("id").replace(/\d+$/, newId));
+
+ for (let i = 0, len = node.childNodes.length; i < len; i++)
+ fixId(node.childNodes[i], newId);
+ }
+ return node;
+ }
+ E("viewMenu").appendChild(fixId(E("filters-view-menu1").cloneNode(true), "2"));
+ },
+
+ /**
+ * <tree> element containing the filters.
+ * @type XULElement
+ */
+ get treeElement() E("filtersTree"),
+
+ /**
+ * Tests whether the tree is currently visible.
+ */
+ get visible()
+ {
+ return !this.treeElement.parentNode.collapsed;
+ },
+
+ /**
+ * Tests whether the tree is currently focused.
+ * @type Boolean
+ */
+ get focused()
+ {
+ let focused = document.commandDispatcher.focusedElement;
+ while (focused)
+ {
+ if ("treeBoxObject" in focused && focused.treeBoxObject == FilterView.boxObject)
+ return true;
+ focused = focused.parentNode;
+ }
+ return false;
+ },
+
+ /**
+ * Updates visible filter commands whenever the selected subscription changes.
+ */
+ updateCommands: function()
+ {
+ E("filters-add-command").setAttribute("disabled", !FilterView.editable);
+ },
+
+ /**
+ * Called whenever filter actions menu is opened, initializes menu items.
+ */
+ fillActionsPopup: function()
+ {
+ let editable = FilterView.editable;
+ let items = FilterView.selectedItems.filter(function(i) !i.filter.dummy);
+ items.sort(function(entry1, entry2) entry1.index - entry2.index);
+ let activeItems = items.filter(function(i) i.filter instanceof ActiveFilter);
+
+ E("filters-edit-command").setAttribute("disabled", !editable || !items.length);
+ E("filters-delete-command").setAttribute("disabled", !editable || !items.length);
+ E("filters-resetHitCounts-command").setAttribute("disabled", !activeItems.length);
+ E("filters-moveUp-command").setAttribute("disabled", !editable || FilterView.isSorted() || !items.length || items[0].index == 0);
+ E("filters-moveDown-command").setAttribute("disabled", !editable || FilterView.isSorted() || !items.length || items[items.length - 1].index == FilterView.rowCount - 1);
+ E("filters-copy-command").setAttribute("disabled", !items.length);
+ E("filters-cut-command").setAttribute("disabled", !editable || !items.length);
+ E("filters-paste-command").setAttribute("disabled", !editable || !Utils.clipboard.hasDataMatchingFlavors(["text/unicode"], 1, Utils.clipboard.kGlobalClipboard));
+ },
+
+ /**
+ * Changes sort current order for the tree. Sorts by filter column if the list is unsorted.
+ * @param {String} order either "ascending" or "descending"
+ */
+ setSortOrder: function(sortOrder)
+ {
+ let col = (FilterView.sortColumn ? FilterView.sortColumn.id : "col-filter");
+ FilterView.sortBy(col, sortOrder);
+ },
+
+ /**
+ * Toggles the visibility of a tree column.
+ */
+ toggleColumn: function(/**String*/ id)
+ {
+ let col = E(id);
+ col.setAttribute("hidden", col.hidden ? "false" : "true");
+ },
+
+ /**
+ * Enables or disables all filters in the current selection.
+ */
+ selectionToggleDisabled: function()
+ {
+ if (this.treeElement.editingColumn)
+ return;
+
+ let items = FilterView.selectedItems.filter(function(i) i.filter instanceof ActiveFilter);
+ if (items.length)
+ {
+ FilterView.boxObject.beginUpdateBatch();
+ let newValue = !items[0].filter.disabled;
+ for (let i = 0; i < items.length; i++)
+ items[i].filter.disabled = newValue;
+ FilterView.boxObject.endUpdateBatch();
+ }
+ },
+
+ /**
+ * Selects all entries in the list.
+ */
+ selectAll: function()
+ {
+ if (this.treeElement.editingColumn)
+ return;
+
+ FilterView.selection.selectAll();
+ this.treeElement.focus();
+ },
+
+ /**
+ * Starts editing the current filter.
+ */
+ startEditing: function()
+ {
+ if (this.treeElement.editingColumn)
+ return;
+
+ this.treeElement.startEditing(FilterView.selection.currentIndex, FilterView.boxObject.columns.getNamedColumn("col-filter"));
+ },
+
+ /**
+ * Starts editing a new filter at the current position.
+ */
+ insertFilter: function()
+ {
+ if (!FilterView.editable || this.treeElement.editingColumn)
+ return;
+
+ FilterView.insertEditDummy();
+ this.startEditing();
+
+ let tree = this.treeElement;
+ let listener = function(event)
+ {
+ if (event.attrName == "editing" && tree.editingRow < 0)
+ {
+ tree.removeEventListener("DOMAttrModified", listener, false);
+ FilterView.removeEditDummy();
+ }
+ }
+ tree.addEventListener("DOMAttrModified", listener, false);
+ },
+
+ /**
+ * Deletes items from the list.
+ */
+ deleteItems: function(/**Array*/ items)
+ {
+ let oldIndex = FilterView.selection.currentIndex;
+ items.sort(function(entry1, entry2) entry2.index - entry1.index);
+
+ for (let i = 0; i < items.length; i++)
+ FilterStorage.removeFilter(items[i].filter, FilterView._subscription, items[i].index);
+
+ FilterView.selectRow(oldIndex);
+ },
+
+ /**
+ * Deletes selected filters.
+ */
+ deleteSelected: function()
+ {
+ if (!FilterView.editable || this.treeElement.editingColumn)
+ return;
+
+ let items = FilterView.selectedItems;
+ if (items.length == 0 || (items.length >= 2 && !Utils.confirm(window, this.treeElement.getAttribute("_removewarning"))))
+ return;
+
+ this.deleteItems(items)
+ },
+
+ /**
+ * Resets hit counts of the selected filters.
+ */
+ resetHitCounts: function()
+ {
+ if (this.treeElement.editingColumn)
+ return;
+
+ let items = FilterView.selectedItems.filter(function(i) i.filter instanceof ActiveFilter);
+ if (items.length)
+ FilterStorage.resetHitCounts(items.map(function(i) i.filter));
+ },
+
+ /**
+ * Moves items to a different position in the list.
+ * @param {Array} items
+ * @param {Integer} offset negative offsets move the items up, positive down
+ */
+ _moveItems: function(/**Array*/ items, /**Integer*/ offset)
+ {
+ if (!items.length)
+ return;
+
+ if (offset < 0)
+ {
+ items.sort(function(entry1, entry2) entry1.index - entry2.index);
+ let position = items[0].index + offset;
+ if (position < 0)
+ return;
+
+ for (let i = 0; i < items.length; i++)
+ FilterStorage.moveFilter(items[i].filter, FilterView._subscription, items[i].index, position++);
+ FilterView.selection.rangedSelect(position - items.length, position - 1, false);
+ }
+ else if (offset > 0)
+ {
+ items.sort(function(entry1, entry2) entry2.index - entry1.index);
+ let position = items[0].index + offset;
+ if (position >= FilterView.rowCount)
+ return;
+
+ for (let i = 0; i < items.length; i++)
+ FilterStorage.moveFilter(items[i].filter, FilterView._subscription, items[i].index, position--);
+ FilterView.selection.rangedSelect(position + 1, position + items.length, false);
+ }
+ },
+
+ /**
+ * Moves selected filters one line up.
+ */
+ moveUp: function()
+ {
+ if (!FilterView.editable || FilterView.isEmpty || FilterView.isSorted() || this.treeElement.editingColumn)
+ return;
+
+ this._moveItems(FilterView.selectedItems, -1);
+ },
+
+ /**
+ * Moves selected filters one line down.
+ */
+ moveDown: function()
+ {
+ if (!FilterView.editable || FilterView.isEmpty || FilterView.isSorted() || this.treeElement.editingColumn)
+ return;
+
+ this._moveItems(FilterView.selectedItems, 1);
+ },
+
+ /**
+ * Fills the context menu of the filters columns.
+ */
+ fillColumnPopup: function(/**Element*/ element)
+ {
+ let suffix = element.id.match(/\d+$/)[0] || "1";
+
+ E("filters-view-filter" + suffix).setAttribute("checked", !E("col-filter").hidden);
+ E("filters-view-slow" + suffix).setAttribute("checked", !E("col-slow").hidden);
+ E("filters-view-enabled" + suffix).setAttribute("checked", !E("col-enabled").hidden);
+ E("filters-view-hitcount" + suffix).setAttribute("checked", !E("col-hitcount").hidden);
+ E("filters-view-lasthit" + suffix).setAttribute("checked", !E("col-lasthit").hidden);
+
+ let sortColumn = FilterView.sortColumn;
+ let sortColumnID = (sortColumn ? sortColumn.id : null);
+ let sortDir = (sortColumn ? sortColumn.getAttribute("sortDirection") : "natural");
+ E("filters-sort-none" + suffix).setAttribute("checked", sortColumn == null);
+ E("filters-sort-filter" + suffix).setAttribute("checked", sortColumnID == "col-filter");
+ E("filters-sort-enabled" + suffix).setAttribute("checked", sortColumnID == "col-enabled");
+ E("filters-sort-hitcount" + suffix).setAttribute("checked", sortColumnID == "col-hitcount");
+ E("filters-sort-lasthit" + suffix).setAttribute("checked", sortColumnID == "col-lasthit");
+ E("filters-sort-asc" + suffix).setAttribute("checked", sortDir == "ascending");
+ E("filters-sort-desc" + suffix).setAttribute("checked", sortDir == "descending");
+ },
+
+ /**
+ * Fills tooltip with the item data.
+ */
+ fillTooltip: function(event)
+ {
+ let item = FilterView.getItemAt(event.clientX, event.clientY);
+ if (!item || item.filter.dummy)
+ {
+ event.preventDefault();
+ return;
+ }
+
+ function setMultilineContent(box, text)
+ {
+ while (box.firstChild)
+ box.removeChild(box.firstChild);
+
+ for (var i = 0; i < text.length; i += 80)
+ {
+ var description = document.createElement("description");
+ description.setAttribute("value", text.substr(i, 80));
+ box.appendChild(description);
+ }
+ }
+
+ setMultilineContent(E("tooltip-filter"), item.filter.text);
+
+ E("tooltip-hitcount-row").hidden = !(item.filter instanceof ActiveFilter);
+ E("tooltip-lasthit-row").hidden = !(item.filter instanceof ActiveFilter) || !item.filter.lastHit;
+ if (item.filter instanceof ActiveFilter)
+ {
+ E("tooltip-hitcount").setAttribute("value", item.filter.hitCount)
+ E("tooltip-lasthit").setAttribute("value", Utils.formatTime(item.filter.lastHit))
+ }
+
+ E("tooltip-additional").hidden = false;
+ if (item.filter instanceof InvalidFilter && item.filter.reason)
+ E("tooltip-additional").textContent = item.filter.reason;
+ else if (item.filter instanceof RegExpFilter && defaultMatcher.isSlowFilter(item.filter))
+ E("tooltip-additional").textContent = Utils.getString("filter_regexp_tooltip");
+ else
+ E("tooltip-additional").hidden = true;
+ },
+
+ /**
+ * Called whenever a key is pressed on the list.
+ */
+ keyPress: function(/**Event*/ event)
+ {
+ if (event.target != E("filtersTree"))
+ return;
+
+ let modifiers = 0;
+ if (event.altKey)
+ modifiers |= SubscriptionActions._altMask;
+ if (event.ctrlKey)
+ modifiers |= SubscriptionActions._ctrlMask;
+ if (event.metaKey)
+ modifiers |= SubscriptionActions._metaMask;
+
+ if (event.charCode == " ".charCodeAt(0) && modifiers == 0 && !E("col-enabled").hidden)
+ this.selectionToggleDisabled();
+ else if (event.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_UP && modifiers == SubscriptionActions._accelMask)
+ {
+ E("filters-moveUp-command").doCommand();
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ else if (event.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_DOWN && modifiers == SubscriptionActions._accelMask)
+ {
+ E("filters-moveDown-command").doCommand();
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ },
+
+ /**
+ * Copies selected items to clipboard and optionally removes them from the
+ * list after that.
+ */
+ copySelected: function(/**Boolean*/ keep)
+ {
+ let items = FilterView.selectedItems;
+ if (!items.length)
+ return;
+
+ items.sort(function(entry1, entry2) entry1.index - entry2.index);
+ let text = items.map(function(i) i.filter.text).join(IO.lineBreak);
+ Utils.clipboardHelper.copyString(text);
+
+ if (!keep && FilterView.editable && !this.treeElement.editingColumn)
+ this.deleteItems(items);
+ },
+
+ /**
+ * Pastes text from clipboard as filters at the current position.
+ */
+ paste: function()
+ {
+ if (!FilterView.editable || this.treeElement.editingColumn)
+ return;
+
+ let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
+ transferable.addDataFlavor("text/unicode");
+
+ let data;
+ try
+ {
+ data = {};
+ Utils.clipboard.getData(transferable, Utils.clipboard.kGlobalClipboard);
+ transferable.getTransferData("text/unicode", data, {});
+ data = data.value.QueryInterface(Ci.nsISupportsString).data;
+ }
+ catch (e) {
+ return;
+ }
+
+ let item = FilterView.currentItem;
+ let position = (item ? item.index : FilterView.data.length);
+
+ let lines = data.replace(/\r/g, "").split("\n");
+ for (let i = 0; i < lines.length; i++)
+ {
+ let line = Filter.normalize(lines[i]);
+ if (line)
+ {
+ let filter = Filter.fromText(line);
+ FilterStorage.addFilter(filter, FilterView._subscription, position++);
+ }
+ }
+ },
+
+ dragItems: null,
+
+ /**
+ * Called whenever the user starts a drag operation.
+ */
+ startDrag: function(/**Event*/ event)
+ {
+ let items = FilterView.selectedItems;
+ if (!items.length)
+ return;
+
+ items.sort(function(entry1, entry2) entry1.index - entry2.index);
+ event.dataTransfer.setData("text/plain", items.map(function(i) i.filter.text).join(IO.lineBreak));
+ this.dragItems = items;
+ event.stopPropagation();
+ },
+
+ /**
+ * Called to check whether moving the items to the given position is possible.
+ */
+ canDrop: function(/**Integer*/ newPosition, /**nsIDOMDataTransfer*/ dataTransfer)
+ {
+ if (!FilterView.editable || this.treeElement.editingColumn)
+ return false;
+
+ // If we aren't dragging items then maybe we got filters as plain text
+ if (!this.dragItems)
+ return dataTransfer && dataTransfer.getData("text/plain");
+
+ if (FilterView.isEmpty || FilterView.isSorted())
+ return false;
+
+ if (newPosition < this.dragItems[0].index)
+ return true;
+ else if (newPosition > this.dragItems[this.dragItems.length - 1].index + 1)
+ return true;
+ else
+ return false;
+ },
+
+ /**
+ * Called when the user decides to drop the items.
+ */
+ drop: function(/**Integer*/ newPosition, /**nsIDOMDataTransfer*/ dataTransfer)
+ {
+ if (!FilterView.editable || this.treeElement.editingColumn)
+ return;
+
+ if (!this.dragItems)
+ {
+ // We got filters as plain text, insert them into the list
+ let data = (dataTransfer ? dataTransfer.getData("text/plain") : null);
+ if (data)
+ {
+ let lines = data.replace(/\r/g, "").split("\n");
+ for (let i = 0; i < lines.length; i++)
+ {
+ let line = Filter.normalize(lines[i]);
+ if (line)
+ {
+ let filter = Filter.fromText(line);
+ FilterStorage.addFilter(filter, FilterView._subscription, newPosition++);
+ }
+ }
+ }
+ return;
+ }
+
+ if (FilterView.isEmpty || FilterView.isSorted())
+ return;
+
+ if (newPosition < this.dragItems[0].index)
+ this._moveItems(this.dragItems, newPosition - this.dragItems[0].index);
+ else if (newPosition > this.dragItems[this.dragItems.length - 1].index + 1)
+ this._moveItems(this.dragItems, newPosition - this.dragItems[this.dragItems.length - 1].index - 1);
+ },
+
+ /**
+ * Called whenever the a drag operation finishes.
+ */
+ endDrag: function(/**Event*/ event)
+ {
+ this.dragItems = null;
+ },
+
+ /**
+ * Called if filters have been dragged into a subscription and need to be removed.
+ */
+ removeDraggedFilters: function()
+ {
+ if (!this.dragItems)
+ return;
+
+ this.deleteItems(this.dragItems);
+ }
+};
+
+window.addEventListener("load", function()
+{
+ FilterActions.init();
+}, false);
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-filterview.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-filterview.js
new file mode 100644
index 0000000..dcee183
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-filterview.js
@@ -0,0 +1,849 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+/**
+ * nsITreeView implementation to display filters of a particular filter
+ * subscription.
+ * @class
+ */
+var FilterView =
+{
+ /**
+ * Initialization function.
+ */
+ init: function()
+ {
+ // "Manually" remove access key for col-slow tooltip, Utils.splitAllLabels()
+ // won't do it.
+ let slowColumn = document.getElementById("col-slow");
+ if (slowColumn)
+ {
+ for (let attr of ["display", "tooltiptext"])
+ {
+ let value = slowColumn.getAttribute(attr);
+ if (!value)
+ continue;
+ let [label, accessKey] = Utils.splitLabel(value);
+ if (label != value)
+ slowColumn.setAttribute(attr, label);
+ }
+ }
+
+ if (this.sortProcs)
+ return;
+
+ function compareText(/**Filter*/ filter1, /**Filter*/ filter2)
+ {
+ if (filter1.text < filter2.text)
+ return -1;
+ else if (filter1.text > filter2.text)
+ return 1;
+ else
+ return 0;
+ }
+ function compareSlow(/**Filter*/ filter1, /**Filter*/ filter2)
+ {
+ let isSlow1 = filter1 instanceof RegExpFilter && defaultMatcher.isSlowFilter(filter1);
+ let isSlow2 = filter2 instanceof RegExpFilter && defaultMatcher.isSlowFilter(filter2);
+ return isSlow1 - isSlow2;
+ }
+ function compareEnabled(/**Filter*/ filter1, /**Filter*/ filter2)
+ {
+ let hasEnabled1 = (filter1 instanceof ActiveFilter ? 1 : 0);
+ let hasEnabled2 = (filter2 instanceof ActiveFilter ? 1 : 0);
+ if (hasEnabled1 != hasEnabled2)
+ return hasEnabled1 - hasEnabled2;
+ else if (hasEnabled1)
+ return (filter2.disabled - filter1.disabled);
+ else
+ return 0;
+ }
+ function compareHitCount(/**Filter*/ filter1, /**Filter*/ filter2)
+ {
+ let hasHitCount1 = (filter1 instanceof ActiveFilter ? 1 : 0);
+ let hasHitCount2 = (filter2 instanceof ActiveFilter ? 1 : 0);
+ if (hasHitCount1 != hasHitCount2)
+ return hasHitCount1 - hasHitCount2;
+ else if (hasHitCount1)
+ return filter1.hitCount - filter2.hitCount;
+ else
+ return 0;
+ }
+ function compareLastHit(/**Filter*/ filter1, /**Filter*/ filter2)
+ {
+ let hasLastHit1 = (filter1 instanceof ActiveFilter ? 1 : 0);
+ let hasLastHit2 = (filter2 instanceof ActiveFilter ? 1 : 0);
+ if (hasLastHit1 != hasLastHit2)
+ return hasLastHit1 - hasLastHit2;
+ else if (hasLastHit1)
+ return filter1.lastHit - filter2.lastHit;
+ else
+ return 0;
+ }
+
+ /**
+ * Creates a sort function from a primary and a secondary comparison function.
+ * @param {Function} cmpFunc comparison function to be called first
+ * @param {Function} fallbackFunc (optional) comparison function to be called if primary function returns 0
+ * @param {Boolean} desc if true, the result of the primary function (not the secondary function) will be reversed - sorting in descending order
+ * @result {Function} comparison function to be used
+ */
+ function createSortFunction(cmpFunc, fallbackFunc, desc)
+ {
+ let factor = (desc ? -1 : 1);
+
+ return function(entry1, entry2)
+ {
+ // Comment replacements not bound to a filter always go last
+ let isLast1 = ("origFilter" in entry1 && entry1.filter == null);
+ let isLast2 = ("origFilter" in entry2 && entry2.filter == null);
+ if (isLast1)
+ return (isLast2 ? 0 : 1)
+ else if (isLast2)
+ return -1;
+
+ let ret = cmpFunc(entry1.filter, entry2.filter);
+ if (ret == 0 && fallbackFunc)
+ return fallbackFunc(entry1.filter, entry2.filter);
+ else
+ return factor * ret;
+ }
+ }
+
+ this.sortProcs = {
+ filter: createSortFunction(compareText, null, false),
+ filterDesc: createSortFunction(compareText, null, true),
+ slow: createSortFunction(compareSlow, compareText, true),
+ slowDesc: createSortFunction(compareSlow, compareText, false),
+ enabled: createSortFunction(compareEnabled, compareText, false),
+ enabledDesc: createSortFunction(compareEnabled, compareText, true),
+ hitcount: createSortFunction(compareHitCount, compareText, false),
+ hitcountDesc: createSortFunction(compareHitCount, compareText, true),
+ lasthit: createSortFunction(compareLastHit, compareText, false),
+ lasthitDesc: createSortFunction(compareLastHit, compareText, true)
+ };
+
+ let me = this;
+ let proxy = function()
+ {
+ return me._onChange.apply(me, arguments);
+ };
+ FilterNotifier.addListener(proxy);
+ window.addEventListener("unload", function()
+ {
+ FilterNotifier.removeListener(proxy);
+ }, false);
+ },
+
+ /**
+ * Filter change processing.
+ * @see FilterNotifier.addListener()
+ */
+ _onChange: function(action, item, param1, param2, param3)
+ {
+ switch (action)
+ {
+ case "subscription.updated":
+ {
+ if (item == this._subscription)
+ this.refresh(true);
+ break;
+ }
+ case "filter.disabled":
+ case "filter.hitCount":
+ case "filter.lastHit":
+ {
+ this.updateFilter(item);
+ break;
+ }
+ case "filter.added":
+ {
+ let subscription = param1;
+ let position = param2;
+ if (subscription == this._subscription)
+ this.addFilterAt(position, item);
+ break;
+ }
+ case "filter.removed":
+ {
+ let subscription = param1;
+ let position = param2;
+ if (subscription == this._subscription)
+ this.removeFilterAt(position);
+ break;
+ }
+ case "filter.moved":
+ {
+ let subscription = param1;
+ let oldPosition = param2;
+ let newPosition = param3;
+ if (subscription == this._subscription)
+ this.moveFilterAt(oldPosition, newPosition);
+ break;
+ }
+ }
+ },
+
+ /**
+ * Box object of the tree that this view is attached to.
+ * @type nsITreeBoxObject
+ */
+ boxObject: null,
+
+ /**
+ * Map of used cell properties to the corresponding nsIAtom representations.
+ */
+ atoms: null,
+
+ /**
+ * "Filter" to be displayed if no filter group is selected.
+ */
+ noGroupDummy: null,
+
+ /**
+ * "Filter" to be displayed if the selected group is empty.
+ */
+ noFiltersDummy: null,
+
+ /**
+ * "Filter" to be displayed for a new filter being edited.
+ */
+ editDummy: null,
+
+ /**
+ * Displayed list of filters, might be sorted.
+ * @type Filter[]
+ */
+ data: [],
+
+ /**
+ * <tree> element that the view is attached to.
+ * @type XULElement
+ */
+ get treeElement() this.boxObject ? this.boxObject.treeBody.parentNode : null,
+
+ /**
+ * Checks whether the list is currently empty (regardless of dummy entries).
+ * @type Boolean
+ */
+ get isEmpty()
+ {
+ return !this._subscription || !this._subscription.filters.length;
+ },
+
+ /**
+ * Checks whether the filters in the view can be changed.
+ * @type Boolean
+ */
+ get editable()
+ {
+ return (FilterView._subscription instanceof SpecialSubscription);
+ },
+
+ /**
+ * Returns current item of the list.
+ * @type Object
+ */
+ get currentItem()
+ {
+ let index = this.selection.currentIndex;
+ if (index >= 0 && index < this.data.length)
+ return this.data[index];
+ return null;
+ },
+
+ /**
+ * Returns items that are currently selected in the list.
+ * @type Object[]
+ */
+ get selectedItems()
+ {
+ let items = []
+ for (let i = 0; i < this.selection.getRangeCount(); i++)
+ {
+ let min = {};
+ let max = {};
+ this.selection.getRangeAt(i, min, max);
+ for (let j = min.value; j <= max.value; j++)
+ if (j >= 0 && j < this.data.length)
+ items.push(this.data[j]);
+ }
+ return items;
+ },
+
+ getItemAt: function(x, y)
+ {
+ let row = this.boxObject.getRowAt(x, y);
+ if (row >= 0 && row < this.data.length)
+ return this.data[row];
+ else
+ return null;
+ },
+
+ _subscription: 0,
+
+ /**
+ * Filter subscription being displayed.
+ * @type Subscription
+ */
+ get subscription() this._subscription,
+ set subscription(value)
+ {
+ if (value == this._subscription)
+ return;
+
+ // Make sure the editor is done before we update the list.
+ if (this.treeElement)
+ this.treeElement.stopEditing(true);
+
+ this._subscription = value;
+ this.refresh(true);
+ },
+
+ /**
+ * Will be true if updates are outstanding because the list was hidden.
+ */
+ _dirty: false,
+
+ /**
+ * Updates internal view data after a change.
+ * @param {Boolean} force if false, a refresh will only happen if previous
+ * changes were suppressed because the list was hidden
+ */
+ refresh: function(force)
+ {
+ if (FilterActions.visible)
+ {
+ if (!force && !this._dirty)
+ return;
+ this._dirty = false;
+ this.updateData();
+ this.selectRow(0);
+ }
+ else
+ this._dirty = true;
+ },
+
+ /**
+ * Map of comparison functions by column ID or column ID + "Desc" for
+ * descending sort order.
+ * @const
+ */
+ sortProcs: null,
+
+ /**
+ * Column that the list is currently sorted on.
+ * @type Element
+ */
+ sortColumn: null,
+
+ /**
+ * Sorting function currently in use.
+ * @type Function
+ */
+ sortProc: null,
+
+ /**
+ * Resorts the list.
+ * @param {String} col ID of the column to sort on. If null, the natural order is restored.
+ * @param {String} direction "ascending" or "descending", if null the sort order is toggled.
+ */
+ sortBy: function(col, direction)
+ {
+ let newSortColumn = null;
+ if (col)
+ {
+ newSortColumn = this.boxObject.columns.getNamedColumn(col).element;
+ if (!direction)
+ {
+ if (this.sortColumn == newSortColumn)
+ direction = (newSortColumn.getAttribute("sortDirection") == "ascending" ? "descending" : "ascending");
+ else
+ direction = "ascending";
+ }
+ }
+
+ if (this.sortColumn && this.sortColumn != newSortColumn)
+ this.sortColumn.removeAttribute("sortDirection");
+
+ this.sortColumn = newSortColumn;
+ if (this.sortColumn)
+ {
+ this.sortColumn.setAttribute("sortDirection", direction);
+ this.sortProc = this.sortProcs[col.replace(/^col-/, "") + (direction == "descending" ? "Desc" : "")];
+ }
+ else
+ this.sortProc = null;
+
+ if (this.data.length > 1)
+ {
+ this.updateData();
+ this.boxObject.invalidate();
+ }
+ },
+
+ /**
+ * Inserts dummy entry into the list if necessary.
+ */
+ addDummyRow: function()
+ {
+ if (this.boxObject && this.data.length == 0)
+ {
+ if (this._subscription)
+ this.data.splice(0, 0, this.noFiltersDummy);
+ else
+ this.data.splice(0, 0, this.noGroupDummy);
+ this.boxObject.rowCountChanged(0, 1);
+ }
+ },
+
+ /**
+ * Removes dummy entry from the list if present.
+ */
+ removeDummyRow: function()
+ {
+ if (this.boxObject && this.isEmpty && this.data.length)
+ {
+ this.data.splice(0, 1);
+ this.boxObject.rowCountChanged(0, -1);
+ }
+ },
+
+ /**
+ * Inserts dummy row when a new filter is being edited.
+ */
+ insertEditDummy: function()
+ {
+ FilterView.removeDummyRow();
+ let position = this.selection.currentIndex;
+ if (position >= this.data.length)
+ position = this.data.length - 1;
+ if (position < 0)
+ position = 0;
+
+ this.editDummy.index = (position < this.data.length ? this.data[position].index : this.data.length);
+ this.editDummy.position = position;
+ this.data.splice(position, 0, this.editDummy);
+ this.boxObject.rowCountChanged(position, 1);
+ this.selectRow(position);
+ },
+
+ /**
+ * Removes dummy row once the edit is finished.
+ */
+ removeEditDummy: function()
+ {
+ let position = this.editDummy.position;
+ if (typeof position != "undefined" && position < this.data.length && this.data[position] == this.editDummy)
+ {
+ this.data.splice(position, 1);
+ this.boxObject.rowCountChanged(position, -1);
+ FilterView.addDummyRow();
+
+ this.selectRow(position);
+ }
+ },
+
+ /**
+ * Selects a row in the tree and makes sure it is visible.
+ */
+ selectRow: function(row)
+ {
+ if (this.selection)
+ {
+ row = Math.min(Math.max(row, 0), this.data.length - 1);
+ this.selection.select(row);
+ this.boxObject.ensureRowIsVisible(row);
+ }
+ },
+
+ /**
+ * Finds a particular filter in the list and selects it.
+ */
+ selectFilter: function(/**Filter*/ filter)
+ {
+ let index = -1;
+ for (let i = 0; i < this.data.length; i++)
+ {
+ if (this.data[i].filter == filter)
+ {
+ index = i;
+ break;
+ }
+ }
+ if (index >= 0)
+ {
+ this.selectRow(index);
+ this.treeElement.focus();
+ }
+ },
+
+ /**
+ * Updates value of data property on sorting or filter subscription changes.
+ */
+ updateData: function()
+ {
+ let oldCount = this.rowCount;
+ if (this._subscription && this._subscription.filters.length)
+ {
+ this.data = this._subscription.filters.map(function(f, i) ({index: i, filter: f}));
+ if (this.sortProc)
+ {
+ // Hide comments in the list, they should be sorted like the filter following them
+ let followingFilter = null;
+ for (let i = this.data.length - 1; i >= 0; i--)
+ {
+ if (this.data[i].filter instanceof CommentFilter)
+ {
+ this.data[i].origFilter = this.data[i].filter;
+ this.data[i].filter = followingFilter;
+ }
+ else
+ followingFilter = this.data[i].filter;
+ }
+
+ this.data.sort(this.sortProc);
+
+ // Restore comments
+ for (let i = 0; i < this.data.length; i++)
+ {
+ if ("origFilter" in this.data[i])
+ {
+ this.data[i].filter = this.data[i].origFilter;
+ delete this.data[i].origFilter;
+ }
+ }
+ }
+ }
+ else
+ this.data = [];
+
+ if (this.boxObject)
+ {
+ this.boxObject.rowCountChanged(0, -oldCount);
+ this.boxObject.rowCountChanged(0, this.rowCount);
+ }
+
+ this.addDummyRow();
+ },
+
+ /**
+ * Called to update the view when a filter property is changed.
+ */
+ updateFilter: function(/**Filter*/ filter)
+ {
+ for (let i = 0; i < this.data.length; i++)
+ if (this.data[i].filter == filter)
+ this.boxObject.invalidateRow(i);
+ },
+
+ /**
+ * Called if a filter has been inserted at the specified position.
+ */
+ addFilterAt: function(/**Integer*/ position, /**Filter*/ filter)
+ {
+ if (this.data.length == 1 && this.data[0].filter.dummy)
+ {
+ this.data.splice(0, 1);
+ this.boxObject.rowCountChanged(0, -1);
+ }
+
+ if (this.sortProc)
+ {
+ this.updateData();
+ for (let i = 0; i < this.data.length; i++)
+ {
+ if (this.data[i].index == position)
+ {
+ position = i;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (let i = 0; i < this.data.length; i++)
+ if (this.data[i].index >= position)
+ this.data[i].index++;
+ this.data.splice(position, 0, {index: position, filter: filter});
+ }
+ this.boxObject.rowCountChanged(position, 1);
+ this.selectRow(position);
+ },
+
+ /**
+ * Called if a filter has been removed at the specified position.
+ */
+ removeFilterAt: function(/**Integer*/ position)
+ {
+ for (let i = 0; i < this.data.length; i++)
+ {
+ if (this.data[i].index == position)
+ {
+ this.data.splice(i, 1);
+ this.boxObject.rowCountChanged(i, -1);
+ i--;
+ }
+ else if (this.data[i].index > position)
+ this.data[i].index--;
+ }
+ this.addDummyRow();
+ },
+
+ /**
+ * Called if a filter has been moved within the list.
+ */
+ moveFilterAt: function(/**Integer*/ oldPosition, /**Integer*/ newPosition)
+ {
+ let dir = (oldPosition < newPosition ? 1 : -1);
+ for (let i = 0; i < this.data.length; i++)
+ {
+ if (this.data[i].index == oldPosition)
+ this.data[i].index = newPosition;
+ else if (dir * this.data[i].index > dir * oldPosition && dir * this.data[i].index <= dir * newPosition)
+ this.data[i].index -= dir;
+ }
+
+ if (!this.sortProc)
+ {
+ let item = this.data[oldPosition];
+ this.data.splice(oldPosition, 1);
+ this.data.splice(newPosition, 0, item);
+ this.boxObject.invalidateRange(Math.min(oldPosition, newPosition), Math.max(oldPosition, newPosition));
+ }
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsITreeView]),
+
+ setTree: function(boxObject)
+ {
+ this.init();
+ this.boxObject = boxObject;
+
+ if (this.boxObject)
+ {
+ this.noGroupDummy = {index: 0, filter: {text: this.boxObject.treeBody.getAttribute("noGroupText"), dummy: true}};
+ this.noFiltersDummy = {index: 0, filter: {text: this.boxObject.treeBody.getAttribute("noFiltersText"), dummy: true}};
+ this.editDummy = {filter: {text: ""}};
+
+ let atomService = Cc["@mozilla.org/atom-service;1"].getService(Ci.nsIAtomService);
+ let stringAtoms = ["col-filter", "col-enabled", "col-hitcount", "col-lasthit", "type-comment", "type-filterlist", "type-whitelist", "type-elemhide", "type-elemhideexception", "type-invalid"];
+ let boolAtoms = ["selected", "dummy", "slow", "disabled"];
+
+ this.atoms = {};
+ for each (let atom in stringAtoms)
+ this.atoms[atom] = atomService.getAtom(atom);
+ for each (let atom in boolAtoms)
+ {
+ this.atoms[atom + "-true"] = atomService.getAtom(atom + "-true");
+ this.atoms[atom + "-false"] = atomService.getAtom(atom + "-false");
+ }
+
+ let columns = this.boxObject.columns;
+ for (let i = 0; i < columns.length; i++)
+ if (columns[i].element.hasAttribute("sortDirection"))
+ this.sortBy(columns[i].id, columns[i].element.getAttribute("sortDirection"));
+
+ this.refresh(true);
+ }
+ },
+
+ selection: null,
+
+ get rowCount() this.data.length,
+
+ getCellText: function(row, col)
+ {
+ if (row < 0 || row >= this.data.length)
+ return null;
+
+ col = col.id;
+ if (col != "col-filter" && col != "col-slow" && col != "col-hitcount" && col != "col-lasthit")
+ return null;
+
+ let filter = this.data[row].filter;
+ if (col == "col-filter")
+ return filter.text;
+ else if (col == "col-slow")
+ return (filter instanceof RegExpFilter && defaultMatcher.isSlowFilter(filter) ? "!" : null);
+ else if (filter instanceof ActiveFilter)
+ {
+ if (col == "col-hitcount")
+ return filter.hitCount;
+ else if (col == "col-lasthit")
+ return (filter.lastHit ? Utils.formatTime(filter.lastHit) : null);
+ }
+
+ return null;
+ },
+
+ generateProperties: function(list, properties)
+ {
+ if (properties)
+ {
+ // Gecko 21 and below: we have an nsISupportsArray parameter, add atoms
+ // to that.
+ for (let i = 0; i < list.length; i++)
+ if (list[i] in this.atoms)
+ properties.AppendElement(this.atoms[list[i]]);
+ return null;
+ }
+ else
+ {
+ // Gecko 22+: no parameter, just return a string
+ return list.join(" ");
+ }
+ },
+
+ getColumnProperties: function(col, properties)
+ {
+ return this.generateProperties(["col-" + col.id], properties);
+ },
+
+ getRowProperties: function(row, properties)
+ {
+ if (row < 0 || row >= this.data.length)
+ return "";
+
+ let list = [];
+ let filter = this.data[row].filter;
+ list.push("selected-" + this.selection.isSelected(row));
+ list.push("slow-" + (filter instanceof RegExpFilter && defaultMatcher.isSlowFilter(filter)));
+ if (filter instanceof ActiveFilter)
+ list.push("disabled-" + filter.disabled);
+ list.push("dummy-" + ("dummy" in filter));
+
+ if (filter instanceof CommentFilter)
+ list.push("type-comment");
+ else if (filter instanceof BlockingFilter)
+ list.push("type-filterlist");
+ else if (filter instanceof WhitelistFilter)
+ list.push("type-whitelist");
+ else if (filter instanceof ElemHideFilter)
+ list.push("type-elemhide");
+ else if (filter instanceof ElemHideException)
+ list.push("type-elemhideexception");
+ else if (filter instanceof InvalidFilter)
+ list.push("type-invalid");
+
+ return this.generateProperties(list, properties);
+ },
+
+ getCellProperties: function(row, col, properties)
+ {
+ return this.getRowProperties(row, properties) + " " + this.getColumnProperties(col, properties);
+ },
+
+ cycleHeader: function(col)
+ {
+ let oldDirection = col.element.getAttribute("sortDirection");
+ if (oldDirection == "ascending")
+ this.sortBy(col.id, "descending");
+ else if (oldDirection == "descending")
+ this.sortBy(null, null);
+ else
+ this.sortBy(col.id, "ascending");
+ },
+
+ isSorted: function()
+ {
+ return (this.sortProc != null);
+ },
+
+ canDrop: function(row, orientation, dataTransfer)
+ {
+ if (orientation == Ci.nsITreeView.DROP_ON || row < 0 || row >= this.data.length || !this.editable)
+ return false;
+
+ let item = this.data[row];
+ let position = (orientation == Ci.nsITreeView.DROP_BEFORE ? item.index : item.index + 1);
+ return FilterActions.canDrop(position, dataTransfer);
+ },
+
+ drop: function(row, orientation, dataTransfer)
+ {
+ if (orientation == Ci.nsITreeView.DROP_ON || row < 0 || row >= this.data.length || !this.editable)
+ return;
+
+ let item = this.data[row];
+ let position = (orientation == Ci.nsITreeView.DROP_BEFORE ? item.index : item.index + 1);
+ FilterActions.drop(position, dataTransfer);
+ },
+
+ isEditable: function(row, col)
+ {
+ if (row < 0 || row >= this.data.length || !this.editable)
+ return false;
+
+ let filter = this.data[row].filter;
+ if (col.id == "col-filter")
+ return !("dummy" in filter);
+ else
+ return false;
+ },
+
+ setCellText: function(row, col, value)
+ {
+ if (row < 0 || row >= this.data.length || col.id != "col-filter")
+ return;
+
+ let oldFilter = this.data[row].filter;
+ let position = this.data[row].index;
+ value = Filter.normalize(value);
+ if (!value || value == oldFilter.text)
+ return;
+
+ // Make sure we don't get called recursively (see https://adblockplus.org/forum/viewtopic.php?t=9003)
+ this.treeElement.stopEditing();
+
+ let newFilter = Filter.fromText(value);
+ if (this.data[row] == this.editDummy)
+ this.removeEditDummy();
+ else
+ FilterStorage.removeFilter(oldFilter, this._subscription, position);
+ FilterStorage.addFilter(newFilter, this._subscription, position);
+ },
+
+ cycleCell: function(row, col)
+ {
+ if (row < 0 || row >= this.data.length || col.id != "col-enabled")
+ return;
+
+ let filter = this.data[row].filter;
+ if (filter instanceof ActiveFilter)
+ filter.disabled = !filter.disabled;
+ },
+
+ isContainer: function(row) false,
+ isContainerOpen: function(row) false,
+ isContainerEmpty: function(row) true,
+ getLevel: function(row) 0,
+ getParentIndex: function(row) -1,
+ hasNextSibling: function(row, afterRow) false,
+ toggleOpenState: function(row) {},
+ getProgressMode: function() null,
+ getImageSrc: function() null,
+ isSeparator: function() false,
+ performAction: function() {},
+ performActionOnRow: function() {},
+ performActionOnCell: function() {},
+ getCellValue: function() null,
+ setCellValue: function() {},
+ selectionChanged: function() {},
+};
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-search.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-search.js
new file mode 100644
index 0000000..4e817ac
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-search.js
@@ -0,0 +1,269 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Implementation of the filter search functionality.
+ * @class
+ */
+var FilterSearch =
+{
+ /**
+ * Initializes findbar widget.
+ */
+ init: function()
+ {
+ let findbar = E("findbar");
+ findbar.browser = FilterSearch.fakeBrowser;
+
+ findbar.addEventListener("keypress", function(event)
+ {
+ // Work-around for bug 490047
+ if (event.keyCode == KeyEvent.DOM_VK_RETURN)
+ event.preventDefault();
+ }, false);
+
+ // Hack to prevent "highlight all" from getting enabled
+ findbar.toggleHighlight = function() {};
+ },
+
+ /**
+ * Performs a text search.
+ * @param {String} text text to be searched
+ * @param {Integer} direction search direction: -1 (backwards), 0 (forwards
+ * starting with current), 1 (forwards starting with next)
+ * @param {Boolean} caseSensitive if true, a case-sensitive search is performed
+ * @result {Integer} one of the nsITypeAheadFind constants
+ */
+ search: function(text, direction, caseSensitive)
+ {
+ function normalizeString(string) caseSensitive ? string : string.toLowerCase();
+
+ function findText(text, direction, startIndex)
+ {
+ let list = E("filtersTree");
+ let col = list.columns.getNamedColumn("col-filter");
+ let count = list.view.rowCount;
+ for (let i = startIndex + direction; i >= 0 && i < count; i += (direction || 1))
+ {
+ let filter = normalizeString(list.view.getCellText(i, col));
+ if (filter.indexOf(text) >= 0)
+ {
+ FilterView.selectRow(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ text = normalizeString(text);
+
+ // First try to find the entry in the current list
+ if (findText(text, direction, E("filtersTree").currentIndex))
+ return Ci.nsITypeAheadFind.FIND_FOUND;
+
+ // Now go through the other subscriptions
+ let result = Ci.nsITypeAheadFind.FIND_FOUND;
+ let subscriptions = FilterStorage.subscriptions.slice();
+ subscriptions.sort(function(s1, s2) (s1 instanceof SpecialSubscription) - (s2 instanceof SpecialSubscription));
+ let current = subscriptions.indexOf(FilterView.subscription);
+ direction = direction || 1;
+ for (let i = current + direction; ; i+= direction)
+ {
+ if (i < 0)
+ {
+ i = subscriptions.length - 1;
+ result = Ci.nsITypeAheadFind.FIND_WRAPPED;
+ }
+ else if (i >= subscriptions.length)
+ {
+ i = 0;
+ result = Ci.nsITypeAheadFind.FIND_WRAPPED;
+ }
+ if (i == current)
+ break;
+
+ let subscription = subscriptions[i];
+ for (let j = 0; j < subscription.filters.length; j++)
+ {
+ let filter = normalizeString(subscription.filters[j].text);
+ if (filter.indexOf(text) >= 0)
+ {
+ let list = E(subscription instanceof SpecialSubscription ? "groups" : "subscriptions");
+ let node = Templater.getNodeForData(list, "subscription", subscription);
+ if (!node)
+ break;
+
+ // Select subscription in its list and restore focus after that
+ let oldFocus = document.commandDispatcher.focusedElement;
+ E("tabs").selectedIndex = (subscription instanceof SpecialSubscription ? 1 : 0);
+ list.ensureElementIsVisible(node);
+ list.selectItem(node);
+ if (oldFocus)
+ {
+ oldFocus.focus();
+ Utils.runAsync(oldFocus.focus, oldFocus);
+ }
+
+ Utils.runAsync(findText, null, text, direction, direction == 1 ? -1 : subscription.filters.length);
+ return result;
+ }
+ }
+ }
+
+ return Ci.nsITypeAheadFind.FIND_NOTFOUND;
+ }
+};
+
+/**
+ * Fake browser implementation to make findbar widget happy - searches in
+ * the filter list.
+ */
+FilterSearch.fakeBrowser =
+{
+ finder:
+ {
+ _resultListeners: [],
+ searchString: null,
+ caseSensitive: false,
+ lastResult: null,
+
+ _notifyResultListeners: function(result, findBackwards)
+ {
+ this.lastResult = result;
+ for each (let listener in this._resultListeners)
+ listener.onFindResult(result, findBackwards);
+ },
+
+ fastFind: function(searchString, linksOnly, drawOutline)
+ {
+ this.searchString = searchString;
+ let result = FilterSearch.search(this.searchString, 0,
+ this.caseSensitive);
+ this._notifyResultListeners(result, false);
+ },
+
+ findAgain: function(findBackwards, linksOnly, drawOutline)
+ {
+ let result = FilterSearch.search(this.searchString,
+ findBackwards ? -1 : 1,
+ this.caseSensitive);
+ this._notifyResultListeners(result, findBackwards);
+ },
+
+ addResultListener: function(listener)
+ {
+ if (this._resultListeners.indexOf(listener) === -1)
+ this._resultListeners.push(listener);
+ },
+
+ removeResultListener: function(listener)
+ {
+ let index = this._resultListeners.indexOf(listener);
+ if (index !== -1)
+ this._resultListeners.splice(index, 1);
+ },
+
+ // Irrelevant for us
+ highlight: function(highlight, word) {},
+ enableSelection: function() {},
+ removeSelection: function() {},
+ focusContent: function() {},
+ keyPress: function() {}
+ },
+
+ get _lastSearchString()
+ {
+ return this.finder.searchString;
+ },
+
+ // This was used before Firefox 27 instead of the "finder" property.
+ fastFind:
+ {
+ get searchString()
+ {
+ return FilterSearch.fakeBrowser.finder.searchString;
+ },
+
+ set searchString(searchString)
+ {
+ FilterSearch.fakeBrowser.finder.searchString = searchString;
+ },
+
+ foundLink: null,
+ foundEditable: null,
+
+ get caseSensitive()
+ {
+ return FilterSearch.fakeBrowser.finder.caseSensitive;
+ },
+
+ set caseSensitive(caseSensitive)
+ {
+ FilterSearch.fakeBrowser.finder.caseSensitive = caseSensitive;
+ },
+
+ get currentWindow() FilterSearch.fakeBrowser.contentWindow,
+
+ find: function(searchString, linksOnly)
+ {
+ FilterSearch.fakeBrowser.finder.fastFind(searchString, linksOnly);
+ return FilterSearch.fakeBrowser.finder.lastResult;
+ },
+
+ findAgain: function(findBackwards, linksOnly)
+ {
+ FilterSearch.fakeBrowser.finder.findAgain(findBackwards, linksOnly);
+ return FilterSearch.fakeBrowser.finder.lastResult;
+ },
+
+ // Irrelevant for us
+ init: function() {},
+ setDocShell: function() {},
+ setSelectionModeAndRepaint: function() {},
+ collapseSelection: function() {}
+ },
+ currentURI: Utils.makeURI("http://example.com/"),
+ contentWindow:
+ {
+ focus: function()
+ {
+ E("filtersTree").focus();
+ },
+ scrollByLines: function(num)
+ {
+ E("filtersTree").boxObject.scrollByLines(num);
+ },
+ scrollByPages: function(num)
+ {
+ E("filtersTree").boxObject.scrollByPages(num);
+ },
+ },
+
+ addEventListener: function(event, handler, capture)
+ {
+ E("filtersTree").addEventListener(event, handler, capture);
+ },
+ removeEventListener: function(event, handler, capture)
+ {
+ E("filtersTree").addEventListener(event, handler, capture);
+ },
+};
+
+window.addEventListener("load", function()
+{
+ FilterSearch.init();
+}, false);
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-subscriptionactions.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-subscriptionactions.js
new file mode 100644
index 0000000..222a4a6
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-subscriptionactions.js
@@ -0,0 +1,606 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Implemetation of the various actions that can be performed on subscriptions.
+ * @class
+ */
+var SubscriptionActions =
+{
+ /**
+ * Returns the subscription list currently having focus if any.
+ * @type Element
+ */
+ get focusedList()
+ {
+ return E("tabs").selectedPanel.getElementsByTagName("richlistbox")[0];
+ },
+
+ /**
+ * Returns the currently selected and focused subscription item if any.
+ * @type Element
+ */
+ get selectedItem()
+ {
+ let list = this.focusedList;
+ return (list ? list.selectedItem : null);
+ },
+
+ /**
+ * Finds the subscription for a particular filter, selects it and selects the
+ * filter.
+ */
+ selectFilter: function(/**Filter*/ filter)
+ {
+ let node = null;
+ let tabIndex = -1;
+ let subscriptions = filter.subscriptions.slice();
+ subscriptions.sort(function(s1, s2) s1.disabled - s2.disabled);
+ for (let i = 0; i < subscriptions.length; i++)
+ {
+ let subscription = subscriptions[i];
+ let list = E(subscription instanceof SpecialSubscription ? "groups" : "subscriptions");
+ tabIndex = (subscription instanceof SpecialSubscription ? 1 : 0);
+ node = Templater.getNodeForData(list, "subscription", subscription);
+ if (node)
+ break;
+ }
+ if (node)
+ {
+ E("tabs").selectedIndex = tabIndex;
+ Utils.runAsync(function()
+ {
+ node.parentNode.ensureElementIsVisible(node);
+ node.parentNode.selectItem(node);
+ if (!FilterActions.visible)
+ E("subscription-showHideFilters-command").doCommand();
+ Utils.runAsync(FilterView.selectFilter, FilterView, filter);
+ });
+ }
+ },
+
+ /**
+ * Updates subscription commands whenever the selected subscription changes.
+ * Note: this method might be called with a wrong "this" value.
+ */
+ updateCommands: function()
+ {
+ let node = SubscriptionActions.selectedItem;
+ let data = Templater.getDataForNode(node);
+ let subscription = (data ? data.subscription : null)
+ E("subscription-editTitle-command").setAttribute("disabled", !subscription ||
+ subscription.fixedTitle);
+ E("subscription-update-command").setAttribute("disabled", !subscription ||
+ !(subscription instanceof DownloadableSubscription) ||
+ Synchronizer.isExecuting(subscription.url));
+ E("subscription-moveUp-command").setAttribute("disabled", !subscription ||
+ !node || !node.previousSibling || !!node.previousSibling.id);
+ E("subscription-moveDown-command").setAttribute("disabled", !subscription ||
+ !node || !node.nextSibling || !!node.nextSibling.id);
+ },
+
+ /**
+ * Starts title editing for the selected subscription.
+ */
+ editTitle: function()
+ {
+ let node = this.selectedItem;
+ if (node)
+ TitleEditor.start(node);
+ },
+
+ /**
+ * Triggers re-download of a filter subscription.
+ */
+ updateFilters: function(/**Node*/ node)
+ {
+ let data = Templater.getDataForNode(node || this.selectedItem);
+ if (data && data.subscription instanceof DownloadableSubscription)
+ Synchronizer.execute(data.subscription, true);
+ },
+
+ /**
+ * Triggers re-download of all filter subscriptions.
+ */
+ updateAllFilters: function()
+ {
+ for (let i = 0; i < FilterStorage.subscriptions.length; i++)
+ {
+ let subscription = FilterStorage.subscriptions[i];
+ if (subscription instanceof DownloadableSubscription)
+ Synchronizer.execute(subscription, true);
+ }
+ },
+
+ /**
+ * Sets Subscription.disabled field to a new value.
+ */
+ setDisabled: function(/**Element*/ node, /**Boolean*/ value)
+ {
+ let data = Templater.getDataForNode(node || this.selectedItem);
+ if (data)
+ data.subscription.disabled = value;
+ },
+
+ /**
+ * Enables all disabled filters in a subscription.
+ */
+ enableFilters: function(/**Element*/ node)
+ {
+ let data = Templater.getDataForNode(node);
+ if (!data)
+ return;
+
+ let filters = data.subscription.filters;
+ for (let i = 0, l = filters.length; i < l; i++)
+ if (filters[i] instanceof ActiveFilter && filters[i].disabled)
+ filters[i].disabled = false;
+ },
+
+ /**
+ * Removes a filter subscription from the list (after a warning).
+ */
+ remove: function(/**Node*/ node)
+ {
+ let data = Templater.getDataForNode(node || this.selectedItem);
+ if (data && Utils.confirm(window, Utils.getString(data.subscription instanceof SpecialSubscription ? "remove_group_warning" : "remove_subscription_warning")))
+ FilterStorage.removeSubscription(data.subscription);
+ },
+
+ /**
+ * Adds a new filter group and allows the user to change its title.
+ */
+ addGroup: function()
+ {
+ let subscription = SpecialSubscription.create();
+ FilterStorage.addSubscription(subscription);
+
+ let list = E("groups");
+ let node = Templater.getNodeForData(list, "subscription", subscription);
+ if (node)
+ {
+ list.focus();
+ list.ensureElementIsVisible(node);
+ list.selectedItem = node;
+ this.editTitle();
+ }
+ },
+
+ /**
+ * Moves a filter subscription one line up.
+ */
+ moveUp: function(/**Node*/ node)
+ {
+ node = Templater.getDataNode(node || this.selectedItem);
+ let data = Templater.getDataForNode(node);
+ if (!data)
+ return;
+
+ let previousData = Templater.getDataForNode(node.previousSibling);
+ if (!previousData)
+ return;
+
+ FilterStorage.moveSubscription(data.subscription, previousData.subscription);
+ },
+
+ /**
+ * Moves a filter subscription one line down.
+ */
+ moveDown: function(/**Node*/ node)
+ {
+ node = Templater.getDataNode(node || this.selectedItem);
+ let data = Templater.getDataForNode(node);
+ if (!data)
+ return;
+
+ let nextNode = node.nextSibling;
+ if (!Templater.getDataForNode(nextNode))
+ return;
+
+ let nextData = Templater.getDataForNode(nextNode.nextSibling);
+ FilterStorage.moveSubscription(data.subscription, nextData ? nextData.subscription : null);
+ },
+
+ /**
+ * Opens the context menu for a subscription node.
+ */
+ openMenu: function(/**Event*/ event, /**Node*/ node)
+ {
+ node.getElementsByClassName("actionMenu")[0].openPopupAtScreen(event.screenX, event.screenY, true);
+ },
+
+ _altMask: 2,
+ _ctrlMask: 4,
+ _metaMask: 8,
+ get _accelMask()
+ {
+ let result = this._ctrlMask;
+ try {
+ let accelKey = Utils.prefService.getIntPref("ui.key.accelKey");
+ if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_META)
+ result = this._metaMask;
+ else if (accelKey == Ci.nsIDOMKeyEvent.DOM_VK_ALT)
+ result = this._altMask;
+ } catch(e) {}
+ this.__defineGetter__("_accelMask", function() result);
+ return result;
+ },
+
+ /**
+ * Called when a key is pressed on the subscription list.
+ */
+ keyPress: function(/**Event*/ event)
+ {
+ let modifiers = 0;
+ if (event.altKey)
+ modifiers |= this._altMask;
+ if (event.ctrlKey)
+ modifiers |= this._ctrlMask;
+ if (event.metaKey)
+ modifiers |= this._metaMask;
+
+ if (event.charCode == " ".charCodeAt(0) && modifiers == 0)
+ {
+ // Ignore if Space is pressed on a button
+ for (let node = event.target; node; node = node.parentNode)
+ if (node.localName == "button")
+ return;
+
+ let data = Templater.getDataForNode(this.selectedItem);
+ if (data)
+ data.subscription.disabled = !data.subscription.disabled;
+ }
+ else if (event.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_UP && modifiers == this._accelMask)
+ {
+ E("subscription-moveUp-command").doCommand();
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ else if (event.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_DOWN && modifiers == this._accelMask)
+ {
+ E("subscription-moveDown-command").doCommand();
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ },
+
+ /**
+ * Subscription currently being dragged if any.
+ * @type Subscription
+ */
+ dragSubscription: null,
+
+ /**
+ * Called when a subscription entry is dragged.
+ */
+ startDrag: function(/**Event*/ event, /**Node*/ node)
+ {
+ let data = Templater.getDataForNode(node);
+ if (!data)
+ return;
+
+ event.dataTransfer.addElement(node);
+ event.dataTransfer.setData("text/x-moz-url", data.subscription.url);
+ event.dataTransfer.setData("text/plain", data.subscription.title);
+ this.dragSubscription = data.subscription;
+ event.stopPropagation();
+ },
+
+ /**
+ * Called when something is dragged over a subscription entry or subscriptions list.
+ */
+ dragOver: function(/**Event*/ event)
+ {
+ // Don't allow dragging onto a scroll bar
+ for (let node = event.originalTarget; node; node = node.parentNode)
+ if (node.localName == "scrollbar")
+ return;
+
+ // Don't allow dragging onto element's borders
+ let target = event.originalTarget;
+ while (target && target.localName != "richlistitem")
+ target = target.parentNode;
+ if (!target)
+ target = event.originalTarget;
+
+ let styles = window.getComputedStyle(target, null);
+ let rect = target.getBoundingClientRect();
+ if (event.clientX < rect.left + parseInt(styles.borderLeftWidth, 10) ||
+ event.clientY < rect.top + parseInt(styles.borderTopWidth, 10) ||
+ event.clientX > rect.right - parseInt(styles.borderRightWidth, 10) - 1 ||
+ event.clientY > rect.bottom - parseInt(styles.borderBottomWidth, 10) - 1)
+ {
+ return;
+ }
+
+ // If not dragging a subscription check whether we can accept plain text
+ if (!this.dragSubscription)
+ {
+ let data = Templater.getDataForNode(event.target);
+ if (!data || !(data.subscription instanceof SpecialSubscription) || !event.dataTransfer.getData("text/plain"))
+ return;
+ }
+
+ event.preventDefault();
+ event.stopPropagation();
+ },
+
+ /**
+ * Called when something is dropped on a subscription entry or subscriptions list.
+ */
+ drop: function(/**Event*/ event, /**Node*/ node)
+ {
+ if (!this.dragSubscription)
+ {
+ // Not dragging a subscription, maybe this is plain text that we can add as filters?
+ let data = Templater.getDataForNode(node);
+ if (data && data.subscription instanceof SpecialSubscription)
+ {
+ let lines = event.dataTransfer.getData("text/plain").replace(/\r/g, "").split("\n");
+ for (let i = 0; i < lines.length; i++)
+ {
+ let line = Filter.normalize(lines[i]);
+ if (line)
+ {
+ let filter = Filter.fromText(line);
+ FilterStorage.addFilter(filter, data.subscription);
+ }
+ }
+ FilterActions.removeDraggedFilters();
+ event.stopPropagation();
+ }
+ return;
+ }
+
+ // When dragging down we need to insert after the drop node, otherwise before it.
+ node = Templater.getDataNode(node);
+ if (node)
+ {
+ let dragNode = Templater.getNodeForData(node.parentNode, "subscription", this.dragSubscription);
+ if (node.compareDocumentPosition(dragNode) & node.DOCUMENT_POSITION_PRECEDING)
+ node = node.nextSibling;
+ }
+
+ let data = Templater.getDataForNode(node);
+ FilterStorage.moveSubscription(this.dragSubscription, data ? data.subscription : null);
+ event.stopPropagation();
+ },
+
+ /**
+ * Called when the drag operation for a subscription is finished.
+ */
+ endDrag: function()
+ {
+ this.dragSubscription = null;
+ }
+};
+
+/**
+ * Subscription title editing functionality.
+ * @class
+ */
+var TitleEditor =
+{
+ /**
+ * List item corresponding with the currently edited subscription if any.
+ * @type Node
+ */
+ subscriptionEdited: null,
+
+ /**
+ * Starts editing of a subscription title.
+ * @param {Node} node subscription list entry or a child node
+ * @param {Boolean} [checkSelection] if true the editor will not start if the
+ * item was selected in the preceding mousedown event
+ */
+ start: function(node, checkSelection)
+ {
+ if (this.subscriptionEdited)
+ this.end(true);
+
+ let subscriptionNode = Templater.getDataNode(node);
+ if (!subscriptionNode || (checkSelection && !subscriptionNode._wasSelected))
+ return;
+
+ let subscription = Templater.getDataForNode(subscriptionNode).subscription;
+ if (!subscription || subscription.fixedTitle)
+ return;
+
+ subscriptionNode.getElementsByClassName("titleBox")[0].selectedIndex = 1;
+ let editor = subscriptionNode.getElementsByClassName("titleEditor")[0];
+ editor.value = subscription.title;
+ editor.setSelectionRange(0, editor.value.length);
+ this.subscriptionEdited = subscriptionNode;
+ editor.focus();
+ },
+
+ /**
+ * Stops editing of a subscription title.
+ * @param {Boolean} save if true the entered value will be saved, otherwise dismissed
+ */
+ end: function(save)
+ {
+ if (!this.subscriptionEdited)
+ return;
+
+ let subscriptionNode = this.subscriptionEdited;
+ this.subscriptionEdited = null;
+
+ let newTitle = null;
+ if (save)
+ {
+ newTitle = subscriptionNode.getElementsByClassName("titleEditor")[0].value;
+ newTitle = newTitle.replace(/^\s+/, "").replace(/\s+$/, "");
+ }
+
+ let subscription = Templater.getDataForNode(subscriptionNode).subscription
+ if (newTitle && newTitle != subscription.title)
+ subscription.title = newTitle;
+ else
+ {
+ subscriptionNode.getElementsByClassName("titleBox")[0].selectedIndex = 0;
+ subscriptionNode.parentNode.focus();
+ }
+ },
+
+ /**
+ * Processes keypress events on the subscription title editor field.
+ */
+ keyPress: function(/**Event*/ event)
+ {
+ // Prevent any key presses from triggering outside actions
+ event.stopPropagation();
+
+ if (event.keyCode == event.DOM_VK_RETURN || event.keyCode == event.DOM_VK_ENTER)
+ {
+ event.preventDefault();
+ this.end(true);
+ }
+ else if (event.keyCode == event.DOM_VK_CANCEL || event.keyCode == event.DOM_VK_ESCAPE)
+ {
+ event.preventDefault();
+ this.end(false);
+ }
+ }
+};
+
+/**
+ * Methods called when choosing and adding a new filter subscription.
+ * @class
+ */
+var SelectSubscription =
+{
+ /**
+ * Starts selection of a filter subscription to add.
+ */
+ start: function(/**Event*/ event)
+ {
+ let panel = E("selectSubscriptionPanel");
+ let list = E("selectSubscription");
+ let template = E("selectSubscriptionTemplate");
+ let parent = list.menupopup;
+
+ if (panel.state == "open")
+ {
+ list.focus();
+ return;
+ }
+
+ // Remove existing entries if any
+ while (parent.lastChild)
+ parent.removeChild(parent.lastChild);
+
+ // Load data
+ let request = new XMLHttpRequest();
+ request.open("GET", "subscriptions.xml");
+ request.onload = function()
+ {
+ // Avoid race condition if two downloads are started in parallel
+ if (panel.state == "open")
+ return;
+
+ // Add subscription entries to the list
+ let subscriptions = request.responseXML.getElementsByTagName("subscription");
+ let listedSubscriptions = [];
+ for (let i = 0; i < subscriptions.length; i++)
+ {
+ let subscription = subscriptions[i];
+ let url = subscription.getAttribute("url");
+ if (!url || url in FilterStorage.knownSubscriptions)
+ continue;
+
+ let localePrefix = Utils.checkLocalePrefixMatch(subscription.getAttribute("prefixes"));
+ let node = Templater.process(template, {
+ __proto__: null,
+ node: subscription,
+ localePrefix: localePrefix
+ });
+ parent.appendChild(node);
+ listedSubscriptions.push(subscription);
+ }
+ let selectedNode = Utils.chooseFilterSubscription(listedSubscriptions);
+ list.selectedItem = Templater.getNodeForData(parent, "node", selectedNode) || parent.firstChild;
+
+ // Show panel and focus list
+ let position = (Utils.versionComparator.compare(Utils.platformVersion, "2.0") < 0 ? "after_end" : "bottomcenter topleft");
+ panel.openPopup(E("selectSubscriptionButton"), position, 0, 0, false, false, event);
+ Utils.runAsync(list.focus, list);
+ };
+ request.send();
+ },
+
+ /**
+ * Adds filter subscription that is selected.
+ */
+ add: function()
+ {
+ E("selectSubscriptionPanel").hidePopup();
+
+ let data = Templater.getDataForNode(E("selectSubscription").selectedItem);
+ if (!data)
+ return;
+
+ let subscription = Subscription.fromURL(data.node.getAttribute("url"));
+ if (!subscription)
+ return;
+
+ FilterStorage.addSubscription(subscription);
+ subscription.disabled = false;
+ subscription.title = data.node.getAttribute("title");
+ subscription.homepage = data.node.getAttribute("homepage");
+
+ // Make sure the subscription is visible and selected
+ let list = E("subscriptions");
+ let node = Templater.getNodeForData(list, "subscription", subscription);
+ if (node)
+ {
+ list.ensureElementIsVisible(node);
+ list.selectedItem = node;
+ list.focus();
+ }
+
+ // Trigger download if necessary
+ if (subscription instanceof DownloadableSubscription && !subscription.lastDownload)
+ Synchronizer.execute(subscription);
+ },
+
+ /**
+ * Called if the user chooses to view the complete subscriptions list.
+ */
+ chooseOther: function()
+ {
+ E("selectSubscriptionPanel").hidePopup();
+ window.openDialog("subscriptionSelection.xul", "_blank", "chrome,centerscreen,modal,resizable,dialog=no", null, null);
+ },
+
+ /**
+ * Called for keys pressed on the subscription selection panel.
+ */
+ keyPress: function(/**Event*/ event)
+ {
+ // Buttons and text links handle Enter key themselves
+ if (event.target.localName == "button" || event.target.localName == "label")
+ return;
+
+ if (event.keyCode == event.DOM_VK_RETURN || event.keyCode == event.DOM_VK_ENTER)
+ {
+ // This shouldn't accept our dialog, only the panel
+ event.preventDefault();
+ E("selectSubscriptionAccept").doCommand();
+ }
+ }
+};
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-subscriptionview.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-subscriptionview.js
new file mode 100644
index 0000000..a1673e4
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters-subscriptionview.js
@@ -0,0 +1,330 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Fills a list of filter groups and keeps it updated.
+ * @param {Element} list richlistbox element to be filled
+ * @param {Node} template template to use for the groups
+ * @param {Function} filter filter to decide which lists should be included
+ * @param {Function} listener function to be called on changes
+ * @constructor
+ */
+function ListManager(list, template, filter, listener)
+{
+ this._list = list;
+ this._template = template;
+ this._filter = filter;
+ this._listener = listener || function(){};
+
+ this._deck = this._list.parentNode;
+
+ this._list.listManager = this;
+ this.reload();
+
+ let me = this;
+ let proxy = function()
+ {
+ return me._onChange.apply(me, arguments);
+ };
+ FilterNotifier.addListener(proxy);
+ window.addEventListener("unload", function()
+ {
+ FilterNotifier.removeListener(proxy);
+ }, false);
+}
+ListManager.prototype =
+{
+ /**
+ * List element being managed.
+ * @type Element
+ */
+ _list: null,
+ /**
+ * Template used for the groups.
+ * @type Node
+ */
+ _template: null,
+ /**
+ * Filter function to decide which subscriptions should be included.
+ * @type Function
+ */
+ _filter: null,
+ /**
+ * Function to be called whenever list contents change.
+ * @type Function
+ */
+ _listener: null,
+ /**
+ * Deck switching between list display and "no entries" message.
+ * @type Element
+ */
+ _deck: null,
+
+ /**
+ * Completely rebuilds the list.
+ */
+ reload: function()
+ {
+ // Remove existing entries if any
+ while (this._list.firstChild)
+ this._list.removeChild(this._list.firstChild);
+
+ // Now add all subscriptions
+ let subscriptions = FilterStorage.subscriptions.filter(this._filter, this);
+ if (subscriptions.length)
+ {
+ for each (let subscription in subscriptions)
+ this.addSubscription(subscription, null);
+
+ // Make sure first list item is selected after list initialization
+ Utils.runAsync(function()
+ {
+ this._list.selectItem(this._list.getItemAtIndex(this._list.getIndexOfFirstVisibleRow()));
+ }, this);
+ }
+
+ this._deck.selectedIndex = (subscriptions.length ? 1 : 0);
+ this._listener();
+ },
+
+ /**
+ * Adds a filter subscription to the list.
+ */
+ addSubscription: function(/**Subscription*/ subscription, /**Node*/ insertBefore) /**Node*/
+ {
+ let disabledFilters = 0;
+ for (let i = 0, l = subscription.filters.length; i < l; i++)
+ if (subscription.filters[i] instanceof ActiveFilter && subscription.filters[i].disabled)
+ disabledFilters++;
+
+ let node = Templater.process(this._template, {
+ __proto__: null,
+ subscription: subscription,
+ isExternal: subscription instanceof ExternalSubscription,
+ downloading: Synchronizer.isExecuting(subscription.url),
+ disabledFilters: disabledFilters
+ });
+ if (insertBefore)
+ this._list.insertBefore(node, insertBefore);
+ else
+ this._list.appendChild(node);
+ return node;
+ },
+
+ /**
+ * Map indicating subscriptions that need their "disabledFilters" property to
+ * be updated by next updateDisabled() call.
+ * @type Object
+ */
+ _scheduledUpdateDisabled: null,
+
+ /**
+ * Updates subscriptions that had some of their filters enabled/disabled.
+ */
+ updateDisabled: function()
+ {
+ let list = this._scheduledUpdateDisabled;
+ this._scheduledUpdateDisabled = null;
+ for (let url in list)
+ {
+ let subscription = Subscription.fromURL(url);
+ let subscriptionNode = Templater.getNodeForData(this._list, "subscription", subscription);
+ if (subscriptionNode)
+ {
+ let data = Templater.getDataForNode(subscriptionNode);
+ let disabledFilters = 0;
+ for (let i = 0, l = subscription.filters.length; i < l; i++)
+ if (subscription.filters[i] instanceof ActiveFilter && subscription.filters[i].disabled)
+ disabledFilters++;
+
+ if (disabledFilters != data.disabledFilters)
+ {
+ data.disabledFilters = disabledFilters;
+ Templater.update(this._template, subscriptionNode);
+
+ if (!document.commandDispatcher.focusedElement)
+ this._list.focus();
+ }
+ }
+ }
+ },
+
+ /**
+ * Subscriptions change processing.
+ * @see FilterNotifier.addListener()
+ */
+ _onChange: function(action, item, param1, param2)
+ {
+ if ((action == "subscription.added" || action == "subscription.removed") && item.url == Prefs.subscriptions_exceptionsurl)
+ E("acceptableAds").checked = FilterStorage.subscriptions.some(function(s) s.url == Prefs.subscriptions_exceptionsurl);
+
+ if (action == "filter.disabled")
+ {
+ if (this._scheduledUpdateDisabled == null)
+ {
+ this._scheduledUpdateDisabled = {__proto__: null};
+ Utils.runAsync(this.updateDisabled, this);
+ }
+ for (let i = 0; i < item.subscriptions.length; i++)
+ this._scheduledUpdateDisabled[item.subscriptions[i].url] = true;
+ return;
+ }
+
+ if (action != "load" && !this._filter(item))
+ return;
+
+ switch (action)
+ {
+ case "load":
+ {
+ this.reload();
+ break;
+ }
+ case "subscription.added":
+ {
+ let index = FilterStorage.subscriptions.indexOf(item);
+ if (index >= 0)
+ {
+ let insertBefore = null;
+ for (index++; index < FilterStorage.subscriptions.length && !insertBefore; index++)
+ insertBefore = Templater.getNodeForData(this._list, "subscription", FilterStorage.subscriptions[index]);
+ this.addSubscription(item, insertBefore);
+ this._deck.selectedIndex = 1;
+ this._listener();
+ }
+ break;
+ }
+ case "subscription.removed":
+ {
+ let node = Templater.getNodeForData(this._list, "subscription", item);
+ if (node)
+ {
+ let newSelection = node.nextSibling || node.previousSibling;
+ node.parentNode.removeChild(node);
+ if (!this._list.firstChild)
+ {
+ this._deck.selectedIndex = 0;
+ this._list.selectedIndex = -1;
+ }
+ else if (newSelection)
+ {
+ this._list.ensureElementIsVisible(newSelection);
+ this._list.selectedItem = newSelection;
+ }
+ this._listener();
+ }
+ break
+ }
+ case "subscription.moved":
+ {
+ let node = Templater.getNodeForData(this._list, "subscription", item);
+ if (node)
+ {
+ node.parentNode.removeChild(node);
+ let insertBefore = null;
+ let index = FilterStorage.subscriptions.indexOf(item);
+ if (index >= 0)
+ for (index++; index < FilterStorage.subscriptions.length && !insertBefore; index++)
+ insertBefore = Templater.getNodeForData(this._list, "subscription", FilterStorage.subscriptions[index]);
+ this._list.insertBefore(node, insertBefore);
+ this._list.ensureElementIsVisible(node);
+ this._listener();
+ }
+ break;
+ }
+ case "subscription.title":
+ case "subscription.disabled":
+ case "subscription.homepage":
+ case "subscription.lastDownload":
+ case "subscription.downloadStatus":
+ {
+ let subscriptionNode = Templater.getNodeForData(this._list, "subscription", item);
+ if (subscriptionNode)
+ {
+ Templater.getDataForNode(subscriptionNode).downloading = Synchronizer.isExecuting(item.url);
+ Templater.update(this._template, subscriptionNode);
+
+ if (!document.commandDispatcher.focusedElement)
+ this._list.focus();
+ this._listener();
+ }
+ break;
+ }
+ case "subscription.fixedTitle":
+ {
+ SubscriptionActions.updateCommands();
+ break;
+ }
+ case "subscription.updated":
+ {
+ if (this._scheduledUpdateDisabled == null)
+ {
+ this._scheduledUpdateDisabled = {__proto__: null};
+ Utils.runAsync(this.updateDisabled, this);
+ }
+ this._scheduledUpdateDisabled[item.url] = true;
+ break;
+ }
+ }
+ }
+};
+
+/**
+ * Attaches list managers to the lists.
+ */
+ListManager.init = function()
+{
+ new ListManager(E("subscriptions"),
+ E("subscriptionTemplate"),
+ function(s) s instanceof RegularSubscription && !(ListManager.acceptableAdsCheckbox && s.url == Prefs.subscriptions_exceptionsurl),
+ SubscriptionActions.updateCommands);
+ new ListManager(E("groups"),
+ E("groupTemplate"),
+ function(s) s instanceof SpecialSubscription,
+ SubscriptionActions.updateCommands);
+ E("acceptableAds").checked = FilterStorage.subscriptions.some(function(s) s.url == Prefs.subscriptions_exceptionsurl);
+ E("acceptableAds").parentNode.hidden = !ListManager.acceptableAdsCheckbox;
+};
+
+/**
+ * Defines whether the "acceptable ads" subscription needs special treatment.
+ * @type Boolean
+ */
+ListManager.acceptableAdsCheckbox = Prefs.subscriptions_exceptionscheckbox;
+
+/**
+ * Adds or removes filter subscription allowing acceptable ads.
+ */
+ListManager.allowAcceptableAds = function(/**Boolean*/ allow)
+{
+ let subscription = Subscription.fromURL(Prefs.subscriptions_exceptionsurl);
+ if (!subscription)
+ return;
+
+ subscription.disabled = false;
+ subscription.title = "Allow non-intrusive advertising";
+ if (allow)
+ {
+ FilterStorage.addSubscription(subscription);
+ if (subscription instanceof DownloadableSubscription && !subscription.lastDownload)
+ Synchronizer.execute(subscription);
+ }
+ else
+ FilterStorage.removeSubscription(subscription);
+};
+
+window.addEventListener("load", ListManager.init, false);
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters.js
new file mode 100644
index 0000000..8830fdb
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters.js
@@ -0,0 +1,226 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Initialization function, called when the window is loaded.
+ */
+function init()
+{
+ if (window.arguments && window.arguments.length)
+ {
+ let filter = window.arguments[0].wrappedJSObject;
+ if (filter instanceof Filter)
+ Utils.runAsync(SubscriptionActions.selectFilter, SubscriptionActions, filter);
+ }
+}
+
+/**
+ * Called whenever the currently selected tab changes.
+ */
+function onTabChange(/**Element*/ tabbox)
+{
+ updateSelectedSubscription();
+
+ Utils.runAsync(function()
+ {
+ let panel = tabbox.selectedPanel;
+ if (panel)
+ panel.getElementsByClassName("initialFocus")[0].focus();
+ SubscriptionActions.updateCommands();
+ });
+}
+
+/**
+ * Called whenever the selected subscription changes.
+ */
+function onSelectionChange(/**Element*/ list)
+{
+ SubscriptionActions.updateCommands();
+ updateSelectedSubscription();
+ list.focus();
+
+ // Take elements of the previously selected item out of the tab order
+ if ("previousSelection" in list && list.previousSelection)
+ {
+ let elements = list.previousSelection.getElementsByClassName("tabable");
+ for (let i = 0; i < elements.length; i++)
+ elements[i].setAttribute("tabindex", "-1");
+ }
+ // Put elements of the selected item into tab order
+ if (list.selectedItem)
+ {
+ let elements = list.selectedItem.getElementsByClassName("tabable");
+ for (let i = 0; i < elements.length; i++)
+ elements[i].removeAttribute("tabindex");
+ }
+ list.previousSelection = list.selectedItem;
+}
+
+/**
+ * Called when splitter state changes to make sure it is persisted properly.
+ */
+function onSplitterStateChange(/**Element*/ splitter)
+{
+ let state = splitter.getAttribute("state");
+ if (!state)
+ {
+ splitter.setAttribute("state", "open");
+ document.persist(splitter.id, "state");
+ }
+}
+
+/**
+ * Updates filter list when selected subscription changes.
+ */
+function updateSelectedSubscription()
+{
+ let panel = E("tabs").selectedPanel;
+ if (!panel)
+ return;
+
+ let list = panel.getElementsByTagName("richlistbox")[0];
+ if (!list)
+ return;
+
+ let data = Templater.getDataForNode(list.selectedItem);
+ FilterView.subscription = (data ? data.subscription : null);
+ FilterActions.updateCommands();
+}
+
+/**
+ * Template processing functions.
+ * @class
+ */
+var Templater =
+{
+ /**
+ * Processes a template node using given data object.
+ */
+ process: function(/**Node*/ template, /**Object*/ data) /**Node*/
+ {
+ // Use a sandbox to resolve attributes (for convenience, not security)
+ let sandbox = Cu.Sandbox(window);
+ for (let key in data)
+ sandbox[key] = data[key];
+ sandbox.formatTime = Utils.formatTime;
+
+ // Clone template but remove id/hidden attributes from it
+ let result = template.cloneNode(true);
+ result.removeAttribute("id");
+ result.removeAttribute("hidden");
+ result._data = data;
+
+ // Resolve any attributes of the for attr="{obj.foo}"
+ let conditionals = [];
+ let nodeIterator = document.createNodeIterator(result, NodeFilter.SHOW_ELEMENT, null, false);
+ for (let node = nodeIterator.nextNode(); node; node = nodeIterator.nextNode())
+ {
+ if (node.localName == "if")
+ conditionals.push(node);
+ for (let i = 0; i < node.attributes.length; i++)
+ {
+ let attribute = node.attributes[i];
+ let len = attribute.value.length;
+ if (len >= 2 && attribute.value[0] == "{" && attribute.value[len - 1] == "}")
+ {
+ let value = Cu.evalInSandbox(attribute.value.substr(1, len - 2), sandbox);
+ if (attribute.name == "condition")
+ value = value ? "true" : "false";
+ attribute.value = value;
+ }
+ }
+ }
+
+ // Process <if> tags - remove if condition is false, replace by their children
+ // if it is true
+ for each (let node in conditionals)
+ {
+ let fragment = document.createDocumentFragment();
+ let condition = node.getAttribute("condition");
+ if (condition == "false")
+ condition = false;
+ for (let i = 0; i < node.childNodes.length; i++)
+ {
+ let child = node.childNodes[i];
+ if (child.localName == "elif" || child.localName == "else")
+ {
+ if (condition)
+ break;
+ condition = (child.localName == "elif" ? child.getAttribute("condition") : true);
+ if (condition == "false")
+ condition = false;
+ }
+ else if (condition)
+ fragment.appendChild(node.childNodes[i--]);
+ }
+ node.parentNode.replaceChild(fragment, node);
+ }
+
+ return result;
+ },
+
+ /**
+ * Updates first child of a processed template if the underlying data changed.
+ */
+ update: function(/**Node*/ template, /**Node*/ node)
+ {
+ if (!("_data" in node))
+ return;
+ let newChild = Templater.process(template.firstChild, node._data);
+ delete newChild._data;
+ node.replaceChild(newChild, node.firstChild);
+ },
+
+ /**
+ * Walks up the parent chain for a node until the node corresponding with a
+ * template is found.
+ */
+ getDataNode: function(/**Node*/ node) /**Node*/
+ {
+ while (node)
+ {
+ if ("_data" in node)
+ return node;
+ node = node.parentNode;
+ }
+ return null;
+ },
+
+ /**
+ * Returns the data used to generate the node from a template.
+ */
+ getDataForNode: function(/**Node*/ node) /**Object*/
+ {
+ node = Templater.getDataNode(node);
+ if (node)
+ return node._data;
+ else
+ return null;
+ },
+
+ /**
+ * Returns a node that has been generated from a template using a particular
+ * data object.
+ */
+ getNodeForData: function(/**Node*/ parent, /**String*/ property, /**Object*/ data) /**Node*/
+ {
+ for (let child = parent.firstChild; child; child = child.nextSibling)
+ if ("_data" in child && property in child._data && child._data[property] == data)
+ return child;
+ return null;
+ }
+};
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters.xul b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters.xul
new file mode 100644
index 0000000..ce65659
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/filters.xul
@@ -0,0 +1,400 @@
+<?xml version="1.0"?>
+
+<!--
+ - This file is part of Adblock Plus <http://adblockplus.org/>,
+ - Copyright (C) 2006-2014 Eyeo GmbH
+ -
+ - Adblock Plus is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU General Public License version 3 as
+ - published by the Free Software Foundation.
+ -
+ - Adblock Plus is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU General Public License for more details.
+ -
+ - You should have received a copy of the GNU General Public License
+ - along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://adblockplus/skin/filters.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://adblockplus/locale/filters.dtd">
+
+<dialog
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&dialog.title;"
+ id="abpFiltersWindow"
+ onload="init()"
+ buttons="accept"
+ width="950"
+ height="450"
+ persist="screenX screenY width height sizemode"
+ windowtype="abp:filters">
+
+<script type="application/x-javascript;version=1.7" src="utils.js"/>
+<script type="application/x-javascript;version=1.7" src="filters.js"/>
+<script type="application/x-javascript;version=1.7" src="filters-subscriptionview.js"/>
+<script type="application/x-javascript;version=1.7" src="filters-subscriptionactions.js"/>
+<script type="application/x-javascript;version=1.7" src="filters-filterview.js"/>
+<script type="application/x-javascript;version=1.7" src="filters-filteractions.js"/>
+<script type="application/x-javascript;version=1.7" src="filters-backup.js"/>
+<script type="application/x-javascript;version=1.7" src="filters-search.js"/>
+
+<keyset id="filtersKeyset">
+ <key id="subscription-update-key" key="T" modifiers="accel" command="subscription-update-command"/>
+ <key id="subscription-update-all-key" key="T" modifiers="accel,shift" command="subscription-update-all-command"/>
+ <key id="edit-key" keycode="VK_F2" oncommand="E(FilterActions.focused ? 'filters-edit-command' : 'subscription-editTitle-command').doCommand();"/>
+ <key id="delete-key" keycode="VK_DELETE" oncommand="E(FilterActions.focused ? 'filters-delete-command' : 'subscription-delete-command').doCommand();"/>
+ <key id="subscription-showHideFilters-key" key="R" modifiers="accel" command="subscription-showHideFilters-command"/>
+ <key id="moveUp-key" keycode="VK_UP" modifiers="accel"/>
+ <key id="moveDown-key" keycode="VK_DOWN" modifiers="accel"/>
+ <key id="filters-add-key" keycode="VK_INSERT" oncommand="E('filters-add-command').doCommand();"/>
+ <key id="filters-selectAll-key" key="A" modifiers="accel" oncommand="if (FilterActions.focused) E('filters-selectAll-command').doCommand();"/>
+ <key id="filters-copy-key" key="C" modifiers="accel" oncommand="if (FilterActions.focused) E('filters-copy-command').doCommand();"/>
+ <key id="filters-cut-key" key="X" modifiers="accel" oncommand="if (FilterActions.focused) E('filters-cut-command').doCommand();"/>
+ <key id="filters-paste-key" key="V" modifiers="accel" oncommand="if (FilterActions.focused) E('filters-paste-command').doCommand();"/>
+ <key id="backup-key" key="E" modifiers="accel" oncommand="E('backup').doCommand();"/>
+ <key id="restore-key" key="I" modifiers="accel" oncommand="E('restoreOwnBackup').doCommand();"/>
+ <key id="find-key" key="F" modifiers="accel" oncommand="if (FilterActions.visible) E('find-command').doCommand();"/>
+ <key id="find-again-key" key="G" modifiers="accel" oncommand="if (FilterActions.visible) E('find-again-command').doCommand();"/>
+ <key id="find-previous-key" key="G" modifiers="accel,shift" oncommand="if (FilterActions.visible) E('find-previous-command').doCommand();"/>
+ <key id="find-again-key2" keycode="VK_F3" oncommand="if (FilterActions.visible) E('find-again-command').doCommand();"/>
+ <key id="find-previous-key2" keycode="VK_F3" modifiers="shift" oncommand="if (FilterActions.visible) E('find-previous-command').doCommand();"/>
+</keyset>
+
+<commandset id="filtersCommandset">
+ <command id="subscription-update-command" oncommand="SubscriptionActions.updateFilters();"/>
+ <command id="subscription-update-all-command" oncommand="SubscriptionActions.updateAllFilters();"/>
+ <command id="subscription-editTitle-command" oncommand="SubscriptionActions.editTitle();"/>
+ <command id="subscription-delete-command" oncommand="SubscriptionActions.remove();"/>
+ <command id="subscription-showHideFilters-command" oncommand="E('filtersGrippy').doCommand();"/>
+ <command id="subscription-moveUp-command" oncommand="SubscriptionActions.moveUp();"/>
+ <command id="subscription-moveDown-command" oncommand="SubscriptionActions.moveDown();"/>
+ <command id="subscription-add-command" oncommand="SelectSubscription.start(event);"/>
+ <command id="subscription-addSelected-command" oncommand="SelectSubscription.add();"/>
+ <command id="subscription-addOther-command" oncommand="SelectSubscription.chooseOther();"/>
+ <command id="group-add-command" oncommand="SubscriptionActions.addGroup();"/>
+ <command id="filters-selectAll-command" oncommand="FilterActions.selectAll();"/>
+ <command id="filters-edit-command" oncommand="FilterActions.startEditing();"/>
+ <command id="filters-add-command" oncommand="FilterActions.insertFilter();"/>
+ <command id="filters-delete-command" oncommand="FilterActions.deleteSelected();"/>
+ <command id="filters-resetHitCounts-command" oncommand="FilterActions.resetHitCounts();"/>
+ <command id="filters-moveUp-command" oncommand="FilterActions.moveUp();"/>
+ <command id="filters-moveDown-command" oncommand="FilterActions.moveDown();"/>
+ <command id="filters-copy-command" oncommand="FilterActions.copySelected(true);"/>
+ <command id="filters-cut-command" oncommand="FilterActions.copySelected(false);"/>
+ <command id="filters-paste-command" oncommand="FilterActions.paste();"/>
+ <command id="find-command" oncommand="E('findbar').startFind(E('findbar').FIND_NORMAL)"/>
+ <command id="find-again-command" oncommand="E('findbar').onFindAgainCommand(false)"/>
+ <command id="find-previous-command" oncommand="E('findbar').onFindAgainCommand(true)"/>
+</commandset>
+
+<popupset id="filtersPopupset">
+ <menupopup id="filters-view-menu1" onpopupshowing="FilterActions.fillColumnPopup(this);">
+ <menuitem id="filters-view-filter1" label="&filter.column;" type="checkbox" disabled="true"/>
+ <menuitem id="filters-view-slow1" label="&slow.column;" type="checkbox" oncommand="FilterActions.toggleColumn('col-slow')"/>
+ <menuitem id="filters-view-enabled1" label="&enabled.column;" type="checkbox" oncommand="FilterActions.toggleColumn('col-enabled')"/>
+ <menuitem id="filters-view-hitcount1" label="&hitcount.column;" type="checkbox" oncommand="FilterActions.toggleColumn('col-hitcount')"/>
+ <menuitem id="filters-view-lasthit1" label="&lasthit.column;" type="checkbox" oncommand="FilterActions.toggleColumn('col-lasthit')"/>
+ <menuseparator/>
+ <menu id="filters-sort-menu1" label="&sort.label;">
+ <menupopup id="filters-sort-popup1">
+ <menuitem id="filters-sort-none1" label="&sort.none.label;" type="radio" name="sortColumn" oncommand="FilterView.sortBy(null)"/>
+ <menuitem id="filters-sort-filter1" label="&filter.column;" type="radio" name="sortColumn" oncommand="FilterView.sortBy('col-filter')"/>
+ <menuitem id="filters-sort-slow1" label="&slow.column;" type="radio" name="sortColumn" oncommand="FilterView.sortBy('col-slow')"/>
+ <menuitem id="filters-sort-enabled1" label="&enabled.column;" type="radio" name="sortColumn" oncommand="FilterView.sortBy('col-enabled')"/>
+ <menuitem id="filters-sort-hitcount1" label="&hitcount.column;" type="radio" name="sortColumn" oncommand="FilterView.sortBy('col-hitcount')"/>
+ <menuitem id="filters-sort-lasthit1" label="&lasthit.column;" type="radio" name="sortColumn" oncommand="FilterView.sortBy('col-lasthit')"/>
+ <menuseparator/>
+ <menuitem id="filters-sort-asc1" label="&sort.ascending.label;" type="radio" name="sortOrder" oncommand="FilterActions.setSortOrder('ascending')"/>
+ <menuitem id="filters-sort-desc1" label="&sort.descending.label;" type="radio" name="sortOrder" oncommand="FilterActions.setSortOrder('descending')"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+ <tooltip id="filtersTooltip" onpopupshowing="FilterActions.fillTooltip(event);">
+ <grid>
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row id="tooltip-filter-row" align="top">
+ <label class="tooltipLabel" value="&filter.column;"/>
+ <vbox id="tooltip-filter"/>
+ </row>
+ <row id="tooltip-hitcount-row">
+ <label class="tooltipLabel" value="&hitcount.column;"/>
+ <description id="tooltip-hitcount"/>
+ </row>
+ <row id="tooltip-lasthit-row">
+ <label class="tooltipLabel" value="&lasthit.column;"/>
+ <description id="tooltip-lasthit"/>
+ </row>
+ </rows>
+ </grid>
+
+ <description id="tooltip-additional"/>
+ </tooltip>
+</popupset>
+
+<hbox id="content" flex="1">
+ <tabbox id="tabs" flex="1" persist="selectedIndex">
+ <tabs onselect="onTabChange(this.parentNode);">
+ <tab label="&subscriptions.tab.label;"/>
+ <tab label="&filters.tab.label;"/>
+ </tabs>
+ <tabpanels flex="1">
+ <tabpanel id="subscriptionsTab" orient="vertical" flex="1">
+ <hbox pack="end">
+ <button id="selectSubscriptionButton" label="&addSubscription.label;…" command="subscription-add-command"/>
+ </hbox>
+
+ <panel id="selectSubscriptionPanel" type="arrow" position="bottomcenter topleft"
+ orient="vertical" onkeypress="SelectSubscription.keyPress(event);">
+ <menuitem id="selectSubscriptionTemplate" hidden="true"
+ class="{localePrefix ? 'localeMatch' : ''}"
+ label="{node.getAttribute('title')}"
+ value="{node.getAttribute('url')}">
+ <label class="selectSubscriptionItem" value="{node.getAttribute('title') + ' (' + node.getAttribute('specialization') + ')'}"/>
+ </menuitem>
+ <menulist id="selectSubscription">
+ <menupopup/>
+ </menulist>
+ <hbox align="baseline">
+ <label class="text-link" value="&addSubscriptionOther.label;" onclick="E('subscription-addOther-command').doCommand();"/>
+ <spacer flex="1"/>
+ <button id="selectSubscriptionAccept" default="true" label="&addSubscriptionAdd.label;" command="subscription-addSelected-command"/>
+ <spacer flex="1"/>
+ <button label="&addSubscriptionCancel.label;" oncommand="E('selectSubscriptionPanel').hidePopup();"/>
+ </hbox>
+ </panel>
+
+ <richlistitem id="subscriptionTemplate" class="subscription" hidden="true" orient="vertical"
+ onmousedown="this._wasSelected = (this.parentNode.selectedItem == this);"
+ ondragstart="SubscriptionActions.startDrag(event, this);"
+ ondragend="SubscriptionActions.endDrag();"
+ ondragover="SubscriptionActions.dragOver(event);"
+ ondrop="SubscriptionActions.drop(event, this);"
+ oncontextmenu="SubscriptionActions.openMenu(event, this);">
+ <vbox class="{subscription.disabled ? 'disabled' : ''}">
+ <hbox align="center">
+ <checkbox label="&subscription.enabled.label;" class="enabledCheckbox tabable" tabindex="-1"
+ checked="{subscription.disabled ? 'false' : 'true'}" oncommand="SubscriptionActions.setDisabled(this, !this.checked);"/>
+
+ <vbox flex="1">
+ <hbox align="center">
+ <deck class="titleBox" flex="1" selectedIndex="0" onselect="event.stopPropagation();">
+ <description ondblclick="if (event.button == 0) TitleEditor.start(this, true);">
+ <description class="title" value="{subscription.title}" flex="1" crop="end"/>
+ (<if condition="{isExternal}">
+ <description value="&subscription.external.label;"/>
+ <else/>
+ <if condition="{subscription.homepage}">
+ <description class="link" value="&subscription.homepage.label;"
+ _url="{subscription.homepage}" tooltiptext="{subscription.homepage}"
+ onclick="if (event.button == 0) { event.stopPropagation();UI.loadInBrowser(this.getAttribute('_url')); }"/>,&#160;
+ </if>
+ <description class="link" value="&subscription.source.label;"
+ _url="{subscription.url}" tooltiptext="{subscription.url}"
+ onclick="if (event.button == 0) { event.stopPropagation();UI.loadInBrowser(this.getAttribute('_url')); }"/>
+ </if>)
+ </description>
+ <textbox oncontextmenu="event.stopPropagation();" class="titleEditor" onkeypress="TitleEditor.keyPress(event);" onblur="TitleEditor.end(true);"/>
+ </deck>
+ </hbox>
+ <hbox align="center">
+ <description flex="1" class="status">
+ <description value="&subscription.lastDownload.label;"/>&#160;
+ <if condition="{downloading}">
+ <description value="&subscription.lastDownload.inProgress;"/>
+ <elif condition="{!subscription.lastDownload}"/>
+ <description value="&subscription.lastDownload.unknown;"/>
+ <else/>
+ <description value="{formatTime(subscription.lastDownload * 1000)}"/>
+ <if condition="{subscription.downloadStatus}">
+ <description>
+ &#160;(<if condition="{subscription.downloadStatus == 'synchronize_invalid_url'}">
+ <description value="&subscription.lastDownload.invalidURL;"/>
+ <elif condition="{subscription.downloadStatus == 'synchronize_connection_error'}"/>
+ <description value="&subscription.lastDownload.connectionError;"/>
+ <elif condition="{subscription.downloadStatus == 'synchronize_invalid_data'}"/>
+ <description value="&subscription.lastDownload.invalidData;"/>
+ <elif condition="{subscription.downloadStatus == 'synchronize_checksum_mismatch'}"/>
+ <description value="&subscription.lastDownload.checksumMismatch;"/>
+ <else/> <!-- synchronize_ok -->
+ <description value="&subscription.lastDownload.success;"/>
+ </if>)
+ </description>
+ </if>
+ </if>
+ </description>
+ </hbox>
+ </vbox>
+
+ <button class="actionButton tabable" type="menu" label="&subscription.actions.label;" tabindex="-1">
+ <menupopup class="actionMenu">
+ <menuitem label="&subscription.editTitle.label;" key="edit-key" command="subscription-editTitle-command"/>
+ <menuitem label="&subscription.update.label;" key="subscription-update-key" command="subscription-update-command"/>
+ <menuitem label="&subscription.showHideFilters.label;" key="subscription-showHideFilters-key" command="subscription-showHideFilters-command"/>
+ <menuitem label="&subscription.delete.label;…" key="delete-key" command="subscription-delete-command"/>
+ <menuseparator/>
+ <menuitem label="&subscription.moveUp.label;" key="moveUp-key" command="subscription-moveUp-command"/>
+ <menuitem label="&subscription.moveDown.label;" key="moveDown-key" command="subscription-moveDown-command"/>
+ </menupopup>
+ </button>
+ </hbox>
+
+ <description class="warning" hidden="{!subscription.upgradeRequired}">&subscription.minVersion.warning;</description>
+ <description class="warning" hidden="{!disabledFilters}">
+ &subscription.disabledFilters.warning;
+ <description class="link" value="&subscription.disabledFilters.enable;" onclick="SubscriptionActions.enableFilters(this);"/>
+ </description>
+ </vbox>
+ </richlistitem>
+
+ <deck id="noSubscriptionsDeck" flex="1">
+ <description flex="1">&noSubscriptions.text;</description>
+ <richlistbox id="subscriptions" class="initialFocus" flex="1"
+ onselect="onSelectionChange(this);"
+ ondragover="SubscriptionActions.dragOver(event);"
+ ondrop="SubscriptionActions.drop(event, null);"
+ onkeypress="SubscriptionActions.keyPress(event);">
+ </richlistbox>
+ </deck>
+
+ <!--description>
+ <checkbox id="acceptableAds" label="&acceptableAds2.label;" oncommand="ListManager.allowAcceptableAds(this.checked);"/>
+ <label class="text-link" value="&viewList.label;" onclick="UI.loadInBrowser(Prefs.subscriptions_exceptionsurl);"/>
+ <label class="text-link" value="&readMore.label;" onclick="UI.loadDocLink('acceptable_ads');"/>
+ </description-->
+ </tabpanel>
+ <tabpanel id="filtersTab" orient="vertical" flex="1">
+ <hbox pack="end">
+ <button id="addGroupButton" label="&addGroup.label;" command="group-add-command"/>
+ </hbox>
+
+ <richlistitem id="groupTemplate" class="subscription" hidden="true" orient="vertical"
+ onmousedown="this._wasSelected = (this.parentNode.selectedItem == this);"
+ ondragstart="SubscriptionActions.startDrag(event, this);"
+ ondragend="SubscriptionActions.endDrag();"
+ ondragover="SubscriptionActions.dragOver(event);"
+ ondrop="SubscriptionActions.drop(event, this);"
+ oncontextmenu="SubscriptionActions.openMenu(event, this);">
+ <hbox class="{subscription.disabled ? 'disabled' : ''}" align="center">
+ <checkbox label="&subscription.enabled.label;" class="enabledCheckbox tabable" tabindex="-1"
+ checked="{subscription.disabled ? 'false' : 'true'}" oncommand="SubscriptionActions.setDisabled(this, !this.checked);"/>
+ <hbox align="center" flex="1">
+ <deck class="titleBox" flex="1" selectedIndex="0" onselect="event.stopPropagation();">
+ <description class="title" value="{subscription.title}" crop="end" ondblclick="if (event.button == 0) TitleEditor.start(this, true);"/>
+ <textbox oncontextmenu="event.stopPropagation();" class="titleEditor" onkeypress="TitleEditor.keyPress(event);" onblur="TitleEditor.end(true);"/>
+ </deck>
+ </hbox>
+ <button class="actionButton tabable" type="menu" label="&subscription.actions.label;" tabindex="-1">
+ <menupopup class="actionMenu">
+ <menuitem label="&subscription.editTitle.label;" key="edit-key" command="subscription-editTitle-command"/>
+ <menuitem label="&subscription.showHideFilters.label;" key="subscription-showHideFilters-key" command="subscription-showHideFilters-command"/>
+ <menuitem label="&subscription.delete.label;…" key="delete-key" command="subscription-delete-command"/>
+ <menuseparator/>
+ <menuitem label="&subscription.moveUp.label;" key="moveUp-key" command="subscription-moveUp-command"/>
+ <menuitem label="&subscription.moveDown.label;" key="moveDown-key" command="subscription-moveDown-command"/>
+ </menupopup>
+ </button>
+ </hbox>
+ </richlistitem>
+
+ <deck id="noFiltersDeck" flex="1">
+ <description flex="1">&noFilters.text;</description>
+ <richlistbox id="groups" class="initialFocus" flex="1"
+ onselect="onSelectionChange(this);"
+ ondragover="SubscriptionActions.dragOver(event);"
+ ondrop="SubscriptionActions.drop(event, null);"
+ onkeypress="SubscriptionActions.keyPress(event);">
+ </richlistbox>
+ </deck>
+ </tabpanel>
+ </tabpanels>
+ </tabbox>
+
+ <splitter id="filtersSplitter" persist="state" orient="horizontal" collapse="after" state="collapsed" oncommand="FilterView.refresh();onSplitterStateChange(this);">
+ <grippy id="filtersGrippy"/>
+ </splitter>
+
+ <vbox id="filtersContainer" persist="width height" width="500">
+ <hbox pack="end">
+ <button id="findButton" label="&find.label;" command="find-command"/>
+ <button id="filterActionButton" type="menu" label="&filter.actions.label;">
+ <menupopup id="filterActionMenu" onpopupshowing="FilterActions.fillActionsPopup();">
+ <menuitem label="&filter.edit.label;" key="edit-key" command="filters-edit-command"/>
+ <menuitem label="&filter.cut.label;" key="filters-cut-key" command="filters-cut-command"/>
+ <menuitem label="&filter.copy.label;" key="filters-copy-key" command="filters-copy-command"/>
+ <menuitem label="&filter.paste.label;" key="filters-paste-key" command="filters-paste-command"/>
+ <menuitem label="&filter.delete.label;" key="delete-key" command="filters-delete-command"/>
+ <menuseparator/>
+ <menuitem label="&filter.selectAll.label;" key="filters-selectAll-key" command="filters-selectAll-command"/>
+ <menuitem label="&filter.resetHitCounts.label;" command="filters-resetHitCounts-command"/>
+ <menuseparator/>
+ <menuitem label="&filter.moveUp.label;" key="moveUp-key" command="filters-moveUp-command"/>
+ <menuitem label="&filter.moveDown.label;" key="moveDown-key" command="filters-moveDown-command"/>
+ <menuseparator/>
+ <menu id="viewMenu" label="&viewMenu.label;"/>
+ </menupopup>
+ </button>
+ <button id="addFilterButton" label="&addFilter.label;" command="filters-add-command"/>
+ </hbox>
+ <tree id="filtersTree"
+ flex="1"
+ editable="true"
+ seltype="multiple"
+ enableColumnDrag="true"
+ hidecolumnpicker="true"
+ _removewarning="&filters.remove.warning;">
+ <treecols context="filters-view-menu1">
+ <treecol id="col-enabled" label="&enabled.column;" cycler="true" flex="0" persist="width ordinal sortDirection hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="col-filter" label="&filter.column;" flex="10" persist="width ordinal sortDirection hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="col-slow" label="!" display="&slow.column;" tooltiptext="&slow.column;" flex="0" width="16" persist="width ordinal sortDirection hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="col-hitcount" label="&hitcount.column;" flex="0" persist="width ordinal sortDirection hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="col-lasthit" label="&lasthit.column;" hidden="true" flex="4" persist="width ordinal sortDirection hidden"/>
+ </treecols>
+
+ <treechildren id="filtersTreeChildren"
+ oncontextmenu="E('filterActionMenu').openPopupAtScreen(event.screenX, event.screenY, true);"
+ tooltip="filtersTooltip"
+ noGroupText="&noGroupSelected.text;"
+ noFiltersText="&noFiltersInGroup.text;"
+ ondragstart="FilterActions.startDrag(event);"
+ ondragend="FilterActions.endDrag(event);"/>
+ </tree>
+ </vbox>
+</hbox>
+
+<findbar id="findbar"/>
+
+<hbox id="buttons">
+ <button id="backupButton" type="menu"
+ label="&backupButton.label;"
+ _backupDialogTitle="&backup.label;" _restoreDialogTitle="&restore.own.label;"
+ _fileFilterComplete="&backup.complete.title;" _fileFilterCustom="&backup.custom.title;"
+ _backupError="&backup.error;" _restoreError="&restore.error;"
+ _restoreCompleteWarning="&restore.complete.warning;" _restoreCustomWarning="&restore.custom.warning;"
+ _restoreVersionWarning="&restore.minVersion.warning;"
+ oncommand="if (event.target == this) Utils.runAsync(function() this.open = true, this);">
+ <menupopup onpopupshowing="Backup.fillRestorePopup();">
+ <menuitem id="backup" key="backup-key" label="&backup.label;…" oncommand="Backup.backupToFile();"/>
+ <menuseparator/>
+ <menuitem id="restoreBackupTemplate" label="&restore.default.label;" hidden="true"/>
+ <menuitem id="restoreOwnBackup" key="restore-key" label="&restore.own.label;…" oncommand="Backup.restoreFromFile();"/>
+ </menupopup>
+ </button>
+
+ <spacer flex="1"/>
+
+ <button id="close" dlgtype="accept" label="&close.label;"/>
+</hbox>
+
+</dialog>
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/firstRun.html b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/firstRun.html
new file mode 100644
index 0000000..36f5cf7
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/firstRun.html
@@ -0,0 +1,141 @@
+<!DOCTYPE html>
+<!--
+ - This file is part of Adblock Plus <http://adblockplus.org/>,
+ - Copyright (C) 2006-2014 Eyeo GmbH
+ -
+ - Adblock Plus is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU General Public License version 3 as
+ - published by the Free Software Foundation.
+ -
+ - Adblock Plus is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU General Public License for more details.
+ -
+ - You should have received a copy of the GNU General Public License
+ - along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<html>
+ <head>
+ <title class="i18n_firstRun_title"></title>
+ <meta charset="utf-8">
+ <link type="text/css" href="/skin/firstRun.css" rel="stylesheet"/>
+ <script type="text/javascript" src="utils.js"></script>
+ <script type="text/javascript" src="i18n.js"></script>
+ <script type="text/javascript" src="firstRun.js"></script>
+ </head>
+ <body>
+
+
+ <header>
+ <div id="logo"></div>
+ <h1 id="title-main" class="i18n_firstRun_title"></h1>
+ </header>
+
+ <section id="legacySafariWarning" class="i18n_firstRun_legacySafariWarning warning" hidden="true"></section>
+ <section id="filterlistsReinitializedWarning" class="i18n_firstRun_filterlistsReinitializedWarning warning" hidden="true"></section>
+ <section id="dataCorruptionWarning" class="i18n_firstRun_dataCorruptionWarning warning" hidden="true"></section>
+
+ <section id="acceptable-ads">
+ <h2 class="i18n_firstRun_acceptableAdsHeadline"></h2>
+ <p id="acceptableAdsExplanation" class="i18n_firstRun_acceptableAdsExplanation"></p>
+ </section>
+
+
+ <section id="share">
+ <h2 id="share-headline" class="i18n_firstRun_share_headline"></h2>
+
+ <div id="donate-block">
+ <a id="donate" class="i18n_firstRun_donate" target="_blank"></a>
+ <span id="donate-label" class="i18n_firstRun_donate_label"></span>
+ </div>
+
+ <div id="share-block">
+ <div id="share-general" class="share-buttons">
+ <a id="share-facebook" href="https://www.facebook.com/adblockplus" target="_blank" data-script="https://facebook.com/plugins/like.php?">
+ </a>
+ <a id="share-twitter" href="https://twitter.com/adblockplus" target="_blank" data-script="https://platform.twitter.com/widgets.js">
+ </a>
+ <a id="share-gplus" href="https://www.google.com/+AdblockPlus" target="_blank" data-script="https://apis.google.com/js/plusone.js">
+ </a>
+ </div>
+
+ <!-- Chinese social networks -->
+ <div id="share-chinese" class="share-buttons">
+ <a id="share-renren" href="http://www.renren.com/601651969" target="_blank">
+ </a>
+ <a id="share-weibo" href="http://e.weibo.com/adblockplus/" target="_blank">
+ </a>
+ </div>
+ <span class="i18n_firstRun_share"></span>
+ </div>
+ </section>
+
+
+ <section id="can-do-more">
+ <h2 class="i18n_firstRun_features"></h2>
+
+ <div id="can-do-more-content">
+ <ul id="features">
+ <li id="feature-malware" class="feature">
+ <div class="feature-image feature-malware-image"></div>
+ <div class="feature-description">
+ <div class="feature-description-textblock">
+ <h3 class="i18n_firstRun_feature_malware feature-title"></h3>
+ <span class="i18n_firstRun_feature_malware_description"></span>
+ </div>
+ <div id="toggle-malware" class="toggle">
+ <div class="i18n_firstRun_toggle_on toggle-on"></div>
+ <div class="toggle-blob"></div>
+ <div class="i18n_firstRun_toggle_off toggle-off"></div>
+ </div>
+ </div>
+
+ </li>
+ <li id="feature-social" class="feature">
+ <div class="feature-image feature-social-image"></div>
+ <div class="feature-description">
+ <div class="feature-description-textblock">
+ <h3 class="i18n_firstRun_feature_social feature-title"></h3>
+ <span class="i18n_firstRun_feature_social_description"></span>
+ </div>
+ <div id="toggle-social" class="toggle">
+ <div class="i18n_firstRun_toggle_on toggle-on"></div>
+ <div class="toggle-blob"></div>
+ <div class="i18n_firstRun_toggle_off toggle-off"></div>
+ </div>
+ </div>
+ </li>
+
+ <li id="feature-tracking" class="feature">
+ <div class="feature-image feature-tracking-image"></div>
+ <div class="feature-description">
+ <div class="feature-description-textblock">
+ <h3 class="i18n_firstRun_feature_tracking feature-title"></h3>
+ <span class="i18n_firstRun_feature_tracking_description"></span>
+ </div>
+ <div id="toggle-tracking" class="toggle">
+ <div class="i18n_firstRun_toggle_on toggle-on"></div>
+ <div class="toggle-blob"></div>
+ <div class="i18n_firstRun_toggle_off toggle-off"></div>
+ </div>
+ </div>
+ </li>
+
+ </ul>
+
+ </div>
+ </section>
+
+
+ <footer>
+ <a id="contributors" class="i18n_firstRun_contributor_credits"></a>
+ </footer>
+
+
+ <div id="glass-pane">
+ <iframe id="share-popup" scrolling="no"></iframe>
+ </div>
+ </body>
+</html> \ No newline at end of file
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/firstRun.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/firstRun.js
new file mode 100644
index 0000000..be2caa6
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/firstRun.js
@@ -0,0 +1,253 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+(function()
+{
+ // Load subscriptions for features
+ var featureSubscriptions = [
+ {
+ feature: "malware",
+ homepage: "http://malwaredomains.com/",
+ title: "Malware Domains",
+ url: "https://easylist-downloads.adblockplus.org/malwaredomains_full.txt"
+ },
+ {
+ feature: "social",
+ homepage: "https://www.fanboy.co.nz/",
+ title: "Fanboy's Social Blocking List",
+ url: "https://easylist-downloads.adblockplus.org/fanboy-social.txt"
+ },
+ {
+ feature: "tracking",
+ homepage: "https://easylist.adblockplus.org/",
+ title: "EasyPrivacy",
+ url: "https://easylist-downloads.adblockplus.org/easyprivacy.txt"
+ }
+ ];
+
+ function onDOMLoaded()
+ {
+ var locale = require("utils").Utils.appLocale;
+ document.documentElement.setAttribute("lang", locale);
+
+ // Set up URLs
+ var donateLink = E("donate");
+ donateLink.href = Utils.getDocLink("donate");
+
+ var contributors = E("contributors");
+ contributors.href = Utils.getDocLink("contributors");
+
+ setLinks("acceptableAdsExplanation", Utils.getDocLink("acceptable_ads_criteria"), openFilters);
+ setLinks("share-headline", Utils.getDocLink("contribute"));
+
+ if (typeof backgroundPage != "undefined")
+ {
+ // Show warning if data corruption was detected
+ if (backgroundPage.seenDataCorruption)
+ {
+ E("dataCorruptionWarning").removeAttribute("hidden");
+ setLinks("dataCorruptionWarning", Utils.getDocLink("knownIssuesChrome_filterstorage"));
+ }
+
+ // Show warning if filterlists settings were reinitialized
+ if (backgroundPage.filterlistsReinitialized)
+ {
+ E("filterlistsReinitializedWarning").removeAttribute("hidden");
+ setLinks("filterlistsReinitializedWarning", openFilters);
+ }
+ }
+
+ // Show warning if Safari version isn't supported
+ var info = require("info");
+ if (info.platform == "safari" && (
+ Services.vc.compare(info.platformVersion, "6.0") < 0 || // beforeload breaks websites in Safari 5
+ Services.vc.compare(info.platformVersion, "6.1") == 0 || // extensions are broken in 6.1 and 7.0
+ Services.vc.compare(info.platformVersion, "7.0") == 0
+ ))
+ E("legacySafariWarning").removeAttribute("hidden");
+
+ // Set up feature buttons linked to subscriptions
+ featureSubscriptions.forEach(setToggleSubscriptionButton);
+ var filterListener = function(action)
+ {
+ if (/^subscription\.(added|removed|disabled)$/.test(action))
+ {
+ for (var i = 0; i < featureSubscriptions.length; i++)
+ {
+ var featureSubscription = featureSubscriptions[i];
+ updateToggleButton(featureSubscription.feature, isSubscriptionEnabled(featureSubscription));
+ }
+ }
+ }
+ FilterNotifier.addListener(filterListener);
+ window.addEventListener("unload", function(event)
+ {
+ FilterNotifier.removeListener(filterListener);
+ }, false);
+
+ initSocialLinks();
+ }
+
+ function isSubscriptionEnabled(featureSubscription)
+ {
+ return featureSubscription.url in FilterStorage.knownSubscriptions
+ && !Subscription.fromURL(featureSubscription.url).disabled;
+ }
+
+ function setToggleSubscriptionButton(featureSubscription)
+ {
+ var feature = featureSubscription.feature;
+
+ var element = E("toggle-" + feature);
+ updateToggleButton(feature, isSubscriptionEnabled(featureSubscription));
+ element.addEventListener("click", function(event)
+ {
+ var subscription = Subscription.fromURL(featureSubscription.url);
+ if (isSubscriptionEnabled(featureSubscription))
+ FilterStorage.removeSubscription(subscription);
+ else
+ {
+ subscription.disabled = false;
+ subscription.title = featureSubscription.title;
+ subscription.homepage = featureSubscription.homepage;
+ FilterStorage.addSubscription(subscription);
+ if (!subscription.lastDownload)
+ Synchronizer.execute(subscription);
+ }
+ }, false);
+ }
+
+ function openSharePopup(url)
+ {
+ var iframe = E("share-popup");
+ var glassPane = E("glass-pane");
+ var popupMessageReceived = false;
+
+ var popupMessageListener = function(event)
+ {
+ var originFilter = Filter.fromText("||adblockplus.org^");
+ if (!originFilter.matches(event.origin, "OTHER", null, null))
+ return;
+
+ var width = event.data.width;
+ var height = event.data.height;
+ iframe.width = width;
+ iframe.height = height;
+ iframe.style.marginTop = -height/2 + "px";
+ iframe.style.marginLeft = -width/2 + "px";
+ popupMessageReceived = true;
+ window.removeEventListener("message", popupMessageListener);
+ };
+ // Firefox requires last parameter to be true to be triggered by unprivileged pages
+ window.addEventListener("message", popupMessageListener, false, true);
+
+ var popupLoadListener = function()
+ {
+ if (popupMessageReceived)
+ {
+ iframe.className = "visible";
+
+ var popupCloseListener = function()
+ {
+ iframe.className = glassPane.className = "";
+ document.removeEventListener("click", popupCloseListener);
+ };
+ document.addEventListener("click", popupCloseListener, false);
+ }
+ else
+ {
+ glassPane.className = "";
+ window.removeEventListener("message", popupMessageListener);
+ }
+
+ iframe.removeEventListener("load", popupLoadListener);
+ };
+ iframe.addEventListener("load", popupLoadListener, false);
+
+ iframe.src = url;
+ glassPane.className = "visible";
+ }
+
+ function initSocialLinks()
+ {
+ var networks = ["twitter", "facebook", "gplus"];
+ networks.forEach(function(network)
+ {
+ var link = E("share-" + network);
+ link.addEventListener("click", onSocialLinkClick, false);
+ });
+ }
+
+ function onSocialLinkClick(event)
+ {
+ // Don't open the share page if the sharing script would be blocked
+ var filter = defaultMatcher.matchesAny(event.target.getAttribute("data-script"), "SCRIPT", "adblockplus.org", true);
+ if (!(filter instanceof BlockingFilter))
+ {
+ event.preventDefault();
+ openSharePopup(Utils.getDocLink(event.target.id));
+ }
+ }
+
+ function setLinks(id)
+ {
+ var element = E(id);
+ if (!element)
+ {
+ return;
+ }
+
+ var links = element.getElementsByTagName("a");
+
+ for (var i = 0; i < links.length; i++)
+ {
+ if (typeof arguments[i + 1] == "string")
+ {
+ links[i].href = arguments[i + 1];
+ links[i].setAttribute("target", "_blank");
+ }
+ else if (typeof arguments[i + 1] == "function")
+ {
+ links[i].href = "javascript:void(0);";
+ links[i].addEventListener("click", arguments[i + 1], false);
+ }
+ }
+ }
+
+ function openFilters()
+ {
+ if (typeof UI != "undefined")
+ UI.openFiltersDialog();
+ else
+ {
+ backgroundPage.openOptions();
+ }
+ }
+
+ function updateToggleButton(feature, isEnabled)
+ {
+ var button = E("toggle-" + feature);
+ if (isEnabled)
+ button.classList.remove("off");
+ else
+ button.classList.add("off");
+ }
+
+ document.addEventListener("DOMContentLoaded", onDOMLoaded, false);
+})();
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/flasher.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/flasher.js
new file mode 100644
index 0000000..ddb4131
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/flasher.js
@@ -0,0 +1,108 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * Draws a blinking border for a list of matching nodes.
+ */
+
+var flasher = {
+ nodes: null,
+ count: 0,
+ timer: null,
+
+ flash: function(nodes)
+ {
+ this.stop();
+ if (nodes)
+ nodes = nodes.filter(function(node) node.nodeType == Node.ELEMENT_NODE);
+ if (!nodes || !nodes.length)
+ return;
+
+ if (Prefs.flash_scrolltoitem && nodes[0].ownerDocument)
+ {
+ // Ensure that at least one node is visible when flashing
+ let wnd = nodes[0].ownerDocument.defaultView;
+ try
+ {
+ let topWnd = Utils.getChromeWindow(wnd);
+ let {getBrowser} = require("appSupport");
+ let browser = (getBrowser ? getBrowser(topWnd) : null);
+ if (browser)
+ browser.markupDocumentViewer.scrollToNode(nodes[0]);
+ }
+ catch(e)
+ {
+ Cu.reportError(e);
+ }
+ }
+
+ this.nodes = nodes;
+ this.count = 0;
+
+ this.doFlash();
+ },
+
+ doFlash: function() {
+ if (this.count >= 12) {
+ this.stop();
+ return;
+ }
+
+ if (this.count % 2)
+ this.switchOff();
+ else
+ this.switchOn();
+
+ this.count++;
+
+ this.timer = window.setTimeout(function() {flasher.doFlash()}, 300);
+ },
+
+ stop: function() {
+ if (this.timer) {
+ window.clearTimeout(this.timer);
+ this.timer = null;
+ }
+
+ if (this.nodes) {
+ this.switchOff();
+ this.nodes = null;
+ }
+ },
+
+ setOutline: function(outline, offset)
+ {
+ for (var i = 0; i < this.nodes.length; i++)
+ {
+ if ("style" in this.nodes[i])
+ {
+ this.nodes[i].style.outline = outline;
+ this.nodes[i].style.outlineOffset = offset;
+ }
+ }
+ },
+
+ switchOn: function()
+ {
+ this.setOutline("#CC0000 dotted 2px", "-2px");
+ },
+
+ switchOff: function()
+ {
+ this.setOutline("", "");
+ }
+};
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/i18n.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/i18n.js
new file mode 100644
index 0000000..8366268
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/i18n.js
@@ -0,0 +1,150 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+var i18n;
+
+if (typeof ext != "undefined")
+ i18n = ext.i18n;
+else if (typeof chrome != "undefined")
+ // TODO: This check only exist for backwards compatibility, while the Safari
+ // port isn't merged into the adblockpluschrome repo. So this branch should
+ // be removed when the Safari port was merged.
+ i18n = chrome.i18n;
+else
+{
+ // Using Firefox' approach on i18n instead
+
+ // Randomize URI to work around bug 719376
+ var pageName = location.pathname.replace(/.*\//, '').replace(/\..*?$/, '');
+ var stringBundle = Services.strings.createBundle("chrome://adblockplus/locale/" + pageName +
+ ".properties?" + Math.random());
+
+ function getI18nMessage(key)
+ {
+ return {
+ "message": stringBundle.GetStringFromName(key)
+ };
+ }
+
+ i18n = (function()
+ {
+ function getText(message, args)
+ {
+ var text = message.message;
+ var placeholders = message.placeholders;
+
+ if (!args || !placeholders)
+ return text;
+
+ for (var key in placeholders)
+ {
+ var content = placeholders[key].content;
+ if (!content)
+ continue;
+
+ var index = parseInt(content.slice(1), 10);
+ if (isNaN(index))
+ continue;
+
+ var replacement = args[index - 1];
+ if (typeof replacement === "undefined")
+ continue;
+
+ text = text.split("$" + key + "$").join(replacement);
+ }
+ return text;
+ }
+
+ return {
+ getMessage: function(key, args)
+ {
+ try{
+ var message = getI18nMessage(key);
+ return getText(message, args);
+ }
+ catch(e)
+ {
+ Cu.reportError(e);
+ return "Missing translation: " + key;
+ }
+ }
+ };
+ })();
+}
+
+// Inserts i18n strings into matching elements. Any inner HTML already in the element is
+// parsed as JSON and used as parameters to substitute into placeholders in the i18n
+// message.
+i18n.setElementText = function(element, stringName, arguments)
+{
+ function processString(str, element)
+ {
+ var match = /^(.*?)<(a|strong)>(.*?)<\/\2>(.*)$/.exec(str);
+ if (match)
+ {
+ processString(match[1], element);
+
+ var e = document.createElement(match[2]);
+ processString(match[3], e);
+ element.appendChild(e);
+
+ processString(match[4], element);
+ }
+ else
+ element.appendChild(document.createTextNode(str));
+ }
+
+ while (element.lastChild)
+ element.removeChild(element.lastChild);
+ processString(i18n.getMessage(stringName, arguments), element);
+}
+
+// Loads i18n strings
+function loadI18nStrings()
+{
+ var nodes = document.querySelectorAll("[class^='i18n_']");
+ for(var i = 0; i < nodes.length; i++)
+ {
+ var node = nodes[i];
+ var arguments = JSON.parse("[" + node.textContent + "]");
+ if (arguments.length == 0)
+ arguments = null;
+
+ var className = node.className;
+ if (className instanceof SVGAnimatedString)
+ className = className.animVal;
+ var stringName = className.split(/\s/)[0].substring(5);
+
+ i18n.setElementText(node, stringName, arguments);
+ }
+}
+
+// Provides a more readable string of the current date and time
+function i18n_timeDateStrings(when)
+{
+ var d = new Date(when);
+ var timeString = d.toLocaleTimeString();
+
+ var now = new Date();
+ if (d.toDateString() == now.toDateString())
+ return [timeString];
+ else
+ return [timeString, d.toLocaleDateString()];
+}
+
+// Fill in the strings as soon as possible
+window.addEventListener("DOMContentLoaded", loadI18nStrings, true);
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/overlay.xul b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/overlay.xul
new file mode 100644
index 0000000..9724859
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/overlay.xul
@@ -0,0 +1,134 @@
+<?xml version="1.0"?>
+
+<!--
+ - This file is part of Adblock Plus <http://adblockplus.org/>,
+ - Copyright (C) 2006-2014 Eyeo GmbH
+ -
+ - Adblock Plus is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU General Public License version 3 as
+ - published by the Free Software Foundation.
+ -
+ - Adblock Plus is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU General Public License for more details.
+ -
+ - You should have received a copy of the GNU General Public License
+ - along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<!DOCTYPE overlay [
+ <!ENTITY % overlayDTD SYSTEM "chrome://adblockplus/locale/overlay.dtd">
+ %overlayDTD;
+ <!ENTITY % subscriptionsDTD SYSTEM "chrome://adblockplus/locale/subscriptionSelection.dtd">
+ %subscriptionsDTD;
+]>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ imagecontextlabel="&context.image.label;…" objectcontextlabel="&context.object.label;…"
+ mediacontextlabel="&context.media.label;…" subdocumentcontextlabel="&context.frame.label;…"
+ whitelistcontextlabel="&context.removeWhitelist.label;"
+ objtabtext="&objecttab.title;…" objtabtooltip="&objecttab.tooltip;"
+ subscriptionDialogTitle="&dialog.title;"
+ subscriptionDialogMessage="&title.label; ?1?&#10;&#10;&location.label; ?2?">
+ <popupset id="abp-popupset">
+ <!-- Icon's tooltip -->
+ <tooltip id="abp-tooltip" orient="vertical">
+ <description id="abp-tooltip-action" hidden="true"/>
+ <label id="abp-tooltip-status-label" value="&status.tooltip;"/>
+ <description id="abp-tooltip-status"/>
+ <label id="abp-tooltip-blocked-label" value="&blocked.tooltip;" hidden="true"/>
+ <description id="abp-tooltip-blocked" hidden="true"/>
+ <label id="abp-tooltip-filters-label" value="&filters.tooltip;" hidden="true"/>
+ <vbox id="abp-tooltip-filters" hidden="true"/>
+ <description id="abp-tooltip-more-filters" value="…" hidden="true"/>
+ </tooltip>
+ </popupset>
+
+ <!-- Icon's context menu -->
+ <menupopup id="abp-status-popup" context="">
+ <menuitem id="abp-status-sendReport" label="&sendReport.label;…" key="abp-key-sendReport" command="abp-command-sendReport"/>
+ <menuitem id="abp-status-openbottombar" label="&opensidebar.label;" key="abp-key-sidebar" command="abp-command-sidebar"/>
+ <menuitem id="abp-status-closebottombar" label="&closesidebar.label;" key="abp-key-sidebar" command="abp-command-sidebar"/>
+ <menuitem id="abp-status-filters" label="&filters.label;…" key="abp-key-filters" command="abp-command-filters"/>
+ <menuseparator id="abp-status-whitelist-sep"/>
+ <menuitem id="abp-status-whitelistsite" labeltempl="&whitelist.site.label;" type="checkbox" command="abp-command-togglesitewhitelist"/>
+ <menuitem id="abp-status-whitelistpage" label="&whitelist.page.label;" type="checkbox" command="abp-command-togglepagewhitelist"/>
+ <menuitem id="abp-status-disabled" label="&disable.label;" type="checkbox" key="abp-key-enable" command="abp-command-enable"/>
+ <menuseparator/>
+ <menu id="abp-status-options" label="&options.label;">
+ <menupopup id="abp-status-options-popup">
+ <menuitem id="abp-status-frameobjects" label="&objecttabs.label;" type="checkbox" command="abp-command-toggleobjtabs"/>
+ <menuitem id="abp-status-slowcollapse" label="&hideplaceholders.label;" type="checkbox" command="abp-command-togglecollapse"/>
+ <menuitem id="abp-status-savestats" label="&counthits.label;" type="checkbox" command="abp-command-togglesavestats"/>
+ <menuitem id="abp-status-sync" label="&sync.label;" type="checkbox" command="abp-command-togglesync"/>
+ <menuseparator id="abp-status-iconSettingsSeparator"/>
+ <menuitem id="abp-status-showintoolbar" label="&showintoolbar.label;" type="checkbox" command="abp-command-toggleshowintoolbar"/>
+ <menuitem id="abp-status-showinstatusbar" label="&showinstatusbar.label;" type="checkbox" command="abp-command-toggleshowinstatusbar"/>
+ </menupopup>
+ </menu>
+
+ <hbox class="abp-contributebutton" id="abp-status-contributebutton" pack="center" align="center">
+ <!-- noautoclose attribute tells Australis menu that it shouldn't close when
+ these are clicked, see https://bugzilla.mozilla.org/show_bug.cgi?id=940693 -->
+ <button class="abp-contributebutton-btn" label="&contribute.label;" command="abp-command-contribute" flex="1" noautoclose="true"/>
+ <toolbarbutton class="abp-contributebutton-close" command="abp-command-contribute-hide" noautoclose="true"/>
+ </hbox>
+ </menupopup>
+
+ <keyset id="abp-keyset"/>
+
+ <!-- Dummy oncommand attributes are work-arounds for bug 371900 -->
+ <commandset id="abp-commandset">
+ <command id="abp-command-sendReport" oncommand="//"/>
+ <command id="abp-command-filters" oncommand="//"/>
+ <command id="abp-command-settings" oncommand="//"/>
+ <command id="abp-command-sidebar" oncommand="//"/>
+ <command id="abp-command-togglesitewhitelist"/>
+ <command id="abp-command-togglepagewhitelist"/>
+ <command id="abp-command-toggleobjtabs"/>
+ <command id="abp-command-togglecollapse"/>
+ <command id="abp-command-togglesavestats"/>
+ <command id="abp-command-togglesync"/>
+ <command id="abp-command-toggleshowintoolbar"/>
+ <command id="abp-command-toggleshowinstatusbar"/>
+ <command id="abp-command-enable" oncommand="//"/>
+ <command id="abp-command-contribute"/>
+ <command id="abp-command-contribute-hide"/>
+ </commandset>
+
+ <statusbarpanel id="abp-status" class="statusbarpanel-iconic"
+ tooltip="abp-tooltip"/>
+
+ <toolbarbutton id="abp-toolbarbutton" class="toolbarbutton-1" label="&toolbarbutton.label;"
+ tooltip="abp-tooltip"/>
+
+ <!-- Tools menu -->
+ <menu id="abp-menuitem" label="&toolbarbutton.label;"/>
+
+ <!-- Bottom bar -->
+ <hbox id="abp-bottombar-container">
+ <splitter id="abp-bottombar-splitter"/>
+ <vbox id="abp-bottombar">
+ <toolbox id="abp-bottombar-header">
+ <toolbar id="abp-bottombar-toolbar" align="center" grippyhidden="true" fullscreentoolbar="true">
+ <label id="abp-bottombar-title" control="abp-bottombar-browser" value="&sidebar.title;" flex="1" crop="end"/>
+ <toolbarbutton id="abp-bottombar-close" command="abp-command-sidebar" tooltiptext="&closesidebar.label;"/>
+ </toolbar>
+ </toolbox>
+ <iframe id="abp-bottombar-browser" src="chrome://adblockplus/content/ui/sidebar.xul" flex="1"/>
+ </vbox>
+ </hbox>
+
+ <!-- Notification panel -->
+ <panel id="abp-notification" type="arrow" orient="vertical"
+ xmlns:html="http://www.w3.org/1999/xhtml">
+ <label id="abp-notification-title" class="header"/>
+ <html:p id="abp-notification-message"/>
+ <hbox id="abp-notification-question">
+ <button id="abp-notification-yes" label="&notification.button.yes;"/>
+ <button id="abp-notification-no" label="&notification.button.no;"/>
+ <toolbarbutton id="abp-notification-close" label="&notification.button.close;"></toolbarbutton>
+ </hbox>
+ </panel>
+</overlay>
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/progressBar.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/progressBar.js
new file mode 100644
index 0000000..659acdd
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/progressBar.js
@@ -0,0 +1,158 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+(function()
+{
+ let progressBar, canvas, headers, isRTL;
+
+ function onLoad()
+ {
+ window.removeEventListener("load", onLoad, false);
+
+ // Init global variables
+ progressBar = document.getElementById("progressBar");
+ canvas = document.getElementById("progressBarCanvas");
+
+ headers = Array.prototype.slice.call(progressBar.getElementsByTagName("label"));
+ for (let i = 0; i < headers.length; i++)
+ canvas.parentNode.appendChild(headers[i]);
+
+ // Expose properties
+ progressBar.__defineGetter__("activeItem", getActiveItem);
+ progressBar.__defineSetter__("activeItem", setActiveItem);
+ progressBar.__defineGetter__("activeItemComplete", getActiveItemComplete);
+ progressBar.__defineSetter__("activeItemComplete", setActiveItemComplete);
+
+ isRTL = (window.getComputedStyle(document.documentElement).direction == "rtl");
+
+ // Run actual drawing delayed, once the sizes are fixed
+ window.setTimeout(init, 0);
+ };
+ window.addEventListener("load", onLoad, false);
+
+ function init()
+ {
+ const gapWidth = 5;
+ const arrowheadWidth = 5;
+
+ let width = canvas.width = canvas.offsetWidth;
+ let height = canvas.height = canvas.offsetHeight;
+
+ let context = canvas.getContext("2d");
+ context.fillStyle = window.getComputedStyle(progressBar, "").color;
+ context.strokeStyle = window.getComputedStyle(progressBar, "").color;
+ context.lineWidth = 1;
+ if (isRTL)
+ {
+ context.translate(width, 0);
+ context.scale(-1, 1);
+ }
+
+ let panelCount = headers.length;
+ let panelWidth = (width - gapWidth * (panelCount - 1) - 1) / panelCount;
+ for (let i = 0; i < panelCount; i++)
+ {
+ context.save();
+ context.translate(Math.round(i * (panelWidth + gapWidth)) + 0.5, 0.5);
+ context.beginPath();
+ if (i)
+ context.moveTo(-arrowheadWidth, 0);
+ else
+ context.moveTo(0, 0);
+ context.lineTo(panelWidth - arrowheadWidth, 0);
+ context.lineTo(panelWidth, (height - 1) / 2);
+ context.lineTo(panelWidth - arrowheadWidth, height - 1);
+ if (i)
+ {
+ context.lineTo(-arrowheadWidth, height - 1);
+ context.lineTo(0, (height - 1) / 2);
+ context.lineTo(-arrowheadWidth, 0);
+ }
+ else
+ {
+ context.lineTo(0, height - 1);
+ context.lineTo(0, 0);
+ }
+
+ context.stroke();
+ context.restore();
+
+ let childLeft = Math.round(i * (panelWidth + gapWidth) + 1);
+ let childWidth = panelWidth - arrowheadWidth - 2;
+ let child = headers[i];
+ child.style.MozMarginStart = childLeft + "px";
+ child.style.MozMarginEnd = (width - childLeft - childWidth) + "px";
+ child.style.width = childWidth + "px";
+ }
+
+ // Resize after initialization should be ignored
+ canvas.parentNode.removeAttribute("flex");
+ }
+
+ function getActiveItem()
+ {
+ for (let i = 0; i < headers.length; i++)
+ {
+ let header = headers[i];
+ if (header.classList.contains("active"))
+ return header;
+ }
+ return null;
+ }
+
+ function setActiveItem(val)
+ {
+ let complete = true;
+ for (let i = 0; i < headers.length; i++)
+ {
+ let header = headers[i];
+ if (header == val)
+ complete = false;
+
+ if (!complete && header.value[0] == "✔")
+ header.value = header.value.replace(/^✔\s*/, "");
+ else if (complete && header.value[0] != "✔")
+ header.value = "✔ " + header.value;
+
+ if (header == val)
+ header.classList.add("active");
+ else
+ header.classList.remove("active");
+ }
+ }
+
+ function getActiveItemComplete()
+ {
+ let activeItem = this.activeItem;
+ if (!activeItem)
+ return false;
+ else
+ return activeItem.value[0] == "✔";
+ }
+
+ function setActiveItemComplete(val)
+ {
+ let activeItem = this.activeItem;
+ if (!activeItem)
+ return;
+
+ if (!val && activeItem.value[0] == "✔")
+ activeItem.value = activeItem.value.replace(/^✔\s*/, "");
+ else if (val && activeItem.value[0] != "✔")
+ activeItem.value = "✔ " + activeItem.value;
+ }
+})();
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/progressBar.xul b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/progressBar.xul
new file mode 100644
index 0000000..d940e61
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/progressBar.xul
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+
+<!--
+ - This file is part of Adblock Plus <http://adblockplus.org/>,
+ - Copyright (C) 2006-2014 Eyeo GmbH
+ -
+ - Adblock Plus is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU General Public License version 3 as
+ - published by the Free Software Foundation.
+ -
+ - Adblock Plus is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU General Public License for more details.
+ -
+ - You should have received a copy of the GNU General Public License
+ - along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<overlay id="progressBarOverlay"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/x-javascript;version=1.7" src="progressBar.js"/>
+
+ <hbox id="progressBar" pack="center">
+ <stack flex="1">
+ <canvas xmlns="http://www.w3.org/1999/xhtml" id="progressBarCanvas" width="1" height="1"/>
+ </stack>
+ </hbox>
+</overlay>
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sendReport.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sendReport.js
new file mode 100644
index 0000000..3ccc0e6
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sendReport.js
@@ -0,0 +1,1571 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+//
+// Report data template, more data will be added during data collection
+//
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+
+const MILLISECONDS_IN_SECOND = 1000;
+const SECONDS_IN_MINUTE = 60;
+const SECONDS_IN_HOUR = 60 * SECONDS_IN_MINUTE;
+const SECONDS_IN_DAY = 24 * SECONDS_IN_HOUR;
+
+let contentWindow = window.arguments[0];
+let windowURI = (window.arguments[1] instanceof Ci.nsIURI ? window.arguments[1] : null);
+
+let reportData = new DOMParser().parseFromString("<report></report>", "text/xml");
+
+// Some helper functions to work with the report data
+function reportElement(tag)
+{
+ for (let child = reportData.documentElement.firstChild; child; child = child.nextSibling)
+ if (child.nodeType == Node.ELEMENT_NODE && child.tagName == tag)
+ return child;
+ let element = reportData.createElement(tag);
+ reportData.documentElement.appendChild(element);
+ return element;
+}
+function removeReportElement(tag)
+{
+ for (let child = reportData.documentElement.firstChild; child; child = child.nextSibling)
+ if (child.nodeType == Node.ELEMENT_NODE && child.tagName == tag)
+ child.parentNode.removeChild(child);
+}
+function appendElement(parent, tag, attributes, body)
+{
+ let element = parent.ownerDocument.createElement(tag);
+ if (typeof attributes == "object" && attributes !== null)
+ for (let attribute in attributes)
+ if (attributes.hasOwnProperty(attribute))
+ element.setAttribute(attribute, attributes[attribute]);
+ if (typeof body != "undefined" && body !== null)
+ element.textContent = body;
+ parent.appendChild(element);
+ return element;
+}
+function serializeReportData()
+{
+ let result = new XMLSerializer().serializeToString(reportData);
+
+ // Insert line breaks before each new tag
+ result = result.replace(/(<[^\/]([^"<>]*|"[^"]*")*>)/g, "\n$1");
+ result = result.replace(/^\n+/, "");
+ return result;
+}
+
+let (element = reportElement("adblock-plus"))
+{
+ let {addonVersion} = require("info");
+ element.setAttribute("version", addonVersion);
+ element.setAttribute("locale", Utils.appLocale);
+};
+let (element = reportElement("application"))
+{
+ element.setAttribute("name", Services.appinfo.name);
+ element.setAttribute("vendor", Services.appinfo.vendor);
+ element.setAttribute("version", Services.appinfo.version);
+ element.setAttribute("userAgent", window.navigator.userAgent);
+};
+let (element = reportElement("platform"))
+{
+ element.setAttribute("name", "Gecko");
+ element.setAttribute("version", Services.appinfo.platformVersion);
+ element.setAttribute("build", Services.appinfo.platformBuildID);
+};
+let (element = reportElement("options"))
+{
+ appendElement(element, "option", {id: "enabled"}, Prefs.enabled);
+ appendElement(element, "option", {id: "objecttabs"}, Prefs.frameobjects);
+ appendElement(element, "option", {id: "collapse"}, !Prefs.fastcollapse);
+ appendElement(element, "option", {id: "privateBrowsing"}, PrivateBrowsing.enabledForWindow(contentWindow) || PrivateBrowsing.enabled);
+ appendElement(element, "option", {id: "subscriptionsAutoUpdate"}, Prefs.subscriptions_autoupdate);
+ appendElement(element, "option", {id: "javascript"}, Services.prefs.getBoolPref("javascript.enabled"));
+ appendElement(element, "option", {id: "cookieBehavior"}, Services.prefs.getIntPref("network.cookie.cookieBehavior"));
+};
+
+//
+// Data collectors
+//
+
+let reportsListDataSource =
+{
+ list: [],
+
+ collectData: function(wnd, windowURI, callback)
+ {
+ let data = Prefs.recentReports;
+ if (data && "length" in data)
+ {
+ for (let i = 0; i < data.length; i++)
+ {
+ let entry = data[i];
+ if (typeof entry.reportURL == "string" && entry.reportURL &&
+ typeof entry.time == "number" && Date.now() - entry.time < 30*24*60*60*1000)
+ {
+ let newEntry = {site: null, reportURL: entry.reportURL, time: entry.time};
+ if (typeof entry.site == "string" && entry.site)
+ newEntry.site = entry.site;
+ this.list.push(newEntry);
+ }
+ }
+ }
+
+ if (this.list.length > 10)
+ this.list.splice(10);
+
+ E("recentReports").hidden = !this.list.length;
+ if (this.list.length)
+ {
+ let rows = E("recentReportsRows")
+ for (let i = 0; i < this.list.length; i++)
+ {
+ let entry = this.list[i];
+ let row = document.createElement("row");
+
+ let link = document.createElement("description");
+ link.setAttribute("class", "text-link");
+ link.setAttribute("url", entry.reportURL);
+ link.textContent = entry.reportURL.replace(/^.*\/(?=[^\/])/, "");
+ row.appendChild(link);
+
+ let site = document.createElement("description");
+ if (entry.site)
+ site.textContent = entry.site;
+ row.appendChild(site);
+
+ let time = document.createElement("description");
+ time.textContent = Utils.formatTime(entry.time);
+ row.appendChild(time);
+
+ rows.appendChild(row);
+ }
+ }
+
+ callback();
+ },
+
+ addReport: function(site, reportURL)
+ {
+ this.list.unshift({site: site, reportURL: reportURL, time: Date.now()});
+ Prefs.recentReports = this.list;
+ },
+
+ clear: function()
+ {
+ this.list = [];
+ Prefs.recentReports = this.list;
+ E("recentReports").hidden = true;
+ },
+
+ handleClick: function(event)
+ {
+ if (event.button != 0 || !event.target || !event.target.hasAttribute("url"))
+ return;
+
+ UI.loadInBrowser(event.target.getAttribute("url"));
+ }
+};
+
+let requestsDataSource =
+{
+ requests: reportElement("requests"),
+ origRequests: [],
+ requestNotifier: null,
+ callback: null,
+ nodeByKey: {__proto__: null},
+
+ collectData: function(wnd, windowURI, callback)
+ {
+ this.callback = callback;
+ this.requestNotifier = new RequestNotifier(wnd, this.onRequestFound, this);
+ },
+
+ onRequestFound: function(frame, node, entry, scanComplete)
+ {
+ if (entry)
+ {
+ let key = entry.location + " " + entry.typeDescr + " " + entry.docDomain;
+ let requestXML;
+ if (key in this.nodeByKey)
+ {
+ requestXML = this.nodeByKey[key];
+ requestXML.setAttribute("count", parseInt(requestXML.getAttribute("count"), 10) + 1);
+ }
+ else
+ {
+ requestXML = this.nodeByKey[key] = appendElement(this.requests, "request", {
+ location: censorURL(entry.location),
+ type: entry.typeDescr,
+ docDomain: entry.docDomain,
+ thirdParty: entry.thirdParty,
+ count: 1
+ });
+ }
+
+ // Location is meaningless for element hiding hits
+ if (entry.filter && entry.filter instanceof ElemHideBase)
+ requestXML.removeAttribute("location");
+
+ if (entry.filter)
+ requestXML.setAttribute("filter", entry.filter.text);
+
+ if (node instanceof Element)
+ {
+ requestXML.setAttribute("node", (node.namespaceURI ? node.namespaceURI + "#" : "") + node.localName);
+
+ try
+ {
+ requestXML.setAttribute("size", node.offsetWidth + "x" + node.offsetHeight);
+ } catch(e) {}
+ }
+ this.origRequests.push(entry);
+ }
+
+ if (scanComplete)
+ {
+ this.requestNotifier.shutdown();
+ this.requestNotifier = null;
+ this.callback();
+ }
+ }
+};
+
+let filtersDataSource =
+{
+ origFilters: [],
+
+ collectData: function(wnd, windowURI, callback)
+ {
+ let wndStats = RequestNotifier.getWindowStatistics(wnd);
+ if (wndStats)
+ {
+ let filters = reportElement("filters");
+ for (let f in wndStats.filters)
+ {
+ let filter = Filter.fromText(f)
+ let hitCount = wndStats.filters[f];
+ appendElement(filters, "filter", {
+ text: filter.text,
+ subscriptions: filter.subscriptions.filter(subscriptionsDataSource.subscriptionFilter).map(function(s) s.url).join(" "),
+ hitCount: hitCount
+ });
+ this.origFilters.push(filter);
+ }
+ }
+ callback();
+ }
+};
+
+let subscriptionsDataSource =
+{
+ subscriptionFilter: function(s)
+ {
+ if (s.disabled || !(s instanceof RegularSubscription))
+ return false;
+ if (s instanceof DownloadableSubscription && !/^(http|https|ftp):/i.test(s.url))
+ return false;
+ return true;
+ },
+
+ collectData: function(wnd, windowURI, callback)
+ {
+ let subscriptions = reportElement("subscriptions");
+ let now = Math.round(Date.now() / 1000);
+ for (let i = 0; i < FilterStorage.subscriptions.length; i++)
+ {
+ let subscription = FilterStorage.subscriptions[i];
+ if (!this.subscriptionFilter(subscription))
+ continue;
+
+ let subscriptionXML = appendElement(subscriptions, "subscription", {
+ id: subscription.url,
+ disabledFilters: subscription.filters.filter(function(filter) filter instanceof ActiveFilter && filter.disabled).length
+ });
+ if (subscription.version)
+ subscriptionXML.setAttribute("version", subscription.version);
+ if (subscription.lastDownload)
+ subscriptionXML.setAttribute("lastDownloadAttempt", subscription.lastDownload - now);
+ if (subscription instanceof DownloadableSubscription)
+ {
+ if (subscription.lastSuccess)
+ subscriptionXML.setAttribute("lastDownloadSuccess", subscription.lastSuccess - now);
+ if (subscription.softExpiration)
+ subscriptionXML.setAttribute("softExpiration", subscription.softExpiration - now);
+ if (subscription.expires)
+ subscriptionXML.setAttribute("hardExpiration", subscription.expires - now);
+ subscriptionXML.setAttribute("downloadStatus", subscription.downloadStatus);
+ }
+ }
+ callback();
+ }
+};
+
+let screenshotDataSource =
+{
+ imageOffset: 10,
+
+ // Fields used for color reduction
+ _mapping: [0x00, 0x55, 0xAA, 0xFF],
+ _i: null,
+ _max: null,
+ _pixelData: null,
+ _callback: null,
+
+ // Fields used for user interaction
+ _enabled: true,
+ _canvas: null,
+ _context: null,
+ _selectionType: "mark",
+ _currentData: null,
+ _undoQueue: [],
+
+ collectData: function(wnd, windowURI, callback)
+ {
+ this._callback = callback;
+ this._canvas = E("screenshotCanvas");
+ this._canvas.width = this._canvas.offsetWidth;
+
+ // Do not resize canvas any more (no idea why Gecko requires both to be set)
+ this._canvas.parentNode.style.MozBoxAlign = "center";
+ this._canvas.parentNode.align = "center";
+
+ this._context = this._canvas.getContext("2d");
+ let wndWidth = wnd.document.documentElement.scrollWidth;
+ let wndHeight = wnd.document.documentElement.scrollHeight;
+
+ // Copy scaled screenshot of the webpage. We scale the webpage by width
+ // but leave 10px on each side for easier selecting.
+
+ // Gecko doesn't like sizes more than 64k, restrict to 30k to be on the safe side.
+ // Also, make sure height is at most five times the width to keep image size down.
+ let copyWidth = Math.min(wndWidth, 30000);
+ let copyHeight = Math.min(wndHeight, 30000, copyWidth * 5);
+ let copyX = Math.max(Math.min(wnd.scrollX - copyWidth / 2, wndWidth - copyWidth), 0);
+ let copyY = Math.max(Math.min(wnd.scrollY - copyHeight / 2, wndHeight - copyHeight), 0);
+
+ let scalingFactor = (this._canvas.width - this.imageOffset * 2) / copyWidth;
+ this._canvas.height = copyHeight * scalingFactor + this.imageOffset * 2;
+
+ this._context.save();
+ this._context.translate(this.imageOffset, this.imageOffset);
+ this._context.scale(scalingFactor, scalingFactor);
+ this._context.drawWindow(wnd, copyX, copyY, copyWidth, copyHeight, "rgb(255,255,255)");
+ this._context.restore();
+
+ // Init canvas settings
+ this._context.fillStyle = "rgb(0, 0, 0)";
+ this._context.strokeStyle = "rgba(255, 0, 0, 0.7)";
+ this._context.lineWidth = 3;
+ this._context.lineJoin = "round";
+
+ // Reduce colors asynchronously
+ this._pixelData = this._context.getImageData(this.imageOffset, this.imageOffset,
+ this._canvas.width - this.imageOffset * 2,
+ this._canvas.height - this.imageOffset * 2);
+ this._max = this._pixelData.width * this._pixelData.height * 4;
+ this._i = 0;
+ Utils.runAsync(this.run.bind(this));
+ },
+
+ run: function()
+ {
+ // Process only 5000 bytes at a time to prevent browser hangs
+ let endIndex = Math.min(this._i + 5000, this._max);
+ let i = this._i;
+ for (; i < endIndex; i++)
+ this._pixelData.data[i] = this._mapping[this._pixelData.data[i] >> 6];
+
+ if (i >= this._max)
+ {
+ // Save data back and we are done
+ this._context.putImageData(this._pixelData, this.imageOffset, this.imageOffset);
+ this._callback();
+ }
+ else
+ {
+ this._i = i;
+ Utils.runAsync(this.run.bind(this));
+ }
+ },
+
+ get enabled() this._enabled,
+ set enabled(enabled)
+ {
+ if (this._enabled == enabled)
+ return;
+
+ this._enabled = enabled;
+ this._canvas.style.opacity = this._enabled ? "" : "0.3"
+ E("screenshotMarkButton").disabled = !this._enabled;
+ E("screenshotRemoveButton").disabled = !this._enabled;
+ E("screenshotUndoButton").disabled = !this._enabled || !this._undoQueue.length;
+ },
+
+ get selectionType() this._selectionType,
+ set selectionType(type)
+ {
+ if (this._selectionType == type)
+ return;
+
+ // Abort selection already in progress
+ this.abortSelection();
+
+ this._selectionType = type;
+ },
+
+ exportData: function()
+ {
+ removeReportElement("screenshot");
+ if (this.enabled)
+ {
+ appendElement(reportData.documentElement, "screenshot", {
+ edited: (this._undoQueue.length ? 'true' : 'false')
+ }, this._canvas.toDataURL());
+ }
+ },
+
+ abortSelection: function()
+ {
+ if (this._currentData && this._currentData.data)
+ {
+ this._context.putImageData(this._currentData.data,
+ Math.min(this._currentData.anchorX, this._currentData.currentX),
+ Math.min(this._currentData.anchorY, this._currentData.currentY));
+ }
+ document.removeEventListener("keypress", this.handleKeyPress, true);
+ this._currentData = null;
+ },
+
+ handleKeyPress: function(event)
+ {
+ if (event.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE)
+ {
+ event.stopPropagation();
+ event.preventDefault();
+ screenshotDataSource.abortSelection();
+ }
+ },
+
+ startSelection: function(event)
+ {
+ if (event.button == 2)
+ this.abortSelection(); // Right mouse button aborts selection
+
+ if (event.button != 0 || !this.enabled)
+ return;
+
+ // Abort selection already in progress
+ this.abortSelection();
+
+ let boxObject = document.getBoxObjectFor(this._canvas);
+ let [x, y] = [event.screenX - boxObject.screenX, event.screenY - boxObject.screenY];
+ this._currentData = {
+ data: null,
+ anchorX: x,
+ anchorY: y,
+ currentX: -1,
+ currentY: -1
+ };
+ this.updateSelection(event);
+
+ document.addEventListener("keypress", this.handleKeyPress, true);
+ },
+
+ updateSelection: function(event)
+ {
+ if (event.button != 0 || !this._currentData)
+ return;
+
+ let boxObject = document.getBoxObjectFor(this._canvas);
+ let [x, y] = [event.screenX - boxObject.screenX, event.screenY - boxObject.screenY];
+ if (this._currentData.currentX == x && this._currentData.currentY == y)
+ return;
+
+ if (this._currentData.data)
+ {
+ this._context.putImageData(this._currentData.data,
+ Math.min(this._currentData.anchorX, this._currentData.currentX),
+ Math.min(this._currentData.anchorY, this._currentData.currentY));
+ }
+
+ this._currentData.currentX = x;
+ this._currentData.currentY = y;
+
+ let left = Math.min(this._currentData.anchorX, this._currentData.currentX);
+ let right = Math.max(this._currentData.anchorX, this._currentData.currentX);
+ let top = Math.min(this._currentData.anchorY, this._currentData.currentY);
+ let bottom = Math.max(this._currentData.anchorY, this._currentData.currentY);
+
+ let minDiff = (this._selectionType == "mark" ? 3 : 1);
+ if (right - left >= minDiff && bottom - top >= minDiff)
+ this._currentData.data = this._context.getImageData(left, top, right - left, bottom - top);
+ else
+ this._currentData.data = null;
+
+ if (this._selectionType == "mark")
+ {
+ // all coordinates need to be moved 1.5px inwards to get the desired result
+ left += 1.5;
+ right -= 1.5;
+ top += 1.5;
+ bottom -= 1.5;
+ if (left < right && top < bottom)
+ this._context.strokeRect(left, top, right - left, bottom - top);
+ }
+ else if (this._selectionType == "remove")
+ this._context.fillRect(left, top, right - left, bottom - top);
+ },
+
+ stopSelection: function(event)
+ {
+ if (event.button != 0 || !this._currentData)
+ return;
+
+ if (this._currentData.data)
+ {
+ this._undoQueue.push(this._currentData);
+ E("screenshotUndoButton").disabled = false;
+ }
+
+ this._currentData = null;
+ document.removeEventListener("keypress", this.handleKeyPress, true);
+ },
+
+ undo: function()
+ {
+ let op = this._undoQueue.pop();
+ if (!op)
+ return;
+
+ this._context.putImageData(op.data,
+ Math.min(op.anchorX, op.currentX),
+ Math.min(op.anchorY, op.currentY));
+
+ if (!this._undoQueue.length)
+ E("screenshotUndoButton").disabled = true;
+ }
+};
+
+let framesDataSource =
+{
+ site: null,
+
+ collectData: function(wnd, windowURI, callback)
+ {
+ try
+ {
+ this.site = windowURI.host;
+ if (this.site)
+ document.title += " (" + this.site + ")";
+ }
+ catch (e)
+ {
+ // Expected exception - not all URL schemes have a host name
+ }
+
+ let window = reportElement("window");
+ window.setAttribute("url", censorURL(windowURI ? windowURI.spec : wnd.location.href));
+ if (wnd.opener && wnd.opener.location.href)
+ window.setAttribute("opener", censorURL(wnd.opener.location.href));
+ if (wnd.document.referrer)
+ window.setAttribute("referrer", censorURL(wnd.document.referrer));
+ this.scanFrames(wnd, window);
+
+ callback();
+ },
+
+ scanFrames: function(wnd, xmlList)
+ {
+ try
+ {
+ for (let i = 0; i < wnd.frames.length; i++)
+ {
+ let frame = wnd.frames[i];
+ let frameXML = appendElement(xmlList, "frame", {
+ url: censorURL(frame.location.href)
+ });
+ this.scanFrames(frame, frameXML);
+ }
+ }
+ catch (e)
+ {
+ // Don't break if something goes wrong
+ Cu.reportError(e);
+ }
+ }
+};
+
+let errorsDataSource =
+{
+ collectData: function(wnd, windowURI, callback)
+ {
+ let {addonID} = require("info");
+ addonID = addonID.replace(/[\{\}]/g, "");
+
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=664695 - starting with
+ // Gecko 19 this function returns the result, before that it wrote to a
+ // parameter.
+ let outparam = {};
+ let messages = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).getMessageArray(outparam, {});
+ messages = messages || outparam.value || [];
+ messages = messages.filter(function(message)
+ {
+ return (message instanceof Ci.nsIScriptError &&
+ !/^https?:/i.test(message.sourceName) &&
+ (/adblock/i.test(message.errorMessage) || /adblock/i.test(message.sourceName) ||
+ message.errorMessage.indexOf(addonID) >= 0 || message.sourceName && message.sourceName.indexOf(addonID) >= 0));
+ });
+ if (messages.length > 10) // Only the last 10 messages
+ messages = messages.slice(messages.length - 10, messages.length);
+
+ // Censor app and profile paths in error messages
+ let censored = {__proto__: null};
+ let pathList = [["ProfD", "%PROFILE%"], ["GreD", "%GRE%"], ["CurProcD", "%APP%"]];
+ for (let i = 0; i < pathList.length; i++)
+ {
+ let [pathID, placeholder] = pathList[i];
+ try
+ {
+ let file = FileUtils.getDir(pathID, [], false);
+ censored[file.path.replace(/[\\\/]+$/, '')] = placeholder;
+ let uri = Utils.ioService.newFileURI(file);
+ censored[uri.spec.replace(/[\\\/]+$/, '')] = placeholder;
+ } catch(e) {}
+ }
+
+ let errors = reportElement("errors");
+ for (let i = 0; i < messages.length; i++)
+ {
+ let message = messages[i];
+
+ let text = message.errorMessage;
+ for (let path in censored)
+ text = text.replace(path, censored[path], "gi");
+ if (text.length > 256)
+ text = text.substr(0, 256) + "...";
+
+ let file = message.sourceName;
+ for (let path in censored)
+ file = file.replace(path, censored[path], "gi");
+ if (file.length > 256)
+ file = file.substr(0, 256) + "...";
+
+ let sourceLine = message.sourceLine;
+ if (sourceLine.length > 256)
+ sourceLine = sourceLine.substr(0, 256) + "...";
+
+ appendElement(errors, "error", {
+ type: message.flags & Ci.nsIScriptError.warningFlag ? "warning" : "error",
+ text: text,
+ file: file,
+ line: message.lineNumber,
+ column: message.columnNumber,
+ sourceLine: sourceLine
+ });
+ }
+
+ callback();
+ }
+};
+
+let extensionsDataSource =
+{
+ data: reportData.createElement("extensions"),
+
+ collectData: function(wnd, windowURI, callback)
+ {
+ try
+ {
+ let AddonManager = Cu.import("resource://gre/modules/AddonManager.jsm", null).AddonManager;
+ AddonManager.getAddonsByTypes(["extension", "plugin"], function(items)
+ {
+ for (let i = 0; i < items.length; i++)
+ {
+ let item = items[i];
+ if (!item.isActive)
+ continue;
+ appendElement(this.data, "extension", {
+ id: item.id,
+ name: item.name,
+ type: item.type,
+ version: item.version
+ });
+ }
+ callback();
+ }.bind(this));
+ }
+ catch (e)
+ {
+ // No add-on manager, what's going on? Skip this step.
+ callback();
+ }
+ },
+
+ exportData: function(doExport)
+ {
+ if (doExport)
+ reportData.documentElement.appendChild(this.data);
+ else if (this.data.parentNode)
+ this.data.parentNode.removeChild(this.data);
+ }
+};
+
+let subscriptionUpdateDataSource =
+{
+ contentWnd: null,
+ type: null,
+ outdated: null,
+ needUpdate: null,
+
+ collectData: function(wnd, windowURI, callback)
+ {
+ this.contentWnd = wnd;
+ let now = Date.now() / MILLISECONDS_IN_SECOND;
+ let outdatedThreshold = now - 14 * SECONDS_IN_DAY;
+ let needUpdateThreshold = now - 1 * SECONDS_IN_HOUR;
+
+ this.outdated = [];
+ this.needUpdate = [];
+
+ let subscriptions = FilterStorage.subscriptions.filter(issuesDataSource.subscriptionFilter);
+ for (let i = 0; i < subscriptions.length; i++)
+ {
+ let lastSuccess = subscriptions[i].lastSuccess;
+ if (lastSuccess < outdatedThreshold)
+ this.outdated.push(subscriptions[i]);
+ if (lastSuccess < needUpdateThreshold)
+ this.needUpdate.push(subscriptions[i]);
+ }
+
+ callback();
+ },
+
+ updatePage: function(type)
+ {
+ this.type = type;
+ E("updateInProgress").hidden = (type != "false positive" || this.needUpdate.length == 0);
+ E("outdatedSubscriptions").hidden = !E("updateInProgress").hidden || this.outdated.length == 0;
+ if (!E("outdatedSubscriptions").hidden)
+ {
+ let template = E("outdatedSubscriptionTemplate");
+ let list = E("outdatedSubscriptionsList");
+ while (list.lastChild)
+ list.removeChild(list.lastChild);
+
+ for (let i = 0; i < this.outdated.length; i++)
+ {
+ let subscription = this.outdated[i];
+ let entry = template.cloneNode(true);
+ entry.removeAttribute("id");
+ entry.removeAttribute("hidden");
+ entry.setAttribute("_url", subscription.url);
+ entry.setAttribute("tooltiptext", subscription.url);
+ entry.textContent = subscription.title;
+ list.appendChild(entry);
+ }
+ }
+ return !E("updateInProgress").hidden || !E("outdatedSubscriptions").hidden;
+ },
+
+ showPage: function()
+ {
+ document.documentElement.canAdvance = false;
+
+ if (!E("updateInProgress").hidden)
+ {
+ document.documentElement.canRewind = false;
+
+ for (let i = 0; i < this.needUpdate.length; i++)
+ Synchronizer.execute(this.needUpdate[i], true);
+
+ let listener = function(action)
+ {
+ if (!/^subscription\./.test(action))
+ return;
+
+ for (let i = 0; i < this.needUpdate.length; i++)
+ if (Synchronizer.isExecuting(this.needUpdate[i].url))
+ return;
+
+ FilterNotifier.removeListener(listener);
+ E("updateInProgress").hidden = "true";
+
+ let filtersRemoved = false;
+ let requests = requestsDataSource.origRequests;
+ for (let i = 0; i < requests.length; i++)
+ if (requests[i].filter && !requests[i].filter.subscriptions.filter(function(s) !s.disabled).length)
+ filtersRemoved = true;
+
+ if (filtersRemoved)
+ {
+ // Force the user to reload the page
+ E("updateFixedIssue").hidden = false;
+ document.documentElement.canAdvance = true;
+
+ let nextButton = document.documentElement.getButton("next");
+ [nextButton.label, nextButton.accessKey] = Utils.splitLabel(E("updatePage").getAttribute("reloadButtonLabel"));
+ document.documentElement.addEventListener("wizardnext", function(event)
+ {
+ event.preventDefault();
+ event.stopPropagation();
+ window.close();
+ this.contentWnd.location.reload();
+ }.bind(this), true);
+ }
+ else
+ {
+ this.collectData(null, null, function() {});
+ this.needUpdate = [];
+ if (this.outdated.length)
+ {
+ document.documentElement.canRewind = true;
+
+ this.updatePage(this.type);
+ this.showPage();
+ }
+ else
+ {
+ // No more issues, make sure to remove this page from history and
+ // advance to the next page.
+ document.documentElement.canRewind = true;
+ document.documentElement.canAdvance = true;
+
+ let next = document.documentElement.currentPage.next;
+ document.documentElement.rewind();
+ document.documentElement.currentPage.next = next;
+
+ document.documentElement.advance();
+ }
+ }
+ }.bind(this);
+
+ FilterNotifier.addListener(listener);
+ window.addEventListener("unload", function()
+ {
+ FilterNotifier.removeListener(listener);
+ });
+ }
+ },
+
+ updateOutdated: function()
+ {
+ for (let i = 0; i < this.outdated.length; i++)
+ Synchronizer.execute(this.outdated[i], true);
+ }
+}
+
+let issuesDataSource =
+{
+ contentWnd: null,
+ isEnabled: Prefs.enabled,
+ whitelistFilter: null,
+ disabledFilters: [],
+ disabledSubscriptions: [],
+ ownFilters: [],
+ numSubscriptions: 0,
+ numAppliedFilters: Infinity,
+
+ subscriptionFilter: function(s)
+ {
+ if (s instanceof DownloadableSubscription)
+ return subscriptionsDataSource.subscriptionFilter(s);
+ else
+ return false;
+ },
+
+ collectData: function(wnd, windowURI, callback)
+ {
+ this.contentWnd = wnd;
+ this.whitelistFilter = Policy.isWindowWhitelisted(wnd);
+
+ if (!this.whitelistFilter && this.isEnabled)
+ {
+ // Find disabled filters in active subscriptions matching any of the requests
+ let disabledMatcher = new CombinedMatcher();
+ for each (let subscription in FilterStorage.subscriptions)
+ {
+ if (subscription.disabled)
+ continue;
+
+ for each (let filter in subscription.filters)
+ if (filter instanceof BlockingFilter && filter.disabled)
+ disabledMatcher.add(filter);
+ }
+
+ let seenFilters = {__proto__: null};
+ for each (let request in requestsDataSource.origRequests)
+ {
+ if (request.filter)
+ continue;
+
+ let filter = disabledMatcher.matchesAny(request.location, request.typeDescr, request.docDomain, request.thirdParty);
+ if (filter && !(filter.text in seenFilters))
+ {
+ this.disabledFilters.push(filter);
+ seenFilters[filter.text] = true;
+ }
+ }
+
+ // Find disabled subscriptions with filters matching any of the requests
+ let seenSubscriptions = {__proto__: null};
+ for each (let subscription in FilterStorage.subscriptions)
+ {
+ if (!subscription.disabled)
+ continue;
+
+ disabledMatcher.clear();
+ for each (let filter in subscription.filters)
+ if (filter instanceof BlockingFilter)
+ disabledMatcher.add(filter);
+
+ for each (let request in requestsDataSource.origRequests)
+ {
+ if (request.filter)
+ continue;
+
+ let filter = disabledMatcher.matchesAny(request.location, request.typeDescr, request.docDomain, request.thirdParty);
+ if (filter && !(subscription.url in seenSubscriptions))
+ {
+ this.disabledSubscriptions.push(subscription);
+ seenSubscriptions[subscription.text] = true;
+ break;
+ }
+ }
+ }
+
+ this.numSubscriptions = FilterStorage.subscriptions.filter(this.subscriptionFilter).length;
+ this.numAppliedFilters = 0;
+ for each (let filter in filtersDataSource.origFilters)
+ {
+ if (filter instanceof WhitelistFilter)
+ continue;
+
+ this.numAppliedFilters++;
+ if (filter.subscriptions.some(function(subscription) subscription instanceof SpecialSubscription))
+ this.ownFilters.push(filter);
+ }
+ }
+
+ callback();
+ },
+
+ updateIssues: function(type)
+ {
+ if (type == "other")
+ {
+ E("typeSelectorPage").next = "typeWarning";
+ return;
+ }
+
+ E("issuesWhitelistBox").hidden = !this.whitelistFilter;
+ E("issuesDisabledBox").hidden = this.isEnabled;
+ E("issuesNoFiltersBox").hidden = (type != "false positive" || this.numAppliedFilters > 0);
+ E("issuesNoSubscriptionsBox").hidden = (type != "false negative" || this.numAppliedFilters > 0 || this.numSubscriptions > 0);
+ E("issuesSubscriptionCountBox").hidden = (this.numSubscriptions < 5);
+
+ let ownFiltersBox = E("issuesOwnFilters");
+ if (this.ownFilters.length && !ownFiltersBox.firstChild)
+ {
+ let template = E("issuesOwnFiltersTemplate");
+ for each (let filter in this.ownFilters)
+ {
+ let element = template.cloneNode(true);
+ element.removeAttribute("id");
+ element.removeAttribute("hidden");
+ element.firstChild.setAttribute("value", filter.text);
+ element.firstChild.setAttribute("tooltiptext", filter.text);
+ element.abpFilter = filter;
+ ownFiltersBox.appendChild(element);
+ }
+ }
+ E("issuesOwnFiltersBox").hidden = (type != "false positive" || this.ownFilters.length == 0);
+
+ let disabledSubscriptionsBox = E("issuesDisabledSubscriptions");
+ if (this.disabledSubscriptions.length && !disabledSubscriptionsBox.firstChild)
+ {
+ let template = E("issuesDisabledSubscriptionsTemplate");
+ for each (let subscription in this.disabledSubscriptions)
+ {
+ let element = template.cloneNode(true);
+ element.removeAttribute("id");
+ element.removeAttribute("hidden");
+ element.firstChild.setAttribute("value", subscription.title);
+ element.setAttribute("tooltiptext", subscription instanceof DownloadableSubscription ? subscription.url : subscription.title);
+ element.abpSubscription = subscription;
+ disabledSubscriptionsBox.appendChild(element);
+ }
+ }
+ E("issuesDisabledSubscriptionsBox").hidden = (type != "false negative" || this.disabledSubscriptions.length == 0);
+
+ let disabledFiltersBox = E("issuesDisabledFilters");
+ if (this.disabledFilters.length && !disabledFiltersBox.firstChild)
+ {
+ let template = E("issuesDisabledFiltersTemplate");
+ for each (let filter in this.disabledFilters)
+ {
+ let element = template.cloneNode(true);
+ element.removeAttribute("id");
+ element.removeAttribute("hidden");
+ element.firstChild.setAttribute("value", filter.text);
+ element.setAttribute("tooltiptext", filter.text);
+ element.abpFilter = filter;
+ disabledFiltersBox.appendChild(element);
+ }
+ }
+ E("issuesDisabledFiltersBox").hidden = (type != "false negative" || this.disabledFilters.length == 0);
+
+ // Don't allow sending report if the page is whitelisted - we need the data.
+ // Also disallow reports without matching filters or without subscriptions,
+ // subscription authors cannot do anything about those.
+ E("issuesOverride").hidden = !E("issuesWhitelistBox").hidden ||
+ !E("issuesDisabledBox").hidden ||
+ !E("issuesNoFiltersBox").hidden ||
+ !E("issuesNoSubscriptionsBox").hidden ||
+ !E("issuesSubscriptionCountBox").hidden;
+
+ let page = E("typeSelectorPage");
+ if (subscriptionUpdateDataSource.updatePage(type))
+ {
+ page.next = "update";
+ page = E("updatePage");
+ }
+
+ if (E("issuesWhitelistBox").hidden && E("issuesDisabledBox").hidden &&
+ E("issuesNoFiltersBox").hidden && E("issuesNoSubscriptionsBox").hidden &&
+ E("issuesOwnFiltersBox").hidden && E("issuesDisabledFiltersBox").hidden &&
+ E("issuesDisabledSubscriptionsBox").hidden && E("issuesSubscriptionCountBox").hidden)
+ {
+ page.next = "screenshot";
+ }
+ else
+ {
+ page.next = "issues";
+ }
+ },
+
+ forceReload: function()
+ {
+ // User changed configuration, don't allow sending report now - page needs
+ // to be reloaded
+ E("issuesOverride").hidden = true;
+ E("issuesChangeMessage").hidden = false;
+ document.documentElement.canRewind = false;
+ document.documentElement.canAdvance = true;
+
+ let contentWnd = this.contentWnd;
+ let nextButton = document.documentElement.getButton("next");
+ [nextButton.label, nextButton.accessKey] = Utils.splitLabel(E("updatePage").getAttribute("reloadButtonLabel"));
+ document.documentElement.addEventListener("wizardnext", function(event)
+ {
+ event.preventDefault();
+ event.stopPropagation();
+ window.close();
+ contentWnd.location.reload();
+ }, true);
+ },
+
+ removeWhitelist: function()
+ {
+ if (this.whitelistFilter && this.whitelistFilter.subscriptions.length)
+ this.whitelistFilter.disabled = true;
+ E("issuesWhitelistBox").hidden = true;
+ this.forceReload();
+ },
+
+ enable: function()
+ {
+ Prefs.enabled = true;
+ E("issuesDisabledBox").hidden = true;
+ this.forceReload();
+ },
+
+ addSubscription: function()
+ {
+ let result = {};
+ openDialog("subscriptionSelection.xul", "_blank", "chrome,centerscreen,modal,resizable,dialog=no", null, result);
+ if (!("url" in result))
+ return;
+
+ let subscriptionResults = [[result.url, result.title]];
+ if ("mainSubscriptionURL" in result)
+ subscriptionResults.push([result.mainSubscriptionURL, result.mainSubscriptionTitle]);
+
+ for each (let [url, title] in subscriptionResults)
+ {
+ let subscription = Subscription.fromURL(url);
+ if (!subscription)
+ continue;
+
+ FilterStorage.addSubscription(subscription);
+
+ subscription.disabled = false;
+ subscription.title = title;
+
+ if (subscription instanceof DownloadableSubscription && !subscription.lastDownload)
+ Synchronizer.execute(subscription);
+ }
+
+ E("issuesNoSubscriptionsBox").hidden = true;
+ this.forceReload();
+ },
+
+ disableFilter: function(node)
+ {
+ let filter = node.abpFilter;
+ if (filter && filter.subscriptions.length)
+ filter.disabled = true;
+
+ node.parentNode.removeChild(node);
+ if (!E("issuesOwnFilters").firstChild)
+ E("issuesOwnFiltersBox").hidden = true;
+ this.forceReload();
+ },
+
+ enableFilter: function(node)
+ {
+ let filter = node.abpFilter;
+ if (filter && filter.subscriptions.length)
+ filter.disabled = false;
+
+ node.parentNode.removeChild(node);
+ if (!E("issuesDisabledFilters").firstChild)
+ E("issuesDisabledFiltersBox").hidden = true;
+ this.forceReload();
+ },
+
+
+ enableSubscription: function(node)
+ {
+ let subscription = node.abpSubscription;
+ if (subscription)
+ subscription.disabled = false;
+
+ node.parentNode.removeChild(node);
+ if (!E("issuesDisabledSubscriptions").firstChild)
+ E("issuesDisabledSubscriptionsBox").hidden = true;
+ this.forceReload();
+ }
+};
+
+let dataCollectors = [reportsListDataSource, requestsDataSource, filtersDataSource, subscriptionsDataSource,
+ screenshotDataSource, framesDataSource, errorsDataSource, extensionsDataSource,
+ subscriptionUpdateDataSource, issuesDataSource];
+
+//
+// Wizard logic
+//
+
+function initWizard()
+{
+ // Make sure no issue type is selected by default
+ E("typeGroup").selectedItem = null;
+ document.documentElement.addEventListener("pageshow", updateNextButton, false);
+
+ // Move wizard header
+ let header = document.getAnonymousElementByAttribute(document.documentElement, "class", "wizard-header");
+ if (header)
+ {
+ document.getElementById("wizardHeaderLabel").setAttribute("value", document.documentElement.wizardPages[0].getAttribute("label"));
+ document.documentElement.insertBefore(document.getElementById("wizardHeader"), document.documentElement.firstChild);
+ document.documentElement.addEventListener("pageshow", function()
+ {
+ document.getElementById("wizardHeaderDeck").selectedIndex = (document.documentElement.pageIndex == 0 ? 0 : 1);
+ }, false);
+ }
+
+ // Move privacy link
+ let extraButton = document.documentElement.getButton("extra1");
+ extraButton.parentNode.insertBefore(E("privacyLink"), extraButton);
+}
+
+function updateNextButton()
+{
+ let nextButton = document.documentElement.getButton("next");
+ if (!nextButton)
+ return;
+
+ if (document.documentElement.currentPage.id == "commentPage")
+ {
+ if (!("_origLabel" in nextButton))
+ {
+ nextButton._origLabel = nextButton.label;
+ nextButton._origAccessKey = nextButton.accessKey;
+ [nextButton.label, nextButton.accessKey] = Utils.splitLabel(document.documentElement.getAttribute("sendbuttonlabel"));
+ }
+ }
+ else
+ {
+ if ("_origLabel" in nextButton)
+ {
+ nextButton.label = nextButton._origLabel;
+ nextButton.accessKey = nextButton._origAccessKey;
+ delete nextButton._origLabel;
+ delete nextButton._origAccessKey;
+ }
+ }
+}
+
+function initDataCollectorPage()
+{
+ document.documentElement.canAdvance = false;
+
+ let totalSteps = dataCollectors.length;
+ let initNextDataSource = function()
+ {
+ if (!dataCollectors.length)
+ {
+ // We are done, continue to next page
+ document.documentElement.canAdvance = true;
+ document.documentElement.advance();
+ return;
+ }
+
+ let progress = (totalSteps - dataCollectors.length) / totalSteps * 100;
+ if (progress > 0)
+ {
+ let progressMeter = E("dataCollectorProgress");
+ progressMeter.mode = "determined";
+ progressMeter.value = progress;
+ }
+
+ // Continue with the next data source, asynchronously to allow progress meter to update
+ let dataSource = dataCollectors.shift();
+ Utils.runAsync(function()
+ {
+ dataSource.collectData(contentWindow, windowURI, initNextDataSource);
+ });
+ };
+
+ initNextDataSource();
+}
+
+function initTypeSelectorPage()
+{
+ E("progressBar").activeItem = E("typeSelectorHeader");
+ let header = document.getAnonymousElementByAttribute(document.documentElement, "class", "wizard-header");
+ if (header)
+ header.setAttribute("viewIndex", "1");
+
+ document.documentElement.canRewind = false;
+ typeSelectionUpdated();
+}
+
+function typeSelectionUpdated()
+{
+ let selection = E("typeGroup").selectedItem;
+ document.documentElement.canAdvance = (selection != null);
+ if (selection)
+ {
+ if (reportData.documentElement.getAttribute("type") != selection.value)
+ {
+ E("screenshotCheckbox").checked = (selection.value != "other");
+ E("screenshotCheckbox").doCommand();
+ E("extensionsCheckbox").checked = (selection.value == "other");
+ E("extensionsCheckbox").doCommand();
+ }
+ reportData.documentElement.setAttribute("type", selection.value);
+
+ issuesDataSource.updateIssues(selection.value);
+ }
+}
+
+function initIssuesPage()
+{
+ updateIssuesOverride();
+}
+
+function updateIssuesOverride()
+{
+ document.documentElement.canAdvance = E("issuesOverride").checked;
+}
+
+function initTypeWarningPage()
+{
+ updateTypeWarningOverride();
+
+ let textElement = E("typeWarningText");
+ if ("abpInitialized" in textElement)
+ return;
+
+ let template = textElement.textContent.replace(/[\r\n\s]+/g, " ");
+
+ let [, beforeLink, linkText, afterLink] = /(.*)\[link\](.*)\[\/link\](.*)/.exec(template) || [null, "", template, ""];
+ while (textElement.firstChild && textElement.firstChild.nodeType != Node.ELEMENT_NODE)
+ textElement.removeChild(textElement.firstChild);
+ while (textElement.lastChild && textElement.lastChild.nodeType != Node.ELEMENT_NODE)
+ textElement.removeChild(textElement.lastChild);
+
+ if (textElement.firstChild)
+ textElement.firstChild.textContent = linkText;
+ textElement.insertBefore(document.createTextNode(beforeLink), textElement.firstChild);
+ textElement.appendChild(document.createTextNode(afterLink));
+ textElement.abpInitialized = true;
+}
+
+function updateTypeWarningOverride()
+{
+ document.documentElement.canAdvance = E("typeWarningOverride").checked;
+}
+
+function initScreenshotPage()
+{
+ document.documentElement.canAdvance = true;
+
+ E("progressBar").activeItem = E("screenshotHeader");
+}
+
+function initCommentPage()
+{
+ E("progressBar").activeItem = E("commentPageHeader");
+
+ updateEmail();
+
+ screenshotDataSource.exportData();
+ updateDataField();
+}
+
+function showDataField()
+{
+ E('dataDeck').selectedIndex = 1;
+ updateDataField();
+ E('data').focus();
+}
+
+let _dataFieldUpdateTimeout = null;
+
+function _updateDataField()
+{
+ let dataField = E("data");
+ let [selectionStart, selectionEnd] = [dataField.selectionStart, dataField.selectionEnd];
+ dataField.value = serializeReportData();
+ dataField.setSelectionRange(selectionStart, selectionEnd);
+}
+
+function updateDataField()
+{
+ // Don't do anything if data field is hidden
+ if (E('dataDeck').selectedIndex != 1)
+ return;
+
+ if (_dataFieldUpdateTimeout)
+ {
+ window.clearTimeout(_dataFieldUpdateTimeout);
+ _dataFieldUpdateTimeout = null;
+ }
+
+ _dataFieldUpdateTimeout = window.setTimeout(_updateDataField, 200);
+}
+
+function updateComment()
+{
+ removeReportElement("comment");
+
+ let value = E("comment").value;
+ appendElement(reportData.documentElement, "comment", null, value.substr(0, 1000));
+ E("commentLengthWarning").setAttribute("visible", value.length > 1000);
+ updateDataField();
+}
+
+function updateEmail()
+{
+ removeReportElement("email");
+
+ let anonymous = E("anonymousCheckbox").checked;
+
+ let value = E("email").value;
+
+ // required for persist to work on textbox, see: https://bugzilla.mozilla.org/show_bug.cgi?id=111486
+ E("email").setAttribute("value", value);
+
+ E("email").disabled = anonymous;
+ E("emailLabel").disabled = anonymous;
+ E("anonymityWarning").setAttribute("visible", anonymous);
+
+ if (!anonymous)
+ appendElement(reportData.documentElement, "email", null, value);
+
+ updateDataField();
+
+ document.documentElement.canAdvance = anonymous || /\S/.test(value);
+}
+
+function updateExtensions(attach)
+{
+ extensionsDataSource.exportData(attach);
+ updateDataField();
+}
+
+function initSendPage()
+{
+ E("progressBar").activeItem = E("sendPageHeader");
+
+ E("result").hidden = true;
+ E("sendReportErrorBox").hidden = true;
+ E("sendReportMessage").hidden = false;
+ E("sendReportProgress").hidden = false;
+ E("sendReportProgress").mode = "undetermined";
+
+ document.documentElement.canRewind = false;
+ document.documentElement.getButton("finish").disabled = true;
+
+ let guid = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString().replace(/[\{\}]/g, "");
+ let url = Prefs.report_submiturl.replace(/%GUID%/g, guid).replace(/%LANG%/g, Utils.appLocale);
+ let request = new XMLHttpRequest();
+ request.open("POST", url);
+ request.setRequestHeader("Content-Type", "text/xml");
+ request.setRequestHeader("X-Adblock-Plus", "1");
+ request.addEventListener("load", reportSent, false);
+ request.addEventListener("error", reportSent, false);
+ if ("upload" in request && request.upload)
+ request.upload.addEventListener("progress", updateReportProgress, false);
+ request.send(serializeReportData());
+}
+
+function updateReportProgress(event)
+{
+ if (!event.lengthComputable)
+ return;
+
+ let progress = Math.round(event.loaded / event.total * 100);
+ if (progress > 0)
+ {
+ let progressMeter = E("sendReportProgress");
+ progressMeter.mode = "determined";
+ progressMeter.value = progress;
+ }
+}
+
+function reportSent(event)
+{
+ let request = event.target;
+ let success = false;
+ let errorMessage = E("sendReportError").getAttribute("defaultError");
+ try
+ {
+ let status = request.channel.status;
+ if (Components.isSuccessCode(status))
+ {
+ success = (request.status == 200 || request.status == 0);
+ errorMessage = request.status + " " + request.statusText;
+ }
+ else
+ {
+ errorMessage = "0x" + status.toString(16);
+
+ // Try to find the name for the status code
+ let exception = Cc["@mozilla.org/js/xpc/Exception;1"].createInstance(Ci.nsIXPCException);
+ exception.initialize(null, status, null, null, null, null);
+ if (exception.name)
+ errorMessage = exception.name;
+ }
+ } catch (e) {}
+
+ let result = "";
+ try
+ {
+ result = request.responseText;
+ } catch (e) {}
+
+ result = result.replace(/%CONFIRMATION%/g, encodeHTML(E("result").getAttribute("confirmationMessage")));
+ result = result.replace(/%KNOWNISSUE%/g, encodeHTML(E("result").getAttribute("knownIssueMessage")));
+ result = result.replace(/(<html)\b/, '$1 dir="' + window.getComputedStyle(document.documentElement, "").direction + '"');
+
+ if (!success)
+ {
+ let errorElement = E("sendReportError");
+ let template = errorElement.getAttribute("textTemplate").replace(/[\r\n\s]+/g, " ");
+
+ let [, beforeLink, linkText, afterLink] = /(.*)\[link\](.*)\[\/link\](.*)/.exec(template) || [null, "", template, ""];
+ beforeLink = beforeLink.replace(/\?1\?/g, errorMessage);
+ afterLink = afterLink.replace(/\?1\?/g, errorMessage);
+
+ while (errorElement.firstChild && errorElement.firstChild.nodeType != Node.ELEMENT_NODE)
+ errorElement.removeChild(errorElement.firstChild);
+ while (errorElement.lastChild && errorElement.lastChild.nodeType != Node.ELEMENT_NODE)
+ errorElement.removeChild(errorElement.lastChild);
+
+ if (errorElement.firstChild)
+ errorElement.firstChild.textContent = linkText;
+ errorElement.insertBefore(document.createTextNode(beforeLink), errorElement.firstChild);
+ errorElement.appendChild(document.createTextNode(afterLink));
+
+ E("sendReportErrorBox").hidden = false;
+ }
+
+ E("sendReportProgress").hidden = true;
+
+ let frame = E("result");
+ frame.hidden = false;
+ frame.docShell.allowAuth = false;
+ frame.docShell.allowJavascript = false;
+ frame.docShell.allowMetaRedirects = false;
+ frame.docShell.allowPlugins = false;
+ frame.docShell.allowSubframes = false;
+
+ frame.setAttribute("src", "data:text/html;charset=utf-8," + encodeURIComponent(result));
+
+ E("sendReportMessage").hidden = true;
+
+ if (success)
+ {
+ try
+ {
+ let link = request.responseXML.getElementById("link").getAttribute("href");
+ let button = E("copyLink");
+ button.setAttribute("url", link);
+ button.removeAttribute("disabled");
+
+ if (!PrivateBrowsing.enabledForWindow(contentWindow) && !PrivateBrowsing.enabled)
+ reportsListDataSource.addReport(framesDataSource.site, link);
+ } catch (e) {}
+ E("copyLinkBox").hidden = false;
+
+ document.documentElement.getButton("finish").disabled = false;
+ document.documentElement.getButton("cancel").disabled = true;
+ E("progressBar").activeItemComplete = true;
+ }
+}
+
+function processLinkClick(event)
+{
+ event.preventDefault();
+
+ let link = event.target;
+ while (link && !(link instanceof HTMLAnchorElement))
+ link = link.parentNode;
+
+ if (link && (link.protocol == "http:" || link.protocol == "https:"))
+ UI.loadInBrowser(link.href);
+}
+
+function copyLink(url)
+{
+ Utils.clipboardHelper.copyString(url);
+}
+
+function censorURL(url)
+{
+ return url.replace(/([?;&\/#][^?;&\/#]+?=)[^?;&\/#]+/g, "$1*");
+}
+
+function encodeHTML(str)
+{
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
+}
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sendReport.xul b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sendReport.xul
new file mode 100644
index 0000000..54e0726
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sendReport.xul
@@ -0,0 +1,259 @@
+<?xml version="1.0"?>
+
+<!--
+ - This file is part of Adblock Plus <http://adblockplus.org/>,
+ - Copyright (C) 2006-2014 Eyeo GmbH
+ -
+ - Adblock Plus is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU General Public License version 3 as
+ - published by the Free Software Foundation.
+ -
+ - Adblock Plus is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU General Public License for more details.
+ -
+ - You should have received a copy of the GNU General Public License
+ - along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://global/skin/wizard.css" type="text/css"?>
+<?xml-stylesheet href="chrome://adblockplus/skin/sendReport.css" type="text/css"?>
+
+<?xul-overlay href="progressBar.xul"?>
+
+<!DOCTYPE dialog [
+<!ENTITY % reporterDTD SYSTEM "chrome://adblockplus/locale/sendReport.dtd">
+%reporterDTD;
+<!ENTITY % filtersDTD SYSTEM "chrome://adblockplus/locale/filters.dtd">
+%filtersDTD;
+]>
+
+<wizard
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&wizard.title;"
+ id="abpSendReportWizard"
+ onload="initWizard();"
+ width="800"
+ height="550"
+ sendbuttonlabel="&sendButton.label;"
+ windowtype="abp:sendReport">
+
+<script type="application/x-javascript;version=1.7" src="utils.js"/>
+<script type="application/x-javascript;version=1.7" src="sendReport.js"/>
+
+<keyset id="wizardKeys">
+ <key id="undoKey" modifiers="accel" key="Z" oncommand="if (document.documentElement.currentPage.id == 'screenshotPage') screenshotDataSource.undo();"/>
+</keyset>
+
+<box hidden="true">
+ <vbox id="wizardHeader" class="wizard-header">
+ <deck id="wizardHeaderDeck">
+ <description id="wizardHeaderLabel" class="wizard-header-label"/>
+ <hbox id="progressBar">
+ <label id="typeSelectorHeader" class="progressLabel" value="&typeSelector.heading;" crop="end"/>
+ <label id="screenshotHeader" class="progressLabel" value="&screenshot.heading;" crop="end"/>
+ <label id="commentPageHeader" class="progressLabel" value="&commentPage.heading;" crop="end"/>
+ <label id="sendPageHeader" class="progressLabel" value="&sendPage.heading;" crop="end"/>
+ </hbox>
+ </deck>
+ </vbox>
+
+ <label id="privacyLink" class="text-link" value="&privacyPolicy.label;" onclick="UI.loadDocLink('reporter_privacy');"/>
+</box>
+
+<wizardpage id="dataCollectorPage" pageid="dataCollector" next="typeSelector" label="&dataCollector.heading;" onpageshow="initDataCollectorPage();">
+ <description>&dataCollector.description;</description>
+
+ <progressmeter id="dataCollectorProgress" mode="undetermined"/>
+</wizardpage>
+
+<wizardpage id="typeSelectorPage" pageid="typeSelector" next="screenshot" label="&typeSelector.heading;" onpageshow="initTypeSelectorPage();">
+ <description>&typeSelector.description;</description>
+
+ <radiogroup id="typeGroup" oncommand="typeSelectionUpdated();">
+ <radio id="typeFalsePositive" value="false positive" label="&typeSelector.falsePositive.label;"/>
+ <description class="radioDescription">&typeSelector.falsePositive.description;</description>
+ <radio id="typeFalseNegative" value="false negative" label="&typeSelector.falseNegative.label;"/>
+ <description class="radioDescription">&typeSelector.falseNegative.description;</description>
+ <radio id="typeOther" value="other" label="&typeSelector.other.label;"/>
+ <description class="radioDescription">&typeSelector.other.description;</description>
+ </radiogroup>
+
+ <deck id="recentReports" currentIndex="0" flex="1">
+ <vbox pack="end">
+ <label class="text-link" value="&showRecentReports.label;" onclick="E('recentReports').selectedIndex = 1;"/>
+ </vbox>
+ <groupbox flex="1">
+ <caption label="&recentReports.label;"/>
+ <grid flex="1" id="recentReportsList">
+ <columns>
+ <column flex="2"/>
+ <column flex="1"/>
+ <column/>
+ </columns>
+ <rows id="recentReportsRows" onclick="reportsListDataSource.handleClick(event);"/>
+ </grid>
+
+ <hbox pack="start">
+ <button label="&recentReports.clear.label;" oncommand="reportsListDataSource.clear();"/>
+ </hbox>
+ </groupbox>
+ </deck>
+</wizardpage>
+
+<wizardpage id="updatePage" pageid="update" next="screenshot" onpageshow="subscriptionUpdateDataSource.showPage();" reloadButtonLabel="&reloadButton.label;">
+ <vbox id="updateInProgress">
+ <description>&update.inProgress.description;</description>
+ <progressmeter mode="undetermined"/>
+ </vbox>
+
+ <description id="updateFixedIssue" hidden="true">&update.fixed.description;</description>
+
+ <vbox id="outdatedSubscriptions">
+ <description>&outdatedSubscriptions.description;</description>
+
+ <description id="outdatedSubscriptionTemplate" class="text-link" onclick="UI.loadInBrowser(this.getAttribute('_url'));"/>
+
+ <vbox id="outdatedSubscriptionsList"/>
+
+ <hbox>
+ <button label="&update.start.label;" oncommand="subscriptionUpdateDataSource.updateOutdated();window.close();"/>
+ <button label="&issues.openPreferences.label;" oncommand="UI.openFiltersDialog();window.close();"/>
+ </hbox>
+ </vbox>
+</wizardpage>
+
+<wizardpage id="issuesPage" pageid="issues" next="screenshot" onpageshow="initIssuesPage();" reloadButtonLabel="&reloadButton.label;">
+ <description>&issues.description;</description>
+
+ <vbox id="issuesBox" flex="1">
+ <groupbox id="issuesWhitelistBox" hidden="true">
+ <description>&issues.whitelist.description;</description>
+ <hbox pack="end">
+ <button label="&issues.whitelist.remove.label;" oncommand="issuesDataSource.removeWhitelist();"/>
+ </hbox>
+ </groupbox>
+ <groupbox id="issuesDisabledBox" hidden="true">
+ <description>&issues.disabled.description;</description>
+ <hbox pack="end">
+ <button label="&issues.disabled.enable.label;" oncommand="issuesDataSource.enable();"/>
+ </hbox>
+ </groupbox>
+ <groupbox id="issuesNoFiltersBox" hidden="true">
+ <description>&issues.nofilters.description;</description>
+ </groupbox>
+ <groupbox id="issuesNoSubscriptionsBox" hidden="true">
+ <description>&issues.nosubscriptions.description;</description>
+ <hbox pack="end">
+ <button label="&issues.nosubscriptions.add.label;" oncommand="issuesDataSource.addSubscription();"/>
+ </hbox>
+ </groupbox>
+ <groupbox id="issuesSubscriptionCountBox" hidden="true">
+ <description>&issues.subscriptionCount.description;</description>
+ <hbox pack="end">
+ <button label="&issues.openPreferences.label;" oncommand="UI.openFiltersDialog();window.close();"/>
+ </hbox>
+ </groupbox>
+ <groupbox id="issuesOwnFiltersBox" hidden="true">
+ <description>&issues.ownfilters.description;</description>
+ <hbox id="issuesOwnFiltersTemplate" align="center" hidden="true">
+ <description flex="1" crop="end"/>
+ <button label="&issues.ownfilters.disable.label;" oncommand="issuesDataSource.disableFilter(this.parentNode);"/>
+ </hbox>
+ <vbox id="issuesOwnFilters"/>
+ </groupbox>
+ <groupbox id="issuesDisabledSubscriptionsBox" hidden="true">
+ <description>&issues.disabledgroups.description;</description>
+ <hbox id="issuesDisabledSubscriptionsTemplate" align="center" hidden="true">
+ <description flex="1" crop="end"/>
+ <button label="&issues.disabledgroups.enable.label;" oncommand="issuesDataSource.enableSubscription(this.parentNode);"/>
+ </hbox>
+ <vbox id="issuesDisabledSubscriptions"/>
+ </groupbox>
+ <groupbox id="issuesDisabledFiltersBox" hidden="true">
+ <description>&issues.disabledfilters.description;</description>
+ <hbox id="issuesDisabledFiltersTemplate" align="center" hidden="true">
+ <description flex="1" crop="end"/>
+ <button label="&issues.disabledfilters.enable.label;" oncommand="issuesDataSource.enableFilter(this.parentNode);"/>
+ </hbox>
+ <vbox id="issuesDisabledFilters"/>
+ </groupbox>
+ </vbox>
+
+ <checkbox id="issuesOverride" label="&issues.override.label;" oncommand="updateIssuesOverride();"/>
+ <description id="issuesChangeMessage" hidden="true">&issues.change.description;</description>
+</wizardpage>
+
+<wizardpage id="typeWarningPage" pageid="typeWarning" next="screenshot" onpageshow="initTypeWarningPage();">
+ <description id="typeWarningText">
+ &typeWarning.description;
+ <label id="typeWarningTextLink" class="text-link" onclick="UI.loadDocLink('reporter_other_link');"/>
+ </description>
+
+ <checkbox id="typeWarningOverride" label="&typeWarning.override.label;" oncommand="updateTypeWarningOverride();"/>
+</wizardpage>
+
+<wizardpage id="screenshotPage" pageid="screenshot" next="comment" label="&screenshot.heading;" onpageshow="initScreenshotPage();">
+ <description>&screenshot.description;</description>
+
+ <checkbox id="screenshotCheckbox" checked="true" label="&screenshot.attach.label;" oncommand="screenshotDataSource.enabled = this.checked;"/>
+ <hbox id="screenshotButtons" pack="end">
+ <button id="screenshotMarkButton" type="radio" group="selectionType" oncommand="screenshotDataSource.selectionType = 'mark';" checked="true" label="&screenshot.mark.label;"/>
+ <button id="screenshotRemoveButton" type="radio" group="selectionType" oncommand="screenshotDataSource.selectionType = 'remove';" label="&screenshot.remove.label;"/>
+ <button id="screenshotUndoButton" oncommand="screenshotDataSource.undo();" disabled="true" label="&screenshot.undo.label;"/>
+ </hbox>
+ <vbox id="screenshotBox" flex="1">
+ <canvas xmlns="http://www.w3.org/1999/xhtml" id="screenshotCanvas" onmousedown="screenshotDataSource.startSelection(event);" onmouseup="screenshotDataSource.stopSelection(event);" onmouseout="screenshotDataSource.stopSelection(event);" onmousemove="screenshotDataSource.updateSelection(event);"/>
+ </vbox>
+</wizardpage>
+
+<wizardpage id="commentPage" pageid="comment" next="send" label="&commentPage.heading;" onpageshow="initCommentPage();">
+ <description>&emailComment.label;</description>
+ <hbox class="topLabel" align="baseline">
+ <label id="emailLabel" control="email" value="&email.label;"/>
+ <textbox id="email" persist="value" flex="1" maxlength="200" oninput="updateEmail();"/>
+ </hbox>
+ <checkbox id="anonymousCheckbox" label="&anonymous.label;" oncommand="updateEmail();"/>
+ <description id="anonymityWarning" visible="false">&anonymity.warning;</description>
+
+ <description class="topLabel">&commentPage.description;</description>
+ <label class="topLabel" control="comment" value="&comment.label;"/>
+ <textbox id="comment" multiline="true" flex="1" oninput="updateComment();"/>
+ <description id="commentLengthWarning" visible="false">&comment.lengthWarning;</description>
+
+ <checkbox id="extensionsCheckbox" label="&attachExtensions.label;" oncommand="updateExtensions(this.checked);"/>
+
+ <deck id="dataDeck" selectedIndex="0" flex="2">
+ <vbox pack="start">
+ <label class="text-link" value="&showData.label;" onclick="showDataField();"/>
+ </vbox>
+ <vbox>
+ <label control="data" value="&data.label;"/>
+ <textbox id="data" readonly="true" multiline="true" wrap="off" flex="1"/>
+ </vbox>
+ </deck>
+</wizardpage>
+
+<wizardpage id="sendPage" pageid="send" label="&sendPage.heading;" onpageshow="initSendPage();">
+ <description id="sendReportMessage">&sendPage.waitMessage;</description>
+
+ <vbox id="sendReportErrorBox" align="end" hidden="true">
+ <description id="sendReportError" textTemplate="&sendPage.errorMessage;" defaultError="&subscription.lastDownload.connectionError;">
+ <label id="sendReportErrorLinks" class="text-link" onclick="UI.loadDocLink('reporter_connect_issue');"/>
+ </description>
+ <button id="sendRetryButton" label="&sendPage.retry.label;" oncommand="initSendPage();"/>
+ </vbox>
+
+ <progressmeter id="sendReportProgress" mode="undetermined"/>
+
+ <iframe id="result" type="content" flex="1" hidden="true" onclick="processLinkClick(event);"
+ confirmationMessage="&sendPage.confirmation;" knownIssueMessage="&sendPage.knownIssue;"/>
+
+ <hbox id="copyLinkBox" pack="end" hidden="true">
+ <button id="copyLink" disabled="true" label="&copyLink.label;" oncommand="copyLink(this.getAttribute('url'));"/>
+ </hbox>
+</wizardpage>
+
+</wizard>
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/settings.xul b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/settings.xul
new file mode 100644
index 0000000..4215a38
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/settings.xul
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+
+<!--
+ - This file is part of Adblock Plus <http://adblockplus.org/>,
+ - Copyright (C) 2006-2014 Eyeo GmbH
+ -
+ - Adblock Plus is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU General Public License version 3 as
+ - published by the Free Software Foundation.
+ -
+ - Adblock Plus is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU General Public License for more details.
+ -
+ - You should have received a copy of the GNU General Public License
+ - along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<!DOCTYPE vbox SYSTEM "chrome://adblockplus/locale/overlay.dtd">
+
+<window
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="1"
+ height="1"
+ onload="window.close();"> <!-- Close window if it gets opened directly for some reason -->
+ <setting type="control">
+ <button id="adblockplus-filters" label="&filters.label;…"/>
+ </setting>
+ <setting pref="extensions.adblockplus.enabled" type="bool" inverted="true" title="&disable.label;"/>
+ <setting pref="extensions.adblockplus.frameobjects" type="bool" title="&objecttabs.label;"/>
+ <setting pref="extensions.adblockplus.fastcollapse" type="bool" inverted="true" title="&hideplaceholders.label;"/>
+ <setting id="adblockplus-savestats" type="bool" title="&counthits.label;"/>
+ <setting id="adblockplus-sync" type="bool" title="&sync.label;"/>
+ <setting id="adblockplus-showintoolbar" type="bool" title="&showintoolbar.label;"/>
+ <setting id="adblockplus-showinstatusbar" pref="extensions.adblockplus.showinstatusbar" type="bool" title="&showinstatusbar.label;"/>
+ <setting id="adblockplus-autoupdate" pref="extensions.adblockplus.subscriptions_autoupdate" type="bool" title="&subscription.update.label;"/>
+</window>
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sidebar.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sidebar.js
new file mode 100644
index 0000000..358aebe
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sidebar.js
@@ -0,0 +1,1246 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+// Main browser window
+var mainWin = parent;
+
+// The window handler currently in use
+var requestNotifier = null;
+
+var cacheSession = null;
+var noFlash = false;
+
+// Matcher for disabled filters
+var disabledMatcher = new CombinedMatcher();
+
+// Cached string values
+var docDomainThirdParty = null;
+var docDomainFirstParty = null;
+
+function init() {
+ docDomainThirdParty = document.documentElement.getAttribute("docDomainThirdParty");
+ docDomainFirstParty = document.documentElement.getAttribute("docDomainFirstParty");
+
+ var list = E("list");
+ list.view = treeView;
+
+ // Restore previous state
+ var params = Utils.getParams();
+ if (params && params.filter)
+ {
+ E("searchField").value = params.filter;
+ treeView.setFilter(params.filter);
+ }
+ if (params && params.focus && E(params.focus))
+ E(params.focus).focus();
+ else
+ E("searchField").focus();
+
+ var selected = null;
+ if (/sidebarDetached\.xul$/.test(parent.location.href))
+ {
+ mainWin = parent.opener;
+ mainWin.addEventListener("unload", mainUnload, false);
+ E("detachButton").hidden = true;
+ E("reattachButton").hidden = false;
+
+ let mustDetach = parent.arguments[0];
+ if (mustDetach)
+ E("reattachButton").setAttribute("disabled", "true");
+ if ("sidebar" in UI.hotkeys)
+ {
+ let {KeySelector} = require("keySelector");
+ parent.addEventListener("keypress", function(event)
+ {
+ if (KeySelector.matchesKey(event, UI.hotkeys.sidebar))
+ doClose();
+ }, false);
+ }
+
+ // Set default size/position unless already persisted
+ let defaults = {screenX: 0, screenY: 0, width: 600, height: 300};
+ if (params && params.position)
+ defaults = params.position;
+
+ let wnd = parent.document.documentElement;
+ for (let attr in defaults)
+ if (!wnd.hasAttribute(attr))
+ wnd.setAttribute(attr, defaults[attr]);
+ }
+
+ let {getBrowser, addBrowserLocationListener} = require("appSupport");
+ window.__defineGetter__("content", function() {return getBrowser(mainWin).contentWindow;});
+
+ // Initialize matcher for disabled filters
+ reloadDisabledFilters();
+ FilterNotifier.addListener(reloadDisabledFilters);
+ Prefs.addListener(onPrefChange);
+
+ // Activate flasher
+ list.addEventListener("select", onSelectionChange, false);
+
+ // Initialize data
+ handleLocationChange();
+
+ // Install a progress listener to catch location changes
+ if (addBrowserLocationListener)
+ addBrowserLocationListener(mainWin, handleLocationChange, true);
+}
+
+// To be called for a detached window when the main window has been closed
+function mainUnload() {
+ parent.close();
+}
+
+// To be called on unload
+function cleanUp() {
+ flasher.stop();
+ requestNotifier.shutdown();
+ FilterNotifier.removeListener(reloadDisabledFilters);
+ Prefs.removeListener(onPrefChange);
+ E("list").view = null;
+
+ let {removeBrowserLocationListener} = require("appSupport");
+ if (removeBrowserLocationListener)
+ removeBrowserLocationListener(mainWin, handleLocationChange);
+ mainWin.removeEventListener("unload", mainUnload, false);
+}
+
+/**
+ * Tracks preference changes, calls reloadDisabledFilters whenever Adblock Plus
+ * is enabled/disabled.
+ */
+function onPrefChange(name)
+{
+ if (name == "enabled")
+ reloadDisabledFilters();
+}
+
+let reloadDisabledScheduled = false;
+
+/**
+ * Updates matcher for disabled filters (global disabledMatcher variable),
+ * called on each filter change. Execute delayed to prevent multiple subsequent
+ * invocations.
+ */
+function reloadDisabledFilters()
+{
+ if (reloadDisabledScheduled)
+ return;
+
+ Utils.runAsync(reloadDisabledFiltersInternal);
+ reloadDisabledScheduled = true;
+}
+
+function reloadDisabledFiltersInternal()
+{
+ reloadDisabledScheduled = false;
+ disabledMatcher.clear();
+
+ if (Prefs.enabled)
+ {
+ for each (let subscription in FilterStorage.subscriptions)
+ {
+ if (subscription.disabled)
+ continue;
+
+ for each (let filter in subscription.filters)
+ if (filter instanceof RegExpFilter && filter.disabled)
+ disabledMatcher.add(filter);
+ }
+ }
+
+ treeView.updateFilters();
+}
+
+// Called whenever list selection changes - triggers flasher
+function onSelectionChange() {
+ var item = treeView.getSelectedItem();
+ if (item)
+ E("copy-command").removeAttribute("disabled");
+ else
+ E("copy-command").setAttribute("disabled", "true");
+
+ if (item && window.content)
+ {
+ let key = item.location + " " + item.type + " " + item.docDomain;
+ RequestNotifier.storeSelection(window.content, key);
+ treeView.itemToSelect = null;
+ }
+
+ if (!noFlash)
+ flasher.flash(item ? item.nodes : null);
+}
+
+function handleLocationChange()
+{
+ if (requestNotifier)
+ requestNotifier.shutdown();
+
+ treeView.clearData();
+ treeView.itemToSelect = RequestNotifier.getSelection(window.content);
+ requestNotifier = new RequestNotifier(window.content, function(wnd, node, item, scanComplete)
+ {
+ if (item)
+ treeView.addItem(node, item, scanComplete);
+ });
+}
+
+// Fills a box with text splitting it up into multiple lines if necessary
+function setMultilineContent(box, text, noRemove)
+{
+ if (!noRemove)
+ while (box.firstChild)
+ box.removeChild(box.firstChild);
+
+ for (var i = 0; i < text.length; i += 80)
+ {
+ var description = document.createElement("description");
+ description.setAttribute("value", text.substr(i, 80));
+ box.appendChild(description);
+ }
+}
+
+// Fill in tooltip data before showing it
+function fillInTooltip(e) {
+ // Prevent tooltip from overlapping menu
+ if (E("context").state == "open")
+ {
+ e.preventDefault();
+ return;
+ }
+
+ var item;
+ if (treeView.data && !treeView.data.length)
+ item = treeView.getDummyTooltip();
+ else
+ item = treeView.getItemAt(e.clientX, e.clientY);
+
+ if (!item)
+ {
+ e.preventDefault();
+ return;
+ }
+
+ let filter = ("filter" in item && item.filter ? item.filter : null);
+ let size = ("tooltip" in item ? null : getItemSize(item));
+ let subscriptions = (filter ? filter.subscriptions.filter(function(subscription) { return !subscription.disabled; }) : []);
+
+ E("tooltipDummy").hidden = !("tooltip" in item);
+ E("tooltipAddressRow").hidden = ("tooltip" in item);
+ E("tooltipTypeRow").hidden = ("tooltip" in item);
+ E("tooltipSizeRow").hidden = !size;
+ E("tooltipDocDomainRow").hidden = ("tooltip" in item || !item.docDomain);
+ E("tooltipFilterRow").hidden = !filter;
+ E("tooltipFilterSourceRow").hidden = !subscriptions.length;
+
+ if ("tooltip" in item)
+ E("tooltipDummy").setAttribute("value", item.tooltip);
+ else
+ {
+ E("tooltipAddress").parentNode.hidden = (item.typeDescr == "ELEMHIDE");
+ setMultilineContent(E("tooltipAddress"), item.location);
+
+ var type = item.localizedDescr;
+ if (filter && filter instanceof WhitelistFilter)
+ type += " " + E("tooltipType").getAttribute("whitelisted");
+ else if (filter && item.typeDescr != "ELEMHIDE")
+ type += " " + E("tooltipType").getAttribute("filtered");
+ E("tooltipType").setAttribute("value", type);
+
+ if (size)
+ E("tooltipSize").setAttribute("value", size.join(" x "));
+
+ E("tooltipDocDomain").setAttribute("value", item.docDomain + " " + (item.thirdParty ? docDomainThirdParty : docDomainFirstParty));
+ }
+
+ if (filter)
+ {
+ let filterField = E("tooltipFilter");
+ setMultilineContent(filterField, filter.text);
+ if (filter.disabled)
+ {
+ let disabledText = document.createElement("description");
+ disabledText.className = "disabledTextLabel";
+ disabledText.textContent = filterField.getAttribute("disabledText");
+ filterField.appendChild(disabledText);
+ }
+
+ if (subscriptions.length)
+ {
+ let sourceElement = E("tooltipFilterSource");
+ while (sourceElement.firstChild)
+ sourceElement.removeChild(sourceElement.firstChild);
+ for (let i = 0; i < subscriptions.length; i++)
+ setMultilineContent(sourceElement, subscriptions[i].title, true);
+ }
+ }
+
+ var showPreview = Prefs.previewimages && !("tooltip" in item);
+ showPreview = showPreview && item.typeDescr == "IMAGE";
+ showPreview = showPreview && (!item.filter || item.filter.disabled || item.filter instanceof WhitelistFilter);
+ if (showPreview)
+ {
+ // Check whether image is in cache (stolen from ImgLikeOpera)
+ if (!cacheSession)
+ {
+ var cacheService = Cc["@mozilla.org/network/cache-service;1"].getService(Ci.nsICacheService);
+ cacheSession = cacheService.createSession("HTTP", Ci.nsICache.STORE_ANYWHERE, true);
+ }
+
+ let cacheListener =
+ {
+ onCacheEntryAvailable: function(descriptor, accessGranted, status)
+ {
+ if (!descriptor)
+ return;
+
+ descriptor.close();
+ // Show preview here since this is asynchronous now
+ // and we have a valid descriptor
+ E("tooltipPreview").setAttribute("src", item.location);
+ E("tooltipPreviewBox").hidden = false;
+ },
+ onCacheEntryDoomed: function(status)
+ {
+ }
+ };
+ try
+ {
+ cacheSession.asyncOpenCacheEntry(item.location, Ci.nsICache.ACCESS_READ, cacheListener);
+ }
+ catch (e)
+ {
+ Cu.reportError(e);
+ }
+ }
+
+ E("tooltipPreviewBox").hidden = true;
+}
+
+const visual = {
+ OTHER: true,
+ IMAGE: true,
+ SUBDOCUMENT: true
+}
+
+/**
+ * Updates context menu before it is shown.
+ */
+function fillInContext(/**Event*/ e)
+{
+ let item, allItems;
+ if (treeView.data && !treeView.data.length)
+ {
+ item = treeView.getDummyTooltip();
+ allItems = [item];
+ }
+ else
+ {
+ item = treeView.getItemAt(e.clientX, e.clientY);
+ allItems = treeView.getAllSelectedItems();
+ }
+
+ if (!item || ("tooltip" in item && !("filter" in item)))
+ return false;
+
+ E("contextDisableFilter").hidden = true;
+ E("contextEnableFilter").hidden = true;
+ E("contextDisableOnSite").hidden = true;
+ if ("filter" in item && item.filter)
+ {
+ let filter = item.filter;
+ let menuItem = E(filter.disabled ? "contextEnableFilter" : "contextDisableFilter");
+ menuItem.setAttribute("label", menuItem.getAttribute("labeltempl").replace(/\?1\?/, filter.text));
+ menuItem.hidden = false;
+
+ if (filter instanceof ActiveFilter && !filter.disabled && filter.subscriptions.length && !filter.subscriptions.some(function(subscription) !(subscription instanceof SpecialSubscription)))
+ {
+ let domain = null;
+ try {
+ domain = Utils.effectiveTLD.getBaseDomainFromHost(item.docDomain);
+ } catch (e) {}
+
+ if (domain && !filter.isActiveOnlyOnDomain(domain))
+ {
+ menuItem = E("contextDisableOnSite");
+ menuItem.setAttribute("label", menuItem.getAttribute("labeltempl").replace(/\?1\?/, domain));
+ menuItem.hidden = false;
+ }
+ }
+ }
+
+ E("contextWhitelist").hidden = ("tooltip" in item || !item.filter || item.filter.disabled || item.filter instanceof WhitelistFilter || item.typeDescr == "ELEMHIDE");
+ E("contextBlock").hidden = !E("contextWhitelist").hidden;
+ E("contextBlock").setAttribute("disabled", "filter" in item && item.filter && !item.filter.disabled);
+ E("contextEditFilter").setAttribute("disabled", !("filter" in item && item.filter));
+ E("contextOpen").setAttribute("disabled", "tooltip" in item || item.typeDescr == "ELEMHIDE");
+ E("contextFlash").setAttribute("disabled", "tooltip" in item || !(item.typeDescr in visual) || (item.filter && !item.filter.disabled && !(item.filter instanceof WhitelistFilter)));
+ E("contextCopyFilter").setAttribute("disabled", !allItems.some(function(item) {return "filter" in item && item.filter}));
+
+ return true;
+}
+
+/**
+ * Resets context menu data once the context menu is closed.
+ */
+function clearContextMenu(/**Event*/ event)
+{
+ if (event.eventPhase != event.AT_TARGET)
+ return;
+
+ {
+ let menuItem = E("contextDisableOnSite");
+ menuItem.item = item;
+ menuItem.filter = filter;
+ menuItem.domain = domain;
+ }
+}
+
+/**
+ * Processed mouse clicks on the item list.
+ * @param {Event} event
+ */
+function handleClick(event)
+{
+ let item = treeView.getItemAt(event.clientX, event.clientY);
+ if (event.button == 0 && treeView.getColumnAt(event.clientX, event.clientY) == "state")
+ {
+ if (item.filter)
+ enableFilter(item.filter, item.filter.disabled);
+ event.preventDefault();
+ }
+ else if (event.button == 1)
+ {
+ openInTab(item, event);
+ event.preventDefault();
+ }
+}
+
+/**
+ * Processes double-clicks on the item list.
+ * @param {Event} event
+ */
+function handleDblClick(event)
+{
+ if (event.button != 0 || treeView.getColumnAt(event.clientX, event.clientY) == "state")
+ return;
+
+ doBlock();
+}
+
+/**
+ * Opens the item in a new tab.
+ */
+function openInTab(item, /**Event*/ event)
+{
+ let items = (item ? [item] : treeView.getAllSelectedItems());
+ for each (let item in items)
+ {
+ if (item && item.typeDescr != "ELEMHIDE")
+ UI.loadInBrowser(item.location, mainWin, event);
+ }
+}
+
+function doBlock() {
+ var item = treeView.getSelectedItem();
+ if (!item || item.typeDescr == "ELEMHIDE")
+ return;
+
+ var filter = null;
+ if (item.filter && !item.filter.disabled)
+ filter = item.filter;
+
+ if (filter && filter instanceof WhitelistFilter)
+ return;
+
+ openDialog("chrome://adblockplus/content/ui/composer.xul", "_blank", "chrome,centerscreen,resizable,dialog=no,dependent", item.nodes, item.orig);
+}
+
+function editFilter()
+{
+ var item = treeView.getSelectedItem();
+ if (treeView.data && !treeView.data.length)
+ item = treeView.getDummyTooltip();
+
+ if (!("filter" in item) || !item.filter)
+ return;
+
+ if (!("location") in item)
+ item.location = undefined
+
+ UI.openFiltersDialog(item.filter);
+}
+
+function enableFilter(filter, enable) {
+ filter.disabled = !enable;
+
+ treeView.boxObject.invalidate();
+}
+
+/**
+ * Edits the filter to disable it on a particular domain.
+ */
+function disableOnSite()
+{
+ let item = treeView.getSelectedItem();
+ let filter = item.filter;
+ if (!(filter instanceof ActiveFilter) || filter.disabled || !filter.subscriptions.length || filter.subscriptions.some(function(subscription) !(subscription instanceof SpecialSubscription)))
+ return;
+
+ let domain;
+ try {
+ domain = Utils.effectiveTLD.getBaseDomainFromHost(item.docDomain).toUpperCase();
+ }
+ catch (e)
+ {
+ return;
+ }
+
+ // Generate text for new filter that excludes current domain
+ let text = filter.text;
+ if (filter instanceof RegExpFilter)
+ {
+ let match = Filter.optionsRegExp.exec(text);
+ if (match)
+ {
+ let found = false;
+ let options = match[1].toUpperCase().split(",");
+ for (let i = 0; i < options.length; i++)
+ {
+ let match = /^DOMAIN=(.*)/.exec(options[i]);
+ if (match)
+ {
+ let domains = match[1].split("|").filter(function(d) d != domain && d != "~" + domain && (d.length <= domain.length || d.lastIndexOf("." + domain) != d.length - domain.length - 1));
+ domains.push("~" + domain);
+ options[i] = "DOMAIN=" + domains.join("|");
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ options.push("DOMAIN=~" + domain);
+
+ text = text.replace(Filter.optionsRegExp, "$" + options.join(",").toLowerCase());
+ }
+ else
+ text += "$domain=~" + domain.toLowerCase();
+ }
+ else if (filter instanceof ElemHideBase)
+ {
+ let match = /^([^#]+)(#.*)/.exec(text);
+ if (match)
+ {
+ let selector = match[2];
+ let domains = match[1].toUpperCase().split(",").filter(function(d) d != domain && (d.length <= domain.length || d != "~" + domain && d.lastIndexOf("." + domain) != d.length - domain.length - 1));
+ domains.push("~" + domain);
+ text = domains.join(",").toLowerCase() + selector;
+ }
+ else
+ text = "~" + domain.toLowerCase() + text;
+ }
+
+ if (text == filter.text)
+ return; // Just in case, shouldn't happen
+
+ // Insert new filter before the old one and remove the old one then
+ let newFilter = Filter.fromText(text);
+ if (newFilter.disabled && newFilter.subscriptions.length)
+ newFilter.disabled = false;
+ else if (!newFilter.subscriptions.length)
+ {
+ newFilter.disabled = false;
+ let subscription = filter.subscriptions.filter(function(s) s instanceof SpecialSubscription)[0];
+ if (subscription)
+ FilterStorage.addFilter(newFilter, subscription, subscription.filters.indexOf(filter));
+ }
+ FilterStorage.removeFilter(filter);
+
+ // Update display
+ for (let i = 0; i < treeView.allData.length; i++)
+ if (treeView.allData[i].filter == filter)
+ treeView.allData[i].filter = null;
+ treeView.boxObject.invalidate();
+}
+
+function copyToClipboard() {
+ var items = treeView.getAllSelectedItems();
+ if (!items.length)
+ return;
+
+ Utils.clipboardHelper.copyString(items.map(function(item) {return item.location}).join(IO.lineBreak));
+}
+
+function copyFilter() {
+ var items = treeView.getAllSelectedItems().filter(function(item) {return item.filter});
+ if (treeView.data && !treeView.data.length)
+ items = [treeView.getDummyTooltip()];
+
+ if (!items.length)
+ return;
+
+ Utils.clipboardHelper.copyString(items.map(function(item) {return item.filter.text}).join(IO.lineBreak));
+}
+
+function selectAll() {
+ treeView.selectAll();
+}
+
+// Saves sidebar's state before detaching/reattaching
+function saveState() {
+ var focused = document.commandDispatcher.focusedElement;
+ while (focused && (!focused.id || !("focus" in focused)))
+ focused = focused.parentNode;
+
+ // Calculate default position for the detached window
+ var boxObject = document.documentElement.boxObject;
+ var position = {screenX: boxObject.screenX, screenY: boxObject.screenY, width: boxObject.width, height: boxObject.height};
+
+ var params = {
+ filter: treeView.filter,
+ focus: (focused ? focused.id : null),
+ position: position
+ };
+ Utils.setParams(params);
+}
+
+// closes the sidebar
+function doClose()
+{
+ mainWin.document.getElementById("abp-command-sidebar").doCommand();
+}
+
+// detaches/reattaches the sidebar
+function detach(doDetach)
+{
+ saveState();
+
+ // Store variables locally, global variables will go away when we are closed
+ let myPrefs = Prefs;
+ let myMainWin = mainWin;
+
+ // Close sidebar and open detached window
+ myMainWin.document.getElementById("abp-command-sidebar").doCommand();
+ myPrefs.detachsidebar = doDetach;
+ myMainWin.document.getElementById("abp-command-sidebar").doCommand();
+}
+
+// Returns items size in the document if available
+function getItemSize(item)
+{
+ if (item.filter && !item.filter.disabled && item.filter instanceof BlockingFilter)
+ return null;
+
+ for each (let node in item.nodes)
+ {
+ if (node instanceof HTMLImageElement && (node.naturalWidth || node.naturalHeight))
+ return [node.naturalWidth, node.naturalHeight];
+ else if (node instanceof HTMLElement && (node.offsetWidth || node.offsetHeight))
+ return [node.offsetWidth, node.offsetHeight];
+ }
+ return null;
+}
+
+// Sort functions for the item list
+function sortByAddress(item1, item2) {
+ if (item1.location < item2.location)
+ return -1;
+ else if (item1.location > item2.location)
+ return 1;
+ else
+ return 0;
+}
+
+function sortByAddressDesc(item1, item2) {
+ return -sortByAddress(item1, item2);
+}
+
+function compareType(item1, item2) {
+ if (item1.localizedDescr < item2.localizedDescr)
+ return -1;
+ else if (item1.localizedDescr > item2.localizedDescr)
+ return 1;
+ else
+ return 0;
+}
+
+function compareFilter(item1, item2) {
+ var hasFilter1 = (item1.filter ? 1 : 0);
+ var hasFilter2 = (item2.filter ? 1 : 0);
+ if (hasFilter1 != hasFilter2)
+ return hasFilter1 - hasFilter2;
+ else if (hasFilter1 && item1.filter.text < item2.filter.text)
+ return -1;
+ else if (hasFilter1 && item1.filter.text > item2.filter.text)
+ return 1;
+ else
+ return 0;
+}
+
+function compareState(item1, item2) {
+ var state1 = (!item1.filter ? 0 : (item1.filter.disabled ? 1 : (item1.filter instanceof WhitelistFilter ? 2 : 3)));
+ var state2 = (!item2.filter ? 0 : (item2.filter.disabled ? 1 : (item2.filter instanceof WhitelistFilter ? 2 : 3)));
+ return state1 - state2;
+}
+
+function compareSize(item1, item2) {
+ var size1 = getItemSize(item1);
+ size1 = size1 ? size1[0] * size1[1] : 0;
+
+ var size2 = getItemSize(item2);
+ size2 = size2 ? size2[0] * size2[1] : 0;
+ return size1 - size2;
+}
+
+function compareDocDomain(item1, item2)
+{
+ if (item1.docDomain < item2.docDomain)
+ return -1;
+ else if (item1.docDomain > item2.docDomain)
+ return 1;
+ else if (item1.thirdParty && !item2.thirdParty)
+ return -1;
+ else if (!item1.thirdParty && item2.thirdParty)
+ return 1;
+ else
+ return 0;
+}
+
+function compareFilterSource(item1, item2)
+{
+ let subs1 = item1.filter ? item1.filter.subscriptions.map(function(s) s.title).join(", ") : "";
+ let subs2 = item2.filter ? item2.filter.subscriptions.map(function(s) s.title).join(", ") : "";
+ if (subs1 < subs2)
+ return -1;
+ else if (subs1 > subs2)
+ return 1;
+ else
+ return 0;
+}
+
+function createSortWithFallback(cmpFunc, fallbackFunc, desc) {
+ var factor = (desc ? -1 : 1);
+ return function(item1, item2) {
+ var ret = cmpFunc(item1, item2);
+ if (ret == 0)
+ return fallbackFunc(item1, item2);
+ else
+ return factor * ret;
+ }
+}
+
+// Item list's tree view object
+var treeView = {
+ //
+ // nsISupports implementation
+ //
+
+ QueryInterface: function(uuid) {
+ if (!uuid.equals(Ci.nsISupports) &&
+ !uuid.equals(Ci.nsITreeView))
+ {
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+
+ return this;
+ },
+
+ //
+ // nsITreeView implementation
+ //
+
+ selection: null,
+
+ setTree: function(boxObject) {
+ if (!boxObject)
+ return;
+ this.boxObject = boxObject;
+ this.itemsDummy = boxObject.treeBody.getAttribute("noitemslabel");
+ this.whitelistDummy = boxObject.treeBody.getAttribute("whitelistedlabel");
+ var stringAtoms = ["col-address", "col-type", "col-filter", "col-state", "col-size", "col-docDomain", "col-filterSource", "state-regular", "state-filtered", "state-whitelisted", "state-hidden", "state-hiddenexception"];
+ var boolAtoms = ["selected", "dummy", "filter-disabled"];
+ var atomService = Cc["@mozilla.org/atom-service;1"].getService(Ci.nsIAtomService);
+ this.atoms = {};
+ for each (let atom in stringAtoms)
+ this.atoms[atom] = atomService.getAtom(atom);
+ for each (let atom in boolAtoms)
+ {
+ this.atoms[atom + "-true"] = atomService.getAtom(atom + "-true");
+ this.atoms[atom + "-false"] = atomService.getAtom(atom + "-false");
+ }
+
+ this.itemsDummyTooltip = Utils.getString("no_blocking_suggestions");
+ this.whitelistDummyTooltip = Utils.getString("whitelisted_page");
+
+ // Check current sort direction
+ var cols = document.getElementsByTagName("treecol");
+ var sortDir = null;
+ for (let i = 0; i < cols.length; i++) {
+ var col = cols[i];
+ var dir = col.getAttribute("sortDirection");
+ if (dir && dir != "natural") {
+ this.sortColumn = col;
+ sortDir = dir;
+ }
+ }
+ if (!this.sortColumn)
+ {
+ let defaultSort = E("list").getAttribute("defaultSort");
+ let match = /^(\w+)\s+(ascending|descending)$/.exec(defaultSort);
+ if (match)
+ {
+ this.sortColumn = E(match[1]);
+ if (this.sortColumn)
+ {
+ sortDir = match[2];
+ this.sortColumn.setAttribute("sortDirection", sortDir);
+ }
+ }
+ }
+
+ if (sortDir)
+ {
+ this.sortProc = this.sortProcs[this.sortColumn.id + (sortDir == "descending" ? "Desc" : "")];
+ E("list").setAttribute("defaultSort", " ");
+ }
+
+ // Make sure to update the dummy row every two seconds
+ setInterval(function(view) {
+ if (!view.data || !view.data.length)
+ view.boxObject.invalidateRow(0);
+ }, 2000, this);
+
+ // Prevent a reference through closures
+ boxObject = null;
+ },
+ get rowCount() {
+ return (this.data && this.data.length ? this.data.length : 1);
+ },
+ getCellText: function(row, col) {
+ col = col.id;
+ if (col != "type" && col != "address" && col != "filter" && col != "size" && col != "docDomain" && col != "filterSource")
+ return "";
+ if (this.data && this.data.length) {
+ if (row >= this.data.length)
+ return "";
+ if (col == "type")
+ return this.data[row].localizedDescr;
+ else if (col == "filter")
+ return (this.data[row].filter ? this.data[row].filter.text : "");
+ else if (col == "size")
+ {
+ let size = getItemSize(this.data[row]);
+ return (size ? size.join(" x ") : "");
+ }
+ else if (col == "docDomain")
+ return this.data[row].docDomain + " " + (this.data[row].thirdParty ? docDomainThirdParty : docDomainFirstParty);
+ else if (col == "filterSource")
+ {
+ if (!this.data[row].filter)
+ return "";
+
+ return this.data[row].filter.subscriptions.filter(function(s) !s.disabled).map(function(s) s.title).join(", ");
+ }
+ else
+ return this.data[row].location;
+ }
+ else {
+ // Empty list, show dummy
+ if (row > 0 || (col != "address" && col != "filter"))
+ return "";
+ if (col == "filter") {
+ var filter = Policy.isWindowWhitelisted(window.content);
+ return filter ? filter.text : "";
+ }
+
+ return (Policy.isWindowWhitelisted(window.content) ? this.whitelistDummy : this.itemsDummy);
+ }
+ },
+
+ generateProperties: function(list, properties)
+ {
+ if (properties)
+ {
+ // Gecko 21 and below: we have an nsISupportsArray parameter, add atoms
+ // to that.
+ for (let i = 0; i < list.length; i++)
+ if (list[i] in this.atoms)
+ properties.AppendElement(this.atoms[list[i]]);
+ return null;
+ }
+ else
+ {
+ // Gecko 22+: no parameter, just return a string
+ return list.join(" ");
+ }
+ },
+
+ getColumnProperties: function(col, properties)
+ {
+ return this.generateProperties(["col-" + col.id], properties);
+ },
+
+ getRowProperties: function(row, properties)
+ {
+ if (row >= this.rowCount)
+ return "";
+
+ let list = [];
+ list.push("selected-" + this.selection.isSelected(row));
+
+ let state;
+ if (this.data && this.data.length) {
+ list.push("dummy-false");
+
+ let filter = this.data[row].filter;
+ if (filter)
+ list.push("filter-disabled-" + filter.disabled);
+
+ state = "state-regular";
+ if (filter && !filter.disabled)
+ {
+ if (filter instanceof WhitelistFilter)
+ state = "state-whitelisted";
+ else if (filter instanceof BlockingFilter)
+ state = "state-filtered";
+ else if (filter instanceof ElemHideFilter)
+ state = "state-hidden";
+ else if (filter instanceof ElemHideException)
+ state = "state-hiddenexception";
+ }
+ }
+ else {
+ list.push("dummy-true");
+
+ state = "state-filtered";
+ if (this.data && Policy.isWindowWhitelisted(window.content))
+ state = "state-whitelisted";
+ }
+ list.push(state);
+ return this.generateProperties(list, properties);
+ },
+
+ getCellProperties: function(row, col, properties)
+ {
+ return this.getRowProperties(row, properties) + " " + this.getColumnProperties(col, properties);
+ },
+
+ cycleHeader: function(col) {
+ col = col.id;
+
+ col = E(col);
+ if (!col)
+ return;
+
+ var cycle = {
+ natural: 'ascending',
+ ascending: 'descending',
+ descending: 'natural'
+ };
+
+ var curDirection = "natural";
+ if (this.sortColumn == col)
+ curDirection = col.getAttribute("sortDirection");
+ else if (this.sortColumn)
+ this.sortColumn.removeAttribute("sortDirection");
+
+ curDirection = cycle[curDirection];
+
+ if (curDirection == "natural")
+ this.sortProc = null;
+ else
+ this.sortProc = this.sortProcs[col.id + (curDirection == "descending" ? "Desc" : "")];
+
+ if (this.data)
+ this.refilter();
+
+ col.setAttribute("sortDirection", curDirection);
+ this.sortColumn = col;
+
+ this.boxObject.invalidate();
+ },
+
+ isSorted: function() {
+ return this.sortProc;
+ },
+
+ isContainer: function() {return false},
+ isContainerOpen: function() {return false},
+ isContainerEmpty: function() {return false},
+ getLevel: function() {return 0},
+ getParentIndex: function() {return -1},
+ hasNextSibling: function() {return false},
+ toggleOpenState: function() {},
+ canDrop: function() {return false},
+ drop: function() {},
+ getCellValue: function() {return null},
+ getProgressMode: function() {return null},
+ getImageSrc: function() {return null},
+ isSeparator: function() {return false},
+ isEditable: function() {return false},
+ cycleCell: function() {},
+ performAction: function() {},
+ performActionOnRow: function() {},
+ performActionOnCell: function() {},
+ selectionChanged: function() {},
+
+ //
+ // Custom properties and methods
+ //
+
+ boxObject: null,
+ atoms: null,
+ filter: "",
+ data: null,
+ allData: [],
+ dataMap: {__proto__: null},
+ sortColumn: null,
+ sortProc: null,
+ resortTimeout: null,
+ itemsDummy: null,
+ whitelistDummy: null,
+ itemsDummyTooltip: null,
+ whitelistDummyTooltip: null,
+ itemToSelect: null,
+
+ sortProcs: {
+ address: sortByAddress,
+ addressDesc: sortByAddressDesc,
+ type: createSortWithFallback(compareType, sortByAddress, false),
+ typeDesc: createSortWithFallback(compareType, sortByAddress, true),
+ filter: createSortWithFallback(compareFilter, sortByAddress, false),
+ filterDesc: createSortWithFallback(compareFilter, sortByAddress, true),
+ state: createSortWithFallback(compareState, sortByAddress, false),
+ stateDesc: createSortWithFallback(compareState, sortByAddress, true),
+ size: createSortWithFallback(compareSize, sortByAddress, false),
+ sizeDesc: createSortWithFallback(compareSize, sortByAddress, true),
+ docDomain: createSortWithFallback(compareDocDomain, sortByAddress, false),
+ docDomainDesc: createSortWithFallback(compareDocDomain, sortByAddress, true),
+ filterSource: createSortWithFallback(compareFilterSource, sortByAddress, false),
+ filterSourceDesc: createSortWithFallback(compareFilterSource, sortByAddress, true)
+ },
+ clearData: function(data) {
+ var oldRows = this.rowCount;
+ this.allData = [];
+ this.dataMap = {__proto__: null};
+ this.refilter();
+
+ this.boxObject.rowCountChanged(0, -oldRows);
+ this.boxObject.rowCountChanged(0, this.rowCount);
+ },
+
+ addItem: function(/**Node*/ node, /**RequestEntry*/ item, /**Boolean*/ scanComplete)
+ {
+ // Merge duplicate entries
+ let key = item.location + " " + item.type + " " + item.docDomain;
+ if (key in this.dataMap)
+ {
+ // We know this item already - take over the filter if any and be done with it
+ let existing = this.dataMap[key];
+ if (item.filter)
+ existing.filter = item.filter;
+
+ existing.nodes.push(node);
+ this.invalidateItem(existing);
+ return;
+ }
+
+ // Add new item to the list
+ // Store original item in orig property - reading out prototype is messed up in Gecko 1.9.2
+ item = {__proto__: item, orig: item, nodes: [node]};
+ this.allData.push(item);
+ this.dataMap[key] = item;
+
+ // Show disabled filters if no other filter applies
+ if (!item.filter)
+ item.filter = disabledMatcher.matchesAny(item.location, item.typeDescr, item.docDomain, item.thirdParty);
+
+ if (!this.matchesFilter(item))
+ return;
+
+ let index = -1;
+ if (this.sortProc && this.sortColumn && this.sortColumn.id == "size")
+ {
+ // Sorting by size requires accessing content document, and that's
+ // dangerous from a content policy (and we are likely called directly
+ // from a content policy call). Size data will be inaccurate anyway,
+ // delay sorting until later.
+ if (this.resortTimeout)
+ clearTimeout(this.resortTimeout);
+ this.resortTimeout = setTimeout(function(me)
+ {
+ if (me.sortProc)
+ me.data.sort(me.sortProc);
+ me.boxObject.invalidate();
+ }, 500, this);
+ }
+ else if (this.sortProc)
+ for (var i = 0; index < 0 && i < this.data.length; i++)
+ if (this.sortProc(item, this.data[i]) < 0)
+ index = i;
+
+ if (index >= 0)
+ this.data.splice(index, 0, item);
+ else {
+ this.data.push(item);
+ index = this.data.length - 1;
+ }
+
+ if (this.data.length == 1)
+ this.boxObject.invalidateRow(0);
+ else
+ this.boxObject.rowCountChanged(index, 1);
+
+ if (this.itemToSelect == key)
+ {
+ this.selection.select(index);
+ this.boxObject.ensureRowIsVisible(index);
+ this.itemToSelect = null;
+ }
+ else if (!scanComplete && this.selection.currentIndex >= 0) // Keep selected row visible while scanning
+ this.boxObject.ensureRowIsVisible(this.selection.currentIndex);
+ },
+
+ updateFilters: function()
+ {
+ for each (let item in this.allData)
+ {
+ if (item.filter instanceof RegExpFilter && item.filter.disabled)
+ delete item.filter;
+ if (!item.filter)
+ item.filter = disabledMatcher.matchesAny(item.location, item.typeDescr, item.docDomain, item.thirdParty);
+ }
+ this.refilter();
+ },
+
+ /**
+ * Updates the list after a filter or sorting change.
+ */
+ refilter: function()
+ {
+ if (this.resortTimeout)
+ clearTimeout(this.resortTimeout);
+
+ this.data = this.allData.filter(this.matchesFilter, this);
+
+ if (this.sortProc)
+ this.data.sort(this.sortProc);
+ },
+
+ /**
+ * Tests whether an item matches current list filter.
+ * @return {Boolean} true if the item should be shown
+ */
+ matchesFilter: function(item)
+ {
+ if (!this.filter)
+ return true;
+
+ return (item.location.toLowerCase().indexOf(this.filter) >= 0 ||
+ (item.filter && item.filter.text.toLowerCase().indexOf(this.filter) >= 0) ||
+ item.typeDescr.toLowerCase().indexOf(this.filter.replace(/-/g, "_")) >= 0 ||
+ item.localizedDescr.toLowerCase().indexOf(this.filter) >= 0 ||
+ (item.docDomain && item.docDomain.toLowerCase().indexOf(this.filter) >= 0) ||
+ (item.docDomain && item.thirdParty && docDomainThirdParty.toLowerCase().indexOf(this.filter) >= 0) ||
+ (item.docDomain && !item.thirdParty && docDomainFirstParty.toLowerCase().indexOf(this.filter) >= 0));
+ },
+
+ setFilter: function(filter) {
+ var oldRows = this.rowCount;
+
+ this.filter = filter.toLowerCase();
+ this.refilter();
+
+ var newRows = this.rowCount;
+ if (oldRows != newRows)
+ this.boxObject.rowCountChanged(oldRows < newRows ? oldRows : newRows, this.rowCount - oldRows);
+ this.boxObject.invalidate();
+ },
+
+ selectAll: function() {
+ this.selection.selectAll();
+ },
+
+ getSelectedItem: function() {
+ if (!this.data || this.selection.currentIndex < 0 || this.selection.currentIndex >= this.data.length)
+ return null;
+
+ return this.data[this.selection.currentIndex];
+ },
+
+ getAllSelectedItems: function() {
+ let result = [];
+ if (!this.data)
+ return result;
+
+ let numRanges = this.selection.getRangeCount();
+ for (let i = 0; i < numRanges; i++)
+ {
+ let min = {};
+ let max = {};
+ let range = this.selection.getRangeAt(i, min, max);
+ for (let j = min.value; j <= max.value; j++)
+ {
+ if (j >= 0 && j < this.data.length)
+ result.push(this.data[j]);
+ }
+ }
+ return result;
+ },
+
+ getItemAt: function(x, y)
+ {
+ if (!this.data)
+ return null;
+
+ var row = this.boxObject.getRowAt(x, y);
+ if (row < 0 || row >= this.data.length)
+ return null;
+
+ return this.data[row];
+ },
+
+ getColumnAt: function(x, y)
+ {
+ if (!this.data)
+ return null;
+
+ let col = {};
+ this.boxObject.getCellAt(x, y, {}, col, {});
+ return (col.value ? col.value.id : null);
+ },
+
+ getDummyTooltip: function() {
+ if (!this.data || this.data.length)
+ return null;
+
+ var filter = Policy.isWindowWhitelisted(window.content);
+ if (filter)
+ return {tooltip: this.whitelistDummyTooltip, filter: filter};
+ else
+ return {tooltip: this.itemsDummyTooltip};
+ },
+
+ invalidateItem: function(item)
+ {
+ let row = this.data.indexOf(item);
+ if (row >= 0)
+ this.boxObject.invalidateRow(row);
+ }
+}
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sidebar.xul b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sidebar.xul
new file mode 100644
index 0000000..180b3ec
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sidebar.xul
@@ -0,0 +1,139 @@
+<?xml version="1.0"?>
+
+<!--
+ - This file is part of Adblock Plus <http://adblockplus.org/>,
+ - Copyright (C) 2006-2014 Eyeo GmbH
+ -
+ - Adblock Plus is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU General Public License version 3 as
+ - published by the Free Software Foundation.
+ -
+ - Adblock Plus is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU General Public License for more details.
+ -
+ - You should have received a copy of the GNU General Public License
+ - along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://adblockplus/skin/sidebar.css" type="text/css"?>
+
+<!DOCTYPE page SYSTEM "chrome://adblockplus/locale/sidebar.dtd">
+
+<page
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="abp-sidebar"
+ onload="init()"
+ onunload="cleanUp()"
+ docDomainThirdParty="&docDomain.thirdParty;"
+ docDomainFirstParty="&docDomain.firstParty;">
+
+ <script type="application/x-javascript;version=1.7" src="utils.js"/>
+ <script type="application/x-javascript;version=1.7" src="sidebar.js"/>
+ <script type="application/x-javascript;version=1.7" src="flasher.js"/>
+
+ <keyset id="sidebarKeys">
+ <key id="block-key" keycode="VK_ENTER"/>
+ <key id="copy-key" modifiers="accel" key="C" command="copy-command"/>
+ <key id="selectAll-key" modifiers="accel" key="A" command="selectAll-command"/>
+ </keyset>
+
+ <commandset id="sidebarCommands">
+ <command id="copy-command" oncommand="copyToClipboard()" disabled="true"/>
+ <command id="selectAll-command" oncommand="selectAll()"/>
+ </commandset>
+
+ <popupset id="sidebarPopups">
+ <tooltip id="tooltip" orient="vertical" onpopupshowing="fillInTooltip(event);">
+ <description id="tooltipDummy"/>
+ <hbox id="tooltipPreviewBox" pack="start">
+ <image id="tooltipPreview" validate="never"/>
+ </hbox>
+ <grid>
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+ <row id="tooltipAddressRow" align="top">
+ <label value="&tooltip.address.label;"/>
+ <vbox id="tooltipAddress"/>
+ </row>
+ <row id="tooltipTypeRow">
+ <label value="&tooltip.type.label;"/>
+ <description id="tooltipType" filtered="&tooltip.type.blocked;" whitelisted="&tooltip.type.whitelisted;"/>
+ </row>
+ <row id="tooltipSizeRow">
+ <label value="&tooltip.size.label;"/>
+ <description id="tooltipSize"/>
+ </row>
+ <row id="tooltipDocDomainRow">
+ <label value="&tooltip.docDomain.label;"/>
+ <description id="tooltipDocDomain"/>
+ </row>
+ <row id="tooltipFilterRow" align="top">
+ <label value="&tooltip.filter.label;"/>
+ <vbox id="tooltipFilter" disabledText="&tooltip.filter.disabled;"/>
+ </row>
+ <row id="tooltipFilterSourceRow" align="top">
+ <label value="&tooltip.filterSource.label;"/>
+ <vbox id="tooltipFilterSource"/>
+ </row>
+ </rows>
+ </grid>
+ </tooltip>
+
+ <menupopup id="context" onpopupshowing="return fillInContext(event)">
+ <menuitem id="contextBlock" label="&context.block.label;…" oncommand="doBlock()" key="block-key"/>
+ <menuitem id="contextWhitelist" label="&context.whitelist.label;…" oncommand="doBlock()" key="block-key"/>
+ <menuitem id="contextEditFilter" label="&context.editfilter.label;…" oncommand="editFilter()"/>
+ <menuitem id="contextDisableFilter" labeltempl="&context.disablefilter.label;" oncommand="enableFilter(treeView.getSelectedItem().filter, false)"/>
+ <menuitem id="contextEnableFilter" labeltempl="&context.enablefilter.label;" oncommand="enableFilter(treeView.getSelectedItem().filter, true)"/>
+ <menuitem id="contextDisableOnSite" labeltempl="&context.disablefilteronsite.label;" oncommand="disableOnSite()"/>
+ <menuseparator id="contextOpenSep"/>
+ <menuitem id="contextOpen" label="&context.open.label;" oncommand="openInTab(null, event)"/>
+ <menuitem id="contextFlash" label="&context.flash.label;" oncommand="onSelectionChange()"/>
+ <menuitem id="contextCopy" label="&context.copy.label;" command="copy-command" key="copy-key"/>
+ <menuitem id="contextCopyFilter" label="&context.copyFilter.label;" oncommand="copyFilter()"/>
+ <menuseparator id="contextSelectSep"/>
+ <menuitem id="contextSelectAll" label="&context.selectAll.label;" command="selectAll-command" key="selectAll-key"/>
+ </menupopup>
+ </popupset>
+
+ <hbox>
+ <hbox align="center" flex="1">
+ <label value="&search.label;" control="searchField"/>
+ <textbox id="searchField" flex="1" type="search" oncommand="treeView.setFilter(this.value)"/>
+ </hbox>
+ <description id="detachButton" value="&detach.label;" onclick="detach(true)"/>
+ <description id="reattachButton" value="&reattach.label;" onclick="if (this.getAttribute('disabled') != 'true') detach(false)" hidden="true"/>
+ </hbox>
+
+ <tree id="list" context="context" flex="1" seltype="multiple" enableColumnDrag="true"
+ defaultSort="state descending" persist="defaultSort"
+ onkeypress="if (event.keyCode == event.DOM_VK_RETURN || event.keyCode == event.DOM_VK_ENTER) doBlock()">
+ <treecols>
+ <treecol id="address" label="&address.label;" flex="2" crop="center" persist="width ordinal sortDirection hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="filter" label="&filter.label;" flex="1" persist="width ordinal sortDirection hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="type" label="&type.label;" width="80" persist="width ordinal sortDirection hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="state" label="&state.label;" width="16" persist="width ordinal sortDirection hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="size" label="&size.label;" width="60" hidden="true" persist="width ordinal sortDirection hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="docDomain" label="&docDomain.label;" width="100" hidden="true" persist="width ordinal sortDirection hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="filterSource" label="&filterSource.label;" width="100" hidden="true" persist="width ordinal sortDirection hidden"/>
+ </treecols>
+ <treechildren id="treechildren"
+ tooltip="tooltip"
+ onclick="handleClick(event)"
+ ondblclick="handleDblClick(event)"
+ noitemslabel="&noitems.label;"
+ whitelistedlabel="&whitelisted.label;"/>
+ </tree>
+</page>
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sidebarDetached.xul b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sidebarDetached.xul
new file mode 100644
index 0000000..b7e117b
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/sidebarDetached.xul
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+
+<!--
+ - This file is part of Adblock Plus <http://adblockplus.org/>,
+ - Copyright (C) 2006-2014 Eyeo GmbH
+ -
+ - Adblock Plus is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU General Public License version 3 as
+ - published by the Free Software Foundation.
+ -
+ - Adblock Plus is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU General Public License for more details.
+ -
+ - You should have received a copy of the GNU General Public License
+ - along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+
+<!DOCTYPE page SYSTEM "chrome://adblockplus/locale/sidebar.dtd">
+
+<window
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ id="abpDetachedSidebar"
+ title="&detached.title;"
+ persist="screenX screenY width height sizemode"
+ onclose="document.getElementById('abp-command-sidebar').doCommand(); return false;">
+
+ <script type="application/x-javascript">
+ // Some people actually switch off browser.frames.enabled and are surprised
+ // that things stop working...
+ window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIWebNavigation)
+ .QueryInterface(Components.interfaces.nsIDocShell)
+ .allowSubframes = true;
+ </script>
+
+ <keyset>
+ <key keycode="VK_ESCAPE" command="command-close"/>
+ <key modifiers="accel" key="w" command="command-close"/>
+ </keyset>
+
+ <commandset>
+ <command id="command-close" oncommand="document.getElementById('sidebarFrame').contentWindow.doClose()"/>
+ </commandset>
+
+ <iframe src="sidebar.xul" id="sidebarFrame" flex="1"/>
+</window>
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/subscriptionSelection.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/subscriptionSelection.js
new file mode 100644
index 0000000..3fdcc18
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/subscriptionSelection.js
@@ -0,0 +1,308 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+let subscriptionListLoading = false;
+
+function init()
+{
+ if (window.arguments && window.arguments.length && window.arguments[0])
+ {
+ let source = window.arguments[0];
+ setCustomSubscription(source.title, source.url,
+ source.mainSubscriptionTitle, source.mainSubscriptionURL);
+
+ E("all-subscriptions-container").hidden = true;
+ E("fromWebText").hidden = false;
+ }
+ else
+ loadSubscriptionList();
+}
+
+function updateSubscriptionInfo()
+{
+ let selectedSubscription = E("all-subscriptions").selectedItem;
+
+ E("subscriptionInfo").setAttribute("invisible", !selectedSubscription);
+ if (selectedSubscription)
+ {
+ let url = selectedSubscription.getAttribute("_url");
+ let homePage = selectedSubscription.getAttribute("_homepage")
+
+ let viewLink = E("view-list");
+ viewLink.setAttribute("_url", url);
+ viewLink.setAttribute("tooltiptext", url);
+
+ let homePageLink = E("visit-homepage");
+ homePageLink.hidden = !homePage;
+ if (homePage)
+ {
+ homePageLink.setAttribute("_url", homePage);
+ homePageLink.setAttribute("tooltiptext", homePage);
+ }
+ }
+}
+
+function reloadSubscriptionList()
+{
+ subscriptionListLoading = false;
+ loadSubscriptionList();
+}
+
+function loadSubscriptionList()
+{
+ if (subscriptionListLoading)
+ return;
+
+ E("all-subscriptions-container").selectedIndex = 0;
+ E("all-subscriptions-loading").hidden = false;
+
+ let request = new XMLHttpRequest();
+ let errorHandler = function()
+ {
+ E("all-subscriptions-container").selectedIndex = 2;
+ E("all-subscriptions-loading").hidden = true;
+ };
+ let successHandler = function()
+ {
+ if (!request.responseXML || request.responseXML.documentElement.localName != "subscriptions")
+ {
+ errorHandler();
+ return;
+ }
+
+ try
+ {
+ processSubscriptionList(request.responseXML);
+ E("all-subscriptions").selectedIndex = 0;
+ E("all-subscriptions").focus();
+ }
+ catch (e)
+ {
+ Cu.reportError(e);
+ errorHandler();
+ }
+ };
+
+ request.open("GET", Prefs.subscriptions_listurl);
+ request.addEventListener("error", errorHandler, false);
+ request.addEventListener("load", successHandler, false);
+ request.send(null);
+
+ subscriptionListLoading = true;
+}
+
+function processSubscriptionList(doc)
+{
+ let list = E("all-subscriptions");
+ while (list.firstChild)
+ list.removeChild(list.firstChild);
+
+ addSubscriptions(list, doc.documentElement, 0, null, null);
+ E("all-subscriptions-container").selectedIndex = 1;
+ E("all-subscriptions-loading").hidden = true;
+}
+
+function addSubscriptions(list, parent, level, parentTitle, parentURL)
+{
+ for (let i = 0; i < parent.childNodes.length; i++)
+ {
+ let node = parent.childNodes[i];
+ if (node.nodeType != Node.ELEMENT_NODE || node.localName != "subscription")
+ continue;
+
+ if (node.getAttribute("type") != "ads" || node.getAttribute("deprecated") == "true")
+ continue;
+
+ let variants = node.getElementsByTagName("variants");
+ if (!variants.length || !variants[0].childNodes.length)
+ continue;
+ variants = variants[0].childNodes;
+
+ let isFirst = true;
+ let mainTitle = null;
+ let mainURL = null;
+ for (let j = 0; j < variants.length; j++)
+ {
+ let variant = variants[j];
+ if (variant.nodeType != Node.ELEMENT_NODE || variant.localName != "variant")
+ continue;
+
+ let item = document.createElement("richlistitem");
+ item.setAttribute("_title", variant.getAttribute("title"));
+ item.setAttribute("_url", variant.getAttribute("url"));
+ if (parentTitle && parentURL && variant.getAttribute("complete") != "true")
+ {
+ item.setAttribute("_supplementForTitle", parentTitle);
+ item.setAttribute("_supplementForURL", parentURL);
+ }
+ item.setAttribute("tooltiptext", variant.getAttribute("url"));
+ item.setAttribute("_homepage", node.getAttribute("homepage"));
+
+ let title = document.createElement("description");
+ if (isFirst)
+ {
+ if (Utils.checkLocalePrefixMatch(node.getAttribute("prefixes")))
+ title.setAttribute("class", "subscriptionTitle localeMatch");
+ else
+ title.setAttribute("class", "subscriptionTitle");
+ title.textContent = node.getAttribute("title") + " (" + node.getAttribute("specialization") + ")";
+ mainTitle = variant.getAttribute("title");
+ mainURL = variant.getAttribute("url");
+ isFirst = false;
+ }
+ title.setAttribute("flex", "1");
+ title.style.marginLeft = (20 * level) + "px";
+ item.appendChild(title);
+
+ let variantTitle = document.createElement("description");
+ variantTitle.setAttribute("class", "variant");
+ variantTitle.textContent = variant.getAttribute("title");
+ variantTitle.setAttribute("crop", "end");
+ item.appendChild(variantTitle);
+
+ list.appendChild(item);
+ }
+
+ let supplements = node.getElementsByTagName("supplements");
+ if (supplements.length)
+ addSubscriptions(list, supplements[0], level + 1, mainTitle, mainURL);
+ }
+}
+
+function onSelectionChange()
+{
+ let selectedItem = E("all-subscriptions").selectedItem;
+ if (!selectedItem)
+ return;
+
+ setCustomSubscription(selectedItem.getAttribute("_title"), selectedItem.getAttribute("_url"),
+ selectedItem.getAttribute("_supplementForTitle"), selectedItem.getAttribute("_supplementForURL"));
+
+ updateSubscriptionInfo();
+}
+
+function setCustomSubscription(title, url, mainSubscriptionTitle, mainSubscriptionURL)
+{
+ E("title").value = title;
+ E("location").value = url;
+
+ let messageElement = E("supplementMessage");
+ let addMainCheckbox = E("addMainSubscription");
+ if (mainSubscriptionURL && !hasSubscription(mainSubscriptionURL))
+ {
+ messageElement.removeAttribute("invisible");
+ addMainCheckbox.removeAttribute("invisible");
+
+ let [, beforeLink, afterLink] = /(.*)\?1\?(.*)/.exec(messageElement.getAttribute("_textTemplate")) || [null, messageElement.getAttribute("_textTemplate"), ""];
+ while (messageElement.firstChild)
+ messageElement.removeChild(messageElement.firstChild);
+ messageElement.appendChild(document.createTextNode(beforeLink));
+ let link = document.createElement("label");
+ link.className = "text-link";
+ link.setAttribute("tooltiptext", mainSubscriptionURL);
+ link.addEventListener("click", function() UI.loadInBrowser(mainSubscriptionURL), false);
+ link.textContent = mainSubscriptionTitle;
+ messageElement.appendChild(link);
+ messageElement.appendChild(document.createTextNode(afterLink));
+
+ addMainCheckbox.value = mainSubscriptionURL;
+ addMainCheckbox.setAttribute("_mainSubscriptionTitle", mainSubscriptionTitle)
+ let [label, accesskey] = Utils.splitLabel(addMainCheckbox.getAttribute("_labelTemplate"));
+ addMainCheckbox.label = label.replace(/\?1\?/g, mainSubscriptionTitle);
+ addMainCheckbox.accessKey = accesskey;
+ }
+ else
+ {
+ messageElement.setAttribute("invisible", "true");
+ addMainCheckbox.setAttribute("invisible", "true");
+ }
+}
+
+function validateURL(url)
+{
+ if (!url)
+ return null;
+ url = url.replace(/^\s+/, "").replace(/\s+$/, "");
+
+ // Is this a file path?
+ try {
+ let file = new FileUtils.File(url);
+ return Services.io.newFileURI(file).spec;
+ } catch (e) {}
+
+ // Is this a valid URL?
+ let uri = Utils.makeURI(url);
+ if (uri)
+ return uri.spec;
+
+ return null;
+}
+
+function addSubscription()
+{
+ let url = E("location").value;
+ url = validateURL(url);
+ if (!url)
+ {
+ Utils.alert(window, Utils.getString("subscription_invalid_location"));
+ E("location").focus();
+ return false;
+ }
+
+ let title = E("title").value.replace(/^\s+/, "").replace(/\s+$/, "");
+ if (!title)
+ title = url;
+
+ doAddSubscription(url, title);
+
+ let addMainCheckbox = E("addMainSubscription")
+ if (addMainCheckbox.getAttribute("invisible") != "true" && addMainCheckbox.checked)
+ {
+ let mainSubscriptionTitle = addMainCheckbox.getAttribute("_mainSubscriptionTitle");
+ let mainSubscriptionURL = validateURL(addMainCheckbox.value);
+ if (mainSubscriptionURL)
+ doAddSubscription(mainSubscriptionURL, mainSubscriptionTitle);
+ }
+
+ return true;
+}
+
+/**
+ * Adds a new subscription to the list.
+ */
+function doAddSubscription(/**String*/ url, /**String*/ title)
+{
+ let subscription = Subscription.fromURL(url);
+ if (!subscription)
+ return;
+
+ FilterStorage.addSubscription(subscription);
+
+ subscription.disabled = false;
+ subscription.title = title;
+
+ if (subscription instanceof DownloadableSubscription && !subscription.lastDownload)
+ Synchronizer.execute(subscription);
+}
+
+function hasSubscription(url)
+{
+ return FilterStorage.subscriptions.some(function(subscription) subscription instanceof DownloadableSubscription && subscription.url == url);
+}
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/subscriptionSelection.xul b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/subscriptionSelection.xul
new file mode 100644
index 0000000..17f1854
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/subscriptionSelection.xul
@@ -0,0 +1,75 @@
+<?xml version="1.0"?>
+
+<!--
+ - This file is part of Adblock Plus <http://adblockplus.org/>,
+ - Copyright (C) 2006-2014 Eyeo GmbH
+ -
+ - Adblock Plus is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU General Public License version 3 as
+ - published by the Free Software Foundation.
+ -
+ - Adblock Plus is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU General Public License for more details.
+ -
+ - You should have received a copy of the GNU General Public License
+ - along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://adblockplus/skin/subscriptionSelection.css" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://adblockplus/locale/subscriptionSelection.dtd">
+
+<dialog
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ buttons="accept,cancel"
+ buttonlabelaccept="&addSubscription.label;"
+ title="&dialog.title;"
+ id="abpSubscriptionSelection"
+ windowtype="abp:subscriptionSelection"
+ onload="init();"
+ ondialogaccept="return addSubscription();">
+
+ <script type="application/x-javascript;version=1.7" src="utils.js"/>
+ <script type="application/x-javascript;version=1.7" src="subscriptionSelection.js"/>
+
+ <deck id="all-subscriptions-container" selectedIndex="0" flex="1">
+ <vbox pack="center">
+ <progressmeter id="all-subscriptions-loading" mode="undetermined"/>
+ </vbox>
+ <vbox>
+ <richlistbox id="all-subscriptions" onselect="onSelectionChange()" flex="1"/>
+ <hbox id="subscriptionInfo" invisible="true">
+ <label id="view-list" class="text-link" value="&viewList.label;" onclick="UI.loadInBrowser(this.getAttribute('_url'))"/>
+ <spacer flex="1"/>
+ <label id="visit-homepage" class="text-link" value="&visitHomepage.label;" onclick="UI.loadInBrowser(this.getAttribute('_url'))"/>
+ </hbox>
+ </vbox>
+ <vbox pack="center" align="center">
+ <description value="&list.download.failed;"/>
+ <hbox>
+ <button label="&list.download.retry;" oncommand="reloadSubscriptionList()"/>
+ <button label="&list.download.website;" oncommand="UI.loadDocLink('subscriptions')"/>
+ </hbox>
+ </vbox>
+ </deck>
+
+ <description id="fromWebText" hidden="true">&fromWeb.description;</description>
+
+ <groupbox id="differentSubscription">
+ <label value="&title.label;" control="title"/>
+ <textbox id="title"/>
+
+ <label value="&location.label;" control="location"/>
+ <textbox id="location"/>
+ </groupbox>
+
+ <description id="supplementMessage" invisible="true" _textTemplate="&supplementMessage;">
+ &supplementMessage;
+ <label class="text-link" oncommand="">dummy dummy dummy dummy dummy dummy dummy dummy dummy dummy</label>
+ </description>
+ <checkbox id="addMainSubscription" invisible="true" checked="true" _labelTemplate="&addMain.label;" label="&addMain.label;"/>
+
+</dialog>
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/subscriptions.xml b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/subscriptions.xml
new file mode 100644
index 0000000..6a05d5e
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/subscriptions.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0"?>
+
+<!--
+ - This file is part of the Adblock Plus web scripts,
+ - Copyright (C) 2006-2014 Eyeo GmbH
+ -
+ - Adblock Plus is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU General Public License version 3 as
+ - published by the Free Software Foundation.
+ -
+ - Adblock Plus is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU General Public License for more details.
+ -
+ - You should have received a copy of the GNU General Public License
+ - along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<subscriptions>
+ <subscription title="EasyList"
+ specialization="English"
+ url="https://easylist-downloads.adblockplus.org/easylist.txt"
+ homepage="https://easylist.adblockplus.org/"
+ prefixes="en"
+ author="fanboy, MonztA, Famlam, Khrin"/>
+ <subscription title="ABPindo+EasyList"
+ specialization="Bahasa Indonesia"
+ url="https://easylist-downloads.adblockplus.org/abpindo+easylist.txt"
+ homepage="http://abpindo.blogspot.com/"
+ prefixes="id"
+ author="heradhis"/>
+ <subscription title="Bulgarian list+EasyList"
+ specialization="български"
+ url="https://easylist-downloads.adblockplus.org/bulgarian_list+easylist.txt"
+ homepage="http://stanev.org/abp/"
+ prefixes="bg"
+ author="Александър Станев"/>
+ <subscription title="EasyList China+EasyList"
+ specialization="中文"
+ url="https://easylist-downloads.adblockplus.org/easylistchina+easylist.txt"
+ homepage="http://abpchina.org/forum/"
+ prefixes="zh"
+ author="John, Li, Jiefei"/>
+ <subscription title="EasyList Czech and Slovak+EasyList"
+ specialization="čeština, slovenčina"
+ url="https://easylist-downloads.adblockplus.org/easylistczechslovak+easylist.txt"
+ homepage="http://adblocksk.tk/"
+ prefixes="cs,sk"
+ author="tomasko126"/>
+ <subscription title="EasyList Dutch+EasyList"
+ specialization="Nederlands"
+ url="https://easylist-downloads.adblockplus.org/easylistdutch+easylist.txt"
+ homepage="https://easylist.adblockplus.org/"
+ prefixes="nl"
+ author="Famlam"/>
+ <subscription title="EasyList Germany+EasyList"
+ specialization="Deutsch"
+ url="https://easylist-downloads.adblockplus.org/easylistgermany+easylist.txt"
+ homepage="https://easylist.adblockplus.org/"
+ prefixes="de"
+ author="MonztA, Famlam"/>
+ <subscription title="EasyList Hebrew+EasyList"
+ specialization="עברית"
+ url="https://easylist-downloads.adblockplus.org/israellist+easylist.txt"
+ homepage="https://github.com/AdBlockPlusIsrael/EasyListHebrew"
+ prefixes="he"
+ author="BsT"/>
+ <subscription title="EasyList Italy+EasyList"
+ specialization="italiano"
+ url="https://easylist-downloads.adblockplus.org/easylistitaly+easylist.txt"
+ homepage="https://easylist.adblockplus.org/"
+ prefixes="it"
+ author="Khrin"/>
+ <subscription title="EasyList Lithuania+EasyList"
+ specialization="lietuvių kalba"
+ url="https://easylist-downloads.adblockplus.org/easylistlithuania+easylist.txt"
+ homepage="http://margevicius.lt/"
+ prefixes="lt"
+ author="Algimantas Margevičius"/>
+ <subscription title="Latvian List+EasyList"
+ specialization="latviešu valoda"
+ url="https://easylist-downloads.adblockplus.org/latvianlist+easylist.txt"
+ homepage="http://latvian-list.site11.com/"
+ prefixes="lv"
+ author="anonymous74100"/>
+ <subscription title="Liste AR+Liste FR+EasyList"
+ specialization="العربية"
+ url="https://easylist-downloads.adblockplus.org/liste_ar+liste_fr+easylist.txt"
+ homepage="https://code.google.com/p/liste-ar-adblock/"
+ prefixes="ar"
+ author="smed79"/>
+ <subscription title="Liste FR+EasyList"
+ specialization="français"
+ url="https://easylist-downloads.adblockplus.org/liste_fr+easylist.txt"
+ homepage="http://adblock-listefr.com/"
+ prefixes="fr"
+ author="Lian, Crits, smed79"/>
+ <subscription title="ROList+EasyList"
+ specialization="românesc"
+ url="https://easylist-downloads.adblockplus.org/rolist+easylist.txt"
+ homepage="http://www.zoso.ro/rolist"
+ prefixes="ro"
+ author="MenetZ, Zoso"/>
+ <subscription title="RuAdList+EasyList"
+ specialization="русский, українська"
+ url="https://easylist-downloads.adblockplus.org/ruadlist+easylist.txt"
+ homepage="https://code.google.com/p/ruadlist/"
+ prefixes="ru,uk"
+ author="Lain_13"/>
+ <subscription title="Wiltteri+EasyList"
+ specialization="suomi"
+ url="https://easylist-downloads.adblockplus.org/wiltteri+easylist.txt"
+ homepage="http://wiltteri.net/"
+ prefixes="fi"
+ author="None"/>
+ </subscriptions> \ No newline at end of file
diff --git a/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/utils.js b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/utils.js
new file mode 100644
index 0000000..49f6e7f
--- /dev/null
+++ b/helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui/utils.js
@@ -0,0 +1,66 @@
+/*
+ * This file is part of Adblock Plus <http://adblockplus.org/>,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+/**
+ * Imports a module from Adblock Plus core.
+ */
+function require(/**String*/ module)
+{
+ var result = {};
+ result.wrappedJSObject = result;
+ Services.obs.notifyObservers(result, "adblockplus-require", module);
+ return result.exports;
+}
+
+var {Policy} = require("contentPolicy");
+var {Filter, InvalidFilter, CommentFilter, ActiveFilter, RegExpFilter,
+ BlockingFilter, WhitelistFilter, ElemHideBase, ElemHideFilter, ElemHideException} = require("filterClasses");
+var {FilterNotifier} = require("filterNotifier");
+var {FilterStorage, PrivateBrowsing} = require("filterStorage");
+var {IO} = require("io");
+var {defaultMatcher, Matcher, CombinedMatcher} = require("matcher");
+var {Prefs} = require("prefs");
+var {RequestNotifier} = require("requestNotifier");
+var {Subscription, SpecialSubscription, RegularSubscription,
+ ExternalSubscription, DownloadableSubscription} = require("subscriptionClasses");
+var {Synchronizer} = require("synchronizer");
+var {UI} = require("ui");
+var {Utils} = require("utils");
+
+/**
+ * Shortcut for document.getElementById(id)
+ */
+function E(id)
+{
+ return document.getElementById(id);
+}
+
+/**
+ * Split up all labels into the label and access key portions.
+ */
+document.addEventListener("DOMContentLoaded", function splitAllLabelsHandler()
+{
+ document.removeEventListener("DOMContentLoaded", splitAllLabelsHandler, false);
+ Utils.splitAllLabels(document);
+}, false);