| By Kurt Cagle | Article Rating: |
|
| March 27, 2007 11:15 AM EDT | Reads: |
4,580 |
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 4,580
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).
- The Top 150 Players in Cloud Computing
- Commercial vs Federal Cloud Computing
- Why IBM’s Server Chief Got Busted
- Industry Experts Discuss the State of Cloud Computing
- Cloud Expo New York Call for Papers Now Open
- Cloud Computing on Gartner's Top 10 List and SYS-CON Events' 2010 Calendar
- US Federal Government is Major Cloud Computing Innovator
- Google Wave
- Ulitzer.com Named Exclusive "New Media" Sponsor of Cloud Computing Conference & Expo
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- Adaptivity & Cloud Computing: Exclusive Q&A with CEO Tony Bishop
- 4th International Cloud Expo: Photo Album
- The Top 150 Players in Cloud Computing
- SYS-CON.TV: Cloud Computing Expo Power Panel
- Commercial vs Federal Cloud Computing
- Why IBM’s Server Chief Got Busted
- 1st Annual GovIT Expo: Letter from the Technical Chair
- Industry Experts Discuss the State of Cloud Computing
- Deputy CIO of the CIA to Keynote 1st Annual GovIT Expo
- SOA World Power Panel on SYS-CON.TV
- CIA was Headed to an Enterprise Cloud All Along: Jill Tummler Singer
- Cloud Expo New York Call for Papers Now Open
- 1st Annual Government IT Conference & Expo: Themes & Topics
- Stock in Focus: Dragon Capital
- The i-Technology Right Stuff
- Who Are The All-Time Heroes of i-Technology?
- Get the Message
- Where Are RIA Technologies Headed in 2008?
- i-Technology Viewpoint: Is Web 2.0 the Global SOA?
- i-Technology Viewpoint: Thinking Outside the VC Box
- ESB Myth Busters: 10 Enterprise Service Bus Myths Debunked
- i-Technology Viewpoint: When to Leave Your First IT Job
- SOA Web Services Edge Conference Coverage on SYS-CON.TV
- Five Reasons Why Web 2.0 Matters
- SYS-CON.TV's "SOA Web Services" and "Enterprise Open Source" Programs To Air in December
- SOA World Conference & Expo SYS-CON.TV Power Panel Live From Times Square









Cloud computing is a game changer. The cloud ...





















