not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

839 lines
26 KiB

  1. // Copyright (c) 2008-2013, Andrew Brehaut, Tim Baumann, Matt Wilson,
  2. // Simon Heimler, Michel Vielmetter
  3. //
  4. // All rights reserved.
  5. //
  6. // Redistribution and use in source and binary forms, with or without
  7. // modification, are permitted provided that the following conditions are met:
  8. //
  9. // * Redistributions of source code must retain the above copyright notice,
  10. // this list of conditions and the following disclaimer.
  11. // * Redistributions in binary form must reproduce the above copyright notice,
  12. // this list of conditions and the following disclaimer in the documentation
  13. // and/or other materials provided with the distribution.
  14. //
  15. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  19. // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  20. // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  21. // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  22. // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  23. // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  24. // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  25. // POSSIBILITY OF SUCH DAMAGE.
  26. // color.js - version 1.0.1
  27. //
  28. // HSV <-> RGB code based on code from http://www.cs.rit.edu/~ncs/color/t_convert.html
  29. // object function created by Douglas Crockford.
  30. // Color scheme degrees taken from the colorjack.com colorpicker
  31. //
  32. // HSL support kindly provided by Tim Baumann - http://github.com/timjb
  33. // create namespaces
  34. /*global net */
  35. if ("undefined" == typeof net) { var net = {}; }
  36. if (!net.brehaut) { net.brehaut = {}; }
  37. // this module function is called with net.brehaut as 'this'
  38. (function ( ) {
  39. "use strict";
  40. // Constants
  41. // css_colors maps color names onto their hex values
  42. // these names are defined by W3C
  43. var css_colors = {aliceblue:'#F0F8FF',antiquewhite:'#FAEBD7',aqua:'#00FFFF',aquamarine:'#7FFFD4',azure:'#F0FFFF',beige:'#F5F5DC',bisque:'#FFE4C4',black:'#000000',blanchedalmond:'#FFEBCD',blue:'#0000FF',blueviolet:'#8A2BE2',brown:'#A52A2A',burlywood:'#DEB887',cadetblue:'#5F9EA0',chartreuse:'#7FFF00',chocolate:'#D2691E',coral:'#FF7F50',cornflowerblue:'#6495ED',cornsilk:'#FFF8DC',crimson:'#DC143C',cyan:'#00FFFF',darkblue:'#00008B',darkcyan:'#008B8B',darkgoldenrod:'#B8860B',darkgray:'#A9A9A9',darkgrey:'#A9A9A9',darkgreen:'#006400',darkkhaki:'#BDB76B',darkmagenta:'#8B008B',darkolivegreen:'#556B2F',darkorange:'#FF8C00',darkorchid:'#9932CC',darkred:'#8B0000',darksalmon:'#E9967A',darkseagreen:'#8FBC8F',darkslateblue:'#483D8B',darkslategray:'#2F4F4F',darkslategrey:'#2F4F4F',darkturquoise:'#00CED1',darkviolet:'#9400D3',deeppink:'#FF1493',deepskyblue:'#00BFFF',dimgray:'#696969',dimgrey:'#696969',dodgerblue:'#1E90FF',firebrick:'#B22222',floralwhite:'#FFFAF0',forestgreen:'#228B22',fuchsia:'#FF00FF',gainsboro:'#DCDCDC',ghostwhite:'#F8F8FF',gold:'#FFD700',goldenrod:'#DAA520',gray:'#808080',grey:'#808080',green:'#008000',greenyellow:'#ADFF2F',honeydew:'#F0FFF0',hotpink:'#FF69B4',indianred:'#CD5C5C',indigo:'#4B0082',ivory:'#FFFFF0',khaki:'#F0E68C',lavender:'#E6E6FA',lavenderblush:'#FFF0F5',lawngreen:'#7CFC00',lemonchiffon:'#FFFACD',lightblue:'#ADD8E6',lightcoral:'#F08080',lightcyan:'#E0FFFF',lightgoldenrodyellow:'#FAFAD2',lightgray:'#D3D3D3',lightgrey:'#D3D3D3',lightgreen:'#90EE90',lightpink:'#FFB6C1',lightsalmon:'#FFA07A',lightseagreen:'#20B2AA',lightskyblue:'#87CEFA',lightslategray:'#778899',lightslategrey:'#778899',lightsteelblue:'#B0C4DE',lightyellow:'#FFFFE0',lime:'#00FF00',limegreen:'#32CD32',linen:'#FAF0E6',magenta:'#FF00FF',maroon:'#800000',mediumaquamarine:'#66CDAA',mediumblue:'#0000CD',mediumorchid:'#BA55D3',mediumpurple:'#9370D8',mediumseagreen:'#3CB371',mediumslateblue:'#7B68EE',mediumspringgreen:'#00FA9A',mediumturquoise:'#48D1CC',mediumvioletred:'#C71585',midnightblue:'#191970',mintcream:'#F5FFFA',mistyrose:'#FFE4E1',moccasin:'#FFE4B5',navajowhite:'#FFDEAD',navy:'#000080',oldlace:'#FDF5E6',olive:'#808000',olivedrab:'#6B8E23',orange:'#FFA500',orangered:'#FF4500',orchid:'#DA70D6',palegoldenrod:'#EEE8AA',palegreen:'#98FB98',paleturquoise:'#AFEEEE',palevioletred:'#D87093',papayawhip:'#FFEFD5',peachpuff:'#FFDAB9',peru:'#CD853F',pink:'#FFC0CB',plum:'#DDA0DD',powderblue:'#B0E0E6',purple:'#800080',red:'#FF0000',rosybrown:'#BC8F8F',royalblue:'#4169E1',saddlebrown:'#8B4513',salmon:'#FA8072',sandybrown:'#F4A460',seagreen:'#2E8B57',seashell:'#FFF5EE',sienna:'#A0522D',silver:'#C0C0C0',skyblue:'#87CEEB',slateblue:'#6A5ACD',slategray:'#708090',slategrey:'#708090',snow:'#FFFAFA',springgreen:'#00FF7F',steelblue:'#4682B4',tan:'#D2B48C',teal:'#008080',thistle:'#D8BFD8',tomato:'#FF6347',turquoise:'#40E0D0',violet:'#EE82EE',wheat:'#F5DEB3',white:'#FFFFFF',whitesmoke:'#F5F5F5',yellow:'#FFFF00',yellowgreen:'#9ACD32"'};
  44. // CSS value regexes, according to http://www.w3.org/TR/css3-values/
  45. var css_integer = '(?:\\+|-)?\\d+';
  46. var css_float = '(?:\\+|-)?\\d*\\.\\d+';
  47. var css_number = '(?:' + css_integer + ')|(?:' + css_float + ')';
  48. css_integer = '(' + css_integer + ')';
  49. css_float = '(' + css_float + ')';
  50. css_number = '(' + css_number + ')';
  51. var css_percentage = css_number + '%';
  52. var css_whitespace = '\\s*?';
  53. // http://www.w3.org/TR/2003/CR-css3-color-20030514/
  54. var hsl_hsla_regex = new RegExp([
  55. '^hsl(a?)\\(', css_number, ',', css_percentage, ',', css_percentage, '(,(', css_number, '))?\\)$'
  56. ].join(css_whitespace) );
  57. var rgb_rgba_integer_regex = new RegExp([
  58. '^rgb(a?)\\(', css_integer, ',', css_integer, ',', css_integer, '(,(', css_number, '))?\\)$'
  59. ].join(css_whitespace) );
  60. var rgb_rgba_percentage_regex = new RegExp([
  61. '^rgb(a?)\\(', css_percentage, ',', css_percentage, ',', css_percentage, '(,(', css_number, '))?\\)$'
  62. ].join(css_whitespace) );
  63. // Package wide variables
  64. // becomes the top level prototype object
  65. var color;
  66. /* registered_models contains the template objects for all the
  67. * models that have been registered for the color class.
  68. */
  69. var registered_models = [];
  70. /* factories contains methods to create new instance of
  71. * different color models that have been registered.
  72. */
  73. var factories = {};
  74. // Utility functions
  75. /* object is Douglas Crockfords object function for prototypal
  76. * inheritance.
  77. */
  78. if (!this.object) {
  79. this.object = function (o) {
  80. function F () { }
  81. F.prototype = o;
  82. return new F();
  83. };
  84. }
  85. var object = this.object;
  86. /* takes a value, converts to string if need be, then pads it
  87. * to a minimum length.
  88. */
  89. function pad ( val, len ) {
  90. val = val.toString();
  91. var padded = [];
  92. for (var i = 0, j = Math.max( len - val.length, 0); i < j; i++) {
  93. padded.push('0');
  94. }
  95. padded.push(val);
  96. return padded.join('');
  97. }
  98. /* takes a string and returns a new string with the first letter
  99. * capitalised
  100. */
  101. function capitalise ( s ) {
  102. return s.slice(0,1).toUpperCase() + s.slice(1);
  103. }
  104. /* removes leading and trailing whitespace
  105. */
  106. function trim ( str ) {
  107. return str.replace(/^\s+|\s+$/g, '');
  108. }
  109. /* used to apply a method to object non-destructively by
  110. * cloning the object and then apply the method to that
  111. * new object
  112. */
  113. function cloneOnApply( meth ) {
  114. return function ( ) {
  115. var cloned = this.clone();
  116. meth.apply(cloned, arguments);
  117. return cloned;
  118. };
  119. }
  120. /* registerModel is used to add additional representations
  121. * to the color code, and extend the color API with the new
  122. * operatiosn that model provides. see before for examples
  123. */
  124. function registerModel( name, model ) {
  125. var proto = object(color);
  126. var fields = []; // used for cloning and generating accessors
  127. var to_meth = 'to'+ capitalise(name);
  128. function convertAndApply( meth ) {
  129. return function ( ) {
  130. return meth.apply(this[to_meth](), arguments);
  131. };
  132. }
  133. for (var key in model) if (model.hasOwnProperty(key)) {
  134. proto[key] = model[key];
  135. var prop = proto[key];
  136. if (key.slice(0,1) == '_') { continue; }
  137. if (!(key in color) && "function" == typeof prop) {
  138. // the method found on this object is a) public and b) not
  139. // currently supported by the color object. Create an impl that
  140. // calls the toModel function and passes that new object
  141. // onto the correct method with the args.
  142. color[key] = convertAndApply(prop);
  143. }
  144. else if ("function" != typeof prop) {
  145. // we have found a public property. create accessor methods
  146. // and bind them up correctly
  147. fields.push(key);
  148. var getter = 'get'+capitalise(key);
  149. var setter = 'set'+capitalise(key);
  150. color[getter] = convertAndApply(
  151. proto[getter] = (function ( key ) {
  152. return function ( ) {
  153. return this[key];
  154. };
  155. })( key )
  156. );
  157. color[setter] = convertAndApply(
  158. proto[setter] = (function ( key ) {
  159. return function ( val ) {
  160. var cloned = this.clone();
  161. cloned[key] = val;
  162. return cloned;
  163. };
  164. })( key )
  165. );
  166. }
  167. } // end of for over model
  168. // a method to create a new object - largely so prototype chains dont
  169. // get insane. This uses an unrolled 'object' so that F is cached
  170. // for later use. this is approx a 25% speed improvement
  171. function F () { }
  172. F.prototype = proto;
  173. function factory ( ) {
  174. return new F();
  175. }
  176. factories[name] = factory;
  177. proto.clone = function () {
  178. var cloned = factory();
  179. for (var i = 0, j = fields.length; i < j; i++) {
  180. var key = fields[i];
  181. cloned[key] = this[key];
  182. }
  183. return cloned;
  184. };
  185. color[to_meth] = function ( ) {
  186. return factory();
  187. };
  188. registered_models.push(proto);
  189. return proto;
  190. }// end of registerModel
  191. // Template Objects
  192. /* color is the root object in the color hierarchy. It starts
  193. * life as a very simple object, but as color models are
  194. * registered it has methods programmatically added to manage
  195. * conversions as needed.
  196. */
  197. color = {
  198. /* fromObject takes an argument and delegates to the internal
  199. * color models to try to create a new instance.
  200. */
  201. fromObject: function ( o ) {
  202. if (!o) {
  203. return object(color);
  204. }
  205. for (var i = 0, j = registered_models.length; i < j; i++) {
  206. var nu = registered_models[i].fromObject(o);
  207. if (nu) {
  208. return nu;
  209. }
  210. }
  211. return object(color);
  212. },
  213. toString: function ( ) {
  214. return this.toCSS();
  215. }
  216. };
  217. var transparent = null; // defined with an RGB later.
  218. /* RGB is the red green blue model. This definition is converted
  219. * to a template object by registerModel.
  220. */
  221. registerModel('RGB', {
  222. red: 0,
  223. green: 0,
  224. blue: 0,
  225. alpha: 0,
  226. /* getLuminance returns a value between 0 and 1, this is the
  227. * luminance calcuated according to
  228. * http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html#RTFToC9
  229. */
  230. getLuminance: function ( ) {
  231. return (this.red * 0.2126) + (this.green * 0.7152) + (this.blue * 0.0722);
  232. },
  233. /* does an alpha based blend of color onto this. alpha is the
  234. * amount of 'color' to use. (0 to 1)
  235. */
  236. blend: function ( color , alpha ) {
  237. color = color.toRGB();
  238. alpha = Math.min(Math.max(alpha, 0), 1);
  239. var rgb = this.clone();
  240. rgb.red = (rgb.red * (1 - alpha)) + (color.red * alpha);
  241. rgb.green = (rgb.green * (1 - alpha)) + (color.green * alpha);
  242. rgb.blue = (rgb.blue * (1 - alpha)) + (color.blue * alpha);
  243. rgb.alpha = (rgb.alpha * (1 - alpha)) + (color.alpha * alpha);
  244. return rgb;
  245. },
  246. /* fromObject attempts to convert an object o to and RGB
  247. * instance. This accepts an object with red, green and blue
  248. * members or a string. If the string is a known CSS color name
  249. * or a hexdecimal string it will accept it.
  250. */
  251. fromObject: function ( o ) {
  252. if (o instanceof Array) {
  253. return this._fromRGBArray ( o );
  254. }
  255. if ("string" == typeof o) {
  256. return this._fromCSS( trim( o ) );
  257. }
  258. if (o.hasOwnProperty('red') &&
  259. o.hasOwnProperty('green') &&
  260. o.hasOwnProperty('blue')) {
  261. return this._fromRGB ( o );
  262. }
  263. // nothing matchs, not an RGB object
  264. },
  265. _stringParsers: [
  266. // CSS RGB(A) literal:
  267. function ( css ) {
  268. css = trim(css);
  269. var withInteger = match(rgb_rgba_integer_regex, 255);
  270. if(withInteger) {
  271. return withInteger;
  272. }
  273. return match(rgb_rgba_percentage_regex, 100);
  274. function match(regex, max_value) {
  275. var colorGroups = css.match( regex );
  276. // If there is an "a" after "rgb", there must be a fourth parameter and the other way round
  277. if (!colorGroups || (!!colorGroups[1] + !!colorGroups[5] === 1)) {
  278. return null;
  279. }
  280. var rgb = factories.RGB();
  281. rgb.red = Math.min(1, Math.max(0, colorGroups[2] / max_value));
  282. rgb.green = Math.min(1, Math.max(0, colorGroups[3] / max_value));
  283. rgb.blue = Math.min(1, Math.max(0, colorGroups[4] / max_value));
  284. rgb.alpha = !!colorGroups[5] ? Math.min(Math.max(parseFloat(colorGroups[6]), 0), 1) : 1;
  285. return rgb;
  286. }
  287. },
  288. function ( css ) {
  289. var lower = css.toLowerCase();
  290. if (lower in css_colors) {
  291. css = css_colors[lower];
  292. }
  293. if (!css.match(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/)) {
  294. return;
  295. }
  296. css = css.replace(/^#/,'');
  297. var bytes = css.length / 3;
  298. var max = Math.pow(16, bytes) - 1;
  299. var rgb = factories.RGB();
  300. rgb.red = parseInt(css.slice(0, bytes), 16) / max;
  301. rgb.green = parseInt(css.slice(bytes * 1,bytes * 2), 16) / max;
  302. rgb.blue = parseInt(css.slice(bytes * 2), 16) / max;
  303. rgb.alpha = 1;
  304. return rgb;
  305. },
  306. function ( css ) {
  307. if (css.toLowerCase() !== 'transparent') return;
  308. return transparent;
  309. }
  310. ],
  311. _fromCSS: function ( css ) {
  312. var color = null;
  313. for (var i = 0, j = this._stringParsers.length; i < j; i++) {
  314. color = this._stringParsers[i](css);
  315. if (color) return color;
  316. }
  317. },
  318. _fromRGB: function ( RGB ) {
  319. var newRGB = factories.RGB();
  320. newRGB.red = RGB.red;
  321. newRGB.green = RGB.green;
  322. newRGB.blue = RGB.blue;
  323. newRGB.alpha = RGB.hasOwnProperty('alpha') ? RGB.alpha : 1;
  324. return newRGB;
  325. },
  326. _fromRGBArray: function ( RGB ) {
  327. var newRGB = factories.RGB();
  328. newRGB.red = Math.max(0, Math.min(1, RGB[0] / 255));
  329. newRGB.green = Math.max(0, Math.min(1, RGB[1] / 255));
  330. newRGB.blue = Math.max(0, Math.min(1, RGB[2] / 255));
  331. newRGB.alpha = RGB[3] !== undefined ? Math.max(0, Math.min(1, RGB[3])) : 1;
  332. return newRGB;
  333. },
  334. // convert to a CSS string. defaults to two bytes a value
  335. toCSSHex: function ( bytes ) {
  336. bytes = bytes || 2;
  337. var max = Math.pow(16, bytes) - 1;
  338. var css = [
  339. "#",
  340. pad ( Math.round(this.red * max).toString( 16 ).toUpperCase(), bytes ),
  341. pad ( Math.round(this.green * max).toString( 16 ).toUpperCase(), bytes ),
  342. pad ( Math.round(this.blue * max).toString( 16 ).toUpperCase(), bytes )
  343. ];
  344. return css.join('');
  345. },
  346. toCSS: function ( bytes ) {
  347. if (this.alpha === 1) return this.toCSSHex(bytes);
  348. var max = 255;
  349. var components = [
  350. 'rgba(',
  351. Math.max(0, Math.min(max, Math.round(this.red * max))), ',',
  352. Math.max(0, Math.min(max, Math.round(this.green * max))), ',',
  353. Math.max(0, Math.min(max, Math.round(this.blue * max))), ',',
  354. Math.max(0, Math.min(1, this.alpha)),
  355. ')'
  356. ];
  357. return components.join('');
  358. },
  359. toHSV: function ( ) {
  360. var hsv = factories.HSV();
  361. var min, max, delta;
  362. min = Math.min(this.red, this.green, this.blue);
  363. max = Math.max(this.red, this.green, this.blue);
  364. hsv.value = max; // v
  365. delta = max - min;
  366. if( delta == 0 ) { // white, grey, black
  367. hsv.hue = hsv.saturation = 0;
  368. }
  369. else { // chroma
  370. hsv.saturation = delta / max;
  371. if( this.red == max ) {
  372. hsv.hue = ( this.green - this.blue ) / delta; // between yellow & magenta
  373. }
  374. else if( this.green == max ) {
  375. hsv.hue = 2 + ( this.blue - this.red ) / delta; // between cyan & yellow
  376. }
  377. else {
  378. hsv.hue = 4 + ( this.red - this.green ) / delta; // between magenta & cyan
  379. }
  380. hsv.hue = ((hsv.hue * 60) + 360) % 360; // degrees
  381. }
  382. hsv.alpha = this.alpha;
  383. return hsv;
  384. },
  385. toHSL: function ( ) {
  386. return this.toHSV().toHSL();
  387. },
  388. toRGB: function ( ) {
  389. return this.clone();
  390. }
  391. });
  392. transparent = color.fromObject({red: 0, blue: 0, green: 0, alpha: 0});
  393. /* Like RGB above, this object describes what will become the HSV
  394. * template object. This model handles hue, saturation and value.
  395. * hue is the number of degrees around the color wheel, saturation
  396. * describes how much color their is and value is the brightness.
  397. */
  398. registerModel('HSV', {
  399. hue: 0,
  400. saturation: 0,
  401. value: 1,
  402. alpha: 1,
  403. shiftHue: cloneOnApply(function ( degrees ) {
  404. var hue = (this.hue + degrees) % 360;
  405. if (hue < 0) {
  406. hue = (360 + hue) % 360;
  407. }
  408. this.hue = hue;
  409. }),
  410. devalueByAmount: cloneOnApply(function ( val ) {
  411. this.value = Math.min(1, Math.max(this.value - val, 0));
  412. }),
  413. devalueByRatio: cloneOnApply(function ( val ) {
  414. this.value = Math.min(1, Math.max(this.value * (1 - val), 0));
  415. }),
  416. valueByAmount: cloneOnApply(function ( val ) {
  417. this.value = Math.min(1, Math.max(this.value + val, 0));
  418. }),
  419. valueByRatio: cloneOnApply(function ( val ) {
  420. this.value = Math.min(1, Math.max(this.value * (1 + val), 0));
  421. }),
  422. desaturateByAmount: cloneOnApply(function ( val ) {
  423. this.saturation = Math.min(1, Math.max(this.saturation - val, 0));
  424. }),
  425. desaturateByRatio: cloneOnApply(function ( val ) {
  426. this.saturation = Math.min(1, Math.max(this.saturation * (1 - val), 0));
  427. }),
  428. saturateByAmount: cloneOnApply(function ( val ) {
  429. this.saturation = Math.min(1, Math.max(this.saturation + val, 0));
  430. }),
  431. saturateByRatio: cloneOnApply(function ( val ) {
  432. this.saturation = Math.min(1, Math.max(this.saturation * (1 + val), 0));
  433. }),
  434. schemeFromDegrees: function ( degrees ) {
  435. var newColors = [];
  436. for (var i = 0, j = degrees.length; i < j; i++) {
  437. var col = this.clone();
  438. col.hue = (this.hue + degrees[i]) % 360;
  439. newColors.push(col);
  440. }
  441. return newColors;
  442. },
  443. complementaryScheme: function ( ) {
  444. return this.schemeFromDegrees([0,180]);
  445. },
  446. splitComplementaryScheme: function ( ) {
  447. return this.schemeFromDegrees([0,150,320]);
  448. },
  449. splitComplementaryCWScheme: function ( ) {
  450. return this.schemeFromDegrees([0,150,300]);
  451. },
  452. splitComplementaryCCWScheme: function ( ) {
  453. return this.schemeFromDegrees([0,60,210]);
  454. },
  455. triadicScheme: function ( ) {
  456. return this.schemeFromDegrees([0,120,240]);
  457. },
  458. clashScheme: function ( ) {
  459. return this.schemeFromDegrees([0,90,270]);
  460. },
  461. tetradicScheme: function ( ) {
  462. return this.schemeFromDegrees([0,90,180,270]);
  463. },
  464. fourToneCWScheme: function ( ) {
  465. return this.schemeFromDegrees([0,60,180,240]);
  466. },
  467. fourToneCCWScheme: function ( ) {
  468. return this.schemeFromDegrees([0,120,180,300]);
  469. },
  470. fiveToneAScheme: function ( ) {
  471. return this.schemeFromDegrees([0,115,155,205,245]);
  472. },
  473. fiveToneBScheme: function ( ) {
  474. return this.schemeFromDegrees([0,40,90,130,245]);
  475. },
  476. fiveToneCScheme: function ( ) {
  477. return this.schemeFromDegrees([0,50,90,205,320]);
  478. },
  479. fiveToneDScheme: function ( ) {
  480. return this.schemeFromDegrees([0,40,155,270,310]);
  481. },
  482. fiveToneEScheme: function ( ) {
  483. return this.schemeFromDegrees([0,115,230,270,320]);
  484. },
  485. sixToneCWScheme: function ( ) {
  486. return this.schemeFromDegrees([0,30,120,150,240,270]);
  487. },
  488. sixToneCCWScheme: function ( ) {
  489. return this.schemeFromDegrees([0,90,120,210,240,330]);
  490. },
  491. neutralScheme: function ( ) {
  492. return this.schemeFromDegrees([0,15,30,45,60,75]);
  493. },
  494. analogousScheme: function ( ) {
  495. return this.schemeFromDegrees([0,30,60,90,120,150]);
  496. },
  497. fromObject: function ( o ) {
  498. if (o.hasOwnProperty('hue') &&
  499. o.hasOwnProperty('saturation') &&
  500. o.hasOwnProperty('value')) {
  501. var hsv = factories.HSV();
  502. hsv.hue = o.hue;
  503. hsv.saturation = o.saturation;
  504. hsv.value = o.value;
  505. hsv.alpha = o.hasOwnProperty('alpha') ? o.alpha : 1;
  506. return hsv;
  507. }
  508. // nothing matches, not an HSV object
  509. return null;
  510. },
  511. _normalise: function ( ) {
  512. this.hue %= 360;
  513. this.saturation = Math.min(Math.max(0, this.saturation), 1);
  514. this.value = Math.min(Math.max(0, this.value));
  515. this.alpha = Math.min(1, Math.max(0, this.alpha));
  516. },
  517. toRGB: function ( ) {
  518. this._normalise();
  519. var rgb = factories.RGB();
  520. var i;
  521. var f, p, q, t;
  522. if( this.saturation === 0 ) {
  523. // achromatic (grey)
  524. rgb.red = this.value;
  525. rgb.green = this.value;
  526. rgb.blue = this.value;
  527. rgb.alpha = this.alpha;
  528. return rgb;
  529. }
  530. var h = this.hue / 60; // sector 0 to 5
  531. i = Math.floor( h );
  532. f = h - i; // factorial part of h
  533. p = this.value * ( 1 - this.saturation );
  534. q = this.value * ( 1 - this.saturation * f );
  535. t = this.value * ( 1 - this.saturation * ( 1 - f ) );
  536. switch( i ) {
  537. case 0:
  538. rgb.red = this.value;
  539. rgb.green = t;
  540. rgb.blue = p;
  541. break;
  542. case 1:
  543. rgb.red = q;
  544. rgb.green = this.value;
  545. rgb.blue = p;
  546. break;
  547. case 2:
  548. rgb.red = p;
  549. rgb.green = this.value;
  550. rgb.blue = t;
  551. break;
  552. case 3:
  553. rgb.red = p;
  554. rgb.green = q;
  555. rgb.blue = this.value;
  556. break;
  557. case 4:
  558. rgb.red = t;
  559. rgb.green = p;
  560. rgb.blue = this.value;
  561. break;
  562. default: // case 5:
  563. rgb.red = this.value;
  564. rgb.green = p;
  565. rgb.blue = q;
  566. break;
  567. }
  568. rgb.alpha = this.alpha;
  569. return rgb;
  570. },
  571. toHSL: function() {
  572. this._normalise();
  573. var hsl = factories.HSL();
  574. hsl.hue = this.hue;
  575. var l = (2 - this.saturation) * this.value,
  576. s = this.saturation * this.value;
  577. if(l && 2 - l) {
  578. s /= (l <= 1) ? l : 2 - l;
  579. }
  580. l /= 2;
  581. hsl.saturation = s;
  582. hsl.lightness = l;
  583. hsl.alpha = this.alpha;
  584. return hsl;
  585. },
  586. toHSV: function ( ) {
  587. return this.clone();
  588. }
  589. });
  590. registerModel('HSL', {
  591. hue: 0,
  592. saturation: 0,
  593. lightness: 0,
  594. alpha: 1,
  595. darkenByAmount: cloneOnApply(function ( val ) {
  596. this.lightness = Math.min(1, Math.max(this.lightness - val, 0));
  597. }),
  598. darkenByRatio: cloneOnApply(function ( val ) {
  599. this.lightness = Math.min(1, Math.max(this.lightness * (1 - val), 0));
  600. }),
  601. lightenByAmount: cloneOnApply(function ( val ) {
  602. this.lightness = Math.min(1, Math.max(this.lightness + val, 0));
  603. }),
  604. lightenByRatio: cloneOnApply(function ( val ) {
  605. this.lightness = Math.min(1, Math.max(this.lightness * (1 + val), 0));
  606. }),
  607. fromObject: function ( o ) {
  608. if ("string" == typeof o) {
  609. return this._fromCSS( o );
  610. }
  611. if (o.hasOwnProperty('hue') &&
  612. o.hasOwnProperty('saturation') &&
  613. o.hasOwnProperty('lightness')) {
  614. return this._fromHSL ( o );
  615. }
  616. // nothing matchs, not an RGB object
  617. },
  618. _fromCSS: function ( css ) {
  619. var colorGroups = trim( css ).match( hsl_hsla_regex );
  620. // if there is an "a" after "hsl", there must be a fourth parameter and the other way round
  621. if (!colorGroups || (!!colorGroups[1] + !!colorGroups[5] === 1)) {
  622. return null;
  623. }
  624. var hsl = factories.HSL();
  625. hsl.hue = (colorGroups[2] % 360 + 360) % 360;
  626. hsl.saturation = Math.max(0, Math.min(parseInt(colorGroups[3], 10) / 100, 1));
  627. hsl.lightness = Math.max(0, Math.min(parseInt(colorGroups[4], 10) / 100, 1));
  628. hsl.alpha = !!colorGroups[5] ? Math.max(0, Math.min(1, parseFloat(colorGroups[6]))) : 1;
  629. return hsl;
  630. },
  631. _fromHSL: function ( HSL ) {
  632. var newHSL = factories.HSL();
  633. newHSL.hue = HSL.hue;
  634. newHSL.saturation = HSL.saturation;
  635. newHSL.lightness = HSL.lightness;
  636. newHSL.alpha = HSL.hasOwnProperty('alpha') ? HSL.alpha : 1;
  637. return newHSL;
  638. },
  639. _normalise: function ( ) {
  640. this.hue = (this.hue % 360 + 360) % 360;
  641. this.saturation = Math.min(Math.max(0, this.saturation), 1);
  642. this.lightness = Math.min(Math.max(0, this.lightness));
  643. this.alpha = Math.min(1, Math.max(0, this.alpha));
  644. },
  645. toHSL: function() {
  646. return this.clone();
  647. },
  648. toHSV: function() {
  649. this._normalise();
  650. var hsv = factories.HSV();
  651. // http://ariya.blogspot.com/2008/07/converting-between-hsl-and-hsv.html
  652. hsv.hue = this.hue; // H
  653. var l = 2 * this.lightness,
  654. s = this.saturation * ((l <= 1) ? l : 2 - l);
  655. hsv.value = (l + s) / 2; // V
  656. hsv.saturation = ((2 * s) / (l + s)) || 0; // S
  657. hsv.alpha = this.alpha;
  658. return hsv;
  659. },
  660. toRGB: function() {
  661. return this.toHSV().toRGB();
  662. }
  663. });
  664. // Package specific exports
  665. /* the Color function is a factory for new color objects.
  666. */
  667. function Color( o ) {
  668. return color.fromObject( o );
  669. }
  670. Color.isValid = function( str ) {
  671. var key, c = Color( str );
  672. var length = 0;
  673. for(key in c) {
  674. if(c.hasOwnProperty(key)) {
  675. length++;
  676. }
  677. }
  678. return length > 0;
  679. };
  680. net.brehaut.Color = Color;
  681. }).call(net.brehaut);
  682. /* Export to CommonJS
  683. */
  684. var module;
  685. if(module) {
  686. module.exports.Color = net.brehaut.Color;
  687. }