http://blagtalkan.ru
http://oktpoisk.ru
http://mysorru.ru/
http://san-okt.ru/
http://pes6evolution.ru/
http://chasikigov.ru
http://serafimsovet.ru
http://filmsgov.ru
http://free-domen.ru
http://fithonda.ru/forums/index.php
http://avtogov.ru
http://www.1001bilet.com.ua/base/on-line/
http://pravilavoini.ru
http://school41ufa.ru
http://rusrav4.ru
XParser | Книга AJAX
 

Первый компонент приложения FooReader.NET- это XParser, класс JavaScript, отвечающий за преобразование документов в форматах RSS и Atom в объекты JavaScript, которые затем могут быть использо¬ваны приложением. Основная цель класса XParser в том, чтобы предо¬ставить разработчику интерфейс простого доступа к наиболее важным элементам. Этот класс позволяет не только уменьшить объем про¬граммного кода (и, как следствие, время загрузки), но и объем работы, выполняемой клиентом.
Прототипом такой архитектуры послужила платформа .NET, а в осно¬ве ее лежат следующие три класса, входящие в состав основного клас¬са XParser: XParserltem, XParserElement и XParserAttribute.
Начнем с самого простого класса - XParserAttribute, который является представлением атрибута элемента. Атрибут характеризует только од¬ну сторону данных, а именно значение, и потому XParserAttribute име¬ет всего одно свойство.
function XParserAttribute(oNode) { this.value = oNode.nodeValue;
}
Конструктор XParserAttribute принимает единственный аргуме узел DOM. Из этого узла извлекается значение атрибута (свой nodeValue) и сохраняется в свойстве value. Просто, не правда ли?
Класс XParserElement является представлением элемента XML и о-ет за доступ к элементу и извлечение его значения и его атриб Конструктор класса принимает два аргумента: узел элемента и значение этого узла.
function XParserElement(oNode,sValue) { this.node = oNode || false;
this.value = sValue || (this.node && this.node.text) || false;
В этих нескольких начальных строчках двум из четырех свойств са присваиваются значения входных аргументов. Обратите вним на использование оператора 11 (логическое ИЛИ) при присваивании.
В операции присваивания оператор логического ИЛИ представляет бой сокращенную версию трехместного оператора и дает тот же са-результат, что и конструкция this, node = (oNode)?oNode:false;. On ция присваивания для свойства value больше напоминает блок if.
else if.
if (sValue) {
this.value = sValue; } else if (this.node && this.node.text) {
this.value = this.node.text; } else {
this.value = false;
}
Сокращенная версия требует ввода практически в два раза меньше с волов, чем блок if... else if, а лишние символы всегда лучше убрать.
В реализации DOM Firefox отсутствует свойство text. Чтобы предостав необходимую функциональность, XParser использует библиотеку zXtnl (о мы рассказывали в предыдущих главах), которая расширяет реализацию в Firefox, включая свойства text и ml.
Следующие несколько строк создают коллекцию attributes - колл цию объектов XParserAttributes.
if (this.node) {
this.attributes = [];
var oAtts = this.node.attributes;
for (var i = 0; i < oAtts.length; i++) {
this.attributes[i] = new XParserAttribute(oAtts[i]); this.attributes[oAtts[i].nodeName] = new XParserAttribute(oAtts[i]);
}
} else {
this.attributes = false;
}
this.isNull = (Ithis.node && !this.value && !this.attributes);
Сначала проверяется существование узла. (Нет смысла создавать набор атрибутов для узла, которого не существует.) Затем создается массив, в который с помощью свойства att ributes интерфейса DOM будут собра¬ны атрибуты элемента. Внутри цикла for для каждого атрибута узла создаются объекты XParserAttribute; один помещается в массив по це¬лочисленному ключу, а второй по имени атрибута в качестве ключа, которое извлекается из свойства nodeName. Этот второй объект позволя¬ет упростить доступ к тому или иному атрибуту, если известно его имя.
Если узел элемента отсутствует, то в свойство attributes записывается значение false. (Как нельзя разделить число на 0, так нельзя получить и атрибуты несуществующего узла.) В заключение устанавливается свойство isNull. Это свойство позволяет проверить, является ли полу¬ченный объект пустым. Если все три свойства node, value и attributes содержат значение false, то объект XParserElement рассматривается как пустой.
Класс XParserltem является представлением элемента или (с этого момента мы будем называть эти элементы просто элементами). Элементы состоят из нескольких узлов, что, само собой, требует проведения синтаксического анализа. Конструктор XParser¬ltem принимает один аргумент с именем itemNode, который представля¬ет узел DOM элемента.
function XParserltem(itemNode) {
this.title=this.link=this.author=this.description=this. date = new XParserElement();
Эта первая строка имеет большое значение. RSS и Atom - это стандар¬ты, но это совсем не означает, что все в обязательном порядке будут следовать им. В документе RSS может отсутствовать тег в од¬ном или во всех элементах. Либо в документе Atom может быть проиг¬норирован тег и отдано предпочтение тегу . В свя¬жи с вероятностью появления подобных несоответствий очень важно, чтобы свойства XParserltem имели значения по умолчанию во избежа¬ние последующих ошибок. Значение по умолчанию - пустой элемент XParserElement. Теперь начнем разбор дочерних узлов.
for (var i = 0; i < itemNode.childNodes.length; i++) { var oNode = itemNode.childNodes[i]; if (oNode.nodeType == 1) {
switch (oNode.tagName.toLowerCaseO) {
В цикле for производится перебор всех дочерних узлов. В первую оче¬редь проверяется тип узла - если nodeType имеет значение 1, то теку¬щий узел является узлом. Проверка типа узла может показаться из¬лишней, но она совершенно необходима. Броузер Mozilla может при¬нять незаполненное пространство между узлами за дочерние элемен¬ты, поэтому проверка значения свойства nodeType помогает избежать ошибок при передаче узла в функцию XParserElement. Если текущий
узел действительно является узлом, функция передает имя тега в б switch.. .case, где выполняется присваивание тегов соответствую свойствам элемента.
//Теги, общие для обоих стандартов case "title":
this.title = new XParserElement(oNode);
break; case "link":
if (oNode.getAttribute("href")) { this.link =
new XParserElement(oNode,oNode.getAttribute("href")); } else {
this.link = new XParserElement(oNode);
>
break; case "author":
this.author = new XParserElement(oNode); break;
Несмотря на имеющиеся различия, RSS и Atom имеют несколько о наковых тегов. В данном случае это теги , и и , если они есть, передаются функции ХРа
serElement.
// Теги Atom case "content":
this.description = new XParserElement(oNode);
break; case "issued":
this.date = new XParserElement(oNode);
break;
В этом фрагменте выполняется проверка на соответствие тегам с дарта Atom: и . В последнюю очередь проверя наличие дополнительного тега расширения:
// Расширения case "dc:date":
this.date = new XParserElement(oNode);

break; default; break;
Этот фрагмент проверяет наличие тега , который является составной частью расширения Dublin Core (http://dublincore.org/docu-ments/dcmi-terms/). Это расширение получило большое распростране¬ние, поэтому имеет смысл проверить его наличие и использовать его значение.
Бели по каким-либо причинам в документе не окажется тега, описан¬ного в блоке switch, то соответствующее свойство будет иметь значение по умолчанию - пустой объект XParserElement (из первой строки класса <?arserltem). Поскольку XParser - это класс JavaScript, совсем не обяза¬тельно анализировать каждый тег. Однако оператор выбора switch по¬зволяет легко добавлять возможность анализа других тегов, если в этом возникнет необходимость.
Класс XParser является основным классом, который включает в себя все классы, обсуждавшиеся выше. Конструктор класса XParser принимает аргумент sFileName и необязательный аргумент blsXml. Для достижения максимальной гибкости XParser предусматривает возможность как са¬мостоятельного выполнения запроса с помощью объекта XMLHttp, так и получение результатов выполнения внешнего запроса в виде аргумента. Бели в аргументе blsXml передается значение false или undefined, то со¬держимое аргумента sFileNAme интерпретируется как URL, и в этом слу¬чае XParser выполняет собственный запрос. В противном случае sFileNa-яе интерпретируется как строка XML и загружается в объект XML DOM.
var oThis = this;
this. title=this. link=this. description=this. copy right=this. generator
this.modified=this.author = new XParserElementO; this.onload = null;
Может показаться странным, что переменной присваивается значение ссылки на объект, но это пригодится нам, когда мы будем иметь дело с обработчиком события onreadystatechange() объекта XMLHttp, с которым мы вскоре встретимся. Вторая строка похожа на первую строку в XPar¬serltem. Она присваивает значения по умолчанию наиболее важным для вас свойствам объекта. Следующая строка объявляет обработчик собы¬тия onload, которое будет возникать после полной загрузки документа.
Метод load() класса XParser вызывается для загрузки документа XML. Когда конструктору передается документ XML, сразу же вызывается метод load(), и ему передается содержимое аргумента sFileName.
if (blsXml) {
this.load(sFileName);
}
Однако когда конструктору передается URL (и в аргументе blsXml зна¬чение false или undefined), XParser выполняет собственный запрос.

else {
var oReq = zXmlHttp.createRequest(); oReq.onreadystatechange = function () { if (oReq.readyState == 4) {
// только если все в порядке if (oReq.status == 200) {
oThis.load(oReq.responseText);
}
}
};
oReq.open("GET", sFileName, true); oReq.send(null);
Обратите внимание: здесь используется унифицированная фаб zXMLHttp, описанная ранее в этой книге. По событию onreadystatech проверяется состояние запроса, и responseText передается методу 1о если запрос завершился успехом. Именно здесь вступает в игру менная oThis. Если бы вместо переменной мы использовали вызов t load(oReq. responseText), это привело бы к ошибке, сообщающей о что this, load() не является функцией. Все дело в том, что к мом вызова обработчика события onreadystatechange ключевое слово this дет содержать ссылку на обработчик события, а не на объект ХРа rser,
И наконец, запрос посылается серверу с помощью метода send().
Метод load() вызывается только в том случае, если данные XML ностью получены и готовы к проведению анализа. Он принимает о аргумент, sXml, в котором содержатся данные XML, предназначе для разбора.
XParser.prototype.load = function (sXml) { var oXmlDom = zXmlDom.createDocument(); oXmlDom.loadXML(sXml);
Этот фрагмент загружает данные в объект XML DOM, созданный с п щью унифицированной фабрики zXmlDom, о которой мы говорили в ве 4. Итак, интерфейс DOM готов к работе, и можно начинать р полученных данных.
this.root = oXmlDom.documentElement;
Прежде всего определим простейшие характеристики файла. Сво" во root, которое соответствует корневому элементу документа позволяет определить формат разбираемого файла.
this.isRss = (this. root. tagName.toLowerCaseO == "rss"); if (this.isRss && parselnt(this.root.getAttribute("version")) < 2) { throw new Error("Версия RSS ниже 2.0");
}
this.isAtom = (this. root.tagName.toLowerCaseO == "feed"); this.type = (this.isRss)? "RSS": "Atom";
Чтобы определить тип рассылки, проверяется корневой элемент доку¬мента. Если имя тега - rss, то далее проверяется номер версии. Если корневым является элемент документа feed, значит, эта рассылка име¬ет формат Atom. Соответствующие логические значения записывают¬ся в свойства isRss и isAtoro. Последнее свойство type служит для того, чтобы отражать тип рассылки. Значение этого свойства будет отобра¬жаться перед пользователем.
Если был получен документ RSS, важно проверить версию RSS. Класс XParser предусматривает работу с версией RSS 2.x, поэтому, если версия RSS ниже 2.0, возбуждается исключение и разбор документа прекращается.
Оба формата, и RSS, и Atom, имеют элементы, содержащие информа¬цию о самой рассылке, но она размещается в разных частях докумен¬та. В RSS эта информация содержится в элементе , который включает в себя все остальные элементы рассылки. В формате Atom ата информация находится в корневом элементе. Это подобие может быть использовано для упрощения разбора документа:
var oChannel =
(this.isRss)?this.root.getElementsByTagName("channel")[0]:this.root;
Если рассылка имеет тип RSS, то в переменную oChannel записывается ссылка на элемент , в противном случае - на корневой эле¬мент документа. После этого начинается разбор документа:
i++) {
for (var i = 0; i < oChannel.childNodes.length; var oNode = oChannel.childNodes[i]; if (oNode.nodeType == 1) {
switch (oNode.tagName.toLowerCaseO) { /I Общие теги case "title":
this.title = new XParserElement(oNode); break; case "link":
if (this.isAtom) {
if (oNode.getAttributeC'href)) { this.link = new
XParserElement(oNode,oNode.getAttribute("href"));
>
) else {
this.link = new XParserElement(oNode);
>
break; case "copyright":
this.copyright = new XParserElement(oNode);
break; case "generator":
this.generator = new XParserElement(oNode); break;
Форматы RSS и Atom имеют много одинаковых элементов, что з тельно упрощает нашу жизнь (и уменьшает объем программного да). Главное различие между форматами в этой группе элементов ключается в элементе (как и в XParserltem). Рассылки Atom держат элемент , но его значение приходится получать из а бута. С помощью метода getAtribute извлекается значение атри href и передается конструктору XParserElement. Далее выполня анализ элементов RSS:
//Теги RSS case "description":
this.description = new XParserElement(oNode);
break; case "lastbuilddate":
this.modified = new XParserElement(oNode);
break; case "managingeditor":
this.author = new XParserElement(oNode);
break;
Затем анализируются элементы Atom.
// Теги Atom case "tagline":
this.description = new XParserElement(oNode);
break; case "modified":
this.modified = new XParserElement(oNode);
break; case "author":
this.author = new XParserElement(oNode);
break; default:
break;
Разобрав информационные узлы, можно приступить к заполнению сива items. Как следует из названия массива items, он представляет бой набор объектов типа XParserltem. В цикле мы обойдем все элеме1 и и передадим их конструктору XParserltet.
var oltems = null; if (this.isRss) {
oltems = oChannel.getElementsByTagName("item"); } else {
try {
oXmlDom.setProperty('SelectionLanguage', 'XPath'); oXmlDom.setProperty("SelectionNamespaces",
"xmlns:atom='http://www.w3.org/2005/Atom "');
oltems = oXmlDom.selectNodes("/atoin:feecl/atom:entry"); } catch (oError) {
oltems = oChannel.getElementsByTagName("entry");
}
Начиная с версии Microsoft XML DOM 4.0 метод getElementsByTagName() несколько изменился. В версии 3.0 и ниже полные имена узлов игно¬рировались в методе getElementsByTagNameO, поэтому извлечение эле¬ментов производилось простой передачей методу имени тега.
В версии 4 и более поздних выбор элементов из пространства имен по умолчанию необходимо осуществлять с помощью метода selectNodes(), который принимает в качестве аргумента выражение XPath. Метод set-Property(), который устанавливает значения дополнительных свойств синтаксического анализатора XML DOM, позволяет определить про¬странство имен, в котором метод selectNodesQ будет производить вы¬борку узлов. В предыдущем фрагменте мы определяли, какой из мето¬дов должен вызываться, с помощью блока try... catch. Сначала выпол¬няется попытка использовать более новую версию метода selectNodes(), а если он терпит неудачу, то вызывается метод getElementsByTagName().
Метод selectNodesQ есть только в Internet Explorer и входит в состав библиотеки MSXML. В броузерах Mozilla для извлечения элементов из про¬странства имен по умолчанию по-прежнему используется метод getEle¬mentsByTagNameO.
После извлечения элементов и каждый от¬дельный узел, входящий в них, передается конструктору класса XParserltem, а результат записывается в массив items:
for (var i = 0; i < oltems.length; i++) {
this.items[i] = new XParserItem(oItems[i]);
}
К этому моменту все интересующие нас элементы уже разобраны ■ размещены в соответствующих им свойствах. Осталось вызвать со¬бытие onload:
if (typeof this.onload == "function") { this. onloadQ;
>
Объявляя onload, мы записали туда значение null. Поэтому очень важ¬но проверить тип onload. Если это функция (в том смысле, что был на¬значен обработчик события), то вызывается метод onloadQ. Как обра¬батывается это событие, мы покажем позднее в этой же главе.