diff --git a/.gitignore b/.gitignore index 4d23c3c..e31026f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ src/settings.local.js static-files/mix-master-dialog /locale /src/locale/*.js +.idea/ +.DS_Store diff --git a/src/get-bookmarklet-url.js b/src/get-bookmarklet-url.js index 9f738cd..94b572c 100644 --- a/src/get-bookmarklet-url.js +++ b/src/get-bookmarklet-url.js @@ -8,6 +8,7 @@ var Webxray = (function() { baseURI = baseURI || document.baseURI; var baseCode = "(function(){var script=document.createElement('script');script.src='http://localhost:8000/webxray.js';script.className='webxray';document.head.appendChild(script);})();"; + var baseCode = '(function(){var doc=document.getElementsByTagName("iframe")[0],doc=doc?doc.contentDocument:document,script=doc.createElement("script");script.src="http://localhost:8000/webxray.js";script.className="webxray";doc.head.appendChild(script)})();'; var code = baseCode.replace('http://localhost:8000/', baseURI); return 'javascript:' + code; diff --git a/src/input.js b/src/input.js index 15e661f..4098dde 100644 --- a/src/input.js +++ b/src/input.js @@ -318,7 +318,8 @@ {key: 'RIGHT', cmd: 'redo'}, {key: 'UP', cmd: 'dom-ascend'}, {key: 'DOWN', cmd: 'dom-descend'}, - {key: 'P', cmd: 'uproot'} + {key: 'P', cmd: 'uproot'}, + {key: 'X', cmd: 'xpath'} ], showKeyboardHelp: function() { var help = jQuery.createKeyboardHelpReference(self.keyboardHelp); @@ -345,6 +346,11 @@ P: function() { persistence.saveHistoryToDOM(); jQuery.openUprootDialog(self); + }, + X: function() { + mixMaster.xpathForFocusedElement({ + input: self + }); } }); diff --git a/src/mix-master.js b/src/mix-master.js index 6985c71..3a96e46 100644 --- a/src/mix-master.js +++ b/src/mix-master.js @@ -95,6 +95,42 @@ html = '' + html + ''; return $(html); }, + createXpathFromElement: function xpathFromElement(doc, element) { + if (element == doc || !element) { + return "/"; + } + + var nodeList; + if (element.id) { + nodeList = doc.evaluate("//*[@id='" + element.id + "']", doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + if (nodeList.snapshotLength == 1) { + return "//*[@id='" + element.id + "']"; + } + } + + var parentSelector = "", + relativeElement = doc; + + if (element.parentNode != doc) { + parentSelector = xpathFromElement(doc, element.parentNode); + // TODO: extract our evaluate someplace so changes are easier + relativeElement = doc.evaluate(parentSelector, doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; + } + + nodeList = doc.evaluate(element.nodeName, relativeElement, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + + if (nodeList.snapshotLength == 1) { + return (parentSelector + "/" + element.nodeName).trim(); + } else { + for (var i = 0; i < nodeList.snapshotLength; ++i) { + if (element == nodeList.snapshotItem(i)) { + return (parentSelector + "/" + element.nodeName + "[" + (i + 1) + "]").trim(); + } + } + + throw new Error("Node not found by tag name"); + } + }, deleteFocusedElement: function deleteFocusedElement() { var elementToDelete = focused.getPrimaryElement(); if (elementToDelete) { @@ -126,6 +162,17 @@ open(url, 'info'); } }, + xpathForFocusedElement: function xpathForFocusedElement(options) { + var element = focused.getPrimaryElement(); + var xpath = self.createXpathFromElement(document, element); + jQuery.simpleModalDialog({ + input: options.input, + url: jQuery.webxraySettings.baseURI + 'xpath-dialog.html', + payload: JSON.stringify({ + xpath: xpath + }) + }); + }, replaceElement: function(elementToReplace, html) { var newContent = self.htmlToJQuery(html); runCommand("ReplaceWithCmd", { diff --git a/static-files/xpath-dialog.html b/static-files/xpath-dialog.html new file mode 100644 index 0000000..2ee561c --- /dev/null +++ b/static-files/xpath-dialog.html @@ -0,0 +1,28 @@ + + +Get Element XPath + +
+
+
Close +
+
+ +
+ +
+
+ +