Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ajax In Action (2006).pdf
Скачиваний:
63
Добавлен:
17.08.2013
Размер:
8.36 Mб
Скачать

230CHAPTER 6

The user experience

var txtTd=document.createElement("td"); txtTd.valign='top'; txtTd.className="msg_text"; this.row.appendChild(txtTd); txtTd.innerHTML=this.message;

el.appendChild(topEl);

}

The renderFull() method generates a table row for the message. It checks the DOM element that it is being appended to, and it will append itself directly if it is a <tbody> tag or generate the necessary <table> and <tbody> tags if not. This allows multiple messages to be presented in the same table in the main dialog and the tooltip <div> tag to be correctly populated.

Note that the message text is attached to the user interface using innerHTML rather than the W3C DOM methods that we usually use. This allows notifications to use HTML markup to present themselves in a richer fashion than if we were simply generating a text node.

6.4.3Putting the pieces together

Having provided mechanisms for iconized and full-size display of messages, we’ve provided a comprehensive render() method for individual messages. The dialog and status bar themselves are generated by a top-level render() method, as shown in listing 6.6.

Listing 6.6 msg.render() function

msg.render=function(msgbar){

 

 

 

 

if (!msgbar){

 

 

 

 

msgbar='msgbar';

 

 

 

 

}

 

 

 

Ensure status

msg.msgbarDiv=xGetElementById(msgbar);

 

b

 

if (!msg.msgbarDiv){

 

 

 

bar exists

msg.msgbarDiv=msg.createBar(msgbar);

 

 

 

}

 

 

 

 

styling.removeAllChildren(msg.msgbarDiv);

 

 

 

var lows=new Array();

 

 

 

 

var meds=new Array();

 

 

 

 

var highs=new Array();

c Sort messages by priority

for (var i in msg.messages){

var message=msg.messages[i];

 

 

 

 

if (message){

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

Implementing a notification framework

231

 

 

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

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

}

}

}

for (var i=0;i<lows.length;i++){ d Render low-priority messages lows[i].render(msg.msgbarDiv);

}

if (meds.length+highs.length>0){ e Render higher-priority messages msg.dialog=xGetElementById(msgbar+"_dialog");

if (!msg.dialog){ msg.dialog=msg.createDialog(

msgbar+"_dialog", msg.msgbarDiv,

(highs.length>0) ); f Ensure dialog exists

}

styling.removeAllChildren(msg.dialog.tbod); for (var i=0;i<highs.length;i++){

highs[i].render(msg.dialog.tbod);

}

for (var i=0;i<meds.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;

}

}

}

msg.createBar=function(id){ g Create a status bar 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){ h Create a pop-up dialog var dialog=document.createElement("div"); dialog.className='dialog';

dialog.id=id;

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

232CHAPTER 6

The user experience

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

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

if (isModal){ i Add modal layer if need be dialog.modalLayer=document.createElement("div"); dialog.modalLayer.className='modal'; dialog.modalLayer.appendChild(dialog); document.body.appendChild(dialog.modalLayer);

}else{

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

}

dialog.ico=document.createElement("img"); dialog.ico.className="msg_dialog_icon"; dialog.ico.dialog=dialog; dialog.ico.onclick=msg.showDialog; bar.appendChild(dialog.ico);

return dialog;

}

 

msg.hideDialog=function(e){

Hide the dialog

var dialog=(this.dialog) ? this.dialog : msg.dialog; if (dialog){

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

}else{

dialog.style.display='none';

}

}

}

 

msg.showDialog=function(e){

Show the dialog

var dialog=(this.dialog) ? this.dialog : msg.dialog; if (dialog){

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

}else{

dialog.style.display='block';

}

}

}

Implementing a notification framework

233

 

 

render() can be called more than once. It will check for the presence of the common UI components b, f and create them if necessary, using the createDialog() hand createBar() gfunctions. These assemble the UI components using standard DOM manipulation methods and event handlers, such as those used to show and hide the dialog.

To render all notifications, the system first sorts them by priority into three temporary arrays c. Low-priority messages are then rendered to the status bar dand other messages to the dialog, higher-priority messages first e.

To implement a modal dialog, we simply nest the visible dialog within another DIV element that occupies the entire screen, blocking any mouse events from getting through to the main user interface i. This modal DIV has a background pattern of alternating white and transparent pixels to gray out the interface, giving a clear indication that the dialog is modal. We use this rather than CSS transparency settings because the latter will make any nested elements, such as the dialog itself, transparent, too. This is implemented in the CSS file for our notification framework, presented in listing 6.7.

Listing 6.7 msg.css

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

}

.msg_dialog_icon{ height: 32px; width: 32px; position:relative; float:right;

}

.msg_large_icon{ height: 64px; width: 64px;

}

.msg_text{ font-family: arial; font-weight: light; font-size: 14pt; color: blue;

}

234CHAPTER 6

The user experience

.msgbar{

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

height: 38px; padding: 2px;

}

.dialog{

position: absolute; background-color: white; border: solid blue 1px; width: 420px;

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

}

.popup{

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

}

.non-modal{

}

.modal{

position: absolute; top: 0px;

left: 0px; width: 100%; height: 100%;

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

}

It’s worth noting the use of the CSS float attribute in the msg_small_icon and msg_dialog_icon classes, which are used to render the icons in the status bar. msg_small_icon, which renders the icons for low-priority messages that present the tooltips, uses a left float to align them to the left edge, and msg_dialog_icon uses a right float to align the icon that launches the dialog to the right edge. The framework allows the status bar to be rendered in any shape or size of DIV element. Floating elements will align themselves in a sensible fashion, wrapping into vertically aligned bars, if needed (figure 6.5).

Implementing a notification framework

235

 

 

Figure 6.5

Using CSS float attributes allows a list of icons to fit into a variety of shapes of container. Here we have changed the status bar to a square, and the cross and blue sphere icons on the left wrap themselves into the new area automatically, while the launcher for the dialog floats to the right.

Finally, we need to modify our Message object now that we have a user interface for it. As individual messages are created, they have the ability to add themselves to the user interface and will remove themselves when the message expires. Listing 6.8 presents the changes needed to implement this functionality.

Listing 6.8 Modified Message object

var msg=new Object();

 

 

 

 

 

msg.PRIORITY_LOW=

{ id:1, lifetime:30, icon:"img/msg_lo.png"

};

msg.PRIORITY_DEFAULT={

id:2,

lifetime:60,

icon:"img/msg_def.png" };

msg.PRIORITY_HIGH=

{

id:3,

lifetime:-1,

icon:"img/msg_hi.png"

};

msg.messages=new Array(); 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.defaultIcon();

if (this.lifetime>0){ this.fader=setTimeout(

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

this.lifetime*1000

);

}

if (!msg.suppressRender){ b Extra arguments this.attachToBar();

}

}

 

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

c Extra arguments

if (!msg.msgbarDiv){

 

msg.render();

 

236CHAPTER 6

The user experience

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

}else{

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){ d Extra arguments this.row.style.display='none';

this.row.messageObj=null;

this.row=null;

}

if (this.icoTd){ this.icoTd.style.display='none'; this.icoTd.messageObj=null; this.icoTd=null;

}

}

We want the framework to be easy for developers to work with, so when a message is created, we automatically attach it to the user interface b. Simply invoking the constructor will cause the new message to render itself. Depending on the message priority, it will attach itself to the status bar or dialog as appropriate c. For cases where we don’t want the overhead of rendering for each message—such as when adding several messages at once—we provide a flag to suppress automatic rendering. In such cases, we can manually call msg.render() after creating a large number of messages.

Likewise, when removing a message in the clear() function, we automatically remove any user interface elements, so that the message goes away d.

We now have a useful framework for presenting notifications to the user. We can trigger it manually but also make use of it in our other reusable code components. In the following section, we demonstrate how to hook it up to our ContentLoader object to report on the progress of network downloads.