1 (function() {
  2   
  3   var fabric = this.fabric || (this.fabric = { });
  4   
  5   if (fabric.Color) {
  6     fabric.warn('fabric.Color is already defined.');
  7     return;
  8   }
  9   
 10   /**
 11    * The purpose of fabric.Color is to abstract and encapsulate common color operations;
 12    * fabric.Color is a constructor and creates instances of fabric.Color objects.
 13    *
 14    * @class Color
 15    * @memberOf fabric
 16    * @param {String} color (optional) in hex or rgb(a) format
 17    */
 18   function Color(color) {
 19     if (!color) {
 20       this.setSource([0, 0, 0, 1]);
 21     }
 22     else {
 23       this._tryParsingColor(color);
 24     }
 25   }
 26   
 27   fabric.Color = Color;
 28   
 29   fabric.Color.prototype = /** @scope fabric.Color.prototype */ {
 30     
 31     /**
 32      * @private
 33      * @method _tryParsingColor
 34      */
 35     _tryParsingColor: function(color) {
 36       var source = Color.sourceFromHex(color);
 37       if (!source) {
 38         source = Color.sourceFromRgb(color);
 39       }
 40       if (source) {
 41         this.setSource(source);
 42       }
 43     },
 44 
 45     /**
 46      * Returns source of this color (where source is an array representation; ex: [200, 200, 100, 1])
 47      * @method getSource
 48      * @return {Array}
 49      */
 50     getSource: function() {
 51       return this._source;
 52     },
 53 
 54     /**
 55      * Sets source of this color (where source is an array representation; ex: [200, 200, 100, 1])
 56      * @method setSource
 57      * @param {Array} source
 58      */
 59     setSource: function(source) {
 60       this._source = source;
 61     },
 62 
 63     /**
 64      * Returns color represenation in RGB format
 65      * @method toRgb
 66      * @return {String} ex: rgb(0-255,0-255,0-255)
 67      */
 68     toRgb: function() {
 69       var source = this.getSource();
 70       return 'rgb(' + source[0] + ',' + source[1] + ',' + source[2] + ')';
 71     },
 72 
 73     /**
 74      * Returns color represenation in RGBA format
 75      * @method toRgba
 76      * @return {String} ex: rgba(0-255,0-255,0-255,0-1)
 77      */
 78     toRgba: function() {
 79       var source = this.getSource();
 80       return 'rgba(' + source[0] + ',' + source[1] + ',' + source[2] + ',' + source[3] + ')';
 81     },
 82 
 83     /**
 84      * Returns color represenation in HEX format
 85      * @method toHex
 86      * @return {String} ex: FF5555
 87      */
 88     toHex: function() {
 89       var source = this.getSource();
 90 
 91       var r = source[0].toString(16);
 92       r = (r.length == 1) ? ('0' + r) : r;
 93 
 94       var g = source[1].toString(16);
 95       g = (g.length == 1) ? ('0' + g) : g;
 96 
 97       var b = source[2].toString(16);
 98       b = (b.length == 1) ? ('0' + b) : b;
 99 
100       return r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
101     },
102 
103     /**
104      * Gets value of alpha channel for this color 
105      * @method getAlpha
106      * @return {Number} 0-1
107      */
108     getAlpha: function() {
109       return this.getSource()[3];
110     },
111 
112     /**
113      * Sets value of alpha channel for this color
114      * @method setAlpha
115      * @param {Number} 0-1
116      * @return {fabric.Color} thisArg
117      */
118     setAlpha: function(alpha) {
119       var source = this.getSource();
120       source[3] = alpha;
121       this.setSource(source);
122       return this;
123     },
124 
125     /**
126      * Transforms color to its grayscale representation
127      * @method toGrayscale
128      * @return {fabric.Color} thisArg
129      */
130     toGrayscale: function() {
131       var source = this.getSource(),
132           average = parseInt((source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0), 10),
133           currentAlpha = source[3];
134       this.setSource([average, average, average, currentAlpha]);
135       return this;
136     },
137 
138     /**
139      * Transforms color to its black and white representation
140      * @method toGrayscale
141      * @return {fabric.Color} thisArg
142      */
143     toBlackWhite: function(threshold) {
144       var source = this.getSource(),
145           average = (source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0),
146           currentAlpha = source[3],
147           threshold = threshold || 127;
148 
149       average = (Number(average) < Number(threshold)) ? 0 : 255;
150       this.setSource([average, average, average, currentAlpha]);
151       return this;
152     },
153 
154     /**
155      * Overlays color with another color
156      * @method overlayWith
157      * @param {String|fabric.Color} otherColor
158      * @return {fabric.Color} thisArg
159      */
160     overlayWith: function(otherColor) {
161       if (!(otherColor instanceof Color)) {
162         otherColor = new Color(otherColor);
163       }
164 
165       var result = [],
166           alpha = this.getAlpha(),
167           otherAlpha = 0.5,
168           source = this.getSource(),
169           otherSource = otherColor.getSource();
170 
171       for (var i = 0; i < 3; i++) {
172         result.push(Math.round((source[i] * (1 - otherAlpha)) + (otherSource[i] * otherAlpha)));
173       }
174 
175       result[4] = alpha;
176       this.setSource(result);
177       return this;
178     }
179   };
180   
181   /**
182    * Regex matching color in RGB or RGBA formats (ex: rgb(0, 0, 0), rgb(255, 100, 10, 0.5), rgb(1,1,1))
183    * @static
184    * @field
185    */
186   fabric.Color.reRGBa = /^rgba?\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*(\d+(?:\.\d+)?))?\)$/;
187   
188   /**
189    * Regex matching color in HEX format (ex: #FF5555, 010155, aff)
190    * @static
191    * @field
192    */
193   fabric.Color.reHex = /^#?([0-9a-f]{6}|[0-9a-f]{3})$/i;
194 
195   /**
196    * Returns new color object, when given a color in RGB format
197    * @method fromRgb
198    * @param {String} color ex: rgb(0-255,0-255,0-255)
199    * @return {fabric.Color}
200    */
201   fabric.Color.fromRgb = function(color) {
202     return Color.fromSource(Color.sourceFromRgb(color));
203   };
204   
205   /**
206    * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in RGB or RGBA format
207    * @method sourceFromRgb
208    * @param {String} color ex: rgb(0-255,0-255,0-255)
209    * @return {Array} source
210    */
211   fabric.Color.sourceFromRgb = function(color) {
212     var match = color.match(Color.reRGBa);
213     if (match) {
214       return [
215         parseInt(match[1], 10),
216         parseInt(match[2], 10),
217         parseInt(match[3], 10),
218         match[4] ? parseFloat(match[4]) : 1
219       ];
220     }
221   };
222 
223   /**
224    * Returns new color object, when given a color in RGBA format
225    * @static
226    * @function
227    * @method fromRgba
228    * @param {String} color
229    * @return {fabric.Color}
230    */
231   fabric.Color.fromRgba = Color.fromRgb;
232 
233   /**
234    * Returns new color object, when given a color in HEX format
235    * @static
236    * @method fromHex
237    * @return {fabric.Color}
238    */
239   fabric.Color.fromHex = function(color) {
240     return Color.fromSource(Color.sourceFromHex(color));
241   };
242   
243   /**
244    * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in HEX format
245    * @static
246    * @method sourceFromHex
247    * @param {String} color ex: FF5555
248    * @return {Array} source
249    */
250   fabric.Color.sourceFromHex = function(color) {
251     if (color.match(Color.reHex)) {
252       var value = color.slice(color.indexOf('#') + 1),
253           isShortNotation = (value.length === 3),
254           r = isShortNotation ? (value.charAt(0) + value.charAt(0)) : value.substring(0, 2),
255           g = isShortNotation ? (value.charAt(1) + value.charAt(1)) : value.substring(2, 4),
256           b = isShortNotation ? (value.charAt(2) + value.charAt(2)) : value.substring(4, 6);
257 
258       return [
259         parseInt(r, 16),
260         parseInt(g, 16),
261         parseInt(b, 16),
262         1
263       ];
264     }
265   };
266   
267   /**
268    * Returns new color object, when given color in array representation (ex: [200, 100, 100, 0.5])
269    * @static
270    * @method fromSource
271    * @return {fabric.Color}
272    */
273   fabric.Color.fromSource = function(source) {
274     var oColor = new Color();
275     oColor.setSource(source);
276     return oColor;
277   };
278 })();