2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
8 * @description <p>Makes an element resizable</p>
9 * @namespace YAHOO.util
10 * @requires yahoo, dom, dragdrop, element, event
15 var D = YAHOO.util.Dom,
16 Event = YAHOO.util.Event,
22 * @extends YAHOO.util.Element
23 * @description <p>Makes an element resizable</p>
24 * @param {String/HTMLElement} el The element to make resizable.
25 * @param {Object} attrs Object liternal containing configuration parameters.
28 var Resize = function(el, config) {
29 YAHOO.log('Creating Resize Object', 'info', 'Resize');
32 attributes: config || {}
35 Resize.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
41 * @property _instances
42 * @description Internal hash table for all resize instances
45 Resize._instances = {};
48 * @method getResizeById
49 * @description Get's a resize object by the HTML id of the element associated with the Resize object.
50 * @return {Object} The Resize Object
52 Resize.getResizeById = function(id) {
53 if (Resize._instances[id]) {
54 return Resize._instances[id];
56 YAHOO.log('No Instance Found', 'error', 'Resize');
60 YAHOO.extend(Resize, YAHOO.util.Element, {
63 * @property CSS_RESIZE
64 * @description Base CSS class name
67 CSS_RESIZE: 'yui-resize',
71 * @description Class name added when dragging is enabled
74 CSS_DRAG: 'yui-draggable',
78 * @description Class name used for hover only handles
81 CSS_HOVER: 'yui-resize-hover',
85 * @description Class name given to the proxy element
88 CSS_PROXY: 'yui-resize-proxy',
92 * @description Class name given to the wrap element
95 CSS_WRAP: 'yui-resize-wrap',
99 * @description Class name used to make the knob style handles
102 CSS_KNOB: 'yui-resize-knob',
105 * @property CSS_HIDDEN
106 * @description Class name given to the wrap element to make all handles hidden
109 CSS_HIDDEN: 'yui-resize-hidden',
112 * @property CSS_HANDLE
113 * @description Class name given to all handles, used as a base for single handle names as well.. Handle "t" will get this.CSS_HANDLE + '-t' as well as this.CSS_HANDLE
116 CSS_HANDLE: 'yui-resize-handle',
119 * @property CSS_STATUS
120 * @description Class name given to the status element
123 CSS_STATUS: 'yui-resize-status',
126 * @property CSS_GHOST
127 * @description Class name given to the wrap element when the ghost property is active
130 CSS_GHOST: 'yui-resize-ghost',
133 * @property CSS_RESIZING
134 * @description Class name given to the wrap element when a resize action is taking place.
137 CSS_RESIZING: 'yui-resize-resizing',
140 * @property _resizeEvent
141 * @description The mouse event used to resize with
148 * @description The <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> instance used if draggable is true
155 * @description A copy of the YAHOO.env.ua property
158 browser: YAHOO.env.ua,
162 * @description A flag to show if the resize is locked
168 * @property _positioned
169 * @description A flag to show if the element is absolutely positioned
176 * @description An Object containing references to all of the <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> instances used for the resize handles
183 * @description The HTML reference of the element wrapper
190 * @description The HTML reference of the element proxy
197 * @description An object containing references to all of the resize handles.
203 * @property _currentHandle
204 * @description The string identifier of the currently active handle. e.g. 'r', 'br', 'tl'
207 _currentHandle: null,
210 * @property _currentDD
211 * @description A link to the currently active DD object
218 * @description An lookup table containing key information for the element being resized. e.g. height, width, x position, y position, etc..
225 * @description Flag to show if the resize is active. Used for events.
231 * @method _createProxy
232 * @description Creates the proxy element if the proxy config is true
234 _createProxy: function() {
235 if (this.get('proxy')) {
236 YAHOO.log('Creating the Proxy Element', 'info', 'Resize');
237 this._proxy = document.createElement('div');
238 this._proxy.className = this.CSS_PROXY;
239 this._proxy.style.height = this.get('element').clientHeight + 'px';
240 this._proxy.style.width = this.get('element').clientWidth + 'px';
241 this._wrap.parentNode.appendChild(this._proxy);
243 YAHOO.log('No proxy element, turn off animate config option', 'info', 'Resize');
244 this.set('animate', false);
249 * @method _createWrap
250 * @description Creates the wrap element if the wrap config is true. It will auto wrap the following element types: img, textarea, input, iframe, select
252 _createWrap: function() {
253 YAHOO.log('Create the wrap element', 'info', 'Resize');
254 this._positioned = false;
255 //Force wrap for elements that can't have children
256 if (this.get('wrap') === false) {
257 switch (this.get('element').tagName.toLowerCase()) {
263 YAHOO.log('Auto-wrapping the element (' + this.get('element').tagName.toLowerCase() + ')', 'warn', 'Resize');
264 this.set('wrap', true);
268 if (this.get('wrap') === true) {
269 YAHOO.log('Creating the wrap element', 'info', 'Resize');
270 this._wrap = document.createElement('div');
271 this._wrap.id = this.get('element').id + '_wrap';
272 this._wrap.className = this.CSS_WRAP;
273 if (this.get('element').tagName.toLowerCase() == 'textarea') {
274 D.addClass(this._wrap, 'yui-resize-textarea');
276 D.setStyle(this._wrap, 'width', this.get('width') + 'px');
277 D.setStyle(this._wrap, 'height', this.get('height') + 'px');
278 D.setStyle(this._wrap, 'z-index', this.getStyle('z-index'));
279 this.setStyle('z-index', 0);
280 var pos = D.getStyle(this.get('element'), 'position');
281 D.setStyle(this._wrap, 'position', ((pos == 'static') ? 'relative' : pos));
282 D.setStyle(this._wrap, 'top', D.getStyle(this.get('element'), 'top'));
283 D.setStyle(this._wrap, 'left', D.getStyle(this.get('element'), 'left'));
284 if (D.getStyle(this.get('element'), 'position') == 'absolute') {
285 this._positioned = true;
286 YAHOO.log('The element is positioned absolute', 'info', 'Resize');
287 D.setStyle(this.get('element'), 'position', 'relative');
288 D.setStyle(this.get('element'), 'top', '0');
289 D.setStyle(this.get('element'), 'left', '0');
291 var par = this.get('element').parentNode;
292 par.replaceChild(this._wrap, this.get('element'));
293 this._wrap.appendChild(this.get('element'));
295 this._wrap = this.get('element');
296 if (D.getStyle(this._wrap, 'position') == 'absolute') {
297 this._positioned = true;
300 if (this.get('draggable')) {
301 this._setupDragDrop();
303 if (this.get('hover')) {
304 D.addClass(this._wrap, this.CSS_HOVER);
306 if (this.get('knobHandles')) {
307 D.addClass(this._wrap, this.CSS_KNOB);
309 if (this.get('hiddenHandles')) {
310 D.addClass(this._wrap, this.CSS_HIDDEN);
312 D.addClass(this._wrap, this.CSS_RESIZE);
316 * @method _setupDragDrop
317 * @description Setup the <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> instance on the element
319 _setupDragDrop: function() {
320 YAHOO.log('Setting up the dragdrop instance on the element', 'info', 'Resize');
321 D.addClass(this._wrap, this.CSS_DRAG);
322 this.dd = new YAHOO.util.DD(this._wrap, this.get('id') + '-resize', { dragOnly: true, useShim: this.get('useShim') });
323 this.dd.on('dragEvent', function() {
324 this.fireEvent('dragEvent', arguments);
329 * @method _createHandles
330 * @description Creates the handles as specified in the config
332 _createHandles: function() {
333 YAHOO.log('Creating the handles', 'info', 'Resize');
336 var h = this.get('handles');
337 for (var i = 0; i < h.length; i++) {
338 YAHOO.log('Creating handle position: ' + h[i], 'info', 'Resize');
339 this._handles[h[i]] = document.createElement('div');
340 this._handles[h[i]].id = D.generateId(this._handles[h[i]]);
341 this._handles[h[i]].className = this.CSS_HANDLE + ' ' + this.CSS_HANDLE + '-' + h[i];
342 var k = document.createElement('div');
343 k.className = this.CSS_HANDLE + '-inner-' + h[i];
344 this._handles[h[i]].appendChild(k);
345 this._wrap.appendChild(this._handles[h[i]]);
346 Event.on(this._handles[h[i]], 'mouseover', this._handleMouseOver, this, true);
347 Event.on(this._handles[h[i]], 'mouseout', this._handleMouseOut, this, true);
348 this._dds[h[i]] = new YAHOO.util.DragDrop(this._handles[h[i]], this.get('id') + '-handle-' + h, { useShim: this.get('useShim') });
349 this._dds[h[i]].setPadding(15, 15, 15, 15);
350 this._dds[h[i]].on('startDragEvent', this._handleStartDrag, this._dds[h[i]], this);
351 this._dds[h[i]].on('mouseDownEvent', this._handleMouseDown, this._dds[h[i]], this);
353 YAHOO.log('Creating the Status box', 'info', 'Resize');
354 this._status = document.createElement('span');
355 this._status.className = this.CSS_STATUS;
356 document.body.insertBefore(this._status, document.body.firstChild);
360 * @method _ieSelectFix
361 * @description The function we use as the onselectstart handler when we start a drag in Internet Explorer
363 _ieSelectFix: function() {
368 * @property _ieSelectBack
369 * @description We will hold a copy of the current "onselectstart" method on this property, and reset it after we are done using it.
374 * @method _setAutoRatio
375 * @param {Event} ev A mouse event.
376 * @description This method checks to see if the "autoRatio" config is set. If it is, we will check to see if the "Shift Key" is pressed. If so, we will set the config ratio to true.
378 _setAutoRatio: function(ev) {
379 if (this.get('autoRatio')) {
380 YAHOO.log('Setting up AutoRatio', 'info', 'Resize');
381 if (ev && ev.shiftKey) {
383 YAHOO.log('Shift key presses, turning on ratio', 'info', 'Resize');
384 this.set('ratio', true);
386 YAHOO.log('Resetting ratio back to default', 'info', 'Resize');
387 this.set('ratio', this._configs.ratio._initialConfig.value);
393 * @method _handleMouseDown
394 * @param {Event} ev A mouse event.
395 * @description This method preps the autoRatio on MouseDown.
397 _handleMouseDown: function(ev) {
399 YAHOO.log('Resize Locked', 'info', 'Resize');
402 if (D.getStyle(this._wrap, 'position') == 'absolute') {
403 this._positioned = true;
406 this._setAutoRatio(ev);
408 if (this.browser.ie) {
409 this._ieSelectBack = document.body.onselectstart;
410 document.body.onselectstart = this._ieSelectFix;
415 * @method _handleMouseOver
416 * @param {Event} ev A mouse event.
417 * @description Adds CSS class names to the handles
419 _handleMouseOver: function(ev) {
421 YAHOO.log('Resize Locked', 'info', 'Resize');
424 D.removeClass(this._wrap, this.CSS_RESIZE);
426 if (this.get('hover')) {
427 D.removeClass(this._wrap, this.CSS_HOVER);
429 var tar = Event.getTarget(ev);
430 if (!D.hasClass(tar, this.CSS_HANDLE)) {
431 tar = tar.parentNode;
433 if (D.hasClass(tar, this.CSS_HANDLE) && !this._active) {
434 D.addClass(tar, this.CSS_HANDLE + '-active');
435 for (var i in this._handles) {
436 if (Lang.hasOwnProperty(this._handles, i)) {
437 if (this._handles[i] == tar) {
438 D.addClass(tar, this.CSS_HANDLE + '-' + i + '-active');
445 D.addClass(this._wrap, this.CSS_RESIZE);
449 * @method _handleMouseOut
450 * @param {Event} ev A mouse event.
451 * @description Removes CSS class names to the handles
453 _handleMouseOut: function(ev) {
454 D.removeClass(this._wrap, this.CSS_RESIZE);
455 if (this.get('hover') && !this._active) {
456 D.addClass(this._wrap, this.CSS_HOVER);
458 var tar = Event.getTarget(ev);
459 if (!D.hasClass(tar, this.CSS_HANDLE)) {
460 tar = tar.parentNode;
462 if (D.hasClass(tar, this.CSS_HANDLE) && !this._active) {
463 D.removeClass(tar, this.CSS_HANDLE + '-active');
464 for (var i in this._handles) {
465 if (Lang.hasOwnProperty(this._handles, i)) {
466 if (this._handles[i] == tar) {
467 D.removeClass(tar, this.CSS_HANDLE + '-' + i + '-active');
473 D.addClass(this._wrap, this.CSS_RESIZE);
477 * @method _handleStartDrag
478 * @param {Object} args The args passed from the CustomEvent.
479 * @param {Object} dd The <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> object we are working with.
480 * @description Resizes the proxy, sets up the <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> handlers, updates the status div and preps the cache
482 _handleStartDrag: function(args, dd) {
483 YAHOO.log('startDrag', 'info', 'Resize');
484 var tar = dd.getDragEl();
485 if (D.hasClass(tar, this.CSS_HANDLE)) {
486 if (D.getStyle(this._wrap, 'position') == 'absolute') {
487 this._positioned = true;
490 this._currentDD = dd;
492 YAHOO.log('Activate proxy element', 'info', 'Resize');
493 this._proxy.style.visibility = 'visible';
494 this._proxy.style.zIndex = '1000';
495 this._proxy.style.height = this.get('element').clientHeight + 'px';
496 this._proxy.style.width = this.get('element').clientWidth + 'px';
499 for (var i in this._handles) {
500 if (Lang.hasOwnProperty(this._handles, i)) {
501 if (this._handles[i] == tar) {
502 this._currentHandle = i;
503 var handle = '_handle_for_' + i;
504 D.addClass(tar, this.CSS_HANDLE + '-' + i + '-active');
505 dd.on('dragEvent', this[handle], this, true);
506 dd.on('mouseUpEvent', this._handleMouseUp, this, true);
507 YAHOO.log('Adding DragEvents to: ' + i, 'info', 'Resize');
514 D.addClass(tar, this.CSS_HANDLE + '-active');
516 if (this.get('proxy')) {
517 YAHOO.log('Posiiton Proxy Element', 'info', 'Resize');
518 var xy = D.getXY(this.get('element'));
519 D.setXY(this._proxy, xy);
520 if (this.get('ghost')) {
521 YAHOO.log('Add Ghost Class', 'info', 'Resize');
522 this.addClass(this.CSS_GHOST);
525 D.addClass(this._wrap, this.CSS_RESIZING);
527 this._updateStatus(this._cache.height, this._cache.width, this._cache.top, this._cache.left);
528 YAHOO.log('Firing startResize Event', 'info', 'Resize');
529 this.fireEvent('startResize', { type: 'startresize', target: this});
535 * @description Sets up the this._cache hash table.
537 _setCache: function() {
538 YAHOO.log('Setting up property cache', 'info', 'Resize');
539 this._cache.xy = D.getXY(this._wrap);
540 D.setXY(this._wrap, this._cache.xy);
541 this._cache.height = this.get('clientHeight');
542 this._cache.width = this.get('clientWidth');
543 this._cache.start.height = this._cache.height;
544 this._cache.start.width = this._cache.width;
545 this._cache.start.top = this._cache.xy[1];
546 this._cache.start.left = this._cache.xy[0];
547 this._cache.top = this._cache.xy[1];
548 this._cache.left = this._cache.xy[0];
549 this.set('height', this._cache.height, true);
550 this.set('width', this._cache.width, true);
554 * @method _handleMouseUp
555 * @param {Event} ev A mouse event.
556 * @description Cleans up listeners, hides proxy element and removes class names.
558 _handleMouseUp: function(ev) {
559 this._active = false;
561 var handle = '_handle_for_' + this._currentHandle;
562 this._currentDD.unsubscribe('dragEvent', this[handle], this, true);
563 this._currentDD.unsubscribe('mouseUpEvent', this._handleMouseUp, this, true);
566 YAHOO.log('Hide Proxy Element', 'info', 'Resize');
567 this._proxy.style.visibility = 'hidden';
568 this._proxy.style.zIndex = '-1';
569 if (this.get('setSize')) {
570 YAHOO.log('Setting Size', 'info', 'Resize');
571 this.resize(ev, this._cache.height, this._cache.width, this._cache.top, this._cache.left, true);
573 YAHOO.log('Firing Resize Event', 'info', 'Resize');
574 this.fireEvent('resize', { ev: 'resize', target: this, height: this._cache.height, width: this._cache.width, top: this._cache.top, left: this._cache.left });
577 if (this.get('ghost')) {
578 YAHOO.log('Removing Ghost Class', 'info', 'Resize');
579 this.removeClass(this.CSS_GHOST);
583 if (this.get('hover')) {
584 D.addClass(this._wrap, this.CSS_HOVER);
587 D.setStyle(this._status, 'display', 'none');
589 if (this.browser.ie) {
590 YAHOO.log('Resetting IE onselectstart function', 'info', 'Resize');
591 document.body.onselectstart = this._ieSelectBack;
594 if (this.browser.ie) {
595 D.removeClass(this._wrap, this.CSS_RESIZE);
598 for (var i in this._handles) {
599 if (Lang.hasOwnProperty(this._handles, i)) {
600 D.removeClass(this._handles[i], this.CSS_HANDLE + '-active');
603 if (this.get('hover') && !this._active) {
604 D.addClass(this._wrap, this.CSS_HOVER);
606 D.removeClass(this._wrap, this.CSS_RESIZING);
608 D.removeClass(this._handles[this._currentHandle], this.CSS_HANDLE + '-' + this._currentHandle + '-active');
609 D.removeClass(this._handles[this._currentHandle], this.CSS_HANDLE + '-active');
611 if (this.browser.ie) {
612 D.addClass(this._wrap, this.CSS_RESIZE);
615 this._resizeEvent = null;
616 this._currentHandle = null;
618 if (!this.get('animate')) {
619 this.set('height', this._cache.height, true);
620 this.set('width', this._cache.width, true);
623 YAHOO.log('Firing endResize Event', 'info', 'Resize');
624 this.fireEvent('endResize', { ev: 'endResize', target: this, height: this._cache.height, width: this._cache.width, top: this._cache.top, left: this._cache.left });
629 * @param {Number} h The height offset.
630 * @param {Number} w The with offset.
631 * @param {Number} t The top offset.
632 * @param {Number} l The left offset.
633 * @description Using the Height, Width, Top & Left, it recalcuates them based on the original element size.
634 * @return {Array} The new Height, Width, Top & Left settings
636 _setRatio: function(h, w, t, l) {
637 YAHOO.log('Setting Ratio', 'info', 'Resize');
639 if (this.get('ratio')) {
640 var orgH = this._cache.height,
641 orgW = this._cache.width,
642 nh = parseInt(this.get('height'), 10),
643 nw = parseInt(this.get('width'), 10),
644 maxH = this.get('maxHeight'),
645 minH = this.get('minHeight'),
646 maxW = this.get('maxWidth'),
647 minW = this.get('minWidth');
649 switch (this._currentHandle) {
652 h = Math.min(Math.max(minH, h), maxH);
654 t = (this._cache.start.top - (-((nh - h) / 2)));
655 l = (this._cache.start.left - (-((nw - w))));
659 h = Math.min(Math.max(minH, h), maxH);
661 t = (this._cache.start.top - (-((nh - h) / 2)));
666 l = (this._cache.start.left - (-((nw - w) / 2)));
667 t = (this._cache.start.top - (-((nh - h))));
672 l = (this._cache.start.left - (-((nw - w) / 2)));
677 l = (this._cache.start.left - (-((nw - w))));
686 l = (this._cache.start.left - (-((nw - w))));
687 t = (this._cache.start.top - (-((nh - h))));
692 l = (this._cache.start.left);
693 t = (this._cache.start.top - (-((nh - h))));
696 oh = this._checkHeight(h);
697 ow = this._checkWidth(w);
698 if ((oh != h) || (ow != w)) {
702 ow = this._cache.width;
705 oh = this._cache.height;
709 return [oh, ow, t, l];
713 * @method _updateStatus
714 * @param {Number} h The new height setting.
715 * @param {Number} w The new width setting.
716 * @param {Number} t The new top setting.
717 * @param {Number} l The new left setting.
718 * @description Using the Height, Width, Top & Left, it updates the status element with the elements sizes.
720 _updateStatus: function(h, w, t, l) {
721 if (this._resizeEvent && (!Lang.isString(this._resizeEvent))) {
722 YAHOO.log('Updating Status Box', 'info', 'Resize');
723 h = ((h === 0) ? this._cache.start.height : h);
724 w = ((w === 0) ? this._cache.start.width : w);
725 var h1 = parseInt(this.get('height'), 10),
726 w1 = parseInt(this.get('width'), 10);
729 h1 = parseInt(h, 10);
732 w1 = parseInt(w, 10);
734 var diffH = (parseInt(h, 10) - h1);
735 var diffW = (parseInt(w, 10) - w1);
736 this._cache.offsetHeight = diffH;
737 this._cache.offsetWidth = diffW;
738 if (this.get('status')) {
739 YAHOO.log('Showing Status Box', 'info', 'Resize');
740 D.setStyle(this._status, 'display', 'inline');
741 //This will cause IE8 to crash if the status box is hidden..
742 this._status.innerHTML = '<strong>' + parseInt(h, 10) + ' x ' + parseInt(w, 10) + '</strong><em>' + ((diffH > 0) ? '+' : '') + diffH + ' x ' + ((diffW > 0) ? '+' : '') + diffW + '</em>';
743 D.setXY(this._status, [Event.getPageX(this._resizeEvent) + 12, Event.getPageY(this._resizeEvent) + 12]);
749 * @description Lock the resize so it can't be resized
750 * @param {Boolean} dd If the draggable config is set, lock it too
751 * @return {<a href="YAHOO.util.Resize.html">YAHOO.util.Resize</a>} The Resize instance
756 D.removeClass(this._wrap, 'yui-draggable');
763 * @description Unlock the resize so it can be resized
764 * @param {Boolean} dd If the draggable config is set, unlock it too
765 * @return {<a href="YAHOO.util.Resize.html">YAHOO.util.Resize</a>} The Resize instance
767 unlock: function(dd) {
768 this._locked = false;
770 D.addClass(this._wrap, 'yui-draggable');
777 * @description Check the locked status of the resize instance
780 isLocked: function() {
785 * @description Resets the element to is start state.
786 * @return {<a href="YAHOO.util.Resize.html">YAHOO.util.Resize</a>} The Resize instance
789 YAHOO.log('Resetting to cached sizes and position', 'info', 'Resize');
790 this.resize(null, this._cache.start.height, this._cache.start.width, this._cache.start.top, this._cache.start.left, true);
796 * @param {Event} ev The mouse event.
797 * @param {Number} h The new height setting.
798 * @param {Number} w The new width setting.
799 * @param {Number} t The new top setting.
800 * @param {Number} l The new left setting.
801 * @param {Boolean} force Resize the element (used for proxy resize).
802 * @param {Boolean} silent Don't fire the beforeResize Event.
803 * @description Resizes the element, wrapper or proxy based on the data from the handlers.
804 * @return {<a href="YAHOO.util.Resize.html">YAHOO.util.Resize</a>} The Resize instance
806 resize: function(ev, h, w, t, l, force, silent) {
808 YAHOO.log('Resize Locked', 'info', 'Resize');
811 YAHOO.log('Resize: ' + h + ',' + w + ',' + t + ',' + l, 'info', 'Resize');
812 this._resizeEvent = ev;
813 var el = this._wrap, anim = this.get('animate'), set = true;
814 if (this._proxy && !force) {
818 this._setAutoRatio(ev);
819 if (this._positioned) {
821 t = this._cache.top - t;
822 l = this._cache.left - l;
827 var ratio = this._setRatio(h, w, t, l);
828 h = parseInt(ratio[0], 10);
829 w = parseInt(ratio[1], 10);
830 t = parseInt(ratio[2], 10);
831 l = parseInt(ratio[3], 10);
834 //No Offset, get from cache
838 //No Offset, get from cache
844 if (this._positioned) {
845 if (this._proxy && force) {
847 el.style.top = this._proxy.style.top;
848 el.style.left = this._proxy.style.left;
850 t = this._proxy.style.top;
851 l = this._proxy.style.left;
854 if (!this.get('ratio') && !this._proxy) {
855 t = this._cache.top + -(t);
856 l = this._cache.left + -(l);
859 if (this.get('minY')) {
860 if (t < this.get('minY')) {
861 t = this.get('minY');
864 if (this.get('maxY')) {
865 if (t > this.get('maxY')) {
866 t = this.get('maxY');
871 if (this.get('minX')) {
872 if (l < this.get('minX')) {
873 l = this.get('minX');
876 if (this.get('maxX')) {
877 if ((l + w) > this.get('maxX')) {
878 l = (this.get('maxX') - w);
885 YAHOO.log('beforeResize', 'info', 'Resize');
886 var beforeReturn = this.fireEvent('beforeResize', { ev: 'beforeResize', target: this, height: h, width: w, top: t, left: l });
887 if (beforeReturn === false) {
888 YAHOO.log('Resized cancelled because befireResize returned false', 'info', 'Resize');
893 this._updateStatus(h, w, t, l);
896 if (this._positioned) {
897 if (this._proxy && force) {
906 this._cache.left = l;
913 if (this._proxy && force) {
914 if (!this.get('setSize')) {
919 el.style.height = h + 'px';
921 if ((this._proxy && force) || !this._proxy) {
922 if (this._wrap != this.get('element')) {
923 this.get('element').style.height = h + 'px';
927 this._cache.height = h;
930 this._cache.width = w;
933 if (this._proxy && force) {
934 if (!this.get('setSize')) {
939 el.style.width = w + 'px';
941 if ((this._proxy && force) || !this._proxy) {
942 if (this._wrap != this.get('element')) {
943 this.get('element').style.width = w + 'px';
949 if (YAHOO.util.Anim) {
950 var _anim = new YAHOO.util.Anim(el, {
952 to: this._cache.height
955 to: this._cache.width
957 }, this.get('animateDuration'), this.get('animateEasing'));
958 if (this._positioned) {
960 _anim.attributes.top = {
965 _anim.attributes.left = {
971 if (this._wrap != this.get('element')) {
972 _anim.onTween.subscribe(function() {
973 this.get('element').style.height = el.style.height;
974 this.get('element').style.width = el.style.width;
978 _anim.onComplete.subscribe(function() {
979 YAHOO.log('Animation onComplete fired', 'info', 'Resize');
980 this.set('height', h);
981 this.set('width', w);
982 this.fireEvent('resize', { ev: 'resize', target: this, height: h, width: w, top: t, left: l });
988 if (this._proxy && !force) {
989 YAHOO.log('proxyResize', 'info', 'Resize');
990 this.fireEvent('proxyResize', { ev: 'proxyresize', target: this, height: h, width: w, top: t, left: l });
992 YAHOO.log('resize', 'info', 'Resize');
993 this.fireEvent('resize', { ev: 'resize', target: this, height: h, width: w, top: t, left: l });
1000 * @method _handle_for_br
1001 * @param {Object} args The arguments from the CustomEvent.
1002 * @description Handles the sizes for the Bottom Right handle.
1004 _handle_for_br: function(args) {
1005 YAHOO.log('Handle BR', 'info', 'Resize');
1006 var newW = this._setWidth(args.e);
1007 var newH = this._setHeight(args.e);
1008 this.resize(args.e, newH, newW, 0, 0);
1012 * @method _handle_for_bl
1013 * @param {Object} args The arguments from the CustomEvent.
1014 * @description Handles the sizes for the Bottom Left handle.
1016 _handle_for_bl: function(args) {
1017 YAHOO.log('Handle BL', 'info', 'Resize');
1018 var newW = this._setWidth(args.e, true);
1019 var newH = this._setHeight(args.e);
1020 var l = (newW - this._cache.width);
1021 this.resize(args.e, newH, newW, 0, l);
1025 * @method _handle_for_tl
1026 * @param {Object} args The arguments from the CustomEvent.
1027 * @description Handles the sizes for the Top Left handle.
1029 _handle_for_tl: function(args) {
1030 YAHOO.log('Handle TL', 'info', 'Resize');
1031 var newW = this._setWidth(args.e, true);
1032 var newH = this._setHeight(args.e, true);
1033 var t = (newH - this._cache.height);
1034 var l = (newW - this._cache.width);
1035 this.resize(args.e, newH, newW, t, l);
1039 * @method _handle_for_tr
1040 * @param {Object} args The arguments from the CustomEvent.
1041 * @description Handles the sizes for the Top Right handle.
1043 _handle_for_tr: function(args) {
1044 YAHOO.log('Handle TR', 'info', 'Resize');
1045 var newW = this._setWidth(args.e);
1046 var newH = this._setHeight(args.e, true);
1047 var t = (newH - this._cache.height);
1048 this.resize(args.e, newH, newW, t, 0);
1052 * @method _handle_for_r
1053 * @param {Object} args The arguments from the CustomEvent.
1054 * @description Handles the sizes for the Right handle.
1056 _handle_for_r: function(args) {
1057 YAHOO.log('Handle R', 'info', 'Resize');
1058 this._dds.r.setYConstraint(0,0);
1059 var newW = this._setWidth(args.e);
1060 this.resize(args.e, 0, newW, 0, 0);
1064 * @method _handle_for_l
1065 * @param {Object} args The arguments from the CustomEvent.
1066 * @description Handles the sizes for the Left handle.
1068 _handle_for_l: function(args) {
1069 YAHOO.log('Handle L', 'info', 'Resize');
1070 this._dds.l.setYConstraint(0,0);
1071 var newW = this._setWidth(args.e, true);
1072 var l = (newW - this._cache.width);
1073 this.resize(args.e, 0, newW, 0, l);
1077 * @method _handle_for_b
1078 * @param {Object} args The arguments from the CustomEvent.
1079 * @description Handles the sizes for the Bottom handle.
1081 _handle_for_b: function(args) {
1082 YAHOO.log('Handle B', 'info', 'Resize');
1083 this._dds.b.setXConstraint(0,0);
1084 var newH = this._setHeight(args.e);
1085 this.resize(args.e, newH, 0, 0, 0);
1089 * @method _handle_for_t
1090 * @param {Object} args The arguments from the CustomEvent.
1091 * @description Handles the sizes for the Top handle.
1093 _handle_for_t: function(args) {
1094 YAHOO.log('Handle T', 'info', 'Resize');
1095 this._dds.t.setXConstraint(0,0);
1096 var newH = this._setHeight(args.e, true);
1097 var t = (newH - this._cache.height);
1098 this.resize(args.e, newH, 0, t, 0);
1103 * @param {Event} ev The mouse event.
1104 * @param {Boolean} flip Argument to determine the direction of the movement.
1105 * @description Calculates the width based on the mouse event.
1106 * @return {Number} The new value
1108 _setWidth: function(ev, flip) {
1109 YAHOO.log('Set width based on Event', 'info', 'Resize');
1110 var xy = this._cache.xy[0],
1111 w = this._cache.width,
1112 x = Event.getPageX(ev),
1116 nw = (xy - x) + parseInt(this.get('width'), 10);
1119 nw = this._snapTick(nw, this.get('xTicks'));
1120 nw = this._checkWidth(nw);
1125 * @method _checkWidth
1126 * @param {Number} w The width to check.
1127 * @description Checks the value passed against the maxWidth and minWidth.
1128 * @return {Number} the new value
1130 _checkWidth: function(w) {
1131 YAHOO.log('Checking the min/max width', 'info', 'Resize');
1132 if (this.get('minWidth')) {
1133 if (w <= this.get('minWidth')) {
1134 YAHOO.log('Using minWidth', 'info', 'Resize');
1135 w = this.get('minWidth');
1138 if (this.get('maxWidth')) {
1139 if (w >= this.get('maxWidth')) {
1140 YAHOO.log('Using Max Width', 'info', 'Resize');
1141 w = this.get('maxWidth');
1148 * @method _checkHeight
1149 * @param {Number} h The height to check.
1150 * @description Checks the value passed against the maxHeight and minHeight.
1151 * @return {Number} The new value
1153 _checkHeight: function(h) {
1154 YAHOO.log('Checking the min/max height', 'info', 'Resize');
1155 if (this.get('minHeight')) {
1156 if (h <= this.get('minHeight')) {
1157 YAHOO.log('Using minHeight', 'info', 'Resize');
1158 h = this.get('minHeight');
1161 if (this.get('maxHeight')) {
1162 if (h >= this.get('maxHeight')) {
1163 YAHOO.log('using maxHeight', 'info', 'Resize');
1164 h = this.get('maxHeight');
1171 * @method _setHeight
1172 * @param {Event} ev The mouse event.
1173 * @param {Boolean} flip Argument to determine the direction of the movement.
1174 * @description Calculated the height based on the mouse event.
1175 * @return {Number} The new value
1177 _setHeight: function(ev, flip) {
1178 YAHOO.log('Setting the height based on the Event', 'info', 'Resize');
1179 var xy = this._cache.xy[1],
1180 h = this._cache.height,
1181 y = Event.getPageY(ev),
1185 nh = (xy - y) + parseInt(this.get('height'), 10);
1187 nh = this._snapTick(nh, this.get('yTicks'));
1188 nh = this._checkHeight(nh);
1195 * @param {Number} size The size to tick against.
1196 * @param {Number} pix The tick pixels.
1197 * @description Adjusts the number based on the ticks used.
1198 * @return {Number} the new snapped position
1200 _snapTick: function(size, pix) {
1201 YAHOO.log('Snapping to ticks', 'info', 'Resize');
1202 if (!size || !pix) {
1206 var _x = size % pix;
1208 if (_x > (pix / 2)) {
1209 _s = size + (pix - _x);
1219 * @description The Resize class's initialization method
1221 init: function(p_oElement, p_oAttributes) {
1222 YAHOO.log('init', 'info', 'Resize');
1223 this._locked = false;
1240 Resize.superclass.init.call(this, p_oElement, p_oAttributes);
1242 this.set('setSize', this.get('setSize'));
1244 if (p_oAttributes.height) {
1245 this.set('height', parseInt(p_oAttributes.height, 10));
1247 var h = this.getStyle('height');
1249 this.set('height', parseInt(this.get('element').offsetHeight, 10));
1252 if (p_oAttributes.width) {
1253 this.set('width', parseInt(p_oAttributes.width, 10));
1255 var w = this.getStyle('width');
1257 this.set('width', parseInt(this.get('element').offsetWidth, 10));
1261 var id = p_oElement;
1262 if (!Lang.isString(id)) {
1263 id = D.generateId(id);
1265 Resize._instances[id] = this;
1267 this._active = false;
1270 this._createProxy();
1271 this._createHandles();
1275 * @method getProxyEl
1276 * @description Get the HTML reference for the proxy, returns null if no proxy.
1277 * @return {HTMLElement} The proxy element
1279 getProxyEl: function() {
1284 * @description Get the HTML reference for the wrap element, returns the current element if not wrapped.
1285 * @return {HTMLElement} The wrap element
1287 getWrapEl: function() {
1291 * @method getStatusEl
1292 * @description Get the HTML reference for the status element.
1293 * @return {HTMLElement} The status element
1295 getStatusEl: function() {
1296 return this._status;
1299 * @method getActiveHandleEl
1300 * @description Get the HTML reference for the currently active resize handle.
1301 * @return {HTMLElement} The handle element that is active
1303 getActiveHandleEl: function() {
1304 return this._handles[this._currentHandle];
1308 * @description Returns true or false if a resize operation is currently active on the element.
1311 isActive: function() {
1312 return ((this._active) ? true : false);
1316 * @method initAttributes
1317 * @description Initializes all of the configuration attributes used to create a resizable element.
1318 * @param {Object} attr Object literal specifying a set of
1319 * configuration attributes used to create the utility.
1321 initAttributes: function(attr) {
1322 Resize.superclass.initAttributes.call(this, attr);
1325 * @attribute useShim
1326 * @description This setting will be passed to the DragDrop instances on the resize handles and for the draggable property.
1327 * This property should be used if you want the resize handles to work over iframe and other elements.
1330 this.setAttributeConfig('useShim', {
1331 value: ((attr.useShim === true) ? true : false),
1332 validator: YAHOO.lang.isBoolean,
1333 method: function(u) {
1334 for (var i in this._dds) {
1335 if (Lang.hasOwnProperty(this._dds, i)) {
1336 this._dds[i].useShim = u;
1340 this.dd.useShim = u;
1345 * @attribute setSize
1346 * @description Set the size of the resized element, if set to false the element will not be auto resized,
1347 * the resize event will contain the dimensions so the end user can resize it on their own.
1348 * This setting will only work with proxy set to true and animate set to false.
1351 this.setAttributeConfig('setSize', {
1352 value: ((attr.setSize === false) ? false : true),
1353 validator: YAHOO.lang.isBoolean
1358 * @description Should we wrap the element
1361 this.setAttributeConfig('wrap', {
1363 validator: YAHOO.lang.isBoolean,
1364 value: attr.wrap || false
1368 * @attribute handles
1369 * @description The handles to use (any combination of): 't', 'b', 'r', 'l', 'bl', 'br', 'tl', 'tr'. Defaults to: ['r', 'b', 'br'].
1370 * Can use a shortcut of All. Note: 8 way resizing should be done on an element that is absolutely positioned.
1373 this.setAttributeConfig('handles', {
1375 value: attr.handles || ['r', 'b', 'br'],
1376 validator: function(handles) {
1377 if (Lang.isString(handles) && handles.toLowerCase() == 'all') {
1378 handles = ['t', 'b', 'r', 'l', 'bl', 'br', 'tl', 'tr'];
1380 if (!Lang.isArray(handles)) {
1381 handles = handles.replace(/, /g, ',');
1382 handles = handles.split(',');
1384 this._configs.handles.value = handles;
1390 * @description The width of the element
1393 this.setAttributeConfig('width', {
1394 value: attr.width || parseInt(this.getStyle('width'), 10),
1395 validator: YAHOO.lang.isNumber,
1396 method: function(width) {
1397 width = parseInt(width, 10);
1399 if (this.get('setSize')) {
1400 this.setStyle('width', width + 'px');
1402 this._cache.width = width;
1403 this._configs.width.value = width;
1410 * @description The height of the element
1413 this.setAttributeConfig('height', {
1414 value: attr.height || parseInt(this.getStyle('height'), 10),
1415 validator: YAHOO.lang.isNumber,
1416 method: function(height) {
1417 height = parseInt(height, 10);
1419 if (this.get('setSize')) {
1420 this.setStyle('height', height + 'px');
1422 this._cache.height = height;
1423 this._configs.height.value = height;
1429 * @attribute minWidth
1430 * @description The minimum width of the element
1433 this.setAttributeConfig('minWidth', {
1434 value: attr.minWidth || 15,
1435 validator: YAHOO.lang.isNumber
1439 * @attribute minHeight
1440 * @description The minimum height of the element
1443 this.setAttributeConfig('minHeight', {
1444 value: attr.minHeight || 15,
1445 validator: YAHOO.lang.isNumber
1449 * @attribute maxWidth
1450 * @description The maximum width of the element
1453 this.setAttributeConfig('maxWidth', {
1454 value: attr.maxWidth || 10000,
1455 validator: YAHOO.lang.isNumber
1459 * @attribute maxHeight
1460 * @description The maximum height of the element
1463 this.setAttributeConfig('maxHeight', {
1464 value: attr.maxHeight || 10000,
1465 validator: YAHOO.lang.isNumber
1470 * @description The minimum y coord of the element
1473 this.setAttributeConfig('minY', {
1474 value: attr.minY || false
1479 * @description The minimum x coord of the element
1482 this.setAttributeConfig('minX', {
1483 value: attr.minX || false
1487 * @description The max y coord of the element
1490 this.setAttributeConfig('maxY', {
1491 value: attr.maxY || false
1496 * @description The max x coord of the element
1499 this.setAttributeConfig('maxX', {
1500 value: attr.maxX || false
1504 * @attribute animate
1505 * @description Should be use animation to resize the element (can only be used if we use proxy).
1508 this.setAttributeConfig('animate', {
1509 value: attr.animate || false,
1510 validator: function(value) {
1512 if (!YAHOO.util.Anim) {
1520 * @attribute animateEasing
1521 * @description The Easing to apply to the animation.
1524 this.setAttributeConfig('animateEasing', {
1525 value: attr.animateEasing || function() {
1527 if (YAHOO.util.Easing && YAHOO.util.Easing.easeOut) {
1528 easing = YAHOO.util.Easing.easeOut;
1535 * @attribute animateDuration
1536 * @description The Duration to apply to the animation.
1539 this.setAttributeConfig('animateDuration', {
1540 value: attr.animateDuration || 0.5
1545 * @description Resize a proxy element instead of the real element.
1548 this.setAttributeConfig('proxy', {
1549 value: attr.proxy || false,
1550 validator: YAHOO.lang.isBoolean
1555 * @description Maintain the element's ratio when resizing.
1558 this.setAttributeConfig('ratio', {
1559 value: attr.ratio || false,
1560 validator: YAHOO.lang.isBoolean
1565 * @description Apply an opacity filter to the element being resized (only works with proxy).
1568 this.setAttributeConfig('ghost', {
1569 value: attr.ghost || false,
1570 validator: YAHOO.lang.isBoolean
1574 * @attribute draggable
1575 * @description A convienence method to make the element draggable
1578 this.setAttributeConfig('draggable', {
1579 value: attr.draggable || false,
1580 validator: YAHOO.lang.isBoolean,
1581 method: function(dd) {
1582 if (dd && this._wrap) {
1583 this._setupDragDrop();
1586 D.removeClass(this._wrap, this.CSS_DRAG);
1595 * @description Only show the handles when they are being moused over.
1598 this.setAttributeConfig('hover', {
1599 value: attr.hover || false,
1600 validator: YAHOO.lang.isBoolean
1604 * @attribute hiddenHandles
1605 * @description Don't show the handles, just use the cursor to the user.
1608 this.setAttributeConfig('hiddenHandles', {
1609 value: attr.hiddenHandles || false,
1610 validator: YAHOO.lang.isBoolean
1614 * @attribute knobHandles
1615 * @description Use the smaller handles, instead if the full size handles.
1618 this.setAttributeConfig('knobHandles', {
1619 value: attr.knobHandles || false,
1620 validator: YAHOO.lang.isBoolean
1625 * @description The number of x ticks to span the resize to.
1626 * @type Number or False
1628 this.setAttributeConfig('xTicks', {
1629 value: attr.xTicks || false
1634 * @description The number of y ticks to span the resize to.
1635 * @type Number or False
1637 this.setAttributeConfig('yTicks', {
1638 value: attr.yTicks || false
1643 * @description Show the status (new size) of the resize.
1646 this.setAttributeConfig('status', {
1647 value: attr.status || false,
1648 validator: YAHOO.lang.isBoolean
1652 * @attribute autoRatio
1653 * @description Using the shift key during a resize will toggle the ratio config.
1656 this.setAttributeConfig('autoRatio', {
1657 value: attr.autoRatio || false,
1658 validator: YAHOO.lang.isBoolean
1664 * @description Destroys the resize object and all of it's elements & listeners.
1666 destroy: function() {
1667 YAHOO.log('Destroying Resize', 'info', 'Resize');
1668 for (var h in this._handles) {
1669 if (Lang.hasOwnProperty(this._handles, h)) {
1670 Event.purgeElement(this._handles[h]);
1671 this._handles[h].parentNode.removeChild(this._handles[h]);
1675 this._proxy.parentNode.removeChild(this._proxy);
1678 this._status.parentNode.removeChild(this._status);
1682 D.removeClass(this._wrap, this.CSS_DRAG);
1684 if (this._wrap != this.get('element')) {
1685 this.setStyle('position', '');
1686 this.setStyle('top', '');
1687 this.setStyle('left', '');
1688 this._wrap.parentNode.replaceChild(this.get('element'), this._wrap);
1690 this.removeClass(this.CSS_RESIZE);
1692 delete YAHOO.util.Resize._instances[this.get('id')];
1693 //Brutal Object Destroy
1694 for (var i in this) {
1695 if (Lang.hasOwnProperty(this, i)) {
1703 * @description Returns a string representing the Resize Object.
1706 toString: function() {
1708 return 'Resize (#' + this.get('id') + ')';
1710 return 'Resize Utility';
1714 YAHOO.util.Resize = Resize;
1718 * @description Fires when the <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> dragEvent is fired for the config option draggable.
1719 * @type YAHOO.util.CustomEvent
1722 * @event startResize
1723 * @description Fires when a resize action is started.
1724 * @type YAHOO.util.CustomEvent
1728 * @description Fires when the mouseUp event from the Drag Instance fires.
1729 * @type YAHOO.util.CustomEvent
1733 * @description Fires on every element resize (only fires once when used with proxy config setting).
1734 * @type YAHOO.util.CustomEvent
1737 * @event beforeResize
1738 * @description Fires before every element resize after the size calculations, returning false will stop the resize.
1739 * @type YAHOO.util.CustomEvent
1742 * @event proxyResize
1743 * @description Fires on every proxy resize (only fires when used with proxy config setting).
1744 * @type YAHOO.util.CustomEvent
1749 YAHOO.register("resize", YAHOO.util.Resize, {version: "2.8.0r4", build: "2449"});