// Colorful.js, v0.3.0
// Copyright (c) 2016-2017 Michael Heim, Zeilenwechsel.de
// Distributed under MIT license
// http://github.com/hashchange/colorful.js

;( function ( root, factory ) {
    "use strict";

    // UMD for a Backbone plugin. Supports AMD, Node.js, CommonJS and globals.
    //
    // - Code lives in the Backbone namespace.
    // - The module does not export a meaningful value.
    // - The module does not create a global.

    var supportsExports = typeof exports === "object" && exports && !exports.nodeType && typeof module === "object" && module && !module.nodeType;

    // AMD:
    // - Some AMD build optimizers like r.js check for condition patterns like the AMD check below, so keep it as is.
    // - Check for `exports` after `define` in case a build optimizer adds an `exports` object.
    // - The AMD spec requires the dependencies to be an array **literal** of module IDs. Don't use a variable there,
    //   or optimizers may fail.
    if ( typeof define === "function" && typeof define.amd === "object" && define.amd ) {

        // AMD module
        define( [ "exports", "underscore" ], factory );

    } else if ( supportsExports ) {

        // Node module, CommonJS module
        factory( exports, require( "underscore" ) );

    } else  {

        // Global (browser or Rhino)
        factory( root, _ );

    }

}( this, function ( exports, _ ) {
    "use strict";

    /** Source for CSS color name mapping: https://github.com/dfcreative/color-name
     * @readonly
     */
    var CssColorNames = {
            "aliceblue": [240, 248, 255],
            "antiquewhite": [250, 235, 215],
            "aqua": [0, 255, 255],
            "aquamarine": [127, 255, 212],
            "azure": [240, 255, 255],
            "beige": [245, 245, 220],
            "bisque": [255, 228, 196],
            "black": [0, 0, 0],
            "blanchedalmond": [255, 235, 205],
            "blue": [0, 0, 255],
            "blueviolet": [138, 43, 226],
            "brown": [165, 42, 42],
            "burlywood": [222, 184, 135],
            "cadetblue": [95, 158, 160],
            "chartreuse": [127, 255, 0],
            "chocolate": [210, 105, 30],
            "coral": [255, 127, 80],
            "cornflowerblue": [100, 149, 237],
            "cornsilk": [255, 248, 220],
            "crimson": [220, 20, 60],
            "cyan": [0, 255, 255],
            "darkblue": [0, 0, 139],
            "darkcyan": [0, 139, 139],
            "darkgoldenrod": [184, 134, 11],
            "darkgray": [169, 169, 169],
            "darkgreen": [0, 100, 0],
            "darkgrey": [169, 169, 169],
            "darkkhaki": [189, 183, 107],
            "darkmagenta": [139, 0, 139],
            "darkolivegreen": [85, 107, 47],
            "darkorange": [255, 140, 0],
            "darkorchid": [153, 50, 204],
            "darkred": [139, 0, 0],
            "darksalmon": [233, 150, 122],
            "darkseagreen": [143, 188, 143],
            "darkslateblue": [72, 61, 139],
            "darkslategray": [47, 79, 79],
            "darkslategrey": [47, 79, 79],
            "darkturquoise": [0, 206, 209],
            "darkviolet": [148, 0, 211],
            "deeppink": [255, 20, 147],
            "deepskyblue": [0, 191, 255],
            "dimgray": [105, 105, 105],
            "dimgrey": [105, 105, 105],
            "dodgerblue": [30, 144, 255],
            "firebrick": [178, 34, 34],
            "floralwhite": [255, 250, 240],
            "forestgreen": [34, 139, 34],
            "fuchsia": [255, 0, 255],
            "gainsboro": [220, 220, 220],
            "ghostwhite": [248, 248, 255],
            "gold": [255, 215, 0],
            "goldenrod": [218, 165, 32],
            "gray": [128, 128, 128],
            "green": [0, 128, 0],
            "greenyellow": [173, 255, 47],
            "grey": [128, 128, 128],
            "honeydew": [240, 255, 240],
            "hotpink": [255, 105, 180],
            "indianred": [205, 92, 92],
            "indigo": [75, 0, 130],
            "ivory": [255, 255, 240],
            "khaki": [240, 230, 140],
            "lavender": [230, 230, 250],
            "lavenderblush": [255, 240, 245],
            "lawngreen": [124, 252, 0],
            "lemonchiffon": [255, 250, 205],
            "lightblue": [173, 216, 230],
            "lightcoral": [240, 128, 128],
            "lightcyan": [224, 255, 255],
            "lightgoldenrodyellow": [250, 250, 210],
            "lightgray": [211, 211, 211],
            "lightgreen": [144, 238, 144],
            "lightgrey": [211, 211, 211],
            "lightpink": [255, 182, 193],
            "lightsalmon": [255, 160, 122],
            "lightseagreen": [32, 178, 170],
            "lightskyblue": [135, 206, 250],
            "lightslategray": [119, 136, 153],
            "lightslategrey": [119, 136, 153],
            "lightsteelblue": [176, 196, 222],
            "lightyellow": [255, 255, 224],
            "lime": [0, 255, 0],
            "limegreen": [50, 205, 50],
            "linen": [250, 240, 230],
            "magenta": [255, 0, 255],
            "maroon": [128, 0, 0],
            "mediumaquamarine": [102, 205, 170],
            "mediumblue": [0, 0, 205],
            "mediumorchid": [186, 85, 211],
            "mediumpurple": [147, 112, 219],
            "mediumseagreen": [60, 179, 113],
            "mediumslateblue": [123, 104, 238],
            "mediumspringgreen": [0, 250, 154],
            "mediumturquoise": [72, 209, 204],
            "mediumvioletred": [199, 21, 133],
            "midnightblue": [25, 25, 112],
            "mintcream": [245, 255, 250],
            "mistyrose": [255, 228, 225],
            "moccasin": [255, 228, 181],
            "navajowhite": [255, 222, 173],
            "navy": [0, 0, 128],
            "oldlace": [253, 245, 230],
            "olive": [128, 128, 0],
            "olivedrab": [107, 142, 35],
            "orange": [255, 165, 0],
            "orangered": [255, 69, 0],
            "orchid": [218, 112, 214],
            "palegoldenrod": [238, 232, 170],
            "palegreen": [152, 251, 152],
            "paleturquoise": [175, 238, 238],
            "palevioletred": [219, 112, 147],
            "papayawhip": [255, 239, 213],
            "peachpuff": [255, 218, 185],
            "peru": [205, 133, 63],
            "pink": [255, 192, 203],
            "plum": [221, 160, 221],
            "powderblue": [176, 224, 230],
            "purple": [128, 0, 128],
            "rebeccapurple": [102, 51, 153],
            "red": [255, 0, 0],
            "rosybrown": [188, 143, 143],
            "royalblue": [65, 105, 225],
            "saddlebrown": [139, 69, 19],
            "salmon": [250, 128, 114],
            "sandybrown": [244, 164, 96],
            "seagreen": [46, 139, 87],
            "seashell": [255, 245, 238],
            "sienna": [160, 82, 45],
            "silver": [192, 192, 192],
            "skyblue": [135, 206, 235],
            "slateblue": [106, 90, 205],
            "slategray": [112, 128, 144],
            "slategrey": [112, 128, 144],
            "snow": [255, 250, 250],
            "springgreen": [0, 255, 127],
            "steelblue": [70, 130, 180],
            "tan": [210, 180, 140],
            "teal": [0, 128, 128],
            "thistle": [216, 191, 216],
            "tomato": [255, 99, 71],
            "turquoise": [64, 224, 208],
            "violet": [238, 130, 238],
            "wheat": [245, 222, 179],
            "white": [255, 255, 255],
            "whitesmoke": [245, 245, 245],
            "yellow": [255, 255, 0],
            "yellowgreen": [154, 205, 50]
        },

        _rxstrFraction = "(?:1(?:\\.0+)?|0?\\.\\d+|0)",
        rxstrAlpha = "(" + _rxstrFraction + ")",
        rxstrOptionalAlpha = "(" + _rxstrFraction + "?)",

        rxstrRgbChannelBase256 = "(255(?:\\.0+)?|25[0-4](?:\\.\\d+)?|2[0-4]\\d(?:\\.\\d+)?|(?:[0-1]?\\d)?\\d(?:\\.\\d+)?)",
        rxstrRgbChannelPercent = "(100(?:\\.0+)?|\\d{0,2}(?:\\.\\d+)|\\d{1,2})\\s*%",
        rxstrRgbChannelHexLC = "([a-f\\d]{2})",
        rxstrRgbChannelHexUC = "([A-F\\d]{2})",
        rxstrRgbChannelShortHexLC = "([a-f\\d])",
        rxstrRgbChannelShortHexUC = "([A-F\\d])",
        rxstrRgbChannelFraction = rxstrAlpha,

        rxRgbChannelBase256 = new RegExp( "^\\s*" + rxstrRgbChannelBase256 + "\\s*$" ),
        rxRgbChannelPercent = new RegExp( "^\\s*" + rxstrRgbChannelPercent + "\\s*$" ),
        rxAlphaChannel = new RegExp( "^\\s*" + rxstrAlpha + "\\s*$" ),

        rxHexLC = buildColorRx( "#", rxstrRgbChannelHexLC ),
        rxHexUC = buildColorRx( "#", rxstrRgbChannelHexUC ),
        rxHexShortLC = buildColorRx( "#", rxstrRgbChannelShortHexLC ),
        rxHexShortUC = buildColorRx( "#", rxstrRgbChannelShortHexUC ),

        rxRgbBase256 = buildColorRx( "rgb", rxstrRgbChannelBase256 ),
        rxRgbaBase256 = buildColorRx( "rgba", rxstrRgbChannelBase256, true ),

        rxRgbPercent = buildColorRx( "rgb", rxstrRgbChannelPercent ),
        rxRgbaPercent = buildColorRx( "rgba", rxstrRgbChannelPercent, true ),

        rxAgColor = buildColorRx( "AgColor", rxstrRgbChannelFraction, true );


    /*
     * Rounding helpers
     */

    /**
     * Adjusts a number to a given precision, working around the buggy floating-point math of Javascript. Works for
     * round, floor, ceil operations.
     *
     * Lifted from the Math.round entry of MDN. Minor changes without effect on the algorithm.
     *
     * @param   {string} operation  "round", "floor", "ceil"
     * @param   {number} value
     * @param   {number} [precision=0]   can be negative: round( 104,-1 ) => 100
     * @returns {number}
     */
    function _decimalAdjust ( operation, value, precision ) {
        var exp;

        if ( typeof precision === 'undefined' || +precision === 0 ) return Math[operation]( value );

        value = +value;
        precision = +precision;

        // Return NaN if the value is not a number or the precision is not an integer
        if ( isNaN( value ) || !(typeof precision === 'number' && precision % 1 === 0) ) return NaN;

        exp = -precision;

        // Shift
        value = value.toString().split( 'e' );
        value = Math[operation]( +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)) );

        // Shift back
        value = value.toString().split( 'e' );
        return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
    }

    var Nums = {};

    Nums.round = function ( value, precision ) {
        return _decimalAdjust( 'round', value, precision );
    };

    Nums.floor = function ( value, precision ) {
        return _decimalAdjust( 'floor', value, precision );
    };

    Nums.ceil = function ( value, precision ) {
        return _decimalAdjust( 'ceil', value, precision );
    };

    /**
     * Converts a number to a decimal string. Ensures that even very small numbers are returned in decimal notation,
     * rather than scientific notation.
     *
     * Numbers are allowed a maximum of 20 decimal digits when expressed in decimal notation (a limitation caused by the
     * Javascript function .toFixed()). Any digits beyond that limit are rounded.
     *
     * NB The function doesn't handle scientific notation for very large numbers because they don't occur in the context
     * of colour.
     *
     * @param   {number} number
     * @returns {string}
     */
    function toDecimalNotation ( number ) {
        var numStr = number + "";

        // If we encounter scientific notation in the stringified number, we don't just modify parts of it. Instead, we
        // reconstruct the entire stringified number from scratch. (That's possible because we use a regex matching the
        // stringified number in full.)
        numStr = numStr.replace( /^(\d+)(?:\.(\d+))?e-(\d+)$/i, function ( match, intDigits, fractionalDigits, absExponent ) {
            var digits = +absExponent + ( fractionalDigits ? fractionalDigits.length : 0 );

            // The argument for String.toFixed( digits ) is allowed to be between 0 and 20, so we have to limit the
            // range accordingly.
            if ( digits > 20 ) {
                Nums.round( number, 20 );
                digits = 20;
            }

            return number.toFixed( digits );
        } );

        return numStr;
    }


    /*
     * Color parsing
     */

    function buildColorRx ( prefix, rxRgbChannel, withAlpha ) {
        var isHex = prefix === "#",
            channelSeparator = isHex ? "" : "\\s*,\\s*",
            suffix = isHex ? "" : "\\s*\\)",
            rxChannels = [ rxRgbChannel, rxRgbChannel, rxRgbChannel ];

        if ( withAlpha ) rxChannels.push( rxstrAlpha );
        if ( !isHex ) prefix += "\\s*\\(\\s*";

        return new RegExp( "^\\s*" + prefix + rxChannels.join( channelSeparator ) + suffix + "\\s*$" );
    }

    function matchColor ( color, rx ) {
        var matches = color.match( rx );
        return {
            isMatch: !!matches,
            rgb: matches && [ matches[1], matches[2], matches[3] ],
            a:  matches && matches[4]
        };
    }

    function getHexData ( color ) {
        // Long form #RRGGBB
        var data = matchColor( color, rxHexLC );
        if ( !data.isMatch ) data = matchColor( color, rxHexUC );

        // Short form #RGB
        if ( !data.isMatch ) {
            data = matchColor( color, rxHexShortLC );
            if ( !data.isMatch ) data = matchColor( color, rxHexShortUC );

            // Converting short form to long form
            if ( data.isMatch ) data.rgb = _.map( data.rgb, function ( value ) {
                return value + value;
            } );
        }

        // Converting hex values to base 256
        if ( data.isMatch ) {
            data.rgb = _.map( data.rgb, function ( value ) {
                return parseInt( value, 16 );
            } );
        }

        return data;
    }

    function getBase256Data ( color ) {
        var data = matchColor( color, rxRgbBase256 );
        if ( !data.isMatch ) data = matchColor( color, rxRgbaBase256 );

        // Conversion to base 256 is not necessary here, just convert to numbers
        if ( data.isMatch ) {
            data.rgb = _.map( data.rgb, function ( value ) {
                return +value;
            } );
        }

        return data;
    }

    function getPercentData ( color ) {
        var data = matchColor( color, rxRgbPercent );
        if ( !data.isMatch ) data = matchColor( color, rxRgbaPercent );

        // Converting percentages to base 256 (but without removing fractional parts)
        if ( data.isMatch ) {
            data.rgb = _.map( data.rgb, function ( value ) {
                return value * 255 / 100;
            } );
        }

        return data;
    }

    function getAgColorData ( color ) {
        var data = matchColor( color, rxAgColor );

        // Converting fractions to base 256 (but without removing fractional parts)
        if ( data.isMatch ) {
            data.rgb = _.map( data.rgb, function ( value ) {
                return value * 255;
            } );
        }

        return data;
    }

    function parseRgbChannel ( channel ) {
        var matches,
            parsedNum = -1;

        if ( _.isNumber( channel ) ) {

            parsedNum = channel;

        } else if ( _.isString( channel ) ) {

            matches = channel.match( rxRgbChannelBase256 );

            if ( matches ) {
                // Converting to number
                parsedNum = +matches[1];
            } else {
                matches = channel.match( rxRgbChannelPercent );

                // Converting percentages to base 256 (but without removing fractional parts)
                if ( matches ) parsedNum = matches[1] * 255 / 100;
            }

        }

        return ( parsedNum >= 0 && parsedNum <= 255 ) ? parsedNum : undefined;
    }

    function parseAlphaChannel ( channel ) {
        var matches,
            parsedNum = -1;

        if ( _.isNumber( channel ) ) {

            parsedNum = channel;

        } else if ( _.isString( channel ) ) {

            matches = channel.match( rxAlphaChannel );
            if ( matches ) parsedNum = +matches[1];
        }

        return ( parsedNum >= 0 && parsedNum <= 1 ) ? parsedNum : undefined;
    }

    function parseColorArray ( colorArray ) {
        var parsed,

            rawRgb = [colorArray[0], colorArray[1], colorArray[2]],
            rawAlpha = colorArray[3],

            parsedRgb = _.map( rawRgb, parseRgbChannel ),
            parsedAlpha = parseAlphaChannel( rawAlpha ),

            success = !_.some( parsedRgb, _.isUndefined ) && ( rawAlpha === undefined || parsedAlpha !== undefined );

        // Conversion of data object to parsed data
        if ( success ) {
            parsed = _.object( [ "r", "g", "b" ], parsedRgb );
            parsed.a = parsedAlpha;
        }

        return parsed;
    }

    // NB We can't handle the CSS keyword "currentcolor" here because we lack the context for it.
    function parseColor ( color ) {
        var parsed, keys, colorArr, data;

        if ( color instanceof Color ) {

            parsed = _.clone( color._rawColor );

        } else if ( _.isArray( color ) && ( color.length === 3 || color.length === 4 ) ) {

            parsed = parseColorArray( color );

        } else if ( _.isObject( color ) ) {
            keys = _.keys( color );

            if ( _.intersection( [ "r", "g", "b" ], keys ).length === 3 || _.intersection( [ "r", "g", "b", "a" ], keys ).length === 4 ) {

                colorArr = [ color.r, color.g, color.b ];
                if ( keys.length === 4 ) colorArr.push( color.a );

                parsed = parseColorArray( colorArr );

            }

        } else if ( _.isString( color ) ) {

            if ( CssColorNames[color] ) {
                parsed = _.object( [ "r", "g", "b" ], CssColorNames[color] );
            } else if ( color.toLowerCase() === "transparent" ) {
                parsed = { r: 0, g: 0, b: 0, a: 0 };
            } else {

                data = getHexData( color );
                if ( !data.isMatch ) data = getBase256Data( color );
                if ( !data.isMatch ) data = getPercentData( color );
                if ( !data.isMatch ) data = getAgColorData( color );

                // Conversion of data object to parsed data
                if ( data.isMatch ) {
                    parsed = _.object( [ "r", "g", "b" ], data.rgb );
                    parsed.a = data.a;
                }

            }

        }

        // Make sure alpha is converted to a number, and set to 1 (opaque) if not defined
        if ( parsed && parsed.a !== undefined ) parsed.a = +parsed.a;
        if ( parsed && parsed.a === undefined ) parsed.a = 1;

        return parsed;
    }


    /*
     * Color conversion
     */

    function rawToHex( rawChannel, options ) {
        // Defaults to lower case
        var upperCase = options && ( options.upperCase || options.lowerCase === false ),
            hex = Nums.round( rawChannel ).toString( 16 );

        if ( hex.length < 2 ) hex = "0" + hex;
        return upperCase ? hex.toUpperCase() : hex.toLowerCase();
    }

    function rawToBase256 ( rawChannel, options ) {
        var precision = options && options.precision || 0;

        return precision === "max" ? rawChannel : Nums.round( rawChannel, precision );
    }

    function rawToPercent ( rawChannel, options ) {
        var precision = options && options.precision || 0,
            percentage = rawChannel * 100 / 255;

        if ( precision !== "max" ) percentage = Nums.round( percentage, precision );
        return toDecimalNotation( percentage ) + "%";
    }

    function rawToFraction ( rawChannel ) {
        return rawChannel / 255;
    }


    /*
     * Color object
     */

    function Color ( value ) {
        if ( !(this instanceof Color) ) return new Color( value );

        this._input = value;
        this._rawColor = parseColor( value );
    }

    Color.version = "0.3.0";

    _.extend( Color.prototype, {

        isColor: function () {
            return !_.isUndefined( this._rawColor );
        },

        ensureColor: function () {
            if ( !this.isColor() ) throw new Error( "Color.ensureColor: The color object does not represent a valid color. It was created from the value " + this._input );
            return true;
        },

        ensureOpaque: function () {
            this.ensureColor();
            if ( !this.isOpaque() ) throw new Error( "Color.ensureOpaque: Color is required to be opaque, but it is not (a = " + this._rawColor.a + ")" );
            return true;
        },

        ensureTransparent: function () {
            this.ensureColor();
            if ( !this.isTransparent() ) throw new Error( "Color.ensureTransparent: Color is required to be transparent, but it is not" );
            return true;
        },

        isOpaque: function () {
            return this.isColor() && this._rawColor.a === 1;
        },

        isTransparent: function () {
            return this.isColor() && this._rawColor.a < 1;
        },

        /**
         * @param   {Object}  [options]
         * @param   {boolean} [options.lowerCase=true]
         * @param   {boolean} [options.upperCase=false]
         * @param   {boolean} [options.prefix=true]
         * @returns {string}
         */
        asHex: function ( options ) {
            var prefix = ( options && options.prefix === false ) ? "" : "#";
            this.ensureOpaque();
            return prefix + _.map( this._getRawArrayRgb(), _.partial( rawToHex, _, options ) ).join( "" );
        },

        asHexUC: function () {
            return this.asHex( { upperCase: true } );
        },

        asHexLC: function () {
            return this.asHex( { lowerCase: true } );
        },

        asRgb: function () {
            this.ensureOpaque();
            return "rgb(" + this.asRgbArray().join( ", " ) + ")";
        },

        /**
         * @param   {Object}       [options]
         * @param   {number|"max"} [options.precision=0]  number of fractional digits, or "max" for all digits
         * @returns {string}
         */
        asRgbPercent: function ( options ) {
            var mapCb = options && options.precision ? _.partial( rawToPercent, _, options ) : rawToPercent;

            this.ensureOpaque();
            return "rgb(" + _.map( this._getRawArrayRgb(), mapCb ).join( ", " ) + ")";
        },

        asRgba: function () {
            this.ensureColor();
            return "rgba(" + this._asRgbArray().concat( toDecimalNotation( this._rawColor.a ) ).join( ", " ) + ")";
        },

        /**
         * @param   {Object}       [options]
         * @param   {number|"max"} [options.precision=0]  number of fractional digits, or "max" for all digits
         * @returns {string}
         */
        asRgbaPercent: function ( options ) {
            var mapCb = options && options.precision ? _.partial( rawToPercent, _, options ) : rawToPercent;

            this.ensureColor();
            return "rgba(" + _.map( this._getRawArrayRgb(), mapCb ).concat( toDecimalNotation( this._rawColor.a ) ).join( ", " ) + ")";
        },

        asAgColor: function () {
            this.ensureColor();
            return "AgColor( " + _.map( this._asAgColorArray(), toDecimalNotation ).join( ", " ) + " )";
        },

        /**
         * @param   {Object}       [options]
         * @param   {number|"max"} [options.precision=0]  number of fractional digits, or "max" for all digits
         * @returns {number[]}
         */
        asRgbArray: function ( options ) {
            this.ensureOpaque();
            return this._asRgbArray( options );
        },

        /**
         * @param   {Object}       [options]
         * @param   {number|"max"} [options.precision=0]  number of fractional digits, or "max" for all digits
         * @returns {number[]}
         */
        asRgbaArray: function ( options ) {
            this.ensureColor();
            return this._asRgbArray( options ).concat( this._rawColor.a );
        },

        asComputed: function () {
            return this.isOpaque() ? this.asRgb() : this.asRgba();
        },

        /**
         * @param   {*}       otherColor
         * @param   {Object}  [options]
         * @param   {number}  [options.tolerance=0]
         * @returns {boolean}
         */
        equals: function ( otherColor, options ) {
            var isEqual, isColor, pairedChannels, pairedAlpha,
                tolerance = options && options.tolerance || 0;

            if ( !( otherColor instanceof Color ) ) otherColor = new Color( otherColor );

            isColor = this.isColor() && otherColor.isColor();
            isEqual = isColor && this.asRgba() === otherColor.asRgba();

            if ( isColor && !isEqual && tolerance > 0 ) {

                pairedChannels = _.zip( this.asRgbaArray(), otherColor.asRgbaArray() );
                pairedAlpha = pairedChannels.pop();

                isEqual = _.every( pairedChannels, function ( pairedChannel ) {
                    return pairedChannel[0] <= pairedChannel[1] + tolerance && pairedChannel[0] >= pairedChannel[1] - tolerance;
                } );

                isEqual = isEqual && pairedAlpha[0] === pairedAlpha[1];
            }

            return isEqual;
        },

        /**
         * @param   {*} otherColor
         * @returns {boolean}
         */
        strictlyEquals: function ( otherColor ) {
            if ( !( otherColor instanceof Color ) ) otherColor = new Color( otherColor );
            return this.isColor() && otherColor.isColor() && this.asRgbaPercent( { precision: "max" } ) === otherColor.asRgbaPercent( { precision: "max" } );
        },

        _asAgColorArray: function () {
            return _.map( this._getRawArrayRgb(), rawToFraction ).concat( this._rawColor.a );
        },

        _asRgbArray: function ( options ) {
            var mapCb = options && options.precision ? _.partial( rawToBase256, _, options ) : rawToBase256;

            return _.map( this._getRawArrayRgb(), mapCb );
        },

        _getRawArrayRgb: function () {
            return [ this._rawColor.r, this._rawColor.g, this._rawColor.b ];
        }

    } );

    // Module return value
    exports.Color = Color;

} ) );