1 (function() {
  2   
  3   function getColorStopFromStyle(el) {
  4     var style = el.getAttribute('style');
  5     
  6     if (style) {
  7       var keyValuePairs = style.split(/\s*;\s*/);
  8       
  9       for (var i = keyValuePairs.length; i--; ) {
 10         
 11         var split = keyValuePairs[i].split(/\s*:\s*/),
 12             key = split[0].trim(),
 13             value = split[1].trim();
 14             
 15         if (key === 'stop-color') {
 16           return value;
 17         }
 18       }
 19     }
 20   }
 21   
 22   /** @namespace */
 23   
 24   fabric.Gradient = {
 25     
 26     /**
 27      * @method create
 28      * @static
 29      */
 30     create: function(ctx, options) {
 31       options || (options = { });
 32 
 33       var x1 = options.x1 || 0,
 34           y1 = options.y1 || 0,
 35           x2 = options.x2 || ctx.canvas.width,
 36           y2 = options.y2 || 0,
 37           colorStops = options.colorStops;
 38           
 39       var gradient = ctx.createLinearGradient(x1, y1, x2, y2);
 40       
 41       for (var position in colorStops) {
 42         var colorValue = colorStops[position];
 43         gradient.addColorStop(parseFloat(position), colorValue);
 44       }
 45       return gradient;
 46     },
 47     
 48     /**
 49      * @method fromElement
 50      * @static
 51      * @see http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement
 52      */
 53     fromElement: function(el, ctx, instance) {
 54       
 55       /**
 56        *  @example:
 57        *
 58        *  <linearGradient id="grad1">
 59        *    <stop offset="0%" stop-color="white"/>
 60        *    <stop offset="100%" stop-color="black"/>
 61        *  </linearGradient>
 62        *
 63        *  OR
 64        *
 65        *  <linearGradient id="grad1">
 66        *    <stop offset="0%" style="stop-color:rgb(255,255,255)"/>
 67        *    <stop offset="100%" style="stop-color:rgb(0,0,0)"/>
 68        *  </linearGradient>
 69        *
 70        */
 71 
 72       var colorStopEls = el.getElementsByTagName('stop'),
 73           el,
 74           offset,
 75           colorStops = { },
 76           colorStopFromStyle;
 77 
 78       for (var i = colorStopEls.length; i--; ) {
 79         el = colorStopEls[i];
 80         offset = parseInt(el.getAttribute('offset'), 10) / 100;
 81         colorStops[offset] = getColorStopFromStyle(el) || el.getAttribute('stop-color');
 82       }
 83       
 84       var coords = {
 85         x1: el.getAttribute('x1') || 0,
 86         y1: el.getAttribute('y1') || 0,
 87         x2: el.getAttribute('x2') || '100%',
 88         y2: el.getAttribute('y2') || 0
 89       };
 90       
 91       _convertPercentUnitsToValues(instance, coords);
 92       
 93       return fabric.Gradient.create(ctx, {
 94         x1: coords.x1,
 95         y1: coords.y1,
 96         x2: coords.x2,
 97         y2: coords.y2,
 98         colorStops: colorStops
 99       });
100     },
101     
102     /**
103      * @method forObject
104      * @static
105      */
106     forObject: function(obj, ctx, options) {
107       options || (options = { });
108       
109       _convertPercentUnitsToValues(obj, options);
110 
111       var gradient = fabric.Gradient.create(ctx, { 
112         x1: options.x1 - (obj.width / 2),
113         y1: options.y1 - (obj.height / 2),
114         x2: options.x2 - (obj.width / 2),
115         y2: options.y2 - (obj.height / 2),
116         colorStops: options.colorStops
117       });
118 
119       return gradient;
120     }
121   };
122   
123   function _convertPercentUnitsToValues(object, options) {
124     for (var prop in options) {
125       if (typeof options[prop] === 'string' && /^\d+%$/.test(options[prop])) {
126         var percents = parseFloat(options[prop], 10);
127         if (prop === 'x1' || prop === 'x2') {
128           options[prop] = object.width * percents / 100;
129         }
130         else if (prop === 'y1' || prop === 'y2') {
131           options[prop] = object.height * percents / 100;
132         }
133       }
134       // normalize rendering point (should be from top/left corner rather than center of the shape)
135       if (prop === 'x1' || prop === 'x2') {
136         options[prop] -= object.width / 2;
137       }
138       else if (prop === 'y1' || prop === 'y2') {
139         options[prop] -= object.height / 2;
140       }
141     }
142   }
143   
144   /**
145    * Parses an SVG document, returning all of the gradient declarations found in it
146    * @static
147    * @function
148    * @memberOf fabric
149    * @method getGradientDefs
150    * @param {SVGDocument} doc SVG document to parse
151    * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element
152    */
153   function getGradientDefs(doc) {
154     var linearGradientEls = doc.getElementsByTagName('linearGradient'),
155         radialGradientEls = doc.getElementsByTagName('radialGradient'),
156         el,
157         gradientDefs = { };
158     
159     for (var i = linearGradientEls.length; i--; ) {
160       el = linearGradientEls[i];
161       gradientDefs[el.id] = el;
162     }
163     
164     for (var i = radialGradientEls.length; i--; ) {
165       el = radialGradientEls[i];
166       gradientDefs[el.id] = el;
167     }
168     
169     return gradientDefs;
170   }
171   
172   fabric.getGradientDefs = getGradientDefs;
173   
174 })();