| By Kurt Cagle | Article Rating: |
|
| March 27, 2007 11:15 AM EDT | Reads: |
5,566 |
This content is reprinted from Real-World AJAX: Secrets of the Masters published by SYS-CON Books. To order the entire book now along with companion DVDs for the special pre-order price, click here for more information. Aimed at everyone from enterprise developers to self-taught scripters, Real-World AJAX: Secrets of the Masters is the perfect book for anyone who wants to start developing AJAX applications.
Instantiating the Bindings
In Mozilla, the XBL binding language (an XML language) is used to associate bindings with their respective elements, with these tied in via CSS. Unfortunately, there are no clean hooks for adding a binding in this way for Opera (and a different mechanism for handling it in Internet Explorer).
Because of this, the approach I took to binding here was somewhat different and involved two separate routines: applyBindings() and applyBinding(bindingName,node). The first routine will always be called (it's invoked in the windows.onload event) and its role is to retrieve each <binding> element from the Web page, retrieve from that binding both its name (from the target attribute) and the binding URL (as contained in the src attribute), and then to load the JavaScript code into an object.
Once that is done, the second function applyBinding() is invoked with one argument (the name of the binding). When invoked in this manner, the applyBinding() method will retrieve all elements with this name from the calling document (the original XHTML file), and will then directly apply the binding to each such element.
It is also possible to call applyBinding() with a second argument - a specific element object - in order to apply the binding to that object directly. In general this usage will be performed via some later script. For instance, you could modify the code in the XInclude binding to automatically check and bind newly introduced elements that have just been rendered.
In addition to these two functions, this script also defines the getXPath() function, which takes as arguments an XPath expression and the node on which to apply it, and returns a nodeList object containing zero or more nodes of those that satisfy the given XPath call. Note that this is a fairly bare-bones version and is designed to work with both Mozilla, which does have its own more sophisticated XPathEvaluator object, and Opera, which doesn't. The XPath in Opera is handled by using an inline XSLT transformation, which Opera 9.0 supports.
The Knee Bone Is Connected to ...
The JavaScript binding files generally follow the same general skeleton:
var obj = {
constructor:function(){
// constructor code
},
methods:{
methodA:function(){
// code for method A
}
},
methodB:function(arg1,arg2){
// code for method B
}
},
events:{
eventA:function(evt){
// event handler for eventA
},
eventB:function(evt){
// event handler for eventB
}
}
};
obj;
These are actually attached to the target element directly, such that if you have a reference to element X, you can call methodA on X directly as X.methodA(). Note that this approach does tend to be fairly expensive when compared to attaching the code directly to a constructor prototype. But unfortunately all elements that are not explicitly defined in both the Mozilla and Opera DOMs are treated as a single anonymous element class and consequently such generalized bindings would cause a great deal of havoc.
Note that the skeleton currently doesn't recognize destructors though these are easy enough to add properties, the way XBL does. The latter lack of property support comes about because IE7 does not recognize setters or getters in JavaScript, along with the fact that you can always create setXXX and getXXX methods directly.
Finally, the event names should be given without the "on" prefix: "load" instead of "onload", "mousedown" vs. "onmousedown", and so forth. The code given here also doesn't differentiate bubbling phases, primarily again because of restrictions with Internet Explorer's event flow model.
The code for performing the specific XInclude binding is given as follows:
XIncludeBinding.js
var obj = {
constructor:function(){
if (this.getAttribute("id") == null){
this.setAttribute("id", this.nodeName + "_" + Math.floor
(100000*Math.random()) + "_" + Math.floor(100000*Math.random()));
}
this.child_id = this.nodeName + "_" + Math.floor(100000*Math.random());
this.innerHTML = "<div id='"+this.child_id+"'></div>";
this.old_path = "";
this.current_path = "";
this.current_doc_id = "";
this.activeDocument = null;
this.refresh();
this.toc_displayed = true;
},
methods:{
refresh:function(){
var control = this;
if (control.getAttribute("timeoutInterval ")!== ""){
var timeoutInterval = control.getAttribute("timeoutInterval");
}
else {
var timeoutInterval = 10000;
}
// var container = document.getElementById(control.child_id);
var container = control;
var src = control.getAttribute("src");
if (/#/.test(src)){
var srcArr = src.match(/(.*)#(.*)/);
control.current_path = RegExp.$1;
control.current_doc_id = RegExp.$2;
}
else {
control.current_path = src;
control.current_doc_id = "";
}
if (control.current_path != control.old_path){
control.old_path = control.current_path;
container.innerHTML = "Loading ...";
control.http = new XMLHttpRequest();
var timeoutNeeded = true;
control.http.open("GET",control.current_path,true);
if (control.getAttribute("disable-cache") == "yes"){
control.http.setRequestHeader("Expires",0);
control.http.setRequestHeader("Cache-Control","no-cache");
}
control.http.onreadystatechange= function(){
if (control.http.readyState == 4){
var doc = control.http.responseXML;
control.activeDocument = doc;
control.paint();
}
}
control.http.send(null);
}
else {
control.paint();
}
},
paint:function(content){
// alert("Paint Called");
var control = this;
var toc_show = false;
// var container = document.getElementById(control.child_id);
var container = control;
// xml_alert(control.activeDocument);
if (control.current_doc_id != ""){
var node = control.activeDocument.getElementById(control.current_doc_id);
var docStr = ((new XMLSerializer()).serializeToString(node
));
}
else {
if ((control.getAttribute("buttons") != "no") && (control.getAttribute("buttons") != "")){
var node = getXPath(".//*[@id]",control.activeDocument).item(0);
//var docStr = ((new XMLSerializer()).serializeToString(node));
control.current_doc_id = node.getAttribute("id");
toc_show = true;
}
else {
var docStr = ((new XMLSerializer()).serializeToString(
control.activeDocument.documentElement));
}
}
if (content == null){
var body =docStr;
}
else {
var body = content;
}
if ((control.getAttribute("buttons") != "no") && (control.getAttribute("buttons") != "")){
var buttonState = control.getAttribute("buttons");
var current_button_index = 0;
function getButtonBar(location){
startBuf = "<div xmlns='http://www.w3.org/1999/xhtml'
class='xincludeButtonBar xincludeButtonBar_"+location+"'>";
var buf = "";
var className = "";
var idNodes = getXPath("//*[@id]",control.activeDocument);
for (var index = 0; index != idNodes.length; index++){
var idNode = idNodes.item(index);
if (control.current_doc_id == idNode.getAttribute("id")){
className = 'xincludeButtonSelected';
current_button_index = index;
}
else {
className = 'xincludeButton';
}
var buttonTitle = "";
for (var child_index = 0; child_index != idNode.
childNodes.length;child_index++){
var childNode = idNode.childNodes.item(child_index);
if (childNode.nodeType == childNode.ELEMENT_NODE){
buttonTitle = childNode.textContent.replace(/</g,"<");
buttonTitle = buttonTitle.replace(/\"/g,""");
break;
}
}
var button = '<button title="'+buttonTitle+'"
onclick="var control = document.getElementById(\''+control.getAttribute("id")+'\
');control.showPage(\''+control.current_path+"#"+idNode.getAttribute("id")+'\',\
''+idNode.getAttribute("id")+'\')" class="'+className+'">'+(1 + index)+'</button>';
buf += button;
}
var toc_button = '<button onclick="document.
getElementById(\''+control.getAttribute("id")+'\').showTOC()"
class="xinclude_toc">TOC</button>';
var previous_id = idNodes.item((idNodes.length + current_
button_index - 1) % idNodes.length).getAttribute("id");
var next_id = idNodes.item((current_button_index + 1)
% idNodes.length).getAttribute("id");
var previous_button = '<button onclick="document.
getElementById(\''+control.getAttribute("id")+'\').showPage(\''+control.
current_path+"#"+previous_id+'\',\''+previous_id+'\')" class="xinclude_
previous"><<</button>';
var next_button = '<button onclick="document.
getElementById(\''+control.getAttribute("id")+'\').showPage(\''+control.current_
path+"#"+ next_id+'\',\''+next_id+'\')" class="xinclude_next">>></button>';
if (control.getAttribute("page-buttons") == "no"){
buf = "";
}
buf = startBuf + toc_button + previous_button +
next_button + buf;
buf += "</div>";
return buf;
}
if ((buttonState == "yes") || (buttonState == "top") ||
(buttonState == "both")){
container.innerHTML = getButtonBar("top");
}
container.innerHTML += "<div class='xinclude_body'>"+body+"</div>";
if ((buttonState == "yes") || (buttonState == "bottom") ||
(buttonState == "both")){
container.innerHTML += getButtonBar("bottom");
}
if ((control.getAttribute("title") != null)){
var titleKey = control.getAttribute("title");
var titleElement = control.activeDocument.getElementsByTagName(titleKey).item(0);
var buf = '<h1 class="xincludeTitle">'+titleElement.innerHTML+'</h1>';
container.innerHTML = buf + container.innerHTML;
}
}
if (toc_show){
control.toc_displayed = false;
control.showTOC();
}
},
showPage:function(url,id){
this.setAttribute('src',url);
this.current_doc_id = id;
},
showTOC:function(){
var control = this;
var container = control;
if (!control.toc_displayed){
control.toc_displayed = true;
var buf = "<div class='xinclude_toc'><ol>";
var idNodes = getXPath("//*[@id]",control.activeDocument.documentElement);
for (var index = 0; index != idNodes.length; index++){
var idNode = idNodes.item(index);
if (control.current_doc_id == idNode.getAttribute("id")){
className = 'xincludeButtonSelected';
current_button_index = index;
}
else {
className = 'xincludeButton';
}
var title = "";
for (var child_index = 0; child_index != idNode.childNodes.
length;child_index++){
var childNode = idNode.childNodes.item(child_index);
if (childNode.nodeType == 1){
title = childNode.textContent.replace(/\</g,"<");
title = title.replace(/\"/g,""");
title = title.replace(/&/g,"&");
// title = title.replace(/\'/g,"'");
// title = title.replace(/\&/g,"&");
break;
}
}
var listItem = '<li><span onclick="var control = document.
getElementById(\''+control.getAttribute("id")+'\');control.showPage(\''+control.
current_path+"#"+idNode.getAttribute("id")+'\',\''+idNode.getAttribute("id")+'\')"
class="xinclude_TOC_item">'+title+'</span></li>';
buf += listItem;
}
buf += "</ol></div>";
control.paint(buf);
// control.toc_displayed = false;
}
else {
control.toc_displayed = false;
control.paint();
}
}
},
events:{
DOMAttrModified:function(evt){
var target = evt.target;
if (target.old_src != target.getAttribute("src")){
target.refresh();
}
}
}
};
obj;
This content is reprinted from Real-World AJAX: Secrets of the Masters published by SYS-CON Books. To order the entire book now along with companion DVDs, click here to order.
Published March 27, 2007 Reads 5,566
Copyright © 2007 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Kurt Cagle
Kurt Cagle is a developer and author, with nearly 20 books to his name and several dozen articles. He writes about Web technologies, open source, Java, and .NET programming issues. He has also worked with Microsoft and others to develop white papers on these technologies. He is the owner of Cagle Communications and a co-author of Real-World AJAX: Secrets of the Masters (SYS-CON books, 2006).
- Big Data in Telecom: The Need for Analytics
- Patterns for Building High Performance Applications
- Microsoft Tries Hadoop on Azure
- Amazon to Fix Some Kindle Fire Problems
- What Motivates Open Standards in the Cloud?
- What to Expect in 2012: Cloud Computing and Open Source Software
- Will PaaS Finally Bring Open Source Love to the Enterprise?
- Ten Hot Trends in Cloud Data for 2012
- Oracle Disaster Recovery Site Hosted by Amazon Cloud
- Cross-Platform Mobile Website Development – a Tool Comparison
- Three Buzzwords That Every CIO Hears but One They Should Listen To
- Write Once Run Anywhere or Cross Platform Mobile Development Tools
- The Future of Cloud Computing: Industry Predictions for 2012
- Make Customer On-Boarding Easy as Paint-by-Numbers for Cloud Services
- Gartner Hype Cycle for Emerging Technologies 2011
- Book Excerpt: Introducing HTML5
- Adobe Sends Flex to the Apache Foundation
- Big Data in Telecom: The Need for Analytics
- Book Excerpt: Java Application Profiling Tips and Tricks
- i-Technology in 2012: Five Industry Predictions
- Patterns for Building High Performance Applications
- Microsoft Tries Hadoop on Azure
- The Next Web Architecture
- Cloud Computing: A Comparison of Computing Models
- The i-Technology Right Stuff
- The Top 150 Players in Cloud Computing
- Who Are The All-Time Heroes of i-Technology?
- Where Are RIA Technologies Headed in 2008?
- Get the Message
- ESB Myth Busters: 10 Enterprise Service Bus Myths Debunked
- i-Technology Viewpoint: Is Web 2.0 the Global SOA?
- i-Technology Viewpoint: Thinking Outside the VC Box
- i-Technology Viewpoint: When to Leave Your First IT Job
- SOA Web Services Edge Conference Coverage on SYS-CON.TV
- SYS-CON.TV's "SOA Web Services" and "Enterprise Open Source" Programs To Air in December
- Five Reasons Why Web 2.0 Matters


















