Home | History | Annotate | Download | only in front_end
      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  */
     36 WebInspector.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  */
     55 WebInspector.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  */
    139 WebInspector.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  */
    148 WebInspector.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 
    164 WebInspector.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 /**
    360  * @param {string} value
    361  * return {number}
    362  */
    363 WebInspector.Color._parseRgbNumeric = function(value)
    364 {
    365     var parsed = parseInt(value, 10);
    366     if (value.indexOf("%") !== -1)
    367         parsed /= 100;
    368     else
    369         parsed /= 255;
    370     return parsed;
    371 }
    372 
    373 /**
    374  * @param {string} value
    375  * return {number}
    376  */
    377 WebInspector.Color._parseHueNumeric = function(value)
    378 {
    379     return isNaN(value) ? 0 : (parseFloat(value) / 360) % 1;
    380 }
    381 
    382 /**
    383  * @param {string} value
    384  * return {number}
    385  */
    386 WebInspector.Color._parseSatLightNumeric = function(value)
    387 {
    388     return parseFloat(value) / 100;
    389 }
    390 
    391 /**
    392  * @param {string} value
    393  * return {number}
    394  */
    395 WebInspector.Color._parseAlphaNumeric = function(value)
    396 {
    397     return isNaN(value) ? 0 : parseFloat(value);
    398 }
    399 
    400 /**
    401  * @param {Array.<number>} hsl
    402  * @return {Array.<number>}
    403  */
    404 WebInspector.Color._hsl2rgb = function(hsl)
    405 {
    406     var h = hsl[0];
    407     var s = hsl[1];
    408     var l = hsl[2];
    409 
    410     function hue2rgb(p, q, h)
    411     {
    412         if (h < 0)
    413             h += 1;
    414         else if (h > 1)
    415             h -= 1;
    416 
    417         if ((h * 6) < 1)
    418             return p + (q - p) * h * 6;
    419         else if ((h * 2) < 1)
    420             return q;
    421         else if ((h * 3) < 2)
    422             return p + (q - p) * ((2 / 3) - h) * 6;
    423         else
    424             return p;
    425     }
    426 
    427     if (s < 0)
    428         s = 0;
    429 
    430     if (l <= 0.5)
    431         var q = l * (1 + s);
    432     else
    433         var q = l + s - (l * s);
    434 
    435     var p = 2 * l - q;
    436 
    437     var tr = h + (1 / 3);
    438     var tg = h;
    439     var tb = h - (1 / 3);
    440 
    441     var r = hue2rgb(p, q, tr);
    442     var g = hue2rgb(p, q, tg);
    443     var b = hue2rgb(p, q, tb);
    444     return [r, g, b, hsl[3]];
    445 }
    446 
    447 WebInspector.Color.Nicknames = {
    448     "aliceBlue":          [240,248,255],
    449     "antiqueWhite":       [250,235,215],
    450     "aquamarine":         [127,255,212],
    451     "azure":              [240,255,255],
    452     "beige":              [245,245,220],
    453     "bisque":             [255,228,196],
    454     "black":              [0,0,0],
    455     "blanchedAlmond":     [255,235,205],
    456     "blue":               [0,0,255],
    457     "blueViolet":         [138,43,226],
    458     "brown":              [165,42,42],
    459     "burlyWood":          [222,184,135],
    460     "cadetBlue":          [95,158,160],
    461     "chartreuse":         [127,255,0],
    462     "chocolate":          [210,105,30],
    463     "coral":              [255,127,80],
    464     "cornflowerBlue":     [100,149,237],
    465     "cornsilk":           [255,248,220],
    466     "crimson":            [237,20,61],
    467     "cyan":               [0,255,255],
    468     "darkBlue":           [0,0,139],
    469     "darkCyan":           [0,139,139],
    470     "darkGoldenrod":      [184,134,11],
    471     "darkGray":           [169,169,169],
    472     "darkGreen":          [0,100,0],
    473     "darkKhaki":          [189,183,107],
    474     "darkMagenta":        [139,0,139],
    475     "darkOliveGreen":     [85,107,47],
    476     "darkOrange":         [255,140,0],
    477     "darkOrchid":         [153,50,204],
    478     "darkRed":            [139,0,0],
    479     "darkSalmon":         [233,150,122],
    480     "darkSeaGreen":       [143,188,143],
    481     "darkSlateBlue":      [72,61,139],
    482     "darkSlateGray":      [47,79,79],
    483     "darkTurquoise":      [0,206,209],
    484     "darkViolet":         [148,0,211],
    485     "deepPink":           [255,20,147],
    486     "deepSkyBlue":        [0,191,255],
    487     "dimGray":            [105,105,105],
    488     "dodgerBlue":         [30,144,255],
    489     "fireBrick":          [178,34,34],
    490     "floralWhite":        [255,250,240],
    491     "forestGreen":        [34,139,34],
    492     "gainsboro":          [220,220,220],
    493     "ghostWhite":         [248,248,255],
    494     "gold":               [255,215,0],
    495     "goldenrod":          [218,165,32],
    496     "gray":               [128,128,128],
    497     "green":              [0,128,0],
    498     "greenYellow":        [173,255,47],
    499     "honeyDew":           [240,255,240],
    500     "hotPink":            [255,105,180],
    501     "indianRed":          [205,92,92],
    502     "indigo":             [75,0,130],
    503     "ivory":              [255,255,240],
    504     "khaki":              [240,230,140],
    505     "lavender":           [230,230,250],
    506     "lavenderBlush":      [255,240,245],
    507     "lawnGreen":          [124,252,0],
    508     "lemonChiffon":       [255,250,205],
    509     "lightBlue":          [173,216,230],
    510     "lightCoral":         [240,128,128],
    511     "lightCyan":          [224,255,255],
    512     "lightGoldenrodYellow":[250,250,210],
    513     "lightGreen":         [144,238,144],
    514     "lightGrey":          [211,211,211],
    515     "lightPink":          [255,182,193],
    516     "lightSalmon":        [255,160,122],
    517     "lightSeaGreen":      [32,178,170],
    518     "lightSkyBlue":       [135,206,250],
    519     "lightSlateGray":     [119,136,153],
    520     "lightSteelBlue":     [176,196,222],
    521     "lightYellow":        [255,255,224],
    522     "lime":               [0,255,0],
    523     "limeGreen":          [50,205,50],
    524     "linen":              [250,240,230],
    525     "magenta":            [255,0,255],
    526     "maroon":             [128,0,0],
    527     "mediumAquaMarine":   [102,205,170],
    528     "mediumBlue":         [0,0,205],
    529     "mediumOrchid":       [186,85,211],
    530     "mediumPurple":       [147,112,219],
    531     "mediumSeaGreen":     [60,179,113],
    532     "mediumSlateBlue":    [123,104,238],
    533     "mediumSpringGreen":  [0,250,154],
    534     "mediumTurquoise":    [72,209,204],
    535     "mediumVioletRed":    [199,21,133],
    536     "midnightBlue":       [25,25,112],
    537     "mintCream":          [245,255,250],
    538     "mistyRose":          [255,228,225],
    539     "moccasin":           [255,228,181],
    540     "navajoWhite":        [255,222,173],
    541     "navy":               [0,0,128],
    542     "oldLace":            [253,245,230],
    543     "olive":              [128,128,0],
    544     "oliveDrab":          [107,142,35],
    545     "orange":             [255,165,0],
    546     "orangeRed":          [255,69,0],
    547     "orchid":             [218,112,214],
    548     "paleGoldenrod":      [238,232,170],
    549     "paleGreen":          [152,251,152],
    550     "paleTurquoise":      [175,238,238],
    551     "paleVioletRed":      [219,112,147],
    552     "papayaWhip":         [255,239,213],
    553     "peachPuff":          [255,218,185],
    554     "peru":               [205,133,63],
    555     "pink":               [255,192,203],
    556     "plum":               [221,160,221],
    557     "powderBlue":         [176,224,230],
    558     "purple":             [128,0,128],
    559     "red":                [255,0,0],
    560     "rosyBrown":          [188,143,143],
    561     "royalBlue":          [65,105,225],
    562     "saddleBrown":        [139,69,19],
    563     "salmon":             [250,128,114],
    564     "sandyBrown":         [244,164,96],
    565     "seaGreen":           [46,139,87],
    566     "seaShell":           [255,245,238],
    567     "sienna":             [160,82,45],
    568     "silver":             [192,192,192],
    569     "skyBlue":            [135,206,235],
    570     "slateBlue":          [106,90,205],
    571     "slateGray":          [112,128,144],
    572     "snow":               [255,250,250],
    573     "springGreen":        [0,255,127],
    574     "steelBlue":          [70,130,180],
    575     "tan":                [210,180,140],
    576     "teal":               [0,128,128],
    577     "thistle":            [216,191,216],
    578     "tomato":             [255,99,71],
    579     "turquoise":          [64,224,208],
    580     "violet":             [238,130,238],
    581     "wheat":              [245,222,179],
    582     "white":              [255,255,255],
    583     "whiteSmoke":         [245,245,245],
    584     "yellow":             [255,255,0],
    585     "yellowGreen":        [154,205,50],
    586     "transparent":        [0, 0, 0, 0],
    587 };
    588 
    589 WebInspector.Color.PageHighlight = {
    590     Content: WebInspector.Color.fromRGBA([111, 168, 220, .66]),
    591     ContentLight: WebInspector.Color.fromRGBA([111, 168, 220, .5]),
    592     ContentOutline: WebInspector.Color.fromRGBA([9, 83, 148]),
    593     Padding: WebInspector.Color.fromRGBA([147, 196, 125, .55]),
    594     PaddingLight: WebInspector.Color.fromRGBA([147, 196, 125, .4]),
    595     Border: WebInspector.Color.fromRGBA([255, 229, 153, .66]),
    596     BorderLight: WebInspector.Color.fromRGBA([255, 229, 153, .5]),
    597     Margin: WebInspector.Color.fromRGBA([246, 178, 107, .66]),
    598     MarginLight: WebInspector.Color.fromRGBA([246, 178, 107, .5]),
    599     EventTarget: WebInspector.Color.fromRGBA([255, 196, 196, .66])
    600 }
    601 
    602 WebInspector.Color.Format = {
    603     Original: "original",
    604     Nickname: "nickname",
    605     HEX: "hex",
    606     ShortHEX: "shorthex",
    607     RGB: "rgb",
    608     RGBA: "rgba",
    609     HSL: "hsl",
    610     HSLA: "hsla"
    611 }
    612