├── .gitignore
├── .travis.yml
├── Gruntfile.coffee
├── LICENSE
├── README.md
├── dist
├── c0lor.js
└── c0lor.min.js
├── package.json
├── src
├── LCh.coffee
├── Lab.coffee
├── RGBInt.coffee
├── XYZ.coffee
├── gamut.coffee
├── hsv.coffee
├── index.coffee
├── rgb.coffee
├── space
│ ├── lab.coffee
│ └── rgb.coffee
├── white.coffee
└── xyY.coffee
├── tasks
└── mdExtract.coffee
└── test
└── browser
├── jasmine
├── SpecRunner.html
└── lib
│ ├── jasmine-jsreporter.js
│ └── jasmine
│ ├── boot.js
│ ├── console.js
│ ├── jasmine-html.js
│ ├── jasmine.css
│ ├── jasmine.js
│ └── jasmine_favicon.png
└── src
├── LCh.coffee
├── Lab.coffee
├── README.md.spec.coffee
├── RGBInt.coffee
├── XYZ.coffee
├── console.coffee
├── gamut(node_only).coffee
├── hsv.coffee
├── indexToTest.coffee
├── matcher.coffee
├── rgb.coffee
├── space
├── lab.coffee
└── rgb.coffee
└── xyY.coffee
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /.idea
3 | /build
4 | /*.sh
5 | /coverage
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "stable"
4 | sudo: false
5 | before_deploy: "cd build/src"
6 |
7 | deploy:
8 | provider: npm
9 | email: hendrik@helwich.de
10 | api_key:
11 | secure: PUUs2ajF50L6HQk1n6UqGkCdooDyxrc3XdjdJ9+REnymGhJQ0/1f9xdjUVh1HDZ2lGh5WbGLiinUsB4YWYIqbT3AXpi+CpKYHB0xHxnaqs5EcL7WorarNSIY8gaZxvhBCbqkYTZFkzzhwBEg4mpQKfi9AMERPtWSujhWVN5vPuU=
12 | on:
13 | tags: true
14 | skip_cleanup: true
15 | env:
16 | global:
17 | - secure: yFiSsYp1lhq7ISOMP/ySpUm5tJdMSn+uqBhGIbg0+7rwCBHutAvVWrHkbp0G6QF6XyUZzFC2+iI/zTzmhl6FIpQEyw6Qvv30CcaYE3+6DG6Pw/NTkj7kCjwguuabKLePNknhYDYD0sN4S7EIvoY+zVMXcdanjXx0pWCtL1seCos=
18 | - secure: OEEQGxpKYqBJ1Sw7glV7HrYIYm6hp5/vsYAtPTMcEt1+Xu84qS9uxgyDA77XTe3IvdzoSrsKJT3nx9EmeKr6q7ChNuI+QwGx5oE29l97jGg/g2wkbcKHurwaxw204OrUO6yRyDC/AhsJDz5C7t1oUB86srwDokAeIIxKhJ6a+Tk=
19 |
--------------------------------------------------------------------------------
/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 | workDir = "build"
2 | srcDir = "src"
3 | testSrcDir = "test"
4 | distDir = "dist"
5 | pkg = require "./package"
6 |
7 |
8 | module.exports = (grunt) ->
9 |
10 | browsers = []
11 | browsers.push browser for browser in ({browserName: "iphone", version: "#{vers}"} for vers in ["5.1", "6.1", "7.1", "8.4", "9.2"])
12 | browsers.push browser for browser in ({browserName: "android", version: "#{vers}"} for vers in ["4.0", "4.4", "5.1"])
13 | browsers.push browser for browser in ({browserName: "safari", version: "#{vers}"} for vers in [5..9])
14 | browsers.push browser for browser in ({platform: "XP", browserName: "opera", version: "#{vers}"} for vers in [11..12])
15 | browsers.push browser for browser in ({platform: "XP", browserName: "googlechrome", version: "#{vers}"} for vers in [26..46] by 3)
16 | browsers.push browser for browser in ({platform: "linux", browserName: "firefox", version: "#{vers}"} for vers in [4..42] by 5)
17 | browsers.push browser for browser in ({browserName: "internet explorer", version: "#{vers}"} for vers in [6..11])
18 |
19 | grunt.initConfig
20 |
21 | pkg: pkg
22 |
23 | clean: ["#{workDir}"]
24 |
25 | watch:
26 | files: [
27 | "Gruntfile.*"
28 | "#{srcDir}/**/*.coffee"
29 | "#{testSrcDir}/**/*.coffee"
30 | "#{testSrcDir}/**/*.js"
31 | "#{testSrcDir}/**/*.html"
32 | ]
33 | tasks: ["default"]
34 |
35 | # Transcompile CoffeeScript to JavaScript files
36 | coffee:
37 | main:
38 | options:
39 | bare: true
40 | cwd: "#{srcDir}"
41 | expand: true
42 | src: ["**/*.coffee"]
43 | dest: "#{workDir}/#{srcDir}"
44 | ext: ".js"
45 | test:
46 | options:
47 | bare: true
48 | cwd: "#{testSrcDir}"
49 | expand: true
50 | src: ["**/*.coffee"]
51 | dest: "#{workDir}/#{testSrcDir}"
52 | ext: ".js"
53 |
54 |
55 | copy:
56 | markup:
57 | src: "*.md"
58 | dest: "#{workDir}/#{srcDir}/"
59 | jasmine:
60 | src: ["test/**/*.html", "test/**/*.js", "test/**/*.css"]
61 | dest: "#{workDir}/"
62 |
63 | browserify:
64 | dist:
65 | files: do ->
66 | files = {}
67 | files["#{distDir}/#{pkg.name}.js"] = ["#{workDir}/#{srcDir}/index.js"]
68 | files
69 | tests:
70 | files: do ->
71 | files = {}
72 | files["#{workDir}/#{testSrcDir}/browser/#{pkg.name}Spec.js"] = ["#{workDir}/#{testSrcDir}/browser/src/**/*.js"]
73 | files
74 |
75 | uglify:
76 | dist:
77 | files:do ->
78 | files = {}
79 | files["#{distDir}/#{pkg.name}.min.js"] = ["#{distDir}/#{pkg.name}.js"]
80 | files
81 |
82 | usebanner:
83 | taskName:
84 | options:
85 | position: "top"
86 | banner: "// <%= pkg.name %> v<%= pkg.version %> | (c) 2013-<%= grunt.template.today('yyyy') %> <%= pkg.author %> | <%= pkg.license %> License\n'use strict';"
87 | linebreak: false
88 | files:
89 | src: [ "#{distDir}/#{pkg.name}.min.js", "#{distDir}/#{pkg.name}.js" ]
90 |
91 |
92 | connect:
93 | server:
94 | options:
95 | base: ""
96 | port: 9999
97 | #hostname: "*"
98 |
99 |
100 | 'saucelabs-jasmine':
101 | all:
102 | options:
103 | urls: ["http://127.0.0.1:9999/build/test/browser/jasmine/SpecRunner.html"]
104 | tunnelTimeout: 5
105 | build: process.env.TRAVIS_JOB_ID
106 | concurrency: 3
107 | browsers: browsers
108 | testname: "browser black box tests"
109 | tags: ["master"]
110 | statusCheckAttempts: 450 # Each test has statusCheckAttempts * 2 seconds to complete (900s = 15 min)
111 |
112 | mdExtract:
113 | foo:
114 | src: ["README.md"]
115 | dest: "build/test/browser/src"
116 |
117 | # Loading dependencies
118 | for name of pkg.devDependencies
119 | if name != "grunt" and name != "grunt-cli" and (name.indexOf "grunt") == 0
120 | grunt.loadNpmTasks name
121 |
122 | grunt.registerTask "travis", ["clean", "coffee", "copy", "adaptPackageJson", "mdExtract", "browserify", "uglify", "usebanner", "connect", "saucelabs-jasmine"]
123 | grunt.registerTask "dev", ["clean", "coffee", "copy", "adaptPackageJson", "mdExtract", "browserify", "uglify", "usebanner", "connect", "watch"]
124 | grunt.registerTask "default", [ "dev"]
125 |
126 | grunt.loadTasks "tasks"
127 |
128 | grunt.registerTask "adaptPackageJson", ->
129 | fs = require "fs"
130 | tmpPkg = require "./package"
131 |
132 | tmpPkg.devDependencies = {}
133 | delete tmpPkg.private
134 | delete tmpPkg.scripts
135 | fs.writeFileSync "./build/src/package.json", JSON.stringify tmpPkg, null, 2
136 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Hendrik Helwich
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | c0lor [](http://badge.fury.io/js/c0lor) [](https://travis-ci.org/hhelwich/c0lor) [](https://coveralls.io/r/hhelwich/c0lor)
2 | =====
3 |
4 | A color space conversion library to use in the browser or node.js. It currently contains:
5 |
6 | * RGB, HSV color space
7 | * XYZ, xyY color space
8 | * Lab, LCh color space
9 |
10 |
11 | Browser
12 | -------
13 |
14 | To use the library in the browser, you need to include [this](https://raw.github.com/hhelwich/c0lor/master/dist/c0lor.min.js) JavaScript file:
15 |
16 | ```html
17 |
18 | ```
19 |
20 | It exports the global `c0lor` object. You can create an alias variable if you like:
21 |
22 | ```javascript
23 | var C = c0lor;
24 | ```
25 |
26 | The following browsers are tested:
27 |
28 | [](https://saucelabs.com/u/hhelwich-c0lor)
29 |
30 |
31 | node.js
32 | -------
33 |
34 | You can install this package with:
35 |
36 | ```
37 | npm install c0lor
38 | ```
39 |
40 | You can load the global module:
41 |
42 | ```javascript
43 | var C = require('c0lor');
44 | ```
45 |
46 | You can also require directly on submodules
47 | (can e.g. be useful with [browserify](https://github.com/substack/node-browserify)):
48 |
49 | ```javascript
50 | var rgb = require('c0lor/rgb'); // same as: require('c0lor').rgb;
51 | ```
52 |
53 |
54 | Examples
55 | ========
56 |
57 | Color Creation
58 | --------------
59 |
60 | ```javascript
61 | var cyan_rgb, red_RGB, yellow_RGB, magenta_hsv,
62 | green_XYZ, orange_xyY, purple_Lab, blue_LCh;
63 |
64 | // create RGB colors:
65 | cyan_rgb = C.rgb(0, 1, 1);
66 | red_RGB = C.RGB(255, 0, 0);
67 | yellow_RGB = C.RGB().hex('FFFF00');
68 |
69 | // create HSV color:
70 | magenta_hsv = C.hsv(0.8, 1, 1);
71 |
72 | // create XYZ and xyY color:
73 | green_XYZ = C.XYZ(0.4, 0.7, 0.2);
74 | orange_xyY = C.xyY(0.5, 0.4, 0.3);
75 |
76 | // create Lab and LCh color:
77 | purple_Lab = C.Lab(31, 24, -22);
78 | blue_LCh = C.LCh(30, 56, -1);
79 | ```
80 |
81 | Simple Conversions
82 | ------------------
83 |
84 | ```javascript
85 | var cyan_RGB, red_hexStr, yellow_rgb, magenta_rgb, yellow_hsv,
86 | green_xyY, orange_XYZ, purple_LCh, blue_Lab;
87 |
88 | // convert between RGB color spaces:
89 | cyan_RGB = cyan_rgb.RGB();
90 | red_hexStr = red_RGB.hex();
91 | yellow_rgb = yellow_RGB.rgb();
92 |
93 | // convert between hsv and rgb color space:
94 | magenta_rgb = magenta_hsv.rgb();
95 | yellow_hsv = yellow_rgb.hsv();
96 |
97 | // convert between XYZ and xyY color space:
98 | green_xyY = green_XYZ.xyY();
99 | orange_XYZ = orange_xyY.XYZ();
100 |
101 | // convert between Lab and LCh color space:
102 | purple_LCh = purple_Lab.LCh();
103 | blue_Lab = blue_LCh.Lab();
104 | ```
105 |
106 | Instead of creating a new color, you can also pass an existing color which will be used to store the result:
107 |
108 | ```javascript
109 | // convert hsv to rgb color:
110 | hsvColor.rgb(rgbColor);
111 | ```
112 |
113 | rgb ↔ XYZ Conversion
114 | --------------------
115 |
116 | To convert between rgb and XYZ color spaces you need to pick one of the predefined rgb color space definitions or create
117 | one by yourself.
118 | The following rgb color spaces are included:
119 |
120 | Adobe-98, Adobe-RGB, CIE-RGB, ColorMatch, EBU-Monitor, ECI-RGB, HDTV, Kodak-DC, NTSC-53, PAL-SECAM, sRGB, WideGamut
121 |
122 | Use predefined rgb color spaces to convert colors:
123 |
124 | ```javascript
125 | var sRGB, adobe98, cyan_XYZ;
126 |
127 | // create alias to rgb color spaces:
128 | sRGB = C.space.rgb.sRGB;
129 | adobe98 = C.space.rgb['Adobe-98'];
130 |
131 | // convert color from sRGB to XYZ color space:
132 | cyan_XYZ = sRGB.XYZ(cyan_rgb);
133 | // convert color from XYZ to Adobe-98 color space:
134 | cyan_rgb = adobe98.rgb(cyan_XYZ);
135 | ```
136 |
137 | Instead of creating a new color, you can also pass an existing color which will be used to store the result:
138 |
139 | ```javascript
140 | // convert color from XYZ to Adobe-98 color space:
141 | adobe98.rgb(cyan_XYZ, cyan_rgb);
142 | ```
143 |
144 | You can create your own rgb color space if you like:
145 |
146 | ```javascript
147 | var myRed, myGreen, myBlue, myRgbSpace;
148 |
149 | myRed = C.xyY(0.6, 0.3);
150 | myGreen = C.xyY(0.2, 0.7);
151 | myBlue = C.xyY(0.1, 0.1);
152 |
153 | myRgbSpace = C.space.rgb(myRed, myGreen, myBlue, C.white.D65, 2.1);
154 | ```
155 |
156 | Instead of giving a gamma value you can also give a gamma function and its inverse function:
157 |
158 | ```javascript
159 | myRgbSpace = C.space.rgb(myRed, myGreen, myBlue, C.white.D65, function (x) {
160 | return Math.pow(x, 2.1);
161 | }, function (x) {
162 | return Math.pow(x, 1 / 2.1);
163 | });
164 | ```
165 |
166 | The following white points (in XYZ color space) are included:
167 |
168 | A, B, C, D50, D55, D65, D75, E, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12
169 |
170 |
171 | Lab ↔ XYZ Conversion
172 | --------------------
173 |
174 | Convert between Lab and XYZ color space:
175 |
176 | ```javascript
177 | var lab50, purple_XYZ, green_Lab;
178 |
179 | // create Lab color space with D50 white point
180 | labD50 = C.space.lab(C.white.D50);
181 |
182 | // convert color from Lab to XYZ color space:
183 | purple_XYZ = labD50.XYZ(purple_Lab);
184 | // convert color from XYZ to Lab color space:
185 | green_Lab = labD50.Lab(green_XYZ);
186 | ```
187 |
188 | Instead of creating a new color, you can also pass an existing color which will be used to store the result:
189 |
190 | ```javascript
191 | // convert color from XYZ to Lab color space:
192 | labD50.Lab(green_XYZ, green_Lab);
193 | ```
194 |
--------------------------------------------------------------------------------
/dist/c0lor.js:
--------------------------------------------------------------------------------
1 | // c0lor v0.1.1 | (c) 2013-2021 Hendrik Helwich | MIT License
2 | 'use strict';(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o e3) {
347 | return pow(w, 1 / 3);
348 | } else {
349 | return foo * w + N;
350 | }
351 | };
352 |
353 | fInv = function(w) {
354 | if (w > e) {
355 | return pow(w, 3);
356 | } else {
357 | return (w - N) / foo;
358 | }
359 | };
360 |
361 | fDeriv = function(w) {
362 | if (w > e3) {
363 | return 1 / (3 * (pow(w, 2 / 3)));
364 | } else {
365 | return foo;
366 | }
367 | };
368 |
369 | labCsPrototype = {
370 | Lab: function(XYZ, T) {
371 | var l;
372 | if (T == null) {
373 | T = Lab();
374 | }
375 | l = f(XYZ.Y / this.white.Y);
376 | T.L = 116 * l - 16;
377 | T.a = 500 * ((f(XYZ.X / this.white.X)) - l);
378 | T.b = 200 * (l - (f(XYZ.Z / this.white.Z)));
379 | return T;
380 | },
381 | XYZ: function(Lab, T) {
382 | var fy;
383 | if (T == null) {
384 | T = xyz();
385 | }
386 | fy = (Lab.L + 16) / 116;
387 | T.X = (fInv(fy + Lab.a / 500)) * this.white.X;
388 | T.Y = (fInv(fy)) * this.white.Y;
389 | T.Z = (fInv(fy - Lab.b / 200)) * this.white.Z;
390 | return T;
391 | },
392 | LabderivL: function(Y) {
393 | return 116 * (fDeriv(Y / this.white.Y)) / this.white.Y;
394 | }
395 | };
396 |
397 | module.exports = O((function(white) {
398 | this.white = white;
399 | }), labCsPrototype);
400 |
401 | },{"../Lab":2,"../XYZ":4,"ut1l/create/object":16}],9:[function(require,module,exports){
402 | var M, O, XYZ, createSpace, gammaSRgb, gammaSRgbInv, lazyInitRgbBase, lu, pow, rgbSpaceConstructor, rgbSpacePrototype, white, xyY;
403 |
404 | O = require("ut1l/create/object");
405 |
406 | M = require("m4th/matrix");
407 |
408 | lu = require("m4th/lu");
409 |
410 | white = require("../white");
411 |
412 | xyY = require("../xyY");
413 |
414 | XYZ = require("../XYZ");
415 |
416 | pow = Math.pow;
417 |
418 | gammaSRgb = function(x) {
419 | if (x <= 0.04045) {
420 | return x / 12.92;
421 | } else {
422 | return pow((x + 0.055) / 1.055, 2.4);
423 | }
424 | };
425 |
426 | gammaSRgbInv = function(x) {
427 | if (x <= 0.0031308) {
428 | return x * 12.92;
429 | } else {
430 | return 1.055 * (pow(x, 1 / 2.4)) - 0.055;
431 | }
432 | };
433 |
434 | lazyInitRgbBase = function() {
435 | var bxyz, bxyzLU, w;
436 | bxyz = M([this.red.x, this.green.x, this.blue.x, this.red.y, this.green.y, this.blue.y, 1 - this.red.x - this.red.y, 1 - this.green.x - this.green.y, 1 - this.blue.x - this.blue.y]);
437 | bxyzLU = lu(bxyz);
438 | w = M(3, [this.white.X, this.white.Y, this.white.Z]);
439 | bxyzLU.solve(w, w);
440 | bxyz = bxyz.mult(M.diag(w.array));
441 | this.base = bxyz.array;
442 | this.baseInv = (lu(bxyz)).getInverse().array;
443 | delete this.XYZ;
444 | delete this.rgb;
445 | };
446 |
447 | rgbSpaceConstructor = function(red, green, blue, white1, gamma, gammaInv) {
448 | this.red = red;
449 | this.green = green;
450 | this.blue = blue;
451 | this.white = white1;
452 | if (typeof gamma === "function") {
453 | this.gamma = gamma;
454 | this.gammaInv = gammaInv;
455 | } else {
456 | this.g = gamma;
457 | this.gInv = 1 / gamma;
458 | }
459 | this.rgb = function() {
460 | lazyInitRgbBase.call(this);
461 | return this.rgb.apply(this, arguments);
462 | };
463 | this.XYZ = function() {
464 | lazyInitRgbBase.call(this);
465 | return this.XYZ.apply(this, arguments);
466 | };
467 | };
468 |
469 | rgbSpacePrototype = {
470 | gamma: function(x) {
471 | return pow(x, this.g);
472 | },
473 | gammaInv: function(x) {
474 | return pow(x, this.gInv);
475 | },
476 | rgb: function(XYZ, T) {
477 | var a;
478 | if (T == null) {
479 | T = require("../rgb")();
480 | }
481 | a = this.baseInv;
482 | T.r = this.gammaInv(a[0] * XYZ.X + a[1] * XYZ.Y + a[2] * XYZ.Z);
483 | T.g = this.gammaInv(a[3] * XYZ.X + a[4] * XYZ.Y + a[5] * XYZ.Z);
484 | T.b = this.gammaInv(a[6] * XYZ.X + a[7] * XYZ.Y + a[8] * XYZ.Z);
485 | return T;
486 | },
487 | XYZ: function(Rgb, T) {
488 | var a, gb, gg, gr;
489 | if (T == null) {
490 | T = XYZ();
491 | }
492 | a = this.base;
493 | gr = this.gamma(Rgb.r);
494 | gg = this.gamma(Rgb.g);
495 | gb = this.gamma(Rgb.b);
496 | T.X = a[0] * gr + a[1] * gg + a[2] * gb;
497 | T.Y = a[3] * gr + a[4] * gg + a[5] * gb;
498 | T.Z = a[6] * gr + a[7] * gg + a[8] * gb;
499 | return T;
500 | }
501 | };
502 |
503 | createSpace = O(rgbSpaceConstructor, rgbSpacePrototype);
504 |
505 | createSpace["Adobe-98"] = createSpace(xyY(0.6400, 0.3300), xyY(0.2100, 0.7100), xyY(0.1500, 0.0600), white.D65, 2.2);
506 |
507 | createSpace["Adobe-RGB"] = createSpace(xyY(0.6250, 0.3400), xyY(0.2800, 0.5950), xyY(0.1550, 0.0700), white.D65, 1.8);
508 |
509 | createSpace["CIE-RGB"] = createSpace(xyY(0.7350, 0.2650), xyY(0.2740, 0.7170), xyY(0.1670, 0.0090), white.E, 1);
510 |
511 | createSpace["ColorMatch"] = createSpace(xyY(0.6300, 0.3400), xyY(0.2950, 0.6050), xyY(0.1500, 0.0750), white.D50, 1.8);
512 |
513 | createSpace["EBU-Monitor"] = createSpace(xyY(0.6314, 0.3391), xyY(0.2809, 0.5971), xyY(0.1487, 0.0645), white.D50, 1.9);
514 |
515 | createSpace["ECI-RGB"] = createSpace(xyY(0.6700, 0.3300), xyY(0.2100, 0.7100), xyY(0.1400, 0.0800), white.D50, 1.8);
516 |
517 | createSpace["HDTV"] = createSpace(xyY(0.6400, 0.3300), xyY(0.2900, 0.6000), xyY(0.1500, 0.0600), white.D65, 2.2);
518 |
519 | createSpace["Kodak-DC"] = createSpace(xyY(0.6492, 0.3314), xyY(0.3219, 0.5997), xyY(0.1548, 0.0646), white.D50, 2.22);
520 |
521 | createSpace["NTSC-53"] = createSpace(xyY(0.6700, 0.3300), xyY(0.2100, 0.7100), xyY(0.1400, 0.0800), white.C, 2.2);
522 |
523 | createSpace["PAL-SECAM"] = createSpace(xyY(0.6400, 0.3300), xyY(0.2900, 0.6000), xyY(0.1500, 0.0600), white.D65, 2.2);
524 |
525 | createSpace["sRGB"] = createSpace(xyY(0.6400, 0.3300), xyY(0.3000, 0.6000), xyY(0.1500, 0.0600), white.D65, gammaSRgb, gammaSRgbInv);
526 |
527 | createSpace["WideGamut"] = createSpace(xyY(0.7347, 0.2653), xyY(0.1152, 0.8264), xyY(0.1566, 0.0177), white.D50, 2.2);
528 |
529 | module.exports = createSpace;
530 |
531 | },{"../XYZ":4,"../rgb":7,"../white":10,"../xyY":11,"m4th/lu":12,"m4th/matrix":13,"ut1l/create/object":16}],10:[function(require,module,exports){
532 | var xy, xyY;
533 |
534 | xyY = require("./xyY");
535 |
536 | xy = function(x, y) {
537 | return (xyY(x, y, 1)).XYZ();
538 | };
539 |
540 | module.exports = {
541 | A: xy(0.44757, 0.40745),
542 | B: xy(0.34842, 0.35161),
543 | C: xy(0.31006, 0.31616),
544 | D50: xy(0.34567, 0.35850),
545 | D55: xy(0.33242, 0.34743),
546 | D65: xy(0.31271, 0.32902),
547 | D75: xy(0.29902, 0.31485),
548 | E: xy(1 / 3, 1 / 3),
549 | F1: xy(0.31310, 0.33727),
550 | F2: xy(0.37208, 0.37529),
551 | F3: xy(0.40910, 0.39430),
552 | F4: xy(0.44018, 0.40329),
553 | F5: xy(0.31379, 0.34531),
554 | F6: xy(0.37790, 0.38835),
555 | F7: xy(0.31292, 0.32933),
556 | F8: xy(0.34588, 0.35875),
557 | F9: xy(0.37417, 0.37281),
558 | F10: xy(0.34609, 0.35986),
559 | F11: xy(0.38052, 0.37713),
560 | F12: xy(0.43695, 0.40441)
561 | };
562 |
563 | },{"./xyY":11}],11:[function(require,module,exports){
564 | var O, xyyPrototype;
565 |
566 | O = require("ut1l/create/object");
567 |
568 | xyyPrototype = {
569 | XYZ: function(T) {
570 | if (T == null) {
571 | T = require("./XYZ")();
572 | }
573 | T.X = this.x * this.Y / this.y;
574 | T.Y = this.Y;
575 | T.Z = (1 - this.x - this.y) * this.Y / this.y;
576 | return T;
577 | },
578 | isDefined: function() {
579 | return (this.x != null) && (this.y != null) && (this.Y != null);
580 | },
581 | toString: function() {
582 | return "x=" + this.x + ", y=" + this.y + ", Y=" + this.Y;
583 | }
584 | };
585 |
586 | module.exports = O((function(x, y, Y) {
587 | this.x = x;
588 | this.y = y;
589 | this.Y = Y;
590 | }), xyyPrototype);
591 |
592 | },{"./XYZ":4,"ut1l/create/object":16}],12:[function(require,module,exports){
593 | var M, T, creator, fail, luDecompConstructor, luDecompPrototype;
594 |
595 | M = require("./matrix");
596 |
597 | creator = require("ut1l/create/object");
598 |
599 | T = require("ut1l/create/throwable");
600 |
601 | fail = T("MatrixException");
602 |
603 |
604 | /*
605 | A very basic LU decomposition implementation without pivoting. Decomposition is done in place. Given buffer must
606 | be square and regular. The values of L below the diagonal are stored. The ones on the diagonal and the zeros
607 | above the diagonal are not stored. The values of U on and above the diagonal are stored. The zero values below
608 | the diagonal are not stored.
609 | */
610 |
611 | luDecompConstructor = function(A, T) {
612 | var i, j, k, _i, _j, _k, _l, _m, _ref, _ref1, _ref2, _ref3;
613 | if (T == null) {
614 | T = A.clone();
615 | }
616 | for (i = _i = 0, _ref = T.columns; _i < _ref; i = _i += 1) {
617 | for (j = _j = i, _ref1 = T.columns; _j < _ref1; j = _j += 1) {
618 | for (k = _k = 0; _k < i; k = _k += 1) {
619 | T.set(i, j, (T.get(i, j)) - (T.get(i, k)) * (T.get(k, j)));
620 | }
621 | }
622 | for (j = _l = _ref2 = i + 1, _ref3 = T.columns; _l < _ref3; j = _l += 1) {
623 | for (k = _m = 0; _m < i; k = _m += 1) {
624 | T.set(j, i, (T.get(j, i)) - (T.get(j, k)) * (T.get(k, i)));
625 | }
626 | T.set(j, i, (T.get(j, i)) / (T.get(i, i)));
627 | }
628 | }
629 | this.lu = T;
630 | };
631 |
632 | luDecompPrototype = {
633 |
634 | /* Calculate X = A^-1 * B in place or not in place */
635 | solve: function(B, T) {
636 | var A, i, j, k, _i, _j, _k, _l, _m, _n, _o, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6;
637 | if (T == null) {
638 | T = B.clone();
639 | }
640 | A = this.lu;
641 | if (B.rows !== A.columns || !B.isSize(T)) {
642 | fail("unmatching matrix dimension");
643 | }
644 | for (k = _i = 0, _ref = A.columns; _i < _ref; k = _i += 1) {
645 | for (i = _j = _ref1 = k + 1, _ref2 = A.columns; _j < _ref2; i = _j += 1) {
646 | for (j = _k = 0, _ref3 = T.columns; _k < _ref3; j = _k += 1) {
647 | T.set(i, j, (T.get(i, j)) - (T.get(k, j)) * (A.get(i, k)));
648 | }
649 | }
650 | }
651 | for (k = _l = _ref4 = A.columns - 1; _l >= 0; k = _l += -1) {
652 | for (j = _m = 0, _ref5 = T.columns; _m < _ref5; j = _m += 1) {
653 | T.set(k, j, (T.get(k, j)) / (A.get(k, k)));
654 | }
655 | for (i = _n = 0; _n < k; i = _n += 1) {
656 | for (j = _o = 0, _ref6 = T.columns; _o < _ref6; j = _o += 1) {
657 | T.set(i, j, (T.get(i, j)) - (T.get(k, j)) * (A.get(i, k)));
658 | }
659 | }
660 | }
661 | return T;
662 | },
663 | getInverse: function() {
664 | var I;
665 | I = M.I(this.lu.columns);
666 | return this.solve(I, I);
667 | }
668 | };
669 |
670 | module.exports = creator(luDecompConstructor, luDecompPrototype);
671 |
672 | },{"./matrix":13,"ut1l/create/object":14,"ut1l/create/throwable":15}],13:[function(require,module,exports){
673 | var T, add, ceil, createMatrix, creator, each, eachDiagonal, eachInRow, fail, failUnmatchingDimensions, floor, id, isNumber, makeReduce, matrixConstructor, matrixProto, matrixStatic, min, minus, sqrt;
674 |
675 | creator = require("ut1l/create/object");
676 |
677 | T = require("ut1l/create/throwable");
678 |
679 | fail = T("MatrixException");
680 |
681 | failUnmatchingDimensions = function() {
682 | return fail("invalid dimension");
683 | };
684 |
685 | floor = Math.floor, ceil = Math.ceil, sqrt = Math.sqrt, min = Math.min;
686 |
687 | id = function(x) {
688 | return x;
689 | };
690 |
691 | add = function(a, b) {
692 | return a + b;
693 | };
694 |
695 | minus = function(a, b) {
696 | return a - b;
697 | };
698 |
699 | isNumber = function(n) {
700 | return typeof n === "number";
701 | };
702 |
703 | matrixConstructor = function(arrayOrRows, arrayOrColumns, arrayOpt) {
704 | var array, cols, rows;
705 | if (!isNumber(arrayOrRows)) {
706 | array = arrayOrRows;
707 | } else {
708 | rows = arrayOrRows;
709 | if (!isNumber(arrayOrColumns)) {
710 | array = arrayOrColumns;
711 | } else {
712 | cols = arrayOrColumns;
713 | array = arrayOpt;
714 | }
715 | }
716 | if (rows == null) {
717 | rows = ceil(sqrt(array.length));
718 | }
719 | if (cols == null) {
720 | cols = rows === 0 ? 0 : array != null ? ceil(array.length / rows) : rows;
721 | }
722 | if (array == null) {
723 | array = [];
724 | }
725 | this.columns = cols;
726 | this.rows = rows;
727 | this.array = array;
728 | };
729 |
730 | matrixStatic = {
731 | I: function(rows, columns) {
732 | var i, _i, _ref;
733 | if (columns == null) {
734 | columns = rows;
735 | }
736 | T = createMatrix(rows, columns);
737 | T.fill(0, T);
738 | for (i = _i = 0, _ref = min(rows, columns); _i < _ref; i = _i += 1) {
739 | T.set(i, i, 1);
740 | }
741 | return T;
742 | },
743 | diag: function(x, T) {
744 | if (T == null) {
745 | T = createMatrix(x.length, x.length);
746 | } else if (!T.isSize(x.length)) {
747 | failUnmatchingDimensions();
748 | }
749 | T.each(function(val, r, c) {
750 | return T.set(r, c, r === c ? x[r] : 0);
751 | });
752 | return T;
753 | }
754 | };
755 |
756 | eachInRow = function(row, handler) {
757 | var j, _i, _ref;
758 | for (j = _i = 0, _ref = this.columns; _i < _ref; j = _i += 1) {
759 | handler.call(this, this.get(row, j), row, j);
760 | }
761 | return this;
762 | };
763 |
764 | each = function(handler) {
765 | var i, _i, _ref;
766 | for (i = _i = 0, _ref = this.rows; _i < _ref; i = _i += 1) {
767 | eachInRow.call(this, i, handler);
768 | }
769 | return this;
770 | };
771 |
772 | eachDiagonal = function(handler) {
773 | var ij, _i, _ref;
774 | for (ij = _i = 0, _ref = min(this.rows, this.columns); _i < _ref; ij = _i += 1) {
775 | handler.call(this, this.get(ij, ij), ij, ij);
776 | }
777 | return this;
778 | };
779 |
780 | makeReduce = function(eachFunc) {
781 | return function(callback, initialValue) {
782 | var value;
783 | value = initialValue;
784 | eachFunc.call(this, function(val, i, j) {
785 | if (value != null) {
786 | value = callback.call(this, value, val, i, j);
787 | } else {
788 | value = val;
789 | }
790 | });
791 | return value;
792 | };
793 | };
794 |
795 | matrixProto = {
796 | get: function(row, col) {
797 | if (col == null) {
798 | col = 0;
799 | }
800 | return this.array[row * this.columns + col];
801 | },
802 | set: function(row, col, val) {
803 | this.array[row * this.columns + col] = val;
804 | return this;
805 | },
806 | isSize: function(rowsOrM, columns) {
807 | if (isNumber(rowsOrM)) {
808 | if (columns == null) {
809 | columns = rowsOrM;
810 | }
811 | return this.rows === rowsOrM && this.columns === columns;
812 | } else {
813 | return this.isSize(rowsOrM.rows, rowsOrM.columns);
814 | }
815 | },
816 | isSquare: function() {
817 | return this.rows === this.columns;
818 | },
819 | each: each,
820 | eachDiagonal: eachDiagonal,
821 | reduce: makeReduce(each),
822 | reduceDiagonal: makeReduce(eachDiagonal),
823 | reduceRows: function(callback, initialValue) {
824 | var i, j, rdcRows, val, value, _i, _j, _ref, _ref1;
825 | rdcRows = [];
826 | for (i = _i = 0, _ref = this.rows; _i < _ref; i = _i += 1) {
827 | value = initialValue;
828 | for (j = _j = 0, _ref1 = this.columns; _j < _ref1; j = _j += 1) {
829 | val = this.get(i, j);
830 | if (value != null) {
831 | value = callback.call(this, value, val, i, j);
832 | } else {
833 | value = val;
834 | }
835 | }
836 | rdcRows.push(value);
837 | }
838 | return rdcRows;
839 | },
840 | map: function() {
841 | var args, elements, func, l, n;
842 | args = arguments;
843 | n = args.length - 1;
844 | if (args[n] === void 0) {
845 | n -= 1;
846 | }
847 | if (typeof args[n] !== "function") {
848 | T = args[n--];
849 | if (!this.isSize(T)) {
850 | failUnmatchingDimensions();
851 | }
852 | } else {
853 | T = createMatrix(this.rows, this.columns);
854 | }
855 | func = args[n];
856 | l = T.rows * T.columns;
857 | elements = [];
858 | T.each((function(_this) {
859 | return function(val, i, j) {
860 | var k, _i;
861 | elements[0] = _this.get(i, j);
862 | for (k = _i = 0; _i < n; k = _i += 1) {
863 | elements[k + 1] = args[k].get(i, j);
864 | }
865 | elements[++k] = i;
866 | elements[++k] = j;
867 | return T.set(i, j, func.apply(_this, elements));
868 | };
869 | })(this));
870 | return T;
871 | },
872 | clone: function(T) {
873 | return this.map(id, T);
874 | },
875 | fill: function(s, T) {
876 | return this.map((function() {
877 | return s;
878 | }), T);
879 | },
880 | times: function(s, T) {
881 | return this.map((function(x) {
882 | return s * x;
883 | }), T);
884 | },
885 | add: function(B, T) {
886 | return this.map(B, add, T);
887 | },
888 | minus: function(B, T) {
889 | return this.map(B, minus, T);
890 | },
891 | transp: function(T) {
892 | var B, i, j, _i, _j, _ref, _ref1, _ref2;
893 | if (T == null) {
894 | T = createMatrix(this.columns, this.rows);
895 | }
896 | if (T === this) {
897 | B = this.clone();
898 | _ref = [this.columns, this.rows], this.rows = _ref[0], this.columns = _ref[1];
899 | return B.transp(this);
900 | } else {
901 | if (this.rows !== T.columns || this.columns !== T.rows) {
902 | failUnmatchingDimensions();
903 | }
904 | for (i = _i = 0, _ref1 = this.columns; _i < _ref1; i = _i += 1) {
905 | for (j = _j = 0, _ref2 = this.rows; _j < _ref2; j = _j += 1) {
906 | T.set(i, j, this.get(j, i));
907 | }
908 | }
909 | return T;
910 | }
911 | },
912 | mult: function(B, T) {
913 | var i, j, k, _i, _j, _k, _ref, _ref1, _ref2;
914 | if (T == null) {
915 | T = createMatrix(this.rows, B.columns);
916 | }
917 | if (this.columns !== B.rows || T.rows !== this.rows || T.columns !== B.columns) {
918 | failUnmatchingDimensions();
919 | }
920 | T.fill(0, T);
921 | for (i = _i = 0, _ref = this.rows; _i < _ref; i = _i += 1) {
922 | for (j = _j = 0, _ref1 = B.columns; _j < _ref1; j = _j += 1) {
923 | for (k = _k = 0, _ref2 = this.columns; _k < _ref2; k = _k += 1) {
924 | T.array[i * T.columns + j] += (this.get(i, k)) * (B.get(k, j));
925 | }
926 | }
927 | }
928 | return T;
929 | },
930 | toString: (function() {
931 | var concatEntries;
932 | concatEntries = function(x, y) {
933 | return x + " " + y;
934 | };
935 | return function() {
936 | return (this.reduceRows(concatEntries)).join("\n");
937 | };
938 | })()
939 | };
940 |
941 | module.exports = createMatrix = creator(matrixStatic, matrixConstructor, matrixProto);
942 |
943 | },{"ut1l/create/object":14,"ut1l/create/throwable":15}],14:[function(require,module,exports){
944 | var createBuilder;
945 |
946 | createBuilder = function(extend, constructor, prototype) {
947 | var F, f, key, value;
948 | if (typeof extend === "function") {
949 | prototype = constructor;
950 | constructor = extend;
951 | extend = null;
952 | } else if ((constructor == null) && (prototype == null)) {
953 | prototype = extend;
954 | extend = null;
955 | }
956 | F = constructor != null ? function(args) {
957 | var ret;
958 | ret = constructor.apply(this, args);
959 | if (ret !== void 0) {
960 | return ret;
961 | } else {
962 | return this;
963 | }
964 | } : function() {};
965 | if (prototype == null) {
966 | prototype = {};
967 | }
968 | F.prototype = prototype;
969 | f = function() {
970 | return new F(arguments);
971 | };
972 | f.prototype = prototype;
973 | for (key in extend) {
974 | value = extend[key];
975 | f[key] = value;
976 | }
977 | return f;
978 | };
979 |
980 | module.exports = createBuilder;
981 |
982 | },{}],15:[function(require,module,exports){
983 | var O, createCreateThrowable, createTopThrowable, throwableConstr, throwableProto;
984 |
985 | O = require("./object");
986 |
987 | throwableProto = {
988 | name: "Error",
989 | toString: function() {
990 | if (this.message != null) {
991 | return "" + this.name + ": " + this.message;
992 | } else {
993 | return this.name;
994 | }
995 | }
996 | };
997 |
998 | createTopThrowable = O(throwableProto);
999 |
1000 | throwableConstr = function(message) {
1001 | var e;
1002 | this.message = message;
1003 | e = Error.call(this, message);
1004 | this.stack = e.stack;
1005 | };
1006 |
1007 | createCreateThrowable = function(name, parent) {
1008 | var proto;
1009 | if (parent == null) {
1010 | parent = createTopThrowable;
1011 | }
1012 | proto = parent();
1013 | if (name != null) {
1014 | proto.name = name;
1015 | }
1016 | return O(throwableConstr, proto);
1017 | };
1018 |
1019 | createCreateThrowable.c4tch = function() {
1020 | var action, arg, args, idx, onError, throwables, _i, _len;
1021 | args = arguments;
1022 | throwables = [];
1023 | for (idx = _i = 0, _len = args.length; _i < _len; idx = ++_i) {
1024 | arg = args[idx];
1025 | if (arg.prototype instanceof createTopThrowable) {
1026 | throwables.push(arg);
1027 | } else {
1028 | break;
1029 | }
1030 | }
1031 | if (throwables.length === 0) {
1032 | throwables.push(createTopThrowable);
1033 | }
1034 | action = args[idx];
1035 | onError = args[idx + 1];
1036 | return function() {
1037 | var e, t, _j, _len1;
1038 | try {
1039 | return action.apply(this, arguments);
1040 | } catch (_error) {
1041 | e = _error;
1042 | for (_j = 0, _len1 = throwables.length; _j < _len1; _j++) {
1043 | t = throwables[_j];
1044 | if (e instanceof t) {
1045 | return (onError != null ? onError.call(this, e) : void 0);
1046 | }
1047 | }
1048 | throw e;
1049 | }
1050 | };
1051 | };
1052 |
1053 | module.exports = createCreateThrowable;
1054 |
1055 | },{"./object":14}],16:[function(require,module,exports){
1056 | var addProto, createBuilder, defineProperty;
1057 |
1058 | defineProperty = Object.defineProperty;
1059 |
1060 | addProto = defineProperty !== void 0 ? function(func, proto) {
1061 | func.prototype = proto;
1062 | defineProperty(func, "prototype", {
1063 | enumerable: false
1064 | });
1065 | } : function(func, proto) {
1066 | func.prototype = proto;
1067 | };
1068 |
1069 | createBuilder = function(extend, constructor, prototype) {
1070 | var F, f, key, value;
1071 | if (typeof extend === "function") {
1072 | prototype = constructor;
1073 | constructor = extend;
1074 | extend = null;
1075 | } else if ((constructor == null) && (prototype == null)) {
1076 | prototype = extend;
1077 | extend = null;
1078 | }
1079 | F = constructor != null ? function(args) {
1080 | var ret;
1081 | ret = constructor.apply(this, args);
1082 | if (ret !== void 0) {
1083 | return ret;
1084 | } else {
1085 | return this;
1086 | }
1087 | } : function() {};
1088 | if (prototype == null) {
1089 | prototype = {};
1090 | }
1091 | F.prototype = prototype;
1092 | f = function() {
1093 | return new F(arguments);
1094 | };
1095 | addProto(f, prototype);
1096 | for (key in extend) {
1097 | value = extend[key];
1098 | f[key] = value;
1099 | }
1100 | return f;
1101 | };
1102 |
1103 | module.exports = createBuilder;
1104 |
1105 | },{}]},{},[6]);
1106 |
--------------------------------------------------------------------------------
/dist/c0lor.min.js:
--------------------------------------------------------------------------------
1 | // c0lor v0.1.1 | (c) 2013-2021 Hendrik Helwich | MIT License
2 | 'use strict';!function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;gh?n(a,1/3):l*a+e},k=function(a){return a>g?n(a,3):(a-e)/l},j=function(a){return a>h?1/(3*n(a,2/3)):l},m={Lab:function(a,b){var c;return null==b&&(b=d()),c=i(a.Y/this.white.Y),b.L=116*c-16,b.a=500*(i(a.X/this.white.X)-c),b.b=200*(c-i(a.Z/this.white.Z)),b},XYZ:function(a,b){var c;return null==b&&(b=o()),c=(a.L+16)/116,b.X=k(c+a.a/500)*this.white.X,b.Y=k(c)*this.white.Y,b.Z=k(c-a.b/200)*this.white.Z,b},LabderivL:function(a){return 116*j(a/this.white.Y)/this.white.Y}},b.exports=f(function(a){this.white=a},m)},{"../Lab":2,"../XYZ":4,"ut1l/create/object":16}],9:[function(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p;e=a("ut1l/create/object"),d=a("m4th/matrix"),k=a("m4th/lu"),o=a("../white"),p=a("../xyY"),f=a("../XYZ"),l=Math.pow,h=function(a){return a<=.04045?a/12.92:l((a+.055)/1.055,2.4)},i=function(a){return a<=.0031308?12.92*a:1.055*l(a,1/2.4)-.055},j=function(){var a,b,c;a=d([this.red.x,this.green.x,this.blue.x,this.red.y,this.green.y,this.blue.y,1-this.red.x-this.red.y,1-this.green.x-this.green.y,1-this.blue.x-this.blue.y]),b=k(a),c=d(3,[this.white.X,this.white.Y,this.white.Z]),b.solve(c,c),a=a.mult(d.diag(c.array)),this.base=a.array,this.baseInv=k(a).getInverse().array,delete this.XYZ,delete this.rgb},m=function(a,b,c,d,e,f){this.red=a,this.green=b,this.blue=c,this.white=d,"function"==typeof e?(this.gamma=e,this.gammaInv=f):(this.g=e,this.gInv=1/e),this.rgb=function(){return j.call(this),this.rgb.apply(this,arguments)},this.XYZ=function(){return j.call(this),this.XYZ.apply(this,arguments)}},n={gamma:function(a){return l(a,this.g)},gammaInv:function(a){return l(a,this.gInv)},rgb:function(b,c){var d;return null==c&&(c=a("../rgb")()),d=this.baseInv,c.r=this.gammaInv(d[0]*b.X+d[1]*b.Y+d[2]*b.Z),c.g=this.gammaInv(d[3]*b.X+d[4]*b.Y+d[5]*b.Z),c.b=this.gammaInv(d[6]*b.X+d[7]*b.Y+d[8]*b.Z),c},XYZ:function(a,b){var c,d,e,g;return null==b&&(b=f()),c=this.base,g=this.gamma(a.r),e=this.gamma(a.g),d=this.gamma(a.b),b.X=c[0]*g+c[1]*e+c[2]*d,b.Y=c[3]*g+c[4]*e+c[5]*d,b.Z=c[6]*g+c[7]*e+c[8]*d,b}},g=e(m,n),g["Adobe-98"]=g(p(.64,.33),p(.21,.71),p(.15,.06),o.D65,2.2),g["Adobe-RGB"]=g(p(.625,.34),p(.28,.595),p(.155,.07),o.D65,1.8),g["CIE-RGB"]=g(p(.735,.265),p(.274,.717),p(.167,.009),o.E,1),g.ColorMatch=g(p(.63,.34),p(.295,.605),p(.15,.075),o.D50,1.8),g["EBU-Monitor"]=g(p(.6314,.3391),p(.2809,.5971),p(.1487,.0645),o.D50,1.9),g["ECI-RGB"]=g(p(.67,.33),p(.21,.71),p(.14,.08),o.D50,1.8),g.HDTV=g(p(.64,.33),p(.29,.6),p(.15,.06),o.D65,2.2),g["Kodak-DC"]=g(p(.6492,.3314),p(.3219,.5997),p(.1548,.0646),o.D50,2.22),g["NTSC-53"]=g(p(.67,.33),p(.21,.71),p(.14,.08),o.C,2.2),g["PAL-SECAM"]=g(p(.64,.33),p(.29,.6),p(.15,.06),o.D65,2.2),g.sRGB=g(p(.64,.33),p(.3,.6),p(.15,.06),o.D65,h,i),g.WideGamut=g(p(.7347,.2653),p(.1152,.8264),p(.1566,.0177),o.D50,2.2),b.exports=g},{"../XYZ":4,"../rgb":7,"../white":10,"../xyY":11,"m4th/lu":12,"m4th/matrix":13,"ut1l/create/object":16}],10:[function(a,b,c){var d,e;e=a("./xyY"),d=function(a,b){return e(a,b,1).XYZ()},b.exports={A:d(.44757,.40745),B:d(.34842,.35161),C:d(.31006,.31616),D50:d(.34567,.3585),D55:d(.33242,.34743),D65:d(.31271,.32902),D75:d(.29902,.31485),E:d(1/3,1/3),F1:d(.3131,.33727),F2:d(.37208,.37529),F3:d(.4091,.3943),F4:d(.44018,.40329),F5:d(.31379,.34531),F6:d(.3779,.38835),F7:d(.31292,.32933),F8:d(.34588,.35875),F9:d(.37417,.37281),F10:d(.34609,.35986),F11:d(.38052,.37713),F12:d(.43695,.40441)}},{"./xyY":11}],11:[function(a,b,c){var d,e;d=a("ut1l/create/object"),e={XYZ:function(b){return null==b&&(b=a("./XYZ")()),b.X=this.x*this.Y/this.y,b.Y=this.Y,b.Z=(1-this.x-this.y)*this.Y/this.y,b},isDefined:function(){return null!=this.x&&null!=this.y&&null!=this.Y},toString:function(){return"x="+this.x+", y="+this.y+", Y="+this.Y}},b.exports=d(function(a,b,c){this.x=a,this.y=b,this.Y=c},e)},{"./XYZ":4,"ut1l/create/object":16}],12:[function(a,b,c){var d,e,f,g,h,i;d=a("./matrix"),f=a("ut1l/create/object"),e=a("ut1l/create/throwable"),g=e("MatrixException"),h=function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n;for(null==b&&(b=a.clone()),c=f=0,k=b.columns;f=0;f=k+=-1){for(e=l=0,t=b.columns;l
13 | T.L = @L
14 | T.a = @C * cos @h
15 | T.b = @C * sin @h
16 | T
17 |
18 | # Returns a human readable string serialization of the color.
19 | toString: ->
20 | "L=#{@L}, C=#{@C}, h=#{@h}"
21 |
22 |
23 | # Public API
24 | # ----------
25 |
26 | module.exports = O ((@L, @C, @h) ->), lchPrototype
--------------------------------------------------------------------------------
/src/Lab.coffee:
--------------------------------------------------------------------------------
1 | # Imports / Shortcuts
2 | # -------------------
3 |
4 | O = require "ut1l/create/object"
5 |
6 | sqrt = Math.sqrt
7 | atan2 = Math.atan2
8 |
9 |
10 | # Lab color prototype
11 | # -------------------
12 |
13 | labPrototype =
14 |
15 | # Converts color from Lab to LCh color space.
16 | LCh: (T = do require "./LCh") ->
17 | T.L = @L # lightness
18 | T.C = sqrt @a * @a + @b * @b # chroma
19 | T.h = atan2 @b, @a # hue
20 | T
21 |
22 | # Returns a human readable string serialization of the color.
23 | toString: ->
24 | "L=#{@L}, a=#{@a}, b=#{@b}"
25 |
26 |
27 |
28 | # Public API
29 | # ----------
30 |
31 | module.exports = O ((@L, @a, @b) ->), labPrototype
--------------------------------------------------------------------------------
/src/RGBInt.coffee:
--------------------------------------------------------------------------------
1 |
2 |
3 | O = require "ut1l/create/object"
4 |
5 |
6 | fromByte = (b) ->
7 | if b == undefined then b else b / 255
8 |
9 | to2Hex = (b) ->
10 | hex = do (b.toString 16).toUpperCase
11 | if hex.length == 1 then "0" + hex else hex
12 |
13 | from2Hex = (str) ->
14 | parseInt str, 16
15 |
16 | isValid = (n) ->
17 | 0 <= n <= 255 and n % 1 == 0
18 |
19 | # RGB 24 bit prototype
20 | # --------------------
21 |
22 | rgb24Prototype =
23 |
24 | rgb: (T = do require "./rgb") ->
25 | T.r = fromByte @R
26 | T.g = fromByte @G
27 | T.b = fromByte @B
28 | T
29 |
30 | hex: (str) ->
31 | if str?
32 | @R = from2Hex str.substring 0, 2
33 | @G = from2Hex str.substring 2, 4
34 | @B = from2Hex str.substring 4, 6
35 | @
36 | else
37 | if do @isDefined then (to2Hex @R) + (to2Hex @G) + (to2Hex @B) else undefined
38 |
39 | isDefined: ->
40 | @R? and @G? and @B?
41 |
42 | isValid: ->
43 | @isDefined() and (isValid @R) and (isValid @G) and (isValid @B)
44 |
45 | toString: ->
46 | "R=#{@R}, G=#{@G}, B=#{@B}"
47 |
48 |
49 |
50 | # Public api
51 | # ----------
52 |
53 | module.exports = O ((@R, @G, @B) ->), rgb24Prototype
--------------------------------------------------------------------------------
/src/XYZ.coffee:
--------------------------------------------------------------------------------
1 | # XYZ and xyY color space implementation.
2 |
3 | # Imports
4 | # -------
5 |
6 | O = require "ut1l/create/object"
7 |
8 | # XYZ prototype
9 | # -------------
10 |
11 | xyzPrototype =
12 |
13 | # Convert the color from *XYZ* to *xyY* color space. Give optional *xyY* color `T` to store the result.
14 | xyY: (T = do require "./xyY") ->
15 | T.x = @X / (@X + @Y + @Z)
16 | T.y = @Y / (@X + @Y + @Z)
17 | T.Y = @Y
18 | T
19 |
20 | # Returns `true` if all components of the color are not `null` and not `undefined`.
21 | isDefined: ->
22 | @X? and @Y? and @Z?
23 |
24 | # Returns a human readable string serialization of the color.
25 | toString: ->
26 | "X=#{@X}, Y=#{@Y}, Z=#{@Z}"
27 |
28 |
29 |
30 |
31 | # Public API
32 | # ----------
33 |
34 | module.exports = O ((@X, @Y, @Z) ->), xyzPrototype
35 |
--------------------------------------------------------------------------------
/src/gamut.coffee:
--------------------------------------------------------------------------------
1 | #{info, warn, fail} = require "ut1l/log"
2 | pow = Math.pow
3 | rgbM = require "./rgb"
4 | xyzM = require "./XYZ"
5 |
6 | ###
7 | solveCubic = (a0, a, b, c) -> # see: http://de.wikipedia.org/wiki/Cardanische_Formeln
8 | # normalize
9 | a /= a0
10 | b /= a0
11 | c /= a0
12 | # substitute x=z-a/3 => z^3 + p*z + q = 0
13 | p = b - a*a / 3
14 | q = 2*(pow a, 3)/27 - a*b/3 + c
15 | # calculate dicriminat
16 | D = (pow p, 3)/27 + q*q/4
17 | if D > 0
18 | D = Math.sqrt D
19 | ru = -q/2 + D
20 | rv = -q/2 - D
21 |
22 | if ru < 0
23 | ru = -(pow -ru, 1/3)
24 | else
25 | ru = pow ru, 1/3
26 |
27 | if rv < 0
28 | rv = -(pow -rv, 1/3)
29 | else
30 | rv = pow rv, 1/3
31 |
32 | X = ru + rv
33 |
34 | X -= a/3
35 | else if D < 0
36 | ppp = (pow p,3) / 27
37 | ppp = Math.abs ppp
38 | ppp = 2 * Math.pow ppp , 1/2
39 | argu = -q/ppp
40 | phi = Math.acos argu
41 | phi = phi/3
42 | # alert(phi);
43 | pp = Math.abs p/3
44 | pp = 2*pow pp, 1/2
45 | X = pp * Math.cos phi
46 |
47 | X -= a/3
48 | else
49 | fail "D = 0 is not implemented"
50 |
51 |
52 |
53 | #console.log "solution x = "+X
54 | #console.log "test: x^3 + a*x^2 + b*x + c = " + ((pow X, 3) + a*X*X + b*X + c)
55 |
56 | info "expect to be 0: " + (X*X*X + a*X*X + b*X + c)
57 | X
58 |
59 |
60 | checkSolution = (C, a, b, fy, rgb, nbase, mr, mg, mb) ->
61 | if C >= 0
62 | X = pow fy + a * C, 3
63 | Z = pow fy + b * C, 3
64 | if not rgb.r?
65 | rgb.r = nbase[0] * X + nbase[2] * Z + mr
66 | else info "expected #{rgb.r} == "+ (nbase[0] * X + nbase[2] * Z + mr)
67 | if not rgb.g?
68 | rgb.g = nbase[3] * X + nbase[5] * Z + mg
69 | else info "expected #{rgb.g} == "+ (nbase[3] * X + nbase[5] * Z + mg)
70 | if not rgb.b?
71 | rgb.b = nbase[6] * X + nbase[8] * Z + mb
72 | else info "expected #{rgb.b} == "+ (nbase[6] * X + nbase[8] * Z + mb )
73 | if rgb.isValid()
74 | return true
75 | rgb.r = rgb.g = rgb.b = null
76 | return false
77 |
78 | gammaInv = (rgb, rgbCs) ->
79 | rgb.r = rgbCs.gammaInv rgb.r
80 | rgb.g = rgbCs.gammaInv rgb.g
81 | rgb.b = rgbCs.gammaInv rgb.b
82 | rgb
83 | ###
84 |
85 | class GamutMapping
86 |
87 | constructor: (@rgbCs, @labCs) ->
88 | ###
89 | rgbCs.init() # make sure rgb base is initialized
90 | # store rgb inverse base normalised with lab white point
91 | a = rgbCs.baseInv
92 | @nbase = [
93 | a[0] * @labCs.white.X, a[1] * @labCs.white.Y, a[2] * @labCs.white.Z
94 | a[3] * @labCs.white.X, a[4] * @labCs.white.Y, a[5] * @labCs.white.Z
95 | a[6] * @labCs.white.X, a[7] * @labCs.white.Y, a[8] * @labCs.white.Z
96 | ]
97 | ###
98 |
99 |
100 | LChMaxC: (LCh, rgb = rgbM()) ->
101 | # naive binary search implementation
102 | # TODO: manage correct direct calculation
103 | LCh.C = 0
104 | step = 110
105 | validC = null
106 | for n in [0..50] by 1
107 | lab = LCh.Lab lab
108 | xyz = @labCs.XYZ lab, xyz
109 | @rgbCs.rgb xyz, rgb
110 | if rgb.isValid()
111 | validC = LCh.C
112 | LCh.C += step
113 | else
114 | LCh.C -= step
115 | step /= 2
116 | LCh.C = validC
117 | if validC? # solution found
118 | @rgbCs.rgb (@labCs.XYZ (LCh.Lab lab), xyz), rgb
119 | else
120 | null
121 |
122 |
123 |
124 | ###
125 |
126 | LChMaxCExp: (LCh, rgb = rgbM.rgb()) ->
127 |
128 |
129 | xyz = xyzM.XYZ() # temporary variable
130 |
131 | # valid (r, 0, 0) with given luminance possible?
132 | r = (pow 16 + LCh.L, 3) / (@labCs.white.Y * 1560896 * @rgbCs.base[3]) # 1560896 = 116^3
133 | if 0 <= r <= 1 # valid rgb element ?
134 | r = @rgbCs.gammaInv r # linear rgb -> nonlinear rgb
135 | _rgb = rgbM.rgb r, 0, 0
136 | # map to LCh to get hue
137 | @rgbCs.toXYZ _rgb, xyz
138 | lab = @labCs.fromXYZ xyz, lab
139 | lch = lab.LCh lch
140 |
141 |
142 | # valid (0, g, 0) with given luminance possible?
143 | g = (pow 16 + LCh.L, 3) / (@labCs.white.Y * 1560896 * @rgbCs.base[4]) # 1560896 = 116^3
144 | if 0 <= g <= 1 # valid rgb element ?
145 | g = @rgbCs.gammaInv g # linear rgb -> nonlinear rgb
146 | _rgb = rgbM.rgb 0, g, 0
147 | # map to LCh to get hue
148 | @rgbCs.toXYZ _rgb, xyz
149 | lab = @labCs.fromXYZ xyz, lab
150 | lch = lab.LCh lch
151 |
152 |
153 |
154 | # valid (0, 0, b) with given luminance possible?
155 | b = (pow 16 + LCh.L, 3) / (@labCs.white.Y * 1560896 * @rgbCs.base[5]) # 1560896 = 116^3
156 | if 0 <= b <= 1 # valid rgb element ?
157 | b = @rgbCs.gammaInv b # linear rgb -> nonlinear rgb
158 | _rgb = rgbM.rgb 0, 0, b
159 | # map to LCh to get hue
160 | @rgbCs.toXYZ _rgb, xyz
161 | lab = @labCs.fromXYZ xyz, lab
162 | lch = lab.LCh lch
163 | else # valid (r, 0, 1) with given luminance possible?
164 | r = ((pow (16 + LCh.L) / 116, 3) * @labCs.white.Y - @rgbCs.base[5]) / @rgbCs.base[3]
165 |
166 | #lchTest = (@labCs.fromXYZ @rgbCs.toXYZ rgbTest).LCh()
167 |
168 | #lchTest = (@labCs.fromXYZ @rgbCs.toXYZ rgbTest.set 0, 1, 0).LCh()
169 |
170 | #lchTest = (@labCs.fromXYZ @rgbCs.toXYZ rgbTest.set 0, 0, 1).LCh()
171 |
172 | # L and h fixed. maximum C is wanted so that the LCh color is a valid in current rgb color space
173 | # calculate a,b for chroma 1. Now we need to find the factor for a and b (the chroma)
174 | a = Math.cos LCh.h
175 | b = Math.sin LCh.h
176 | # to XYZ (for now ignore linear case)
177 | a /= 500
178 | b /= -200
179 | # calculate some constant values
180 | fy = (LCh.L + 16) / 116
181 | Y = pow(fy, 3)
182 | mr = @nbase[1] * Y # maximum red value for X / Z params
183 | mg = @nbase[4] * Y # maximum green value for X / Z params
184 | mb = @nbase[7] * Y # maximum blue value for X / Z params
185 | #X = pow(fy + a, 3)
186 | #Z = pow(fy + b, 3)
187 | # to linear rgb (maximum value 1)
188 |
189 |
190 | #mr = a0 * X + a2 * Z #red
191 | #mg = a3 * X + a5 * Z #green
192 | #mb = a6 * X + a8 * Z #blue
193 |
194 |
195 | bxr = @nbase[0]
196 | bzr = @nbase[2]
197 | bxg = @nbase[3]
198 | bzg = @nbase[5]
199 | bxb = @nbase[6]
200 | bzb = @nbase[8]
201 |
202 | a2 = a*a
203 | a3 = a2*a
204 | b2 = b*b
205 | b3 = b2*b
206 | fy_3 = 3*fy
207 | fy2_3 = fy_3*fy
208 | fy3 = pow fy, 3
209 |
210 | lr = solveCubic bxr*a3 + bzr*b3, fy_3*(bxr*a2 + bzr*b2), fy2_3*(bxr*a + bzr*b), fy3*(bxr+bzr)+mr-1
211 |
212 | rgb.r = 1
213 | rgb.g = rgb.b = null
214 | if (checkSolution lr, a, b, fy, rgb, @nbase, mr, mg, mb)
215 | return gammaInv rgb, @rgbCs
216 |
217 | lg = solveCubic bxg*a3 + bzg*b3, fy_3*(bxg*a2 + bzg*b2), fy2_3*(bxg*a + bzg*b), fy3*(bxg + bzg) + mg-1
218 |
219 | rgb.g = 1
220 | if (checkSolution lg, a, b, fy, rgb, @nbase, mr, mg, mb)
221 | return gammaInv rgb, @rgbCs
222 |
223 |
224 |
225 | lb = solveCubic bxb*a3 + bzb*b3, fy_3*(bxb*a2 + bzb*b2), fy2_3*(bxb*a + bzb*b), fy3*(bxb+bzb)+mb-1
226 | rgb.b = 1
227 | if (checkSolution lb, a, b, fy, rgb, @nbase, mr, mg, mb)
228 | return gammaInv rgb, @rgbCs
229 |
230 |
231 | lr0 = solveCubic bxr*a3 + bzr*b3, fy_3*(bxr*a2 + bzr*b), fy2_3*(bxr*a + bzr*b), fy3*(bxr+bzr)+mr
232 |
233 | rgb.r = 0
234 | if (checkSolution lr0, a, b, fy, rgb, @nbase, mr, mg, mb)
235 | return gammaInv rgb, @rgbCs
236 |
237 | lg0 = solveCubic bxg*a3 + bzg*b3, fy_3*(bxg*a2 + bzg*b), fy2_3*(bxg*a + bzg*b), fy3*(bxg+bzg)+mg
238 |
239 | rgb.g = 0
240 | if (checkSolution lg0, a, b, fy, rgb, @nbase, mr, mg, mb)
241 | return gammaInv rgb, @rgbCs
242 |
243 |
244 | lb0 = solveCubic bxb*a3 + bzb*b3, fy_3*(bxb*a2 + bzb*b2), fy2_3*(bxb*a + bzb*b), fy3*(bxb+bzb)+mb
245 | rgb.b = 0
246 | if (checkSolution lb0, a, b, fy, rgb, @nbase, mr, mg, mb)
247 | return gammaInv rgb, @rgbCs
248 |
249 |
250 |
251 |
252 |
253 | ###
254 |
255 |
256 |
257 | module.exports = (rgbCs, labCs) ->
258 | new GamutMapping rgbCs, labCs
259 |
--------------------------------------------------------------------------------
/src/hsv.coffee:
--------------------------------------------------------------------------------
1 | # see: http://en.wikipedia.org/wiki/HSL_and_HSV
2 |
3 | # Imports / Shortcuts
4 | # -------------------
5 |
6 | O = require "ut1l/create/object"
7 |
8 | floor = Math.floor
9 |
10 |
11 | # HSV color prototype
12 | # -------------------
13 |
14 | hsvPrototype =
15 |
16 | rgb: (T = do require "./rgb") ->
17 | if @s == 0 # simplification
18 | T.set @v, @v, @v
19 | else
20 | h = (@h - floor @h) * 6 # 0 <= h < 6
21 | f = h - floor h # 0 <= f < 1
22 | p = @v * (1 - @s)
23 | q = @v * (1 - @s * f)
24 | t = @v * (1 - @s * (1 - f))
25 | switch floor h
26 | when 0 then T.set @v, t, p
27 | when 1 then T.set q, @v, p
28 | when 2 then T.set p, @v, t
29 | when 3 then T.set p, q, @v
30 | when 4 then T.set t, p, @v
31 | when 5 then T.set @v, p, q
32 |
33 | set: (@h, @s, @v) ->
34 | @
35 |
36 | toString: ->
37 | "h=#{@h}, s=#{@s}, v=#{@v}"
38 |
39 |
40 | # Extend RGB module
41 | # -----------------
42 |
43 | hsv = createHsv = O ((@h, @s, @v) ->), hsvPrototype
44 |
45 | (require "./rgb").extendRgb (rgb) ->
46 |
47 | rgb.hsv = (T = do createHsv) ->
48 | max = Math.max @r, @g, @b
49 | min = Math.min @r, @g, @b
50 | T.v = max
51 | d = max - min
52 | T.s = if max != 0 then d / max else 0
53 | if T.s == 0
54 | T.h = 0
55 | else
56 | switch max
57 | when @r
58 | T.h = if @g < @b then 6 else 0
59 | T.h += (@g - @b) / d
60 | when @g
61 | T.h = 2 + (@b - @r) / d
62 | else # blue
63 | T.h = 4 + (@r - @g) / d
64 | T.h /= 6
65 | T
66 |
67 |
68 |
69 | # Public API
70 | # ----------
71 |
72 | module.exports = hsv
--------------------------------------------------------------------------------
/src/index.coffee:
--------------------------------------------------------------------------------
1 | module.exports = index =
2 | rgb: require "./rgb"
3 | RGB: require "./RGBInt"
4 | hsv: require "./hsv"
5 | Lab: require "./Lab"
6 | LCh: require "./LCh"
7 | XYZ: require "./XYZ"
8 | xyY: require "./xyY"
9 | white: require "./white"
10 | #gamut: require "./gamut"
11 | space:
12 | rgb: require "./space/rgb"
13 | lab: require "./space/lab"
14 |
15 | # create global index if in browser
16 | if window?
17 | window.c0lor = index
18 |
--------------------------------------------------------------------------------
/src/rgb.coffee:
--------------------------------------------------------------------------------
1 | # Imports / Shortcuts
2 | # -------------------
3 |
4 |
5 | O = require "ut1l/create/object"
6 |
7 | round = Math.round
8 |
9 | # Helpers
10 | # -------
11 |
12 |
13 | toByte = (d) ->
14 | round d * 255 if d?
15 |
16 | validRgbEl = (x) ->
17 | (isFinite x) and 0 <= x <= 1
18 |
19 | # RGB prototype
20 | # -------------
21 |
22 | rgbPrototype =
23 |
24 | RGB: (T = do require "./RGBInt") ->
25 | T.R = toByte @r
26 | T.G = toByte @g
27 | T.B = toByte @b
28 | T
29 |
30 | set: (@r, @g, @b) ->
31 | @
32 |
33 | isDefined: ->
34 | @r? and @g? and @b?
35 |
36 | isValid: ->
37 | do @isDefined and (validRgbEl @r) and (validRgbEl @g) and (validRgbEl @b)
38 |
39 | toString: ->
40 | "r=#{@r}, g=#{@g}, b=#{@b}"
41 |
42 |
43 |
44 | # Public api
45 | # ----------
46 |
47 | module.exports = O (extendRgb: (f) -> f rgbPrototype), ((@r, @g, @b) ->), rgbPrototype
--------------------------------------------------------------------------------
/src/space/lab.coffee:
--------------------------------------------------------------------------------
1 | O = require "ut1l/create/object"
2 |
3 | xyz = require "../XYZ"
4 | Lab = require "../Lab"
5 |
6 | pow = Math.pow
7 |
8 | # Helpers
9 | # -------
10 |
11 | N = 4 / 29 # = 16 / 116
12 | e3 = 216 / 24389 # = e^3
13 | foo = 841 / 108 # = (1 / 116) * (24389 / 27) = 1/3 * (29/6)^2
14 | e = 6 / 29 # = e3^(1/3)
15 |
16 | f = (w) ->
17 | if w > e3
18 | pow w, 1/3
19 | else
20 | foo * w + N
21 |
22 | fInv = (w) ->
23 | if w > e
24 | pow w, 3
25 | else
26 | (w - N) / foo
27 |
28 | # Derivative of `f`.
29 | fDeriv = (w) ->
30 | if w > e3
31 | 1 / (3 * (pow w, 2/3))
32 | else
33 | foo
34 |
35 | # Lab color space prototype
36 | # -------------------------
37 |
38 | labCsPrototype =
39 |
40 | # Converts color `XYZ` from *XYZ* to this *Lab* color space.
41 | Lab: (XYZ, T = do Lab) ->
42 | l = f XYZ.Y / @white.Y
43 | T.L = 116 * l - 16
44 | T.a = 500 * ((f XYZ.X / @white.X) - l)
45 | T.b = 200 * (l - (f XYZ.Z / @white.Z))
46 | T
47 |
48 | XYZ: (Lab, T = do xyz) ->
49 | fy = (Lab.L + 16) / 116
50 | T.X = (fInv fy + Lab.a / 500) * @white.X
51 | T.Y = (fInv fy) * @white.Y
52 | T.Z = (fInv fy - Lab.b / 200) * @white.Z
53 | T
54 |
55 | LabderivL: (Y) ->
56 | 116 * (fDeriv Y / @white.Y) / @white.Y
57 |
58 |
59 |
60 | # Public API
61 | # ----------
62 |
63 | module.exports = O ((@white) ->), labCsPrototype
--------------------------------------------------------------------------------
/src/space/rgb.coffee:
--------------------------------------------------------------------------------
1 | # Imports / Shortcuts
2 | # -------------------
3 |
4 |
5 | O = require "ut1l/create/object"
6 |
7 |
8 | M = require "m4th/matrix"
9 | lu = require "m4th/lu"
10 |
11 |
12 | white = require "../white"
13 | xyY = require "../xyY"
14 |
15 | XYZ = require "../XYZ"
16 |
17 | pow = Math.pow
18 |
19 | # Helpers
20 | # -------
21 |
22 |
23 | gammaSRgb = (x) ->
24 | if x <= 0.04045
25 | x / 12.92;
26 | else
27 | pow (x + 0.055) / 1.055, 2.4
28 |
29 | gammaSRgbInv = (x) ->
30 | if x <= 0.0031308
31 | x * 12.92
32 | else
33 | 1.055 * (pow x, 1 / 2.4) - 0.055
34 |
35 |
36 | # RGB space constructor
37 | # ---------------------
38 |
39 | lazyInitRgbBase = ->
40 | # create xyz base (luminance is unknown => need to multiply each column by a scalar)
41 | bxyz = M [
42 | @red.x , @green.x , @blue.x
43 | @red.y, @green.y, @blue.y
44 | 1 - @red.x - @red.y, 1 - @green.x - @green.y, 1 - @blue.x - @blue.y
45 | ]
46 | # calculate LU decomposition of xyz base
47 | bxyzLU = lu bxyz
48 | w = M 3, [ @white.X, @white.Y, @white.Z ]
49 | # get the needed scales or the columns of bxyz (sum of the columns of the base must be the white point)
50 | bxyzLU.solve w, w # calculate in place
51 | # scale bxyz to get the wanted XYZ base (sum of columns is white point)
52 | bxyz = bxyz.mult M.diag w.array
53 | @base = bxyz.array
54 | @baseInv = (lu bxyz).getInverse().array
55 |
56 | delete @XYZ
57 | delete @rgb
58 | return
59 |
60 | rgbSpaceConstructor = (@red, @green, @blue, @white, gamma, gammaInv) ->
61 | if typeof gamma == "function"
62 | @gamma = gamma
63 | @gammaInv = gammaInv
64 | else
65 | @g = gamma
66 | @gInv = 1 / gamma
67 | # Lazy definition of following two methods.
68 | @rgb = ->
69 | lazyInitRgbBase.call @
70 | @rgb.apply @, arguments
71 | @XYZ = ->
72 | lazyInitRgbBase.call @
73 | @XYZ.apply @, arguments
74 | return
75 |
76 |
77 |
78 | # RGB space prototype
79 | # -------------------
80 |
81 | rgbSpacePrototype =
82 |
83 |
84 | gamma: (x) ->
85 | pow(x, @g)
86 |
87 | gammaInv: (x) ->
88 | pow(x, @gInv)
89 |
90 | rgb: (XYZ, T = do require "../rgb") ->
91 | a = @baseInv
92 | T.r = @gammaInv a[0] * XYZ.X + a[1] * XYZ.Y + a[2] * XYZ.Z
93 | T.g = @gammaInv a[3] * XYZ.X + a[4] * XYZ.Y + a[5] * XYZ.Z
94 | T.b = @gammaInv a[6] * XYZ.X + a[7] * XYZ.Y + a[8] * XYZ.Z
95 | T
96 |
97 | XYZ: (Rgb, T = XYZ()) ->
98 | a = @base
99 | gr = @gamma Rgb.r
100 | gg = @gamma Rgb.g
101 | gb = @gamma Rgb.b
102 | T.X = a[0] * gr + a[1] * gg + a[2] * gb
103 | T.Y = a[3] * gr + a[4] * gg + a[5] * gb
104 | T.Z = a[6] * gr + a[7] * gg + a[8] * gb
105 | T
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | # Public api
114 | # ----------
115 |
116 |
117 |
118 | createSpace = O rgbSpaceConstructor, rgbSpacePrototype
119 |
120 | createSpace["Adobe-98"] = createSpace (xyY 0.6400, 0.3300), (xyY 0.2100, 0.7100), (xyY 0.1500, 0.0600), white.D65, 2.2
121 | createSpace["Adobe-RGB"] = createSpace (xyY 0.6250, 0.3400), (xyY 0.2800, 0.5950), (xyY 0.1550, 0.0700), white.D65, 1.8
122 | createSpace["CIE-RGB"] = createSpace (xyY 0.7350, 0.2650), (xyY 0.2740, 0.7170), (xyY 0.1670, 0.0090), white.E , 1
123 | createSpace["ColorMatch"] = createSpace (xyY 0.6300, 0.3400), (xyY 0.2950, 0.6050), (xyY 0.1500, 0.0750), white.D50, 1.8
124 | createSpace["EBU-Monitor"] = createSpace (xyY 0.6314, 0.3391), (xyY 0.2809, 0.5971), (xyY 0.1487, 0.0645), white.D50, 1.9
125 | createSpace["ECI-RGB"] = createSpace (xyY 0.6700, 0.3300), (xyY 0.2100, 0.7100), (xyY 0.1400, 0.0800), white.D50, 1.8
126 | createSpace["HDTV"] = createSpace (xyY 0.6400, 0.3300), (xyY 0.2900, 0.6000), (xyY 0.1500, 0.0600), white.D65, 2.2
127 | createSpace["Kodak-DC"] = createSpace (xyY 0.6492, 0.3314), (xyY 0.3219, 0.5997), (xyY 0.1548, 0.0646), white.D50, 2.22
128 | createSpace["NTSC-53"] = createSpace (xyY 0.6700, 0.3300), (xyY 0.2100, 0.7100), (xyY 0.1400, 0.0800), white.C , 2.2
129 | createSpace["PAL-SECAM"] = createSpace (xyY 0.6400, 0.3300), (xyY 0.2900, 0.6000), (xyY 0.1500, 0.0600), white.D65, 2.2
130 | createSpace["sRGB"] = createSpace (xyY 0.6400, 0.3300), (xyY 0.3000, 0.6000), (xyY 0.1500, 0.0600), white.D65, gammaSRgb, gammaSRgbInv
131 | createSpace["WideGamut"] = createSpace (xyY 0.7347, 0.2653), (xyY 0.1152, 0.8264), (xyY 0.1566, 0.0177), white.D50, 2.2
132 |
133 |
134 | module.exports = createSpace
135 |
--------------------------------------------------------------------------------
/src/white.coffee:
--------------------------------------------------------------------------------
1 | # Map of white points in *XYZ* color space.
2 |
3 | # Imports
4 | # -------
5 |
6 | # *xyY* color constructor.
7 | xyY = require "./xyY"
8 |
9 |
10 | # Helpers
11 | # -------
12 |
13 | # Create *XYZ* color with luminance *1* from chromaticity.
14 | xy = (x, y) -> do (xyY x, y, 1).XYZ
15 |
16 |
17 | # Public API
18 | # ----------
19 |
20 | # Export Map of white points.
21 | module.exports =
22 | A: xy 0.44757, 0.40745
23 | B: xy 0.34842, 0.35161
24 | C: xy 0.31006, 0.31616
25 | D50: xy 0.34567, 0.35850
26 | D55: xy 0.33242, 0.34743
27 | D65: xy 0.31271, 0.32902
28 | D75: xy 0.29902, 0.31485
29 | E: xy 1 / 3, 1 / 3
30 | F1: xy 0.31310, 0.33727
31 | F2: xy 0.37208, 0.37529
32 | F3: xy 0.40910, 0.39430
33 | F4: xy 0.44018, 0.40329
34 | F5: xy 0.31379, 0.34531
35 | F6: xy 0.37790, 0.38835
36 | F7: xy 0.31292, 0.32933
37 | F8: xy 0.34588, 0.35875
38 | F9: xy 0.37417, 0.37281
39 | F10: xy 0.34609, 0.35986
40 | F11: xy 0.38052, 0.37713
41 | F12: xy 0.43695, 0.40441
42 |
43 |
--------------------------------------------------------------------------------
/src/xyY.coffee:
--------------------------------------------------------------------------------
1 | # XYZ and xyY color space implementation.
2 |
3 | # Imports
4 | # -------
5 |
6 | O = require "ut1l/create/object"
7 |
8 | # xyY prototype
9 | # -------------
10 |
11 | xyyPrototype =
12 |
13 | # Convert color from *xyY* to *XYZ* color space. Give optional *XYZ* color `T` to store the result.
14 | XYZ: (T = do require "./XYZ") ->
15 | T.X = @x * @Y / @y
16 | T.Y = @Y
17 | T.Z = (1 - @x - @y) * @Y / @y
18 | T
19 |
20 | # Returns `true` if all components of the color are not `null` and not `undefined`.
21 | isDefined: ->
22 | @x? and @y? and @Y?
23 |
24 | # Returns a human readable string serialization of the color.
25 | toString: ->
26 | "x=#{@x}, y=#{@y}, Y=#{@Y}"
27 |
28 |
29 |
30 |
31 | # Public API
32 | # ----------
33 |
34 | module.exports = O ((@x, @y, @Y) ->), xyyPrototype
--------------------------------------------------------------------------------
/tasks/mdExtract.coffee:
--------------------------------------------------------------------------------
1 | esprima = require "esprima"
2 |
3 | separator = "\n// ---------------------------------------------------------------------------------------------------------------------\n"
4 |
5 | header = (text) ->
6 | pos = Math.floor (separator.length - text.length) / 2
7 | (separator.substring 0, pos) + text + (separator.substring pos + text.length)
8 |
9 | extractVars = (node, vars, declaration) ->
10 | switch node.type
11 | when "Program"
12 | for child in node.body
13 | extractVars child, vars
14 | when "VariableDeclaration"
15 | for dec in node.declarations
16 | extractVars dec, vars
17 | when "VariableDeclarator"
18 | extractVars node.id, vars, true
19 | if node.init?
20 | extractVars node.init, vars
21 | when "MemberExpression"
22 | extractVars node.object, vars
23 | when "Identifier"
24 | vars[node.name] = vars[node.name] == true || declaration == true
25 | when "CallExpression"
26 | extractVars node.callee, vars
27 | for arg in node.arguments
28 | extractVars arg, vars
29 | when "ExpressionStatement"
30 | extractVars node.expression, vars
31 | when "AssignmentExpression"
32 | extractVars node.left, vars
33 | extractVars node.right, vars
34 | when "BinaryExpression"
35 | extractVars node.left, vars
36 | extractVars node.right, vars
37 | when "ForStatement"
38 | extractVars node.init, vars
39 | extractVars node.test, vars
40 | extractVars node.update, vars
41 | extractVars node.body, vars
42 |
43 |
44 | getVars = (ast) ->
45 | vars = {}
46 | extractVars ast, vars
47 | vars
48 |
49 | module.exports = (grunt) ->
50 |
51 | grunt.registerMultiTask "mdExtract", ->
52 |
53 | files = @files.slice()
54 |
55 |
56 | for file in files
57 |
58 | destDir = file.dest
59 |
60 | sfiles = file.src.filter (filepath) ->
61 | # Warn on and remove invalid source files (if nonull was set).
62 | fileExists = grunt.file.exists filepath
63 | if not fileExists
64 | grunt.log.warn "Source file '#{filepath}' not found."
65 | fileExists
66 |
67 | rexp = /```javascript([^`]*)/g
68 |
69 | for sfile in sfiles # iterate source files
70 |
71 | outJs = "module.exports = [\n"
72 |
73 | str = grunt.file.read sfile
74 |
75 | # extract snippets array
76 | snippets = while (myArray = (rexp.exec str)) != null
77 | myArray[1]
78 |
79 |
80 | snippets = snippets.map (snippet, i) ->
81 | snippet = snippet.replace "require", "_require"
82 |
83 | vars = getVars esprima.parse snippet
84 | allVars = Object.keys vars
85 |
86 | undeclared = []
87 | for v of vars
88 | if not vars[v]
89 | undeclared.push v
90 |
91 | out = "function(#{if allVars.length > 0 then "_" else ""}){"
92 |
93 | # declare undeclared variables
94 | if undeclared.length > 0
95 | out += "var #{undeclared.join ","};"
96 | # initialize variables
97 | if allVars.length > 0
98 | out += "if(_!=null){"
99 | out += (allVars.map (v) ->
100 | "#{v}=_.#{v};").join ""
101 | out += "}"
102 |
103 | out += header " Snippet #{i} "
104 |
105 | out += snippet
106 |
107 | out += separator
108 | out += "return{#{allVars.map (v) -> "#{v}:#{v}"}};}"
109 |
110 | outJs += snippets.join ","
111 |
112 |
113 | outJs += "];\n"
114 |
115 | grunt.file.write "#{destDir}/#{sfile}.js", outJs
116 |
--------------------------------------------------------------------------------
/test/browser/jasmine/SpecRunner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Jasmine Spec Runner v2.0.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/test/browser/jasmine/lib/jasmine-jsreporter.js:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of the Jasmine JSReporter project from Ivan De Marino.
3 |
4 | Copyright (C) 2011-2014 Ivan De Marino
5 | Copyright (C) 2014 Alex Treppass
6 |
7 | Redistribution and use in source and binary forms, with or without
8 | modification, are permitted provided that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright
11 | notice, this list of conditions and the following disclaimer.
12 | * Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 | * Neither the name of the nor the
16 | names of its contributors may be used to endorse or promote products
17 | derived from this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 | ARE DISCLAIMED. IN NO EVENT SHALL IVAN DE MARINO BE LIABLE FOR ANY
23 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | */
30 | (function (jasmine) {
31 |
32 | if (!jasmine) {
33 | throw new Error("[Jasmine JSReporter] 'Jasmine' library not found");
34 | }
35 |
36 | // ------------------------------------------------------------------------
37 | // Jasmine JSReporter for Jasmine 1.x
38 | // ------------------------------------------------------------------------
39 |
40 | /**
41 | * Calculate elapsed time, in Seconds.
42 | * @param startMs Start time in Milliseconds
43 | * @param finishMs Finish time in Milliseconds
44 | * @return Elapsed time in Seconds */
45 | function elapsedSec (startMs, finishMs) {
46 | return (finishMs - startMs) / 1000;
47 | }
48 |
49 | /**
50 | * Round an amount to the given number of Digits.
51 | * If no number of digits is given, than '2' is assumed.
52 | * @param amount Amount to round
53 | * @param numOfDecDigits Number of Digits to round to. Default value is '2'.
54 | * @return Rounded amount */
55 | function round (amount, numOfDecDigits) {
56 | numOfDecDigits = numOfDecDigits || 2;
57 | return Math.round(amount * Math.pow(10, numOfDecDigits)) / Math.pow(10, numOfDecDigits);
58 | }
59 |
60 | /**
61 | * Create a new array which contains only the failed items.
62 | * @param items Items which will be filtered
63 | * @returns {Array} of failed items */
64 | function failures (items) {
65 | var fs = [], i, v;
66 | for (i = 0; i < items.length; i += 1) {
67 | v = items[i];
68 | if (!v.passed_) {
69 | fs.push(v);
70 | }
71 | }
72 | return fs;
73 | }
74 |
75 | /**
76 | * Collect information about a Suite, recursively, and return a JSON result.
77 | * @param suite The Jasmine Suite to get data from
78 | */
79 | function getSuiteData (suite) {
80 | var suiteData = {
81 | description : suite.description,
82 | durationSec : 0,
83 | specs: [],
84 | suites: [],
85 | passed: true
86 | },
87 | specs = suite.specs(),
88 | suites = suite.suites(),
89 | i, ilen;
90 |
91 | // Loop over all the Suite's Specs
92 | for (i = 0, ilen = specs.length; i < ilen; ++i) {
93 | suiteData.specs[i] = {
94 | description : specs[i].description,
95 | durationSec : specs[i].durationSec,
96 | passed : specs[i].results().passedCount === specs[i].results().totalCount,
97 | skipped : specs[i].results().skipped,
98 | passedCount : specs[i].results().passedCount,
99 | failedCount : specs[i].results().failedCount,
100 | totalCount : specs[i].results().totalCount,
101 | failures: failures(specs[i].results().getItems())
102 | };
103 | suiteData.passed = !suiteData.specs[i].passed ? false : suiteData.passed;
104 | suiteData.durationSec += suiteData.specs[i].durationSec;
105 | }
106 |
107 | // Loop over all the Suite's sub-Suites
108 | for (i = 0, ilen = suites.length; i < ilen; ++i) {
109 | suiteData.suites[i] = getSuiteData(suites[i]); //< recursive population
110 | suiteData.passed = !suiteData.suites[i].passed ? false : suiteData.passed;
111 | suiteData.durationSec += suiteData.suites[i].durationSec;
112 | }
113 |
114 | // Rounding duration numbers to 3 decimal digits
115 | suiteData.durationSec = round(suiteData.durationSec, 4);
116 |
117 | return suiteData;
118 | }
119 |
120 | var JSReporter = function () {
121 | };
122 |
123 | JSReporter.prototype = {
124 | reportRunnerStarting: function (runner) {
125 | // Nothing to do
126 | },
127 |
128 | reportSpecStarting: function (spec) {
129 | // Start timing this spec
130 | spec.startedAt = new Date();
131 | },
132 |
133 | reportSpecResults: function (spec) {
134 | // Finish timing this spec and calculate duration/delta (in sec)
135 | spec.finishedAt = new Date();
136 | // If the spec was skipped, reportSpecStarting is never called and spec.startedAt is undefined
137 | spec.durationSec = spec.startedAt ? elapsedSec(spec.startedAt.getTime(), spec.finishedAt.getTime()) : 0;
138 | },
139 |
140 | reportSuiteResults: function (suite) {
141 | // Nothing to do
142 | },
143 |
144 | reportRunnerResults: function (runner) {
145 | var suites = runner.suites(),
146 | i, j, ilen;
147 |
148 | // Attach results to the "jasmine" object to make those results easy to scrap/find
149 | jasmine.runnerResults = {
150 | suites: [],
151 | durationSec : 0,
152 | passed : true
153 | };
154 |
155 | // Loop over all the Suites
156 | for (i = 0, ilen = suites.length, j = 0; i < ilen; ++i) {
157 | if (suites[i].parentSuite === null) {
158 | jasmine.runnerResults.suites[j] = getSuiteData(suites[i]);
159 | // If 1 suite fails, the whole runner fails
160 | jasmine.runnerResults.passed = !jasmine.runnerResults.suites[j].passed ? false : jasmine.runnerResults.passed;
161 | // Add up all the durations
162 | jasmine.runnerResults.durationSec += jasmine.runnerResults.suites[j].durationSec;
163 | j++;
164 | }
165 | }
166 |
167 | // Decorate the 'jasmine' object with getters
168 | jasmine.getJSReport = function () {
169 | if (jasmine.runnerResults) {
170 | return jasmine.runnerResults;
171 | }
172 | return null;
173 | };
174 | jasmine.getJSReportAsString = function () {
175 | return JSON.stringify(jasmine.getJSReport());
176 | };
177 | }
178 | };
179 |
180 | // export public
181 | jasmine.JSReporter = JSReporter;
182 |
183 |
184 | // ------------------------------------------------------------------------
185 | // Jasmine JSReporter for Jasmine 2.0
186 | // ------------------------------------------------------------------------
187 |
188 | /*
189 | Simple timer implementation
190 | */
191 | var Timer = function () {};
192 |
193 | Timer.prototype.start = function () {
194 | this.startTime = new Date().getTime();
195 | return this;
196 | };
197 |
198 | Timer.prototype.elapsed = function () {
199 | if (this.startTime == null) {
200 | return -1;
201 | }
202 | return new Date().getTime() - this.startTime;
203 | };
204 |
205 | /*
206 | Utility methods
207 | */
208 | var _extend = function (obj1, obj2) {
209 | for (var prop in obj2) {
210 | obj1[prop] = obj2[prop];
211 | }
212 | return obj1;
213 | };
214 | var _clone = function (obj) {
215 | if (obj !== Object(obj)) {
216 | return obj;
217 | }
218 | return _extend({}, obj);
219 | };
220 |
221 | jasmine.JSReporter2 = function () {
222 | this.specs = {};
223 | this.suites = {};
224 | this.rootSuites = [];
225 | this.suiteStack = [];
226 |
227 | // export methods under jasmine namespace
228 | jasmine.getJSReport = this.getJSReport;
229 | jasmine.getJSReportAsString = this.getJSReportAsString;
230 | };
231 |
232 | var JSR = jasmine.JSReporter2.prototype;
233 |
234 | // Reporter API methods
235 | // --------------------
236 |
237 | JSR.suiteStarted = function (suite) {
238 | suite = this._cacheSuite(suite);
239 | // build up suite tree as we go
240 | suite.specs = [];
241 | suite.suites = [];
242 | suite.passed = true;
243 | suite.parentId = this.suiteStack.slice(this.suiteStack.length -1)[0];
244 | if (suite.parentId) {
245 | this.suites[suite.parentId].suites.push(suite);
246 | } else {
247 | this.rootSuites.push(suite.id);
248 | }
249 | this.suiteStack.push(suite.id);
250 | suite.timer = new Timer().start();
251 | };
252 |
253 | JSR.suiteDone = function (suite) {
254 | suite = this._cacheSuite(suite);
255 | suite.duration = suite.timer.elapsed();
256 | suite.durationSec = suite.duration / 1000;
257 | this.suiteStack.pop();
258 |
259 | // maintain parent suite state
260 | var parent = this.suites[suite.parentId];
261 | if (parent) {
262 | parent.passed = parent.passed && suite.passed;
263 | }
264 |
265 | // keep report representation clean
266 | delete suite.timer;
267 | delete suite.id;
268 | delete suite.parentId;
269 | delete suite.fullName;
270 | };
271 |
272 | JSR.specStarted = function (spec) {
273 | spec = this._cacheSpec(spec);
274 | spec.timer = new Timer().start();
275 | // build up suites->spec tree as we go
276 | spec.suiteId = this.suiteStack.slice(this.suiteStack.length -1)[0];
277 | this.suites[spec.suiteId].specs.push(spec);
278 | };
279 |
280 | JSR.specDone = function (spec) {
281 | spec = this._cacheSpec(spec);
282 |
283 | spec.duration = spec.timer.elapsed();
284 | spec.durationSec = spec.duration / 1000;
285 |
286 | spec.skipped = spec.status === 'pending';
287 | spec.passed = spec.skipped || spec.status === 'passed';
288 |
289 | // totalCount and passedCount will be populated if/when jasmine#575 gets accepted
290 | spec.totalCount = spec.totalExpectations || 0;
291 | spec.passedCount = spec.passedExpectations ? spec.passedExpectations.length : 0;
292 |
293 | spec.failedCount = spec.failedExpectations.length;
294 | spec.failures = [];
295 |
296 | for (var i = 0, j = spec.failedExpectations.length; i < j; i++) {
297 | var fail = spec.failedExpectations[i];
298 | spec.failures.push({
299 | type: 'expect',
300 | expected: fail.expected,
301 | passed: false,
302 | message: fail.message,
303 | matcherName: fail.matcherName,
304 | trace: {
305 | stack: fail.stack
306 | }
307 | });
308 | }
309 |
310 | // maintain parent suite state
311 | var parent = this.suites[spec.suiteId];
312 | if (spec.failed) {
313 | parent.failingSpecs.push(spec);
314 | }
315 | parent.passed = parent.passed && spec.passed;
316 |
317 | // keep report representation clean
318 | delete spec.timer;
319 | delete spec.totalExpectations;
320 | delete spec.passedExpectations;
321 | delete spec.suiteId;
322 | delete spec.fullName;
323 | delete spec.id;
324 | delete spec.status;
325 | delete spec.failedExpectations;
326 | };
327 |
328 | JSR.jasmineDone = function () {
329 | this._buildReport();
330 | };
331 |
332 | JSR.getJSReport = function () {
333 | if (jasmine.jsReport) {
334 | return jasmine.jsReport;
335 | }
336 | };
337 |
338 | JSR.getJSReportAsString = function () {
339 | if (jasmine.jsReport) {
340 | return JSON.stringify(jasmine.jsReport);
341 | }
342 | };
343 |
344 | // Private methods
345 | // ---------------
346 |
347 | JSR._haveSpec = function (spec) {
348 | return this.specs[spec.id] != null;
349 | };
350 |
351 | JSR._cacheSpec = function (spec) {
352 | var existing = this.specs[spec.id];
353 | if (existing == null) {
354 | existing = this.specs[spec.id] = _clone(spec);
355 | } else {
356 | _extend(existing, spec);
357 | }
358 | return existing;
359 | };
360 |
361 | JSR._haveSuite = function (suite) {
362 | return this.suites[suite.id] != null;
363 | };
364 |
365 | JSR._cacheSuite = function (suite) {
366 | var existing = this.suites[suite.id];
367 | if (existing == null) {
368 | existing = this.suites[suite.id] = _clone(suite);
369 | } else {
370 | _extend(existing, suite);
371 | }
372 | return existing;
373 | };
374 |
375 | // workaround for sauce labs bug.
376 | // TODO: remove after they fixed the bug
377 | function thereCanBeOnlyOne(suite) {
378 | if (suite.passed) { // get first passing test
379 | if (suite.specs.length > 0) {
380 | suite.specs = [suite.specs[0]];
381 | suite.suites = [];
382 | return true;
383 | } else {
384 | thereCanBeOnlyOne(suite.suites[0]);
385 | suite.suites = [suite.suites[0]];
386 | }
387 | } else {
388 | var failedSuites = [];
389 | for (var i = 0; i < suite.suites.length; i += 1) {
390 | var s = suite.suites[i];
391 | if (!s.passed) {
392 | failedSuites.push(s);
393 | thereCanBeOnlyOne(s);
394 | }
395 | }
396 | suite.suites = failedSuites;
397 | var failedSpecs = [];
398 | for (i = 0; i < suite.specs.length; i += 1) {
399 | var spec = suite.specs[i];
400 | if (!spec.passed) {
401 | failedSpecs.push(spec);
402 | }
403 | }
404 | suite.specs = failedSpecs;
405 | }
406 | }
407 |
408 | // workaround for sauce labs bug.
409 | // TODO: remove after they fixed the bug
410 | function countPassed(suite) {
411 | for (var i = 0; i < suite.specs.length; i += 1) {
412 | var spec = suite.specs[i];
413 | if (spec.passed) {
414 | if (spec.passedCount === 0) {
415 | spec.passedCount = 1;
416 | }
417 |
418 | if (spec.totalCount === 0) {
419 | spec.totalCount = 1;
420 | }
421 | }
422 | }
423 | for (i = 0; i < suite.suites.length; i += 1) {
424 | countPassed(suite.suites[i]);
425 | }
426 | }
427 |
428 | JSR._buildReport = function () {
429 | var overallDuration = 0;
430 | var overallPassed = true;
431 | var overallSuites = [];
432 |
433 | for (var i = 0, j = this.rootSuites.length; i < j; i++) {
434 | var suite = this.suites[this.rootSuites[i]];
435 | thereCanBeOnlyOne(suite);
436 | countPassed(suite);
437 | overallDuration += suite.duration;
438 | overallPassed = overallPassed && suite.passed;
439 | overallSuites.push(suite);
440 | }
441 |
442 | jasmine.jsReport = {
443 | passed: overallPassed,
444 | durationSec: overallDuration / 1000,
445 | suites: overallSuites
446 | };
447 | };
448 |
449 | })(jasmine);
--------------------------------------------------------------------------------
/test/browser/jasmine/lib/jasmine/boot.js:
--------------------------------------------------------------------------------
1 | /**
2 | Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
3 |
4 | If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
5 |
6 | The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
7 |
8 | [jasmine-gem]: http://github.com/pivotal/jasmine-gem
9 | */
10 |
11 | (function() {
12 |
13 | /**
14 | * ## Require & Instantiate
15 | *
16 | * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
17 | */
18 | window.jasmine = jasmineRequire.core(jasmineRequire);
19 |
20 | /**
21 | * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
22 | */
23 | jasmineRequire.html(jasmine);
24 |
25 | /**
26 | * Create the Jasmine environment. This is used to run all specs in a project.
27 | */
28 | var env = jasmine.getEnv();
29 |
30 | /**
31 | * ## The Global Interface
32 | *
33 | * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
34 | */
35 | var jasmineInterface = {
36 | describe: function(description, specDefinitions) {
37 | return env.describe(description, specDefinitions);
38 | },
39 |
40 | xdescribe: function(description, specDefinitions) {
41 | return env.xdescribe(description, specDefinitions);
42 | },
43 |
44 | it: function(desc, func) {
45 | return env.it(desc, func);
46 | },
47 |
48 | xit: function(desc, func) {
49 | return env.xit(desc, func);
50 | },
51 |
52 | beforeEach: function(beforeEachFunction) {
53 | return env.beforeEach(beforeEachFunction);
54 | },
55 |
56 | afterEach: function(afterEachFunction) {
57 | return env.afterEach(afterEachFunction);
58 | },
59 |
60 | expect: function(actual) {
61 | return env.expect(actual);
62 | },
63 |
64 | pending: function() {
65 | return env.pending();
66 | },
67 |
68 | spyOn: function(obj, methodName) {
69 | return env.spyOn(obj, methodName);
70 | },
71 |
72 | jsApiReporter: new jasmine.JsApiReporter({
73 | timer: new jasmine.Timer()
74 | })
75 | };
76 |
77 | /**
78 | * Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
79 | */
80 | if (typeof window == "undefined" && typeof exports == "object") {
81 | extend(exports, jasmineInterface);
82 | } else {
83 | extend(window, jasmineInterface);
84 | }
85 |
86 | /**
87 | * Expose the interface for adding custom equality testers.
88 | */
89 | jasmine.addCustomEqualityTester = function(tester) {
90 | env.addCustomEqualityTester(tester);
91 | };
92 |
93 | /**
94 | * Expose the interface for adding custom expectation matchers
95 | */
96 | jasmine.addMatchers = function(matchers) {
97 | return env.addMatchers(matchers);
98 | };
99 |
100 | /**
101 | * Expose the mock interface for the JavaScript timeout functions
102 | */
103 | jasmine.clock = function() {
104 | return env.clock;
105 | };
106 |
107 | /**
108 | * ## Runner Parameters
109 | *
110 | * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
111 | */
112 |
113 | var queryString = new jasmine.QueryString({
114 | getWindowLocation: function() { return window.location; }
115 | });
116 |
117 | var catchingExceptions = queryString.getParam("catch");
118 | env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
119 |
120 | /**
121 | * ## Reporters
122 | * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
123 | */
124 | var htmlReporter = new jasmine.HtmlReporter({
125 | env: env,
126 | onRaiseExceptionsClick: function() { queryString.setParam("catch", !env.catchingExceptions()); },
127 | getContainer: function() { return document.body; },
128 | createElement: function(type) { return document.createElement(type); },
129 | createTextNode: function(child) { return document.createTextNode(child); },
130 | timer: new jasmine.Timer()
131 | });
132 |
133 | /**
134 | * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
135 | */
136 | env.addReporter(jasmineInterface.jsApiReporter);
137 | env.addReporter(htmlReporter);
138 |
139 | /**
140 | * Filter which specs will be run by matching the start of the full name against the `spec` query param.
141 | */
142 | var specFilter = new jasmine.HtmlSpecFilter({
143 | filterString: function() { return queryString.getParam("spec"); }
144 | });
145 |
146 | env.specFilter = function(spec) {
147 | return specFilter.matches(spec.getFullName());
148 | };
149 |
150 | /**
151 | * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
152 | */
153 | window.setTimeout = window.setTimeout;
154 | window.setInterval = window.setInterval;
155 | window.clearTimeout = window.clearTimeout;
156 | window.clearInterval = window.clearInterval;
157 |
158 | /**
159 | * ## Execution
160 | *
161 | * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
162 | */
163 | var currentWindowOnload = window.onload;
164 |
165 | window.onload = function() {
166 | if (currentWindowOnload) {
167 | currentWindowOnload();
168 | }
169 | htmlReporter.initialize();
170 | env.execute();
171 | };
172 |
173 | /**
174 | * Helper function for readability above.
175 | */
176 | function extend(destination, source) {
177 | for (var property in source) destination[property] = source[property];
178 | return destination;
179 | }
180 |
181 | }());
182 |
--------------------------------------------------------------------------------
/test/browser/jasmine/lib/jasmine/console.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008-2013 Pivotal Labs
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 | function getJasmineRequireObj() {
24 | if (typeof module !== "undefined" && module.exports) {
25 | return exports;
26 | } else {
27 | window.jasmineRequire = window.jasmineRequire || {};
28 | return window.jasmineRequire;
29 | }
30 | }
31 |
32 | getJasmineRequireObj().console = function(jRequire, j$) {
33 | j$.ConsoleReporter = jRequire.ConsoleReporter();
34 | };
35 |
36 | getJasmineRequireObj().ConsoleReporter = function() {
37 |
38 | var noopTimer = {
39 | start: function(){},
40 | elapsed: function(){ return 0; }
41 | };
42 |
43 | function ConsoleReporter(options) {
44 | var print = options.print,
45 | showColors = options.showColors || false,
46 | onComplete = options.onComplete || function() {},
47 | timer = options.timer || noopTimer,
48 | specCount,
49 | failureCount,
50 | failedSpecs = [],
51 | pendingCount,
52 | ansi = {
53 | green: '\x1B[32m',
54 | red: '\x1B[31m',
55 | yellow: '\x1B[33m',
56 | none: '\x1B[0m'
57 | };
58 |
59 | this.jasmineStarted = function() {
60 | specCount = 0;
61 | failureCount = 0;
62 | pendingCount = 0;
63 | print("Started");
64 | printNewline();
65 | timer.start();
66 | };
67 |
68 | this.jasmineDone = function() {
69 | printNewline();
70 | for (var i = 0; i < failedSpecs.length; i++) {
71 | specFailureDetails(failedSpecs[i]);
72 | }
73 |
74 | printNewline();
75 | var specCounts = specCount + " " + plural("spec", specCount) + ", " +
76 | failureCount + " " + plural("failure", failureCount);
77 |
78 | if (pendingCount) {
79 | specCounts += ", " + pendingCount + " pending " + plural("spec", pendingCount);
80 | }
81 |
82 | print(specCounts);
83 |
84 | printNewline();
85 | var seconds = timer.elapsed() / 1000;
86 | print("Finished in " + seconds + " " + plural("second", seconds));
87 |
88 | printNewline();
89 |
90 | onComplete(failureCount === 0);
91 | };
92 |
93 | this.specDone = function(result) {
94 | specCount++;
95 |
96 | if (result.status == "pending") {
97 | pendingCount++;
98 | print(colored("yellow", "*"));
99 | return;
100 | }
101 |
102 | if (result.status == "passed") {
103 | print(colored("green", '.'));
104 | return;
105 | }
106 |
107 | if (result.status == "failed") {
108 | failureCount++;
109 | failedSpecs.push(result);
110 | print(colored("red", 'F'));
111 | }
112 | };
113 |
114 | return this;
115 |
116 | function printNewline() {
117 | print("\n");
118 | }
119 |
120 | function colored(color, str) {
121 | return showColors ? (ansi[color] + str + ansi.none) : str;
122 | }
123 |
124 | function plural(str, count) {
125 | return count == 1 ? str : str + "s";
126 | }
127 |
128 | function repeat(thing, times) {
129 | var arr = [];
130 | for (var i = 0; i < times; i++) {
131 | arr.push(thing);
132 | }
133 | return arr;
134 | }
135 |
136 | function indent(str, spaces) {
137 | var lines = (str || '').split("\n");
138 | var newArr = [];
139 | for (var i = 0; i < lines.length; i++) {
140 | newArr.push(repeat(" ", spaces).join("") + lines[i]);
141 | }
142 | return newArr.join("\n");
143 | }
144 |
145 | function specFailureDetails(result) {
146 | printNewline();
147 | print(result.fullName);
148 |
149 | for (var i = 0; i < result.failedExpectations.length; i++) {
150 | var failedExpectation = result.failedExpectations[i];
151 | printNewline();
152 | print(indent(failedExpectation.stack, 2));
153 | }
154 |
155 | printNewline();
156 | }
157 | }
158 |
159 | return ConsoleReporter;
160 | };
161 |
--------------------------------------------------------------------------------
/test/browser/jasmine/lib/jasmine/jasmine-html.js:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2008-2013 Pivotal Labs
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining
5 | a copy of this software and associated documentation files (the
6 | "Software"), to deal in the Software without restriction, including
7 | without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to
9 | permit persons to whom the Software is furnished to do so, subject to
10 | the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | */
23 | jasmineRequire.html = function(j$) {
24 | j$.ResultsNode = jasmineRequire.ResultsNode();
25 | j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
26 | j$.QueryString = jasmineRequire.QueryString();
27 | j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
28 | };
29 |
30 | jasmineRequire.HtmlReporter = function(j$) {
31 |
32 | var noopTimer = {
33 | start: function() {},
34 | elapsed: function() { return 0; }
35 | };
36 |
37 | function HtmlReporter(options) {
38 | var env = options.env || {},
39 | getContainer = options.getContainer,
40 | createElement = options.createElement,
41 | createTextNode = options.createTextNode,
42 | onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {},
43 | timer = options.timer || noopTimer,
44 | results = [],
45 | specsExecuted = 0,
46 | failureCount = 0,
47 | pendingSpecCount = 0,
48 | htmlReporterMain,
49 | symbols;
50 |
51 | this.initialize = function() {
52 | htmlReporterMain = createDom("div", {className: "html-reporter"},
53 | createDom("div", {className: "banner"},
54 | createDom("span", {className: "title"}, "Jasmine"),
55 | createDom("span", {className: "version"}, j$.version)
56 | ),
57 | createDom("ul", {className: "symbol-summary"}),
58 | createDom("div", {className: "alert"}),
59 | createDom("div", {className: "results"},
60 | createDom("div", {className: "failures"})
61 | )
62 | );
63 | getContainer().appendChild(htmlReporterMain);
64 |
65 | symbols = find(".symbol-summary");
66 | };
67 |
68 | var totalSpecsDefined;
69 | this.jasmineStarted = function(options) {
70 | totalSpecsDefined = options.totalSpecsDefined || 0;
71 | timer.start();
72 | };
73 |
74 | var summary = createDom("div", {className: "summary"});
75 |
76 | var topResults = new j$.ResultsNode({}, "", null),
77 | currentParent = topResults;
78 |
79 | this.suiteStarted = function(result) {
80 | currentParent.addChild(result, "suite");
81 | currentParent = currentParent.last();
82 | };
83 |
84 | this.suiteDone = function(result) {
85 | if (currentParent == topResults) {
86 | return;
87 | }
88 |
89 | currentParent = currentParent.parent;
90 | };
91 |
92 | this.specStarted = function(result) {
93 | currentParent.addChild(result, "spec");
94 | };
95 |
96 | var failures = [];
97 | this.specDone = function(result) {
98 | if (result.status != "disabled") {
99 | specsExecuted++;
100 | }
101 |
102 | symbols.appendChild(createDom("li", {
103 | className: result.status,
104 | id: "spec_" + result.id,
105 | title: result.fullName
106 | }
107 | ));
108 |
109 | if (result.status == "failed") {
110 | failureCount++;
111 |
112 | var failure =
113 | createDom("div", {className: "spec-detail failed"},
114 | createDom("div", {className: "description"},
115 | createDom("a", {title: result.fullName, href: specHref(result)}, result.fullName)
116 | ),
117 | createDom("div", {className: "messages"})
118 | );
119 | var messages = failure.childNodes[1];
120 |
121 | for (var i = 0; i < result.failedExpectations.length; i++) {
122 | var expectation = result.failedExpectations[i];
123 | messages.appendChild(createDom("div", {className: "result-message"}, expectation.message));
124 | messages.appendChild(createDom("div", {className: "stack-trace"}, expectation.stack));
125 | }
126 |
127 | failures.push(failure);
128 | }
129 |
130 | if (result.status == "pending") {
131 | pendingSpecCount++;
132 | }
133 | };
134 |
135 | this.jasmineDone = function() {
136 | var banner = find(".banner");
137 | banner.appendChild(createDom("span", {className: "duration"}, "finished in " + timer.elapsed() / 1000 + "s"));
138 |
139 | var alert = find(".alert");
140 |
141 | alert.appendChild(createDom("span", { className: "exceptions" },
142 | createDom("label", { className: "label", 'for': "raise-exceptions" }, "raise exceptions"),
143 | createDom("input", {
144 | className: "raise",
145 | id: "raise-exceptions",
146 | type: "checkbox"
147 | })
148 | ));
149 | var checkbox = find("input");
150 |
151 | checkbox.checked = !env.catchingExceptions();
152 | checkbox.onclick = onRaiseExceptionsClick;
153 |
154 | if (specsExecuted < totalSpecsDefined) {
155 | var skippedMessage = "Ran " + specsExecuted + " of " + totalSpecsDefined + " specs - run all";
156 | alert.appendChild(
157 | createDom("span", {className: "bar skipped"},
158 | createDom("a", {href: "?", title: "Run all specs"}, skippedMessage)
159 | )
160 | );
161 | }
162 | var statusBarMessage = "" + pluralize("spec", specsExecuted) + ", " + pluralize("failure", failureCount);
163 | if (pendingSpecCount) { statusBarMessage += ", " + pluralize("pending spec", pendingSpecCount); }
164 |
165 | var statusBarClassName = "bar " + ((failureCount > 0) ? "failed" : "passed");
166 | alert.appendChild(createDom("span", {className: statusBarClassName}, statusBarMessage));
167 |
168 | var results = find(".results");
169 | results.appendChild(summary);
170 |
171 | summaryList(topResults, summary);
172 |
173 | function summaryList(resultsTree, domParent) {
174 | var specListNode;
175 | for (var i = 0; i < resultsTree.children.length; i++) {
176 | var resultNode = resultsTree.children[i];
177 | if (resultNode.type == "suite") {
178 | var suiteListNode = createDom("ul", {className: "suite", id: "suite-" + resultNode.result.id},
179 | createDom("li", {className: "suite-detail"},
180 | createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description)
181 | )
182 | );
183 |
184 | summaryList(resultNode, suiteListNode);
185 | domParent.appendChild(suiteListNode);
186 | }
187 | if (resultNode.type == "spec") {
188 | if (domParent.getAttribute("class") != "specs") {
189 | specListNode = createDom("ul", {className: "specs"});
190 | domParent.appendChild(specListNode);
191 | }
192 | specListNode.appendChild(
193 | createDom("li", {
194 | className: resultNode.result.status,
195 | id: "spec-" + resultNode.result.id
196 | },
197 | createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description)
198 | )
199 | );
200 | }
201 | }
202 | }
203 |
204 | if (failures.length) {
205 | alert.appendChild(
206 | createDom('span', {className: "menu bar spec-list"},
207 | createDom("span", {}, "Spec List | "),
208 | createDom('a', {className: "failures-menu", href: "#"}, "Failures")));
209 | alert.appendChild(
210 | createDom('span', {className: "menu bar failure-list"},
211 | createDom('a', {className: "spec-list-menu", href: "#"}, "Spec List"),
212 | createDom("span", {}, " | Failures ")));
213 |
214 | find(".failures-menu").onclick = function() {
215 | setMenuModeTo('failure-list');
216 | };
217 | find(".spec-list-menu").onclick = function() {
218 | setMenuModeTo('spec-list');
219 | };
220 |
221 | setMenuModeTo('failure-list');
222 |
223 | var failureNode = find(".failures");
224 | for (var i = 0; i < failures.length; i++) {
225 | failureNode.appendChild(failures[i]);
226 | }
227 | }
228 | };
229 |
230 | return this;
231 |
232 | function find(selector) {
233 | var container = getContainer(), cls, elem, i;
234 | if (typeof container.querySelector !== "function") { // IE6 compatibility
235 | if (selector.charAt(0) === ".") {
236 | cls = selector.substring(1);
237 | elem = container.getElementsByTagName("*");
238 | for (i = 0; i < elem.length; i += 1) {
239 | if ((" " + elem[i].className + " ").indexOf(" " + cls + " ") !== -1) {
240 | return elem[i];
241 | }
242 | }
243 | } else {
244 | return container.getElementsByTagName(selector);
245 | }
246 | } else { // IE7+
247 | return container.querySelector(selector);
248 | }
249 | }
250 |
251 | function createDom(type, attrs, childrenVarArgs) {
252 | var el = createElement(type);
253 |
254 | for (var i = 2; i < arguments.length; i++) {
255 | var child = arguments[i];
256 |
257 | if (typeof child === 'string') {
258 | el.appendChild(createTextNode(child));
259 | } else {
260 | if (child) {
261 | el.appendChild(child);
262 | }
263 | }
264 | }
265 |
266 | for (var attr in attrs) {
267 | if (attr == "className") {
268 | el[attr] = attrs[attr];
269 | } else {
270 | el.setAttribute(attr, attrs[attr]);
271 | }
272 | }
273 |
274 | return el;
275 | }
276 |
277 | function pluralize(singular, count) {
278 | var word = (count == 1 ? singular : singular + "s");
279 |
280 | return "" + count + " " + word;
281 | }
282 |
283 | function specHref(result) {
284 | return "?spec=" + encodeURIComponent(result.fullName);
285 | }
286 |
287 | function setMenuModeTo(mode) {
288 | htmlReporterMain.setAttribute("class", "html-reporter " + mode);
289 | }
290 | }
291 |
292 | return HtmlReporter;
293 | };
294 |
295 | jasmineRequire.HtmlSpecFilter = function() {
296 | function HtmlSpecFilter(options) {
297 | var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
298 | var filterPattern = new RegExp(filterString);
299 |
300 | this.matches = function(specName) {
301 | return filterPattern.test(specName);
302 | };
303 | }
304 |
305 | return HtmlSpecFilter;
306 | };
307 |
308 | jasmineRequire.ResultsNode = function() {
309 | function ResultsNode(result, type, parent) {
310 | this.result = result;
311 | this.type = type;
312 | this.parent = parent;
313 |
314 | this.children = [];
315 |
316 | this.addChild = function(result, type) {
317 | this.children.push(new ResultsNode(result, type, this));
318 | };
319 |
320 | this.last = function() {
321 | return this.children[this.children.length - 1];
322 | };
323 | }
324 |
325 | return ResultsNode;
326 | };
327 |
328 | jasmineRequire.QueryString = function() {
329 | function QueryString(options) {
330 |
331 | this.setParam = function(key, value) {
332 | var paramMap = queryStringToParamMap();
333 | paramMap[key] = value;
334 | options.getWindowLocation().search = toQueryString(paramMap);
335 | };
336 |
337 | this.getParam = function(key) {
338 | return queryStringToParamMap()[key];
339 | };
340 |
341 | return this;
342 |
343 | function toQueryString(paramMap) {
344 | var qStrPairs = [];
345 | for (var prop in paramMap) {
346 | qStrPairs.push(encodeURIComponent(prop) + "=" + encodeURIComponent(paramMap[prop]));
347 | }
348 | return "?" + qStrPairs.join('&');
349 | }
350 |
351 | function queryStringToParamMap() {
352 | var paramStr = options.getWindowLocation().search.substring(1),
353 | params = [],
354 | paramMap = {};
355 |
356 | if (paramStr.length > 0) {
357 | params = paramStr.split('&');
358 | for (var i = 0; i < params.length; i++) {
359 | var p = params[i].split('=');
360 | var value = decodeURIComponent(p[1]);
361 | if (value === "true" || value === "false") {
362 | value = JSON.parse(value);
363 | }
364 | paramMap[decodeURIComponent(p[0])] = value;
365 | }
366 | }
367 |
368 | return paramMap;
369 | }
370 |
371 | }
372 |
373 | return QueryString;
374 | };
375 |
--------------------------------------------------------------------------------
/test/browser/jasmine/lib/jasmine/jasmine.css:
--------------------------------------------------------------------------------
1 | body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
2 |
3 | .html-reporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
4 | .html-reporter a { text-decoration: none; }
5 | .html-reporter a:hover { text-decoration: underline; }
6 | .html-reporter p, .html-reporter h1, .html-reporter h2, .html-reporter h3, .html-reporter h4, .html-reporter h5, .html-reporter h6 { margin: 0; line-height: 14px; }
7 | .html-reporter .banner, .html-reporter .symbol-summary, .html-reporter .summary, .html-reporter .result-message, .html-reporter .spec .description, .html-reporter .spec-detail .description, .html-reporter .alert .bar, .html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; }
8 | .html-reporter .banner .version { margin-left: 14px; }
9 | .html-reporter #jasmine_content { position: fixed; right: 100%; }
10 | .html-reporter .version { color: #aaaaaa; }
11 | .html-reporter .banner { margin-top: 14px; }
12 | .html-reporter .duration { color: #aaaaaa; float: right; }
13 | .html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; }
14 | .html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; }
15 | .html-reporter .symbol-summary li.passed { font-size: 14px; }
16 | .html-reporter .symbol-summary li.passed:before { color: #5e7d00; content: "\02022"; }
17 | .html-reporter .symbol-summary li.failed { line-height: 9px; }
18 | .html-reporter .symbol-summary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
19 | .html-reporter .symbol-summary li.disabled { font-size: 14px; }
20 | .html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; }
21 | .html-reporter .symbol-summary li.pending { line-height: 17px; }
22 | .html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; }
23 | .html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
24 | .html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
25 | .html-reporter .bar.failed { background-color: #b03911; }
26 | .html-reporter .bar.passed { background-color: #a6b779; }
27 | .html-reporter .bar.skipped { background-color: #bababa; }
28 | .html-reporter .bar.menu { background-color: #fff; color: #aaaaaa; }
29 | .html-reporter .bar.menu a { color: #333333; }
30 | .html-reporter .bar a { color: white; }
31 | .html-reporter.spec-list .bar.menu.failure-list, .html-reporter.spec-list .results .failures { display: none; }
32 | .html-reporter.failure-list .bar.menu.spec-list, .html-reporter.failure-list .summary { display: none; }
33 | .html-reporter .running-alert { background-color: #666666; }
34 | .html-reporter .results { margin-top: 14px; }
35 | .html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
36 | .html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
37 | .html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
38 | .html-reporter.showDetails .summary { display: none; }
39 | .html-reporter.showDetails #details { display: block; }
40 | .html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
41 | .html-reporter .summary { margin-top: 14px; }
42 | .html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; }
43 | .html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; }
44 | .html-reporter .summary li.passed a { color: #5e7d00; }
45 | .html-reporter .summary li.failed a { color: #b03911; }
46 | .html-reporter .summary li.pending a { color: #ba9d37; }
47 | .html-reporter .description + .suite { margin-top: 0; }
48 | .html-reporter .suite { margin-top: 14px; }
49 | .html-reporter .suite a { color: #333333; }
50 | .html-reporter .failures .spec-detail { margin-bottom: 28px; }
51 | .html-reporter .failures .spec-detail .description { background-color: #b03911; }
52 | .html-reporter .failures .spec-detail .description a { color: white; }
53 | .html-reporter .result-message { padding-top: 14px; color: #333333; white-space: pre; }
54 | .html-reporter .result-message span.result { display: block; }
55 | .html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
56 |
--------------------------------------------------------------------------------
/test/browser/jasmine/lib/jasmine/jasmine_favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hhelwich/c0lor/ac637c672ad08b17d6b2bbee8edc401e0aabe8f6/test/browser/jasmine/lib/jasmine/jasmine_favicon.png
--------------------------------------------------------------------------------
/test/browser/src/LCh.coffee:
--------------------------------------------------------------------------------
1 | C = require "./indexToTest"
2 |
3 |
4 | describe "LCh module", ->
5 |
6 | Lab1 = Lab2 = LCh1 = LCh2 = null
7 |
8 | beforeEach ->
9 |
10 | # same color in Lab color space with D50 reference white (http://www.brucelindbloom.com/)
11 | Lab1 = C.Lab 51.8372, -57.4865, -25.7804
12 | Lab2 = C.Lab 1.8066, -7.3832, -2.5470
13 |
14 | # reference values generated here: http://www.brucelindbloom.com/
15 | toRad = (a) ->
16 | (if a > 180 then a - 360 else a) * Math.PI / 180
17 | LCh1 = C.LCh 51.8372, 63.0026, toRad 204.1543
18 | LCh2 = C.LCh 1.8066, 7.8102, toRad 199.0330
19 |
20 | jasmine.addMatchers require "./matcher"
21 |
22 |
23 | describe "LCh.Lab() function", ->
24 |
25 | it "maps correctly to Lab", ->
26 |
27 | (expect LCh1.Lab()).toAllBeCloseTo Lab1, 0.0001
28 | (expect LCh2.Lab()).toAllBeCloseTo Lab2, 0.0001
29 |
30 |
31 | describe "LCh.toString()", ->
32 |
33 | it "creates informative output", ->
34 |
35 | (expect "#{C.LCh 51.8372, 63.0026, -2.7}").toBe "L=51.8372, C=63.0026, h=-2.7"
36 |
--------------------------------------------------------------------------------
/test/browser/src/Lab.coffee:
--------------------------------------------------------------------------------
1 | C = require "./indexToTest"
2 |
3 |
4 | describe "Lab Color", ->
5 |
6 | Lab1 = Lab2 = LCh1 = LCh2 = null
7 |
8 | beforeEach ->
9 |
10 | # same color in Lab color space with D50 reference white (http://www.brucelindbloom.com/)
11 | Lab1 = C.Lab 51.8372, -57.4865, -25.7804
12 | Lab2 = C.Lab 1.8066, -7.3832, -2.5470
13 |
14 | # reference values generated here: http://www.brucelindbloom.com/
15 | toRad = (a) ->
16 | (if a > 180 then a - 360 else a) * Math.PI / 180
17 | LCh1 = C.LCh 51.8372, 63.0026, toRad 204.1543
18 | LCh2 = C.LCh 1.8066, 7.8102, toRad 199.0330
19 |
20 | jasmine.addMatchers require "./matcher"
21 |
22 |
23 | describe "Lab.LCh() function", ->
24 |
25 | it "maps correctly to LCh", ->
26 |
27 | (expect Lab1.LCh()).toAllBeCloseTo LCh1, 4
28 | (expect Lab2.LCh()).toAllBeCloseTo LCh2, 4
29 |
30 |
31 | describe "Lab.toString()", ->
32 |
33 | it "creates informative output", ->
34 |
35 | (expect "#{Lab1}").toBe "L=51.8372, a=-57.4865, b=-25.7804"
36 |
--------------------------------------------------------------------------------
/test/browser/src/README.md.spec.coffee:
--------------------------------------------------------------------------------
1 | C = require "./indexToTest"
2 |
3 | snippets = require "./README.md"
4 |
5 | describe "README.md", ->
6 |
7 | beforeEach ->
8 | jasmine.addMatchers require "./matcher"
9 |
10 |
11 | describe "snippet 3", ->
12 |
13 | it "creates expected colors", ->
14 | # run snippet
15 | _ = snippets[3] C: C
16 | # verify
17 | (expect _.cyan_rgb).toEqual
18 | r: 0
19 | g: 1
20 | b: 1
21 | (expect _.red_RGB).toEqual
22 | R: 255
23 | G: 0
24 | B: 0
25 | (expect _.yellow_RGB).toEqual
26 | R: 255
27 | G: 255
28 | B: 0
29 |
30 | (expect _.magenta_hsv).toEqual
31 | h: 0.8
32 | s: 1
33 | v: 1
34 |
35 | (expect _.green_XYZ).toEqual
36 | X: 0.4
37 | Y: 0.7
38 | Z: 0.2
39 | (expect _.orange_xyY).toEqual
40 | x: 0.5
41 | y: 0.4
42 | Y: 0.3
43 |
44 | (expect _.purple_Lab).toEqual
45 | L: 31
46 | a: 24
47 | b: -22
48 | (expect _.blue_LCh).toEqual
49 | L: 30
50 | C: 56
51 | h: -1
52 |
53 |
54 | describe "snippet 4", ->
55 |
56 | it "converts colors correctly", ->
57 |
58 | # run snippets 3, 4
59 | _ = snippets[4] snippets[3] C: C
60 |
61 | # verify
62 | (expect _.cyan_RGB).toEqual
63 | R: 0
64 | G: 255
65 | B: 255
66 | (expect _.red_hexStr).toBe "FF0000"
67 | (expect _.yellow_rgb).toEqual
68 | r: 1
69 | g: 1
70 | b: 0
71 |
72 | (expect _.magenta_rgb).toEqual
73 | r: 0.8000000000000007
74 | g: 0
75 | b: 1
76 | (expect _.yellow_hsv).toEqual
77 | h: 0.16666666666666666
78 | s: 1
79 | v: 1
80 |
81 | (expect _.green_xyY).toEqual
82 | x: 0.3076923076923077
83 | y: 0.5384615384615384
84 | Y: 0.7
85 | (expect _.orange_XYZ).toEqual
86 | X: 0.37499999999999994
87 | Y: 0.3
88 | Z: 0.07499999999999997
89 |
90 | (expect _.purple_LCh).toEqual
91 | L: 31
92 | C: 32.55764119219941
93 | h: -0.7419472680059175
94 | (expect _.blue_Lab).toAllBeCloseTo
95 | L: 30
96 | a: 30.256929128615827
97 | b: -47.1223751492422
98 | , 13
99 |
100 |
101 | describe "snippet 5", ->
102 |
103 | it "converts color correctly", ->
104 |
105 | rgbColor = C.rgb()
106 | # run snippet
107 | _ = snippets[5]
108 | rgbColor: rgbColor
109 | hsvColor: C.hsv 0.8, 1, 1
110 |
111 | # verify
112 | (expect _.rgbColor).toBe rgbColor # in place?
113 | (expect _.rgbColor).toEqual
114 | r: 0.8000000000000007
115 | g: 0
116 | b: 1
117 |
--------------------------------------------------------------------------------
/test/browser/src/RGBInt.coffee:
--------------------------------------------------------------------------------
1 | C = require "./indexToTest"
2 |
3 |
4 | describe "RGB 24 bit color", ->
5 |
6 | XYZ2 = XYZ3 = XYZ2_s = XYZ3_s = null
7 | rgb1 = rgb2 = rgb3 = rgb4 = null
8 | RGB1 = RGB2 = RGB3 = RGB4 = null
9 | rgbCs1 = rgbCs2 = null
10 |
11 | beforeEach ->
12 |
13 | rgb1 = C.rgb 0, undefined, 0
14 | rgb2 = C.rgb 1, 1, 1
15 | rgb3 = C.rgb 0.5, 0.3, 0.2
16 | rgb4 = C.rgb 1.4, 1.00001, -0.1
17 |
18 | RGB1 = C.RGB 0, undefined, 0
19 | RGB2 = C.RGB 255, 255, 255
20 | RGB3 = C.RGB 128, 77, 51
21 | RGB4 = C.RGB undefined, 255, undefined
22 |
23 | rgbCs1 = C.space.rgb["Adobe-98"]
24 | # Adobe-98 / http://www.brucelindbloom.com
25 | XYZ2 = C.XYZ 0.95047, 1 , 1.08883
26 | XYZ3 = C.XYZ 0.1441 , 0.111282, 0.039618
27 |
28 | rgbCs2 = C.space.rgb.sRGB
29 | # sRGB / http://www.brucelindbloom.com
30 | XYZ2_s = C.XYZ 0.95047 , 1 , 1.08883
31 | XYZ3_s = C.XYZ 0.120444, 0.100287, 0.044327
32 |
33 | jasmine.addMatchers require "./matcher"
34 |
35 |
36 | describe "RGB.rgb()", ->
37 |
38 | it "RGB.rgb() maps correctly from 3 bit rgb", ->
39 |
40 | (expect RGB1.rgb()).toEqual rgb1
41 | (expect RGB2.rgb()).toEqual rgb2
42 | (expect RGB3.rgb()).toAllBeCloseTo rgb3, 2
43 | (expect RGB4.rgb()).toEqual C.rgb undefined, 1, undefined
44 |
45 | it "RGB -> rgb -> RGB = Id", ->
46 |
47 | (expect RGB1.rgb().RGB()).toEqual RGB1
48 | (expect RGB2.rgb().RGB()).toEqual RGB2
49 | (expect RGB3.rgb().RGB()).toEqual RGB3
50 | (expect RGB4.rgb().RGB()).toEqual RGB4
51 |
52 |
53 | describe "RGB.hex()", ->
54 |
55 | it "creates hex string", ->
56 |
57 | (expect RGB1.hex()).toBeUndefined()
58 | (expect RGB2.hex()).toBe "FFFFFF"
59 | (expect RGB3.hex()).toBe "804D33"
60 | (expect RGB4.hex()).toBeUndefined()
61 |
62 | it "parses hex string", ->
63 |
64 | (expect RGB1.hex "FFFFFF").toEqual RGB2
65 | (expect C.RGB().hex "804D33").toEqual RGB3
66 |
67 |
68 | describe "RGB.toString()", ->
69 |
70 | it "creates informative output", ->
71 |
72 | (expect "#{RGB3}").toBe "R=128, G=77, B=51"
73 |
74 |
75 | describe "RGB.isDefined()", ->
76 |
77 | it "is true if all components are defined", ->
78 |
79 | (expect RGB1.isDefined()).toBe false
80 | (expect RGB2.isDefined()).toBe true
81 | (expect RGB3.isDefined()).toBe true
82 | (expect RGB4.isDefined()).toBe false
83 |
84 |
85 | describe "RGB.isValid()", ->
86 |
87 | it "is true if all components are defined and in the desired range", ->
88 |
89 | (expect RGB1.isValid()).toBe false # not defined
90 | (expect RGB2.isValid()).toBe true
91 | (expect RGB3.isValid()).toBe true
92 | (expect RGB4.isValid()).toBe false # not defined
93 | # out of range
94 | (expect (C.RGB -1, 0, 0).isValid()).toBe false
95 | (expect (C.RGB 0, -1, 0).isValid()).toBe false
96 | (expect (C.RGB 0, 0, -1).isValid()).toBe false
97 | (expect (C.RGB 256, 0, 0).isValid()).toBe false
98 | # rational number
99 | (expect (C.RGB 0, 0.1, 0).isValid()).toBe false
100 |
--------------------------------------------------------------------------------
/test/browser/src/XYZ.coffee:
--------------------------------------------------------------------------------
1 | C = require "./indexToTest"
2 |
3 |
4 | describe "XYZ Colorspace module", ->
5 |
6 | XYZ = null
7 | xyY = null
8 |
9 | beforeEach ->
10 |
11 | XYZ = C.XYZ 0.1, 0.2, 0.3
12 | # same color in xyY color space
13 | xyY = C.xyY 1/6, 1/3, 0.2
14 |
15 | jasmine.addMatchers require "./matcher"
16 |
17 |
18 | describe "XYZ constructors", ->
19 |
20 | it "creates a new expected XYZ object", ->
21 |
22 | (expect XYZ.X).toBe 0.1
23 | (expect XYZ.Y).toBe 0.2
24 | (expect XYZ.Z).toBe 0.3
25 |
26 |
27 | describe "XYZ <-> xyY", ->
28 |
29 | it "XYZ maps correctly to xyY", ->
30 |
31 | (expect XYZ.xyY()).toEqual xyY
32 |
33 | it "can map XYZ to xyY in place", ->
34 |
35 | x = C.xyY 0.1, 0.1, 0.1
36 | y = XYZ.xyY x
37 | (expect y).toEqual xyY
38 | (expect y).toBe x
39 |
40 |
41 | describe "XYZ.isDefined()", ->
42 |
43 | it "is true if all components are defined", ->
44 |
45 | (expect C.XYZ().isDefined()).toBe false
46 | (expect (C.XYZ 1).isDefined()).toBe false
47 | (expect (C.XYZ 0.1, 0.2, 0.3).isDefined()).toBe true
48 |
49 |
50 | describe "XYZ.toString()", ->
51 |
52 | it "creates informative output", ->
53 |
54 | (expect "#{XYZ}").toBe "X=0.1, Y=0.2, Z=0.3"
55 |
--------------------------------------------------------------------------------
/test/browser/src/console.coffee:
--------------------------------------------------------------------------------
1 | # create console to log test debug output
2 |
3 | charsToReplace =
4 | "&": "&"
5 | "<": "<"
6 | ">": ">"
7 |
8 | replaceChar = (tag) ->
9 | charsToReplace[tag] || tag;
10 |
11 | safe_tags_replace = (str) ->
12 | str.replace /[&<>]/g, replaceChar
13 |
14 | log =
15 | if document? # in browser => log to page
16 | console = document.getElementById "console"
17 | (message) ->
18 | console.innerHTML = console.innerHTML + "* " + (safe_tags_replace message) + "
";
19 | else if console? # not in browser but console object available?
20 | console.log
21 | else
22 | -> # no console output
23 |
24 | log "Initialized console"
25 |
26 | module.exports = log
27 |
--------------------------------------------------------------------------------
/test/browser/src/gamut(node_only).coffee:
--------------------------------------------------------------------------------
1 | C = require "./indexToTest"
2 |
3 |
4 | if C.BROWSER
5 | return
6 |
7 | C_gamut = require.call null, "../../src/gamut"
8 |
9 | describe "Gamut Mapping module", ->
10 |
11 | labCs = rgbCs = gamut = null
12 |
13 | beforeEach ->
14 |
15 | labCs = C.space.lab C.white.D50
16 | rgbCs = C.space.rgb["Adobe-98"]
17 | gamut = C_gamut rgbCs, labCs
18 |
19 | jasmine.addMatchers require "./matcher"
20 |
21 |
22 | describe "LChMaxC() function", ->
23 |
24 | it "maximizes chroma in rgb / lab space combination", ->
25 |
26 | # minimal luminance
27 | lch1 = C.LCh 0, null, 1
28 | rgb1 = gamut.LChMaxC lch1
29 | (expect lch1).toEqual C.LCh 0, 0, 1
30 | (expect rgb1).toEqual C.rgb 0, 0, 0
31 |
32 | # impossible luminance for current color space combination
33 | lch1 = C.LCh 100, null, 1
34 | rgb1 = gamut.LChMaxC lch1
35 | (expect lch1).toEqual C.LCh 100, null, 1
36 | (expect rgb1).toEqual null
37 |
38 | # check some valid combinations
39 | for L in [10..90] by 40
40 | for h in [0..360] by 30
41 | h *= Math.PI / 180
42 | lch1 = C.LCh L, null, h
43 | rgb1 = gamut.LChMaxC lch1
44 |
45 | # L, h unchanged; C set to valid value
46 | (expect lch1.L).toBe L
47 | (expect lch1.h).toBe h
48 | (expect lch1.C).not.toBe null
49 | (expect 0 <= lch1.C).toBe true
50 | # returned rgb is valid
51 | (expect rgb1.isValid()).toBe true
52 | # rgb color equals set LCh color
53 | lab1 = labCs.Lab rgbCs.XYZ rgb1
54 | (expect lab1).toAllBeCloseTo lch1.Lab(), 12
55 |
56 | # check chroma is maximal
57 | lch1_ = lab1.LCh()
58 | lch1_.C += 0.0001
59 | rgbOut = rgbCs.rgb labCs.XYZ lch1_.Lab()
60 | (expect rgbOut.isValid()).toBe false
61 |
--------------------------------------------------------------------------------
/test/browser/src/hsv.coffee:
--------------------------------------------------------------------------------
1 | C = require "./indexToTest"
2 |
3 |
4 | describe "Hsv module", ->
5 |
6 | hsvBlack = hsvRed = hsvYellow = hsvBrown = hsvWhite = hsvGreen = hsvDarkGreen = hsvCyan = hsvBlue = hsvMagenta = null
7 | rgbBlack = rgbRed = rgbYellow = rgbBrown = rgbWhite = rgbGreen = rgbDarkGreen = rgbCyan = rgbBlue = rgbMagenta = null
8 |
9 | beforeEach ->
10 |
11 | hsvBlack = C.hsv 0, 0, 0 # any value for h & s
12 | hsvRed = C.hsv 0, 1, 1
13 | hsvYellow = C.hsv 60/360, 1, 1
14 | hsvBrown = C.hsv 24/360, .75, .36
15 | hsvWhite = C.hsv 0, 0, 1 # any value for h
16 | hsvGreen = C.hsv 120/360, 1, 1
17 | hsvDarkGreen = C.hsv 120/360, 1, 0.5
18 | hsvCyan = C.hsv 180/360, 1, 1
19 | hsvBlue = C.hsv 240/360, 1, 1
20 | hsvMagenta = C.hsv 300/360, 1, 1
21 |
22 | rgbBlack = C.rgb 0, 0, 0
23 | rgbRed = C.rgb 1, 0, 0
24 | rgbYellow = C.rgb 1, 1, 0
25 | rgbBrown = C.rgb .36, .198, .09
26 | rgbWhite = C.rgb 1, 1, 1
27 | rgbGreen = C.rgb 0, 1, 0
28 | rgbDarkGreen = C.rgb 0, .5, 0
29 | rgbCyan = C.rgb 0, 1, 1
30 | rgbBlue = C.rgb 0, 0, 1
31 | rgbMagenta = C.rgb 1, 0, 1
32 |
33 | jasmine.addMatchers require "./matcher"
34 |
35 |
36 | describe "hsv constructor", ->
37 |
38 | it "stores given args", ->
39 | (expect hsvBrown.h).toBe 24/360
40 | (expect hsvBrown.s).toBe .75
41 | (expect hsvBrown.v).toBe .36
42 |
43 |
44 | describe "hsv.rgb()", ->
45 |
46 | it "maps correctly to rgb", ->
47 |
48 | (expect hsvBlack.rgb()).toEqual rgbBlack
49 | (expect hsvRed.rgb()).toEqual rgbRed
50 | (expect hsvYellow.rgb()).toEqual rgbYellow
51 | (expect hsvBrown.rgb()).toEqual rgbBrown
52 | (expect hsvWhite.rgb()).toEqual rgbWhite
53 | (expect hsvGreen.rgb()).toEqual rgbGreen
54 | (expect hsvDarkGreen.rgb()).toEqual rgbDarkGreen
55 | (expect hsvCyan.rgb()).toEqual rgbCyan
56 | (expect hsvBlue.rgb()).toEqual rgbBlue
57 | (expect hsvMagenta.rgb()).toEqual rgbMagenta
58 |
59 |
60 | describe "rgb.hsv()", ->
61 |
62 | it "maps correctly to hsv", ->
63 |
64 | (expect rgbBlack.hsv()).toEqual hsvBlack
65 | (expect rgbRed.hsv()).toEqual hsvRed
66 | (expect rgbYellow.hsv()).toEqual hsvYellow
67 | (expect rgbBrown.hsv()).toAllBeCloseTo hsvBrown, 15
68 | (expect rgbWhite.hsv()).toEqual hsvWhite
69 | (expect rgbGreen.hsv()).toEqual hsvGreen
70 | (expect rgbDarkGreen.hsv()).toEqual hsvDarkGreen
71 | (expect rgbCyan.hsv()).toEqual hsvCyan
72 | (expect rgbBlue.hsv()).toEqual hsvBlue
73 | (expect rgbMagenta.hsv()).toEqual hsvMagenta
74 |
75 |
76 | describe "hsv.set()", ->
77 |
78 | it "sets the given values", ->
79 |
80 | hsv1 = C.hsv 0.1, 0.2, 0.3
81 | hsv2 = hsv1.set 0.4, 0.5, 0.6
82 | (expect hsv2).toBe hsv1 # return obj for chaining
83 | (expect hsv1).toEqual C.hsv 0.4, 0.5, 0.6 # values changed
84 |
--------------------------------------------------------------------------------
/test/browser/src/indexToTest.coffee:
--------------------------------------------------------------------------------
1 | # Always returns the index object anyway if tested in browser or node. Also does call require so browserify does not
2 | # follow this import.
3 |
4 | index = if (inBrowser = c0lor?) then c0lor else require.call null, "../../src/index"
5 |
6 | index.BROWSER = inBrowser
7 |
8 | module.exports = index
--------------------------------------------------------------------------------
/test/browser/src/matcher.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 |
3 | # like toBeCloseTo of jasmine but works on objects and verifies all number properties
4 | toAllBeCloseTo: (util, customEqualityTesters) ->
5 | compare: (actual, expected, precision) ->
6 | if precision != 0
7 | precision = precision || 2
8 | precision = (Math.pow 10, -precision) / 2
9 | for own key, value of actual
10 | if typeof value == "number"
11 | valueExpected = expected[key]
12 | if typeof valueExpected != "number"
13 | return pass: false
14 | delta = Math.abs value - valueExpected
15 | if delta >= precision
16 | return pass: false
17 | pass: true
--------------------------------------------------------------------------------
/test/browser/src/rgb.coffee:
--------------------------------------------------------------------------------
1 | C = require "./indexToTest"
2 |
3 |
4 | describe "Rgb module", ->
5 |
6 | XYZ2 = XYZ3 = XYZ2_s = XYZ3_s = null
7 | rgb1 = rgb2 = rgb3 = rgb4 = null
8 | RGB1 = RGB2 = RGB3 = RGB4 = null
9 | rgbCs1 = rgbCs2 = null
10 |
11 | beforeEach ->
12 |
13 | rgb1 = C.rgb 0, undefined, 0
14 | rgb2 = C.rgb 1, 1, 1
15 | rgb3 = C.rgb 0.5, 0.3, 0.2
16 | rgb4 = C.rgb 1.4, 1.00001, -0.1
17 |
18 | RGB1 = C.RGB 0, undefined, 0
19 | RGB2 = C.RGB 255, 255, 255
20 | RGB3 = C.RGB 128, 77, 51
21 | RGB4 = C.RGB 357, 255, -25
22 |
23 | rgbCs1 = C.space.rgb["Adobe-98"]
24 | # Adobe-98 / http://www.brucelindbloom.com
25 | XYZ2 = C.XYZ 0.95047, 1 , 1.08883
26 | XYZ3 = C.XYZ 0.1441 , 0.111282, 0.039618
27 |
28 | rgbCs2 = C.space.rgb.sRGB
29 | # sRGB / http://www.brucelindbloom.com
30 | XYZ2_s = C.XYZ 0.95047 , 1 , 1.08883
31 | XYZ3_s = C.XYZ 0.120444, 0.100287, 0.044327
32 |
33 | jasmine.addMatchers require "./matcher"
34 |
35 |
36 | describe "rgb.RGB(), RGB.rgb()", ->
37 |
38 | it "rgb.RGB() maps correctly to 3 bit rgb", ->
39 |
40 | (expect rgb1.RGB()).toEqual RGB1
41 | (expect rgb2.RGB()).toEqual RGB2
42 | (expect rgb3.RGB()).toEqual RGB3
43 | (expect rgb4.RGB()).toEqual RGB4
44 |
45 |
46 | describe "rgb.toString()", ->
47 |
48 | it "creates informative output", ->
49 |
50 | (expect "#{rgb3}").toBe "r=0.5, g=0.3, b=0.2"
51 |
52 |
53 | describe "rgb.isDefined()", ->
54 |
55 | it "is true if all components are defined", ->
56 |
57 | (expect rgb1.isDefined()).toBe false
58 | (expect rgb2.isDefined()).toBe true
59 | (expect rgb3.isDefined()).toBe true
60 | (expect rgb4.isDefined()).toBe true
61 |
62 |
63 | describe "rgb.isValid()", ->
64 |
65 | it "is true if all components are defined and between 0..1", ->
66 |
67 | # ok
68 | (expect (C.rgb 0, 0, 0).isValid()).toBe true
69 | (expect (C.rgb -0, -0, -0).isValid()).toBe true
70 | (expect (C.rgb 1, 1, 1).isValid()).toBe true
71 | (expect (C.rgb 0.1, 0.2, 0.3).isValid()).toBe true
72 | # not ok
73 | (expect (C.rgb 0, -0.001, 0).isValid()).toBe false
74 | (expect (C.rgb 0, 0, 1.0001).isValid()).toBe false
75 | (expect (C.rgb undefined, 0, 1).isValid()).toBe false
76 | (expect (C.rgb 0, null, 0).isValid()).toBe false
77 | (expect (C.rgb 0, Number.POSITIVE_INFINITY, 0).isValid()).toBe false
78 | (expect (C.rgb 0, Number.NaN, 0).isValid()).toBe false
79 | (expect (C.rgb 0, Number.NEGATIVE_INFINITY, 0).isValid()).toBe false
80 |
81 |
82 | describe "rgb.set()", ->
83 |
84 | it "sets the given values", ->
85 |
86 | rgb_ = C.rgb 0.1, 0.2, 0.3
87 | rgb2 = rgb_.set 0.4, 0.5, 0.6
88 | (expect rgb2).toBe rgb_ # return obj for chaining
89 | (expect rgb_).toEqual C.rgb 0.4, 0.5, 0.6 # values changed
90 |
--------------------------------------------------------------------------------
/test/browser/src/space/lab.coffee:
--------------------------------------------------------------------------------
1 | C = require "../indexToTest"
2 |
3 |
4 | describe "Lab Colorspace", ->
5 |
6 | XYZ1 = XYZ2 = Lab1 = Lab2 = LCh1 = LCh2 = labCs = null
7 |
8 | beforeEach ->
9 |
10 | labCs = C.space.lab C.white.D50
11 | XYZ1 = C.XYZ 0.1, 0.2, 0.3
12 | XYZ2 = C.XYZ 0.0001, 0.002, 0.003
13 | # same color in Lab color space with D50 reference white (http://www.brucelindbloom.com/)
14 | Lab1 = C.Lab 51.8372, -57.4865, -25.7804
15 | Lab2 = C.Lab 1.8066, -7.3832, -2.5470
16 |
17 | # reference values generated here: http://www.brucelindbloom.com/
18 | toRad = (a) ->
19 | (if a > 180 then a - 360 else a) * Math.PI / 180
20 | LCh1 = C.LCh 51.8372, 63.0026, toRad 204.1543
21 | LCh2 = C.LCh 1.8066, 7.8102, toRad 199.0330
22 |
23 | jasmine.addMatchers require "../matcher"
24 |
25 |
26 | describe "fromXYZ() function", ->
27 |
28 | it "maps correctly to XYZ", ->
29 |
30 | expect(labCs.Lab XYZ1).toAllBeCloseTo Lab1, 2
31 | expect(labCs.Lab labCs.XYZ Lab1).toAllBeCloseTo Lab1, 13
32 | expect(labCs.Lab XYZ2).toAllBeCloseTo Lab2, 3
33 | expect(labCs.Lab labCs.XYZ Lab2).toAllBeCloseTo Lab2, 13
34 |
35 |
36 | describe "toXYZ() function", ->
37 |
38 | it "maps correctly to Lab", ->
39 |
40 | expect(labCs.XYZ Lab1).toAllBeCloseTo XYZ1, 4
41 | expect(labCs.XYZ labCs.Lab XYZ1).toAllBeCloseTo XYZ1, 15
42 | expect(labCs.XYZ Lab2).toAllBeCloseTo XYZ2, 6
43 | expect(labCs.XYZ labCs.Lab XYZ2).toAllBeCloseTo XYZ2, 17
44 |
45 |
46 | describe "fromXYZderivL()", ->
47 |
48 | it "seems to linear approximate the function at some random point", ->
49 |
50 | f = (Y) -> (labCs.Lab C.XYZ null, Y, null).L
51 | f_ = (Y) -> labCs.LabderivL Y
52 | Y = 0.3 # examine around some point Y
53 | t1 = (x) -> (f Y) + (f_ Y) * (x - Y) # taylor"s theorem
54 | (expect t1 Y).toBe f Y
55 | (expect t1 Y + 0.001).toBeCloseTo (f Y + 0.001), 3
56 | (expect t1 Y - 0.001).toBeCloseTo (f Y - 0.001), 3
57 |
--------------------------------------------------------------------------------
/test/browser/src/space/rgb.coffee:
--------------------------------------------------------------------------------
1 | C = require "../indexToTest"
2 |
3 |
4 | describe "Rgb Colorspace module", ->
5 |
6 | XYZ2 = XYZ3 = XYZ2_s = XYZ3_s = null
7 | rgb1 = rgb2 = rgb3 = rgb4 = null
8 | RGB1 = RGB2 = RGB3 = RGB4 = null
9 | rgbCs1 = rgbCs2 = null
10 |
11 | beforeEach ->
12 |
13 | rgb1 = C.rgb 0, undefined, 0
14 | rgb2 = C.rgb 1, 1, 1
15 | rgb3 = C.rgb 0.5, 0.3, 0.2
16 | rgb4 = C.rgb 1.4, 1.00001, -0.1
17 |
18 | RGB1 = C.RGB 0, undefined, 0
19 | RGB2 = C.RGB 255, 255, 255
20 | RGB3 = C.RGB 128, 77, 51
21 | RGB4 = C.RGB undefined, 255, undefined
22 |
23 | rgbCs1 = C.space.rgb["Adobe-98"]
24 | # Adobe-98 / http://www.brucelindbloom.com
25 | XYZ2 = C.XYZ 0.95047, 1 , 1.08883
26 | XYZ3 = C.XYZ 0.1441 , 0.111282, 0.039618
27 |
28 | rgbCs2 = C.space.rgb.sRGB
29 | # sRGB / http://www.brucelindbloom.com
30 | XYZ2_s = C.XYZ 0.95047 , 1 , 1.08883
31 | XYZ3_s = C.XYZ 0.120444, 0.100287, 0.044327
32 |
33 | jasmine.addMatchers require "../matcher"
34 |
35 |
36 | describe "toXYZ()", ->
37 |
38 | it "maps to XYZ correctly", ->
39 |
40 | (expect rgbCs1.XYZ rgb2).toAllBeCloseTo XYZ2, 3
41 | (expect rgbCs1.XYZ rgb3).toAllBeCloseTo XYZ3, 3
42 |
43 | (expect rgbCs2.XYZ rgb2).toAllBeCloseTo XYZ2_s, 3
44 | (expect rgbCs2.XYZ rgb3).toAllBeCloseTo XYZ3_s, 4
45 |
46 | (expect rgbCs1.XYZ rgbCs1.rgb XYZ2).toAllBeCloseTo XYZ2, 15
47 | (expect rgbCs1.XYZ rgbCs1.rgb XYZ3).toAllBeCloseTo XYZ3, 16
48 |
49 | (expect rgbCs2.XYZ rgbCs2.rgb XYZ2_s).toAllBeCloseTo XYZ2_s, 15
50 | (expect rgbCs2.XYZ rgbCs2.rgb XYZ3_s).toAllBeCloseTo XYZ3_s, 15
51 |
52 |
53 | describe "fromXYZ()", ->
54 |
55 | it "maps to XYZ correctly", ->
56 |
57 | (expect rgbCs1.rgb XYZ2).toAllBeCloseTo rgb2, 4
58 | (expect rgbCs1.rgb XYZ3).toAllBeCloseTo rgb3, 4
59 |
60 | (expect rgbCs2.rgb XYZ2_s).toAllBeCloseTo rgb2, 3
61 | (expect rgbCs2.rgb XYZ3_s).toAllBeCloseTo rgb3, 4
62 |
63 | (expect rgbCs1.rgb rgbCs1.XYZ rgb2).toEqual rgb2
64 | (expect rgbCs1.rgb rgbCs1.XYZ rgb3).toEqual rgb3
65 |
66 | (expect rgbCs2.rgb rgbCs2.XYZ rgb2).toAllBeCloseTo rgb2, 15
67 | (expect rgbCs2.rgb rgbCs2.XYZ rgb3).toAllBeCloseTo rgb3, 15
68 |
69 |
70 | describe "color space constructor", ->
71 |
72 | it "create a new (identity) color space", ->
73 |
74 | cs = C.space.rgb (C.xyY 1, 0), (C.xyY 0, 1), (C.xyY 0, 0), (C.XYZ 1, 1, 1), 1
75 | (expect cs.XYZ C.rgb 0, 0, 0).toEqual C.XYZ 0, 0, 0
76 | (expect cs.XYZ C.rgb 1, 0, 0).toEqual C.XYZ 1, 0, 0
77 | (expect cs.XYZ C.rgb 0, 1, 0).toEqual C.XYZ 0, 1, 0
78 | (expect cs.XYZ C.rgb 0, 0, 1).toEqual C.XYZ 0, 0, 1
79 | (expect cs.XYZ C.rgb 1, 1, 1).toEqual C.XYZ 1, 1, 1
80 | (expect cs.XYZ C.rgb 0.1, 0.2, 0.5).toEqual C.XYZ 0.1, 0.2, 0.5
81 | (expect cs.rgb C.XYZ 0.1, 0.2, 0.5).toEqual C.rgb 0.1, 0.2, 0.5
82 |
83 |
84 | describe "Predefined color spaces", ->
85 |
86 | it "have the expected range in Lab colorspace", ->
87 |
88 | min = C.Lab 0, 0, 0
89 | max = C.Lab 100, 0, 0
90 |
91 | # rgb test colors
92 | rgbTest = [
93 | C.rgb 0, 0, 0
94 | C.rgb 1, 0, 0
95 | C.rgb 0, 1, 0
96 | C.rgb 0, 0, 1
97 | C.rgb 1, 0, 1
98 | C.rgb 0, 1, 1
99 | C.rgb 1, 1, 0
100 | C.rgb 1, 1, 1
101 | ]
102 |
103 | for own rgbSpaceName, rgbSpace of C.space.rgb
104 | if rgbSpaceName == "prototype" # on ios 4.3 / 10.6 the prototype property gets iterable. why?
105 | continue
106 | for rgbCol in rgbTest
107 | xyzCol = rgbSpace.XYZ rgbCol
108 | for whiteName, white of C.white
109 | labColor = (C.space.lab white).Lab xyzCol
110 | min.L = Math.min labColor.L, min.L
111 | min.a = Math.min labColor.a, min.a
112 | min.b = Math.min labColor.b, min.b
113 | max.L = Math.max labColor.L, max.L
114 | max.a = Math.max labColor.a, max.a
115 | max.b = Math.max labColor.b, max.b
116 |
117 | (expect min).toEqual C.Lab 0, -223.4241602806217, -237.05355418094157
118 | (expect max).toEqual C.Lab 100, 191.62605068015958, 158.7312579450673
119 |
--------------------------------------------------------------------------------
/test/browser/src/xyY.coffee:
--------------------------------------------------------------------------------
1 | C = require "./indexToTest"
2 |
3 |
4 | describe "xyY Colorspace module", ->
5 |
6 | XYZ = null
7 | xyY = null
8 |
9 | beforeEach ->
10 |
11 | XYZ = C.XYZ 0.1, 0.2, 0.3
12 | # same color in xyY color space
13 | xyY = C.xyY 1/6, 1/3, 0.2
14 |
15 | jasmine.addMatchers require "./matcher"
16 |
17 |
18 | describe "xyY constructors", ->
19 |
20 | it "creates a new expected xyY object", ->
21 |
22 | (expect xyY.x).toBe 1/6
23 | (expect xyY.y).toBe 1/3
24 | (expect xyY.Y).toBe 0.2
25 |
26 |
27 | describe "XYZ <-> xyY", ->
28 |
29 | it "xyY maps correctly to XYZ", ->
30 |
31 | (expect xyY.XYZ()).toAllBeCloseTo XYZ, 15
32 |
33 | it "can map xyY to XYZ in place", ->
34 |
35 | x = C.XYZ 0.4, 0.4, 0.4
36 | y = xyY.XYZ x
37 | (expect y).toAllBeCloseTo XYZ, 15
38 | (expect y).toBe x
39 |
40 |
41 | describe "xyY.isDefined()", ->
42 |
43 | it "is true if all components are defined", ->
44 |
45 | (expect C.xyY().isDefined()).toBe false
46 | (expect (C.xyY 0.1).isDefined()).toBe false
47 | (expect (C.xyY 0.1, 0.2, 0.3).isDefined()).toBe true
48 |
49 |
50 | describe "xyY.toString()", ->
51 |
52 | it "creates informative output", ->
53 |
54 | (expect "#{C.xyY 0.2, 0.3, 0.4}").toBe "x=0.2, y=0.3, Y=0.4"
55 |
--------------------------------------------------------------------------------