color.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. /*!
  2. * jQuery Color Animations v@VERSION
  3. * https://github.com/jquery/jquery-color
  4. *
  5. * Copyright 2013 jQuery Foundation and other contributors
  6. * Released under the MIT license.
  7. * http://jquery.org/license
  8. *
  9. * Date: @DATE
  10. */
  11. (function( jQuery, undefined ) {
  12. var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
  13. // plusequals test for += 100 -= 100
  14. rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
  15. // a set of RE's that can match strings and generate color tuples.
  16. stringParsers = [{
  17. re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
  18. parse: function( execResult ) {
  19. return [
  20. execResult[ 1 ],
  21. execResult[ 2 ],
  22. execResult[ 3 ],
  23. execResult[ 4 ]
  24. ];
  25. }
  26. }, {
  27. re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
  28. parse: function( execResult ) {
  29. return [
  30. execResult[ 1 ] * 2.55,
  31. execResult[ 2 ] * 2.55,
  32. execResult[ 3 ] * 2.55,
  33. execResult[ 4 ]
  34. ];
  35. }
  36. }, {
  37. // this regex ignores A-F because it's compared against an already lowercased string
  38. re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
  39. parse: function( execResult ) {
  40. return [
  41. parseInt( execResult[ 1 ], 16 ),
  42. parseInt( execResult[ 2 ], 16 ),
  43. parseInt( execResult[ 3 ], 16 )
  44. ];
  45. }
  46. }, {
  47. // this regex ignores A-F because it's compared against an already lowercased string
  48. re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
  49. parse: function( execResult ) {
  50. return [
  51. parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
  52. parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
  53. parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
  54. ];
  55. }
  56. }, {
  57. re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
  58. space: "hsla",
  59. parse: function( execResult ) {
  60. return [
  61. execResult[ 1 ],
  62. execResult[ 2 ] / 100,
  63. execResult[ 3 ] / 100,
  64. execResult[ 4 ]
  65. ];
  66. }
  67. }],
  68. // jQuery.Color( )
  69. color = jQuery.Color = function( color, green, blue, alpha ) {
  70. return new jQuery.Color.fn.parse( color, green, blue, alpha );
  71. },
  72. spaces = {
  73. rgba: {
  74. props: {
  75. red: {
  76. idx: 0,
  77. type: "byte"
  78. },
  79. green: {
  80. idx: 1,
  81. type: "byte"
  82. },
  83. blue: {
  84. idx: 2,
  85. type: "byte"
  86. }
  87. }
  88. },
  89. hsla: {
  90. props: {
  91. hue: {
  92. idx: 0,
  93. type: "degrees"
  94. },
  95. saturation: {
  96. idx: 1,
  97. type: "percent"
  98. },
  99. lightness: {
  100. idx: 2,
  101. type: "percent"
  102. }
  103. }
  104. }
  105. },
  106. propTypes = {
  107. "byte": {
  108. floor: true,
  109. max: 255
  110. },
  111. "percent": {
  112. max: 1
  113. },
  114. "degrees": {
  115. mod: 360,
  116. floor: true
  117. }
  118. },
  119. support = color.support = {},
  120. // element for support tests
  121. supportElem = jQuery( "<p>" )[ 0 ],
  122. // colors = jQuery.Color.names
  123. colors,
  124. // local aliases of functions called often
  125. each = jQuery.each;
  126. // determine rgba support immediately
  127. supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
  128. support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
  129. // define cache name and alpha properties
  130. // for rgba and hsla spaces
  131. each( spaces, function( spaceName, space ) {
  132. space.cache = "_" + spaceName;
  133. space.props.alpha = {
  134. idx: 3,
  135. type: "percent",
  136. def: 1
  137. };
  138. });
  139. function clamp( value, prop, allowEmpty ) {
  140. var type = propTypes[ prop.type ] || {};
  141. if ( value == null ) {
  142. return (allowEmpty || !prop.def) ? null : prop.def;
  143. }
  144. // ~~ is an short way of doing floor for positive numbers
  145. value = type.floor ? ~~value : parseFloat( value );
  146. // IE will pass in empty strings as value for alpha,
  147. // which will hit this case
  148. if ( isNaN( value ) ) {
  149. return prop.def;
  150. }
  151. if ( type.mod ) {
  152. // we add mod before modding to make sure that negatives values
  153. // get converted properly: -10 -> 350
  154. return (value + type.mod) % type.mod;
  155. }
  156. // for now all property types without mod have min and max
  157. return 0 > value ? 0 : type.max < value ? type.max : value;
  158. }
  159. function stringParse( string ) {
  160. var inst = color(),
  161. rgba = inst._rgba = [];
  162. string = string.toLowerCase();
  163. each( stringParsers, function( i, parser ) {
  164. var parsed,
  165. match = parser.re.exec( string ),
  166. values = match && parser.parse( match ),
  167. spaceName = parser.space || "rgba";
  168. if ( values ) {
  169. parsed = inst[ spaceName ]( values );
  170. // if this was an rgba parse the assignment might happen twice
  171. // oh well....
  172. inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
  173. rgba = inst._rgba = parsed._rgba;
  174. // exit each( stringParsers ) here because we matched
  175. return false;
  176. }
  177. });
  178. // Found a stringParser that handled it
  179. if ( rgba.length ) {
  180. // if this came from a parsed string, force "transparent" when alpha is 0
  181. // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
  182. if ( rgba.join() === "0,0,0,0" ) {
  183. jQuery.extend( rgba, colors.transparent );
  184. }
  185. return inst;
  186. }
  187. // named colors
  188. return colors[ string ];
  189. }
  190. color.fn = jQuery.extend( color.prototype, {
  191. parse: function( red, green, blue, alpha ) {
  192. if ( red === undefined ) {
  193. this._rgba = [ null, null, null, null ];
  194. return this;
  195. }
  196. if ( red.jquery || red.nodeType ) {
  197. red = jQuery( red ).css( green );
  198. green = undefined;
  199. }
  200. var inst = this,
  201. type = jQuery.type( red ),
  202. rgba = this._rgba = [];
  203. // more than 1 argument specified - assume ( red, green, blue, alpha )
  204. if ( green !== undefined ) {
  205. red = [ red, green, blue, alpha ];
  206. type = "array";
  207. }
  208. if ( type === "string" ) {
  209. return this.parse( stringParse( red ) || colors._default );
  210. }
  211. if ( type === "array" ) {
  212. each( spaces.rgba.props, function( key, prop ) {
  213. rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
  214. });
  215. return this;
  216. }
  217. if ( type === "object" ) {
  218. if ( red instanceof color ) {
  219. each( spaces, function( spaceName, space ) {
  220. if ( red[ space.cache ] ) {
  221. inst[ space.cache ] = red[ space.cache ].slice();
  222. }
  223. });
  224. } else {
  225. each( spaces, function( spaceName, space ) {
  226. var cache = space.cache;
  227. each( space.props, function( key, prop ) {
  228. // if the cache doesn't exist, and we know how to convert
  229. if ( !inst[ cache ] && space.to ) {
  230. // if the value was null, we don't need to copy it
  231. // if the key was alpha, we don't need to copy it either
  232. if ( key === "alpha" || red[ key ] == null ) {
  233. return;
  234. }
  235. inst[ cache ] = space.to( inst._rgba );
  236. }
  237. // this is the only case where we allow nulls for ALL properties.
  238. // call clamp with alwaysAllowEmpty
  239. inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
  240. });
  241. // everything defined but alpha?
  242. if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
  243. // use the default of 1
  244. inst[ cache ][ 3 ] = 1;
  245. if ( space.from ) {
  246. inst._rgba = space.from( inst[ cache ] );
  247. }
  248. }
  249. });
  250. }
  251. return this;
  252. }
  253. },
  254. is: function( compare ) {
  255. var is = color( compare ),
  256. same = true,
  257. inst = this;
  258. each( spaces, function( _, space ) {
  259. var localCache,
  260. isCache = is[ space.cache ];
  261. if (isCache) {
  262. localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
  263. each( space.props, function( _, prop ) {
  264. if ( isCache[ prop.idx ] != null ) {
  265. same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
  266. return same;
  267. }
  268. });
  269. }
  270. return same;
  271. });
  272. return same;
  273. },
  274. _space: function() {
  275. var used = [],
  276. inst = this;
  277. each( spaces, function( spaceName, space ) {
  278. if ( inst[ space.cache ] ) {
  279. used.push( spaceName );
  280. }
  281. });
  282. return used.pop();
  283. },
  284. transition: function( other, distance ) {
  285. var end = color( other ),
  286. spaceName = end._space(),
  287. space = spaces[ spaceName ],
  288. startColor = this.alpha() === 0 ? color( "transparent" ) : this,
  289. start = startColor[ space.cache ] || space.to( startColor._rgba ),
  290. result = start.slice();
  291. end = end[ space.cache ];
  292. each( space.props, function( key, prop ) {
  293. var index = prop.idx,
  294. startValue = start[ index ],
  295. endValue = end[ index ],
  296. type = propTypes[ prop.type ] || {};
  297. // if null, don't override start value
  298. if ( endValue === null ) {
  299. return;
  300. }
  301. // if null - use end
  302. if ( startValue === null ) {
  303. result[ index ] = endValue;
  304. } else {
  305. if ( type.mod ) {
  306. if ( endValue - startValue > type.mod / 2 ) {
  307. startValue += type.mod;
  308. } else if ( startValue - endValue > type.mod / 2 ) {
  309. startValue -= type.mod;
  310. }
  311. }
  312. result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
  313. }
  314. });
  315. return this[ spaceName ]( result );
  316. },
  317. blend: function( opaque ) {
  318. // if we are already opaque - return ourself
  319. if ( this._rgba[ 3 ] === 1 ) {
  320. return this;
  321. }
  322. var rgb = this._rgba.slice(),
  323. a = rgb.pop(),
  324. blend = color( opaque )._rgba;
  325. return color( jQuery.map( rgb, function( v, i ) {
  326. return ( 1 - a ) * blend[ i ] + a * v;
  327. }));
  328. },
  329. toRgbaString: function() {
  330. var prefix = "rgba(",
  331. rgba = jQuery.map( this._rgba, function( v, i ) {
  332. return v == null ? ( i > 2 ? 1 : 0 ) : v;
  333. });
  334. if ( rgba[ 3 ] === 1 ) {
  335. rgba.pop();
  336. prefix = "rgb(";
  337. }
  338. return prefix + rgba.join() + ")";
  339. },
  340. toHslaString: function() {
  341. var prefix = "hsla(",
  342. hsla = jQuery.map( this.hsla(), function( v, i ) {
  343. if ( v == null ) {
  344. v = i > 2 ? 1 : 0;
  345. }
  346. // catch 1 and 2
  347. if ( i && i < 3 ) {
  348. v = Math.round( v * 100 ) + "%";
  349. }
  350. return v;
  351. });
  352. if ( hsla[ 3 ] === 1 ) {
  353. hsla.pop();
  354. prefix = "hsl(";
  355. }
  356. return prefix + hsla.join() + ")";
  357. },
  358. toHexString: function( includeAlpha ) {
  359. var rgba = this._rgba.slice(),
  360. alpha = rgba.pop();
  361. if ( includeAlpha ) {
  362. rgba.push( ~~( alpha * 255 ) );
  363. }
  364. return "#" + jQuery.map( rgba, function( v ) {
  365. // default to 0 when nulls exist
  366. v = ( v || 0 ).toString( 16 );
  367. return v.length === 1 ? "0" + v : v;
  368. }).join("");
  369. },
  370. toString: function() {
  371. return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
  372. }
  373. });
  374. color.fn.parse.prototype = color.fn;
  375. // hsla conversions adapted from:
  376. // https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
  377. function hue2rgb( p, q, h ) {
  378. h = ( h + 1 ) % 1;
  379. if ( h * 6 < 1 ) {
  380. return p + (q - p) * h * 6;
  381. }
  382. if ( h * 2 < 1) {
  383. return q;
  384. }
  385. if ( h * 3 < 2 ) {
  386. return p + (q - p) * ((2/3) - h) * 6;
  387. }
  388. return p;
  389. }
  390. spaces.hsla.to = function ( rgba ) {
  391. if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
  392. return [ null, null, null, rgba[ 3 ] ];
  393. }
  394. var r = rgba[ 0 ] / 255,
  395. g = rgba[ 1 ] / 255,
  396. b = rgba[ 2 ] / 255,
  397. a = rgba[ 3 ],
  398. max = Math.max( r, g, b ),
  399. min = Math.min( r, g, b ),
  400. diff = max - min,
  401. add = max + min,
  402. l = add * 0.5,
  403. h, s;
  404. if ( min === max ) {
  405. h = 0;
  406. } else if ( r === max ) {
  407. h = ( 60 * ( g - b ) / diff ) + 360;
  408. } else if ( g === max ) {
  409. h = ( 60 * ( b - r ) / diff ) + 120;
  410. } else {
  411. h = ( 60 * ( r - g ) / diff ) + 240;
  412. }
  413. // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
  414. // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
  415. if ( diff === 0 ) {
  416. s = 0;
  417. } else if ( l <= 0.5 ) {
  418. s = diff / add;
  419. } else {
  420. s = diff / ( 2 - add );
  421. }
  422. return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
  423. };
  424. spaces.hsla.from = function ( hsla ) {
  425. if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
  426. return [ null, null, null, hsla[ 3 ] ];
  427. }
  428. var h = hsla[ 0 ] / 360,
  429. s = hsla[ 1 ],
  430. l = hsla[ 2 ],
  431. a = hsla[ 3 ],
  432. q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
  433. p = 2 * l - q;
  434. return [
  435. Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
  436. Math.round( hue2rgb( p, q, h ) * 255 ),
  437. Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
  438. a
  439. ];
  440. };
  441. each( spaces, function( spaceName, space ) {
  442. var props = space.props,
  443. cache = space.cache,
  444. to = space.to,
  445. from = space.from;
  446. // makes rgba() and hsla()
  447. color.fn[ spaceName ] = function( value ) {
  448. // generate a cache for this space if it doesn't exist
  449. if ( to && !this[ cache ] ) {
  450. this[ cache ] = to( this._rgba );
  451. }
  452. if ( value === undefined ) {
  453. return this[ cache ].slice();
  454. }
  455. var ret,
  456. type = jQuery.type( value ),
  457. arr = ( type === "array" || type === "object" ) ? value : arguments,
  458. local = this[ cache ].slice();
  459. each( props, function( key, prop ) {
  460. var val = arr[ type === "object" ? key : prop.idx ];
  461. if ( val == null ) {
  462. val = local[ prop.idx ];
  463. }
  464. local[ prop.idx ] = clamp( val, prop );
  465. });
  466. if ( from ) {
  467. ret = color( from( local ) );
  468. ret[ cache ] = local;
  469. return ret;
  470. } else {
  471. return color( local );
  472. }
  473. };
  474. // makes red() green() blue() alpha() hue() saturation() lightness()
  475. each( props, function( key, prop ) {
  476. // alpha is included in more than one space
  477. if ( color.fn[ key ] ) {
  478. return;
  479. }
  480. color.fn[ key ] = function( value ) {
  481. var vtype = jQuery.type( value ),
  482. fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
  483. local = this[ fn ](),
  484. cur = local[ prop.idx ],
  485. match;
  486. if ( vtype === "undefined" ) {
  487. return cur;
  488. }
  489. if ( vtype === "function" ) {
  490. value = value.call( this, cur );
  491. vtype = jQuery.type( value );
  492. }
  493. if ( value == null && prop.empty ) {
  494. return this;
  495. }
  496. if ( vtype === "string" ) {
  497. match = rplusequals.exec( value );
  498. if ( match ) {
  499. value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
  500. }
  501. }
  502. local[ prop.idx ] = value;
  503. return this[ fn ]( local );
  504. };
  505. });
  506. });
  507. // add cssHook and .fx.step function for each named hook.
  508. // accept a space separated string of properties
  509. color.hook = function( hook ) {
  510. var hooks = hook.split( " " );
  511. each( hooks, function( i, hook ) {
  512. jQuery.cssHooks[ hook ] = {
  513. set: function( elem, value ) {
  514. var parsed, curElem,
  515. backgroundColor = "";
  516. if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
  517. value = color( parsed || value );
  518. if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
  519. curElem = hook === "backgroundColor" ? elem.parentNode : elem;
  520. while (
  521. (backgroundColor === "" || backgroundColor === "transparent") &&
  522. curElem && curElem.style
  523. ) {
  524. try {
  525. backgroundColor = jQuery.css( curElem, "backgroundColor" );
  526. curElem = curElem.parentNode;
  527. } catch ( e ) {
  528. }
  529. }
  530. value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
  531. backgroundColor :
  532. "_default" );
  533. }
  534. value = value.toRgbaString();
  535. }
  536. try {
  537. elem.style[ hook ] = value;
  538. } catch( e ) {
  539. // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
  540. }
  541. }
  542. };
  543. jQuery.fx.step[ hook ] = function( fx ) {
  544. if ( !fx.colorInit ) {
  545. fx.start = color( fx.elem, hook );
  546. fx.end = color( fx.end );
  547. fx.colorInit = true;
  548. }
  549. jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
  550. };
  551. });
  552. };
  553. color.hook( stepHooks );
  554. jQuery.cssHooks.borderColor = {
  555. expand: function( value ) {
  556. var expanded = {};
  557. each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
  558. expanded[ "border" + part + "Color" ] = value;
  559. });
  560. return expanded;
  561. }
  562. };
  563. // Basic color names only.
  564. // Usage of any of the other color names requires adding yourself or including
  565. // jquery.color.svg-names.js.
  566. colors = jQuery.Color.names = {
  567. // 4.1. Basic color keywords
  568. aqua: "#00ffff",
  569. black: "#000000",
  570. blue: "#0000ff",
  571. fuchsia: "#ff00ff",
  572. gray: "#808080",
  573. green: "#008000",
  574. lime: "#00ff00",
  575. maroon: "#800000",
  576. navy: "#000080",
  577. olive: "#808000",
  578. purple: "#800080",
  579. red: "#ff0000",
  580. silver: "#c0c0c0",
  581. teal: "#008080",
  582. white: "#ffffff",
  583. yellow: "#ffff00",
  584. // 4.2.3. "transparent" color keyword
  585. transparent: [ null, null, null, 0 ],
  586. _default: "#ffffff"
  587. };
  588. }( jQuery ));