]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/yui/build/imagecropper/imagecropper.js
Release 6.5.0
[Github/sugarcrm.git] / include / javascript / yui / build / imagecropper / imagecropper.js
1 /*
2 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 2.9.0
6 */
7 /**
8  * @description <p>Creates a Image Cropper control.</p>
9  * @namespace YAHOO.widget
10  * @requires yahoo, dom, dragdrop, element, event, resize
11  * @module imagecropper
12  */
13 (function() {
14 var Dom = YAHOO.util.Dom,
15     Event = YAHOO.util.Event,
16     Lang = YAHOO.lang;
17
18     /**
19      * @constructor
20      * @class ImageCropper
21      * @description <p>Creates a Image Cropper control.</p>
22      * @extends YAHOO.util.Element
23      * @param {String/HTMLElement} el The image element to make croppable.
24      * @param {Object} attrs Object liternal containing configuration parameters.
25     */
26     var Crop = function(el, config) {
27         var oConfig = {
28             element: el,
29             attributes: config || {}
30         };
31
32         Crop.superclass.constructor.call(this, oConfig.element, oConfig.attributes);    
33     };
34
35     /**
36     * @private
37     * @static
38     * @property _instances
39     * @description Internal hash table for all ImageCropper instances
40     * @type Object
41     */ 
42     Crop._instances = {};
43     /**
44     * @static
45     * @method getCropperById 
46     * @description Get's an ImageCropper object by the HTML id of the image associated with the ImageCropper object.
47     * @return {Object} The ImageCropper Object
48     */ 
49     Crop.getCropperById = function(id) {
50         if (Crop._instances[id]) {
51             return Crop._instances[id];
52         }
53         return false;
54     };
55
56     YAHOO.extend(Crop, YAHOO.util.Element, {
57         /**
58         * @private
59         * @property CSS_MAIN
60         * @description The CSS class used to wrap the element 
61         * @type String
62         */
63         CSS_MAIN: 'yui-crop',
64         /**
65         * @private
66         * @property CSS_MASK
67         * @description The CSS class for the mask element
68         * @type String
69         */
70         CSS_MASK: 'yui-crop-mask',
71         /**
72         * @private
73         * @property CSS_RESIZE_MASK
74         * @description The CSS class for the mask inside the resize element
75         * @type {HTML}
76         */
77         CSS_RESIZE_MASK: 'yui-crop-resize-mask',
78
79         /**
80         * @private
81         * @property _image
82         * @description The url of the image we are cropping
83         * @type String
84         */
85         _image: null,
86         /**
87         * @private
88         * @property _active
89         * @description Flag to determine if the crop region is active
90         * @type Boolean
91         */
92         _active: null,
93         /**
94         * @private
95         * @property _resize
96         * @description A reference to the Resize Utility used in this Cropper Instance
97         * @type Object
98         */
99         _resize: null,
100         /**
101         * @private
102         * @property _resizeEl
103         * @description The HTML Element used to create the Resize Oject
104         * @type HTMLElement
105         */
106         _resizeEl: null,
107         /**
108         * @private
109         * @property _resizeMaskEl
110         * @description The HTML Element used to create the Resize mask
111         * @type HTMLElement
112         */
113         _resizeMaskEl: null,
114         /**
115         * @private
116         * @property _wrap
117         * @description The HTML Element created to wrap the image
118         * @type HTMLElement
119         */
120         _wrap: null,
121         /**
122         * @private
123         * @property _mask
124         * @description The HTML Element created to "mask" the image being cropped
125         * @type HTMLElement
126         */
127         _mask: null,
128         /**
129         * @private
130         * @method _createWrap
131         * @description Creates the wrapper element used to wrap the image
132         */
133         _createWrap: function() {
134             this._wrap = document.createElement('div');
135             this._wrap.id = this.get('element').id + '_wrap';
136             this._wrap.className = this.CSS_MAIN;
137             var el = this.get('element');
138             this._wrap.style.width = el.width ? el.width + 'px' : Dom.getStyle(el, 'width');
139             this._wrap.style.height = el.height ? el.height + 'px' : Dom.getStyle(el, 'height');
140             
141             var par = this.get('element').parentNode;
142             par.replaceChild(this._wrap, this.get('element'));
143             this._wrap.appendChild(this.get('element'));
144
145             Event.on(this._wrap, 'mouseover', this._handleMouseOver, this, true);
146             Event.on(this._wrap, 'mouseout', this._handleMouseOut, this, true);
147
148             Event.on(this._wrap, 'click', function(ev) { Event.stopEvent(ev); }, this, true);
149         },
150
151         /**
152         * @private
153         * @method _createMask
154         * @description Creates the mask element used to mask the image
155         */
156         _createMask: function() {
157             this._mask = document.createElement('div');
158             this._mask.className = this.CSS_MASK;
159             this._wrap.appendChild(this._mask);
160         },
161
162         /**
163         * @private
164         * @method _createResize
165         * @description Creates the resize element and the instance of the Resize Utility
166         */
167         _createResize: function() {
168             this._resizeEl = document.createElement('div');
169             this._resizeEl.className = YAHOO.util.Resize.prototype.CSS_RESIZE;
170             this._resizeEl.style.position = 'absolute';
171             
172             this._resizeEl.innerHTML = '<div class="' + this.CSS_RESIZE_MASK + '"></div>';
173             this._resizeMaskEl = this._resizeEl.firstChild;
174             this._wrap.appendChild(this._resizeEl);
175             this._resizeEl.style.top = this.get('initialXY')[1] + 'px';
176             this._resizeEl.style.left = this.get('initialXY')[0] + 'px';
177             this._resizeMaskEl.style.height = Math.floor(this.get('initHeight')) + 'px';
178             this._resizeMaskEl.style.width = Math.floor(this.get('initWidth')) + 'px';
179
180             this._resize = new YAHOO.util.Resize(this._resizeEl, {
181                 knobHandles: true,
182                 handles: 'all',
183                 draggable: true,
184                 status: this.get('status'),
185                 minWidth: this.get('minWidth'),
186                 minHeight: this.get('minHeight'),
187                 ratio: this.get('ratio'),
188                 autoRatio: this.get('autoRatio'),
189                 height: this.get('initHeight'),
190                 width: this.get('initWidth')
191             });
192
193             this._setBackgroundImage(this.get('element').getAttribute('src', 2));
194             this._setBackgroundPosition(-(this.get('initialXY')[0]),  -(this.get('initialXY')[1]));
195
196             this._resize.on('startResize', this._handleStartResizeEvent, this, true);
197             this._resize.on('endResize', this._handleEndResizeEvent, this, true);
198             this._resize.on('dragEvent', this._handleDragEvent, this, true);
199             this._resize.on('beforeResize', this._handleBeforeResizeEvent, this, true);
200             this._resize.on('resize', this._handleResizeEvent, this, true);
201             this._resize.dd.on('b4StartDragEvent', this._handleB4DragEvent, this, true);
202         },
203
204         /**
205         * @private
206         * @method _handleMouseOver
207         * @description Handles the mouseover event
208         */
209         _handleMouseOver: function(ev) {
210             var evType = 'keydown';
211             if (YAHOO.env.ua.gecko || YAHOO.env.ua.opera) {
212                 evType = 'keypress';
213             }
214             if (!this._active) {
215                 this._active = true;
216                 if (this.get('useKeys')) {
217                     Event.on(document, evType, this._handleKeyPress, this, true);
218                 }
219             }
220         },
221         /**
222         * @private
223         * @method _handleMouseOut
224         * @description Handles the mouseout event
225         */
226         _handleMouseOut: function(ev) {
227             var evType = 'keydown';
228             if (YAHOO.env.ua.gecko || YAHOO.env.ua.opera) {
229                 evType = 'keypress';
230             }
231             this._active = false;
232             if (this.get('useKeys')) {
233                 Event.removeListener(document, evType, this._handleKeyPress);
234             }
235         },
236
237         /**
238         * @private
239         * @method _moveEl
240         * @description Moves the resize element based on the arrow keys
241         */
242         _moveEl: function(dir, inc) {
243             var t = 0, l = 0,
244                 region = this._setConstraints(),
245                 resize = true;
246
247             switch (dir) {
248                 case 'down':
249                     t = -(inc);
250                     if ((region.bottom - inc) < 0) {
251                         resize = false;
252                         this._resizeEl.style.top = (region.top + region.bottom) + 'px';
253                     }
254                     break;
255                 case 'up':
256                     t = (inc);
257                     if ((region.top - inc) < 0) {
258                         resize = false;
259                         this._resizeEl.style.top = '0px';
260                     }
261                     break;
262                 case 'right':
263                     l = -(inc);
264                     if ((region.right - inc) < 0) {
265                         resize = false;
266                         this._resizeEl.style.left = (region.left + region.right) + 'px';
267                     }
268                     break;
269                 case 'left':
270                     l = inc;
271                     if ((region.left - inc) < 0) {
272                         resize = false;
273                         this._resizeEl.style.left = '0px';
274                     }
275                     break;
276             }
277
278             if (resize) {
279                 this._resizeEl.style.left = (parseInt(this._resizeEl.style.left, 10) - l) + 'px';
280                 this._resizeEl.style.top = (parseInt(this._resizeEl.style.top, 10) - t) + 'px';
281                 this.fireEvent('moveEvent', { target: 'keypress' });
282             } else {
283                 this._setConstraints();
284             }
285             this._syncBackgroundPosition();
286         },
287
288         /**
289         * @private
290         * @method _handleKeyPress
291         * @description Handles the keypress event
292         */
293         _handleKeyPress: function(ev) {
294             var kc = Event.getCharCode(ev),
295                 stopEvent = false,
296                 inc = ((ev.shiftKey) ? this.get('shiftKeyTick') : this.get('keyTick'));
297
298             switch (kc) {
299                 case 0x25: // left
300                     this._moveEl('left', inc);
301                     stopEvent = true;
302                     break;
303                 case 0x26: // up
304                     this._moveEl('up', inc);
305                     stopEvent = true;
306                     break;
307                 case 0x27: // right
308                     this._moveEl('right', inc);
309                     stopEvent = true;
310                     break;
311                 case 0x28: // down
312                     this._moveEl('down', inc);
313                     stopEvent = true;
314                     break;
315                 default:
316             }
317             if (stopEvent) {
318                 Event.preventDefault(ev);
319             }
320         },
321
322         /**
323         * @private
324         * @method _handleB4DragEvent
325         * @description Handles the DragDrop b4DragEvent event
326         */
327         _handleB4DragEvent: function() {
328             this._setConstraints();
329         },
330
331         /**
332         * @private
333         * @method _handleDragEvent
334         * @description Handles the DragDrop DragEvent event
335         */
336         _handleDragEvent: function() {
337             this._syncBackgroundPosition();
338             this.fireEvent('dragEvent', arguments);
339             this.fireEvent('moveEvent', { target: 'dragevent' });
340         },
341
342         /**
343         * @private
344         * @method _handleBeforeResizeEvent
345         * @description Handles the Resize Utilitys beforeResize event
346         */
347         _handleBeforeResizeEvent: function(args) {
348             var region = Dom.getRegion(this.get('element')),
349                 c = this._resize._cache,
350                 ch = this._resize._currentHandle, h = 0, w = 0;
351
352             if (args.top && (args.top < region.top)) {
353                 h = (c.height + c.top) - region.top;
354                 Dom.setY(this._resize.getWrapEl(), region.top);
355                 this._resize.getWrapEl().style.height = h + 'px';
356                 this._resize._cache.height = h;
357                 this._resize._cache.top = region.top;
358                 this._syncBackgroundPosition();
359                 return false;
360             }
361             if (args.left && (args.left < region.left)) {
362                 w = (c.width + c.left) - region.left;
363                 Dom.setX(this._resize.getWrapEl(), region.left);
364                 this._resize._cache.left = region.left;
365                 this._resize.getWrapEl().style.width = w + 'px';
366                 this._resize._cache.width = w;
367                 this._syncBackgroundPosition();
368                 return false;
369             }
370             if (ch != 'tl' && ch != 'l' && ch != 'bl') {
371                 if (c.left && args.width && ((c.left + args.width) > region.right)) {
372                     w = (region.right - c.left);
373                     Dom.setX(this._resize.getWrapEl(), (region.right - w));
374                     this._resize.getWrapEl().style.width = w + 'px';
375                     this._resize._cache.left = (region.right - w);
376                     this._resize._cache.width = w;
377                     this._syncBackgroundPosition();
378                     return false;
379                 }
380             }
381             if (ch != 't' && ch != 'tr' && ch != 'tl') {
382                 if (c.top && args.height && ((c.top + args.height) > region.bottom)) {
383                     h = (region.bottom - c.top);
384                     Dom.setY(this._resize.getWrapEl(), (region.bottom - h));
385                     this._resize.getWrapEl().style.height = h + 'px';
386                     this._resize._cache.height = h;
387                     this._resize._cache.top = (region.bottom - h);
388                     this._syncBackgroundPosition();
389                     return false;
390                 }
391             }
392         },
393         /**
394         * @private
395         * @method _handleResizeMaskEl
396         * @description Resizes the inner mask element
397         */
398         _handleResizeMaskEl: function() {
399             var a = this._resize._cache;
400             this._resizeMaskEl.style.height = Math.floor(a.height) + 'px';
401             this._resizeMaskEl.style.width = Math.floor(a.width) + 'px';
402         },
403         /**
404         * @private
405         * @method _handleResizeEvent
406         * @param Event ev The Resize Utilitys resize event.
407         * @description Handles the Resize Utilitys Resize event
408         */
409         _handleResizeEvent: function(ev) {
410             this._setConstraints(true);
411             this._syncBackgroundPosition();
412             this.fireEvent('resizeEvent', arguments);
413             this.fireEvent('moveEvent', { target: 'resizeevent' });
414         },
415
416         /**
417         * @private
418         * @method _syncBackgroundPosition
419         * @description Syncs the packground position of the resize element with the resize elements top and left style position
420         */
421         _syncBackgroundPosition: function() {
422             this._handleResizeMaskEl();
423             this._setBackgroundPosition(-(parseInt(this._resizeEl.style.left, 10)), -(parseInt(this._resizeEl.style.top, 10)));
424         },
425
426         /**
427         * @private
428         * @method _setBackgroundPosition
429         * @param Number l The left position
430         * @param Number t The top position
431         * @description Sets the background image position to the top and left position
432         */
433         _setBackgroundPosition: function(l, t) {
434             var bl = parseInt(Dom.getStyle(this._resize.get('element'), 'borderLeftWidth'), 10);
435             var bt = parseInt(Dom.getStyle(this._resize.get('element'), 'borderTopWidth'), 10);
436             if (isNaN(bl)) {
437                 bl = 0;
438             }
439             if (isNaN(bt)) {
440                 bt = 0;
441             }
442             var mask = this._resize.getWrapEl().firstChild;
443             var pos = (l - bl) + 'px ' + (t - bt) + 'px';
444             this._resizeMaskEl.style.backgroundPosition = pos;
445         },
446
447         /**
448         * @private
449         * @method _setBackgroundImage
450         * @param String url The url of the image
451         * @description Sets the background image of the resize element
452         */
453         _setBackgroundImage: function(url) {
454             var mask = this._resize.getWrapEl().firstChild;
455             this._image = url;
456             mask.style.backgroundImage = 'url(' + url + '#)';
457         },
458         
459         /**
460         * @private
461         * @method _handleEndResizeEvent
462         * @description Handles the Resize Utilitys endResize event
463         */
464         _handleEndResizeEvent: function() {
465             this._setConstraints(true);
466         },
467         /**
468         * @private
469         * @method _handleStartResizeEvent
470         * @description Handles the Resize Utilitys startResize event
471         */
472         _handleStartResizeEvent: function() {
473             this._setConstraints(true);
474
475             var h = this._resize._cache.height,
476                  w = this._resize._cache.width,
477                  t = parseInt(this._resize.getWrapEl().style.top, 10),
478                  l = parseInt(this._resize.getWrapEl().style.left, 10),
479                  maxH = 0, maxW = 0;
480  
481             switch (this._resize._currentHandle) {
482                 case 'b':
483                     maxH = (h + this._resize.dd.bottomConstraint);
484                     break;
485                 case 'l':
486                     maxW = (w + this._resize.dd.leftConstraint);
487                     break;
488                 case 'r':
489                     maxH = (h + t);
490                     maxW = (w + this._resize.dd.rightConstraint);
491                     break;
492                  case 'br':
493                      maxH = (h + this._resize.dd.bottomConstraint);
494                      maxW = (w + this._resize.dd.rightConstraint);
495                      break;
496                  case 'tr':
497                      maxH = (h + t);
498                      maxW = (w + this._resize.dd.rightConstraint);
499                      break;
500
501              }
502             
503              if (maxH) {
504                  //this._resize.set('maxHeight', maxH);
505              }
506              if (maxW) {
507                  //this._resize.set('maxWidth', maxW);
508              }
509
510             this.fireEvent('startResizeEvent', arguments);
511         },
512         
513         /**
514         * @private
515         * @method _setConstraints
516         * @param Boolean inside Used when called from inside a resize event, false by default (dragging)
517         * @description Set the DragDrop constraints to keep the element inside the crop area.
518         * @return {Object} Object containing Top, Right, Bottom and Left constraints
519         */
520         _setConstraints: function(inside) {
521             var resize = this._resize;
522             resize.dd.resetConstraints();
523             var height = parseInt(resize.get('height'), 10),
524                 width = parseInt(resize.get('width'), 10);
525             if (inside) {
526                 //Called from inside the resize callback
527                 height = resize._cache.height;
528                 width = resize._cache.width;
529             }
530
531             //Get the top, right, bottom and left positions
532             var region = Dom.getRegion(this.get('element'));
533             //Get the element we are working on
534             var el = resize.getWrapEl();
535
536             //Get the xy position of it
537             var xy = Dom.getXY(el);
538
539             //Set left to x minus left
540             var left = xy[0] - region.left;
541
542             //Set right to right minus x minus width
543             var right = region.right - xy[0] - width;
544
545             //Set top to y minus top
546             var top = xy[1] - region.top;
547
548             //Set bottom to bottom minus y minus height
549             var bottom = region.bottom - xy[1] - height;
550
551             if (top < 0) {
552                 top = 0;
553             }
554             
555             resize.dd.setXConstraint(left, right); 
556             resize.dd.setYConstraint(top, bottom);
557
558             return {
559                 top: top,
560                 right: right,
561                 bottom: bottom,
562                 left: left
563             };
564
565             
566             
567         },
568         /**
569         * @method getCropCoords
570         * @description Returns the coordinates needed to crop the image
571         * @return {Object} The top, left, height, width and image url of the image being cropped
572         */
573         getCropCoords: function() {
574             var coords = {
575                 top: parseInt(this._resize.getWrapEl().style.top, 10),
576                 left: parseInt(this._resize.getWrapEl().style.left, 10),
577                 height: this._resize._cache.height,
578                 width: this._resize._cache.width,
579                 image: this._image
580             };
581             return coords;
582         },
583         /**
584         * @method reset
585         * @description Resets the crop element back to it's original position
586         * @return {<a href="YAHOO.widget.ImageCropper.html">YAHOO.widget.ImageCropper</a>} The ImageCropper instance
587         */
588         reset: function() {
589             this._resize.resize(null, this.get('initHeight'), this.get('initWidth'), 0, 0, true);
590             this._resizeEl.style.top = this.get('initialXY')[1] + 'px';
591             this._resizeEl.style.left = this.get('initialXY')[0] + 'px';
592             this._syncBackgroundPosition();
593             return this;
594         },
595
596         /**
597         * @method getEl
598         * @description Get the HTML reference for the image element.
599         * @return {HTMLElement} The image element
600         */      
601         getEl: function() {
602             return this.get('element');
603         },
604         /**
605         * @method getResizeEl
606         * @description Get the HTML reference for the resize element.
607         * @return {HTMLElement} The resize element
608         */      
609         getResizeEl: function() {
610             return this._resizeEl;
611         },
612         /**
613         * @method getWrapEl
614         * @description Get the HTML reference for the wrap element.
615         * @return {HTMLElement} The wrap element
616         */      
617         getWrapEl: function() {
618             return this._wrap;
619         },
620
621         /**
622         * @method getMaskEl
623         * @description Get the HTML reference for the mask element.
624         * @return {HTMLElement} The mask element
625         */      
626         getMaskEl: function() {
627             return this._mask;
628         },
629
630         /**
631         * @method getResizeMaskEl
632         * @description Get the HTML reference for the resizable object's mask element.
633         * @return {HTMLElement} The resize objects mask element.
634         */      
635         getResizeMaskEl: function() {
636             return this._resizeMaskEl;
637         },
638
639         /**
640         * @method getResizeObject
641         * @description Get the Resize Utility object.
642         * @return {<a href="YAHOO.util.Resize.html">YAHOO.util.Resize</a>} The Resize instance
643         */      
644         getResizeObject: function() {
645             return this._resize;
646         },
647
648         /** 
649         * @private
650         * @method init
651         * @description The ImageCropper class's initialization method
652         */        
653         init: function(p_oElement, p_oAttributes) {
654             Crop.superclass.init.call(this, p_oElement, p_oAttributes);
655
656             var id = p_oElement;
657
658             if (!Lang.isString(id)) {
659                 if (id.tagName && (id.tagName.toLowerCase() == 'img')) {
660                     id = Dom.generateId(id);                    
661                 } else {
662                     return false;
663                 }
664             } else {
665                 var el = Dom.get(id);
666                 if (el.tagName && el.tagName.toLowerCase() == 'img') {
667                     //All good
668                 } else {
669                     return false;
670                 }
671             }
672             
673
674
675             Crop._instances[id] = this;
676             this._createWrap();
677             this._createMask();
678             this._createResize();
679             this._setConstraints();
680
681         },
682         /**
683         * @private
684         * @method initAttributes
685         * @description Initializes all of the configuration attributes used to create a croppable element.
686         * @param {Object} attr Object literal specifying a set of 
687         * configuration attributes used to create the widget.
688         */      
689
690         initAttributes: function(attr) {
691             Crop.superclass.initAttributes.call(this, attr);
692
693             /**
694             * @attribute initialXY
695             * @description Array of the XY position that we need to set the crop element to when we build it. Defaults to [10, 10]
696             * @type Array
697             */
698             this.setAttributeConfig('initialXY', {
699                 validator: YAHOO.lang.isArray,
700                 value: attr.initialXY || [10, 10]
701             });
702             /**
703             * @attribute keyTick
704             * @description The pixel tick for the arrow keys, defaults to 1
705             * @type Number
706             */
707             this.setAttributeConfig('keyTick', {
708                 validator: YAHOO.lang.isNumber,
709                 value: attr.keyTick || 1
710             });
711
712             /**
713             * @attribute shiftKeyTick
714             * @description The pixel tick for shift + the arrow keys, defaults to 10
715             * @type Number
716             */
717             this.setAttributeConfig('shiftKeyTick', {
718                 validator: YAHOO.lang.isNumber,
719                 value: attr.shiftKeyTick || 10
720             });
721
722             /**
723             * @attribute useKeys
724             * @description Should we use the Arrow keys to position the crop element, defaults to true
725             * @type Boolean
726             */
727             this.setAttributeConfig('useKeys', {
728                 validator: YAHOO.lang.isBoolean,
729                 value: ((attr.useKeys === false) ? false : true)
730             });
731
732             /**
733             * @attribute status
734             * @description Show the Resize Utility status, defaults to true
735             * @type Boolean
736             */
737             this.setAttributeConfig('status', {
738                 validator: YAHOO.lang.isBoolean,
739                 value: ((attr.status === false) ? false : true),
740                 method: function(status) {
741                     if (this._resize) {
742                         this._resize.set('status', status);
743                     }
744                 }
745             });
746
747             /**
748             * @attribute minHeight
749             * @description MinHeight of the crop area, default 50
750             * @type Number
751             */
752             this.setAttributeConfig('minHeight', {
753                 validator: YAHOO.lang.isNumber,
754                 value: attr.minHeight || 50,
755                 method: function(h) {
756                     if (this._resize) {
757                         this._resize.set('minHeight', h);
758                     }
759                 }
760             });
761
762             /**
763             * @attribute minWidth
764             * @description MinWidth of the crop area, default 50.
765             * @type Number
766             */
767             this.setAttributeConfig('minWidth', {
768                 validator: YAHOO.lang.isNumber,
769                 value: attr.minWidth || 50,
770                 method: function(w) {
771                     if (this._resize) {
772                         this._resize.set('minWidth', w);
773                     }
774                 }
775             });
776
777             /**
778             * @attribute ratio
779             * @description Set the ratio config option of the Resize Utlility, default false
780             * @type Boolean
781             */
782             this.setAttributeConfig('ratio', {
783                 validator: YAHOO.lang.isBoolean,
784                 value: attr.ratio || false,
785                 method: function(r) {
786                     if (this._resize) {
787                         this._resize.set('ratio', r);
788                     }
789                 }
790             });
791
792             /**
793             * @attribute ratio
794             * @description Set the autoRatio config option of the Resize Utlility, default true
795             * @type Boolean
796             */
797             this.setAttributeConfig('autoRatio', {
798                 validator: YAHOO.lang.isBoolean,
799                 value: ((attr.autoRatio === false) ? false : true),
800                 method: function(a) {
801                     if (this._resize) {
802                         this._resize.set('autoRatio', a);
803                     }
804                 }
805             });
806
807             /**
808             * @attribute initHeight
809             * @description Set the initlal height of the crop area, defaults to 1/4 of the image height
810             * @type Number
811             */
812             this.setAttributeConfig('initHeight', {
813                 writeOnce: true,
814                 validator: YAHOO.lang.isNumber,
815                 value: attr.initHeight || (this.get('element').height / 4)
816             });
817
818             /**
819             * @attribute initWidth
820             * @description Set the initlal width of the crop area, defaults to 1/4 of the image width
821             * @type Number
822             */
823             this.setAttributeConfig('initWidth', {
824                 validator: YAHOO.lang.isNumber,
825                 writeOnce: true,
826                 value: attr.initWidth || (this.get('element').width / 4)
827             });
828
829         },
830         /**
831         * @method destroy
832         * @description Destroys the ImageCropper object and all of it's elements & listeners.
833         */        
834         destroy: function() {
835             this._resize.destroy();
836             this._resizeEl.parentNode.removeChild(this._resizeEl);
837             this._mask.parentNode.removeChild(this._mask);
838             Event.purgeElement(this._wrap);
839             this._wrap.parentNode.replaceChild(this.get('element'), this._wrap);
840             
841             //Brutal Object Destroy
842             for (var i in this) {
843                 if (Lang.hasOwnProperty(this, i)) {
844                     this[i] = null;
845                 }
846             }
847                        
848         },
849         /**
850         * @method toString
851         * @description Returns a string representing the ImageCropper Object.
852         * @return {String}
853         */        
854         toString: function() {
855             if (this.get) {
856                 return 'ImageCropper (#' + this.get('id') + ')';
857             }
858             return 'Image Cropper';
859         }
860     });
861
862     YAHOO.widget.ImageCropper = Crop;
863
864 /**
865 * @event dragEvent
866 * @description Fires when the DragDrop dragEvent
867 * @type YAHOO.util.CustomEvent
868 */
869 /**
870 * @event startResizeEvent
871 * @description Fires when when a resize action is started.
872 * @type YAHOO.util.CustomEvent
873 */
874 /**
875 * @event resizeEvent
876 * @description Fires on every element resize.
877 * @type YAHOO.util.CustomEvent
878 */
879 /**
880 * @event moveEvent
881 * @description Fires on every element move. Inside these methods: _handleKeyPress, _handleDragEvent, _handleResizeEvent
882 * @type YAHOO.util.CustomEvent
883 */
884
885 })();
886
887 YAHOO.register("imagecropper", YAHOO.widget.ImageCropper, {version: "2.9.0", build: "2800"});