mirror of
https://github.com/jquery/jquery.git
synced 2024-11-23 02:54:22 +00:00
681 lines
17 KiB
JavaScript
681 lines
17 KiB
JavaScript
(function( jQuery ) {
|
|
|
|
var elemdisplay = {},
|
|
iframe, iframeDoc,
|
|
rfxtypes = /^(?:toggle|show|hide)$/,
|
|
rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
|
|
timerId,
|
|
fxAttrs = [
|
|
// height animations
|
|
[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
|
|
// width animations
|
|
[ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
|
|
// opacity animations
|
|
[ "opacity" ]
|
|
],
|
|
fxNow;
|
|
|
|
jQuery.fn.extend({
|
|
show: function( speed, easing, callback ) {
|
|
var elem, display;
|
|
|
|
if ( speed || speed === 0 ) {
|
|
return this.animate( genFx("show", 3), speed, easing, callback );
|
|
|
|
} else {
|
|
for ( var i = 0, j = this.length; i < j; i++ ) {
|
|
elem = this[ i ];
|
|
|
|
if ( elem.style ) {
|
|
display = elem.style.display;
|
|
|
|
// Reset the inline display of this element to learn if it is
|
|
// being hidden by cascaded rules or not
|
|
if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
|
|
display = elem.style.display = "";
|
|
}
|
|
|
|
// Set elements which have been overridden with display: none
|
|
// in a stylesheet to whatever the default browser style is
|
|
// for such an element
|
|
if ( display === "" && jQuery.css(elem, "display") === "none" ) {
|
|
jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the display of most of the elements in a second loop
|
|
// to avoid the constant reflow
|
|
for ( i = 0; i < j; i++ ) {
|
|
elem = this[ i ];
|
|
|
|
if ( elem.style ) {
|
|
display = elem.style.display;
|
|
|
|
if ( display === "" || display === "none" ) {
|
|
elem.style.display = jQuery._data( elem, "olddisplay" ) || "";
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
},
|
|
|
|
hide: function( speed, easing, callback ) {
|
|
if ( speed || speed === 0 ) {
|
|
return this.animate( genFx("hide", 3), speed, easing, callback);
|
|
|
|
} else {
|
|
var elem, display,
|
|
i = 0,
|
|
j = this.length;
|
|
|
|
for ( ; i < j; i++ ) {
|
|
elem = this[i];
|
|
if ( elem.style ) {
|
|
display = jQuery.css( elem, "display" );
|
|
|
|
if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) {
|
|
jQuery._data( elem, "olddisplay", display );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the display of the elements in a second loop
|
|
// to avoid the constant reflow
|
|
for ( i = 0; i < j; i++ ) {
|
|
if ( this[i].style ) {
|
|
this[i].style.display = "none";
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
},
|
|
|
|
// Save the old toggle function
|
|
_toggle: jQuery.fn.toggle,
|
|
|
|
toggle: function( fn, fn2, callback ) {
|
|
var bool = typeof fn === "boolean";
|
|
|
|
if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
|
|
this._toggle.apply( this, arguments );
|
|
|
|
} else if ( fn == null || bool ) {
|
|
this.each(function() {
|
|
var state = bool ? fn : jQuery(this).is(":hidden");
|
|
jQuery(this)[ state ? "show" : "hide" ]();
|
|
});
|
|
|
|
} else {
|
|
this.animate(genFx("toggle", 3), fn, fn2, callback);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
fadeTo: function( speed, to, easing, callback ) {
|
|
return this.filter(":hidden").css("opacity", 0).show().end()
|
|
.animate({opacity: to}, speed, easing, callback);
|
|
},
|
|
|
|
animate: function( prop, speed, easing, callback ) {
|
|
var optall = jQuery.speed( speed, easing, callback );
|
|
|
|
if ( jQuery.isEmptyObject( prop ) ) {
|
|
return this.each( optall.complete, [ false ] );
|
|
}
|
|
|
|
// Do not change referenced properties as per-property easing will be lost
|
|
prop = jQuery.extend( {}, prop );
|
|
|
|
function doAnimation() {
|
|
// XXX 'this' does not always have a nodeName when running the
|
|
// test suite
|
|
|
|
if ( optall.queue === false ) {
|
|
jQuery._mark( this );
|
|
}
|
|
|
|
var opt = jQuery.extend( {}, optall ),
|
|
isElement = this.nodeType === 1,
|
|
hidden = isElement && jQuery(this).is(":hidden"),
|
|
name, val, p, e,
|
|
parts, start, end, unit,
|
|
method;
|
|
|
|
// will store per property easing and be used to determine when an animation is complete
|
|
opt.animatedProperties = {};
|
|
|
|
for ( p in prop ) {
|
|
|
|
// property name normalization
|
|
name = jQuery.camelCase( p );
|
|
if ( p !== name ) {
|
|
prop[ name ] = prop[ p ];
|
|
delete prop[ p ];
|
|
}
|
|
|
|
val = prop[ name ];
|
|
|
|
// easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
|
|
if ( jQuery.isArray( val ) ) {
|
|
opt.animatedProperties[ name ] = val[ 1 ];
|
|
val = prop[ name ] = val[ 0 ];
|
|
} else {
|
|
opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing';
|
|
}
|
|
|
|
if ( val === "hide" && hidden || val === "show" && !hidden ) {
|
|
return opt.complete.call( this );
|
|
}
|
|
|
|
if ( isElement && ( name === "height" || name === "width" ) ) {
|
|
// Make sure that nothing sneaks out
|
|
// Record all 3 overflow attributes because IE does not
|
|
// change the overflow attribute when overflowX and
|
|
// overflowY are set to the same value
|
|
opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
|
|
|
|
// Set display property to inline-block for height/width
|
|
// animations on inline elements that are having width/height animated
|
|
if ( jQuery.css( this, "display" ) === "inline" &&
|
|
jQuery.css( this, "float" ) === "none" ) {
|
|
|
|
// inline-level elements accept inline-block;
|
|
// block-level elements need to be inline with layout
|
|
if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) {
|
|
this.style.display = "inline-block";
|
|
|
|
} else {
|
|
this.style.zoom = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( opt.overflow != null ) {
|
|
this.style.overflow = "hidden";
|
|
}
|
|
|
|
for ( p in prop ) {
|
|
e = new jQuery.fx( this, opt, p );
|
|
val = prop[ p ];
|
|
|
|
if ( rfxtypes.test( val ) ) {
|
|
|
|
// Tracks whether to show or hide based on private
|
|
// data attached to the element
|
|
method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 );
|
|
if ( method ) {
|
|
jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" );
|
|
e[ method ]();
|
|
} else {
|
|
e[ val ]();
|
|
}
|
|
|
|
} else {
|
|
parts = rfxnum.exec( val );
|
|
start = e.cur();
|
|
|
|
if ( parts ) {
|
|
end = parseFloat( parts[2] );
|
|
unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
|
|
|
|
// We need to compute starting value
|
|
if ( unit !== "px" ) {
|
|
jQuery.style( this, p, (end || 1) + unit);
|
|
start = ( (end || 1) / e.cur() ) * start;
|
|
jQuery.style( this, p, start + unit);
|
|
}
|
|
|
|
// If a +=/-= token was provided, we're doing a relative animation
|
|
if ( parts[1] ) {
|
|
end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
|
|
}
|
|
|
|
e.custom( start, end, unit );
|
|
|
|
} else {
|
|
e.custom( start, val, "" );
|
|
}
|
|
}
|
|
}
|
|
|
|
// For JS strict compliance
|
|
return true;
|
|
}
|
|
|
|
return optall.queue === false ?
|
|
this.each( doAnimation ) :
|
|
this.queue( optall.queue, doAnimation );
|
|
},
|
|
|
|
stop: function( type, clearQueue, gotoEnd ) {
|
|
if ( typeof type !== "string" ) {
|
|
gotoEnd = clearQueue;
|
|
clearQueue = type;
|
|
type = undefined;
|
|
}
|
|
if ( clearQueue && type !== false ) {
|
|
this.queue( type || "fx", [] );
|
|
}
|
|
|
|
return this.each(function() {
|
|
var i,
|
|
hadTimers = false,
|
|
timers = jQuery.timers,
|
|
data = jQuery._data( this );
|
|
|
|
// clear marker counters if we know they won't be
|
|
if ( !gotoEnd ) {
|
|
jQuery._unmark( true, this );
|
|
}
|
|
|
|
function stopQueue( elem, data, i ) {
|
|
var hooks = data[ i ];
|
|
jQuery.removeData( elem, i, true );
|
|
hooks.stop( gotoEnd );
|
|
}
|
|
|
|
if ( type == null ) {
|
|
for ( i in data ) {
|
|
if ( data[ i ].stop && i.indexOf(".run") === i.length - 4 ) {
|
|
stopQueue( this, data, i );
|
|
}
|
|
}
|
|
} else if ( data[ i = type + ".run" ] && data[ i ].stop ){
|
|
stopQueue( this, data, i );
|
|
}
|
|
|
|
for ( i = timers.length; i--; ) {
|
|
if ( timers[ i ].elem === this && (type == null || timers[ i ].queue === type) ) {
|
|
if ( gotoEnd ) {
|
|
|
|
// force the next step to be the last
|
|
timers[ i ]( true );
|
|
} else {
|
|
timers[ i ].saveState();
|
|
}
|
|
hadTimers = true;
|
|
timers.splice( i, 1 );
|
|
}
|
|
}
|
|
|
|
// start the next in the queue if the last step wasn't forced
|
|
// timers currently will call their complete callbacks, which will dequeue
|
|
// but only if they were gotoEnd
|
|
if ( !( gotoEnd && hadTimers ) ) {
|
|
jQuery.dequeue( this, type );
|
|
}
|
|
});
|
|
}
|
|
|
|
});
|
|
|
|
// Animations created synchronously will run synchronously
|
|
function createFxNow() {
|
|
setTimeout( clearFxNow, 0 );
|
|
return ( fxNow = jQuery.now() );
|
|
}
|
|
|
|
function clearFxNow() {
|
|
fxNow = undefined;
|
|
}
|
|
|
|
// Generate parameters to create a standard animation
|
|
function genFx( type, num ) {
|
|
var obj = {};
|
|
|
|
jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {
|
|
obj[ this ] = type;
|
|
});
|
|
|
|
return obj;
|
|
}
|
|
|
|
// Generate shortcuts for custom animations
|
|
jQuery.each({
|
|
slideDown: genFx( "show", 1 ),
|
|
slideUp: genFx( "hide", 1 ),
|
|
slideToggle: genFx( "toggle", 1 ),
|
|
fadeIn: { opacity: "show" },
|
|
fadeOut: { opacity: "hide" },
|
|
fadeToggle: { opacity: "toggle" }
|
|
}, function( name, props ) {
|
|
jQuery.fn[ name ] = function( speed, easing, callback ) {
|
|
return this.animate( props, speed, easing, callback );
|
|
};
|
|
});
|
|
|
|
jQuery.extend({
|
|
speed: function( speed, easing, fn ) {
|
|
var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
|
|
complete: fn || !fn && easing ||
|
|
jQuery.isFunction( speed ) && speed,
|
|
duration: speed,
|
|
easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
|
|
};
|
|
|
|
opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
|
|
opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
|
|
|
|
// normalize opt.queue - true/undefined/null -> "fx"
|
|
if ( opt.queue == null || opt.queue === true ) {
|
|
opt.queue = "fx";
|
|
}
|
|
|
|
// Queueing
|
|
opt.old = opt.complete;
|
|
|
|
opt.complete = function( noUnmark ) {
|
|
if ( jQuery.isFunction( opt.old ) ) {
|
|
opt.old.call( this );
|
|
}
|
|
|
|
if ( opt.queue ) {
|
|
jQuery.dequeue( this, opt.queue );
|
|
} else if ( noUnmark !== false ) {
|
|
jQuery._unmark( this );
|
|
}
|
|
};
|
|
|
|
return opt;
|
|
},
|
|
|
|
easing: {
|
|
linear: function( p, n, firstNum, diff ) {
|
|
return firstNum + diff * p;
|
|
},
|
|
swing: function( p, n, firstNum, diff ) {
|
|
return ( ( -Math.cos( p*Math.PI ) / 2 ) + 0.5 ) * diff + firstNum;
|
|
}
|
|
},
|
|
|
|
timers: [],
|
|
|
|
fx: function( elem, options, prop ) {
|
|
this.options = options;
|
|
this.elem = elem;
|
|
this.prop = prop;
|
|
|
|
options.orig = options.orig || {};
|
|
}
|
|
|
|
});
|
|
|
|
jQuery.fx.prototype = {
|
|
// Simple function for setting a style value
|
|
update: function() {
|
|
if ( this.options.step ) {
|
|
this.options.step.call( this.elem, this.now, this );
|
|
}
|
|
|
|
( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );
|
|
},
|
|
|
|
// Get the current size
|
|
cur: function() {
|
|
if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) {
|
|
return this.elem[ this.prop ];
|
|
}
|
|
|
|
var parsed,
|
|
r = jQuery.css( this.elem, this.prop );
|
|
// Empty strings, null, undefined and "auto" are converted to 0,
|
|
// complex values such as "rotate(1rad)" are returned as is,
|
|
// simple values such as "10px" are parsed to Float.
|
|
return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
|
|
},
|
|
|
|
// Start an animation from one number to another
|
|
custom: function( from, to, unit ) {
|
|
var self = this,
|
|
fx = jQuery.fx;
|
|
|
|
this.startTime = fxNow || createFxNow();
|
|
this.end = to;
|
|
this.now = this.start = from;
|
|
this.pos = this.state = 0;
|
|
this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" );
|
|
|
|
function t( gotoEnd ) {
|
|
return self.step( gotoEnd );
|
|
}
|
|
|
|
t.queue = this.options.queue;
|
|
t.elem = this.elem;
|
|
t.saveState = function() {
|
|
if ( self.options.hide && jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) {
|
|
jQuery._data( self.elem, "fxshow" + self.prop, self.start );
|
|
}
|
|
};
|
|
|
|
if ( t() && jQuery.timers.push(t) && !timerId ) {
|
|
timerId = setInterval( fx.tick, fx.interval );
|
|
}
|
|
},
|
|
|
|
// Simple 'show' function
|
|
show: function() {
|
|
var dataShow = jQuery._data( this.elem, "fxshow" + this.prop );
|
|
|
|
// Remember where we started, so that we can go back to it later
|
|
this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );
|
|
this.options.show = true;
|
|
|
|
// Begin the animation
|
|
// Make sure that we start at a small width/height to avoid any flash of content
|
|
if ( dataShow !== undefined ) {
|
|
// This show is picking up where a previous hide or show left off
|
|
this.custom( this.cur(), dataShow );
|
|
} else {
|
|
this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() );
|
|
}
|
|
|
|
// Start by showing the element
|
|
jQuery( this.elem ).show();
|
|
},
|
|
|
|
// Simple 'hide' function
|
|
hide: function() {
|
|
// Remember where we started, so that we can go back to it later
|
|
this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop );
|
|
this.options.hide = true;
|
|
|
|
// Begin the animation
|
|
this.custom( this.cur(), 0 );
|
|
},
|
|
|
|
// Each step of an animation
|
|
step: function( gotoEnd ) {
|
|
var p, n, complete,
|
|
t = fxNow || createFxNow(),
|
|
done = true,
|
|
elem = this.elem,
|
|
options = this.options;
|
|
|
|
if ( gotoEnd || t >= options.duration + this.startTime ) {
|
|
this.now = this.end;
|
|
this.pos = this.state = 1;
|
|
this.update();
|
|
|
|
options.animatedProperties[ this.prop ] = true;
|
|
|
|
for ( p in options.animatedProperties ) {
|
|
if ( options.animatedProperties[ p ] !== true ) {
|
|
done = false;
|
|
}
|
|
}
|
|
|
|
if ( done ) {
|
|
// Reset the overflow
|
|
if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
|
|
|
|
jQuery.each( [ "", "X", "Y" ], function( index, value ) {
|
|
elem.style[ "overflow" + value ] = options.overflow[ index ];
|
|
});
|
|
}
|
|
|
|
// Hide the element if the "hide" operation was done
|
|
if ( options.hide ) {
|
|
jQuery( elem ).hide();
|
|
}
|
|
|
|
// Reset the properties, if the item has been hidden or shown
|
|
if ( options.hide || options.show ) {
|
|
for ( p in options.animatedProperties ) {
|
|
jQuery.style( elem, p, options.orig[ p ] );
|
|
jQuery.removeData( elem, "fxshow" + p, true );
|
|
// Toggle data is no longer needed
|
|
jQuery.removeData( elem, "toggle" + p, true );
|
|
}
|
|
}
|
|
|
|
// Execute the complete function
|
|
// in the event that the complete function throws an exception
|
|
// we must ensure it won't be called twice. #5684
|
|
|
|
complete = options.complete;
|
|
if ( complete ) {
|
|
|
|
options.complete = false;
|
|
complete.call( elem );
|
|
}
|
|
}
|
|
|
|
return false;
|
|
|
|
} else {
|
|
// classical easing cannot be used with an Infinity duration
|
|
if ( options.duration == Infinity ) {
|
|
this.now = t;
|
|
} else {
|
|
n = t - this.startTime;
|
|
this.state = n / options.duration;
|
|
|
|
// Perform the easing function, defaults to swing
|
|
this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration );
|
|
this.now = this.start + ( (this.end - this.start) * this.pos );
|
|
}
|
|
// Perform the next step of the animation
|
|
this.update();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
jQuery.extend( jQuery.fx, {
|
|
tick: function() {
|
|
var timer,
|
|
timers = jQuery.timers,
|
|
i = 0;
|
|
|
|
for ( ; i < timers.length; i++ ) {
|
|
timer = timers[ i ];
|
|
// Checks the timer has not already been removed
|
|
if ( !timer() && timers[ i ] === timer ) {
|
|
timers.splice( i--, 1 );
|
|
}
|
|
}
|
|
|
|
if ( !timers.length ) {
|
|
jQuery.fx.stop();
|
|
}
|
|
},
|
|
|
|
interval: 13,
|
|
|
|
stop: function() {
|
|
clearInterval( timerId );
|
|
timerId = null;
|
|
},
|
|
|
|
speeds: {
|
|
slow: 600,
|
|
fast: 200,
|
|
// Default speed
|
|
_default: 400
|
|
},
|
|
|
|
step: {
|
|
opacity: function( fx ) {
|
|
jQuery.style( fx.elem, "opacity", fx.now );
|
|
},
|
|
|
|
_default: function( fx ) {
|
|
if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
|
|
fx.elem.style[ fx.prop ] = fx.now + fx.unit;
|
|
} else {
|
|
fx.elem[ fx.prop ] = fx.now;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Adds width/height step functions
|
|
// Do not set anything below 0
|
|
jQuery.each([ "width", "height" ], function( i, prop ) {
|
|
jQuery.fx.step[ prop ] = function( fx ) {
|
|
jQuery.style( fx.elem, prop, Math.max(0, fx.now) );
|
|
};
|
|
});
|
|
|
|
if ( jQuery.expr && jQuery.expr.filters ) {
|
|
jQuery.expr.filters.animated = function( elem ) {
|
|
return jQuery.grep(jQuery.timers, function( fn ) {
|
|
return elem === fn.elem;
|
|
}).length;
|
|
};
|
|
}
|
|
|
|
// Try to restore the default display value of an element
|
|
function defaultDisplay( nodeName ) {
|
|
|
|
if ( !elemdisplay[ nodeName ] ) {
|
|
|
|
var body = document.body,
|
|
elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
|
|
display = elem.css( "display" );
|
|
elem.remove();
|
|
|
|
// If the simple way fails,
|
|
// get element's real default display by attaching it to a temp iframe
|
|
if ( display === "none" || display === "" ) {
|
|
// No iframe to use yet, so create it
|
|
if ( !iframe ) {
|
|
iframe = document.createElement( "iframe" );
|
|
iframe.frameBorder = iframe.width = iframe.height = 0;
|
|
}
|
|
|
|
body.appendChild( iframe );
|
|
|
|
// Create a cacheable copy of the iframe document on first call.
|
|
// IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
|
|
// document to it; WebKit & Firefox won't allow reusing the iframe document.
|
|
if ( !iframeDoc || !iframe.createElement ) {
|
|
iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
|
|
iframeDoc.write( ( document.compatMode === "CSS1Compat" ? "<!doctype html>" : "" ) + "<html><body>" );
|
|
iframeDoc.close();
|
|
}
|
|
|
|
elem = iframeDoc.createElement( nodeName );
|
|
|
|
iframeDoc.body.appendChild( elem );
|
|
|
|
display = jQuery.css( elem, "display" );
|
|
body.removeChild( iframe );
|
|
}
|
|
|
|
// Store the correct default display
|
|
elemdisplay[ nodeName ] = display;
|
|
}
|
|
|
|
return elemdisplay[ nodeName ];
|
|
}
|
|
|
|
})( jQuery );
|