[Pkg-mozext-commits] [firexpath] 01/18: Initial commit (version 0.9.1)
David Prévot
taffit at moszumanska.debian.org
Sat Mar 26 19:35:46 UTC 2016
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to tag FirePath-0.9.5
in repository firexpath.
commit 6850cc28240512daf7f5154614560d9bd033576c
Author: pierre.tholence <pierre.tholence at dfa30af4-1965-11df-8728-136f2c2ca76e>
Date: Sun Feb 14 12:52:52 2010 +0000
Initial commit (version 0.9.1)
---
build.xml | 46 +
chrome.manifest | 4 +
content/FireXPath.png | Bin 0 -> 2753 bytes
content/XPathPanel.js | 1858 +++++++++++++++++++++++++++++++++
content/bindings.xml | 690 ++++++++++++
content/firebugOverlay.xul | 31 +
defaults/preferences/FireXPathPref.js | 3 +
install.rdf | 23 +
license.txt | 674 ++++++++++++
locale/en-US/FireXPath.dtd | 7 +
locale/en-US/FireXPath.properties | 27 +
skin/classic/FireXPath.css | 75 ++
skin/classic/xPathPanel.css | 43 +
13 files changed, 3481 insertions(+)
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..8a05d33
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- ======================================================================
+ 5 mai 2009 16:19:23
+
+ FireXPath
+ Firebug extension to generate, edit and inspect XPath expressions
+
+ Pierre Tholence
+ ====================================================================== -->
+<project name="FireXPath" default="xpi">
+ <description>
+ Firebug extension to generate, edit and inspect XPath expressions
+ </description>
+
+
+ <property name="filename" value="FireXPath.xpi"/>
+ <property name="builddir" value="build"/>
+ <property name="tempdir" value="temp"/>
+
+ <!-- =================================
+ target: xpi
+ ================================= -->
+ <target name="xpi" depends="clean" description="create the xpi file">
+ <mkdir dir="${tempdir}"/>
+ <copy todir="${tempdir}">
+ <fileset dir="." >
+ <exclude name="${tempdir}"/>
+ <exclude name="build.xml"/>
+ <exclude name=".project"/>
+ <exclude name="**/.svn"/>
+ </fileset>
+ </copy>
+ <mkdir dir="${builddir}"/>
+ <zip destfile="${builddir}/${filename}" basedir="${tempdir}"/>
+ <delete dir="${tempdir}"/>
+ </target>
+
+ <!-- - - - - - - - - - - - - - - - - -
+ target: copyFiles
+ - - - - - - - - - - - - - - - - - -->
+ <target name="clean">
+ <delete dir="${builddir}"/>
+ <delete dir="${tempdir}"/>
+ </target>
+
+</project>
diff --git a/chrome.manifest b/chrome.manifest
new file mode 100644
index 0000000..4c87b40
--- /dev/null
+++ b/chrome.manifest
@@ -0,0 +1,4 @@
+content firexpath content/
+locale firexpath en-US locale/en-US/
+skin firexpath classic/1.0 skin/classic/
+overlay chrome://firebug/content/firebugOverlay.xul chrome://firexpath/content/firebugOverlay.xul
diff --git a/content/FireXPath.png b/content/FireXPath.png
new file mode 100644
index 0000000..cfd609a
Binary files /dev/null and b/content/FireXPath.png differ
diff --git a/content/XPathPanel.js b/content/XPathPanel.js
new file mode 100644
index 0000000..7f094e3
--- /dev/null
+++ b/content/XPathPanel.js
@@ -0,0 +1,1858 @@
+/*
+ * Copyright (C) 2009 Pierre Tholence, DB4ALL
+ *
+ * This file is part of FireXPath
+ *
+ * FireXPath is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FireXPath 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 FireXPath. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+FBL.ns(function() { with (FBL) {
+
+// ************************************************************************************************
+// Constants
+
+const panelName = "xpath";
+
+const prefRegExp = new RegExp(panelName + "\.(.*)");
+
+const defaultTimePeriod = 100;
+
+// ************************************************************************************************
+// XPath Tokens from XPath V1 W3C Recommendation on http://www.w3.org/TR/xpath
+
+var BaseChar = "[\u0041-\u005A]|[\u0061-\u007A]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u00FF]|[\u0100-\u0131]|" +
+ "[\u0134-\u013E]|[\u0141-\u0148]|[\u014A-\u017E]|[\u0180-\u01C3]|[\u01CD-\u01F0]|[\u01F4-\u01F5]|[\u01FA-\u0217]|" +
+ "[\u0250-\u02A8]|[\u02BB-\u02C1]|\u0386|[\u0388-\u038A]|\u038C|[\u038E-\u03A1]|[\u03A3-\u03CE]|[\u03D0-\u03D6]|" +
+ "\u03DA|\u03DC|\u03DE|\u03E0|[\u03E2-\u03F3]|[\u0401-\u040C]|[\u040E-\u044F]|[\u0451-\u045C]|[\u045E-\u0481]|" +
+ "[\u0490-\u04C4]|[\u04C7-\u04C8]|[\u04CB-\u04CC]|[\u04D0-\u04EB]|[\u04EE-\u04F5]|[\u04F8-\u04F9]|[\u0531-\u0556]|" +
+ "\u0559|[\u0561-\u0586]|[\u05D0-\u05EA]|[\u05F0-\u05F2]|[\u0621-\u063A]|[\u0641-\u064A]|[\u0671-\u06B7]|" +
+ "[\u06BA-\u06BE]|[\u06C0-\u06CE]|[\u06D0-\u06D3]|\u06D5|[\u06E5-\u06E6]|[\u0905-\u0939]|\u093D|[\u0958-\u0961]|" +
+ "[\u0985-\u098C]|[\u098F-\u0990]|[\u0993-\u09A8]|[\u09AA-\u09B0]|\u09B2|[\u09B6-\u09B9]|[\u09DC-\u09DD]|" +
+ "[\u09DF-\u09E1]|[\u09F0-\u09F1]|[\u0A05-\u0A0A]|[\u0A0F-\u0A10]|[\u0A13-\u0A28]|[\u0A2A-\u0A30]|[\u0A32-\u0A33]|" +
+ "[\u0A35-\u0A36]|[\u0A38-\u0A39]|[\u0A59-\u0A5C]|\u0A5E|[\u0A72-\u0A74]|[\u0A85-\u0A8B]|\u0A8D|[\u0A8F-\u0A91]|" +
+ "[\u0A93-\u0AA8]|[\u0AAA-\u0AB0]|[\u0AB2-\u0AB3]|[\u0AB5-\u0AB9]|\u0ABD|\u0AE0|[\u0B05-\u0B0C]|[\u0B0F-\u0B10]|" +
+ "[\u0B13-\u0B28]|[\u0B2A-\u0B30]|[\u0B32-\u0B33]|[\u0B36-\u0B39]|\u0B3D|[\u0B5C-\u0B5D]|[\u0B5F-\u0B61]|" +
+ "[\u0B85-\u0B8A]|[\u0B8E-\u0B90]|[\u0B92-\u0B95]|[\u0B99-\u0B9A]|\u0B9C|[\u0B9E-\u0B9F]|[\u0BA3-\u0BA4]|" +
+ "[\u0BA8-\u0BAA]|[\u0BAE-\u0BB5]|[\u0BB7-\u0BB9]|[\u0C05-\u0C0C]|[\u0C0E-\u0C10]|[\u0C12-\u0C28]|[\u0C2A-\u0C33]|" +
+ "[\u0C35-\u0C39]|[\u0C60-\u0C61]|[\u0C85-\u0C8C]|[\u0C8E-\u0C90]|[\u0C92-\u0CA8]|[\u0CAA-\u0CB3]|[\u0CB5-\u0CB9]|" +
+ "\u0CDE|[\u0CE0-\u0CE1]|[\u0D05-\u0D0C]|[\u0D0E-\u0D10]|[\u0D12-\u0D28]|[\u0D2A-\u0D39]|[\u0D60-\u0D61]|" +
+ "[\u0E01-\u0E2E]|\u0E30|[\u0E32-\u0E33]|[\u0E40-\u0E45]|[\u0E81-\u0E82]|\u0E84|[\u0E87-\u0E88]|\u0E8A|\u0E8D|" +
+ "[\u0E94-\u0E97]|[\u0E99-\u0E9F]|[\u0EA1-\u0EA3]|\u0EA5|\u0EA7|[\u0EAA-\u0EAB]|[\u0EAD-\u0EAE]|\u0EB0|" +
+ "[\u0EB2-\u0EB3]|\u0EBD|[\u0EC0-\u0EC4]|[\u0F40-\u0F47]|[\u0F49-\u0F69]|[\u10A0-\u10C5]|[\u10D0-\u10F6]|\u1100|" +
+ "[\u1102-\u1103]|[\u1105-\u1107]|\u1109|[\u110B-\u110C]|[\u110E-\u1112]|\u113C|\u113E|\u1140|\u114C|\u114E|\u1150|" +
+ "[\u1154-\u1155]|\u1159|[\u115F-\u1161]|\u1163|\u1165|\u1167|\u1169|[\u116D-\u116E]|[\u1172-\u1173]|\u1175|\u119E|" +
+ "\u11A8|\u11AB|[\u11AE-\u11AF]|[\u11B7-\u11B8]|\u11BA|[\u11BC-\u11C2]|\u11EB|\u11F0|\u11F9|[\u1E00-\u1E9B]|" +
+ "[\u1EA0-\u1EF9]|[\u1F00-\u1F15]|[\u1F18-\u1F1D]|[\u1F20-\u1F45]|[\u1F48-\u1F4D]|[\u1F50-\u1F57]|\u1F59|\u1F5B|\u1F5D|" +
+ "[\u1F5F-\u1F7D]|[\u1F80-\u1FB4]|[\u1FB6-\u1FBC]|\u1FBE|[\u1FC2-\u1FC4]|[\u1FC6-\u1FCC]|[\u1FD0-\u1FD3]|[\u1FD6-\u1FDB]|" +
+ "[\u1FE0-\u1FEC]|[\u1FF2-\u1FF4]|[\u1FF6-\u1FFC]|\u2126|[\u212A-\u212B]|\u212E|[\u2180-\u2182]|[\u3041-\u3094]|" +
+ "[\u30A1-\u30FA]|[\u3105-\u312C]|[\uAC00-\uD7A3]";
+
+var Ideographic = "[\u4E00-\u9FA5]|\u3007|[\u3021-\u3029]";
+
+var Letter = BaseChar + "|" + Ideographic;
+
+var NCNameStartChar = Letter + "|_";
+
+var NCNameChar = "[A-Z]|_|[a-z]|[\u00C0-\u00D6]|[\u00D8-\u00F6]|[\u00F8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|" +
+ "[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]|" +
+ "-|\\.|[0-9]|\u00B7|[\u0300-\u036F]|[\u203F-\u2040]";
+
+var NCName = "(?:(?:" + NCNameStartChar + ")(?:" + NCNameChar + ")*)";
+
+var QName = NCName + ":" + NCName + "|" + NCName;
+
+var NameTest = "\\*|" + NCName + ":\\*|" + QName;
+
+var NodeType = "comment|text|processing-instruction|node";
+
+var NameOperator = "and|or|mod|div";
+
+var Operator = NameOperator + "|\\*|//|/|\\||\\+|-|!=|<=|<|>=|>|=";
+
+var FunctionName = QName;
+
+var AxisName = "ancestor|ancestor-or-self|attribute|child|descendant|descendant-or-self|following|following-sibling|parent|preceding|" +
+ "preceding-sibling|self";
+
+var Literal = "\"[^\"]*\"|'[^']*'";
+
+var Numbers = "\\d+(?:\\.\\d?)?|\\.\\d+";
+
+var ExprToken = "\\(|\\)|\\[|\\]|\\.\\.|@|,|::|" + NameTest + "|" + NodeType + "|" + Operator + "|" + FunctionName + "|" +
+AxisName + "|" + Literal + "|" + Numbers + "|\\.";
+
+var xPathToken = new RegExp(ExprToken, "g");
+
+var selectedToken = new RegExp(NCName + ":(?!:)|" + ExprToken + "|\\s", "g");
+
+var nameTestToken = new RegExp(NameTest);
+// Function supported by Firefox
+var functionNameToken = new RegExp("concat|substring|substring-after|substring-before|string|local-name|name|namespace-uri|normalize-space" +
+ "translate|ceiling|count|floor|last|number|position|round|sum|boolean|contains|false|lang|not|starts-with|string-length|true");
+
+var expressionToken = new RegExp("\\]|\\)|\\*" + NameTest + "|" + Literal + "|" + Numbers + "|\\.");
+
+var getNamespace = new RegExp("([^:]+:).*");
+
+// ************************************************************************************************
+// XPathPanel
+
+Firebug.XPathPanel = function() {}
+Firebug.XPathPanel.prototype = extend(Firebug.Panel,
+{
+
+ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ // extends Panel ()
+
+ name: panelName,
+ title: "XPath",
+
+ initialize: function() {
+ Firebug.Panel.initialize.apply(this, arguments);
+ this.addStyleSheet(this.document);
+ this.location = this.getDefaultLocation(this.context);
+ this.xPathBar = (this.context.chrome?this.context.chrome.$("FireXPathBar"): $("FireXPathBar"));
+ this.xPathStatusBar = (this.context.chrome?this.context.chrome.$("FireXPathStatusBar"): $("FireXPathStatusBar"));
+
+ this.ioBoxContainer = this.document.createElement("div");
+ setClass(this.ioBoxContainer, "io-box-container");
+ this.panelNode.appendChild(this.ioBoxContainer);
+
+ this.onMouseDown = bind(this.onMouseDown, this);
+
+ this.inspecting = false;
+ },
+
+ destroy: function(state) {
+ state.persistedLocation = this.persisteLocation();
+ this.xPathBar.persiste(state);
+ this.stopLoading();
+
+ if (this.ioBox) {
+ this.ioBox.destroy();
+ delete this.ioBox;
+ }
+ Firebug.Panel.destroy.apply(this, arguments);
+ },
+
+ initializeNode: function(oldPanelNode) {
+ this.panelNode.addEventListener("mousedown", this.onMouseDown, false);
+ },
+
+ destroyNode: function() {
+ this.panelNode.removeEventListener("mousedown", this.onMouseDown, false);
+ },
+
+ show: function(state) {
+ if (this.context.loaded) {
+ this.showModules(true);
+
+ if(!this.ioBox) {
+ this.ioBox = new InsideOutBox(this, this.ioBoxContainer);
+ if(state) {
+ this.restoreLocation(state.persistedLocation);
+ this.xPathBar.restore(state);
+ this.xPathBar.evaluate(true);
+ } else {
+ this.ioBox.createObjectBox(this.rootElement || this.location.document);
+ }
+ }
+ }
+ },
+
+ hide: function() {
+ this.showModules(false);
+ },
+
+ showModules: function(show) {
+ this.xPathBar.show(this.context, show);
+ this.xPathStatusBar.show(this.context, show);
+
+ var $ = FBL.$;
+ if(this.context.chrome)
+ $ = this.context.chrome.$;
+
+ // when there is a visibility: hidden style the collapsed attribute doesn't work correctly
+ $("fbPanelStatus").removeAttribute("style");
+ collapse($("fbSearchBox"), show);
+ collapse($("fbPanelStatus"), show);
+ },
+
+ detach: function(oldChrome, newChrome) {
+ Firebug.Panel.detach.apply(this, arguments);
+ if(this.context == FirebugContext) {
+ this.xPathBar.persiste(this.context);
+ }
+ this.xPathBar = newChrome.$("FireXPathBar");
+ this.xPathStatusBar = newChrome.$("FireXPathStatusBar");
+ if(this.context == FirebugContext) {
+ this.xPathBar.restore(this.context);
+ this.xPathBar.currentContext = this.context;
+ }
+ if(this.context.browser.detached || (newChrome != Firebug.originalChrome && this.context == FirebugContext)) {
+ Firebug.XPathPanel.LocationHighlightModule.addLocationListener(newChrome);
+ } else {
+ Firebug.XPathPanel.LocationHighlightModule.removeLocationListener(oldChrome);
+ }
+ },
+
+ reattach: function(doc) {
+ Firebug.Panel.reattach.apply(this, arguments);
+ this.addStyleSheet(doc);
+ },
+
+ supportsObject: function(object) {
+ if(object instanceof Window)
+ return 2;
+ else if (object instanceof Element ||
+ object instanceof Text ||
+ object instanceof Attr ||
+ object instanceof Comment ||
+ object instanceof Document ||
+ object instanceof SourceText )
+ return 1;
+ else
+ return 0;
+ },
+
+ updateSelection: function(object) {
+ var location;
+ if(object instanceof Document)
+ location = object.defaultView;
+ else
+ location = object.ownerDocument.defaultView;
+
+ this.navigate(location);
+ this.xPathBar.setNode(object, this.inspecting);
+ },
+
+ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ // extends Panel (location menu for iframes)
+
+ getLocationList: function() {
+ var locations = [];
+ var win = this.context.window;
+
+ locations.push(this.context.window);
+
+ for(var i=0; i<win.frames.length; i++) {
+ locations.push(win.frames[i].window);
+ }
+ return locations;
+ },
+
+ updateLocation: function() {
+ this.xPathBar.updateLocation(this.location);
+ this.xPathStatusBar.reset(this.context);
+ this.setResult(null, null);
+ },
+
+ getDefaultLocation: function(context) {
+ return context.window;
+ },
+
+ getObjectDescription: function(object) {
+ var win = this.context.window;
+
+ var path;
+ try{
+ path = object.location.host + object.location.pathname;
+ } catch(e) {
+ path = "" + object.location;
+ }
+
+ var name = "";
+
+ if(object == win) name = $STR_XP("topWindow");
+ else {
+ for(var i=0; i<win.frames.length; i++)
+ if(win.frames[i].window== object) {
+ name = getElementCSSSelector(object.frameElement); //+ "["+i+"]";
+ break;
+ }
+ }
+
+ return {name: name, path: path};
+ },
+
+ persisteLocation: function() {
+ var win = this.context.window;
+ for(var i = 0; i < win.frames.length; i++)
+ if(win.frames[i].window == this.location)
+ return i;
+ return "top";
+ },
+
+ restoreLocation: function(location) {
+ var win =this.context.window;
+
+ if(location != "top" && location < win.frames.length)
+ this.navigate(this.context.window.frames[location]);
+ else
+ this.navigate(win);
+ },
+
+ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ // extends Panel (option menu)
+
+ getOptionsMenuItems: function()
+ {
+ return [
+ getOptionMenu("showXPathContextMenu", "showXPathContext"),
+ getOptionMenu("generateAbsoluteXPathMenu", "generateAbsoluteXPath"),
+ getOptionMenu("showDOMMenu", "showDOM")
+ ];
+ },
+
+ updateOption: function(name, value) {
+ if(prefRegExp.test(name)) {
+ var option = prefRegExp.exec(name)[1];
+ if(option == "showDOM") {
+ this.displayResult();
+ } else if (option == "showXPathContext") {
+ this.xPathBar.showXPathContext(value);
+ }
+ }
+ },
+
+ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ // Style sheet management method
+
+ addStyleSheet: function(doc) {
+ // Make sure the stylesheet isn't appended twice.
+ if ($("xPathStyles", doc))
+ return;
+
+ var styleSheet = createStyleSheet(doc,
+ "chrome://firexpath/skin/xPathPanel.css");
+ styleSheet.setAttribute("id", "xPathStyles");
+ addStyleSheet(doc, styleSheet);
+ },
+
+ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ // Result management methods
+
+ setResult: function(xPathContext, result) {
+ this.rootElement = xPathContext;
+ this.evaluationResult = result;
+ this.displayResult();
+ Firebug.XPathPanel.ResultHighlightModule.refreshHighlight(this);
+ },
+
+ displayResult: function() {
+ this.stopLoading();
+
+ this.setResultInfo();
+
+ clearNode(this.ioBoxContainer);
+ delete this.ioBox.rootObjectBox;
+
+ var showDOM = Firebug.getPref(Firebug.prefDomain, "xpath.showDOM")
+
+ if(this.evaluationResult != null && !this.evaluationResult.message) {
+ if(isArray(this.evaluationResult)) {
+ if(showDOM) {
+ this.ioBox.createObjectBox(this.rootElement || this.location.document);
+ this.ioBox.selectMultiple(this.evaluationResult);
+ } else
+ this.ioBox.addObjects(this.evaluationResult);
+ } else {
+ var type = typeof(this.evaluationResult);
+ type = type.charAt(0).toUpperCase() + type.substring(1);
+ this.ioBoxContainer.textContent = type + ": " + this.evaluationResult;
+ }
+ } else if(showDOM){
+ this.ioBox.createObjectBox(this.rootElement || this.location.document);
+ }
+ },
+
+ setResultInfo: function() {
+ var result = this.evaluationResult;
+ if(result == null) {
+ this.xPathStatusBar.clearResultInfo(this.context);
+ } else if(result.message) {
+ this.xPathStatusBar.setResultInfo(this.context, "error", $STR_XP(result.message));
+ } else {
+ if(FBL.isArray(result)) {
+ var whiteSpace = 0;
+ result.forEach(function(object) {if(isWhitespaceText(object)) whiteSpace++;})
+ switch(result.length) {
+ case 0:
+ this.xPathStatusBar.setResultInfo(this.context, "warning", $STR_XP("noResultError"));
+ break;
+ case 1:
+ this.xPathStatusBar.setResultInfo(this.context, "ok", $STR_XP("oneResultMessage"));
+ break;
+ default:
+ this.xPathStatusBar.setResultInfo(this.context, "ok",
+ $STR_XP("manyResultMessage", result.length) +
+ (whiteSpace > 0?
+ " (" + (whiteSpace == 1?
+ $STR_XP("oneWhiteSpaceMessage"):
+ $STR_XP("manyWhiteSpaceMessage", whiteSpace)
+ ) + ")":
+ "") );
+ break;
+ }
+ } else {
+ switch(typeof(result)) {
+ case "string":
+ this.xPathStatusBar.setResultInfo(this.context, "ok", $STR_XP("stringResultMessage"));
+ break;
+ case "number":
+ this.xPathStatusBar.setResultInfo(this.context, "ok", $STR_XP("numberResultMessage"));
+ break;
+ case "boolean":
+ this.xPathStatusBar.setResultInfo(this.context, "ok", $STR_XP("booleanResultMessage"));
+ break;
+ }
+ }
+ }
+ },
+
+ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ // SourceBox proxy
+
+ createObjectBox: function(object, isRoot) {
+ var tag = getNodeTag(object);
+ if (tag)
+ return tag.replace({object: object}, this.document);
+ },
+
+ getParentObject: function(node) {
+ if (node instanceof SourceText)
+ return node.owner;
+
+ if (this.rootElement && node == this.rootElement)
+ return null;
+
+ var parentNode = node ? node.parentNode : null;
+ return parentNode;
+ },
+
+ getChildObject: function(node, index, previousSibling)
+ {
+ if(node instanceof Document) {
+ if(index == 0)
+ return node.documentElement;
+ }
+ else if (previousSibling) {
+ return findNextSibling(previousSibling);
+ }
+ else {
+ var childIndex = 0;
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ if (!isWhitespaceText(child) && childIndex++ == index)
+ return child;
+ }
+ }
+
+ return null;
+ },
+
+ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ // Result display loading management methods
+
+ startLoading: function(objects, task) {
+ var step = bind(function(object) {task(object); this.increaseLoading()}, this);
+ var end = bind(this.finishLoading, this);
+
+ this.stepper = new Stepper(arrayIterator(objects), step, end);
+
+ //this.panelNode.removeChild(this.ioBoxContainer);
+
+ this.xPathStatusBar.startLoading(this.context, objects.length);
+ this.stepper.start();
+ },
+
+ stopLoading: function() {
+ if(this.stepper) {
+ this.stepper.stop();
+ this.finishLoading();
+ }
+ },
+
+ increaseLoading: function() {
+ this.xPathStatusBar.increaseLoading(this.context);
+ },
+
+ finishLoading: function() {
+ this.xPathStatusBar.stopLoading(this.context);
+
+ //this.panelNode.appendChild(this.ioBoxContainer);
+
+ delete this.stepper;
+ },
+
+ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ // Inspector management methods
+
+ startInspecting: function() {
+ this.inspecting = true;
+ this.previousLocation = this.location;
+ this.previousXPath = this.xPathBar.xPath;
+ this.setResult(null, null);
+ },
+
+ stopInspecting: function(cancelled) {
+ this.inspecting = false;
+ if(cancelled) {
+ this.navigate(this.previousLocation);
+ this.xPathBar.xPath = this.previousXPath;
+ delete this.previousLocation;
+ delete this.previousXPath;
+ }
+ this.xPathBar.evaluate();
+ },
+
+ // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ // Event handlers
+
+ onMouseDown: function(event) {
+ if (!isLeftClick(event))
+ return;
+
+ if (getAncestorByClass(event.target, "nodeTag")) {
+ var element = Firebug.getRepObject(event.target);
+
+ Firebug.Inspector.highlightObject(null);
+ element.scrollIntoView();
+ Firebug.Inspector.highlightObject(element, FirebugContext);
+ }
+ },
+
+});
+
+// ************************************************************************************************
+// Special Reps
+
+Firebug.XPathPanel.Attr = domplate(Firebug.Rep,
+{
+ tag:
+ SPAN({"class": "nodeAttr", _repObject:"$attr"}, " ",
+ SPAN({"class": "nodeName"}, "$attr.nodeName"), "="",
+ SPAN({"class": "nodeValue"}, "$attr.nodeValue|filterFireXPathClassValue"), """
+ ),
+
+ className: "attrNode",
+
+ supportsObject: function(object) {
+ return object instanceof Attr;
+ },
+
+ getRealObject: function(attribute) {
+ return attribute.ownerElement;
+ },
+
+ getContextMenuItems: function(attribute, target, context) {
+ return getNodeContextMenu(attribute, target, context);
+ },
+
+ getTooltip: function(attribute) {
+ return getXPathFromNode(attribute);
+ }
+});
+
+Firebug.XPathPanel.Comment = domplate(Firebug.Rep,
+{
+ tag:
+ DIV({"class": "nodeBox", _repObject: "$object"},
+ DIV({"class": "nodeComment"},
+ "<!--$object.nodeValue-->"
+ )
+ ),
+
+ className: "commentNode",
+
+ supportsObject: function(object) {
+ return object instanceof Comment;
+ },
+
+ getContextMenuItems: function(node, target, context) {
+ return getNodeContextMenu(node, target, context);
+ },
+
+ getTooltip: function(node) {
+ return getXPathFromNode(node);
+ }
+});
+
+Firebug.XPathPanel.CDATASection = domplate(Firebug.Rep,
+{
+ tag:
+ DIV({"class": "nodeBox", _repObject: "$object"},
+ "<![CDATA[",
+ SPAN({"class": "nodeText"}, "$object.nodeValue"),
+ "]]>"
+ ),
+
+ className: "cdataNode",
+
+ supportsObject: function(object) {
+ return object instanceof CDATASection;
+ },
+
+ getContextMenuItems: function(node, target, context) {
+ return getNodeContextMenu(node, target, context);
+ },
+
+ getTooltip: function(node) {
+ return getXPathFromNode(node);
+ }
+});
+
+Firebug.registerRep(
+ Firebug.XPathPanel.Attr,
+ Firebug.XPathPanel.Comment,
+ Firebug.XPathPanel.CDATASection
+);
+
+// ************************************************************************************************
+// Extension of FirebugReps.Element and FirebugReps.TextNode to modify the context menu
+// and tooltip when the xPath tab is selected
+
+function overwriteMethodForPanel(object, methodName, panelName, newMethod ) {
+ if(object[methodName] && object[methodName] instanceof Function)
+ object["default" + methodName] = object[methodName];
+ object[methodName] = function () {
+ var selectedPanelName = FirebugContext.panelName;
+
+ if(selectedPanelName != panelName) {
+ if(object["default" + methodName])
+ return object["default" + methodName].apply(object, arguments);
+ } else {
+ return newMethod.apply(object, arguments);
+ }
+ }
+}
+
+FirebugReps.Element.getTagName = function(node) {
+ return getTagName(node);
+}
+
+// Overwrite FirebugReps.Element.getContextMenuItems
+overwriteMethodForPanel(
+ FirebugReps.Element,
+ "getContextMenuItems",
+ Firebug.XPathPanel.prototype.name,
+ function(node, target, context) {
+ return getNodeContextMenu(node, target, context);
+ }
+);
+
+// Overwrite FirebugReps.Element.getTooltip
+overwriteMethodForPanel(
+ FirebugReps.Element,
+ "getTooltip",
+ Firebug.XPathPanel.prototype.name,
+ function(node) {
+ return getXPathFromNode(node);
+ }
+);
+
+// Overwrite FirebugReps.TextNode.getContextMenuItem
+overwriteMethodForPanel(
+ FirebugReps.TextNode,
+ "getContextMenuItems",
+ Firebug.XPathPanel.prototype.name,
+ function(node, target, context) {
+ return getNodeContextMenu(node, target, context);
+ }
+);
+
+// Overwrite FirebugReps.TextNode.getTooltip
+overwriteMethodForPanel(
+ FirebugReps.TextNode,
+ "getTooltip",
+ Firebug.XPathPanel.prototype.name,
+ function(node) {
+ return getXPathFromNode(node);
+ }
+);
+
+// Overwrite FirebugReps.TextNode.getRealObject
+overwriteMethodForPanel(
+ FirebugReps.TextNode,
+ "getRealObject",
+ Firebug.XPathPanel.prototype.name,
+ function(object) {
+ return object.parentNode;
+ }
+);
+
+// ************************************************************************************************
+// Tree definition
+
+//TODO improve this (add the url, ns...)
+Firebug.XPathPanel.Document = domplate(FirebugReps.Document,
+{
+ tag:
+ DIV({"class": "nodeBox containerNodeBox repIgnore", _repObject: "$object"},
+ DIV({"class": "nodeLabel"},
+ IMG({"class": "twisty"}),
+ SPAN({"class": "nodeLabelBox repTarget"},
+ "<",
+ SPAN({"class": "nodeTag"}, "document"),
+ SPAN({"class": "nodeBracket insertBefore"}, ">")
+ )
+ ),
+ DIV({"class": "nodeChildBox"}),
+ DIV({"class": "nodeCloseLabel"},
+ SPAN({"class": "nodeCloseLabelBox repTarget"},
+ "</",
+ SPAN({"class": "nodeTag"}, "document"),
+ ">"
+ )
+ )
+ )
+});
+
+FirebugReps.Element.filterFireXPathClass = function(attrs) {
+ for (var i=0; i<attrs.length; i++) {
+ var attr = attrs[i];
+ if(attr.localName == "class" && attr.nodeValue == "firexpath-matching-node") {
+ attrs.splice(i, 1);
+ break;
+ }
+ }
+ return attrs;
+}
+
+FirebugReps.Element.filterFireXPathClassValue = function(value) {
+ return value.replace(" firexpath-matching-node", "");
+},
+
+Firebug.XPathPanel.Element = domplate(FirebugReps.Element,
+{
+ tag:
+ DIV({"class": "nodeBox containerNodeBox repIgnore", _repObject: "$object"},
+ DIV({"class": "nodeLabel"},
+ IMG({"class": "twisty"}),
+ SPAN({"class": "nodeLabelBox repTarget"},
+ "<",
+ SPAN({"class": "nodeTag"}, "$object|getTagName"),
+ FOR("attr", "$object|attrIterator|filterFireXPathClass", Firebug.XPathPanel.Attr.tag),
+ SPAN({"class": "nodeBracket insertBefore"}, ">")
+ )
+ ),
+ DIV({"class": "nodeChildBox"}),
+ DIV({"class": "nodeCloseLabel"},
+ SPAN({"class": "nodeCloseLabelBox repTarget"},
+ "</",
+ SPAN({"class": "nodeTag"}, "$object.localName|toLowerCase"),
+ ">"
+ )
+ )
+ )
+});
+
+Firebug.XPathPanel.TextElement = domplate(FirebugReps.Element,
+{
+ tag:
+ DIV({"class": "nodeBox textNodeBox repIgnore", _repObject: "$object"},
+ DIV({"class": "nodeLabel"},
+ SPAN({"class": "nodeLabelBox repTarget"},
+ "<",
+ SPAN({"class": "nodeTag"}, "$object|getTagName"),
+ FOR("attr", "$object|attrIterator|filterFireXPathClass", Firebug.XPathPanel.Attr.tag),
+ SPAN({"class": "nodeBracket insertBefore"}, ">"),
+ SPAN({"class": "nodeText", _repObject: "$object.firstChild"}, "$object.firstChild.nodeValue"),
+ "</",
+ SPAN({"class": "nodeTag"}, "$object.localName|toLowerCase"),
+ ">"
+ )
+ )
+ )
+});
+
+Firebug.XPathPanel.EmptyElement = domplate(FirebugReps.Element,
+{
+ tag:
+ DIV({"class": "nodeBox emptyNodeBox repIgnore", _repObject: "$object"},
+ DIV({"class": "nodeLabel"},
+ SPAN({"class": "nodeLabelBox repTarget"},
+ "<",
+ SPAN({"class": "nodeTag"}, "$object|getTagName"),
+ FOR("attr", "$object|attrIterator|filterFireXPathClass", Firebug.XPathPanel.Attr.tag),
+ SPAN({"class": "nodeBracket insertBefore"}, "/>")
+ )
+ )
+ )
+});
+
+Firebug.XPathPanel.TextNode = domplate(FirebugReps.Element,
+{
+ tag:
+ DIV({"class": "nodeBox", _repObject: "$object"},
+ SPAN({"class": "nodeText"}, "$object.nodeValue")
+ )
+});
+
+// ************************************************************************************************
+// Local Helpers
+
+function getNodeTag(node) {
+ if (node instanceof Element) {
+ if (node instanceof HTMLAppletElement)
+ return Firebug.XPathPanel.EmptyElement.tag;
+ else if (node.firebugIgnore || node.id == "_firebugConsole")
+ return null;
+ else if (isEmptyElement(node))
+ return Firebug.XPathPanel.EmptyElement.tag;
+ else if (isPureText(node))
+ return Firebug.XPathPanel.TextElement.tag;
+ else
+ return Firebug.XPathPanel.Element.tag;
+ }
+ else if(node instanceof Document)
+ return Firebug.XPathPanel.Document.tag;
+ else if (node instanceof Text)
+ return Firebug.XPathPanel.TextNode.tag;
+ else if (node instanceof CDATASection)
+ return Firebug.XPathPanel.CDATASection.tag;
+ else if (node instanceof Comment)
+ return Firebug.XPathPanel.Comment.tag;
+ else
+ return FirebugReps.Nada.tag;
+}
+
+function isWhitespaceText(node) {
+ if (node instanceof HTMLAppletElement)
+ return false;
+ return node.nodeType == 3 && isWhitespace(node.nodeValue);
+}
+
+function findNextSibling(node) {
+ for (var child = node.nextSibling; child; child = child.nextSibling) {
+ if (!isWhitespaceText(child))
+ return child;
+ }
+}
+
+function isPureText(element) {
+ if(element.firstChild.nodeType == 3
+ && element.childNodes.length == 1
+ && !isWhitespaceText(element.firstChild))
+ return true;
+ else
+ return false;
+}
+
+function isEmptyElement(element) {
+ for(var child = element.firstChild; child; child = child.nextSibling) {
+ if(!isWhitespaceText(child)) return false;
+ }
+ return true;
+}
+
+function getOptionMenu(label, option) {
+ var key = Firebug.XPathPanel.prototype.name + "." + option;
+ var value = Firebug.getPref(Firebug.prefDomain, key);
+
+ return {label: $STR_XP(label),
+ nol10n: true,
+ type: "checkbox",
+ checked: value,
+ command: bindFixed(Firebug.setPref, Firebug, Firebug.prefDomain, key, !value) };
+}
+
+function getNodeContextMenu(node, target, context) {
+
+ // This to make sure the context menu is added to the right object
+ if(node != Firebug.getRepObject(target)) return;
+
+ var xPathBar = context.getPanel(panelName).xPathBar;
+
+ var contextMenu = [
+ {label: $STR_XP("copyXPath"),
+ nol10n: true,
+ command: bindFixed(copyXPath, FBL, node)}
+ ];
+
+ if(Firebug.getPref(Firebug.prefDomain, "xpath.showXPathContext")) {
+ var panel = context.getPanel(Firebug.XPathPanel.prototype.name);
+ if(panel.rootElement && panel.rootElement != panel.location.document)
+ contextMenu.push(
+ {label: $STR_XP("copyXPathFromContext"),
+ nol10n: true,
+ command: bindFixed(copyXPath, FBL, node, panel.rootElement) }
+ )
+ }
+
+ contextMenu.push(
+ {label: $STR_XP("setXPath"),
+ nol10n: true,
+ command: bindFixed(xPathBar.setNode, xPathBar, node)}
+ )
+
+ var element = null;
+ if(node instanceof Element) {
+ element = node;
+ if(Firebug.getPref(Firebug.prefDomain, "xpath.showXPathContext"))
+ contextMenu.push(
+ {label: $STR_XP("setXPathContext"),
+ nol10n: true,
+ command: bindFixed(xPathBar.setContextNode, xPathBar, node)}
+ );
+ }
+ else if(node instanceof Attr)
+ element = node.ownerElement;
+ else if(node instanceof Text)
+ element = node.parentNode;
+
+ if(element) {
+ contextMenu.push("-");
+ contextMenu.push({label: "ScrollIntoView", command: bindFixed(element.scrollIntoView, element) });
+ }
+
+ return contextMenu;
+}
+
+function copyXPath(node, context) {
+ var xpath = getXPathFromNode(node, context);
+ copyToClipboard(xpath);
+}
+
+// Generator that create an iterator over an array
+function arrayIterator(array) {
+ for(var i = 0, length = array.length; i < length; i++) {
+ yield array[i];
+ }
+}
+
+// ************************************************************************************************
+// Global Helpers
+
+FBL.$STR_XP = function() {
+ var args = cloneArray(arguments);
+ var key = args.shift();
+ var bundle = document.getElementById("FireXpath_strings");
+ try{
+ if(args.length > 0)
+ return bundle.getFormattedString(key, args);
+ else
+ return bundle.getString(key);
+ } catch(e) {
+ return key;
+ }
+}
+
+FBL.isArray = function (object) {
+ return FirebugReps.Arr.isArray(object);
+}
+
+FBL.getTagName = function(node) {
+ var ns = node.namespaceURI;
+ var prefix = node.lookupPrefix(ns);
+
+ //if an element has a namespace it needs a prefix
+ if(ns != null && !prefix) {
+ prefix = getPrefixFromNS(ns);
+ }
+
+ return (prefix?prefix + ":":"") + node.localName.toLowerCase();
+}
+
+FBL.getPrefixFromNS = function(ns) {
+ return ns.replace(/.*[^\w](\w+)[^\w]*$/, "$1");
+}
+
+FBL.getXPathFromNode = function(node, context) {
+ var result = "";
+ var stop = false;
+ var absolute = Firebug.getPref(Firebug.prefDomain, "xpath.generateAbsoluteXPath");
+
+ var parent = context || node.ownerDocument;
+ while (node && node != parent && !stop) {
+ var str = "";
+ var position = getNodePosition(node);
+ switch (node.nodeType) {
+ case Node.DOCUMENT_NODE:
+ break;
+ case Node.ATTRIBUTE_NODE:
+ str = "@" + node.name;
+ break;
+ case Node.COMMENT_NODE:
+ str = "comment()";
+ break;
+ case Node.TEXT_NODE:
+ str = "text()";
+ break;
+ case Node.ELEMENT_NODE:
+
+ var name = getTagName(node);
+
+ if(!absolute && node.id && node.id != "") {
+ str = ".//*[@id='" + node.id + "']";
+ position = null;
+ stop = true;
+ } else {
+ str = name;
+ }
+
+ break;
+ }
+
+ result = str + (position ? "[" + position + "]" : "") + (result? "/": "") + result;
+
+ if(node instanceof Attr) node = node.ownerElement;
+ else node = node.parentNode;
+
+ }
+
+ return result;
+}
+
+function getNodePosition(node) {
+ if (!node.parentNode)
+ return null;
+
+ var siblings = node.parentNode.childNodes;
+ var count = 0;
+ var position;
+
+ for (var i = 0; i < siblings.length; i++) {
+ var object = siblings[i];
+ if(object.nodeType == node.nodeType && object.nodeName == node.nodeName) {
+ count++;
+ if(object == node) position = count;
+ }
+ }
+
+ if (count > 1)
+ return position;
+ else
+ return null;
+}
+
+// ************************************************************************************************
+// Extension of InsideOutBox to support multiple selection
+
+InsideOutBox.prototype = extend(InsideOutBox.prototype, {
+
+ selectMultiple: function(objects) {
+ var filteredObjects = objects.filter(function(object){return !isWhitespaceText(object);});
+
+ this.firstSelection = true;
+ this.view.startLoading(filteredObjects, bind(this.selectObject, this));
+ },
+
+ selectObject: function(object) {
+
+ // Get correct object to find or create the objectBox
+ var correctObject = object;
+ if(object instanceof Attr)
+ correctObject = object.ownerElement;
+ else if(object instanceof Text)
+ correctObject = object.parentNode;
+
+ // Find the objectBox (node in the ioBox)
+ var objectBox = this.findObjectBox(correctObject);
+
+ // Create it if it doesn't exist yet
+ if(!objectBox)
+ objectBox = this.createObjectBox(correctObject);
+
+ // objectBox can be null for object that have the firebugIgnore attribute
+ if(objectBox != null) {
+
+ // Get the correct node in the ioBox to set its class to selected
+ var nodeToSelect = objectBox;
+
+ if(object instanceof Attr) {
+ // Get the nodes that contains the attributes
+ var attributeNodes = getElementsByClass(objectBox, "nodeAttr");
+
+ // This get the node that contains the attribute we are looking for
+ nodeToSelect = Array.filter(attributeNodes, function(attributeNode) {
+ return attributeNode.childNodes[1].textContent == object.name;
+ })[0];
+ }
+ else if(object instanceof Text) {
+ // Get the nodes that contains the text
+ var textNodes = getElementsByClass(objectBox, "nodeText");
+
+ if(textNodes.length == 0) {
+ this.expandObjectBox(objectBox);
+ textNodes = getElementsByClass(objectBox, "nodeText");
+ }
+
+ // This get the node that contains the text we are looking for
+ nodeToSelect = Array.filter(textNodes, function(textNode) {
+ return textNode.textContent == object.nodeValue;
+ })[0];
+ }
+
+ // Set the correct node's class to selected
+ setClass(nodeToSelect, "selected");
+
+ // Make the objectBox visible
+ this.openObjectBox(objectBox);
+
+ // Scroll into the first element selected in the tree
+ if(this.firstSelection) {
+ this.firstSelection = false;
+ scrollIntoCenterView(nodeToSelect, this.view.ioBoxContainer);
+ }
+ }
+ },
+
+ addObjects: function(objects) {
+ this.view.startLoading(objects, bind(this.addObject, this));
+ },
+
+ addObject: function(object) {
+ var objectBox;
+
+ if(object instanceof Attr) {
+ objectBox = this.view.createObjectBox(object.ownerElement);
+
+ // get the nodes that contains the attributes
+ var attributeNodes = getElementsByClass(objectBox, "nodeAttr");
+
+ // return the node that contains the attribute we are looking for
+ var attr = Array.filter(attributeNodes, function(attributeNode) {
+ // the node containing the attribute name
+ return attributeNode.childNodes[1].textContent == object.name;
+ })[0];
+ if(attr) setClass(attr, "selected");
+ } else {
+ objectBox = this.view.createObjectBox(object);
+ }
+
+ this.box.appendChild(objectBox);
+ }
+});
+
+// ************************************************************************************************
+// Auto Completion
+
+Firebug.XPathPanel.xPathAutoCompleter = function(evaluator) {
+ this.autoCompleter = new Firebug.AutoCompleter(
+ null,
+ bind(this.getAutoCompleteRange, this),
+ bind(this.getAutoCompleteList, this),
+ true, true);
+
+ this.evaluator = evaluator;
+}
+
+Firebug.XPathPanel.xPathAutoCompleter.prototype = {
+
+ reset: function() {
+ this.autoCompleter.reset();
+ },
+
+ complete: function(context, textbox, cycle, reverse) {
+ this.autoCompleter.complete(context, textbox, cycle, reverse);
+ },
+
+ getAutoCompleteRange: function(value, offset, context) {
+
+ var preSelection = value.substring(0, offset);
+ var result;
+ var tokens = [];
+
+ while ((result = selectedToken.exec(preSelection)) != null) {
+ tokens.unshift(result[0]);
+ }
+
+ var start;
+ var end;
+ if(tokens.length == 0) {
+ start = 0;
+ end = value.length;
+ } else if(nameTestToken.test(tokens[0])) {
+ start = preSelection.lastIndexOf(tokens[0]);
+ end = start + tokens[0].length
+ } else {
+ start = preSelection.lastIndexOf(tokens[0]) + tokens[0].length;
+ var postSelection = value.substring(start);
+ var firstTokenIndex = postSelection.search(selectedToken);
+ end = (firstTokenIndex == -1? value.length : start + firstTokenIndex);
+ }
+
+ return{start: start, end: end - 1};
+ },
+
+ getAutoCompleteList: function(preExpr, expr, postExpr, context) {
+ var result;
+ var token;
+ var previousToken = ""
+ var tokens = [];
+ var stack = [];
+ while ((result = xPathToken.exec(preExpr)) != null) {
+ token = result[0];
+ if(token == "[") {
+ stack.unshift({type: "predicate", name: tokens[0] || ""});
+ } else if(token == "(") {
+ if(functionNameToken.test(previousToken))
+ stack.unshift({type: "function", name: tokens[0] || ""});
+ else
+ stack.unshift({type: "bracket", name: tokens[0] || ""});
+ } else if(token == "]" || token == ")") {
+ stack.shift();
+ }
+ tokens.unshift(token);
+ previousToken = token
+ }
+
+ var returning = [];
+
+ var inside = (stack.length > 0? stack[0].type: null);
+
+ if (!tokens.length) {
+ // At the begining
+ returning = extendArrays(["/"], this.getNodeList(expr, inside, tokens, true) || [], this.Functions);
+ } else if(tokens[0] == "|") {
+ // After union operator
+ returning = this.getNodeList(expr, inside, tokens, true) || [];
+ } else if(tokens[0] == "::" || tokens[0] == "@") {
+ // After an Axis
+ returning = this.getNodeList(expr, inside, tokens, false) || [];
+ } else if(include(["/", "//"], tokens[0])) {
+ // After a separator or the root
+ returning = this.getNodeList(expr, inside, tokens, true) || [];
+ } else if(include(["(", ",", "["], tokens[0]) ||
+ // If the token is * or name operator, we make sure it's actually an operator
+ (tokens[0] != "*" && !include(this.NameOperators, tokens[0]) && include(this.Operators, tokens[0])) ||
+ ((tokens[0] == "*" || include(this.NameOperators, tokens[0]))
+ && tokens[1] != undefined && !include(extendArrays(["@", "::", "(", "["], this.Operators), tokens[1]))
+ ) {
+ // After "(", "," "[" or an operator
+ var nodeList = this.getNodeList(expr, inside, tokens, true);
+ if(nodeList)
+ returning = extendArrays(["/"], nodeList, this.Functions);
+ } else if(expressionToken.test(tokens[0])) {
+ // After an expression
+ returning = this.getOperators(tokens[0], inside);
+ } else{
+ return [];
+ }
+ if(tokens[0] == "." && returning.length > 0 && !include(returning, ".")) returning.unshift(".");
+ if(tokens[0] == "/" && returning.length > 0 && !include(returning, "/")) returning.unshift("/");
+
+ return returning;
+ },
+
+ getNodeList: function(expr, inside, tokens, addAxes) {
+ var flattenedExpr = this.flattenExpression(tokens);
+ var returning = [];
+
+ var result = this.evaluator.evaluateExpression(flattenedExpr + "*");
+ var attribute = false;
+ if(!result) return null;
+
+ if(endsWith(flattenedExpr, "@") || endsWith(flattenedExpr, "attribute::")) {
+ result.forEach(function(attribute) {
+ add(returning, attribute.name);
+ });
+ attribute = true;
+ this.addNamespacesAndSort(returning);
+ if(returning.length > 0) returning.unshift("*");
+ } else {
+ result.forEach(function(node) {
+ add(returning, getTagName(node));
+ });
+ this.addNamespacesAndSort(returning);
+ if(this.evaluator.hasResult(flattenedExpr + "comment()")) returning.push("comment()");
+ if(this.evaluator.hasResult(flattenedExpr + "node()")) returning.push("node()");
+ if(this.evaluator.hasResult(flattenedExpr + "text()")) returning.push("text()");
+ if(addAxes) {
+ var parentExpression = flattenedExpr.replace(/\/$/, "");
+ parentExpression += (endsWith(parentExpression, "/")?"descendant-or-self::node()":"");
+ parentExpression = parentExpression || "/";
+ result = this.evaluator.evaluateExpression(parentExpression);
+ if(result) {
+ var hasSelf = result.some(function(node) {return node.nodeType == 1;});
+ var hasAttribute = result.some(function(node) {return node.hasAttributes();});
+ var hasAncestor = result.some(function(node) {return node.parentNode && node.parentNode.nodeType==1;});
+ var hasDescendant = result.some(function(node) {return node.hasChildNodes();});
+ var hasFollowingSibling = result.some(function(node) {return hasNextSibling(node);});
+ var hasFollowing = hasFollowingSibling || result.some(function(node) {return hasFollowingNode(node);});
+ var hasPrecedingSibling = result.some(function(node) {return hasPreviousSibling(node);});
+ var hasPreceding = hasPrecedingSibling || result.some(function(node) {return hasPrecedingNode(node);});
+ returning.push(".");
+ if(hasAttribute) returning.push("@");
+ if(hasAncestor) returning.push("..");
+ if(hasAncestor) returning.push("ancestor::");
+ if(hasSelf || hasAncestor)returning.push("ancestor-or-self::");
+ if(hasDescendant) returning.push("descendant::");
+ if(hasSelf || hasDescendant)returning.push("descendant-or-self::");
+ if(hasFollowing) returning.push("following::");
+ if(hasFollowingSibling) returning.push("following-sibling::");
+ if(hasAncestor) returning.push("parent::");
+ if(hasPreceding) returning.push("preceding::");
+ if(hasPrecedingSibling) returning.push("preceding-sibling::");
+ if(hasDescendant) returning.push("child::");
+ if(hasSelf)returning.push("self::");
+ if(hasAttribute) returning.push("attribute::");
+
+ if(hasDescendant) returning.unshift("*");
+ }
+ } else {
+ if(returning.length > 0) returning.unshift("*");
+ }
+ }
+
+ if(returning.indexOf(expr) != -1) {
+ returning.unshift(expr + " ");
+ if(!attribute) returning.unshift(expr + "/", expr+"[");
+ if(inside == "function") returning.unshift(expr + ",", expr + ")");
+ if(inside == "bracket") returning.unshift(expr + ")");
+ if(inside == "predicate") returning.unshift(expr + "]");
+ if(!attribute) returning.unshift(expr+"|");
+ }
+ return returning;
+ },
+
+ addNamespacesAndSort: function(returning) {
+ var namespaces = [];
+ for each(var node in returning) {
+ if(getNamespace.test(node)) {
+ add(namespaces, getNamespace.exec(node)[1] + "*");
+ }
+ }
+ returning.push.apply(returning, namespaces);
+ returning.sort();
+ },
+
+ getOperators: function(token, inside) {
+ var returning;
+ if((/\)|"[^"]*"|'[^']*'|\d+(?:\.\d?)?|\.\d+/).test(token))
+ returning = cloneArray(this.NumberAndBooleanOperators);
+ else
+ returning = extendArrays(this.NodeSetOperators, this.NumberAndBooleanOperators);
+
+ if(inside == "function") returning.unshift(",", ")");
+ if(inside == "bracket") returning.unshift(")");
+ if(inside == "predicate") returning.unshift("]");
+
+ return returning;
+ },
+
+ flattenExpression: function(tokens) {
+ var inPredicate = 0;
+ var inFunctionOrBracket = 0;
+ var clone = cloneArray(tokens);
+ var token;
+ var flattenedExpression = [];
+
+ // Piece of code used twice in the method
+ function inPF() {
+ if(token =="]") inPredicate++;
+ else if(token =="[") inPredicate--;
+ else if(token ==")") inFunctionOrBracket++;
+ else if(token =="(") inFunctionOrBracket--;
+ }
+
+ while(token = clone.shift()) {
+ // If we are in a function or predicate
+ if(inPredicate == 0 && inFunctionOrBracket == 0 && (
+ include(["(", ",", "["], token) ||
+ // If the token is * or name operator, we make sure it's actually an operator
+ (token != "*" && !include(this.NameOperators, token) && include(this.NumberAndBooleanOperators, token)) ||
+ ((token == "*" || include(this.NameOperators, token))
+ && clone[0] != undefined && !include(extendArrays(["@", "::", "(", "["], this.Operators), clone[0]))
+ )
+ )
+ {
+ //if the last token is the root
+ if(include(["/", "//"], flattenedExpression[0]))
+ break;
+
+ // Find the end of the function or predicate
+ do {
+ inPF();
+ if(inPredicate < 0 || inFunctionOrBracket < 0) break;
+ } while(token = clone.shift());
+
+ // Remove the function name (if in a function)
+ if(inFunctionOrBracket < 0) {
+ if(functionNameToken.test(clone[0]))
+ token = clone.shift();
+ else {
+ inFunctionOrBracket = 0;
+ inPredicate = 0;
+ continue;
+ }
+ }
+
+ // Reset inFunction and inPredicate
+ inFunctionOrBracket = 0;
+ inPredicate = 0;
+
+ flattenedExpression.unshift("/");
+ } else {
+ inPF();
+ if(token =="|") break;
+ flattenedExpression.unshift(token);
+ }
+ }
+ return flattenedExpression.join(" ");
+ },
+
+ NodeSetOperators: [
+ "/",
+ "//",
+ "|",
+ "["
+ ],
+
+ NumberAndBooleanOperators: [
+ "and ",
+ "or ",
+ "< ",
+ "<= ",
+ "> ",
+ ">= ",
+ "= ",
+ "+ ",
+ "- ",
+ "* ",
+ "div ",
+ "mod "
+ ],
+
+ Operators: [
+ "and",
+ "or",
+ "<",
+ "<=",
+ ">",
+ ">=",
+ "=",
+ "+",
+ "-",
+ "*",
+ "div",
+ "mod",
+ "/",
+ "//",
+ "|"
+ ],
+
+ NameOperators: [
+ "and",
+ "or",
+ "div",
+ "mod"
+ ],
+
+ Functions: [
+ "concat(",
+ "substring(",
+ "substring-after(",
+ "substring-before(",
+ "string(",
+ "local-name(",
+ "name(",
+ "namespace-uri(",
+ "normalize-space(",
+ "translate(",
+ "ceiling(",
+ "count(",
+ "floor(",
+ "last()",
+ "number(",
+ "position()",
+ "round(",
+ "sum(",
+ "boolean(",
+ "contains(",
+ "false()",
+ "lang(",
+ "not(",
+ "starts-with(",
+ "string-length(",
+ "true()",
+ ]
+}
+
+// ************************************************************************************************
+// Auto Completion Helpers
+
+function extendArrays(){
+ var newArray = [];
+ for (var i = 0; i < arguments.length; i++) {
+ newArray.push.apply(newArray, arguments[i]);
+ }
+ return newArray;
+}
+
+function include (arrayOrString, value) {
+ try{
+ return arrayOrString.indexOf(value) != -1;
+ } catch(e) {
+ return false;
+ }
+}
+
+function add(array, value) {
+ if(!include(array, value)) {
+ array.push(value);
+ }
+}
+
+function endsWith(string, end) {
+ return string.search(end+"$") != -1;
+}
+
+function hasNextSibling(node) {
+ return !!getNextElement(node.nextSibling)
+}
+
+function hasPreviousSibling(node) {
+ return !!getPreviousElement(node.previousSibling)
+}
+
+function hasFollowingNode(node) {
+ var parent = node;
+ while(parent = parent.parentNode) {
+ if(hasNextSibling(parent))
+ return true;
+ }
+ return false;
+}
+
+function hasPrecedingNode(node) {
+ var parent = node;
+ while(parent = parent.parentNode) {
+ if(hasPreviousSibling(parent))
+ return true;
+ }
+ return false;
+}
+
+// ************************************************************************************************
+// Stepper use to simulate a thread and prevent the UI from freezing
+
+function Stepper(iterator, step, end, timePeriod){
+ this.iterator = iterator
+ this.step = step;
+ this.timePeriod = Math.max(1, timePeriod || defaultTimePeriod);
+ this.end = end;
+ this.stopped = true;
+ this.execute = bind(this.execute, this);
+}
+
+Stepper.prototype = {
+ start: function() {
+ this.timeoutID = setTimeout(this.execute, 0);
+ },
+
+ execute: function() {
+ try {
+ for(var i = 0, date = new Date(); i < this.timePeriod; i = Date.now() - date) {
+ this.step(this.iterator.next());
+ }
+ this.timeoutID = setTimeout(this.execute, 0);
+ } catch(e) {
+ setTimeout(this.end, 0);
+ }
+ },
+
+ stop: function() {
+ clearTimeout(this.timeoutID);
+ this.iterator.close();
+ }
+}
+
+// ************************************************************************************************
+// location highlighting module
+
+Firebug.XPathPanel.LocationHighlightModule = extend(Firebug.Module,
+{
+ // add event listener
+ initializeUI: function(detachArgs) {
+ this.addLocationListener(FirebugChrome);
+ },
+
+ shutdown: function() {
+ this.removeLocationListener(FirebugChrome);
+ },
+
+ addLocationListener: function(chrome) {
+ chrome.$("fbLocationList").addEventListener("mouseover", this.onLocationMouseOver, false);
+ chrome.$("fbLocationList").addEventListener("mouseout", this.onLocationMouseOut, false);
+ },
+
+ removeLocationListener: function(chrome) {
+ chrome.$("fbLocationList").removeEventListener("mouseover", this.onLocationMouseOver, false);
+ chrome.$("fbLocationList").removeEventListener("mouseout", this.onLocationMouseOut, false);
+ },
+
+ onLocationMouseOver: function(event) {
+ var panel = FirebugContext.browser.chrome.getSelectedPanel();
+
+ if(panel.name == panelName) {
+ var repObject = event.originalTarget.repObject;
+ if(repObject && repObject.frameElement) {
+ Firebug.Inspector.highlightObject(repObject.frameElement, FirebugContext);
+ }
+ }
+ },
+
+ onLocationMouseOut: function() {
+ Firebug.Inspector.highlightObject(null);
+ }
+})
+
+// ************************************************************************************************
+// result highlighting module
+
+Firebug.XPathPanel.ResultHighlightModule = extend(Firebug.Module,
+{
+ initialize: function() {
+ this.highlighted = false;
+ this.highlightedElement = [];
+ },
+
+ destroyContext: function(context, persistedState) {
+ persistedState.persistedXPathResultNotHighlighted = !!context.xPathResultNotHighlighted;
+ },
+
+ loadedContext: function(context) {
+ if(context.persistedState)
+ context.xPathResultNotHighlighted = context.persistedState.persistedXPathResultNotHighlighted
+ },
+
+ reattachContext: function(browser, context) {
+ this.highlightButton = browser.chrome.$("FireXPathBarHighlightButton");
+ },
+
+ showContext: function(browser, context) {
+ this.highlightButton = browser.chrome.$("FireXPathBarHighlightButton");
+ this.refreshHighlightButton(context.getPanel(panelName));
+ },
+
+ hideUI: function() {
+ this.clear();
+ },
+
+ showPanel: function(browser, panel) {
+ if(panel.name == panelName) {
+ this.highlightButton.collapsed = false;
+ if(!panel.context.xPathResultNotHighlighted)
+ this.highlight(panel.context);
+ } else {
+ this.highlightButton.collapsed = true;
+ this.clear();
+ }
+ },
+
+ refreshHighlightButton: function(panel) {
+ if(panel.evaluationResult != null && isArray(panel.evaluationResult) &&
+ panel.evaluationResult.some(function(e) {return e.nodeType == 1 && e.namespaceURI == null}))
+ {
+ this.highlightButton.setAttribute("checked", !panel.context.xPathResultNotHighlighted);
+ this.highlightButton.setAttribute("disabled", false);
+ } else {
+ this.highlightButton.setAttribute("checked", false);
+ this.highlightButton.setAttribute("disabled", true);
+ }
+ },
+
+ // call when the result has changed
+ refreshHighlight: function(panel) {
+ this.refreshHighlightButton(panel);
+ if(!panel.context.xPathResultNotHighlighted) {
+ this.clear();
+ this.highlight(panel.context);
+ }
+ },
+
+ toggleHighlight: function(context) {
+ if(!context.xPathResultNotHighlighted) {
+ this.clear();
+ } else {
+ this.highlight(context);
+ }
+ context.xPathResultNotHighlighted = !context.xPathResultNotHighlighted
+ this.highlightButton.setAttribute("checked", !context.xPathResultNotHighlighted);
+ },
+
+ highlight: function(context) {
+ var panel = context.getPanel(panelName);
+ var doc = panel.location.document;
+ var resultElement = panel.evaluationResult;
+
+ var isCSSAttached = doc.getElementById("firexpath-matching-node-style");
+
+ if(!isCSSAttached) {
+ var head = doc.getElementsByTagName("head")[0]
+ if(head) {
+ if(!context.fireXPathStyleSheet) {
+ var styleSheet = document.createElementNS("http://www.w3.org/1999/xhtml", "style");
+ styleSheet.firebugIgnore = true;
+ styleSheet.setAttribute("type", "text/css");
+ styleSheet.setAttribute("id", "firexpath-matching-node-style");
+ styleSheet.innerHTML =
+ ".firexpath-matching-node {" +
+ " outline: 2px dashed #00F;" +
+ "}";
+ context.fireXPathStyleSheet = styleSheet;
+ }
+ head.appendChild(context.fireXPathStyleSheet);
+ } else {
+ // if we can't attach the stylesheet there is no need to highlighting the result
+ // for instance in XML documents
+ return;
+ }
+ }
+
+ var step = bind(function(element) {
+ if(element.nodeType == 1 && isVisible(element)) {
+ setClass(element, "firexpath-matching-node");
+ this.highlightedElement.push(element);
+ }
+ }, this);
+ var end = bind(function() {
+ delete this.stepper;
+ }, this);
+
+ this.stepper = new Stepper(arrayIterator(resultElement), step, end);
+ this.stepper.start();
+ },
+
+ clear: function(doc) {
+ // clear the stylesheet from the doc before evaluating an XPath expression
+ if(doc) {
+ var styleSheet = doc.getElementById("firexpath-matching-node-style");
+ if(styleSheet) {
+ styleSheet.parentNode.removeChild(styleSheet);
+ }
+ }
+
+ if(this.stepper) {
+ this.stepper.stop();
+ delete this.stepper;
+ }
+ var element;
+ while(element = this.highlightedElement.pop()) {
+ removeClass(element, "firexpath-matching-node");
+ if (element.className.length === 0) {
+ element.removeAttribute("class");
+ }
+ }
+ }
+})
+
+// ************************************************************************************************
+// Overwrite inspector to make sure it stay on the XPath tab (instead of going to the HTML tab)
+
+overwriteMethodForPanel(
+ Firebug.Inspector,
+ "startInspecting",
+ panelName,
+ function (context) {
+ if (this.inspecting || !context || !context.loaded)
+ return;
+
+ this.inspecting = true;
+ this.inspectingContext = context;
+
+ context.chrome.setGlobalAttribute("cmd_toggleInspecting", "checked", "true");
+ this.attachInspectListeners(context);
+
+ // Remember the previous panel and bar state so we can revert if the user cancels
+ this.previousPanelName = context.panelName;
+ this.previousSidePanelName = context.sidePanelName;
+ this.previouslyCollapsed = $("fbContentBox").collapsed;
+ this.previouslyFocused = context.detached && context.chrome.isFocused();
+
+ var panel;
+ if(this.previouslyCollapsed) {
+ panel = context.chrome.selectPanel("html");
+ this.previousObject = panel.selection;
+ } else {
+ panel = context.getPanel("xpath");
+ }
+
+ if (context.detached)
+ FirebugChrome.focus();
+ else
+ Firebug.showBar(true);
+
+ panel.panelNode.focus();
+ panel.startInspecting();
+
+ if (context.hoverNode)
+ this.inspectNode(context.hoverNode);
+ }
+);
+
+overwriteMethodForPanel(
+ Firebug.Inspector,
+ "stopInspecting",
+ panelName,
+ function(cancelled, waitForClick){
+ if (!this.inspecting)
+ return;
+
+ var context = this.inspectingContext;
+
+ if (this.inspectTimeout) {
+ context.clearTimeout(this.inspectTimeout);
+ delete this.inspectTimeout;
+ }
+
+ this.detachInspectListeners(context);
+ if (!waitForClick)
+ this.detachClickInspectListeners(context.window);
+
+ context.chrome.setGlobalAttribute("cmd_toggleInspecting", "checked", "false");
+
+ this.inspecting = false;
+
+ var htmlPanel = context.getPanel("html");
+ var xPathPanel = context.getPanel("xpath");
+
+ if (this.previouslyFocused)
+ context.chrome.focus();
+
+ if (cancelled) {
+ if (this.previouslyCollapsed)
+ Firebug.showBar(false);
+
+ if (this.previousPanelName == "html")
+ context.chrome.select(this.previousObject);
+ else
+ context.chrome.selectPanel(this.previousPanelName, this.previousSidePanelName);
+ }
+ else {
+ if(this.previouslyCollapsed) {
+ context.chrome.select(htmlPanel.selection);
+ context.chrome.getSelectedPanel().panelNode.focus();
+ }
+ }
+
+ if(this.previouslyCollapsed)
+ htmlPanel.stopInspecting(htmlPanel.selection, cancelled);
+ else
+ xPathPanel.stopInspecting(cancelled);
+
+ this.inspectNode(null);
+
+ delete this.previousObject;
+ delete this.previousPanelName;
+ delete this.previousSidePanelName;
+ delete this.inspectingContext;
+ }
+);
+
+Firebug.registerPanel(Firebug.XPathPanel);
+Firebug.registerModule(Firebug.XPathPanel.LocationHighlightModule, Firebug.XPathPanel.ResultHighlightModule);
+if(Firebug.registerUIListener) Firebug.registerUIListener(Firebug.XPathPanel.ResultHighlightModule);
+}});
\ No newline at end of file
diff --git a/content/bindings.xml b/content/bindings.xml
new file mode 100644
index 0000000..e8f0cca
--- /dev/null
+++ b/content/bindings.xml
@@ -0,0 +1,690 @@
+<?xml version="1.0"?>
+<!DOCTYPE bindings SYSTEM "chrome://firexpath/locale/FireXPath.dtd">
+<!--
+ Copyright (C) 2009 Pierre Tholence, DB4ALL
+
+ This file is part of FireXPath
+
+ FireXPath is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ FireXPath 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 FireXPath. If not, see <http://www.gnu.org/licenses/>.
+-->
+<bindings xmlns="http://www.mozilla.org/xbl"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <binding id="xPath-textbox" extends="chrome://global/content/bindings/textbox.xml#textbox">
+ <implementation>
+
+ <field name="_xPathBar">null</field>
+ <property name="xPathBar" readonly="true">
+ <getter><![CDATA[
+ return this._xPathBar ?
+ this._xPathBar : this._xPathBar = document.getBindingParent(this);
+ ]]></getter>
+ </property>
+
+ <method name="setStatus">
+ <parameter name="status"/>
+ <parameter name="info"/>
+ <body><![CDATA[
+ if(status)
+ this.setAttribute("status", status);
+ else
+ this.removeAttribute("status");
+ this.tooltipText = info || "";
+ ]]></body>
+ </method>
+
+ <method name="checkSyntax">
+ <body><![CDATA[
+ if(this.value != "" && this.xPathBar.getXPathExpression(this.value) == null)
+ this.setStatus("error", FBL.$STR_XP("invaliXPathError"));
+ else
+ this.setStatus();
+ ]]></body>
+ </method>
+
+ <method name="setValue">
+ <parameter name="value"/>
+ <body><![CDATA[
+ this.value = value;
+ this.checkSyntax();
+ ]]></body>
+ </method>
+ </implementation>
+ <handlers>
+ <handler event="input"><![CDATA[
+ this.checkSyntax();
+ this.xPathBar.autoCompleter.reset();
+ ]]></handler>
+ <handler event="keypress"><![CDATA[
+ switch (event.keyCode) {
+ case KeyEvent.DOM_VK_RETURN:
+ this.xPathBar.evaluate();
+ break;
+ case KeyEvent.DOM_VK_UP:
+ this.xPathBar.autoCompleter.complete(FirebugContext, this, true, true);
+ this.checkSyntax();
+ break;
+ case KeyEvent.DOM_VK_TAB:
+ event.preventDefault();
+ case KeyEvent.DOM_VK_DOWN:
+ this.xPathBar.autoCompleter.complete(FirebugContext, this, true, false);
+ this.checkSyntax();
+ break;
+ }
+ ]]></handler>
+ </handlers>
+ </binding>
+
+ <binding id="xPathBar" extends="xul:hbox">
+ <content>
+ <xul:toolbarseparator/>
+ <xul:toolbaritem align="center" flex="6">
+ <xul:label value="&FireXPath.xPath.label;" control="xPathBar-xpath-textbox" accesskey="&FireXPath.xPath.key;"/>
+ <xul:hbox class="xpath-field-container" flex="1">
+ <xul:textbox anonid="xpath-textbox" id="xPathBar-xpath-textbox" class="xpath-textbox" minwidth="100" flex="1"/>
+ </xul:hbox>
+ </xul:toolbaritem>
+ <xul:toolbaritem anonid="xpath-context-toolbar" align="center" flex="1">
+ <xul:label value="&FireXPath.context.label;" control="xPathBar-xpath-context-textbox" accesskey="&FireXPath.context.key;"/>
+ <xul:hbox class="xpath-field-container" flex="1">
+ <xul:textbox anonid="xpath-context-textbox" id="xPathBar-xpath-context-textbox" class="xpath-textbox" flex="1"/>
+ </xul:hbox>
+ <xul:textbox anonid="xpath-context-node-number-textbox"
+ type="number"
+ size="1"
+ min="1"
+ onchange="document.getBindingParent(this).evaluate();"
+ collapsed="true"
+ increment="1"/>
+ </xul:toolbaritem>
+ <xul:toolbarbutton label="&FireXPath.eval.button;" oncommand="document.getBindingParent(this).evaluate();">
+ </xul:toolbarbutton>
+ </content>
+ <implementation>
+
+ <!--******************************************************************************************************
+ fields and properties
+ -->
+
+ <field name="NSResolver">null</field>
+ <field name="xPathContextNodes">null</field>
+ <field name="lastXPath">null</field>
+ <field name="lastContext">null</field>
+ <field name="lastXPathContext">null</field>
+ <field name="_evaluator">null</field>
+
+ <property name="autoCompleter" readonly="true">
+ <getter><![CDATA[
+ return this._autoCompleter ?
+ this._autoCompleter : this._autoCompleter = new Firebug.XPathPanel.xPathAutoCompleter(this);
+ ]]></getter>
+ </property>
+
+ <property name="evaluator" readonly="true">
+ <getter><![CDATA[
+ return this._evaluator ?
+ this._evaluator : this._evaluator = new XPathEvaluator();
+ ]]></getter>
+ </property>
+
+ <property name="xPath">
+ <getter><![CDATA[
+ return this.getElement("xpath-textbox").value;
+ ]]></getter>
+ <setter><![CDATA[
+ this.getElement("xpath-textbox").setValue(val);
+ return val;
+ ]]></setter>
+ </property>
+
+ <property name="xPathContext">
+ <getter><![CDATA[
+ var textbox = this.getElement("xpath-context-textbox");
+ if(textbox.value == "")
+ textbox.value = "/";
+ return textbox.value;
+ ]]></getter>
+ <setter><![CDATA[
+ this.getElement("xpath-context-textbox").setValue(val);
+ return val;
+ ]]></setter>
+ </property>
+
+ <property name="xPathContextNodeNumber" readonly="true">
+ <getter><![CDATA[
+ return this.getElement("xpath-context-node-number-textbox");
+ ]]></getter>
+ </property>
+
+ <property name="xPathContextToolbar" readonly="true">
+ <getter><![CDATA[
+ return this.getElement("xpath-context-toolbar");
+ ]]></getter>
+ </property>
+
+ <property name="highlightButton" readonly="true">
+ <getter><![CDATA[
+ return this.getElement("highlight-button");
+ ]]></getter>
+ </property>
+
+ <property name="xPathPanel" readonly="true">
+ <getter><![CDATA[
+ return FirebugContext.getPanel(Firebug.XPathPanel.prototype.name);
+ ]]></getter>
+ </property>
+
+ <!--******************************************************************************************************
+ Constructor
+ -->
+
+ <constructor><![CDATA[
+ this.xPathContextToolbar.collapsed = !Firebug.getPref(Firebug.prefDomain, "xpath.showXPathContext");
+ ]]></constructor>
+
+ <!--******************************************************************************************************
+ State management method
+ -->
+
+ <method name="init">
+ <parameter name="context"/>
+ <body><![CDATA[
+ this.NSResolver = this.createNSResolver(context.window.document);
+ this.xPath = "";
+ this.xPathContext = "/";
+ this.xPathContextNodeNumber.collapsed = true;
+ this.xPathContextNodes = null;
+ this.lastXPath = "";
+ this.lastContext = null;
+ context.xPathBarInit = true;
+ ]]></body>
+ </method>
+
+ <method name="show">
+ <parameter name="context"/>
+ <parameter name="show"/>
+ <body><![CDATA[
+ this.collapsed = !show;
+ if(show) {
+ this.currentContext = context;
+ if(!context.xPathBarInit)
+ this.init(context);
+ else
+ this.restore(context);
+ var that = this
+ setTimeout(function() {
+ that.getElement("xpath-textbox").focus();
+ },10)
+ } else {
+ if(context == this.currentContext)
+ this.persiste(context);
+ }
+ ]]></body>
+ </method>
+
+ <method name="persiste">
+ <parameter name="state"/>
+ <body><![CDATA[
+ if(!state.xPathBarState) state.xPathBarState = {};
+ this.copyState(this, state.xPathBarState);
+ ]]></body>
+ </method>
+
+ <method name="restore">
+ <parameter name="state"/>
+ <body><![CDATA[
+ this.copyState(state.xPathBarState, this)
+ ]]></body>
+ </method>
+
+ <method name="copyState">
+ <parameter name="from"/>
+ <parameter name="to"/>
+ <body><![CDATA[
+ to.xPath = from.xPath;
+ to.xPathContext = from.xPathContext;
+ if(!to.xPathContextNodeNumber) to.xPathContextNodeNumber = {};
+ to.xPathContextNodeNumber.value = from.xPathContextNodeNumber.value;
+ to.xPathContextNodeNumber.max = from.xPathContextNodeNumber.max;
+ to.xPathContextNodeNumber.collapsed = from.xPathContextNodeNumber.collapsed;
+ to.xPathContextNodes = from.xPathContextNodes;
+ to.NSResolver = from.NSResolver;
+ to.lastXPath = from.lastXPath;
+ to.lastContext = from.lastContext;
+ ]]></body>
+ </method>
+
+ <!--******************************************************************************************************
+ XPath Evaluation methods
+ -->
+
+ <method name="evaluate">
+ <parameter name="reevaluate"/>
+ <body><![CDATA[
+ var xPath = this.xPath;
+ if(reevaluate)
+ xPath = this.lastXPath;
+
+ var context = this.getContextNode(reevaluate);
+ if(xPath != "") {
+
+ if(!reevaluate && this.lastXPath == xPath && this.lastContext == context)
+ return;
+
+ var contextTextbox = this.getElement("xpath-context-textbox");
+ if(context) {
+ contextTextbox.setStatus();
+ if(FBL.trimLeft(this.xPath).indexOf("/") == 0 && FBL.trimLeft(this.xPathContext) != "/") {
+ context = null;
+ contextTextbox.setStatus("warning", FBL.$STR_XP("unusedParentNode"));
+ }
+ }
+
+ var result = this._evaluateExpression(this.xPath, context);
+ this.lastXPath = this.xPath;
+ this.lastContext = context;
+ this.xPathPanel.setResult(context, result);
+ } else {
+ this.lastXPath = "";
+ this.xPathPanel.setResult(context, null);
+ }
+ ]]></body>
+ </method>
+
+ <method name="getXPathExpression">
+ <parameter name="xPath"/>
+ <body><![CDATA[
+ try {
+ return this.evaluator.createExpression(xPath, this.NSResolver);
+ } catch (e) {
+ return null;
+ }
+ ]]></body>
+ </method>
+
+ <method name="_evaluateExpression">
+ <parameter name="xPath"/>
+ <parameter name="contextNode"/>
+ <body><![CDATA[
+ Firebug.XPathPanel.ResultHighlightModule.clear(this.xPathPanel.location.document);
+ var xPathExpression = this.getXPathExpression(xPath);
+ if(!xPathExpression) return new Error("invaliXPathError");
+
+ var result;
+ try{
+ result = xPathExpression.evaluate(contextNode || this.xPathPanel.location.document, XPathResult.ANY_TYPE, null);
+ } catch(e) {
+ return new Error("invaliXPathError");
+ }
+ return this.processResult(result);
+ ]]></body>
+ </method>
+
+ <method name="createNSResolver">
+ <parameter name="doc"/>
+ <body><![CDATA[
+ var prefixToNS = this.getMappingPrefixNS(doc);
+ return function(prefix) {
+ return prefixToNS[prefix] || null;
+ }
+ ]]></body>
+ </method>
+
+ <method name="getMappingPrefixNS">
+ <parameter name="doc"/>
+ <body><![CDATA[
+ var result = doc.evaluate("//*[namespace-uri() != '']", doc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
+ var nsToPrefix = {};
+ var prefixToNS = {};
+ for ( var i=0, L=result.snapshotLength ; i < L; i++ ) {
+ var node = result.snapshotItem(i);
+ var ns = node.namespaceURI;
+ if(ns) {
+ var prefix = node.lookupPrefix(ns);
+ if(!prefix) {
+ if(!(ns in nsToPrefix)) {
+ nsToPrefix[ns] = FBL.getPrefixFromNS(ns);
+ }
+ prefix = nsToPrefix[ns];
+ }
+ if(!(prefix in prefixToNS))
+ prefixToNS[prefix] = ns;
+ }
+ }
+ return prefixToNS;
+ ]]></body>
+ </method>
+
+ <method name="getContextNode" readonly="true">
+ <parameter name="reevaluate"/>
+ <body><![CDATA[
+ if(this.xPathContextToolbar.collapsed)
+ return null;
+ if(!reevaluate && this.xPathContextNodes && this.lastXPathContext == this.xPathContext)
+ return this.xPathContextNodes[this.xPathContextNodeNumber.value - 1];
+ else {
+ var nodes = this._evaluateExpression(this.xPathContext);
+
+ this.lastXPathContext = this.xPathContext;
+
+ if(FBL.isArray(nodes) && nodes.length > 0) {
+ if(!reevaluate) {
+ this.xPathContextNodeNumber.collapsed = (nodes.length == 1);
+ this.xPathContextNodeNumber.max = nodes.length;
+ this.xPathContextNodeNumber.value = 1;
+ this.xPathContextNodes = nodes;
+ this.getElement("xpath-context-textbox").setStatus();
+ return nodes[0];
+ } else {
+ this.xPathContextNodes = nodes;
+ return nodes[this.xPathContextNodeNumber.value - 1];
+ }
+ } else {
+ this.xPathContextNodeNumber.collapsed = true;
+ this.xPathContextNodeNumber.value = 1;
+ this.xPathContextNodes = [null];
+ if(!(nodes instanceof Error))
+ this.getElement("xpath-context-textbox").setStatus("warning", FBL.$STR_XP("noParentNodeSelected"));
+ return null;
+ }
+ }
+ ]]></body>
+ </method>
+
+ <!--******************************************************************************************************
+ public methods, used by the panel
+ -->
+
+ <method name="setNode">
+ <parameter name="node"/>
+ <parameter name="noEval"/>
+ <body><![CDATA[
+ this.xPath = FBL.getXPathFromNode(node, this.getContextNode());
+ if(!noEval)
+ this.evaluate();
+ ]]></body>
+ </method>
+
+ <method name="setContextNode">
+ <parameter name="node"/>
+ <body><![CDATA[
+ this.xPathContext = FBL.getXPathFromNode(node);
+ this.evaluate();
+ ]]></body>
+ </method>
+
+ <method name="updateLocation">
+ <parameter name="location"/>
+ <body><![CDATA[
+ this.NSResolver = this.createNSResolver(location.document);
+ this.reset();
+ ]]></body>
+ </method>
+
+ <method name="reset">
+ <body><![CDATA[
+ delete this.lastXPath;
+ delete this.lastContext;
+ delete this.lastXPathContext;
+ ]]></body>
+ </method>
+
+ <method name="showXPathContext">
+ <parameter name="show"/>
+ <body><![CDATA[
+ this.xPathContextToolbar.collapsed = !show;
+ this.reset();
+ ]]></body>
+ </method>
+
+ <!--******************************************************************************************************
+ public methods, used by the autoCompleter
+ -->
+
+ <method name="hasResult">
+ <parameter name="xPath"/>
+ <body><![CDATA[
+ var xPathExpression = this.getXPathExpression(xPath);
+ if(!xPathExpression) return false;
+
+ var result;
+ try{
+ result = xPathExpression.evaluate(this.getContextNode() || this.xPathPanel.location.document, XPathResult.ANY_UNORDERED_NODE_TYPE, null);
+ } catch(e) {
+ return false;
+ }
+ return result.singleNodeValue != null;
+ ]]></body>
+ </method>
+
+ <method name="evaluateExpression">
+ <parameter name="xPath"/>
+ <body><![CDATA[
+ var xPathExpression = this.getXPathExpression(xPath);
+ if(!xPathExpression) return null;
+
+ var result;
+ try{
+ result = xPathExpression.evaluate(this.getContextNode() || this.xPathPanel.location.document, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
+ } catch(e) {
+ return null;
+ }
+ return this.processResult(result);
+ ]]></body>
+ </method>
+
+ <!--******************************************************************************************************
+ Helper Methods
+ -->
+
+ <method name="getElement">
+ <parameter name="aAnonymousID"/>
+ <body><![CDATA[
+ return document.getAnonymousElementByAttribute(this, "anonid", aAnonymousID);
+ ]]></body>
+ </method>
+
+ <method name="processResult">
+ <parameter name="evaluationResult"/>
+ <body><![CDATA[
+ switch(evaluationResult.resultType) {
+ case XPathResult.NUMBER_TYPE:
+ return evaluationResult.numberValue;
+ case XPathResult.STRING_TYPE:
+ return evaluationResult.stringValue;
+ case XPathResult.BOOLEAN_TYPE:
+ return evaluationResult.booleanValue;
+ default:
+ var result = [];
+ var node;
+ while (node = evaluationResult.iterateNext()) {
+ //make sure it is not a node created by firebug
+ if(node.firebugIgnore ||
+ (node.ownerElement && node.ownerElement.firebugIgnore) ||
+ (node.parentNode && node.parentNode.firebugIgnore) ||
+ node.id == "_firebugConsole")
+ continue;
+ result.push(node);
+ }
+ return result;
+ }
+ ]]></body>
+ </method>
+ </implementation>
+ </binding>
+
+ <binding id="xPathStatusBar" extends="xul:hbox">
+ <content>
+ <xul:statusbarpanel flex="1" align="center" pack="left">
+ <xul:image anonid="xpath-result-icon" class="xpath-result-icon"/>
+ <xul:label anonid="xpath-result-info" value=""/>
+ </xul:statusbarpanel>
+ <xul:statusbarpanel anonid="xpath-result-progressmeter-container" collapsed="true">
+ <xul:image src="chrome://firebug/skin/errorIcon.png"
+ onmousedown="document.getBindingParent(this).cancelLoading(FirebugContext)"
+ class="cancel-xpath-result-loading-button"/>
+ <xul:progressmeter anonid="xpath-result-progressmeter"/>
+ </xul:statusbarpanel>
+ </content>
+ <implementation>
+
+ <!--******************************************************************************************************
+ fields and properties
+ -->
+
+ <property name="progressMeter" readonly="true">
+ <getter><![CDATA[
+ return this.getElement("xpath-result-progressmeter");
+ ]]></getter>
+ </property>
+
+ <property name="progressMeterContainer" readonly="true">
+ <getter><![CDATA[
+ return this.getElement("xpath-result-progressmeter-container");
+ ]]></getter>
+ </property>
+
+ <property name="resultInfo">
+ <getter><![CDATA[
+ return this.getElement("xpath-result-info").value;
+ ]]></getter>
+ <setter><![CDATA[
+ this.getElement("xpath-result-info").value = val;
+ return val;
+ ]]></setter>
+ </property>
+
+ <property name="resultIcon">
+ <getter><![CDATA[
+ return this.getElement("xpath-result-icon").getAttribute("status");
+ ]]></getter>
+ <setter><![CDATA[
+ this.getElement("xpath-result-icon").setAttribute("status", val);
+ return val;
+ ]]></setter>
+ </property>
+
+ <!--******************************************************************************************************
+ ProgressMeter management
+ -->
+
+ <method name="startLoading">
+ <parameter name="context"/>
+ <parameter name="stepNumber"/>
+ <body><![CDATA[
+ context.xPathStatusBar.loading = true;
+ context.xPathStatusBar.stepNumber = stepNumber;
+ context.xPathStatusBar.currentStep = 0;
+ this.progressMeter.value = 0;
+ this.progressMeterContainer.collapsed = false;
+ ]]></body>
+ </method>
+
+ <method name="increaseLoading">
+ <parameter name="context"/>
+ <body><![CDATA[
+ context.xPathStatusBar.currentStep += 1;
+ if(context.xPathStatusBar.shown)
+ this.progressMeter.value = context.xPathStatusBar.currentStep / context.xPathStatusBar.stepNumber * 100;
+ ]]></body>
+ </method>
+
+ <method name="stopLoading">
+ <parameter name="context"/>
+ <body><![CDATA[
+ delete context.xPathStatusBar.loading;
+ this.progressMeterContainer.collapsed = true;
+ ]]></body>
+ </method>
+
+ <method name="cancelLoading">
+ <parameter name="context"/>
+ <body><![CDATA[
+ var panel = context.getPanel(Firebug.XPathPanel.prototype.name);
+ panel.stopLoading();
+ panel.xPathBar.reset();
+ this.resultInfo = this.resultInfo + " (" + FBL.$STR_XP("nodeSelectedMessage", context.xPathStatusBar.currentStep) + ")";
+ context.xPathStatusBar.resultInfo = this.resultInfo;
+ ]]></body>
+ </method>
+
+
+
+ <!--******************************************************************************************************
+ Result info management
+ -->
+
+ <method name="setResultInfo">
+ <parameter name="context"/>
+ <parameter name="status"/>
+ <parameter name="info"/>
+ <body><![CDATA[
+ context.xPathStatusBar.resultInfo = info;
+ this.resultInfo = info;
+ context.xPathStatusBar.resultIcon = status;
+ this.resultIcon = status;
+ ]]></body>
+ </method>
+
+ <method name="clearResultInfo">
+ <parameter name="context"/>
+ <body><![CDATA[
+ delete context.xPathStatusBar.resultInfo;
+ this.resultInfo = "";
+ delete context.xPathStatusBar.resultIcon;
+ this.resultIcon = "";
+ ]]></body>
+ </method>
+
+ <!--******************************************************************************************************
+ other public methods
+ -->
+
+ <method name="show">
+ <parameter name="context"/>
+ <parameter name="show"/>
+ <body><![CDATA[
+ if(!context.xPathStatusBar)
+ context.xPathStatusBar = {};
+ if(show) {
+ this.resultInfo = context.xPathStatusBar.resultInfo || "";
+ this.resultIcon = context.xPathStatusBar.resultIcon || "";
+ this.progressMeterContainer.collapsed = !context.xPathStatusBar.loading;
+ }
+ context.xPathStatusBar.shown = show;
+ this.collapsed = !show;
+ ]]></body>
+ </method>
+
+ <method name="reset">
+ <parameter name="context"/>
+ <body><![CDATA[
+ this.clearResultInfo(context);
+ ]]></body>
+ </method>
+
+ <!--******************************************************************************************************
+ Helper Methods
+ -->
+
+ <method name="getElement">
+ <parameter name="aAnonymousID"/>
+ <body><![CDATA[
+ return document.getAnonymousElementByAttribute(this, "anonid", aAnonymousID);
+ ]]></body>
+ </method>
+
+ </implementation>
+ </binding>
+</bindings>
\ No newline at end of file
diff --git a/content/firebugOverlay.xul b/content/firebugOverlay.xul
new file mode 100644
index 0000000..89cdce9
--- /dev/null
+++ b/content/firebugOverlay.xul
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!DOCTYPE overlay SYSTEM "chrome://firexpath/locale/FireXPath.dtd">
+<?xml-stylesheet href="chrome://firexpath/skin/FireXPath.css" type="text/css"?>
+
+<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://firexpath/content/XPathPanel.js" type="application/javascript"/>
+
+ <stringbundleset id="stringbundleset">
+ <stringbundle id="FireXpath_strings" src="chrome://firexpath/locale/FireXPath.properties"/>
+ </stringbundleset>
+
+ <popup id="contentAreaContextMenu">
+ <menuitem id="menu_fxpShow" label="&FireXPath.context.show.label;"
+ insertafter="context-viewpartialsource-selection"
+ oncommand="Firebug.toggleBar(true); FirebugChrome.selectPanel('xpath').updateSelection(document.popupNode)"
+ accesskey="X"/>
+ </popup>
+
+ <hbox id="fbToolbarInner">
+ <toolbarbutton id="FireXPathBarHighlightButton" collapsed="true" label="&FireXPath.highlight.button;" class="toolbar-text-button"
+ oncommand="Firebug.XPathPanel.ResultHighlightModule.toggleHighlight(FirebugContext);"
+ checked="false" disabled="true" insertafter="fbLocationList"/>
+ <xpathbar id="FireXPathBar" flex="1" collapsed="true" insertafter="fbLocationList"/>
+ </hbox>
+
+ <vbox id="fbContentBox">
+ <xpathstatusbar id="FireXPathStatusBar" insertafter="fbCommandBox" collapsed="true"/>
+ </vbox>
+
+</overlay>
\ No newline at end of file
diff --git a/defaults/preferences/FireXPathPref.js b/defaults/preferences/FireXPathPref.js
new file mode 100644
index 0000000..4d1ffb7
--- /dev/null
+++ b/defaults/preferences/FireXPathPref.js
@@ -0,0 +1,3 @@
+pref("extensions.firebug.xpath.showXPathContext", false);
+pref("extensions.firebug.xpath.generateAbsoluteXPath", false);
+pref("extensions.firebug.xpath.showDOM", true);
\ No newline at end of file
diff --git a/install.rdf b/install.rdf
new file mode 100644
index 0000000..fa776eb
--- /dev/null
+++ b/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>FireXPath at pierre.tholence.com</em:id>
+ <em:version>0.9.1</em:version>
+ <em:type>2</em:type>
+
+ <!-- Firefox -->
+ <em:targetApplication>
+ <Description>
+ <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+ <em:minVersion>3.0</em:minVersion>
+ <em:maxVersion>3.5.*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <em:name>FireXPath</em:name>
+ <em:description>FireXPath is a Firebug extension that adds a development tool to edit, inspect and generate XPath expressions. </em:description>
+ <em:creator>Pierre Tholence</em:creator>
+ <em:iconURL>chrome://firexpath/content/FireXPath.png</em:iconURL>
+ </Description>
+</RDF>
diff --git a/license.txt b/license.txt
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/locale/en-US/FireXPath.dtd b/locale/en-US/FireXPath.dtd
new file mode 100644
index 0000000..5be5752
--- /dev/null
+++ b/locale/en-US/FireXPath.dtd
@@ -0,0 +1,7 @@
+<!ENTITY FireXPath.context.show.label "Inspect XPath">
+<!ENTITY FireXPath.xPath.label "XPath: ">
+<!ENTITY FireXPath.xPath.key "x">
+<!ENTITY FireXPath.context.label "Parent: ">
+<!ENTITY FireXPath.context.key "t">
+<!ENTITY FireXPath.highlight.button "Highlight">
+<!ENTITY FireXPath.eval.button "Eval">
\ No newline at end of file
diff --git a/locale/en-US/FireXPath.properties b/locale/en-US/FireXPath.properties
new file mode 100644
index 0000000..de8de11
--- /dev/null
+++ b/locale/en-US/FireXPath.properties
@@ -0,0 +1,27 @@
+#result message
+invaliXPathError=Invalid XPath
+noResultError=No matching nodes
+oneResultMessage=1 matching node
+manyResultMessage=%S matching nodes
+oneWhiteSpaceMessage=1 whitespace node
+manyWhiteSpaceMessage=%S whitespace nodes
+nodeSelectedMessage=only %S nodes selected
+stringResultMessage=1 String
+numberResultMessage=1 Number
+booleanResultMessage=1 Boolean
+
+#option menu
+showXPathContextMenu=Show Parent toolbar
+generateAbsoluteXPathMenu=Generate absolute XPath
+showDOMMenu=Show Entire DOM
+
+#context menu
+topWindow=Top Window
+copyXPath=Copy XPath
+copyXPathFromContext=Copy XPath Starting at the Parent
+setXPath=Set as XPath
+setXPathContext=Set as Parent
+
+#input tooltip
+noParentNodeSelected=The expression doesn't match any node
+unusedParentNode=The parent node is not used because the XPath refer to the root (start with "/")
\ No newline at end of file
diff --git a/skin/classic/FireXPath.css b/skin/classic/FireXPath.css
new file mode 100644
index 0000000..f6c190a
--- /dev/null
+++ b/skin/classic/FireXPath.css
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2009 Pierre Tholence, DB4ALL
+ *
+ * This file is part of FireXPath
+ *
+ * FireXPath is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FireXPath 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 FireXPath. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+xpathbar {
+ -moz-binding: url("chrome://firexpath/content/bindings.xml#xPathBar");
+}
+
+xpathstatusbar {
+ -moz-binding: url("chrome://firexpath/content/bindings.xml#xPathStatusBar");
+ -moz-appearance: statusbar;
+ min-height:24px;
+}
+
+/**** xPathBar *****/
+.xpath-textbox {
+ -moz-binding: url("chrome://firexpath/content/bindings.xml#xPath-textbox");
+ -moz-appearance: none;
+ border: none;
+ margin: 0;
+}
+
+.xpath-textbox[status = "error"] {
+ background-color: #FF6666;
+ color: #FFFFFF;
+}
+
+.xpath-textbox[status = "warning"] {
+ background-color: yellow;
+}
+
+.xpath-field-container {
+ -moz-appearance: textfield;
+}
+
+/**** xPathStatusBar *****/
+.xpath-result-icon[status = "error"] {
+ list-style-image: url("chrome://firebug/skin/errorIcon.png");
+}
+
+.xpath-result-icon[status = "warning"] {
+ list-style-image: url("chrome://firebug/skin/warningIcon.png");
+}
+
+.xpath-result-icon[status = "ok"] {
+ list-style-image: url("chrome://firebug/skin/okIcon-sm.png");
+}
+
+.cancel-xpath-result-loading-button {
+ height:20px;
+ width:20px;
+ padding:2px;
+ cursor:pointer;
+ border:1px solid transparent;
+ -moz-border-radius : 3px;
+}
+
+.cancel-loading-button:hover {
+ border-color: Gray;
+}
diff --git a/skin/classic/xPathPanel.css b/skin/classic/xPathPanel.css
new file mode 100644
index 0000000..810c02b
--- /dev/null
+++ b/skin/classic/xPathPanel.css
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 Pierre Tholence, DB4ALL
+ *
+ * This file is part of FireXPath
+ *
+ * FireXPath is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FireXPath 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 FireXPath. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+.nodeBox.selected > .nodeLabel > .nodeLabelBox > .nodeAttr.selected,
+.nodeBox.selected > .nodeLabel > .nodeLabelBox > .nodeText.selected {
+ background-color: blue !important;
+}
+
+.nodeAttr.selected,
+.nodeText.selected,
+.nodeBox.selected > .nodeComment {
+ border-color: Highlight;
+ background-color: Highlight;
+ color: HighlightText !important;
+}
+
+.nodeAttr.selected > .nodeValue {
+ color: inherit !important;
+}
+
+.io-box-container {
+ overflow: auto;
+ width: 100%;
+ height: 100%;
+ background-color: #FFFFFF;
+ color: #000000;
+}
\ No newline at end of file
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/firexpath.git
More information about the Pkg-mozext-commits
mailing list