]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/_base/html.js
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
);