В примере 21.10 приводится определение класса XML.XPathExpression, который одинаково работает и в IE, и в броузерах, соответствующих стандартам, таких как Firefox.
Пример 21.10. Выполнение XPath'выражений
/**
* XML.XPathExpression – это класс, инкапсулирующий XPath_запрос
* и ассоциированное с ним отображение префикса пространства имен в URL.
* После того как объект XML.XPathExpression создан, он может
* использоваться для многократного выполнения выражения (в одном
* или более контекстах) посредством методов getNode() и getNodes().
*
* Первый аргумент конструктора класса _ это текст XPath_выражения.
*
* Если выражение включает в себя какие_либо пространства имен XML, тогда
* второй аргумент должен быть JavaScript_объектом, который отображает
* префиксы пространств имен на URL_адреса, определяющие эти пространства
* имен. Свойствами этого объекта должны быть префиксы, а значениями _
* соответствующие им URL_адреса.
*/
XML.XPathExpression = function(xpathText, namespaces) { this.xpathText = xpathText; // Сохранить текст выражения
this.namespaces = namespaces; // И карту соответствий пространств имен
if (document.createExpression) {
// Если это W3C_совместимый броузер, использовать W3C API
// для компиляции текста XPath_запроса
this.xpathExpr = document.createExpression(xpathText,
// Этой функции передается префикс
// пространства имен, а она возвращает URL. function(prefix) {
return namespaces[prefix];
});
}
else {
// Иначе предположить, что исполнение идет в IE и преобразовать
// объект с пространствами имен в текстовую форму, как того требует IE this.namespaceString = "";
if (namespaces != null) {
for(var prefix in namespaces) {
534 Глава 21. JavaScript и XML
// Добавить пробел, если в строке уже что_то есть
if (this.namespaceString) this.namespaceString += ' '; // И добавить пространство имен
this.namespaceString += 'xmlns:' + prefix + '="' + namespaces[prefix] + '"';
}
}
}
};
/**
* Метод getNodes() класса XML.XPathExpression. Он выполняет XPath_выражение
* в указанном контексте. Аргумент context должен быть объектом Document
* или Element. Возвращаемое значение _ массив или объект, похожий на массив,
* где содержатся узлы, соответствующие выражению.
*/
XML.XPathExpression.prototype.getNodes = function(context) { if (this.xpathExpr) {
// Если это W3C_совместимый броузер, значит, выражение уже
// скомпилировано в конструкторе. Осталось лишь вычислить
// выражение в указанном контексте.
var result = this.xpathExpr.evaluate(context,
// Это – тип требуемого результата XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
// Скопировать результаты в массив.
var a = new Array(result.snapshotLength); for(var i = 0; i < result.snapshotLength; i++) {
a[i] = result.snapshotItem(i);
}
return a;
}
else {
// Если это не W3C_совместимый броузер, попытаться выполнить
// выражение с использованием IE API.
try {
// Чтобы указать пространства имен, необходим объект Document var doc = context.ownerDocument;
// Если контекст не имеет объекта ownerDocument, значит,
// это и есть Document
if (doc == null) doc = context;
// Прием отображения префиксов на URL_адреса, характерный для IE doc.setProperty("SelectionLanguage", "XPath"); doc.setProperty("SelectionNamespaces", this.namespaceString);
// В IE объект Document не может быть контекстом _ только Element,
// таким образом, если контекст _ это документ, использовать
// вместо него documentElement
if (context == doc) context = doc.documentElement;
// Теперь с помощью IE_метода selectNodes() выполнить выражение return context.selectNodes(this.xpathText);
}
catch(e) {
21.4. Выполнение запросов к XML*документу с помощью XPath*выражений
// Если IE API не работает, значит, нам просто не повезло throw "XPath не поддерживается этим броузером.";
}
}
}
/**
* Метод getNode() класса XML.XPathExpression. Он выполняет XPath_выражение
* в заданном контексте и возвращает единственный узел, соответствующий
* выражению (или null, если совпадений не найдено). Если обнаружено
* более одного совпадения, метод возвращает первое из них.
* Реализация этого метода отличается от getNodes() только типом
* возвращаемого значения.
*/
XML.XPathExpression.prototype.getNode = function(context) { if (this.xpathExpr) {
var result = this.xpathExpr.evaluate(context,
// Вернуть первое совпадение XPathResult.FIRST_ORDERED_NODE_TYPE, null);
return result.singleNodeValue;
}
else { try {
var doc = context.ownerDocument; if (doc == null) doc = context;
doc.setProperty("SelectionLanguage", "XPath"); doc.setProperty("SelectionNamespaces", this.namespaceString); if (context == doc) context = doc.documentElement;
// В IE, вместо selectNodes, вызвать selectSingleNode return context.selectSingleNode(this.xpathText);
}
catch(e) {
throw "XPath не поддерживается этим броузером.";
}
}
};
// Вспомогательная функция, которая создает объект XML.XPathExpression
// и вызывает его метод getNodes()
XML.getNodes = function(context, xpathExpr, namespaces) {
return (new XML.XPathExpression(xpathExpr, namespaces)).getNodes(context);
};
// Вспомогательная функция, которая создает объект XML.XPathExpression
// и вызывает его метод getNode()
XML.getNode = function(context, xpathExpr, namespaces) {
return (new XML.XPathExpression(xpathExpr, namespaces)).getNode(context);
};