Опубликован: 10.12.2007 | Доступ: свободный | Студентов: 822 / 20 | Оценка: 5.00 / 5.00 | Длительность: 58:33:00
Лекция 14:

Шаблоны

14.8.3. Шаблон с расширенным синтаксисом для <listbox>

Шаблон для тега <listbox> - более простая задача, так что начнем с нее. Снова заглянем в файл notetaker.rdf, чтобы получить представление о том, как должен выглядеть необходимый запрос. Мы видим, что запись является частью последовательности записей и имеет свойство keyword, то есть имеет место стандартная организация фактов. Проблема состоит в том, что URL записи - известная, фиксированная величина, а не переменная для каждой записи в последовательности. Эта ситуация подобна ситуации с шаблоном для <textbox> на панели инструментов, так что используем расширенную запись запроса.

Вторая причина использовать расширенный синтаксис - необходимость добыть текстовую строку ключевых слов. Эти строки - свойства каждого ключевого слова, а не свойства всей записи. Мы можем легко их получить, расширяя запрос глубже по множеству фактов - один добавочный тег <triple> свяжет найденные ключевые слова записи с их текстовыми значениями. Требуемый шаблон выглядит как листинг 14.23.

<listbox id="dialog.keywords" rows="3" datasources="notetaker.rdf" 
 ref="http://saturn/test1.html">
<template> 
  <rule> 
    <conditions> 
      <content uri="?uri"/> 
        <triple subject="?uri"
          predicate="http://www.mozilla.org/
            notetaker-rdf#keyword"
          object="?keyword"/> 
        <triple subject="?keyword"
          predicate="http://www.mozilla.org/
            notetaker-rdf#label"
          object="?text"/> 
    </conditions> 
    <action> 
      <listitem uri="?keyword" label="?text"/> 
    </action> 
  </rule> 
</template> 
</listbox>
Листинг 14.23. Шаблон списка для панели инструментов NoteTaker.

За исключением добавочного тега <triple>, этот тот же код, что и для <textbox>.

Мы могли бы использовать шаблон и для панели Edit в диалоговом окне Edit. Но у нас есть достаточно оснований так не поступать, так что оставим эту панель в прежнем состоянии. Одно из этих оснований - то, что мы не знаем, какая запись больше всего подойдет, когда загружается данный URL.

14.8.4. Шаблон дерева с расширенным синтаксисом.

Последний шаблон, который мы сконструируем, это шаблон дерева для связанных ключевых слов.

Снова рассмотрим файл notetaker.rdf. Нам нужно свойство label для ключевого слова. Нам также требуются связанные свойства, чтобы найти ключевые слова, связанные с найденным. Наконец, нам нужно, чтобы началом ( level 0 ) дерева были ключевые слова для записи текущего URL. Мы используем этот URL как вершину запроса, но высветим лишь дочерние решения данного URL, но не сам URL.

Для полного рекурсивного запроса нам необходимы такие факты:

<- current-page-url, keyword, ?keyword -> // level 0 
<- ?keyword, label, ?text -> 
<- ?keyword, related, ?keyword2 -> 
<- ?keyword2, label, ?text2 -> // level 1
<- ?keyword2, related, ?keyword3 -> // level 2
<- ?keyword3, label, ?text3 ->

... и так далее ...

Такое множество фактов не отвечает стандартной организации, так что требуется расширенный синтаксис. Это множество фактов является рекурсивным запросом, так что нам действительно нужен именно тег <tree>.

Каким будет запрос? Зададим вопрос более точно: что необходимо получить запросу, чтобы продвинуться на один уровень глубже по дереву решений? На каждом уровне есть два факта, поэтому кажется, что рекурсивный запрос должен охватывать оба этих факта (two-fact query). Но если мы взглянем на структуру файла notetaker.rdf, станет ясно, что для каждого уровня запроса необходим лишь один факт, поскольку ключевые слова заключены в единственном предикате/свойстве. Если нарисовать запрос как RDF диаграмму, мы быстро увидим, что ответом является именно один факт на шаг запроса. Диаграмма приведена на рисунке 14.8.

Диаграмма RDF для рекурсивного запроса шаблона.

Рис. 14.8. Диаграмма RDF для рекурсивного запроса шаблона.

Ясно, что рекурсивность требует одного шага вглубь дерева вдоль вертикальной стрелочки. Так что запрос является запросом единственного факта. А другая стрелочка раскрывает информацию, требуемую на каждом уровне, но не участвующую в рекурсии.

Сначала рассмотрим факты, порождающие рекурсию, и расположенные вдоль вертикальных стрелок. Проблема в том, что иногда они содержат ключевые слова, а иногда - связанные ключевые слова. Другими словами, для рекурсивного запроса есть альтернатива:

<- current-page-url, keyword, ?keyword -> 
<- current-keyword-urn, related, ?keyword ->

Каким-то образом запрос шаблона должен учесть оба случая. Один способ - использовать отдельные правила <rule> для каждой возможности. В нашем случае, однако, хорошее решение - атрибут шаблона containment. К счастью, при любом URI есть либо факт со свойством keyword, либо со свойством related, но не с обоими сразу. А это значит, что мы можем использовать несколько предикатов container вместе. Мы перечислим и предикат related, и предикат keyword в значении этого атрибута.

Чтобы понять, почему мы можем использовать containment, рассмотрим работу запроса. Когда запрос открывает верхний уровень дерева, предикат keyword найдет решения, а предикат related - нет, потому что так в RDF файле устроены факты. Когда запрос обнаруживает решения на следующем уровне, предикат related найдет решения, а предикат keyword - нет. Это в точности то, что нам нужно. Полученный в результате шаблон приведен в листинге 14.24.

<tree id="dialog.related" flex="1" 
datasources="notetaker.rdf"
  ref="http://saturn/test2.html"
  containment="http://www.mozilla.org/notetaker-rdf#related http://
  www.mozilla.org/notetaker-rdf#keyword" hidecolumnpicker="true" 
>
  <treecols> 
    <treecol id="tree.all" hideheader="true" 
      primary="true"
      flex="1"/> 
  </treecols> 
<template> 
  <rule> 
    <conditions> 
      <content uri="?uri"/> 
        <member container="?uri" 
          hild="?keyword"/> 
    </conditions>
    <action> 
      <treechildren> 
        <treeitem uri="?keyword"> 
          <treerow> 
            <treecell label="?keyword"/> 
          </treerow> 
        </treeitem> 
      </treechildren> 
    </action>
  </rule> 
</template> 
</tree>
Листинг 14.24. Рекурсивный шаблон дерева для диалогового окна NoteTaker.

Запрос содержит тег <member>, чтобы проверять соответствие атрибуту containment. Поскольку это рекурсивный запрос, мы не можем устроить проверку просто тегом <description>. Мы обязаны использовать один из тегов, явным образом поддерживающий рекурсивные запросы, в данном случае для <tree>.

Теперь шаблон готов, нужно лишь добавить поиск дополнительной информации, как показано на рисунке 14.8. Это можно сделать, используя тег <binding>. Добавим следующий код сразу после тега

</conditions>: 

<bindings> 
<binding subject="?keyword" 
 predicate="http://www.mozilla.org/notetaker-rdf#label" 
  object="?text"/> 
</bindings>

Поскольку теги <binding> не сказываются на рекурсивном процессе, запрос мы этим не повредим, а лишь уточним им обнаруживаемую информацию. Наконец, нужно изменить тег <treecell>, чтобы вывести label, обнаруживаемый тегом <binding>, а не URN или keyword:

<treecell label="?text"/>

На этом создание всех шаблонов завершено. Хотя теги <listbox> и <tree> имеют контент, порожденный шаблоном, обработчики событий, созданные нами в "Списки и Деревья" , "Списки и деревья", продолжают нормально работать. Они обслуживают DOM структуру порожденного шаблоном контента так же легко, как и статические теги XUL.

С шаблоном <tree> у нас возникает одна проблема. Наш шаблон не обнаруживает связанные ключевые слова так, как это делал экспериментальный снимок шаблона в "Списки и Деревья" , "Списки и деревья". Он обнаруживает лишь то, что есть в RDF файле. Мы можем улучшить ситуацию, добавляя новые комбинации keyword-keyword в файл notetaker.rdf, или мы можем как-то модифицировать шаблон, чтобы факты, обнаруживаемые запросом, проходили специальную обработку, а не высвечивались в "сыром" виде прямо из файла. Мы рассмотрим эту последнюю возможность в "Объекты XPCOM" , "Объекты XPCOM".

14.8.5. Управление и обновление контента шаблона

Наконец, нам нужно реализовать обновление данных, построенных шаблоном. Это означает необходимость связать данные на панели инструментов, в диалоговом окне Edit и HTML-записи с RDF фактами о текущей web-странице, высвеченной в браузере. Обновлять HTML-запись - задача слишком сложная для используемой нами технологии, но все остальные обновления мы выполним легко. Вот наш план:

  1. Обновить <textbox> на панели инструментов каждый раз, когда изменяется текущая запись.
  2. Обновить выпадающее меню каждый раз, когда текущая запись сохраняется или уничтожается, в том случае, если изменяется полный набор ключевых слов.
  3. Обновлять панель Keywords диалогового окна при его открытии.

Чтобы этого добиться, внесем следующие небольшие изменения на панели инструментов:

  1. Обновим JavaScript-версию текущей заметки так, чтобы включить URL.
  2. Создать функцию, обновляющую панель инструментов.
  3. Регулярные проверки, не загружена ли новая страница, должны обновлять <textbox> на панели инструментов.
  4. Для кнопки delete на панели инструментов нужна команда notetaker-delete.
  5. Для кнопки save на панели инструментов нужна команда notetaker-sav e.
  6. Команда notetaker-open-dialog должна обновлять выпадающее меню на панели инструментов, когда закрывается диалоговое окно.

Во-первых, мы должны реализовать объект note на JavaScript и его методы clear() и resolve() и свойство url. Метод clear() уничтожает все данные текущей записи, resolve() преобразует данный URL в URL существующей записи и сохраняет результат. В данной лекции эти изменения будут тривиальны, но мы уточним их позже. Код нового объекта note приведен в листинге 14.25.

function Note() {} // constructor

Note.prototype = { 
  url : null, 
  summary : "", 
  details : "", 
  chop_query : true, 
  home_page : false, 
  width : 100, 
  height : 90, 
  top : 80, 
  left : 70, 
  clear : function () { 
    this.url = null; 
  }, 
  resolve : function (url){ 
    this.url = url; 
  }, 
} 
var note = new Note();
Листинг 14.25. Базовый объект JavaScript для записи NoteTaker.

Во-вторых, реализуем функцию refresh_toolbar(), обновляющую шаблоны панели инструментов. Некоторые из этих функций нам придется усовершенствовать в следующих лекциях. Функция приведена в листинге 14.26.

function refresh_toolbar() { 
  var menu = window.document.getElementById
    ('notetaker-toolbar.keywords');
  menu.firstChild.builder.rebuild(); 
  var box = window.document.getElementById
    ('notetaker-toolbar.summary');
  box.parentNode.setAttribute('ref', note.url);
  box.parentNode.builder.rebuild(); 
}
Листинг 14.26. Код функции, обновляющей шаблон на панели инструментов NoteTaker.

Метод rebuild() автоматически удалит и пересоздаст контент шаблона. В случае выпадающего меню контент изменится, только если изменится исходный RDF файл. В случае <textbox> модифицируется сам шаблон, поэтому запрос каждый раз разный.

Наконец, посмотрим на регулярные проверки. Они вызываются из функции content_poll(), так что поправим ее так, чтобы обновлялась не только высвеченная запись, но и панель инструментов.

function content_poll() { 
  var doc; 
  try { 
    if ( !window.content ) throw('fail');
      doc = window.content.document; 
    if ( !doc ) throw('fail'); 
    if ( doc.getElementsByTagName("body").length == 0 ) 
      throw('fail'); 
    if ( doc.location.href == "about:blank" ) 
      throw('fail'); } 
catch (e) {
    note.clear(); refresh_toolbar(); return; 
  } 
  if ( doc.visited ) 
    return;
  note.resolve(doc.location.href); 
  display_note(); 
  refresh_toolbar();
  doc.visited = true; 
}
Листинг 14.27. Опрос высвеченной страницы и выполнение необходимых обновлений.

Если подходящего URL не существует, текущая запись и панель инструментов очищаются. В противном случае ищется новая текущая запись и обновляются панель инструментов, запись и текущая страница.

Мы еще не выполнили пункты 4 и 6, потому что пока не знаем, как модифицировать RDF файлы - мы пока умеем их только читать. Ту часть команд, которая относится к обновлению шаблона, мы, однако, реализовать можем. Изменим функцию action() так, чтобы она вызывала refresh_toolbar() каждый раз, когда это необходимо. Листинг 14.28 показывает этот простой код.

function action(task) { 
  if ( task == "notetaker-open-dialog" ) {
    window.openDialog("editDialog.xul","_blank","modal=yes");
    refresh_toolbar(); 
  }
  if ( task == "notetaker-display" ) { 
    display_note(); 
  } 
  if ( task == "notetaker-save" ) { 
    refresh_toolbar(); 
  } 
  if ( task == "notetaker-delete" ) { 
  refresh_toolbar(); 
  } 
}
Листинг 14.28. Полный список команд панели инструментов NoteTaker с перестройкой шаблонов.

На этом управление шаблонами завершено. Для диалогового окна нам нужно сделать то же самое для шаблонов панели Keywords. Мы выполним эту задачу в команде notetaker-load. В функции action(), обслуживающей команду, добавим вызов refresh_dialog() и реализуем refresh_dialog() как показано в листинге 14.29.

function refresh_dialog() { 
  var listbox = window.document.getElementById('dialog.keywords');
  listbox.setAttribute('ref', window.opener.note.url);
  listbox.builder.rebuild(); 
  var tree = window.document.getElementById('dialog.keywords');
  tree.setAttribute('ref', window.opener.note.url);
  tree.builder.rebuild(); 
}
Листинг 14.29. Обновление шаблона в диалоговом окне Edit.

Этот код идентичен коду обновления шаблона на панели инструментов выпадающего меню.