NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / resize-base / resize-base-debug.js
blob912c016cbf0a83ea6fee45d2df3b2afe2d6a9e95
1 /*
2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
8 YUI.add('resize-base', function (Y, NAME) {
10 /**
11  * The Resize Utility allows you to make an HTML element resizable.
12  * @module resize
13  * @main resize
14  */
16 var Lang = Y.Lang,
17     isArray = Lang.isArray,
18     isBoolean = Lang.isBoolean,
19     isNumber = Lang.isNumber,
20     isString = Lang.isString,
22     yArray  = Y.Array,
23     trim = Lang.trim,
24     indexOf = yArray.indexOf,
26     COMMA = ',',
27     DOT = '.',
28     EMPTY_STR = '',
29     HANDLE_SUB = '{handle}',
30     SPACE = ' ',
32     ACTIVE = 'active',
33     ACTIVE_HANDLE = 'activeHandle',
34     ACTIVE_HANDLE_NODE = 'activeHandleNode',
35     ALL = 'all',
36     AUTO_HIDE = 'autoHide',
37     BORDER = 'border',
38     BOTTOM = 'bottom',
39     CLASS_NAME = 'className',
40     COLOR = 'color',
41     DEF_MIN_HEIGHT = 'defMinHeight',
42     DEF_MIN_WIDTH = 'defMinWidth',
43     HANDLE = 'handle',
44     HANDLES = 'handles',
45     HANDLES_WRAPPER = 'handlesWrapper',
46     HIDDEN = 'hidden',
47     INNER = 'inner',
48     LEFT = 'left',
49     MARGIN = 'margin',
50     NODE = 'node',
51     NODE_NAME = 'nodeName',
52     NONE = 'none',
53     OFFSET_HEIGHT = 'offsetHeight',
54     OFFSET_WIDTH = 'offsetWidth',
55     PADDING = 'padding',
56     PARENT_NODE = 'parentNode',
57     POSITION = 'position',
58     RELATIVE = 'relative',
59     RESIZE = 'resize',
60     RESIZING = 'resizing',
61     RIGHT = 'right',
62     STATIC = 'static',
63     STYLE = 'style',
64     TOP = 'top',
65     WIDTH = 'width',
66     WRAP = 'wrap',
67     WRAPPER = 'wrapper',
68     WRAP_TYPES = 'wrapTypes',
70     EV_MOUSE_UP = 'resize:mouseUp',
71     EV_RESIZE = 'resize:resize',
72     EV_RESIZE_ALIGN = 'resize:align',
73     EV_RESIZE_END = 'resize:end',
74     EV_RESIZE_START = 'resize:start',
76     T = 't',
77     TR = 'tr',
78     R = 'r',
79     BR = 'br',
80     B = 'b',
81     BL = 'bl',
82     L = 'l',
83     TL = 'tl',
85     concat = function() {
86         return Array.prototype.slice.call(arguments).join(SPACE);
87     },
89     // round the passed number to get rid of pixel-flickering
90     toRoundNumber = function(num) {
91         return Math.round(parseFloat(num)) || 0;
92     },
94     getCompStyle = function(node, val) {
95         return node.getComputedStyle(val);
96     },
98     handleAttrName = function(handle) {
99         return HANDLE + handle.toUpperCase();
100     },
102     isNode = function(v) {
103         return (v instanceof Y.Node);
104     },
106     toInitialCap = Y.cached(
107         function(str) {
108             return str.substring(0, 1).toUpperCase() + str.substring(1);
109         }
110     ),
112     capitalize = Y.cached(function() {
113         var out = [],
114             args = yArray(arguments, 0, true);
116         yArray.each(args, function(part, i) {
117             if (i > 0) {
118                 part = toInitialCap(part);
119             }
120             out.push(part);
121         });
123         return out.join(EMPTY_STR);
124     }),
126     getCN = Y.ClassNameManager.getClassName,
128     CSS_RESIZE = getCN(RESIZE),
129     CSS_RESIZE_HANDLE = getCN(RESIZE, HANDLE),
130     CSS_RESIZE_HANDLE_ACTIVE = getCN(RESIZE, HANDLE, ACTIVE),
131     CSS_RESIZE_HANDLE_INNER = getCN(RESIZE, HANDLE, INNER),
132     CSS_RESIZE_HANDLE_INNER_PLACEHOLDER = getCN(RESIZE, HANDLE, INNER, HANDLE_SUB),
133     CSS_RESIZE_HANDLE_PLACEHOLDER = getCN(RESIZE, HANDLE, HANDLE_SUB),
134     CSS_RESIZE_HIDDEN_HANDLES = getCN(RESIZE, HIDDEN, HANDLES),
135     CSS_RESIZE_HANDLES_WRAPPER = getCN(RESIZE, HANDLES, WRAPPER),
136     CSS_RESIZE_WRAPPER = getCN(RESIZE, WRAPPER);
139 A base class for Resize, providing:
141    * Basic Lifecycle (initializer, renderUI, bindUI, syncUI, destructor)
142    * Applies drag handles to an element to make it resizable
143    * Here is the list of valid resize handles:
144        `[ 't', 'tr', 'r', 'br', 'b', 'bl', 'l', 'tl' ]`. You can
145        read this list as top, top-right, right, bottom-right, bottom,
146        bottom-left, left, top-left.
147    * The drag handles are inserted into the element and positioned
148        absolute. Some elements, such as a textarea or image, don't support
149        children. To overcome that, set wrap:true in your config and the
150        element willbe wrapped for you automatically.
152 Quick Example:
154     var instance = new Y.Resize({
155         node: '#resize1',
156         preserveRatio: true,
157         wrap: true,
158         maxHeight: 170,
159         maxWidth: 400,
160         handles: 't, tr, r, br, b, bl, l, tl'
161     });
163 Check the list of <a href="Resize.html#attrs">Configuration Attributes</a> available for
164 Resize.
166 @class Resize
167 @param config {Object} Object literal specifying widget configuration properties.
168 @constructor
169 @extends Base
172 function Resize() {
173     Resize.superclass.constructor.apply(this, arguments);
176 Y.mix(Resize, {
177     /**
178      * Static property provides a string to identify the class.
179      *
180      * @property NAME
181      * @type String
182      * @static
183      */
184     NAME: RESIZE,
186     /**
187      * Static property used to define the default attribute
188      * configuration for the Resize.
189      *
190      * @property ATTRS
191      * @type Object
192      * @static
193      */
194     ATTRS: {
195         /**
196          * Stores the active handle during the resize.
197          *
198          * @attribute activeHandle
199          * @default null
200          * @private
201          * @type String
202          */
203         activeHandle: {
204             value: null,
205             validator: function(v) {
206                 return Y.Lang.isString(v) || Y.Lang.isNull(v);
207             }
208         },
210         /**
211          * Stores the active handle element during the resize.
212          *
213          * @attribute activeHandleNode
214          * @default null
215          * @private
216          * @type Node
217          */
218         activeHandleNode: {
219             value: null,
220             validator: isNode
221         },
223         /**
224          * False to ensure that the resize handles are always visible, true to
225          * display them only when the user mouses over the resizable borders.
226          *
227          * @attribute autoHide
228          * @default false
229          * @type boolean
230          */
231         autoHide: {
232             value: false,
233             validator: isBoolean
234         },
236         /**
237          * The default minimum height of the element. Only used when
238          * ResizeConstrained is not plugged.
239          *
240          * @attribute defMinHeight
241          * @default 15
242          * @type Number
243          */
244         defMinHeight: {
245             value: 15,
246             validator: isNumber
247         },
249         /**
250          * The default minimum width of the element. Only used when
251          * ResizeConstrained is not plugged.
252          *
253          * @attribute defMinWidth
254          * @default 15
255          * @type Number
256          */
257         defMinWidth: {
258             value: 15,
259             validator: isNumber
260         },
262         /**
263          * The handles to use (any combination of): 't', 'b', 'r', 'l', 'bl',
264          * 'br', 'tl', 'tr'. Can use a shortcut of All.
265          *
266          * @attribute handles
267          * @default all
268          * @type Array | String
269          */
270         handles: {
271             setter: '_setHandles',
272             value: ALL
273         },
275         /**
276          * Node to wrap the resize handles.
277          *
278          * @attribute handlesWrapper
279          * @type Node
280          */
281         handlesWrapper: {
282             readOnly: true,
283             setter: Y.one,
284             valueFn: '_valueHandlesWrapper'
285         },
287         /**
288          * The selector or element to resize. Required.
289          *
290          * @attribute node
291          * @type Node
292          */
293         node: {
294             setter: Y.one
295         },
297         /**
298          * True when the element is being Resized.
299          *
300          * @attribute resizing
301          * @default false
302          * @type boolean
303          */
304         resizing: {
305             value: false,
306             validator: isBoolean
307         },
309         /**
310          * True to wrap an element with a div if needed (required for textareas
311          * and images, defaults to false) in favor of the handles config option.
312          * The wrapper element type (default div) could be over-riden passing the
313          * <code>wrapper</code> attribute.
314          *
315          * @attribute wrap
316          * @default false
317          * @type boolean
318          */
319         wrap: {
320             setter: '_setWrap',
321             value: false,
322             validator: isBoolean
323         },
325         /**
326          * Elements that requires a wrapper by default. Normally are elements
327          * which cannot have children elements.
328          *
329          * @attribute wrapTypes
330          * @default /canvas|textarea|input|select|button|img/i
331          * @readOnly
332          * @type Regex
333          */
334         wrapTypes: {
335             readOnly: true,
336             value: /^canvas|textarea|input|select|button|img|iframe|table|embed$/i
337         },
339         /**
340          * Element to wrap the <code>wrapTypes</code>. This element will house
341          * the handles elements.
342          *
343          * @attribute wrapper
344          * @default div
345          * @type String | Node
346          * @writeOnce
347          */
348         wrapper: {
349             readOnly: true,
350             valueFn: '_valueWrapper',
351             writeOnce: true
352         }
353     },
355     RULES: {
356         b: function(instance, dx, dy) {
357             var info = instance.info,
358                 originalInfo = instance.originalInfo;
360             info.offsetHeight = originalInfo.offsetHeight + dy;
361         },
363         l: function(instance, dx) {
364             var info = instance.info,
365                 originalInfo = instance.originalInfo;
367             info.left = originalInfo.left + dx;
368             info.offsetWidth = originalInfo.offsetWidth - dx;
369         },
371         r: function(instance, dx) {
372             var info = instance.info,
373                 originalInfo = instance.originalInfo;
375             info.offsetWidth = originalInfo.offsetWidth + dx;
376         },
378         t: function(instance, dx, dy) {
379             var info = instance.info,
380                 originalInfo = instance.originalInfo;
382             info.top = originalInfo.top + dy;
383             info.offsetHeight = originalInfo.offsetHeight - dy;
384         },
386         tr: function() {
387             this.t.apply(this, arguments);
388             this.r.apply(this, arguments);
389         },
391         bl: function() {
392             this.b.apply(this, arguments);
393             this.l.apply(this, arguments);
394         },
396         br: function() {
397             this.b.apply(this, arguments);
398             this.r.apply(this, arguments);
399         },
401         tl: function() {
402             this.t.apply(this, arguments);
403             this.l.apply(this, arguments);
404         }
405     },
407     capitalize: capitalize
410 Y.Resize = Y.extend(
411     Resize,
412     Y.Base,
413     {
414         /**
415          * Array containing all possible resizable handles.
416          *
417          * @property ALL_HANDLES
418          * @type {String}
419          */
420         ALL_HANDLES: [ T, TR, R, BR, B, BL, L, TL ],
422         /**
423          * Regex which matches with the handles that could change the height of
424          * the resizable element.
425          *
426          * @property REGEX_CHANGE_HEIGHT
427          * @type {String}
428          */
429         REGEX_CHANGE_HEIGHT: /^(t|tr|b|bl|br|tl)$/i,
431         /**
432          * Regex which matches with the handles that could change the left of
433          * the resizable element.
434          *
435          * @property REGEX_CHANGE_LEFT
436          * @type {String}
437          */
438         REGEX_CHANGE_LEFT: /^(tl|l|bl)$/i,
440         /**
441          * Regex which matches with the handles that could change the top of
442          * the resizable element.
443          *
444          * @property REGEX_CHANGE_TOP
445          * @type {String}
446          */
447         REGEX_CHANGE_TOP: /^(tl|t|tr)$/i,
449         /**
450          * Regex which matches with the handles that could change the width of
451          * the resizable element.
452          *
453          * @property REGEX_CHANGE_WIDTH
454          * @type {String}
455          */
456         REGEX_CHANGE_WIDTH: /^(bl|br|l|r|tl|tr)$/i,
458         /**
459          * Template used to create the resize wrapper for the handles.
460          *
461          * @property HANDLES_WRAP_TEMPLATE
462          * @type {String}
463          */
464         HANDLES_WRAP_TEMPLATE: '<div class="'+CSS_RESIZE_HANDLES_WRAPPER+'"></div>',
466         /**
467          * Template used to create the resize wrapper node when needed.
468          *
469          * @property WRAP_TEMPLATE
470          * @type {String}
471          */
472         WRAP_TEMPLATE: '<div class="'+CSS_RESIZE_WRAPPER+'"></div>',
474         /**
475          * Template used to create each resize handle.
476          *
477          * @property HANDLE_TEMPLATE
478          * @type {String}
479          */
480         HANDLE_TEMPLATE: '<div class="'+concat(CSS_RESIZE_HANDLE, CSS_RESIZE_HANDLE_PLACEHOLDER)+'">' +
481                             '<div class="'+concat(CSS_RESIZE_HANDLE_INNER, CSS_RESIZE_HANDLE_INNER_PLACEHOLDER)+'">&nbsp;</div>' +
482                         '</div>',
485         /**
486          * Each box has a content area and optional surrounding padding and
487          * border areas. This property stores the sum of all horizontal
488          * surrounding * information needed to adjust the node height.
489          *
490          * @property totalHSurrounding
491          * @default 0
492          * @type number
493          */
494         totalHSurrounding: 0,
496         /**
497          * Each box has a content area and optional surrounding padding and
498          * border areas. This property stores the sum of all vertical
499          * surrounding * information needed to adjust the node height.
500          *
501          * @property totalVSurrounding
502          * @default 0
503          * @type number
504          */
505         totalVSurrounding: 0,
507         /**
508          * Stores the <a href="Resize.html#attr_node">node</a>
509          * surrounding information retrieved from
510          * <a href="Resize.html#method__getBoxSurroundingInfo">_getBoxSurroundingInfo</a>.
511          *
512          * @property nodeSurrounding
513          * @type Object
514          * @default null
515          */
516         nodeSurrounding: null,
518         /**
519          * Stores the <a href="Resize.html#attr_wrapper">wrapper</a>
520          * surrounding information retrieved from
521          * <a href="Resize.html#method__getBoxSurroundingInfo">_getBoxSurroundingInfo</a>.
522          *
523          * @property wrapperSurrounding
524          * @type Object
525          * @default null
526          */
527         wrapperSurrounding: null,
529         /**
530          * Whether the handle being dragged can change the height.
531          *
532          * @property changeHeightHandles
533          * @default false
534          * @type boolean
535          */
536         changeHeightHandles: false,
538         /**
539          * Whether the handle being dragged can change the left.
540          *
541          * @property changeLeftHandles
542          * @default false
543          * @type boolean
544          */
545         changeLeftHandles: false,
547         /**
548          * Whether the handle being dragged can change the top.
549          *
550          * @property changeTopHandles
551          * @default false
552          * @type boolean
553          */
554         changeTopHandles: false,
556         /**
557          * Whether the handle being dragged can change the width.
558          *
559          * @property changeWidthHandles
560          * @default false
561          * @type boolean
562          */
563         changeWidthHandles: false,
565         /**
566          * Store DD.Delegate reference for the respective Resize instance.
567          *
568          * @property delegate
569          * @default null
570          * @type Object
571          */
572         delegate: null,
574         /**
575          * Stores the current values for the height, width, top and left. You are
576          * able to manipulate these values on resize in order to change the resize
577          * behavior.
578          *
579          * @property info
580          * @type Object
581          * @protected
582          */
583         info: null,
585         /**
586          * Stores the last values for the height, width, top and left.
587          *
588          * @property lastInfo
589          * @type Object
590          * @protected
591          */
592         lastInfo: null,
594         /**
595          * Stores the original values for the height, width, top and left, stored
596          * on resize start.
597          *
598          * @property originalInfo
599          * @type Object
600          * @protected
601          */
602         originalInfo: null,
604         /**
605          * Construction logic executed during Resize instantiation. Lifecycle.
606          *
607          * @method initializer
608          * @protected
609          */
610         initializer: function() {
611             this._eventHandles = [];
613             this.renderer();
614         },
616         /**
617          * Create the DOM structure for the Resize. Lifecycle.
618          *
619          * @method renderUI
620          * @protected
621          */
622         renderUI: function() {
623             var instance = this;
625             instance._renderHandles();
626         },
628         /**
629          * Bind the events on the Resize UI. Lifecycle.
630          *
631          * @method bindUI
632          * @protected
633          */
634         bindUI: function() {
635             var instance = this;
637             instance._createEvents();
638             instance._bindDD();
639             instance._bindHandle();
640         },
642         /**
643          * Sync the Resize UI.
644          *
645          * @method syncUI
646          * @protected
647          */
648         syncUI: function() {
649             var instance = this;
651             this.get(NODE).addClass(CSS_RESIZE);
653             // hide handles if AUTO_HIDE is true
654             instance._setHideHandlesUI(
655                 instance.get(AUTO_HIDE)
656             );
657         },
659         /**
660          * Destructor lifecycle implementation for the Resize class.
661          * Detaches all previously attached listeners and removes the Resize handles.
662          *
663          * @method destructor
664          * @protected
665          */
666         destructor: function() {
667             var instance = this,
668                 node = instance.get(NODE),
669                 wrapper = instance.get(WRAPPER),
670                 pNode = wrapper.get(PARENT_NODE);
672             Y.each(
673                 instance._eventHandles,
674                 function(handle) {
675                     handle.detach();
676                 }
677             );
679             instance._eventHandles.length = 0;
681             // destroy handles dd and remove them from the dom
682             instance.eachHandle(function(handleEl) {
683                 instance.delegate.dd.destroy();
685                 // remove handle
686                 handleEl.remove(true);
687             });
689             instance.delegate.destroy();
691             // unwrap node
692             if (instance.get(WRAP)) {
693                 instance._copyStyles(wrapper, node);
695                 if (pNode) {
696                     pNode.insertBefore(node, wrapper);
697                 }
699                 wrapper.remove(true);
700             }
702             node.removeClass(CSS_RESIZE);
703             node.removeClass(CSS_RESIZE_HIDDEN_HANDLES);
704         },
706         /**
707          * Creates DOM (or manipulates DOM for progressive enhancement)
708          * This method is invoked by initializer(). It's chained automatically for
709          * subclasses if required.
710          *
711          * @method renderer
712          * @protected
713          */
714         renderer: function() {
715             this.renderUI();
716             this.bindUI();
717             this.syncUI();
718         },
720         /**
721          * <p>Loop through each handle which is being used and executes a callback.</p>
722          * <p>Example:</p>
723          * <pre><code>instance.eachHandle(
724          *      function(handleName, index) { ... }
725          *  );</code></pre>
726          *
727          * @method eachHandle
728          * @param {function} fn Callback function to be executed for each handle.
729          */
730         eachHandle: function(fn) {
731             var instance = this;
733             Y.each(
734                 instance.get(HANDLES),
735                 function(handle, i) {
736                     var handleEl = instance.get(
737                         handleAttrName(handle)
738                     );
740                     fn.apply(instance, [handleEl, handle, i]);
741                 }
742             );
743         },
745         /**
746          * Bind the handles DragDrop events to the Resize instance.
747          *
748          * @method _bindDD
749          * @private
750          */
751         _bindDD: function() {
752             var instance = this;
754             instance.delegate = new Y.DD.Delegate(
755                 {
756                     bubbleTargets: instance,
757                     container: instance.get(HANDLES_WRAPPER),
758                     dragConfig: {
759                         clickPixelThresh: 0,
760                         clickTimeThresh: 0,
761                         useShim: true,
762                         move: false
763                     },
764                     nodes: DOT+CSS_RESIZE_HANDLE,
765                     target: false
766                 }
767             );
769             instance._eventHandles.push(
770                 instance.on('drag:drag', instance._handleResizeEvent),
771                 instance.on('drag:dropmiss', instance._handleMouseUpEvent),
772                 instance.on('drag:end', instance._handleResizeEndEvent),
773                 instance.on('drag:start', instance._handleResizeStartEvent)
774             );
775         },
777         /**
778          * Bind the events related to the handles (_onHandleMouseEnter, _onHandleMouseLeave).
779          *
780          * @method _bindHandle
781          * @private
782          */
783         _bindHandle: function() {
784             var instance = this,
785                 wrapper = instance.get(WRAPPER);
787             instance._eventHandles.push(
788                 wrapper.on('mouseenter', Y.bind(instance._onWrapperMouseEnter, instance)),
789                 wrapper.on('mouseleave', Y.bind(instance._onWrapperMouseLeave, instance)),
790                 wrapper.delegate('mouseenter', Y.bind(instance._onHandleMouseEnter, instance), DOT+CSS_RESIZE_HANDLE),
791                 wrapper.delegate('mouseleave', Y.bind(instance._onHandleMouseLeave, instance), DOT+CSS_RESIZE_HANDLE)
792             );
793         },
795         /**
796          * Create the custom events used on the Resize.
797          *
798          * @method _createEvents
799          * @private
800          */
801         _createEvents: function() {
802             var instance = this,
803                 // create publish function for kweight optimization
804                 publish = function(name, fn) {
805                     instance.publish(name, {
806                         defaultFn: fn,
807                         queuable: false,
808                         emitFacade: true,
809                         bubbles: true,
810                         prefix: RESIZE
811                     });
812                 };
814             /**
815              * Handles the resize start event. Fired when a handle starts to be
816              * dragged.
817              *
818              * @event resize:start
819              * @preventable _defResizeStartFn
820              * @param {Event.Facade} event The resize start event.
821              * @bubbles Resize
822              * @type {Event.Custom}
823              */
824             publish(EV_RESIZE_START, this._defResizeStartFn);
826             /**
827              * Handles the resize event. Fired on each pixel when the handle is
828              * being dragged.
829              *
830              * @event resize:resize
831              * @preventable _defResizeFn
832              * @param {Event.Facade} event The resize event.
833              * @bubbles Resize
834              * @type {Event.Custom}
835              */
836             publish(EV_RESIZE, this._defResizeFn);
838             /**
839              * Handles the resize align event.
840              *
841              * @event resize:align
842              * @preventable _defResizeAlignFn
843              * @param {Event.Facade} event The resize align event.
844              * @bubbles Resize
845              * @type {Event.Custom}
846              */
847             publish(EV_RESIZE_ALIGN, this._defResizeAlignFn);
849             /**
850              * Handles the resize end event. Fired when a handle stop to be
851              * dragged.
852              *
853              * @event resize:end
854              * @preventable _defResizeEndFn
855              * @param {Event.Facade} event The resize end event.
856              * @bubbles Resize
857              * @type {Event.Custom}
858              */
859             publish(EV_RESIZE_END, this._defResizeEndFn);
861             /**
862              * Handles the resize mouseUp event. Fired when a mouseUp event happens on a
863              * handle.
864              *
865              * @event resize:mouseUp
866              * @preventable _defMouseUpFn
867              * @param {Event.Facade} event The resize mouseUp event.
868              * @bubbles Resize
869              * @type {Event.Custom}
870              */
871             publish(EV_MOUSE_UP, this._defMouseUpFn);
872         },
874         /**
875           * Responsible for loop each handle element and append to the wrapper.
876           *
877           * @method _renderHandles
878           * @protected
879           */
880         _renderHandles: function() {
881             var instance = this,
882                 wrapper = instance.get(WRAPPER),
883                 handlesWrapper = instance.get(HANDLES_WRAPPER);
885             instance.eachHandle(function(handleEl) {
886                 handlesWrapper.append(handleEl);
887             });
889             wrapper.append(handlesWrapper);
890         },
892         /**
893          * Creates the handle element based on the handle name and initialize the
894          * DragDrop on it.
895          *
896          * @method _buildHandle
897          * @param {String} handle Handle name ('t', 'tr', 'b', ...).
898          * @protected
899          */
900         _buildHandle: function(handle) {
901             var instance = this;
903             return Y.Node.create(
904                 Y.Lang.sub(instance.HANDLE_TEMPLATE, {
905                     handle: handle
906                 })
907             );
908         },
910         /**
911          * Basic resize calculations.
912          *
913          * @method _calcResize
914          * @protected
915          */
916         _calcResize: function() {
917             var instance = this,
918                 handle = instance.handle,
919                 info = instance.info,
920                 originalInfo = instance.originalInfo,
922                 dx = info.actXY[0] - originalInfo.actXY[0],
923                 dy = info.actXY[1] - originalInfo.actXY[1];
925             if (handle && Y.Resize.RULES[handle]) {
926                 Y.Resize.RULES[handle](instance, dx, dy);
927             }
928             else {
929                 Y.log('Handle rule not found: ' + handle, 'warn', 'resize');
930             }
931         },
933         /**
934          * Helper method to update the current size value on
935          * <a href="Resize.html#property_info">info</a> to respect the
936          * min/max values and fix the top/left calculations.
937          *
938          * @method _checkSize
939          * @param {String} offset 'offsetHeight' or 'offsetWidth'
940          * @param {number} size Size to restrict the offset
941          * @protected
942          */
943         _checkSize: function(offset, size) {
944             var instance = this,
945                 info = instance.info,
946                 originalInfo = instance.originalInfo,
947                 axis = (offset === OFFSET_HEIGHT) ? TOP : LEFT;
949             // forcing the offsetHeight/offsetWidth to be the passed size
950             info[offset] = size;
952             // predicting, based on the original information, the last left valid in case of reach the min/max dimension
953             // this calculation avoid browser event leaks when user interact very fast
954             if (((axis === LEFT) && instance.changeLeftHandles) ||
955                 ((axis === TOP) && instance.changeTopHandles)) {
957                 info[axis] = originalInfo[axis] + originalInfo[offset] - size;
958             }
959         },
961         /**
962          * Copy relevant styles of the <a href="Resize.html#attr_node">node</a>
963          * to the <a href="Resize.html#attr_wrapper">wrapper</a>.
964          *
965          * @method _copyStyles
966          * @param {Node} node Node from.
967          * @param {Node} wrapper Node to.
968          * @protected
969          */
970         _copyStyles: function(node, wrapper) {
971             var position = node.getStyle(POSITION).toLowerCase(),
972                 surrounding = this._getBoxSurroundingInfo(node),
973                 wrapperStyle;
975             // resizable wrapper should be positioned
976             if (position === STATIC) {
977                 position = RELATIVE;
978             }
980             wrapperStyle = {
981                 position: position,
982                 left: getCompStyle(node, LEFT),
983                 top: getCompStyle(node, TOP)
984             };
986             Y.mix(wrapperStyle, surrounding.margin);
987             Y.mix(wrapperStyle, surrounding.border);
989             wrapper.setStyles(wrapperStyle);
991             // remove margin and border from the internal node
992             node.setStyles({ border: 0, margin: 0 });
994             wrapper.sizeTo(
995                 node.get(OFFSET_WIDTH) + surrounding.totalHBorder,
996                 node.get(OFFSET_HEIGHT) + surrounding.totalVBorder
997             );
998         },
1000         // extract handle name from a string
1001         // using Y.cached to memoize the function for performance
1002         _extractHandleName: Y.cached(
1003             function(node) {
1004                 var className = node.get(CLASS_NAME),
1006                     match = className.match(
1007                         new RegExp(
1008                             getCN(RESIZE, HANDLE, '(\\w{1,2})\\b')
1009                         )
1010                     );
1012                 return match ? match[1] : null;
1013             }
1014         ),
1016         /**
1017          * <p>Generates metadata to the <a href="Resize.html#property_info">info</a>
1018          * and <a href="Resize.html#property_originalInfo">originalInfo</a></p>
1019          * <pre><code>bottom, actXY, left, top, offsetHeight, offsetWidth, right</code></pre>
1020          *
1021          * @method _getInfo
1022          * @param {Node} node
1023          * @param {EventFacade} event
1024          * @private
1025          */
1026         _getInfo: function(node, event) {
1027             var actXY = [0,0],
1028                 drag = event.dragEvent.target,
1029                 nodeXY = node.getXY(),
1030                 nodeX = nodeXY[0],
1031                 nodeY = nodeXY[1],
1032                 offsetHeight = node.get(OFFSET_HEIGHT),
1033                 offsetWidth = node.get(OFFSET_WIDTH);
1035             if (event) {
1036                 // the xy that the node will be set to. Changing this will alter the position as it's dragged.
1037                 actXY = (drag.actXY.length ? drag.actXY : drag.lastXY);
1038             }
1040             return {
1041                 actXY: actXY,
1042                 bottom: (nodeY + offsetHeight),
1043                 left: nodeX,
1044                 offsetHeight: offsetHeight,
1045                 offsetWidth: offsetWidth,
1046                 right: (nodeX + offsetWidth),
1047                 top: nodeY
1048             };
1049         },
1051         /**
1052          * Each box has a content area and optional surrounding margin,
1053          * padding and * border areas. This method get all this information from
1054          * the passed node. For more reference see
1055          * <a href="http://www.w3.org/TR/CSS21/box.html#box-dimensions">
1056          * http://www.w3.org/TR/CSS21/box.html#box-dimensions</a>.
1057          *
1058          * @method _getBoxSurroundingInfo
1059          * @param {Node} node
1060          * @private
1061          * @return {Object}
1062          */
1063         _getBoxSurroundingInfo: function(node) {
1064             var info = {
1065                 padding: {},
1066                 margin: {},
1067                 border: {}
1068             };
1070             if (isNode(node)) {
1071                 Y.each([ TOP, RIGHT, BOTTOM, LEFT ], function(dir) {
1072                     var paddingProperty = capitalize(PADDING, dir),
1073                         marginProperty = capitalize(MARGIN, dir),
1074                         borderWidthProperty = capitalize(BORDER, dir, WIDTH),
1075                         borderColorProperty = capitalize(BORDER, dir, COLOR),
1076                         borderStyleProperty = capitalize(BORDER, dir, STYLE);
1078                     info.border[borderColorProperty] = getCompStyle(node, borderColorProperty);
1079                     info.border[borderStyleProperty] = getCompStyle(node, borderStyleProperty);
1080                     info.border[borderWidthProperty] = getCompStyle(node, borderWidthProperty);
1081                     info.margin[marginProperty] = getCompStyle(node, marginProperty);
1082                     info.padding[paddingProperty] = getCompStyle(node, paddingProperty);
1083                 });
1084             }
1086             info.totalHBorder = (toRoundNumber(info.border.borderLeftWidth) + toRoundNumber(info.border.borderRightWidth));
1087             info.totalHPadding = (toRoundNumber(info.padding.paddingLeft) + toRoundNumber(info.padding.paddingRight));
1088             info.totalVBorder = (toRoundNumber(info.border.borderBottomWidth) + toRoundNumber(info.border.borderTopWidth));
1089             info.totalVPadding = (toRoundNumber(info.padding.paddingBottom) + toRoundNumber(info.padding.paddingTop));
1091             return info;
1092         },
1094         /**
1095          * Sync the Resize UI with internal values from
1096          * <a href="Resize.html#property_info">info</a>.
1097          *
1098          * @method _syncUI
1099          * @protected
1100          */
1101         _syncUI: function() {
1102             var instance = this,
1103                 info = instance.info,
1104                 wrapperSurrounding = instance.wrapperSurrounding,
1105                 wrapper = instance.get(WRAPPER),
1106                 node = instance.get(NODE);
1108             wrapper.sizeTo(info.offsetWidth, info.offsetHeight);
1110             if (instance.changeLeftHandles || instance.changeTopHandles) {
1111                 wrapper.setXY([info.left, info.top]);
1112             }
1114             // if a wrap node is being used
1115             if (!wrapper.compareTo(node)) {
1116                 // the original internal node borders were copied to the wrapper on
1117                 // _copyStyles, to compensate that subtract the borders from the internal node
1118                 node.sizeTo(
1119                     info.offsetWidth - wrapperSurrounding.totalHBorder,
1120                     info.offsetHeight - wrapperSurrounding.totalVBorder
1121                 );
1122             }
1124             // prevent webkit textarea resize
1125             if (Y.UA.webkit) {
1126                 node.setStyle(RESIZE, NONE);
1127             }
1128         },
1130         /**
1131          * Update <code>instance.changeHeightHandles,
1132          * instance.changeLeftHandles, instance.changeTopHandles,
1133          * instance.changeWidthHandles</code> information.
1134          *
1135          * @method _updateChangeHandleInfo
1136          * @private
1137          */
1138         _updateChangeHandleInfo: function(handle) {
1139             var instance = this;
1141             instance.changeHeightHandles = instance.REGEX_CHANGE_HEIGHT.test(handle);
1142             instance.changeLeftHandles = instance.REGEX_CHANGE_LEFT.test(handle);
1143             instance.changeTopHandles = instance.REGEX_CHANGE_TOP.test(handle);
1144             instance.changeWidthHandles = instance.REGEX_CHANGE_WIDTH.test(handle);
1145         },
1147         /**
1148          * Update <a href="Resize.html#property_info">info</a> values (bottom, actXY, left, top, offsetHeight, offsetWidth, right).
1149          *
1150          * @method _updateInfo
1151          * @private
1152          */
1153         _updateInfo: function(event) {
1154             var instance = this;
1156             instance.info = instance._getInfo(instance.get(WRAPPER), event);
1157         },
1159         /**
1160          * Update properties
1161          * <a href="Resize.html#property_nodeSurrounding">nodeSurrounding</a>,
1162          * <a href="Resize.html#property_nodeSurrounding">wrapperSurrounding</a>,
1163          * <a href="Resize.html#property_nodeSurrounding">totalVSurrounding</a>,
1164          * <a href="Resize.html#property_nodeSurrounding">totalHSurrounding</a>.
1165          *
1166          * @method _updateSurroundingInfo
1167          * @private
1168          */
1169         _updateSurroundingInfo: function() {
1170             var instance = this,
1171                 node = instance.get(NODE),
1172                 wrapper = instance.get(WRAPPER),
1173                 nodeSurrounding = instance._getBoxSurroundingInfo(node),
1174                 wrapperSurrounding = instance._getBoxSurroundingInfo(wrapper);
1176             instance.nodeSurrounding = nodeSurrounding;
1177             instance.wrapperSurrounding = wrapperSurrounding;
1179             instance.totalVSurrounding = (nodeSurrounding.totalVPadding + wrapperSurrounding.totalVBorder);
1180             instance.totalHSurrounding = (nodeSurrounding.totalHPadding + wrapperSurrounding.totalHBorder);
1181         },
1183         /**
1184          * Set the active state of the handles.
1185          *
1186          * @method _setActiveHandlesUI
1187          * @param {boolean} val True to activate the handles, false to deactivate.
1188          * @protected
1189          */
1190         _setActiveHandlesUI: function(val) {
1191             var instance = this,
1192                 activeHandleNode = instance.get(ACTIVE_HANDLE_NODE);
1194             if (activeHandleNode) {
1195                 if (val) {
1196                     // remove CSS_RESIZE_HANDLE_ACTIVE from all handles before addClass on the active
1197                     instance.eachHandle(
1198                         function(handleEl) {
1199                             handleEl.removeClass(CSS_RESIZE_HANDLE_ACTIVE);
1200                         }
1201                     );
1203                     activeHandleNode.addClass(CSS_RESIZE_HANDLE_ACTIVE);
1204                 }
1205                 else {
1206                     activeHandleNode.removeClass(CSS_RESIZE_HANDLE_ACTIVE);
1207                 }
1208             }
1209         },
1211         /**
1212          * Setter for the handles attribute
1213          *
1214          * @method _setHandles
1215          * @protected
1216          * @param {String} val
1217          */
1218         _setHandles: function(val) {
1219             var instance = this,
1220                 handles = [];
1222             // handles attr accepts both array or string
1223             if (isArray(val)) {
1224                 handles = val;
1225             }
1226             else if (isString(val)) {
1227                 // if the handles attr passed in is an ALL string...
1228                 if (val.toLowerCase() === ALL) {
1229                     handles = instance.ALL_HANDLES;
1230                 }
1231                 // otherwise, split the string to extract the handles
1232                 else {
1233                     Y.each(
1234                         val.split(COMMA),
1235                         function(node) {
1236                             var handle = trim(node);
1238                             // if its a valid handle, add it to the handles output
1239                             if (indexOf(instance.ALL_HANDLES, handle) > -1) {
1240                                 handles.push(handle);
1241                             }
1242                         }
1243                     );
1244                 }
1245             }
1247             return handles;
1248         },
1250         /**
1251          * Set the visibility of the handles.
1252          *
1253          * @method _setHideHandlesUI
1254          * @param {boolean} val True to hide the handles, false to show.
1255          * @protected
1256          */
1257         _setHideHandlesUI: function(val) {
1258             var instance = this,
1259                 wrapper = instance.get(WRAPPER);
1261             if (!instance.get(RESIZING)) {
1262                 if (val) {
1263                     wrapper.addClass(CSS_RESIZE_HIDDEN_HANDLES);
1264                 }
1265                 else {
1266                     wrapper.removeClass(CSS_RESIZE_HIDDEN_HANDLES);
1267                 }
1268             }
1269         },
1271         /**
1272          * Setter for the wrap attribute
1273          *
1274          * @method _setWrap
1275          * @protected
1276          * @param {boolean} val
1277          */
1278         _setWrap: function(val) {
1279             var instance = this,
1280                 node = instance.get(NODE),
1281                 nodeName = node.get(NODE_NAME),
1282                 typeRegex = instance.get(WRAP_TYPES);
1284             // if nodeName is listed on WRAP_TYPES force use the wrapper
1285             if (typeRegex.test(nodeName)) {
1286                 val = true;
1287             }
1289             return val;
1290         },
1292         /**
1293          * Default resize:mouseUp handler
1294          *
1295          * @method _defMouseUpFn
1296          * @param {EventFacade} event The Event object
1297          * @protected
1298          */
1299         _defMouseUpFn: function() {
1300             var instance = this;
1302             instance.set(RESIZING, false);
1303         },
1305         /**
1306          * Default resize:resize handler
1307          *
1308          * @method _defResizeFn
1309          * @param {EventFacade} event The Event object
1310          * @protected
1311          */
1312         _defResizeFn: function(event) {
1313             var instance = this;
1315             instance._resize(event);
1316         },
1318         /**
1319          * Logic method for _defResizeFn. Allow AOP.
1320          *
1321          * @method _resize
1322          * @param {EventFacade} event The Event object
1323          * @protected
1324          */
1325         _resize: function(event) {
1326             var instance = this;
1328             instance._handleResizeAlignEvent(event.dragEvent);
1330             // _syncUI of the wrapper, not using proxy
1331             instance._syncUI();
1332         },
1334         /**
1335          * Default resize:align handler
1336          *
1337          * @method _defResizeAlignFn
1338          * @param {EventFacade} event The Event object
1339          * @protected
1340          */
1341         _defResizeAlignFn: function(event) {
1342             var instance = this;
1344             instance._resizeAlign(event);
1345         },
1347         /**
1348          * Logic method for _defResizeAlignFn. Allow AOP.
1349          *
1350          * @method _resizeAlign
1351          * @param {EventFacade} event The Event object
1352          * @protected
1353          */
1354         _resizeAlign: function(event) {
1355             var instance = this,
1356                 info,
1357                 defMinHeight,
1358                 defMinWidth;
1360             instance.lastInfo = instance.info;
1362             // update the instance.info values
1363             instance._updateInfo(event);
1365             info = instance.info;
1367             // basic resize calculations
1368             instance._calcResize();
1370             // if Y.Plugin.ResizeConstrained is not plugged, check for min dimension
1371             if (!instance.con) {
1372                 defMinHeight = (instance.get(DEF_MIN_HEIGHT) + instance.totalVSurrounding);
1373                 defMinWidth = (instance.get(DEF_MIN_WIDTH) + instance.totalHSurrounding);
1375                 if (info.offsetHeight <= defMinHeight) {
1376                     instance._checkSize(OFFSET_HEIGHT, defMinHeight);
1377                 }
1379                 if (info.offsetWidth <= defMinWidth) {
1380                     instance._checkSize(OFFSET_WIDTH, defMinWidth);
1381                 }
1382             }
1383         },
1385         /**
1386          * Default resize:end handler
1387          *
1388          * @method _defResizeEndFn
1389          * @param {EventFacade} event The Event object
1390          * @protected
1391          */
1392         _defResizeEndFn: function(event) {
1393             var instance = this;
1395             instance._resizeEnd(event);
1396         },
1398         /**
1399          * Logic method for _defResizeEndFn. Allow AOP.
1400          *
1401          * @method _resizeEnd
1402          * @param {EventFacade} event The Event object
1403          * @protected
1404          */
1405         _resizeEnd: function(event) {
1406             var instance = this,
1407                 drag = event.dragEvent.target;
1409             // reseting actXY from drag when drag end
1410             drag.actXY = [];
1412             // syncUI when resize end
1413             instance._syncUI();
1415             instance._setActiveHandlesUI(false);
1417             instance.set(ACTIVE_HANDLE, null);
1418             instance.set(ACTIVE_HANDLE_NODE, null);
1420             instance.handle = null;
1421         },
1423         /**
1424          * Default resize:start handler
1425          *
1426          * @method _defResizeStartFn
1427          * @param {EventFacade} event The Event object
1428          * @protected
1429          */
1430         _defResizeStartFn: function(event) {
1431             var instance = this;
1433             instance._resizeStart(event);
1434         },
1436         /**
1437          * Logic method for _defResizeStartFn. Allow AOP.
1438          *
1439          * @method _resizeStart
1440          * @param {EventFacade} event The Event object
1441          * @protected
1442          */
1443         _resizeStart: function(event) {
1444             var instance = this,
1445                 wrapper = instance.get(WRAPPER);
1447             instance.handle = instance.get(ACTIVE_HANDLE);
1449             instance.set(RESIZING, true);
1451             instance._updateSurroundingInfo();
1453             // create an originalInfo information for reference
1454             instance.originalInfo = instance._getInfo(wrapper, event);
1456             instance._updateInfo(event);
1457         },
1459         /**
1460          * Fires the resize:mouseUp event.
1461          *
1462          * @method _handleMouseUpEvent
1463          * @param {EventFacade} event resize:mouseUp event facade
1464          * @protected
1465          */
1466         _handleMouseUpEvent: function(event) {
1467             this.fire(EV_MOUSE_UP, { dragEvent: event, info: this.info });
1468         },
1470         /**
1471          * Fires the resize:resize event.
1472          *
1473          * @method _handleResizeEvent
1474          * @param {EventFacade} event resize:resize event facade
1475          * @protected
1476          */
1477         _handleResizeEvent: function(event) {
1478             this.fire(EV_RESIZE, { dragEvent: event, info: this.info });
1479         },
1481         /**
1482          * Fires the resize:align event.
1483          *
1484          * @method _handleResizeAlignEvent
1485          * @param {EventFacade} event resize:resize event facade
1486          * @protected
1487          */
1488         _handleResizeAlignEvent: function(event) {
1489             this.fire(EV_RESIZE_ALIGN, { dragEvent: event, info: this.info });
1490         },
1492         /**
1493          * Fires the resize:end event.
1494          *
1495          * @method _handleResizeEndEvent
1496          * @param {EventFacade} event resize:end event facade
1497          * @protected
1498          */
1499         _handleResizeEndEvent: function(event) {
1500             this.fire(EV_RESIZE_END, { dragEvent: event, info: this.info });
1501         },
1503         /**
1504          * Fires the resize:start event.
1505          *
1506          * @method _handleResizeStartEvent
1507          * @param {EventFacade} event resize:start event facade
1508          * @protected
1509          */
1510         _handleResizeStartEvent: function(event) {
1511             if (!this.get(ACTIVE_HANDLE)) {
1512                 //This handles the "touch" case
1513                 this._setHandleFromNode(event.target.get('node'));
1514             }
1515             this.fire(EV_RESIZE_START, { dragEvent: event, info: this.info });
1516         },
1518         /**
1519          * Mouseenter event handler for the <a href="Resize.html#attr_wrapper">wrapper</a>.
1520          *
1521          * @method _onWrapperMouseEnter
1522          * @param {EventFacade} event
1523          * @protected
1524          */
1525         _onWrapperMouseEnter: function() {
1526             var instance = this;
1528             if (instance.get(AUTO_HIDE)) {
1529                 instance._setHideHandlesUI(false);
1530             }
1531         },
1533         /**
1534          * Mouseleave event handler for the <a href="Resize.html#attr_wrapper">wrapper</a>.
1535          *
1536          * @method _onWrapperMouseLeave
1537          * @param {EventFacade} event
1538          * @protected
1539          */
1540         _onWrapperMouseLeave: function() {
1541             var instance = this;
1543             if (instance.get(AUTO_HIDE)) {
1544                 instance._setHideHandlesUI(true);
1545             }
1546         },
1548         /**
1549          * Handles setting the activeHandle from a node, used from startDrag (for touch) and mouseenter (for mouse).
1550          *
1551          * @method _setHandleFromNode
1552          * @param {Node} node
1553          * @protected
1554          */
1555         _setHandleFromNode: function(node) {
1556             var instance = this,
1557                 handle = instance._extractHandleName(node);
1559             if (!instance.get(RESIZING)) {
1560                 instance.set(ACTIVE_HANDLE, handle);
1561                 instance.set(ACTIVE_HANDLE_NODE, node);
1563                 instance._setActiveHandlesUI(true);
1564                 instance._updateChangeHandleInfo(handle);
1565             }
1566         },
1568         /**
1569          * Mouseenter event handler for the handles.
1570          *
1571          * @method _onHandleMouseEnter
1572          * @param {EventFacade} event
1573          * @protected
1574          */
1575         _onHandleMouseEnter: function(event) {
1576             this._setHandleFromNode(event.currentTarget);
1577         },
1579         /**
1580          * Mouseout event handler for the handles.
1581          *
1582          * @method _onHandleMouseLeave
1583          * @param {EventFacade} event
1584          * @protected
1585          */
1586         _onHandleMouseLeave: function() {
1587             var instance = this;
1589             if (!instance.get(RESIZING)) {
1590                 instance._setActiveHandlesUI(false);
1591             }
1592         },
1594         /**
1595          * Default value for the wrapper handles node attribute
1596          *
1597          * @method _valueHandlesWrapper
1598          * @protected
1599          * @readOnly
1600          */
1601         _valueHandlesWrapper: function() {
1602             return Y.Node.create(this.HANDLES_WRAP_TEMPLATE);
1603         },
1605         /**
1606          * Default value for the wrapper attribute
1607          *
1608          * @method _valueWrapper
1609          * @protected
1610          * @readOnly
1611          */
1612         _valueWrapper: function() {
1613             var instance = this,
1614                 node = instance.get(NODE),
1615                 pNode = node.get(PARENT_NODE),
1616                 // by deafult the wrapper is always the node
1617                 wrapper = node;
1619             // if the node is listed on the wrapTypes or wrap is set to true, create another wrapper
1620             if (instance.get(WRAP)) {
1621                 wrapper = Y.Node.create(instance.WRAP_TEMPLATE);
1623                 if (pNode) {
1624                     pNode.insertBefore(wrapper, node);
1625                 }
1627                 wrapper.append(node);
1629                 instance._copyStyles(node, wrapper);
1631                 // remove positioning of wrapped node, the WRAPPER take care about positioning
1632                 node.setStyles({
1633                     position: STATIC,
1634                     left: 0,
1635                     top: 0
1636                 });
1637             }
1639             return wrapper;
1640         }
1641     }
1644 Y.each(Y.Resize.prototype.ALL_HANDLES, function(handle) {
1645     // creating ATTRS with the handles elements
1646     Y.Resize.ATTRS[handleAttrName(handle)] = {
1647         setter: function() {
1648             return this._buildHandle(handle);
1649         },
1650         value: null,
1651         writeOnce: true
1652     };
1656 }, '3.13.0', {"requires": ["base", "widget", "event", "oop", "dd-drag", "dd-delegate", "dd-drop"], "skinnable": true});