This is the JavasSript code that runs in the XBL binding.
<?xml version="1.0" encoding="ISO-8859-1"?> <bindings xmlns="http://www.mozilla.org/xbl" xmlns:html="http://www.w3.org/1999/xhtml"> <!-- moz-behaviors.xml - version 1.1.2 (2010-04-02) Copyright 2004-2010, Dean Edwards http://www.opensource.org/licenses/mit-license.php --> <!-- ======================================================================= TO DO ======================================================================= hideFocus fix CSSStyleDeclaration pixel*.__defineGetter__ behaviors FAQ test Event.returnValue http://developer-test.mozilla.org/docs/Working_around_the_Firefox_1.0.3_DHTML_regression --> <!-- add a behavior through css --> <binding id="behavior" extends="#-moz-behaviors"> <implementation><constructor> <![CDATA[ addBehavior(0); ]]></constructor></implementation> </binding> <!-- manually attach behaviors to child elements of <table>s --> <binding id="table" extends="#-moz-behaviors"> <implementation><constructor><![CDATA[ var i, j; if (tHead) tHead.addBehavior(0); for (i = 0; i < tBodies.length; i++) tBodies[i].addBehavior(0); for (i = 0; i < rows.length; i++) { rows[i].addBehavior(0); for (j = 0; j < rows[i].cells.length; j++) { rows[i].cells[j].addBehavior(0); } } if (tFoot) tFoot.addBehavior(0); ]]></constructor></implementation> </binding> <binding id="-moz-behaviors" extends="#-moz-ie"> <implementation><constructor> <![CDATA[if(!this.addBehavior){ // ------------------------------------------------------------------ // htc extensions for mozilla // ------------------------------------------------------------------ /* here we define the addBehavior/removeBehavior methods for an element these methods are used to add and remove all dhtml behaviors */ var _cookie = -1; // no support for removeBehavior yet // implement the addBehavior method for all elements Element.prototype.addBehavior = function(_url) { try { // calling this method with the first argument as zero // initialises the object's behaviors if (_url === 0) { // grab the htc's url from the css setting var $binding = getComputedStyle(this, null).getPropertyValue("-moz-binding"); $binding = $binding.replace(/^url\(([^)]*)\)$/, "$1").split("#"); if ($binding) { var $path = $binding[0].replace(/[^\/]+$/, ""); // support multiple behaviors var $htcs = $binding[1].split("|"); var i = $htcs.length; while (i--) this.addBehavior($path + $htcs[i]); } } if (!_url) return; // check the cache if (!document.behaviorUrns[_url]) { // constants var $SEPARATOR = ";"; function _getTagName($node) { var $tagName = $node.tagName.toLowerCase(); // this fixes a bug(?) in Mozilla 1.6b that includes the // namespace prefix in the tagName return $tagName.slice($tagName.indexOf(":") + 1); }; function _getAttribute($node, $attribute) { return $node.getAttribute($attribute) || $node.getAttribute($attribute.toUpperCase()); }; // this function converts elements in a behavior to a program // declaration, for example: // <public:attach for="window" event="onload" handler="init"/> // becomes: // window.addEventListener("load", init); function _asDeclaration($behaviorNode) { switch (_getTagName($behaviorNode)) { case "event": var id = _getAttribute($behaviorNode, "id"); return (id) ? "var " + id + "={fire:function(event){element.fireEvent('" + _getAttribute($behaviorNode, "name") + "',event)}}" : ""; case "property": var $name = _getAttribute($behaviorNode, "name"); var $get = _getAttribute($behaviorNode, "get") || ""; if ($get) $get = "__defineGetter__('" + $name + "'," + $get + ")"; var $put = _getAttribute($behaviorNode, "put") || ""; if ($put) $put = ";__defineSetter__('" + $name + "'," + $put + ")"; return $get + $put; case "method": return "element." + _getAttribute($behaviorNode, "name") + "=" + _getAttribute($behaviorNode, "name"); case "attach": var $handler = _getAttribute($behaviorNode, "handler") || ""; $handler += ($handler) ? "()" : _getAttribute($behaviorNode, "onevent"); $handler = "function(event){window.event=event;return " + $handler + "}"; var $event = _getAttribute($behaviorNode, "event"); switch ($event) { case "oncontentready": return "window.setTimeout(" + $handler + ",1)"; case "ondocumentready": return "document.behaviorUrns.__private.push(" + $handler + ")"; } return (_getAttribute($behaviorNode, "for")||"element") + ".addEventListener('" + $event.slice(2) + "'," + $handler + ",false)"; case "defaults": // not implemented default: return ""; } }; function _asDefault($node) { return (_getAttribute($node, "put")) ? ";var __tmp=getAttribute('" + _getAttribute($node, "name") + "')||" + (_getAttribute($node, "value") || "null") + ";if(__tmp!=null)element['" + _getAttribute($node, "name") + "']=__tmp" : ""; }; // extract the body of a function function _getFunctionBody($function) { with (String($function)) return slice(indexOf("{") + 1, lastIndexOf("}")); }; // behaviors are defined as xml documents, so we can use // the http request object to load them and the dom parser // object to parse them into a dom tree var _httpRequest = new XMLHttpRequest; function _loadFile($url) { try { // load the behavior if ($url.indexOf('"') === 0) $url = $url.slice(1, -1); _httpRequest.open("GET", $url, false); _httpRequest.send(null); return _httpRequest.responseText; } catch ($ignore) { // ignore (but don't crash) }}; // analyse the dom tree, build the interface and create the script var _declarations = []; var _defaults = ""; var _script = ""; function _load() { // build a dom representation of the loaded xml document var $dom = (new DOMParser).parseFromString(_loadFile(_url), "text/xml"); var $childNodes = $dom.documentElement.childNodes, $node; for (var i = 0; ( $node = $childNodes[i]); i++) { if ($node.nodeType == Node.ELEMENT_NODE) { if (_getTagName($node) == "script") { var $src = _getAttribute($node, "src"); if ($src) { _script += _loadFile($src); } else { // build the script from the text nodes of the script element for (var j = 0; j < $node.childNodes.length; j++) _script += $node.childNodes[j].nodeValue; } } else { // convert the dom node representation of a // <public:declaration/> to a javascript statement // and store it in our declarations collection _declarations.push(_asDeclaration($node)); if (_getTagName($node) == "property") { _defaults += _asDefault($node); } } } } _defaults += ";delete __tmp"; }; _load(); // we've finished collecting interface declarations. // they are now held as an array of strings. // build a function from the script and extract the function body // this has the effect of formatting the script (removing comments etc) _script = _getFunctionBody(new Function(_script)); // support: new ActiveXObject var $ACTIVEX = /\bnew\s+ActiveXObject\s*\(\s*(["'])\w\.XMLHTTP\1\s*\)/gi; _script = _script.replace($ACTIVEX, "new XMLHttpRequest()"); // begin: annoying parse of script to "shuffle" declarations // and inline code. // microsoft dhtml behaviors add the interface first, then // apply inline script. // to achieve this, we have to strip out all of the inline // code, leaving only function declarations. the inline code // then gets appended to the script block for later // execution. // in between the function declarations and inline script, we // sandwich the property getters and setters. // this is a real nuisance actually... // on the upside regular expressions are really quick... // i'm using "#" as a placeholder, so i'll have to escape these out _script = _script.replace(/#/g, "\\x23"); // parse out strings, regexps and program // blocks - anything between curly braces {..} var $ = [_declarations.join($SEPARATOR)]; var $BLOCKS_REGEXPS_STRINGS = /(\"[^\"\n]+\")|(\/[^\/\n]+\/)|(\{[^\{\}]*\})/g; var _ENCODED = /#(\d+)\b/g; // store a string and return a unique id function _encode($match) {return "#" + $.push($match)}; function _decode($match, $index) {return $[$index - 1]}; while ($BLOCKS_REGEXPS_STRINGS.test(_script)) { _script = _script.replace($BLOCKS_REGEXPS_STRINGS, _encode); } // we are now left with function declarations and inline statements // remove function declarations and save them var $FUNCTIONS = /\n\s*function[^\n]*\n/g; var _functions = _script.match($FUNCTIONS) || []; _script = _script.replace($FUNCTIONS, ""); // re-assemble the encoded script, in the following // order: function declarations, interface definition // (getters and setters), inline script _script = _functions.concat("#1", _script).join($SEPARATOR); // decode the script var i = $.length; do _script = _script.replace("#" + i, $[--i]); while (i); // end: annoying parse of script // build the final script _script += _defaults; // create an anonymous function in the global namespace. // this function will add the interface defined by the dhtml behavior. // after we've built this function we'll store it so that we don't // have to go through this process again. document.behaviorUrns[_url] = new Function("element", "with(this){" + _script + "}"); } // because we loaded synchronously (or got it from the cache) // we can apply the behavior immediately... document.behaviorUrns[_url].call(this, this); // this might mean somthing later return _cookie; } catch ($error) { return 0; }}; // implement the removeBehavior method for all elements Element.prototype.removeBehavior = function($cookie) { // mmm, not in a hurry to write this }; // cache for previously loaded behaviors // -also store some "default" behaviors document.behaviorUrns = { __private : [] }; // support multiple behaviors and ondocumentready window.addEventListener("load", function() { try { var $handlers = document.behaviorUrns.__private; var i = $handlers.length; while (i) $handlers[--i](); delete document.behaviorUrns.__private; } catch ($ignore) { }}, false); }]]></constructor></implementation> </binding> <binding id="-moz-ie"> <implementation><constructor> <![CDATA[if(!this.attachEvent){ // ------------------------------------------------------------------ // explorer emulation for mozilla // ------------------------------------------------------------------ // thanks to Erik Arvidsson (http://webfx.eae.net/dhtml/ieemu/) /* we're going to mess about with some of mozilla's interfaces to make them more explorer-like */ /* note: in my comments where i say support/mimic a property support = exactly the same as explorer mimic = close enough */ // CSSStyleDeclaration // ------------------- // support microsoft's styleFloat CSSStyleDeclaration.prototype.__defineGetter__("styleFloat", function() { return this.cssFloat; }); CSSStyleDeclaration.prototype.__defineSetter__("styleFloat", function($value) { this.cssFloat = $value; }); // mimic microsoft's pixel representations of left/top/width/height // the getters only work for values that are already pixels CSSStyleDeclaration.prototype.__defineGetter__("pixelLeft", function() { return parseInt(this.left) || 0; }); CSSStyleDeclaration.prototype.__defineSetter__("pixelLeft", function($value) { this.left = $value + "px"; }); CSSStyleDeclaration.prototype.__defineGetter__("pixelHeight", function() { return parseInt(this.height) || 0; }); CSSStyleDeclaration.prototype.__defineSetter__("pixelHeight", function($value) { this.height = $value + "px"; }); CSSStyleDeclaration.prototype.__defineGetter__("pixelTop", function() { return parseInt(this.top) || 0; }); CSSStyleDeclaration.prototype.__defineSetter__("pixelTop", function($value) { this.top = $value + "px"; }); CSSStyleDeclaration.prototype.__defineGetter__("pixelWidth", function() { return parseInt(this.width) || 0; }); CSSStyleDeclaration.prototype.__defineSetter__("pixelWidth", function($value) { this.width = $value + "px"; }); // for older versions of gecko we need to use getPropertyValue() to // access css properties returned by getComputedStyle(). // we don't want this so we fix it. try { var $computedStyle = getComputedStyle(this, null); // the next line will throw an error for some versions of mozilla var $test = $computedStyle.display; } catch ($ignore) { // the previous line will throw an error for some versions of mozilla } finally { if (!$test) { // the above code didn't work so we need to fix CSSStyleDeclaration var $UPPER = /[A-Z]/g; function _dashLower($match){return "-" + $match.toLowerCase()}; function _cssName($propertyName) {return $propertyName.replace($UPPER, _dashLower)}; for (var $propertyName in this.style) { if (typeof this.style[$propertyName] == "string") { CSSStyleDeclaration.prototype.__defineGetter__($propertyName, function() { return this.getPropertyValue(_cssName($propertyName)); }); } } }} // HTMLDocument // ------------ // support microsoft's "all" property HTMLDocument.prototype.__defineGetter__("all", function() { return this.getElementsByTagName("*"); }); // mimic the "createEventObject" method for the document object HTMLDocument.prototype.createEventObject = function() { return document.createEvent("Events"); }; // HTMLElement // ----------- // mimic microsoft's "all" property HTMLElement.prototype.__defineGetter__("all", function() { return this.getElementsByTagName("*"); }); // support "parentElement" HTMLElement.prototype.__defineGetter__("parentElement", function() { return (this.parentNode == this.ownerDocument) ? null : this.parentNode; }); // support "uniqueID" HTMLElement.prototype.__defineGetter__("uniqueID", function() { // a global counter is stored privately as a property of this getter function. // initialise the counter if (!arguments.callee.count) arguments.callee.count = 0; // create the id and increment the counter var $uniqueID = "moz_id" + arguments.callee.count++; // creating a unique id, creates a global reference window[$uniqueID] = this; // we don't want to increment next time, so redefine the getter this.__defineGetter__("uniqueID", function(){return $uniqueID}); return $uniqueID; }); // mimic microsoft's "currentStyle" HTMLElement.prototype.__defineGetter__("currentStyle", function() { return getComputedStyle(this, null); }); // mimic microsoft's "runtimeStyle" HTMLElement.prototype.__defineGetter__("runtimeStyle", function() { //# this doesn't work yet (https://bugzilla.mozilla.org/show_bug.cgi?id=45424) //# return this.ownerDocument.defaultView.getOverrideStyle(this, null); return this.style; }); // support "innerText" HTMLElement.prototype.__defineGetter__("innerText", function() { return this.textContent; }); HTMLElement.prototype.__defineSetter__("innerText", function($value) { this.textContent = $value; }); // mimic the "attachEvent" method HTMLElement.prototype.attachEvent = function($name, $handler) { this.addEventListener($name.slice(2), $handler, false); }; // mimic the "removeEvent" method HTMLElement.prototype.removeEvent = function($name, $handler) { this.removeEventListener($name.slice(2), $handler, false); }; // mimic the "createEventObject" method HTMLElement.prototype.createEventObject = function() { return this.ownerDocument.createEventObject(); }; // mimic the "fireEvent" method HTMLElement.prototype.fireEvent = function($name, $event) { if (!$event) $event = this.ownerDocument.createEventObject(); $event.initEvent($name.slice(2), false, false); this.dispatchEvent($event); // not sure that this should be here?? if (typeof this[$name] == "function") this[$name](); else if (this.getAttribute($name)) eval(this.getAttribute($name)); }; // support the "contains" method HTMLElement.prototype.contains = function($element) { return Boolean($element == this || ($element && this.contains($element.parentElement))); }; // Event // ----- // support microsoft's proprietary event properties Event.prototype.__defineGetter__("srcElement", function() { return (this.target.nodeType == Node.ELEMENT_NODE) ? this.target : this.target.parentNode; }); Event.prototype.__defineGetter__("fromElement",function() { return (this.type == "mouseover") ? this.relatedTarget : (this.type == "mouseout") ? this.srcElement : null; }); Event.prototype.__defineGetter__("toElement", function() { return (this.type == "mouseout") ? this.relatedTarget : (this.type == "mouseover") ? this.srcElement : null; }); // convert w3c button id's to microsoft's Event.prototype.__defineGetter__("button", function() { return (this.which == 1) ? 1 : (this.which == 2) ? 4 : 2; }); // mimic "returnValue" (default is "true") Event.prototype.__defineGetter__("returnValue", function() { return true; }); Event.prototype.__defineSetter__("returnValue", function($value) { if (this.cancelable && !$value) { // this can't be undone! this.preventDefault(); this.__defineGetter__("returnValue", function() { return false; }); } }); // mozilla already supports the read-only "cancelBubble" // so we only need to define the setter Event.prototype.__defineSetter__("cancelBubble", function($value) { // this can't be undone! if ($value) this.stopPropagation(); }); Event.prototype.__defineGetter__("offsetX", function() { return this.layerX; }); Event.prototype.__defineGetter__("offsetY", function() { return this.layerY; }); // and that's it! // thanks mozilla for being such a developer's playground :D }]]></constructor></implementation> </binding> <binding id="block-netscape6"> <content> <html:script type="text/javascript"><![CDATA[ // netscape6 does not retain the -moz-binding css property value // so we disable moz-behaviors if (/netscape6/i.test(navigator.userAgent)) { document.styleSheets[0].insertRule("*{-moz-binding:none!important}", 0); } ]]></html:script> <children/> </content> </binding> </bindings>