Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: chrome/content/ui/ehh-composer.js

Issue 6525035187535872: issue #390 - Integrate parts of Element Hiding Helper functionality into Adblock Plus (Closed)
Patch Set: Created July 18, 2014, 12:54 p.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | chrome/content/ui/ehh-composer.xul » ('j') | chrome/locale/en-US/ehh-composer.dtd » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/content/ui/ehh-composer.js
===================================================================
new file mode 100644
--- /dev/null
+++ b/chrome/content/ui/ehh-composer.js
@@ -0,0 +1,645 @@
+/*
+ * This Source Code is subject to the terms of the Mozilla Public License
+ * version 2.0 (the "License"). You can obtain a copy of the License at
+ * http://mozilla.org/MPL/2.0/.
+ */
+
+let {Prefs} = require("prefs");
+let {FilterStorage} = require("filterStorage");
+let {Filter} = require("filterClasses");
+
+let domainData;
+let nodeData;
+let selectedNode = null;
+let advancedMode = false;
+let treeView = null;
+let stylesheetData;
+let previewStyle = null;
+let doc;
+
+/*******************
+ * NodeData object *
+ *******************/
+
+function NodeData(node, parentNode) {
+ this.tagName = {value: node.tagName, checked: false};
+
+ if (typeof parentNode == "undefined")
+ parentNode = (node.parentNode && node.parentNode.nodeType == node.ELEMENT_NODE ? new NodeData(node.parentNode) : null);
+ this.parentNode = parentNode;
+
+ var prevSibling = node.previousSibling;
+ while (prevSibling && prevSibling.nodeType != node.ELEMENT_NODE)
+ prevSibling = prevSibling.previousSibling;
+ this.prevSibling = (prevSibling ? new NodeData(prevSibling, this.parentNode) : null);
+
+ if (parentNode && !prevSibling)
+ this.firstChild = {checked: false};
+
+ var nextSibling = node.nextSibling;
+ while (nextSibling && nextSibling.nodeType != node.ELEMENT_NODE)
+ nextSibling = nextSibling.nextSibling;
+ if (parentNode && !nextSibling)
+ this.lastChild = {checked: false};
+
+ this.attributes = [];
+ for (var i = 0; i < node.attributes.length; i++) {
+ var attribute = node.attributes[i];
+ var data = {name: attribute.name, value: attribute.value, selected: attribute.value, checked: false};
+ if (data.name == "id" || data.name == "class")
+ this.attributes.unshift(data);
+ else
+ this.attributes.push(data);
+ }
+
+ if (this.attributes.length >= 2 && this.attributes[1].name == "id") {
+ // Make sure ID attribute comes first
+ var tmp = this.attributes[1];
+ this.attributes[1] = this.attributes[0];
+ this.attributes[0] = tmp;
+ }
+
+ this.customCSS = {selected: "", checked: false};
+}
+
+/*******************
+ * TreeView object *
+ *******************/
+
+function TreeView(tree) {
+ var origView = tree.view;
+ this.getRowProperties = TreeView_getRowProperties;
+ this.getCellProperties = TreeView_getCellProperties;
+
+ createQIProxy(this, origView);
+
+ for (var key in origView) {
+ if (this.hasOwnProperty(key))
+ continue;
+
+ createPropertyProxy(this, origView, key);
+ }
+
+ tree.view = this;
+}
+
+function createQIProxy(obj, orig) {
+ obj.QueryInterface = function(iid) {
+ var impl = orig.QueryInterface(iid);
+ if (impl != orig)
+ throw Cr.NS_ERROR_NO_INTERFACE;
+
+ return obj;
+ };
+}
+
+function createPropertyProxy(obj, orig, key) {
+ if (typeof orig[key] == "function") {
+ obj[key] = function() {
+ return orig[key].apply(orig, arguments);
+ };
+ }
+ else {
+ obj.__defineGetter__(key, function() {
+ return orig[key];
+ });
+ obj.__defineSetter__(key, function(value) {
+ orig[key] = value;
+ });
+ }
+}
+
+function TreeView_getRowProperties(row) {
+ let properties = "selected-" + this.selection.isSelected(row);
+
+ var item = this.getItemAtIndex(row);
+ if (item && (item.nodeData.expression != "*" || item.nodeData == nodeData))
+ properties += " anchor";
+
+ return properties;
+}
+
+function TreeView_getCellProperties(row, col) {
+ this.getRowProperties(row);
+}
+
+/*********************
+ * General functions *
+ *********************/
+
+function init() {
+ var element = window.arguments[0];
+ doc = element.ownerDocument;
+ var wnd = doc.defaultView;
+
+ // Check whether element hiding group is disabled
+ let subscription = FilterStorage.knownSubscriptions["~eh~"];
saroyanm 2014/07/18 13:14:41 In Element Hiding Helper we used -> AdblockPlus.ge
+ if (subscription && subscription.disabled)
+ {
+ let warning = document.getElementById("groupDisabledWarning");
+ if (/\?1\?/.test(warning.textContent))
+ warning.textContent = warning.textContent.replace(/\?1\?/g, subscription.title);
+ warning.hidden = false;
+ }
+
+ nodeData = new NodeData(element);
+ nodeData.tagName.checked = true;
+ if (nodeData.attributes.length > 0)
+ {
+ let maxLen = 0;
+ let bestAttr = null;
+ for (let i = 0; i < nodeData.attributes.length; i++)
+ {
+ let len = nodeData.attributes[i].value.length;
+ if ((nodeData.attributes[i].name == "id" || nodeData.attributes[i].name == "class") && len)
+ {
+ len = 0x7FFFFFFF;
+ nodeData.tagName.checked = false;
+ }
+ if (len > maxLen)
+ {
+ maxLen = len;
+ bestAttr = nodeData.attributes[i];
+ }
+ }
+ if (bestAttr)
+ {
+ bestAttr.selected = bestAttr.value;
+ bestAttr.checked = true;
+ }
+ }
+
+ let domain = wnd.location.hostname;
+ let selectedDomain;
+ switch (Prefs.ehh_composer_defaultDomain)
+ {
+ case 0:
+ selectedDomain = "";
+ break;
+ case 1:
+ try
+ {
+ // EffectiveTLDService will throw for IP addresses, just go to the next case then
+ let effectiveTLD = Cc["@mozilla.org/network/effective-tld-service;1"].getService(Ci.nsIEffectiveTLDService);
+ selectedDomain = effectiveTLD.getPublicSuffixFromHost(domain);
+ break;
+ } catch (e) {}
+ case 2:
+ try
+ {
+ // EffectiveTLDService will throw for IP addresses, just go to the next case then
+ let effectiveTLD = Cc["@mozilla.org/network/effective-tld-service;1"].getService(Ci.nsIEffectiveTLDService);
+ selectedDomain = effectiveTLD.getBaseDomainFromHost(domain);
+ break;
+ } catch (e) {}
+ case 3:
+ selectedDomain = domain.replace(/^www\./, "");
+ break;
+ default:
+ selectedDomain = domain;
+ break;
+ }
+ domainData = {value: domain, selected: selectedDomain};
+
+ fillDomains(domainData);
+ fillNodes(nodeData);
+ setAdvancedMode(document.documentElement.getAttribute("advancedMode") == "true");
+ updateExpression();
+
+ setTimeout(function() {
+ document.getElementById("domainGroup").selectedItem.focus();
+ if (document.getElementById("preview").checked)
+ togglePreview(true);
+ }, 0);
+}
+
+function updateExpression()
+{
+ var curNode = nodeData;
+
+ function escapeName(name)
+ {
+ return name.replace(/([^\w\-])/g, "\\$1")
+ .replace(/\\([\{\}])/g, escapeChar);
+ }
+
+ while (curNode)
+ {
+ let expression = (curNode.tagName.checked ? curNode.tagName.value : "");
+
+ for (var i = 0; i < curNode.attributes.length; i++)
+ {
+ var attr = curNode.attributes[i];
+
+ if (attr.checked) {
+ var escapedName = escapeName(attr.name);
+ if (attr.selected != "")
+ {
+ var op = "*=";
+ if (attr.selected == attr.value)
+ op = "=";
+ else if (attr.value.substr(0, attr.selected.length) == attr.selected)
+ op = "^=";
+ else if (attr.value.substr(attr.value.length - attr.selected.length) == attr.selected)
+ op = "$=";
+
+ let useFallback = false;
+ if (attr.name == "id" && op == "=")
+ expression += "#" + escapeName(attr.selected).replace(/^([^a-zA-Z\\])/, escapeChar).replace(/\\(\s)$/, escapeChar);
+ else if (attr.name == "class" && /\S/.test(attr.selected))
+ {
+ let knownClasses = {};
+ for each (let cls in attr.value.split(/\s+/))
+ knownClasses[cls] = true;
+
+ let classes = attr.selected.split(/\s+/).filter(function(cls) cls != "");
+ if (classes.every(function(cls) knownClasses.hasOwnProperty(cls)))
+ expression += "." + classes.map(escapeName).join(".");
+ else
+ useFallback = true;
+ }
+ else
+ useFallback = true;
+
+ if (useFallback)
+ {
+ var escapedValue = attr.selected.replace(/(["\\])/g, '\\$1')
+ .replace(/([\{\}])/g, escapeChar)
+ .replace(/([^\S ])/g, escapeChar);
+ expression += "[" + escapedName + op + '"' + escapedValue + '"' + "]";
+ }
+ }
+ else
+ {
+ expression += "[" + escapedName + "]";
+ }
+ }
+ }
+
+ if (curNode.customCSS.checked && curNode.customCSS.selected != "")
+ {
+ expression += curNode.customCSS.selected
+ .replace(/([\{\}])/g, escapeChar)
+ .replace(/([^\S ])/g, escapeChar);
+ }
+
+ if ("firstChild" in curNode && curNode.firstChild.checked)
+ expression += ":first-child";
+ if ("lastChild" in curNode && curNode.lastChild.checked)
+ expression += ":last-child";
+
+ if (expression == "")
+ expression = "*";
+
+ curNode.expression = expression;
+
+ if (curNode.prevSibling)
+ curNode = curNode.prevSibling;
+ else
+ curNode = curNode.parentNode;
+ }
+
+ let expression = nodeData.expression;
+
+ var isParent = false;
+ var isRemoteParent = false;
+ var siblingCount = 0;
+ var firstRun = true;
+
+ var curData = nodeData;
+ while (curData) {
+ if (!firstRun && curData.expression != "*") {
+ var parentRelation = "";
+ if (isRemoteParent)
+ parentRelation = " ";
+ else if (isParent)
+ parentRelation = " > ";
+
+ var siblingRelation = "";
+ for (var i = 0; i < siblingCount; i++)
+ siblingRelation += "* + ";
+ siblingRelation = siblingRelation.replace(/^\*/, '');
+
+ var relation;
+ if (parentRelation != "" && siblingRelation != "")
+ relation = siblingRelation + "*" + parentRelation;
+ else if (parentRelation != "")
+ relation = parentRelation;
+ else
+ relation = siblingRelation;
+
+ expression = curData.expression + relation + expression;
+
+ isParent = false;
+ isRemoteParent = false;
+ siblingCount = 0;
+ }
+ firstRun = false;
+
+ if (curData.prevSibling) {
+ siblingCount++;
+ curData = curData.prevSibling;
+ }
+ else if (curData.parentNode) {
+ siblingCount = 0;
+ if (isParent)
+ isRemoteParent = true;
+ else
+ isParent = true;
+ curData = curData.parentNode;
+ }
+ else
+ curData = null;
+ }
+
+ stylesheetData = expression + "{display: none !important;}";
+ expression = domainData.selected + "##" + expression;
+
+ document.getElementById("expression").value = expression;
+
+ var tree = document.getElementById("nodes-tree");
+ if (tree.view && tree.view.selection)
+ tree.treeBoxObject.invalidateRow(tree.view.selection.currentIndex);
+
+ if (previewStyle)
+ previewStyle.textContent = stylesheetData;
+}
+
+function escapeChar(dummy, match)
+{
+ return "\\" + match.charCodeAt(0).toString(16) + " ";
+}
+
+function fillDomains(domainData) {
+ var list = document.getElementById("domainGroup");
+
+ var commandHandler = function() {
+ changeDomain(this);
+ };
+
+ var node = document.createElement("radio");
+ node.setAttribute("label", list.getAttribute("_labelnone"));
+ node.setAttribute("value", "");
+ node.addEventListener("command", commandHandler, false);
+ if (domainData.selected == "")
+ node.setAttribute("selected", "true");
+ list.appendChild(node);
+
+ var parts = domainData.value.split(".");
+ if (parts[0] == "")
+ parts.shift();
+
+ for (var i = 1; i <= parts.length; i++) {
+ if (parts[parts.length - i] == "")
+ continue;
+
+ var curDomain = parts.slice(parts.length - i).join(".");
+
+ node = document.createElement("radio");
+ node.setAttribute("label", curDomain)
+ node.setAttribute("value", curDomain);
+ node.addEventListener("command", commandHandler, false);
+ if (domainData.selected == curDomain)
+ node.setAttribute("selected", "true");
+ list.appendChild(node);
+ }
+}
+
+function fillNodes(nodeData) {
+ var curContainer = document.createElement("treechildren");
+ var curChildren = null;
+ while (nodeData) {
+ var id = "";
+ var className = "";
+ var i = 0;
+ if (nodeData.attributes.length > i && nodeData.attributes[i].name == "id")
+ id = nodeData.attributes[i++].value;
+ if (nodeData.attributes.length > i && nodeData.attributes[i].name == "class")
+ className = nodeData.attributes[i++].value;
+
+ var item = document.createElement("treeitem");
+ var row = document.createElement("treerow");
+
+ var cell = document.createElement("treecell");
+ cell.setAttribute("label", nodeData.tagName.value);
+ row.appendChild(cell);
+
+ cell = document.createElement("treecell");
+ cell.setAttribute("label", id);
+ row.appendChild(cell);
+
+ cell = document.createElement("treecell");
+ cell.setAttribute("label", className);
+ row.appendChild(cell);
+
+ item.appendChild(row);
+ item.nodeData = nodeData;
+
+ if (curChildren) {
+ item.appendChild(curChildren);
+ item.setAttribute("container", "true");
+ item.setAttribute("open", "true");
+ }
+ curChildren = null;
+
+ if (curContainer.firstChild)
+ curContainer.insertBefore(item, curContainer.firstChild);
+ else
+ curContainer.appendChild(item);
+
+ if (nodeData.prevSibling)
+ nodeData = nodeData.prevSibling;
+ else if (nodeData.parentNode) {
+ curChildren = curContainer;
+ curContainer = document.createElement("treechildren");
+ nodeData = nodeData.parentNode;
+ }
+ else
+ nodeData = null;
+ }
+
+ var tree = document.getElementById("nodes-tree");
+ var body = document.getElementById("nodes-tree-children");
+ while (curContainer.firstChild)
+ body.appendChild(curContainer.firstChild);
+}
+
+function createAttribute(template, attr, text, value)
+{
+ template = E(template == "basic" ? "basicAttributeTemplate" : "advancedAttributeTemplate");
+
+ let result = template.cloneNode(true);
+ result.removeAttribute("id");
+ result.removeAttribute("hidden");
+ result.attr = attr;
+
+ let checkbox = result.getElementsByClassName("checkbox")[0];
+ checkbox.setAttribute("checked", attr.checked);
+ checkbox.attr = attr;
+
+ let label = result.getElementsByClassName("label");
+ if (label.length)
+ {
+ label = label[0];
+ label.setAttribute("value", text);
+
+ let randID = "i" + String(Math.random()).replace(/\D/g, "");
+ checkbox.setAttribute("id", randID);
+ label.setAttribute("control", randID);
+ }
+ else
+ checkbox.setAttribute("label", text);
+
+ let textbox = result.getElementsByClassName("textbox");
+ if (textbox.length)
+ {
+ textbox = textbox[0];
+ textbox.setAttribute("value", value);
+ textbox.attr = attr;
+ }
+
+ return result;
+}
+
+function fillAttributes(nodeData)
+{
+ selectedNode = nodeData;
+
+ let list = document.getElementById("attributes-list");
+ while(list.firstChild)
+ list.removeChild(list.firstChild);
+
+ // Add tag name entry
+ let node = createAttribute("basic", nodeData.tagName, list.getAttribute("_labeltagname") + " " + nodeData.tagName.value);
+ list.appendChild(node);
+
+ // Add first/last child entries
+ if (advancedMode && "firstChild" in nodeData)
+ {
+ node = createAttribute("basic", nodeData.firstChild, list.getAttribute("_labelfirstchild"));
+ list.appendChild(node);
+ }
+ if (advancedMode && "lastChild" in nodeData)
+ {
+ node = createAttribute("basic", nodeData.lastChild, list.getAttribute("_labellastchild"));
+ list.appendChild(node);
+ }
+
+ // Add attribute entries
+ for (let i = 0; i < nodeData.attributes.length; i++)
+ {
+ let attr = nodeData.attributes[i];
+ node = createAttribute(advancedMode ? "advanced" : "basic", attr, attr.name + ": " + attr.value, attr.selected);
+ list.appendChild(node);
+ }
+
+ if (advancedMode)
+ {
+ // Add custom CSS entry
+ node = createAttribute("advanced", nodeData.customCSS, list.getAttribute("_labelcustom"), nodeData.customCSS.selected);
+ list.appendChild(node);
+ }
+}
+
+function togglePreview(preview) {
+ if (preview) {
+ if (!previewStyle || !previewStyle.parentNode) {
+ previewStyle = doc.createElementNS("http://www.w3.org/1999/xhtml", "style");
+ previewStyle.setAttribute("type", "text/css");
+ doc.documentElement.appendChild(previewStyle);
+ }
+ previewStyle.textContent = stylesheetData;
+ }
+ else {
+ try
+ {
+ if (previewStyle && previewStyle.parentNode)
+ previewStyle.parentNode.removeChild(previewStyle);
+ }
+ catch (e)
+ {
+ // if the window was closed (reloaded) we end up with dead object reference
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=695480
+ // just ignore this case
+ }
+ previewStyle = null;
+ }
+}
+
+function changeDomain(node) {
+ domainData.selected = node.getAttribute("value");
+ updateExpression();
+}
+
+function toggleAttr(node) {
+ node.attr.checked = node.checked;
+ updateExpression();
+}
+
+function setSelectedAttrValue(node) {
+ node.attr.selected = node.value;
+ if (node.attr.checked)
+ updateExpression();
+}
+
+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"));
+
+ fillAttributes(nodeData);
+
+ if (advancedMode) {
+ setTimeout(function() {
+ var tree = document.getElementById("nodes-tree");
+
+ if (!treeView)
+ treeView = new TreeView(tree);
+
+ if (selectedNode) {
+ // Expand all containers
+ var items = tree.getElementsByTagName("treeitem");
+ for (var i = 0; i < items.length; i++)
+ if (items[i].getAttribute("container") == "true")
+ items[i].setAttribute("open", "true");
+
+ tree.treeBoxObject.ensureRowIsVisible(tree.view.rowCount - 1);
+ tree.view.selection.select(tree.view.rowCount - 1);
+ }
+ }, 0);
+ }
+}
+
+function updateNodeSelection() {
+ var tree = document.getElementById("nodes-tree");
+ var selection = tree.view.selection;
+ if (selection.count < 1)
+ return;
+
+ var min = {};
+ selection.getRangeAt(0, min, {});
+
+ var item = tree.view
+ .QueryInterface(Ci.nsITreeContentView)
+ .getItemAtIndex(min.value);
+ if (!item || !item.nodeData)
+ return;
+
+ fillAttributes(item.nodeData);
+}
+
+function addExpression()
+{
+ let filter = Filter.fromText(Filter.normalize(document.getElementById("expression").value));
saroyanm 2014/07/18 13:14:41 In Element Hiding Helper we used "AdblockPlus.addP
+ if (filter)
+ {
+ filter.disabled = false;
+ FilterStorage.addFilter(filter);
+ }
+
+ togglePreview(false);
+}
« no previous file with comments | « no previous file | chrome/content/ui/ehh-composer.xul » ('j') | chrome/locale/en-US/ehh-composer.dtd » ('J')

Powered by Google App Engine
This is Rietveld