Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
107
Добавлен:
02.05.2014
Размер:
6.34 Mб
Скачать

542 Часть IV Ajax в примерах

Рис. 13.10. В приложение чтения RSS-лент добавлена новая лента

С помощью данного списка мы добавляем указатели URL на ленты RSS, не содержащиеся в предварительно загруженном массиве RSS-лент. В данном случае мы добавили две RSS-ленты из радиоблога JavaRanch. К кнопке btnAdd был добавлен обработчик событий onclick, позволяющий выполнить функцию LoadNews (). Последним этапом загрузки дополнительных лент является создание класса CSS, добавляемого в таблицу стилей. Благодаря этому пользователь может увидеть, что лента загружена.

.added{background-color: silver;}

В приведенный класс CSS мы можем добавить любое правило CSS, позволяющее отличать добавленные ленты от всех остальных. В данном случае мы изменили цвет фона опции на серебристый, так что теперь эта опция визуально выделяется из списка. Добавив класс, мы снова можем протестировать полученное приложение.

Как показано на рис. 13.10, мы добавили RSS-ленту с пометкой Frank (она обозначена серебристым цветом). Лента, помеченная как Gregg, еще не добавлена, поскольку ее цвет является принятым по умолчанию белым цветом фона. Кроме того, после добавления ленты число сообщений в приложении увеличилось с 31 до 54. Уже почти все готово. Осталось добавить кнопки "Вперед", "Назад" и "Пауза".

13.5.2. Интеграция функций пропуска и паузы

Одной из наиболее полезных дополнительных возможностей является пропуск сообщений. Если нам попадается сообщение, которое нас не интересует, мы щелкаем на кнопке пропуска и смотрим следующее, не ожидая, пока до

Глава 13. Создание приложений Ajax, не использующих сервер S43

него дойдет очередь. Функция паузы позволяет приостановить работу приложения, чтобы мы могли не торопясь прочесть заинтересовавшее нас сообщение. Поскольку для представления таймеров, паузы и счетчика currentMessage используются глобальные переменные, мы очень просто можем влиять на текущее состояние приложения. В листинге 13 16 показан код, позволяющий пользователю быстро проходить по лентам.

Листинг 13.16. Функция JavaScript, отвечающая за остановку и пропуск лент

/ / О Создать функцию MoveFeed() function MoveFeed {xOption){

//@ Определить, требуется ли приостановка или возобновление работы if(xOption == 0){

//© Приостановить работу

if(IbPaused){ bPaused • true; if(timerSwitch)

clearTimeout(timerSwitch) ; document.getElementById{"btnPause").value = "RESUME";

}

// // О Возобновить работу else(

bPaused = false; setTimeout("ChangeView()",300);

document.getElementById("btnPause").value • "PAUSE";

}

}

// 0 Изменить текущее сообщение else{

if(timerSwitch)

clearTimeout(timerSwitch); if(xOption "™ -1)currentMessage += -2; if(currentMessage < 0)

currentMessage = arrayMessage.length + currentMessage;

ChangeView();

}

_ >

 

 

 

Создав функцию MoveFeed () О и позволив ей принимать в качестве аргумента единственный параметр, мы сможем обрабатывать три ситуации: паузу, перемотку вперед и перемотку назад. Чтобы различать действия мы можем использовать целочисленное значение. Чтобы приостановить работу приложения, мы будем передавать функции MoveFeed () значение 0, чтобы перейти на следующее сообщение — значение 1, а чтобы перейти не предыдущее сообщение — значение —1.

В приведенном коде мы вначале проверяем условие паузы — равенство нулю аргумента функции © Кнопка паузы отвечает за два действия — приостановку работы приложения (прекращение выполнения переходов) и возобновление работы (продолжение слайд-шоу с переходами между сообщениями).

Если в текущий момент лента не приостановлена ©, тогда значение переменной bPaused необходимо установить равным true и проверить, запущен ли

544 Часть IV Ajax в примерах

таймер timerSwitch. Если да, то мы должны его сбросить, используя метод clearTimeout. Далее текст кнопки изменяется на "RESUME". Если на кнопке щелкают для того, чтобы возобновить работу, мы должны выполнить противоположные действия О. Значение переменной bPaused устанавливается равным false; мы вызываем функцию ChangeView() с небольшой задержкой во времени и изменяем текст кнопки на "PAUSE".

Все Работу с паузой мы завершили.

Теперь необходимо создать функции пропуска сообщений и возврата к предыдущим сообщениям ©. Поскольку при этом мы будем изменять отображаемые статьи, необходимо убрать таймер, чтобы избежать проблем с пропуском нескольких сообщений. После этого требуется проверить, не равен ли параметр действия — 1 . Если мц возвращаемся к предыдущим сообщениям, из значения переменной currentMessage необходимо вычесть 2. Это происходит из-за того, что переменная currentMessage в действительности содержит номер следующего сообщения, поскольку ее значение уже было увеличено на 1 Вычитая 1 из значения переменной, мы получаем то же самое сообщение. Следовательно, поскольку в переменной currentMessage уже хранится номер следующего сообщения, при реализации кнопки перемотки вперед нам ничего не нужно делать.

Затем нам необходимо убедиться, что значение переменной не меньше нуля. Если оно меньше нуля, значение переменной нужно установить согласно последнему сообщению в массиве. Изменив значение currentMessage, мы можем вызвать функцию ChangeView{) для загрузки сообщения. Все, что от нас требуется, — добавить обработчики событий к кнопкам (листинг 13.17), чтобы мы могли выполнить функцию MoveFeed ().

Листинг 13.17. Обработчики событий onclick для действий с кнопками

v a l u e = "

<ВАСК

"

onclick="MoveFeed{ - 1)">

 

v a l u e = "

PAUSE

"

onclick="MoveFeed(0)">

 

value="NEXT>"

onclick="MoveFeed(1)">

Для инициализации функции мы добавляем к кнопкам обработчики событий onclick. Эти обработчики вызывают функцию MoveFeed(), которая принимает в качестве аргументов такие значения: -1 — вернуться к предыдущему сообщению; 0 — приостановить работу, 1 — перейти на следующее сообщение. Чтобы проверить работу функций, мы сохраняем документ и открываем данную страницу в браузере.

Теперь, когда мы можем пропускать сообщения, можно легко перейти к сообщениям в середине списка RSS-ленты. На рис. 13.11 показано, как выглядит приложение при нажатой паузе: на кнопке btnAdd отображается надпись RESUME ("Возобновить"). Таким образом, используя описанные в разделе дополнительные функции, мы получаем приложение, позволяющее читать RSS-ленты на рабочем столе, не посещая Web-сайты, поставляющие эти ленты.

Рис. 13.11. Работа показанного в окне приложения чтения RSS-лент приостановлена, поскольку центральная кнопка имеет название RESUME

13.6.Как избежать ограничений проекта

Спомощью основанного на Ajax приложения мы можем читать RSS-ленты из HTML-файла, записанного на рабочем столе, не требуя серверного кода. Данное приложение можно использовать для захвата RSS-лент без посещения соответствующих Web-сайтов. Мы можем предложить посетителям нашего Web-сайта загрузить данную HTML-страницу, настроив ее на чтение RSSлент этого сайта. Поскольку представленный сценарий можно запустить и на самом Web-сайте, его можно использовать и для других целей. Например, это может быть переключение рекламы в баннере, баннер новостей компании или все, что мы можем придумать. Однако представленный сценарий обладает определенными ограничениями; кроме того, при его запуске на рабочем столе

спомощью браузера Mozilla могут возникнуть определенные проблемы.

13.6.1. Обход системы безопасности браузеров Mozilla

В отличие от Microsoft Internet Explorer, браузеры Firefox и Mozilla не позволяют запускать созданное приложение с рабочего стола из-за встроенных ограничений безопасности. Данные ограничения не дают Ajax общаться с другими Web-сайтами, поскольку направлены на то, чтобы код не мог отправлять информацию без нашего ведома.

Чтобы убедиться, что проблема работы сценария связана именно с этим, необходимо исследовать сообщение об ошибке. В браузере Mozilla мы должны открыть консоль JavaScript, вызываемую из меню Tools =?• Web Development =Ф- JavaScript Console (Инструменты => Web-разработка =Ф Консоль JavaScript) (рис. 13.12).

546 Часть IV Ajax в примерах

Рис. 13.12. В браузере Mozilla

выберите T o o l s

=> Web

Development

=>

J a v a S c r i p t

C o n s o l e

(Инструменты =^

Web-разработка =$>

Консоль J a v a S c r i p t )

Рис. 13.13. Сообщение об ошибке "недостаточно полномочий", порожденной объектом

X M L H t t p R e q u e s t

Выбрав команду JavaScript Console (консоль JavaScript), мы открываем дополнительное окно (рис. 13-13).

На рис. 13.13 показана ошибка "permission denied" ("недостаточно полномочий"), порожденная объектом XMLHttpRequest. Исправить ее можно двумя способами. Например, можно открыть конфигурационный файл браузера Mozilla и изменить настройки разрешений, позволив объекту XMLHttpRequest выполнить то, что он хочет. Для этого в адресной строке браузера следует ввести about: conf ig и изменить необходимые настройки, однако данные действия небезопасны.

Небезопасность изменения настроек связана с тем, что наши разрешения касаются всего, что происходит на компьютере. Это означает, что любой сценарий, желающий связаться с внешним миром, сможет это сделать. Как этого можно избежать, разрешив данное общение только нашему сценарию? В качестве решения мы можем установить безопасность с помощью JavaScript. О том, как это сделать, при условии, что браузер настроен на обработку запросов к PrivilegeManager, мы рассказывали в главе 7. Напомним кратко, о чем идет речь. В листинге 13.18 показан общий код, который предоставляет

дополнительные привилегии, требуемые для чтения внешних ресурсов.

 

•:;:. Листинг 13.18. Управление привилегиями Security Privilege Manager

 

if(window.netscape &&

 

window.netscape.security.PrivilegeManager.enablePrivilege)

 

netscape.security.PrivilegeManager.enablePrivilege(

 

'UniversalBrowserRead1 );

В приведенном коде проверяется, можем ли мы обращаться к Privilege-

 

Manager. Если да, тогда мы получаем привилегию UniversalBrowserRead.

 

Данный код необходимо добавить в два различных места объекта Content-

 

Loader, отвечающего за функциональные возможности Ajax.

 

Вначале мы должны вставить указанный код сразу после объявления

 

loadXMLDoc, как показано в листинге 13.19.

 

Глава 13 Создание приложений Ajax, не использующих сервер 547

Рис. 13.14. Запрос, требующий от пользователя подтвердить изменение прав доступа

Листинг 13.19. Вставка "кода т функцию loadXMLDoc

net.ContentLoader.prototype.loadXMLDoc = function( url,method,params,contentType){

if(window.netscape && window.netscape.security.PrivilegeManager.enablePrivilege) netscape.security.PrivilegeManager.enablePrivilege(

'UniversalBrowserRead1); _

Кроме того, код необходимо вставить в функцию onReadyState (ли-

стинг 13.20).

 

Либтинг 13.20, Вставка кода в функцию onReadyState

И М Я

net.ContentLoader.onReadyState=function(){

 

if(window.netscape &&

 

window.netscape.security

 

.PrivilegeManager.enablePrivilege)

 

netscape.security.PrivilegeManager

 

.enablePrivilege('

 

UniversalBrowserRead');

д

Обе указанные функции взаимодействуют с данными из внешнего мира. Именно поэтому мы должны ввести обозначенные фрагменты в обоих местах. При выполнении сценария мы получаем сообщение-запрос, требующее подтверждения изменений настроек безопасности (рис. 13.14).

Если просто щелкнуть на кнопке Allow (Разрешить), запрос будет открываться при каждом вызове функции. Чтобы избежать этого, необходимо

548 Часть IV. А/'ах в примерах

установить флажок Remember this decision ("Запомнить мой выбор"). Таким образом, браузер запоминает ваш выбор и позволяет выполнять объект XMLHttpRequest без запроса подтверждения.

Изменив настройки безопасности браузера, мы можем запустить приложение на рабочем столе, используя браузеры Mozilla, Firefox и Netscape. Используя данное приложение, мы можем обращаться к ХМ1^лентам с любого сайта, не открывая несколько закладок или окон. Кроме того, приложение можно изменить, чтобы оно получало из Web другую информацию, например, прогноз погоды и комиксы.

13.6.2. Изменение масштаба приложения

Наше приложение может не только читать XML-ленты, извлекаемые с различных сайтов. Мы легко можем использовать его как переключатель рекламных объявлений в баннере, сценарий обновления новостей компании, календарь событий и многое другое.

Например, мы можем хранить баннерную рекламу в документе XML. Таким образом в XML-файл легко ввести новую рекламу, не затрагивая HTMLфайлы или серверный код. Мы можем предварительно загрузить баннерную рекламу, а затем отобразить ее в нашем приложении. Вместо того чтобы на экране находился один рекламный ролик, мы можем циклически менять их, пока пользователь изучает сайт.

Мы можем поместить в XML-документ новости компании, чтобы отображать текущие статьи сотрудникам или клиентам. Все, что от нас требуется, — это заполнить стандартные элементы XML-ленты. Кроме того, можно отображать информацию об обновлении сайта или любые другие данные. Как видите, возможности приложения не ограничиваются простым чтением XML-лент.

13.7. Реструктуризация

Итак, мы получили готовый сценарий для чтения RSS-лент и можем, как обычно, заняться его улучшением. Как отмечалось ранее, существуют богатые возможности расширения сценария с точки зрения представления различных типов содержимого. В данном разделе мы сфокусируем внимание на реорганизации сценария согласно архитектуре "модель-представление- контроллер" (Model-View-Controller — MVC). Как объяснялось в главах 3 и 4, MVC широко распространена и позволяет разделять обязанности между различными фрагментами кода. Свое обсуждение мы начнем с определения типов моделей, затем создадим представление модели и разработаем контроллер, связывающий в единое целое все компоненты.

Глава 13 Создание приложений Ajax, не использующих сервер 549

13.7.1. Модель приложения

Разработанное выше приложение чтения RSS-лент очевидно только выиграет от наличия нескольких типов модели. Благодаря этому программное обеспечение будет концептуально понятнее, его станет легче использовать и модифицировать. Поскольку приложения Ajax возлагают более интенсивную нагрузку на DHTML-код клиента, чем традиционные Web-приложения, важность понятного управляемого кода становится первостепенной. Классы модели, которые мы собираемся разработать, должны быть в общем случае применимы и к другим приложениями, работающим с RSS-лентами. Чтобы немного упростить синтаксис кода, для определения типов мы, как и в главе 10, будем использовать библиотеку Prototype.

Начнем с определения класса модели для RSS-ленты. В данном случае RSS-лента представляет собой XML-документ, обладающий предопределенной структурой и имеющий URL, задающий путь доступа к этому документу. Основными атрибутами структуры являются название, ссылка и описание, кроме того, как обсуждалось выше, имеется множество дополнительных атрибутов. Кроме того, лента содержит несколько элементов item, которые можно рассматривать как набор статей. Для начала давайте соберем все, что мы знаем о ленте, в такой форме, как показано в листинге 13.21.

Листинг 13.21. Класс модели RSSFeed • . ^ДВИЯДЦ^И^^ИЭД^^^^Р RSSFeed = Class.create();

RSSFeed.prototype = {

initialize: function( title, link, description ) { this.title - title;

this.link = link; this.description = description; this.items = [];

Ь

addltem: function(anltem) { this.iterns.push(anltem); }

В приведенном коде тип RSSFeed определяется посредством функции Class.create() библиотеки Prototype. Напомним, что, используя эту идиому, мы с помощью сгенерированного конструктора вызываем метод i t n i - tialize . Таким образом, при данном определении класса модели RSS-ленты ленту можно создать с помощью следующей строки кода:

var rssFeed = new RSSFeed{ 'JavaRanch News', 'http://radio.javaranch.com/news/', 'Stories from around the ranch' );

По сути, это все, что требуется для определения объекта модели RSSFeed. Обратите внимание на то, что RSSFeed имеет API addltem, разрешающий добавление статей к исходному массиву элементов. Каждая статья должна представлять собой объект модели, который инкапсулирует атрибуты каждого элемента ленты. Вспомнив все, что мы знаем о статьях RSS, можно определить класс модели статьи, как показано в листинге 13.22.

550 Часть IV Ajax в примерах

Листинг 13.22. Класс модели RSSltem

RSSItem = Class.create(); RSSltem.prototype = {

initialize: function( rssFeed, title, link, description ) {

 

this.rssFeed = rssFeed,-

 

this.title =

title;

 

this.link =

link;

 

this.description = description;

 

J

 

 

};

 

_

я

Ничего особенного. В элемент статьи инкапсулированы атрибуты t i t l e , link и description, кроме того, имеется ссылка на объект RSSFeed, к которому относится статья. Изучая два описанных класса, видим, что статью и одну из лент можно сформировать следующим образом:

var rssFeed = new RSSFeed( 'JavaRanch News', 'http://radio.javaranch.com/news/', 'Stories from around the ranch' );

var feedl = new RSSItem( rssFeed, 'Win a copy of JBoss',

' h t t p : / / r a d i o . j a v a r a n c h . e o m / n e w s / 0 5 / 0 7 / 2 0 / 9 . h t m l ' , 'Text o f A r t i c l e ' ) ;

r s s F e e d . a d d l t e r n ( f e e d l ) ;

Пока что все хорошо. Модель является довольно прямолинейной инкапсуляцией атрибутов RSS-ленты и ее статей. Данные концепции инкапсулируются в двух классах модели —- RSSFeed и RSSltem. Далее мы рассмотрим собственно построение модели. Мы знаем, что эти объекты будут обработаны в результате загрузки XML-данных клиенту в ответ на запрос Ajax. Поэтому мы определим программный интерфейс, вызывая который наш обработчик Ajax сможет преобразовать XML-отклик в экземпляр класса модели RSSFeed. Для этого мы определим вначале контракт процесса, формирующего модель.

var rssFeed = RSSFeed.parseXML{ rssXML );

Данный контракт подразумевает, что мы передаем XML-отклик, возвращаемый обработчиком Ajax, методу синтаксического разбора нашего приложения RSSFeed; в результате мы должны получить экземпляр RSSFeed. Будем придерживаться сказанного и реализуем метод parseXML(), как показано в листинге 13.23.

RSSFeed.parseXML = function(xmlDoc) { var rssFeed = new RSSFeed(

RSSFeed.getFirstValue(xmlDoc, 'title'),

RSSFeed.getFirstValue(xmlDoc, 'link' ), RSSFeed.getFirstValue(xmlDoc, 'description'));

var feedltems = xmlDoc.getElementsByTagName('item'); for ( var i = 0 ; i < feedltems.length ; i++ ) {

rssFeed.addltern(new RSSltem(rssFeed, RSSFeed.getFirstValue(feedltems[i], 'title'), RSSFeed.getFirstValue(feedltems[i], 'link' ), RSSFeed.getFirstValue(feedltems[i], 'description'))

 

Глава 13 Создание приложений Ajax, не использующих сервер

551

}

 

 

 

r e t u rn rssFeed;

 

 

}

 

m

 

Приведенный метод выполняет классический разбор XML-отклика, с которым вы уже неоднократно сталкивались. Метод принимает значения элементов t i t l e , link и description, используя их для создания RSSFeed. Затем ои последовательно проходит по всем элементам item, выполняя эти же действия и создавая для каждого элемента экземпляр RSSltem. На каждой итерации используется метод addltemO, добавляющий статью к родительской RSS-ленте. Обратите внимание на использование вспомогательного метода для получения значения узла первого дочернего элемента с данным именем дескриптора. Данный вспомогательный метод getFirstValue показан в листинге 13.24.

Листинг 13.24. Вспомогательный метод синтаксического разбора

RSSFeed.getFirstValue =

function(element,

tagName) {

var

children

= element.getElementsByTagName(tagName);

if {

children

== null

I | children, length

*== 0 )

return "";

if ( children[O] - firstChild && children[O].firstChild.nodeValue )

return children[O].firstChild.nodeValue;

}

return

"";

 

я

Это все, что нам требуется с точки зрения модели. Очевидно, мы можем добавить атрибуты, представляющие все необязательные элементы RSSленты, и присвоить им значения, если эти элементы присутствуют в ленте. В данном случае нам это делать не нужно, поскольку приложение чтения RSS-лент не использует дополнительные атрибуты (и не требует их предоставления). Тем не менее возможность предоставления дополнительных метаданных существует. Кроме того, мы можем определить методы, предлагающие более формальный контракт доступа к атрибутам. Например, мы можем написать пару getTitle( )/setTitle(), предназначенную для доступа к атрибуту заголовка. Поскольку JavaScript, в отличие от других объектноориентированных языков, не поддерживает визуальную семантику (например, ключевые слова private/protected в Java), мы этим вопросом заниматься не будем. Итак, с моделью мы закончили, переходим к представлению.

13.7.2. Представление приложения

Разобравшись с классами модели, мы можем рассматривать класс представления. Мы можем разработать один класс представления для RSSFeed, а другой — для RSSltem, но поскольку приложение RSSReader в действительности не представляет ленту независимо, мы определим один класс представления RSSltemView, который инкапсулирует представление RSSltem в контексте родительского элемента RSSFeed Поскольку в данном случае представ-