- •preface
- •acknowledgments
- •about this book
- •Who should read this book?
- •Roadmap
- •Code conventions
- •Code downloads
- •Author Online
- •About the title
- •About the cover illustration
- •Rethinking the web application
- •A new design for the Web
- •1.1 Why Ajax rich clients?
- •1.1.1 Comparing the user experiences
- •1.1.2 Network latency
- •1.1.3 Asynchronous interactions
- •1.1.4 Sovereign and transient usage patterns
- •1.1.5 Unlearning the Web
- •1.2 The four defining principles of Ajax
- •1.2.1 The browser hosts an application, not content
- •1.2.2 The server delivers data, not content
- •1.2.3 User interaction with the application can be fluid and continuous
- •1.2.4 This is real coding and requires discipline
- •1.3 Ajax rich clients in the real world
- •1.3.1 Surveying the field
- •1.3.2 Google Maps
- •1.4 Alternatives to Ajax
- •1.4.2 Java Web Start and related technologies
- •1.5 Summary
- •1.6 Resources
- •First steps with Ajax
- •2.1 The key elements of Ajax
- •2.2 Orchestrating the user experience with JavaScript
- •2.3 Defining look and feel using CSS
- •2.3.1 CSS selectors
- •2.3.2 CSS style properties
- •2.3.3 A simple CSS example
- •2.4 Organizing the view using the DOM
- •2.4.1 Working with the DOM using JavaScript
- •2.4.2 Finding a DOM node
- •2.4.3 Creating a DOM node
- •2.4.4 Adding styles to your document
- •2.4.5 A shortcut: Using the innerHTML property
- •2.5 Loading data asynchronously using XML technologies
- •2.5.1 IFrames
- •2.5.2 XmlDocument and XMLHttpRequest objects
- •2.5.3 Sending a request to the server
- •2.5.4 Using callback functions to monitor the request
- •2.5.5 The full lifecycle
- •2.6 What sets Ajax apart
- •2.7 Summary
- •2.8 Resources
- •Introducing order to Ajax
- •3.1 Order out of chaos
- •3.1.1 Patterns: creating a common vocabulary
- •3.1.2 Refactoring and Ajax
- •3.1.3 Keeping a sense of proportion
- •3.1.4 Refactoring in action
- •3.2 Some small refactoring case studies
- •3.2.2 Managing event handlers: Observer pattern
- •3.2.3 Reusing user action handlers: Command pattern
- •3.2.4 Keeping only one reference to a resource: Singleton pattern
- •3.3 Model-View-Controller
- •3.4 Web server MVC
- •3.4.1 The Ajax web server tier without patterns
- •3.4.2 Refactoring the domain model
- •3.4.3 Separating content from presentation
- •3.5 Third-party libraries and frameworks
- •3.5.2 Widgets and widget suites
- •3.5.3 Application frameworks
- •3.6 Summary
- •3.7 Resources
- •Core techniques
- •The page as an application
- •4.1 A different kind of MVC
- •4.1.1 Repeating the pattern at different scales
- •4.1.2 Applying MVC in the browser
- •4.2 The View in an Ajax application
- •4.2.1 Keeping the logic out of the View
- •4.2.2 Keeping the View out of the logic
- •4.3 The Controller in an Ajax application
- •4.3.1 Classic JavaScript event handlers
- •4.3.2 The W3C event model
- •4.3.3 Implementing a flexible event model in JavaScript
- •4.4 Models in an Ajax application
- •4.4.1 Using JavaScript to model the business domain
- •4.4.2 Interacting with the server
- •4.5 Generating the View from the Model
- •4.5.1 Reflecting on a JavaScript object
- •4.5.2 Dealing with arrays and objects
- •4.5.3 Adding a Controller
- •4.6 Summary
- •4.7 Resources
- •The role of the server
- •5.1 Working with the server side
- •5.2 Coding the server side
- •5.2.1 Popular implementation languages
- •5.3 The big picture: common server-side designs
- •5.3.1 Naive web server coding without a framework
- •5.3.2 Working with Model2 workflow frameworks
- •5.4 The details: exchanging data
- •5.4.2 Introducing the planet browser example
- •5.5 Writing to the server
- •5.5.1 Using HTML forms
- •5.5.2 Using the XMLHttpRequest object
- •5.5.3 Managing user updates effectively
- •5.6 Summary
- •5.7 Resources
- •Professional Ajax
- •The user experience
- •6.1 Getting it right: building a quality application
- •6.1.1 Responsiveness
- •6.1.2 Robustness
- •6.1.3 Consistency
- •6.1.4 Simplicity
- •6.1.5 Making it work
- •6.2 Keeping the user informed
- •6.2.1 Handling responses to our own requests
- •6.2.2 Handling updates from other users
- •6.3 Designing a notification system for Ajax
- •6.3.1 Modeling notifications
- •6.3.2 Defining user interface requirements
- •6.4 Implementing a notification framework
- •6.4.1 Rendering status bar icons
- •6.4.2 Rendering detailed notifications
- •6.4.3 Putting the pieces together
- •6.5 Using the framework with network requests
- •6.6 Indicating freshness of data
- •6.6.1 Defining a simple highlighting style
- •6.6.2 Highlighting with the Scriptaculous Effects library
- •6.7 Summary
- •6.8 Resources
- •Security and Ajax
- •7.1 JavaScript and browser security
- •7.1.1 Introducing the “server of origin” policy
- •7.1.2 Considerations for Ajax
- •7.1.3 Problems with subdomains
- •7.2 Communicating with remote services
- •7.2.1 Proxying remote services
- •7.2.2 Working with web services
- •7.3 Protecting confidential data
- •7.3.1 The man in the middle
- •7.3.2 Using secure HTTP
- •7.3.3 Encrypting data over plain HTTP using JavaScript
- •7.4 Policing access to Ajax data streams
- •7.4.1 Designing a secure web tier
- •7.4.2 Restricting access to web data
- •7.5 Summary
- •7.6 Resources
- •Performance
- •8.1 What is performance?
- •8.2 JavaScript execution speed
- •8.2.1 Timing your application the hard way
- •8.2.2 Using the Venkman profiler
- •8.2.3 Optimizing execution speed for Ajax
- •8.3 JavaScript memory footprint
- •8.3.1 Avoiding memory leaks
- •8.3.2 Special considerations for Ajax
- •8.4 Designing for performance
- •8.4.1 Measuring memory footprint
- •8.4.2 A simple example
- •8.5 Summary
- •8.6 Resources
- •Ajax by example
- •Dynamic double combo
- •9.1 A double-combo script
- •9.2 The client-side architecture
- •9.2.1 Designing the form
- •9.2.2 Designing the client/server interactions
- •9.3 Implementing the server: VB .NET
- •9.3.1 Defining the XML response format
- •9.4 Presenting the results
- •9.4.1 Navigating the XML document
- •9.4.2 Applying Cascading Style Sheets
- •9.5 Advanced issues
- •9.5.2 Moving from a double combo to a triple combo
- •9.6 Refactoring
- •9.6.1 New and improved net.ContentLoader
- •9.7 Summary
- •Type-ahead suggest
- •10.1 Examining type-ahead applications
- •10.1.2 Google Suggest
- •10.2.1 The server and the database
- •10.3 The client-side framework
- •10.3.1 The HTML
- •10.3.2 The JavaScript
- •10.3.3 Accessing the server
- •10.5 Refactoring
- •10.5.1 Day 1: developing the TextSuggest component game plan
- •10.5.3 Day 3: Ajax enabled
- •10.5.4 Day 4: handling events
- •10.5.6 Refactor debriefing
- •10.6 Summary
- •11.1 The evolving portal
- •11.1.1 The classic portal
- •11.1.2 The rich user interface portal
- •11.2 The Ajax portal architecture using Java
- •11.3 The Ajax login
- •11.3.1 The user table
- •11.4 Implementing DHTML windows
- •11.4.1 The portal windows database
- •11.4.3 Adding the JS external library
- •11.5 Adding Ajax autosave functionality
- •11.5.1 Adapting the library
- •11.5.2 Autosaving the information to the database
- •11.6 Refactoring
- •11.6.1 Defining the constructor
- •11.6.2 Adapting the AjaxWindows.js library
- •11.6.3 Specifying the portal commands
- •11.6.4 Performing the Ajax processing
- •11.6.5 Refactoring debrief
- •11.7 Summary
- •Live search using XSLT
- •12.1 Understanding the search techniques
- •12.1.1 Looking at the classic search
- •12.1.3 Examining a live search with Ajax and XSLT
- •12.1.4 Sending the results back to the client
- •12.2 The client-side code
- •12.2.1 Setting up the client
- •12.2.2 Initiating the process
- •12.3 The server-side code: PHP
- •12.3.1 Building the XML document
- •12.3.2 Building the XSLT document
- •12.4 Combining the XSLT and XML documents
- •12.4.1 Working with Microsoft Internet Explorer
- •12.4.2 Working with Mozilla
- •12.5 Completing the search
- •12.5.1 Applying a Cascading Style Sheet
- •12.5.2 Improving the search
- •12.5.3 Deciding to use XSLT
- •12.5.4 Overcoming the Ajax bookmark pitfall
- •12.6 Refactoring
- •12.6.1 An XSLTHelper
- •12.6.2 A live search component
- •12.6.3 Refactoring debriefing
- •12.7 Summary
- •Building stand-alone applications with Ajax
- •13.1 Reading information from the outside world
- •13.1.1 Discovering XML feeds
- •13.1.2 Examining the RSS structure
- •13.2 Creating the rich user interface
- •13.2.1 The process
- •13.2.3 Compliant CSS formatting
- •13.3 Loading the RSS feeds
- •13.3.1 Global scope
- •13.3.2 Ajax preloading functionality
- •13.4 Adding a rich transition effect
- •13.4.2 Implementing the fading transition
- •13.4.3 Integrating JavaScript timers
- •13.5 Additional functionality
- •13.5.1 Inserting additional feeds
- •13.5.2 Integrating the skipping and pausing functionality
- •13.6 Avoiding the project’s restrictions
- •13.6.1 Overcoming Mozilla’s security restriction
- •13.6.2 Changing the application scope
- •13.7 Refactoring
- •13.7.1 RSS reader Model
- •13.7.2 RSS reader view
- •13.7.3 RSS reader Controller
- •13.7.4 Refactoring debrief
- •13.8 Summary
- •The Ajax craftsperson’s toolkit
- •A.1 Working smarter with the right toolset
- •A.1.1 Acquiring tools that fit
- •A.1.2 Building your own tools
- •A.1.3 Maintaining your toolkit
- •A.2 Editors and IDEs
- •A.2.1 What to look for in a code editor
- •A.2.2 Current offerings
- •A.3 Debuggers
- •A.3.1 Why we use a debugger
- •A.3.2 JavaScript debuggers
- •A.3.3 HTTP debuggers
- •A.3.4 Building your own cross-browser output console
- •A.4 DOM inspectors
- •A.4.1 Using the Mozilla DOM Inspector
- •A.4.2 DOM inspectors for Internet Explorer
- •A.4.3 The Safari DOM Inspector for Mac OS X
- •A.5 Installing Firefox extensions
- •A.6 Resources
- •JavaScript for object-oriented programmers
- •B.1 JavaScript is not Java
- •B.2 Objects in JavaScript
- •B.2.1 Building ad hoc objects
- •B.2.2 Constructor functions, classes, and prototypes
- •B.2.3 Extending built-in classes
- •B.2.4 Inheritance of prototypes
- •B.2.5 Reflecting on JavaScript objects
- •B.2.6 Interfaces and duck typing
- •B.3 Methods and functions
- •B.3.1 Functions as first-class citizens
- •B.3.2 Attaching functions to objects
- •B.3.3 Borrowing functions from other objects
- •B.3.4 Ajax event handling and function contexts
- •B.3.5 Closures in JavaScript
- •B.4 Conclusions
- •B.5 Resources
- •Ajax frameworks and libraries
- •Accesskey Underlining Library
- •ActiveWidgets
- •Ajax JavaServer Faces Framework
- •Ajax JSP Tag Library
- •Ajax.NET
- •AjaxAC
- •AjaxAspects
- •AjaxCaller
- •AjaxFaces
- •BackBase
- •Behaviour
- •Bindows
- •BlueShoes
- •CakePHP
- •CL-Ajax
- •ComfortASP.NET
- •Coolest DHTML Calendar
- •Dojo
- •DWR (Direct Web Remoting)
- •Echo 2
- •FCKEditor
- •Flash JavaScript Integration Kit
- •Google AjaxSLT
- •Guise
- •HTMLHttpRequest
- •Interactive Website Framework
- •Jackbe
- •JPSpan
- •jsolait
- •JSON
- •JSRS (JavaScript Remote Scripting)
- •LibXMLHttpRequest
- •Mochikit
- •netWindows
- •Oddpost
- •OpenRico
- •Pragmatic Objects
- •Prototype
- •Qooxdoo
- •RSLite
- •Ruby on Rails
- •Sack
- •SAJAX
- •Sarissa
- •Scriptaculous
- •SWATO…
- •Tibet
- •TinyMCE
- •TrimPath Templates
- •Walter Zorn’s DHTML Libraries
- •WebORB for .NET
- •WebORB for Java
- •XAJAX
- •x-Desktop
- •XHConn
- •index
- •Symbols
- •Numerics
Combining the XSLT and XML documents |
481 |
|
|
elements in the XML namespace are recognized in the source document and not in the results document. The prefix xsl is used to define all our elements in the XSLT namespace. We can then set the template rule to the match pattern of / d, which references the whole document.
We can start building the table template that displays our results. We add the table tag e, giving the table an ID. The table header row f is next inserted, which contains the column names to be displayed to the user so she can understand what information is contained in the table.
By looping through the source node set, we obtain the remaining rows of the table. For this, we use the for-each loop g to iterate over the records to obtain the nodes that are located in phonebook/entry.
The column values have to be selected as we are looping through the document tree. To select the values from the nodes, we use value-of h, which extracts the value of an XML element and adds it to the output stream of the transformation. To specify the XML element whose text we want to retrieve, we use the select attribute with the element’s name. Now that we have built the XSLT file and created the code to dynamically generate the XML document, we can finish building the JavaScript code to see how the XSLT transformation structures our XML file into a viewable table when we combine them.
The next step takes us back to the client, which retrieves the files that we just created with the HTTP response.
12.4 Combining the XSLT and XML documents
Back on the client, we need to combine the XSLT and XML documents from the server. When using an XSLT transformation, we’ll find that the browsers differ on how they combine the two documents. Therefore, we first check to see what method the browser supports in order to load and combine our two documents.
Again we’re using the ContentLoader object, introduced in chapter 3. It is contained in the external JavaScript file, net.js. This file does all of the work of determining how to send the information to the server, hiding any browserspecific code behind the easy-to-use wrapper object.
<script type="text/javascript" src="net.js"></script>
Now we can begin the process of obtaining the server-side files and combining them on the client. In listing 12.7, the LoadXMLXSLTDoc() function is being called from the function GrabNumber() in listing 12.2. The function GrabNumber() passes in the values for the URL that generates the XML data, the XSL file, and
482CHAPTER 12
Live search using XSLT
the output reference ID. With these three values, we are able to load the two documents and combine them when both have been completely loaded. After they have been loaded, we have to do some cross-browser coding in order to combine the XML and XSL files. You can see all of this happening in listing 12.7.
Listing 12.7 LoadXMLXSLTDoc
var xmlDoc; |
b Declare global |
|
|
|
var xslDoc; |
variables |
|
|
|
var objOutput; |
|
|
|
|
function LoadXMLXSLTDoc(urlXML,urlXSL,elementID){ |
|
|||
xmlDoc=null; |
c Set variables |
|
|
|
xslDoc=null; |
to null |
|
d Reference |
|
|
||||
objOutput = document.getElementById( |
|
|||
|
elementId); |
|
|
output element |
new net.ContentLoader(urlXML,onXMLLoad); |
|
e |
Load XML and |
|
|
||||
new net.ContentLoader(urlXSL,onXSLLoad); |
|
|
XSL files |
} |
|
|
function onXMLLoad(){ |
|
f Handle XML |
|
||
xmlDoc=this.req.responseXML; |
|
document |
doXSLT(); |
|
|
|
|
|
} |
|
|
function onXSLLoad(){ |
|
g Handle XSL |
|
||
xslDoc=this.req.responseXML; |
|
document |
doXSLT(); |
|
|
|
|
|
} |
|
|
function doXSLT(){
if (xmlDoc==null || xslDoc==null){ return; } h if (window.ActiveXObject){
objOutput.innerHTML=xmlDoc.transformNode(xslDoc);
}
else{
var xsltProcessor = new XSLTProcessor(); xsltProcessor.importStylesheet(xslDoc); var fragment =xsltProcessor.
transformToFragment(
xmlDoc,document); objOutput.innerHTML = ""; objOutput.appendChild(fragment);
}
iTransformXML document
}
Combining the XSLT and XML documents |
483 |
|
|
To simplify the client-side script, we need to declare three global variables b to hold three different objects. The first two variables, xmlDoc and xslDoc, are going to hold the XML and XSLT files returned from the server. The third variable, objOutput, holds the object reference to our DOM element where the results are to be inserted. With these variables defined, we can now build the function LoadXMLXSLTDoc(), which we invoked from the function GrabNumber().
Since we are loading two documents, we need a way to determine when they are both loaded. We do this by looking to see if the variables xmlDoc and xslDoc contain their respective documents. Before we start, we must set the variables to null c. This removes any data that may exist in the variables if this function is run more than once on the page. The output location for the results is set d by using the passed element ID from the function call. Now we call the ContentLoader etwice, once for the XML document and once for the XSL document. In each call, the ContentLoader will get the URL and then call another function to load the documents. onXMLLoad() floads the returned XML results into our global variable xmlDoc and then calls the function doXSLT() for future processing. onXSLLoad() gloads the XSL document into the global variable xslDoc and also calls the doXSLT() function.
Processing cannot continue until both documents have been loaded, and there is no way of knowing which will be loaded first, so the doXSLT() function first checks for that. It is called twice, once after the XML document is loaded and once after the XSL document is loaded. The first time it is called, one of our global variables is still set to null and we exit the function h. The next time it is called, the function will not exit since neither of the variables will be null. With both documents now loaded, we are able to perform the XSLT transformation i.
Once the two documents are loaded, we need to transform the XML document with the XSLT. As you can see by looking at the code in the listing, there are two different ways to do this, depending on the browser. Internet Explorer uses transformNode(), whereas Mozilla uses the XSLTProcessor object. Let’s examine these two different implementations of performing the transformation in greater detail.
12.4.1Working with Microsoft Internet Explorer
Internet Explorer makes it easy to transform the XML document with the XSLT with only a few lines of code. We use the transformNode() method, which takes the XML and XSLT documents and combines them in one step:
484CHAPTER 12
Live search using XSLT
if (window.ActiveXObject){ objOutput.innerHTML=xmlDoc.transformNode(xslDoc);
}
We first check to see if the browser supports the transformNode() method. We’ve done this here by testing for ActiveX object support. If it supports it, we call the transformNode() method on the global variable containing our XML data, passing it the global variable containing our XSLT data. The result of this transformation is added to the innerHTML of our result element, which then contains the newly formatted search results.
Now that we are able to format the results for Internet Explorer, let’s get this functioning for Mozilla-compatible browsers.
12.4.2Working with Mozilla
With Mozilla, we need to use the XSLTProcessor object, which lets us combine the XML and XSLT documents. Note that even though Opera and Safari both support the XMLHttpRequest object, they still do not support the XSLTProcessor object, and they cannot run this project without support from the server (we will address this issue in section 12.5.2). In the listing 12.8, we transform the XML document using the XSLT and display the formatted result set.
Listing 12.8 Invoking XSLT for Mozilla
else{
var xsltProcessor = new XSLTProcessor(); xsltProcessor.importStylesheet(xslDoc);
var fragment=xsltProcessor.transformToFragment(xmlDoc,document); objOutput.innerHTML = "";
objOutput.appendChild(fragment);
}
The first step is to initialize the XSLTProcessor object, which enables us to join the XML and XSLT files together. The importStylesheet method of the XSLTProcessor object allows us to import the XSLT file so that we can join it to the XML file in the upcoming steps. When the XSLT file is loaded into the processor, we are left with transforming the XML document. The XSLTProcessor is used again with a new method called transformToFragment(). The transformToFragment() method takes the XML file and combines it with the XSLT, and then returns the formatted result tree.
We replace the content that exists in our result element by setting the innerHTML with a blank string. This removes our loading animation from the