UNPKG

39 kBJavaScriptView Raw
1'use strict'
2
3var hasOwn = require('hasown')
4var copyUtils = require('copy-utils')
5var copyList = copyUtils.copyList
6var F = require('functionally')
7var isObject = require('i-s').object
8
9/**
10 * @class Region
11 *
12 * # z.region
13 *
14 * The Region class is an abstraction that allows the developer to refer to rectangles on the screen,
15 * and move them around, make diffs and unions, detect intersections, compute areas, etc.
16 *
17 * ## Creating a region
18 *
19 *
20 *
21 * var region = require('region')({
22 * top : 10,
23 * left : 10,
24 * bottom: 100,
25 * right : 100
26 * })
27 * //this region is a square, 90x90, starting from (10,10) to (100,100)
28 *
29 * var second = require('region')({ top: 10, left: 100, right: 200, bottom: 60})
30 * var union = region.getUnion(second)
31 *
32 * //the "union" region is a union between "region" and "second"
33 *
34 * ## Element regions
35 *
36 * The {@link Element} class has {@link Element#getRegion} and {@link Element#setRegion} methods, so you can easily
37 * retrieve and set element size and position.
38 *
39 * var bodyElement = Element.select('body'),
40 * bodyRegion = bodyElement.getRegion()
41 *
42 * bodyRegion.setWidth(100).setHeight(200)
43 *
44 * //this makes the body just 100px in width and 200px in height
45 * bodyElement.setRegion(bodyRegion)
46 *
47 * //you can even bind an element to a region
48 *
49 * var reg = bodyElement.getRegion({bound: true})
50 *
51 * reg.setWidth(200) //also sets the width of the bodyElement
52 *
53 */
54
55var classy = require('classy')
56var EventEmitter = require('zemitter').mixin
57
58var MAX = Math.max,
59 MIN = Math.min,
60 POINT_POSITIONS = {
61 cy: 'YCenter',
62 cx: 'XCenter',
63 t : 'Top',
64 tc: 'TopCenter',
65 tl: 'TopLeft',
66 tr: 'TopRight',
67 b : 'Bottom',
68 bc: 'BottomCenter',
69 bl: 'BottomLeft',
70 br: 'BottomRight',
71 l : 'Left',
72 lc: 'LeftCenter',
73 r : 'Right',
74 rc: 'RightCenter',
75 c : 'Center'
76 }
77
78var REGION = classy.define({
79
80 forceInstance: true,
81
82 mixins: [
83 EventEmitter
84 // ,
85 // 'z.alignable'
86 ],
87
88 statics: {
89 init: function(){
90 var exportAsNonStatic = {
91 getIntersection : true,
92 getIntersectionArea : true,
93 getIntersectionHeight: true,
94 getIntersectionWidth : true,
95 getUnion : true
96 }
97 var thisProto = this.prototype
98 var newName
99
100 var exportHasOwn = hasOwn(exportAsNonStatic)
101 var methodName
102
103 for (methodName in exportAsNonStatic) if (exportHasOwn(methodName)) {
104 newName = exportAsNonStatic[methodName]
105 if (typeof newName != 'string'){
106 newName = methodName
107 }
108
109 (function(proto, methodName, protoMethodName){
110
111 proto[methodName] = function(region){
112 //<debug>
113 if (!this.$ownClass[protoMethodName]){
114 console.warn('cannot find method ', protoMethodName,' on ', this.$ownClass)
115 }
116 //</debug>
117 return this.$ownClass[protoMethodName](this, region)
118 }
119
120 })(thisProto, newName, methodName)
121 }
122 },
123
124 /**
125 * @static
126 * Returns true if the given region is valid, false otherwise.
127 * @param {Region} region The region to check
128 * @return {Boolean} True, if the region is valid, false otherwise.
129 * A region is valid if
130 * * left <= right &&
131 * * top <= bottom
132 */
133 validate: function(region){
134
135 var isValid = true
136
137 if (region.right < region.left){
138 isValid = false
139 region.right = region.left
140 }
141
142 if (region.bottom < region.top){
143 isValid = false
144 region.bottom = region.top
145 }
146
147 return isValid
148 },
149
150 /**
151 * Returns the region corresponding to the documentElement
152 * @return {Region} The region corresponding to the documentElement. This region is the maximum region visible on the screen.
153 */
154 getDocRegion: function(){
155 return REGION.fromDOM(document.documentElement)
156 },
157
158 fromDOM: function(dom){
159 return new REGION({
160 top : dom.offsetTop,
161 left : dom.offsetLeft,
162 width : dom.offsetWidth,
163 height: dom.offsetHeight
164 })
165 },
166
167 /**
168 * @static
169 * Returns a region that is the intersection of the given two regions
170 * @param {Region} first The first region
171 * @param {Region} second The second region
172 * @return {Region/Boolean} The intersection region or false if no intersection found
173 */
174 getIntersection: function(first, second){
175
176 var area = this.getIntersectionArea(first, second)
177
178 if (area){
179 return new REGION(area)
180 }
181
182 return false
183 },
184
185 getIntersectionWidth: function(first, second){
186 var minRight = MIN(first.right, second.right)
187 var maxLeft = MAX(first.left, second.left)
188
189 if (maxLeft < minRight){
190 return minRight - maxLeft
191 }
192
193 return 0
194 },
195
196 getIntersectionHeight: function(first, second){
197 var maxTop = MAX(first.top, second.top)
198 var minBottom = MIN(first.bottom,second.bottom)
199
200 if (maxTop < minBottom){
201 return minBottom - maxTop
202 }
203
204 return 0
205 },
206
207 getIntersectionArea: function(first, second){
208 var maxTop = MAX(first.top, second.top)
209 var minRight = MIN(first.right, second.right)
210 var minBottom = MIN(first.bottom,second.bottom)
211 var maxLeft = MAX(first.left, second.left)
212
213 if (
214 maxTop < minBottom &&
215 maxLeft < minRight
216 ){
217 return {
218 top : maxTop,
219 right : minRight,
220 bottom : minBottom,
221 left : maxLeft,
222
223 width : minRight - maxLeft,
224 height : minBottom - maxTop
225 }
226 }
227
228 return false
229 },
230
231 /**
232 * @static
233 * Returns a region that is the union of the given two regions
234 * @param {Region} first The first region
235 * @param {Region} second The second region
236 * @return {Region} The union region. The smallest region that contains both given regions.
237 */
238 getUnion: function(first, second){
239 var top = MIN(first.top, second.top)
240 var right = MAX(first.right, second.right)
241 var bottom = MAX(first.bottom,second.bottom)
242 var left = MIN(first.left, second.left)
243
244 return new REGION(top, right, bottom, left)
245 },
246
247 /**
248 * @static
249 * Returns a region. If the reg argument is a region, returns it, otherwise return a new region built from the reg object.
250 *
251 * @param {Region} reg A region or an object with either top, left, bottom, right or
252 * with top, left, width, height
253 * @return {Region} A region
254 */
255 getRegion: function(reg){
256 return reg instanceof REGION?
257 reg :
258 new REGION(reg)
259 },
260
261 /**
262 * @static
263 * Aligns the source region to the target region, so as to correspond to the given alignment.
264 *
265 * NOTE that this method makes changes on the sourceRegion in order for it to be aligned as specified.
266 *
267 * @param {Region} sourceRegion
268 * @param {Region} targetRegion
269 *
270 * @param {String} align A string with 2 valid align positions, eg: 'tr-bl'.
271 * For valid positions, see {@link Region#getPoint}
272 *
273 * Having 2 regions, we need to be able to align them as we wish:
274 *
275 * for example, if we have
276 *
277 * source target
278 * ________________
279 * ____
280 * | | ________
281 * |____| | |
282 * | |
283 * |________|
284 *
285 * and we align 't-t', we get:
286 *
287 * source target
288 * _________________
289 *
290 * ____ ________
291 * | | | |
292 * |____| | |
293 * |________|
294 *
295 * In this case, the source was moved down to be aligned to the top of the target
296 *
297 *
298 * and if we align 'tc-tc' we get
299 *
300 * source target
301 * __________________
302 *
303 * ________
304 * | | | |
305 * | |____| |
306 * |________|
307 *
308 * Since the source was moved to have the top-center point to be the same with target top-center
309 *
310 *
311 *
312 * @return {RegionClass} The Region class
313 */
314 // align: function(sourceRegion, targetRegion, align){
315 // align = (align || 'c-c').split('-')
316
317 // //<debug>
318 // if (align.length != 2){
319 // console.warn('Incorrect region alignment! The align parameter need to be in the form \'br-c\', that is, a - separated string!', align)
320 // }
321 // //</debug>
322
323 // this.alignToPoint(sourceRegion, targetRegion.getPoint(align[1]), align[0])
324
325 // return REGION
326 // },
327
328 /**
329 * Modifies the given region to be aligned to the point, as specified by anchor
330 *
331 * @param {Region} region The region to align to the point
332 * @param {Object} point The point to be used as a reference
333 * @param {Number} point.x
334 * @param {Number} point.y
335 * @param {String} anchor The position where to anchor the region to the point. See {@link #getPoint} for available options/
336 * @return {RegionClass} The Region class
337 */
338 // alignToPoint: function(region, point, anchor){
339 // var sourcePoint = region.getPoint(anchor),
340 // count = 0,
341 // shiftObj = {}
342
343 // if (
344 // sourcePoint.x != null &&
345 // point.x != null
346 // ){
347
348 // count++
349 // shiftObj.left = point.x - sourcePoint.x
350 // }
351
352 // if (
353 // sourcePoint.y != null &&
354 // point.y != null
355 // ){
356 // count++
357 // shiftObj.top = point.y - sourcePoint.y
358 // }
359
360 // if (count){
361
362 // region.shift(shiftObj)
363
364 // }
365
366 // return REGION
367 // },
368
369 /**
370 * Creates a region that corresponds to a point.
371 *
372 * @param {Object} xy The point
373 * @param {Number} xy.x
374 * @param {Number} xy.y
375 *
376 * @return {Region} The new region, with top==xy.y, bottom = xy.y and left==xy.x, right==xy.x
377 */
378 fromPoint: function(xy){
379 return new REGION({
380 top : xy.y,
381 bottom : xy.y,
382 left : xy.x,
383 right : xy.x
384 })
385 }
386 },
387
388 /**
389 * @cfg {Boolean} emitChangeEvents If this is set to true, the region
390 * will emit 'changesize' and 'changeposition' whenever the size or the position changs
391 */
392 emitChangeEvents: false,
393
394 /**
395 * @cfg {Number} changeEventsBuffer If {@link #emitChangeEvents} is true, the change events will be emitted in a buffered manner,
396 * if this value is greater than 0
397 */
398 changeEventsBuffer: 0,
399
400 /**
401 * Returns this region, or a clone of this region
402 * @param {Boolean} [clone] If true, this method will return a clone of this region
403 * @return {Region} This region, or a clone of this
404 */
405 getRegion: function(clone){
406 return clone?
407 this.clone():
408 this
409 },
410
411 /**
412 * Sets the properties of this region to those of the given region
413 * @param {Region/Object} reg The region or object to use for setting properties of this region
414 * @return {Region} this
415 */
416 setRegion: function(reg){
417
418 if (reg instanceof REGION){
419 this.set(reg.get())
420 } else {
421 this.set(reg)
422 }
423
424 return this
425 },
426
427 /**
428 * Returns true if this region is valid, false otherwise
429 *
430 * @param {Region} region The region to check
431 * @return {Boolean} True, if the region is valid, false otherwise.
432 * A region is valid if
433 * * left <= right &&
434 * * top <= bottom
435 */
436 validate: function(){
437 return REGION.validate(this)
438 },
439
440 _before: function(){
441 if (this.emitChangeEvents){
442 return copyList(this, {}, ['left','top','bottom','right'])
443 }
444 },
445
446 _after: function(before){
447 if (this.emitChangeEvents){
448
449 if(this.top != before.top || this.left != before.left) {
450 this.emitPositionChange()
451 }
452
453 if(this.right != before.right || this.bottom != before.bottom) {
454 this.emitSizeChange()
455 }
456 }
457 },
458
459 notifyPositionChange: function(){
460 this.emit('changeposition', this)
461 },
462
463 emitPositionChange: function(){
464 if (this.changeEventsBuffer){
465 if (!this.emitPositionChangeBuffered){
466 this.emitPositionChangeBuffered = F.buffer(this.notifyPositionChange, changeEventsBuffer)
467 }
468 this.emitPositionChangeBuffered()
469 }
470
471 this.notifyPositionChange()
472 },
473
474 notifySizeChange: function(){
475 this.emit('changesize', this)
476 },
477
478 emitSizeChange: function(){
479 if (this.changeEventsBuffer){
480 if (!this.emitSizeChangeBuffered){
481 this.emitSizeChangeBuffered = F.buffer(this.notifySizeChange, changeEventsBuffer)
482 }
483 this.emitSizeChangeBuffered()
484 }
485
486 this.notifySizeChange()
487 },
488
489 /**
490 * Add the given amounts to each specified side. Example
491 *
492 * region.add({
493 * top: 50, //add 50 px to the top side
494 * bottom: -100 //substract 100 px from the bottom side
495 * })
496 *
497 * @param {Object} directions
498 * @param {Number} [directions.top]
499 * @param {Number} [directions.left]
500 * @param {Number} [directions.bottom]
501 * @param {Number} [directions.right]
502 *
503 * @return {Region} this
504 */
505 add: function(directions){
506
507 var before = this._before()
508 var direction
509
510 for (direction in directions) if ( hasOwn(directions, direction) ) {
511 this[direction] += directions[direction]
512 }
513
514 this[0] = this.left
515 this[1] = this.top
516
517 this._after(before)
518
519 return this
520 },
521
522 /**
523 * The same as {@link #add}, but substracts the given values
524 * @param {Object} directions
525 * @param {Number} [directions.top]
526 * @param {Number} [directions.left]
527 * @param {Number} [directions.bottom]
528 * @param {Number} [directions.right]
529 *
530 * @return {Region} this
531 */
532 substract: function(directions){
533
534 var before = this._before()
535 var direction
536
537 for (direction in directions) if (hasOwn(directions, direction) ) {
538 this[direction] -= directions[direction]
539 }
540
541 this[0] = this.left
542 this[1] = this.top
543
544 this._after(before)
545
546 return this
547 },
548
549 /**
550 * Retrieves the size of the region.
551 * @return {Object} An object with {width, height}, corresponding to the width and height of the region
552 */
553 getSize: function(){
554 return {
555 width : this.getWidth(),
556 height : this.getHeight()
557 }
558 },
559
560 /**
561 * Move the region to the given position and keeps the region width and height.
562 *
563 * @param {Object} position An object with {top, left} properties. The values in {top,left} are used to move the region by the given amounts.
564 * @param {Number} [position.left]
565 * @param {Number} [position.top]
566 *
567 * @return {Region} this
568 */
569 setPosition: function(position){
570 var width = this.getWidth(),
571 height = this.getHeight()
572
573 if (position.left){
574 position.right = position.left + width
575 }
576
577 if (position.top){
578 position.bottom = position.top + height
579 }
580
581 return this.set(position)
582 },
583
584 /**
585 * Sets both the height and the width of this region to the given size.
586 *
587 * @param {Number} size The new size for the region
588 * @return {Region} this
589 */
590 setSize: function(size){
591 if (size.height && size.width){
592 return this.set({
593 right : this.left + size.width,
594 bottom : this.top + size.height
595 })
596 }
597
598 if (size.width){
599 this.setWidth(size.width)
600 }
601
602 if (size.height){
603 this.setHeight(size.height)
604 }
605
606 return this
607 },
608
609 /**
610 * @chainable
611 *
612 * Sets the width of this region
613 * @param {Number} width The new width for this region
614 * @return {Region} this
615 */
616 setWidth: function(width){
617 return this.set({
618 right: this.left + width
619 })
620 },
621
622 /**
623 * @chainable
624 *
625 * Sets the height of this region
626 * @param {Number} height The new height for this region
627 * @return {Region} this
628 */
629 setHeight: function(height){
630 return this.set({
631 bottom: this.top + height
632 })
633 },
634
635 /**
636 * Sets the given properties on this region
637 *
638 * @param {Object} directions an object containing top, left, and EITHER bottom, right OR width, height
639 * @param {Number} [directions.top]
640 * @param {Number} [directions.left]
641 *
642 * @param {Number} [directions.bottom]
643 * @param {Number} [directions.right]
644 *
645 * @param {Number} [directions.width]
646 * @param {Number} [directions.height]
647 *
648 *
649 * @return {Region} this
650 */
651 set: function(directions){
652 var before = this._before()
653
654 copyList(directions, this, ['left','top','bottom','right'])
655
656 if (directions.bottom == null && directions.height != null){
657 this.bottom = this.top + directions.height
658 }
659 if (directions.right == null && directions.width != null){
660 this.right = this.left + directions.width
661 }
662
663 this[0] = this.left
664 this[1] = this.top
665
666 this._after(before)
667
668 return this
669 },
670
671 /**
672 * Retrieves the given property from this region. If no property is given, return an object
673 * with {left, top, right, bottom}
674 *
675 * @param {String} [dir] the property to retrieve from this region
676 * @return {Number/Object}
677 */
678 get: function(dir){
679 return dir? this[dir]:
680 copyList(this, {}, ['left','right','top','bottom'])
681 },
682
683 /**
684 * Shifts this region to either top, or left or both.
685 * Shift is similar to {@link #add} by the fact that it adds the given dimensions to top/left sides, but also adds the given dimensions
686 * to bottom and right
687 *
688 * @param {Object} directions
689 * @param {Number} [directions.top]
690 * @param {Number} [directions.left]
691 *
692 * @return {Region} this
693 */
694 shift: function(directions){
695
696 var before = this._before()
697
698 if (directions.top){
699 this.top += directions.top
700 this.bottom += directions.top
701 }
702
703 if (directions.left){
704 this.left += directions.left
705 this.right += directions.left
706 }
707
708 this[0] = this.left
709 this[1] = this.top
710
711 this._after(before)
712
713 return this
714 },
715
716 /**
717 * Same as {@link #shift}, but substracts the given values
718 * @chainable
719 *
720 * @param {Object} directions
721 * @param {Number} [directions.top]
722 * @param {Number} [directions.left]
723 *
724 * @return {Region} this
725 */
726 unshift: function(directions){
727
728 if (directions.top){
729 directions.top *= -1
730 }
731
732 if (directions.left){
733 directions.left *= -1
734 }
735
736 return this.shift(directions)
737 },
738
739 /**
740 * Compare this region and the given region. Return true if they have all the same size and position
741 * @param {Region} region The region to compare with
742 * @return {Boolean} True if this and region have same size and position
743 */
744 equals: function(region){
745 return this.equalsPosition(region) && this.equalsSize(region)
746 },
747
748 /**
749 * Returns true if this region has the same bottom,right properties as the given region
750 * @param {Region/Object} size The region to compare against
751 * @return {Boolean} true if this region is the same size as the given size
752 */
753 equalsSize: function(size){
754 var isInstance = size instanceof REGION
755
756 var s = {
757 width: size.width == null && isInstance?
758 size.getWidth():
759 size.width,
760
761 height: size.height == null && isInstance?
762 size.getHeight():
763 size.height
764 }
765 return this.getWidth() == s.width && this.getHeight() == s.height
766 },
767
768 /**
769 * Returns true if this region has the same top,left properties as the given region
770 * @param {Region} region The region to compare against
771 * @return {Boolean} true if this.top == region.top and this.left == region.left
772 */
773 equalsPosition: function(region){
774 return this.top == region.top && this.left == region.left
775 },
776
777 /**
778 * Adds the given ammount to the left side of this region
779 * @param {Number} left The ammount to add
780 * @return {Region} this
781 */
782 addLeft: function(left){
783 var before = this._before()
784
785 this.left = this[0] = this.left + left
786
787 this._after(before)
788
789 return this
790 },
791
792 /**
793 * Adds the given ammount to the top side of this region
794 * @param {Number} top The ammount to add
795 * @return {Region} this
796 */
797 addTop: function(top){
798 var before = this._before()
799
800 this.top = this[1] = this.top + top
801
802 this._after(before)
803
804 return this
805 },
806
807 /**
808 * Adds the given ammount to the bottom side of this region
809 * @param {Number} bottom The ammount to add
810 * @return {Region} this
811 */
812 addBottom: function(bottom){
813 var before = this._before()
814
815 this.bottom += bottom
816
817 this._after(before)
818
819 return this
820 },
821
822 /**
823 * Adds the given ammount to the right side of this region
824 * @param {Number} right The ammount to add
825 * @return {Region} this
826 */
827 addRight: function(right){
828 var before = this._before()
829
830 this.right += right
831
832 this._after(before)
833
834 return this
835 },
836
837 /**
838 * Minimize the top side.
839 * @return {Region} this
840 */
841 minTop: function(){
842 return this.expand({top: 1})
843 },
844 /**
845 * Minimize the bottom side.
846 * @return {Region} this
847 */
848 maxBottom: function(){
849 return this.expand({bottom: 1})
850 },
851 /**
852 * Minimize the left side.
853 * @return {Region} this
854 */
855 minLeft: function(){
856 return this.expand({left: 1})
857 },
858 /**
859 * Maximize the right side.
860 * @return {Region} this
861 */
862 maxRight: function(){
863 return this.expand({right: 1})
864 },
865
866 /**
867 * Expands this region to the dimensions of the given region, or the document region, if no region is expanded.
868 * But only expand the given sides (any of the four can be expanded).
869 *
870 * @param {Object} directions
871 * @param {Boolean} [directions.top]
872 * @param {Boolean} [directions.bottom]
873 * @param {Boolean} [directions.left]
874 * @param {Boolean} [directions.right]
875 *
876 * @param {Region} [region] the region to expand to, defaults to the document region
877 * @return {Region} this region
878 */
879 expand: function(directions, region){
880 var docRegion = region || REGION.getDocRegion(),
881 list = [],
882 direction,
883 before = this._before()
884
885 for (direction in directions) if ( hasOwn(directions, direction) ) {
886 list.push(direction)
887 }
888
889 copyList(docRegion, this, list)
890
891 this[0] = this.left
892 this[1] = this.top
893
894 this._after(before)
895
896 return this
897 },
898
899 /**
900 * Returns a clone of this region
901 * @return {Region} A new region, with the same position and dimension as this region
902 */
903 clone: function(){
904 return new REGION({
905 top : this.top,
906 left : this.left,
907 right : this.right,
908 bottom : this.bottom
909 })
910 },
911
912 /**
913 * Returns true if this region contains the given point
914 * @param {Number/Object} x the x coordinate of the point
915 * @param {Number} [y] the y coordinate of the point
916 *
917 * @return {Boolean} true if this region constains the given point, false otherwise
918 */
919 containsPoint: function(x, y){
920 if (arguments.length == 1){
921 y = x.y
922 x = x.x
923 }
924
925 return this.left <= x &&
926 x <= this.right &&
927 this.top <= y &&
928 y <= this.bottom
929 },
930
931 /**
932 *
933 * @param region
934 *
935 * @return {Boolean} true if this region contains the given region, false otherwise
936 */
937 containsRegion: function(region){
938 return this.containsPoint(region.left, region.top) &&
939 this.containsPoint(region.right, region.bottom)
940 },
941
942 /**
943 * Returns an object with the difference for {top, bottom} positions betwen this and the given region,
944 *
945 * See {@link #diff}
946 * @param {Region} region The region to use for diff
947 * @return {Object} {top,bottom}
948 */
949 diffHeight: function(region){
950 return this.diff(region, {top: true, bottom: true})
951 },
952
953 /**
954 * Returns an object with the difference for {left, right} positions betwen this and the given region,
955 *
956 * See {@link #diff}
957 * @param {Region} region The region to use for diff
958 * @return {Object} {left,right}
959 */
960 diffWidth: function(region){
961 return this.diff(region, {left: true, right: true})
962 },
963
964 /**
965 * Returns an object with the difference in sizes for the given directions, between this and region
966 *
967 * @param {Region} region The region to use for diff
968 * @param {Object} directions An object with the directions to diff. Can have any of the following keys:
969 * * left
970 * * right
971 * * top
972 * * bottom
973 *
974 * @return {Object} and object with the same keys as the directions object, but the values being the
975 * differences between this region and the given region
976 */
977 diff: function(region, directions){
978 var result = {},
979 dirName
980
981 for (dirName in directions) if ( hasOwn(directions, dirName) ) {
982 result[dirName] = this[dirName] - region[dirName]
983 }
984
985 return result
986 },
987
988 /**
989 * Returns the position, in {left,top} properties, of this region
990 *
991 * @return {Object} {left,top}
992 */
993 getPosition: function(){
994 return {
995 left: this.left,
996 top : this.top
997 }
998 },
999
1000 /**
1001 * Returns the point at the given position from this region.
1002 *
1003 * @param {String} position Any of:
1004 *
1005 * * 'cx' - See {@link #getPointXCenter}
1006 * * 'cy' - See {@link #getPointYCenter}
1007 * * 'b' - See {@link #getPointBottom}
1008 * * 'bc' - See {@link #getPointBottomCenter}
1009 * * 'l' - See {@link #getPointLeft}
1010 * * 'lc' - See {@link #getPointLeftCenter}
1011 * * 't' - See {@link #getPointTop}
1012 * * 'tc' - See {@link #getPointTopCenter}
1013 * * 'r' - See {@link #getPointRight}
1014 * * 'rc' - See {@link #getPointRightCenter}
1015 * * 'c' - See {@link #getPointCenter}
1016 * * 'tl' - See {@link #getPointTopLeft}
1017 * * 'bl' - See {@link #getPointBottomLeft}
1018 * * 'br' - See {@link #getPointBottomRight}
1019 * * 'tr' - See {@link #getPointTopRight}
1020 *
1021 * @param {Boolean} asLeftTop
1022 *
1023 * @return {Object} either an object with {x,y} or {left,top} if asLeftTop is true
1024 */
1025 getPoint: function(position, asLeftTop){
1026
1027 //<debug>
1028 if (!POINT_POSITIONS[position]) {
1029 console.warn('The position ', position, ' could not be found! Available options are tl, bl, tr, br, l, r, t, b.');
1030 }
1031 //</debug>
1032
1033 var method = 'getPoint' + POINT_POSITIONS[position],
1034 result = this[method]()
1035
1036 if (asLeftTop){
1037 return {
1038 left : result.x,
1039 top : result.y
1040 }
1041 }
1042
1043 return result
1044 },
1045
1046 /**
1047 * Returns a point with x = null and y being the middle of the left region segment
1048 * @return {Object} {x,y}
1049 */
1050 getPointYCenter: function(){
1051 return { x: null, y: this.top + this.getHeight() / 2 }
1052 },
1053
1054 /**
1055 * Returns a point with y = null and x being the middle of the top region segment
1056 * @return {Object} {x,y}
1057 */
1058 getPointXCenter: function(){
1059 return { x: this.left + this.getWidth() / 2, y: null }
1060 },
1061
1062 /**
1063 * Returns a point with x = null and y the region top position on the y axis
1064 * @return {Object} {x,y}
1065 */
1066 getPointTop: function(){
1067 return { x: null, y: this.top }
1068 },
1069
1070 /**
1071 * Returns a point that is the middle point of the region top segment
1072 * @return {Object} {x,y}
1073 */
1074 getPointTopCenter: function(){
1075 return { x: this.left + this.getWidth() / 2, y: this.top }
1076 },
1077
1078 /**
1079 * Returns a point that is the top-left point of the region
1080 * @return {Object} {x,y}
1081 */
1082 getPointTopLeft: function(){
1083 return { x: this.left, y: this.top}
1084 },
1085
1086 /**
1087 * Returns a point that is the top-right point of the region
1088 * @return {Object} {x,y}
1089 */
1090 getPointTopRight: function(){
1091 return { x: this.right, y: this.top}
1092 },
1093
1094 /**
1095 * Returns a point with x = null and y the region bottom position on the y axis
1096 * @return {Object} {x,y}
1097 */
1098 getPointBottom: function(){
1099 return { x: null, y: this.bottom }
1100 },
1101
1102 /**
1103 * Returns a point that is the middle point of the region bottom segment
1104 * @return {Object} {x,y}
1105 */
1106 getPointBottomCenter: function(){
1107 return { x: this.left + this.getWidth() / 2, y: this.bottom }
1108 },
1109
1110 /**
1111 * Returns a point that is the bottom-left point of the region
1112 * @return {Object} {x,y}
1113 */
1114 getPointBottomLeft: function(){
1115 return { x: this.left, y: this.bottom}
1116 },
1117
1118 /**
1119 * Returns a point that is the bottom-right point of the region
1120 * @return {Object} {x,y}
1121 */
1122 getPointBottomRight: function(){
1123 return { x: this.right, y: this.bottom}
1124 },
1125
1126 /**
1127 * Returns a point with y = null and x the region left position on the x axis
1128 * @return {Object} {x,y}
1129 */
1130 getPointLeft: function(){
1131 return { x: this.left, y: null }
1132 },
1133
1134 /**
1135 * Returns a point that is the middle point of the region left segment
1136 * @return {Object} {x,y}
1137 */
1138 getPointLeftCenter: function(){
1139 return { x: this.left, y: this.top + this.getHeight() / 2 }
1140 },
1141
1142 /**
1143 * Returns a point with y = null and x the region right position on the x axis
1144 * @return {Object} {x,y}
1145 */
1146 getPointRight: function(){
1147 return { x: this.right, y: null }
1148 },
1149
1150 /**
1151 * Returns a point that is the middle point of the region right segment
1152 * @return {Object} {x,y}
1153 */
1154 getPointRightCenter: function(){
1155 return { x: this.right, y: this.top + this.getHeight() / 2 }
1156 },
1157
1158 /**
1159 * Returns a point that is the center of the region
1160 * @return {Object} {x,y}
1161 */
1162 getPointCenter: function(){
1163 return { x: this.left + this.getWidth() / 2, y: this.top + this.getHeight() / 2 }
1164 },
1165
1166 /**
1167 * @return {Number} returns the height of the region
1168 */
1169 getHeight: function(){
1170 return this.bottom - this.top
1171 },
1172
1173 /**
1174 * @return {Number} returns the width of the region
1175 */
1176 getWidth: function(){
1177 return this.right - this.left
1178 },
1179
1180 /**
1181 * @return {Number} returns the top property of the region
1182 */
1183 getTop: function(){
1184 return this.top
1185 },
1186
1187 /**
1188 * @return {Number} returns the left property of the region
1189 */
1190 getLeft: function(){
1191 return this.left
1192 },
1193
1194 /**
1195 * @return {Number} returns the bottom property of the region
1196 */
1197 getBottom: function(){
1198 return this.bottom
1199 },
1200
1201 /**
1202 * @return {Number} returns the right property of the region
1203 */
1204 getRight: function(){
1205 return this.right
1206 },
1207
1208 /**
1209 * Returns the area of the region
1210 * @return {Number} the computed area
1211 */
1212 getArea: function(){
1213 return this.getWidth() * this.getHeight()
1214 },
1215
1216 constrainTo: function(contrain){
1217 var intersect = this.getIntersection(contrain),
1218 shift
1219
1220 if (!intersect || !intersect.equals(this)){
1221
1222 var contrainWidth = contrain.getWidth(),
1223 contrainHeight = contrain.getHeight(),
1224
1225 shift = {}
1226
1227 if (this.getWidth() > contrainWidth){
1228 this.left = contrain.left
1229 this.setWidth(contrainWidth)
1230 }
1231
1232 if (this.getHeight() > contrainHeight){
1233 this.top = contrain.top
1234 this.setHeight(contrainHeight)
1235 }
1236
1237 shift = {}
1238
1239 if (this.right > contrain.right){
1240 shift.left = contrain.right - this.right
1241 }
1242
1243 if (this.bottom > contrain.bottom){
1244 shift.top = contrain.bottom - this.bottom
1245 }
1246
1247 if (this.left < contrain.left){
1248 shift.left = contrain.left - this.left
1249 }
1250
1251 if (this.top < contrain.top){
1252 shift.top = contrain.top - this.top
1253 }
1254
1255 this.shift(shift)
1256
1257 return true
1258 }
1259
1260 return false
1261 },
1262
1263 /**
1264 * @constructor
1265 *
1266 * Construct a new Region.
1267 *
1268 * Example:
1269 *
1270 * var r = root.create('z.region', { top: 10, left: 20, bottom: 100, right: 200 })
1271 *
1272 * //or, the same, but with numbers
1273 *
1274 * r = root.create('z.region', 10, 200, 100, 20)
1275 *
1276 * //or, with width and height
1277 *
1278 * r = root.create('z.region', { top: 10, left: 20, width: 180, height: 90})
1279 *
1280 * @param {Number|Object} top The top pixel position, or an object with top, left, bottom, right properties. If an object is passed,
1281 * instead of having bottom and right, it can have width and height.
1282 *
1283 * @param {Number} right The right pixel position
1284 * @param {Number} bottom The bottom pixel position
1285 * @param {Number} left The left pixel position
1286 *
1287 * @return {Region} this
1288 */
1289 init: function(top, right, bottom, left){
1290
1291 if (isObject(top)){
1292 copyList(top, this, ['top','right','bottom','left'])
1293
1294 if (top.bottom == null && top.height != null){
1295 this.bottom = this.top + top.height
1296 }
1297 if (top.right == null && top.width != null){
1298 this.right = this.left + top.width
1299 }
1300
1301 if (top.emitChangeEvents){
1302 this.emitChangeEvents = top.emitChangeEvents
1303 }
1304 } else {
1305 this.top = top
1306 this.right = right
1307 this.bottom = bottom
1308 this.left = left
1309 }
1310
1311 this[0] = this.left
1312 this[1] = this.top
1313
1314 REGION.validate(this)
1315 },
1316
1317 /**
1318 * @property {Number} top
1319 */
1320
1321 /**
1322 * @property {Number} right
1323 */
1324
1325 /**
1326 * @property {Number} bottom
1327 */
1328
1329 /**
1330 * @property {Number} left
1331 */
1332
1333 /**
1334 * @property {Number} [0] the top property
1335 */
1336
1337 /**
1338 * @property {Number} [1] the left property
1339 */
1340
1341 /**
1342 *
1343 * Aligns this region to the given region
1344 * @param {Region} region
1345 * @param {String} alignPositions For available positions, see {@link #getPoint}
1346 *
1347 * eg: 'tr-bl'
1348 *
1349 * @return this
1350 */
1351 // alignToRegion: function(region, alignPositions){
1352 // REGION.align(this, region, alignPositions)
1353
1354 // return this
1355 // },
1356
1357 /**
1358 * Aligns this region to the given point, in the anchor position
1359 * @param {Object} point eg: {x: 20, y: 600}
1360 * @param {Number} point.x
1361 * @param {Number} point.y
1362 *
1363 * @param {String} anchor For available positions, see {@link #getPoint}
1364 *
1365 * eg: 'bl'
1366 *
1367 * @return this
1368 */
1369 // alignToPoint: function(point, anchor){
1370 // REGION.alignToPoint(this, point, anchor)
1371
1372 // return this
1373 // }
1374
1375 /**
1376 * @method getIntersection
1377 * Returns a region that is the intersection of this region and the given region
1378 * @param {Region} region The region to intersect with
1379 * @return {Region} The intersection region
1380 */
1381
1382 /**
1383 * @method getUnion
1384 * Returns a region that is the union of this region with the given region
1385 * @param {Region} region The region to make union with
1386 * @return {Region} The union region. The smallest region that contains both this and the given region.
1387 */
1388
1389})
1390
1391module.exports = REGION
\No newline at end of file