diff options
author | Scott González <scott.gonzalez@gmail.com> | 2012-06-25 19:25:27 -0400 |
---|---|---|
committer | Scott González <scott.gonzalez@gmail.com> | 2012-06-25 19:25:27 -0400 |
commit | 13d560b8f05a8eedbf28851824fe639a254f9fd0 (patch) | |
tree | 43ca694862cff838baa34f01816f53eb42d5bd80 /ui | |
parent | abbf6a79a91cdc64005fcbb28d98ad71a27b61c0 (diff) | |
download | jquery-ui-13d560b8f05a8eedbf28851824fe639a254f9fd0.tar.gz jquery-ui-13d560b8f05a8eedbf28851824fe639a254f9fd0.zip |
Upgrade to jquery-color 2.0pre. Fixes #6910 - Trying to animate the background color of a document fragment can easily fail. Fixes #5215 - Effects color: Add support for rgba animations. Fixes #4372 - Animate backgroundColor to 'transparent'. Fixes #5393 - getColor reports inaccurate color for transparent elements in Webkit. Fixes #3806 - Feature request: expose getRGB and getColor.
Diffstat (limited to 'ui')
-rw-r--r-- | ui/jquery.ui.effect.js | 740 |
1 files changed, 637 insertions, 103 deletions
diff --git a/ui/jquery.ui.effect.js b/ui/jquery.ui.effect.js index 1bb1415a9..366c2d190 100644 --- a/ui/jquery.ui.effect.js +++ b/ui/jquery.ui.effect.js @@ -17,140 +17,674 @@ $.effects = { effect: {} }; -/******************************************************************************/ -/****************************** COLOR ANIMATIONS ******************************/ -/******************************************************************************/ -(function() { +/*! + * jQuery Color Animations + * http://jquery.org/ + * + * Copyright 2011 John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */ +(function( jQuery, undefined ) { + +var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color outlineColor".split(" "), + + // plusequals test for += 100 -= 100 + rplusequals = /^([\-+])=\s*(\d+\.?\d*)/, + // a set of RE's that can match strings and generate color tuples. + stringParsers = [{ + re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/, + parse: function( execResult ) { + return [ + execResult[ 1 ], + execResult[ 2 ], + execResult[ 3 ], + execResult[ 4 ] + ]; + } + }, { + re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/, + parse: function( execResult ) { + return [ + 2.55 * execResult[1], + 2.55 * execResult[2], + 2.55 * execResult[3], + execResult[ 4 ] + ]; + } + }, { + re: /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/, + parse: function( execResult ) { + return [ + parseInt( execResult[ 1 ], 16 ), + parseInt( execResult[ 2 ], 16 ), + parseInt( execResult[ 3 ], 16 ) + ]; + } + }, { + re: /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/, + parse: function( execResult ) { + return [ + parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ), + parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ), + parseInt( execResult[ 3 ] + execResult[ 3 ], 16 ) + ]; + } + }, { + re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/, + space: "hsla", + parse: function( execResult ) { + return [ + execResult[1], + execResult[2] / 100, + execResult[3] / 100, + execResult[4] + ]; + } + }], -// override the animation for color styles -$.each(["backgroundColor", "borderBottomColor", "borderLeftColor", - "borderRightColor", "borderTopColor", "borderColor", "color", "outlineColor"], -function(i, attr) { - $.fx.step[attr] = function(fx) { - if (!fx.colorInit) { - fx.start = getColor(fx.elem, attr); - fx.end = getRGB(fx.end); - fx.colorInit = true; + // jQuery.Color( ) + color = jQuery.Color = function( color, green, blue, alpha ) { + return new jQuery.Color.fn.parse( color, green, blue, alpha ); + }, + spaces = { + rgba: { + cache: "_rgba", + props: { + red: { + idx: 0, + type: "byte", + empty: true + }, + green: { + idx: 1, + type: "byte", + empty: true + }, + blue: { + idx: 2, + type: "byte", + empty: true + }, + alpha: { + idx: 3, + type: "percent", + def: 1 + } + } + }, + hsla: { + cache: "_hsla", + props: { + hue: { + idx: 0, + type: "degrees", + empty: true + }, + saturation: { + idx: 1, + type: "percent", + empty: true + }, + lightness: { + idx: 2, + type: "percent", + empty: true + } + } } + }, + propTypes = { + "byte": { + floor: true, + min: 0, + max: 255 + }, + "percent": { + min: 0, + max: 1 + }, + "degrees": { + mod: 360, + floor: true + } + }, + rgbaspace = spaces.rgba.props, + support = color.support = {}, - fx.elem.style[attr] = "rgb(" + - Math.max(Math.min(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10), 255), 0) + "," + - Math.max(Math.min(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10), 255), 0) + "," + - Math.max(Math.min(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10), 255), 0) + ")"; - }; -}); + // colors = jQuery.Color.names + colors, + + // local aliases of functions called often + each = jQuery.each; + +spaces.hsla.props.alpha = rgbaspace.alpha; + +function clamp( value, prop, alwaysAllowEmpty ) { + var type = propTypes[ prop.type ] || {}, + allowEmpty = prop.empty || alwaysAllowEmpty; + + if ( allowEmpty && value == null ) { + return null; + } + if ( prop.def && value == null ) { + return prop.def; + } + if ( type.floor ) { + value = ~~value; + } else { + value = parseFloat( value ); + } + if ( value == null || isNaN( value ) ) { + return prop.def; + } + if ( type.mod ) { + value = value % type.mod; + // -10 -> 350 + return value < 0 ? type.mod + value : value; + } + + // for now all property types without mod have min and max + return type.min > value ? type.min : type.max < value ? type.max : value; +} + +function stringParse( string ) { + var inst = color(), + rgba = inst._rgba = []; + + string = string.toLowerCase(); + + each( stringParsers, function( i, parser ) { + var match = parser.re.exec( string ), + values = match && parser.parse( match ), + parsed, + spaceName = parser.space || "rgba", + cache = spaces[ spaceName ].cache; -// Color Conversion functions from highlightFade -// By Blair Mitchelmore -// http://jquery.offput.ca/highlightFade/ -// Parse strings looking for color tuples [255,255,255] -function getRGB(color) { - var result; + if ( values ) { + parsed = inst[ spaceName ]( values ); - // Check if we're already dealing with an array of colors - if ( color && color.constructor === Array && color.length === 3 ) { - return color; + // if this was an rgba parse the assignment might happen twice + // oh well.... + inst[ cache ] = parsed[ cache ]; + rgba = inst._rgba = parsed._rgba; + + // exit each( stringParsers ) here because we matched + return false; } + }); - // Look for rgb(num,num,num) - if ( (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) ) { - return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)]; + // Found a stringParser that handled it + if ( rgba.length !== 0 ) { + + // if this came from a parsed string, force "transparent" when alpha is 0 + // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0) + if ( Math.max.apply( Math, rgba ) === 0 ) { + jQuery.extend( rgba, colors.transparent ); } + return inst; + } - // Look for rgb(num%,num%,num%) - if ( (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) ) { - return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55]; + // named colors / default - filter back through parse function + if ( string = colors[ string ] ) { + return string; + } +} + +color.fn = color.prototype = { + constructor: color, + parse: function( red, green, blue, alpha ) { + if ( red === undefined ) { + this._rgba = [ null, null, null, null ]; + return this; + } + if ( red instanceof jQuery || red.nodeType ) { + red = red instanceof jQuery ? red.css( green ) : jQuery( red ).css( green ); + green = undefined; } - // Look for #a0b1c2 - if ( (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) ) { - return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)]; + var inst = this, + type = jQuery.type( red ), + rgba = this._rgba = [], + source; + + // more than 1 argument specified - assume ( red, green, blue, alpha ) + if ( green !== undefined ) { + red = [ red, green, blue, alpha ]; + type = "array"; } - // Look for #fff - if ( (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) ) { - return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)]; + if ( type === "string" ) { + return this.parse( stringParse( red ) || colors._default ); } - // Look for rgba(0, 0, 0, 0) == transparent in Safari 3 - if ( (result = /rgba\(0, 0, 0, 0\)/.exec(color)) ) { - return colors.transparent; + if ( type === "array" ) { + each( rgbaspace, function( key, prop ) { + rgba[ prop.idx ] = clamp( red[ prop.idx ], prop ); + }); + return this; } - // Otherwise, we're most likely dealing with a named color - return colors[$.trim(color).toLowerCase()]; -} + if ( type === "object" ) { + if ( red instanceof color ) { + each( spaces, function( spaceName, space ) { + if ( red[ space.cache ] ) { + inst[ space.cache ] = red[ space.cache ].slice(); + } + }); + } else { + each( spaces, function( spaceName, space ) { + each( space.props, function( key, prop ) { + var cache = space.cache; + + // if the cache doesn't exist, and we know how to convert + if ( !inst[ cache ] && space.to ) { + + // if the value was null, we don't need to copy it + // if the key was alpha, we don't need to copy it either + if ( red[ key ] == null || key === "alpha") { + return; + } + inst[ cache ] = space.to( inst._rgba ); + } + + // this is the only case where we allow nulls for ALL properties. + // call clamp with alwaysAllowEmpty + inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true ); + }); + }); + } + return this; + } + }, + is: function( compare ) { + var is = color( compare ), + same = true, + myself = this; + + each( spaces, function( _, space ) { + var isCache = is[ space.cache ], + localCache; + if (isCache) { + localCache = myself[ space.cache ] || space.to && space.to( myself._rgba ) || []; + each( space.props, function( _, prop ) { + if ( isCache[ prop.idx ] != null ) { + same = ( isCache[ prop.idx ] === localCache[ prop.idx ] ); + return same; + } + }); + } + return same; + }); + return same; + }, + _space: function() { + var used = [], + inst = this; + each( spaces, function( spaceName, space ) { + if ( inst[ space.cache ] ) { + used.push( spaceName ); + } + }); + return used.pop(); + }, + transition: function( other, distance ) { + var end = color( other ), + spaceName = end._space(), + space = spaces[ spaceName ], + start = this[ space.cache ] || space.to( this._rgba ), + result = start.slice(); + + end = end[ space.cache ]; + each( space.props, function( key, prop ) { + var index = prop.idx, + startValue = start[ index ], + endValue = end[ index ], + type = propTypes[ prop.type ] || {}; + + // if null, don't override start value + if ( endValue === null ) { + return; + } + // if null - use end + if ( startValue === null ) { + result[ index ] = endValue; + } else { + if ( type.mod ) { + if ( endValue - startValue > type.mod / 2 ) { + startValue += type.mod; + } else if ( startValue - endValue > type.mod / 2 ) { + startValue -= type.mod; + } + } + result[ prop.idx ] = clamp( ( endValue - startValue ) * distance + startValue, prop ); + } + }); + return this[ spaceName ]( result ); + }, + blend: function( opaque ) { + // if we are already opaque - return ourself + if ( this._rgba[ 3 ] === 1 ) { + return this; + } + + var rgb = this._rgba.slice(), + a = rgb.pop(), + blend = color( opaque )._rgba; + + return color( jQuery.map( rgb, function( v, i ) { + return ( 1 - a ) * blend[ i ] + a * v; + })); + }, + toRgbaString: function() { + var prefix = "rgba(", + rgba = jQuery.map( this._rgba, function( v, i ) { + return v == null ? ( i > 2 ? 1 : 0 ) : v; + }); -function getColor(elem, attr) { - var color; + if ( rgba[ 3 ] === 1 ) { + rgba.pop(); + prefix = "rgb("; + } - do { - color = $.css(elem, attr); + return prefix + rgba.join(",") + ")"; + }, + toHslaString: function() { + var prefix = "hsla(", + hsla = jQuery.map( this.hsla(), function( v, i ) { + if ( v == null ) { + v = i > 2 ? 1 : 0; + } - // Keep going until we find an element that has color, or we hit the body - if ( color && color !== "transparent" || $.nodeName(elem, "body") ) { - break; + // catch 1 and 2 + if ( i && i < 3 ) { + v = Math.round( v * 100 ) + "%"; } + return v; + }); + + if ( hsla[ 3 ] === 1 ) { + hsla.pop(); + prefix = "hsl("; + } + return prefix + hsla.join(",") + ")"; + }, + toHexString: function( includeAlpha ) { + var rgba = this._rgba.slice(), + alpha = rgba.pop(); + + if ( includeAlpha ) { + rgba.push( ~~( alpha * 255 ) ); + } + + return "#" + jQuery.map( rgba, function( v, i ) { + + // default to 0 when nulls exist + v = ( v || 0 ).toString( 16 ); + return v.length === 1 ? "0" + v : v; + }).join(""); + }, + toString: function() { + return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString(); + } +}; +color.fn.parse.prototype = color.fn; - attr = "backgroundColor"; - } while ( (elem = elem.parentNode) ); +// hsla conversions adapted from: +// http://www.google.com/codesearch/p#OAMlx_jo-ck/src/third_party/WebKit/Source/WebCore/inspector/front-end/Color.js&d=7&l=193 - return getRGB(color); +function hue2rgb( p, q, h ) { + h = ( h + 1 ) % 1; + if ( h * 6 < 1 ) { + return p + (q - p) * 6 * h; + } + if ( h * 2 < 1) { + return q; + } + if ( h * 3 < 2 ) { + return p + (q - p) * ((2/3) - h) * 6; + } + return p; } +spaces.hsla.to = function ( rgba ) { + if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) { + return [ null, null, null, rgba[ 3 ] ]; + } + var r = rgba[ 0 ] / 255, + g = rgba[ 1 ] / 255, + b = rgba[ 2 ] / 255, + a = rgba[ 3 ], + max = Math.max( r, g, b ), + min = Math.min( r, g, b ), + diff = max - min, + add = max + min, + l = add * 0.5, + h, s; + + if ( min === max ) { + h = 0; + } else if ( r === max ) { + h = ( 60 * ( g - b ) / diff ) + 360; + } else if ( g === max ) { + h = ( 60 * ( b - r ) / diff ) + 120; + } else { + h = ( 60 * ( r - g ) / diff ) + 240; + } + + if ( l === 0 || l === 1 ) { + s = l; + } else if ( l <= 0.5 ) { + s = diff / add; + } else { + s = diff / ( 2 - add ); + } + return [ Math.round(h) % 360, s, l, a == null ? 1 : a ]; +}; + +spaces.hsla.from = function ( hsla ) { + if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) { + return [ null, null, null, hsla[ 3 ] ]; + } + var h = hsla[ 0 ] / 360, + s = hsla[ 1 ], + l = hsla[ 2 ], + a = hsla[ 3 ], + q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s, + p = 2 * l - q, + r, g, b; + + return [ + Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ), + Math.round( hue2rgb( p, q, h ) * 255 ), + Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ), + a + ]; +}; + + +each( spaces, function( spaceName, space ) { + var props = space.props, + cache = space.cache, + to = space.to, + from = space.from; + + // makes rgba() and hsla() + color.fn[ spaceName ] = function( value ) { + + // generate a cache for this space if it doesn't exist + if ( to && !this[ cache ] ) { + this[ cache ] = to( this._rgba ); + } + if ( value === undefined ) { + return this[ cache ].slice(); + } + + var type = jQuery.type( value ), + arr = ( type === "array" || type === "object" ) ? value : arguments, + local = this[ cache ].slice(), + ret; + + each( props, function( key, prop ) { + var val = arr[ type === "object" ? key : prop.idx ]; + if ( val == null ) { + val = local[ prop.idx ]; + } + local[ prop.idx ] = clamp( val, prop ); + }); + + if ( from ) { + ret = color( from( local ) ); + ret[ cache ] = local; + return ret; + } else { + return color( local ); + } + }; + + // makes red() green() blue() alpha() hue() saturation() lightness() + each( props, function( key, prop ) { + // alpha is included in more than one space + if ( color.fn[ key ] ) { + return; + } + color.fn[ key ] = function( value ) { + var vtype = jQuery.type( value ), + fn = ( key === 'alpha' ? ( this._hsla ? 'hsla' : 'rgba' ) : spaceName ), + local = this[ fn ](), + cur = local[ prop.idx ], + match; + + if ( vtype === "undefined" ) { + return cur; + } + + if ( vtype === "function" ) { + value = value.call( this, cur ); + vtype = jQuery.type( value ); + } + if ( value == null && prop.empty ) { + return this; + } + if ( vtype === "string" ) { + match = rplusequals.exec( value ); + if ( match ) { + value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 ); + } + } + local[ prop.idx ] = value; + return this[ fn ]( local ); + }; + }); +}); + +// add .fx.step functions +each( stepHooks, function( i, hook ) { + jQuery.cssHooks[ hook ] = { + set: function( elem, value ) { + var parsed, backgroundColor, curElem; + + if ( jQuery.type( value ) !== 'string' || ( parsed = stringParse( value ) ) ) + { + value = color( parsed || value ); + if ( !support.rgba && value._rgba[ 3 ] !== 1 ) { + curElem = hook === "backgroundColor" ? elem.parentNode : elem; + do { + backgroundColor = jQuery.curCSS( curElem, "backgroundColor" ); + } while ( + ( backgroundColor === "" || backgroundColor === "transparent" ) && + ( curElem = curElem.parentNode ) && + curElem.style + ); + + value = value.blend( backgroundColor && backgroundColor !== "transparent" ? + backgroundColor : + "_default" ); + } + + value = value.toRgbaString(); + } + try { + elem.style[ hook ] = value; + } catch( value ) { + // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit' + } + } + }; + jQuery.fx.step[ hook ] = function( fx ) { + if ( !fx.colorInit ) { + fx.start = color( fx.elem, hook ); + fx.end = color( fx.end ); + fx.colorInit = true; + } + jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) ); + }; +}); + +// detect rgba support +jQuery(function() { + var div = document.createElement( "div" ), + div_style = div.style; + + div_style.cssText = "background-color:rgba(1,1,1,.5)"; + support.rgba = div_style.backgroundColor.indexOf( "rgba" ) > -1; +}); + // Some named colors to work with // From Interface by Stefan Petre // http://interface.eyecon.ro/ - -var colors = { - aqua:[0,255,255], - azure:[240,255,255], - beige:[245,245,220], - black:[0,0,0], - blue:[0,0,255], - brown:[165,42,42], - cyan:[0,255,255], - darkblue:[0,0,139], - darkcyan:[0,139,139], - darkgrey:[169,169,169], - darkgreen:[0,100,0], - darkkhaki:[189,183,107], - darkmagenta:[139,0,139], - darkolivegreen:[85,107,47], - darkorange:[255,140,0], - darkorchid:[153,50,204], - darkred:[139,0,0], - darksalmon:[233,150,122], - darkviolet:[148,0,211], - fuchsia:[255,0,255], - gold:[255,215,0], - green:[0,128,0], - indigo:[75,0,130], - khaki:[240,230,140], - lightblue:[173,216,230], - lightcyan:[224,255,255], - lightgreen:[144,238,144], - lightgrey:[211,211,211], - lightpink:[255,182,193], - lightyellow:[255,255,224], - lime:[0,255,0], - magenta:[255,0,255], - maroon:[128,0,0], - navy:[0,0,128], - olive:[128,128,0], - orange:[255,165,0], - pink:[255,192,203], - purple:[128,0,128], - violet:[128,0,128], - red:[255,0,0], - silver:[192,192,192], - white:[255,255,255], - yellow:[255,255,0], - transparent: [255,255,255] +colors = jQuery.Color.names = { + aqua: "#00ffff", + azure: "#f0ffff", + beige: "#f5f5dc", + black: "#000000", + blue: "#0000ff", + brown: "#a52a2a", + cyan: "#00ffff", + darkblue: "#00008b", + darkcyan: "#008b8b", + darkgrey: "#a9a9a9", + darkgreen: "#006400", + darkkhaki: "#bdb76b", + darkmagenta: "#8b008b", + darkolivegreen: "#556b2f", + darkorange: "#ff8c00", + darkorchid: "#9932cc", + darkred: "#8b0000", + darksalmon: "#e9967a", + darkviolet: "#9400d3", + fuchsia: "#ff00ff", + gold: "#ffd700", + green: "#008000", + indigo: "#4b0082", + khaki: "#f0e68c", + lightblue: "#add8e6", + lightcyan: "#e0ffff", + lightgreen: "#90ee90", + lightgrey: "#d3d3d3", + lightpink: "#ffb6c1", + lightyellow: "#ffffe0", + lime: "#00ff00", + magenta: "#ff00ff", + maroon: "#800000", + navy: "#000080", + olive: "#808000", + orange: "#ffa500", + pink: "#ffc0cb", + purple: "#800080", + violet: "#800080", + red: "#ff0000", + silver: "#c0c0c0", + white: "#ffffff", + yellow: "#ffff00", + transparent: [ null, null, null, 0 ], + _default: "#ffffff" }; -})(); +})( jQuery ); + + /******************************************************************************/ /****************************** CLASS ANIMATIONS ******************************/ |