• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2009 Apple Inc.  All rights reserved.
3 * Copyright (C) 2009 Joseph Pecoraro
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1.  Redistributions of source code must retain the above copyright
10 *     notice, this list of conditions and the following disclaimer.
11 * 2.  Redistributions in binary form must reproduce the above copyright
12 *     notice, this list of conditions and the following disclaimer in the
13 *     documentation and/or other materials provided with the distribution.
14 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 *     its contributors may be used to endorse or promote products derived
16 *     from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/**
31 * @param {!Array.<number>} rgba
32 * @param {string=} format
33 * @param {string=} originalText
34 * @constructor
35 */
36WebInspector.Color = function(rgba, format, originalText)
37{
38    this._rgba = rgba;
39    this._originalText = originalText || null;
40    this._format = format || null;
41    if (typeof this._rgba[3] === "undefined")
42        this._rgba[3] = 1;
43    for (var i = 0; i < 4; ++i) {
44        if (this._rgba[i] < 0)
45            this._rgba[i] = 0;
46        if (this._rgba[i] > 1)
47            this._rgba[i] = 1;
48    }
49}
50
51/**
52 * @param {string} text
53 * @return {?WebInspector.Color}
54 */
55WebInspector.Color.parse = function(text)
56{
57    // Simple - #hex, rgb(), nickname, hsl()
58    var value = text.toLowerCase().replace(/\s+/g, "");
59    var simple = /^(?:#([0-9a-f]{3,6})|rgb\(([^)]+)\)|(\w+)|hsl\(([^)]+)\))$/i;
60    var match = value.match(simple);
61    if (match) {
62        if (match[1]) { // hex
63            var hex = match[1].toUpperCase();
64            var format;
65            if (hex.length === 3) {
66                format = WebInspector.Color.Format.ShortHEX;
67                hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
68            } else
69                format = WebInspector.Color.Format.HEX;
70            var r = parseInt(hex.substring(0,2), 16);
71            var g = parseInt(hex.substring(2,4), 16);
72            var b = parseInt(hex.substring(4,6), 16);
73            return new WebInspector.Color([r / 255, g / 255, b / 255, 1], format, text);
74        }
75
76        if (match[2]) { // rgb
77            var rgbString = match[2].split(/\s*,\s*/);
78            var rgba = [ WebInspector.Color._parseRgbNumeric(rgbString[0]),
79                         WebInspector.Color._parseRgbNumeric(rgbString[1]),
80                         WebInspector.Color._parseRgbNumeric(rgbString[2]), 1 ];
81            return new WebInspector.Color(rgba, WebInspector.Color.Format.RGB, text);
82        }
83
84        if (match[3]) { // nickname
85            var nickname = match[3].toLowerCase();
86            if (nickname in WebInspector.Color.Nicknames) {
87                var rgba = WebInspector.Color.Nicknames[nickname];
88                var color = WebInspector.Color.fromRGBA(rgba);
89                color._format = WebInspector.Color.Format.Nickname;
90                color._originalText = nickname;
91                return color;
92            }
93            return null;
94        }
95
96        if (match[4]) { // hsl
97            var hslString = match[4].replace(/%/g, "").split(/\s*,\s*/);
98            var hsla = [ WebInspector.Color._parseHueNumeric(hslString[0]),
99                         WebInspector.Color._parseSatLightNumeric(hslString[1]),
100                         WebInspector.Color._parseSatLightNumeric(hslString[2]), 1 ];
101            var rgba = WebInspector.Color._hsl2rgb(hsla);
102            return new WebInspector.Color(rgba, WebInspector.Color.Format.HSL, text);
103        }
104
105        return null;
106    }
107
108    // Advanced - rgba(), hsla()
109    var advanced = /^(?:rgba\(([^)]+)\)|hsla\(([^)]+)\))$/;
110    match = value.match(advanced);
111    if (match) {
112        if (match[1]) { // rgba
113            var rgbaString = match[1].split(/\s*,\s*/);
114            var rgba = [ WebInspector.Color._parseRgbNumeric(rgbaString[0]),
115                         WebInspector.Color._parseRgbNumeric(rgbaString[1]),
116                         WebInspector.Color._parseRgbNumeric(rgbaString[2]),
117                         WebInspector.Color._parseAlphaNumeric(rgbaString[3]) ];
118            return new WebInspector.Color(rgba, WebInspector.Color.Format.RGBA, text);
119        }
120
121        if (match[2]) { // hsla
122            var hslaString = match[2].replace(/%/g, "").split(/\s*,\s*/);
123            var hsla = [ WebInspector.Color._parseHueNumeric(hslaString[0]),
124                         WebInspector.Color._parseSatLightNumeric(hslaString[1]),
125                         WebInspector.Color._parseSatLightNumeric(hslaString[2]),
126                         WebInspector.Color._parseAlphaNumeric(hslaString[3]) ];
127            var rgba = WebInspector.Color._hsl2rgb(hsla);
128            return new WebInspector.Color(rgba, WebInspector.Color.Format.HSLA, text);
129        }
130    }
131
132    return null;
133}
134
135/**
136 * @param {!Array.<number>} rgba
137 * @return {!WebInspector.Color}
138 */
139WebInspector.Color.fromRGBA = function(rgba)
140{
141    return new WebInspector.Color([rgba[0] / 255, rgba[1] / 255, rgba[2] / 255, rgba[3]]);
142}
143
144/**
145 * @param {!Array.<number>} hsva
146 * @return {!WebInspector.Color}
147 */
148WebInspector.Color.fromHSVA = function(hsva)
149{
150    var h = hsva[0];
151    var s = hsva[1];
152    var v = hsva[2];
153
154    var t = (2 - s) * v;
155    if (v === 0 || s === 0)
156        s = 0;
157    else
158        s *= v / (t < 1 ? t : 2 - t);
159    var hsla = [h, s, t / 2, hsva[3]];
160
161    return new WebInspector.Color(WebInspector.Color._hsl2rgb(hsla), WebInspector.Color.Format.HSLA);
162}
163
164WebInspector.Color.prototype = {
165    /**
166     * @return {?string}
167     */
168    format: function()
169    {
170        return this._format;
171    },
172
173    /**
174     * @return {!Array.<number>} HSLA with components within [0..1]
175     */
176    hsla: function()
177    {
178        if (this._hsla)
179            return this._hsla;
180        var r = this._rgba[0];
181        var g = this._rgba[1];
182        var b = this._rgba[2];
183        var max = Math.max(r, g, b);
184        var min = Math.min(r, g, b);
185        var diff = max - min;
186        var add = max + min;
187
188        if (min === max)
189            var h = 0;
190        else if (r === max)
191            var h = ((1/6 * (g - b) / diff) + 1) % 1;
192        else if (g === max)
193            var h = (1/6 * (b - r) / diff) + 1/3;
194        else
195            var h = (1/6 * (r - g) / diff) + 2/3;
196
197        var l = 0.5 * add;
198
199        if (l === 0)
200            var s = 0;
201        else if (l === 1)
202            var s = 1;
203        else if (l <= 0.5)
204            var s = diff / add;
205        else
206            var s = diff / (2 - add);
207
208        this._hsla = [h, s, l, this._rgba[3]];
209        return this._hsla;
210    },
211
212    /**
213     * @return {!Array.<number>} HSVA with components within [0..1]
214     */
215    hsva: function()
216    {
217        var hsla = this.hsla();
218        var h = hsla[0];
219        var s = hsla[1];
220        var l = hsla[2];
221
222        s *= l < 0.5 ? l : 1 - l;
223        return [h, s !== 0 ? 2 * s / (l + s) : 0, (l + s), hsla[3]];
224    },
225
226    /**
227     * @return {boolean}
228     */
229    hasAlpha: function()
230    {
231        return this._rgba[3] !== 1;
232    },
233
234    /**
235     * @return {boolean}
236     */
237    canBeShortHex: function()
238    {
239        if (this.hasAlpha())
240            return false;
241        for (var i = 0; i < 3; ++i) {
242            var c = Math.round(this._rgba[i] * 255);
243            if (c % 17)
244                return false;
245        }
246        return true;
247    },
248
249    /**
250     * @return {?string}
251     */
252    toString: function(format)
253    {
254        if (!format)
255            format = this._format;
256
257        /**
258         * @param {number} value
259         * @return {number}
260         */
261        function toRgbValue(value)
262        {
263            return Math.round(value * 255);
264        }
265
266        /**
267         * @param {number} value
268         * @return {string}
269         */
270        function toHexValue(value)
271        {
272            var hex = Math.round(value * 255).toString(16);
273            return hex.length === 1 ? "0" + hex : hex;
274        }
275
276        /**
277         * @param {number} value
278         * @return {string}
279         */
280        function toShortHexValue(value)
281        {
282            return (Math.round(value * 255) / 17).toString(16);
283        }
284
285        switch (format) {
286        case WebInspector.Color.Format.Original:
287            return this._originalText;
288        case WebInspector.Color.Format.RGB:
289            if (this.hasAlpha())
290                return null;
291            return String.sprintf("rgb(%d, %d, %d)", toRgbValue(this._rgba[0]), toRgbValue(this._rgba[1]), toRgbValue(this._rgba[2]));
292        case WebInspector.Color.Format.RGBA:
293            return String.sprintf("rgba(%d, %d, %d, %f)", toRgbValue(this._rgba[0]), toRgbValue(this._rgba[1]), toRgbValue(this._rgba[2]), this._rgba[3]);
294        case WebInspector.Color.Format.HSL:
295            if (this.hasAlpha())
296                return null;
297            var hsl = this.hsla();
298            return String.sprintf("hsl(%d, %d%, %d%)", Math.round(hsl[0] * 360), Math.round(hsl[1] * 100), Math.round(hsl[2] * 100));
299        case WebInspector.Color.Format.HSLA:
300            var hsla = this.hsla();
301            return String.sprintf("hsla(%d, %d%, %d%, %f)", Math.round(hsla[0] * 360), Math.round(hsla[1] * 100), Math.round(hsla[2] * 100), hsla[3]);
302        case WebInspector.Color.Format.HEX:
303            if (this.hasAlpha())
304                return null;
305            return String.sprintf("#%s%s%s", toHexValue(this._rgba[0]), toHexValue(this._rgba[1]), toHexValue(this._rgba[2])).toUpperCase();
306        case WebInspector.Color.Format.ShortHEX:
307            if (!this.canBeShortHex())
308                return null;
309            return String.sprintf("#%s%s%s", toShortHexValue(this._rgba[0]), toShortHexValue(this._rgba[1]), toShortHexValue(this._rgba[2])).toUpperCase();
310        case WebInspector.Color.Format.Nickname:
311            return this.nickname();
312        }
313
314        return this._originalText;
315    },
316
317    /**
318     * @return {!Array.<number>}
319     */
320    _canonicalRGBA: function()
321    {
322        var rgba = new Array(3);
323        for (var i = 0; i < 3; ++i)
324            rgba[i] = Math.round(this._rgba[i] * 255);
325        if (this._rgba[3] !== 1)
326            rgba.push(this._rgba[3]);
327        return rgba;
328    },
329
330    /**
331     * @return {?string} nickname
332     */
333    nickname: function()
334    {
335        if (!WebInspector.Color._rgbaToNickname) {
336            WebInspector.Color._rgbaToNickname = {};
337            for (var nickname in WebInspector.Color.Nicknames) {
338                var rgba = WebInspector.Color.Nicknames[nickname];
339                WebInspector.Color._rgbaToNickname[rgba] = nickname;
340            }
341        }
342
343        return WebInspector.Color._rgbaToNickname[this._canonicalRGBA()] || null;
344    },
345
346    /**
347     * @return {!DOMAgent.RGBA}
348     */
349    toProtocolRGBA: function()
350    {
351        var rgba = this._canonicalRGBA();
352        var result = { r: rgba[0], g: rgba[1], b: rgba[2] };
353        if (rgba[3] !== 1)
354            result.a = rgba[3];
355        return result;
356    },
357
358    /**
359     * @return {!WebInspector.Color}
360     */
361    invert: function()
362    {
363        var rgba = [];
364        rgba[0] = 1 - this._rgba[0];
365        rgba[1] = 1 - this._rgba[1];
366        rgba[2] = 1 - this._rgba[2];
367        rgba[3] = this._rgba[3];
368        return new WebInspector.Color(rgba);
369    },
370
371    /**
372     * @param {number} alpha
373     * @return {!WebInspector.Color}
374     */
375     setAlpha: function(alpha)
376     {
377         var rgba = this._rgba.slice();
378         rgba[3] = alpha;
379         return new WebInspector.Color(rgba);
380     }
381}
382
383/**
384 * @param {string} value
385 * return {number}
386 */
387WebInspector.Color._parseRgbNumeric = function(value)
388{
389    var parsed = parseInt(value, 10);
390    if (value.indexOf("%") !== -1)
391        parsed /= 100;
392    else
393        parsed /= 255;
394    return parsed;
395}
396
397/**
398 * @param {string} value
399 * return {number}
400 */
401WebInspector.Color._parseHueNumeric = function(value)
402{
403    return isNaN(value) ? 0 : (parseFloat(value) / 360) % 1;
404}
405
406/**
407 * @param {string} value
408 * return {number}
409 */
410WebInspector.Color._parseSatLightNumeric = function(value)
411{
412    return parseFloat(value) / 100;
413}
414
415/**
416 * @param {string} value
417 * return {number}
418 */
419WebInspector.Color._parseAlphaNumeric = function(value)
420{
421    return isNaN(value) ? 0 : parseFloat(value);
422}
423
424/**
425 * @param {!Array.<number>} hsl
426 * @return {!Array.<number>}
427 */
428WebInspector.Color._hsl2rgb = function(hsl)
429{
430    var h = hsl[0];
431    var s = hsl[1];
432    var l = hsl[2];
433
434    function hue2rgb(p, q, h)
435    {
436        if (h < 0)
437            h += 1;
438        else if (h > 1)
439            h -= 1;
440
441        if ((h * 6) < 1)
442            return p + (q - p) * h * 6;
443        else if ((h * 2) < 1)
444            return q;
445        else if ((h * 3) < 2)
446            return p + (q - p) * ((2 / 3) - h) * 6;
447        else
448            return p;
449    }
450
451    if (s < 0)
452        s = 0;
453
454    if (l <= 0.5)
455        var q = l * (1 + s);
456    else
457        var q = l + s - (l * s);
458
459    var p = 2 * l - q;
460
461    var tr = h + (1 / 3);
462    var tg = h;
463    var tb = h - (1 / 3);
464
465    var r = hue2rgb(p, q, tr);
466    var g = hue2rgb(p, q, tg);
467    var b = hue2rgb(p, q, tb);
468    return [r, g, b, hsl[3]];
469}
470
471WebInspector.Color.Nicknames = {
472    "aliceblue":          [240,248,255],
473    "antiquewhite":       [250,235,215],
474    "aquamarine":         [127,255,212],
475    "azure":              [240,255,255],
476    "beige":              [245,245,220],
477    "bisque":             [255,228,196],
478    "black":              [0,0,0],
479    "blanchedalmond":     [255,235,205],
480    "blue":               [0,0,255],
481    "blueviolet":         [138,43,226],
482    "brown":              [165,42,42],
483    "burlywood":          [222,184,135],
484    "cadetblue":          [95,158,160],
485    "chartreuse":         [127,255,0],
486    "chocolate":          [210,105,30],
487    "coral":              [255,127,80],
488    "cornflowerblue":     [100,149,237],
489    "cornsilk":           [255,248,220],
490    "crimson":            [237,20,61],
491    "cyan":               [0,255,255],
492    "darkblue":           [0,0,139],
493    "darkcyan":           [0,139,139],
494    "darkgoldenrod":      [184,134,11],
495    "darkgray":           [169,169,169],
496    "darkgreen":          [0,100,0],
497    "darkkhaki":          [189,183,107],
498    "darkmagenta":        [139,0,139],
499    "darkolivegreen":     [85,107,47],
500    "darkorange":         [255,140,0],
501    "darkorchid":         [153,50,204],
502    "darkred":            [139,0,0],
503    "darksalmon":         [233,150,122],
504    "darkseagreen":       [143,188,143],
505    "darkslateblue":      [72,61,139],
506    "darkslategray":      [47,79,79],
507    "darkturquoise":      [0,206,209],
508    "darkviolet":         [148,0,211],
509    "deeppink":           [255,20,147],
510    "deepskyblue":        [0,191,255],
511    "dimgray":            [105,105,105],
512    "dodgerblue":         [30,144,255],
513    "firebrick":          [178,34,34],
514    "floralwhite":        [255,250,240],
515    "forestgreen":        [34,139,34],
516    "gainsboro":          [220,220,220],
517    "ghostwhite":         [248,248,255],
518    "gold":               [255,215,0],
519    "goldenrod":          [218,165,32],
520    "gray":               [128,128,128],
521    "green":              [0,128,0],
522    "greenyellow":        [173,255,47],
523    "honeydew":           [240,255,240],
524    "hotpink":            [255,105,180],
525    "indianred":          [205,92,92],
526    "indigo":             [75,0,130],
527    "ivory":              [255,255,240],
528    "khaki":              [240,230,140],
529    "lavender":           [230,230,250],
530    "lavenderblush":      [255,240,245],
531    "lawngreen":          [124,252,0],
532    "lemonchiffon":       [255,250,205],
533    "lightblue":          [173,216,230],
534    "lightcoral":         [240,128,128],
535    "lightcyan":          [224,255,255],
536    "lightgoldenrodyellow":[250,250,210],
537    "lightgreen":         [144,238,144],
538    "lightgrey":          [211,211,211],
539    "lightpink":          [255,182,193],
540    "lightsalmon":        [255,160,122],
541    "lightseagreen":      [32,178,170],
542    "lightskyblue":       [135,206,250],
543    "lightslategray":     [119,136,153],
544    "lightsteelblue":     [176,196,222],
545    "lightyellow":        [255,255,224],
546    "lime":               [0,255,0],
547    "limegreen":          [50,205,50],
548    "linen":              [250,240,230],
549    "magenta":            [255,0,255],
550    "maroon":             [128,0,0],
551    "mediumaquamarine":   [102,205,170],
552    "mediumblue":         [0,0,205],
553    "mediumorchid":       [186,85,211],
554    "mediumpurple":       [147,112,219],
555    "mediumseagreen":     [60,179,113],
556    "mediumslateblue":    [123,104,238],
557    "mediumspringgreen":  [0,250,154],
558    "mediumturquoise":    [72,209,204],
559    "mediumvioletred":    [199,21,133],
560    "midnightblue":       [25,25,112],
561    "mintcream":          [245,255,250],
562    "mistyrose":          [255,228,225],
563    "moccasin":           [255,228,181],
564    "navajowhite":        [255,222,173],
565    "navy":               [0,0,128],
566    "oldlace":            [253,245,230],
567    "olive":              [128,128,0],
568    "olivedrab":          [107,142,35],
569    "orange":             [255,165,0],
570    "orangered":          [255,69,0],
571    "orchid":             [218,112,214],
572    "palegoldenrod":      [238,232,170],
573    "palegreen":          [152,251,152],
574    "paleturquoise":      [175,238,238],
575    "palevioletred":      [219,112,147],
576    "papayawhip":         [255,239,213],
577    "peachpuff":          [255,218,185],
578    "peru":               [205,133,63],
579    "pink":               [255,192,203],
580    "plum":               [221,160,221],
581    "powderblue":         [176,224,230],
582    "purple":             [128,0,128],
583    "red":                [255,0,0],
584    "rosybrown":          [188,143,143],
585    "royalblue":          [65,105,225],
586    "saddlebrown":        [139,69,19],
587    "salmon":             [250,128,114],
588    "sandybrown":         [244,164,96],
589    "seagreen":           [46,139,87],
590    "seashell":           [255,245,238],
591    "sienna":             [160,82,45],
592    "silver":             [192,192,192],
593    "skyblue":            [135,206,235],
594    "slateblue":          [106,90,205],
595    "slategray":          [112,128,144],
596    "snow":               [255,250,250],
597    "springgreen":        [0,255,127],
598    "steelblue":          [70,130,180],
599    "tan":                [210,180,140],
600    "teal":               [0,128,128],
601    "thistle":            [216,191,216],
602    "tomato":             [255,99,71],
603    "turquoise":          [64,224,208],
604    "violet":             [238,130,238],
605    "wheat":              [245,222,179],
606    "white":              [255,255,255],
607    "whitesmoke":         [245,245,245],
608    "yellow":             [255,255,0],
609    "yellowgreen":        [154,205,50],
610    "transparent":        [0, 0, 0, 0],
611};
612
613WebInspector.Color.PageHighlight = {
614    Content: WebInspector.Color.fromRGBA([111, 168, 220, .66]),
615    ContentLight: WebInspector.Color.fromRGBA([111, 168, 220, .5]),
616    ContentOutline: WebInspector.Color.fromRGBA([9, 83, 148]),
617    Padding: WebInspector.Color.fromRGBA([147, 196, 125, .55]),
618    PaddingLight: WebInspector.Color.fromRGBA([147, 196, 125, .4]),
619    Border: WebInspector.Color.fromRGBA([255, 229, 153, .66]),
620    BorderLight: WebInspector.Color.fromRGBA([255, 229, 153, .5]),
621    Margin: WebInspector.Color.fromRGBA([246, 178, 107, .66]),
622    MarginLight: WebInspector.Color.fromRGBA([246, 178, 107, .5]),
623    EventTarget: WebInspector.Color.fromRGBA([255, 196, 196, .66])
624}
625
626WebInspector.Color.Format = {
627    Original: "original",
628    Nickname: "nickname",
629    HEX: "hex",
630    ShortHEX: "shorthex",
631    RGB: "rgb",
632    RGBA: "rgba",
633    HSL: "hsl",
634    HSLA: "hsla"
635}
636