2 Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
8 if(!dojo._hasResource["dojo._base.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9 dojo._hasResource["dojo._base.html"] = true;
10 dojo.require("dojo._base.lang");
11 dojo.provide("dojo._base.html");
13 // FIXME: need to add unit tests for all the semi-public methods
16 document.execCommand("BackgroundImageCache", false, true);
18 // sane browsers don't have cache "issues"
21 // =============================
23 // =============================
26 dojo.byId = function(id, doc){
28 // Returns DOM node with matching `id` attribute or `null`
29 // if not found. If `id` is a DomNode, this function is a no-op.
32 // A string to match an HTML id attribute or a reference to a DOM Node
35 // Document to work in. Defaults to the current value of
36 // dojo.doc. Can be used to retrieve
37 // node references from other documents.
40 // Look up a node by ID:
41 // | var n = dojo.byId("foo");
44 // Check if a node exists, and use it.
45 // | var n = dojo.byId("bar");
46 // | if(n){ doStuff() ... }
49 // Allow string or DomNode references to be passed to a custom function:
50 // | var foo = function(nodeOrId){
51 // | nodeOrId = dojo.byId(nodeOrId);
52 // | // ... more stuff
56 if(dojo.isIE || dojo.isOpera){
57 dojo.byId = function(id, doc){
58 if(typeof id != "string"){
61 var _d = doc || dojo.doc, te = _d.getElementById(id);
62 // attributes.id.value is better than just id in case the
63 // user has a name=id inside a form
64 if(te && (te.attributes.id.value == id || te.id == id)){
67 var eles = _d.all[id];
68 if(!eles || eles.nodeName){
71 // if more than 1, choose first with the correct id
73 while((te=eles[i++])){
74 if((te.attributes && te.attributes.id && te.attributes.id.value == id)
82 dojo.byId = function(id, doc){
83 // inline'd type check
84 return (typeof id == "string") ? (doc || dojo.doc).getElementById(id) : id; // DomNode
95 var _destroyContainer = null,
97 d.addOnWindowUnload(function(){
98 _destroyContainer = null; //prevent IE leak
102 dojo._destroyElement = function(node){
104 // Existing alias for `dojo.destroy`. Deprecated, will be removed
108 dojo._destroyElement = dojo.destroy = function(/*String|DomNode*/node){
110 // Removes a node from its parent, clobbering it and all of its
114 // Removes a node from its parent, clobbering it and all of its
115 // children. Function only works with DomNodes, and returns nothing.
118 // A String ID or DomNode reference of the element to be destroyed
121 // Destroy a node byId:
122 // | dojo.destroy("someId");
125 // Destroy all nodes in a list by reference:
126 // | dojo.query(".someNode").forEach(dojo.destroy);
130 var doc = node.ownerDocument;
131 // cannot use _destroyContainer.ownerDocument since this can throw an exception on IE
132 if(!_destroyContainer || _destroyDoc != doc){
133 _destroyContainer = doc.createElement("div");
136 _destroyContainer.appendChild(node.parentNode ? node.parentNode.removeChild(node) : node);
137 // NOTE: see http://trac.dojotoolkit.org/ticket/2931. This may be a bug and not a feature
138 _destroyContainer.innerHTML = "";
144 dojo.isDescendant = function(/*DomNode|String*/node, /*DomNode|String*/ancestor){
146 // Returns true if node is a descendant of ancestor
147 // node: string id or node reference to test
148 // ancestor: string id or node reference of potential parent to test against
151 // Test is node id="bar" is a descendant of node id="foo"
152 // | if(dojo.isDescendant("bar", "foo")){ ... }
155 ancestor = byId(ancestor);
157 if(node == ancestor){
158 return true; // Boolean
160 node = node.parentNode;
162 }catch(e){ /* squelch, return false */ }
163 return false; // Boolean
166 dojo.setSelectable = function(/*DomNode|String*/node, /*Boolean*/selectable){
168 // Enable or disable selection on a node
170 // id or reference to node
172 // state to put the node in. false indicates unselectable, true
175 // Make the node id="bar" unselectable
176 // | dojo.setSelectable("bar");
178 // Make the node id="bar" selectable
179 // | dojo.setSelectable("bar", true);
182 node.style.MozUserSelect = selectable ? "" : "none";
183 }else if(d.isKhtml || d.isWebKit){
184 node.style.KhtmlUserSelect = selectable ? "auto" : "none";
186 var v = (node.unselectable = selectable ? "" : "on");
187 d.query("*", node).forEach("item.unselectable = '"+v+"'");
189 //FIXME: else? Opera?
192 var _insertBefore = function(/*DomNode*/node, /*DomNode*/ref){
193 var parent = ref.parentNode;
195 parent.insertBefore(node, ref);
199 var _insertAfter = function(/*DomNode*/node, /*DomNode*/ref){
201 // Try to insert node after ref
202 var parent = ref.parentNode;
204 if(parent.lastChild == ref){
205 parent.appendChild(node);
207 parent.insertBefore(node, ref.nextSibling);
212 dojo.place = function(node, refNode, position){
214 // Attempt to insert node into the DOM, choosing from various positioning options.
215 // Returns the first argument resolved to a DOM node.
217 // node: String|DomNode
218 // id or node reference, or HTML fragment starting with "<" to place relative to refNode
220 // refNode: String|DomNode
221 // id or node reference to use as basis for placement
223 // position: String|Number?
224 // string noting the position of node relative to refNode or a
225 // number indicating the location in the childNodes collection of refNode.
226 // Accepted string values are:
233 // "first" and "last" indicate positions as children of refNode, "replace" replaces refNode,
234 // "only" replaces all children. position defaults to "last" if not specified
237 // Returned values is the first argument resolved to a DOM node.
239 // .place() is also a method of `dojo.NodeList`, allowing `dojo.query` node lookups.
242 // Place a node by string id as the last child of another node by string id:
243 // | dojo.place("someNode", "anotherNode");
246 // Place a node by string id before another node by string id
247 // | dojo.place("someNode", "anotherNode", "before");
250 // Create a Node, and place it in the body element (last child):
251 // | dojo.place("<div></div>", dojo.body());
254 // Put a new LI as the first child of a list by id:
255 // | dojo.place("<li></li>", "someUl", "first");
257 refNode = byId(refNode);
258 if(typeof node == "string"){ // inline'd type check
259 node = node.charAt(0) == "<" ? d._toDom(node, refNode.ownerDocument) : byId(node);
261 if(typeof position == "number"){ // inline'd type check
262 var cn = refNode.childNodes;
263 if(!cn.length || cn.length <= position){
264 refNode.appendChild(node);
266 _insertBefore(node, cn[position < 0 ? 0 : position]);
271 _insertBefore(node, refNode);
274 _insertAfter(node, refNode);
277 refNode.parentNode.replaceChild(node, refNode);
281 refNode.appendChild(node);
284 if(refNode.firstChild){
285 _insertBefore(node, refNode.firstChild);
288 // else fallthrough...
289 default: // aka: last
290 refNode.appendChild(node);
293 return node; // DomNode
296 // Box functions will assume this model.
297 // On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode.
298 // Can be set to change behavior of box setters.
302 // "content-box" (default)
303 dojo.boxModel = "content-box";
305 // We punt per-node box mode testing completely.
306 // If anybody cares, we can provide an additional (optional) unit
307 // that overrides existing code to include per-node box sensitivity.
309 // Opera documentation claims that Opera 9 uses border-box in BackCompat mode.
310 // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box.
311 // IIRC, earlier versions of Opera did in fact use border-box.
312 // Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault.
314 if(d.isIE /*|| dojo.isOpera*/){
315 // client code may have to adjust if compatMode varies across iframes
316 d.boxModel = document.compatMode == "BackCompat" ? "border-box" : "content-box";
319 // =============================
321 // =============================
323 // getComputedStyle drives most of the style code.
324 // Wherever possible, reuse the returned object.
326 // API functions below that need to access computed styles accept an
327 // optional computedStyle parameter.
328 // If this parameter is omitted, the functions will call getComputedStyle themselves.
329 // This way, calling code can access computedStyle once, and then pass the reference to
330 // multiple API functions.
333 dojo.getComputedStyle = function(node){
335 // Returns a "computed style" object.
338 // Gets a "computed style" object which can be used to gather
339 // information about the current state of the rendered node.
341 // Note that this may behave differently on different browsers.
342 // Values may have different formats and value encodings across
345 // Note also that this method is expensive. Wherever possible,
346 // reuse the returned object.
348 // Use the dojo.style() method for more consistent (pixelized)
352 // A reference to a DOM node. Does NOT support taking an
353 // ID string for speed reasons.
355 // | dojo.getComputedStyle(dojo.byId('foo')).borderWidth;
358 // Reusing the returned object, avoiding multiple lookups:
359 // | var cs = dojo.getComputedStyle(dojo.byId("someNode"));
360 // | var w = cs.width, h = cs.height;
361 return; // CSS2Properties
365 // Although we normally eschew argument validation at this
366 // level, here we test argument 'node' for (duck)type,
367 // by testing nodeType, ecause 'document' is the 'parentNode' of 'body'
368 // it is frequently sent to this function even
369 // though it is not Element.
372 gcs = function(/*DomNode*/node){
374 if(node.nodeType == 1){
375 var dv = node.ownerDocument.defaultView;
376 s = dv.getComputedStyle(node, null);
377 if(!s && node.style){
378 node.style.display = "";
379 s = dv.getComputedStyle(node, null);
385 gcs = function(node){
386 // IE (as of 7) doesn't expose Element like sane browsers
387 return node.nodeType == 1 /* ELEMENT_NODE*/ ? node.currentStyle : {};
390 gcs = function(node){
391 return node.nodeType == 1 ?
392 node.ownerDocument.defaultView.getComputedStyle(node, null) : {};
395 dojo.getComputedStyle = gcs;
398 d._toPixelValue = function(element, value){
399 // style values can be floats, client code may want
400 // to round for integer pixels.
401 return parseFloat(value) || 0;
404 d._toPixelValue = function(element, avalue){
405 if(!avalue){ return 0; }
406 // on IE7, medium is usually 4 pixels
407 if(avalue == "medium"){ return 4; }
408 // style values can be floats, client code may
409 // want to round this value for integer pixels.
410 if(avalue.slice && avalue.slice(-2) == 'px'){ return parseFloat(avalue); }
412 var sLeft = style.left;
413 var rsLeft = runtimeStyle.left;
414 runtimeStyle.left = currentStyle.left;
416 // 'avalue' may be incompatible with style.left, which can cause IE to throw
417 // this has been observed for border widths using "thin", "medium", "thick" constants
418 // those particular constants could be trapped by a lookup
419 // but perhaps there are more
421 avalue = style.pixelLeft;
426 runtimeStyle.left = rsLeft;
431 var px = d._toPixelValue;
433 // FIXME: there opacity quirks on FF that we haven't ported over. Hrm.
435 dojo._getOpacity = function(node){
437 // Returns the current opacity of the passed node as a
438 // floating-point value between 0 and 1.
440 // a reference to a DOM node. Does NOT support taking an
441 // ID string for speed reasons.
442 // returns: Number between 0 and 1
447 var astr = "DXImageTransform.Microsoft.Alpha";
448 var af = function(n, f){
450 return n.filters.item(astr);
452 return f ? {} : null;
457 d.isIE ? function(node){
459 return af(node).Opacity / 100; // Number
465 return gcs(node).opacity;
469 dojo._setOpacity = function(node, opacity){
471 // set the opacity of the passed node portably. Returns the
472 // new opacity of the node.
474 // a reference to a DOM node. Does NOT support taking an
475 // ID string for performance reasons.
477 // A Number between 0 and 1. 0 specifies transparent.
478 // returns: Number between 0 and 1
484 d.isIE ? function(/*DomNode*/node, /*Number*/opacity){
485 var ov = opacity * 100, opaque = opacity == 1;
486 node.style.zoom = opaque ? "" : 1;
492 node.style.filter += " progid:" + astr + "(Opacity=" + ov + ")";
494 af(node, 1).Opacity = ov;
497 // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so disable it altogether (bug #2661),
498 //but still update the opacity value so we can get a correct reading if it is read later.
499 af(node, 1).Enabled = !opaque;
501 if(node.nodeName.toLowerCase() == "tr"){
502 d.query("> td", node).forEach(function(i){
503 d._setOpacity(i, opacity);
508 function(node, opacity){
509 return node.style.opacity = opacity;
512 var _pixelNamesCache = {
513 left: true, top: true
515 var _pixelRegExp = /margin|padding|width|height|max|min|offset/; // |border
516 var _toStyleValue = function(node, type, value){
517 type = type.toLowerCase(); // FIXME: should we really be doing string case conversion here? Should we cache it? Need to profile!
520 if(type == "height"){ return node.offsetHeight; }
521 if(type == "width"){ return node.offsetWidth; }
523 if(type == "fontweight"){
525 case 700: return "bold";
527 default: return "normal";
531 if(!(type in _pixelNamesCache)){
532 _pixelNamesCache[type] = _pixelRegExp.test(type);
534 return _pixelNamesCache[type] ? px(node, value) : value;
537 var _floatStyle = d.isIE ? "styleFloat" : "cssFloat",
538 _floatAliases = { "cssFloat": _floatStyle, "styleFloat": _floatStyle, "float": _floatStyle }
543 dojo.style = function( /*DomNode|String*/ node,
544 /*String?|Object?*/ style,
547 // Accesses styles on a node. If 2 arguments are
548 // passed, acts as a getter. If 3 arguments are passed, acts
551 // Getting the style value uses the computed style for the node, so the value
552 // will be a calculated value, not just the immediate node.style value.
553 // Also when getting values, use specific style names,
554 // like "borderBottomWidth" instead of "border" since compound values like
555 // "border" are not necessarily reflected as expected.
556 // If you want to get node dimensions, use `dojo.marginBox()`,
557 // `dojo.contentBox()` or `dojo.position()`.
559 // id or reference to node to get/set style for
561 // the style property to set in DOM-accessor format
562 // ("borderWidth", not "border-width") or an object with key/value
563 // pairs suitable for setting each property.
565 // If passed, sets value on the node for style, handling
566 // cross-browser concerns. When setting a pixel value,
567 // be sure to include "px" in the value. For instance, top: "200px".
568 // Otherwise, in some cases, some browsers will not apply the style.
570 // Passing only an ID or node returns the computed style object of
572 // | dojo.style("thinger");
574 // Passing a node and a style property returns the current
575 // normalized, computed value for that property:
576 // | dojo.style("thinger", "opacity"); // 1 by default
579 // Passing a node, a style property, and a value changes the
580 // current display of the node and returns the new computed value
581 // | dojo.style("thinger", "opacity", 0.5); // == 0.5
584 // Passing a node, an object-style style property sets each of the values in turn and returns the computed style object of the node:
585 // | dojo.style("thinger", {
587 // | "border": "3px solid black",
588 // | "height": "300px"
592 // When the CSS style property is hyphenated, the JavaScript property is camelCased.
593 // font-size becomes fontSize, and so on.
594 // | dojo.style("thinger",{
595 // | fontSize:"14pt",
596 // | letterSpacing:"1.2em"
600 // dojo.NodeList implements .style() using the same syntax, omitting the "node" parameter, calling
601 // dojo.style() on every element of the list. See: `dojo.query()` and `dojo.NodeList()`
602 // | dojo.query(".someClassName").style("visibility","hidden");
604 // | dojo.query("#baz > div").style({
609 var n = byId(node), args = arguments.length, op = (style == "opacity");
610 style = _floatAliases[style] || style;
612 return op ? d._setOpacity(n, value) : n.style[style] = value; /*Number*/
615 return d._getOpacity(n);
618 if(args == 2 && typeof style != "string"){ // inline'd type check
620 d.style(node, x, style[x]);
624 return (args == 1) ? s : _toStyleValue(n, style, s[style] || n.style[style]); /* CSS2Properties||String||Number */
627 // =============================
629 // =============================
631 dojo._getPadExtents = function(/*DomNode*/n, /*Object*/computedStyle){
633 // Returns object with special values specifically useful for node
636 // Returns an object with `w`, `h`, `l`, `t` properties:
637 // | l/t = left/top padding (respectively)
638 // | w = the total of the left and right padding
639 // | h = the total of the top and bottom padding
640 // If 'node' has position, l/t forms the origin for child nodes.
641 // The w/h are used for calculating boxes.
642 // Normally application code will not need to invoke this
643 // directly, and will use the ...box... functions instead.
645 s = computedStyle||gcs(n),
646 l = px(n, s.paddingLeft),
647 t = px(n, s.paddingTop);
651 w: l+px(n, s.paddingRight),
652 h: t+px(n, s.paddingBottom)
656 dojo._getBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
658 // returns an object with properties useful for noting the border
661 // * l/t = the sum of left/top border (respectively)
662 // * w = the sum of the left and right border
663 // * h = the sum of the top and bottom border
665 // The w/h are used for calculating boxes.
666 // Normally application code will not need to invoke this
667 // directly, and will use the ...box... functions instead.
670 s = computedStyle||gcs(n),
671 bl = (s.borderLeftStyle != ne ? px(n, s.borderLeftWidth) : 0),
672 bt = (s.borderTopStyle != ne ? px(n, s.borderTopWidth) : 0);
676 w: bl + (s.borderRightStyle!=ne ? px(n, s.borderRightWidth) : 0),
677 h: bt + (s.borderBottomStyle!=ne ? px(n, s.borderBottomWidth) : 0)
681 dojo._getPadBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
683 // Returns object with properties useful for box fitting with
684 // regards to padding.
686 // * l/t = the sum of left/top padding and left/top border (respectively)
687 // * w = the sum of the left and right padding and border
688 // * h = the sum of the top and bottom padding and border
690 // The w/h are used for calculating boxes.
691 // Normally application code will not need to invoke this
692 // directly, and will use the ...box... functions instead.
694 s = computedStyle||gcs(n),
695 p = d._getPadExtents(n, s),
696 b = d._getBorderExtents(n, s);
705 dojo._getMarginExtents = function(n, computedStyle){
707 // returns object with properties useful for box fitting with
708 // regards to box margins (i.e., the outer-box).
710 // * l/t = marginLeft, marginTop, respectively
711 // * w = total width, margin inclusive
712 // * h = total height, margin inclusive
714 // The w/h are used for calculating boxes.
715 // Normally application code will not need to invoke this
716 // directly, and will use the ...box... functions instead.
718 s = computedStyle||gcs(n),
719 l = px(n, s.marginLeft),
720 t = px(n, s.marginTop),
721 r = px(n, s.marginRight),
722 b = px(n, s.marginBottom);
723 if(d.isWebKit && (s.position != "absolute")){
724 // FIXME: Safari's version of the computed right margin
725 // is the space between our right edge and the right edge
726 // of our offsetParent.
727 // What we are looking for is the actual margin value as
728 // determined by CSS.
729 // Hack solution is to assume left/right margins are the same.
740 // Box getters work in any box context because offsetWidth/clientWidth
741 // are invariant wrt box context
743 // They do *not* work for display: inline objects that have padding styles
744 // because the user agent ignores padding (it's bogus styling in any case)
746 // Be careful with IMGs because they are inline or block depending on
747 // browser and browser mode.
749 // Although it would be easier to read, there are not separate versions of
750 // _getMarginBox for each browser because:
751 // 1. the branching is not expensive
752 // 2. factoring the shared code wastes cycles (function call overhead)
753 // 3. duplicating the shared code wastes bytes
755 dojo._getMarginBox = function(/*DomNode*/node, /*Object*/computedStyle){
757 // returns an object that encodes the width, height, left and top
758 // positions of the node's margin box.
759 var s = computedStyle || gcs(node), me = d._getMarginExtents(node, s);
760 var l = node.offsetLeft - me.l, t = node.offsetTop - me.t, p = node.parentNode;
763 // If offsetParent has a computed overflow != visible, the offsetLeft is decreased
764 // by the parent's border.
765 // We don't want to compute the parent's style, so instead we examine node's
766 // computed left/top which is more stable.
767 var sl = parseFloat(s.left), st = parseFloat(s.top);
768 if(!isNaN(sl) && !isNaN(st)){
771 // If child's computed left/top are not parseable as a number (e.g. "auto"), we
772 // have no choice but to examine the parent's computed style.
775 if(pcs.overflow != "visible"){
776 var be = d._getBorderExtents(p, pcs);
777 l += be.l, t += be.t;
781 }else if(d.isOpera || (d.isIE > 7 && !d.isQuirks)){
782 // On Opera and IE 8, offsetLeft/Top includes the parent's border
784 be = d._getBorderExtents(p);
792 w: node.offsetWidth + me.w,
793 h: node.offsetHeight + me.h
797 dojo._getContentBox = function(node, computedStyle){
799 // Returns an object that encodes the width, height, left and top
800 // positions of the node's content box, irrespective of the
801 // current box model.
803 // clientWidth/Height are important since the automatically account for scrollbars
804 // fallback to offsetWidth/Height for special cases (see #3378)
805 var s = computedStyle || gcs(node),
806 pe = d._getPadExtents(node, s),
807 be = d._getBorderExtents(node, s),
808 w = node.clientWidth,
812 w = node.offsetWidth, h = node.offsetHeight;
814 h = node.clientHeight, be.w = be.h = 0;
816 // On Opera, offsetLeft includes the parent's border
817 if(d.isOpera){ pe.l += be.l; pe.t += be.t; };
826 dojo._getBorderBox = function(node, computedStyle){
827 var s = computedStyle || gcs(node),
828 pe = d._getPadExtents(node, s),
829 cb = d._getContentBox(node, s)
839 // Box setters depend on box context because interpretation of width/height styles
840 // vary wrt box context.
842 // The value of dojo.boxModel is used to determine box context.
843 // dojo.boxModel can be set directly to change behavior.
845 // Beware of display: inline objects that have padding styles
846 // because the user agent ignores padding (it's a bogus setup anyway)
848 // Be careful with IMGs because they are inline or block depending on
849 // browser and browser mode.
851 // Elements other than DIV may have special quirks, like built-in
852 // margins or padding, or values not detectable via computedStyle.
853 // In particular, margins on TABLE do not seems to appear
854 // at all in computedStyle on Mozilla.
856 dojo._setBox = function(/*DomNode*/node, /*Number?*/l, /*Number?*/t, /*Number?*/w, /*Number?*/h, /*String?*/u){
858 // sets width/height/left/top in the current (native) box-model
859 // dimentions. Uses the unit passed in u.
861 // DOM Node reference. Id string not supported for performance
864 // left offset from parent.
866 // top offset from parent.
868 // width in current box model.
870 // width in current box model.
872 // unit measure to use for other measures. Defaults to "px".
875 if(!isNaN(l)){ s.left = l + u; }
876 if(!isNaN(t)){ s.top = t + u; }
877 if(w >= 0){ s.width = w + u; }
878 if(h >= 0){ s.height = h + u; }
881 dojo._isButtonTag = function(/*DomNode*/node) {
883 // True if the node is BUTTON or INPUT.type="button".
884 return node.tagName == "BUTTON"
885 || node.tagName=="INPUT" && (node.getAttribute("type")||'').toUpperCase() == "BUTTON"; // boolean
888 dojo._usesBorderBox = function(/*DomNode*/node){
890 // True if the node uses border-box layout.
892 // We could test the computed style of node to see if a particular box
893 // has been specified, but there are details and we choose not to bother.
895 // TABLE and BUTTON (and INPUT type=button) are always border-box by default.
896 // If you have assigned a different box to either one via CSS then
897 // box functions will break.
899 var n = node.tagName;
900 return d.boxModel=="border-box" || n=="TABLE" || d._isButtonTag(node); // boolean
903 dojo._setContentSize = function(/*DomNode*/node, /*Number*/widthPx, /*Number*/heightPx, /*Object*/computedStyle){
905 // Sets the size of the node's contents, irrespective of margins,
906 // padding, or borders.
907 if(d._usesBorderBox(node)){
908 var pb = d._getPadBorderExtents(node, computedStyle);
909 if(widthPx >= 0){ widthPx += pb.w; }
910 if(heightPx >= 0){ heightPx += pb.h; }
912 d._setBox(node, NaN, NaN, widthPx, heightPx);
915 dojo._setMarginBox = function(/*DomNode*/node, /*Number?*/leftPx, /*Number?*/topPx,
916 /*Number?*/widthPx, /*Number?*/heightPx,
917 /*Object*/computedStyle){
919 // sets the size of the node's margin box and placement
920 // (left/top), irrespective of box model. Think of it as a
921 // passthrough to dojo._setBox that handles box-model vagaries for
924 var s = computedStyle || gcs(node),
925 // Some elements have special padding, margin, and box-model settings.
926 // To use box functions you may need to set padding, margin explicitly.
927 // Controlling box-model is harder, in a pinch you might set dojo.boxModel.
928 bb = d._usesBorderBox(node),
929 pb = bb ? _nilExtents : d._getPadBorderExtents(node, s)
932 // on Safari (3.1.2), button nodes with no explicit size have a default margin
933 // setting an explicit size eliminates the margin.
934 // We have to swizzle the width to get correct margin reading.
935 if(d._isButtonTag(node)){
937 if(widthPx >= 0 && !ns.width) { ns.width = "4px"; }
938 if(heightPx >= 0 && !ns.height) { ns.height = "4px"; }
941 var mb = d._getMarginExtents(node, s);
942 if(widthPx >= 0){ widthPx = Math.max(widthPx - pb.w - mb.w, 0); }
943 if(heightPx >= 0){ heightPx = Math.max(heightPx - pb.h - mb.h, 0); }
944 d._setBox(node, leftPx, topPx, widthPx, heightPx);
947 var _nilExtents = { l:0, t:0, w:0, h:0 };
951 dojo.marginBox = function(/*DomNode|String*/node, /*Object?*/box){
953 // Getter/setter for the margin-box of node.
955 // Getter/setter for the margin-box of node.
956 // Returns an object in the expected format of box (regardless
957 // if box is passed). The object might look like:
958 // `{ l: 50, t: 200, w: 300: h: 150 }`
959 // for a node offset from its parent 50px to the left, 200px from
960 // the top with a margin width of 300px and a margin-height of
963 // id or reference to DOM Node to get/set box for
965 // If passed, denotes that dojo.marginBox() should
966 // update/set the margin box for node. Box is an object in the
967 // above format. All properties are optional if passed.
969 // Retrieve the marginbox of a passed node
970 // | var box = dojo.marginBox("someNodeId");
971 // | console.dir(box);
974 // Set a node's marginbox to the size of another node
975 // | var box = dojo.marginBox("someNodeId");
976 // | dojo.marginBox("someOtherNode", box);
978 var n = byId(node), s = gcs(n), b = box;
979 return !b ? d._getMarginBox(n, s) : d._setMarginBox(n, b.l, b.t, b.w, b.h, s); // Object
982 dojo.contentBox = function(/*DomNode|String*/node, /*Object?*/box){
984 // Getter/setter for the content-box of node.
986 // Returns an object in the expected format of box (regardless if box is passed).
987 // The object might look like:
988 // `{ l: 50, t: 200, w: 300: h: 150 }`
989 // for a node offset from its parent 50px to the left, 200px from
990 // the top with a content width of 300px and a content-height of
991 // 150px. Note that the content box may have a much larger border
992 // or margin box, depending on the box model currently in use and
993 // CSS values set/inherited for node.
994 // While the getter will return top and left values, the
995 // setter only accepts setting the width and height.
997 // id or reference to DOM Node to get/set box for
999 // If passed, denotes that dojo.contentBox() should
1000 // update/set the content box for node. Box is an object in the
1001 // above format, but only w (width) and h (height) are supported.
1002 // All properties are optional if passed.
1003 var n = byId(node), s = gcs(n), b = box;
1004 return !b ? d._getContentBox(n, s) : d._setContentSize(n, b.w, b.h, s); // Object
1007 // =============================
1009 // =============================
1011 var _sumAncestorProperties = function(node, prop){
1012 if(!(node = (node||0).parentNode)){return 0}
1013 var val, retVal = 0, _b = d.body();
1014 while(node && node.style){
1015 if(gcs(node).position == "fixed"){
1021 // opera and khtml #body & #html has the same values, we only
1023 if(node == _b){ break; }
1025 node = node.parentNode;
1027 return retVal; // integer
1030 dojo._docScroll = function(){
1032 return "pageXOffset" in n? { x:n.pageXOffset, y:n.pageYOffset } :
1033 (n=d.doc.documentElement, n.clientHeight? { x:d._fixIeBiDiScrollLeft(n.scrollLeft), y:n.scrollTop } :
1034 (n=d.body(), { x:n.scrollLeft||0, y:n.scrollTop||0 }));
1037 dojo._isBodyLtr = function(){
1038 return "_bodyLtr" in d? d._bodyLtr :
1039 d._bodyLtr = (d.body().dir || d.doc.documentElement.dir || "ltr").toLowerCase() == "ltr"; // Boolean
1042 dojo._getIeDocumentElementOffset = function(){
1044 // returns the offset in x and y from the document body to the
1045 // visual edge of the page
1047 // The following values in IE contain an offset:
1050 // | node.getBoundingClientRect().left
1051 // | node.getBoundingClientRect().top
1052 // But other position related values do not contain this offset,
1053 // such as node.offsetLeft, node.offsetTop, node.style.left and
1054 // node.style.top. The offset is always (2, 2) in LTR direction.
1055 // When the body is in RTL direction, the offset counts the width
1056 // of left scroll bar's width. This function computes the actual
1059 //NOTE: assumes we're being called in an IE browser
1061 var de = d.doc.documentElement; // only deal with HTML element here, _abs handles body/quirks
1064 var r = de.getBoundingClientRect(); // works well for IE6+
1065 //console.debug('rect left,top = ' + r.left+','+r.top + ', html client left/top = ' + de.clientLeft+','+de.clientTop + ', rtl = ' + (!d._isBodyLtr()) + ', quirks = ' + d.isQuirks);
1069 l += de.clientLeft; // scrollbar size in strict/RTL, or,
1070 t += de.clientTop; // HTML border size in strict
1073 x: l < 0? 0 : l, // FRAME element border size can lead to inaccurate negative values
1085 dojo._fixIeBiDiScrollLeft = function(/*Integer*/ scrollLeft){
1086 // In RTL direction, scrollLeft should be a negative value, but IE < 8
1087 // returns a positive one. All codes using documentElement.scrollLeft
1088 // must call this function to fix this error, otherwise the position
1089 // will offset to right when there is a horizontal scrollbar.
1092 if(d.isIE < 8 && !d._isBodyLtr()){
1093 var de = d.isQuirks ? dd.body : dd.documentElement;
1094 return scrollLeft + de.clientWidth - de.scrollWidth; // Integer
1096 return scrollLeft; // Integer
1099 // FIXME: need a setter for coords or a moveTo!!
1100 dojo._abs = dojo.position = function(/*DomNode*/node, /*Boolean?*/includeScroll){
1102 // Gets the position and size of the passed element relative to
1103 // the viewport (if includeScroll==false), or relative to the
1104 // document root (if includeScroll==true).
1107 // Returns an object of the form:
1108 // { x: 100, y: 300, w: 20, h: 15 }
1109 // If includeScroll==true, the x and y values will include any
1110 // document offsets that may affect the position relative to the
1112 // Uses the border-box model (inclusive of border and padding but
1113 // not margin). Does not act as a setter.
1115 var db = d.body(), dh = db.parentNode, ret;
1117 if(node["getBoundingClientRect"]){
1118 // IE6+, FF3+, super-modern WebKit, and Opera 9.6+ all take this branch
1119 ret = node.getBoundingClientRect();
1120 ret = { x: ret.left, y: ret.top, w: ret.right - ret.left, h: ret.bottom - ret.top };
1122 // On IE there's a 2px offset that we need to adjust for, see _getIeDocumentElementOffset()
1123 var offset = d._getIeDocumentElementOffset();
1125 // fixes the position in IE, quirks mode
1126 ret.x -= offset.x + (d.isQuirks ? db.clientLeft+db.offsetLeft : 0);
1127 ret.y -= offset.y + (d.isQuirks ? db.clientTop+db.offsetTop : 0);
1128 }else if(d.isFF == 3){
1129 // In FF3 you have to subtract the document element margins.
1130 // Fixed in FF3.5 though.
1132 ret.x -= px(dh, cs.marginLeft) + px(dh, cs.borderLeftWidth);
1133 ret.y -= px(dh, cs.marginTop) + px(dh, cs.borderTopWidth);
1136 // FF2 and older WebKit
1140 w: node.offsetWidth,
1141 h: node.offsetHeight
1143 if(node["offsetParent"]){
1144 ret.x -= _sumAncestorProperties(node, "scrollLeft");
1145 ret.y -= _sumAncestorProperties(node, "scrollTop");
1149 var n = curnode.offsetLeft,
1150 t = curnode.offsetTop;
1151 ret.x += isNaN(n) ? 0 : n;
1152 ret.y += isNaN(t) ? 0 : t;
1155 if(curnode != node){
1157 // tried left+right with differently sized left/right borders
1158 // it really is 2xleft border in FF, not left+right, even in RTL!
1159 ret.x += 2 * px(curnode,cs.borderLeftWidth);
1160 ret.y += 2 * px(curnode,cs.borderTopWidth);
1162 ret.x += px(curnode, cs.borderLeftWidth);
1163 ret.y += px(curnode, cs.borderTopWidth);
1166 // static children in a static div in FF2 are affected by the div's border as well
1167 // but offsetParent will skip this div!
1168 if(d.isMoz && cs.position=="static"){
1169 var parent=curnode.parentNode;
1170 while(parent!=curnode.offsetParent){
1171 var pcs=gcs(parent);
1172 if(pcs.position=="static"){
1173 ret.x += px(curnode,pcs.borderLeftWidth);
1174 ret.y += px(curnode,pcs.borderTopWidth);
1176 parent=parent.parentNode;
1179 curnode = curnode.offsetParent;
1180 }while((curnode != dh) && curnode);
1181 }else if(node.x && node.y){
1182 ret.x += isNaN(node.x) ? 0 : node.x;
1183 ret.y += isNaN(node.y) ? 0 : node.y;
1186 // account for document scrolling
1187 // if offsetParent is used, ret value already includes scroll position
1188 // so we may have to actually remove that value if !includeScroll
1190 var scroll = d._docScroll();
1195 return ret; // Object
1198 dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll){
1200 // Deprecated: Use position() for border-box x/y/w/h
1201 // or marginBox() for margin-box w/h/l/t.
1202 // Returns an object representing a node's size and position.
1205 // Returns an object that measures margin-box (w)idth/(h)eight
1206 // and absolute position x/y of the border-box. Also returned
1207 // is computed (l)eft and (t)op values in pixels from the
1208 // node's offsetParent as returned from marginBox().
1209 // Return value will be in the form:
1210 //| { l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 }
1211 // Does not act as a setter. If includeScroll is passed, the x and
1212 // y params are affected as one would expect in dojo.position().
1213 var n = byId(node), s = gcs(n), mb = d._getMarginBox(n, s);
1214 var abs = d.position(n, includeScroll);
1220 // =============================
1221 // Element attribute Functions
1222 // =============================
1224 // dojo.attr() should conform to http://www.w3.org/TR/DOM-Level-2-Core/
1227 // properties renamed to avoid clashes with reserved words
1228 "class": "className",
1230 // properties written as camelCase
1231 tabindex: "tabIndex",
1232 readonly: "readOnly",
1234 frameborder: "frameBorder",
1236 valuetype: "valueType"
1239 // original attribute names
1243 tabindex: "tabIndex",
1244 readonly: "readOnly"
1253 var _fixAttrName = function(/*String*/ name){
1254 return _attrNames[name.toLowerCase()] || name;
1257 var _hasAttr = function(node, name){
1258 var attr = node.getAttributeNode && node.getAttributeNode(name);
1259 return attr && attr.specified; // Boolean
1262 // There is a difference in the presence of certain properties and their default values
1263 // between browsers. For example, on IE "disabled" is present on all elements,
1264 // but it is value is "false"; "tabIndex" of <div> returns 0 by default on IE, yet other browsers
1267 dojo.hasAttr = function(/*DomNode|String*/node, /*String*/name){
1269 // Returns true if the requested attribute is specified on the
1270 // given element, and false otherwise.
1272 // id or reference to the element to check
1274 // the name of the attribute
1276 // true if the requested attribute is specified on the
1277 // given element, and false otherwise
1278 var lc = name.toLowerCase();
1279 return _forcePropNames[_propNames[lc] || name] || _hasAttr(byId(node), _attrNames[lc] || name); // Boolean
1282 var _evtHdlrMap = {}, _ctr = 0,
1283 _attrId = dojo._scopeName + "attrid",
1284 // the next dictionary lists elements with read-only innerHTML on IE
1285 _roInnerHtml = {col: 1, colgroup: 1,
1286 // frameset: 1, head: 1, html: 1, style: 1,
1287 table: 1, tbody: 1, tfoot: 1, thead: 1, tr: 1, title: 1};
1289 dojo.attr = function(/*DomNode|String*/node, /*String|Object*/name, /*String?*/value){
1291 // Gets or sets an attribute on an HTML element.
1293 // Handles normalized getting and setting of attributes on DOM
1294 // Nodes. If 2 arguments are passed, and a the second argumnt is a
1295 // string, acts as a getter.
1297 // If a third argument is passed, or if the second argument is a
1298 // map of attributes, acts as a setter.
1300 // When passing functions as values, note that they will not be
1301 // directly assigned to slots on the node, but rather the default
1302 // behavior will be removed and the new behavior will be added
1303 // using `dojo.connect()`, meaning that event handler properties
1304 // will be normalized and that some caveats with regards to
1305 // non-standard behaviors for onsubmit apply. Namely that you
1306 // should cancel form submission using `dojo.stopEvent()` on the
1307 // passed event object instead of returning a boolean value from
1308 // the handler itself.
1310 // id or reference to the element to get or set the attribute on
1312 // the name of the attribute to get or set.
1314 // The value to set for the attribute
1316 // when used as a getter, the value of the requested attribute
1317 // or null if that attribute does not have a specified or
1320 // when used as a setter, the DOM node
1323 // | // get the current value of the "foo" attribute on a node
1324 // | dojo.attr(dojo.byId("nodeId"), "foo");
1325 // | // or we can just pass the id:
1326 // | dojo.attr("nodeId", "foo");
1329 // | // use attr() to set the tab index
1330 // | dojo.attr("nodeId", "tabIndex", 3);
1334 // Set multiple values at once, including event handlers:
1335 // | dojo.attr("formId", {
1337 // | "tabIndex": -1,
1338 // | "method": "POST",
1339 // | "onsubmit": function(e){
1340 // | // stop submitting the form. Note that the IE behavior
1341 // | // of returning true or false will have no effect here
1342 // | // since our handler is connect()ed to the built-in
1343 // | // onsubmit behavior and so we need to use
1344 // | // dojo.stopEvent() to ensure that the submission
1345 // | // doesn't proceed.
1346 // | dojo.stopEvent(e);
1348 // | // submit the form with Ajax
1349 // | dojo.xhrPost({ form: "formId" });
1354 // Style is s special case: Only set with an object hash of styles
1355 // | dojo.attr("someNode",{
1358 // | width:"200px", height:"100px", color:"#000"
1363 // Again, only set style as an object hash of styles:
1364 // | var obj = { color:"#fff", backgroundColor:"#000" };
1365 // | dojo.attr("someNode", "style", obj);
1367 // | // though shorter to use `dojo.style()` in this case:
1368 // | dojo.style("someNode", obj);
1371 var args = arguments.length, prop;
1372 if(args == 2 && typeof name != "string"){ // inline'd type check
1373 // the object form of setter: the 2nd argument is a dictionary
1375 d.attr(node, x, name[x]);
1377 return node; // DomNode
1379 var lc = name.toLowerCase(),
1380 propName = _propNames[lc] || name,
1381 forceProp = _forcePropNames[propName],
1382 attrName = _attrNames[lc] || name;
1386 if(propName == "style" && typeof value != "string"){ // inline'd type check
1387 // special case: setting a style
1388 d.style(node, value);
1391 if(propName == "innerHTML"){
1392 // special case: assigning HTML
1393 if(d.isIE && node.tagName.toLowerCase() in _roInnerHtml){
1395 node.appendChild(d._toDom(value, node.ownerDocument));
1397 node[propName] = value;
1401 if(d.isFunction(value)){
1402 // special case: assigning an event handler
1403 // clobber if we can
1404 var attrId = d.attr(node, _attrId);
1407 d.attr(node, _attrId, attrId);
1409 if(!_evtHdlrMap[attrId]){
1410 _evtHdlrMap[attrId] = {};
1412 var h = _evtHdlrMap[attrId][propName];
1417 delete node[propName];
1420 // ensure that event objects are normalized, etc.
1421 _evtHdlrMap[attrId][propName] = d.connect(node, propName, value);
1424 if(forceProp || typeof value == "boolean"){
1425 // special case: forcing assignment to the property
1426 // special case: setting boolean to a property instead of attribute
1427 node[propName] = value;
1431 node.setAttribute(attrName, value);
1433 return node; // DomNode
1436 // should we access this attribute via a property or
1437 // via getAttribute()?
1438 value = node[propName];
1439 if(forceProp && typeof value != "undefined"){
1441 return value; // Anything
1443 if(propName != "href" && (typeof value == "boolean" || d.isFunction(value))){
1445 return value; // Anything
1448 // we need _hasAttr() here to guard against IE returning a default value
1449 return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything
1452 dojo.removeAttr = function(/*DomNode|String*/ node, /*String*/ name){
1454 // Removes an attribute from an HTML element.
1456 // id or reference to the element to remove the attribute from
1458 // the name of the attribute to remove
1459 byId(node).removeAttribute(_fixAttrName(name));
1462 dojo.getNodeProp = function(/*DomNode|String*/ node, /*String*/ name){
1464 // Returns an effective value of a property or an attribute.
1466 // id or reference to the element to remove the attribute from
1468 // the name of the attribute
1470 var lc = name.toLowerCase(),
1471 propName = _propNames[lc] || name;
1472 if((propName in node) && propName != "href"){
1474 return node[propName]; // Anything
1477 var attrName = _attrNames[lc] || name;
1478 return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything
1481 dojo.create = function(tag, attrs, refNode, pos){
1483 // Create an element, allowing for optional attribute decoration
1487 // A DOM Element creation function. A shorthand method for creating a node or
1488 // a fragment, and allowing for a convenient optional attribute setting step,
1489 // as well as an optional DOM placement reference.
1491 // Attributes are set by passing the optional object through `dojo.attr`.
1492 // See `dojo.attr` for noted caveats and nuances, and API if applicable.
1494 // Placement is done via `dojo.place`, assuming the new node to be the action
1495 // node, passing along the optional reference node and position.
1497 // tag: String|DomNode
1498 // A string of the element to create (eg: "div", "a", "p", "li", "script", "br"),
1499 // or an existing DOM node to process.
1502 // An object-hash of attributes to set on the newly created node.
1503 // Can be null, if you don't want to set any attributes/styles.
1504 // See: `dojo.attr` for a description of available attributes.
1506 // refNode: String?|DomNode?
1507 // Optional reference node. Used by `dojo.place` to place the newly created
1508 // node somewhere in the dom relative to refNode. Can be a DomNode reference
1509 // or String ID of a node.
1512 // Optional positional reference. Defaults to "last" by way of `dojo.place`,
1513 // though can be set to "first","after","before","last", "replace" or "only"
1514 // to further control the placement of the new node relative to the refNode.
1515 // 'refNode' is required if a 'pos' is specified.
1521 // | var n = dojo.create("div");
1524 // Create a DIV with content:
1525 // | var n = dojo.create("div", { innerHTML:"<p>hi</p>" });
1528 // Place a new DIV in the BODY, with no attributes set
1529 // | var n = dojo.create("div", null, dojo.body());
1532 // Create an UL, and populate it with LI's. Place the list as the first-child of a
1533 // node with id="someId":
1534 // | var ul = dojo.create("ul", null, "someId", "first");
1535 // | var items = ["one", "two", "three", "four"];
1536 // | dojo.forEach(items, function(data){
1537 // | dojo.create("li", { innerHTML: data }, ul);
1541 // Create an anchor, with an href. Place in BODY:
1542 // | dojo.create("a", { href:"foo.html", title:"Goto FOO!" }, dojo.body());
1545 // Create a `dojo.NodeList()` from a new element (for syntatic sugar):
1546 // | dojo.query(dojo.create('div'))
1547 // | .addClass("newDiv")
1548 // | .onclick(function(e){ console.log('clicked', e.target) })
1549 // | .place("#someNode"); // redundant, but cleaner.
1553 refNode = byId(refNode);
1554 doc = refNode.ownerDocument;
1556 if(typeof tag == "string"){ // inline'd type check
1557 tag = doc.createElement(tag);
1559 if(attrs){ d.attr(tag, attrs); }
1560 if(refNode){ d.place(tag, refNode, pos); }
1561 return tag; // DomNode
1565 dojo.empty = function(node){
1567 // safely removes all children of the node.
1568 // node: DOMNode|String
1569 // a reference to a DOM node or an id.
1571 // Destroy node's children byId:
1572 // | dojo.empty("someId");
1575 // Destroy all nodes' children in a list by reference:
1576 // | dojo.query(".someNode").forEach(dojo.empty);
1581 d.isIE ? function(node){
1583 for(var c; c = node.lastChild;){ // intentional assignment
1588 byId(node).innerHTML = "";
1592 dojo._toDom = function(frag, doc){
1594 // instantiates an HTML fragment returning the corresponding DOM.
1596 // the HTML fragment
1597 // doc: DocumentNode?
1598 // optional document to use when creating DOM nodes, defaults to
1599 // dojo.doc if not specified.
1600 // returns: DocumentFragment
1603 // Create a table row:
1604 // | var tr = dojo._toDom("<tr><td>First!</td></tr>");
1608 // support stuff for dojo._toDom
1614 tr: ["table", "tbody"],
1615 td: ["table", "tbody", "tr"],
1616 th: ["table", "thead", "tr"],
1617 legend: ["fieldset"],
1619 colgroup: ["table"],
1620 col: ["table", "colgroup"],
1623 reTag = /<\s*([\w\:]+)/,
1624 masterNode = {}, masterNum = 0,
1625 masterName = "__" + d._scopeName + "ToDomId";
1627 // generate start/end tag strings to use
1628 // for the injection for each special tag wrap case.
1629 for(var param in tagWrap){
1630 var tw = tagWrap[param];
1631 tw.pre = param == "option" ? '<select multiple="multiple">' : "<" + tw.join("><") + ">";
1632 tw.post = "</" + tw.reverse().join("></") + ">";
1633 // the last line is destructive: it reverses the array,
1634 // but we don't care at this point
1637 d._toDom = function(frag, doc){
1639 // converts HTML string into DOM nodes.
1642 var masterId = doc[masterName];
1644 doc[masterName] = masterId = ++masterNum + "";
1645 masterNode[masterId] = doc.createElement("div");
1648 // make sure the frag is a string.
1651 // find the starting tag, and get node wrapper
1652 var match = frag.match(reTag),
1653 tag = match ? match[1].toLowerCase() : "",
1654 master = masterNode[masterId],
1656 if(match && tagWrap[tag]){
1657 wrap = tagWrap[tag];
1658 master.innerHTML = wrap.pre + frag + wrap.post;
1659 for(i = wrap.length; i; --i){
1660 master = master.firstChild;
1663 master.innerHTML = frag;
1666 // one node shortcut => return the node itself
1667 if(master.childNodes.length == 1){
1668 return master.removeChild(master.firstChild); // DOMNode
1671 // return multiple nodes as a document fragment
1672 df = doc.createDocumentFragment();
1673 while(fc = master.firstChild){ // intentional assignment
1676 return df; // DOMNode
1679 // =============================
1680 // (CSS) Class Functions
1681 // =============================
1682 var _className = "className";
1684 dojo.hasClass = function(/*DomNode|String*/node, /*String*/classStr){
1686 // Returns whether or not the specified classes are a portion of the
1687 // class list currently applied to the node.
1690 // String ID or DomNode reference to check the class for.
1693 // A string class name to look for.
1696 // Do something if a node with id="someNode" has class="aSillyClassName" present
1697 // | if(dojo.hasClass("someNode","aSillyClassName")){ ... }
1699 return ((" "+ byId(node)[_className] +" ").indexOf(" " + classStr + " ") >= 0); // Boolean
1702 var spaces = /\s+/, a1 = [""],
1703 str2array = function(s){
1704 if(typeof s == "string" || s instanceof String){
1705 if(s.indexOf(" ") < 0){
1709 return s.split(spaces);
1712 // assumed to be an array
1716 dojo.addClass = function(/*DomNode|String*/node, /*String|Array*/classStr){
1718 // Adds the specified classes to the end of the class list on the
1719 // passed node. Will not re-apply duplicate classes.
1722 // String ID or DomNode reference to add a class string too
1725 // A String class name to add, or several space-separated class names,
1726 // or an array of class names.
1729 // Add a class to some node:
1730 // | dojo.addClass("someNode", "anewClass");
1733 // Add two classes at once:
1734 // | dojo.addClass("someNode", "firstClass secondClass");
1737 // Add two classes at once (using array):
1738 // | dojo.addClass("someNode", ["firstClass", "secondClass"]);
1741 // Available in `dojo.NodeList` for multiple additions
1742 // | dojo.query("ul > li").addClass("firstLevel");
1745 classStr = str2array(classStr);
1746 var cls = node[_className], oldLen;
1747 cls = cls ? " " + cls + " " : " ";
1748 oldLen = cls.length;
1749 for(var i = 0, len = classStr.length, c; i < len; ++i){
1751 if(c && cls.indexOf(" " + c + " ") < 0){
1755 if(oldLen < cls.length){
1756 node[_className] = cls.substr(1, cls.length - 2);
1760 dojo.removeClass = function(/*DomNode|String*/node, /*String|Array?*/classStr){
1762 // Removes the specified classes from node. No `dojo.hasClass`
1763 // check is required.
1766 // String ID or DomNode reference to remove the class from.
1769 // An optional String class name to remove, or several space-separated
1770 // class names, or an array of class names. If omitted, all class names
1774 // Remove a class from some node:
1775 // | dojo.removeClass("someNode", "firstClass");
1778 // Remove two classes from some node:
1779 // | dojo.removeClass("someNode", "firstClass secondClass");
1782 // Remove two classes from some node (using array):
1783 // | dojo.removeClass("someNode", ["firstClass", "secondClass"]);
1786 // Remove all classes from some node:
1787 // | dojo.removeClass("someNode");
1790 // Available in `dojo.NodeList()` for multiple removal
1791 // | dojo.query(".foo").removeClass("foo");
1795 if(classStr !== undefined){
1796 classStr = str2array(classStr);
1797 cls = " " + node[_className] + " ";
1798 for(var i = 0, len = classStr.length; i < len; ++i){
1799 cls = cls.replace(" " + classStr[i] + " ", " ");
1805 if(node[_className] != cls){ node[_className] = cls; }
1808 dojo.toggleClass = function(/*DomNode|String*/node, /*String|Array*/classStr, /*Boolean?*/condition){
1810 // Adds a class to node if not present, or removes if present.
1811 // Pass a boolean condition if you want to explicitly add or remove.
1813 // If passed, true means to add the class, false means to remove.
1816 // | dojo.toggleClass("someNode", "hovered");
1819 // Forcefully add a class
1820 // | dojo.toggleClass("someNode", "hovered", true);
1823 // Available in `dojo.NodeList()` for multiple toggles
1824 // | dojo.query(".toggleMe").toggleClass("toggleMe");
1826 if(condition === undefined){
1827 condition = !d.hasClass(node, classStr);
1829 d[condition ? "addClass" : "removeClass"](node, classStr);