summaryrefslogtreecommitdiff
path: root/lib/dijit/place.js.uncompressed.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dijit/place.js.uncompressed.js')
-rw-r--r--lib/dijit/place.js.uncompressed.js369
1 files changed, 369 insertions, 0 deletions
diff --git a/lib/dijit/place.js.uncompressed.js b/lib/dijit/place.js.uncompressed.js
new file mode 100644
index 000000000..5283da478
--- /dev/null
+++ b/lib/dijit/place.js.uncompressed.js
@@ -0,0 +1,369 @@
+define("dijit/place", [
+ "dojo/_base/array", // array.forEach array.map array.some
+ "dojo/dom-geometry", // domGeometry.getMarginBox domGeometry.position
+ "dojo/dom-style", // domStyle.getComputedStyle
+ "dojo/_base/kernel", // kernel.deprecated
+ "dojo/_base/window", // win.body
+ "dojo/window", // winUtils.getBox
+ "." // dijit (defining dijit.place to match API doc)
+], function(array, domGeometry, domStyle, kernel, win, winUtils, dijit){
+
+ // module:
+ // dijit/place
+ // summary:
+ // Code to place a popup relative to another node
+
+
+ function _place(/*DomNode*/ node, choices, layoutNode, 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 = winUtils.getBox();
+
+ // This won't work if the node is inside a <div style="position: relative">,
+ // so reattach it to win.doc.body. (Otherwise, the positioning will be wrong
+ // and also it might get cutoff)
+ if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){
+ win.body().appendChild(node);
+ }
+
+ var best = null;
+ array.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: {
+ 'L': view.l + view.w - pos.x,
+ 'R': pos.x - view.l,
+ 'M': view.w
+ }[corner.charAt(1)],
+ h: {
+ 'T': view.t + view.h - pos.y,
+ 'B': pos.y - view.t,
+ 'M': view.h
+ }[corner.charAt(0)]
+ };
+
+ // 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;
+ if(style.display == "none"){
+ style.visibility = "hidden";
+ style.display = "";
+ }
+ var mb = domGeometry. getMarginBox(node);
+ style.display = oldDisplay;
+ style.visibility = oldVis;
+
+ // coordinates and size of node with specified corner placed at pos,
+ // and clipped by viewport
+ var
+ startXpos = {
+ 'L': pos.x,
+ 'R': pos.x - mb.w,
+ 'M': Math.max(view.l, Math.min(view.l + view.w, pos.x + (mb.w >> 1)) - mb.w) // M orientation is more flexible
+ }[corner.charAt(1)],
+ startYpos = {
+ 'T': pos.y,
+ 'B': pos.y - mb.h,
+ 'M': Math.max(view.t, Math.min(view.t + view.h, pos.y + (mb.h >> 1)) - mb.h)
+ }[corner.charAt(0)],
+ startX = Math.max(view.l, startXpos),
+ startY = Math.max(view.t, startYpos),
+ endX = Math.min(view.l + view.w, startXpos + mb.w),
+ endY = Math.min(view.t + view.h, startYpos + mb.h),
+ 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 = domGeometry.isBodyLtr(),
+ s = node.style;
+ s.top = best.y + "px";
+ s[l ? "left" : "right"] = (l ? best.x : view.w - best.x - best.w) + "px";
+ s[l ? "right" : "left"] = "auto"; // needed for FF or else tooltip goes to far left
+
+ return best;
+ }
+
+ /*=====
+ dijit.place.__Position = function(){
+ // x: Integer
+ // horizontal coordinate in pixels, relative to document body
+ // y: Integer
+ // vertical coordinate in pixels, relative to document body
+
+ this.x = x;
+ this.y = y;
+ };
+ =====*/
+
+ /*=====
+ dijit.place.__Rectangle = function(){
+ // x: Integer
+ // horizontal offset in pixels, relative to document body
+ // y: Integer
+ // vertical offset in pixels, relative to document body
+ // w: Integer
+ // width in pixels. Can also be specified as "width" for backwards-compatibility.
+ // h: Integer
+ // height in pixels. Can also be specified as "height" from backwards-compatibility.
+
+ this.x = x;
+ this.y = y;
+ this.w = w;
+ this.h = h;
+ };
+ =====*/
+
+ return (dijit.place = {
+ // summary:
+ // Code to place a DOMNode relative to another DOMNode.
+ // Load using require(["dijit/place"], function(place){ ... }).
+
+ at: function(node, pos, corners, 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.
+ // node: DOMNode
+ // The node to position
+ // pos: dijit.place.__Position
+ // Object like {x: 10, y: 20}
+ // corners: String[]
+ // 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: dijit.place.__Position?
+ // optional param to 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).
+ // | place(node, {x: 10, y: 20}, ["TR", "BL"])
+ var choices = array.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 _place(node, choices);
+ },
+
+ around: function(
+ /*DomNode*/ node,
+ /*DomNode || dijit.place.__Rectangle*/ anchor,
+ /*String[]*/ positions,
+ /*Boolean*/ leftToRight,
+ /*Function?*/ layoutNode){
+
+ // summary:
+ // Position node adjacent or kitty-corner to anchor
+ // 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.
+ //
+ // anchor:
+ // Either a DOMNode or a __Rectangle (object with x, y, width, height).
+ //
+ // positions:
+ // Ordered list of positions to try matching up.
+ // * before: places drop down to the left of the anchor node/widget, or to the right in the case
+ // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
+ // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
+ // * after: places drop down to the right of the anchor node/widget, or to the left in the case
+ // of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
+ // with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
+ // * before-centered: centers drop down to the left of the anchor node/widget, or to the right
+ // in the case of RTL scripts like Hebrew and Arabic
+ // * after-centered: centers drop down to the right of the anchor node/widget, or to the left
+ // in the case of RTL scripts like Hebrew and Arabic
+ // * above-centered: drop down is centered above anchor node
+ // * above: drop down goes above anchor node, left sides aligned
+ // * above-alt: drop down goes above anchor node, right sides aligned
+ // * below-centered: drop down is centered above anchor node
+ // * below: drop down goes below anchor node
+ // * below-alt: drop down goes below anchor node, right sides aligned
+ //
+ // 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.
+ //
+ // leftToRight:
+ // True if widget is LTR, false if widget is RTL. Affects the behavior of "above" and "below"
+ // positions slightly.
+ //
+ // example:
+ // | placeAroundNode(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)
+ //
+
+ // if around is a DOMNode (or DOMNode id), convert to coordinates
+ var aroundNodePos = (typeof anchor == "string" || "offsetWidth" in anchor)
+ ? domGeometry.position(anchor, true)
+ : anchor;
+
+ // Adjust anchor positioning for the case that a parent node has overflw hidden, therefore cuasing the anchor not to be completely visible
+ if(anchor.parentNode){
+ var parent = anchor.parentNode;
+ while(parent && parent.nodeType == 1 && parent.nodeName != "BODY"){ //ignoring the body will help performance
+ var parentPos = domGeometry.position(parent, true);
+ var parentStyleOverflow = domStyle.getComputedStyle(parent).overflow;
+ if(parentStyleOverflow == "hidden" || parentStyleOverflow == "auto" || parentStyleOverflow == "scroll"){
+ var bottomYCoord = Math.min(aroundNodePos.y + aroundNodePos.h, parentPos.y + parentPos.h);
+ var rightXCoord = Math.min(aroundNodePos.x + aroundNodePos.w, parentPos.x + parentPos.w);
+ aroundNodePos.x = Math.max(aroundNodePos.x, parentPos.x);
+ aroundNodePos.y = Math.max(aroundNodePos.y, parentPos.y);
+ aroundNodePos.h = bottomYCoord - aroundNodePos.y;
+ aroundNodePos.w = rightXCoord - aroundNodePos.x;
+ }
+ parent = parent.parentNode;
+ }
+ }
+
+ var x = aroundNodePos.x,
+ y = aroundNodePos.y,
+ width = "w" in aroundNodePos ? aroundNodePos.w : (aroundNodePos.w = aroundNodePos.width),
+ height = "h" in aroundNodePos ? aroundNodePos.h : (kernel.deprecated("place.around: dijit.place.__Rectangle: { x:"+x+", y:"+y+", height:"+aroundNodePos.height+", width:"+width+" } has been deprecated. Please use { x:"+x+", y:"+y+", h:"+aroundNodePos.height+", w:"+width+" }", "", "2.0"), aroundNodePos.h = aroundNodePos.height);
+
+ // Convert positions arguments into choices argument for _place()
+ var choices = [];
+ function push(aroundCorner, corner){
+ choices.push({
+ aroundCorner: aroundCorner,
+ corner: corner,
+ pos: {
+ x: {
+ 'L': x,
+ 'R': x + width,
+ 'M': x + (width >> 1)
+ }[aroundCorner.charAt(1)],
+ y: {
+ 'T': y,
+ 'B': y + height,
+ 'M': y + (height >> 1)
+ }[aroundCorner.charAt(0)]
+ }
+ })
+ }
+ array.forEach(positions, function(pos){
+ var ltr = leftToRight;
+ switch(pos){
+ case "above-centered":
+ push("TM", "BM");
+ break;
+ case "below-centered":
+ push("BM", "TM");
+ break;
+ case "after-centered":
+ ltr = !ltr;
+ // fall through
+ case "before-centered":
+ push(ltr ? "ML" : "MR", ltr ? "MR" : "ML");
+ break;
+ case "after":
+ ltr = !ltr;
+ // fall through
+ case "before":
+ push(ltr ? "TL" : "TR", ltr ? "TR" : "TL");
+ push(ltr ? "BL" : "BR", ltr ? "BR" : "BL");
+ break;
+ case "below-alt":
+ ltr = !ltr;
+ // fall through
+ case "below":
+ // first try to align left borders, next try to align right borders (or reverse for RTL mode)
+ push(ltr ? "BL" : "BR", ltr ? "TL" : "TR");
+ push(ltr ? "BR" : "BL", ltr ? "TR" : "TL");
+ break;
+ case "above-alt":
+ ltr = !ltr;
+ // fall through
+ case "above":
+ // first try to align left borders, next try to align right borders (or reverse for RTL mode)
+ push(ltr ? "TL" : "TR", ltr ? "BL" : "BR");
+ push(ltr ? "TR" : "TL", ltr ? "BR" : "BL");
+ break;
+ default:
+ // To assist dijit/_base/place, accept arguments of type {aroundCorner: "BL", corner: "TL"}.
+ // Not meant to be used directly.
+ push(pos.aroundCorner, pos.corner);
+ }
+ });
+
+ var position = _place(node, choices, layoutNode, {w: width, h: height});
+ position.aroundNodePos = aroundNodePos;
+
+ return position;
+ }
+ });
+});