diff options
Diffstat (limited to 'helpers/DATA/firefox/gnu/extensions/{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}/chrome/content/ui')
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">®exp.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')); }"/>,  + </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;"/>  + <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> +  (<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? &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="¬ification.button.yes;"/> + <button id="abp-notification-no" label="¬ification.button.no;"/> + <toolbarbutton id="abp-notification-close" label="¬ification.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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """); +} 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="©Link.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); |