jquery.color.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. /*!
  2. * jQuery Color Animations v2.0pre
  3. * http://jquery.org/
  4. *
  5. * Copyright 2011 John Resig
  6. * Dual licensed under the MIT or GPL Version 2 licenses.
  7. * http://jquery.org/license
  8. */
  9. (function( jQuery, undefined ){
  10. var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color outlineColor".split(" "),
  11. // plusequals test for += 100 -= 100
  12. rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
  13. // a set of RE's that can match strings and generate color tuples.
  14. stringParsers = [{
  15. re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
  16. parse: function( execResult ) {
  17. return [
  18. execResult[ 1 ],
  19. execResult[ 2 ],
  20. execResult[ 3 ],
  21. execResult[ 4 ]
  22. ];
  23. }
  24. }, {
  25. re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
  26. parse: function( execResult ) {
  27. return [
  28. 2.55 * execResult[1],
  29. 2.55 * execResult[2],
  30. 2.55 * execResult[3],
  31. execResult[ 4 ]
  32. ];
  33. }
  34. }, {
  35. re: /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,
  36. parse: function( execResult ) {
  37. return [
  38. parseInt( execResult[ 1 ], 16 ),
  39. parseInt( execResult[ 2 ], 16 ),
  40. parseInt( execResult[ 3 ], 16 )
  41. ];
  42. }
  43. }, {
  44. re: /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,
  45. parse: function( execResult ) {
  46. return [
  47. parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
  48. parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
  49. parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
  50. ];
  51. }
  52. }, {
  53. re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/,
  54. space: "hsla",
  55. parse: function( execResult ) {
  56. return [
  57. execResult[1],
  58. execResult[2] / 100,
  59. execResult[3] / 100,
  60. execResult[4]
  61. ];
  62. }
  63. }],
  64. // jQuery.Color( )
  65. color = jQuery.Color = function( color, green, blue, alpha ) {
  66. return new jQuery.Color.fn.parse( color, green, blue, alpha );
  67. },
  68. spaces = {
  69. rgba: {
  70. cache: "_rgba",
  71. props: {
  72. red: {
  73. idx: 0,
  74. type: "byte",
  75. empty: true
  76. },
  77. green: {
  78. idx: 1,
  79. type: "byte",
  80. empty: true
  81. },
  82. blue: {
  83. idx: 2,
  84. type: "byte",
  85. empty: true
  86. },
  87. alpha: {
  88. idx: 3,
  89. type: "percent",
  90. def: 1
  91. }
  92. }
  93. },
  94. hsla: {
  95. cache: "_hsla",
  96. props: {
  97. hue: {
  98. idx: 0,
  99. type: "degrees",
  100. empty: true
  101. },
  102. saturation: {
  103. idx: 1,
  104. type: "percent",
  105. empty: true
  106. },
  107. lightness: {
  108. idx: 2,
  109. type: "percent",
  110. empty: true
  111. }
  112. }
  113. }
  114. },
  115. propTypes = {
  116. "byte": {
  117. floor: true,
  118. min: 0,
  119. max: 255
  120. },
  121. "percent": {
  122. min: 0,
  123. max: 1
  124. },
  125. "degrees": {
  126. mod: 360,
  127. floor: true
  128. }
  129. },
  130. rgbaspace = spaces.rgba.props,
  131. support = color.support = {},
  132. // colors = jQuery.Color.names
  133. colors,
  134. // local aliases of functions called often
  135. each = jQuery.each;
  136. spaces.hsla.props.alpha = rgbaspace.alpha;
  137. function clamp( value, prop, alwaysAllowEmpty ) {
  138. var type = propTypes[ prop.type ] || {},
  139. allowEmpty = prop.empty || alwaysAllowEmpty;
  140. if ( allowEmpty && value == null ) {
  141. return null;
  142. }
  143. if ( prop.def && value == null ) {
  144. return prop.def;
  145. }
  146. if ( type.floor ) {
  147. value = ~~value;
  148. } else {
  149. value = parseFloat( value );
  150. }
  151. if ( value == null || isNaN( value ) ) {
  152. return prop.def;
  153. }
  154. if ( type.mod ) {
  155. value = value % type.mod;
  156. // -10 -> 350
  157. return value < 0 ? type.mod + value : value;
  158. }
  159. // for now all property types without mod have min and max
  160. return type.min > value ? type.min : type.max < value ? type.max : value;
  161. }
  162. function stringParse( string ) {
  163. var inst = color(),
  164. rgba = inst._rgba = [];
  165. string = string.toLowerCase();
  166. each( stringParsers, function( i, parser ) {
  167. var match = parser.re.exec( string ),
  168. values = match && parser.parse( match ),
  169. parsed,
  170. spaceName = parser.space || "rgba",
  171. cache = spaces[ spaceName ].cache;
  172. if ( values ) {
  173. parsed = inst[ spaceName ]( values );
  174. // if this was an rgba parse the assignment might happen twice
  175. // oh well....
  176. inst[ cache ] = parsed[ cache ];
  177. rgba = inst._rgba = parsed._rgba;
  178. // exit each( stringParsers ) here because we matched
  179. return false;
  180. }
  181. });
  182. // Found a stringParser that handled it
  183. if ( rgba.length !== 0 ) {
  184. // if this came from a parsed string, force "transparent" when alpha is 0
  185. // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
  186. if ( Math.max.apply( Math, rgba ) === 0 ) {
  187. jQuery.extend( rgba, colors.transparent );
  188. }
  189. return inst;
  190. }
  191. // named colors / default - filter back through parse function
  192. if ( string = colors[ string ] ) {
  193. return string;
  194. }
  195. }
  196. color.fn = color.prototype = {
  197. constructor: color,
  198. parse: function( red, green, blue, alpha ) {
  199. if ( red === undefined ) {
  200. this._rgba = [ null, null, null, null ];
  201. return this;
  202. }
  203. if ( red instanceof jQuery || red.nodeType ) {
  204. red = red instanceof jQuery ? red.css( green ) : jQuery( red ).css( green );
  205. green = undefined;
  206. }
  207. var inst = this,
  208. type = jQuery.type( red ),
  209. rgba = this._rgba = [],
  210. source;
  211. // more than 1 argument specified - assume ( red, green, blue, alpha )
  212. if ( green !== undefined ) {
  213. red = [ red, green, blue, alpha ];
  214. type = "array";
  215. }
  216. if ( type === "string" ) {
  217. return this.parse( stringParse( red ) || colors._default );
  218. }
  219. if ( type === "array" ) {
  220. each( rgbaspace, function( key, prop ) {
  221. rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
  222. });
  223. return this;
  224. }
  225. if ( type === "object" ) {
  226. if ( red instanceof color ) {
  227. each( spaces, function( spaceName, space ) {
  228. if ( red[ space.cache ] ) {
  229. inst[ space.cache ] = red[ space.cache ].slice();
  230. }
  231. });
  232. } else {
  233. each( spaces, function( spaceName, space ) {
  234. each( space.props, function( key, prop ) {
  235. var cache = space.cache;
  236. // if the cache doesn't exist, and we know how to convert
  237. if ( !inst[ cache ] && space.to ) {
  238. // if the value was null, we don't need to copy it
  239. // if the key was alpha, we don't need to copy it either
  240. if ( red[ key ] == null || key === "alpha") {
  241. return;
  242. }
  243. inst[ cache ] = space.to( inst._rgba );
  244. }
  245. // this is the only case where we allow nulls for ALL properties.
  246. // call clamp with alwaysAllowEmpty
  247. inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
  248. });
  249. });
  250. }
  251. return this;
  252. }
  253. },
  254. is: function( compare ) {
  255. var is = color( compare ),
  256. same = true,
  257. myself = this;
  258. each( spaces, function( _, space ) {
  259. var isCache = is[ space.cache ],
  260. localCache;
  261. if (isCache) {
  262. localCache = myself[ space.cache ] || space.to && space.to( myself._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. start = this[ space.cache ] || space.to( this._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[ prop.idx ] = 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. return v == null ? ( i > 2 ? 1 : 0 ) : v;
  332. });
  333. if ( rgba[ 3 ] === 1 ) {
  334. rgba.pop();
  335. prefix = "rgb(";
  336. }
  337. return prefix + rgba.join(",") + ")";
  338. },
  339. toHslaString: function() {
  340. var prefix = "hsla(",
  341. hsla = jQuery.map( this.hsla(), function( v, i ) {
  342. if ( v == null ) {
  343. v = i > 2 ? 1 : 0;
  344. }
  345. // catch 1 and 2
  346. if ( i && i < 3 ) {
  347. v = Math.round( v * 100 ) + "%";
  348. }
  349. return v;
  350. });
  351. if ( hsla[ 3 ] === 1 ) {
  352. hsla.pop();
  353. prefix = "hsl(";
  354. }
  355. return prefix + hsla.join(",") + ")";
  356. },
  357. toHexString: function( includeAlpha ) {
  358. var rgba = this._rgba.slice(),
  359. alpha = rgba.pop();
  360. if ( includeAlpha ) {
  361. rgba.push( ~~( alpha * 255 ) );
  362. }
  363. return "#" + jQuery.map( rgba, function( v, i ) {
  364. // default to 0 when nulls exist
  365. v = ( v || 0 ).toString( 16 );
  366. return v.length === 1 ? "0" + v : v;
  367. }).join("");
  368. },
  369. toString: function() {
  370. return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
  371. }
  372. };
  373. color.fn.parse.prototype = color.fn;
  374. // hsla conversions adapted from:
  375. // http://www.google.com/codesearch/p#OAMlx_jo-ck/src/third_party/WebKit/Source/WebCore/inspector/front-end/Color.js&d=7&l=193
  376. function hue2rgb( p, q, h ) {
  377. h = ( h + 1 ) % 1;
  378. if ( h * 6 < 1 ) {
  379. return p + (q - p) * 6 * h;
  380. }
  381. if ( h * 2 < 1) {
  382. return q;
  383. }
  384. if ( h * 3 < 2 ) {
  385. return p + (q - p) * ((2/3) - h) * 6;
  386. }
  387. return p;
  388. }
  389. spaces.hsla.to = function ( rgba ) {
  390. if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
  391. return [ null, null, null, rgba[ 3 ] ];
  392. }
  393. var r = rgba[ 0 ] / 255,
  394. g = rgba[ 1 ] / 255,
  395. b = rgba[ 2 ] / 255,
  396. a = rgba[ 3 ],
  397. max = Math.max( r, g, b ),
  398. min = Math.min( r, g, b ),
  399. diff = max - min,
  400. add = max + min,
  401. l = add * 0.5,
  402. h, s;
  403. if ( min === max ) {
  404. h = 0;
  405. } else if ( r === max ) {
  406. h = ( 60 * ( g - b ) / diff ) + 360;
  407. } else if ( g === max ) {
  408. h = ( 60 * ( b - r ) / diff ) + 120;
  409. } else {
  410. h = ( 60 * ( r - g ) / diff ) + 240;
  411. }
  412. if ( l === 0 || l === 1 ) {
  413. s = l;
  414. } else if ( l <= 0.5 ) {
  415. s = diff / add;
  416. } else {
  417. s = diff / ( 2 - add );
  418. }
  419. return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
  420. };
  421. spaces.hsla.from = function ( hsla ) {
  422. if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
  423. return [ null, null, null, hsla[ 3 ] ];
  424. }
  425. var h = hsla[ 0 ] / 360,
  426. s = hsla[ 1 ],
  427. l = hsla[ 2 ],
  428. a = hsla[ 3 ],
  429. q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
  430. p = 2 * l - q,
  431. r, g, b;
  432. return [
  433. Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
  434. Math.round( hue2rgb( p, q, h ) * 255 ),
  435. Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
  436. a
  437. ];
  438. };
  439. each( spaces, function( spaceName, space ) {
  440. var props = space.props,
  441. cache = space.cache,
  442. to = space.to,
  443. from = space.from;
  444. // makes rgba() and hsla()
  445. color.fn[ spaceName ] = function( value ) {
  446. // generate a cache for this space if it doesn't exist
  447. if ( to && !this[ cache ] ) {
  448. this[ cache ] = to( this._rgba );
  449. }
  450. if ( value === undefined ) {
  451. return this[ cache ].slice();
  452. }
  453. var type = jQuery.type( value ),
  454. arr = ( type === "array" || type === "object" ) ? value : arguments,
  455. local = this[ cache ].slice(),
  456. ret;
  457. each( props, function( key, prop ) {
  458. var val = arr[ type === "object" ? key : prop.idx ];
  459. if ( val == null ) {
  460. val = local[ prop.idx ];
  461. }
  462. local[ prop.idx ] = clamp( val, prop );
  463. });
  464. if ( from ) {
  465. ret = color( from( local ) );
  466. ret[ cache ] = local;
  467. return ret;
  468. } else {
  469. return color( local );
  470. }
  471. };
  472. // makes red() green() blue() alpha() hue() saturation() lightness()
  473. each( props, function( key, prop ) {
  474. // alpha is included in more than one space
  475. if ( color.fn[ key ] ) {
  476. return;
  477. }
  478. color.fn[ key ] = function( value ) {
  479. var vtype = jQuery.type( value ),
  480. fn = ( key === 'alpha' ? ( this._hsla ? 'hsla' : 'rgba' ) : spaceName ),
  481. local = this[ fn ](),
  482. cur = local[ prop.idx ],
  483. match;
  484. if ( vtype === "undefined" ) {
  485. return cur;
  486. }
  487. if ( vtype === "function" ) {
  488. value = value.call( this, cur );
  489. vtype = jQuery.type( value );
  490. }
  491. if ( value == null && prop.empty ) {
  492. return this;
  493. }
  494. if ( vtype === "string" ) {
  495. match = rplusequals.exec( value );
  496. if ( match ) {
  497. value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
  498. }
  499. }
  500. local[ prop.idx ] = value;
  501. return this[ fn ]( local );
  502. };
  503. });
  504. });
  505. // add .fx.step functions
  506. each( stepHooks, function( i, hook ) {
  507. jQuery.cssHooks[ hook ] = {
  508. set: function( elem, value ) {
  509. var parsed, backgroundColor, curElem;
  510. if ( jQuery.type( value ) !== 'string' || ( parsed = stringParse( value ) ) )
  511. {
  512. value = color( parsed || value );
  513. if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
  514. curElem = hook === "backgroundColor" ? elem.parentNode : elem;
  515. do {
  516. backgroundColor = jQuery.curCSS( curElem, "backgroundColor" );
  517. } while (
  518. ( backgroundColor === "" || backgroundColor === "transparent" ) &&
  519. ( curElem = curElem.parentNode ) &&
  520. curElem.style
  521. );
  522. value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
  523. backgroundColor :
  524. "_default" );
  525. }
  526. value = value.toRgbaString();
  527. }
  528. elem.style[ hook ] = value;
  529. }
  530. };
  531. jQuery.fx.step[ hook ] = function( fx ) {
  532. if ( !fx.colorInit ) {
  533. fx.start = color( fx.elem, hook );
  534. fx.end = color( fx.end );
  535. fx.colorInit = true;
  536. }
  537. jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
  538. };
  539. });
  540. // detect rgba support
  541. jQuery(function() {
  542. var div = document.createElement( "div" ),
  543. div_style = div.style;
  544. div_style.cssText = "background-color:rgba(1,1,1,.5)";
  545. support.rgba = div_style.backgroundColor.indexOf( "rgba" ) > -1;
  546. });
  547. // Some named colors to work with
  548. // From Interface by Stefan Petre
  549. // http://interface.eyecon.ro/
  550. colors = jQuery.Color.names = {
  551. aqua: "#00ffff",
  552. azure: "#f0ffff",
  553. beige: "#f5f5dc",
  554. black: "#000000",
  555. blue: "#0000ff",
  556. brown: "#a52a2a",
  557. cyan: "#00ffff",
  558. darkblue: "#00008b",
  559. darkcyan: "#008b8b",
  560. darkgrey: "#a9a9a9",
  561. darkgreen: "#006400",
  562. darkkhaki: "#bdb76b",
  563. darkmagenta: "#8b008b",
  564. darkolivegreen: "#556b2f",
  565. darkorange: "#ff8c00",
  566. darkorchid: "#9932cc",
  567. darkred: "#8b0000",
  568. darksalmon: "#e9967a",
  569. darkviolet: "#9400d3",
  570. fuchsia: "#ff00ff",
  571. gold: "#ffd700",
  572. green: "#008000",
  573. indigo: "#4b0082",
  574. khaki: "#f0e68c",
  575. lightblue: "#add8e6",
  576. lightcyan: "#e0ffff",
  577. lightgreen: "#90ee90",
  578. lightgrey: "#d3d3d3",
  579. lightpink: "#ffb6c1",
  580. lightyellow: "#ffffe0",
  581. lime: "#00ff00",
  582. magenta: "#ff00ff",
  583. maroon: "#800000",
  584. navy: "#000080",
  585. olive: "#808000",
  586. orange: "#ffa500",
  587. pink: "#ffc0cb",
  588. purple: "#800080",
  589. violet: "#800080",
  590. red: "#ff0000",
  591. silver: "#c0c0c0",
  592. white: "#ffffff",
  593. yellow: "#ffff00",
  594. transparent: [ null, null, null, 0 ],
  595. _default: "#ffffff"
  596. };
  597. })( jQuery );