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