diff options
Diffstat (limited to 'includes/js/dojo/_base/html.js')
-rw-r--r-- | includes/js/dojo/_base/html.js | 1227 |
1 files changed, 1227 insertions, 0 deletions
diff --git a/includes/js/dojo/_base/html.js b/includes/js/dojo/_base/html.js new file mode 100644 index 0000000..d673eb1 --- /dev/null +++ b/includes/js/dojo/_base/html.js @@ -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); +}; + +} |