dojo.provide("dijit._base.place"); dojo.require("dojo.AdapterRegistry"); // ported from dojo.html.util dijit.getViewport = function(){ // summary: // Returns the dimensions and scroll position of the viewable area of a browser window var scrollRoot = (dojo.doc.compatMode == 'BackCompat')? dojo.body() : dojo.doc.documentElement; // get scroll position var scroll = dojo._docScroll(); // scrollRoot.scrollTop/Left should work return { w: scrollRoot.clientWidth, h: scrollRoot.clientHeight, l: scroll.x, t: scroll.y }; }; /*===== 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, /* Array */ choices, /* Function */ layoutNode){ // 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) // 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. // get {x: 10, y: 10, w: 100, h:100} type obj representing position of // viewport over document var view = dijit.getViewport(); // This won't work if the node is inside a
, // 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; // 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 tooltips size changes based on position, due to triangle) if(layoutNode){ layoutNode(node, choice.aroundCorner, corner); } // 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 = (corner.charAt(1) == 'L' ? pos.x : Math.max(view.l, pos.x - mb.w)), startY = (corner.charAt(0) == 'T' ? pos.y : Math.max(view.t, pos.y - mb.h)), endX = (corner.charAt(1) == 'L' ? Math.min(view.l + view.w, startX + mb.w) : pos.x), endY = (corner.charAt(0) == 'T' ? Math.min(view.t + view.h, 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 }; } return !overflow; }); node.style.left = best.x + "px"; node.style.top = best.y + "px"; if(best.overflow && layoutNode){ layoutNode(node, best.aroundCorner, best.corner); } 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 oldDisplay = aroundNode.style.display; aroundNode.style.display=""; // #3172: use the slightly tighter border box instead of marginBox var aroundNodeW = aroundNode.offsetWidth; //mb.w; var aroundNodeH = aroundNode.offsetHeight; //mb.h; var aroundNodePos = dojo.coords(aroundNode, true); aroundNode.style.display=oldDisplay; // place the node around the calculated rectangle return dijit._placeOnScreenAroundRect(node, aroundNodePos.x, aroundNodePos.y, aroundNodeW, aroundNodeH, // 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 thix.x = x; this.y = y; thix.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); }; 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); };