- From: Travis Leithead <travis.leithead@microsoft.com>
- Date: Thu, 24 Nov 2011 22:23:25 +0000
- To: Robin Berjon <robin@berjon.com>
- CC: "public-device-apis@w3.org" <public-device-apis@w3.org>, Adrian Bateman <adrianba@microsoft.com>
- Message-ID: <9768D477C67135458BF978A45BCF9B38381BB8F6@TK5EX14MBXW602.wingroup.windeploy.ntde>
Today is Thanksgiving [1] in the US, where we remember and are thankful for all the blessings in our lives. One thing I am grateful for is ReSpec which helps make many W3C spec-editor's lives easier. Thanks Robin!
I've noticed that in IE9+ when viewing specs that utilize ReSpec (for example, the Contacts API [2]), I would get the following error (see also attached screenshot): "Processing error: TypeError: Object doesn't support property or method 'evaluate'".
[cid:image003.jpg@01CCAAB4.A188E400]
'evaluate', of course, is the API used to execute XPath queries on the DOM using the W3C DOM Level 3 XPath WG Note [3]. Until such time as IE implements XPath support, I'd love to see ReSpec work as well on IE9+ as any other modern web browser. To that end, I wrote up a quick-and-dirty patch (below and attached) that adds the specific XPath support that ReSpec needs for browsers without 'evaluate' support. I've tested it on IE9+ and it seems to work.
Older versions of IE would require many more shims, and since most of us here at Microsoft are using the most current versions of IE, I didn't bother making it work for IE < 9.
The patch can simply be dropped in at the bottom of the http://dev.w3.org/2009/dap/ReSpec.js/js/respec.js file with no side-effects for UAs that have existing XPath support.
I hope you'll accept this patch to give your IE9+ users a reason to also be thankful for ReSpec!
-Travis
[1] http://en.wikipedia.org/wiki/Thanksgiving
[2] http://w3c-test.org/dap/contacts/
[3] http://www.w3.org/TR/DOM-Level-3-XPath/
----Patch contents----
// ReSpec XPath substitute JS workaround for UA's without DOM L3 XPath support
// By Travis Leithead (travil AT microsoft dotcom)
// (select APIs and behaviors specifically for ReSpec's usage of DOM L3 XPath; no more an no less)
// For IE, requires v.9+
(function () {
if (!document.evaluate) {
//////////////////////////////////////
// interface XPathResult
//////////////////////////////////////
// Augments a generic JS Array to appear to be an XPathResult (thus allowing [] notation to work)
window.XPathResult = function (list) {
list.snapshotLength = list.length;
list.snapshotItem = function (index) { return this[index]; };
return list;
}
window.XPathResult.prototype.ORDERED_NODE_SNAPSHOT_TYPE = 7;
window.XPathResult.ORDERED_NODE_SNAPSHOT_TYPE = 7;
//////////////////////////////////////
// interface XPathEvaluator
//////////////////////////////////////
// Not exposed to the window (not needed)
function XPathEvaluator(assignee) {
var findElementsContainingContextNode = function (element, contextNode) {
var allUpList = document.querySelectorAll(element);
var resultSet = [];
for (var i = 0, len = allUpList.length; i < len; i++) {
if (allUpList[i].compareDocumentPosition(contextNode) & 16)
resultSet.push(allUpList[i]);
}
return resultSet;
}
var allTextCache = null;
var buildTextCacheUnderBody = function () {
if (allTextCache == null) {
var iter = document.createNodeIterator(document.body, 4, function () { return 1; }, false);
allTextCache = [];
while (n = iter.nextNode()) {
allTextCache.push(n);
}
}
// Note: no cache invalidation for dynamic updates...
}
var getAllTextNodesUnderContext = function (contextNode) {
buildTextCacheUnderBody();
var candidates = [];
for (var i = 0, len = allTextCache.length; i < len; i++) {
if (allTextCache[i].compareDocumentPosition(contextNode) & 8)
candidates.push(allTextCache[i]);
}
return candidates;
}
var findAncestorsOfContextNode = function (element, contextNode) {
var allUpList = document.querySelectorAll(element);
var candidates = [];
for (var i = 0, len = allUpList.length; i < len; i++) {
if (allUpList[i].compareDocumentPosition(contextNode) & 16)
candidates.push(allUpList[i]);
}
return candidates;
}
var findSpecificChildrenOfContextNode = function (contextNode, selector) { // element.querySelectorAll(":scope > "+elementType)
var allUpList = contextNode.querySelectorAll(selector);
// Limit to children only...
var candidates = [];
for (var i = 0, len = allUpList.length; i < len; i++) {
if (allUpList[i].parentNode == contextNode)
candidates.push(allUpList[i]);
}
return candidates;
}
assignee.evaluate = function (xPathExpression, contextNode, resolverCallback, type, result) {
// "ancestor::x:section|ancestor::section", sec
if (xPathExpression == "ancestor::x:section|ancestor::section") // e.g., "section :scope" (but matching section)
return XPathResult(findElementsContainingContextNode("section", contextNode));
else if (xPathExpression == "./x:section|./section") // e.g., ":scope > section"
return XPathResult(findSpecificChildrenOfContextNode(contextNode, "section"));
else if (xPathExpression == "./x:section[not(@class='introductory')]|./section[not(@class='introductory')]") // e.g., ":scope > section:not([class='introductory'])"
return XPathResult(findSpecificChildrenOfContextNode(contextNode, "section:not([class='introductory'])"));
else if (xPathExpression == ".//text()") // Not possible via Selectors API. Note that ":contains("...") can be used to find particular element containers of text
return XPathResult(getAllTextNodesUnderContext(contextNode));
else if ((xPathExpression == "ancestor::abbr") || (xPathExpression == "ancestor::acronym")) // e.g., "abbr :scope, acronym :scope" (but match the element, not the scope)
return XPathResult(findAncestorsOfContextNode((xPathExpression == "ancestor::abbr") ? "abbr" : "acronym", contextNode));
else if (xPathExpression == "./dt") // e.g., ":scope > dt"
return XPathResult(findSpecificChildrenOfContextNode(contextNode, "dt"));
else if (xPathExpression == "dl[@class='parameters']")
return XPathResult(contextNode.querySelectorAll("dl[class='parameters']"));
else if (xPathExpression == "*[@class='exception']")
return XPathResult(contextNode.querySelectorAll("[class='exception']"));
else // Anything else (not supported)
return XPathResult([]);
};
}
// Document implements XPathExpression
if (window.Document) {
XPathEvaluator(Document.prototype);
}
else // no prototype hierarchy support (or Document doesn't exist)
XPathEvaluator(window.document);
}
})();
Attachments
- image/jpeg attachment: image003.jpg
- text/plain attachment: reSpecXPathShim.js.txt
Received on Thursday, 24 November 2011 22:24:04 UTC