jquery-ui.js (122889B)
1 /*! jQuery UI - v1.12.1 - 2018-09-03 2 * http://jqueryui.com 3 * Includes: widget.js, data.js, scroll-parent.js, widgets/draggable.js, widgets/droppable.js, widgets/sortable.js, widgets/mouse.js 4 * Copyright jQuery Foundation and other contributors; Licensed MIT */ 5 6 (function( factory ) { 7 if ( typeof define === "function" && define.amd ) { 8 9 // AMD. Register as an anonymous module. 10 define([ "jquery" ], factory ); 11 } else { 12 13 // Browser globals 14 factory( jQuery ); 15 } 16 }(function( $ ) { 17 18 $.ui = $.ui || {}; 19 20 var version = $.ui.version = "1.12.1"; 21 22 23 /*! 24 * jQuery UI Widget 1.12.1 25 * http://jqueryui.com 26 * 27 * Copyright jQuery Foundation and other contributors 28 * Released under the MIT license. 29 * http://jquery.org/license 30 */ 31 32 //>>label: Widget 33 //>>group: Core 34 //>>description: Provides a factory for creating stateful widgets with a common API. 35 //>>docs: http://api.jqueryui.com/jQuery.widget/ 36 //>>demos: http://jqueryui.com/widget/ 37 38 39 40 var widgetUuid = 0; 41 var widgetSlice = Array.prototype.slice; 42 43 $.cleanData = ( function( orig ) { 44 return function( elems ) { 45 var events, elem, i; 46 for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) { 47 try { 48 49 // Only trigger remove when necessary to save time 50 events = $._data( elem, "events" ); 51 if ( events && events.remove ) { 52 $( elem ).triggerHandler( "remove" ); 53 } 54 55 // Http://bugs.jquery.com/ticket/8235 56 } catch ( e ) {} 57 } 58 orig( elems ); 59 }; 60 } )( $.cleanData ); 61 62 $.widget = function( name, base, prototype ) { 63 var existingConstructor, constructor, basePrototype; 64 65 // ProxiedPrototype allows the provided prototype to remain unmodified 66 // so that it can be used as a mixin for multiple widgets (#8876) 67 var proxiedPrototype = {}; 68 69 var namespace = name.split( "." )[ 0 ]; 70 name = name.split( "." )[ 1 ]; 71 var fullName = namespace + "-" + name; 72 73 if ( !prototype ) { 74 prototype = base; 75 base = $.Widget; 76 } 77 78 if ( $.isArray( prototype ) ) { 79 prototype = $.extend.apply( null, [ {} ].concat( prototype ) ); 80 } 81 82 // Create selector for plugin 83 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { 84 return !!$.data( elem, fullName ); 85 }; 86 87 $[ namespace ] = $[ namespace ] || {}; 88 existingConstructor = $[ namespace ][ name ]; 89 constructor = $[ namespace ][ name ] = function( options, element ) { 90 91 // Allow instantiation without "new" keyword 92 if ( !this._createWidget ) { 93 return new constructor( options, element ); 94 } 95 96 // Allow instantiation without initializing for simple inheritance 97 // must use "new" keyword (the code above always passes args) 98 if ( arguments.length ) { 99 this._createWidget( options, element ); 100 } 101 }; 102 103 // Extend with the existing constructor to carry over any static properties 104 $.extend( constructor, existingConstructor, { 105 version: prototype.version, 106 107 // Copy the object used to create the prototype in case we need to 108 // redefine the widget later 109 _proto: $.extend( {}, prototype ), 110 111 // Track widgets that inherit from this widget in case this widget is 112 // redefined after a widget inherits from it 113 _childConstructors: [] 114 } ); 115 116 basePrototype = new base(); 117 118 // We need to make the options hash a property directly on the new instance 119 // otherwise we'll modify the options hash on the prototype that we're 120 // inheriting from 121 basePrototype.options = $.widget.extend( {}, basePrototype.options ); 122 $.each( prototype, function( prop, value ) { 123 if ( !$.isFunction( value ) ) { 124 proxiedPrototype[ prop ] = value; 125 return; 126 } 127 proxiedPrototype[ prop ] = ( function() { 128 function _super() { 129 return base.prototype[ prop ].apply( this, arguments ); 130 } 131 132 function _superApply( args ) { 133 return base.prototype[ prop ].apply( this, args ); 134 } 135 136 return function() { 137 var __super = this._super; 138 var __superApply = this._superApply; 139 var returnValue; 140 141 this._super = _super; 142 this._superApply = _superApply; 143 144 returnValue = value.apply( this, arguments ); 145 146 this._super = __super; 147 this._superApply = __superApply; 148 149 return returnValue; 150 }; 151 } )(); 152 } ); 153 constructor.prototype = $.widget.extend( basePrototype, { 154 155 // TODO: remove support for widgetEventPrefix 156 // always use the name + a colon as the prefix, e.g., draggable:start 157 // don't prefix for widgets that aren't DOM-based 158 widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name 159 }, proxiedPrototype, { 160 constructor: constructor, 161 namespace: namespace, 162 widgetName: name, 163 widgetFullName: fullName 164 } ); 165 166 // If this widget is being redefined then we need to find all widgets that 167 // are inheriting from it and redefine all of them so that they inherit from 168 // the new version of this widget. We're essentially trying to replace one 169 // level in the prototype chain. 170 if ( existingConstructor ) { 171 $.each( existingConstructor._childConstructors, function( i, child ) { 172 var childPrototype = child.prototype; 173 174 // Redefine the child widget using the same prototype that was 175 // originally used, but inherit from the new version of the base 176 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, 177 child._proto ); 178 } ); 179 180 // Remove the list of existing child constructors from the old constructor 181 // so the old child constructors can be garbage collected 182 delete existingConstructor._childConstructors; 183 } else { 184 base._childConstructors.push( constructor ); 185 } 186 187 $.widget.bridge( name, constructor ); 188 189 return constructor; 190 }; 191 192 $.widget.extend = function( target ) { 193 var input = widgetSlice.call( arguments, 1 ); 194 var inputIndex = 0; 195 var inputLength = input.length; 196 var key; 197 var value; 198 199 for ( ; inputIndex < inputLength; inputIndex++ ) { 200 for ( key in input[ inputIndex ] ) { 201 value = input[ inputIndex ][ key ]; 202 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { 203 204 // Clone objects 205 if ( $.isPlainObject( value ) ) { 206 target[ key ] = $.isPlainObject( target[ key ] ) ? 207 $.widget.extend( {}, target[ key ], value ) : 208 209 // Don't extend strings, arrays, etc. with objects 210 $.widget.extend( {}, value ); 211 212 // Copy everything else by reference 213 } else { 214 target[ key ] = value; 215 } 216 } 217 } 218 } 219 return target; 220 }; 221 222 $.widget.bridge = function( name, object ) { 223 var fullName = object.prototype.widgetFullName || name; 224 $.fn[ name ] = function( options ) { 225 var isMethodCall = typeof options === "string"; 226 var args = widgetSlice.call( arguments, 1 ); 227 var returnValue = this; 228 229 if ( isMethodCall ) { 230 231 // If this is an empty collection, we need to have the instance method 232 // return undefined instead of the jQuery instance 233 if ( !this.length && options === "instance" ) { 234 returnValue = undefined; 235 } else { 236 this.each( function() { 237 var methodValue; 238 var instance = $.data( this, fullName ); 239 240 if ( options === "instance" ) { 241 returnValue = instance; 242 return false; 243 } 244 245 if ( !instance ) { 246 return $.error( "cannot call methods on " + name + 247 " prior to initialization; " + 248 "attempted to call method '" + options + "'" ); 249 } 250 251 if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) { 252 return $.error( "no such method '" + options + "' for " + name + 253 " widget instance" ); 254 } 255 256 methodValue = instance[ options ].apply( instance, args ); 257 258 if ( methodValue !== instance && methodValue !== undefined ) { 259 returnValue = methodValue && methodValue.jquery ? 260 returnValue.pushStack( methodValue.get() ) : 261 methodValue; 262 return false; 263 } 264 } ); 265 } 266 } else { 267 268 // Allow multiple hashes to be passed on init 269 if ( args.length ) { 270 options = $.widget.extend.apply( null, [ options ].concat( args ) ); 271 } 272 273 this.each( function() { 274 var instance = $.data( this, fullName ); 275 if ( instance ) { 276 instance.option( options || {} ); 277 if ( instance._init ) { 278 instance._init(); 279 } 280 } else { 281 $.data( this, fullName, new object( options, this ) ); 282 } 283 } ); 284 } 285 286 return returnValue; 287 }; 288 }; 289 290 $.Widget = function( /* options, element */ ) {}; 291 $.Widget._childConstructors = []; 292 293 $.Widget.prototype = { 294 widgetName: "widget", 295 widgetEventPrefix: "", 296 defaultElement: "<div>", 297 298 options: { 299 classes: {}, 300 disabled: false, 301 302 // Callbacks 303 create: null 304 }, 305 306 _createWidget: function( options, element ) { 307 element = $( element || this.defaultElement || this )[ 0 ]; 308 this.element = $( element ); 309 this.uuid = widgetUuid++; 310 this.eventNamespace = "." + this.widgetName + this.uuid; 311 312 this.bindings = $(); 313 this.hoverable = $(); 314 this.focusable = $(); 315 this.classesElementLookup = {}; 316 317 if ( element !== this ) { 318 $.data( element, this.widgetFullName, this ); 319 this._on( true, this.element, { 320 remove: function( event ) { 321 if ( event.target === element ) { 322 this.destroy(); 323 } 324 } 325 } ); 326 this.document = $( element.style ? 327 328 // Element within the document 329 element.ownerDocument : 330 331 // Element is window or document 332 element.document || element ); 333 this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow ); 334 } 335 336 this.options = $.widget.extend( {}, 337 this.options, 338 this._getCreateOptions(), 339 options ); 340 341 this._create(); 342 343 if ( this.options.disabled ) { 344 this._setOptionDisabled( this.options.disabled ); 345 } 346 347 this._trigger( "create", null, this._getCreateEventData() ); 348 this._init(); 349 }, 350 351 _getCreateOptions: function() { 352 return {}; 353 }, 354 355 _getCreateEventData: $.noop, 356 357 _create: $.noop, 358 359 _init: $.noop, 360 361 destroy: function() { 362 var that = this; 363 364 this._destroy(); 365 $.each( this.classesElementLookup, function( key, value ) { 366 that._removeClass( value, key ); 367 } ); 368 369 // We can probably remove the unbind calls in 2.0 370 // all event bindings should go through this._on() 371 this.element 372 .off( this.eventNamespace ) 373 .removeData( this.widgetFullName ); 374 this.widget() 375 .off( this.eventNamespace ) 376 .removeAttr( "aria-disabled" ); 377 378 // Clean up events and states 379 this.bindings.off( this.eventNamespace ); 380 }, 381 382 _destroy: $.noop, 383 384 widget: function() { 385 return this.element; 386 }, 387 388 option: function( key, value ) { 389 var options = key; 390 var parts; 391 var curOption; 392 var i; 393 394 if ( arguments.length === 0 ) { 395 396 // Don't return a reference to the internal hash 397 return $.widget.extend( {}, this.options ); 398 } 399 400 if ( typeof key === "string" ) { 401 402 // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } 403 options = {}; 404 parts = key.split( "." ); 405 key = parts.shift(); 406 if ( parts.length ) { 407 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); 408 for ( i = 0; i < parts.length - 1; i++ ) { 409 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; 410 curOption = curOption[ parts[ i ] ]; 411 } 412 key = parts.pop(); 413 if ( arguments.length === 1 ) { 414 return curOption[ key ] === undefined ? null : curOption[ key ]; 415 } 416 curOption[ key ] = value; 417 } else { 418 if ( arguments.length === 1 ) { 419 return this.options[ key ] === undefined ? null : this.options[ key ]; 420 } 421 options[ key ] = value; 422 } 423 } 424 425 this._setOptions( options ); 426 427 return this; 428 }, 429 430 _setOptions: function( options ) { 431 var key; 432 433 for ( key in options ) { 434 this._setOption( key, options[ key ] ); 435 } 436 437 return this; 438 }, 439 440 _setOption: function( key, value ) { 441 if ( key === "classes" ) { 442 this._setOptionClasses( value ); 443 } 444 445 this.options[ key ] = value; 446 447 if ( key === "disabled" ) { 448 this._setOptionDisabled( value ); 449 } 450 451 return this; 452 }, 453 454 _setOptionClasses: function( value ) { 455 var classKey, elements, currentElements; 456 457 for ( classKey in value ) { 458 currentElements = this.classesElementLookup[ classKey ]; 459 if ( value[ classKey ] === this.options.classes[ classKey ] || 460 !currentElements || 461 !currentElements.length ) { 462 continue; 463 } 464 465 // We are doing this to create a new jQuery object because the _removeClass() call 466 // on the next line is going to destroy the reference to the current elements being 467 // tracked. We need to save a copy of this collection so that we can add the new classes 468 // below. 469 elements = $( currentElements.get() ); 470 this._removeClass( currentElements, classKey ); 471 472 // We don't use _addClass() here, because that uses this.options.classes 473 // for generating the string of classes. We want to use the value passed in from 474 // _setOption(), this is the new value of the classes option which was passed to 475 // _setOption(). We pass this value directly to _classes(). 476 elements.addClass( this._classes( { 477 element: elements, 478 keys: classKey, 479 classes: value, 480 add: true 481 } ) ); 482 } 483 }, 484 485 _setOptionDisabled: function( value ) { 486 this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value ); 487 488 // If the widget is becoming disabled, then nothing is interactive 489 if ( value ) { 490 this._removeClass( this.hoverable, null, "ui-state-hover" ); 491 this._removeClass( this.focusable, null, "ui-state-focus" ); 492 } 493 }, 494 495 enable: function() { 496 return this._setOptions( { disabled: false } ); 497 }, 498 499 disable: function() { 500 return this._setOptions( { disabled: true } ); 501 }, 502 503 _classes: function( options ) { 504 var full = []; 505 var that = this; 506 507 options = $.extend( { 508 element: this.element, 509 classes: this.options.classes || {} 510 }, options ); 511 512 function processClassString( classes, checkOption ) { 513 var current, i; 514 for ( i = 0; i < classes.length; i++ ) { 515 current = that.classesElementLookup[ classes[ i ] ] || $(); 516 if ( options.add ) { 517 current = $( $.unique( current.get().concat( options.element.get() ) ) ); 518 } else { 519 current = $( current.not( options.element ).get() ); 520 } 521 that.classesElementLookup[ classes[ i ] ] = current; 522 full.push( classes[ i ] ); 523 if ( checkOption && options.classes[ classes[ i ] ] ) { 524 full.push( options.classes[ classes[ i ] ] ); 525 } 526 } 527 } 528 529 this._on( options.element, { 530 "remove": "_untrackClassesElement" 531 } ); 532 533 if ( options.keys ) { 534 processClassString( options.keys.match( /\S+/g ) || [], true ); 535 } 536 if ( options.extra ) { 537 processClassString( options.extra.match( /\S+/g ) || [] ); 538 } 539 540 return full.join( " " ); 541 }, 542 543 _untrackClassesElement: function( event ) { 544 var that = this; 545 $.each( that.classesElementLookup, function( key, value ) { 546 if ( $.inArray( event.target, value ) !== -1 ) { 547 that.classesElementLookup[ key ] = $( value.not( event.target ).get() ); 548 } 549 } ); 550 }, 551 552 _removeClass: function( element, keys, extra ) { 553 return this._toggleClass( element, keys, extra, false ); 554 }, 555 556 _addClass: function( element, keys, extra ) { 557 return this._toggleClass( element, keys, extra, true ); 558 }, 559 560 _toggleClass: function( element, keys, extra, add ) { 561 add = ( typeof add === "boolean" ) ? add : extra; 562 var shift = ( typeof element === "string" || element === null ), 563 options = { 564 extra: shift ? keys : extra, 565 keys: shift ? element : keys, 566 element: shift ? this.element : element, 567 add: add 568 }; 569 options.element.toggleClass( this._classes( options ), add ); 570 return this; 571 }, 572 573 _on: function( suppressDisabledCheck, element, handlers ) { 574 var delegateElement; 575 var instance = this; 576 577 // No suppressDisabledCheck flag, shuffle arguments 578 if ( typeof suppressDisabledCheck !== "boolean" ) { 579 handlers = element; 580 element = suppressDisabledCheck; 581 suppressDisabledCheck = false; 582 } 583 584 // No element argument, shuffle and use this.element 585 if ( !handlers ) { 586 handlers = element; 587 element = this.element; 588 delegateElement = this.widget(); 589 } else { 590 element = delegateElement = $( element ); 591 this.bindings = this.bindings.add( element ); 592 } 593 594 $.each( handlers, function( event, handler ) { 595 function handlerProxy() { 596 597 // Allow widgets to customize the disabled handling 598 // - disabled as an array instead of boolean 599 // - disabled class as method for disabling individual parts 600 if ( !suppressDisabledCheck && 601 ( instance.options.disabled === true || 602 $( this ).hasClass( "ui-state-disabled" ) ) ) { 603 return; 604 } 605 return ( typeof handler === "string" ? instance[ handler ] : handler ) 606 .apply( instance, arguments ); 607 } 608 609 // Copy the guid so direct unbinding works 610 if ( typeof handler !== "string" ) { 611 handlerProxy.guid = handler.guid = 612 handler.guid || handlerProxy.guid || $.guid++; 613 } 614 615 var match = event.match( /^([\w:-]*)\s*(.*)$/ ); 616 var eventName = match[ 1 ] + instance.eventNamespace; 617 var selector = match[ 2 ]; 618 619 if ( selector ) { 620 delegateElement.on( eventName, selector, handlerProxy ); 621 } else { 622 element.on( eventName, handlerProxy ); 623 } 624 } ); 625 }, 626 627 _off: function( element, eventName ) { 628 eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) + 629 this.eventNamespace; 630 element.off( eventName ).off( eventName ); 631 632 // Clear the stack to avoid memory leaks (#10056) 633 this.bindings = $( this.bindings.not( element ).get() ); 634 this.focusable = $( this.focusable.not( element ).get() ); 635 this.hoverable = $( this.hoverable.not( element ).get() ); 636 }, 637 638 _delay: function( handler, delay ) { 639 function handlerProxy() { 640 return ( typeof handler === "string" ? instance[ handler ] : handler ) 641 .apply( instance, arguments ); 642 } 643 var instance = this; 644 return setTimeout( handlerProxy, delay || 0 ); 645 }, 646 647 _hoverable: function( element ) { 648 this.hoverable = this.hoverable.add( element ); 649 this._on( element, { 650 mouseenter: function( event ) { 651 this._addClass( $( event.currentTarget ), null, "ui-state-hover" ); 652 }, 653 mouseleave: function( event ) { 654 this._removeClass( $( event.currentTarget ), null, "ui-state-hover" ); 655 } 656 } ); 657 }, 658 659 _focusable: function( element ) { 660 this.focusable = this.focusable.add( element ); 661 this._on( element, { 662 focusin: function( event ) { 663 this._addClass( $( event.currentTarget ), null, "ui-state-focus" ); 664 }, 665 focusout: function( event ) { 666 this._removeClass( $( event.currentTarget ), null, "ui-state-focus" ); 667 } 668 } ); 669 }, 670 671 _trigger: function( type, event, data ) { 672 var prop, orig; 673 var callback = this.options[ type ]; 674 675 data = data || {}; 676 event = $.Event( event ); 677 event.type = ( type === this.widgetEventPrefix ? 678 type : 679 this.widgetEventPrefix + type ).toLowerCase(); 680 681 // The original event may come from any element 682 // so we need to reset the target on the new event 683 event.target = this.element[ 0 ]; 684 685 // Copy original event properties over to the new event 686 orig = event.originalEvent; 687 if ( orig ) { 688 for ( prop in orig ) { 689 if ( !( prop in event ) ) { 690 event[ prop ] = orig[ prop ]; 691 } 692 } 693 } 694 695 this.element.trigger( event, data ); 696 return !( $.isFunction( callback ) && 697 callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false || 698 event.isDefaultPrevented() ); 699 } 700 }; 701 702 $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { 703 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { 704 if ( typeof options === "string" ) { 705 options = { effect: options }; 706 } 707 708 var hasOptions; 709 var effectName = !options ? 710 method : 711 options === true || typeof options === "number" ? 712 defaultEffect : 713 options.effect || defaultEffect; 714 715 options = options || {}; 716 if ( typeof options === "number" ) { 717 options = { duration: options }; 718 } 719 720 hasOptions = !$.isEmptyObject( options ); 721 options.complete = callback; 722 723 if ( options.delay ) { 724 element.delay( options.delay ); 725 } 726 727 if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { 728 element[ method ]( options ); 729 } else if ( effectName !== method && element[ effectName ] ) { 730 element[ effectName ]( options.duration, options.easing, callback ); 731 } else { 732 element.queue( function( next ) { 733 $( this )[ method ](); 734 if ( callback ) { 735 callback.call( element[ 0 ] ); 736 } 737 next(); 738 } ); 739 } 740 }; 741 } ); 742 743 var widget = $.widget; 744 745 746 /*! 747 * jQuery UI :data 1.12.1 748 * http://jqueryui.com 749 * 750 * Copyright jQuery Foundation and other contributors 751 * Released under the MIT license. 752 * http://jquery.org/license 753 */ 754 755 //>>label: :data Selector 756 //>>group: Core 757 //>>description: Selects elements which have data stored under the specified key. 758 //>>docs: http://api.jqueryui.com/data-selector/ 759 760 761 var data = $.extend( $.expr[ ":" ], { 762 data: $.expr.createPseudo ? 763 $.expr.createPseudo( function( dataName ) { 764 return function( elem ) { 765 return !!$.data( elem, dataName ); 766 }; 767 } ) : 768 769 // Support: jQuery <1.8 770 function( elem, i, match ) { 771 return !!$.data( elem, match[ 3 ] ); 772 } 773 } ); 774 775 /*! 776 * jQuery UI Scroll Parent 1.12.1 777 * http://jqueryui.com 778 * 779 * Copyright jQuery Foundation and other contributors 780 * Released under the MIT license. 781 * http://jquery.org/license 782 */ 783 784 //>>label: scrollParent 785 //>>group: Core 786 //>>description: Get the closest ancestor element that is scrollable. 787 //>>docs: http://api.jqueryui.com/scrollParent/ 788 789 790 791 var scrollParent = $.fn.scrollParent = function( includeHidden ) { 792 var position = this.css( "position" ), 793 excludeStaticParent = position === "absolute", 794 overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/, 795 scrollParent = this.parents().filter( function() { 796 var parent = $( this ); 797 if ( excludeStaticParent && parent.css( "position" ) === "static" ) { 798 return false; 799 } 800 return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) + 801 parent.css( "overflow-x" ) ); 802 } ).eq( 0 ); 803 804 return position === "fixed" || !scrollParent.length ? 805 $( this[ 0 ].ownerDocument || document ) : 806 scrollParent; 807 }; 808 809 810 811 812 // This file is deprecated 813 var ie = $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); 814 815 /*! 816 * jQuery UI Mouse 1.12.1 817 * http://jqueryui.com 818 * 819 * Copyright jQuery Foundation and other contributors 820 * Released under the MIT license. 821 * http://jquery.org/license 822 */ 823 824 //>>label: Mouse 825 //>>group: Widgets 826 //>>description: Abstracts mouse-based interactions to assist in creating certain widgets. 827 //>>docs: http://api.jqueryui.com/mouse/ 828 829 830 831 var mouseHandled = false; 832 $( document ).on( "mouseup", function() { 833 mouseHandled = false; 834 } ); 835 836 var widgetsMouse = $.widget( "ui.mouse", { 837 version: "1.12.1", 838 options: { 839 cancel: "input, textarea, button, select, option", 840 distance: 1, 841 delay: 0 842 }, 843 _mouseInit: function() { 844 var that = this; 845 846 this.element 847 .on( "mousedown." + this.widgetName, function( event ) { 848 return that._mouseDown( event ); 849 } ) 850 .on( "click." + this.widgetName, function( event ) { 851 if ( true === $.data( event.target, that.widgetName + ".preventClickEvent" ) ) { 852 $.removeData( event.target, that.widgetName + ".preventClickEvent" ); 853 event.stopImmediatePropagation(); 854 return false; 855 } 856 } ); 857 858 this.started = false; 859 }, 860 861 // TODO: make sure destroying one instance of mouse doesn't mess with 862 // other instances of mouse 863 _mouseDestroy: function() { 864 this.element.off( "." + this.widgetName ); 865 if ( this._mouseMoveDelegate ) { 866 this.document 867 .off( "mousemove." + this.widgetName, this._mouseMoveDelegate ) 868 .off( "mouseup." + this.widgetName, this._mouseUpDelegate ); 869 } 870 }, 871 872 _mouseDown: function( event ) { 873 874 // don't let more than one widget handle mouseStart 875 if ( mouseHandled ) { 876 return; 877 } 878 879 this._mouseMoved = false; 880 881 // We may have missed mouseup (out of window) 882 ( this._mouseStarted && this._mouseUp( event ) ); 883 884 this._mouseDownEvent = event; 885 886 var that = this, 887 btnIsLeft = ( event.which === 1 ), 888 889 // event.target.nodeName works around a bug in IE 8 with 890 // disabled inputs (#7620) 891 elIsCancel = ( typeof this.options.cancel === "string" && event.target.nodeName ? 892 $( event.target ).closest( this.options.cancel ).length : false ); 893 if ( !btnIsLeft || elIsCancel || !this._mouseCapture( event ) ) { 894 return true; 895 } 896 897 this.mouseDelayMet = !this.options.delay; 898 if ( !this.mouseDelayMet ) { 899 this._mouseDelayTimer = setTimeout( function() { 900 that.mouseDelayMet = true; 901 }, this.options.delay ); 902 } 903 904 if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) { 905 this._mouseStarted = ( this._mouseStart( event ) !== false ); 906 if ( !this._mouseStarted ) { 907 event.preventDefault(); 908 return true; 909 } 910 } 911 912 // Click event may never have fired (Gecko & Opera) 913 if ( true === $.data( event.target, this.widgetName + ".preventClickEvent" ) ) { 914 $.removeData( event.target, this.widgetName + ".preventClickEvent" ); 915 } 916 917 // These delegates are required to keep context 918 this._mouseMoveDelegate = function( event ) { 919 return that._mouseMove( event ); 920 }; 921 this._mouseUpDelegate = function( event ) { 922 return that._mouseUp( event ); 923 }; 924 925 this.document 926 .on( "mousemove." + this.widgetName, this._mouseMoveDelegate ) 927 .on( "mouseup." + this.widgetName, this._mouseUpDelegate ); 928 929 event.preventDefault(); 930 931 mouseHandled = true; 932 return true; 933 }, 934 935 _mouseMove: function( event ) { 936 937 // Only check for mouseups outside the document if you've moved inside the document 938 // at least once. This prevents the firing of mouseup in the case of IE<9, which will 939 // fire a mousemove event if content is placed under the cursor. See #7778 940 // Support: IE <9 941 if ( this._mouseMoved ) { 942 943 // IE mouseup check - mouseup happened when mouse was out of window 944 if ( $.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && 945 !event.button ) { 946 return this._mouseUp( event ); 947 948 // Iframe mouseup check - mouseup occurred in another document 949 } else if ( !event.which ) { 950 951 // Support: Safari <=8 - 9 952 // Safari sets which to 0 if you press any of the following keys 953 // during a drag (#14461) 954 if ( event.originalEvent.altKey || event.originalEvent.ctrlKey || 955 event.originalEvent.metaKey || event.originalEvent.shiftKey ) { 956 this.ignoreMissingWhich = true; 957 } else if ( !this.ignoreMissingWhich ) { 958 return this._mouseUp( event ); 959 } 960 } 961 } 962 963 if ( event.which || event.button ) { 964 this._mouseMoved = true; 965 } 966 967 if ( this._mouseStarted ) { 968 this._mouseDrag( event ); 969 return event.preventDefault(); 970 } 971 972 if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) { 973 this._mouseStarted = 974 ( this._mouseStart( this._mouseDownEvent, event ) !== false ); 975 ( this._mouseStarted ? this._mouseDrag( event ) : this._mouseUp( event ) ); 976 } 977 978 return !this._mouseStarted; 979 }, 980 981 _mouseUp: function( event ) { 982 this.document 983 .off( "mousemove." + this.widgetName, this._mouseMoveDelegate ) 984 .off( "mouseup." + this.widgetName, this._mouseUpDelegate ); 985 986 if ( this._mouseStarted ) { 987 this._mouseStarted = false; 988 989 if ( event.target === this._mouseDownEvent.target ) { 990 $.data( event.target, this.widgetName + ".preventClickEvent", true ); 991 } 992 993 this._mouseStop( event ); 994 } 995 996 if ( this._mouseDelayTimer ) { 997 clearTimeout( this._mouseDelayTimer ); 998 delete this._mouseDelayTimer; 999 } 1000 1001 this.ignoreMissingWhich = false; 1002 mouseHandled = false; 1003 event.preventDefault(); 1004 }, 1005 1006 _mouseDistanceMet: function( event ) { 1007 return ( Math.max( 1008 Math.abs( this._mouseDownEvent.pageX - event.pageX ), 1009 Math.abs( this._mouseDownEvent.pageY - event.pageY ) 1010 ) >= this.options.distance 1011 ); 1012 }, 1013 1014 _mouseDelayMet: function( /* event */ ) { 1015 return this.mouseDelayMet; 1016 }, 1017 1018 // These are placeholder methods, to be overriden by extending plugin 1019 _mouseStart: function( /* event */ ) {}, 1020 _mouseDrag: function( /* event */ ) {}, 1021 _mouseStop: function( /* event */ ) {}, 1022 _mouseCapture: function( /* event */ ) { return true; } 1023 } ); 1024 1025 1026 1027 1028 // $.ui.plugin is deprecated. Use $.widget() extensions instead. 1029 var plugin = $.ui.plugin = { 1030 add: function( module, option, set ) { 1031 var i, 1032 proto = $.ui[ module ].prototype; 1033 for ( i in set ) { 1034 proto.plugins[ i ] = proto.plugins[ i ] || []; 1035 proto.plugins[ i ].push( [ option, set[ i ] ] ); 1036 } 1037 }, 1038 call: function( instance, name, args, allowDisconnected ) { 1039 var i, 1040 set = instance.plugins[ name ]; 1041 1042 if ( !set ) { 1043 return; 1044 } 1045 1046 if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode || 1047 instance.element[ 0 ].parentNode.nodeType === 11 ) ) { 1048 return; 1049 } 1050 1051 for ( i = 0; i < set.length; i++ ) { 1052 if ( instance.options[ set[ i ][ 0 ] ] ) { 1053 set[ i ][ 1 ].apply( instance.element, args ); 1054 } 1055 } 1056 } 1057 }; 1058 1059 1060 1061 var safeActiveElement = $.ui.safeActiveElement = function( document ) { 1062 var activeElement; 1063 1064 // Support: IE 9 only 1065 // IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe> 1066 try { 1067 activeElement = document.activeElement; 1068 } catch ( error ) { 1069 activeElement = document.body; 1070 } 1071 1072 // Support: IE 9 - 11 only 1073 // IE may return null instead of an element 1074 // Interestingly, this only seems to occur when NOT in an iframe 1075 if ( !activeElement ) { 1076 activeElement = document.body; 1077 } 1078 1079 // Support: IE 11 only 1080 // IE11 returns a seemingly empty object in some cases when accessing 1081 // document.activeElement from an <iframe> 1082 if ( !activeElement.nodeName ) { 1083 activeElement = document.body; 1084 } 1085 1086 return activeElement; 1087 }; 1088 1089 1090 1091 var safeBlur = $.ui.safeBlur = function( element ) { 1092 1093 // Support: IE9 - 10 only 1094 // If the <body> is blurred, IE will switch windows, see #9420 1095 if ( element && element.nodeName.toLowerCase() !== "body" ) { 1096 $( element ).trigger( "blur" ); 1097 } 1098 }; 1099 1100 1101 /*! 1102 * jQuery UI Draggable 1.12.1 1103 * http://jqueryui.com 1104 * 1105 * Copyright jQuery Foundation and other contributors 1106 * Released under the MIT license. 1107 * http://jquery.org/license 1108 */ 1109 1110 //>>label: Draggable 1111 //>>group: Interactions 1112 //>>description: Enables dragging functionality for any element. 1113 //>>docs: http://api.jqueryui.com/draggable/ 1114 //>>demos: http://jqueryui.com/draggable/ 1115 //>>css.structure: ../../themes/base/draggable.css 1116 1117 1118 1119 $.widget( "ui.draggable", $.ui.mouse, { 1120 version: "1.12.1", 1121 widgetEventPrefix: "drag", 1122 options: { 1123 addClasses: true, 1124 appendTo: "parent", 1125 axis: false, 1126 connectToSortable: false, 1127 containment: false, 1128 cursor: "auto", 1129 cursorAt: false, 1130 grid: false, 1131 handle: false, 1132 helper: "original", 1133 iframeFix: false, 1134 opacity: false, 1135 refreshPositions: false, 1136 revert: false, 1137 revertDuration: 500, 1138 scope: "default", 1139 scroll: true, 1140 scrollSensitivity: 20, 1141 scrollSpeed: 20, 1142 snap: false, 1143 snapMode: "both", 1144 snapTolerance: 20, 1145 stack: false, 1146 zIndex: false, 1147 1148 // Callbacks 1149 drag: null, 1150 start: null, 1151 stop: null 1152 }, 1153 _create: function() { 1154 1155 if ( this.options.helper === "original" ) { 1156 this._setPositionRelative(); 1157 } 1158 if ( this.options.addClasses ) { 1159 this._addClass( "ui-draggable" ); 1160 } 1161 this._setHandleClassName(); 1162 1163 this._mouseInit(); 1164 }, 1165 1166 _setOption: function( key, value ) { 1167 this._super( key, value ); 1168 if ( key === "handle" ) { 1169 this._removeHandleClassName(); 1170 this._setHandleClassName(); 1171 } 1172 }, 1173 1174 _destroy: function() { 1175 if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) { 1176 this.destroyOnClear = true; 1177 return; 1178 } 1179 this._removeHandleClassName(); 1180 this._mouseDestroy(); 1181 }, 1182 1183 _mouseCapture: function( event ) { 1184 var o = this.options; 1185 1186 // Among others, prevent a drag on a resizable-handle 1187 if ( this.helper || o.disabled || 1188 $( event.target ).closest( ".ui-resizable-handle" ).length > 0 ) { 1189 return false; 1190 } 1191 1192 //Quit if we're not on a valid handle 1193 this.handle = this._getHandle( event ); 1194 if ( !this.handle ) { 1195 return false; 1196 } 1197 1198 this._blurActiveElement( event ); 1199 1200 this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix ); 1201 1202 return true; 1203 1204 }, 1205 1206 _blockFrames: function( selector ) { 1207 this.iframeBlocks = this.document.find( selector ).map( function() { 1208 var iframe = $( this ); 1209 1210 return $( "<div>" ) 1211 .css( "position", "absolute" ) 1212 .appendTo( iframe.parent() ) 1213 .outerWidth( iframe.outerWidth() ) 1214 .outerHeight( iframe.outerHeight() ) 1215 .offset( iframe.offset() )[ 0 ]; 1216 } ); 1217 }, 1218 1219 _unblockFrames: function() { 1220 if ( this.iframeBlocks ) { 1221 this.iframeBlocks.remove(); 1222 delete this.iframeBlocks; 1223 } 1224 }, 1225 1226 _blurActiveElement: function( event ) { 1227 var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ), 1228 target = $( event.target ); 1229 1230 // Don't blur if the event occurred on an element that is within 1231 // the currently focused element 1232 // See #10527, #12472 1233 if ( target.closest( activeElement ).length ) { 1234 return; 1235 } 1236 1237 // Blur any element that currently has focus, see #4261 1238 $.ui.safeBlur( activeElement ); 1239 }, 1240 1241 _mouseStart: function( event ) { 1242 1243 var o = this.options; 1244 1245 //Create and append the visible helper 1246 this.helper = this._createHelper( event ); 1247 1248 this._addClass( this.helper, "ui-draggable-dragging" ); 1249 1250 //Cache the helper size 1251 this._cacheHelperProportions(); 1252 1253 //If ddmanager is used for droppables, set the global draggable 1254 if ( $.ui.ddmanager ) { 1255 $.ui.ddmanager.current = this; 1256 } 1257 1258 /* 1259 * - Position generation - 1260 * This block generates everything position related - it's the core of draggables. 1261 */ 1262 1263 //Cache the margins of the original element 1264 this._cacheMargins(); 1265 1266 //Store the helper's css position 1267 this.cssPosition = this.helper.css( "position" ); 1268 this.scrollParent = this.helper.scrollParent( true ); 1269 this.offsetParent = this.helper.offsetParent(); 1270 this.hasFixedAncestor = this.helper.parents().filter( function() { 1271 return $( this ).css( "position" ) === "fixed"; 1272 } ).length > 0; 1273 1274 //The element's absolute position on the page minus margins 1275 this.positionAbs = this.element.offset(); 1276 this._refreshOffsets( event ); 1277 1278 //Generate the original position 1279 this.originalPosition = this.position = this._generatePosition( event, false ); 1280 this.originalPageX = event.pageX; 1281 this.originalPageY = event.pageY; 1282 1283 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied 1284 ( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) ); 1285 1286 //Set a containment if given in the options 1287 this._setContainment(); 1288 1289 //Trigger event + callbacks 1290 if ( this._trigger( "start", event ) === false ) { 1291 this._clear(); 1292 return false; 1293 } 1294 1295 //Recache the helper size 1296 this._cacheHelperProportions(); 1297 1298 //Prepare the droppable offsets 1299 if ( $.ui.ddmanager && !o.dropBehaviour ) { 1300 $.ui.ddmanager.prepareOffsets( this, event ); 1301 } 1302 1303 // Execute the drag once - this causes the helper not to be visible before getting its 1304 // correct position 1305 this._mouseDrag( event, true ); 1306 1307 // If the ddmanager is used for droppables, inform the manager that dragging has started 1308 // (see #5003) 1309 if ( $.ui.ddmanager ) { 1310 $.ui.ddmanager.dragStart( this, event ); 1311 } 1312 1313 return true; 1314 }, 1315 1316 _refreshOffsets: function( event ) { 1317 this.offset = { 1318 top: this.positionAbs.top - this.margins.top, 1319 left: this.positionAbs.left - this.margins.left, 1320 scroll: false, 1321 parent: this._getParentOffset(), 1322 relative: this._getRelativeOffset() 1323 }; 1324 1325 this.offset.click = { 1326 left: event.pageX - this.offset.left, 1327 top: event.pageY - this.offset.top 1328 }; 1329 }, 1330 1331 _mouseDrag: function( event, noPropagation ) { 1332 1333 // reset any necessary cached properties (see #5009) 1334 if ( this.hasFixedAncestor ) { 1335 this.offset.parent = this._getParentOffset(); 1336 } 1337 1338 //Compute the helpers position 1339 this.position = this._generatePosition( event, true ); 1340 this.positionAbs = this._convertPositionTo( "absolute" ); 1341 1342 //Call plugins and callbacks and use the resulting position if something is returned 1343 if ( !noPropagation ) { 1344 var ui = this._uiHash(); 1345 if ( this._trigger( "drag", event, ui ) === false ) { 1346 this._mouseUp( new $.Event( "mouseup", event ) ); 1347 return false; 1348 } 1349 this.position = ui.position; 1350 } 1351 1352 this.helper[ 0 ].style.left = this.position.left + "px"; 1353 this.helper[ 0 ].style.top = this.position.top + "px"; 1354 1355 if ( $.ui.ddmanager ) { 1356 $.ui.ddmanager.drag( this, event ); 1357 } 1358 1359 return false; 1360 }, 1361 1362 _mouseStop: function( event ) { 1363 1364 //If we are using droppables, inform the manager about the drop 1365 var that = this, 1366 dropped = false; 1367 if ( $.ui.ddmanager && !this.options.dropBehaviour ) { 1368 dropped = $.ui.ddmanager.drop( this, event ); 1369 } 1370 1371 //if a drop comes from outside (a sortable) 1372 if ( this.dropped ) { 1373 dropped = this.dropped; 1374 this.dropped = false; 1375 } 1376 1377 if ( ( this.options.revert === "invalid" && !dropped ) || 1378 ( this.options.revert === "valid" && dropped ) || 1379 this.options.revert === true || ( $.isFunction( this.options.revert ) && 1380 this.options.revert.call( this.element, dropped ) ) 1381 ) { 1382 $( this.helper ).animate( 1383 this.originalPosition, 1384 parseInt( this.options.revertDuration, 10 ), 1385 function() { 1386 if ( that._trigger( "stop", event ) !== false ) { 1387 that._clear(); 1388 } 1389 } 1390 ); 1391 } else { 1392 if ( this._trigger( "stop", event ) !== false ) { 1393 this._clear(); 1394 } 1395 } 1396 1397 return false; 1398 }, 1399 1400 _mouseUp: function( event ) { 1401 this._unblockFrames(); 1402 1403 // If the ddmanager is used for droppables, inform the manager that dragging has stopped 1404 // (see #5003) 1405 if ( $.ui.ddmanager ) { 1406 $.ui.ddmanager.dragStop( this, event ); 1407 } 1408 1409 // Only need to focus if the event occurred on the draggable itself, see #10527 1410 if ( this.handleElement.is( event.target ) ) { 1411 1412 // The interaction is over; whether or not the click resulted in a drag, 1413 // focus the element 1414 this.element.trigger( "focus" ); 1415 } 1416 1417 return $.ui.mouse.prototype._mouseUp.call( this, event ); 1418 }, 1419 1420 cancel: function() { 1421 1422 if ( this.helper.is( ".ui-draggable-dragging" ) ) { 1423 this._mouseUp( new $.Event( "mouseup", { target: this.element[ 0 ] } ) ); 1424 } else { 1425 this._clear(); 1426 } 1427 1428 return this; 1429 1430 }, 1431 1432 _getHandle: function( event ) { 1433 return this.options.handle ? 1434 !!$( event.target ).closest( this.element.find( this.options.handle ) ).length : 1435 true; 1436 }, 1437 1438 _setHandleClassName: function() { 1439 this.handleElement = this.options.handle ? 1440 this.element.find( this.options.handle ) : this.element; 1441 this._addClass( this.handleElement, "ui-draggable-handle" ); 1442 }, 1443 1444 _removeHandleClassName: function() { 1445 this._removeClass( this.handleElement, "ui-draggable-handle" ); 1446 }, 1447 1448 _createHelper: function( event ) { 1449 1450 var o = this.options, 1451 helperIsFunction = $.isFunction( o.helper ), 1452 helper = helperIsFunction ? 1453 $( o.helper.apply( this.element[ 0 ], [ event ] ) ) : 1454 ( o.helper === "clone" ? 1455 this.element.clone().removeAttr( "id" ) : 1456 this.element ); 1457 1458 if ( !helper.parents( "body" ).length ) { 1459 helper.appendTo( ( o.appendTo === "parent" ? 1460 this.element[ 0 ].parentNode : 1461 o.appendTo ) ); 1462 } 1463 1464 // Http://bugs.jqueryui.com/ticket/9446 1465 // a helper function can return the original element 1466 // which wouldn't have been set to relative in _create 1467 if ( helperIsFunction && helper[ 0 ] === this.element[ 0 ] ) { 1468 this._setPositionRelative(); 1469 } 1470 1471 if ( helper[ 0 ] !== this.element[ 0 ] && 1472 !( /(fixed|absolute)/ ).test( helper.css( "position" ) ) ) { 1473 helper.css( "position", "absolute" ); 1474 } 1475 1476 return helper; 1477 1478 }, 1479 1480 _setPositionRelative: function() { 1481 if ( !( /^(?:r|a|f)/ ).test( this.element.css( "position" ) ) ) { 1482 this.element[ 0 ].style.position = "relative"; 1483 } 1484 }, 1485 1486 _adjustOffsetFromHelper: function( obj ) { 1487 if ( typeof obj === "string" ) { 1488 obj = obj.split( " " ); 1489 } 1490 if ( $.isArray( obj ) ) { 1491 obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 }; 1492 } 1493 if ( "left" in obj ) { 1494 this.offset.click.left = obj.left + this.margins.left; 1495 } 1496 if ( "right" in obj ) { 1497 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; 1498 } 1499 if ( "top" in obj ) { 1500 this.offset.click.top = obj.top + this.margins.top; 1501 } 1502 if ( "bottom" in obj ) { 1503 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; 1504 } 1505 }, 1506 1507 _isRootNode: function( element ) { 1508 return ( /(html|body)/i ).test( element.tagName ) || element === this.document[ 0 ]; 1509 }, 1510 1511 _getParentOffset: function() { 1512 1513 //Get the offsetParent and cache its position 1514 var po = this.offsetParent.offset(), 1515 document = this.document[ 0 ]; 1516 1517 // This is a special case where we need to modify a offset calculated on start, since the 1518 // following happened: 1519 // 1. The position of the helper is absolute, so it's position is calculated based on the 1520 // next positioned parent 1521 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't 1522 // the document, which means that the scroll is included in the initial calculation of the 1523 // offset of the parent, and never recalculated upon drag 1524 if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== document && 1525 $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) { 1526 po.left += this.scrollParent.scrollLeft(); 1527 po.top += this.scrollParent.scrollTop(); 1528 } 1529 1530 if ( this._isRootNode( this.offsetParent[ 0 ] ) ) { 1531 po = { top: 0, left: 0 }; 1532 } 1533 1534 return { 1535 top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ), 1536 left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 ) 1537 }; 1538 1539 }, 1540 1541 _getRelativeOffset: function() { 1542 if ( this.cssPosition !== "relative" ) { 1543 return { top: 0, left: 0 }; 1544 } 1545 1546 var p = this.element.position(), 1547 scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ); 1548 1549 return { 1550 top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) + 1551 ( !scrollIsRootNode ? this.scrollParent.scrollTop() : 0 ), 1552 left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) + 1553 ( !scrollIsRootNode ? this.scrollParent.scrollLeft() : 0 ) 1554 }; 1555 1556 }, 1557 1558 _cacheMargins: function() { 1559 this.margins = { 1560 left: ( parseInt( this.element.css( "marginLeft" ), 10 ) || 0 ), 1561 top: ( parseInt( this.element.css( "marginTop" ), 10 ) || 0 ), 1562 right: ( parseInt( this.element.css( "marginRight" ), 10 ) || 0 ), 1563 bottom: ( parseInt( this.element.css( "marginBottom" ), 10 ) || 0 ) 1564 }; 1565 }, 1566 1567 _cacheHelperProportions: function() { 1568 this.helperProportions = { 1569 width: this.helper.outerWidth(), 1570 height: this.helper.outerHeight() 1571 }; 1572 }, 1573 1574 _setContainment: function() { 1575 1576 var isUserScrollable, c, ce, 1577 o = this.options, 1578 document = this.document[ 0 ]; 1579 1580 this.relativeContainer = null; 1581 1582 if ( !o.containment ) { 1583 this.containment = null; 1584 return; 1585 } 1586 1587 if ( o.containment === "window" ) { 1588 this.containment = [ 1589 $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left, 1590 $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top, 1591 $( window ).scrollLeft() + $( window ).width() - 1592 this.helperProportions.width - this.margins.left, 1593 $( window ).scrollTop() + 1594 ( $( window ).height() || document.body.parentNode.scrollHeight ) - 1595 this.helperProportions.height - this.margins.top 1596 ]; 1597 return; 1598 } 1599 1600 if ( o.containment === "document" ) { 1601 this.containment = [ 1602 0, 1603 0, 1604 $( document ).width() - this.helperProportions.width - this.margins.left, 1605 ( $( document ).height() || document.body.parentNode.scrollHeight ) - 1606 this.helperProportions.height - this.margins.top 1607 ]; 1608 return; 1609 } 1610 1611 if ( o.containment.constructor === Array ) { 1612 this.containment = o.containment; 1613 return; 1614 } 1615 1616 if ( o.containment === "parent" ) { 1617 o.containment = this.helper[ 0 ].parentNode; 1618 } 1619 1620 c = $( o.containment ); 1621 ce = c[ 0 ]; 1622 1623 if ( !ce ) { 1624 return; 1625 } 1626 1627 isUserScrollable = /(scroll|auto)/.test( c.css( "overflow" ) ); 1628 1629 this.containment = [ 1630 ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + 1631 ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ), 1632 ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + 1633 ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ), 1634 ( isUserScrollable ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - 1635 ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) - 1636 ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) - 1637 this.helperProportions.width - 1638 this.margins.left - 1639 this.margins.right, 1640 ( isUserScrollable ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - 1641 ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) - 1642 ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) - 1643 this.helperProportions.height - 1644 this.margins.top - 1645 this.margins.bottom 1646 ]; 1647 this.relativeContainer = c; 1648 }, 1649 1650 _convertPositionTo: function( d, pos ) { 1651 1652 if ( !pos ) { 1653 pos = this.position; 1654 } 1655 1656 var mod = d === "absolute" ? 1 : -1, 1657 scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ); 1658 1659 return { 1660 top: ( 1661 1662 // The absolute mouse position 1663 pos.top + 1664 1665 // Only for relative positioned nodes: Relative offset from element to offset parent 1666 this.offset.relative.top * mod + 1667 1668 // The offsetParent's offset without borders (offset + border) 1669 this.offset.parent.top * mod - 1670 ( ( this.cssPosition === "fixed" ? 1671 -this.offset.scroll.top : 1672 ( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) * mod ) 1673 ), 1674 left: ( 1675 1676 // The absolute mouse position 1677 pos.left + 1678 1679 // Only for relative positioned nodes: Relative offset from element to offset parent 1680 this.offset.relative.left * mod + 1681 1682 // The offsetParent's offset without borders (offset + border) 1683 this.offset.parent.left * mod - 1684 ( ( this.cssPosition === "fixed" ? 1685 -this.offset.scroll.left : 1686 ( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) * mod ) 1687 ) 1688 }; 1689 1690 }, 1691 1692 _generatePosition: function( event, constrainPosition ) { 1693 1694 var containment, co, top, left, 1695 o = this.options, 1696 scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ), 1697 pageX = event.pageX, 1698 pageY = event.pageY; 1699 1700 // Cache the scroll 1701 if ( !scrollIsRootNode || !this.offset.scroll ) { 1702 this.offset.scroll = { 1703 top: this.scrollParent.scrollTop(), 1704 left: this.scrollParent.scrollLeft() 1705 }; 1706 } 1707 1708 /* 1709 * - Position constraining - 1710 * Constrain the position to a mix of grid, containment. 1711 */ 1712 1713 // If we are not dragging yet, we won't check for options 1714 if ( constrainPosition ) { 1715 if ( this.containment ) { 1716 if ( this.relativeContainer ) { 1717 co = this.relativeContainer.offset(); 1718 containment = [ 1719 this.containment[ 0 ] + co.left, 1720 this.containment[ 1 ] + co.top, 1721 this.containment[ 2 ] + co.left, 1722 this.containment[ 3 ] + co.top 1723 ]; 1724 } else { 1725 containment = this.containment; 1726 } 1727 1728 if ( event.pageX - this.offset.click.left < containment[ 0 ] ) { 1729 pageX = containment[ 0 ] + this.offset.click.left; 1730 } 1731 if ( event.pageY - this.offset.click.top < containment[ 1 ] ) { 1732 pageY = containment[ 1 ] + this.offset.click.top; 1733 } 1734 if ( event.pageX - this.offset.click.left > containment[ 2 ] ) { 1735 pageX = containment[ 2 ] + this.offset.click.left; 1736 } 1737 if ( event.pageY - this.offset.click.top > containment[ 3 ] ) { 1738 pageY = containment[ 3 ] + this.offset.click.top; 1739 } 1740 } 1741 1742 if ( o.grid ) { 1743 1744 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid 1745 // argument errors in IE (see ticket #6950) 1746 top = o.grid[ 1 ] ? this.originalPageY + Math.round( ( pageY - 1747 this.originalPageY ) / o.grid[ 1 ] ) * o.grid[ 1 ] : this.originalPageY; 1748 pageY = containment ? ( ( top - this.offset.click.top >= containment[ 1 ] || 1749 top - this.offset.click.top > containment[ 3 ] ) ? 1750 top : 1751 ( ( top - this.offset.click.top >= containment[ 1 ] ) ? 1752 top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) : top; 1753 1754 left = o.grid[ 0 ] ? this.originalPageX + 1755 Math.round( ( pageX - this.originalPageX ) / o.grid[ 0 ] ) * o.grid[ 0 ] : 1756 this.originalPageX; 1757 pageX = containment ? ( ( left - this.offset.click.left >= containment[ 0 ] || 1758 left - this.offset.click.left > containment[ 2 ] ) ? 1759 left : 1760 ( ( left - this.offset.click.left >= containment[ 0 ] ) ? 1761 left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) : left; 1762 } 1763 1764 if ( o.axis === "y" ) { 1765 pageX = this.originalPageX; 1766 } 1767 1768 if ( o.axis === "x" ) { 1769 pageY = this.originalPageY; 1770 } 1771 } 1772 1773 return { 1774 top: ( 1775 1776 // The absolute mouse position 1777 pageY - 1778 1779 // Click offset (relative to the element) 1780 this.offset.click.top - 1781 1782 // Only for relative positioned nodes: Relative offset from element to offset parent 1783 this.offset.relative.top - 1784 1785 // The offsetParent's offset without borders (offset + border) 1786 this.offset.parent.top + 1787 ( this.cssPosition === "fixed" ? 1788 -this.offset.scroll.top : 1789 ( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) 1790 ), 1791 left: ( 1792 1793 // The absolute mouse position 1794 pageX - 1795 1796 // Click offset (relative to the element) 1797 this.offset.click.left - 1798 1799 // Only for relative positioned nodes: Relative offset from element to offset parent 1800 this.offset.relative.left - 1801 1802 // The offsetParent's offset without borders (offset + border) 1803 this.offset.parent.left + 1804 ( this.cssPosition === "fixed" ? 1805 -this.offset.scroll.left : 1806 ( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) 1807 ) 1808 }; 1809 1810 }, 1811 1812 _clear: function() { 1813 this._removeClass( this.helper, "ui-draggable-dragging" ); 1814 if ( this.helper[ 0 ] !== this.element[ 0 ] && !this.cancelHelperRemoval ) { 1815 this.helper.remove(); 1816 } 1817 this.helper = null; 1818 this.cancelHelperRemoval = false; 1819 if ( this.destroyOnClear ) { 1820 this.destroy(); 1821 } 1822 }, 1823 1824 // From now on bulk stuff - mainly helpers 1825 1826 _trigger: function( type, event, ui ) { 1827 ui = ui || this._uiHash(); 1828 $.ui.plugin.call( this, type, [ event, ui, this ], true ); 1829 1830 // Absolute position and offset (see #6884 ) have to be recalculated after plugins 1831 if ( /^(drag|start|stop)/.test( type ) ) { 1832 this.positionAbs = this._convertPositionTo( "absolute" ); 1833 ui.offset = this.positionAbs; 1834 } 1835 return $.Widget.prototype._trigger.call( this, type, event, ui ); 1836 }, 1837 1838 plugins: {}, 1839 1840 _uiHash: function() { 1841 return { 1842 helper: this.helper, 1843 position: this.position, 1844 originalPosition: this.originalPosition, 1845 offset: this.positionAbs 1846 }; 1847 } 1848 1849 } ); 1850 1851 $.ui.plugin.add( "draggable", "connectToSortable", { 1852 start: function( event, ui, draggable ) { 1853 var uiSortable = $.extend( {}, ui, { 1854 item: draggable.element 1855 } ); 1856 1857 draggable.sortables = []; 1858 $( draggable.options.connectToSortable ).each( function() { 1859 var sortable = $( this ).sortable( "instance" ); 1860 1861 if ( sortable && !sortable.options.disabled ) { 1862 draggable.sortables.push( sortable ); 1863 1864 // RefreshPositions is called at drag start to refresh the containerCache 1865 // which is used in drag. This ensures it's initialized and synchronized 1866 // with any changes that might have happened on the page since initialization. 1867 sortable.refreshPositions(); 1868 sortable._trigger( "activate", event, uiSortable ); 1869 } 1870 } ); 1871 }, 1872 stop: function( event, ui, draggable ) { 1873 var uiSortable = $.extend( {}, ui, { 1874 item: draggable.element 1875 } ); 1876 1877 draggable.cancelHelperRemoval = false; 1878 1879 $.each( draggable.sortables, function() { 1880 var sortable = this; 1881 1882 if ( sortable.isOver ) { 1883 sortable.isOver = 0; 1884 1885 // Allow this sortable to handle removing the helper 1886 draggable.cancelHelperRemoval = true; 1887 sortable.cancelHelperRemoval = false; 1888 1889 // Use _storedCSS To restore properties in the sortable, 1890 // as this also handles revert (#9675) since the draggable 1891 // may have modified them in unexpected ways (#8809) 1892 sortable._storedCSS = { 1893 position: sortable.placeholder.css( "position" ), 1894 top: sortable.placeholder.css( "top" ), 1895 left: sortable.placeholder.css( "left" ) 1896 }; 1897 1898 sortable._mouseStop( event ); 1899 1900 // Once drag has ended, the sortable should return to using 1901 // its original helper, not the shared helper from draggable 1902 sortable.options.helper = sortable.options._helper; 1903 } else { 1904 1905 // Prevent this Sortable from removing the helper. 1906 // However, don't set the draggable to remove the helper 1907 // either as another connected Sortable may yet handle the removal. 1908 sortable.cancelHelperRemoval = true; 1909 1910 sortable._trigger( "deactivate", event, uiSortable ); 1911 } 1912 } ); 1913 }, 1914 drag: function( event, ui, draggable ) { 1915 $.each( draggable.sortables, function() { 1916 var innermostIntersecting = false, 1917 sortable = this; 1918 1919 // Copy over variables that sortable's _intersectsWith uses 1920 sortable.positionAbs = draggable.positionAbs; 1921 sortable.helperProportions = draggable.helperProportions; 1922 sortable.offset.click = draggable.offset.click; 1923 1924 if ( sortable._intersectsWith( sortable.containerCache ) ) { 1925 innermostIntersecting = true; 1926 1927 $.each( draggable.sortables, function() { 1928 1929 // Copy over variables that sortable's _intersectsWith uses 1930 this.positionAbs = draggable.positionAbs; 1931 this.helperProportions = draggable.helperProportions; 1932 this.offset.click = draggable.offset.click; 1933 1934 if ( this !== sortable && 1935 this._intersectsWith( this.containerCache ) && 1936 $.contains( sortable.element[ 0 ], this.element[ 0 ] ) ) { 1937 innermostIntersecting = false; 1938 } 1939 1940 return innermostIntersecting; 1941 } ); 1942 } 1943 1944 if ( innermostIntersecting ) { 1945 1946 // If it intersects, we use a little isOver variable and set it once, 1947 // so that the move-in stuff gets fired only once. 1948 if ( !sortable.isOver ) { 1949 sortable.isOver = 1; 1950 1951 // Store draggable's parent in case we need to reappend to it later. 1952 draggable._parent = ui.helper.parent(); 1953 1954 sortable.currentItem = ui.helper 1955 .appendTo( sortable.element ) 1956 .data( "ui-sortable-item", true ); 1957 1958 // Store helper option to later restore it 1959 sortable.options._helper = sortable.options.helper; 1960 1961 sortable.options.helper = function() { 1962 return ui.helper[ 0 ]; 1963 }; 1964 1965 // Fire the start events of the sortable with our passed browser event, 1966 // and our own helper (so it doesn't create a new one) 1967 event.target = sortable.currentItem[ 0 ]; 1968 sortable._mouseCapture( event, true ); 1969 sortable._mouseStart( event, true, true ); 1970 1971 // Because the browser event is way off the new appended portlet, 1972 // modify necessary variables to reflect the changes 1973 sortable.offset.click.top = draggable.offset.click.top; 1974 sortable.offset.click.left = draggable.offset.click.left; 1975 sortable.offset.parent.left -= draggable.offset.parent.left - 1976 sortable.offset.parent.left; 1977 sortable.offset.parent.top -= draggable.offset.parent.top - 1978 sortable.offset.parent.top; 1979 1980 draggable._trigger( "toSortable", event ); 1981 1982 // Inform draggable that the helper is in a valid drop zone, 1983 // used solely in the revert option to handle "valid/invalid". 1984 draggable.dropped = sortable.element; 1985 1986 // Need to refreshPositions of all sortables in the case that 1987 // adding to one sortable changes the location of the other sortables (#9675) 1988 $.each( draggable.sortables, function() { 1989 this.refreshPositions(); 1990 } ); 1991 1992 // Hack so receive/update callbacks work (mostly) 1993 draggable.currentItem = draggable.element; 1994 sortable.fromOutside = draggable; 1995 } 1996 1997 if ( sortable.currentItem ) { 1998 sortable._mouseDrag( event ); 1999 2000 // Copy the sortable's position because the draggable's can potentially reflect 2001 // a relative position, while sortable is always absolute, which the dragged 2002 // element has now become. (#8809) 2003 ui.position = sortable.position; 2004 } 2005 } else { 2006 2007 // If it doesn't intersect with the sortable, and it intersected before, 2008 // we fake the drag stop of the sortable, but make sure it doesn't remove 2009 // the helper by using cancelHelperRemoval. 2010 if ( sortable.isOver ) { 2011 2012 sortable.isOver = 0; 2013 sortable.cancelHelperRemoval = true; 2014 2015 // Calling sortable's mouseStop would trigger a revert, 2016 // so revert must be temporarily false until after mouseStop is called. 2017 sortable.options._revert = sortable.options.revert; 2018 sortable.options.revert = false; 2019 2020 sortable._trigger( "out", event, sortable._uiHash( sortable ) ); 2021 sortable._mouseStop( event, true ); 2022 2023 // Restore sortable behaviors that were modfied 2024 // when the draggable entered the sortable area (#9481) 2025 sortable.options.revert = sortable.options._revert; 2026 sortable.options.helper = sortable.options._helper; 2027 2028 if ( sortable.placeholder ) { 2029 sortable.placeholder.remove(); 2030 } 2031 2032 // Restore and recalculate the draggable's offset considering the sortable 2033 // may have modified them in unexpected ways. (#8809, #10669) 2034 ui.helper.appendTo( draggable._parent ); 2035 draggable._refreshOffsets( event ); 2036 ui.position = draggable._generatePosition( event, true ); 2037 2038 draggable._trigger( "fromSortable", event ); 2039 2040 // Inform draggable that the helper is no longer in a valid drop zone 2041 draggable.dropped = false; 2042 2043 // Need to refreshPositions of all sortables just in case removing 2044 // from one sortable changes the location of other sortables (#9675) 2045 $.each( draggable.sortables, function() { 2046 this.refreshPositions(); 2047 } ); 2048 } 2049 } 2050 } ); 2051 } 2052 } ); 2053 2054 $.ui.plugin.add( "draggable", "cursor", { 2055 start: function( event, ui, instance ) { 2056 var t = $( "body" ), 2057 o = instance.options; 2058 2059 if ( t.css( "cursor" ) ) { 2060 o._cursor = t.css( "cursor" ); 2061 } 2062 t.css( "cursor", o.cursor ); 2063 }, 2064 stop: function( event, ui, instance ) { 2065 var o = instance.options; 2066 if ( o._cursor ) { 2067 $( "body" ).css( "cursor", o._cursor ); 2068 } 2069 } 2070 } ); 2071 2072 $.ui.plugin.add( "draggable", "opacity", { 2073 start: function( event, ui, instance ) { 2074 var t = $( ui.helper ), 2075 o = instance.options; 2076 if ( t.css( "opacity" ) ) { 2077 o._opacity = t.css( "opacity" ); 2078 } 2079 t.css( "opacity", o.opacity ); 2080 }, 2081 stop: function( event, ui, instance ) { 2082 var o = instance.options; 2083 if ( o._opacity ) { 2084 $( ui.helper ).css( "opacity", o._opacity ); 2085 } 2086 } 2087 } ); 2088 2089 $.ui.plugin.add( "draggable", "scroll", { 2090 start: function( event, ui, i ) { 2091 if ( !i.scrollParentNotHidden ) { 2092 i.scrollParentNotHidden = i.helper.scrollParent( false ); 2093 } 2094 2095 if ( i.scrollParentNotHidden[ 0 ] !== i.document[ 0 ] && 2096 i.scrollParentNotHidden[ 0 ].tagName !== "HTML" ) { 2097 i.overflowOffset = i.scrollParentNotHidden.offset(); 2098 } 2099 }, 2100 drag: function( event, ui, i ) { 2101 2102 var o = i.options, 2103 scrolled = false, 2104 scrollParent = i.scrollParentNotHidden[ 0 ], 2105 document = i.document[ 0 ]; 2106 2107 if ( scrollParent !== document && scrollParent.tagName !== "HTML" ) { 2108 if ( !o.axis || o.axis !== "x" ) { 2109 if ( ( i.overflowOffset.top + scrollParent.offsetHeight ) - event.pageY < 2110 o.scrollSensitivity ) { 2111 scrollParent.scrollTop = scrolled = scrollParent.scrollTop + o.scrollSpeed; 2112 } else if ( event.pageY - i.overflowOffset.top < o.scrollSensitivity ) { 2113 scrollParent.scrollTop = scrolled = scrollParent.scrollTop - o.scrollSpeed; 2114 } 2115 } 2116 2117 if ( !o.axis || o.axis !== "y" ) { 2118 if ( ( i.overflowOffset.left + scrollParent.offsetWidth ) - event.pageX < 2119 o.scrollSensitivity ) { 2120 scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + o.scrollSpeed; 2121 } else if ( event.pageX - i.overflowOffset.left < o.scrollSensitivity ) { 2122 scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - o.scrollSpeed; 2123 } 2124 } 2125 2126 } else { 2127 2128 if ( !o.axis || o.axis !== "x" ) { 2129 if ( event.pageY - $( document ).scrollTop() < o.scrollSensitivity ) { 2130 scrolled = $( document ).scrollTop( $( document ).scrollTop() - o.scrollSpeed ); 2131 } else if ( $( window ).height() - ( event.pageY - $( document ).scrollTop() ) < 2132 o.scrollSensitivity ) { 2133 scrolled = $( document ).scrollTop( $( document ).scrollTop() + o.scrollSpeed ); 2134 } 2135 } 2136 2137 if ( !o.axis || o.axis !== "y" ) { 2138 if ( event.pageX - $( document ).scrollLeft() < o.scrollSensitivity ) { 2139 scrolled = $( document ).scrollLeft( 2140 $( document ).scrollLeft() - o.scrollSpeed 2141 ); 2142 } else if ( $( window ).width() - ( event.pageX - $( document ).scrollLeft() ) < 2143 o.scrollSensitivity ) { 2144 scrolled = $( document ).scrollLeft( 2145 $( document ).scrollLeft() + o.scrollSpeed 2146 ); 2147 } 2148 } 2149 2150 } 2151 2152 if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) { 2153 $.ui.ddmanager.prepareOffsets( i, event ); 2154 } 2155 2156 } 2157 } ); 2158 2159 $.ui.plugin.add( "draggable", "snap", { 2160 start: function( event, ui, i ) { 2161 2162 var o = i.options; 2163 2164 i.snapElements = []; 2165 2166 $( o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap ) 2167 .each( function() { 2168 var $t = $( this ), 2169 $o = $t.offset(); 2170 if ( this !== i.element[ 0 ] ) { 2171 i.snapElements.push( { 2172 item: this, 2173 width: $t.outerWidth(), height: $t.outerHeight(), 2174 top: $o.top, left: $o.left 2175 } ); 2176 } 2177 } ); 2178 2179 }, 2180 drag: function( event, ui, inst ) { 2181 2182 var ts, bs, ls, rs, l, r, t, b, i, first, 2183 o = inst.options, 2184 d = o.snapTolerance, 2185 x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width, 2186 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height; 2187 2188 for ( i = inst.snapElements.length - 1; i >= 0; i-- ) { 2189 2190 l = inst.snapElements[ i ].left - inst.margins.left; 2191 r = l + inst.snapElements[ i ].width; 2192 t = inst.snapElements[ i ].top - inst.margins.top; 2193 b = t + inst.snapElements[ i ].height; 2194 2195 if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || 2196 !$.contains( inst.snapElements[ i ].item.ownerDocument, 2197 inst.snapElements[ i ].item ) ) { 2198 if ( inst.snapElements[ i ].snapping ) { 2199 ( inst.options.snap.release && 2200 inst.options.snap.release.call( 2201 inst.element, 2202 event, 2203 $.extend( inst._uiHash(), { snapItem: inst.snapElements[ i ].item } ) 2204 ) ); 2205 } 2206 inst.snapElements[ i ].snapping = false; 2207 continue; 2208 } 2209 2210 if ( o.snapMode !== "inner" ) { 2211 ts = Math.abs( t - y2 ) <= d; 2212 bs = Math.abs( b - y1 ) <= d; 2213 ls = Math.abs( l - x2 ) <= d; 2214 rs = Math.abs( r - x1 ) <= d; 2215 if ( ts ) { 2216 ui.position.top = inst._convertPositionTo( "relative", { 2217 top: t - inst.helperProportions.height, 2218 left: 0 2219 } ).top; 2220 } 2221 if ( bs ) { 2222 ui.position.top = inst._convertPositionTo( "relative", { 2223 top: b, 2224 left: 0 2225 } ).top; 2226 } 2227 if ( ls ) { 2228 ui.position.left = inst._convertPositionTo( "relative", { 2229 top: 0, 2230 left: l - inst.helperProportions.width 2231 } ).left; 2232 } 2233 if ( rs ) { 2234 ui.position.left = inst._convertPositionTo( "relative", { 2235 top: 0, 2236 left: r 2237 } ).left; 2238 } 2239 } 2240 2241 first = ( ts || bs || ls || rs ); 2242 2243 if ( o.snapMode !== "outer" ) { 2244 ts = Math.abs( t - y1 ) <= d; 2245 bs = Math.abs( b - y2 ) <= d; 2246 ls = Math.abs( l - x1 ) <= d; 2247 rs = Math.abs( r - x2 ) <= d; 2248 if ( ts ) { 2249 ui.position.top = inst._convertPositionTo( "relative", { 2250 top: t, 2251 left: 0 2252 } ).top; 2253 } 2254 if ( bs ) { 2255 ui.position.top = inst._convertPositionTo( "relative", { 2256 top: b - inst.helperProportions.height, 2257 left: 0 2258 } ).top; 2259 } 2260 if ( ls ) { 2261 ui.position.left = inst._convertPositionTo( "relative", { 2262 top: 0, 2263 left: l 2264 } ).left; 2265 } 2266 if ( rs ) { 2267 ui.position.left = inst._convertPositionTo( "relative", { 2268 top: 0, 2269 left: r - inst.helperProportions.width 2270 } ).left; 2271 } 2272 } 2273 2274 if ( !inst.snapElements[ i ].snapping && ( ts || bs || ls || rs || first ) ) { 2275 ( inst.options.snap.snap && 2276 inst.options.snap.snap.call( 2277 inst.element, 2278 event, 2279 $.extend( inst._uiHash(), { 2280 snapItem: inst.snapElements[ i ].item 2281 } ) ) ); 2282 } 2283 inst.snapElements[ i ].snapping = ( ts || bs || ls || rs || first ); 2284 2285 } 2286 2287 } 2288 } ); 2289 2290 $.ui.plugin.add( "draggable", "stack", { 2291 start: function( event, ui, instance ) { 2292 var min, 2293 o = instance.options, 2294 group = $.makeArray( $( o.stack ) ).sort( function( a, b ) { 2295 return ( parseInt( $( a ).css( "zIndex" ), 10 ) || 0 ) - 2296 ( parseInt( $( b ).css( "zIndex" ), 10 ) || 0 ); 2297 } ); 2298 2299 if ( !group.length ) { return; } 2300 2301 min = parseInt( $( group[ 0 ] ).css( "zIndex" ), 10 ) || 0; 2302 $( group ).each( function( i ) { 2303 $( this ).css( "zIndex", min + i ); 2304 } ); 2305 this.css( "zIndex", ( min + group.length ) ); 2306 } 2307 } ); 2308 2309 $.ui.plugin.add( "draggable", "zIndex", { 2310 start: function( event, ui, instance ) { 2311 var t = $( ui.helper ), 2312 o = instance.options; 2313 2314 if ( t.css( "zIndex" ) ) { 2315 o._zIndex = t.css( "zIndex" ); 2316 } 2317 t.css( "zIndex", o.zIndex ); 2318 }, 2319 stop: function( event, ui, instance ) { 2320 var o = instance.options; 2321 2322 if ( o._zIndex ) { 2323 $( ui.helper ).css( "zIndex", o._zIndex ); 2324 } 2325 } 2326 } ); 2327 2328 var widgetsDraggable = $.ui.draggable; 2329 2330 2331 /*! 2332 * jQuery UI Droppable 1.12.1 2333 * http://jqueryui.com 2334 * 2335 * Copyright jQuery Foundation and other contributors 2336 * Released under the MIT license. 2337 * http://jquery.org/license 2338 */ 2339 2340 //>>label: Droppable 2341 //>>group: Interactions 2342 //>>description: Enables drop targets for draggable elements. 2343 //>>docs: http://api.jqueryui.com/droppable/ 2344 //>>demos: http://jqueryui.com/droppable/ 2345 2346 2347 2348 $.widget( "ui.droppable", { 2349 version: "1.12.1", 2350 widgetEventPrefix: "drop", 2351 options: { 2352 accept: "*", 2353 addClasses: true, 2354 greedy: false, 2355 scope: "default", 2356 tolerance: "intersect", 2357 2358 // Callbacks 2359 activate: null, 2360 deactivate: null, 2361 drop: null, 2362 out: null, 2363 over: null 2364 }, 2365 _create: function() { 2366 2367 var proportions, 2368 o = this.options, 2369 accept = o.accept; 2370 2371 this.isover = false; 2372 this.isout = true; 2373 2374 this.accept = $.isFunction( accept ) ? accept : function( d ) { 2375 return d.is( accept ); 2376 }; 2377 2378 this.proportions = function( /* valueToWrite */ ) { 2379 if ( arguments.length ) { 2380 2381 // Store the droppable's proportions 2382 proportions = arguments[ 0 ]; 2383 } else { 2384 2385 // Retrieve or derive the droppable's proportions 2386 return proportions ? 2387 proportions : 2388 proportions = { 2389 width: this.element[ 0 ].offsetWidth, 2390 height: this.element[ 0 ].offsetHeight 2391 }; 2392 } 2393 }; 2394 2395 this._addToManager( o.scope ); 2396 2397 o.addClasses && this._addClass( "ui-droppable" ); 2398 2399 }, 2400 2401 _addToManager: function( scope ) { 2402 2403 // Add the reference and positions to the manager 2404 $.ui.ddmanager.droppables[ scope ] = $.ui.ddmanager.droppables[ scope ] || []; 2405 $.ui.ddmanager.droppables[ scope ].push( this ); 2406 }, 2407 2408 _splice: function( drop ) { 2409 var i = 0; 2410 for ( ; i < drop.length; i++ ) { 2411 if ( drop[ i ] === this ) { 2412 drop.splice( i, 1 ); 2413 } 2414 } 2415 }, 2416 2417 _destroy: function() { 2418 var drop = $.ui.ddmanager.droppables[ this.options.scope ]; 2419 2420 this._splice( drop ); 2421 }, 2422 2423 _setOption: function( key, value ) { 2424 2425 if ( key === "accept" ) { 2426 this.accept = $.isFunction( value ) ? value : function( d ) { 2427 return d.is( value ); 2428 }; 2429 } else if ( key === "scope" ) { 2430 var drop = $.ui.ddmanager.droppables[ this.options.scope ]; 2431 2432 this._splice( drop ); 2433 this._addToManager( value ); 2434 } 2435 2436 this._super( key, value ); 2437 }, 2438 2439 _activate: function( event ) { 2440 var draggable = $.ui.ddmanager.current; 2441 2442 this._addActiveClass(); 2443 if ( draggable ) { 2444 this._trigger( "activate", event, this.ui( draggable ) ); 2445 } 2446 }, 2447 2448 _deactivate: function( event ) { 2449 var draggable = $.ui.ddmanager.current; 2450 2451 this._removeActiveClass(); 2452 if ( draggable ) { 2453 this._trigger( "deactivate", event, this.ui( draggable ) ); 2454 } 2455 }, 2456 2457 _over: function( event ) { 2458 2459 var draggable = $.ui.ddmanager.current; 2460 2461 // Bail if draggable and droppable are same element 2462 if ( !draggable || ( draggable.currentItem || 2463 draggable.element )[ 0 ] === this.element[ 0 ] ) { 2464 return; 2465 } 2466 2467 if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || 2468 draggable.element ) ) ) { 2469 this._addHoverClass(); 2470 this._trigger( "over", event, this.ui( draggable ) ); 2471 } 2472 2473 }, 2474 2475 _out: function( event ) { 2476 2477 var draggable = $.ui.ddmanager.current; 2478 2479 // Bail if draggable and droppable are same element 2480 if ( !draggable || ( draggable.currentItem || 2481 draggable.element )[ 0 ] === this.element[ 0 ] ) { 2482 return; 2483 } 2484 2485 if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || 2486 draggable.element ) ) ) { 2487 this._removeHoverClass(); 2488 this._trigger( "out", event, this.ui( draggable ) ); 2489 } 2490 2491 }, 2492 2493 _drop: function( event, custom ) { 2494 2495 var draggable = custom || $.ui.ddmanager.current, 2496 childrenIntersection = false; 2497 2498 // Bail if draggable and droppable are same element 2499 if ( !draggable || ( draggable.currentItem || 2500 draggable.element )[ 0 ] === this.element[ 0 ] ) { 2501 return false; 2502 } 2503 2504 this.element 2505 .find( ":data(ui-droppable)" ) 2506 .not( ".ui-draggable-dragging" ) 2507 .each( function() { 2508 var inst = $( this ).droppable( "instance" ); 2509 if ( 2510 inst.options.greedy && 2511 !inst.options.disabled && 2512 inst.options.scope === draggable.options.scope && 2513 inst.accept.call( 2514 inst.element[ 0 ], ( draggable.currentItem || draggable.element ) 2515 ) && 2516 intersect( 2517 draggable, 2518 $.extend( inst, { offset: inst.element.offset() } ), 2519 inst.options.tolerance, event 2520 ) 2521 ) { 2522 childrenIntersection = true; 2523 return false; } 2524 } ); 2525 if ( childrenIntersection ) { 2526 return false; 2527 } 2528 2529 if ( this.accept.call( this.element[ 0 ], 2530 ( draggable.currentItem || draggable.element ) ) ) { 2531 this._removeActiveClass(); 2532 this._removeHoverClass(); 2533 2534 this._trigger( "drop", event, this.ui( draggable ) ); 2535 return this.element; 2536 } 2537 2538 return false; 2539 2540 }, 2541 2542 ui: function( c ) { 2543 return { 2544 draggable: ( c.currentItem || c.element ), 2545 helper: c.helper, 2546 position: c.position, 2547 offset: c.positionAbs 2548 }; 2549 }, 2550 2551 // Extension points just to make backcompat sane and avoid duplicating logic 2552 // TODO: Remove in 1.13 along with call to it below 2553 _addHoverClass: function() { 2554 this._addClass( "ui-droppable-hover" ); 2555 }, 2556 2557 _removeHoverClass: function() { 2558 this._removeClass( "ui-droppable-hover" ); 2559 }, 2560 2561 _addActiveClass: function() { 2562 this._addClass( "ui-droppable-active" ); 2563 }, 2564 2565 _removeActiveClass: function() { 2566 this._removeClass( "ui-droppable-active" ); 2567 } 2568 } ); 2569 2570 var intersect = $.ui.intersect = ( function() { 2571 function isOverAxis( x, reference, size ) { 2572 return ( x >= reference ) && ( x < ( reference + size ) ); 2573 } 2574 2575 return function( draggable, droppable, toleranceMode, event ) { 2576 2577 if ( !droppable.offset ) { 2578 return false; 2579 } 2580 2581 var x1 = ( draggable.positionAbs || 2582 draggable.position.absolute ).left + draggable.margins.left, 2583 y1 = ( draggable.positionAbs || 2584 draggable.position.absolute ).top + draggable.margins.top, 2585 x2 = x1 + draggable.helperProportions.width, 2586 y2 = y1 + draggable.helperProportions.height, 2587 l = droppable.offset.left, 2588 t = droppable.offset.top, 2589 r = l + droppable.proportions().width, 2590 b = t + droppable.proportions().height; 2591 2592 switch ( toleranceMode ) { 2593 case "fit": 2594 return ( l <= x1 && x2 <= r && t <= y1 && y2 <= b ); 2595 case "intersect": 2596 return ( l < x1 + ( draggable.helperProportions.width / 2 ) && // Right Half 2597 x2 - ( draggable.helperProportions.width / 2 ) < r && // Left Half 2598 t < y1 + ( draggable.helperProportions.height / 2 ) && // Bottom Half 2599 y2 - ( draggable.helperProportions.height / 2 ) < b ); // Top Half 2600 case "pointer": 2601 return isOverAxis( event.pageY, t, droppable.proportions().height ) && 2602 isOverAxis( event.pageX, l, droppable.proportions().width ); 2603 case "touch": 2604 return ( 2605 ( y1 >= t && y1 <= b ) || // Top edge touching 2606 ( y2 >= t && y2 <= b ) || // Bottom edge touching 2607 ( y1 < t && y2 > b ) // Surrounded vertically 2608 ) && ( 2609 ( x1 >= l && x1 <= r ) || // Left edge touching 2610 ( x2 >= l && x2 <= r ) || // Right edge touching 2611 ( x1 < l && x2 > r ) // Surrounded horizontally 2612 ); 2613 default: 2614 return false; 2615 } 2616 }; 2617 } )(); 2618 2619 /* 2620 This manager tracks offsets of draggables and droppables 2621 */ 2622 $.ui.ddmanager = { 2623 current: null, 2624 droppables: { "default": [] }, 2625 prepareOffsets: function( t, event ) { 2626 2627 var i, j, 2628 m = $.ui.ddmanager.droppables[ t.options.scope ] || [], 2629 type = event ? event.type : null, // workaround for #2317 2630 list = ( t.currentItem || t.element ).find( ":data(ui-droppable)" ).addBack(); 2631 2632 droppablesLoop: for ( i = 0; i < m.length; i++ ) { 2633 2634 // No disabled and non-accepted 2635 if ( m[ i ].options.disabled || ( t && !m[ i ].accept.call( m[ i ].element[ 0 ], 2636 ( t.currentItem || t.element ) ) ) ) { 2637 continue; 2638 } 2639 2640 // Filter out elements in the current dragged item 2641 for ( j = 0; j < list.length; j++ ) { 2642 if ( list[ j ] === m[ i ].element[ 0 ] ) { 2643 m[ i ].proportions().height = 0; 2644 continue droppablesLoop; 2645 } 2646 } 2647 2648 m[ i ].visible = m[ i ].element.css( "display" ) !== "none"; 2649 if ( !m[ i ].visible ) { 2650 continue; 2651 } 2652 2653 // Activate the droppable if used directly from draggables 2654 if ( type === "mousedown" ) { 2655 m[ i ]._activate.call( m[ i ], event ); 2656 } 2657 2658 m[ i ].offset = m[ i ].element.offset(); 2659 m[ i ].proportions( { 2660 width: m[ i ].element[ 0 ].offsetWidth, 2661 height: m[ i ].element[ 0 ].offsetHeight 2662 } ); 2663 2664 } 2665 2666 }, 2667 drop: function( draggable, event ) { 2668 2669 var dropped = false; 2670 2671 // Create a copy of the droppables in case the list changes during the drop (#9116) 2672 $.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), function() { 2673 2674 if ( !this.options ) { 2675 return; 2676 } 2677 if ( !this.options.disabled && this.visible && 2678 intersect( draggable, this, this.options.tolerance, event ) ) { 2679 dropped = this._drop.call( this, event ) || dropped; 2680 } 2681 2682 if ( !this.options.disabled && this.visible && this.accept.call( this.element[ 0 ], 2683 ( draggable.currentItem || draggable.element ) ) ) { 2684 this.isout = true; 2685 this.isover = false; 2686 this._deactivate.call( this, event ); 2687 } 2688 2689 } ); 2690 return dropped; 2691 2692 }, 2693 dragStart: function( draggable, event ) { 2694 2695 // Listen for scrolling so that if the dragging causes scrolling the position of the 2696 // droppables can be recalculated (see #5003) 2697 draggable.element.parentsUntil( "body" ).on( "scroll.droppable", function() { 2698 if ( !draggable.options.refreshPositions ) { 2699 $.ui.ddmanager.prepareOffsets( draggable, event ); 2700 } 2701 } ); 2702 }, 2703 drag: function( draggable, event ) { 2704 2705 // If you have a highly dynamic page, you might try this option. It renders positions 2706 // every time you move the mouse. 2707 if ( draggable.options.refreshPositions ) { 2708 $.ui.ddmanager.prepareOffsets( draggable, event ); 2709 } 2710 2711 // Run through all droppables and check their positions based on specific tolerance options 2712 $.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() { 2713 2714 if ( this.options.disabled || this.greedyChild || !this.visible ) { 2715 return; 2716 } 2717 2718 var parentInstance, scope, parent, 2719 intersects = intersect( draggable, this, this.options.tolerance, event ), 2720 c = !intersects && this.isover ? 2721 "isout" : 2722 ( intersects && !this.isover ? "isover" : null ); 2723 if ( !c ) { 2724 return; 2725 } 2726 2727 if ( this.options.greedy ) { 2728 2729 // find droppable parents with same scope 2730 scope = this.options.scope; 2731 parent = this.element.parents( ":data(ui-droppable)" ).filter( function() { 2732 return $( this ).droppable( "instance" ).options.scope === scope; 2733 } ); 2734 2735 if ( parent.length ) { 2736 parentInstance = $( parent[ 0 ] ).droppable( "instance" ); 2737 parentInstance.greedyChild = ( c === "isover" ); 2738 } 2739 } 2740 2741 // We just moved into a greedy child 2742 if ( parentInstance && c === "isover" ) { 2743 parentInstance.isover = false; 2744 parentInstance.isout = true; 2745 parentInstance._out.call( parentInstance, event ); 2746 } 2747 2748 this[ c ] = true; 2749 this[ c === "isout" ? "isover" : "isout" ] = false; 2750 this[ c === "isover" ? "_over" : "_out" ].call( this, event ); 2751 2752 // We just moved out of a greedy child 2753 if ( parentInstance && c === "isout" ) { 2754 parentInstance.isout = false; 2755 parentInstance.isover = true; 2756 parentInstance._over.call( parentInstance, event ); 2757 } 2758 } ); 2759 2760 }, 2761 dragStop: function( draggable, event ) { 2762 draggable.element.parentsUntil( "body" ).off( "scroll.droppable" ); 2763 2764 // Call prepareOffsets one final time since IE does not fire return scroll events when 2765 // overflow was caused by drag (see #5003) 2766 if ( !draggable.options.refreshPositions ) { 2767 $.ui.ddmanager.prepareOffsets( draggable, event ); 2768 } 2769 } 2770 }; 2771 2772 // DEPRECATED 2773 // TODO: switch return back to widget declaration at top of file when this is removed 2774 if ( $.uiBackCompat !== false ) { 2775 2776 // Backcompat for activeClass and hoverClass options 2777 $.widget( "ui.droppable", $.ui.droppable, { 2778 options: { 2779 hoverClass: false, 2780 activeClass: false 2781 }, 2782 _addActiveClass: function() { 2783 this._super(); 2784 if ( this.options.activeClass ) { 2785 this.element.addClass( this.options.activeClass ); 2786 } 2787 }, 2788 _removeActiveClass: function() { 2789 this._super(); 2790 if ( this.options.activeClass ) { 2791 this.element.removeClass( this.options.activeClass ); 2792 } 2793 }, 2794 _addHoverClass: function() { 2795 this._super(); 2796 if ( this.options.hoverClass ) { 2797 this.element.addClass( this.options.hoverClass ); 2798 } 2799 }, 2800 _removeHoverClass: function() { 2801 this._super(); 2802 if ( this.options.hoverClass ) { 2803 this.element.removeClass( this.options.hoverClass ); 2804 } 2805 } 2806 } ); 2807 } 2808 2809 var widgetsDroppable = $.ui.droppable; 2810 2811 2812 /*! 2813 * jQuery UI Sortable 1.12.1 2814 * http://jqueryui.com 2815 * 2816 * Copyright jQuery Foundation and other contributors 2817 * Released under the MIT license. 2818 * http://jquery.org/license 2819 */ 2820 2821 //>>label: Sortable 2822 //>>group: Interactions 2823 //>>description: Enables items in a list to be sorted using the mouse. 2824 //>>docs: http://api.jqueryui.com/sortable/ 2825 //>>demos: http://jqueryui.com/sortable/ 2826 //>>css.structure: ../../themes/base/sortable.css 2827 2828 2829 2830 var widgetsSortable = $.widget( "ui.sortable", $.ui.mouse, { 2831 version: "1.12.1", 2832 widgetEventPrefix: "sort", 2833 ready: false, 2834 options: { 2835 appendTo: "parent", 2836 axis: false, 2837 connectWith: false, 2838 containment: false, 2839 cursor: "auto", 2840 cursorAt: false, 2841 dropOnEmpty: true, 2842 forcePlaceholderSize: false, 2843 forceHelperSize: false, 2844 grid: false, 2845 handle: false, 2846 helper: "original", 2847 items: "> *", 2848 opacity: false, 2849 placeholder: false, 2850 revert: false, 2851 scroll: true, 2852 scrollSensitivity: 20, 2853 scrollSpeed: 20, 2854 scope: "default", 2855 tolerance: "intersect", 2856 zIndex: 1000, 2857 2858 // Callbacks 2859 activate: null, 2860 beforeStop: null, 2861 change: null, 2862 deactivate: null, 2863 out: null, 2864 over: null, 2865 receive: null, 2866 remove: null, 2867 sort: null, 2868 start: null, 2869 stop: null, 2870 update: null 2871 }, 2872 2873 _isOverAxis: function( x, reference, size ) { 2874 return ( x >= reference ) && ( x < ( reference + size ) ); 2875 }, 2876 2877 _isFloating: function( item ) { 2878 return ( /left|right/ ).test( item.css( "float" ) ) || 2879 ( /inline|table-cell/ ).test( item.css( "display" ) ); 2880 }, 2881 2882 _create: function() { 2883 this.containerCache = {}; 2884 this._addClass( "ui-sortable" ); 2885 2886 //Get the items 2887 this.refresh(); 2888 2889 //Let's determine the parent's offset 2890 this.offset = this.element.offset(); 2891 2892 //Initialize mouse events for interaction 2893 this._mouseInit(); 2894 2895 this._setHandleClassName(); 2896 2897 //We're ready to go 2898 this.ready = true; 2899 2900 }, 2901 2902 _setOption: function( key, value ) { 2903 this._super( key, value ); 2904 2905 if ( key === "handle" ) { 2906 this._setHandleClassName(); 2907 } 2908 }, 2909 2910 _setHandleClassName: function() { 2911 var that = this; 2912 this._removeClass( this.element.find( ".ui-sortable-handle" ), "ui-sortable-handle" ); 2913 $.each( this.items, function() { 2914 that._addClass( 2915 this.instance.options.handle ? 2916 this.item.find( this.instance.options.handle ) : 2917 this.item, 2918 "ui-sortable-handle" 2919 ); 2920 } ); 2921 }, 2922 2923 _destroy: function() { 2924 this._mouseDestroy(); 2925 2926 for ( var i = this.items.length - 1; i >= 0; i-- ) { 2927 this.items[ i ].item.removeData( this.widgetName + "-item" ); 2928 } 2929 2930 return this; 2931 }, 2932 2933 _mouseCapture: function( event, overrideHandle ) { 2934 var currentItem = null, 2935 validHandle = false, 2936 that = this; 2937 2938 if ( this.reverting ) { 2939 return false; 2940 } 2941 2942 if ( this.options.disabled || this.options.type === "static" ) { 2943 return false; 2944 } 2945 2946 //We have to refresh the items data once first 2947 this._refreshItems( event ); 2948 2949 //Find out if the clicked node (or one of its parents) is a actual item in this.items 2950 $( event.target ).parents().each( function() { 2951 if ( $.data( this, that.widgetName + "-item" ) === that ) { 2952 currentItem = $( this ); 2953 return false; 2954 } 2955 } ); 2956 if ( $.data( event.target, that.widgetName + "-item" ) === that ) { 2957 currentItem = $( event.target ); 2958 } 2959 2960 if ( !currentItem ) { 2961 return false; 2962 } 2963 if ( this.options.handle && !overrideHandle ) { 2964 $( this.options.handle, currentItem ).find( "*" ).addBack().each( function() { 2965 if ( this === event.target ) { 2966 validHandle = true; 2967 } 2968 } ); 2969 if ( !validHandle ) { 2970 return false; 2971 } 2972 } 2973 2974 this.currentItem = currentItem; 2975 this._removeCurrentsFromItems(); 2976 return true; 2977 2978 }, 2979 2980 _mouseStart: function( event, overrideHandle, noActivation ) { 2981 2982 var i, body, 2983 o = this.options; 2984 2985 this.currentContainer = this; 2986 2987 //We only need to call refreshPositions, because the refreshItems call has been moved to 2988 // mouseCapture 2989 this.refreshPositions(); 2990 2991 //Create and append the visible helper 2992 this.helper = this._createHelper( event ); 2993 2994 //Cache the helper size 2995 this._cacheHelperProportions(); 2996 2997 /* 2998 * - Position generation - 2999 * This block generates everything position related - it's the core of draggables. 3000 */ 3001 3002 //Cache the margins of the original element 3003 this._cacheMargins(); 3004 3005 //Get the next scrolling parent 3006 this.scrollParent = this.helper.scrollParent(); 3007 3008 //The element's absolute position on the page minus margins 3009 this.offset = this.currentItem.offset(); 3010 this.offset = { 3011 top: this.offset.top - this.margins.top, 3012 left: this.offset.left - this.margins.left 3013 }; 3014 3015 $.extend( this.offset, { 3016 click: { //Where the click happened, relative to the element 3017 left: event.pageX - this.offset.left, 3018 top: event.pageY - this.offset.top 3019 }, 3020 parent: this._getParentOffset(), 3021 3022 // This is a relative to absolute position minus the actual position calculation - 3023 // only used for relative positioned helper 3024 relative: this._getRelativeOffset() 3025 } ); 3026 3027 // Only after we got the offset, we can change the helper's position to absolute 3028 // TODO: Still need to figure out a way to make relative sorting possible 3029 this.helper.css( "position", "absolute" ); 3030 this.cssPosition = this.helper.css( "position" ); 3031 3032 //Generate the original position 3033 this.originalPosition = this._generatePosition( event ); 3034 this.originalPageX = event.pageX; 3035 this.originalPageY = event.pageY; 3036 3037 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied 3038 ( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) ); 3039 3040 //Cache the former DOM position 3041 this.domPosition = { 3042 prev: this.currentItem.prev()[ 0 ], 3043 parent: this.currentItem.parent()[ 0 ] 3044 }; 3045 3046 // If the helper is not the original, hide the original so it's not playing any role during 3047 // the drag, won't cause anything bad this way 3048 if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) { 3049 this.currentItem.hide(); 3050 } 3051 3052 //Create the placeholder 3053 this._createPlaceholder(); 3054 3055 //Set a containment if given in the options 3056 if ( o.containment ) { 3057 this._setContainment(); 3058 } 3059 3060 if ( o.cursor && o.cursor !== "auto" ) { // cursor option 3061 body = this.document.find( "body" ); 3062 3063 // Support: IE 3064 this.storedCursor = body.css( "cursor" ); 3065 body.css( "cursor", o.cursor ); 3066 3067 this.storedStylesheet = 3068 $( "<style>*{ cursor: " + o.cursor + " !important; }</style>" ).appendTo( body ); 3069 } 3070 3071 if ( o.opacity ) { // opacity option 3072 if ( this.helper.css( "opacity" ) ) { 3073 this._storedOpacity = this.helper.css( "opacity" ); 3074 } 3075 this.helper.css( "opacity", o.opacity ); 3076 } 3077 3078 if ( o.zIndex ) { // zIndex option 3079 if ( this.helper.css( "zIndex" ) ) { 3080 this._storedZIndex = this.helper.css( "zIndex" ); 3081 } 3082 this.helper.css( "zIndex", o.zIndex ); 3083 } 3084 3085 //Prepare scrolling 3086 if ( this.scrollParent[ 0 ] !== this.document[ 0 ] && 3087 this.scrollParent[ 0 ].tagName !== "HTML" ) { 3088 this.overflowOffset = this.scrollParent.offset(); 3089 } 3090 3091 //Call callbacks 3092 this._trigger( "start", event, this._uiHash() ); 3093 3094 //Recache the helper size 3095 if ( !this._preserveHelperProportions ) { 3096 this._cacheHelperProportions(); 3097 } 3098 3099 //Post "activate" events to possible containers 3100 if ( !noActivation ) { 3101 for ( i = this.containers.length - 1; i >= 0; i-- ) { 3102 this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) ); 3103 } 3104 } 3105 3106 //Prepare possible droppables 3107 if ( $.ui.ddmanager ) { 3108 $.ui.ddmanager.current = this; 3109 } 3110 3111 if ( $.ui.ddmanager && !o.dropBehaviour ) { 3112 $.ui.ddmanager.prepareOffsets( this, event ); 3113 } 3114 3115 this.dragging = true; 3116 3117 this._addClass( this.helper, "ui-sortable-helper" ); 3118 3119 // Execute the drag once - this causes the helper not to be visiblebefore getting its 3120 // correct position 3121 this._mouseDrag( event ); 3122 return true; 3123 3124 }, 3125 3126 _mouseDrag: function( event ) { 3127 var i, item, itemElement, intersection, 3128 o = this.options, 3129 scrolled = false; 3130 3131 //Compute the helpers position 3132 this.position = this._generatePosition( event ); 3133 this.positionAbs = this._convertPositionTo( "absolute" ); 3134 3135 if ( !this.lastPositionAbs ) { 3136 this.lastPositionAbs = this.positionAbs; 3137 } 3138 3139 //Do scrolling 3140 if ( this.options.scroll ) { 3141 if ( this.scrollParent[ 0 ] !== this.document[ 0 ] && 3142 this.scrollParent[ 0 ].tagName !== "HTML" ) { 3143 3144 if ( ( this.overflowOffset.top + this.scrollParent[ 0 ].offsetHeight ) - 3145 event.pageY < o.scrollSensitivity ) { 3146 this.scrollParent[ 0 ].scrollTop = 3147 scrolled = this.scrollParent[ 0 ].scrollTop + o.scrollSpeed; 3148 } else if ( event.pageY - this.overflowOffset.top < o.scrollSensitivity ) { 3149 this.scrollParent[ 0 ].scrollTop = 3150 scrolled = this.scrollParent[ 0 ].scrollTop - o.scrollSpeed; 3151 } 3152 3153 if ( ( this.overflowOffset.left + this.scrollParent[ 0 ].offsetWidth ) - 3154 event.pageX < o.scrollSensitivity ) { 3155 this.scrollParent[ 0 ].scrollLeft = scrolled = 3156 this.scrollParent[ 0 ].scrollLeft + o.scrollSpeed; 3157 } else if ( event.pageX - this.overflowOffset.left < o.scrollSensitivity ) { 3158 this.scrollParent[ 0 ].scrollLeft = scrolled = 3159 this.scrollParent[ 0 ].scrollLeft - o.scrollSpeed; 3160 } 3161 3162 } else { 3163 3164 if ( event.pageY - this.document.scrollTop() < o.scrollSensitivity ) { 3165 scrolled = this.document.scrollTop( this.document.scrollTop() - o.scrollSpeed ); 3166 } else if ( this.window.height() - ( event.pageY - this.document.scrollTop() ) < 3167 o.scrollSensitivity ) { 3168 scrolled = this.document.scrollTop( this.document.scrollTop() + o.scrollSpeed ); 3169 } 3170 3171 if ( event.pageX - this.document.scrollLeft() < o.scrollSensitivity ) { 3172 scrolled = this.document.scrollLeft( 3173 this.document.scrollLeft() - o.scrollSpeed 3174 ); 3175 } else if ( this.window.width() - ( event.pageX - this.document.scrollLeft() ) < 3176 o.scrollSensitivity ) { 3177 scrolled = this.document.scrollLeft( 3178 this.document.scrollLeft() + o.scrollSpeed 3179 ); 3180 } 3181 3182 } 3183 3184 if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) { 3185 $.ui.ddmanager.prepareOffsets( this, event ); 3186 } 3187 } 3188 3189 //Regenerate the absolute position used for position checks 3190 this.positionAbs = this._convertPositionTo( "absolute" ); 3191 3192 //Set the helper position 3193 if ( !this.options.axis || this.options.axis !== "y" ) { 3194 this.helper[ 0 ].style.left = this.position.left + "px"; 3195 } 3196 if ( !this.options.axis || this.options.axis !== "x" ) { 3197 this.helper[ 0 ].style.top = this.position.top + "px"; 3198 } 3199 3200 //Rearrange 3201 for ( i = this.items.length - 1; i >= 0; i-- ) { 3202 3203 //Cache variables and intersection, continue if no intersection 3204 item = this.items[ i ]; 3205 itemElement = item.item[ 0 ]; 3206 intersection = this._intersectsWithPointer( item ); 3207 if ( !intersection ) { 3208 continue; 3209 } 3210 3211 // Only put the placeholder inside the current Container, skip all 3212 // items from other containers. This works because when moving 3213 // an item from one container to another the 3214 // currentContainer is switched before the placeholder is moved. 3215 // 3216 // Without this, moving items in "sub-sortables" can cause 3217 // the placeholder to jitter between the outer and inner container. 3218 if ( item.instance !== this.currentContainer ) { 3219 continue; 3220 } 3221 3222 // Cannot intersect with itself 3223 // no useless actions that have been done before 3224 // no action if the item moved is the parent of the item checked 3225 if ( itemElement !== this.currentItem[ 0 ] && 3226 this.placeholder[ intersection === 1 ? "next" : "prev" ]()[ 0 ] !== itemElement && 3227 !$.contains( this.placeholder[ 0 ], itemElement ) && 3228 ( this.options.type === "semi-dynamic" ? 3229 !$.contains( this.element[ 0 ], itemElement ) : 3230 true 3231 ) 3232 ) { 3233 3234 this.direction = intersection === 1 ? "down" : "up"; 3235 3236 if ( this.options.tolerance === "pointer" || this._intersectsWithSides( item ) ) { 3237 this._rearrange( event, item ); 3238 } else { 3239 break; 3240 } 3241 3242 this._trigger( "change", event, this._uiHash() ); 3243 break; 3244 } 3245 } 3246 3247 //Post events to containers 3248 this._contactContainers( event ); 3249 3250 //Interconnect with droppables 3251 if ( $.ui.ddmanager ) { 3252 $.ui.ddmanager.drag( this, event ); 3253 } 3254 3255 //Call callbacks 3256 this._trigger( "sort", event, this._uiHash() ); 3257 3258 this.lastPositionAbs = this.positionAbs; 3259 return false; 3260 3261 }, 3262 3263 _mouseStop: function( event, noPropagation ) { 3264 3265 if ( !event ) { 3266 return; 3267 } 3268 3269 //If we are using droppables, inform the manager about the drop 3270 if ( $.ui.ddmanager && !this.options.dropBehaviour ) { 3271 $.ui.ddmanager.drop( this, event ); 3272 } 3273 3274 if ( this.options.revert ) { 3275 var that = this, 3276 cur = this.placeholder.offset(), 3277 axis = this.options.axis, 3278 animation = {}; 3279 3280 if ( !axis || axis === "x" ) { 3281 animation.left = cur.left - this.offset.parent.left - this.margins.left + 3282 ( this.offsetParent[ 0 ] === this.document[ 0 ].body ? 3283 0 : 3284 this.offsetParent[ 0 ].scrollLeft 3285 ); 3286 } 3287 if ( !axis || axis === "y" ) { 3288 animation.top = cur.top - this.offset.parent.top - this.margins.top + 3289 ( this.offsetParent[ 0 ] === this.document[ 0 ].body ? 3290 0 : 3291 this.offsetParent[ 0 ].scrollTop 3292 ); 3293 } 3294 this.reverting = true; 3295 $( this.helper ).animate( 3296 animation, 3297 parseInt( this.options.revert, 10 ) || 500, 3298 function() { 3299 that._clear( event ); 3300 } 3301 ); 3302 } else { 3303 this._clear( event, noPropagation ); 3304 } 3305 3306 return false; 3307 3308 }, 3309 3310 cancel: function() { 3311 3312 if ( this.dragging ) { 3313 3314 this._mouseUp( new $.Event( "mouseup", { target: null } ) ); 3315 3316 if ( this.options.helper === "original" ) { 3317 this.currentItem.css( this._storedCSS ); 3318 this._removeClass( this.currentItem, "ui-sortable-helper" ); 3319 } else { 3320 this.currentItem.show(); 3321 } 3322 3323 //Post deactivating events to containers 3324 for ( var i = this.containers.length - 1; i >= 0; i-- ) { 3325 this.containers[ i ]._trigger( "deactivate", null, this._uiHash( this ) ); 3326 if ( this.containers[ i ].containerCache.over ) { 3327 this.containers[ i ]._trigger( "out", null, this._uiHash( this ) ); 3328 this.containers[ i ].containerCache.over = 0; 3329 } 3330 } 3331 3332 } 3333 3334 if ( this.placeholder ) { 3335 3336 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, 3337 // it unbinds ALL events from the original node! 3338 if ( this.placeholder[ 0 ].parentNode ) { 3339 this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] ); 3340 } 3341 if ( this.options.helper !== "original" && this.helper && 3342 this.helper[ 0 ].parentNode ) { 3343 this.helper.remove(); 3344 } 3345 3346 $.extend( this, { 3347 helper: null, 3348 dragging: false, 3349 reverting: false, 3350 _noFinalSort: null 3351 } ); 3352 3353 if ( this.domPosition.prev ) { 3354 $( this.domPosition.prev ).after( this.currentItem ); 3355 } else { 3356 $( this.domPosition.parent ).prepend( this.currentItem ); 3357 } 3358 } 3359 3360 return this; 3361 3362 }, 3363 3364 serialize: function( o ) { 3365 3366 var items = this._getItemsAsjQuery( o && o.connected ), 3367 str = []; 3368 o = o || {}; 3369 3370 $( items ).each( function() { 3371 var res = ( $( o.item || this ).attr( o.attribute || "id" ) || "" ) 3372 .match( o.expression || ( /(.+)[\-=_](.+)/ ) ); 3373 if ( res ) { 3374 str.push( 3375 ( o.key || res[ 1 ] + "[]" ) + 3376 "=" + ( o.key && o.expression ? res[ 1 ] : res[ 2 ] ) ); 3377 } 3378 } ); 3379 3380 if ( !str.length && o.key ) { 3381 str.push( o.key + "=" ); 3382 } 3383 3384 return str.join( "&" ); 3385 3386 }, 3387 3388 toArray: function( o ) { 3389 3390 var items = this._getItemsAsjQuery( o && o.connected ), 3391 ret = []; 3392 3393 o = o || {}; 3394 3395 items.each( function() { 3396 ret.push( $( o.item || this ).attr( o.attribute || "id" ) || "" ); 3397 } ); 3398 return ret; 3399 3400 }, 3401 3402 /* Be careful with the following core functions */ 3403 _intersectsWith: function( item ) { 3404 3405 var x1 = this.positionAbs.left, 3406 x2 = x1 + this.helperProportions.width, 3407 y1 = this.positionAbs.top, 3408 y2 = y1 + this.helperProportions.height, 3409 l = item.left, 3410 r = l + item.width, 3411 t = item.top, 3412 b = t + item.height, 3413 dyClick = this.offset.click.top, 3414 dxClick = this.offset.click.left, 3415 isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t && 3416 ( y1 + dyClick ) < b ), 3417 isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l && 3418 ( x1 + dxClick ) < r ), 3419 isOverElement = isOverElementHeight && isOverElementWidth; 3420 3421 if ( this.options.tolerance === "pointer" || 3422 this.options.forcePointerForContainers || 3423 ( this.options.tolerance !== "pointer" && 3424 this.helperProportions[ this.floating ? "width" : "height" ] > 3425 item[ this.floating ? "width" : "height" ] ) 3426 ) { 3427 return isOverElement; 3428 } else { 3429 3430 return ( l < x1 + ( this.helperProportions.width / 2 ) && // Right Half 3431 x2 - ( this.helperProportions.width / 2 ) < r && // Left Half 3432 t < y1 + ( this.helperProportions.height / 2 ) && // Bottom Half 3433 y2 - ( this.helperProportions.height / 2 ) < b ); // Top Half 3434 3435 } 3436 }, 3437 3438 _intersectsWithPointer: function( item ) { 3439 var verticalDirection, horizontalDirection, 3440 isOverElementHeight = ( this.options.axis === "x" ) || 3441 this._isOverAxis( 3442 this.positionAbs.top + this.offset.click.top, item.top, item.height ), 3443 isOverElementWidth = ( this.options.axis === "y" ) || 3444 this._isOverAxis( 3445 this.positionAbs.left + this.offset.click.left, item.left, item.width ), 3446 isOverElement = isOverElementHeight && isOverElementWidth; 3447 3448 if ( !isOverElement ) { 3449 return false; 3450 } 3451 3452 verticalDirection = this._getDragVerticalDirection(); 3453 horizontalDirection = this._getDragHorizontalDirection(); 3454 3455 return this.floating ? 3456 ( ( horizontalDirection === "right" || verticalDirection === "down" ) ? 2 : 1 ) 3457 : ( verticalDirection && ( verticalDirection === "down" ? 2 : 1 ) ); 3458 3459 }, 3460 3461 _intersectsWithSides: function( item ) { 3462 3463 var isOverBottomHalf = this._isOverAxis( this.positionAbs.top + 3464 this.offset.click.top, item.top + ( item.height / 2 ), item.height ), 3465 isOverRightHalf = this._isOverAxis( this.positionAbs.left + 3466 this.offset.click.left, item.left + ( item.width / 2 ), item.width ), 3467 verticalDirection = this._getDragVerticalDirection(), 3468 horizontalDirection = this._getDragHorizontalDirection(); 3469 3470 if ( this.floating && horizontalDirection ) { 3471 return ( ( horizontalDirection === "right" && isOverRightHalf ) || 3472 ( horizontalDirection === "left" && !isOverRightHalf ) ); 3473 } else { 3474 return verticalDirection && ( ( verticalDirection === "down" && isOverBottomHalf ) || 3475 ( verticalDirection === "up" && !isOverBottomHalf ) ); 3476 } 3477 3478 }, 3479 3480 _getDragVerticalDirection: function() { 3481 var delta = this.positionAbs.top - this.lastPositionAbs.top; 3482 return delta !== 0 && ( delta > 0 ? "down" : "up" ); 3483 }, 3484 3485 _getDragHorizontalDirection: function() { 3486 var delta = this.positionAbs.left - this.lastPositionAbs.left; 3487 return delta !== 0 && ( delta > 0 ? "right" : "left" ); 3488 }, 3489 3490 refresh: function( event ) { 3491 this._refreshItems( event ); 3492 this._setHandleClassName(); 3493 this.refreshPositions(); 3494 return this; 3495 }, 3496 3497 _connectWith: function() { 3498 var options = this.options; 3499 return options.connectWith.constructor === String ? 3500 [ options.connectWith ] : 3501 options.connectWith; 3502 }, 3503 3504 _getItemsAsjQuery: function( connected ) { 3505 3506 var i, j, cur, inst, 3507 items = [], 3508 queries = [], 3509 connectWith = this._connectWith(); 3510 3511 if ( connectWith && connected ) { 3512 for ( i = connectWith.length - 1; i >= 0; i-- ) { 3513 cur = $( connectWith[ i ], this.document[ 0 ] ); 3514 for ( j = cur.length - 1; j >= 0; j-- ) { 3515 inst = $.data( cur[ j ], this.widgetFullName ); 3516 if ( inst && inst !== this && !inst.options.disabled ) { 3517 queries.push( [ $.isFunction( inst.options.items ) ? 3518 inst.options.items.call( inst.element ) : 3519 $( inst.options.items, inst.element ) 3520 .not( ".ui-sortable-helper" ) 3521 .not( ".ui-sortable-placeholder" ), inst ] ); 3522 } 3523 } 3524 } 3525 } 3526 3527 queries.push( [ $.isFunction( this.options.items ) ? 3528 this.options.items 3529 .call( this.element, null, { options: this.options, item: this.currentItem } ) : 3530 $( this.options.items, this.element ) 3531 .not( ".ui-sortable-helper" ) 3532 .not( ".ui-sortable-placeholder" ), this ] ); 3533 3534 function addItems() { 3535 items.push( this ); 3536 } 3537 for ( i = queries.length - 1; i >= 0; i-- ) { 3538 queries[ i ][ 0 ].each( addItems ); 3539 } 3540 3541 return $( items ); 3542 3543 }, 3544 3545 _removeCurrentsFromItems: function() { 3546 3547 var list = this.currentItem.find( ":data(" + this.widgetName + "-item)" ); 3548 3549 this.items = $.grep( this.items, function( item ) { 3550 for ( var j = 0; j < list.length; j++ ) { 3551 if ( list[ j ] === item.item[ 0 ] ) { 3552 return false; 3553 } 3554 } 3555 return true; 3556 } ); 3557 3558 }, 3559 3560 _refreshItems: function( event ) { 3561 3562 this.items = []; 3563 this.containers = [ this ]; 3564 3565 var i, j, cur, inst, targetData, _queries, item, queriesLength, 3566 items = this.items, 3567 queries = [ [ $.isFunction( this.options.items ) ? 3568 this.options.items.call( this.element[ 0 ], event, { item: this.currentItem } ) : 3569 $( this.options.items, this.element ), this ] ], 3570 connectWith = this._connectWith(); 3571 3572 //Shouldn't be run the first time through due to massive slow-down 3573 if ( connectWith && this.ready ) { 3574 for ( i = connectWith.length - 1; i >= 0; i-- ) { 3575 cur = $( connectWith[ i ], this.document[ 0 ] ); 3576 for ( j = cur.length - 1; j >= 0; j-- ) { 3577 inst = $.data( cur[ j ], this.widgetFullName ); 3578 if ( inst && inst !== this && !inst.options.disabled ) { 3579 queries.push( [ $.isFunction( inst.options.items ) ? 3580 inst.options.items 3581 .call( inst.element[ 0 ], event, { item: this.currentItem } ) : 3582 $( inst.options.items, inst.element ), inst ] ); 3583 this.containers.push( inst ); 3584 } 3585 } 3586 } 3587 } 3588 3589 for ( i = queries.length - 1; i >= 0; i-- ) { 3590 targetData = queries[ i ][ 1 ]; 3591 _queries = queries[ i ][ 0 ]; 3592 3593 for ( j = 0, queriesLength = _queries.length; j < queriesLength; j++ ) { 3594 item = $( _queries[ j ] ); 3595 3596 // Data for target checking (mouse manager) 3597 item.data( this.widgetName + "-item", targetData ); 3598 3599 items.push( { 3600 item: item, 3601 instance: targetData, 3602 width: 0, height: 0, 3603 left: 0, top: 0 3604 } ); 3605 } 3606 } 3607 3608 }, 3609 3610 refreshPositions: function( fast ) { 3611 3612 // Determine whether items are being displayed horizontally 3613 this.floating = this.items.length ? 3614 this.options.axis === "x" || this._isFloating( this.items[ 0 ].item ) : 3615 false; 3616 3617 //This has to be redone because due to the item being moved out/into the offsetParent, 3618 // the offsetParent's position will change 3619 if ( this.offsetParent && this.helper ) { 3620 this.offset.parent = this._getParentOffset(); 3621 } 3622 3623 var i, item, t, p; 3624 3625 for ( i = this.items.length - 1; i >= 0; i-- ) { 3626 item = this.items[ i ]; 3627 3628 //We ignore calculating positions of all connected containers when we're not over them 3629 if ( item.instance !== this.currentContainer && this.currentContainer && 3630 item.item[ 0 ] !== this.currentItem[ 0 ] ) { 3631 continue; 3632 } 3633 3634 t = this.options.toleranceElement ? 3635 $( this.options.toleranceElement, item.item ) : 3636 item.item; 3637 3638 if ( !fast ) { 3639 item.width = t.outerWidth(); 3640 item.height = t.outerHeight(); 3641 } 3642 3643 p = t.offset(); 3644 item.left = p.left; 3645 item.top = p.top; 3646 } 3647 3648 if ( this.options.custom && this.options.custom.refreshContainers ) { 3649 this.options.custom.refreshContainers.call( this ); 3650 } else { 3651 for ( i = this.containers.length - 1; i >= 0; i-- ) { 3652 p = this.containers[ i ].element.offset(); 3653 this.containers[ i ].containerCache.left = p.left; 3654 this.containers[ i ].containerCache.top = p.top; 3655 this.containers[ i ].containerCache.width = 3656 this.containers[ i ].element.outerWidth(); 3657 this.containers[ i ].containerCache.height = 3658 this.containers[ i ].element.outerHeight(); 3659 } 3660 } 3661 3662 return this; 3663 }, 3664 3665 _createPlaceholder: function( that ) { 3666 that = that || this; 3667 var className, 3668 o = that.options; 3669 3670 if ( !o.placeholder || o.placeholder.constructor === String ) { 3671 className = o.placeholder; 3672 o.placeholder = { 3673 element: function() { 3674 3675 var nodeName = that.currentItem[ 0 ].nodeName.toLowerCase(), 3676 element = $( "<" + nodeName + ">", that.document[ 0 ] ); 3677 3678 that._addClass( element, "ui-sortable-placeholder", 3679 className || that.currentItem[ 0 ].className ) 3680 ._removeClass( element, "ui-sortable-helper" ); 3681 3682 if ( nodeName === "tbody" ) { 3683 that._createTrPlaceholder( 3684 that.currentItem.find( "tr" ).eq( 0 ), 3685 $( "<tr>", that.document[ 0 ] ).appendTo( element ) 3686 ); 3687 } else if ( nodeName === "tr" ) { 3688 that._createTrPlaceholder( that.currentItem, element ); 3689 } else if ( nodeName === "img" ) { 3690 element.attr( "src", that.currentItem.attr( "src" ) ); 3691 } 3692 3693 if ( !className ) { 3694 element.css( "visibility", "hidden" ); 3695 } 3696 3697 return element; 3698 }, 3699 update: function( container, p ) { 3700 3701 // 1. If a className is set as 'placeholder option, we don't force sizes - 3702 // the class is responsible for that 3703 // 2. The option 'forcePlaceholderSize can be enabled to force it even if a 3704 // class name is specified 3705 if ( className && !o.forcePlaceholderSize ) { 3706 return; 3707 } 3708 3709 //If the element doesn't have a actual height by itself (without styles coming 3710 // from a stylesheet), it receives the inline height from the dragged item 3711 if ( !p.height() ) { 3712 p.height( 3713 that.currentItem.innerHeight() - 3714 parseInt( that.currentItem.css( "paddingTop" ) || 0, 10 ) - 3715 parseInt( that.currentItem.css( "paddingBottom" ) || 0, 10 ) ); 3716 } 3717 if ( !p.width() ) { 3718 p.width( 3719 that.currentItem.innerWidth() - 3720 parseInt( that.currentItem.css( "paddingLeft" ) || 0, 10 ) - 3721 parseInt( that.currentItem.css( "paddingRight" ) || 0, 10 ) ); 3722 } 3723 } 3724 }; 3725 } 3726 3727 //Create the placeholder 3728 that.placeholder = $( o.placeholder.element.call( that.element, that.currentItem ) ); 3729 3730 //Append it after the actual current item 3731 that.currentItem.after( that.placeholder ); 3732 3733 //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317) 3734 o.placeholder.update( that, that.placeholder ); 3735 3736 }, 3737 3738 _createTrPlaceholder: function( sourceTr, targetTr ) { 3739 var that = this; 3740 3741 sourceTr.children().each( function() { 3742 $( "<td> </td>", that.document[ 0 ] ) 3743 .attr( "colspan", $( this ).attr( "colspan" ) || 1 ) 3744 .appendTo( targetTr ); 3745 } ); 3746 }, 3747 3748 _contactContainers: function( event ) { 3749 var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, cur, nearBottom, 3750 floating, axis, 3751 innermostContainer = null, 3752 innermostIndex = null; 3753 3754 // Get innermost container that intersects with item 3755 for ( i = this.containers.length - 1; i >= 0; i-- ) { 3756 3757 // Never consider a container that's located within the item itself 3758 if ( $.contains( this.currentItem[ 0 ], this.containers[ i ].element[ 0 ] ) ) { 3759 continue; 3760 } 3761 3762 if ( this._intersectsWith( this.containers[ i ].containerCache ) ) { 3763 3764 // If we've already found a container and it's more "inner" than this, then continue 3765 if ( innermostContainer && 3766 $.contains( 3767 this.containers[ i ].element[ 0 ], 3768 innermostContainer.element[ 0 ] ) ) { 3769 continue; 3770 } 3771 3772 innermostContainer = this.containers[ i ]; 3773 innermostIndex = i; 3774 3775 } else { 3776 3777 // container doesn't intersect. trigger "out" event if necessary 3778 if ( this.containers[ i ].containerCache.over ) { 3779 this.containers[ i ]._trigger( "out", event, this._uiHash( this ) ); 3780 this.containers[ i ].containerCache.over = 0; 3781 } 3782 } 3783 3784 } 3785 3786 // If no intersecting containers found, return 3787 if ( !innermostContainer ) { 3788 return; 3789 } 3790 3791 // Move the item into the container if it's not there already 3792 if ( this.containers.length === 1 ) { 3793 if ( !this.containers[ innermostIndex ].containerCache.over ) { 3794 this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) ); 3795 this.containers[ innermostIndex ].containerCache.over = 1; 3796 } 3797 } else { 3798 3799 // When entering a new container, we will find the item with the least distance and 3800 // append our item near it 3801 dist = 10000; 3802 itemWithLeastDistance = null; 3803 floating = innermostContainer.floating || this._isFloating( this.currentItem ); 3804 posProperty = floating ? "left" : "top"; 3805 sizeProperty = floating ? "width" : "height"; 3806 axis = floating ? "pageX" : "pageY"; 3807 3808 for ( j = this.items.length - 1; j >= 0; j-- ) { 3809 if ( !$.contains( 3810 this.containers[ innermostIndex ].element[ 0 ], this.items[ j ].item[ 0 ] ) 3811 ) { 3812 continue; 3813 } 3814 if ( this.items[ j ].item[ 0 ] === this.currentItem[ 0 ] ) { 3815 continue; 3816 } 3817 3818 cur = this.items[ j ].item.offset()[ posProperty ]; 3819 nearBottom = false; 3820 if ( event[ axis ] - cur > this.items[ j ][ sizeProperty ] / 2 ) { 3821 nearBottom = true; 3822 } 3823 3824 if ( Math.abs( event[ axis ] - cur ) < dist ) { 3825 dist = Math.abs( event[ axis ] - cur ); 3826 itemWithLeastDistance = this.items[ j ]; 3827 this.direction = nearBottom ? "up" : "down"; 3828 } 3829 } 3830 3831 //Check if dropOnEmpty is enabled 3832 if ( !itemWithLeastDistance && !this.options.dropOnEmpty ) { 3833 return; 3834 } 3835 3836 if ( this.currentContainer === this.containers[ innermostIndex ] ) { 3837 if ( !this.currentContainer.containerCache.over ) { 3838 this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash() ); 3839 this.currentContainer.containerCache.over = 1; 3840 } 3841 return; 3842 } 3843 3844 itemWithLeastDistance ? 3845 this._rearrange( event, itemWithLeastDistance, null, true ) : 3846 this._rearrange( event, null, this.containers[ innermostIndex ].element, true ); 3847 this._trigger( "change", event, this._uiHash() ); 3848 this.containers[ innermostIndex ]._trigger( "change", event, this._uiHash( this ) ); 3849 this.currentContainer = this.containers[ innermostIndex ]; 3850 3851 //Update the placeholder 3852 this.options.placeholder.update( this.currentContainer, this.placeholder ); 3853 3854 this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) ); 3855 this.containers[ innermostIndex ].containerCache.over = 1; 3856 } 3857 3858 }, 3859 3860 _createHelper: function( event ) { 3861 3862 var o = this.options, 3863 helper = $.isFunction( o.helper ) ? 3864 $( o.helper.apply( this.element[ 0 ], [ event, this.currentItem ] ) ) : 3865 ( o.helper === "clone" ? this.currentItem.clone() : this.currentItem ); 3866 3867 //Add the helper to the DOM if that didn't happen already 3868 if ( !helper.parents( "body" ).length ) { 3869 $( o.appendTo !== "parent" ? 3870 o.appendTo : 3871 this.currentItem[ 0 ].parentNode )[ 0 ].appendChild( helper[ 0 ] ); 3872 } 3873 3874 if ( helper[ 0 ] === this.currentItem[ 0 ] ) { 3875 this._storedCSS = { 3876 width: this.currentItem[ 0 ].style.width, 3877 height: this.currentItem[ 0 ].style.height, 3878 position: this.currentItem.css( "position" ), 3879 top: this.currentItem.css( "top" ), 3880 left: this.currentItem.css( "left" ) 3881 }; 3882 } 3883 3884 if ( !helper[ 0 ].style.width || o.forceHelperSize ) { 3885 helper.width( this.currentItem.width() ); 3886 } 3887 if ( !helper[ 0 ].style.height || o.forceHelperSize ) { 3888 helper.height( this.currentItem.height() ); 3889 } 3890 3891 return helper; 3892 3893 }, 3894 3895 _adjustOffsetFromHelper: function( obj ) { 3896 if ( typeof obj === "string" ) { 3897 obj = obj.split( " " ); 3898 } 3899 if ( $.isArray( obj ) ) { 3900 obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 }; 3901 } 3902 if ( "left" in obj ) { 3903 this.offset.click.left = obj.left + this.margins.left; 3904 } 3905 if ( "right" in obj ) { 3906 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; 3907 } 3908 if ( "top" in obj ) { 3909 this.offset.click.top = obj.top + this.margins.top; 3910 } 3911 if ( "bottom" in obj ) { 3912 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; 3913 } 3914 }, 3915 3916 _getParentOffset: function() { 3917 3918 //Get the offsetParent and cache its position 3919 this.offsetParent = this.helper.offsetParent(); 3920 var po = this.offsetParent.offset(); 3921 3922 // This is a special case where we need to modify a offset calculated on start, since the 3923 // following happened: 3924 // 1. The position of the helper is absolute, so it's position is calculated based on the 3925 // next positioned parent 3926 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't 3927 // the document, which means that the scroll is included in the initial calculation of the 3928 // offset of the parent, and never recalculated upon drag 3929 if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== this.document[ 0 ] && 3930 $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) { 3931 po.left += this.scrollParent.scrollLeft(); 3932 po.top += this.scrollParent.scrollTop(); 3933 } 3934 3935 // This needs to be actually done for all browsers, since pageX/pageY includes this 3936 // information with an ugly IE fix 3937 if ( this.offsetParent[ 0 ] === this.document[ 0 ].body || 3938 ( this.offsetParent[ 0 ].tagName && 3939 this.offsetParent[ 0 ].tagName.toLowerCase() === "html" && $.ui.ie ) ) { 3940 po = { top: 0, left: 0 }; 3941 } 3942 3943 return { 3944 top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ), 3945 left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 ) 3946 }; 3947 3948 }, 3949 3950 _getRelativeOffset: function() { 3951 3952 if ( this.cssPosition === "relative" ) { 3953 var p = this.currentItem.position(); 3954 return { 3955 top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) + 3956 this.scrollParent.scrollTop(), 3957 left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) + 3958 this.scrollParent.scrollLeft() 3959 }; 3960 } else { 3961 return { top: 0, left: 0 }; 3962 } 3963 3964 }, 3965 3966 _cacheMargins: function() { 3967 this.margins = { 3968 left: ( parseInt( this.currentItem.css( "marginLeft" ), 10 ) || 0 ), 3969 top: ( parseInt( this.currentItem.css( "marginTop" ), 10 ) || 0 ) 3970 }; 3971 }, 3972 3973 _cacheHelperProportions: function() { 3974 this.helperProportions = { 3975 width: this.helper.outerWidth(), 3976 height: this.helper.outerHeight() 3977 }; 3978 }, 3979 3980 _setContainment: function() { 3981 3982 var ce, co, over, 3983 o = this.options; 3984 if ( o.containment === "parent" ) { 3985 o.containment = this.helper[ 0 ].parentNode; 3986 } 3987 if ( o.containment === "document" || o.containment === "window" ) { 3988 this.containment = [ 3989 0 - this.offset.relative.left - this.offset.parent.left, 3990 0 - this.offset.relative.top - this.offset.parent.top, 3991 o.containment === "document" ? 3992 this.document.width() : 3993 this.window.width() - this.helperProportions.width - this.margins.left, 3994 ( o.containment === "document" ? 3995 ( this.document.height() || document.body.parentNode.scrollHeight ) : 3996 this.window.height() || this.document[ 0 ].body.parentNode.scrollHeight 3997 ) - this.helperProportions.height - this.margins.top 3998 ]; 3999 } 4000 4001 if ( !( /^(document|window|parent)$/ ).test( o.containment ) ) { 4002 ce = $( o.containment )[ 0 ]; 4003 co = $( o.containment ).offset(); 4004 over = ( $( ce ).css( "overflow" ) !== "hidden" ); 4005 4006 this.containment = [ 4007 co.left + ( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) + 4008 ( parseInt( $( ce ).css( "paddingLeft" ), 10 ) || 0 ) - this.margins.left, 4009 co.top + ( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) + 4010 ( parseInt( $( ce ).css( "paddingTop" ), 10 ) || 0 ) - this.margins.top, 4011 co.left + ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - 4012 ( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) - 4013 ( parseInt( $( ce ).css( "paddingRight" ), 10 ) || 0 ) - 4014 this.helperProportions.width - this.margins.left, 4015 co.top + ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - 4016 ( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) - 4017 ( parseInt( $( ce ).css( "paddingBottom" ), 10 ) || 0 ) - 4018 this.helperProportions.height - this.margins.top 4019 ]; 4020 } 4021 4022 }, 4023 4024 _convertPositionTo: function( d, pos ) { 4025 4026 if ( !pos ) { 4027 pos = this.position; 4028 } 4029 var mod = d === "absolute" ? 1 : -1, 4030 scroll = this.cssPosition === "absolute" && 4031 !( this.scrollParent[ 0 ] !== this.document[ 0 ] && 4032 $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? 4033 this.offsetParent : 4034 this.scrollParent, 4035 scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName ); 4036 4037 return { 4038 top: ( 4039 4040 // The absolute mouse position 4041 pos.top + 4042 4043 // Only for relative positioned nodes: Relative offset from element to offset parent 4044 this.offset.relative.top * mod + 4045 4046 // The offsetParent's offset without borders (offset + border) 4047 this.offset.parent.top * mod - 4048 ( ( this.cssPosition === "fixed" ? 4049 -this.scrollParent.scrollTop() : 4050 ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod ) 4051 ), 4052 left: ( 4053 4054 // The absolute mouse position 4055 pos.left + 4056 4057 // Only for relative positioned nodes: Relative offset from element to offset parent 4058 this.offset.relative.left * mod + 4059 4060 // The offsetParent's offset without borders (offset + border) 4061 this.offset.parent.left * mod - 4062 ( ( this.cssPosition === "fixed" ? 4063 -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : 4064 scroll.scrollLeft() ) * mod ) 4065 ) 4066 }; 4067 4068 }, 4069 4070 _generatePosition: function( event ) { 4071 4072 var top, left, 4073 o = this.options, 4074 pageX = event.pageX, 4075 pageY = event.pageY, 4076 scroll = this.cssPosition === "absolute" && 4077 !( this.scrollParent[ 0 ] !== this.document[ 0 ] && 4078 $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? 4079 this.offsetParent : 4080 this.scrollParent, 4081 scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName ); 4082 4083 // This is another very weird special case that only happens for relative elements: 4084 // 1. If the css position is relative 4085 // 2. and the scroll parent is the document or similar to the offset parent 4086 // we have to refresh the relative offset during the scroll so there are no jumps 4087 if ( this.cssPosition === "relative" && !( this.scrollParent[ 0 ] !== this.document[ 0 ] && 4088 this.scrollParent[ 0 ] !== this.offsetParent[ 0 ] ) ) { 4089 this.offset.relative = this._getRelativeOffset(); 4090 } 4091 4092 /* 4093 * - Position constraining - 4094 * Constrain the position to a mix of grid, containment. 4095 */ 4096 4097 if ( this.originalPosition ) { //If we are not dragging yet, we won't check for options 4098 4099 if ( this.containment ) { 4100 if ( event.pageX - this.offset.click.left < this.containment[ 0 ] ) { 4101 pageX = this.containment[ 0 ] + this.offset.click.left; 4102 } 4103 if ( event.pageY - this.offset.click.top < this.containment[ 1 ] ) { 4104 pageY = this.containment[ 1 ] + this.offset.click.top; 4105 } 4106 if ( event.pageX - this.offset.click.left > this.containment[ 2 ] ) { 4107 pageX = this.containment[ 2 ] + this.offset.click.left; 4108 } 4109 if ( event.pageY - this.offset.click.top > this.containment[ 3 ] ) { 4110 pageY = this.containment[ 3 ] + this.offset.click.top; 4111 } 4112 } 4113 4114 if ( o.grid ) { 4115 top = this.originalPageY + Math.round( ( pageY - this.originalPageY ) / 4116 o.grid[ 1 ] ) * o.grid[ 1 ]; 4117 pageY = this.containment ? 4118 ( ( top - this.offset.click.top >= this.containment[ 1 ] && 4119 top - this.offset.click.top <= this.containment[ 3 ] ) ? 4120 top : 4121 ( ( top - this.offset.click.top >= this.containment[ 1 ] ) ? 4122 top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) : 4123 top; 4124 4125 left = this.originalPageX + Math.round( ( pageX - this.originalPageX ) / 4126 o.grid[ 0 ] ) * o.grid[ 0 ]; 4127 pageX = this.containment ? 4128 ( ( left - this.offset.click.left >= this.containment[ 0 ] && 4129 left - this.offset.click.left <= this.containment[ 2 ] ) ? 4130 left : 4131 ( ( left - this.offset.click.left >= this.containment[ 0 ] ) ? 4132 left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) : 4133 left; 4134 } 4135 4136 } 4137 4138 return { 4139 top: ( 4140 4141 // The absolute mouse position 4142 pageY - 4143 4144 // Click offset (relative to the element) 4145 this.offset.click.top - 4146 4147 // Only for relative positioned nodes: Relative offset from element to offset parent 4148 this.offset.relative.top - 4149 4150 // The offsetParent's offset without borders (offset + border) 4151 this.offset.parent.top + 4152 ( ( this.cssPosition === "fixed" ? 4153 -this.scrollParent.scrollTop() : 4154 ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) ) 4155 ), 4156 left: ( 4157 4158 // The absolute mouse position 4159 pageX - 4160 4161 // Click offset (relative to the element) 4162 this.offset.click.left - 4163 4164 // Only for relative positioned nodes: Relative offset from element to offset parent 4165 this.offset.relative.left - 4166 4167 // The offsetParent's offset without borders (offset + border) 4168 this.offset.parent.left + 4169 ( ( this.cssPosition === "fixed" ? 4170 -this.scrollParent.scrollLeft() : 4171 scrollIsRootNode ? 0 : scroll.scrollLeft() ) ) 4172 ) 4173 }; 4174 4175 }, 4176 4177 _rearrange: function( event, i, a, hardRefresh ) { 4178 4179 a ? a[ 0 ].appendChild( this.placeholder[ 0 ] ) : 4180 i.item[ 0 ].parentNode.insertBefore( this.placeholder[ 0 ], 4181 ( this.direction === "down" ? i.item[ 0 ] : i.item[ 0 ].nextSibling ) ); 4182 4183 //Various things done here to improve the performance: 4184 // 1. we create a setTimeout, that calls refreshPositions 4185 // 2. on the instance, we have a counter variable, that get's higher after every append 4186 // 3. on the local scope, we copy the counter variable, and check in the timeout, 4187 // if it's still the same 4188 // 4. this lets only the last addition to the timeout stack through 4189 this.counter = this.counter ? ++this.counter : 1; 4190 var counter = this.counter; 4191 4192 this._delay( function() { 4193 if ( counter === this.counter ) { 4194 4195 //Precompute after each DOM insertion, NOT on mousemove 4196 this.refreshPositions( !hardRefresh ); 4197 } 4198 } ); 4199 4200 }, 4201 4202 _clear: function( event, noPropagation ) { 4203 4204 this.reverting = false; 4205 4206 // We delay all events that have to be triggered to after the point where the placeholder 4207 // has been removed and everything else normalized again 4208 var i, 4209 delayedTriggers = []; 4210 4211 // We first have to update the dom position of the actual currentItem 4212 // Note: don't do it if the current item is already removed (by a user), or it gets 4213 // reappended (see #4088) 4214 if ( !this._noFinalSort && this.currentItem.parent().length ) { 4215 this.placeholder.before( this.currentItem ); 4216 } 4217 this._noFinalSort = null; 4218 4219 if ( this.helper[ 0 ] === this.currentItem[ 0 ] ) { 4220 for ( i in this._storedCSS ) { 4221 if ( this._storedCSS[ i ] === "auto" || this._storedCSS[ i ] === "static" ) { 4222 this._storedCSS[ i ] = ""; 4223 } 4224 } 4225 this.currentItem.css( this._storedCSS ); 4226 this._removeClass( this.currentItem, "ui-sortable-helper" ); 4227 } else { 4228 this.currentItem.show(); 4229 } 4230 4231 if ( this.fromOutside && !noPropagation ) { 4232 delayedTriggers.push( function( event ) { 4233 this._trigger( "receive", event, this._uiHash( this.fromOutside ) ); 4234 } ); 4235 } 4236 if ( ( this.fromOutside || 4237 this.domPosition.prev !== 4238 this.currentItem.prev().not( ".ui-sortable-helper" )[ 0 ] || 4239 this.domPosition.parent !== this.currentItem.parent()[ 0 ] ) && !noPropagation ) { 4240 4241 // Trigger update callback if the DOM position has changed 4242 delayedTriggers.push( function( event ) { 4243 this._trigger( "update", event, this._uiHash() ); 4244 } ); 4245 } 4246 4247 // Check if the items Container has Changed and trigger appropriate 4248 // events. 4249 if ( this !== this.currentContainer ) { 4250 if ( !noPropagation ) { 4251 delayedTriggers.push( function( event ) { 4252 this._trigger( "remove", event, this._uiHash() ); 4253 } ); 4254 delayedTriggers.push( ( function( c ) { 4255 return function( event ) { 4256 c._trigger( "receive", event, this._uiHash( this ) ); 4257 }; 4258 } ).call( this, this.currentContainer ) ); 4259 delayedTriggers.push( ( function( c ) { 4260 return function( event ) { 4261 c._trigger( "update", event, this._uiHash( this ) ); 4262 }; 4263 } ).call( this, this.currentContainer ) ); 4264 } 4265 } 4266 4267 //Post events to containers 4268 function delayEvent( type, instance, container ) { 4269 return function( event ) { 4270 container._trigger( type, event, instance._uiHash( instance ) ); 4271 }; 4272 } 4273 for ( i = this.containers.length - 1; i >= 0; i-- ) { 4274 if ( !noPropagation ) { 4275 delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) ); 4276 } 4277 if ( this.containers[ i ].containerCache.over ) { 4278 delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) ); 4279 this.containers[ i ].containerCache.over = 0; 4280 } 4281 } 4282 4283 //Do what was originally in plugins 4284 if ( this.storedCursor ) { 4285 this.document.find( "body" ).css( "cursor", this.storedCursor ); 4286 this.storedStylesheet.remove(); 4287 } 4288 if ( this._storedOpacity ) { 4289 this.helper.css( "opacity", this._storedOpacity ); 4290 } 4291 if ( this._storedZIndex ) { 4292 this.helper.css( "zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex ); 4293 } 4294 4295 this.dragging = false; 4296 4297 if ( !noPropagation ) { 4298 this._trigger( "beforeStop", event, this._uiHash() ); 4299 } 4300 4301 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, 4302 // it unbinds ALL events from the original node! 4303 this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] ); 4304 4305 if ( !this.cancelHelperRemoval ) { 4306 if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) { 4307 this.helper.remove(); 4308 } 4309 this.helper = null; 4310 } 4311 4312 if ( !noPropagation ) { 4313 for ( i = 0; i < delayedTriggers.length; i++ ) { 4314 4315 // Trigger all delayed events 4316 delayedTriggers[ i ].call( this, event ); 4317 } 4318 this._trigger( "stop", event, this._uiHash() ); 4319 } 4320 4321 this.fromOutside = false; 4322 return !this.cancelHelperRemoval; 4323 4324 }, 4325 4326 _trigger: function() { 4327 if ( $.Widget.prototype._trigger.apply( this, arguments ) === false ) { 4328 this.cancel(); 4329 } 4330 }, 4331 4332 _uiHash: function( _inst ) { 4333 var inst = _inst || this; 4334 return { 4335 helper: inst.helper, 4336 placeholder: inst.placeholder || $( [] ), 4337 position: inst.position, 4338 originalPosition: inst.originalPosition, 4339 offset: inst.positionAbs, 4340 item: inst.currentItem, 4341 sender: _inst ? _inst.element : null 4342 }; 4343 } 4344 4345 } ); 4346 4347 4348 4349 4350 }));