[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 Yahoo! Inc. All rights reserved. 4 Licensed under the BSD License. 5 http://yuilibrary.com/license/ 6 */ 7 8 YUI.add('resize-base', function (Y, NAME) { 9 10 /** 11 * The Resize Utility allows you to make an HTML element resizable. 12 * @module resize 13 * @main resize 14 */ 15 16 var Lang = Y.Lang, 17 isArray = Lang.isArray, 18 isBoolean = Lang.isBoolean, 19 isNumber = Lang.isNumber, 20 isString = Lang.isString, 21 22 yArray = Y.Array, 23 trim = Lang.trim, 24 indexOf = yArray.indexOf, 25 26 COMMA = ',', 27 DOT = '.', 28 EMPTY_STR = '', 29 HANDLE_SUB = '{handle}', 30 SPACE = ' ', 31 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', 69 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', 75 76 T = 't', 77 TR = 'tr', 78 R = 'r', 79 BR = 'br', 80 B = 'b', 81 BL = 'bl', 82 L = 'l', 83 TL = 'tl', 84 85 concat = function() { 86 return Array.prototype.slice.call(arguments).join(SPACE); 87 }, 88 89 // round the passed number to get rid of pixel-flickering 90 toRoundNumber = function(num) { 91 return Math.round(parseFloat(num)) || 0; 92 }, 93 94 getCompStyle = function(node, val) { 95 return node.getComputedStyle(val); 96 }, 97 98 handleAttrName = function(handle) { 99 return HANDLE + handle.toUpperCase(); 100 }, 101 102 isNode = function(v) { 103 return (v instanceof Y.Node); 104 }, 105 106 toInitialCap = Y.cached( 107 function(str) { 108 return str.substring(0, 1).toUpperCase() + str.substring(1); 109 } 110 ), 111 112 capitalize = Y.cached(function() { 113 var out = [], 114 args = yArray(arguments, 0, true); 115 116 yArray.each(args, function(part, i) { 117 if (i > 0) { 118 part = toInitialCap(part); 119 } 120 out.push(part); 121 }); 122 123 return out.join(EMPTY_STR); 124 }), 125 126 getCN = Y.ClassNameManager.getClassName, 127 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); 137 138 /** 139 A base class for Resize, providing: 140 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. 151 152 Quick Example: 153 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 }); 162 163 Check the list of <a href="Resize.html#attrs">Configuration Attributes</a> available for 164 Resize. 165 166 @class Resize 167 @param config {Object} Object literal specifying widget configuration properties. 168 @constructor 169 @extends Base 170 */ 171 172 function Resize() { 173 Resize.superclass.constructor.apply(this, arguments); 174 } 175 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, 185 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 }, 209 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 }, 222 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 }, 235 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 }, 248 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 }, 261 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 }, 274 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 }, 286 287 /** 288 * The selector or element to resize. Required. 289 * 290 * @attribute node 291 * @type Node 292 */ 293 node: { 294 setter: Y.one 295 }, 296 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 }, 308 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 }, 324 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 }, 338 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 }, 354 355 RULES: { 356 b: function(instance, dx, dy) { 357 var info = instance.info, 358 originalInfo = instance.originalInfo; 359 360 info.offsetHeight = originalInfo.offsetHeight + dy; 361 }, 362 363 l: function(instance, dx) { 364 var info = instance.info, 365 originalInfo = instance.originalInfo; 366 367 info.left = originalInfo.left + dx; 368 info.offsetWidth = originalInfo.offsetWidth - dx; 369 }, 370 371 r: function(instance, dx) { 372 var info = instance.info, 373 originalInfo = instance.originalInfo; 374 375 info.offsetWidth = originalInfo.offsetWidth + dx; 376 }, 377 378 t: function(instance, dx, dy) { 379 var info = instance.info, 380 originalInfo = instance.originalInfo; 381 382 info.top = originalInfo.top + dy; 383 info.offsetHeight = originalInfo.offsetHeight - dy; 384 }, 385 386 tr: function() { 387 this.t.apply(this, arguments); 388 this.r.apply(this, arguments); 389 }, 390 391 bl: function() { 392 this.b.apply(this, arguments); 393 this.l.apply(this, arguments); 394 }, 395 396 br: function() { 397 this.b.apply(this, arguments); 398 this.r.apply(this, arguments); 399 }, 400 401 tl: function() { 402 this.t.apply(this, arguments); 403 this.l.apply(this, arguments); 404 } 405 }, 406 407 capitalize: capitalize 408 }); 409 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 ], 421 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, 430 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, 439 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, 448 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, 457 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>', 465 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>', 473 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)+'"> </div>' + 482 '</div>', 483 484 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, 495 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, 506 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, 517 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, 528 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, 537 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, 546 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, 555 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, 564 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, 573 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, 584 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, 593 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, 603 604 /** 605 * Construction logic executed during Resize instantiation. Lifecycle. 606 * 607 * @method initializer 608 * @protected 609 */ 610 initializer: function() { 611 this._eventHandles = []; 612 613 this.renderer(); 614 }, 615 616 /** 617 * Create the DOM structure for the Resize. Lifecycle. 618 * 619 * @method renderUI 620 * @protected 621 */ 622 renderUI: function() { 623 var instance = this; 624 625 instance._renderHandles(); 626 }, 627 628 /** 629 * Bind the events on the Resize UI. Lifecycle. 630 * 631 * @method bindUI 632 * @protected 633 */ 634 bindUI: function() { 635 var instance = this; 636 637 instance._createEvents(); 638 instance._bindDD(); 639 instance._bindHandle(); 640 }, 641 642 /** 643 * Sync the Resize UI. 644 * 645 * @method syncUI 646 * @protected 647 */ 648 syncUI: function() { 649 var instance = this; 650 651 this.get(NODE).addClass(CSS_RESIZE); 652 653 // hide handles if AUTO_HIDE is true 654 instance._setHideHandlesUI( 655 instance.get(AUTO_HIDE) 656 ); 657 }, 658 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); 671 672 Y.each( 673 instance._eventHandles, 674 function(handle) { 675 handle.detach(); 676 } 677 ); 678 679 instance._eventHandles.length = 0; 680 681 // destroy handles dd and remove them from the dom 682 instance.eachHandle(function(handleEl) { 683 instance.delegate.dd.destroy(); 684 685 // remove handle 686 handleEl.remove(true); 687 }); 688 689 instance.delegate.destroy(); 690 691 // unwrap node 692 if (instance.get(WRAP)) { 693 instance._copyStyles(wrapper, node); 694 695 if (pNode) { 696 pNode.insertBefore(node, wrapper); 697 } 698 699 wrapper.remove(true); 700 } 701 702 node.removeClass(CSS_RESIZE); 703 node.removeClass(CSS_RESIZE_HIDDEN_HANDLES); 704 }, 705 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 }, 719 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; 732 733 Y.each( 734 instance.get(HANDLES), 735 function(handle, i) { 736 var handleEl = instance.get( 737 handleAttrName(handle) 738 ); 739 740 fn.apply(instance, [handleEl, handle, i]); 741 } 742 ); 743 }, 744 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; 753 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 ); 768 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 }, 776 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); 786 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 }, 794 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 }; 813 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 {EventFacade} event The resize start event. 821 * @bubbles Resize 822 */ 823 publish(EV_RESIZE_START, this._defResizeStartFn); 824 825 /** 826 * Handles the resize event. Fired on each pixel when the handle is 827 * being dragged. 828 * 829 * @event resize:resize 830 * @preventable _defResizeFn 831 * @param {EventFacade} event The resize event. 832 * @bubbles Resize 833 */ 834 publish(EV_RESIZE, this._defResizeFn); 835 836 /** 837 * Handles the resize align event. 838 * 839 * @event resize:align 840 * @preventable _defResizeAlignFn 841 * @param {EventFacade} event The resize align event. 842 * @bubbles Resize 843 */ 844 publish(EV_RESIZE_ALIGN, this._defResizeAlignFn); 845 846 /** 847 * Handles the resize end event. Fired when a handle stop to be 848 * dragged. 849 * 850 * @event resize:end 851 * @preventable _defResizeEndFn 852 * @param {EventFacade} event The resize end event. 853 * @bubbles Resize 854 */ 855 publish(EV_RESIZE_END, this._defResizeEndFn); 856 857 /** 858 * Handles the resize mouseUp event. Fired when a mouseUp event happens on a 859 * handle. 860 * 861 * @event resize:mouseUp 862 * @preventable _defMouseUpFn 863 * @param {EventFacade} event The resize mouseUp event. 864 * @bubbles Resize 865 */ 866 publish(EV_MOUSE_UP, this._defMouseUpFn); 867 }, 868 869 /** 870 * Responsible for loop each handle element and append to the wrapper. 871 * 872 * @method _renderHandles 873 * @protected 874 */ 875 _renderHandles: function() { 876 var instance = this, 877 wrapper = instance.get(WRAPPER), 878 handlesWrapper = instance.get(HANDLES_WRAPPER); 879 880 instance.eachHandle(function(handleEl) { 881 handlesWrapper.append(handleEl); 882 }); 883 884 wrapper.append(handlesWrapper); 885 }, 886 887 /** 888 * Creates the handle element based on the handle name and initialize the 889 * DragDrop on it. 890 * 891 * @method _buildHandle 892 * @param {String} handle Handle name ('t', 'tr', 'b', ...). 893 * @protected 894 */ 895 _buildHandle: function(handle) { 896 var instance = this; 897 898 return Y.Node.create( 899 Y.Lang.sub(instance.HANDLE_TEMPLATE, { 900 handle: handle 901 }) 902 ); 903 }, 904 905 /** 906 * Basic resize calculations. 907 * 908 * @method _calcResize 909 * @protected 910 */ 911 _calcResize: function() { 912 var instance = this, 913 handle = instance.handle, 914 info = instance.info, 915 originalInfo = instance.originalInfo, 916 917 dx = info.actXY[0] - originalInfo.actXY[0], 918 dy = info.actXY[1] - originalInfo.actXY[1]; 919 920 if (handle && Y.Resize.RULES[handle]) { 921 Y.Resize.RULES[handle](instance, dx, dy); 922 } 923 else { 924 Y.log('Handle rule not found: ' + handle, 'warn', 'resize'); 925 } 926 }, 927 928 /** 929 * Helper method to update the current size value on 930 * <a href="Resize.html#property_info">info</a> to respect the 931 * min/max values and fix the top/left calculations. 932 * 933 * @method _checkSize 934 * @param {String} offset 'offsetHeight' or 'offsetWidth' 935 * @param {number} size Size to restrict the offset 936 * @protected 937 */ 938 _checkSize: function(offset, size) { 939 var instance = this, 940 info = instance.info, 941 originalInfo = instance.originalInfo, 942 axis = (offset === OFFSET_HEIGHT) ? TOP : LEFT; 943 944 // forcing the offsetHeight/offsetWidth to be the passed size 945 info[offset] = size; 946 947 // predicting, based on the original information, the last left valid in case of reach the min/max dimension 948 // this calculation avoid browser event leaks when user interact very fast 949 if (((axis === LEFT) && instance.changeLeftHandles) || 950 ((axis === TOP) && instance.changeTopHandles)) { 951 952 info[axis] = originalInfo[axis] + originalInfo[offset] - size; 953 } 954 }, 955 956 /** 957 * Copy relevant styles of the <a href="Resize.html#attr_node">node</a> 958 * to the <a href="Resize.html#attr_wrapper">wrapper</a>. 959 * 960 * @method _copyStyles 961 * @param {Node} node Node from. 962 * @param {Node} wrapper Node to. 963 * @protected 964 */ 965 _copyStyles: function(node, wrapper) { 966 var position = node.getStyle(POSITION).toLowerCase(), 967 surrounding = this._getBoxSurroundingInfo(node), 968 wrapperStyle; 969 970 // resizable wrapper should be positioned 971 if (position === STATIC) { 972 position = RELATIVE; 973 } 974 975 wrapperStyle = { 976 position: position, 977 left: getCompStyle(node, LEFT), 978 top: getCompStyle(node, TOP) 979 }; 980 981 Y.mix(wrapperStyle, surrounding.margin); 982 Y.mix(wrapperStyle, surrounding.border); 983 984 wrapper.setStyles(wrapperStyle); 985 986 // remove margin and border from the internal node 987 node.setStyles({ border: 0, margin: 0 }); 988 989 wrapper.sizeTo( 990 node.get(OFFSET_WIDTH) + surrounding.totalHBorder, 991 node.get(OFFSET_HEIGHT) + surrounding.totalVBorder 992 ); 993 }, 994 995 // extract handle name from a string 996 // using Y.cached to memoize the function for performance 997 _extractHandleName: Y.cached( 998 function(node) { 999 var className = node.get(CLASS_NAME), 1000 1001 match = className.match( 1002 new RegExp( 1003 getCN(RESIZE, HANDLE, '(\\w{1,2})\\b') 1004 ) 1005 ); 1006 1007 return match ? match[1] : null; 1008 } 1009 ), 1010 1011 /** 1012 * <p>Generates metadata to the <a href="Resize.html#property_info">info</a> 1013 * and <a href="Resize.html#property_originalInfo">originalInfo</a></p> 1014 * <pre><code>bottom, actXY, left, top, offsetHeight, offsetWidth, right</code></pre> 1015 * 1016 * @method _getInfo 1017 * @param {Node} node 1018 * @param {EventFacade} event 1019 * @private 1020 */ 1021 _getInfo: function(node, event) { 1022 var actXY = [0,0], 1023 drag = event.dragEvent.target, 1024 nodeXY = node.getXY(), 1025 nodeX = nodeXY[0], 1026 nodeY = nodeXY[1], 1027 offsetHeight = node.get(OFFSET_HEIGHT), 1028 offsetWidth = node.get(OFFSET_WIDTH); 1029 1030 if (event) { 1031 // the xy that the node will be set to. Changing this will alter the position as it's dragged. 1032 actXY = (drag.actXY.length ? drag.actXY : drag.lastXY); 1033 } 1034 1035 return { 1036 actXY: actXY, 1037 bottom: (nodeY + offsetHeight), 1038 left: nodeX, 1039 offsetHeight: offsetHeight, 1040 offsetWidth: offsetWidth, 1041 right: (nodeX + offsetWidth), 1042 top: nodeY 1043 }; 1044 }, 1045 1046 /** 1047 * Each box has a content area and optional surrounding margin, 1048 * padding and * border areas. This method get all this information from 1049 * the passed node. For more reference see 1050 * <a href="http://www.w3.org/TR/CSS21/box.html#box-dimensions"> 1051 * http://www.w3.org/TR/CSS21/box.html#box-dimensions</a>. 1052 * 1053 * @method _getBoxSurroundingInfo 1054 * @param {Node} node 1055 * @private 1056 * @return {Object} 1057 */ 1058 _getBoxSurroundingInfo: function(node) { 1059 var info = { 1060 padding: {}, 1061 margin: {}, 1062 border: {} 1063 }; 1064 1065 if (isNode(node)) { 1066 Y.each([ TOP, RIGHT, BOTTOM, LEFT ], function(dir) { 1067 var paddingProperty = capitalize(PADDING, dir), 1068 marginProperty = capitalize(MARGIN, dir), 1069 borderWidthProperty = capitalize(BORDER, dir, WIDTH), 1070 borderColorProperty = capitalize(BORDER, dir, COLOR), 1071 borderStyleProperty = capitalize(BORDER, dir, STYLE); 1072 1073 info.border[borderColorProperty] = getCompStyle(node, borderColorProperty); 1074 info.border[borderStyleProperty] = getCompStyle(node, borderStyleProperty); 1075 info.border[borderWidthProperty] = getCompStyle(node, borderWidthProperty); 1076 info.margin[marginProperty] = getCompStyle(node, marginProperty); 1077 info.padding[paddingProperty] = getCompStyle(node, paddingProperty); 1078 }); 1079 } 1080 1081 info.totalHBorder = (toRoundNumber(info.border.borderLeftWidth) + toRoundNumber(info.border.borderRightWidth)); 1082 info.totalHPadding = (toRoundNumber(info.padding.paddingLeft) + toRoundNumber(info.padding.paddingRight)); 1083 info.totalVBorder = (toRoundNumber(info.border.borderBottomWidth) + toRoundNumber(info.border.borderTopWidth)); 1084 info.totalVPadding = (toRoundNumber(info.padding.paddingBottom) + toRoundNumber(info.padding.paddingTop)); 1085 1086 return info; 1087 }, 1088 1089 /** 1090 * Sync the Resize UI with internal values from 1091 * <a href="Resize.html#property_info">info</a>. 1092 * 1093 * @method _syncUI 1094 * @protected 1095 */ 1096 _syncUI: function() { 1097 var instance = this, 1098 info = instance.info, 1099 wrapperSurrounding = instance.wrapperSurrounding, 1100 wrapper = instance.get(WRAPPER), 1101 node = instance.get(NODE); 1102 1103 wrapper.sizeTo(info.offsetWidth, info.offsetHeight); 1104 1105 if (instance.changeLeftHandles || instance.changeTopHandles) { 1106 wrapper.setXY([info.left, info.top]); 1107 } 1108 1109 // if a wrap node is being used 1110 if (!wrapper.compareTo(node)) { 1111 // the original internal node borders were copied to the wrapper on 1112 // _copyStyles, to compensate that subtract the borders from the internal node 1113 node.sizeTo( 1114 info.offsetWidth - wrapperSurrounding.totalHBorder, 1115 info.offsetHeight - wrapperSurrounding.totalVBorder 1116 ); 1117 } 1118 1119 // prevent webkit textarea resize 1120 if (Y.UA.webkit) { 1121 node.setStyle(RESIZE, NONE); 1122 } 1123 }, 1124 1125 /** 1126 * Update <code>instance.changeHeightHandles, 1127 * instance.changeLeftHandles, instance.changeTopHandles, 1128 * instance.changeWidthHandles</code> information. 1129 * 1130 * @method _updateChangeHandleInfo 1131 * @private 1132 */ 1133 _updateChangeHandleInfo: function(handle) { 1134 var instance = this; 1135 1136 instance.changeHeightHandles = instance.REGEX_CHANGE_HEIGHT.test(handle); 1137 instance.changeLeftHandles = instance.REGEX_CHANGE_LEFT.test(handle); 1138 instance.changeTopHandles = instance.REGEX_CHANGE_TOP.test(handle); 1139 instance.changeWidthHandles = instance.REGEX_CHANGE_WIDTH.test(handle); 1140 }, 1141 1142 /** 1143 * Update <a href="Resize.html#property_info">info</a> values (bottom, actXY, left, top, offsetHeight, offsetWidth, right). 1144 * 1145 * @method _updateInfo 1146 * @private 1147 */ 1148 _updateInfo: function(event) { 1149 var instance = this; 1150 1151 instance.info = instance._getInfo(instance.get(WRAPPER), event); 1152 }, 1153 1154 /** 1155 * Update properties 1156 * <a href="Resize.html#property_nodeSurrounding">nodeSurrounding</a>, 1157 * <a href="Resize.html#property_nodeSurrounding">wrapperSurrounding</a>, 1158 * <a href="Resize.html#property_nodeSurrounding">totalVSurrounding</a>, 1159 * <a href="Resize.html#property_nodeSurrounding">totalHSurrounding</a>. 1160 * 1161 * @method _updateSurroundingInfo 1162 * @private 1163 */ 1164 _updateSurroundingInfo: function() { 1165 var instance = this, 1166 node = instance.get(NODE), 1167 wrapper = instance.get(WRAPPER), 1168 nodeSurrounding = instance._getBoxSurroundingInfo(node), 1169 wrapperSurrounding = instance._getBoxSurroundingInfo(wrapper); 1170 1171 instance.nodeSurrounding = nodeSurrounding; 1172 instance.wrapperSurrounding = wrapperSurrounding; 1173 1174 instance.totalVSurrounding = (nodeSurrounding.totalVPadding + wrapperSurrounding.totalVBorder); 1175 instance.totalHSurrounding = (nodeSurrounding.totalHPadding + wrapperSurrounding.totalHBorder); 1176 }, 1177 1178 /** 1179 * Set the active state of the handles. 1180 * 1181 * @method _setActiveHandlesUI 1182 * @param {boolean} val True to activate the handles, false to deactivate. 1183 * @protected 1184 */ 1185 _setActiveHandlesUI: function(val) { 1186 var instance = this, 1187 activeHandleNode = instance.get(ACTIVE_HANDLE_NODE); 1188 1189 if (activeHandleNode) { 1190 if (val) { 1191 // remove CSS_RESIZE_HANDLE_ACTIVE from all handles before addClass on the active 1192 instance.eachHandle( 1193 function(handleEl) { 1194 handleEl.removeClass(CSS_RESIZE_HANDLE_ACTIVE); 1195 } 1196 ); 1197 1198 activeHandleNode.addClass(CSS_RESIZE_HANDLE_ACTIVE); 1199 } 1200 else { 1201 activeHandleNode.removeClass(CSS_RESIZE_HANDLE_ACTIVE); 1202 } 1203 } 1204 }, 1205 1206 /** 1207 * Setter for the handles attribute 1208 * 1209 * @method _setHandles 1210 * @protected 1211 * @param {String} val 1212 */ 1213 _setHandles: function(val) { 1214 var instance = this, 1215 handles = []; 1216 1217 // handles attr accepts both array or string 1218 if (isArray(val)) { 1219 handles = val; 1220 } 1221 else if (isString(val)) { 1222 // if the handles attr passed in is an ALL string... 1223 if (val.toLowerCase() === ALL) { 1224 handles = instance.ALL_HANDLES; 1225 } 1226 // otherwise, split the string to extract the handles 1227 else { 1228 Y.each( 1229 val.split(COMMA), 1230 function(node) { 1231 var handle = trim(node); 1232 1233 // if its a valid handle, add it to the handles output 1234 if (indexOf(instance.ALL_HANDLES, handle) > -1) { 1235 handles.push(handle); 1236 } 1237 } 1238 ); 1239 } 1240 } 1241 1242 return handles; 1243 }, 1244 1245 /** 1246 * Set the visibility of the handles. 1247 * 1248 * @method _setHideHandlesUI 1249 * @param {boolean} val True to hide the handles, false to show. 1250 * @protected 1251 */ 1252 _setHideHandlesUI: function(val) { 1253 var instance = this, 1254 wrapper = instance.get(WRAPPER); 1255 1256 if (!instance.get(RESIZING)) { 1257 if (val) { 1258 wrapper.addClass(CSS_RESIZE_HIDDEN_HANDLES); 1259 } 1260 else { 1261 wrapper.removeClass(CSS_RESIZE_HIDDEN_HANDLES); 1262 } 1263 } 1264 }, 1265 1266 /** 1267 * Setter for the wrap attribute 1268 * 1269 * @method _setWrap 1270 * @protected 1271 * @param {boolean} val 1272 */ 1273 _setWrap: function(val) { 1274 var instance = this, 1275 node = instance.get(NODE), 1276 nodeName = node.get(NODE_NAME), 1277 typeRegex = instance.get(WRAP_TYPES); 1278 1279 // if nodeName is listed on WRAP_TYPES force use the wrapper 1280 if (typeRegex.test(nodeName)) { 1281 val = true; 1282 } 1283 1284 return val; 1285 }, 1286 1287 /** 1288 * Default resize:mouseUp handler 1289 * 1290 * @method _defMouseUpFn 1291 * @param {EventFacade} event The Event object 1292 * @protected 1293 */ 1294 _defMouseUpFn: function() { 1295 var instance = this; 1296 1297 instance.set(RESIZING, false); 1298 }, 1299 1300 /** 1301 * Default resize:resize handler 1302 * 1303 * @method _defResizeFn 1304 * @param {EventFacade} event The Event object 1305 * @protected 1306 */ 1307 _defResizeFn: function(event) { 1308 var instance = this; 1309 1310 instance._resize(event); 1311 }, 1312 1313 /** 1314 * Logic method for _defResizeFn. Allow AOP. 1315 * 1316 * @method _resize 1317 * @param {EventFacade} event The Event object 1318 * @protected 1319 */ 1320 _resize: function(event) { 1321 var instance = this; 1322 1323 instance._handleResizeAlignEvent(event.dragEvent); 1324 1325 // _syncUI of the wrapper, not using proxy 1326 instance._syncUI(); 1327 }, 1328 1329 /** 1330 * Default resize:align handler 1331 * 1332 * @method _defResizeAlignFn 1333 * @param {EventFacade} event The Event object 1334 * @protected 1335 */ 1336 _defResizeAlignFn: function(event) { 1337 var instance = this; 1338 1339 instance._resizeAlign(event); 1340 }, 1341 1342 /** 1343 * Logic method for _defResizeAlignFn. Allow AOP. 1344 * 1345 * @method _resizeAlign 1346 * @param {EventFacade} event The Event object 1347 * @protected 1348 */ 1349 _resizeAlign: function(event) { 1350 var instance = this, 1351 info, 1352 defMinHeight, 1353 defMinWidth; 1354 1355 instance.lastInfo = instance.info; 1356 1357 // update the instance.info values 1358 instance._updateInfo(event); 1359 1360 info = instance.info; 1361 1362 // basic resize calculations 1363 instance._calcResize(); 1364 1365 // if Y.Plugin.ResizeConstrained is not plugged, check for min dimension 1366 if (!instance.con) { 1367 defMinHeight = (instance.get(DEF_MIN_HEIGHT) + instance.totalVSurrounding); 1368 defMinWidth = (instance.get(DEF_MIN_WIDTH) + instance.totalHSurrounding); 1369 1370 if (info.offsetHeight <= defMinHeight) { 1371 instance._checkSize(OFFSET_HEIGHT, defMinHeight); 1372 } 1373 1374 if (info.offsetWidth <= defMinWidth) { 1375 instance._checkSize(OFFSET_WIDTH, defMinWidth); 1376 } 1377 } 1378 }, 1379 1380 /** 1381 * Default resize:end handler 1382 * 1383 * @method _defResizeEndFn 1384 * @param {EventFacade} event The Event object 1385 * @protected 1386 */ 1387 _defResizeEndFn: function(event) { 1388 var instance = this; 1389 1390 instance._resizeEnd(event); 1391 }, 1392 1393 /** 1394 * Logic method for _defResizeEndFn. Allow AOP. 1395 * 1396 * @method _resizeEnd 1397 * @param {EventFacade} event The Event object 1398 * @protected 1399 */ 1400 _resizeEnd: function(event) { 1401 var instance = this, 1402 drag = event.dragEvent.target; 1403 1404 // reseting actXY from drag when drag end 1405 drag.actXY = []; 1406 1407 // syncUI when resize end 1408 instance._syncUI(); 1409 1410 instance._setActiveHandlesUI(false); 1411 1412 instance.set(ACTIVE_HANDLE, null); 1413 instance.set(ACTIVE_HANDLE_NODE, null); 1414 1415 instance.handle = null; 1416 }, 1417 1418 /** 1419 * Default resize:start handler 1420 * 1421 * @method _defResizeStartFn 1422 * @param {EventFacade} event The Event object 1423 * @protected 1424 */ 1425 _defResizeStartFn: function(event) { 1426 var instance = this; 1427 1428 instance._resizeStart(event); 1429 }, 1430 1431 /** 1432 * Logic method for _defResizeStartFn. Allow AOP. 1433 * 1434 * @method _resizeStart 1435 * @param {EventFacade} event The Event object 1436 * @protected 1437 */ 1438 _resizeStart: function(event) { 1439 var instance = this, 1440 wrapper = instance.get(WRAPPER); 1441 1442 instance.handle = instance.get(ACTIVE_HANDLE); 1443 1444 instance.set(RESIZING, true); 1445 1446 instance._updateSurroundingInfo(); 1447 1448 // create an originalInfo information for reference 1449 instance.originalInfo = instance._getInfo(wrapper, event); 1450 1451 instance._updateInfo(event); 1452 }, 1453 1454 /** 1455 * Fires the resize:mouseUp event. 1456 * 1457 * @method _handleMouseUpEvent 1458 * @param {EventFacade} event resize:mouseUp event facade 1459 * @protected 1460 */ 1461 _handleMouseUpEvent: function(event) { 1462 this.fire(EV_MOUSE_UP, { dragEvent: event, info: this.info }); 1463 }, 1464 1465 /** 1466 * Fires the resize:resize event. 1467 * 1468 * @method _handleResizeEvent 1469 * @param {EventFacade} event resize:resize event facade 1470 * @protected 1471 */ 1472 _handleResizeEvent: function(event) { 1473 this.fire(EV_RESIZE, { dragEvent: event, info: this.info }); 1474 }, 1475 1476 /** 1477 * Fires the resize:align event. 1478 * 1479 * @method _handleResizeAlignEvent 1480 * @param {EventFacade} event resize:resize event facade 1481 * @protected 1482 */ 1483 _handleResizeAlignEvent: function(event) { 1484 this.fire(EV_RESIZE_ALIGN, { dragEvent: event, info: this.info }); 1485 }, 1486 1487 /** 1488 * Fires the resize:end event. 1489 * 1490 * @method _handleResizeEndEvent 1491 * @param {EventFacade} event resize:end event facade 1492 * @protected 1493 */ 1494 _handleResizeEndEvent: function(event) { 1495 this.fire(EV_RESIZE_END, { dragEvent: event, info: this.info }); 1496 }, 1497 1498 /** 1499 * Fires the resize:start event. 1500 * 1501 * @method _handleResizeStartEvent 1502 * @param {EventFacade} event resize:start event facade 1503 * @protected 1504 */ 1505 _handleResizeStartEvent: function(event) { 1506 if (!this.get(ACTIVE_HANDLE)) { 1507 //This handles the "touch" case 1508 this._setHandleFromNode(event.target.get('node')); 1509 } 1510 this.fire(EV_RESIZE_START, { dragEvent: event, info: this.info }); 1511 }, 1512 1513 /** 1514 * Mouseenter event handler for the <a href="Resize.html#attr_wrapper">wrapper</a>. 1515 * 1516 * @method _onWrapperMouseEnter 1517 * @param {EventFacade} event 1518 * @protected 1519 */ 1520 _onWrapperMouseEnter: function() { 1521 var instance = this; 1522 1523 if (instance.get(AUTO_HIDE)) { 1524 instance._setHideHandlesUI(false); 1525 } 1526 }, 1527 1528 /** 1529 * Mouseleave event handler for the <a href="Resize.html#attr_wrapper">wrapper</a>. 1530 * 1531 * @method _onWrapperMouseLeave 1532 * @param {EventFacade} event 1533 * @protected 1534 */ 1535 _onWrapperMouseLeave: function() { 1536 var instance = this; 1537 1538 if (instance.get(AUTO_HIDE)) { 1539 instance._setHideHandlesUI(true); 1540 } 1541 }, 1542 1543 /** 1544 * Handles setting the activeHandle from a node, used from startDrag (for touch) and mouseenter (for mouse). 1545 * 1546 * @method _setHandleFromNode 1547 * @param {Node} node 1548 * @protected 1549 */ 1550 _setHandleFromNode: function(node) { 1551 var instance = this, 1552 handle = instance._extractHandleName(node); 1553 1554 if (!instance.get(RESIZING)) { 1555 instance.set(ACTIVE_HANDLE, handle); 1556 instance.set(ACTIVE_HANDLE_NODE, node); 1557 1558 instance._setActiveHandlesUI(true); 1559 instance._updateChangeHandleInfo(handle); 1560 } 1561 }, 1562 1563 /** 1564 * Mouseenter event handler for the handles. 1565 * 1566 * @method _onHandleMouseEnter 1567 * @param {EventFacade} event 1568 * @protected 1569 */ 1570 _onHandleMouseEnter: function(event) { 1571 this._setHandleFromNode(event.currentTarget); 1572 }, 1573 1574 /** 1575 * Mouseout event handler for the handles. 1576 * 1577 * @method _onHandleMouseLeave 1578 * @param {EventFacade} event 1579 * @protected 1580 */ 1581 _onHandleMouseLeave: function() { 1582 var instance = this; 1583 1584 if (!instance.get(RESIZING)) { 1585 instance._setActiveHandlesUI(false); 1586 } 1587 }, 1588 1589 /** 1590 * Default value for the wrapper handles node attribute 1591 * 1592 * @method _valueHandlesWrapper 1593 * @protected 1594 * @readOnly 1595 */ 1596 _valueHandlesWrapper: function() { 1597 return Y.Node.create(this.HANDLES_WRAP_TEMPLATE); 1598 }, 1599 1600 /** 1601 * Default value for the wrapper attribute 1602 * 1603 * @method _valueWrapper 1604 * @protected 1605 * @readOnly 1606 */ 1607 _valueWrapper: function() { 1608 var instance = this, 1609 node = instance.get(NODE), 1610 pNode = node.get(PARENT_NODE), 1611 // by deafult the wrapper is always the node 1612 wrapper = node; 1613 1614 // if the node is listed on the wrapTypes or wrap is set to true, create another wrapper 1615 if (instance.get(WRAP)) { 1616 wrapper = Y.Node.create(instance.WRAP_TEMPLATE); 1617 1618 if (pNode) { 1619 pNode.insertBefore(wrapper, node); 1620 } 1621 1622 wrapper.append(node); 1623 1624 instance._copyStyles(node, wrapper); 1625 1626 // remove positioning of wrapped node, the WRAPPER take care about positioning 1627 node.setStyles({ 1628 position: STATIC, 1629 left: 0, 1630 top: 0 1631 }); 1632 } 1633 1634 return wrapper; 1635 } 1636 } 1637 ); 1638 1639 Y.each(Y.Resize.prototype.ALL_HANDLES, function(handle) { 1640 // creating ATTRS with the handles elements 1641 Y.Resize.ATTRS[handleAttrName(handle)] = { 1642 setter: function() { 1643 return this._buildHandle(handle); 1644 }, 1645 value: null, 1646 writeOnce: true 1647 }; 1648 }); 1649 1650 1651 }, '3.17.2', {"requires": ["base", "widget", "event", "oop", "dd-drag", "dd-delegate", "dd-drop"], "skinnable": true});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |