Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / util / Region.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * This class represents a rectangular region in X,Y space, and performs geometric
17  * transformations or tests upon the region.
18  *
19  * This class may be used to compare the document regions occupied by elements.
20  */
21 Ext.define('Ext.util.Region', {
22
23     /* Begin Definitions */
24
25     requires: ['Ext.util.Offset'],
26
27     statics: {
28         /**
29          * @static
30          * Retrieves an Ext.util.Region for a particular element.
31          * @param {String/HTMLElement/Ext.Element} el An element ID, htmlElement or Ext.Element representing an element in the document.
32          * @returns {Ext.util.Region} region
33          */
34         getRegion: function(el) {
35             return Ext.fly(el).getPageBox(true);
36         },
37
38         /**
39          * @static
40          * Creates a Region from a "box" Object which contains four numeric properties `top`, `right`, `bottom` and `left`.
41          * @param {Object} o An object with `top`, `right`, `bottom` and `left` properties.
42          * @return {Ext.util.Region} region The Region constructed based on the passed object
43          */
44         from: function(o) {
45             return new this(o.top, o.right, o.bottom, o.left);
46         }
47     },
48
49     /* End Definitions */
50
51     /**
52      * Creates a region from the bounding sides.
53      * @param {Number} top Top The topmost pixel of the Region.
54      * @param {Number} right Right The rightmost pixel of the Region.
55      * @param {Number} bottom Bottom The bottom pixel of the Region.
56      * @param {Number} left Left The leftmost pixel of the Region.
57      */
58     constructor : function(t, r, b, l) {
59         var me = this;
60         me.y = me.top = me[1] = t;
61         me.right = r;
62         me.bottom = b;
63         me.x = me.left = me[0] = l;
64     },
65
66     /**
67      * Checks if this region completely contains the region that is passed in.
68      * @param {Ext.util.Region} region
69      * @return {Boolean}
70      */
71     contains : function(region) {
72         var me = this;
73         return (region.x >= me.x &&
74                 region.right <= me.right &&
75                 region.y >= me.y &&
76                 region.bottom <= me.bottom);
77
78     },
79
80     /**
81      * Checks if this region intersects the region passed in.
82      * @param {Ext.util.Region} region
83      * @return {Ext.util.Region/Boolean} Returns the intersected region or false if there is no intersection.
84      */
85     intersect : function(region) {
86         var me = this,
87             t = Math.max(me.y, region.y),
88             r = Math.min(me.right, region.right),
89             b = Math.min(me.bottom, region.bottom),
90             l = Math.max(me.x, region.x);
91
92         if (b > t && r > l) {
93             return new this.self(t, r, b, l);
94         }
95         else {
96             return false;
97         }
98     },
99
100     /**
101      * Returns the smallest region that contains the current AND targetRegion.
102      * @param {Ext.util.Region} region
103      * @return {Ext.util.Region} a new region
104      */
105     union : function(region) {
106         var me = this,
107             t = Math.min(me.y, region.y),
108             r = Math.max(me.right, region.right),
109             b = Math.max(me.bottom, region.bottom),
110             l = Math.min(me.x, region.x);
111
112         return new this.self(t, r, b, l);
113     },
114
115     /**
116      * Modifies the current region to be constrained to the targetRegion.
117      * @param {Ext.util.Region} targetRegion
118      * @return {Ext.util.Region} this
119      */
120     constrainTo : function(r) {
121         var me = this,
122             constrain = Ext.Number.constrain;
123         me.top = me.y = constrain(me.top, r.y, r.bottom);
124         me.bottom = constrain(me.bottom, r.y, r.bottom);
125         me.left = me.x = constrain(me.left, r.x, r.right);
126         me.right = constrain(me.right, r.x, r.right);
127         return me;
128     },
129
130     /**
131      * Modifies the current region to be adjusted by offsets.
132      * @param {Number} top top offset
133      * @param {Number} right right offset
134      * @param {Number} bottom bottom offset
135      * @param {Number} left left offset
136      * @return {Ext.util.Region} this
137      */
138     adjust : function(t, r, b, l) {
139         var me = this;
140         me.top = me.y += t;
141         me.left = me.x += l;
142         me.right += r;
143         me.bottom += b;
144         return me;
145     },
146
147     /**
148      * Get the offset amount of a point outside the region
149      * @param {String} [axis]
150      * @param {Ext.util.Point} [p] the point
151      * @return {Ext.util.Offset}
152      */
153     getOutOfBoundOffset: function(axis, p) {
154         if (!Ext.isObject(axis)) {
155             if (axis == 'x') {
156                 return this.getOutOfBoundOffsetX(p);
157             } else {
158                 return this.getOutOfBoundOffsetY(p);
159             }
160         } else {
161             p = axis;
162             var d = Ext.create('Ext.util.Offset');
163             d.x = this.getOutOfBoundOffsetX(p.x);
164             d.y = this.getOutOfBoundOffsetY(p.y);
165             return d;
166         }
167
168     },
169
170     /**
171      * Get the offset amount on the x-axis
172      * @param {Number} p the offset
173      * @return {Number}
174      */
175     getOutOfBoundOffsetX: function(p) {
176         if (p <= this.x) {
177             return this.x - p;
178         } else if (p >= this.right) {
179             return this.right - p;
180         }
181
182         return 0;
183     },
184
185     /**
186      * Get the offset amount on the y-axis
187      * @param {Number} p the offset
188      * @return {Number}
189      */
190     getOutOfBoundOffsetY: function(p) {
191         if (p <= this.y) {
192             return this.y - p;
193         } else if (p >= this.bottom) {
194             return this.bottom - p;
195         }
196
197         return 0;
198     },
199
200     /**
201      * Check whether the point / offset is out of bound
202      * @param {String} [axis]
203      * @param {Ext.util.Point/Number} [p] the point / offset
204      * @return {Boolean}
205      */
206     isOutOfBound: function(axis, p) {
207         if (!Ext.isObject(axis)) {
208             if (axis == 'x') {
209                 return this.isOutOfBoundX(p);
210             } else {
211                 return this.isOutOfBoundY(p);
212             }
213         } else {
214             p = axis;
215             return (this.isOutOfBoundX(p.x) || this.isOutOfBoundY(p.y));
216         }
217     },
218
219     /**
220      * Check whether the offset is out of bound in the x-axis
221      * @param {Number} p the offset
222      * @return {Boolean}
223      */
224     isOutOfBoundX: function(p) {
225         return (p < this.x || p > this.right);
226     },
227
228     /**
229      * Check whether the offset is out of bound in the y-axis
230      * @param {Number} p the offset
231      * @return {Boolean}
232      */
233     isOutOfBoundY: function(p) {
234         return (p < this.y || p > this.bottom);
235     },
236
237     /**
238      * Restrict a point within the region by a certain factor.
239      * @param {String} [axis]
240      * @param {Ext.util.Point/Ext.util.Offset/Object} [p]
241      * @param {Number} [factor]
242      * @return {Ext.util.Point/Ext.util.Offset/Object/Number}
243      * @private
244      */
245     restrict: function(axis, p, factor) {
246         if (Ext.isObject(axis)) {
247             var newP;
248
249             factor = p;
250             p = axis;
251
252             if (p.copy) {
253                 newP = p.copy();
254             }
255             else {
256                 newP = {
257                     x: p.x,
258                     y: p.y
259                 };
260             }
261
262             newP.x = this.restrictX(p.x, factor);
263             newP.y = this.restrictY(p.y, factor);
264             return newP;
265         } else {
266             if (axis == 'x') {
267                 return this.restrictX(p, factor);
268             } else {
269                 return this.restrictY(p, factor);
270             }
271         }
272     },
273
274     /**
275      * Restrict an offset within the region by a certain factor, on the x-axis
276      * @param {Number} p
277      * @param {Number} [factor=1] The factor.
278      * @return {Number}
279      * @private
280      */
281     restrictX : function(p, factor) {
282         if (!factor) {
283             factor = 1;
284         }
285
286         if (p <= this.x) {
287             p -= (p - this.x) * factor;
288         }
289         else if (p >= this.right) {
290             p -= (p - this.right) * factor;
291         }
292         return p;
293     },
294
295     /**
296      * Restrict an offset within the region by a certain factor, on the y-axis
297      * @param {Number} p
298      * @param {Number} [factor] The factor, defaults to 1
299      * @return {Number}
300      * @private
301      */
302     restrictY : function(p, factor) {
303         if (!factor) {
304             factor = 1;
305         }
306
307         if (p <= this.y) {
308             p -= (p - this.y) * factor;
309         }
310         else if (p >= this.bottom) {
311             p -= (p - this.bottom) * factor;
312         }
313         return p;
314     },
315
316     /**
317      * Get the width / height of this region
318      * @return {Object} an object with width and height properties
319      * @private
320      */
321     getSize: function() {
322         return {
323             width: this.right - this.x,
324             height: this.bottom - this.y
325         };
326     },
327
328     /**
329      * Create a copy of this Region.
330      * @return {Ext.util.Region}
331      */
332     copy: function() {
333         return new this.self(this.y, this.right, this.bottom, this.x);
334     },
335
336     /**
337      * Copy the values of another Region to this Region
338      * @param {Ext.util.Region} p The region to copy from.
339      * @return {Ext.util.Region} This Region
340      */
341     copyFrom: function(p) {
342         var me = this;
343         me.top = me.y = me[1] = p.y;
344         me.right = p.right;
345         me.bottom = p.bottom;
346         me.left = me.x = me[0] = p.x;
347
348         return this;
349     },
350
351     /*
352      * Dump this to an eye-friendly string, great for debugging
353      * @return {String}
354      */
355     toString: function() {
356         return "Region[" + this.top + "," + this.right + "," + this.bottom + "," + this.left + "]";
357     },
358
359     /**
360      * Translate this region by the given offset amount
361      * @param {Ext.util.Offset/Object} x Object containing the `x` and `y` properties.
362      * Or the x value is using the two argument form.
363      * @param {Number} y The y value unless using an Offset object.
364      * @return {Ext.util.Region} this This Region
365      */
366     translateBy: function(x, y) {
367         if (arguments.length == 1) {
368             y = x.y;
369             x = x.x;
370         }
371         var me = this;
372         me.top = me.y += y;
373         me.right += x;
374         me.bottom += y;
375         me.left = me.x += x;
376
377         return me;
378     },
379
380     /**
381      * Round all the properties of this region
382      * @return {Ext.util.Region} this This Region
383      */
384     round: function() {
385         var me = this;
386         me.top = me.y = Math.round(me.y);
387         me.right = Math.round(me.right);
388         me.bottom = Math.round(me.bottom);
389         me.left = me.x = Math.round(me.x);
390
391         return me;
392     },
393
394     /**
395      * Check whether this region is equivalent to the given region
396      * @param {Ext.util.Region} region The region to compare with
397      * @return {Boolean}
398      */
399     equals: function(region) {
400         return (this.top == region.top && this.right == region.right && this.bottom == region.bottom && this.left == region.left);
401     }
402 });
403