]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/widget/widget-position-constrain.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / widget / widget-position-constrain.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('widget-position-constrain', function(Y) {
9
10 /**
11  * Provides constrained xy positioning support for Widgets, through an extension.
12  *
13  * It builds on top of the widget-position module, to provide constrained positioning support.
14  *
15  * @module widget-position-constrain
16  */
17 var CONSTRAIN = "constrain",
18     CONSTRAIN_XYCHANGE = "constrain|xyChange",
19     CONSTRAIN_CHANGE = "constrainChange",
20
21     PREVENT_OVERLAP = "preventOverlap",
22     ALIGN = "align",
23     
24     EMPTY_STR = "",
25
26     BINDUI = "bindUI",
27
28     XY = "xy",
29     X_COORD = "x",
30     Y_COORD = "y",
31
32     Node = Y.Node,
33
34     VIEWPORT_REGION = "viewportRegion",
35     REGION = "region",
36
37     PREVENT_OVERLAP_MAP;
38
39 /**
40  * A widget extension, which can be used to add constrained xy positioning support to the base Widget class,
41  * through the <a href="Base.html#method_build">Base.build</a> method. This extension requires that 
42  * the WidgetPosition extension be added to the Widget (before WidgetPositionConstrain, if part of the same 
43  * extension list passed to Base.build).
44  *
45  * @class WidgetPositionConstrain
46  * @param {Object} User configuration object
47  */
48 function PositionConstrain(config) {
49     if (!this._posNode) {
50         Y.error("WidgetPosition needs to be added to the Widget, before WidgetPositionConstrain is added"); 
51     }
52     Y.after(this._bindUIPosConstrained, this, BINDUI);
53 }
54
55 /**
56  * Static property used to define the default attribute 
57  * configuration introduced by WidgetPositionConstrain.
58  *
59  * @property WidgetPositionConstrain.ATTRS
60  * @type Object
61  * @static
62  */
63 PositionConstrain.ATTRS = {
64
65     /**
66      * @attribute constrain
67      * @type boolean | Node
68      * @default null
69      * @description The node to constrain the widget's bounding box to, when setting xy. Can also be
70      * set to true, to constrain to the viewport.
71      */
72     constrain : {
73         value: null,
74         setter: "_setConstrain"
75     },
76
77     /**
78      * @attribute preventOverlap
79      * @type boolean
80      * @description If set to true, and WidgetPositionAlign is also added to the Widget, 
81      * constrained positioning will attempt to prevent the widget's bounding box from overlapping 
82      * the element to which it has been aligned, by flipping the orientation of the alignment
83      * for corner based alignments  
84      */
85     preventOverlap : {
86         value:false
87     }
88 };
89
90 /**
91  * @property WidgetPositionConstrain._PREVENT_OVERLAP
92  * @static
93  * @protected
94  * @type Object
95  * @description The set of positions for which to prevent
96  * overlap.
97  */
98 PREVENT_OVERLAP_MAP = PositionConstrain._PREVENT_OVERLAP = {
99     x: {
100         "tltr": 1,
101         "blbr": 1,
102         "brbl": 1,
103         "trtl": 1
104     },
105     y : {
106         "trbr": 1,
107         "tlbl": 1,
108         "bltl": 1,
109         "brtr": 1
110     }
111 };
112
113 PositionConstrain.prototype = {
114
115     /**
116      * Calculates the constrained positions for the XY positions provided, using
117      * the provided node argument is passed in. If no node value is passed in, the value of 
118      * the "constrain" attribute is used.
119      * 
120      * @method getConstrainedXY
121      * @param {Array} xy The xy values to constrain
122      * @param {Node | boolean} node Optional. The node to constrain to, or true for the viewport
123      * @return {Array} The constrained xy values
124      */
125     getConstrainedXY : function(xy, node) {
126         node = node || this.get(CONSTRAIN);
127
128         var constrainingRegion = this._getRegion((node === true) ? null : node),
129             nodeRegion = this._posNode.get(REGION);
130
131         return [
132             this._constrain(xy[0], X_COORD, nodeRegion, constrainingRegion),
133             this._constrain(xy[1], Y_COORD, nodeRegion, constrainingRegion)
134         ];
135     },
136
137     /**
138      * Constrains the widget's bounding box to a node (or the viewport). If xy or node are not 
139      * passed in, the current position and the value of "constrain" will be used respectively.
140      * 
141      * The widget's position will be changed to the constrained position.
142      * 
143      * @param {Array} xy Optional. The xy values to constrain
144      * @param {Node | boolean} node Optional. The node to constrain to, or true for the viewport
145      */
146     constrain : function(xy, node) {
147         var currentXY, 
148             constrainedXY,
149             constraint = node || this.get(CONSTRAIN);
150
151         if (constraint) {
152             currentXY = xy || this.get(XY);
153             constrainedXY = this.getConstrainedXY(currentXY, constraint);
154
155             if (constrainedXY[0] !== currentXY[0] || constrainedXY[1] !== currentXY[1]) {
156                 this.set(XY, constrainedXY, { constrained:true });
157             }
158         }
159     },
160
161     /**
162      * The setter implementation for the "constrain" attribute.
163      *
164      * @method _setConstrain
165      * @protected
166      * @param {Node | boolean} val The attribute value 
167      */
168     _setConstrain : function(val) {
169         return (val === true) ? val : Node.one(val);
170     },
171
172     /**
173      * The method which performs the actual constrain calculations for a given axis ("x" or "y") based
174      * on the regions provided.
175      * 
176      * @method _constrain
177      * @protected
178      * 
179      * @param {Number} val The value to constrain
180      * @param {String} axis The axis to use for constrainment
181      * @param {Region} nodeRegion The region of the node to constrain
182      * @param {Region} constrainingRegion The region of the node (or viewport) to constrain to
183      * 
184      * @return {Number} The constrained value
185      */
186     _constrain: function(val, axis, nodeRegion, constrainingRegion) {
187         if (constrainingRegion) {
188
189             if (this.get(PREVENT_OVERLAP)) {
190                 val = this._preventOverlap(val, axis, nodeRegion, constrainingRegion);
191             }
192
193             var x = (axis == X_COORD),
194
195                 regionSize    = (x) ? constrainingRegion.width : constrainingRegion.height,
196                 nodeSize      = (x) ? nodeRegion.width : nodeRegion.height,
197                 minConstraint = (x) ? constrainingRegion.left : constrainingRegion.top,
198                 maxConstraint = (x) ? constrainingRegion.right - nodeSize : constrainingRegion.bottom - nodeSize;
199
200             if (val < minConstraint || val > maxConstraint) {
201                 if (nodeSize < regionSize) {
202                     if (val < minConstraint) {
203                         val = minConstraint;
204                     } else if (val > maxConstraint) {
205                         val = maxConstraint;
206                     }
207                 } else {
208                     val = minConstraint;
209                 }
210             }
211         }
212
213         return val;
214     },
215
216     /**
217      * The method which performs the preventOverlap calculations for a given axis ("x" or "y") based
218      * on the value and regions provided.
219      * 
220      * @method _preventOverlap
221      * @protected
222      *
223      * @param {Number} val The value being constrain
224      * @param {String} axis The axis to being constrained
225      * @param {Region} nodeRegion The region of the node being constrained
226      * @param {Region} constrainingRegion The region of the node (or viewport) we need to constrain to
227      * 
228      * @return {Number} The constrained value
229      */
230     _preventOverlap : function(val, axis, nodeRegion, constrainingRegion) {
231
232         var align = this.get(ALIGN),
233             x = (axis === X_COORD),
234             nodeSize,
235             alignRegion,
236             nearEdge,
237             farEdge,
238             spaceOnNearSide, 
239             spaceOnFarSide;
240
241         if (align && align.points && PREVENT_OVERLAP_MAP[axis][align.points.join(EMPTY_STR)]) {
242
243             alignRegion = this._getRegion(align.node);
244
245             if (alignRegion) {
246                 nodeSize        = (x) ? nodeRegion.width : nodeRegion.height;
247                 nearEdge        = (x) ? alignRegion.left : alignRegion.top;
248                 farEdge         = (x) ? alignRegion.right : alignRegion.bottom;
249                 spaceOnNearSide = (x) ? alignRegion.left - constrainingRegion.left : alignRegion.top - constrainingRegion.top;
250                 spaceOnFarSide  = (x) ? constrainingRegion.right - alignRegion.right : constrainingRegion.bottom - alignRegion.bottom;
251             }
252  
253             if (val > nearEdge) {
254                 if (spaceOnFarSide < nodeSize && spaceOnNearSide > nodeSize) {
255                     val = nearEdge - nodeSize;
256                 }
257             } else {
258                 if (spaceOnNearSide < nodeSize && spaceOnFarSide > nodeSize) {
259                     val = farEdge;
260                 }
261             }
262         }
263
264         return val;
265     },
266
267     /**
268      * Binds event listeners responsible for updating the UI state in response to 
269      * Widget constrained positioning related state changes.
270      * <p>
271      * This method is invoked after bindUI is invoked for the Widget class
272      * using YUI's aop infrastructure.
273      * </p>
274      *
275      * @method _bindUIPosConstrained
276      * @protected
277      */
278     _bindUIPosConstrained : function() {
279         this.after(CONSTRAIN_CHANGE, this._afterConstrainChange);
280         this._enableConstraints(this.get(CONSTRAIN));
281     },
282
283     /**
284      * After change listener for the "constrain" attribute, responsible
285      * for updating the UI, in response to attribute changes.
286      *
287      * @method _afterConstrainChange
288      * @protected
289      * @param {EventFacade} e The event facade
290      */
291     _afterConstrainChange : function(e) {
292         this._enableConstraints(e.newVal);
293     },
294
295     /**
296      * Updates the UI if enabling constraints, and sets up the xyChange event listeners
297      * to constrain whenever the widget is moved. Disabling constraints removes the listeners.
298      * 
299      * @method enable or disable constraints listeners
300      * @private
301      * @param {boolean} enable Enable or disable constraints 
302      */
303     _enableConstraints : function(enable) {
304         if (enable) {
305             this.constrain();
306             this._cxyHandle = this._cxyHandle || this.on(CONSTRAIN_XYCHANGE, this._constrainOnXYChange);
307         } else if (this._cxyHandle) {
308             this._cxyHandle.detach();
309             this._cxyHandle = null;    
310         }
311     },
312
313     /**
314      * The on change listener for the "xy" attribute. Modifies the event facade's
315      * newVal property with the constrained XY value.
316      *
317      * @method _constrainOnXYChange
318      * @protected
319      * @param {EventFacade} e The event facade for the attribute change
320      */
321     _constrainOnXYChange : function(e) {
322         if (!e.constrained) {
323             e.newVal = this.getConstrainedXY(e.newVal);
324         }
325     },
326
327     /**
328      * Utility method to normalize region retrieval from a node instance, 
329      * or the viewport, if no node is provided. 
330      * 
331      * @method _getRegion
332      * @private
333      * @param {Node} node Optional.
334      */
335     _getRegion : function(node) {
336         var region;
337         if (!node) {
338             region = this._posNode.get(VIEWPORT_REGION);
339         } else {
340             node = Node.one(node);
341             if (node) {
342                 region = node.get(REGION);
343             }
344         }
345         return region;
346     }
347 };
348
349 Y.WidgetPositionConstrain = PositionConstrain;
350
351
352 }, '3.3.0' ,{requires:['widget-position']});