Книга: Технология XSLT

Выполнение XSLT-преобразований в JavaScript

Выполнение XSLT-преобразований в JavaScript

JavaScript является одним из наиболее популярных скриптовых языков, которые применяются при программировании для Web. В этой главе мы покажем, как при помощи JavaScript и MSXML создать интерактивный каталог, основанный на XML и XSLT.

Предположим, что каталог организован в виде иерархии категорий приблизительно следующим образом.

Листинг 9.6. XML-документ каталога

<?xml version="1.0" encoding="windows-1251"?>
<catalog>
 <category title="Компьютеры">
  <category title="Настольные компьютеры"/>
  <category title="Серверы"/>
 </category>
 <category title="Комплектующие">
  <category title="Процессоры"/>
  <category title="Материнские платы"/>
 </category>
 <category title="Расходные материалы">
  <category title="Картриджи">
   <category title="Картриджи для плоттеров"/>
   <category title="Картриджи для принтеров"/>
  </category>
  <category title="Тонеры"/>
  <category title="Бумага"/>
 </category>
</catalog>

При отображении этого дерева мы будем раскрывать только определенную выбранную ветвь категорий. Скажем, если пользователь выбрал категорию "Расходные материалы", показывать информацию о компьютерах мы ему не будем. Иными словами, мы будем показывать только те категории, которые являются надкатегориями выбранной. Для того чтобы сделать это как можно эффективнее, мы выполним следующие шаги.

? При помощи ключа и уникального идентификатора, сгенерированного функцией generate-id, мы найдем в дереве требуемую категорию и присвоим ее переменной $category.

? Воспользовавшись осью ansector-or-self, мы найдем все надкатегории данной, то есть все категории, которые прямо или косвенно содержат найденную. Путь выборки будет иметь вид $category/ancestor-or-self::category. Найденное множество мы присвоим переменной $path.

? При обработке каждой из категорий мы будем обрабатывать ее подкатегории только в том случае, если она является надкатегорией выбранной; иначе говоря — только в том случае, когда ее узел принадлежит множеству узлов $path. Проверять это мы будем при помощи условия count(.|$path)=count($path).

Искомое преобразование в итоге запишется в виде.

Листинг 9.7. Преобразование обрабатывающее наш каталог

<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet
 version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <!-- Выводим документ в формате html и кодировке windows-1251 -->
 <xsl:output method="html" encoding="windows-1251"/>
 <!--
  | Переменная, которая содержит уникальный
  | идентификатор выбранного узла дерева
  +-->
 <xsl:param name="current" select="''"/>
 <!-- Определение ключа категории -->
 <xsl:key name="cat" match="category" use="generate-id(.)"/>
 <!-- Находим текущую категорию -->
 <xsl:variable name="category" select="key('cat',$current)"/>
 <!--
  | Находим надкатегории текущей категории, узлы которых
  | мы будем раскрывать в дереве
  +-->
 <xsl:variable name="path"
  select="$category/ancestor-or-self::category"/>
 <!-- Шаблон обработки каталога -->
 <xsl:template match="catalog">
  <xsl:apply-templates select="category"/>
 </xsl:template>
 <!-- Шаблон обработки категории-->
 <xsl:template match="category">
  <!-- Параметр, указывающий отступ -->
  <xsl:param name="indent"/>
  <!-- Выводим отступ -->
  <xsl:value-of select="$indent"/>
  <!-- Выводим информацию о категории в виде ссылки -->
  <а href="javascript:expand('{generate-id(.)}')">
   <!-- Перед названием категории выводим соответствующую иконку -->
   <img height="11" width="11" border="0">
    <xsl:choose>
     <!--
      | Если категория не содержит субэлементов,
      | выводим иконку с точкой
      +-->
     <xsl:when test="not(*)">
      <xsl:attribute name="src">images/dot.gif</xsl:attribute>
     </xsl:when>
     <!--
      | Если категория принадлежит ветке выбранной категории,
      | выводим иконку с минусом, что означает раскрытую ветку
      +-->
     <xsl:when test="count(.|$path)=count($path)">
      <xsl:attribute name="src">images/minus.gif</xsl:attribute>
     </xsl:when>
     <!--
      | Если категория не принадлежит ветке выбранной категории,
      | выводим иконку с плюсом, что означает нераскрытую ветку
      +-->
     <xsl:otherwise>
      <xsl:attribute name="src">images/plus.gif</xsl:attribute>
     </xsl:otherwise>
    </xsl:choose>
   </img>
   <!--
    | Выводим неразрывный пробел.
    | &#xA0; в Unicode соответствует &nbsp;
    +-->
   <xsl:text>&#xA0;</xsl:text>
   <!-- Выводим название категории -->
   <xsl:value-of select="@title"/>
  </a>
  <br/><xsl:text>&#xA;</xsl:text>
  <!--
   | Если категория принадлежит раскрываемой ветке,
   | обрабатываем ее подкатегории
   +-->
  <xsl:if test="count(.|$path)=count($path)">
   <xsl:apply-templates select="category">
    <!-- Увеличиваем отступ на три пробела -->
    <xsl:with-param name="indent"
     select="concat($indent,'&#xA0;&#xA0;&#xA0;')"/>
   </xsl:apply-templates>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

Теперь осталось лишь только создать страницу, которая при помощи JavaScript и MSXML будет выполнять преобразования и выводить результат.

Для того чтобы воспользоваться возможностями MSXML, мы включим в нашу страницу два объекта:

<!-- Объект, представляющий входящий документ -->
<object
 id="source"
 width="0"
 height="0"
 classid="clsid:f5078f32-c551-11d3-89b9-0000f81fe221">
 <param name="async" value="false">
 <param name="validateOnParse" value="false">
</object>
<!-- Объект, представляющий документ преобразования -->
<object
 id="stylesheet"
 width="0"
 height="0"
 classid="clsid:f5078f32-c551-11d3-89b9-0000f81fe221">
 <param name="async" value="false">
 <param name="validateOnParse" value="false">
</object>

"Магический" код clsid:f5078f32-c551-11d3-89b9-0000f81fe221, который присутствует в тегах обоих объектов, на самом деле не что иное, как уникальный идентификатор библиотеки MSXML 3.0, которую мы и будем использовать для выполнения преобразования. Итак, код нашей HTML- страницы будет выглядеть следующим образом.

Листинг 9.8. Код HTML-страницы

<html>
 <head>
  <meta
   http-equiv="Content-Type"
   content="text/html; charset=windows-1251" />
  <style type="text/css">
   body {font-family:Tahoma,Verdana,Arial,sans-serif; font-size:14px}
   a:link {COLOR:#990000; BACKGROUND: #ffffff; TEXT-DECORATION: none}
   a:hover {BACKGROUND: #dddddd; TEXT-DECORATION: none}
   a:visited {COLOR: #990000; TEXT-DECORATION: none}
  </style>
  <script language="JavaScript">
   <!--
   // Объявляем глобальные переменные
   // Входящий документ
   var source;
   // Преобразование
   var stylesheet;
   // Результат
   var result;
   // Функция, выполняющая действия по инициализации
   function init() {
    // Инициализируем ссылку на объект входящего документа
    source = document.all['source'];
    // Загружаем входящий документ
    source.load('source.xml');
    // Инициализируем ссылку на объект преобразования
    stylesheet = document.all['stylesheet'];
    // Загружаем документ преобразования
    stylesheet.load('stylesheet.xsl');
    // Находим элемент, в который мы будем выводить
    // результат обработки
    result = document.all['result'];
   }
   // Функция, выполняющая "раскрытие"
   //определенной ветки дерева категорий.
   function expand(id) {
    // Получаем ссылку на атрибут select
    // объявления параметра current
    var attSelect = stylesheet.selectSingleNode(
     "/xsl:stylesheet/xsl:param[@name='current']/@select");
    // Изменяем значение этого атрибута. Одинарные кавычки необходимы
    // для того, чтобы новое значение воспринималось как литерал.
    attSelect.nodeValue = "'" + id + "'";
    // Выполняем преобразование
    strResult = source.transformNode(stylesheet);
    // Обновляем страницу
    result.innerHTML = strResult;
   }
   //-->
  </script>
 </head>
 <body>
  <!-- Объект, представляющий входящий документ -->
  <object
  
   width="0"
   height="0"
   classid="clsid:f5078f32-c551-11d3-89b9-0000f81fe221">
   <param name="async" value="false">
   <param name="validateOnParse" value="false">
  </object>
  <!-- Объект, представляющий документ преобразования -->
  <object
  
   width="0"
   height="0"
   classid="clsid:f5078f32-c551-11d3-89b9-0000f81fe221">
   <param name="async" value="false">
   <param name="validateOnParse" value="false">
  </object>
  <a href="javascript:expand(' ')">Каталог</а>
  <!-- В этом элементе мы будем выводить результат -->
  <div/>
 </body>
</html>

В браузере эта страница будет выглядеть следующим образом (рис. 9.10).


Рис. 9.10. Динамический каталог на HTML с использованием JavaScript, MSXML на основе XML и XSLT

Оглавление книги


Генерация: 1.487. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз