jquery.color.js 15 KB

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