/*
* This file is part of Adblock Plus ,
* 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 .
*/
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();
}