/js/colors.js

https://gitlab.com/Etern4l/BitcoinDice · JavaScript · 653 lines · 525 code · 75 blank · 53 comment · 109 complexity · 9121c7c7c12f913538978f18e0b7754f MD5 · raw file

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