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