[ 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 } 925 }, 926 927 /** 928 * Helper method to update the current size value on 929 * <a href="Resize.html#property_info">info</a> to respect the 930 * min/max values and fix the top/left calculations. 931 * 932 * @method _checkSize 933 * @param {String} offset 'offsetHeight' or 'offsetWidth' 934 * @param {number} size Size to restrict the offset 935 * @protected 936 */ 937 _checkSize: function(offset, size) { 938 var instance = this, 939 info = instance.info, 940 originalInfo = instance.originalInfo, 941 axis = (offset === OFFSET_HEIGHT) ? TOP : LEFT; 942 943 // forcing the offsetHeight/offsetWidth to be the passed size 944 info[offset] = size; 945 946 // predicting, based on the original information, the last left valid in case of reach the min/max dimension 947 // this calculation avoid browser event leaks when user interact very fast 948 if (((axis === LEFT) && instance.changeLeftHandles) || 949 ((axis === TOP) && instance.changeTopHandles)) { 950 951 info[axis] = originalInfo[axis] + originalInfo[offset] - size; 952 } 953 }, 954 955 /** 956 * Copy relevant styles of the <a href="Resize.html#attr_node">node</a> 957 * to the <a href="Resize.html#attr_wrapper">wrapper</a>. 958 * 959 * @method _copyStyles 960 * @param {Node} node Node from. 961 * @param {Node} wrapper Node to. 962 * @protected 963 */ 964 _copyStyles: function(node, wrapper) { 965 var position = node.getStyle(POSITION).toLowerCase(), 966 surrounding = this._getBoxSurroundingInfo(node), 967 wrapperStyle; 968 969 // resizable wrapper should be positioned 970 if (position === STATIC) { 971 position = RELATIVE; 972 } 973 974 wrapperStyle = { 975 position: position, 976 left: getCompStyle(node, LEFT), 977 top: getCompStyle(node, TOP) 978 }; 979 980 Y.mix(wrapperStyle, surrounding.margin); 981 Y.mix(wrapperStyle, surrounding.border); 982 983 wrapper.setStyles(wrapperStyle); 984 985 // remove margin and border from the internal node 986 node.setStyles({ border: 0, margin: 0 }); 987 988 wrapper.sizeTo( 989 node.get(OFFSET_WIDTH) + surrounding.totalHBorder, 990 node.get(OFFSET_HEIGHT) + surrounding.totalVBorder 991 ); 992 }, 993 994 // extract handle name from a string 995 // using Y.cached to memoize the function for performance 996 _extractHandleName: Y.cached( 997 function(node) { 998 var className = node.get(CLASS_NAME), 999 1000 match = className.match( 1001 new RegExp( 1002 getCN(RESIZE, HANDLE, '(\\w{1,2})\\b') 1003 ) 1004 ); 1005 1006 return match ? match[1] : null; 1007 } 1008 ), 1009 1010 /** 1011 * <p>Generates metadata to the <a href="Resize.html#property_info">info</a> 1012 * and <a href="Resize.html#property_originalInfo">originalInfo</a></p> 1013 * <pre><code>bottom, actXY, left, top, offsetHeight, offsetWidth, right</code></pre> 1014 * 1015 * @method _getInfo 1016 * @param {Node} node 1017 * @param {EventFacade} event 1018 * @private 1019 */ 1020 _getInfo: function(node, event) { 1021 var actXY = [0,0], 1022 drag = event.dragEvent.target, 1023 nodeXY = node.getXY(), 1024 nodeX = nodeXY[0], 1025 nodeY = nodeXY[1], 1026 offsetHeight = node.get(OFFSET_HEIGHT), 1027 offsetWidth = node.get(OFFSET_WIDTH); 1028 1029 if (event) { 1030 // the xy that the node will be set to. Changing this will alter the position as it's dragged. 1031 actXY = (drag.actXY.length ? drag.actXY : drag.lastXY); 1032 } 1033 1034 return { 1035 actXY: actXY, 1036 bottom: (nodeY + offsetHeight), 1037 left: nodeX, 1038 offsetHeight: offsetHeight, 1039 offsetWidth: offsetWidth, 1040 right: (nodeX + offsetWidth), 1041 top: nodeY 1042 }; 1043 }, 1044 1045 /** 1046 * Each box has a content area and optional surrounding margin, 1047 * padding and * border areas. This method get all this information from 1048 * the passed node. For more reference see 1049 * <a href="http://www.w3.org/TR/CSS21/box.html#box-dimensions"> 1050 * http://www.w3.org/TR/CSS21/box.html#box-dimensions</a>. 1051 * 1052 * @method _getBoxSurroundingInfo 1053 * @param {Node} node 1054 * @private 1055 * @return {Object} 1056 */ 1057 _getBoxSurroundingInfo: function(node) { 1058 var info = { 1059 padding: {}, 1060 margin: {}, 1061 border: {} 1062 }; 1063 1064 if (isNode(node)) { 1065 Y.each([ TOP, RIGHT, BOTTOM, LEFT ], function(dir) { 1066 var paddingProperty = capitalize(PADDING, dir), 1067 marginProperty = capitalize(MARGIN, dir), 1068 borderWidthProperty = capitalize(BORDER, dir, WIDTH), 1069 borderColorProperty = capitalize(BORDER, dir, COLOR), 1070 borderStyleProperty = capitalize(BORDER, dir, STYLE); 1071 1072 info.border[borderColorProperty] = getCompStyle(node, borderColorProperty); 1073 info.border[borderStyleProperty] = getCompStyle(node, borderStyleProperty); 1074 info.border[borderWidthProperty] = getCompStyle(node, borderWidthProperty); 1075 info.margin[marginProperty] = getCompStyle(node, marginProperty); 1076 info.padding[paddingProperty] = getCompStyle(node, paddingProperty); 1077 }); 1078 } 1079 1080 info.totalHBorder = (toRoundNumber(info.border.borderLeftWidth) + toRoundNumber(info.border.borderRightWidth)); 1081 info.totalHPadding = (toRoundNumber(info.padding.paddingLeft) + toRoundNumber(info.padding.paddingRight)); 1082 info.totalVBorder = (toRoundNumber(info.border.borderBottomWidth) + toRoundNumber(info.border.borderTopWidth)); 1083 info.totalVPadding = (toRoundNumber(info.padding.paddingBottom) + toRoundNumber(info.padding.paddingTop)); 1084 1085 return info; 1086 }, 1087 1088 /** 1089 * Sync the Resize UI with internal values from 1090 * <a href="Resize.html#property_info">info</a>. 1091 * 1092 * @method _syncUI 1093 * @protected 1094 */ 1095 _syncUI: function() { 1096 var instance = this, 1097 info = instance.info, 1098 wrapperSurrounding = instance.wrapperSurrounding, 1099 wrapper = instance.get(WRAPPER), 1100 node = instance.get(NODE); 1101 1102 wrapper.sizeTo(info.offsetWidth, info.offsetHeight); 1103 1104 if (instance.changeLeftHandles || instance.changeTopHandles) { 1105 wrapper.setXY([info.left, info.top]); 1106 } 1107 1108 // if a wrap node is being used 1109 if (!wrapper.compareTo(node)) { 1110 // the original internal node borders were copied to the wrapper on 1111 // _copyStyles, to compensate that subtract the borders from the internal node 1112 node.sizeTo( 1113 info.offsetWidth - wrapperSurrounding.totalHBorder, 1114 info.offsetHeight - wrapperSurrounding.totalVBorder 1115 ); 1116 } 1117 1118 // prevent webkit textarea resize 1119 if (Y.UA.webkit) { 1120 node.setStyle(RESIZE, NONE); 1121 } 1122 }, 1123 1124 /** 1125 * Update <code>instance.changeHeightHandles, 1126 * instance.changeLeftHandles, instance.changeTopHandles, 1127 * instance.changeWidthHandles</code> information. 1128 * 1129 * @method _updateChangeHandleInfo 1130 * @private 1131 */ 1132 _updateChangeHandleInfo: function(handle) { 1133 var instance = this; 1134 1135 instance.changeHeightHandles = instance.REGEX_CHANGE_HEIGHT.test(handle); 1136 instance.changeLeftHandles = instance.REGEX_CHANGE_LEFT.test(handle); 1137 instance.changeTopHandles = instance.REGEX_CHANGE_TOP.test(handle); 1138 instance.changeWidthHandles = instance.REGEX_CHANGE_WIDTH.test(handle); 1139 }, 1140 1141 /** 1142 * Update <a href="Resize.html#property_info">info</a> values (bottom, actXY, left, top, offsetHeight, offsetWidth, right). 1143 * 1144 * @method _updateInfo 1145 * @private 1146 */ 1147 _updateInfo: function(event) { 1148 var instance = this; 1149 1150 instance.info = instance._getInfo(instance.get(WRAPPER), event); 1151 }, 1152 1153 /** 1154 * Update properties 1155 * <a href="Resize.html#property_nodeSurrounding">nodeSurrounding</a>, 1156 * <a href="Resize.html#property_nodeSurrounding">wrapperSurrounding</a>, 1157 * <a href="Resize.html#property_nodeSurrounding">totalVSurrounding</a>, 1158 * <a href="Resize.html#property_nodeSurrounding">totalHSurrounding</a>. 1159 * 1160 * @method _updateSurroundingInfo 1161 * @private 1162 */ 1163 _updateSurroundingInfo: function() { 1164 var instance = this, 1165 node = instance.get(NODE), 1166 wrapper = instance.get(WRAPPER), 1167 nodeSurrounding = instance._getBoxSurroundingInfo(node), 1168 wrapperSurrounding = instance._getBoxSurroundingInfo(wrapper); 1169 1170 instance.nodeSurrounding = nodeSurrounding; 1171 instance.wrapperSurrounding = wrapperSurrounding; 1172 1173 instance.totalVSurrounding = (nodeSurrounding.totalVPadding + wrapperSurrounding.totalVBorder); 1174 instance.totalHSurrounding = (nodeSurrounding.totalHPadding + wrapperSurrounding.totalHBorder); 1175 }, 1176 1177 /** 1178 * Set the active state of the handles. 1179 * 1180 * @method _setActiveHandlesUI 1181 * @param {boolean} val True to activate the handles, false to deactivate. 1182 * @protected 1183 */ 1184 _setActiveHandlesUI: function(val) { 1185 var instance = this, 1186 activeHandleNode = instance.get(ACTIVE_HANDLE_NODE); 1187 1188 if (activeHandleNode) { 1189 if (val) { 1190 // remove CSS_RESIZE_HANDLE_ACTIVE from all handles before addClass on the active 1191 instance.eachHandle( 1192 function(handleEl) { 1193 handleEl.removeClass(CSS_RESIZE_HANDLE_ACTIVE); 1194 } 1195 ); 1196 1197 activeHandleNode.addClass(CSS_RESIZE_HANDLE_ACTIVE); 1198 } 1199 else { 1200 activeHandleNode.removeClass(CSS_RESIZE_HANDLE_ACTIVE); 1201 } 1202 } 1203 }, 1204 1205 /** 1206 * Setter for the handles attribute 1207 * 1208 * @method _setHandles 1209 * @protected 1210 * @param {String} val 1211 */ 1212 _setHandles: function(val) { 1213 var instance = this, 1214 handles = []; 1215 1216 // handles attr accepts both array or string 1217 if (isArray(val)) { 1218 handles = val; 1219 } 1220 else if (isString(val)) { 1221 // if the handles attr passed in is an ALL string... 1222 if (val.toLowerCase() === ALL) { 1223 handles = instance.ALL_HANDLES; 1224 } 1225 // otherwise, split the string to extract the handles 1226 else { 1227 Y.each( 1228 val.split(COMMA), 1229 function(node) { 1230 var handle = trim(node); 1231 1232 // if its a valid handle, add it to the handles output 1233 if (indexOf(instance.ALL_HANDLES, handle) > -1) { 1234 handles.push(handle); 1235 } 1236 } 1237 ); 1238 } 1239 } 1240 1241 return handles; 1242 }, 1243 1244 /** 1245 * Set the visibility of the handles. 1246 * 1247 * @method _setHideHandlesUI 1248 * @param {boolean} val True to hide the handles, false to show. 1249 * @protected 1250 */ 1251 _setHideHandlesUI: function(val) { 1252 var instance = this, 1253 wrapper = instance.get(WRAPPER); 1254 1255 if (!instance.get(RESIZING)) { 1256 if (val) { 1257 wrapper.addClass(CSS_RESIZE_HIDDEN_HANDLES); 1258 } 1259 else { 1260 wrapper.removeClass(CSS_RESIZE_HIDDEN_HANDLES); 1261 } 1262 } 1263 }, 1264 1265 /** 1266 * Setter for the wrap attribute 1267 * 1268 * @method _setWrap 1269 * @protected 1270 * @param {boolean} val 1271 */ 1272 _setWrap: function(val) { 1273 var instance = this, 1274 node = instance.get(NODE), 1275 nodeName = node.get(NODE_NAME), 1276 typeRegex = instance.get(WRAP_TYPES); 1277 1278 // if nodeName is listed on WRAP_TYPES force use the wrapper 1279 if (typeRegex.test(nodeName)) { 1280 val = true; 1281 } 1282 1283 return val; 1284 }, 1285 1286 /** 1287 * Default resize:mouseUp handler 1288 * 1289 * @method _defMouseUpFn 1290 * @param {EventFacade} event The Event object 1291 * @protected 1292 */ 1293 _defMouseUpFn: function() { 1294 var instance = this; 1295 1296 instance.set(RESIZING, false); 1297 }, 1298 1299 /** 1300 * Default resize:resize handler 1301 * 1302 * @method _defResizeFn 1303 * @param {EventFacade} event The Event object 1304 * @protected 1305 */ 1306 _defResizeFn: function(event) { 1307 var instance = this; 1308 1309 instance._resize(event); 1310 }, 1311 1312 /** 1313 * Logic method for _defResizeFn. Allow AOP. 1314 * 1315 * @method _resize 1316 * @param {EventFacade} event The Event object 1317 * @protected 1318 */ 1319 _resize: function(event) { 1320 var instance = this; 1321 1322 instance._handleResizeAlignEvent(event.dragEvent); 1323 1324 // _syncUI of the wrapper, not using proxy 1325 instance._syncUI(); 1326 }, 1327 1328 /** 1329 * Default resize:align handler 1330 * 1331 * @method _defResizeAlignFn 1332 * @param {EventFacade} event The Event object 1333 * @protected 1334 */ 1335 _defResizeAlignFn: function(event) { 1336 var instance = this; 1337 1338 instance._resizeAlign(event); 1339 }, 1340 1341 /** 1342 * Logic method for _defResizeAlignFn. Allow AOP. 1343 * 1344 * @method _resizeAlign 1345 * @param {EventFacade} event The Event object 1346 * @protected 1347 */ 1348 _resizeAlign: function(event) { 1349 var instance = this, 1350 info, 1351 defMinHeight, 1352 defMinWidth; 1353 1354 instance.lastInfo = instance.info; 1355 1356 // update the instance.info values 1357 instance._updateInfo(event); 1358 1359 info = instance.info; 1360 1361 // basic resize calculations 1362 instance._calcResize(); 1363 1364 // if Y.Plugin.ResizeConstrained is not plugged, check for min dimension 1365 if (!instance.con) { 1366 defMinHeight = (instance.get(DEF_MIN_HEIGHT) + instance.totalVSurrounding); 1367 defMinWidth = (instance.get(DEF_MIN_WIDTH) + instance.totalHSurrounding); 1368 1369 if (info.offsetHeight <= defMinHeight) { 1370 instance._checkSize(OFFSET_HEIGHT, defMinHeight); 1371 } 1372 1373 if (info.offsetWidth <= defMinWidth) { 1374 instance._checkSize(OFFSET_WIDTH, defMinWidth); 1375 } 1376 } 1377 }, 1378 1379 /** 1380 * Default resize:end handler 1381 * 1382 * @method _defResizeEndFn 1383 * @param {EventFacade} event The Event object 1384 * @protected 1385 */ 1386 _defResizeEndFn: function(event) { 1387 var instance = this; 1388 1389 instance._resizeEnd(event); 1390 }, 1391 1392 /** 1393 * Logic method for _defResizeEndFn. Allow AOP. 1394 * 1395 * @method _resizeEnd 1396 * @param {EventFacade} event The Event object 1397 * @protected 1398 */ 1399 _resizeEnd: function(event) { 1400 var instance = this, 1401 drag = event.dragEvent.target; 1402 1403 // reseting actXY from drag when drag end 1404 drag.actXY = []; 1405 1406 // syncUI when resize end 1407 instance._syncUI(); 1408 1409 instance._setActiveHandlesUI(false); 1410 1411 instance.set(ACTIVE_HANDLE, null); 1412 instance.set(ACTIVE_HANDLE_NODE, null); 1413 1414 instance.handle = null; 1415 }, 1416 1417 /** 1418 * Default resize:start handler 1419 * 1420 * @method _defResizeStartFn 1421 * @param {EventFacade} event The Event object 1422 * @protected 1423 */ 1424 _defResizeStartFn: function(event) { 1425 var instance = this; 1426 1427 instance._resizeStart(event); 1428 }, 1429 1430 /** 1431 * Logic method for _defResizeStartFn. Allow AOP. 1432 * 1433 * @method _resizeStart 1434 * @param {EventFacade} event The Event object 1435 * @protected 1436 */ 1437 _resizeStart: function(event) { 1438 var instance = this, 1439 wrapper = instance.get(WRAPPER); 1440 1441 instance.handle = instance.get(ACTIVE_HANDLE); 1442 1443 instance.set(RESIZING, true); 1444 1445 instance._updateSurroundingInfo(); 1446 1447 // create an originalInfo information for reference 1448 instance.originalInfo = instance._getInfo(wrapper, event); 1449 1450 instance._updateInfo(event); 1451 }, 1452 1453 /** 1454 * Fires the resize:mouseUp event. 1455 * 1456 * @method _handleMouseUpEvent 1457 * @param {EventFacade} event resize:mouseUp event facade 1458 * @protected 1459 */ 1460 _handleMouseUpEvent: function(event) { 1461 this.fire(EV_MOUSE_UP, { dragEvent: event, info: this.info }); 1462 }, 1463 1464 /** 1465 * Fires the resize:resize event. 1466 * 1467 * @method _handleResizeEvent 1468 * @param {EventFacade} event resize:resize event facade 1469 * @protected 1470 */ 1471 _handleResizeEvent: function(event) { 1472 this.fire(EV_RESIZE, { dragEvent: event, info: this.info }); 1473 }, 1474 1475 /** 1476 * Fires the resize:align event. 1477 * 1478 * @method _handleResizeAlignEvent 1479 * @param {EventFacade} event resize:resize event facade 1480 * @protected 1481 */ 1482 _handleResizeAlignEvent: function(event) { 1483 this.fire(EV_RESIZE_ALIGN, { dragEvent: event, info: this.info }); 1484 }, 1485 1486 /** 1487 * Fires the resize:end event. 1488 * 1489 * @method _handleResizeEndEvent 1490 * @param {EventFacade} event resize:end event facade 1491 * @protected 1492 */ 1493 _handleResizeEndEvent: function(event) { 1494 this.fire(EV_RESIZE_END, { dragEvent: event, info: this.info }); 1495 }, 1496 1497 /** 1498 * Fires the resize:start event. 1499 * 1500 * @method _handleResizeStartEvent 1501 * @param {EventFacade} event resize:start event facade 1502 * @protected 1503 */ 1504 _handleResizeStartEvent: function(event) { 1505 if (!this.get(ACTIVE_HANDLE)) { 1506 //This handles the "touch" case 1507 this._setHandleFromNode(event.target.get('node')); 1508 } 1509 this.fire(EV_RESIZE_START, { dragEvent: event, info: this.info }); 1510 }, 1511 1512 /** 1513 * Mouseenter event handler for the <a href="Resize.html#attr_wrapper">wrapper</a>. 1514 * 1515 * @method _onWrapperMouseEnter 1516 * @param {EventFacade} event 1517 * @protected 1518 */ 1519 _onWrapperMouseEnter: function() { 1520 var instance = this; 1521 1522 if (instance.get(AUTO_HIDE)) { 1523 instance._setHideHandlesUI(false); 1524 } 1525 }, 1526 1527 /** 1528 * Mouseleave event handler for the <a href="Resize.html#attr_wrapper">wrapper</a>. 1529 * 1530 * @method _onWrapperMouseLeave 1531 * @param {EventFacade} event 1532 * @protected 1533 */ 1534 _onWrapperMouseLeave: function() { 1535 var instance = this; 1536 1537 if (instance.get(AUTO_HIDE)) { 1538 instance._setHideHandlesUI(true); 1539 } 1540 }, 1541 1542 /** 1543 * Handles setting the activeHandle from a node, used from startDrag (for touch) and mouseenter (for mouse). 1544 * 1545 * @method _setHandleFromNode 1546 * @param {Node} node 1547 * @protected 1548 */ 1549 _setHandleFromNode: function(node) { 1550 var instance = this, 1551 handle = instance._extractHandleName(node); 1552 1553 if (!instance.get(RESIZING)) { 1554 instance.set(ACTIVE_HANDLE, handle); 1555 instance.set(ACTIVE_HANDLE_NODE, node); 1556 1557 instance._setActiveHandlesUI(true); 1558 instance._updateChangeHandleInfo(handle); 1559 } 1560 }, 1561 1562 /** 1563 * Mouseenter event handler for the handles. 1564 * 1565 * @method _onHandleMouseEnter 1566 * @param {EventFacade} event 1567 * @protected 1568 */ 1569 _onHandleMouseEnter: function(event) { 1570 this._setHandleFromNode(event.currentTarget); 1571 }, 1572 1573 /** 1574 * Mouseout event handler for the handles. 1575 * 1576 * @method _onHandleMouseLeave 1577 * @param {EventFacade} event 1578 * @protected 1579 */ 1580 _onHandleMouseLeave: function() { 1581 var instance = this; 1582 1583 if (!instance.get(RESIZING)) { 1584 instance._setActiveHandlesUI(false); 1585 } 1586 }, 1587 1588 /** 1589 * Default value for the wrapper handles node attribute 1590 * 1591 * @method _valueHandlesWrapper 1592 * @protected 1593 * @readOnly 1594 */ 1595 _valueHandlesWrapper: function() { 1596 return Y.Node.create(this.HANDLES_WRAP_TEMPLATE); 1597 }, 1598 1599 /** 1600 * Default value for the wrapper attribute 1601 * 1602 * @method _valueWrapper 1603 * @protected 1604 * @readOnly 1605 */ 1606 _valueWrapper: function() { 1607 var instance = this, 1608 node = instance.get(NODE), 1609 pNode = node.get(PARENT_NODE), 1610 // by deafult the wrapper is always the node 1611 wrapper = node; 1612 1613 // if the node is listed on the wrapTypes or wrap is set to true, create another wrapper 1614 if (instance.get(WRAP)) { 1615 wrapper = Y.Node.create(instance.WRAP_TEMPLATE); 1616 1617 if (pNode) { 1618 pNode.insertBefore(wrapper, node); 1619 } 1620 1621 wrapper.append(node); 1622 1623 instance._copyStyles(node, wrapper); 1624 1625 // remove positioning of wrapped node, the WRAPPER take care about positioning 1626 node.setStyles({ 1627 position: STATIC, 1628 left: 0, 1629 top: 0 1630 }); 1631 } 1632 1633 return wrapper; 1634 } 1635 } 1636 ); 1637 1638 Y.each(Y.Resize.prototype.ALL_HANDLES, function(handle) { 1639 // creating ATTRS with the handles elements 1640 Y.Resize.ATTRS[handleAttrName(handle)] = { 1641 setter: function() { 1642 return this._buildHandle(handle); 1643 }, 1644 value: null, 1645 writeOnce: true 1646 }; 1647 }); 1648 1649 1650 }, '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 |