]> git.pond.sub.org Git - eow/blobdiff - static/dojo-release-1.1.1/dojo/_base/html.js
add Dojo 1.1.1
[eow] / static / dojo-release-1.1.1 / dojo / _base / html.js
diff --git a/static/dojo-release-1.1.1/dojo/_base/html.js b/static/dojo-release-1.1.1/dojo/_base/html.js
new file mode 100644 (file)
index 0000000..d673eb1
--- /dev/null
@@ -0,0 +1,1227 @@
+if(!dojo._hasResource["dojo._base.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo._base.html"] = true;
+dojo.require("dojo._base.lang");
+dojo.provide("dojo._base.html");
+
+// FIXME: need to add unit tests for all the semi-public methods
+
+try{
+       document.execCommand("BackgroundImageCache", false, true);
+}catch(e){
+       // sane browsers don't have cache "issues"
+}
+
+// =============================
+// DOM Functions
+// =============================
+
+/*=====
+dojo.byId = function(id, doc){
+       //      summary:
+       //              Returns DOM node with matching `id` attribute or `null` 
+       //              if not found, similar to "$" function in another library.
+       //              If `id` is a DomNode, this function is a no-op.
+       //
+       //      id: String|DOMNode
+       //              A string to match an HTML id attribute or a reference to a DOM Node
+       //
+       //      doc: Document?
+       //              Document to work in. Defaults to the current value of
+       //              dojo.doc.  Can be used to retrieve
+       //              node references from other documents.
+=====*/
+if(dojo.isIE || dojo.isOpera){
+       dojo.byId = function(id, doc){
+               if(dojo.isString(id)){
+                       var _d = doc || dojo.doc;
+                       var te = _d.getElementById(id);
+                       // attributes.id.value is better than just id in case the 
+                       // user has a name=id inside a form
+                       if(te && te.attributes.id.value == id){
+                               return te;
+                       }else{
+                               var eles = _d.all[id];
+                               if(!eles || !eles.length){ return eles; }
+                               // if more than 1, choose first with the correct id
+                               var i=0;
+                               while((te=eles[i++])){
+                                       if(te.attributes.id.value == id){ return te; }
+                               }
+                       }
+               }else{
+                       return id; // DomNode
+               }
+       }
+}else{
+       dojo.byId = function(id, doc){
+               return dojo.isString(id) ? (doc || dojo.doc).getElementById(id) : id; // DomNode
+       }
+}
+/*=====
+}
+=====*/
+
+(function(){
+       /*
+       dojo.createElement = function(obj, parent, position){
+               // TODO: need to finish this!
+       }
+       */
+
+       var d = dojo;
+
+       var _destroyContainer = null;
+       dojo.addOnUnload(function(){
+               _destroyContainer=null; //prevent IE leak
+       });
+       dojo._destroyElement = function(/*String||DomNode*/node){
+               // summary:
+               //              removes node from its parent, clobbers it and all of its
+               //              children.
+               //      node:
+               //              the element to be destroyed, either as an ID or a reference
+
+               node = d.byId(node);
+               try{
+                       if(!_destroyContainer){
+                               _destroyContainer = document.createElement("div");
+                       }
+                       _destroyContainer.appendChild(node.parentNode ? node.parentNode.removeChild(node) : node);
+                       // NOTE: see http://trac.dojotoolkit.org/ticket/2931. This may be a bug and not a feature
+                       _destroyContainer.innerHTML = ""; 
+               }catch(e){
+                       /* squelch */
+               }
+       };
+
+       dojo.isDescendant = function(/*DomNode|String*/node, /*DomNode|String*/ancestor){
+               //      summary:
+               //              Returns true if node is a descendant of ancestor
+               //      node: id or node reference to test
+               //      ancestor: id or node reference of potential parent to test against
+               try{
+                       node = d.byId(node);
+                       ancestor = d.byId(ancestor);
+                       while(node){
+                               if(node === ancestor){
+                                       return true; // Boolean
+                               }
+                               node = node.parentNode;
+                       }
+               }catch(e){ /* squelch, return false */ }
+               return false; // Boolean
+       };
+
+       dojo.setSelectable = function(/*DomNode|String*/node, /*Boolean*/selectable){
+               //      summary: enable or disable selection on a node
+               //      node:
+               //              id or reference to node
+               //      selectable:
+               node = d.byId(node);
+               if(d.isMozilla){
+                       node.style.MozUserSelect = selectable ? "" : "none";
+               }else if(d.isKhtml){
+                       node.style.KhtmlUserSelect = selectable ? "auto" : "none";
+               }else if(d.isIE){
+                       node.unselectable = selectable ? "" : "on";
+                       d.query("*", node).forEach(function(descendant){
+                               descendant.unselectable = selectable ? "" : "on";
+                       });
+               }
+               //FIXME: else?  Opera?
+       };
+
+       var _insertBefore = function(/*Node*/node, /*Node*/ref){
+               ref.parentNode.insertBefore(node, ref);
+               return true;    //      boolean
+       }
+
+       var _insertAfter = function(/*Node*/node, /*Node*/ref){
+               //      summary:
+               //              Try to insert node after ref
+               var pn = ref.parentNode;
+               if(ref == pn.lastChild){
+                       pn.appendChild(node);
+               }else{
+                       return _insertBefore(node, ref.nextSibling);    //      boolean
+               }
+               return true;    //      boolean
+       }
+
+       dojo.place = function(/*String|DomNode*/node, /*String|DomNode*/refNode, /*String|Number*/position){
+               //      summary:
+               //              Attempt to insert node into the DOM, choosing from various positioning options.
+               //              Returns true if successful, false otherwise.
+               //      node: 
+               //              id or node reference to place relative to refNode
+               //      refNode: 
+               //              id or node reference to use as basis for placement
+               //      position:
+               //              string noting the position of node relative to refNode or a
+               //              number indicating the location in the childNodes collection of
+               //              refNode. Accepted string values are:
+               //
+               //              * before
+               //              * after
+               //              * first
+               //              * last
+               //
+               //              "first" and "last" indicate positions as children of refNode.
+
+               // FIXME: need to write tests for this!!!!
+               if(!node || !refNode || position === undefined){ 
+                       return false;   //      boolean 
+               }
+               node = d.byId(node);
+               refNode = d.byId(refNode);
+               if(typeof position == "number"){
+                       var cn = refNode.childNodes;
+                       if((position == 0 && cn.length == 0) ||
+                               cn.length == position){
+                               refNode.appendChild(node); return true;
+                       }
+                       if(position == 0){
+                               return _insertBefore(node, refNode.firstChild);
+                       }
+                       return _insertAfter(node, cn[position-1]);
+               }
+               switch(position.toLowerCase()){
+                       case "before":
+                               return _insertBefore(node, refNode);    //      boolean
+                       case "after":
+                               return _insertAfter(node, refNode);             //      boolean
+                       case "first":
+                               if(refNode.firstChild){
+                                       return _insertBefore(node, refNode.firstChild); //      boolean
+                               }
+                               // else fallthrough...
+                       default: // aka: last
+                               refNode.appendChild(node);
+                               return true;    //      boolean
+               }
+       }
+
+       // Box functions will assume this model.
+       // On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode.
+       // Can be set to change behavior of box setters.
+       
+       // can be either:
+       //      "border-box"
+       //      "content-box" (default)
+       dojo.boxModel = "content-box";
+       
+       // We punt per-node box mode testing completely.
+       // If anybody cares, we can provide an additional (optional) unit 
+       // that overrides existing code to include per-node box sensitivity.
+
+       // Opera documentation claims that Opera 9 uses border-box in BackCompat mode.
+       // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box.
+       // IIRC, earlier versions of Opera did in fact use border-box.
+       // Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault.
+
+       if(d.isIE /*|| dojo.isOpera*/){
+               var _dcm = document.compatMode;
+               // client code may have to adjust if compatMode varies across iframes
+               d.boxModel = _dcm == "BackCompat" || _dcm == "QuirksMode" || d.isIE<6 ? "border-box" : "content-box"; // FIXME: remove IE < 6 support?
+       }
+
+       // =============================
+       // Style Functions
+       // =============================
+       
+       // getComputedStyle drives most of the style code.
+       // Wherever possible, reuse the returned object.
+       //
+       // API functions below that need to access computed styles accept an 
+       // optional computedStyle parameter.
+       // If this parameter is omitted, the functions will call getComputedStyle themselves.
+       // This way, calling code can access computedStyle once, and then pass the reference to 
+       // multiple API functions. 
+
+/*=====
+       dojo.getComputedStyle = function(node){
+               //      summary:
+               //              Returns a "computed style" object.
+               //
+               //      description:
+               //              Gets a "computed style" object which can be used to gather
+               //              information about the current state of the rendered node. 
+               //
+               //              Note that this may behave differently on different browsers.
+               //              Values may have different formats and value encodings across
+               //              browsers.
+               //
+               //              Note also that this method is expensive.  Wherever possible,
+               //              reuse the returned object.
+               //
+               //              Use the dojo.style() method for more consistent (pixelized)
+               //              return values.
+               //
+               //      node: DOMNode
+               //              A reference to a DOM node. Does NOT support taking an
+               //              ID string for speed reasons.
+               //      example:
+               //      |       dojo.getComputedStyle(dojo.byId('foo')).borderWidth;
+               return; // CSS2Properties
+       }
+=====*/
+
+       var gcs, dv = document.defaultView;
+       if(d.isSafari){
+               gcs = function(/*DomNode*/node){
+                       var s = dv.getComputedStyle(node, null);
+                       if(!s && node.style){ 
+                               node.style.display = ""; 
+                               s = dv.getComputedStyle(node, null);
+                       }
+                       return s || {};
+               }; 
+       }else if(d.isIE){
+               gcs = function(node){
+                       return node.currentStyle;
+               };
+       }else{
+               gcs = function(node){
+                       return dv.getComputedStyle(node, null);
+               };
+       }
+       dojo.getComputedStyle = gcs;
+
+       if(!d.isIE){
+               dojo._toPixelValue = function(element, value){
+                       // style values can be floats, client code may want
+                       // to round for integer pixels.
+                       return parseFloat(value) || 0; 
+               }
+       }else{
+               dojo._toPixelValue = function(element, avalue){
+                       if(!avalue){ return 0; }
+                       // on IE7, medium is usually 4 pixels
+                       if(avalue=="medium"){ return 4; }
+                       // style values can be floats, client code may
+                       // want to round this value for integer pixels.
+                       if(avalue.slice && (avalue.slice(-2)=='px')){ return parseFloat(avalue); }
+                       with(element){
+                               var sLeft = style.left;
+                               var rsLeft = runtimeStyle.left;
+                               runtimeStyle.left = currentStyle.left;
+                               try{
+                                       // 'avalue' may be incompatible with style.left, which can cause IE to throw
+                                       // this has been observed for border widths using "thin", "medium", "thick" constants
+                                       // those particular constants could be trapped by a lookup
+                                       // but perhaps there are more
+                                       style.left = avalue;
+                                       avalue = style.pixelLeft;
+                               }catch(e){
+                                       avalue = 0;
+                               }
+                               style.left = sLeft;
+                               runtimeStyle.left = rsLeft;
+                       }
+                       return avalue;
+               }
+       }
+       var px = d._toPixelValue;
+
+       // FIXME: there opacity quirks on FF that we haven't ported over. Hrm.
+       /*=====
+       dojo._getOpacity = function(node){
+                       //      summary:
+                       //              Returns the current opacity of the passed node as a
+                       //              floating-point value between 0 and 1.
+                       //      node: DomNode
+                       //              a reference to a DOM node. Does NOT support taking an
+                       //              ID string for speed reasons.
+                       //      return: Number between 0 and 1
+       }
+       =====*/
+
+       dojo._getOpacity = d.isIE ? function(node){
+               try{
+                       return node.filters.alpha.opacity / 100; // Number
+               }catch(e){
+                       return 1; // Number
+               }
+       } : function(node){
+               return gcs(node).opacity;
+       };
+
+       /*=====
+       dojo._setOpacity = function(node, opacity){
+                       //      summary:
+                       //              set the opacity of the passed node portably. Returns the
+                       //              new opacity of the node.
+                       //      node: DOMNode
+                       //              a reference to a DOM node. Does NOT support taking an
+                       //              ID string for performance reasons.
+                       //      opacity: Number
+                       //              A Number between 0 and 1. 0 specifies transparent.
+                       //      return: Number between 0 and 1
+       }
+       =====*/
+
+       dojo._setOpacity = d.isIE ? function(/*DomNode*/node, /*Number*/opacity){
+               if(opacity == 1){
+                       // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so remove it altogether (bug #2661)
+                       var filterRE = /FILTER:[^;]*;?/i;
+                       node.style.cssText = node.style.cssText.replace(filterRE, "");
+                       if(node.nodeName.toLowerCase() == "tr"){
+                               d.query("> td", node).forEach(function(i){
+                                       i.style.cssText = i.style.cssText.replace(filterRE, "");
+                               });
+                       }
+               }else{
+                       var o = "Alpha(Opacity="+ opacity * 100 +")";
+                       node.style.filter = o;
+               }
+               if(node.nodeName.toLowerCase() == "tr"){
+                       d.query("> td", node).forEach(function(i){
+                               i.style.filter = o;
+                       });
+               }
+               return opacity;
+       } : function(node, opacity){
+               return node.style.opacity = opacity;
+       };
+
+       var _pixelNamesCache = {
+               left: true, top: true
+       };
+       var _pixelRegExp = /margin|padding|width|height|max|min|offset/;  // |border
+       var _toStyleValue = function(node, type, value){
+               type = type.toLowerCase();
+               if(d.isIE && value == "auto"){
+                       if(type == "height"){ return node.offsetHeight; }
+                       if(type == "width"){ return node.offsetWidth; }
+               }
+               if(!(type in _pixelNamesCache)){
+                       //      if(dojo.isOpera && type == "cssText"){
+                       //              FIXME: add workaround for #2855 here
+                       //      }
+                       _pixelNamesCache[type] = _pixelRegExp.test(type);
+               }
+               return _pixelNamesCache[type] ? px(node, value) : value;
+       }
+
+       var _floatStyle = d.isIE ? "styleFloat" : "cssFloat";
+       var _floatAliases = { "cssFloat": _floatStyle, "styleFloat": _floatStyle, "float": _floatStyle };
+       
+       // public API
+       
+       dojo.style = function(  /*DomNode|String*/ node, 
+                                                       /*String?|Object?*/ style, 
+                                                       /*String?*/ value){
+               //      summary:
+               //              Accesses styles on a node. If 2 arguments are
+               //              passed, acts as a getter. If 3 arguments are passed, acts
+               //              as a setter.
+               //      node:
+               //              id or reference to node to get/set style for
+               //      style:
+               //              the style property to set in DOM-accessor format
+               //              ("borderWidth", not "border-width") or an object with key/value
+               //              pairs suitable for setting each property.
+               //      value:
+               //              If passed, sets value on the node for style, handling
+               //              cross-browser concerns.
+               //      example:
+               //              Passing only an ID or node returns the computed style object of
+               //              the node:
+               //      |       dojo.style("thinger");
+               //      example:
+               //              Passing a node and a style property returns the current
+               //              normalized, computed value for that property:
+               //      |       dojo.style("thinger", "opacity"); // 1 by default
+               //
+               //      example:
+               //              Passing a node, a style property, and a value changes the
+               //              current display of the node and returns the new computed value
+               //      |       dojo.style("thinger", "opacity", 0.5); // == 0.5
+               //
+               //      example:
+               //              Passing a node, an object-style style property sets each of the values in turn and returns the computed style object of the node:
+               //      |       dojo.style("thinger", {
+               //      |               "opacity": 0.5,
+               //      |               "border": "3px solid black",
+               //      |               "height": 300
+               //      |       });
+               //
+               //      example:
+               //              When the CSS style property is hyphenated, the JavaScript property is camelCased.
+               //              font-size becomes fontSize, and so on.
+               //      |       dojo.style("thinger",{
+               //      |               fontSize:"14pt",
+               //      |               letterSpacing:"1.2em"
+               //      |       });
+               //
+               //      example:
+               //              dojo.NodeList implements .style() using the same syntax, omitting the "node" parameter, calling
+               //              dojo.style() on every element of the list. See: dojo.query and dojo.NodeList
+               //      |       dojo.query(".someClassName").style("visibility","hidden");
+               //      |       // or
+               //      |       dojo.query("#baz > div").style({
+               //      |               opacity:0.75,
+               //      |               fontSize:"13pt"
+               //      |       });
+
+               var n = d.byId(node), args = arguments.length, op = (style=="opacity");
+               style = _floatAliases[style] || style;
+               if(args == 3){
+                       return op ? d._setOpacity(n, value) : n.style[style] = value; /*Number*/
+               }
+               if(args == 2 && op){
+                       return d._getOpacity(n);
+               }
+               var s = gcs(n);
+               if(args == 2 && !d.isString(style)){
+                       for(var x in style){
+                               d.style(node, x, style[x]);
+                       }
+                       return s;
+               }
+               return (args == 1) ? s : _toStyleValue(n, style, s[style]); /* CSS2Properties||String||Number */
+       }
+
+       // =============================
+       // Box Functions
+       // =============================
+
+       dojo._getPadExtents = function(/*DomNode*/n, /*Object*/computedStyle){
+               //      summary:
+               //              Returns object with special values specifically useful for node
+               //              fitting.
+               //
+               //              * l/t = left/top padding (respectively)
+               //              * w = the total of the left and right padding 
+               //              * h = the total of the top and bottom padding
+               //
+               //              If 'node' has position, l/t forms the origin for child nodes. 
+               //              The w/h are used for calculating boxes.
+               //              Normally application code will not need to invoke this
+               //              directly, and will use the ...box... functions instead.
+               var 
+                       s = computedStyle||gcs(n), 
+                       l = px(n, s.paddingLeft), 
+                       t = px(n, s.paddingTop);
+               return { 
+                       l: l,
+                       t: t,
+                       w: l+px(n, s.paddingRight),
+                       h: t+px(n, s.paddingBottom)
+               };
+       }
+
+       dojo._getBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
+               //      summary:
+               //              returns an object with properties useful for noting the border
+               //              dimensions.
+               //
+               //              * l/t = the sum of left/top border (respectively)
+               //              * w = the sum of the left and right border
+               //              * h = the sum of the top and bottom border
+               //
+               //              The w/h are used for calculating boxes.
+               //              Normally application code will not need to invoke this
+               //              directly, and will use the ...box... functions instead.
+               var 
+                       ne = "none",
+                       s = computedStyle||gcs(n), 
+                       bl = (s.borderLeftStyle != ne ? px(n, s.borderLeftWidth) : 0),
+                       bt = (s.borderTopStyle != ne ? px(n, s.borderTopWidth) : 0);
+               return { 
+                       l: bl,
+                       t: bt,
+                       w: bl + (s.borderRightStyle!=ne ? px(n, s.borderRightWidth) : 0),
+                       h: bt + (s.borderBottomStyle!=ne ? px(n, s.borderBottomWidth) : 0)
+               };
+       }
+
+       dojo._getPadBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
+               //      summary:
+               //              returns object with properties useful for box fitting with
+               //              regards to padding.
+               //
+               //              * l/t = the sum of left/top padding and left/top border (respectively)
+               //              * w = the sum of the left and right padding and border
+               //              * h = the sum of the top and bottom padding and border
+               //
+               //              The w/h are used for calculating boxes.
+               //              Normally application code will not need to invoke this
+               //              directly, and will use the ...box... functions instead.
+               var 
+                       s = computedStyle||gcs(n), 
+                       p = d._getPadExtents(n, s),
+                       b = d._getBorderExtents(n, s);
+               return { 
+                       l: p.l + b.l,
+                       t: p.t + b.t,
+                       w: p.w + b.w,
+                       h: p.h + b.h
+               };
+       }
+
+       dojo._getMarginExtents = function(n, computedStyle){
+               //      summary:
+               //              returns object with properties useful for box fitting with
+               //              regards to box margins (i.e., the outer-box).
+               //
+               //              * l/t = marginLeft, marginTop, respectively
+               //              * w = total width, margin inclusive
+               //              * h = total height, margin inclusive
+               //
+               //              The w/h are used for calculating boxes.
+               //              Normally application code will not need to invoke this
+               //              directly, and will use the ...box... functions instead.
+               var 
+                       s = computedStyle||gcs(n), 
+                       l = px(n, s.marginLeft),
+                       t = px(n, s.marginTop),
+                       r = px(n, s.marginRight),
+                       b = px(n, s.marginBottom);
+               if(d.isSafari && (s.position != "absolute")){
+                       // FIXME: Safari's version of the computed right margin
+                       // is the space between our right edge and the right edge 
+                       // of our offsetParent. 
+                       // What we are looking for is the actual margin value as 
+                       // determined by CSS.
+                       // Hack solution is to assume left/right margins are the same.
+                       r = l;
+               }
+               return { 
+                       l: l,
+                       t: t,
+                       w: l+r,
+                       h: t+b
+               };
+       }
+
+       // Box getters work in any box context because offsetWidth/clientWidth
+       // are invariant wrt box context
+       //
+       // They do *not* work for display: inline objects that have padding styles
+       // because the user agent ignores padding (it's bogus styling in any case)
+       //
+       // Be careful with IMGs because they are inline or block depending on 
+       // browser and browser mode.
+
+       // Although it would be easier to read, there are not separate versions of 
+       // _getMarginBox for each browser because:
+       // 1. the branching is not expensive
+       // 2. factoring the shared code wastes cycles (function call overhead)
+       // 3. duplicating the shared code wastes bytes
+       
+       dojo._getMarginBox = function(/*DomNode*/node, /*Object*/computedStyle){
+               // summary:
+               //              returns an object that encodes the width, height, left and top
+               //              positions of the node's margin box.
+               var s = computedStyle||gcs(node), me = d._getMarginExtents(node, s);
+               var     l = node.offsetLeft - me.l,     t = node.offsetTop - me.t;
+               if(d.isMoz){
+                       // Mozilla:
+                       // If offsetParent has a computed overflow != visible, the offsetLeft is decreased
+                       // by the parent's border.
+                       // We don't want to compute the parent's style, so instead we examine node's
+                       // computed left/top which is more stable.
+                       var sl = parseFloat(s.left), st = parseFloat(s.top);
+                       if(!isNaN(sl) && !isNaN(st)){
+                               l = sl, t = st;
+                       }else{
+                               // If child's computed left/top are not parseable as a number (e.g. "auto"), we
+                               // have no choice but to examine the parent's computed style.
+                               var p = node.parentNode;
+                               if(p && p.style){
+                                       var pcs = gcs(p);
+                                       if(pcs.overflow != "visible"){
+                                               var be = d._getBorderExtents(p, pcs);
+                                               l += be.l, t += be.t;
+                                       }
+                               }
+                       }
+               }else if(d.isOpera){
+                       // On Opera, offsetLeft includes the parent's border
+                       var p = node.parentNode;
+                       if(p){
+                               var be = d._getBorderExtents(p);
+                               l -= be.l, t -= be.t;
+                       }
+               }
+               return { 
+                       l: l, 
+                       t: t, 
+                       w: node.offsetWidth + me.w, 
+                       h: node.offsetHeight + me.h 
+               };
+       }
+       
+       dojo._getContentBox = function(node, computedStyle){
+               // summary:
+               //              Returns an object that encodes the width, height, left and top
+               //              positions of the node's content box, irrespective of the
+               //              current box model.
+
+               // clientWidth/Height are important since the automatically account for scrollbars
+               // fallback to offsetWidth/Height for special cases (see #3378)
+               var s=computedStyle||gcs(node), pe=d._getPadExtents(node, s), be=d._getBorderExtents(node, s), w=node.clientWidth, h;
+               if(!w){
+                       w=node.offsetWidth, h=node.offsetHeight;
+               }else{
+                       h=node.clientHeight, be.w = be.h = 0; 
+               }
+               // On Opera, offsetLeft includes the parent's border
+               if(d.isOpera){ pe.l += be.l; pe.t += be.t; };
+               return { 
+                       l: pe.l, 
+                       t: pe.t, 
+                       w: w - pe.w - be.w, 
+                       h: h - pe.h - be.h
+               };
+       }
+
+       dojo._getBorderBox = function(node, computedStyle){
+               var s=computedStyle||gcs(node), pe=d._getPadExtents(node, s), cb=d._getContentBox(node, s);
+               return { 
+                       l: cb.l - pe.l, 
+                       t: cb.t - pe.t, 
+                       w: cb.w + pe.w, 
+                       h: cb.h + pe.h
+               };
+       }
+
+       // Box setters depend on box context because interpretation of width/height styles
+       // vary wrt box context.
+       //
+       // The value of dojo.boxModel is used to determine box context.
+       // dojo.boxModel can be set directly to change behavior.
+       //
+       // Beware of display: inline objects that have padding styles
+       // because the user agent ignores padding (it's a bogus setup anyway)
+       //
+       // Be careful with IMGs because they are inline or block depending on 
+       // browser and browser mode.
+       // 
+       // Elements other than DIV may have special quirks, like built-in
+       // margins or padding, or values not detectable via computedStyle.
+       // In particular, margins on TABLE do not seems to appear 
+       // at all in computedStyle on Mozilla.
+       
+       dojo._setBox = function(/*DomNode*/node, /*Number?*/l, /*Number?*/t, /*Number?*/w, /*Number?*/h, /*String?*/u){
+               //      summary:
+               //              sets width/height/left/top in the current (native) box-model
+               //              dimentions. Uses the unit passed in u.
+               //      node: DOM Node reference. Id string not supported for performance reasons.
+               //      l: optional. left offset from parent.
+               //      t: optional. top offset from parent.
+               //      w: optional. width in current box model.
+               //      h: optional. width in current box model.
+               //      u: optional. unit measure to use for other measures. Defaults to "px".
+               u = u || "px";
+               var s = node.style;
+               if(!isNaN(l)){ s.left = l+u; }
+               if(!isNaN(t)){ s.top = t+u; }
+               if(w>=0){ s.width = w+u; }
+               if(h>=0){ s.height = h+u; }
+       }
+
+       dojo._usesBorderBox = function(/*DomNode*/node){
+               //      summary: 
+               //              True if the node uses border-box layout.
+
+               // We could test the computed style of node to see if a particular box
+               // has been specified, but there are details and we choose not to bother.
+               var n = node.tagName;
+               // For whatever reason, TABLE and BUTTON are always border-box by default.
+               // If you have assigned a different box to either one via CSS then
+               // box functions will break.
+               return d.boxModel=="border-box" || n=="TABLE" || n=="BUTTON"; // boolean
+       }
+
+       dojo._setContentSize = function(/*DomNode*/node, /*Number*/widthPx, /*Number*/heightPx, /*Object*/computedStyle){
+               //      summary:
+               //              Sets the size of the node's contents, irrespective of margins,
+               //              padding, or borders.
+               if(d._usesBorderBox(node)){
+                       var pb = d._getPadBorderExtents(node, computedStyle);
+                       if(widthPx >= 0){ widthPx += pb.w; }
+                       if(heightPx >= 0){ heightPx += pb.h; }
+               }
+               d._setBox(node, NaN, NaN, widthPx, heightPx);
+       }
+
+       dojo._setMarginBox = function(/*DomNode*/node,  /*Number?*/leftPx, /*Number?*/topPx, 
+                                                                                                       /*Number?*/widthPx, /*Number?*/heightPx, 
+                                                                                                       /*Object*/computedStyle){
+               //      summary:
+               //              sets the size of the node's margin box and placement
+               //              (left/top), irrespective of box model. Think of it as a
+               //              passthrough to dojo._setBox that handles box-model vagaries for
+               //              you.
+
+               var s = computedStyle||gcs(node);
+               // Some elements have special padding, margin, and box-model settings. 
+               // To use box functions you may need to set padding, margin explicitly.
+               // Controlling box-model is harder, in a pinch you might set dojo.boxModel.
+               var bb=d._usesBorderBox(node),
+                               pb=bb ? _nilExtents : d._getPadBorderExtents(node, s),
+                               mb=d._getMarginExtents(node, s);
+               if(widthPx>=0){ widthPx = Math.max(widthPx - pb.w - mb.w, 0); }
+               if(heightPx>=0){ heightPx = Math.max(heightPx - pb.h - mb.h, 0); }
+               d._setBox(node, leftPx, topPx, widthPx, heightPx);
+       }
+       
+       var _nilExtents = { l:0, t:0, w:0, h:0 };
+
+       // public API
+       
+       dojo.marginBox = function(/*DomNode|String*/node, /*Object?*/box){
+               //      summary:
+               //              Getter/setter for the margin-box of node.
+               //      description: 
+               //              Returns an object in the expected format of box (regardless
+               //              if box is passed). The object might look like:
+               //                      `{ l: 50, t: 200, w: 300: h: 150 }`
+               //              for a node offset from its parent 50px to the left, 200px from
+               //              the top with a margin width of 300px and a margin-height of
+               //              150px.
+               //      node:
+               //              id or reference to DOM Node to get/set box for
+               //      box:
+               //              If passed, denotes that dojo.marginBox() should
+               //              update/set the margin box for node. Box is an object in the
+               //              above format. All properties are optional if passed.
+               var n=d.byId(node), s=gcs(n), b=box;
+               return !b ? d._getMarginBox(n, s) : d._setMarginBox(n, b.l, b.t, b.w, b.h, s); // Object
+       }
+
+       dojo.contentBox = function(/*DomNode|String*/node, /*Object?*/box){
+               //      summary:
+               //              Getter/setter for the content-box of node.
+               //      description:
+               //              Returns an object in the expected format of box (regardless if box is passed).
+               //              The object might look like:
+               //                      `{ l: 50, t: 200, w: 300: h: 150 }`
+               //              for a node offset from its parent 50px to the left, 200px from
+               //              the top with a content width of 300px and a content-height of
+               //              150px. Note that the content box may have a much larger border
+               //              or margin box, depending on the box model currently in use and
+               //              CSS values set/inherited for node.
+               //      node:
+               //              id or reference to DOM Node to get/set box for
+               //      box:
+               //              If passed, denotes that dojo.contentBox() should
+               //              update/set the content box for node. Box is an object in the
+               //              above format. All properties are optional if passed.
+               var n=dojo.byId(node), s=gcs(n), b=box;
+               return !b ? d._getContentBox(n, s) : d._setContentSize(n, b.w, b.h, s); // Object
+       }
+       
+       // =============================
+       // Positioning 
+       // =============================
+       
+       var _sumAncestorProperties = function(node, prop){
+               if(!(node = (node||0).parentNode)){return 0};
+               var val, retVal = 0, _b = d.body();
+               while(node && node.style){
+                       if(gcs(node).position == "fixed"){
+                               return 0;
+                       }
+                       val = node[prop];
+                       if(val){
+                               retVal += val - 0;
+                               // opera and khtml #body & #html has the same values, we only
+                               // need one value
+                               if(node == _b){ break; }
+                       }
+                       node = node.parentNode;
+               }
+               return retVal;  //      integer
+       }
+
+       dojo._docScroll = function(){
+               var 
+                       _b = d.body(),
+                       _w = d.global,
+                       de = d.doc.documentElement;
+               return {
+                       y: (_w.pageYOffset || de.scrollTop || _b.scrollTop || 0),
+                       x: (_w.pageXOffset || d._fixIeBiDiScrollLeft(de.scrollLeft) || _b.scrollLeft || 0)
+               };
+       };
+       
+       dojo._isBodyLtr = function(){
+               //FIXME: could check html and body tags directly instead of computed style?  need to ignore case, accept empty values
+               return !("_bodyLtr" in d) ? 
+                       d._bodyLtr = gcs(d.body()).direction == "ltr" :
+                       d._bodyLtr; // Boolean 
+       }
+       
+       dojo._getIeDocumentElementOffset = function(){
+               // summary
+               // The following values in IE contain an offset:
+               //     event.clientX 
+               //     event.clientY 
+               //     node.getBoundingClientRect().left
+               //     node.getBoundingClientRect().top
+               // But other position related values do not contain this offset, such as
+               // node.offsetLeft, node.offsetTop, node.style.left and node.style.top.
+               // The offset is always (2, 2) in LTR direction. When the body is in RTL
+               // direction, the offset counts the width of left scroll bar's width.
+               // This function computes the actual offset.
+
+               //NOTE: assumes we're being called in an IE browser
+
+               var de = d.doc.documentElement;
+               //FIXME: use this instead?                      var de = d.compatMode == "BackCompat" ? d.body : d.documentElement;
+
+               return (d.isIE >= 7) ?
+                       {x: de.getBoundingClientRect().left, y: de.getBoundingClientRect().top}
+               :
+                       // IE 6.0
+                       {x: d._isBodyLtr() || window.parent == window ?
+                               de.clientLeft : de.offsetWidth - de.clientWidth - de.clientLeft, 
+                               y: de.clientTop}; // Object
+       };
+       
+       dojo._fixIeBiDiScrollLeft = function(/*Integer*/ scrollLeft){
+               // In RTL direction, scrollLeft should be a negative value, but IE 
+               // returns a positive one. All codes using documentElement.scrollLeft
+               // must call this function to fix this error, otherwise the position
+               // will offset to right when there is a horizontal scrollbar.
+               var dd = d.doc;
+               if(d.isIE && !dojo._isBodyLtr()){
+                       var de = dd.compatMode == "BackCompat" ? dd.body : dd.documentElement;
+                       return scrollLeft + de.clientWidth - de.scrollWidth; // Integer
+               }
+               return scrollLeft; // Integer
+       }
+
+       dojo._abs = function(/*DomNode*/node, /*Boolean?*/includeScroll){
+               //      summary:
+               //              Gets the position of the passed element relative to
+               //              the viewport (if includeScroll==false), or relative to the
+               //              document root (if includeScroll==true).
+               //
+               //              Returns an object of the form:
+               //                      { x: 100, y: 300 }
+               //              if includeScroll is passed, the x and y values will include any
+               //              document offsets that may affect the position relative to the
+               //              viewport.
+
+               // FIXME: need to decide in the brave-new-world if we're going to be
+               // margin-box or border-box.
+               var ownerDocument = node.ownerDocument;
+               var ret = {
+                       x: 0,
+                       y: 0
+               };
+
+               // targetBoxType == "border-box"
+               var db = d.body();
+               if(d.isIE || (d.isFF >= 3)){
+                       var client = node.getBoundingClientRect();
+                       var offset = (d.isIE) ? d._getIeDocumentElementOffset() : { x: 0, y: 0};
+                       ret.x = client.left - offset.x;
+                       ret.y = client.top - offset.y;
+               }else if(ownerDocument["getBoxObjectFor"]){
+                       // mozilla
+                       var bo = ownerDocument.getBoxObjectFor(node),
+                               b = d._getBorderExtents(node);
+                       ret.x = bo.x - b.l - _sumAncestorProperties(node, "scrollLeft");
+                       ret.y = bo.y - b.t - _sumAncestorProperties(node, "scrollTop");
+               }else{
+                       if(node["offsetParent"]){
+                               var endNode;
+                               // in Safari, if the node is an absolutely positioned child of
+                               // the body and the body has a margin the offset of the child
+                               // and the body contain the body's margins, so we need to end
+                               // at the body
+                               // FIXME: getting contrary results to the above in latest WebKit.
+                               if(d.isSafari &&
+                                       //(node.style.getPropertyValue("position") == "absolute") &&
+                                       (gcs(node).position == "absolute") &&
+                                       (node.parentNode == db)){
+                                       endNode = db;
+                               }else{
+                                       endNode = db.parentNode;
+                               }
+                               if(node.parentNode != db){
+                                       var nd = node;
+                                       if(d.isOpera){ nd = db; }
+                                       ret.x -= _sumAncestorProperties(nd, "scrollLeft");
+                                       ret.y -= _sumAncestorProperties(nd, "scrollTop");
+                               }
+                               var curnode = node;
+                               do{
+                                       var n = curnode.offsetLeft;
+                                       //FIXME: ugly hack to workaround the submenu in 
+                                       //popupmenu2 does not shown up correctly in opera. 
+                                       //Someone have a better workaround?
+                                       if(!d.isOpera || n > 0){
+                                               ret.x += isNaN(n) ? 0 : n;
+                                       }
+                                       var t = curnode.offsetTop;
+                                       ret.y += isNaN(t) ? 0 : t;
+                                       if(d.isSafari && curnode != node){
+                                               var cs = gcs(curnode);
+                                               ret.x += px(curnode, cs.borderLeftWidth);
+                                               ret.y += px(curnode, cs.borderTopWidth);
+                                       }
+                                       curnode = curnode.offsetParent;
+                               }while((curnode != endNode) && curnode);
+                       }else if(node.x && node.y){
+                               ret.x += isNaN(node.x) ? 0 : node.x;
+                               ret.y += isNaN(node.y) ? 0 : node.y;
+                       }
+               }
+               // account for document scrolling
+               // if offsetParent is used, ret value already includes scroll position
+               // so we may have to actually remove that value if !includeScroll
+               if(includeScroll){
+                       var scroll = d._docScroll();
+                       ret.y += scroll.y;
+                       ret.x += scroll.x;
+               }
+
+               return ret; // object
+       }
+
+       // FIXME: need a setter for coords or a moveTo!!
+       dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll){
+               //      summary:
+               //              Returns an object that measures margin box width/height and
+               //              absolute positioning data from dojo._abs().
+               //
+               //      description:
+               //              Returns an object that measures margin box width/height and
+               //              absolute positioning data from dojo._abs().
+               //              Return value will be in the form:
+               //                      `{ l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 }`
+               //              Does not act as a setter. If includeScroll is passed, the x and
+               //              y params are affected as one would expect in dojo._abs().
+               var n=d.byId(node), s=gcs(n), mb=d._getMarginBox(n, s);
+               var abs = d._abs(n, includeScroll);
+               mb.x = abs.x;
+               mb.y = abs.y;
+               return mb;
+       }
+
+       // =============================
+       // Element attribute Functions
+       // =============================
+
+       var _fixAttrName = function(/*String*/name){
+               switch(name.toLowerCase()){
+                       case "tabindex":
+                               // Internet Explorer will only set or remove tabindex
+                               // if it is spelled "tabIndex"
+                               // console.debug((dojo.isIE && dojo.isIE < 8)? "tabIndex" : "tabindex");
+                               return (d.isIE && d.isIE < 8) ? "tabIndex" : "tabindex";
+                       default:
+                               return name;
+               }
+       }
+
+       // non-deprecated HTML4 attributes with default values
+       // http://www.w3.org/TR/html401/index/attributes.html
+       // FF and Safari will return the default values if you
+       // access the attributes via a property but not
+       // via getAttribute()
+       var _attrProps = {
+               colspan: "colSpan",
+               enctype: "enctype",
+               frameborder: "frameborder",
+               method: "method",
+               rowspan: "rowSpan",
+               scrolling: "scrolling",
+               shape: "shape",
+               span: "span",
+               type: "type",
+               valuetype: "valueType"
+       }
+
+       dojo.hasAttr = function(/*DomNode|String*/node, /*String*/name){
+               //      summary:
+               //              Returns true if the requested attribute is specified on the
+               //              given element, and false otherwise.
+               //      node:
+               //              id or reference to the element to check
+               //      name:
+               //              the name of the attribute
+               //      returns:
+               //              true if the requested attribute is specified on the
+               //              given element, and false otherwise
+               var attr = d.byId(node).getAttributeNode(_fixAttrName(name));
+               return attr ? attr.specified : false; // Boolean
+       }
+
+       var _evtHdlrMap = {
+               
+       }
+
+       var _ctr = 0;
+       var _attrId = dojo._scopeName + "attrid";
+
+       dojo.attr = function(/*DomNode|String*/node, /*String|Object*/name, /*String?*/value){
+               //      summary:
+               //              Gets or sets an attribute on an HTML element.
+               //      description:
+               //              Handles normalized getting and setting of attributes on DOM
+               //              Nodes. If 2 arguments are passed, and a the second argumnt is a
+               //              string, acts as a getter.
+               //      
+               //              If a third argument is passed, or if the second argumnt is a
+               //              map of attributes, acts as a setter.
+               //
+               //              When passing functions as values, note that they will not be
+               //              directly assigned to slots on the node, but rather the default
+               //              behavior will be removed and the new behavior will be added
+               //              using `dojo.connect()`, meaning that event handler properties
+               //              will be normalized and that some caveats with regards to
+               //              non-standard behaviors for onsubmit apply. Namely that you
+               //              should cancel form submission using `dojo.stopEvent()` on the
+               //              passed event object instead of returning a boolean value from
+               //              the handler itself.
+               //      node:
+               //              id or reference to the element to get or set the attribute on
+               //      name:
+               //              the name of the attribute to get or set.
+               //      value:
+               //              The value to set for the attribute
+               //      returns:
+               //              when used as a getter, the value of the requested attribute
+               //              or null if that attribute does not have a specified or
+               //              default value;
+               //
+               //              when user as a setter, undefined
+               //      example:
+               //      |       // get the current value of the "foo" attribute on a node
+               //      |       dojo.attr(dojo.byId("nodeId"), "foo");
+               //      |       
+               //      |       // we can just pass the id:
+               //      |       dojo.attr("nodeId", "foo");
+               //      |
+               //      |       // use attr() to set the tab index
+               //      |       dojo.attr("nodeId", "tabindex", 3);
+               //      |
+               //      |       // set multiple values at once, including event handlers:
+               //      |       dojo.attr("formId", {
+               //      |               "foo": "bar",
+               //      |               "tabindex": -1,
+               //      |               "method": "POST",
+               //      |               "onsubmit": function(e){
+               //      |                       // stop submitting the form. Note that the IE behavior
+               //      |                       // of returning true or false will have no effect here
+               //      |                       // since our handler is connect()ed to the built-in
+               //      |                       // onsubmit behavior and so we need to use
+               //      |                       // dojo.stopEvent() to ensure that the submission
+               //      |                       // doesn't proceed.
+               //      |                       dojo.stopEvent(e);
+               //      |
+               //      |                       // submit the form with Ajax
+               //      |                       dojo.xhrPost({ form: "formId" });
+               //      |               }
+               //      |       });
+
+               var args = arguments.length;
+               if(args == 2 && !d.isString(name)){
+                       for(var x in name){ d.attr(node, x, name[x]); }
+                       return;
+               }
+               node = d.byId(node);
+               name = _fixAttrName(name);
+               if(args == 3){
+                       if(d.isFunction(value)){
+                               // clobber if we can
+                               var attrId = d.attr(node, _attrId);
+                               if(!attrId){
+                                       attrId = _ctr++;
+                                       d.attr(node, _attrId, attrId);
+                               }
+                               if(!_evtHdlrMap[attrId]){
+                                       _evtHdlrMap[attrId] = {};
+                               }
+                               var h = _evtHdlrMap[attrId][name];
+                               if(h){
+                                       d.disconnect(h);
+                               }else{
+                                       try{
+                                               delete node[name];
+                                       }catch(e){}
+                               }
+
+                               // ensure that event objects are normalized, etc.
+                               _evtHdlrMap[attrId][name] = d.connect(node, name, value);
+
+                       }else if(typeof value == "boolean"){ // e.g. onsubmit, disabled
+                               // if a function, we should normalize the event object here!!!
+                               node[name] = value;
+                       }else{
+                               node.setAttribute(name, value);
+                       }
+                       return;
+               }else{
+                       // should we access this attribute via a property or
+                       // via getAttribute()?
+                       var prop = _attrProps[name.toLowerCase()];
+                       if(prop){
+                               return node[prop];
+                       }else{
+                               var value = node[name];
+                               return (typeof value == 'boolean' || typeof value == 'function') ? value : (d.hasAttr(node, name) ? node.getAttribute(name) : null);
+                       }
+               }
+       }
+
+       dojo.removeAttr = function(/*DomNode|String*/node, /*String*/name){
+               //      summary:
+               //              Removes an attribute from an HTML element.
+               //      node:
+               //              id or reference to the element to remove the attribute from
+               //      name:
+               //              the name of the attribute to remove
+               d.byId(node).removeAttribute(_fixAttrName(name));
+       }
+})();
+
+// =============================
+// (CSS) Class Functions
+// =============================
+
+dojo.hasClass = function(/*DomNode|String*/node, /*String*/classStr){
+       //      summary:
+       //              Returns whether or not the specified classes are a portion of the
+       //              class list currently applied to the node. 
+       return ((" "+dojo.byId(node).className+" ").indexOf(" "+classStr+" ") >= 0);  // Boolean
+};
+
+dojo.addClass = function(/*DomNode|String*/node, /*String*/classStr){
+       //      summary:
+       //              Adds the specified classes to the end of the class list on the
+       //              passed node.
+       node = dojo.byId(node);
+       var cls = node.className;
+       if((" "+cls+" ").indexOf(" "+classStr+" ") < 0){
+               node.className = cls + (cls ? ' ' : '') + classStr;
+       }
+};
+
+dojo.removeClass = function(/*DomNode|String*/node, /*String*/classStr){
+       // summary: Removes the specified classes from node.
+       node = dojo.byId(node);
+       var t = dojo.trim((" " + node.className + " ").replace(" " + classStr + " ", " "));
+       if(node.className != t){ node.className = t; }
+};
+
+dojo.toggleClass = function(/*DomNode|String*/node, /*String*/classStr, /*Boolean?*/condition){
+       //      summary:        
+       //              Adds a class to node if not present, or removes if present.
+       //              Pass a boolean condition if you want to explicitly add or remove.
+       //      condition:
+       //              If passed, true means to add the class, false means to remove.
+       if(condition === undefined){
+               condition = !dojo.hasClass(node, classStr);
+       }
+       dojo[condition ? "addClass" : "removeClass"](node, classStr);
+};
+
+}