• 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    "darkgrey":           [169,169,169],
497    "darkgreen":          [0,100,0],
498    "darkkhaki":          [189,183,107],
499    "darkmagenta":        [139,0,139],
500    "darkolivegreen":     [85,107,47],
501    "darkorange":         [255,140,0],
502    "darkorchid":         [153,50,204],
503    "darkred":            [139,0,0],
504    "darksalmon":         [233,150,122],
505    "darkseagreen":       [143,188,143],
506    "darkslateblue":      [72,61,139],
507    "darkslategray":      [47,79,79],
508    "darkslategrey":      [47,79,79],
509    "darkturquoise":      [0,206,209],
510    "darkviolet":         [148,0,211],
511    "deeppink":           [255,20,147],
512    "deepskyblue":        [0,191,255],
513    "dimgray":            [105,105,105],
514    "dimgrey":            [105,105,105],
515    "dodgerblue":         [30,144,255],
516    "firebrick":          [178,34,34],
517    "floralwhite":        [255,250,240],
518    "forestgreen":        [34,139,34],
519    "gainsboro":          [220,220,220],
520    "ghostwhite":         [248,248,255],
521    "gold":               [255,215,0],
522    "goldenrod":          [218,165,32],
523    "gray":               [128,128,128],
524    "grey":               [128,128,128],
525    "green":              [0,128,0],
526    "greenyellow":        [173,255,47],
527    "honeydew":           [240,255,240],
528    "hotpink":            [255,105,180],
529    "indianred":          [205,92,92],
530    "indigo":             [75,0,130],
531    "ivory":              [255,255,240],
532    "khaki":              [240,230,140],
533    "lavender":           [230,230,250],
534    "lavenderblush":      [255,240,245],
535    "lawngreen":          [124,252,0],
536    "lemonchiffon":       [255,250,205],
537    "lightblue":          [173,216,230],
538    "lightcoral":         [240,128,128],
539    "lightcyan":          [224,255,255],
540    "lightgoldenrodyellow":[250,250,210],
541    "lightgreen":         [144,238,144],
542    "lightgray":          [211,211,211],
543    "lightgrey":          [211,211,211],
544    "lightpink":          [255,182,193],
545    "lightsalmon":        [255,160,122],
546    "lightseagreen":      [32,178,170],
547    "lightskyblue":       [135,206,250],
548    "lightslategray":     [119,136,153],
549    "lightslategrey":     [119,136,153],
550    "lightsteelblue":     [176,196,222],
551    "lightyellow":        [255,255,224],
552    "lime":               [0,255,0],
553    "limegreen":          [50,205,50],
554    "linen":              [250,240,230],
555    "magenta":            [255,0,255],
556    "maroon":             [128,0,0],
557    "mediumaquamarine":   [102,205,170],
558    "mediumblue":         [0,0,205],
559    "mediumorchid":       [186,85,211],
560    "mediumpurple":       [147,112,219],
561    "mediumseagreen":     [60,179,113],
562    "mediumslateblue":    [123,104,238],
563    "mediumspringgreen":  [0,250,154],
564    "mediumturquoise":    [72,209,204],
565    "mediumvioletred":    [199,21,133],
566    "midnightblue":       [25,25,112],
567    "mintcream":          [245,255,250],
568    "mistyrose":          [255,228,225],
569    "moccasin":           [255,228,181],
570    "navajowhite":        [255,222,173],
571    "navy":               [0,0,128],
572    "oldlace":            [253,245,230],
573    "olive":              [128,128,0],
574    "olivedrab":          [107,142,35],
575    "orange":             [255,165,0],
576    "orangered":          [255,69,0],
577    "orchid":             [218,112,214],
578    "palegoldenrod":      [238,232,170],
579    "palegreen":          [152,251,152],
580    "paleturquoise":      [175,238,238],
581    "palevioletred":      [219,112,147],
582    "papayawhip":         [255,239,213],
583    "peachpuff":          [255,218,185],
584    "peru":               [205,133,63],
585    "pink":               [255,192,203],
586    "plum":               [221,160,221],
587    "powderblue":         [176,224,230],
588    "purple":             [128,0,128],
589    "red":                [255,0,0],
590    "rosybrown":          [188,143,143],
591    "royalblue":          [65,105,225],
592    "saddlebrown":        [139,69,19],
593    "salmon":             [250,128,114],
594    "sandybrown":         [244,164,96],
595    "seagreen":           [46,139,87],
596    "seashell":           [255,245,238],
597    "sienna":             [160,82,45],
598    "silver":             [192,192,192],
599    "skyblue":            [135,206,235],
600    "slateblue":          [106,90,205],
601    "slategray":          [112,128,144],
602    "slategrey":          [112,128,144],
603    "snow":               [255,250,250],
604    "springgreen":        [0,255,127],
605    "steelblue":          [70,130,180],
606    "tan":                [210,180,140],
607    "teal":               [0,128,128],
608    "thistle":            [216,191,216],
609    "tomato":             [255,99,71],
610    "turquoise":          [64,224,208],
611    "violet":             [238,130,238],
612    "wheat":              [245,222,179],
613    "white":              [255,255,255],
614    "whitesmoke":         [245,245,245],
615    "yellow":             [255,255,0],
616    "yellowgreen":        [154,205,50],
617    "transparent":        [0, 0, 0, 0],
618};
619
620WebInspector.Color.PageHighlight = {
621    Content: WebInspector.Color.fromRGBA([111, 168, 220, .66]),
622    ContentLight: WebInspector.Color.fromRGBA([111, 168, 220, .5]),
623    ContentOutline: WebInspector.Color.fromRGBA([9, 83, 148]),
624    Padding: WebInspector.Color.fromRGBA([147, 196, 125, .55]),
625    PaddingLight: WebInspector.Color.fromRGBA([147, 196, 125, .4]),
626    Border: WebInspector.Color.fromRGBA([255, 229, 153, .66]),
627    BorderLight: WebInspector.Color.fromRGBA([255, 229, 153, .5]),
628    Margin: WebInspector.Color.fromRGBA([246, 178, 107, .66]),
629    MarginLight: WebInspector.Color.fromRGBA([246, 178, 107, .5]),
630    EventTarget: WebInspector.Color.fromRGBA([255, 196, 196, .66])
631}
632
633WebInspector.Color.Format = {
634    Original: "original",
635    Nickname: "nickname",
636    HEX: "hex",
637    ShortHEX: "shorthex",
638    RGB: "rgb",
639    RGBA: "rgba",
640    HSL: "hsl",
641    HSLA: "hsla"
642}
643