wp/wp-includes/js/jquery/ui/droppable.js
changeset 18 be944660c56a
child 19 3d72ae0968f4
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
       
     1 /*!
       
     2  * jQuery UI Droppable 1.12.1
       
     3  * http://jqueryui.com
       
     4  *
       
     5  * Copyright jQuery Foundation and other contributors
       
     6  * Released under the MIT license.
       
     7  * http://jquery.org/license
       
     8  */
       
     9 
       
    10 //>>label: Droppable
       
    11 //>>group: Interactions
       
    12 //>>description: Enables drop targets for draggable elements.
       
    13 //>>docs: http://api.jqueryui.com/droppable/
       
    14 //>>demos: http://jqueryui.com/droppable/
       
    15 
       
    16 ( function( factory ) {
       
    17 	if ( typeof define === "function" && define.amd ) {
       
    18 
       
    19 		// AMD. Register as an anonymous module.
       
    20 		define( [
       
    21 			"jquery",
       
    22 			"./draggable",
       
    23 			"./mouse",
       
    24 			"./core"
       
    25 		], factory );
       
    26 	} else {
       
    27 
       
    28 		// Browser globals
       
    29 		factory( jQuery );
       
    30 	}
       
    31 }( function( $ ) {
       
    32 
       
    33 $.widget( "ui.droppable", {
       
    34 	version: "1.12.1",
       
    35 	widgetEventPrefix: "drop",
       
    36 	options: {
       
    37 		accept: "*",
       
    38 		addClasses: true,
       
    39 		greedy: false,
       
    40 		scope: "default",
       
    41 		tolerance: "intersect",
       
    42 
       
    43 		// Callbacks
       
    44 		activate: null,
       
    45 		deactivate: null,
       
    46 		drop: null,
       
    47 		out: null,
       
    48 		over: null
       
    49 	},
       
    50 	_create: function() {
       
    51 
       
    52 		var proportions,
       
    53 			o = this.options,
       
    54 			accept = o.accept;
       
    55 
       
    56 		this.isover = false;
       
    57 		this.isout = true;
       
    58 
       
    59 		this.accept = $.isFunction( accept ) ? accept : function( d ) {
       
    60 			return d.is( accept );
       
    61 		};
       
    62 
       
    63 		this.proportions = function( /* valueToWrite */ ) {
       
    64 			if ( arguments.length ) {
       
    65 
       
    66 				// Store the droppable's proportions
       
    67 				proportions = arguments[ 0 ];
       
    68 			} else {
       
    69 
       
    70 				// Retrieve or derive the droppable's proportions
       
    71 				return proportions ?
       
    72 					proportions :
       
    73 					proportions = {
       
    74 						width: this.element[ 0 ].offsetWidth,
       
    75 						height: this.element[ 0 ].offsetHeight
       
    76 					};
       
    77 			}
       
    78 		};
       
    79 
       
    80 		this._addToManager( o.scope );
       
    81 
       
    82 		o.addClasses && this._addClass( "ui-droppable" );
       
    83 
       
    84 	},
       
    85 
       
    86 	_addToManager: function( scope ) {
       
    87 
       
    88 		// Add the reference and positions to the manager
       
    89 		$.ui.ddmanager.droppables[ scope ] = $.ui.ddmanager.droppables[ scope ] || [];
       
    90 		$.ui.ddmanager.droppables[ scope ].push( this );
       
    91 	},
       
    92 
       
    93 	_splice: function( drop ) {
       
    94 		var i = 0;
       
    95 		for ( ; i < drop.length; i++ ) {
       
    96 			if ( drop[ i ] === this ) {
       
    97 				drop.splice( i, 1 );
       
    98 			}
       
    99 		}
       
   100 	},
       
   101 
       
   102 	_destroy: function() {
       
   103 		var drop = $.ui.ddmanager.droppables[ this.options.scope ];
       
   104 
       
   105 		this._splice( drop );
       
   106 	},
       
   107 
       
   108 	_setOption: function( key, value ) {
       
   109 
       
   110 		if ( key === "accept" ) {
       
   111 			this.accept = $.isFunction( value ) ? value : function( d ) {
       
   112 				return d.is( value );
       
   113 			};
       
   114 		} else if ( key === "scope" ) {
       
   115 			var drop = $.ui.ddmanager.droppables[ this.options.scope ];
       
   116 
       
   117 			this._splice( drop );
       
   118 			this._addToManager( value );
       
   119 		}
       
   120 
       
   121 		this._super( key, value );
       
   122 	},
       
   123 
       
   124 	_activate: function( event ) {
       
   125 		var draggable = $.ui.ddmanager.current;
       
   126 
       
   127 		this._addActiveClass();
       
   128 		if ( draggable ) {
       
   129 			this._trigger( "activate", event, this.ui( draggable ) );
       
   130 		}
       
   131 	},
       
   132 
       
   133 	_deactivate: function( event ) {
       
   134 		var draggable = $.ui.ddmanager.current;
       
   135 
       
   136 		this._removeActiveClass();
       
   137 		if ( draggable ) {
       
   138 			this._trigger( "deactivate", event, this.ui( draggable ) );
       
   139 		}
       
   140 	},
       
   141 
       
   142 	_over: function( event ) {
       
   143 
       
   144 		var draggable = $.ui.ddmanager.current;
       
   145 
       
   146 		// Bail if draggable and droppable are same element
       
   147 		if ( !draggable || ( draggable.currentItem ||
       
   148 				draggable.element )[ 0 ] === this.element[ 0 ] ) {
       
   149 			return;
       
   150 		}
       
   151 
       
   152 		if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem ||
       
   153 				draggable.element ) ) ) {
       
   154 			this._addHoverClass();
       
   155 			this._trigger( "over", event, this.ui( draggable ) );
       
   156 		}
       
   157 
       
   158 	},
       
   159 
       
   160 	_out: function( event ) {
       
   161 
       
   162 		var draggable = $.ui.ddmanager.current;
       
   163 
       
   164 		// Bail if draggable and droppable are same element
       
   165 		if ( !draggable || ( draggable.currentItem ||
       
   166 				draggable.element )[ 0 ] === this.element[ 0 ] ) {
       
   167 			return;
       
   168 		}
       
   169 
       
   170 		if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem ||
       
   171 				draggable.element ) ) ) {
       
   172 			this._removeHoverClass();
       
   173 			this._trigger( "out", event, this.ui( draggable ) );
       
   174 		}
       
   175 
       
   176 	},
       
   177 
       
   178 	_drop: function( event, custom ) {
       
   179 
       
   180 		var draggable = custom || $.ui.ddmanager.current,
       
   181 			childrenIntersection = false;
       
   182 
       
   183 		// Bail if draggable and droppable are same element
       
   184 		if ( !draggable || ( draggable.currentItem ||
       
   185 				draggable.element )[ 0 ] === this.element[ 0 ] ) {
       
   186 			return false;
       
   187 		}
       
   188 
       
   189 		this.element
       
   190 			.find( ":data(ui-droppable)" )
       
   191 			.not( ".ui-draggable-dragging" )
       
   192 			.each( function() {
       
   193 				var inst = $( this ).droppable( "instance" );
       
   194 				if (
       
   195 					inst.options.greedy &&
       
   196 					!inst.options.disabled &&
       
   197 					inst.options.scope === draggable.options.scope &&
       
   198 					inst.accept.call(
       
   199 						inst.element[ 0 ], ( draggable.currentItem || draggable.element )
       
   200 					) &&
       
   201 					intersect(
       
   202 						draggable,
       
   203 						$.extend( inst, { offset: inst.element.offset() } ),
       
   204 						inst.options.tolerance, event
       
   205 					)
       
   206 				) {
       
   207 					childrenIntersection = true;
       
   208 					return false; }
       
   209 			} );
       
   210 		if ( childrenIntersection ) {
       
   211 			return false;
       
   212 		}
       
   213 
       
   214 		if ( this.accept.call( this.element[ 0 ],
       
   215 				( draggable.currentItem || draggable.element ) ) ) {
       
   216 			this._removeActiveClass();
       
   217 			this._removeHoverClass();
       
   218 
       
   219 			this._trigger( "drop", event, this.ui( draggable ) );
       
   220 			return this.element;
       
   221 		}
       
   222 
       
   223 		return false;
       
   224 
       
   225 	},
       
   226 
       
   227 	ui: function( c ) {
       
   228 		return {
       
   229 			draggable: ( c.currentItem || c.element ),
       
   230 			helper: c.helper,
       
   231 			position: c.position,
       
   232 			offset: c.positionAbs
       
   233 		};
       
   234 	},
       
   235 
       
   236 	// Extension points just to make backcompat sane and avoid duplicating logic
       
   237 	// TODO: Remove in 1.13 along with call to it below
       
   238 	_addHoverClass: function() {
       
   239 		this._addClass( "ui-droppable-hover" );
       
   240 	},
       
   241 
       
   242 	_removeHoverClass: function() {
       
   243 		this._removeClass( "ui-droppable-hover" );
       
   244 	},
       
   245 
       
   246 	_addActiveClass: function() {
       
   247 		this._addClass( "ui-droppable-active" );
       
   248 	},
       
   249 
       
   250 	_removeActiveClass: function() {
       
   251 		this._removeClass( "ui-droppable-active" );
       
   252 	}
       
   253 } );
       
   254 
       
   255 var intersect = $.ui.intersect = ( function() {
       
   256 	function isOverAxis( x, reference, size ) {
       
   257 		return ( x >= reference ) && ( x < ( reference + size ) );
       
   258 	}
       
   259 
       
   260 	return function( draggable, droppable, toleranceMode, event ) {
       
   261 
       
   262 		if ( !droppable.offset ) {
       
   263 			return false;
       
   264 		}
       
   265 
       
   266 		var x1 = ( draggable.positionAbs ||
       
   267 				draggable.position.absolute ).left + draggable.margins.left,
       
   268 			y1 = ( draggable.positionAbs ||
       
   269 				draggable.position.absolute ).top + draggable.margins.top,
       
   270 			x2 = x1 + draggable.helperProportions.width,
       
   271 			y2 = y1 + draggable.helperProportions.height,
       
   272 			l = droppable.offset.left,
       
   273 			t = droppable.offset.top,
       
   274 			r = l + droppable.proportions().width,
       
   275 			b = t + droppable.proportions().height;
       
   276 
       
   277 		switch ( toleranceMode ) {
       
   278 		case "fit":
       
   279 			return ( l <= x1 && x2 <= r && t <= y1 && y2 <= b );
       
   280 		case "intersect":
       
   281 			return ( l < x1 + ( draggable.helperProportions.width / 2 ) && // Right Half
       
   282 				x2 - ( draggable.helperProportions.width / 2 ) < r && // Left Half
       
   283 				t < y1 + ( draggable.helperProportions.height / 2 ) && // Bottom Half
       
   284 				y2 - ( draggable.helperProportions.height / 2 ) < b ); // Top Half
       
   285 		case "pointer":
       
   286 			return isOverAxis( event.pageY, t, droppable.proportions().height ) &&
       
   287 				isOverAxis( event.pageX, l, droppable.proportions().width );
       
   288 		case "touch":
       
   289 			return (
       
   290 				( y1 >= t && y1 <= b ) || // Top edge touching
       
   291 				( y2 >= t && y2 <= b ) || // Bottom edge touching
       
   292 				( y1 < t && y2 > b ) // Surrounded vertically
       
   293 			) && (
       
   294 				( x1 >= l && x1 <= r ) || // Left edge touching
       
   295 				( x2 >= l && x2 <= r ) || // Right edge touching
       
   296 				( x1 < l && x2 > r ) // Surrounded horizontally
       
   297 			);
       
   298 		default:
       
   299 			return false;
       
   300 		}
       
   301 	};
       
   302 } )();
       
   303 
       
   304 /*
       
   305 	This manager tracks offsets of draggables and droppables
       
   306 */
       
   307 $.ui.ddmanager = {
       
   308 	current: null,
       
   309 	droppables: { "default": [] },
       
   310 	prepareOffsets: function( t, event ) {
       
   311 
       
   312 		var i, j,
       
   313 			m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
       
   314 			type = event ? event.type : null, // workaround for #2317
       
   315 			list = ( t.currentItem || t.element ).find( ":data(ui-droppable)" ).addBack();
       
   316 
       
   317 		droppablesLoop: for ( i = 0; i < m.length; i++ ) {
       
   318 
       
   319 			// No disabled and non-accepted
       
   320 			if ( m[ i ].options.disabled || ( t && !m[ i ].accept.call( m[ i ].element[ 0 ],
       
   321 					( t.currentItem || t.element ) ) ) ) {
       
   322 				continue;
       
   323 			}
       
   324 
       
   325 			// Filter out elements in the current dragged item
       
   326 			for ( j = 0; j < list.length; j++ ) {
       
   327 				if ( list[ j ] === m[ i ].element[ 0 ] ) {
       
   328 					m[ i ].proportions().height = 0;
       
   329 					continue droppablesLoop;
       
   330 				}
       
   331 			}
       
   332 
       
   333 			m[ i ].visible = m[ i ].element.css( "display" ) !== "none";
       
   334 			if ( !m[ i ].visible ) {
       
   335 				continue;
       
   336 			}
       
   337 
       
   338 			// Activate the droppable if used directly from draggables
       
   339 			if ( type === "mousedown" ) {
       
   340 				m[ i ]._activate.call( m[ i ], event );
       
   341 			}
       
   342 
       
   343 			m[ i ].offset = m[ i ].element.offset();
       
   344 			m[ i ].proportions( {
       
   345 				width: m[ i ].element[ 0 ].offsetWidth,
       
   346 				height: m[ i ].element[ 0 ].offsetHeight
       
   347 			} );
       
   348 
       
   349 		}
       
   350 
       
   351 	},
       
   352 	drop: function( draggable, event ) {
       
   353 
       
   354 		var dropped = false;
       
   355 
       
   356 		// Create a copy of the droppables in case the list changes during the drop (#9116)
       
   357 		$.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), function() {
       
   358 
       
   359 			if ( !this.options ) {
       
   360 				return;
       
   361 			}
       
   362 			if ( !this.options.disabled && this.visible &&
       
   363 					intersect( draggable, this, this.options.tolerance, event ) ) {
       
   364 				dropped = this._drop.call( this, event ) || dropped;
       
   365 			}
       
   366 
       
   367 			if ( !this.options.disabled && this.visible && this.accept.call( this.element[ 0 ],
       
   368 					( draggable.currentItem || draggable.element ) ) ) {
       
   369 				this.isout = true;
       
   370 				this.isover = false;
       
   371 				this._deactivate.call( this, event );
       
   372 			}
       
   373 
       
   374 		} );
       
   375 		return dropped;
       
   376 
       
   377 	},
       
   378 	dragStart: function( draggable, event ) {
       
   379 
       
   380 		// Listen for scrolling so that if the dragging causes scrolling the position of the
       
   381 		// droppables can be recalculated (see #5003)
       
   382 		draggable.element.parentsUntil( "body" ).on( "scroll.droppable", function() {
       
   383 			if ( !draggable.options.refreshPositions ) {
       
   384 				$.ui.ddmanager.prepareOffsets( draggable, event );
       
   385 			}
       
   386 		} );
       
   387 	},
       
   388 	drag: function( draggable, event ) {
       
   389 
       
   390 		// If you have a highly dynamic page, you might try this option. It renders positions
       
   391 		// every time you move the mouse.
       
   392 		if ( draggable.options.refreshPositions ) {
       
   393 			$.ui.ddmanager.prepareOffsets( draggable, event );
       
   394 		}
       
   395 
       
   396 		// Run through all droppables and check their positions based on specific tolerance options
       
   397 		$.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() {
       
   398 
       
   399 			if ( this.options.disabled || this.greedyChild || !this.visible ) {
       
   400 				return;
       
   401 			}
       
   402 
       
   403 			var parentInstance, scope, parent,
       
   404 				intersects = intersect( draggable, this, this.options.tolerance, event ),
       
   405 				c = !intersects && this.isover ?
       
   406 					"isout" :
       
   407 					( intersects && !this.isover ? "isover" : null );
       
   408 			if ( !c ) {
       
   409 				return;
       
   410 			}
       
   411 
       
   412 			if ( this.options.greedy ) {
       
   413 
       
   414 				// find droppable parents with same scope
       
   415 				scope = this.options.scope;
       
   416 				parent = this.element.parents( ":data(ui-droppable)" ).filter( function() {
       
   417 					return $( this ).droppable( "instance" ).options.scope === scope;
       
   418 				} );
       
   419 
       
   420 				if ( parent.length ) {
       
   421 					parentInstance = $( parent[ 0 ] ).droppable( "instance" );
       
   422 					parentInstance.greedyChild = ( c === "isover" );
       
   423 				}
       
   424 			}
       
   425 
       
   426 			// We just moved into a greedy child
       
   427 			if ( parentInstance && c === "isover" ) {
       
   428 				parentInstance.isover = false;
       
   429 				parentInstance.isout = true;
       
   430 				parentInstance._out.call( parentInstance, event );
       
   431 			}
       
   432 
       
   433 			this[ c ] = true;
       
   434 			this[ c === "isout" ? "isover" : "isout" ] = false;
       
   435 			this[ c === "isover" ? "_over" : "_out" ].call( this, event );
       
   436 
       
   437 			// We just moved out of a greedy child
       
   438 			if ( parentInstance && c === "isout" ) {
       
   439 				parentInstance.isout = false;
       
   440 				parentInstance.isover = true;
       
   441 				parentInstance._over.call( parentInstance, event );
       
   442 			}
       
   443 		} );
       
   444 
       
   445 	},
       
   446 	dragStop: function( draggable, event ) {
       
   447 		draggable.element.parentsUntil( "body" ).off( "scroll.droppable" );
       
   448 
       
   449 		// Call prepareOffsets one final time since IE does not fire return scroll events when
       
   450 		// overflow was caused by drag (see #5003)
       
   451 		if ( !draggable.options.refreshPositions ) {
       
   452 			$.ui.ddmanager.prepareOffsets( draggable, event );
       
   453 		}
       
   454 	}
       
   455 };
       
   456 
       
   457 // DEPRECATED
       
   458 // TODO: switch return back to widget declaration at top of file when this is removed
       
   459 if ( $.uiBackCompat !== false ) {
       
   460 
       
   461 	// Backcompat for activeClass and hoverClass options
       
   462 	$.widget( "ui.droppable", $.ui.droppable, {
       
   463 		options: {
       
   464 			hoverClass: false,
       
   465 			activeClass: false
       
   466 		},
       
   467 		_addActiveClass: function() {
       
   468 			this._super();
       
   469 			if ( this.options.activeClass ) {
       
   470 				this.element.addClass( this.options.activeClass );
       
   471 			}
       
   472 		},
       
   473 		_removeActiveClass: function() {
       
   474 			this._super();
       
   475 			if ( this.options.activeClass ) {
       
   476 				this.element.removeClass( this.options.activeClass );
       
   477 			}
       
   478 		},
       
   479 		_addHoverClass: function() {
       
   480 			this._super();
       
   481 			if ( this.options.hoverClass ) {
       
   482 				this.element.addClass( this.options.hoverClass );
       
   483 			}
       
   484 		},
       
   485 		_removeHoverClass: function() {
       
   486 			this._super();
       
   487 			if ( this.options.hoverClass ) {
       
   488 				this.element.removeClass( this.options.hoverClass );
       
   489 			}
       
   490 		}
       
   491 	} );
       
   492 }
       
   493 
       
   494 return $.ui.droppable;
       
   495 
       
   496 } ) );