ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

droppable.js (12817B)


      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 } ) );