2 * jQuery Color Animations v@VERSION
3 * https://github.com/jquery/jquery-color
5 * Copyright jQuery Foundation and other contributors
6 * Released under the MIT license.
7 * http://jquery.org/license
12 (function( root, factory ) {
13 if ( typeof define === "function" && define.amd) {
15 // AMD. Register as an anonymous module.
16 define( [ "jquery" ], factory);
17 } else if ( typeof exports === "object" ) {
18 module.exports = factory( require( "jquery" ) );
20 factory( root.jQuery );
22 })( this, function( jQuery, undefined ) {
24 var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
26 // plusequals test for += 100 -= 100
27 rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
28 // a set of RE's that can match strings and generate color tuples.
30 re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
31 parse: function( execResult ) {
40 re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
41 parse: function( execResult ) {
43 execResult[ 1 ] * 2.55,
44 execResult[ 2 ] * 2.55,
45 execResult[ 3 ] * 2.55,
50 // this regex ignores A-F because it's compared against an already lowercased string
51 re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
52 parse: function( execResult ) {
54 parseInt( execResult[ 1 ], 16 ),
55 parseInt( execResult[ 2 ], 16 ),
56 parseInt( execResult[ 3 ], 16 )
60 // this regex ignores A-F because it's compared against an already lowercased string
61 re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
62 parse: function( execResult ) {
64 parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
65 parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
66 parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
70 re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
72 parse: function( execResult ) {
75 execResult[ 2 ] / 100,
76 execResult[ 3 ] / 100,
83 color = jQuery.Color = function( color, green, blue, alpha ) {
84 return new jQuery.Color.fn.parse( color, green, blue, alpha );
134 support = color.support = {},
136 // element for support tests
137 supportElem = jQuery( "<p>" )[ 0 ],
139 // colors = jQuery.Color.names
142 // local aliases of functions called often
145 // determine rgba support immediately
146 supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
147 support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
149 // define cache name and alpha properties
150 // for rgba and hsla spaces
151 each( spaces, function( spaceName, space ) {
152 space.cache = "_" + spaceName;
153 space.props.alpha = {
160 function clamp( value, prop, allowEmpty ) {
161 var type = propTypes[ prop.type ] || {};
163 if ( value == null ) {
164 return (allowEmpty || !prop.def) ? null : prop.def;
167 // ~~ is an short way of doing floor for positive numbers
168 value = type.floor ? ~~value : parseFloat( value );
170 // IE will pass in empty strings as value for alpha,
171 // which will hit this case
172 if ( isNaN( value ) ) {
177 // we add mod before modding to make sure that negatives values
178 // get converted properly: -10 -> 350
179 return (value + type.mod) % type.mod;
182 // for now all property types without mod have min and max
183 return 0 > value ? 0 : type.max < value ? type.max : value;
186 function stringParse( string ) {
188 rgba = inst._rgba = [];
190 string = string.toLowerCase();
192 each( stringParsers, function( i, parser ) {
194 match = parser.re.exec( string ),
195 values = match && parser.parse( match ),
196 spaceName = parser.space || "rgba";
199 parsed = inst[ spaceName ]( values );
201 // if this was an rgba parse the assignment might happen twice
203 inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
204 rgba = inst._rgba = parsed._rgba;
206 // exit each( stringParsers ) here because we matched
211 // Found a stringParser that handled it
214 // if this came from a parsed string, force "transparent" when alpha is 0
215 // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
216 if ( rgba.join() === "0,0,0,0" ) {
217 jQuery.extend( rgba, colors.transparent );
223 return colors[ string ];
226 color.fn = jQuery.extend( color.prototype, {
227 parse: function( red, green, blue, alpha ) {
228 if ( red === undefined ) {
229 this._rgba = [ null, null, null, null ];
232 if ( red.jquery || red.nodeType ) {
233 red = jQuery( red ).css( green );
238 type = jQuery.type( red ),
239 rgba = this._rgba = [];
241 // more than 1 argument specified - assume ( red, green, blue, alpha )
242 if ( green !== undefined ) {
243 red = [ red, green, blue, alpha ];
247 if ( type === "string" ) {
248 return this.parse( stringParse( red ) || colors._default );
251 if ( type === "array" ) {
252 each( spaces.rgba.props, function( key, prop ) {
253 rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
258 if ( type === "object" ) {
259 if ( red instanceof color ) {
260 each( spaces, function( spaceName, space ) {
261 if ( red[ space.cache ] ) {
262 inst[ space.cache ] = red[ space.cache ].slice();
266 each( spaces, function( spaceName, space ) {
267 var cache = space.cache;
268 each( space.props, function( key, prop ) {
270 // if the cache doesn't exist, and we know how to convert
271 if ( !inst[ cache ] && space.to ) {
273 // if the value was null, we don't need to copy it
274 // if the key was alpha, we don't need to copy it either
275 if ( key === "alpha" || red[ key ] == null ) {
278 inst[ cache ] = space.to( inst._rgba );
281 // this is the only case where we allow nulls for ALL properties.
282 // call clamp with alwaysAllowEmpty
283 inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
286 // everything defined but alpha?
287 if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
288 // use the default of 1
289 inst[ cache ][ 3 ] = 1;
291 inst._rgba = space.from( inst[ cache ] );
299 is: function( compare ) {
300 var is = color( compare ),
304 each( spaces, function( _, space ) {
306 isCache = is[ space.cache ];
308 localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
309 each( space.props, function( _, prop ) {
310 if ( isCache[ prop.idx ] != null ) {
311 same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
323 each( spaces, function( spaceName, space ) {
324 if ( inst[ space.cache ] ) {
325 used.push( spaceName );
330 transition: function( other, distance ) {
331 var end = color( other ),
332 spaceName = end._space(),
333 space = spaces[ spaceName ],
334 startColor = this.alpha() === 0 ? color( "transparent" ) : this,
335 start = startColor[ space.cache ] || space.to( startColor._rgba ),
336 result = start.slice();
338 end = end[ space.cache ];
339 each( space.props, function( key, prop ) {
340 var index = prop.idx,
341 startValue = start[ index ],
342 endValue = end[ index ],
343 type = propTypes[ prop.type ] || {};
345 // if null, don't override start value
346 if ( endValue === null ) {
350 if ( startValue === null ) {
351 result[ index ] = endValue;
354 if ( endValue - startValue > type.mod / 2 ) {
355 startValue += type.mod;
356 } else if ( startValue - endValue > type.mod / 2 ) {
357 startValue -= type.mod;
360 result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
363 return this[ spaceName ]( result );
365 blend: function( opaque ) {
366 // if we are already opaque - return ourself
367 if ( this._rgba[ 3 ] === 1 ) {
371 var rgb = this._rgba.slice(),
373 blend = color( opaque )._rgba;
375 return color( jQuery.map( rgb, function( v, i ) {
376 return ( 1 - a ) * blend[ i ] + a * v;
379 toRgbaString: function() {
380 var prefix = "rgba(",
381 rgba = jQuery.map( this._rgba, function( v, i ) {
382 return v == null ? ( i > 2 ? 1 : 0 ) : v;
385 if ( rgba[ 3 ] === 1 ) {
390 return prefix + rgba.join() + ")";
392 toHslaString: function() {
393 var prefix = "hsla(",
394 hsla = jQuery.map( this.hsla(), function( v, i ) {
401 v = Math.round( v * 100 ) + "%";
406 if ( hsla[ 3 ] === 1 ) {
410 return prefix + hsla.join() + ")";
412 toHexString: function( includeAlpha ) {
413 var rgba = this._rgba.slice(),
416 if ( includeAlpha ) {
417 rgba.push( ~~( alpha * 255 ) );
420 return "#" + jQuery.map( rgba, function( v ) {
422 // default to 0 when nulls exist
423 v = ( v || 0 ).toString( 16 );
424 return v.length === 1 ? "0" + v : v;
427 toString: function() {
428 return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
431 color.fn.parse.prototype = color.fn;
433 // hsla conversions adapted from:
434 // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
436 function hue2rgb( p, q, h ) {
439 return p + (q - p) * h * 6;
445 return p + (q - p) * ((2/3) - h) * 6;
450 spaces.hsla.to = function ( rgba ) {
451 if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
452 return [ null, null, null, rgba[ 3 ] ];
454 var r = rgba[ 0 ] / 255,
458 max = Math.max( r, g, b ),
459 min = Math.min( r, g, b ),
467 } else if ( r === max ) {
468 h = ( 60 * ( g - b ) / diff ) + 360;
469 } else if ( g === max ) {
470 h = ( 60 * ( b - r ) / diff ) + 120;
472 h = ( 60 * ( r - g ) / diff ) + 240;
475 // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
476 // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
479 } else if ( l <= 0.5 ) {
482 s = diff / ( 2 - add );
484 return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
487 spaces.hsla.from = function ( hsla ) {
488 if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
489 return [ null, null, null, hsla[ 3 ] ];
491 var h = hsla[ 0 ] / 360,
495 q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
499 Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
500 Math.round( hue2rgb( p, q, h ) * 255 ),
501 Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
507 each( spaces, function( spaceName, space ) {
508 var props = space.props,
513 // makes rgba() and hsla()
514 color.fn[ spaceName ] = function( value ) {
516 // generate a cache for this space if it doesn't exist
517 if ( to && !this[ cache ] ) {
518 this[ cache ] = to( this._rgba );
520 if ( value === undefined ) {
521 return this[ cache ].slice();
525 type = jQuery.type( value ),
526 arr = ( type === "array" || type === "object" ) ? value : arguments,
527 local = this[ cache ].slice();
529 each( props, function( key, prop ) {
530 var val = arr[ type === "object" ? key : prop.idx ];
532 val = local[ prop.idx ];
534 local[ prop.idx ] = clamp( val, prop );
538 ret = color( from( local ) );
539 ret[ cache ] = local;
542 return color( local );
546 // makes red() green() blue() alpha() hue() saturation() lightness()
547 each( props, function( key, prop ) {
548 // alpha is included in more than one space
549 if ( color.fn[ key ] ) {
552 color.fn[ key ] = function( value ) {
553 var vtype = jQuery.type( value ),
554 fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
555 local = this[ fn ](),
556 cur = local[ prop.idx ],
559 if ( vtype === "undefined" ) {
563 if ( vtype === "function" ) {
564 value = value.call( this, cur );
565 vtype = jQuery.type( value );
567 if ( value == null && prop.empty ) {
570 if ( vtype === "string" ) {
571 match = rplusequals.exec( value );
573 value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
576 local[ prop.idx ] = value;
577 return this[ fn ]( local );
582 // add cssHook and .fx.step function for each named hook.
583 // accept a space separated string of properties
584 color.hook = function( hook ) {
585 var hooks = hook.split( " " );
586 each( hooks, function( i, hook ) {
587 jQuery.cssHooks[ hook ] = {
588 set: function( elem, value ) {
590 backgroundColor = "";
592 if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
593 value = color( parsed || value );
594 if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
595 curElem = hook === "backgroundColor" ? elem.parentNode : elem;
597 (backgroundColor === "" || backgroundColor === "transparent") &&
598 curElem && curElem.style
601 backgroundColor = jQuery.css( curElem, "backgroundColor" );
602 curElem = curElem.parentNode;
607 value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
612 value = value.toRgbaString();
615 elem.style[ hook ] = value;
617 // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
621 jQuery.fx.step[ hook ] = function( fx ) {
622 if ( !fx.colorInit ) {
623 fx.start = color( fx.elem, hook );
624 fx.end = color( fx.end );
627 jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
633 color.hook( stepHooks );
635 jQuery.cssHooks.borderColor = {
636 expand: function( value ) {
639 each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
640 expanded[ "border" + part + "Color" ] = value;
646 // Basic color names only.
647 // Usage of any of the other color names requires adding yourself or including
648 // jquery.color.svg-names.js.
649 colors = jQuery.Color.names = {
650 // 4.1. Basic color keywords
668 // 4.2.3. "transparent" color keyword
669 transparent: [ null, null, null, 0 ],