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

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

работник обновления. Итак, мы несколько продвинулись по пути решения вопроса об асинхронном обновлении модели предметной области и информировании пользователя о внесенных изменениях. В следующем разделе мы рассмотрим вопросы представления информации и влияние ее на нормальный ход работы пользователя. Там же мы постараемся отказаться от функ-' ции alert () в пользу более подходящего решения.

6.3. Создание системы оповещения

Функция alert, которую мы применяли до сих пор, позволяет реализовать лишь примитивное решение, типичное для ранних периодов развития JavaScript, когда Web-страницы были в основном статическими, а фоновая активность — минимальной. Внешний вид окна с сообщением не поддается контролю средствами CSS, поэтому, работая над профессиональным продуктом, желательно разработать механизм оповещения, используя технологии, применяемые для остальной части пользовательского интерфейса Ajax. Тем самым обеспечивается дополнительная степень гибкости приложения.

Если вы проанализируете существующие вычислительные системы, то увидите, что в них используются самые различные средства оповещения, различающиеся и по степени воздействия на пользователя. Менее всего привлекают внимание такие эффекты, как изменение вида курсора мыши (классический пример песочных часов в системе Windows) или добавление к пиктограмме, представляющей объект, дополнительных элементов, которые позволяют судить о состоянии папки. Такие простые индикаторы представляют минимум информации. Более детальные сведения о фоновых событиях можно получить, прочитав сообщение в строке состояния. И наконец, самым информативным является диалоговое окно. На рис. 6.1 показан пример использования соглашений об оповещении, принятых для окружения рабочего стола в системе Unix.

Вданном случае папка lost+f ound не доступна текущему пользователю, поэтому к ней добавлено изображение замка. Строка состояния в нижней части окна предоставляет более подробную информацию о содержимом папки и в то же время не отвлекает пользователя от основной работы. Окно

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

Вотличие от рассмотренных способов оповещения, функция a l e r t () применима лишь для отдельных случаев. Хотя решение, предполагающее ее применение, реализуется просто, его нельзя назвать изящным. Если мы стремимся достичь надежности, согласованности и простоты, имеет смысл разработать базовые средства для оповещения пользователя, которые будут применяться во всем приложении. В следующих разделах мы постараемся сделать это.

Рис. 6.1. Соглашения, принятые для предоставления информации о состоянии. Внешний вид пиктограммы отражает характеристики объекта (в данном случае права доступа), строка состояния позволяет получить общие сведения, а модальное диалоговое окно — подробную информацию об объекте. В данном случае в качестве примера приведена среда рабочего стола Unix, но аналогичные соглашения действуют во всех популярных графических интерфейсах

6.3.1 Основные принципы оповещения

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

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

ккаждому сообщению.

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

Объект Message, код которого приведен в листинге 6.3, создан с учетом этих требований. Этот объект реализует универсальный механизм оповещения пользователя. Установив модель оповещения, мы можем оформлять сообщения различными способами.

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

I

Листинг 6.3. Объект Message

 

var msg=new Objectt);

|

rasg.PRIORITY_LOWb { id:l, lifetime:30, icon:"img/msg_lo.png" J; msg.PRIORITY_DEFAULT={ id:2, lifetime:60, icon:"img/msg_def.png" }; 1

msg.PRIORITY_HIGH= { id:3, lifetime:-l, icon:"img/msg_hi.png" };

 

 

msg.messages=new Array();

 

I

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 J ? icon : this.defaultlcon();

 

 

 

if

(this.lifetime>0){

 

j

 

this.fader=setTimeout(

 

 

 

"msg.messages['"+thia.id+"'].clear()",

',

 

this.lifetime*1000

 

 

);

 

 

 

 

 

>

 

 

 

 

 

}

 

 

 

 

 

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

 

 

msg.messages[this.id1«null;

 

 

)

 

 

 

 

 

msg.Message.prototype,defaultLifetime=function(){

 

 

 

if

(this.priority<=msg.PRIORITY_LOW.id){

 

 

 

return msg. PRIORITY__LOW. lifetime;

 

 

 

}else if {this.priority==msg.PRIORITY_DEFAULT.id){

 

 

 

return msg.PRIORITY_DEFAULT.lifetime;

 

 

 

}else if (this.priority>=msg.PRIORITY_HIGH.id){

 

 

 

return msg.PRIORITY_HIGH.lifetime;

 

 

)1

 

 

 

 

 

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

 

 

 

if

(this.priority<=msg.PRIORITY_LOW.id){

 

 

 

return msg.PRIORITY_LOW.icon;

 

 

 

Jelse if (this.priority==msg.PRIORITY_DEFAULT.id){

 

 

 

return msg.PRIORITY_DEFAULT.icon;

 

 

 

Jelse if (this.priority>=msg.PRIORITY_HIGH.id){

 

 

 

return msg.PRIORITY_HIGH.icon;

 

 

}

 

 

 

 

 

_ }

 

 

'_

 

ш

 

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

Уровни приоритета, низкий, средний (он же приоритет по умолчанию) и высокий, определяются с помощью констант. В соответствие каждому приоритету поставлены пиктограмма и время жизни (задаваемое в секундах). Пиктограмму и время жизни можно переопределить с помощью необязательных параметров, предусмотренных в конструкторе. Время жизни, равное -1.

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

I

Рис. 6.2. Интерфейс системы оповещения в виде строки состояния. Сообщения отображаются в виде пиктограмм

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

>

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

Ы.2. Определение требований к пользовательскому интерфейсу

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

.визуального отображения информации, предназначенной для пользователя. В данном случае мы выберем вариант строки состояния, в которой информация будет отображаться в виде пиктограмм (рис. 6.2).

Пиктограмма с красным знаком X — стандартное средство оповещения на вязком уровне. Третья слева пиктограмма на рисунке — это шар синего цвета, отбрасывающий тень; данная пиктограмма переопределяет изображение X. используемое по умолчанию. Более подробную информацию о сообщении, ^соответствующем каждой пиктограмме в строке состояния, можно получить $ окне подсказки (рис. 6.3).

[ Такой механизм оповещения нельзя назвать навязчивым. Строка состоя- •шя занимает относительно мало места на экране, но появление новой пиктотраммы заметно для пользователя. Срочные сообщения надо сделать более заметными, поэтому мы отображаем в строке состояния только низкоприо- ^ригетные сообщения. Информация с более высоким приоритетом выводится -В диалоговом окне (рис. 6.4), которое отображается на экране до тех пор, дюка пользователь не удалит его.

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

6.4. Реализация базовых средств оповещения

I

 

 

Мы определили два основных элемента пользовательского интерфейса: стрск! ку состояния и всплывающее диалоговое окно. Теперь приступим к их реаЦ лизации. Система оповещения достаточно сложна, поэтому разобьем работу| над ней на отдельные этапы. Сначала модифицируем объект Message так| чтобы он мог обеспечить свое отображение в разных ситуациях когда <ж представлен в строке состояния в виде пиктограммы, при выводе окна под-*] сказки или в составе всплывающего диалогового окна. Начнем с реализации: строки состояния.

6.4.1. Отображение пиктограмм в строке состояния

Строка состояния должна отображаться на экране и содержать пиктограм-' мы, представляющие активизированные сообщения. Обязанности по воспроизведению конкретных пиктограмм мы делегируем объекту Message. Его можно описать в терминах архитектуры MVC. Средства отображения рассматриваются как представление, а интерактивные элементы выполняют роль контроллера. Если бы мы предполагали использовать произвольные механизмы отображения, то столкнулись бы с серьезными проблемами при реализации системы оповещения. Однако мы не планируем подобное решение. Более того, для обеспечения согласованности в пределах приложения надо использовать единый механизм оповещения. В листинге 6.4 показан метод, предназначенный для воспроизведения объекта Message.

Листинг 6.4. базовые 'Средства отображения сообц&йрй^ < * * ~""~Щ

//О Воспроизведение сообщения rasg.Message.prototype.render=function(el){

if (this.priority<=msg.PRIORITY_LOW,id){ this.renderSmall(el);

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

253

lelse if (this.priority>=msg.PRIORXTX_DEFAULT.id){ this.renderFull(el);

}

)

//@ Вывести в качестве пиктограммы с поддержкой

//окна подсказки

msg.Message.prototype.renderSmall=function{el){

this.icoTd=document.createElement("div"); var ico=document.createElement("img"); ico.src=this.icon; ico.classNarae="msg_smalI_icon"; this.icoTd.appendChild(ico); this.icoTd.messageObj=this;

this . icoTd.onmouseover=msg.moverIconTooltip,- this.icoTd.onmouseout=msg.moutlconTooltip; this.icoTd.onclick=msg.clicklconTooltip; el.appendChild(this.icoTd);

}

// ® Помещение курсора мыши над кнопкой msg.moverIconTooltip=function(e){

var event=e || window.event; var message=this.messageObj; var popped=message.popped; if ('popped){

message. showi?opup(event, f a l s e ) ;

}

}

// О Перемещение курсора м ш и за пределы кнопки msg.moutIconTooltip=function(e){

var message=this.messageObj; v a r popped=rnessage.popped; var pinned=message.pinned; if {popped && 'pinned){ message.hidePopup();

}

)

/ / 0 Щелчок мьппью

m s g . c l i c k I c o n T o o l t i p = f u n c t i o n { e ) { var event=e || window.event; var message=this.messageObj; var popped=message.popped;

var pinned=message.pinned; var 6xpired=message.expired; if (popped && pinned){ message.hidePopup{);

if (expired){ message.unrender();

}

}else{

message.showPopup(event.true);

}

}

// © Вывести окно с подсказкой msg.Message.prototype.showPopup=function(event,pinned){

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

this.pinned=pinned; if (!this.popup){

this.popup=document.createEleraent("dxv"); this.popup.className='popup' ; this.renderFull(this.popup); document.body.appendChild(this.popup);

}

this.popup.style.display='block1; var popX=event.clientX;

var popY=event.clientY-xHeight(this.popup)-12; xMoveTo(thispopup,popX,popY);

if {msg.popper && msg.popper!=this){ msg.popper.hidePopupf);

}

this.popped=true;

msg.popper=this;

}

// Скрыть окно с подсказкой msg.Message.prototype.hidePopup=function(){

i£ (this.popped){ if (this.popup){

this.popup.style.display='none' ;

}

this.popped=false;

}

__}

 

m

 

 

 

 

 

Мы реализовали для объекта Message высокоуровневый код отображения и определили детали представления в строке состояния. Начнем с высокоуровневого кода. Метод render () О получает в качестве параметра элемент DOM. В зависимости от приоритета сообщения он передает управление либо методу renderSmall() ®, либо методу renderFulK) ©. В строке состояния представлены только сообщения с низким приоритетом, которые отображаются в виде пиктограмм. При помещении на такую пиктограмму курсора мыши выводится окно с подсказкой (рис. 6.2 и 6.3).

Функция rendersmalio отображает пиктограмму в составе элемента DOM и устанавливает обработчики событий, в процессе работы которых выводится подсказка.

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

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

6.4.2. Отображение подробных сообщений

В диалоговом окне выводятся высокоприоритетные сообщения либо сообщения, имеющие приоритет по умолчанию. Они отображаются в виде пиктограмм, сопровождаемых строкой текста (рис. 6.4). Такое же представление требуется нам для окон подсказки, соответствующих пиктограммам в строке состояния. Функция showPopupO, отвечающая за вывод подсказки, обращается к методу renderFull {), который отображает детальное сообщение. Этот же метод используется для представления информации в диалоговом окне. Такой подход исключает неоправданное дублирование кода и, кроме того, обеспечивает согласованность внешнего вида различных элементов пользовательского интерфейса. Код метода renderFull () приведен в листинге 6.5.

ЛИСТИНГ 6.5. Метод renderFull О

! / •

-ЦЦ

msg.Message.prototype.renderFull=functaon(el){

 

var inTable=(el.tagName=="TBODY"); var topEl=null;

this.row=document.createElement("tr"); if (!inTable){

topEl=document.createElement{"table") ; var bod=document.createElement("tbody"); topEl.appendChild(bod); bod.appendChild(this.row);

}else{

topEl=this.row;

}

var icoTd=document.createElement("td"); icoTd.valign=icenter' ; this.row.appendChild(icoTd);

var ico=document.createElement{"img"); ico.src=this.icon; icoTd.className="msg_large_icon";

icoTd.appendChild(ico);

var txtTd=document.createElement("td"); txtTd . valign - 'top' ;

txtTd.className="msg_text"; this.row.appendChild{txtTd); txtTd.innerHTML=thi s.message;

el.appendChild(topEl);

_}

я

Метод renderFull () генерирует строку таблицы. Он проверяет элемент DOM и, если тот представляет собой дескриптор <tbody>, непосредственно включает его в таблицу. В противном случае генерируются необходимые дескрипторы <table> и <tbody>. Такой подход позволяет включать в таблицу, содержащуюся в диалоговом окне, несколько сообщений и в то же время обеспечивает корректное заполнение элемента <div> в окне подсказки.

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

*

Заметьте, что мы не используем здесь методы W3C DOM, текст сообщения включается в состав таблицы посредством свойства innerHTML. Это открывает дополнительные возможности по представлению HTML-элементов по сравнению с генерацией обычного текстового узла.

6.4.3. Формирование готовой системы

На данном этапе нами полностью реализована поддержка пиктограмм в строке состояния и отображение текста сообщений. За вывод диалогового окна и строки состояний отвечает высокоуровневый метод render (), код которого показан в листинге 6.6.

Листинг 6.6. Функция msg. render ()

msg.render=function(msgbar){ if (!msgbar){ msgbar='msgbar' ;

}

// О Обеспечить вывод строки состояния msg.msgbarDiv=xGetElementById(msgbar); if (!msg.msgbarDiv){ msg.msgbarDiv=msg.createBar(msgbar);

}

styling.removeAllChildren(msg.msgbarDiv); var lows=new Array();

v a r nveds=new Array О ; var highs=new Array();

// @ Сортировать сообщения в соответствии с приоритетом for (var i in msg.messages){

var message=msg.messages[i]; if (message){

if (message.priority<=msg.PRIORITY__LOW.id) { lows.append (message);

}else if (raessage.priority==msg.PRIORITY_DEFAULT.id){ meds.append(message);

Jelse if (message.priority>=msg.PRIORITY_HIGH.id){ highs.append(message);

}

}

}

// © Воспроизвести низкоприоритетные сообщения for (var i=0;i<lows.length;i++){

lows fi].render(msg.msgbarDiv);

}

// О Воспроизвести сообщения с высоким и средним приоритетом if (meds.length+highs.length>O){ msg.dialog=xGetElementByld(msgbar+"_dialog");

if (!msg.dialog){ msg.dialog=msg.createDialog( msgbar+"_dialog", msg.msgbarDiv, (highs.length>0) );

// 0 Обеспечить отображение диалогового окна

}

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

styling.removeAHChildren(msg.dialog.tbod); for (var i=0;i<highs.length;i++){ highs[i].render(msg.dialog.tbod);

}

for (var i=0;Kmeds.length;i++){ meds[i].render(msg.dialog.tbod);

}

if (highs.length>0){ msg.dialog.ico.src=msg.PRIORITY_HIGH.icon; }else{ msg.dialog.ico.src=msg.PRIORITY_DEFAULT.icon;

}

)

}

// 0 Создать строку состояния msg.createBar=function(id){

var msgbar=document.createElement("div"); msgbar.className='msgbar' ;

msgbar,id=id;

var parentEl=document.body; parentEl.append{msgbar); return msgbar;

}

// в Создать диалоговое окно msg.createDialog=function(id,bar,isModal){

var dialog=document.createElement("div"); dialog.className='dialog' ;

dialog.id=id;

var tbl=document.createElement("table"); dialog.appendChiId(tbl); dialog.tbod=document.createElement("tbody"); tbl.appendChild(dialog.tbod);

var closeB-atton=document. createElement (" div" ); closeButton.dialog=dialog; closeButton.onclick=msg.hideDialog;

var closeTxt=document.createTextNode("x"); closeButton.appendChild(closeTxt); dialog.appendChild(closeButton);

// О Добавить модальный уровень if (isModal){

dialog.modalLayer=document.createElement("div"); dialog.modalLayer.className='modal' ; dialog.modalLayer.appendChild(dialog); document.body.appendChild(dialog.modalLayer); }else{

dialog.clas sName+=' non-modal'; document.body.appendChild(dialog);

}

d i a l o g . i c o = d o c u m e n t . c r e a t e E l e m e n t ( " i m g " ) ; d i a l o g . i c o . c l a s s N a m e = " m s g _ d i a l o g _ i c o n " ;

d i a l o g . i c o . d i a l o g = d i a l o g ;

d i a l o g . i c o . o n c l i c k = m s g - s h o w D i a l o g ; b a r . a p p e n d C h i l d ( d i a l o g . i c o ) ;

r e t u r n d i a l o g ;

}