2 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
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) {
31 attributes: config || {}
34 Resize.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
40 * @property _instances
41 * @description Internal hash table for all resize instances
44 Resize._instances = {};
47 * @method getResizeById
48 * @description Get's a resize object by the HTML id of the element associated with the Resize object.
49 * @return {Object} The Resize Object
51 Resize.getResizeById = function(id) {
52 if (Resize._instances[id]) {
53 return Resize._instances[id];
58 YAHOO.extend(Resize, YAHOO.util.Element, {
61 * @property CSS_RESIZE
62 * @description Base CSS class name
65 CSS_RESIZE: 'yui-resize',
69 * @description Class name added when dragging is enabled
72 CSS_DRAG: 'yui-draggable',
76 * @description Class name used for hover only handles
79 CSS_HOVER: 'yui-resize-hover',
83 * @description Class name given to the proxy element
86 CSS_PROXY: 'yui-resize-proxy',
90 * @description Class name given to the wrap element
93 CSS_WRAP: 'yui-resize-wrap',
97 * @description Class name used to make the knob style handles
100 CSS_KNOB: 'yui-resize-knob',
103 * @property CSS_HIDDEN
104 * @description Class name given to the wrap element to make all handles hidden
107 CSS_HIDDEN: 'yui-resize-hidden',
110 * @property CSS_HANDLE
111 * @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
114 CSS_HANDLE: 'yui-resize-handle',
117 * @property CSS_STATUS
118 * @description Class name given to the status element
121 CSS_STATUS: 'yui-resize-status',
124 * @property CSS_GHOST
125 * @description Class name given to the wrap element when the ghost property is active
128 CSS_GHOST: 'yui-resize-ghost',
131 * @property CSS_RESIZING
132 * @description Class name given to the wrap element when a resize action is taking place.
135 CSS_RESIZING: 'yui-resize-resizing',
138 * @property _resizeEvent
139 * @description The mouse event used to resize with
146 * @description The <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> instance used if draggable is true
153 * @description A copy of the YAHOO.env.ua property
156 browser: YAHOO.env.ua,
160 * @description A flag to show if the resize is locked
166 * @property _positioned
167 * @description A flag to show if the element is absolutely positioned
174 * @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
181 * @description The HTML reference of the element wrapper
188 * @description The HTML reference of the element proxy
195 * @description An object containing references to all of the resize handles.
201 * @property _currentHandle
202 * @description The string identifier of the currently active handle. e.g. 'r', 'br', 'tl'
205 _currentHandle: null,
208 * @property _currentDD
209 * @description A link to the currently active DD object
216 * @description An lookup table containing key information for the element being resized. e.g. height, width, x position, y position, etc..
223 * @description Flag to show if the resize is active. Used for events.
229 * @method _createProxy
230 * @description Creates the proxy element if the proxy config is true
232 _createProxy: function() {
233 if (this.get('proxy')) {
234 this._proxy = document.createElement('div');
235 this._proxy.className = this.CSS_PROXY;
236 this._proxy.style.height = this.get('element').clientHeight + 'px';
237 this._proxy.style.width = this.get('element').clientWidth + 'px';
238 this._wrap.parentNode.appendChild(this._proxy);
240 this.set('animate', false);
245 * @method _createWrap
246 * @description Creates the wrap element if the wrap config is true. It will auto wrap the following element types: img, textarea, input, iframe, select
248 _createWrap: function() {
249 this._positioned = false;
250 //Force wrap for elements that can't have children
251 if (this.get('wrap') === false) {
252 switch (this.get('element').tagName.toLowerCase()) {
258 this.set('wrap', true);
262 if (this.get('wrap') === true) {
263 this._wrap = document.createElement('div');
264 this._wrap.id = this.get('element').id + '_wrap';
265 this._wrap.className = this.CSS_WRAP;
266 if (this.get('element').tagName.toLowerCase() == 'textarea') {
267 D.addClass(this._wrap, 'yui-resize-textarea');
269 D.setStyle(this._wrap, 'width', this.get('width') + 'px');
270 D.setStyle(this._wrap, 'height', this.get('height') + 'px');
271 D.setStyle(this._wrap, 'z-index', this.getStyle('z-index'));
272 this.setStyle('z-index', 0);
273 var pos = D.getStyle(this.get('element'), 'position');
274 D.setStyle(this._wrap, 'position', ((pos == 'static') ? 'relative' : pos));
275 D.setStyle(this._wrap, 'top', D.getStyle(this.get('element'), 'top'));
276 D.setStyle(this._wrap, 'left', D.getStyle(this.get('element'), 'left'));
277 if (D.getStyle(this.get('element'), 'position') == 'absolute') {
278 this._positioned = true;
279 D.setStyle(this.get('element'), 'position', 'relative');
280 D.setStyle(this.get('element'), 'top', '0');
281 D.setStyle(this.get('element'), 'left', '0');
283 var par = this.get('element').parentNode;
284 par.replaceChild(this._wrap, this.get('element'));
285 this._wrap.appendChild(this.get('element'));
287 this._wrap = this.get('element');
288 if (D.getStyle(this._wrap, 'position') == 'absolute') {
289 this._positioned = true;
292 if (this.get('draggable')) {
293 this._setupDragDrop();
295 if (this.get('hover')) {
296 D.addClass(this._wrap, this.CSS_HOVER);
298 if (this.get('knobHandles')) {
299 D.addClass(this._wrap, this.CSS_KNOB);
301 if (this.get('hiddenHandles')) {
302 D.addClass(this._wrap, this.CSS_HIDDEN);
304 D.addClass(this._wrap, this.CSS_RESIZE);
308 * @method _setupDragDrop
309 * @description Setup the <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> instance on the element
311 _setupDragDrop: function() {
312 D.addClass(this._wrap, this.CSS_DRAG);
313 this.dd = new YAHOO.util.DD(this._wrap, this.get('id') + '-resize', { dragOnly: true, useShim: this.get('useShim') });
314 this.dd.on('dragEvent', function() {
315 this.fireEvent('dragEvent', arguments);
320 * @method _createHandles
321 * @description Creates the handles as specified in the config
323 _createHandles: function() {
326 var h = this.get('handles');
327 for (var i = 0; i < h.length; i++) {
328 this._handles[h[i]] = document.createElement('div');
329 this._handles[h[i]].id = D.generateId(this._handles[h[i]]);
330 this._handles[h[i]].className = this.CSS_HANDLE + ' ' + this.CSS_HANDLE + '-' + h[i];
331 var k = document.createElement('div');
332 k.className = this.CSS_HANDLE + '-inner-' + h[i];
333 this._handles[h[i]].appendChild(k);
334 this._wrap.appendChild(this._handles[h[i]]);
335 Event.on(this._handles[h[i]], 'mouseover', this._handleMouseOver, this, true);
336 Event.on(this._handles[h[i]], 'mouseout', this._handleMouseOut, this, true);
337 this._dds[h[i]] = new YAHOO.util.DragDrop(this._handles[h[i]], this.get('id') + '-handle-' + h, { useShim: this.get('useShim') });
338 this._dds[h[i]].setPadding(15, 15, 15, 15);
339 this._dds[h[i]].on('startDragEvent', this._handleStartDrag, this._dds[h[i]], this);
340 this._dds[h[i]].on('mouseDownEvent', this._handleMouseDown, this._dds[h[i]], this);
342 this._status = document.createElement('span');
343 this._status.className = this.CSS_STATUS;
344 document.body.insertBefore(this._status, document.body.firstChild);
348 * @method _ieSelectFix
349 * @description The function we use as the onselectstart handler when we start a drag in Internet Explorer
351 _ieSelectFix: function() {
356 * @property _ieSelectBack
357 * @description We will hold a copy of the current "onselectstart" method on this property, and reset it after we are done using it.
362 * @method _setAutoRatio
363 * @param {Event} ev A mouse event.
364 * @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.
366 _setAutoRatio: function(ev) {
367 if (this.get('autoRatio')) {
368 if (ev && ev.shiftKey) {
370 this.set('ratio', true);
372 this.set('ratio', this._configs.ratio._initialConfig.value);
378 * @method _handleMouseDown
379 * @param {Event} ev A mouse event.
380 * @description This method preps the autoRatio on MouseDown.
382 _handleMouseDown: function(ev) {
386 if (D.getStyle(this._wrap, 'position') == 'absolute') {
387 this._positioned = true;
390 this._setAutoRatio(ev);
392 if (this.browser.ie) {
393 this._ieSelectBack = document.body.onselectstart;
394 document.body.onselectstart = this._ieSelectFix;
399 * @method _handleMouseOver
400 * @param {Event} ev A mouse event.
401 * @description Adds CSS class names to the handles
403 _handleMouseOver: function(ev) {
407 D.removeClass(this._wrap, this.CSS_RESIZE);
409 if (this.get('hover')) {
410 D.removeClass(this._wrap, this.CSS_HOVER);
412 var tar = Event.getTarget(ev);
413 if (!D.hasClass(tar, this.CSS_HANDLE)) {
414 tar = tar.parentNode;
416 if (D.hasClass(tar, this.CSS_HANDLE) && !this._active) {
417 D.addClass(tar, this.CSS_HANDLE + '-active');
418 for (var i in this._handles) {
419 if (Lang.hasOwnProperty(this._handles, i)) {
420 if (this._handles[i] == tar) {
421 D.addClass(tar, this.CSS_HANDLE + '-' + i + '-active');
428 D.addClass(this._wrap, this.CSS_RESIZE);
432 * @method _handleMouseOut
433 * @param {Event} ev A mouse event.
434 * @description Removes CSS class names to the handles
436 _handleMouseOut: function(ev) {
437 D.removeClass(this._wrap, this.CSS_RESIZE);
438 if (this.get('hover') && !this._active) {
439 D.addClass(this._wrap, this.CSS_HOVER);
441 var tar = Event.getTarget(ev);
442 if (!D.hasClass(tar, this.CSS_HANDLE)) {
443 tar = tar.parentNode;
445 if (D.hasClass(tar, this.CSS_HANDLE) && !this._active) {
446 D.removeClass(tar, this.CSS_HANDLE + '-active');
447 for (var i in this._handles) {
448 if (Lang.hasOwnProperty(this._handles, i)) {
449 if (this._handles[i] == tar) {
450 D.removeClass(tar, this.CSS_HANDLE + '-' + i + '-active');
456 D.addClass(this._wrap, this.CSS_RESIZE);
460 * @method _handleStartDrag
461 * @param {Object} args The args passed from the CustomEvent.
462 * @param {Object} dd The <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> object we are working with.
463 * @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
465 _handleStartDrag: function(args, dd) {
466 var tar = dd.getDragEl();
467 if (D.hasClass(tar, this.CSS_HANDLE)) {
468 if (D.getStyle(this._wrap, 'position') == 'absolute') {
469 this._positioned = true;
472 this._currentDD = dd;
474 this._proxy.style.visibility = 'visible';
475 this._proxy.style.zIndex = '1000';
476 this._proxy.style.height = this.get('element').clientHeight + 'px';
477 this._proxy.style.width = this.get('element').clientWidth + 'px';
480 for (var i in this._handles) {
481 if (Lang.hasOwnProperty(this._handles, i)) {
482 if (this._handles[i] == tar) {
483 this._currentHandle = i;
484 var handle = '_handle_for_' + i;
485 D.addClass(tar, this.CSS_HANDLE + '-' + i + '-active');
486 dd.on('dragEvent', this[handle], this, true);
487 dd.on('mouseUpEvent', this._handleMouseUp, this, true);
494 D.addClass(tar, this.CSS_HANDLE + '-active');
496 if (this.get('proxy')) {
497 var xy = D.getXY(this.get('element'));
498 D.setXY(this._proxy, xy);
499 if (this.get('ghost')) {
500 this.addClass(this.CSS_GHOST);
503 D.addClass(this._wrap, this.CSS_RESIZING);
505 this._updateStatus(this._cache.height, this._cache.width, this._cache.top, this._cache.left);
506 this.fireEvent('startResize', { type: 'startresize', target: this});
512 * @description Sets up the this._cache hash table.
514 _setCache: function() {
515 this._cache.xy = D.getXY(this._wrap);
516 D.setXY(this._wrap, this._cache.xy);
517 this._cache.height = this.get('clientHeight');
518 this._cache.width = this.get('clientWidth');
519 this._cache.start.height = this._cache.height;
520 this._cache.start.width = this._cache.width;
521 this._cache.start.top = this._cache.xy[1];
522 this._cache.start.left = this._cache.xy[0];
523 this._cache.top = this._cache.xy[1];
524 this._cache.left = this._cache.xy[0];
525 this.set('height', this._cache.height, true);
526 this.set('width', this._cache.width, true);
530 * @method _handleMouseUp
531 * @param {Event} ev A mouse event.
532 * @description Cleans up listeners, hides proxy element and removes class names.
534 _handleMouseUp: function(ev) {
535 this._active = false;
537 var handle = '_handle_for_' + this._currentHandle;
538 this._currentDD.unsubscribe('dragEvent', this[handle], this, true);
539 this._currentDD.unsubscribe('mouseUpEvent', this._handleMouseUp, this, true);
542 this._proxy.style.visibility = 'hidden';
543 this._proxy.style.zIndex = '-1';
544 if (this.get('setSize')) {
545 this.resize(ev, this._cache.height, this._cache.width, this._cache.top, this._cache.left, true);
547 this.fireEvent('resize', { ev: 'resize', target: this, height: this._cache.height, width: this._cache.width, top: this._cache.top, left: this._cache.left });
550 if (this.get('ghost')) {
551 this.removeClass(this.CSS_GHOST);
555 if (this.get('hover')) {
556 D.addClass(this._wrap, this.CSS_HOVER);
559 D.setStyle(this._status, 'display', 'none');
561 if (this.browser.ie) {
562 document.body.onselectstart = this._ieSelectBack;
565 if (this.browser.ie) {
566 D.removeClass(this._wrap, this.CSS_RESIZE);
569 for (var i in this._handles) {
570 if (Lang.hasOwnProperty(this._handles, i)) {
571 D.removeClass(this._handles[i], this.CSS_HANDLE + '-active');
574 if (this.get('hover') && !this._active) {
575 D.addClass(this._wrap, this.CSS_HOVER);
577 D.removeClass(this._wrap, this.CSS_RESIZING);
579 D.removeClass(this._handles[this._currentHandle], this.CSS_HANDLE + '-' + this._currentHandle + '-active');
580 D.removeClass(this._handles[this._currentHandle], this.CSS_HANDLE + '-active');
582 if (this.browser.ie) {
583 D.addClass(this._wrap, this.CSS_RESIZE);
586 this._resizeEvent = null;
587 this._currentHandle = null;
589 if (!this.get('animate')) {
590 this.set('height', this._cache.height, true);
591 this.set('width', this._cache.width, true);
594 this.fireEvent('endResize', { ev: 'endResize', target: this, height: this._cache.height, width: this._cache.width, top: this._cache.top, left: this._cache.left });
599 * @param {Number} h The height offset.
600 * @param {Number} w The with offset.
601 * @param {Number} t The top offset.
602 * @param {Number} l The left offset.
603 * @description Using the Height, Width, Top & Left, it recalcuates them based on the original element size.
604 * @return {Array} The new Height, Width, Top & Left settings
606 _setRatio: function(h, w, t, l) {
608 if (this.get('ratio')) {
609 var orgH = this._cache.height,
610 orgW = this._cache.width,
611 nh = parseInt(this.get('height'), 10),
612 nw = parseInt(this.get('width'), 10),
613 maxH = this.get('maxHeight'),
614 minH = this.get('minHeight'),
615 maxW = this.get('maxWidth'),
616 minW = this.get('minWidth');
618 switch (this._currentHandle) {
621 h = Math.min(Math.max(minH, h), maxH);
623 t = (this._cache.start.top - (-((nh - h) / 2)));
624 l = (this._cache.start.left - (-((nw - w))));
628 h = Math.min(Math.max(minH, h), maxH);
630 t = (this._cache.start.top - (-((nh - h) / 2)));
635 l = (this._cache.start.left - (-((nw - w) / 2)));
636 t = (this._cache.start.top - (-((nh - h))));
641 l = (this._cache.start.left - (-((nw - w) / 2)));
646 l = (this._cache.start.left - (-((nw - w))));
655 l = (this._cache.start.left - (-((nw - w))));
656 t = (this._cache.start.top - (-((nh - h))));
661 l = (this._cache.start.left);
662 t = (this._cache.start.top - (-((nh - h))));
665 oh = this._checkHeight(h);
666 ow = this._checkWidth(w);
667 if ((oh != h) || (ow != w)) {
671 ow = this._cache.width;
674 oh = this._cache.height;
678 return [oh, ow, t, l];
682 * @method _updateStatus
683 * @param {Number} h The new height setting.
684 * @param {Number} w The new width setting.
685 * @param {Number} t The new top setting.
686 * @param {Number} l The new left setting.
687 * @description Using the Height, Width, Top & Left, it updates the status element with the elements sizes.
689 _updateStatus: function(h, w, t, l) {
690 if (this._resizeEvent && (!Lang.isString(this._resizeEvent))) {
691 h = ((h === 0) ? this._cache.start.height : h);
692 w = ((w === 0) ? this._cache.start.width : w);
693 var h1 = parseInt(this.get('height'), 10),
694 w1 = parseInt(this.get('width'), 10);
697 h1 = parseInt(h, 10);
700 w1 = parseInt(w, 10);
702 var diffH = (parseInt(h, 10) - h1);
703 var diffW = (parseInt(w, 10) - w1);
704 this._cache.offsetHeight = diffH;
705 this._cache.offsetWidth = diffW;
706 if (this.get('status')) {
707 D.setStyle(this._status, 'display', 'inline');
708 //This will cause IE8 to crash if the status box is hidden..
709 this._status.innerHTML = '<strong>' + parseInt(h, 10) + ' x ' + parseInt(w, 10) + '</strong><em>' + ((diffH > 0) ? '+' : '') + diffH + ' x ' + ((diffW > 0) ? '+' : '') + diffW + '</em>';
710 D.setXY(this._status, [Event.getPageX(this._resizeEvent) + 12, Event.getPageY(this._resizeEvent) + 12]);
716 * @description Lock the resize so it can't be resized
717 * @param {Boolean} dd If the draggable config is set, lock it too
718 * @return {<a href="YAHOO.util.Resize.html">YAHOO.util.Resize</a>} The Resize instance
723 D.removeClass(this._wrap, 'yui-draggable');
730 * @description Unlock the resize so it can be resized
731 * @param {Boolean} dd If the draggable config is set, unlock it too
732 * @return {<a href="YAHOO.util.Resize.html">YAHOO.util.Resize</a>} The Resize instance
734 unlock: function(dd) {
735 this._locked = false;
737 D.addClass(this._wrap, 'yui-draggable');
744 * @description Check the locked status of the resize instance
747 isLocked: function() {
752 * @description Resets the element to is start state.
753 * @return {<a href="YAHOO.util.Resize.html">YAHOO.util.Resize</a>} The Resize instance
756 this.resize(null, this._cache.start.height, this._cache.start.width, this._cache.start.top, this._cache.start.left, true);
762 * @param {Event} ev The mouse event.
763 * @param {Number} h The new height setting.
764 * @param {Number} w The new width setting.
765 * @param {Number} t The new top setting.
766 * @param {Number} l The new left setting.
767 * @param {Boolean} force Resize the element (used for proxy resize).
768 * @param {Boolean} silent Don't fire the beforeResize Event.
769 * @description Resizes the element, wrapper or proxy based on the data from the handlers.
770 * @return {<a href="YAHOO.util.Resize.html">YAHOO.util.Resize</a>} The Resize instance
772 resize: function(ev, h, w, t, l, force, silent) {
776 this._resizeEvent = ev;
777 var el = this._wrap, anim = this.get('animate'), set = true;
778 if (this._proxy && !force) {
782 this._setAutoRatio(ev);
783 if (this._positioned) {
785 t = this._cache.top - t;
786 l = this._cache.left - l;
791 var ratio = this._setRatio(h, w, t, l);
792 h = parseInt(ratio[0], 10);
793 w = parseInt(ratio[1], 10);
794 t = parseInt(ratio[2], 10);
795 l = parseInt(ratio[3], 10);
798 //No Offset, get from cache
802 //No Offset, get from cache
808 if (this._positioned) {
809 if (this._proxy && force) {
811 el.style.top = this._proxy.style.top;
812 el.style.left = this._proxy.style.left;
814 t = this._proxy.style.top;
815 l = this._proxy.style.left;
818 if (!this.get('ratio') && !this._proxy) {
819 t = this._cache.top + -(t);
820 l = this._cache.left + -(l);
823 if (this.get('minY')) {
824 if (t < this.get('minY')) {
825 t = this.get('minY');
828 if (this.get('maxY')) {
829 if (t > this.get('maxY')) {
830 t = this.get('maxY');
835 if (this.get('minX')) {
836 if (l < this.get('minX')) {
837 l = this.get('minX');
840 if (this.get('maxX')) {
841 if ((l + w) > this.get('maxX')) {
842 l = (this.get('maxX') - w);
849 var beforeReturn = this.fireEvent('beforeResize', { ev: 'beforeResize', target: this, height: h, width: w, top: t, left: l });
850 if (beforeReturn === false) {
855 this._updateStatus(h, w, t, l);
858 if (this._positioned) {
859 if (this._proxy && force) {
868 this._cache.left = l;
875 if (this._proxy && force) {
876 if (!this.get('setSize')) {
881 el.style.height = h + 'px';
883 if ((this._proxy && force) || !this._proxy) {
884 if (this._wrap != this.get('element')) {
885 this.get('element').style.height = h + 'px';
889 this._cache.height = h;
892 this._cache.width = w;
895 if (this._proxy && force) {
896 if (!this.get('setSize')) {
901 el.style.width = w + 'px';
903 if ((this._proxy && force) || !this._proxy) {
904 if (this._wrap != this.get('element')) {
905 this.get('element').style.width = w + 'px';
911 if (YAHOO.util.Anim) {
912 var _anim = new YAHOO.util.Anim(el, {
914 to: this._cache.height
917 to: this._cache.width
919 }, this.get('animateDuration'), this.get('animateEasing'));
920 if (this._positioned) {
922 _anim.attributes.top = {
927 _anim.attributes.left = {
933 if (this._wrap != this.get('element')) {
934 _anim.onTween.subscribe(function() {
935 this.get('element').style.height = el.style.height;
936 this.get('element').style.width = el.style.width;
940 _anim.onComplete.subscribe(function() {
941 this.set('height', h);
942 this.set('width', w);
943 this.fireEvent('resize', { ev: 'resize', target: this, height: h, width: w, top: t, left: l });
949 if (this._proxy && !force) {
950 this.fireEvent('proxyResize', { ev: 'proxyresize', target: this, height: h, width: w, top: t, left: l });
952 this.fireEvent('resize', { ev: 'resize', target: this, height: h, width: w, top: t, left: l });
959 * @method _handle_for_br
960 * @param {Object} args The arguments from the CustomEvent.
961 * @description Handles the sizes for the Bottom Right handle.
963 _handle_for_br: function(args) {
964 var newW = this._setWidth(args.e);
965 var newH = this._setHeight(args.e);
966 this.resize(args.e, newH, newW, 0, 0);
970 * @method _handle_for_bl
971 * @param {Object} args The arguments from the CustomEvent.
972 * @description Handles the sizes for the Bottom Left handle.
974 _handle_for_bl: function(args) {
975 var newW = this._setWidth(args.e, true);
976 var newH = this._setHeight(args.e);
977 var l = (newW - this._cache.width);
978 this.resize(args.e, newH, newW, 0, l);
982 * @method _handle_for_tl
983 * @param {Object} args The arguments from the CustomEvent.
984 * @description Handles the sizes for the Top Left handle.
986 _handle_for_tl: function(args) {
987 var newW = this._setWidth(args.e, true);
988 var newH = this._setHeight(args.e, true);
989 var t = (newH - this._cache.height);
990 var l = (newW - this._cache.width);
991 this.resize(args.e, newH, newW, t, l);
995 * @method _handle_for_tr
996 * @param {Object} args The arguments from the CustomEvent.
997 * @description Handles the sizes for the Top Right handle.
999 _handle_for_tr: function(args) {
1000 var newW = this._setWidth(args.e);
1001 var newH = this._setHeight(args.e, true);
1002 var t = (newH - this._cache.height);
1003 this.resize(args.e, newH, newW, t, 0);
1007 * @method _handle_for_r
1008 * @param {Object} args The arguments from the CustomEvent.
1009 * @description Handles the sizes for the Right handle.
1011 _handle_for_r: function(args) {
1012 this._dds.r.setYConstraint(0,0);
1013 var newW = this._setWidth(args.e);
1014 this.resize(args.e, 0, newW, 0, 0);
1018 * @method _handle_for_l
1019 * @param {Object} args The arguments from the CustomEvent.
1020 * @description Handles the sizes for the Left handle.
1022 _handle_for_l: function(args) {
1023 this._dds.l.setYConstraint(0,0);
1024 var newW = this._setWidth(args.e, true);
1025 var l = (newW - this._cache.width);
1026 this.resize(args.e, 0, newW, 0, l);
1030 * @method _handle_for_b
1031 * @param {Object} args The arguments from the CustomEvent.
1032 * @description Handles the sizes for the Bottom handle.
1034 _handle_for_b: function(args) {
1035 this._dds.b.setXConstraint(0,0);
1036 var newH = this._setHeight(args.e);
1037 this.resize(args.e, newH, 0, 0, 0);
1041 * @method _handle_for_t
1042 * @param {Object} args The arguments from the CustomEvent.
1043 * @description Handles the sizes for the Top handle.
1045 _handle_for_t: function(args) {
1046 this._dds.t.setXConstraint(0,0);
1047 var newH = this._setHeight(args.e, true);
1048 var t = (newH - this._cache.height);
1049 this.resize(args.e, newH, 0, t, 0);
1054 * @param {Event} ev The mouse event.
1055 * @param {Boolean} flip Argument to determine the direction of the movement.
1056 * @description Calculates the width based on the mouse event.
1057 * @return {Number} The new value
1059 _setWidth: function(ev, flip) {
1060 var xy = this._cache.xy[0],
1061 w = this._cache.width,
1062 x = Event.getPageX(ev),
1066 nw = (xy - x) + parseInt(this.get('width'), 10);
1069 nw = this._snapTick(nw, this.get('xTicks'));
1070 nw = this._checkWidth(nw);
1075 * @method _checkWidth
1076 * @param {Number} w The width to check.
1077 * @description Checks the value passed against the maxWidth and minWidth.
1078 * @return {Number} the new value
1080 _checkWidth: function(w) {
1081 if (this.get('minWidth')) {
1082 if (w <= this.get('minWidth')) {
1083 w = this.get('minWidth');
1086 if (this.get('maxWidth')) {
1087 if (w >= this.get('maxWidth')) {
1088 w = this.get('maxWidth');
1095 * @method _checkHeight
1096 * @param {Number} h The height to check.
1097 * @description Checks the value passed against the maxHeight and minHeight.
1098 * @return {Number} The new value
1100 _checkHeight: function(h) {
1101 if (this.get('minHeight')) {
1102 if (h <= this.get('minHeight')) {
1103 h = this.get('minHeight');
1106 if (this.get('maxHeight')) {
1107 if (h >= this.get('maxHeight')) {
1108 h = this.get('maxHeight');
1115 * @method _setHeight
1116 * @param {Event} ev The mouse event.
1117 * @param {Boolean} flip Argument to determine the direction of the movement.
1118 * @description Calculated the height based on the mouse event.
1119 * @return {Number} The new value
1121 _setHeight: function(ev, flip) {
1122 var xy = this._cache.xy[1],
1123 h = this._cache.height,
1124 y = Event.getPageY(ev),
1128 nh = (xy - y) + parseInt(this.get('height'), 10);
1130 nh = this._snapTick(nh, this.get('yTicks'));
1131 nh = this._checkHeight(nh);
1138 * @param {Number} size The size to tick against.
1139 * @param {Number} pix The tick pixels.
1140 * @description Adjusts the number based on the ticks used.
1141 * @return {Number} the new snapped position
1143 _snapTick: function(size, pix) {
1144 if (!size || !pix) {
1148 var _x = size % pix;
1150 if (_x > (pix / 2)) {
1151 _s = size + (pix - _x);
1161 * @description The Resize class's initialization method
1163 init: function(p_oElement, p_oAttributes) {
1164 this._locked = false;
1181 Resize.superclass.init.call(this, p_oElement, p_oAttributes);
1183 this.set('setSize', this.get('setSize'));
1185 if (p_oAttributes.height) {
1186 this.set('height', parseInt(p_oAttributes.height, 10));
1188 var h = this.getStyle('height');
1190 this.set('height', parseInt(this.get('element').offsetHeight, 10));
1193 if (p_oAttributes.width) {
1194 this.set('width', parseInt(p_oAttributes.width, 10));
1196 var w = this.getStyle('width');
1198 this.set('width', parseInt(this.get('element').offsetWidth, 10));
1202 var id = p_oElement;
1203 if (!Lang.isString(id)) {
1204 id = D.generateId(id);
1206 Resize._instances[id] = this;
1208 this._active = false;
1211 this._createProxy();
1212 this._createHandles();
1216 * @method getProxyEl
1217 * @description Get the HTML reference for the proxy, returns null if no proxy.
1218 * @return {HTMLElement} The proxy element
1220 getProxyEl: function() {
1225 * @description Get the HTML reference for the wrap element, returns the current element if not wrapped.
1226 * @return {HTMLElement} The wrap element
1228 getWrapEl: function() {
1232 * @method getStatusEl
1233 * @description Get the HTML reference for the status element.
1234 * @return {HTMLElement} The status element
1236 getStatusEl: function() {
1237 return this._status;
1240 * @method getActiveHandleEl
1241 * @description Get the HTML reference for the currently active resize handle.
1242 * @return {HTMLElement} The handle element that is active
1244 getActiveHandleEl: function() {
1245 return this._handles[this._currentHandle];
1249 * @description Returns true or false if a resize operation is currently active on the element.
1252 isActive: function() {
1253 return ((this._active) ? true : false);
1257 * @method initAttributes
1258 * @description Initializes all of the configuration attributes used to create a resizable element.
1259 * @param {Object} attr Object literal specifying a set of
1260 * configuration attributes used to create the utility.
1262 initAttributes: function(attr) {
1263 Resize.superclass.initAttributes.call(this, attr);
1266 * @attribute useShim
1267 * @description This setting will be passed to the DragDrop instances on the resize handles and for the draggable property.
1268 * This property should be used if you want the resize handles to work over iframe and other elements.
1271 this.setAttributeConfig('useShim', {
1272 value: ((attr.useShim === true) ? true : false),
1273 validator: YAHOO.lang.isBoolean,
1274 method: function(u) {
1275 for (var i in this._dds) {
1276 if (Lang.hasOwnProperty(this._dds, i)) {
1277 this._dds[i].useShim = u;
1281 this.dd.useShim = u;
1286 * @attribute setSize
1287 * @description Set the size of the resized element, if set to false the element will not be auto resized,
1288 * the resize event will contain the dimensions so the end user can resize it on their own.
1289 * This setting will only work with proxy set to true and animate set to false.
1292 this.setAttributeConfig('setSize', {
1293 value: ((attr.setSize === false) ? false : true),
1294 validator: YAHOO.lang.isBoolean
1299 * @description Should we wrap the element
1302 this.setAttributeConfig('wrap', {
1304 validator: YAHOO.lang.isBoolean,
1305 value: attr.wrap || false
1309 * @attribute handles
1310 * @description The handles to use (any combination of): 't', 'b', 'r', 'l', 'bl', 'br', 'tl', 'tr'. Defaults to: ['r', 'b', 'br'].
1311 * Can use a shortcut of All. Note: 8 way resizing should be done on an element that is absolutely positioned.
1314 this.setAttributeConfig('handles', {
1316 value: attr.handles || ['r', 'b', 'br'],
1317 validator: function(handles) {
1318 if (Lang.isString(handles) && handles.toLowerCase() == 'all') {
1319 handles = ['t', 'b', 'r', 'l', 'bl', 'br', 'tl', 'tr'];
1321 if (!Lang.isArray(handles)) {
1322 handles = handles.replace(/, /g, ',');
1323 handles = handles.split(',');
1325 this._configs.handles.value = handles;
1331 * @description The width of the element
1334 this.setAttributeConfig('width', {
1335 value: attr.width || parseInt(this.getStyle('width'), 10),
1336 validator: YAHOO.lang.isNumber,
1337 method: function(width) {
1338 width = parseInt(width, 10);
1340 if (this.get('setSize')) {
1341 this.setStyle('width', width + 'px');
1343 this._cache.width = width;
1344 this._configs.width.value = width;
1351 * @description The height of the element
1354 this.setAttributeConfig('height', {
1355 value: attr.height || parseInt(this.getStyle('height'), 10),
1356 validator: YAHOO.lang.isNumber,
1357 method: function(height) {
1358 height = parseInt(height, 10);
1360 if (this.get('setSize')) {
1361 this.setStyle('height', height + 'px');
1363 this._cache.height = height;
1364 this._configs.height.value = height;
1370 * @attribute minWidth
1371 * @description The minimum width of the element
1374 this.setAttributeConfig('minWidth', {
1375 value: attr.minWidth || 15,
1376 validator: YAHOO.lang.isNumber
1380 * @attribute minHeight
1381 * @description The minimum height of the element
1384 this.setAttributeConfig('minHeight', {
1385 value: attr.minHeight || 15,
1386 validator: YAHOO.lang.isNumber
1390 * @attribute maxWidth
1391 * @description The maximum width of the element
1394 this.setAttributeConfig('maxWidth', {
1395 value: attr.maxWidth || 10000,
1396 validator: YAHOO.lang.isNumber
1400 * @attribute maxHeight
1401 * @description The maximum height of the element
1404 this.setAttributeConfig('maxHeight', {
1405 value: attr.maxHeight || 10000,
1406 validator: YAHOO.lang.isNumber
1411 * @description The minimum y coord of the element
1414 this.setAttributeConfig('minY', {
1415 value: attr.minY || false
1420 * @description The minimum x coord of the element
1423 this.setAttributeConfig('minX', {
1424 value: attr.minX || false
1428 * @description The max y coord of the element
1431 this.setAttributeConfig('maxY', {
1432 value: attr.maxY || false
1437 * @description The max x coord of the element
1440 this.setAttributeConfig('maxX', {
1441 value: attr.maxX || false
1445 * @attribute animate
1446 * @description Should be use animation to resize the element (can only be used if we use proxy).
1449 this.setAttributeConfig('animate', {
1450 value: attr.animate || false,
1451 validator: function(value) {
1453 if (!YAHOO.util.Anim) {
1461 * @attribute animateEasing
1462 * @description The Easing to apply to the animation.
1465 this.setAttributeConfig('animateEasing', {
1466 value: attr.animateEasing || function() {
1468 if (YAHOO.util.Easing && YAHOO.util.Easing.easeOut) {
1469 easing = YAHOO.util.Easing.easeOut;
1476 * @attribute animateDuration
1477 * @description The Duration to apply to the animation.
1480 this.setAttributeConfig('animateDuration', {
1481 value: attr.animateDuration || 0.5
1486 * @description Resize a proxy element instead of the real element.
1489 this.setAttributeConfig('proxy', {
1490 value: attr.proxy || false,
1491 validator: YAHOO.lang.isBoolean
1496 * @description Maintain the element's ratio when resizing.
1499 this.setAttributeConfig('ratio', {
1500 value: attr.ratio || false,
1501 validator: YAHOO.lang.isBoolean
1506 * @description Apply an opacity filter to the element being resized (only works with proxy).
1509 this.setAttributeConfig('ghost', {
1510 value: attr.ghost || false,
1511 validator: YAHOO.lang.isBoolean
1515 * @attribute draggable
1516 * @description A convienence method to make the element draggable
1519 this.setAttributeConfig('draggable', {
1520 value: attr.draggable || false,
1521 validator: YAHOO.lang.isBoolean,
1522 method: function(dd) {
1523 if (dd && this._wrap && !this.dd) {
1524 this._setupDragDrop();
1528 //activating an old DD instance..
1529 D.addClass(this._wrap, this.CSS_DRAG);
1530 this.dd.DDM.regDragDrop(this.dd, "default");
1532 D.removeClass(this._wrap, this.CSS_DRAG);
1542 * @description Only show the handles when they are being moused over.
1545 this.setAttributeConfig('hover', {
1546 value: attr.hover || false,
1547 validator: YAHOO.lang.isBoolean
1551 * @attribute hiddenHandles
1552 * @description Don't show the handles, just use the cursor to the user.
1555 this.setAttributeConfig('hiddenHandles', {
1556 value: attr.hiddenHandles || false,
1557 validator: YAHOO.lang.isBoolean
1561 * @attribute knobHandles
1562 * @description Use the smaller handles, instead if the full size handles.
1565 this.setAttributeConfig('knobHandles', {
1566 value: attr.knobHandles || false,
1567 validator: YAHOO.lang.isBoolean
1572 * @description The number of x ticks to span the resize to.
1573 * @type Number or False
1575 this.setAttributeConfig('xTicks', {
1576 value: attr.xTicks || false
1581 * @description The number of y ticks to span the resize to.
1582 * @type Number or False
1584 this.setAttributeConfig('yTicks', {
1585 value: attr.yTicks || false
1590 * @description Show the status (new size) of the resize.
1593 this.setAttributeConfig('status', {
1594 value: attr.status || false,
1595 validator: YAHOO.lang.isBoolean
1599 * @attribute autoRatio
1600 * @description Using the shift key during a resize will toggle the ratio config.
1603 this.setAttributeConfig('autoRatio', {
1604 value: attr.autoRatio || false,
1605 validator: YAHOO.lang.isBoolean
1611 * @description Destroys the resize object and all of it's elements & listeners.
1613 destroy: function() {
1614 for (var h in this._handles) {
1615 if (Lang.hasOwnProperty(this._handles, h)) {
1616 Event.purgeElement(this._handles[h]);
1617 this._handles[h].parentNode.removeChild(this._handles[h]);
1621 this._proxy.parentNode.removeChild(this._proxy);
1624 this._status.parentNode.removeChild(this._status);
1628 D.removeClass(this._wrap, this.CSS_DRAG);
1630 if (this._wrap != this.get('element')) {
1631 this.setStyle('position', (this._positioned ? 'absolute' : 'relative'));
1632 this.setStyle('top', D.getStyle(this._wrap, 'top'));
1633 this.setStyle('left',D.getStyle(this._wrap, 'left'));
1634 this._wrap.parentNode.replaceChild(this.get('element'), this._wrap);
1636 this.removeClass(this.CSS_RESIZE);
1638 delete YAHOO.util.Resize._instances[this.get('id')];
1639 //Brutal Object Destroy
1640 for (var i in this) {
1641 if (Lang.hasOwnProperty(this, i)) {
1649 * @description Returns a string representing the Resize Object.
1652 toString: function() {
1654 return 'Resize (#' + this.get('id') + ')';
1656 return 'Resize Utility';
1660 YAHOO.util.Resize = Resize;
1664 * @description Fires when the <a href="YAHOO.util.DragDrop.html">YAHOO.util.DragDrop</a> dragEvent is fired for the config option draggable.
1665 * @type YAHOO.util.CustomEvent
1668 * @event startResize
1669 * @description Fires when a resize action is started.
1670 * @type YAHOO.util.CustomEvent
1674 * @description Fires when the mouseUp event from the Drag Instance fires.
1675 * @type YAHOO.util.CustomEvent
1679 * @description Fires on every element resize (only fires once when used with proxy config setting).
1680 * @type YAHOO.util.CustomEvent
1683 * @event beforeResize
1684 * @description Fires before every element resize after the size calculations, returning false will stop the resize.
1685 * @type YAHOO.util.CustomEvent
1688 * @event proxyResize
1689 * @description Fires on every proxy resize (only fires when used with proxy config setting).
1690 * @type YAHOO.util.CustomEvent
1695 YAHOO.register("resize", YAHOO.util.Resize, {version: "2.9.0", build: "2800"});