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

Ajax в действии

.pdf
Скачиваний:
92
Добавлен:
01.05.2014
Размер:
6.34 Mб
Скачать

258 Часть III Создание профессиональных Ajax-приложений

// Скрыть диалоговое окно m s g . h i d e D i a l o g = f u n c t i o n ( e ) {

va r d i a l o g s ( t h i s . d i a l o g ) ? t h i s . d i a l o g : m s g . d i a l o g ; i f ( d i a l o g ) {

if (dialog.modalLayer){ dialog.modalLayer.style.display='none' ;

}else{ dialog.style.display='none' ;

}

 

}

 

}

 

//

Отобразить диалоговое окно

msg.showDialog=function(e){

var dialog=(this.dialog) ? this.dialog : msg,dialog;

 

if

(dialog){

 

if

(dialog.modalLayer){

 

dialog.modalLayer.style.display*1block1 ;

 

}else{

 

dialog.style.display='block';

}

 

}

 

}

m

К функции render () можно обращаться многократно. В процессе работы она проверяет присутствие компонентов пользовательского интерфейса О, © и по мере необходимости создает их, используя функции createDialog © и createBar ©. Для формирования компонентов интерфейса используются стандартные методы обработки элементов DOM. Обработчики событий позволяют отображать и скрывать диалоговое окно.

Для воспроизведения всех сообщений система сначала сортирует их по приоритету и размещает в трех временных массивах @. Затем низкоприоритетные сообщения выводятся в строке состояния ©, а остальные — в диалоговом окне, причем первыми располагаются высокоприоритетные сообщения О.

Для того чтобы реализовать модальное окно, мы помещаем видимые средства поддержки диалога в элемент DIV, занимающий весь экран, и блокирующий все события, связанные с мышью ©. Для модального элемента DIV определен белый фон с прозрачными пикселями. По этому признаку можно отличить модальное окно. Мы не используем установки прозрачности CSS, потому что в этом случае любой вложенный элемент будет также прозрачным. Файл CSS для системы оповещения представлен в листинге 6.7.

Листинг 6.7. Содержимое файла msg. ess

•msg_small_icon{ height: 32px; width: 32px; position:relative; float:left;

}

• ™sg_jiialog_icon { height: 32px; width: 32px;

Глава 6. Информация для пользователя 259

p o s i t i o n : r e l a t i v e ; f l o a t : r i g h t ;

1

• msg _ large _ icon { h e i g h t : 64px;

w i d t h : 64px;

}

.msg_text{

f o n t - f a m i l y : a r i a l ; f o n t - w e i g h t : l i g h t ; f o n t - s i z e : 14pt;

c o l o r : b l u e ;

}

,msgbar{

position:relative; background-color: white; border: solid blue lpx; width: 100%;

height: 38px; padding: 2px;

}

.dialog{

position: absolute; background-color: whiteborder: solid blue lpx; width: 420px;

top: 64px; left: 64px; padding: 4px;

}

.popup{

position; absolute; background-color: white; border: solid blue lpx; padding: 4px;

}

.non-modal{

}

.modal{

position: absolute; top: Opx;

left: Opx; width: 100%; height: 100%;

background-image:url(img/modal_overlay.gif);

260 Часть III. Создание профессиональных Ajax-приложений

Рис. 6.5. Использование CSS-атрибутов f l o a t позволяет размещать пиктограммы в контейнере любой формы.

В данном примере мы задали квадратную строку состояния; пиктограммы автоматически перекомпоновались с учетом выравнивания по левому и правому краю

Следует заметить, что в GSb-классах msg_small_icon и msg_dialog_icon используется стиль float. Класс msg_small_icon применяется для воспроизведения пиктограмм, соответствующих низкоприоритетным сообщениям. Эти пиктограммы располагаются в ряд, начиная с левого края. Для msg__dialog_icon задано выравнивание по правому краю. Базовые средства позволяют отображать строку состояния любой формы и любого размера. Плавающие элементы располагаются в строке состояния в ряд; при заполнении текущего ряда формируется следующий ряд (рис. 6.5).

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

Листинг 6.8. Модифицированный объект Message

var msg=new Object();

msg.PRIORITY_LOWs= { id: 1, lifetime: 30, icon: "img/msg_lo.png" } ; msg.PRIORITY_DEFAULT={ id:2, lifetime:60, icon:"img/msg_def.png" J; msg.PRIORITY_HIGH= { i d : 3 , lifetime:- l, icon: "img/msg__hi .png" } ; msg.messages=new ArrayO;

msg.dialog=null;

msg.msgBarDiv=null;

msg.suppressRender=false;

msg.Message=function(id,message,priority,lifetime,icon){

this.id=id;

msg.messages[id]=this;

this.message=message;

this.priority={priority) ? priority : msg. PRIORITY__DEFAULT. id; this.lifetime=(lifetime) ? lifetime : this.defaultLifetime(); this.icon={icon) ? icon : this.defaultlcon();

if (this.lifetime>0){ this.fader=setTimeout( "msg.messages['"+this.id+"'].clear()", this.lifetime*1000

);

}

// О Дополнительные параметры if (!msg.suppressRender){ this.attachToBar();

Глава 6. Информация для пользователя 261

}

}

// @ Дополнительные параметры msg.Message.prototype.attachToBar=function(){

if (!msg.msgbarDiv){ msg.render();

}else if (this.priority==msg.PRIORITY_LOW.id){ this.render(msg.msgbarDiv);

lelsei

if (!msg.dialog){ msg.dialog=msg.createDialog( msg.msgbarDiv.id+"_dialog", msg.msgbarDiv, (this.priority==msg.PRIORITY_HIGH.id) );

}

this.render(msg.dialog.tbod);

msg.showDialog();

}

}

msg.Message.prototype.clear=function(){

msg.messages[this.id]=null;

// & Дополнительные параметры if (this.row){ this.row.style.display='none' ; this. row.messageObj'^nullfthis.row^null;

}

i f ( t h i s . i c o T d ) {

t h i s . i c o T d . s t y l e . d i s p l a y s ' n o n e ' ; t h i s . i c o T d . m e s s a g e O b j = n u l l ;

t h i s . i c o T d = n u l l ;

}

_ 1

я

Мы стремимся упростить работы с системой, поэтому при создании сообщения оно автоматически включается в состав пользовательского интерфейса О. Для того чтобы обеспечить воспроизведение нового сообщения, достаточно вызвать конструктор объекта. В зависимости от приоритета сообщения оно будет помещено либо в строку состояния, либо в диалоговое окно ©. Если нежелательно воспроизводить каждое сообщение, например, когда мы добавляем одновременно несколько сообщений, предусмотрен флаг, позволяющий отменить автоматическое отображение. В этих случаях после создания всех необходимых сообщений мы можем вручную вызвать функцию msg.render().

Удаляя сообщения с помощью функции clear(), мы автоматически удаляем соответствующие элементы пользовательского интерфейса ©.

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

262 Часть III Создание профессиональных Ajax-приложений

вторного использования. В следующем разделе будет продемонстрирована совместная работа системы оповещения с объектом ContentLoader Пользователю будет предоставляться информация о загрузке объектов по сети.

6,5. Предоставление информации в запросах

В главе 5 мы представили объект ContentLoader, инкапсулирующий сетевой трафик. Применим систему оповещения для информирования пользователя о состоянии запроса. Начнем с формулирования общих требований.

При передаче запроса серверу мы должны сформировать низкоприоритетное сообщение о том, что данный запрос обрабатывается. Для того чтобы отделить запрос по сети от других оповещений с низким приоритетом, мы изменим внешний вид пиктограммы. Будем использовать для представления сообщения данного типа условное изображение земного шара. Оно присутствовало в программе просмотра информации о планетах, которая рассматривалась в главах 4 и 5.

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

Для того чтобы реализовать подобное поведение, нам надо создавать объекты Message на определенных этапах жизненного цикла запроса, а именно: при его инициализации, а также по завершении или получении информации об ошибках. Модифицированный код объекта ContentLoader показан в листинге 6.9.

Листинг б.Э.-Объект ContentLoader с оповещением пользователя net.ContentLoader=function( ... )

{ ... } ;

net.ContentLoader.msgld=l;

net.ContentLoader.prototyped

loadXMLDoc:function{url,method,params,contentType){ if {Imethod){

method="GET";

}

if {IcontentType && method=="POST"){

contentType='application/x-www-form-urlencoded' ;

I

if (window.XMLHttpRequest){ this.req=new XMLHttpRequest();

} else if (window.ActiveXObject){

this.req=new ActiveXObj ect("Microsoft.XMLHTTP");

I

if (this.reqH try{

var loader=this; this.req.onreadystatechange=function(){ loader.onReadyState.call(loader);

)

Глава 6 Информация для пользователя 263

t h i s . r e q . o p e n ( m e t h o d , u r l , t r u e ) ; i f ( c o n t e n t T y p e ) I

t h i s . r e q . s e t R e q u e s t H e a d e r ( ' C o n t e n t - T y p e ' , c o n t e n t T y p e ) ;

}

// О Оповещение об инициализации запроса this.notification=new msg.Message( "netOO"+net.ContentLoader.msgld, "loading "+url,

msg.PRIORITY_LOW.id, -1, "img/ball-earth.gif" );

net.ContentLoader.msgld++;

this.req.send(params); }catch (err){ this.onerror.call(this);

}

}

Ь

onReadyState:function(){ var req=this.req;

var ready=req.readyState;

if (ready==net.READY_STATE__COMPLETE) { var httpStatus=req.status;

if (httpStatus==200 || httpStatus==*O) { this.onload.call(this);

// @ Удаление существующего оповещения this.notification.clear{);

}else{

this.onerror.call(this);

}

}

},

// © Сообщение об ошибке defaultError:function(){

var msgTxt="error fetching data!"

+ "<ulxli>readyState: "+this. req.readyState +"<li>status: "+this.req.status

+"<li>headers: "+this.req.getAHResponseHeaders() +"</ul>";

if (this.notification){ this.notification.clear();

J

this.notifxcation=new msg.Message(

"net__errOO"+net. ContentLoader .msgld, msgTxt,msg.PRIORITY__DEFAULT.id

);

net.ContentLoader.msgld++;

}

> ^

264 Часть III Создание профессиональных Ajax-приложений

Когда мы создаем запрос посредством вызова loadXMLDoct), мы формируем оповещение О и связываем ссылку на него с объектом ContentLoader.

Заметьте, что время жизни

устанавливается равным -1, поэтому сообщение

не будет автоматически удаляться.

В методе onReadyState()

мы удаляем сообщение при благополучном за-

вершении операции €). При

наличии ошибки мы вызываем метод defaultEr -

r o r (), который формирует соответствующее сообщение €>. Чтобы сообщение лучше воспринималось пользователями, вместо обычного текста для его формирования применяются средства HTML.

В листинге 6.10 показан пример страницы, в которой используется модифицированный объект ContentLoader

Листинг 6.10. Страница, предусматривающая оповещение пользователя

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"

"http://www.w3.org/TR/xhtmll/DTD/xhtmll-strict.dtd"> <btml>

<head>

<title>Notifications test<Vtitle>

<link rel=stylesheet type="text/css" href="msg.ess"/> <script type="text/javascript" src="x/x_core.js"X/script>

<script type="text/javascript" src="extras-array.js"x/script> <script type="text/javascript" src="styling.js"></script> <script type="text/javascript" src="msg.js"></script>

<script type="text/javascript" src="net_notify.js"X/script> <script type="text/javascript">

window.onload=function(){

msg.render('msgbar');

}

var msgld=l;

function submitUrl(){

var url=document,getElementById{'urlbar').value; // О Запрос к. серверу

var loader=new net.ContentLoader(url,notifyLoaded);

}

function notifyLoadedf){

// © Оповещение о том, что ресурс загружен var doneMsg=new msg.Message( "done00"+msgld,

"loaded that resource you asked for: "+this.url, msg.PRIORITY_LOW.id

);

msgld++; msg.render('msgbar1);

}

</script>

</head>

<body>

<div class='content'>

<p>here is some content. This is what the web

Глава 6 Информация для пользователя 265

рис. 6.6. В окне подсказки, которое отображается при помещении курсора на пиктограмму, содержатся сведения об успешной загрузке ресурса

Рис 6.7. Если при обработке запроса возникла ошибка, информация о ней отображается в диалоговом окне (в данном случае в окне отображены два сообщения; второе информирует об ошибке)

application is up to when not being bugged silly by all these messages and notifications and stuff. <p>Type in a URL in the box below (from the

same domain, see Chapter 7), and hit 'submit'. A souped-up contentloader that understands the notification system will be invoked.

<input id='urlbar' type='text1/> 

<a href='javascript:submitUrl()'>submit</a>

</div>

 

 

 

<div id='msgbar'

class='msgbar'></div>

</body>

 

 

 

</html>

 

m

 

 

 

Web-страница (рис. 6.6 и 6.7) содержит простую HTML-форму, в которой пользователь может ввести URL. По щелчку на ссылке submit предпринимается попытка загрузить ресурс, на который указывает URL О. В случае успешного завершения операции происходит обращение к функции обратного вызова — notifyLoaded(). Функция notifyLoaded() не выполняет с ресурсом никаких действий, а лишь сообщает о его загрузке, создавая объект Message ©.

Заметьте, что поведение в случае успешной обработки запроса не предусмотрено при реализации системы. Оно обеспечивается посредством обработчика onload. Благодаря такому решению система может быть адаптирована с учетом различных требований. В примере, приведенном в листинге 6.9, поведение в случае ошибки было реализовано непосредственно в коде. В ре-

266 Часть III. Создание профессиональных Ajax-приложений

альных приложениях не каждая ошибка, связанная с передачей данных по сети, настолько важна, чтобы отображать сообщение о ней в диалоговом окне. Мы предлагаем читателю в качестве упражнения самостоятельно добавить к ContentLoader параметр, определяющий приоритет сообщения об ошибке (либо другим способом изменить обработчик onError для реализации менее строгой политики).

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

иприведем пример их применения.

6.6.Информация о новизне данных

Система оповещения, созданная нами, предоставляет набор компонентов верхнего уровня, отображающих информацию о системной активности. В некоторых случаях пользователю требуются дополнительные сведения, например, о том, что некоторые данные были модифицированы. В этом разделе мы модифицируем объект ObjectViewer, который рассматривался в главах 4 и 5, так, чтобы он сообщал пользователю информацию о данных, недавно подвергшихся изменению.

6.6.1. Простой способ выделения данных

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

.new{ background-color: IfOeOdO; }

Мы выбрали очень простой стиль. Для реального приложения этого недостаточно; чтобы интерфейс выглядел хорошо, надо приложить дополнительные усилия. В листинге 6.11 показаны изменения кода ObjectViewer, предназначенные для того, чтобы выделить свойства, недавно подвергшиеся редактированию. В данном случае признак новизны данных автоматически удаляется по истечении заданного интервала времени.

Листинг 6.11. ObjectViewer, предусматривающий выделение новых данных'' objviewer,PropertyViewer.prototype.commitEdit=function(value){

if (this.type=objviewer.TYPE_SIMPLE){ this.value=value;

var valDiv=this.renderSimple(); var td=this.valTd;

td.replaceChild(valDiv,td.firstChild);

this.viewer.notifyChange(this);

Глава 6. Информация для пользователя

267

// О Установить признак новизны this.setStatus(objviewer.STATUS_NEW);

}

}

objviewer.STATUS_NORMAL=1; obj viewer.STATUS_NEW=2;

objviewer.PropertyViewer.prototype.setStatus=function(status){

this.status=status; if (this.fader){

clearTimeout(this.fader);

}

if (status==objviewer.STATUS_NORMAL){ this.valTd.className='objViewValue' ; }else if (status==objviewer.STATUS_NEW){ this.valTd.className='objViewValue new1 ; var rnd="fade_"+new Date(),getTime(); this . valTd . id=rnd;

this . valTd . fadee=this;

// 0 Установить значение тайы-аута

t h i s . f a d e r = s e t T i m e o u t ( " o b j v i e w e r . a g e ( ' " + r n d + " 1 ) " , 5 0 0 0 ) ;

}

}

o b j v i e w e r . a g e = f u n c t i o n ( i d ) {

v a r e l = d o c u m e n t . g e t E l e m e n t B y I d ( i d ) ; v a r v i e w e r = e l . f a d e e ;

// © Восстановить состояние по окончании отсчета времени viewer.setStatus(objviewer.STATUS_NORMAL);

e l . i d - " " ;

e l . f a d e e = n u l l ;

_}

m

 

 

Мы определили два варианта данных: "обычные" и "новые". Можно было бы использовать для их идентификации логическое значение isNew, но мы предпочли определить два признака. Такой подход допускает дальнейшее расширение; например, мы сможем ввести дополнительный признак для тех данных, которые были модифицированы и информация об обновлении которых передана на сервер. При фиксации изменяемых значений вызывается метод setStatus () О. В результате задается соответствующий класс CSS и для "новых" данных устанавливается значение таймера ©. После истечения пятисекундного интервала данные переходят в "нормальное" состояние. (Для реального приложения время, в течение которого информация будет отображаться как "новая", должно быть намного больше. Интервал в пять секунд выбран только для проверки и демонстрации возможностей данного решения.) Объект сохраняет ссылку на таймер и может переустановить его значение в случае, если до истечения заданного времени возникнут новые изменения.