/ *
Copyright ( c ) 2004 - 2011 , The Dojo Foundation All Rights Reserved .
Available via Academic Free License >= 2.1 OR the modified BSD license .
see : http : //dojotoolkit.org/license for details
* /
if ( ! dojo . _hasResource [ "dijit._base.place" ] ) { //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo . _hasResource [ "dijit._base.place" ] = true ;
dojo . provide ( "dijit._base.place" ) ;
dojo . require ( "dojo.window" ) ;
dojo . require ( "dojo.AdapterRegistry" ) ;
dijit . getViewport = function ( ) {
// summary:
// Returns the dimensions and scroll position of the viewable area of a browser window
return dojo . window . getBox ( ) ;
} ;
/ * = = = = =
dijit . _ _Position = function ( ) {
// x: Integer
// horizontal coordinate in pixels, relative to document body
// y: Integer
// vertical coordinate in pixels, relative to document body
thix . x = x ;
this . y = y ;
}
=== == * /
dijit . placeOnScreen = function (
/* DomNode */ node ,
/* dijit.__Position */ pos ,
/* String[] */ corners ,
/* dijit.__Position? */ padding ) {
// summary:
// Positions one of the node's corners at specified position
// such that node is fully visible in viewport.
// description:
// NOTE: node is assumed to be absolutely or relatively positioned.
// pos:
// Object like {x: 10, y: 20}
// corners:
// Array of Strings representing order to try corners in, like ["TR", "BL"].
// Possible values are:
// * "BL" - bottom left
// * "BR" - bottom right
// * "TL" - top left
// * "TR" - top right
// padding:
// set padding to put some buffer around the element you want to position.
// example:
// Try to place node's top right corner at (10,20).
// If that makes node go (partially) off screen, then try placing
// bottom left corner at (10,20).
// | placeOnScreen(node, {x: 10, y: 20}, ["TR", "BL"])
var choices = dojo . map ( corners , function ( corner ) {
var c = { corner : corner , pos : { x : pos . x , y : pos . y } } ;
if ( padding ) {
c . pos . x += corner . charAt ( 1 ) == 'L' ? padding . x : - padding . x ;
c . pos . y += corner . charAt ( 0 ) == 'T' ? padding . y : - padding . y ;
}
return c ;
} ) ;
return dijit . _place ( node , choices ) ;
}
dijit . _place = function ( /*DomNode*/ node , choices , layoutNode , /*Object*/ aroundNodeCoords ) {
// summary:
// Given a list of spots to put node, put it at the first spot where it fits,
// of if it doesn't fit anywhere then the place with the least overflow
// choices: Array
// Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} }
// Above example says to put the top-left corner of the node at (10,20)
// layoutNode: Function(node, aroundNodeCorner, nodeCorner, size)
// for things like tooltip, they are displayed differently (and have different dimensions)
// based on their orientation relative to the parent. This adjusts the popup based on orientation.
// It also passes in the available size for the popup, which is useful for tooltips to
// tell them that their width is limited to a certain amount. layoutNode() may return a value expressing
// how much the popup had to be modified to fit into the available space. This is used to determine
// what the best placement is.
// aroundNodeCoords: Object
// Size of aroundNode, ex: {w: 200, h: 50}
// get {x: 10, y: 10, w: 100, h:100} type obj representing position of
// viewport over document
var view = dojo . window . getBox ( ) ;
// This won't work if the node is inside a <div style="position: relative">,
// so reattach it to dojo.doc.body. (Otherwise, the positioning will be wrong
// and also it might get cutoff)
if ( ! node . parentNode || String ( node . parentNode . tagName ) . toLowerCase ( ) != "body" ) {
dojo . body ( ) . appendChild ( node ) ;
}
var best = null ;
dojo . some ( choices , function ( choice ) {
var corner = choice . corner ;
var pos = choice . pos ;
var overflow = 0 ;
// calculate amount of space available given specified position of node
var spaceAvailable = {
w : corner . charAt ( 1 ) == 'L' ? ( view . l + view . w ) - pos . x : pos . x - view . l ,
h : corner . charAt ( 1 ) == 'T' ? ( view . t + view . h ) - pos . y : pos . y - view . t
} ;
// configure node to be displayed in given position relative to button
// (need to do this in order to get an accurate size for the node, because
// a tooltip's size changes based on position, due to triangle)
if ( layoutNode ) {
var res = layoutNode ( node , choice . aroundCorner , corner , spaceAvailable , aroundNodeCoords ) ;
overflow = typeof res == "undefined" ? 0 : res ;
}
// get node's size
var style = node . style ;
var oldDisplay = style . display ;
var oldVis = style . visibility ;
style . visibility = "hidden" ;
style . display = "" ;
var mb = dojo . marginBox ( node ) ;
style . display = oldDisplay ;
style . visibility = oldVis ;
// coordinates and size of node with specified corner placed at pos,
// and clipped by viewport
var startX = Math . max ( view . l , corner . charAt ( 1 ) == 'L' ? pos . x : ( pos . x - mb . w ) ) ,
startY = Math . max ( view . t , corner . charAt ( 0 ) == 'T' ? pos . y : ( pos . y - mb . h ) ) ,
endX = Math . min ( view . l + view . w , corner . charAt ( 1 ) == 'L' ? ( startX + mb . w ) : pos . x ) ,
endY = Math . min ( view . t + view . h , corner . charAt ( 0 ) == 'T' ? ( startY + mb . h ) : pos . y ) ,
width = endX - startX ,
height = endY - startY ;
overflow += ( mb . w - width ) + ( mb . h - height ) ;
if ( best == null || overflow < best . overflow ) {
best = {
corner : corner ,
aroundCorner : choice . aroundCorner ,
x : startX ,
y : startY ,
w : width ,
h : height ,
overflow : overflow ,
spaceAvailable : spaceAvailable
} ;
}
return ! overflow ;
} ) ;
// In case the best position is not the last one we checked, need to call
// layoutNode() again.
if ( best . overflow && layoutNode ) {
layoutNode ( node , best . aroundCorner , best . corner , best . spaceAvailable , aroundNodeCoords ) ;
}
// And then position the node. Do this last, after the layoutNode() above
// has sized the node, due to browser quirks when the viewport is scrolled
// (specifically that a Tooltip will shrink to fit as though the window was
// scrolled to the left).
//
// In RTL mode, set style.right rather than style.left so in the common case,
// window resizes move the popup along with the aroundNode.
var l = dojo . _isBodyLtr ( ) ,
s = node . style ;
s . top = best . y + "px" ;
s [ l ? "left" : "right" ] = ( l ? best . x : view . w - best . x - best . w ) + "px" ;
return best ;
}
dijit . placeOnScreenAroundNode = function (
/* DomNode */ node ,
/* DomNode */ aroundNode ,
/* Object */ aroundCorners ,
/* Function? */ layoutNode ) {
// summary:
// Position node adjacent or kitty-corner to aroundNode
// such that it's fully visible in viewport.
//
// description:
// Place node such that corner of node touches a corner of
// aroundNode, and that node is fully visible.
//
// aroundCorners:
// Ordered list of pairs of corners to try matching up.
// Each pair of corners is represented as a key/value in the hash,
// where the key corresponds to the aroundNode's corner, and
// the value corresponds to the node's corner:
//
// | { aroundNodeCorner1: nodeCorner1, aroundNodeCorner2: nodeCorner2, ...}
//
// The following strings are used to represent the four corners:
// * "BL" - bottom left
// * "BR" - bottom right
// * "TL" - top left
// * "TR" - top right
//
// layoutNode: Function(node, aroundNodeCorner, nodeCorner)
// For things like tooltip, they are displayed differently (and have different dimensions)
// based on their orientation relative to the parent. This adjusts the popup based on orientation.
//
// example:
// | dijit.placeOnScreenAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'});
// This will try to position node such that node's top-left corner is at the same position
// as the bottom left corner of the aroundNode (ie, put node below
// aroundNode, with left edges aligned). If that fails it will try to put
// the bottom-right corner of node where the top right corner of aroundNode is
// (ie, put node above aroundNode, with right edges aligned)
//
// get coordinates of aroundNode
aroundNode = dojo . byId ( aroundNode ) ;
var aroundNodePos = dojo . position ( aroundNode , true ) ;
// place the node around the calculated rectangle
return dijit . _placeOnScreenAroundRect ( node ,
aroundNodePos . x , aroundNodePos . y , aroundNodePos . w , aroundNodePos . h , // rectangle
aroundCorners , layoutNode ) ;
} ;
/ * = = = = =
dijit . _ _Rectangle = function ( ) {
// x: Integer
// horizontal offset in pixels, relative to document body
// y: Integer
// vertical offset in pixels, relative to document body
// width: Integer
// width in pixels
// height: Integer
// height in pixels
this . x = x ;
this . y = y ;
this . width = width ;
this . height = height ;
}
=== == * /
dijit . placeOnScreenAroundRectangle = function (
/* DomNode */ node ,
/* dijit.__Rectangle */ aroundRect ,
/* Object */ aroundCorners ,
/* Function */ layoutNode ) {
// summary:
// Like dijit.placeOnScreenAroundNode(), except that the "around"
// parameter is an arbitrary rectangle on the screen (x, y, width, height)
// instead of a dom node.
return dijit . _placeOnScreenAroundRect ( node ,
aroundRect . x , aroundRect . y , aroundRect . width , aroundRect . height , // rectangle
aroundCorners , layoutNode ) ;
} ;
dijit . _placeOnScreenAroundRect = function (
/* DomNode */ node ,
/* Number */ x ,
/* Number */ y ,
/* Number */ width ,
/* Number */ height ,
/* Object */ aroundCorners ,
/* Function */ layoutNode ) {
// summary:
// Like dijit.placeOnScreenAroundNode(), except it accepts coordinates
// of a rectangle to place node adjacent to.
// TODO: combine with placeOnScreenAroundRectangle()
// Generate list of possible positions for node
var choices = [ ] ;
for ( var nodeCorner in aroundCorners ) {
choices . push ( {
aroundCorner : nodeCorner ,
corner : aroundCorners [ nodeCorner ] ,
pos : {
x : x + ( nodeCorner . charAt ( 1 ) == 'L' ? 0 : width ) ,
y : y + ( nodeCorner . charAt ( 0 ) == 'T' ? 0 : height )
}
} ) ;
}
return dijit . _place ( node , choices , layoutNode , { w : width , h : height } ) ;
} ;
dijit . placementRegistry = new dojo . AdapterRegistry ( ) ;
dijit . placementRegistry . register ( "node" ,
function ( n , x ) {
return typeof x == "object" &&
typeof x . offsetWidth != "undefined" && typeof x . offsetHeight != "undefined" ;
} ,
dijit . placeOnScreenAroundNode ) ;
dijit . placementRegistry . register ( "rect" ,
function ( n , x ) {
return typeof x == "object" &&
"x" in x && "y" in x && "width" in x && "height" in x ;
} ,
dijit . placeOnScreenAroundRectangle ) ;
dijit . placeOnScreenAroundElement = function (
/* DomNode */ node ,
/* Object */ aroundElement ,
/* Object */ aroundCorners ,
/* Function */ layoutNode ) {
// summary:
// Like dijit.placeOnScreenAroundNode(), except it accepts an arbitrary object
// for the "around" argument and finds a proper processor to place a node.
return dijit . placementRegistry . match . apply ( dijit . placementRegistry , arguments ) ;
} ;
dijit . getPopupAroundAlignment = function ( /*Array*/ position , /*Boolean*/ leftToRight ) {
// summary:
// Transforms the passed array of preferred positions into a format suitable for passing as the aroundCorners argument to dijit.placeOnScreenAroundElement.
//
// position: String[]
// This variable controls the position of the drop down.
// It's an array of strings with the following values:
//
// * before: places drop down to the left of the target node/widget, or to the right in
// the case of RTL scripts like Hebrew and Arabic
// * after: places drop down to the right of the target node/widget, or to the left in
// the case of RTL scripts like Hebrew and Arabic
// * above: drop down goes above target node
// * below: drop down goes below target node
//
// The list is positions is tried, in order, until a position is found where the drop down fits
// within the viewport.
//
// leftToRight: Boolean
// Whether the popup will be displaying in leftToRight mode.
//
var align = { } ;
dojo . forEach ( position , function ( pos ) {
switch ( pos ) {
case "after" :
align [ leftToRight ? "BR" : "BL" ] = leftToRight ? "BL" : "BR" ;
break ;
case "before" :
align [ leftToRight ? "BL" : "BR" ] = leftToRight ? "BR" : "BL" ;
break ;
case "below-alt" :
leftToRight = ! leftToRight ;
// fall through
case "below" :
// first try to align left borders, next try to align right borders (or reverse for RTL mode)
align [ leftToRight ? "BL" : "BR" ] = leftToRight ? "TL" : "TR" ;
align [ leftToRight ? "BR" : "BL" ] = leftToRight ? "TR" : "TL" ;
break ;
case "above-alt" :
leftToRight = ! leftToRight ;
// fall through
case "above" :
default :
// first try to align left borders, next try to align right borders (or reverse for RTL mode)
align [ leftToRight ? "TL" : "TR" ] = leftToRight ? "BL" : "BR" ;
align [ leftToRight ? "TR" : "TL" ] = leftToRight ? "BR" : "BL" ;
break ;
}
} ) ;
return align ;
} ;
}