- •Введение
- •Несколько слов о книге
- •Глава 1. Каким должен бытъ Web-интерфейс
- •Действия пользователя при работе с приложением
- •Накладные расходы при работе в сети
- •Асинхронное взаимодействие
- •Независимый и переходный образы использования
- •Четыре основных принципа Ajax
- •Браузер имеет дело с приложением, а не с содержимым
- •Сервер доставляет данные, а не содержимое
- •Реальное кодирование требует порядка
- •Применение богатых клиентов Ajax
- •Системы, созданные с использованием Ajax
- •Google Maps
- •Альтернативные технологии
- •Macromedia Flash
- •Java Web Start
- •Резюме
- •Ресурсы
- •Основные элементы Ajax
- •JavaScript изучался не зря
- •Определение внешнего вида с помощью CSS
- •Селекторы CSS
- •Свойства стилей
- •Простой пример использования CSS
- •Обработка DOM с помощью JavaScript
- •Поиск узла DOM
- •Создание узла DOM
- •Добавление стилей к документу
- •Свойство innerHTML
- •Асинхронная загрузка с использованием XML
- •Элементы IFrame
- •Объекты XmlDocument и XMLHttpRequest
- •Использование фуниции обратного вызова для контроля запроса
- •Жизненный цикл процедуры поддержки запроса
- •Отличия Ajax от классических технологий
- •Резюме
- •Ресурсы
- •Порядок из хаоса
- •Образы разработки
- •Реструктуризация и Ajax
- •Во всем надо знать меру
- •Реструктуризация в действии
- •Варианты применения реструктуризации
- •Несоответствие браузеров: образы разработки Fagade и Adapter
- •Управление обработчиками событий: образ разработки Observer
- •Повторное использование обработчиков событий: образ разработки Command
- •Обеспечение единственной ссылки на ресурс: образ разработки Singleton
- •"Модель-представление-контроллер "
- •Серверная программа Ajax, созданная без применения образов разработки
- •Реструктуризация модели
- •Разделение содержимого и представления
- •Библиотеки независимых производителей
- •Библиотеки, обеспечивающие работу с различными браузерами
- •Компоненты и наборы компонентов
- •Элементы, располагаемые на стороне сервера
- •Резюме
- •Ресурсы
- •Применение архитектуры MVC к программам различных уровней
- •Применение архитектуры MVC к объектам, присутствующим в среде браузера
- •Представление в составе Ajax-приложения
- •Отделение логики от представления
- •Отделение представления от логики
- •Контроллер в составе Ajax-приложения
- •Классические JavaScript-обработчики
- •Модель обработки событий W3C
- •Реализация гибкой модели событий в JavaScript
- •Модель в составе Ajax-приложения
- •Использование JavaScript для моделирования предметной области
- •Взаимодействие с сервером
- •Генерация представления на основе модели
- •Отражение объектов JavaScript
- •Обработка массивов и объектов
- •Включение контроллера
- •Резюме
- •Ресурсы
- •Программы, выполняемые на сервере
- •Создание программ на стороне сервера
- •N-уровневые архитектуры
- •Управление моделью предметной области на стороне клиента и на стороне сервера
- •Принципы создания программ на сервере
- •Серверные программы, не соответствующие основным принципам разработки
- •Использование архитектуры Model!
- •Использование архитектуры на базе компонентов
- •Архитектуры, ориентированные на использование Web-служб
- •Частные решения: обмен данными
- •Взаимодействие, затрагивающее только клиентскую программу
- •Пример отображения информации о планетах
- •Взаимодействие, ориентированное на содержимое
- •Взаимодействие, ориентированное на сценарий
- •Передача данных серверу
- •Использование HTML-форм
- •Использование объекта XMLHttpRequest
- •Управление обновлением модели
- •Резюме
- •Ресурсы
- •Создание качественного приложения
- •Отклик программы
- •Надежность
- •Согласованность
- •Простота
- •Как получить результат
- •Предоставление сведений пользователю
- •Поддержка ответов на собственные запросы
- •Обработка обновлений, выполненных другими пользователями
- •Создание системы оповещения
- •Основные принципы оповещения
- •Реализация базовых средств оповещения
- •Отображение пиктограмм в строке состояния
- •Отображение подробных сообщений
- •Формирование готовой системы
- •Предоставление информации в запросах
- •Информация о новизне данных
- •Простой способ выделения данных
- •Выделение данных с использованием библиотеки Scriptaculous
- •Резюме
- •Ресурсы
- •JavaScript и защита браузера
- •Политика "сервера-источника"
- •Особенности выполнения сценариев в Ajax-приложении
- •Проблемы с поддоменами
- •Взаимодействие с удаленным сервером
- •Взаимодействие с Web-службами
- •Защита конфиденциальной информации
- •Вмешательство в процесс передачи данных
- •Организация защищенного НТТР-взаимодействия
- •Передача шифрованных данных в ходе обычного HTTP-взаимодействия
- •Управление доступом к потокам данных Ajax
- •Создание защищенных программ на уровне сервера
- •Ограничение доступа к данным из Web
- •Резюме
- •Ресурсы
- •Что такое производительность
- •Скорость выполнения JavaScript-программ
- •Определение времени выполнения приложения
- •Использование профилировщика Venkman
- •Оптимизация скорости выполнения Ajax-приложения
- •Использование памяти JavaScript-кодом
- •Борьба с утечкой памяти
- •Особенности управления памятью в приложениях Ajax
- •Разработка с учетом производительности
- •Простой пример управления памятью
- •Как уменьшить объем используемой памяти в 150 раз
- •Резюме
- •Ресурсы
- •Сценарий двойной комбинации
- •Недостатки клиентского решения
- •Недостатки клиентского решения
- •Архитектура клиента
- •Разработка взаимодействия клиент/сервер
- •Реализация сервера: VB.NET
- •Написание кода сервера
- •Представление результатов
- •Применение каскадных таблиц стилей
- •Дополнительные вопросы
- •Запросы при выборе нескольких элементов
- •Переход от двойного связного выбора к тройному
- •Реструктуризация
- •Новый и улучшенный объект netContentLoader
- •Создание компонента двойного списка
- •Резюме
- •Глава 10. Опережающий ввод
- •Изучаем опережающий ввод
- •Типичные элементы приложений опережающего ввода
- •Google Suggest
- •Ajax как средство опережающего ввода
- •Структура серверной части сценария: С#
- •Сервер и база данных
- •Тестирование серверного кода
- •Структура клиентской части сценария
- •HTML
- •JavaScript
- •Обращение к серверу
- •Дополнительные возможности
- •Реструктуризация
- •День 1: план разработки компонента TextSuggest
- •День 3: включаем Ajax
- •День 4: обработка событий
- •День 5: пользовательский интерфейс всплывающего окна с предлагаемыми вариантами
- •Итоги
- •Резюме
- •Эволюционирующий портал
- •Классический портал
- •Портал с богатым пользовательским интерфейсом
- •Создание портала с использованием Java
- •Таблица пользователя
- •Серверная часть кода регистрации: Java
- •Структура регистрации (клиентская часть)
- •Реализация окон DHTML
- •База данных окон портала
- •Серверный код окна портала
- •Добавление внешней библиотеки JavaScript
- •Возможность автоматического сохранения
- •Адаптация библиотеки
- •Автоматическая запись информации в базе данных
- •Реструктуризация
- •Определение конструктора
- •Адаптация библиотеки AjaxWindows.js
- •Задание команд портала
- •Выводы
- •Резюме
- •Понимание технологий поиска
- •Классический поиск
- •"Живой" поиск с использованием Ajax и XSLT
- •Возврат результатов клиенту
- •Код клиентской части сценария
- •Настройка клиента
- •Инициализация процесса
- •Код серверной части приложения: РНР
- •Создание XML-документа
- •Создание документа XSLT
- •Объединение документов XSL и XML
- •Совместимость с браузером Microsoft Internet Explorer
- •Совместимость с браузерами Mozilla
- •Последние штрихи
- •Применение каскадных таблиц стилей
- •Улучшение поиска
- •Поддержка браузерами Opera и Safari
- •Использовать ли XSLT
- •Решение проблемы закладок
- •Реструктуризация
- •Объект XSLTHelper
- •Компонент "живого" поиска
- •Выводы
- •Резюме
- •Считывание информации из внешнего мира
- •Поиск XML-лент
- •Изучение структуры RSS
- •Богатый пользовательский интерфейс
- •Чтение лент
- •HTML-структура без таблиц
- •Гибкое CSS-форматироеание
- •Глобальный уровень
- •Предварительная загрузка средствами Ajax
- •Богатый эффект перехода
- •Правила прозрачности, учитывающие индивидуальность браузеров
- •Реализация затухающего перехода
- •Интеграция таймеров JavaScript
- •Дополнительные возможности
- •Введение дополнительных лент
- •Интеграция функций пропуска и паузы
- •Как избежать ограничений проекта
- •Обход системы безопасности браузеров Mozilla
- •Изменение масштаба приложения
- •Реструктуризация
- •Модель приложения
- •Представление приложения
- •Контроллер приложения
- •Выводы
- •Резюме
- •Отладчики
- •Для чего нужен отладчик
- •Средство Safari DOM Inspector для Mac OS X
- •Ресурсы
- •JavaScript — это не Java
- •Формирование объектов
RSSItemView Result
Рис. 13.18. Окончательный вид класса RSSItemView
Причина, по которой мы дали представлению метод tostring, заключается в том, что это позволяет нам взаимозаменяемо использовать экземпляр представления и генерируемую им строку HTML. Например, мы можем присвоить значение атрибуту innerHTML элемента, равное классу представления, и далее будет использоваться строковое представление (сгенерированный HTML-файл). Например, ниже приводится код, присваивающий сгенерированный HTML-документ представления атрибуту innerHTML элемента div с идентификатором contentDiv.
var rssItemView - |
new RSSItemView( anRSSFeed, 0, 0, 5 ); |
$('contentDiv1 ). |
innerHTML = rssItemView; |
(Помните, $() — это функция из библиотеки Prototype, позволяющая извлекать элементы DOM по их идентификаторам.) Теперь, когда у нас есть хороший набор абстракций для классов модели и представления, рассмотрим контроллер приложения, который свяжет воедино все эти компоненты.
13.7.3. Контроллер приложения
Класс RSSReader будет объединять функции, связанные с манипуляцией классами модели и представления, координируя все действия, соотнесенные с приложением чтения RSS-лент. Напомним, что интерфейс приложения реализован в виде слайд-шоу лент, когда каждая статья будет отображаться на определенный период времени, а затем постепенно переходить в следующую
556 Часть IV. Ajax в примерах
статью. Приложение имеет кнопки, позволяющие переходить на следующую и предыдущую статьи, а также кнопки паузы/возобновления слайд-шоу. Наконец, имеются раскрывающийся список и кнопка добавления новых лент к исходному набору. Ниже перечислены пять необходимых категорий поведения, которые должны быть реализованы в приложении.
1.Построение объектов и первоначальная настройка.
2.Реализация слайд-шоу.
3.Создание эффектов перехода.
4.Загрузка RSS-лент средствами Ajax.
5.Обновление пользовательского интерфейса.
Чтобы уменьшить сложность и объем кода, требуемого для выполнения всех этих задач, используем библиотеку Prototype (позволяющую получить синтаксическую лаконичность), библиотеку Rico (обеспечивающую функциональные возможности, требуемые для эффектов перехода) и объект net.ContentLoader (предлагающий поддержку Ajax). Итак, начнем с первоначального построения и настройки.
Построение и настройка
Фактически разработка всех наших предыдущих компонентов начиналась с конструкторов. Будем верны традиции и сейчас — начнем с определения конструктора, которым в данном случае является простой метод, задающий первоначальные значения по умолчанию для какого-то состояния компонента и, как в других примерах, устанавливающего конфигурационные опции и выполняющего инициализацию. Учитывая это, конструктор RSSReader определяется так, как показано в листинге 13.27.
Листинг 13.27.* Конструктор RSSReader ; |
^ Х ^ В |
RSSReader = Class.create*); |
|
RSSReader.prototype = { |
|
initialize: function( readerld, options ) |
{ |
// О Установить значения по умолчанию |
|
t h i s . i d = r e a d e r l d ; |
|
t h i s . t r a n s i t i o n T i m e r = n u l l ; |
|
t h i s . p a u s e d = f a l s e ; |
|
t h i s . v i s i b l e L a y e r = 0; |
|
// © Настроить опции конфигурации |
|
t h i s . s e t O p t i o n s ( o p t i o n s ) ; |
|
// © Инициализировать поведение |
|
t h i s . s t a r t { ) ; |
|
) , |
|
Приведенный выше конструктор принимает два аргумента: идентификатор и объект опций. Идентификатор используется как уникальная метка приложения и добавляется как префикс к идентификаторам кнопок, кото-
Глава 13. Создание приложений Ajax, не использующих сервер 557
рые он должен будет определить в структуре DOM. Реализация сказанного будет показана ниже, в методе applyButtonBehaviors. Первое, что делает конструктор О, — это устанавливает значения по умолчанию для состояния. Далее (как и в большинстве других проектов) используется объект опций ©, с помощью которого задаются конфигурационные опции компонента. Для этого применяется метод setoptions. Наконец, в методе star t © происходит все, что требуется для активизации компонента. Далее мы сначала рассмотрим конфигурацию, а затем перейдем к поведению.
За конфигурацию отвечает идиома setoptions, показанная в листинге 13.28. Давайте разберемся с реализацией setoptions и обсудим опции конфигурации.
Листинг 13.28. Метод setoptions |
j j j l |
||
setOptions: function(options) |
{ |
||
|
t h i s . o p t i o n s = { |
|
|
|
slideTransitionDelay: 7000, |
||
|
fadeDuration |
: 300, |
|
|
errorHTML : |
'<hr/>Error retrievin g content.<br/>' |
|
|
}.extend(options); |
|
|
ь |
|
|
ш |
|
|
|
|
В приведенном методе setOptions указаны свойства приложения, которые мы решили сделать настраиваемыми. Свойство slideTransitionDelay задает число миллисекунд, в течение которых один "слайд" статьи виден до перехода во второй. Свойство fadeDuration задает время в миллисекундах, требуемое для полного затухания "слайда" (а следовательно, для "проявления" следующего "слайда"). Наконец, при возникновении ошибки в процессе загрузки RSS-ленты используется свойство errorHTML, задающее HTMLдокумент, отображаемый в качестве сообщения об ошибке. В приведенном коде также показаны значения данных свойств по умолчанию, которые сохраняются, если пользователь явно не укажет другие. Здесь стоит отметить, что компонент ожидает, что ему в качестве исходного набора лент для обработки будет передано свойство rssFeeds объекта опций. Поскольку мы в действительности не можем предложить разумное значение по умолчанию для этой величины, в методе setoptions она не определяется. Подразумевается, что приложение будет создано с объектом опций, подобным показанному ниже.
var options = |
{ |
rssFeeds: [ |
"http://radio.javaranch.com/news/rss.xml", |
|
"http://radio.j avaranch.com/pascarello/rss.xml", |
|
"http://radio.javaranch.com/bear/rss.xml", |
|
"http://radio.javaranch.com/lasse/rss.xml" ] }; |
var rssReader |
= new RSSReader{'rssReader', options ); |
Итак, мы довольно быстро закодировали создание и настройку конфигурации. Теперь можно переходить к магическому методу start, который и запустит весь процесс. В следующих разделах мы кратко рассмотрим этот метод и посмотрим, к чему приводит его использование. Начнем, как обычно, с реализации, показанной в листинге 13.29.
558 Часть IV. Ajax в примерах
Листинг 13.29. Метод start :ЯНННИНИННННННННН|
start: function() { this.applyButtonBehaviors();
new Effect.FadeTo( this.getLayer(1), 0.0, 1, 1, {} ); this.loadRSSFeed(0,true);
this.startSlideShow{false);
}, |
|
щ |
Метод applyButtonBehaviors настраивает обработчики событий onclick для кнопок перехода к предыдущей/последующей статье, паузы/возобновления, а также кнопки добавления новой ленты. Этот метод мы рассмотрим следующим. Эффект затенения, представленный во второй строке, вызывает постепенное исчезновение с экрана видимого элемента div, чтобы его можно было скрыть при загрузке первого слайда. Обратите внимание на то, что
вданной реализации мы не кодируем эффект самостоятельно, а используем готовое решение из библиотеки Rico, что уменьшает объем кода, который требуется писать, отлаживать и сопровождать. Метод loadRSSFeed инициализирует запрос Ajax на загрузку в первую ленту, а метод startSlideShow запускает таймер со значением slideTransitionDelay, инициируя таким образом слайд-шоу. Подробнее метод loadRSSFeed будет рассмотрен в разделе "'Загрузка RSS-лент с помощью Ajax", а метод startSlideShow — в разделе 'Реализация слайд-шоу". На этом мы, как и обещали, завершаем обсуждение построения и настройки, рассмотрев упомянутый в листинге 13.29 метод applyButtonBehaviors.
Как отмечалось ранее, метод applyButtonBehaviors подключает кнопки
кметодам, реализующим их поведение. Реализация этого метода показана
влистинге 13.30.
Листинг 13.30. Метод applyButtonBehaviors .
applyButtonBehaviors: function!) |
{ |
|
|
|
|
S(this.id + '__prevBtn') .onclick |
= |
this.previous.bind(this) ; |
|
|
$ ( t h i s . i d + ' _ n e x t B t n ' ) . o n c l i c k • t h i s . n e x t . b i n d ( t h i s ) ; |
|||
|
$ ( t h i s . i d + ' _ p a u s e B t n ' ) . o n c l i c k - t h i s . p a u s e . b i n d ( t h i s ) ; |
|||
|
$ ( t h i s . i d + ' _ a d d B t n ' ) - o n c l i c k = t h i s . a d d F e e d . b i n d ( t h i s ) ; |
|||
_ ± |
|
|
|
. |
Здесь, пожалуй, стоит напомнить, какой синтаксис и какие идиомы мы используем. В частности, мы задействовали пару синтаксических элементов библиотеки Prototype. Во-первых, это метод $, который можно рассматривать как вызов document .getElementByld. Во-вторых, метод bind неявно создает для нас замыкание, так что обработчик событий onclick для каждой кнопки может вызывать методы первого класса нашего компонента. Рассмотрим теперь детали реализации.
Реализация открывает неявный контракт между компонентом и HTMLразметкой приложения. Компонент создается с идентификатором, который хранится в атрибуте this . id . Позже этот идентификатор используется как префикс для нахождения различных элементов в разметке. В данном случае предполагается, что идентификаторы кнопок -— это метки, переданные в
Глава 13 Создание приложений Ajax, не использующих сервер 559
Таблица 13.4. Атрибуты контроллера
Атрибут |
Описание |
t h i s . currentFeed |
Экземпляр RSSFeed, в текущий момент загруженный в память |
t h i s . feedlndex |
Номер ленты, показываемой в текущий момент. Является |
|
индексом массива t h i s . o p t i o n s .rssFeeds |
t h i s . itemlndex |
Номер статьи, показываемой в текущий момент Является |
|
индексом массива статей объекта RSSFeed, показываемого |
|
в текущий момент |
конструктор, к которым добавлено _prevBtn, _nextBtn, _pauseBtn и addBtn. Чтобы проиллюстрировать сказанное, рассмотрим приведенный выше код. В качестве идентификатора здесь используется rssReader, а компонент ожидает, что кнопки будут заданы следующим образом:
<input type="button" id="rssReader_prevBtn" value=" « " /> <input type="button" id="rssReader_pauseBtn" value=" I I " /> <input type="button" id="rssReader_nextBtn" value=" » " /> <input type="button" id="rssReader_addBtn" value="Add Feed" />
Видно, что контроллер RSSReader начинает приобретать форму, и теперь мы можем рассмотреть детали реализации слайд-шоу.
Реализация слайд-шоу
Теперь самое время поговорить об изменении семантики по сравнению
спредыдущей версией сценария. Изначально мы загружали все RSS-ленты
впамять в момент запуска, а затем просто формировали переходы между внутренними представлениями лент. Это было просто, но масштабируемость такого решения вызывала определенные сомнения. Если мы регулярно прочитываем десятки или сотни RSS-лент, содержащих десятки статей, предварительная загрузка их всех была бы чересчур сильной нагрузкой на браузер. Поэтому в разделе реструктуризации мы попытаемся добиться нормальной масштабируемости и производительности приложения, так изменив семантику, чтобы за раз в память загружалась единственная RSS-лента. Все статьи RSSItems одной ленты побывают в памяти, однако в каждый отдельный момент времени в памяти будет присутствовать только один элемент RSSFeed. За то, на каком этапе находится слайд-шоу в процессе отображения содержимого, отвечают три атрибута контроллера, описанных в табл. 13.4.
Разобравшись с семантикой, переходим к навигации. Если мы планируем реализовать навигацию по всем статьям (элементам item) всех RSS-лент, то должны изучить несколько методов. Рассмотрим для начала пару методов перехода к предыдущей/следующей статье. Механизм перехода требуется не только для реализации явных событий, связанных с кнопками, но и для пассивного чтения при просмотре автоматизированного слайд-шоу.
Итак, рассмотрим пару булевых методов, сообщающих читателю, может ли он переходить вперед или назад. Реализация этих двух методов hasPrevious и hasNext показана в листинге 13.31.
560 Часть IV Ajax в примерах |
|
... ЛИСТИЙГ 13.31. Пара методов has Preyious/has Next |
: , |
hasPrevious: function() { |
|
return ! (this.feedlndex == 0 && this .iteinlndex == 0); |
|
}, |
|
hasNext: function() {
return !(this.feedlndex == this.options.rssFeeds.length - 1 && this.itemlndex == this.currentFeed.items.length - 1);
}, щ
С помощью представленных методов определяется, доступен ли предыдущий или последующий слайд. При указанной реализации предыдущий "слайд" будет доступен в том случае, если в текущий момент приложение демонстрирует не первую статью первой ленты, а следующие "слайд" доступен, если мы читаем не последнюю статью последней ленты.
Рассмотрим теперь, что такое переход на предыдущую и последующую статью ленты. Начнем с метода previous (), показанного в листинге 13.32.
I Листинг 13.32. Метод previous ()Г ^ 1 Д | Д
previous: function() {
if ( !this.hasPrevious() ) return;
var requiresLoad = this.itemlndex == 0;
this.fadeOut( this.visibleLayer, Prototype.emptyFunction ); this.visibleLayer = {this.visibleLayer + 1 ) % 2;
if { requiresLoad )
this.loadRSSFeedf this.feedlndex - 1, false ); else
setTimeout( this.previousPartTwo.bind(this), parselnt (this. options. fadeDuration/4) ). ;
Ь
previousPartTwo: function() |
{ |
|
|
this.itemlndex'—; this.updateView(); |
|
Ь |
|
m |
Первое, что мы сделали при написании метода previous (), — поместили в самом его начале защитное условие. Если предыдущей статьи не существует, метод previous () ничего не делает. Если значение requiresLoad равно true, тогда содержимое RSS-статьи, на которую планируется переход, еще не загружено. Если мы находимся на первой статье ленты и переходим назад, требуется загрузка предыдущей ленты. Метод затухания, подробно рассмотренный в разделе "Эффекты перехода", постепенно ослабляет видимый слой. Дальнейшие действия этого метода зависят от того, требуется ли перед отображением загрузка какого-либо содержимого. Если да, то мы инициируем загрузку необходимых данных посредством метода loadRSSFeed(). Первым параметром данного метода является номер загружаемой ленты, вторым — булево значение, указывающее направление: true — вперед, false (как в данном случае) — назад. Если же содержимое статьи уже загружено, то мы вызываем previousPartTwo() после паузы, равной одной четвертой общей длительности fadeDuration. Во "второй части" данного метода про-
Глава 13 Создание приложений Ajax, не использующих сервер 561
сто обновляется свойство itemlndex и вызывается функция updateView (), приводящая к затуханию соответствующего слайда.
Ну что, запутались? Ладно, объясняем нормальным языком: если текст, который нужно отобразить, не загружен, его загрузка начинается немедленно, что приводит к обновлению пользовательского интерфейса сразу же после поступления данных. Время, которое требуется на прием данных, используется для естественной реализации затухания! С другой стороны, если содержимое уже загружено {т.е. мы переходим на другую статью загруженной ленты), то мы вводим искусственную задержку (четверть времени затухания) перед проявлением следующей статьи. Довольно хитро, правда?
Метод next (), показанный в листинге 13.33, представляет собой реализацию алгоритма, обратного к приведенному выше.
next: |
function{} |
{ |
|
|
if |
( |
ithis.hasNext() |
) return; |
|
var |
requiresLoad = |
|
||
|
this.itemlndex == (this.currentFeed.items.length - 1); |
|||
|
this.fadeOut( this.visibleLayer, Prototype.emptyFunction ) ; |
|||
|
this.visibleLayer = (this.visibleLayer + 1 ) % 2; |
|||
if |
( |
requiresLoad > |
|
|
|
this.loadRSSFeed{ |
this.feedlndex + 1, true ); |
||
else |
|
|
||
|
setTimeout( |
this.nextPartTwo.bind(this), |
||
|
|
|
parselnt(this.options.fadeDuration/4) ); |
|
Ь
nextPartTwo: function() { |
|
this.itemlndex++; this.updateView(); |
|
Ь |
m |
Выглядит знакомо? Метод next () использует противоположную логику индексирования, а во всем остальном идентичен приведенному выше алгоритму. Обратите внимание на то, что при каждом переходе пара методов previous () /next {) переключает видимый слой с одного слайда на другой с помощью такого выражения:
this.visibleLayer = (this.visibleLayer + 1) % 2;
Таким образом, мы сообщаем коду, который в конечном счете обновит пользовательский интерфейс (после загрузки содержимого или явного вызова функции updateView ()), на какой слой помещать результат. Напомним, что контекстная область приложения содержит HTML-разметку, которые выглядит примерно следующим образом:
<!— Контекстная область —>
<div class="content" id="rssReader_content"> <div class="layerl">Layer 0</div>
<div class="layer2">Layer K/div> </div>
Здесь visibleLayer — целочисленное свойство, отслеживающее, в какой элемент div требуется помещать содержимое. Индекс 0 указывает, что при об-
562 Часть IV. Ajax в примерах
новлении пользовательского интерфейса содержимое должно помещаться на слой 0. Значение 1 приводит к вставке данных на слой 1.
Подведем итог. У нас есть методы, с помощью которых реализуется переход вперед/назад по набору статей, и мы можем их использовать для создания методов слайд-шоу. Рассмотрим их В листинге 13.34 показаны метод startSlideShow, который, как вы помните, вызывался из метода s t a r t ( ) , и дополняющий его метод nextSlide ().
ЛистинМ3.34. Методы навигации по слайд-шоу
startSlideShow: function(resume) {
var delay = resume ? 1 : this.options.slideTransitionDelay; this.transitionTimer = setTimeout{this.nextSlide.bind(this),
delay);
К |
|
|
nextSlide: function{) { |
||
|
if ( this.hasNextO |
) |
|
this.next(); |
|
|
else |
|
|
this.loadRSSFeed(0, true); |
|
|
this.transitionTimer = setTimeout{ |
|
|
|
this.nextSlide.bind(this), |
), |
|
this.options.slideTransitionDelay ) ; |
|
ш |
|
Приведенный метод startSlideShow вызывает nextSlide после заданной задержки. В зависимости от того, возобновляем ли мы слайд-шоу после паузы или нет, задержка равна либо slideTransitionDelay, либо одной миллисекунде (практически мгновенно). Таким же простым является и метод nextSlide, вызывающий метод next() при наличии следующего слайда. Если текущий слайд является последним в данной ленте, вызывается loadRSSFeed(0,true) и мы переходим на начало ленты. После этого просто устанавливается таймер и процесс повторяется. Все очень легко!
Мы уже говорили, что слайд-шоу можно приостановить с помощью кнопки паузы, однако соответствующий метод еще не был реализован. Восполним этот пробел и создадим метод, показанный в листинге 13.35.
riv*i к i n i i O . O J . i v ( С 1чЛЦ u a U o c
"•явчвИнЯиИИН^ВДИв^вРИ^1 *i "•• -•;••••••'. • •Щ^шШЯШЩшШШШШЯШЯШЩШЯ^Ш^ШВЯШШЯШЩШШШШШШшШШШШШШШШШШШШШШШШШШ
pause: function() { if ( this.paused )
this.startSlideShow(true); else
clearTimeout( this.transitionTimer ); this.paused = !this.paused;
- ± |
- |
Метод pause переключает состояние паузы нашего слайд-шоу. Текущее состояние отслеживается с помощью булева атрибута this.paused. Если слайд-шоу уже приостановлено, метод pause вызывает функцию s t a r t - SlideShow, в качестве значения свойства resume передавая значение true;
Глава 13 Создание приложений Ajax, не использующих сервер 563
в противном случае метод обнуляет атрибут transitionTimer, подавляющий все переходы между слайдами до тех пор, пока кнопка паузы не будет нажата повторно.
Чтобы завершить реализацию слайд-шоу, нужно реализовать функцию, которая бы позволила добавлять в этот процесс новые RSS-ленты. Изучая код функции applyButtonBehaviors(), мы видели, что кнопка добавления новой ленты вызывает метод addFeed. Давайте реализуем этот метод (см. листинг 13.36) и будем считать, что мы сделали все для получения управляемого слайд-шоу.
Листинг 13.36. Метод addFeed
addFeed: function() {
var selectBox = $(this.id + '_newFeeds'); var feedToAdd = selectBox.options[
selectBox.selectedlndex ].value; this.options.rssFeeds.push(feedToAdd);
},
m
Приведенный выше метод также зависит от неявного контракта с HTMLразметкой с позиции договоренности об именовании списка дополнительных RSS-лент. Идентификатор данного списка должен быть идентификатором приложения с суффиксом _newsFeeds. Метод addFeed просто принимает выбранную RSS-ленту в списке и добавляет ее в конец массива t h i s . options. rssFeeds. Больше ничего не требуется! Ну как вам это нравится — добавление новых функциональных возможностей требует всего пары строк кода!
На этом мы завершаем разработку методов, связанных со слайд-шоу. Теперь рассмотрим кратко методы, поддерживающие эффекты перехода.
Эффекты перехода
В нашем коде упоминалось несколько методов, поддерживающих затухающие переходы между слайдами. Давайте рассмотрим эти переходы подробнее. Итак, прежде всего мы определим пару методов fadeln() и fadeOutO, как показано в листинге 13 37.
Листинг 13.37. Пара методов fadeln< )/fadeOut() |:[•:' |
; = | | | 1 | И |
|||
fadeln: function( layer, onComplete ) |
{ |
|
|
|
|
this.fadeTo( 0.9999, layer, onComplete ); |
|
|
|
fadeOut: function( layer, onComplete ) |
{ |
|
|
|
|
this.fadeTo( 0.0001, layer, onComplete ); |
|
|
|
ь |
|
|
M |
|
|
|
|
|
|
Оба указанных метода делегируют свои полномочия методу fadeToO (он разобран ниже). Они передают методу fadeToO параметр прозрачности — значение между 0 и 1, где 0 соответствует полностью невидимому слою, а 1 — полностью видимому. В некоторых браузерах значение, очень
564 Часть IV. Ajax в примерах
Таблица 13.5. Параметры Effect . FadeTo
Параметр |
Описание |
t h i s . getLayer ( l a y e r ) |
Затухающий элемент DOM |
п |
Степень прозрачности (значение между 0 и 1) |
t h i s . o p t i o n s . f adeDuration |
Время затухания |
12 |
Число промежуточных этапов |
{complete: onComplete} |
Обратный вызов, запускаемый после завершения |
близкое к 1 (но не точно 1), похоже, немного уменьшает мерцание изображения, поэтому вместо 1 мы будем использовать величину 0,9999. Чтобы указать, какой слой должен затухать, мы передаем функции номер слоя (0 или 1). Наконец, последний аргумент — это функция, обеспечивающая перехват обратного вызова сразу после завершения перехода. Реализация метода fadeTo() показана в листинге 13.38.
fadeTo: function( n, layer, onComplete ) { new Effect.FadeTo( this.getLayer(layer),
n,
this.options.fadeDuration,
12,
{complete: onComplete J |
); |
|
Ь |
|
л |
Из-за внезапного приступа лени или в качестве продуманного методологического приема мы решили не изобретать заново эффект перехода. Вместо этого мы используем метод Effect. FadeTo из библиотеки Rico, который и выполнит всю работу. Параметры Effect.FadeTo приведены в табл. 3.5.
Чтобы получить элемент div, соответствующий затухающему слою содержимого, мы использовали вспомогательный метод getLayerO, показанный в листинге 13.39.
Листинг 13.39. Метод aetLaverO _ | Ц ^ Н ^ ^ ^ ^ Д | Н ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ И в getLayer: function(n) {
var contentArea - $(this.id+'_content');
var children = contentArea.childNodes; var j = 0; for ( var i = 0 ; i < children.length ; i++ } {
if ( children[i].tagName && children[i].tagName.toLowerCase() == 'div' ) { if ( j -~ n ) return children[i];
}
}
return null;
Глава 13. Создание приложений Ajax, не использующих сервер 565
Данный метод находит область содержимого, предполагая, что ее идентификатор построен как идентификатор приложения с дописанным в конец значением _content. Найдя элемент содержимого, метод прослеживает его потомков и находит n-й дочерний элемент div.
Все, закончили и с переходами. Теперь рассмотрим тему загрузки RSSлент посредством магии Ajax.
Загрузка RSS-лент с помощью Ajax
Мы уделили довольно много внимания темам создания компонента и обеспечения богатой семантики слайд-шоу, а также необычных технологий DHTML для переходов между слайдами. Однако если все это не создано на основе инфраструктуры Ajax, гордиться совершенно нечем. Все дело в том, что идеальный пользовательский интерфейс может обеспечить только Ajax со своей масштабируемостью и индивидуальным извлечением данных плюс сложный DHTML со своими богатыми возможностями и эффектами. Говорить об этом можно долго, так что лучше все лее рассмотрим требуемую инфраструктуру Ajax, начав с метода, приведенного в листинге 13.40 и выполняющего загрузку RSS-ленты в память.
loadRSSFeed: function{feedlndex, |
forward) { |
|||
|
|
thxs.feedlndex = feedlndex; |
|
|
|
|
this.iteralndex = forward ? 0 |
: |
"last"; |
|
new net.ContentLoader(this, |
|
|
|
|
|
this.options.rssFeeds[feedlndex], |
||
|
|
"GET", |
[] |
).sendRequest(); |
) , |
|
|
я |
|
Данный метод с помощью давно знакомого объекта net.ContentLoader формирует запрос Ajax, передавая в качестве параметра URL RSS-ленты, заданный в массиве this.options.rssFeeds. Параметр forward представляет собой булеву величину, указывающую, что мы загружаем (или не загружаем) новое содержимое вследствие перехода на следующую статью. Далее, согласно этой информации, обновляется свойство itemlndex. Обратите внимание на то, что, если мы переходим назад, itemlndex получает значение, равное last, а не целочисленной величине. Такое решение объясняется тем, что свойство itemlndex должно указывать индекс последней статьи предыдущей RSS-ленты. Единственная проблема заключается в том, что мы не знаем, сколько статей в ленте, поскольку она еще не загружена.
Напомним, что согласно неявному контракту с net.ContentLoader нам требуются методы ajaxUpdate и handleError. Разберем вначале метод aj axUpdate, показанный в листинге 13.41, и посмотрим, как с помощью данной реализации разрешить нашу дилемму индексирования.
566 Часть IV. Ajax в примерах
Листинг 13.41. Метод *1а^Аз^ШШШШЯИШШШШШШШШ
aj axUpdate: function(request) { if ( window.netscape Sfi
window.netscape.security.PrivilegeManager.enablePrivilege)
netscape.security.PrivilegeManager.enablePrivilege( 'UniversalBrowserRead') ;
this.currentFeed = RSSFeed.parseXML(request.responseXML.documentElement);
if ( this-itemlndex == "last" )
this.itemlndex = this.currentFeed.items.length - 1; this.updateView();
__L |
|
ш |
Метод ajaxUpdate начинает с проверки того, что он работает в среде, предлагающей PrivilegeManager. Если да, тогда он затребует привилегию UniversalBrowserRead. Как отмечалось выше, это сделано для того, чтобы наше приложение могло запускаться локально в браузерах Mozilla.
В приведенном коде this.currentFeed — это экземпляр объекта модели RSSFeed, определенный в разделе "Модель". Он соответствует одной ленте RSSFeed, загруженной в память после отклика Ajax. Если значение this.itemlndex равно l a s t (такое значение устанавливается методом loadRSSFeed при возврате к предыдущей статье), свойство itemlndex обновляется в соответствии с реальным числом статей в новой загруженной ленте RSSFeed. Наконец, в ответ на вызов updateView() обновляется пользовательский интерфейс.
Не забывайте, что нам еще нужно определить метод handleError (не только потому, что это подразумевает контракт с net.ContentLoader, но и потому, что нам действительно нужно какое-то средство обработки ошибок). Если ленту RSS загрузить не удается, мы предоставляем сообщение, показанное в реализации handleError. Разумеется, возможны (и желательны) более сложные реализации.
handleError: function(request) {
this.getLayer(this.visibleLayer).innerHTML = this.options.errorHTML;
Ь
Итак, мы реализовали все необходимые возможности Ajax в приложении RSSReader, остался последний штрих — написать пару методов, обновляющих пользовательских интерфейс.
Обработка пользовательского интерфейса
Напомним, что классы модели и представления мы создавали так, чтобы облегчить себе реструктуризацию кода приложения. Теперь, переходя к контроллеру, отвечающему за обновление пользовательского интерфейса, мы можем считать, что сделана почти все работа. Это действительно так. Рассмотрим, например, метод updateView () (листинг 13.42), на который мы неоднократно ссылались в ходе предыдущей работы.
