1 if(!dojo._hasResource["dojo._base.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojo._base.html"] = true;
3 dojo.require("dojo._base.lang");
4 dojo.provide("dojo._base.html");
6 // FIXME: need to add unit tests for all the semi-public methods
9 document.execCommand("BackgroundImageCache", false, true);
11 // sane browsers don't have cache "issues"
14 // =============================
16 // =============================
19 dojo.byId = function(id, doc){
21 // Returns DOM node with matching `id` attribute or `null`
22 // if not found, similar to "$" function in another library.
23 // If `id` is a DomNode, this function is a no-op.
26 // A string to match an HTML id attribute or a reference to a DOM Node
29 // Document to work in. Defaults to the current value of
30 // dojo.doc. Can be used to retrieve
31 // node references from other documents.
33 if(dojo.isIE || dojo.isOpera){
34 dojo.byId = function(id, doc){
35 if(dojo.isString(id)){
36 var _d = doc || dojo.doc;
37 var te = _d.getElementById(id);
38 // attributes.id.value is better than just id in case the
39 // user has a name=id inside a form
40 if(te && te.attributes.id.value == id){
43 var eles = _d.all[id];
44 if(!eles || !eles.length){ return eles; }
45 // if more than 1, choose first with the correct id
47 while((te=eles[i++])){
48 if(te.attributes.id.value == id){ return te; }
56 dojo.byId = function(id, doc){
57 return dojo.isString(id) ? (doc || dojo.doc).getElementById(id) : id; // DomNode
66 dojo.createElement = function(obj, parent, position){
67 // TODO: need to finish this!
73 var _destroyContainer = null;
74 dojo.addOnUnload(function(){
75 _destroyContainer=null; //prevent IE leak
77 dojo._destroyElement = function(/*String||DomNode*/node){
79 // removes node from its parent, clobbers it and all of its
82 // the element to be destroyed, either as an ID or a reference
86 if(!_destroyContainer){
87 _destroyContainer = document.createElement("div");
89 _destroyContainer.appendChild(node.parentNode ? node.parentNode.removeChild(node) : node);
90 // NOTE: see http://trac.dojotoolkit.org/ticket/2931. This may be a bug and not a feature
91 _destroyContainer.innerHTML = "";
97 dojo.isDescendant = function(/*DomNode|String*/node, /*DomNode|String*/ancestor){
99 // Returns true if node is a descendant of ancestor
100 // node: id or node reference to test
101 // ancestor: id or node reference of potential parent to test against
104 ancestor = d.byId(ancestor);
106 if(node === ancestor){
107 return true; // Boolean
109 node = node.parentNode;
111 }catch(e){ /* squelch, return false */ }
112 return false; // Boolean
115 dojo.setSelectable = function(/*DomNode|String*/node, /*Boolean*/selectable){
116 // summary: enable or disable selection on a node
118 // id or reference to node
122 node.style.MozUserSelect = selectable ? "" : "none";
124 node.style.KhtmlUserSelect = selectable ? "auto" : "none";
126 node.unselectable = selectable ? "" : "on";
127 d.query("*", node).forEach(function(descendant){
128 descendant.unselectable = selectable ? "" : "on";
131 //FIXME: else? Opera?
134 var _insertBefore = function(/*Node*/node, /*Node*/ref){
135 ref.parentNode.insertBefore(node, ref);
136 return true; // boolean
139 var _insertAfter = function(/*Node*/node, /*Node*/ref){
141 // Try to insert node after ref
142 var pn = ref.parentNode;
143 if(ref == pn.lastChild){
144 pn.appendChild(node);
146 return _insertBefore(node, ref.nextSibling); // boolean
148 return true; // boolean
151 dojo.place = function(/*String|DomNode*/node, /*String|DomNode*/refNode, /*String|Number*/position){
153 // Attempt to insert node into the DOM, choosing from various positioning options.
154 // Returns true if successful, false otherwise.
156 // id or node reference to place relative to refNode
158 // id or node reference to use as basis for placement
160 // string noting the position of node relative to refNode or a
161 // number indicating the location in the childNodes collection of
162 // refNode. Accepted string values are:
169 // "first" and "last" indicate positions as children of refNode.
171 // FIXME: need to write tests for this!!!!
172 if(!node || !refNode || position === undefined){
173 return false; // boolean
176 refNode = d.byId(refNode);
177 if(typeof position == "number"){
178 var cn = refNode.childNodes;
179 if((position == 0 && cn.length == 0) ||
180 cn.length == position){
181 refNode.appendChild(node); return true;
184 return _insertBefore(node, refNode.firstChild);
186 return _insertAfter(node, cn[position-1]);
188 switch(position.toLowerCase()){
190 return _insertBefore(node, refNode); // boolean
192 return _insertAfter(node, refNode); // boolean
194 if(refNode.firstChild){
195 return _insertBefore(node, refNode.firstChild); // boolean
197 // else fallthrough...
198 default: // aka: last
199 refNode.appendChild(node);
200 return true; // boolean
204 // Box functions will assume this model.
205 // On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode.
206 // Can be set to change behavior of box setters.
210 // "content-box" (default)
211 dojo.boxModel = "content-box";
213 // We punt per-node box mode testing completely.
214 // If anybody cares, we can provide an additional (optional) unit
215 // that overrides existing code to include per-node box sensitivity.
217 // Opera documentation claims that Opera 9 uses border-box in BackCompat mode.
218 // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box.
219 // IIRC, earlier versions of Opera did in fact use border-box.
220 // Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault.
222 if(d.isIE /*|| dojo.isOpera*/){
223 var _dcm = document.compatMode;
224 // client code may have to adjust if compatMode varies across iframes
225 d.boxModel = _dcm == "BackCompat" || _dcm == "QuirksMode" || d.isIE<6 ? "border-box" : "content-box"; // FIXME: remove IE < 6 support?
228 // =============================
230 // =============================
232 // getComputedStyle drives most of the style code.
233 // Wherever possible, reuse the returned object.
235 // API functions below that need to access computed styles accept an
236 // optional computedStyle parameter.
237 // If this parameter is omitted, the functions will call getComputedStyle themselves.
238 // This way, calling code can access computedStyle once, and then pass the reference to
239 // multiple API functions.
242 dojo.getComputedStyle = function(node){
244 // Returns a "computed style" object.
247 // Gets a "computed style" object which can be used to gather
248 // information about the current state of the rendered node.
250 // Note that this may behave differently on different browsers.
251 // Values may have different formats and value encodings across
254 // Note also that this method is expensive. Wherever possible,
255 // reuse the returned object.
257 // Use the dojo.style() method for more consistent (pixelized)
261 // A reference to a DOM node. Does NOT support taking an
262 // ID string for speed reasons.
264 // | dojo.getComputedStyle(dojo.byId('foo')).borderWidth;
265 return; // CSS2Properties
269 var gcs, dv = document.defaultView;
271 gcs = function(/*DomNode*/node){
272 var s = dv.getComputedStyle(node, null);
273 if(!s && node.style){
274 node.style.display = "";
275 s = dv.getComputedStyle(node, null);
280 gcs = function(node){
281 return node.currentStyle;
284 gcs = function(node){
285 return dv.getComputedStyle(node, null);
288 dojo.getComputedStyle = gcs;
291 dojo._toPixelValue = function(element, value){
292 // style values can be floats, client code may want
293 // to round for integer pixels.
294 return parseFloat(value) || 0;
297 dojo._toPixelValue = function(element, avalue){
298 if(!avalue){ return 0; }
299 // on IE7, medium is usually 4 pixels
300 if(avalue=="medium"){ return 4; }
301 // style values can be floats, client code may
302 // want to round this value for integer pixels.
303 if(avalue.slice && (avalue.slice(-2)=='px')){ return parseFloat(avalue); }
305 var sLeft = style.left;
306 var rsLeft = runtimeStyle.left;
307 runtimeStyle.left = currentStyle.left;
309 // 'avalue' may be incompatible with style.left, which can cause IE to throw
310 // this has been observed for border widths using "thin", "medium", "thick" constants
311 // those particular constants could be trapped by a lookup
312 // but perhaps there are more
314 avalue = style.pixelLeft;
319 runtimeStyle.left = rsLeft;
324 var px = d._toPixelValue;
326 // FIXME: there opacity quirks on FF that we haven't ported over. Hrm.
328 dojo._getOpacity = function(node){
330 // Returns the current opacity of the passed node as a
331 // floating-point value between 0 and 1.
333 // a reference to a DOM node. Does NOT support taking an
334 // ID string for speed reasons.
335 // return: Number between 0 and 1
339 dojo._getOpacity = d.isIE ? function(node){
341 return node.filters.alpha.opacity / 100; // Number
346 return gcs(node).opacity;
350 dojo._setOpacity = function(node, opacity){
352 // set the opacity of the passed node portably. Returns the
353 // new opacity of the node.
355 // a reference to a DOM node. Does NOT support taking an
356 // ID string for performance reasons.
358 // A Number between 0 and 1. 0 specifies transparent.
359 // return: Number between 0 and 1
363 dojo._setOpacity = d.isIE ? function(/*DomNode*/node, /*Number*/opacity){
365 // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so remove it altogether (bug #2661)
366 var filterRE = /FILTER:[^;]*;?/i;
367 node.style.cssText = node.style.cssText.replace(filterRE, "");
368 if(node.nodeName.toLowerCase() == "tr"){
369 d.query("> td", node).forEach(function(i){
370 i.style.cssText = i.style.cssText.replace(filterRE, "");
374 var o = "Alpha(Opacity="+ opacity * 100 +")";
375 node.style.filter = o;
377 if(node.nodeName.toLowerCase() == "tr"){
378 d.query("> td", node).forEach(function(i){
383 } : function(node, opacity){
384 return node.style.opacity = opacity;
387 var _pixelNamesCache = {
388 left: true, top: true
390 var _pixelRegExp = /margin|padding|width|height|max|min|offset/; // |border
391 var _toStyleValue = function(node, type, value){
392 type = type.toLowerCase();
393 if(d.isIE && value == "auto"){
394 if(type == "height"){ return node.offsetHeight; }
395 if(type == "width"){ return node.offsetWidth; }
397 if(!(type in _pixelNamesCache)){
398 // if(dojo.isOpera && type == "cssText"){
399 // FIXME: add workaround for #2855 here
401 _pixelNamesCache[type] = _pixelRegExp.test(type);
403 return _pixelNamesCache[type] ? px(node, value) : value;
406 var _floatStyle = d.isIE ? "styleFloat" : "cssFloat";
407 var _floatAliases = { "cssFloat": _floatStyle, "styleFloat": _floatStyle, "float": _floatStyle };
411 dojo.style = function( /*DomNode|String*/ node,
412 /*String?|Object?*/ style,
415 // Accesses styles on a node. If 2 arguments are
416 // passed, acts as a getter. If 3 arguments are passed, acts
419 // id or reference to node to get/set style for
421 // the style property to set in DOM-accessor format
422 // ("borderWidth", not "border-width") or an object with key/value
423 // pairs suitable for setting each property.
425 // If passed, sets value on the node for style, handling
426 // cross-browser concerns.
428 // Passing only an ID or node returns the computed style object of
430 // | dojo.style("thinger");
432 // Passing a node and a style property returns the current
433 // normalized, computed value for that property:
434 // | dojo.style("thinger", "opacity"); // 1 by default
437 // Passing a node, a style property, and a value changes the
438 // current display of the node and returns the new computed value
439 // | dojo.style("thinger", "opacity", 0.5); // == 0.5
442 // Passing a node, an object-style style property sets each of the values in turn and returns the computed style object of the node:
443 // | dojo.style("thinger", {
445 // | "border": "3px solid black",
450 // When the CSS style property is hyphenated, the JavaScript property is camelCased.
451 // font-size becomes fontSize, and so on.
452 // | dojo.style("thinger",{
453 // | fontSize:"14pt",
454 // | letterSpacing:"1.2em"
458 // dojo.NodeList implements .style() using the same syntax, omitting the "node" parameter, calling
459 // dojo.style() on every element of the list. See: dojo.query and dojo.NodeList
460 // | dojo.query(".someClassName").style("visibility","hidden");
462 // | dojo.query("#baz > div").style({
467 var n = d.byId(node), args = arguments.length, op = (style=="opacity");
468 style = _floatAliases[style] || style;
470 return op ? d._setOpacity(n, value) : n.style[style] = value; /*Number*/
473 return d._getOpacity(n);
476 if(args == 2 && !d.isString(style)){
478 d.style(node, x, style[x]);
482 return (args == 1) ? s : _toStyleValue(n, style, s[style]); /* CSS2Properties||String||Number */
485 // =============================
487 // =============================
489 dojo._getPadExtents = function(/*DomNode*/n, /*Object*/computedStyle){
491 // Returns object with special values specifically useful for node
494 // * l/t = left/top padding (respectively)
495 // * w = the total of the left and right padding
496 // * h = the total of the top and bottom padding
498 // If 'node' has position, l/t forms the origin for child nodes.
499 // The w/h are used for calculating boxes.
500 // Normally application code will not need to invoke this
501 // directly, and will use the ...box... functions instead.
503 s = computedStyle||gcs(n),
504 l = px(n, s.paddingLeft),
505 t = px(n, s.paddingTop);
509 w: l+px(n, s.paddingRight),
510 h: t+px(n, s.paddingBottom)
514 dojo._getBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
516 // returns an object with properties useful for noting the border
519 // * l/t = the sum of left/top border (respectively)
520 // * w = the sum of the left and right border
521 // * h = the sum of the top and bottom border
523 // The w/h are used for calculating boxes.
524 // Normally application code will not need to invoke this
525 // directly, and will use the ...box... functions instead.
528 s = computedStyle||gcs(n),
529 bl = (s.borderLeftStyle != ne ? px(n, s.borderLeftWidth) : 0),
530 bt = (s.borderTopStyle != ne ? px(n, s.borderTopWidth) : 0);
534 w: bl + (s.borderRightStyle!=ne ? px(n, s.borderRightWidth) : 0),
535 h: bt + (s.borderBottomStyle!=ne ? px(n, s.borderBottomWidth) : 0)
539 dojo._getPadBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
541 // returns object with properties useful for box fitting with
542 // regards to padding.
544 // * l/t = the sum of left/top padding and left/top border (respectively)
545 // * w = the sum of the left and right padding and border
546 // * h = the sum of the top and bottom padding and border
548 // The w/h are used for calculating boxes.
549 // Normally application code will not need to invoke this
550 // directly, and will use the ...box... functions instead.
552 s = computedStyle||gcs(n),
553 p = d._getPadExtents(n, s),
554 b = d._getBorderExtents(n, s);
563 dojo._getMarginExtents = function(n, computedStyle){
565 // returns object with properties useful for box fitting with
566 // regards to box margins (i.e., the outer-box).
568 // * l/t = marginLeft, marginTop, respectively
569 // * w = total width, margin inclusive
570 // * h = total height, margin inclusive
572 // The w/h are used for calculating boxes.
573 // Normally application code will not need to invoke this
574 // directly, and will use the ...box... functions instead.
576 s = computedStyle||gcs(n),
577 l = px(n, s.marginLeft),
578 t = px(n, s.marginTop),
579 r = px(n, s.marginRight),
580 b = px(n, s.marginBottom);
581 if(d.isSafari && (s.position != "absolute")){
582 // FIXME: Safari's version of the computed right margin
583 // is the space between our right edge and the right edge
584 // of our offsetParent.
585 // What we are looking for is the actual margin value as
586 // determined by CSS.
587 // Hack solution is to assume left/right margins are the same.
598 // Box getters work in any box context because offsetWidth/clientWidth
599 // are invariant wrt box context
601 // They do *not* work for display: inline objects that have padding styles
602 // because the user agent ignores padding (it's bogus styling in any case)
604 // Be careful with IMGs because they are inline or block depending on
605 // browser and browser mode.
607 // Although it would be easier to read, there are not separate versions of
608 // _getMarginBox for each browser because:
609 // 1. the branching is not expensive
610 // 2. factoring the shared code wastes cycles (function call overhead)
611 // 3. duplicating the shared code wastes bytes
613 dojo._getMarginBox = function(/*DomNode*/node, /*Object*/computedStyle){
615 // returns an object that encodes the width, height, left and top
616 // positions of the node's margin box.
617 var s = computedStyle||gcs(node), me = d._getMarginExtents(node, s);
618 var l = node.offsetLeft - me.l, t = node.offsetTop - me.t;
621 // If offsetParent has a computed overflow != visible, the offsetLeft is decreased
622 // by the parent's border.
623 // We don't want to compute the parent's style, so instead we examine node's
624 // computed left/top which is more stable.
625 var sl = parseFloat(s.left), st = parseFloat(s.top);
626 if(!isNaN(sl) && !isNaN(st)){
629 // If child's computed left/top are not parseable as a number (e.g. "auto"), we
630 // have no choice but to examine the parent's computed style.
631 var p = node.parentNode;
634 if(pcs.overflow != "visible"){
635 var be = d._getBorderExtents(p, pcs);
636 l += be.l, t += be.t;
641 // On Opera, offsetLeft includes the parent's border
642 var p = node.parentNode;
644 var be = d._getBorderExtents(p);
645 l -= be.l, t -= be.t;
651 w: node.offsetWidth + me.w,
652 h: node.offsetHeight + me.h
656 dojo._getContentBox = function(node, computedStyle){
658 // Returns an object that encodes the width, height, left and top
659 // positions of the node's content box, irrespective of the
660 // current box model.
662 // clientWidth/Height are important since the automatically account for scrollbars
663 // fallback to offsetWidth/Height for special cases (see #3378)
664 var s=computedStyle||gcs(node), pe=d._getPadExtents(node, s), be=d._getBorderExtents(node, s), w=node.clientWidth, h;
666 w=node.offsetWidth, h=node.offsetHeight;
668 h=node.clientHeight, be.w = be.h = 0;
670 // On Opera, offsetLeft includes the parent's border
671 if(d.isOpera){ pe.l += be.l; pe.t += be.t; };
680 dojo._getBorderBox = function(node, computedStyle){
681 var s=computedStyle||gcs(node), pe=d._getPadExtents(node, s), cb=d._getContentBox(node, s);
690 // Box setters depend on box context because interpretation of width/height styles
691 // vary wrt box context.
693 // The value of dojo.boxModel is used to determine box context.
694 // dojo.boxModel can be set directly to change behavior.
696 // Beware of display: inline objects that have padding styles
697 // because the user agent ignores padding (it's a bogus setup anyway)
699 // Be careful with IMGs because they are inline or block depending on
700 // browser and browser mode.
702 // Elements other than DIV may have special quirks, like built-in
703 // margins or padding, or values not detectable via computedStyle.
704 // In particular, margins on TABLE do not seems to appear
705 // at all in computedStyle on Mozilla.
707 dojo._setBox = function(/*DomNode*/node, /*Number?*/l, /*Number?*/t, /*Number?*/w, /*Number?*/h, /*String?*/u){
709 // sets width/height/left/top in the current (native) box-model
710 // dimentions. Uses the unit passed in u.
711 // node: DOM Node reference. Id string not supported for performance reasons.
712 // l: optional. left offset from parent.
713 // t: optional. top offset from parent.
714 // w: optional. width in current box model.
715 // h: optional. width in current box model.
716 // u: optional. unit measure to use for other measures. Defaults to "px".
719 if(!isNaN(l)){ s.left = l+u; }
720 if(!isNaN(t)){ s.top = t+u; }
721 if(w>=0){ s.width = w+u; }
722 if(h>=0){ s.height = h+u; }
725 dojo._usesBorderBox = function(/*DomNode*/node){
727 // True if the node uses border-box layout.
729 // We could test the computed style of node to see if a particular box
730 // has been specified, but there are details and we choose not to bother.
731 var n = node.tagName;
732 // For whatever reason, TABLE and BUTTON are always border-box by default.
733 // If you have assigned a different box to either one via CSS then
734 // box functions will break.
735 return d.boxModel=="border-box" || n=="TABLE" || n=="BUTTON"; // boolean
738 dojo._setContentSize = function(/*DomNode*/node, /*Number*/widthPx, /*Number*/heightPx, /*Object*/computedStyle){
740 // Sets the size of the node's contents, irrespective of margins,
741 // padding, or borders.
742 if(d._usesBorderBox(node)){
743 var pb = d._getPadBorderExtents(node, computedStyle);
744 if(widthPx >= 0){ widthPx += pb.w; }
745 if(heightPx >= 0){ heightPx += pb.h; }
747 d._setBox(node, NaN, NaN, widthPx, heightPx);
750 dojo._setMarginBox = function(/*DomNode*/node, /*Number?*/leftPx, /*Number?*/topPx,
751 /*Number?*/widthPx, /*Number?*/heightPx,
752 /*Object*/computedStyle){
754 // sets the size of the node's margin box and placement
755 // (left/top), irrespective of box model. Think of it as a
756 // passthrough to dojo._setBox that handles box-model vagaries for
759 var s = computedStyle||gcs(node);
760 // Some elements have special padding, margin, and box-model settings.
761 // To use box functions you may need to set padding, margin explicitly.
762 // Controlling box-model is harder, in a pinch you might set dojo.boxModel.
763 var bb=d._usesBorderBox(node),
764 pb=bb ? _nilExtents : d._getPadBorderExtents(node, s),
765 mb=d._getMarginExtents(node, s);
766 if(widthPx>=0){ widthPx = Math.max(widthPx - pb.w - mb.w, 0); }
767 if(heightPx>=0){ heightPx = Math.max(heightPx - pb.h - mb.h, 0); }
768 d._setBox(node, leftPx, topPx, widthPx, heightPx);
771 var _nilExtents = { l:0, t:0, w:0, h:0 };
775 dojo.marginBox = function(/*DomNode|String*/node, /*Object?*/box){
777 // Getter/setter for the margin-box of node.
779 // Returns an object in the expected format of box (regardless
780 // if box is passed). The object might look like:
781 // `{ l: 50, t: 200, w: 300: h: 150 }`
782 // for a node offset from its parent 50px to the left, 200px from
783 // the top with a margin width of 300px and a margin-height of
786 // id or reference to DOM Node to get/set box for
788 // If passed, denotes that dojo.marginBox() should
789 // update/set the margin box for node. Box is an object in the
790 // above format. All properties are optional if passed.
791 var n=d.byId(node), s=gcs(n), b=box;
792 return !b ? d._getMarginBox(n, s) : d._setMarginBox(n, b.l, b.t, b.w, b.h, s); // Object
795 dojo.contentBox = function(/*DomNode|String*/node, /*Object?*/box){
797 // Getter/setter for the content-box of node.
799 // Returns an object in the expected format of box (regardless if box is passed).
800 // The object might look like:
801 // `{ l: 50, t: 200, w: 300: h: 150 }`
802 // for a node offset from its parent 50px to the left, 200px from
803 // the top with a content width of 300px and a content-height of
804 // 150px. Note that the content box may have a much larger border
805 // or margin box, depending on the box model currently in use and
806 // CSS values set/inherited for node.
808 // id or reference to DOM Node to get/set box for
810 // If passed, denotes that dojo.contentBox() should
811 // update/set the content box for node. Box is an object in the
812 // above format. All properties are optional if passed.
813 var n=dojo.byId(node), s=gcs(n), b=box;
814 return !b ? d._getContentBox(n, s) : d._setContentSize(n, b.w, b.h, s); // Object
817 // =============================
819 // =============================
821 var _sumAncestorProperties = function(node, prop){
822 if(!(node = (node||0).parentNode)){return 0};
823 var val, retVal = 0, _b = d.body();
824 while(node && node.style){
825 if(gcs(node).position == "fixed"){
831 // opera and khtml #body & #html has the same values, we only
833 if(node == _b){ break; }
835 node = node.parentNode;
837 return retVal; // integer
840 dojo._docScroll = function(){
844 de = d.doc.documentElement;
846 y: (_w.pageYOffset || de.scrollTop || _b.scrollTop || 0),
847 x: (_w.pageXOffset || d._fixIeBiDiScrollLeft(de.scrollLeft) || _b.scrollLeft || 0)
851 dojo._isBodyLtr = function(){
852 //FIXME: could check html and body tags directly instead of computed style? need to ignore case, accept empty values
853 return !("_bodyLtr" in d) ?
854 d._bodyLtr = gcs(d.body()).direction == "ltr" :
855 d._bodyLtr; // Boolean
858 dojo._getIeDocumentElementOffset = function(){
860 // The following values in IE contain an offset:
863 // node.getBoundingClientRect().left
864 // node.getBoundingClientRect().top
865 // But other position related values do not contain this offset, such as
866 // node.offsetLeft, node.offsetTop, node.style.left and node.style.top.
867 // The offset is always (2, 2) in LTR direction. When the body is in RTL
868 // direction, the offset counts the width of left scroll bar's width.
869 // This function computes the actual offset.
871 //NOTE: assumes we're being called in an IE browser
873 var de = d.doc.documentElement;
874 //FIXME: use this instead? var de = d.compatMode == "BackCompat" ? d.body : d.documentElement;
876 return (d.isIE >= 7) ?
877 {x: de.getBoundingClientRect().left, y: de.getBoundingClientRect().top}
880 {x: d._isBodyLtr() || window.parent == window ?
881 de.clientLeft : de.offsetWidth - de.clientWidth - de.clientLeft,
882 y: de.clientTop}; // Object
885 dojo._fixIeBiDiScrollLeft = function(/*Integer*/ scrollLeft){
886 // In RTL direction, scrollLeft should be a negative value, but IE
887 // returns a positive one. All codes using documentElement.scrollLeft
888 // must call this function to fix this error, otherwise the position
889 // will offset to right when there is a horizontal scrollbar.
891 if(d.isIE && !dojo._isBodyLtr()){
892 var de = dd.compatMode == "BackCompat" ? dd.body : dd.documentElement;
893 return scrollLeft + de.clientWidth - de.scrollWidth; // Integer
895 return scrollLeft; // Integer
898 dojo._abs = function(/*DomNode*/node, /*Boolean?*/includeScroll){
900 // Gets the position of the passed element relative to
901 // the viewport (if includeScroll==false), or relative to the
902 // document root (if includeScroll==true).
904 // Returns an object of the form:
905 // { x: 100, y: 300 }
906 // if includeScroll is passed, the x and y values will include any
907 // document offsets that may affect the position relative to the
910 // FIXME: need to decide in the brave-new-world if we're going to be
911 // margin-box or border-box.
912 var ownerDocument = node.ownerDocument;
918 // targetBoxType == "border-box"
920 if(d.isIE || (d.isFF >= 3)){
921 var client = node.getBoundingClientRect();
922 var offset = (d.isIE) ? d._getIeDocumentElementOffset() : { x: 0, y: 0};
923 ret.x = client.left - offset.x;
924 ret.y = client.top - offset.y;
925 }else if(ownerDocument["getBoxObjectFor"]){
927 var bo = ownerDocument.getBoxObjectFor(node),
928 b = d._getBorderExtents(node);
929 ret.x = bo.x - b.l - _sumAncestorProperties(node, "scrollLeft");
930 ret.y = bo.y - b.t - _sumAncestorProperties(node, "scrollTop");
932 if(node["offsetParent"]){
934 // in Safari, if the node is an absolutely positioned child of
935 // the body and the body has a margin the offset of the child
936 // and the body contain the body's margins, so we need to end
938 // FIXME: getting contrary results to the above in latest WebKit.
940 //(node.style.getPropertyValue("position") == "absolute") &&
941 (gcs(node).position == "absolute") &&
942 (node.parentNode == db)){
945 endNode = db.parentNode;
947 if(node.parentNode != db){
949 if(d.isOpera){ nd = db; }
950 ret.x -= _sumAncestorProperties(nd, "scrollLeft");
951 ret.y -= _sumAncestorProperties(nd, "scrollTop");
955 var n = curnode.offsetLeft;
956 //FIXME: ugly hack to workaround the submenu in
957 //popupmenu2 does not shown up correctly in opera.
958 //Someone have a better workaround?
959 if(!d.isOpera || n > 0){
960 ret.x += isNaN(n) ? 0 : n;
962 var t = curnode.offsetTop;
963 ret.y += isNaN(t) ? 0 : t;
964 if(d.isSafari && curnode != node){
965 var cs = gcs(curnode);
966 ret.x += px(curnode, cs.borderLeftWidth);
967 ret.y += px(curnode, cs.borderTopWidth);
969 curnode = curnode.offsetParent;
970 }while((curnode != endNode) && curnode);
971 }else if(node.x && node.y){
972 ret.x += isNaN(node.x) ? 0 : node.x;
973 ret.y += isNaN(node.y) ? 0 : node.y;
976 // account for document scrolling
977 // if offsetParent is used, ret value already includes scroll position
978 // so we may have to actually remove that value if !includeScroll
980 var scroll = d._docScroll();
985 return ret; // object
988 // FIXME: need a setter for coords or a moveTo!!
989 dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll){
991 // Returns an object that measures margin box width/height and
992 // absolute positioning data from dojo._abs().
995 // Returns an object that measures margin box width/height and
996 // absolute positioning data from dojo._abs().
997 // Return value will be in the form:
998 // `{ l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 }`
999 // Does not act as a setter. If includeScroll is passed, the x and
1000 // y params are affected as one would expect in dojo._abs().
1001 var n=d.byId(node), s=gcs(n), mb=d._getMarginBox(n, s);
1002 var abs = d._abs(n, includeScroll);
1008 // =============================
1009 // Element attribute Functions
1010 // =============================
1012 var _fixAttrName = function(/*String*/name){
1013 switch(name.toLowerCase()){
1015 // Internet Explorer will only set or remove tabindex
1016 // if it is spelled "tabIndex"
1017 // console.debug((dojo.isIE && dojo.isIE < 8)? "tabIndex" : "tabindex");
1018 return (d.isIE && d.isIE < 8) ? "tabIndex" : "tabindex";
1024 // non-deprecated HTML4 attributes with default values
1025 // http://www.w3.org/TR/html401/index/attributes.html
1026 // FF and Safari will return the default values if you
1027 // access the attributes via a property but not
1028 // via getAttribute()
1032 frameborder: "frameborder",
1035 scrolling: "scrolling",
1039 valuetype: "valueType"
1042 dojo.hasAttr = function(/*DomNode|String*/node, /*String*/name){
1044 // Returns true if the requested attribute is specified on the
1045 // given element, and false otherwise.
1047 // id or reference to the element to check
1049 // the name of the attribute
1051 // true if the requested attribute is specified on the
1052 // given element, and false otherwise
1053 var attr = d.byId(node).getAttributeNode(_fixAttrName(name));
1054 return attr ? attr.specified : false; // Boolean
1062 var _attrId = dojo._scopeName + "attrid";
1064 dojo.attr = function(/*DomNode|String*/node, /*String|Object*/name, /*String?*/value){
1066 // Gets or sets an attribute on an HTML element.
1068 // Handles normalized getting and setting of attributes on DOM
1069 // Nodes. If 2 arguments are passed, and a the second argumnt is a
1070 // string, acts as a getter.
1072 // If a third argument is passed, or if the second argumnt is a
1073 // map of attributes, acts as a setter.
1075 // When passing functions as values, note that they will not be
1076 // directly assigned to slots on the node, but rather the default
1077 // behavior will be removed and the new behavior will be added
1078 // using `dojo.connect()`, meaning that event handler properties
1079 // will be normalized and that some caveats with regards to
1080 // non-standard behaviors for onsubmit apply. Namely that you
1081 // should cancel form submission using `dojo.stopEvent()` on the
1082 // passed event object instead of returning a boolean value from
1083 // the handler itself.
1085 // id or reference to the element to get or set the attribute on
1087 // the name of the attribute to get or set.
1089 // The value to set for the attribute
1091 // when used as a getter, the value of the requested attribute
1092 // or null if that attribute does not have a specified or
1095 // when user as a setter, undefined
1097 // | // get the current value of the "foo" attribute on a node
1098 // | dojo.attr(dojo.byId("nodeId"), "foo");
1100 // | // we can just pass the id:
1101 // | dojo.attr("nodeId", "foo");
1103 // | // use attr() to set the tab index
1104 // | dojo.attr("nodeId", "tabindex", 3);
1106 // | // set multiple values at once, including event handlers:
1107 // | dojo.attr("formId", {
1109 // | "tabindex": -1,
1110 // | "method": "POST",
1111 // | "onsubmit": function(e){
1112 // | // stop submitting the form. Note that the IE behavior
1113 // | // of returning true or false will have no effect here
1114 // | // since our handler is connect()ed to the built-in
1115 // | // onsubmit behavior and so we need to use
1116 // | // dojo.stopEvent() to ensure that the submission
1117 // | // doesn't proceed.
1118 // | dojo.stopEvent(e);
1120 // | // submit the form with Ajax
1121 // | dojo.xhrPost({ form: "formId" });
1125 var args = arguments.length;
1126 if(args == 2 && !d.isString(name)){
1127 for(var x in name){ d.attr(node, x, name[x]); }
1130 node = d.byId(node);
1131 name = _fixAttrName(name);
1133 if(d.isFunction(value)){
1134 // clobber if we can
1135 var attrId = d.attr(node, _attrId);
1138 d.attr(node, _attrId, attrId);
1140 if(!_evtHdlrMap[attrId]){
1141 _evtHdlrMap[attrId] = {};
1143 var h = _evtHdlrMap[attrId][name];
1152 // ensure that event objects are normalized, etc.
1153 _evtHdlrMap[attrId][name] = d.connect(node, name, value);
1155 }else if(typeof value == "boolean"){ // e.g. onsubmit, disabled
1156 // if a function, we should normalize the event object here!!!
1159 node.setAttribute(name, value);
1163 // should we access this attribute via a property or
1164 // via getAttribute()?
1165 var prop = _attrProps[name.toLowerCase()];
1169 var value = node[name];
1170 return (typeof value == 'boolean' || typeof value == 'function') ? value : (d.hasAttr(node, name) ? node.getAttribute(name) : null);
1175 dojo.removeAttr = function(/*DomNode|String*/node, /*String*/name){
1177 // Removes an attribute from an HTML element.
1179 // id or reference to the element to remove the attribute from
1181 // the name of the attribute to remove
1182 d.byId(node).removeAttribute(_fixAttrName(name));
1186 // =============================
1187 // (CSS) Class Functions
1188 // =============================
1190 dojo.hasClass = function(/*DomNode|String*/node, /*String*/classStr){
1192 // Returns whether or not the specified classes are a portion of the
1193 // class list currently applied to the node.
1194 return ((" "+dojo.byId(node).className+" ").indexOf(" "+classStr+" ") >= 0); // Boolean
1197 dojo.addClass = function(/*DomNode|String*/node, /*String*/classStr){
1199 // Adds the specified classes to the end of the class list on the
1201 node = dojo.byId(node);
1202 var cls = node.className;
1203 if((" "+cls+" ").indexOf(" "+classStr+" ") < 0){
1204 node.className = cls + (cls ? ' ' : '') + classStr;
1208 dojo.removeClass = function(/*DomNode|String*/node, /*String*/classStr){
1209 // summary: Removes the specified classes from node.
1210 node = dojo.byId(node);
1211 var t = dojo.trim((" " + node.className + " ").replace(" " + classStr + " ", " "));
1212 if(node.className != t){ node.className = t; }
1215 dojo.toggleClass = function(/*DomNode|String*/node, /*String*/classStr, /*Boolean?*/condition){
1217 // Adds a class to node if not present, or removes if present.
1218 // Pass a boolean condition if you want to explicitly add or remove.
1220 // If passed, true means to add the class, false means to remove.
1221 if(condition === undefined){
1222 condition = !dojo.hasClass(node, classStr);
1224 dojo[condition ? "addClass" : "removeClass"](node, classStr);