1/* build: `node build.js modules=ALL exclude=json,gestures minifier=uglifyjs` */
2/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
3
4var fabric = fabric || { version: "1.5.0" };
5if (typeof exports !== 'undefined') {
6  exports.fabric = fabric;
7}
8
9if (typeof document !== 'undefined' && typeof window !== 'undefined') {
10  fabric.document = document;
11  fabric.window = window;
12  // ensure globality even if entire library were function wrapped (as in Meteor.js packaging system)
13  window.fabric = fabric;
14}
15else {
16  // assume we're running under node.js when document/window are not present
17  fabric.document = require("jsdom")
18    .jsdom("<!DOCTYPE html><html><head></head><body></body></html>");
19
20  if (fabric.document.createWindow) {
21    fabric.window = fabric.document.createWindow();
22  } else {
23    fabric.window = fabric.document.parentWindow;
24  }
25}
26
27/**
28 * True when in environment that supports touch events
29 * @type boolean
30 */
31fabric.isTouchSupported = "ontouchstart" in fabric.document.documentElement;
32
33/**
34 * True when in environment that's probably Node.js
35 * @type boolean
36 */
37fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
38                      typeof window === 'undefined';
39
40/* _FROM_SVG_START_ */
41/**
42 * Attributes parsed from all SVG elements
43 * @type array
44 */
45fabric.SHARED_ATTRIBUTES = [
46  "display",
47  "transform",
48  "fill", "fill-opacity", "fill-rule",
49  "opacity",
50  "stroke", "stroke-dasharray", "stroke-linecap",
51  "stroke-linejoin", "stroke-miterlimit",
52  "stroke-opacity", "stroke-width"
53];
54/* _FROM_SVG_END_ */
55
56/**
57 * Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion.
58 */
59fabric.DPI = 96;
60fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)';
61
62
63(function() {
64
65  /**
66   * @private
67   * @param {String} eventName
68   * @param {Function} handler
69   */
70  function _removeEventListener(eventName, handler) {
71    if (!this.__eventListeners[eventName]) {
72      return;
73    }
74
75    if (handler) {
76      fabric.util.removeFromArray(this.__eventListeners[eventName], handler);
77    }
78    else {
79      this.__eventListeners[eventName].length = 0;
80    }
81  }
82
83  /**
84   * Observes specified event
85   * @deprecated `observe` deprecated since 0.8.34 (use `on` instead)
86   * @memberOf fabric.Observable
87   * @alias on
88   * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
89   * @param {Function} handler Function that receives a notification when an event of the specified type occurs
90   * @return {Self} thisArg
91   * @chainable
92   */
93  function observe(eventName, handler) {
94    if (!this.__eventListeners) {
95      this.__eventListeners = { };
96    }
97    // one object with key/value pairs was passed
98    if (arguments.length === 1) {
99      for (var prop in eventName) {
100        this.on(prop, eventName[prop]);
101      }
102    }
103    else {
104      if (!this.__eventListeners[eventName]) {
105        this.__eventListeners[eventName] = [ ];
106      }
107      this.__eventListeners[eventName].push(handler);
108    }
109    return this;
110  }
111
112  /**
113   * Stops event observing for a particular event handler. Calling this method
114   * without arguments removes all handlers for all events
115   * @deprecated `stopObserving` deprecated since 0.8.34 (use `off` instead)
116   * @memberOf fabric.Observable
117   * @alias off
118   * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
119   * @param {Function} handler Function to be deleted from EventListeners
120   * @return {Self} thisArg
121   * @chainable
122   */
123  function stopObserving(eventName, handler) {
124    if (!this.__eventListeners) {
125      return;
126    }
127
128    // remove all key/value pairs (event name -> event handler)
129    if (arguments.length === 0) {
130      this.__eventListeners = { };
131    }
132    // one object with key/value pairs was passed
133    else if (arguments.length === 1 && typeof arguments[0] === 'object') {
134      for (var prop in eventName) {
135        _removeEventListener.call(this, prop, eventName[prop]);
136      }
137    }
138    else {
139      _removeEventListener.call(this, eventName, handler);
140    }
141    return this;
142  }
143
144  /**
145   * Fires event with an optional options object
146   * @deprecated `fire` deprecated since 1.0.7 (use `trigger` instead)
147   * @memberOf fabric.Observable
148   * @alias trigger
149   * @param {String} eventName Event name to fire
150   * @param {Object} [options] Options object
151   * @return {Self} thisArg
152   * @chainable
153   */
154  function fire(eventName, options) {
155    if (!this.__eventListeners) {
156      return;
157    }
158
159    var listenersForEvent = this.__eventListeners[eventName];
160    if (!listenersForEvent) {
161      return;
162    }
163
164    for (var i = 0, len = listenersForEvent.length; i < len; i++) {
165      // avoiding try/catch for perf. reasons
166      listenersForEvent[i].call(this, options || { });
167    }
168    return this;
169  }
170
171  /**
172   * @namespace fabric.Observable
173   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#events}
174   * @see {@link http://fabricjs.com/events/|Events demo}
175   */
176  fabric.Observable = {
177    observe: observe,
178    stopObserving: stopObserving,
179    fire: fire,
180
181    on: observe,
182    off: stopObserving,
183    trigger: fire
184  };
185})();
186
187
188/**
189 * @namespace fabric.Collection
190 */
191fabric.Collection = {
192
193  /**
194   * Adds objects to collection, then renders canvas (if `renderOnAddRemove` is not `false`)
195   * Objects should be instances of (or inherit from) fabric.Object
196   * @param {...fabric.Object} object Zero or more fabric instances
197   * @return {Self} thisArg
198   */
199  add: function () {
200    this._objects.push.apply(this._objects, arguments);
201    for (var i = 0, length = arguments.length; i < length; i++) {
202      this._onObjectAdded(arguments[i]);
203    }
204    this.renderOnAddRemove && this.renderAll();
205    return this;
206  },
207
208  /**
209   * Inserts an object into collection at specified index, then renders canvas (if `renderOnAddRemove` is not `false`)
210   * An object should be an instance of (or inherit from) fabric.Object
211   * @param {Object} object Object to insert
212   * @param {Number} index Index to insert object at
213   * @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs
214   * @return {Self} thisArg
215   * @chainable
216   */
217  insertAt: function (object, index, nonSplicing) {
218    var objects = this.getObjects();
219    if (nonSplicing) {
220      objects[index] = object;
221    }
222    else {
223      objects.splice(index, 0, object);
224    }
225    this._onObjectAdded(object);
226    this.renderOnAddRemove && this.renderAll();
227    return this;
228  },
229
230  /**
231   * Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
232   * @param {...fabric.Object} object Zero or more fabric instances
233   * @return {Self} thisArg
234   * @chainable
235   */
236  remove: function() {
237    var objects = this.getObjects(),
238        index;
239
240    for (var i = 0, length = arguments.length; i < length; i++) {
241      index = objects.indexOf(arguments[i]);
242
243      // only call onObjectRemoved if an object was actually removed
244      if (index !== -1) {
245        objects.splice(index, 1);
246        this._onObjectRemoved(arguments[i]);
247      }
248    }
249
250    this.renderOnAddRemove && this.renderAll();
251    return this;
252  },
253
254  /**
255   * Executes given function for each object in this group
256   * @param {Function} callback
257   *                   Callback invoked with current object as first argument,
258   *                   index - as second and an array of all objects - as third.
259   *                   Iteration happens in reverse order (for performance reasons).
260   *                   Callback is invoked in a context of Global Object (e.g. `window`)
261   *                   when no `context` argument is given
262   *
263   * @param {Object} context Context (aka thisObject)
264   * @return {Self} thisArg
265   */
266  forEachObject: function(callback, context) {
267    var objects = this.getObjects(),
268        i = objects.length;
269    while (i--) {
270      callback.call(context, objects[i], i, objects);
271    }
272    return this;
273  },
274
275  /**
276   * Returns an array of children objects of this instance
277   * Type parameter introduced in 1.3.10
278   * @param {String} [type] When specified, only objects of this type are returned
279   * @return {Array}
280   */
281  getObjects: function(type) {
282    if (typeof type === 'undefined') {
283      return this._objects;
284    }
285    return this._objects.filter(function(o) {
286      return o.type === type;
287    });
288  },
289
290  /**
291   * Returns object at specified index
292   * @param {Number} index
293   * @return {Self} thisArg
294   */
295  item: function (index) {
296    return this.getObjects()[index];
297  },
298
299  /**
300   * Returns true if collection contains no objects
301   * @return {Boolean} true if collection is empty
302   */
303  isEmpty: function () {
304    return this.getObjects().length === 0;
305  },
306
307  /**
308   * Returns a size of a collection (i.e: length of an array containing its objects)
309   * @return {Number} Collection size
310   */
311  size: function() {
312    return this.getObjects().length;
313  },
314
315  /**
316   * Returns true if collection contains an object
317   * @param {Object} object Object to check against
318   * @return {Boolean} `true` if collection contains an object
319   */
320  contains: function(object) {
321    return this.getObjects().indexOf(object) > -1;
322  },
323
324  /**
325   * Returns number representation of a collection complexity
326   * @return {Number} complexity
327   */
328  complexity: function () {
329    return this.getObjects().reduce(function (memo, current) {
330      memo += current.complexity ? current.complexity() : 0;
331      return memo;
332    }, 0);
333  }
334};
335
336
337(function(global) {
338
339  var sqrt = Math.sqrt,
340      atan2 = Math.atan2,
341      PiBy180 = Math.PI / 180;
342
343  /**
344   * @namespace fabric.util
345   */
346  fabric.util = {
347
348    /**
349     * Removes value from an array.
350     * Presence of value (and its position in an array) is determined via `Array.prototype.indexOf`
351     * @static
352     * @memberOf fabric.util
353     * @param {Array} array
354     * @param {Any} value
355     * @return {Array} original array
356     */
357    removeFromArray: function(array, value) {
358      var idx = array.indexOf(value);
359      if (idx !== -1) {
360        array.splice(idx, 1);
361      }
362      return array;
363    },
364
365    /**
366     * Returns random number between 2 specified ones.
367     * @static
368     * @memberOf fabric.util
369     * @param {Number} min lower limit
370     * @param {Number} max upper limit
371     * @return {Number} random value (between min and max)
372     */
373    getRandomInt: function(min, max) {
374      return Math.floor(Math.random() * (max - min + 1)) + min;
375    },
376
377    /**
378     * Transforms degrees to radians.
379     * @static
380     * @memberOf fabric.util
381     * @param {Number} degrees value in degrees
382     * @return {Number} value in radians
383     */
384    degreesToRadians: function(degrees) {
385      return degrees * PiBy180;
386    },
387
388    /**
389     * Transforms radians to degrees.
390     * @static
391     * @memberOf fabric.util
392     * @param {Number} radians value in radians
393     * @return {Number} value in degrees
394     */
395    radiansToDegrees: function(radians) {
396      return radians / PiBy180;
397    },
398
399    /**
400     * Rotates `point` around `origin` with `radians`
401     * @static
402     * @memberOf fabric.util
403     * @param {fabric.Point} point The point to rotate
404     * @param {fabric.Point} origin The origin of the rotation
405     * @param {Number} radians The radians of the angle for the rotation
406     * @return {fabric.Point} The new rotated point
407     */
408    rotatePoint: function(point, origin, radians) {
409      var sin = Math.sin(radians),
410          cos = Math.cos(radians);
411
412      point.subtractEquals(origin);
413
414      var rx = point.x * cos - point.y * sin,
415          ry = point.x * sin + point.y * cos;
416
417      return new fabric.Point(rx, ry).addEquals(origin);
418    },
419
420    /**
421     * Apply transform t to point p
422     * @static
423     * @memberOf fabric.util
424     * @param  {fabric.Point} p The point to transform
425     * @param  {Array} t The transform
426     * @param  {Boolean} [ignoreOffset] Indicates that the offset should not be applied
427     * @return {fabric.Point} The transformed point
428     */
429    transformPoint: function(p, t, ignoreOffset) {
430      if (ignoreOffset) {
431        return new fabric.Point(
432          t[0] * p.x + t[2] * p.y,
433          t[1] * p.x + t[3] * p.y
434        );
435      }
436      return new fabric.Point(
437        t[0] * p.x + t[2] * p.y + t[4],
438        t[1] * p.x + t[3] * p.y + t[5]
439      );
440    },
441
442    /**
443     * Invert transformation t
444     * @static
445     * @memberOf fabric.util
446     * @param {Array} t The transform
447     * @return {Array} The inverted transform
448     */
449    invertTransform: function(t) {
450      var r = t.slice(),
451          a = 1 / (t[0] * t[3] - t[1] * t[2]);
452      r = [a * t[3], -a * t[1], -a * t[2], a * t[0], 0, 0];
453      var o = fabric.util.transformPoint({ x: t[4], y: t[5] }, r);
454      r[4] = -o.x;
455      r[5] = -o.y;
456      return r;
457    },
458
459    /**
460     * A wrapper around Number#toFixed, which contrary to native method returns number, not string.
461     * @static
462     * @memberOf fabric.util
463     * @param {Number|String} number number to operate on
464     * @param {Number} fractionDigits number of fraction digits to "leave"
465     * @return {Number}
466     */
467    toFixed: function(number, fractionDigits) {
468      return parseFloat(Number(number).toFixed(fractionDigits));
469    },
470
471    /**
472     * Converts from attribute value to pixel value if applicable.
473     * Returns converted pixels or original value not converted.
474     * @param {Number|String} value number to operate on
475     * @return {Number|String}
476     */
477    parseUnit: function(value, fontSize) {
478      var unit = /\D{0,2}$/.exec(value),
479          number = parseFloat(value);
480      if (!fontSize) {
481        fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
482      }
483      switch (unit[0]) {
484        case 'mm':
485          return number * fabric.DPI / 25.4;
486
487        case 'cm':
488          return number * fabric.DPI / 2.54;
489
490        case 'in':
491          return number * fabric.DPI;
492
493        case 'pt':
494          return number * fabric.DPI / 72; // or * 4 / 3
495
496        case 'pc':
497          return number * fabric.DPI / 72 * 12; // or * 16
498
499        case 'em':
500          return number * fontSize;
501
502        default:
503          return number;
504      }
505    },
506
507    /**
508     * Function which always returns `false`.
509     * @static
510     * @memberOf fabric.util
511     * @return {Boolean}
512     */
513    falseFunction: function() {
514      return false;
515    },
516
517    /**
518      * Returns klass "Class" object of given namespace
519      * @memberOf fabric.util
520      * @param {String} type Type of object (eg. 'circle')
521      * @param {String} namespace Namespace to get klass "Class" object from
522      * @return {Object} klass "Class"
523      */
524    getKlass: function(type, namespace) {
525      // capitalize first letter only
526      type = fabric.util.string.camelize(type.charAt(0).toUpperCase() + type.slice(1));
527      return fabric.util.resolveNamespace(namespace)[type];
528    },
529
530    /**
531     * Returns object of given namespace
532     * @memberOf fabric.util
533     * @param {String} namespace Namespace string e.g. 'fabric.Image.filter' or 'fabric'
534     * @return {Object} Object for given namespace (default fabric)
535     */
536    resolveNamespace: function(namespace) {
537      if (!namespace) {
538        return fabric;
539      }
540
541      var parts = namespace.split('.'),
542          len = parts.length,
543          obj = global || fabric.window;
544
545      for (var i = 0; i < len; ++i) {
546        obj = obj[parts[i]];
547      }
548
549      return obj;
550    },
551
552    /**
553     * Loads image element from given url and passes it to a callback
554     * @memberOf fabric.util
555     * @param {String} url URL representing an image
556     * @param {Function} callback Callback; invoked with loaded image
557     * @param {Any} [context] Context to invoke callback in
558     * @param {Object} [crossOrigin] crossOrigin value to set image element to
559     */
560    loadImage: function(url, callback, context, crossOrigin) {
561      if (!url) {
562        callback && callback.call(context, url);
563        return;
564      }
565
566      var img = fabric.util.createImage();
567
568      /** @ignore */
569      img.onload = function () {
570        callback && callback.call(context, img);
571        img = img.onload = img.onerror = null;
572      };
573
574      /** @ignore */
575      img.onerror = function() {
576        fabric.log('Error loading ' + img.src);
577        callback && callback.call(context, null, true);
578        img = img.onload = img.onerror = null;
579      };
580
581      // data-urls appear to be buggy with crossOrigin
582      // https://github.com/kangax/fabric.js/commit/d0abb90f1cd5c5ef9d2a94d3fb21a22330da3e0a#commitcomment-4513767
583      // see https://code.google.com/p/chromium/issues/detail?id=315152
584      //     https://bugzilla.mozilla.org/show_bug.cgi?id=935069
585      if (url.indexOf('data') !== 0 && typeof crossOrigin !== 'undefined') {
586        img.crossOrigin = crossOrigin;
587      }
588
589      img.src = url;
590    },
591
592    /**
593     * Creates corresponding fabric instances from their object representations
594     * @static
595     * @memberOf fabric.util
596     * @param {Array} objects Objects to enliven
597     * @param {Function} callback Callback to invoke when all objects are created
598     * @param {String} namespace Namespace to get klass "Class" object from
599     * @param {Function} reviver Method for further parsing of object elements,
600     * called after each fabric object created.
601     */
602    enlivenObjects: function(objects, callback, namespace, reviver) {
603      objects = objects || [ ];
604
605      function onLoaded() {
606        if (++numLoadedObjects === numTotalObjects) {
607          callback && callback(enlivenedObjects);
608        }
609      }
610
611      var enlivenedObjects = [ ],
612          numLoadedObjects = 0,
613          numTotalObjects = objects.length;
614
615      if (!numTotalObjects) {
616        callback && callback(enlivenedObjects);
617        return;
618      }
619
620      objects.forEach(function (o, index) {
621        // if sparse array
622        if (!o || !o.type) {
623          onLoaded();
624          return;
625        }
626        var klass = fabric.util.getKlass(o.type, namespace);
627        if (klass.async) {
628          klass.fromObject(o, function (obj, error) {
629            if (!error) {
630              enlivenedObjects[index] = obj;
631              reviver && reviver(o, enlivenedObjects[index]);
632            }
633            onLoaded();
634          });
635        }
636        else {
637          enlivenedObjects[index] = klass.fromObject(o);
638          reviver && reviver(o, enlivenedObjects[index]);
639          onLoaded();
640        }
641      });
642    },
643
644    /**
645     * Groups SVG elements (usually those retrieved from SVG document)
646     * @static
647     * @memberOf fabric.util
648     * @param {Array} elements SVG elements to group
649     * @param {Object} [options] Options object
650     * @return {fabric.Object|fabric.PathGroup}
651     */
652    groupSVGElements: function(elements, options, path) {
653      var object;
654
655      object = new fabric.PathGroup(elements, options);
656
657      if (typeof path !== 'undefined') {
658        object.setSourcePath(path);
659      }
660      return object;
661    },
662
663    /**
664     * Populates an object with properties of another object
665     * @static
666     * @memberOf fabric.util
667     * @param {Object} source Source object
668     * @param {Object} destination Destination object
669     * @return {Array} properties Propertie names to include
670     */
671    populateWithProperties: function(source, destination, properties) {
672      if (properties && Object.prototype.toString.call(properties) === '[object Array]') {
673        for (var i = 0, len = properties.length; i < len; i++) {
674          if (properties[i] in source) {
675            destination[properties[i]] = source[properties[i]];
676          }
677        }
678      }
679    },
680
681    /**
682     * Draws a dashed line between two points
683     *
684     * This method is used to draw dashed line around selection area.
685     * See <a href="http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas">dotted stroke in canvas</a>
686     *
687     * @param {CanvasRenderingContext2D} ctx context
688     * @param {Number} x  start x coordinate
689     * @param {Number} y start y coordinate
690     * @param {Number} x2 end x coordinate
691     * @param {Number} y2 end y coordinate
692     * @param {Array} da dash array pattern
693     */
694    drawDashedLine: function(ctx, x, y, x2, y2, da) {
695      var dx = x2 - x,
696          dy = y2 - y,
697          len = sqrt(dx * dx + dy * dy),
698          rot = atan2(dy, dx),
699          dc = da.length,
700          di = 0,
701          draw = true;
702
703      ctx.save();
704      ctx.translate(x, y);
705      ctx.moveTo(0, 0);
706      ctx.rotate(rot);
707
708      x = 0;
709      while (len > x) {
710        x += da[di++ % dc];
711        if (x > len) {
712          x = len;
713        }
714        ctx[draw ? 'lineTo' : 'moveTo'](x, 0);
715        draw = !draw;
716      }
717
718      ctx.restore();
719    },
720
721    /**
722     * Creates canvas element and initializes it via excanvas if necessary
723     * @static
724     * @memberOf fabric.util
725     * @param {CanvasElement} [canvasEl] optional canvas element to initialize;
726     * when not given, element is created implicitly
727     * @return {CanvasElement} initialized canvas element
728     */
729    createCanvasElement: function(canvasEl) {
730      canvasEl || (canvasEl = fabric.document.createElement('canvas'));
731      //jscs:disable requireCamelCaseOrUpperCaseIdentifiers
732      if (!canvasEl.getContext && typeof G_vmlCanvasManager !== 'undefined') {
733        G_vmlCanvasManager.initElement(canvasEl);
734      }
735      //jscs:enable requireCamelCaseOrUpperCaseIdentifiers
736      return canvasEl;
737    },
738
739    /**
740     * Creates image element (works on client and node)
741     * @static
742     * @memberOf fabric.util
743     * @return {HTMLImageElement} HTML image element
744     */
745    createImage: function() {
746      return fabric.isLikelyNode
747        ? new (require('canvas').Image)()
748        : fabric.document.createElement('img');
749    },
750
751    /**
752     * Creates accessors (getXXX, setXXX) for a "class", based on "stateProperties" array
753     * @static
754     * @memberOf fabric.util
755     * @param {Object} klass "Class" to create accessors for
756     */
757    createAccessors: function(klass) {
758      var proto = klass.prototype;
759
760      for (var i = proto.stateProperties.length; i--; ) {
761
762        var propName = proto.stateProperties[i],
763            capitalizedPropName = propName.charAt(0).toUpperCase() + propName.slice(1),
764            setterName = 'set' + capitalizedPropName,
765            getterName = 'get' + capitalizedPropName;
766
767        // using `new Function` for better introspection
768        if (!proto[getterName]) {
769          proto[getterName] = (function(property) {
770            return new Function('return this.get("' + property + '")');
771          })(propName);
772        }
773        if (!proto[setterName]) {
774          proto[setterName] = (function(property) {
775            return new Function('value', 'return this.set("' + property + '", value)');
776          })(propName);
777        }
778      }
779    },
780
781    /**
782     * @static
783     * @memberOf fabric.util
784     * @param {fabric.Object} receiver Object implementing `clipTo` method
785     * @param {CanvasRenderingContext2D} ctx Context to clip
786     */
787    clipContext: function(receiver, ctx) {
788      ctx.save();
789      ctx.beginPath();
790      receiver.clipTo(ctx);
791      ctx.clip();
792    },
793
794    /**
795     * Multiply matrix A by matrix B to nest transformations
796     * @static
797     * @memberOf fabric.util
798     * @param  {Array} a First transformMatrix
799     * @param  {Array} b Second transformMatrix
800     * @return {Array} The product of the two transform matrices
801     */
802    multiplyTransformMatrices: function(a, b) {
803      // Matrix multiply a * b
804      return [
805        a[0] * b[0] + a[2] * b[1],
806        a[1] * b[0] + a[3] * b[1],
807        a[0] * b[2] + a[2] * b[3],
808        a[1] * b[2] + a[3] * b[3],
809        a[0] * b[4] + a[2] * b[5] + a[4],
810        a[1] * b[4] + a[3] * b[5] + a[5]
811      ];
812    },
813
814    /**
815     * Returns string representation of function body
816     * @param {Function} fn Function to get body of
817     * @return {String} Function body
818     */
819    getFunctionBody: function(fn) {
820      return (String(fn).match(/function[^{]*\{([\s\S]*)\}/) || {})[1];
821    },
822
823    /**
824     * Returns true if context has transparent pixel
825     * at specified location (taking tolerance into account)
826     * @param {CanvasRenderingContext2D} ctx context
827     * @param {Number} x x coordinate
828     * @param {Number} y y coordinate
829     * @param {Number} tolerance Tolerance
830     */
831    isTransparent: function(ctx, x, y, tolerance) {
832
833      // If tolerance is > 0 adjust start coords to take into account.
834      // If moves off Canvas fix to 0
835      if (tolerance > 0) {
836        if (x > tolerance) {
837          x -= tolerance;
838        }
839        else {
840          x = 0;
841        }
842        if (y > tolerance) {
843          y -= tolerance;
844        }
845        else {
846          y = 0;
847        }
848      }
849
850      var _isTransparent = true,
851          imageData = ctx.getImageData(x, y, (tolerance * 2) || 1, (tolerance * 2) || 1);
852
853      // Split image data - for tolerance > 1, pixelDataSize = 4;
854      for (var i = 3, l = imageData.data.length; i < l; i += 4) {
855        var temp = imageData.data[i];
856        _isTransparent = temp <= 0;
857        if (_isTransparent === false) {
858          break; // Stop if colour found
859        }
860      }
861
862      imageData = null;
863
864      return _isTransparent;
865    }
866  };
867
868})(typeof exports !== 'undefined' ? exports : this);
869
870
871(function() {
872
873  var arcToSegmentsCache = { },
874      segmentToBezierCache = { },
875      boundsOfCurveCache = { },
876      _join = Array.prototype.join;
877
878  /* Adapted from http://dxr.mozilla.org/mozilla-central/source/content/svg/content/src/nsSVGPathDataParser.cpp
879   * by Andrea Bogazzi code is under MPL. if you don't have a copy of the license you can take it here
880   * http://mozilla.org/MPL/2.0/
881   */
882  function arcToSegments(toX, toY, rx, ry, large, sweep, rotateX) {
883    var argsString = _join.call(arguments);
884    if (arcToSegmentsCache[argsString]) {
885      return arcToSegmentsCache[argsString];
886    }
887
888    var PI = Math.PI, th = rotateX * PI / 180,
889        sinTh = Math.sin(th),
890        cosTh = Math.cos(th),
891        fromX = 0, fromY = 0;
892
893    rx = Math.abs(rx);
894    ry = Math.abs(ry);
895
896    var px = -cosTh * toX * 0.5 - sinTh * toY * 0.5,
897        py = -cosTh * toY * 0.5 + sinTh * toX * 0.5,
898        rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px,
899        pl = rx2 * ry2 - rx2 * py2 - ry2 * px2,
900        root = 0;
901
902    if (pl < 0) {
903      var s = Math.sqrt(1 - pl/(rx2 * ry2));
904      rx *= s;
905      ry *= s;
906    }
907    else {
908      root = (large === sweep ? -1.0 : 1.0) *
909              Math.sqrt( pl /(rx2 * py2 + ry2 * px2));
910    }
911
912    var cx = root * rx * py / ry,
913        cy = -root * ry * px / rx,
914        cx1 = cosTh * cx - sinTh * cy + toX * 0.5,
915        cy1 = sinTh * cx + cosTh * cy + toY * 0.5,
916        mTheta = calcVectorAngle(1, 0, (px - cx) / rx, (py - cy) / ry),
917        dtheta = calcVectorAngle((px - cx) / rx, (py - cy) / ry, (-px - cx) / rx, (-py - cy) / ry);
918
919    if (sweep === 0 && dtheta > 0) {
920      dtheta -= 2 * PI;
921    }
922    else if (sweep === 1 && dtheta < 0) {
923      dtheta += 2 * PI;
924    }
925
926    // Convert into cubic bezier segments <= 90deg
927    var segments = Math.ceil(Math.abs(dtheta / PI * 2)),
928        result = [], mDelta = dtheta / segments,
929        mT = 8 / 3 * Math.sin(mDelta / 4) * Math.sin(mDelta / 4) / Math.sin(mDelta / 2),
930        th3 = mTheta + mDelta;
931
932    for (var i = 0; i < segments; i++) {
933      result[i] = segmentToBezier(mTheta, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY);
934      fromX = result[i][4];
935      fromY = result[i][5];
936      mTheta = th3;
937      th3 += mDelta;
938    }
939    arcToSegmentsCache[argsString] = result;
940    return result;
941  }
942
943  function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) {
944    var argsString2 = _join.call(arguments);
945    if (segmentToBezierCache[argsString2]) {
946      return segmentToBezierCache[argsString2];
947    }
948
949    var costh2 = Math.cos(th2),
950        sinth2 = Math.sin(th2),
951        costh3 = Math.cos(th3),
952        sinth3 = Math.sin(th3),
953        toX = cosTh * rx * costh3 - sinTh * ry * sinth3 + cx1,
954        toY = sinTh * rx * costh3 + cosTh * ry * sinth3 + cy1,
955        cp1X = fromX + mT * ( - cosTh * rx * sinth2 - sinTh * ry * costh2),
956        cp1Y = fromY + mT * ( - sinTh * rx * sinth2 + cosTh * ry * costh2),
957        cp2X = toX + mT * ( cosTh * rx * sinth3 + sinTh * ry * costh3),
958        cp2Y = toY + mT * ( sinTh * rx * sinth3 - cosTh * ry * costh3);
959
960    segmentToBezierCache[argsString2] = [
961      cp1X, cp1Y,
962      cp2X, cp2Y,
963      toX, toY
964    ];
965    return segmentToBezierCache[argsString2];
966  }
967
968  /*
969  * Private
970  */
971  function calcVectorAngle(ux, uy, vx, vy) {
972    var ta = Math.atan2(uy, ux),
973        tb = Math.atan2(vy, vx);
974    if (tb >= ta) {
975      return tb - ta;
976    }
977    else {
978      return 2 * Math.PI - (ta - tb);
979    }
980  }
981
982  /**
983   * Draws arc
984   * @param {CanvasRenderingContext2D} ctx
985   * @param {Number} fx
986   * @param {Number} fy
987   * @param {Array} coords
988   */
989  fabric.util.drawArc = function(ctx, fx, fy, coords) {
990    var rx = coords[0],
991        ry = coords[1],
992        rot = coords[2],
993        large = coords[3],
994        sweep = coords[4],
995        tx = coords[5],
996        ty = coords[6],
997        segs = [[ ], [ ], [ ], [ ]],
998        segsNorm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
999
1000    for (var i = 0, len = segsNorm.length; i < len; i++) {
1001      segs[i][0] = segsNorm[i][0] + fx;
1002      segs[i][1] = segsNorm[i][1] + fy;
1003      segs[i][2] = segsNorm[i][2] + fx;
1004      segs[i][3] = segsNorm[i][3] + fy;
1005      segs[i][4] = segsNorm[i][4] + fx;
1006      segs[i][5] = segsNorm[i][5] + fy;
1007      ctx.bezierCurveTo.apply(ctx, segs[i]);
1008    }
1009  };
1010
1011  /**
1012   * Calculate bounding box of a elliptic-arc
1013   * @param {Number} fx start point of arc
1014   * @param {Number} fy
1015   * @param {Number} rx horizontal radius
1016   * @param {Number} ry vertical radius
1017   * @param {Number} rot angle of horizontal axe
1018   * @param {Number} large 1 or 0, whatever the arc is the big or the small on the 2 points
1019   * @param {Number} sweep 1 or 0, 1 clockwise or counterclockwise direction
1020   * @param {Number} tx end point of arc
1021   * @param {Number} ty
1022   */
1023  fabric.util.getBoundsOfArc = function(fx, fy, rx, ry, rot, large, sweep, tx, ty) {
1024
1025    var fromX = 0, fromY = 0, bound = [ ], bounds = [ ],
1026    segs = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot),
1027    boundCopy = [[ ], [ ]];
1028
1029    for (var i = 0, len = segs.length; i < len; i++) {
1030      bound = getBoundsOfCurve(fromX, fromY, segs[i][0], segs[i][1], segs[i][2], segs[i][3], segs[i][4], segs[i][5]);
1031      boundCopy[0].x = bound[0].x + fx;
1032      boundCopy[0].y = bound[0].y + fy;
1033      boundCopy[1].x = bound[1].x + fx;
1034      boundCopy[1].y = bound[1].y + fy;
1035      bounds.push(boundCopy[0]);
1036      bounds.push(boundCopy[1]);
1037      fromX = segs[i][4];
1038      fromY = segs[i][5];
1039    }
1040    return bounds;
1041  };
1042
1043  /**
1044   * Calculate bounding box of a beziercurve
1045   * @param {Number} x0 starting point
1046   * @param {Number} y0
1047   * @param {Number} x1 first control point
1048   * @param {Number} y1
1049   * @param {Number} x2 secondo control point
1050   * @param {Number} y2
1051   * @param {Number} x3 end of beizer
1052   * @param {Number} y3
1053   */
1054  // taken from http://jsbin.com/ivomiq/56/edit  no credits available for that.
1055  function getBoundsOfCurve(x0, y0, x1, y1, x2, y2, x3, y3) {
1056    var argsString = _join.call(arguments);
1057    if (boundsOfCurveCache[argsString]) {
1058      return boundsOfCurveCache[argsString];
1059    }
1060
1061    var sqrt = Math.sqrt,
1062        min = Math.min, max = Math.max,
1063        abs = Math.abs, tvalues = [ ],
1064        bounds = [[ ], [ ]],
1065        a, b, c, t, t1, t2, b2ac, sqrtb2ac;
1066
1067    b = 6 * x0 - 12 * x1 + 6 * x2;
1068    a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
1069    c = 3 * x1 - 3 * x0;
1070
1071    for (var i = 0; i < 2; ++i) {
1072      if (i > 0) {
1073        b = 6 * y0 - 12 * y1 + 6 * y2;
1074        a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
1075        c = 3 * y1 - 3 * y0;
1076      }
1077
1078      if (abs(a) < 1e-12) {
1079        if (abs(b) < 1e-12) {
1080          continue;
1081        }
1082        t = -c / b;
1083        if (0 < t && t < 1) {
1084          tvalues.push(t);
1085        }
1086        continue;
1087      }
1088      b2ac = b * b - 4 * c * a;
1089      if (b2ac < 0) {
1090        continue;
1091      }
1092      sqrtb2ac = sqrt(b2ac);
1093      t1 = (-b + sqrtb2ac) / (2 * a);
1094      if (0 < t1 && t1 < 1) {
1095        tvalues.push(t1);
1096      }
1097      t2 = (-b - sqrtb2ac) / (2 * a);
1098      if (0 < t2 && t2 < 1) {
1099        tvalues.push(t2);
1100      }
1101    }
1102
1103    var x, y, j = tvalues.length, jlen = j, mt;
1104    while (j--) {
1105      t = tvalues[j];
1106      mt = 1 - t;
1107      x = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3);
1108      bounds[0][j] = x;
1109
1110      y = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3);
1111      bounds[1][j] = y;
1112    }
1113
1114    bounds[0][jlen] = x0;
1115    bounds[1][jlen] = y0;
1116    bounds[0][jlen + 1] = x3;
1117    bounds[1][jlen + 1] = y3;
1118    var result = [
1119      {
1120        x: min.apply(null, bounds[0]),
1121        y: min.apply(null, bounds[1])
1122      },
1123      {
1124        x: max.apply(null, bounds[0]),
1125        y: max.apply(null, bounds[1])
1126      }
1127    ];
1128    boundsOfCurveCache[argsString] = result;
1129    return result;
1130  }
1131
1132  fabric.util.getBoundsOfCurve = getBoundsOfCurve;
1133
1134})();
1135
1136
1137(function() {
1138
1139  var slice = Array.prototype.slice;
1140
1141  /* _ES5_COMPAT_START_ */
1142
1143  if (!Array.prototype.indexOf) {
1144    /**
1145     * Finds index of an element in an array
1146     * @param {Any} searchElement
1147     * @param {Number} [fromIndex]
1148     * @return {Number}
1149     */
1150    Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
1151      if (this === void 0 || this === null) {
1152        throw new TypeError();
1153      }
1154      var t = Object(this), len = t.length >>> 0;
1155      if (len === 0) {
1156        return -1;
1157      }
1158      var n = 0;
1159      if (arguments.length > 0) {
1160        n = Number(arguments[1]);
1161        if (n !== n) { // shortcut for verifying if it's NaN
1162          n = 0;
1163        }
1164        else if (n !== 0 && n !== Number.POSITIVE_INFINITY && n !== Number.NEGATIVE_INFINITY) {
1165          n = (n > 0 || -1) * Math.floor(Math.abs(n));
1166        }
1167      }
1168      if (n >= len) {
1169        return -1;
1170      }
1171      var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
1172      for (; k < len; k++) {
1173        if (k in t && t[k] === searchElement) {
1174          return k;
1175        }
1176      }
1177      return -1;
1178    };
1179  }
1180
1181  if (!Array.prototype.forEach) {
1182    /**
1183     * Iterates an array, invoking callback for each element
1184     * @param {Function} fn Callback to invoke for each element
1185     * @param {Object} [context] Context to invoke callback in
1186     * @return {Array}
1187     */
1188    Array.prototype.forEach = function(fn, context) {
1189      for (var i = 0, len = this.length >>> 0; i < len; i++) {
1190        if (i in this) {
1191          fn.call(context, this[i], i, this);
1192        }
1193      }
1194    };
1195  }
1196
1197  if (!Array.prototype.map) {
1198    /**
1199     * Returns a result of iterating over an array, invoking callback for each element
1200     * @param {Function} fn Callback to invoke for each element
1201     * @param {Object} [context] Context to invoke callback in
1202     * @return {Array}
1203     */
1204    Array.prototype.map = function(fn, context) {
1205      var result = [ ];
1206      for (var i = 0, len = this.length >>> 0; i < len; i++) {
1207        if (i in this) {
1208          result[i] = fn.call(context, this[i], i, this);
1209        }
1210      }
1211      return result;
1212    };
1213  }
1214
1215  if (!Array.prototype.every) {
1216    /**
1217     * Returns true if a callback returns truthy value for all elements in an array
1218     * @param {Function} fn Callback to invoke for each element
1219     * @param {Object} [context] Context to invoke callback in
1220     * @return {Boolean}
1221     */
1222    Array.prototype.every = function(fn, context) {
1223      for (var i = 0, len = this.length >>> 0; i < len; i++) {
1224        if (i in this && !fn.call(context, this[i], i, this)) {
1225          return false;
1226        }
1227      }
1228      return true;
1229    };
1230  }
1231
1232  if (!Array.prototype.some) {
1233    /**
1234     * Returns true if a callback returns truthy value for at least one element in an array
1235     * @param {Function} fn Callback to invoke for each element
1236     * @param {Object} [context] Context to invoke callback in
1237     * @return {Boolean}
1238     */
1239    Array.prototype.some = function(fn, context) {
1240      for (var i = 0, len = this.length >>> 0; i < len; i++) {
1241        if (i in this && fn.call(context, this[i], i, this)) {
1242          return true;
1243        }
1244      }
1245      return false;
1246    };
1247  }
1248
1249  if (!Array.prototype.filter) {
1250    /**
1251     * Returns the result of iterating over elements in an array
1252     * @param {Function} fn Callback to invoke for each element
1253     * @param {Object} [context] Context to invoke callback in
1254     * @return {Array}
1255     */
1256    Array.prototype.filter = function(fn, context) {
1257      var result = [ ], val;
1258      for (var i = 0, len = this.length >>> 0; i < len; i++) {
1259        if (i in this) {
1260          val = this[i]; // in case fn mutates this
1261          if (fn.call(context, val, i, this)) {
1262            result.push(val);
1263          }
1264        }
1265      }
1266      return result;
1267    };
1268  }
1269
1270  if (!Array.prototype.reduce) {
1271    /**
1272     * Returns "folded" (reduced) result of iterating over elements in an array
1273     * @param {Function} fn Callback to invoke for each element
1274     * @param {Object} [initial] Object to use as the first argument to the first call of the callback
1275     * @return {Any}
1276     */
1277    Array.prototype.reduce = function(fn /*, initial*/) {
1278      var len = this.length >>> 0,
1279          i = 0,
1280          rv;
1281
1282      if (arguments.length > 1) {
1283        rv = arguments[1];
1284      }
1285      else {
1286        do {
1287          if (i in this) {
1288            rv = this[i++];
1289            break;
1290          }
1291          // if array contains no values, no initial value to return
1292          if (++i >= len) {
1293            throw new TypeError();
1294          }
1295        }
1296        while (true);
1297      }
1298      for (; i < len; i++) {
1299        if (i in this) {
1300          rv = fn.call(null, rv, this[i], i, this);
1301        }
1302      }
1303      return rv;
1304    };
1305  }
1306
1307  /* _ES5_COMPAT_END_ */
1308
1309  /**
1310   * Invokes method on all items in a given array
1311   * @memberOf fabric.util.array
1312   * @param {Array} array Array to iterate over
1313   * @param {String} method Name of a method to invoke
1314   * @return {Array}
1315   */
1316  function invoke(array, method) {
1317    var args = slice.call(arguments, 2), result = [ ];
1318    for (var i = 0, len = array.length; i < len; i++) {
1319      result[i] = args.length ? array[i][method].apply(array[i], args) : array[i][method].call(array[i]);
1320    }
1321    return result;
1322  }
1323
1324  /**
1325   * Finds maximum value in array (not necessarily "first" one)
1326   * @memberOf fabric.util.array
1327   * @param {Array} array Array to iterate over
1328   * @param {String} byProperty
1329   * @return {Any}
1330   */
1331  function max(array, byProperty) {
1332    return find(array, byProperty, function(value1, value2) {
1333      return value1 >= value2;
1334    });
1335  }
1336
1337  /**
1338   * Finds minimum value in array (not necessarily "first" one)
1339   * @memberOf fabric.util.array
1340   * @param {Array} array Array to iterate over
1341   * @param {String} byProperty
1342   * @return {Any}
1343   */
1344  function min(array, byProperty) {
1345    return find(array, byProperty, function(value1, value2) {
1346      return value1 < value2;
1347    });
1348  }
1349
1350  /**
1351   * @private
1352   */
1353  function find(array, byProperty, condition) {
1354    if (!array || array.length === 0) {
1355      return;
1356    }
1357
1358    var i = array.length - 1,
1359        result = byProperty ? array[i][byProperty] : array[i];
1360    if (byProperty) {
1361      while (i--) {
1362        if (condition(array[i][byProperty], result)) {
1363          result = array[i][byProperty];
1364        }
1365      }
1366    }
1367    else {
1368      while (i--) {
1369        if (condition(array[i], result)) {
1370          result = array[i];
1371        }
1372      }
1373    }
1374    return result;
1375  }
1376
1377  /**
1378   * @namespace fabric.util.array
1379   */
1380  fabric.util.array = {
1381    invoke: invoke,
1382    min: min,
1383    max: max
1384  };
1385
1386})();
1387
1388
1389(function() {
1390
1391  /**
1392   * Copies all enumerable properties of one object to another
1393   * @memberOf fabric.util.object
1394   * @param {Object} destination Where to copy to
1395   * @param {Object} source Where to copy from
1396   * @return {Object}
1397   */
1398  function extend(destination, source) {
1399    // JScript DontEnum bug is not taken care of
1400    for (var property in source) {
1401      destination[property] = source[property];
1402    }
1403    return destination;
1404  }
1405
1406  /**
1407   * Creates an empty object and copies all enumerable properties of another object to it
1408   * @memberOf fabric.util.object
1409   * @param {Object} object Object to clone
1410   * @return {Object}
1411   */
1412  function clone(object) {
1413    return extend({ }, object);
1414  }
1415
1416  /** @namespace fabric.util.object */
1417  fabric.util.object = {
1418    extend: extend,
1419    clone: clone
1420  };
1421
1422})();
1423
1424
1425(function() {
1426
1427  /* _ES5_COMPAT_START_ */
1428  if (!String.prototype.trim) {
1429    /**
1430     * Trims a string (removing whitespace from the beginning and the end)
1431     * @function external:String#trim
1432     * @see <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/String/Trim">String#trim on MDN</a>
1433     */
1434    String.prototype.trim = function () {
1435      // this trim is not fully ES3 or ES5 compliant, but it should cover most cases for now
1436      return this.replace(/^[\s\xA0]+/, '').replace(/[\s\xA0]+$/, '');
1437    };
1438  }
1439  /* _ES5_COMPAT_END_ */
1440
1441  /**
1442   * Camelizes a string
1443   * @memberOf fabric.util.string
1444   * @param {String} string String to camelize
1445   * @return {String} Camelized version of a string
1446   */
1447  function camelize(string) {
1448    return string.replace(/-+(.)?/g, function(match, character) {
1449      return character ? character.toUpperCase() : '';
1450    });
1451  }
1452
1453  /**
1454   * Capitalizes a string
1455   * @memberOf fabric.util.string
1456   * @param {String} string String to capitalize
1457   * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
1458   * and other letters stay untouched, if false first letter is capitalized
1459   * and other letters are converted to lowercase.
1460   * @return {String} Capitalized version of a string
1461   */
1462  function capitalize(string, firstLetterOnly) {
1463    return string.charAt(0).toUpperCase() +
1464      (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
1465  }
1466
1467  /**
1468   * Escapes XML in a string
1469   * @memberOf fabric.util.string
1470   * @param {String} string String to escape
1471   * @return {String} Escaped version of a string
1472   */
1473  function escapeXml(string) {
1474    return string.replace(/&/g, '&amp;')
1475       .replace(/"/g, '&quot;')
1476       .replace(/'/g, '&apos;')
1477       .replace(/</g, '&lt;')
1478       .replace(/>/g, '&gt;');
1479  }
1480
1481  /**
1482   * String utilities
1483   * @namespace fabric.util.string
1484   */
1485  fabric.util.string = {
1486    camelize: camelize,
1487    capitalize: capitalize,
1488    escapeXml: escapeXml
1489  };
1490}());
1491
1492
1493/* _ES5_COMPAT_START_ */
1494(function() {
1495
1496  var slice = Array.prototype.slice,
1497      apply = Function.prototype.apply,
1498      Dummy = function() { };
1499
1500  if (!Function.prototype.bind) {
1501    /**
1502     * Cross-browser approximation of ES5 Function.prototype.bind (not fully spec conforming)
1503     * @see <a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind">Function#bind on MDN</a>
1504     * @param {Object} thisArg Object to bind function to
1505     * @param {Any[]} [...] Values to pass to a bound function
1506     * @return {Function}
1507     */
1508    Function.prototype.bind = function(thisArg) {
1509      var _this = this, args = slice.call(arguments, 1), bound;
1510      if (args.length) {
1511        bound = function() {
1512          return apply.call(_this, this instanceof Dummy ? this : thisArg, args.concat(slice.call(arguments)));
1513        };
1514      }
1515      else {
1516        /** @ignore */
1517        bound = function() {
1518          return apply.call(_this, this instanceof Dummy ? this : thisArg, arguments);
1519        };
1520      }
1521      Dummy.prototype = this.prototype;
1522      bound.prototype = new Dummy();
1523
1524      return bound;
1525    };
1526  }
1527
1528})();
1529/* _ES5_COMPAT_END_ */
1530
1531
1532(function() {
1533
1534  var slice = Array.prototype.slice, emptyFunction = function() { },
1535
1536      IS_DONTENUM_BUGGY = (function() {
1537        for (var p in { toString: 1 }) {
1538          if (p === 'toString') {
1539            return false;
1540          }
1541        }
1542        return true;
1543      })(),
1544
1545      /** @ignore */
1546      addMethods = function(klass, source, parent) {
1547        for (var property in source) {
1548
1549          if (property in klass.prototype &&
1550              typeof klass.prototype[property] === 'function' &&
1551              (source[property] + '').indexOf('callSuper') > -1) {
1552
1553            klass.prototype[property] = (function(property) {
1554              return function() {
1555
1556                var superclass = this.constructor.superclass;
1557                this.constructor.superclass = parent;
1558                var returnValue = source[property].apply(this, arguments);
1559                this.constructor.superclass = superclass;
1560
1561                if (property !== 'initialize') {
1562                  return returnValue;
1563                }
1564              };
1565            })(property);
1566          }
1567          else {
1568            klass.prototype[property] = source[property];
1569          }
1570
1571          if (IS_DONTENUM_BUGGY) {
1572            if (source.toString !== Object.prototype.toString) {
1573              klass.prototype.toString = source.toString;
1574            }
1575            if (source.valueOf !== Object.prototype.valueOf) {
1576              klass.prototype.valueOf = source.valueOf;
1577            }
1578          }
1579        }
1580      };
1581
1582  function Subclass() { }
1583
1584  function callSuper(methodName) {
1585    var fn = this.constructor.superclass.prototype[methodName];
1586    return (arguments.length > 1)
1587      ? fn.apply(this, slice.call(arguments, 1))
1588      : fn.call(this);
1589  }
1590
1591  /**
1592   * Helper for creation of "classes".
1593   * @memberOf fabric.util
1594   * @param {Function} [parent] optional "Class" to inherit from
1595   * @param {Object} [properties] Properties shared by all instances of this class
1596   *                  (be careful modifying objects defined here as this would affect all instances)
1597   */
1598  function createClass() {
1599    var parent = null,
1600        properties = slice.call(arguments, 0);
1601
1602    if (typeof properties[0] === 'function') {
1603      parent = properties.shift();
1604    }
1605    function klass() {
1606      this.initialize.apply(this, arguments);
1607    }
1608
1609    klass.superclass = parent;
1610    klass.subclasses = [ ];
1611
1612    if (parent) {
1613      Subclass.prototype = parent.prototype;
1614      klass.prototype = new Subclass();
1615      parent.subclasses.push(klass);
1616    }
1617    for (var i = 0, length = properties.length; i < length; i++) {
1618      addMethods(klass, properties[i], parent);
1619    }
1620    if (!klass.prototype.initialize) {
1621      klass.prototype.initialize = emptyFunction;
1622    }
1623    klass.prototype.constructor = klass;
1624    klass.prototype.callSuper = callSuper;
1625    return klass;
1626  }
1627
1628  fabric.util.createClass = createClass;
1629})();
1630
1631
1632(function () {
1633
1634  var unknown = 'unknown';
1635
1636  /* EVENT HANDLING */
1637
1638  function areHostMethods(object) {
1639    var methodNames = Array.prototype.slice.call(arguments, 1),
1640        t, i, len = methodNames.length;
1641    for (i = 0; i < len; i++) {
1642      t = typeof object[methodNames[i]];
1643      if (!(/^(?:function|object|unknown)$/).test(t)) {
1644        return false;
1645      }
1646    }
1647    return true;
1648  }
1649
1650  /** @ignore */
1651  var getElement,
1652      setElement,
1653      getUniqueId = (function () {
1654        var uid = 0;
1655        return function (element) {
1656          return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++);
1657        };
1658      })();
1659
1660  (function () {
1661    var elements = { };
1662    /** @ignore */
1663    getElement = function (uid) {
1664      return elements[uid];
1665    };
1666    /** @ignore */
1667    setElement = function (uid, element) {
1668      elements[uid] = element;
1669    };
1670  })();
1671
1672  function createListener(uid, handler) {
1673    return {
1674      handler: handler,
1675      wrappedHandler: createWrappedHandler(uid, handler)
1676    };
1677  }
1678
1679  function createWrappedHandler(uid, handler) {
1680    return function (e) {
1681      handler.call(getElement(uid), e || fabric.window.event);
1682    };
1683  }
1684
1685  function createDispatcher(uid, eventName) {
1686    return function (e) {
1687      if (handlers[uid] && handlers[uid][eventName]) {
1688        var handlersForEvent = handlers[uid][eventName];
1689        for (var i = 0, len = handlersForEvent.length; i < len; i++) {
1690          handlersForEvent[i].call(this, e || fabric.window.event);
1691        }
1692      }
1693    };
1694  }
1695
1696  var shouldUseAddListenerRemoveListener = (
1697        areHostMethods(fabric.document.documentElement, 'addEventListener', 'removeEventListener') &&
1698        areHostMethods(fabric.window, 'addEventListener', 'removeEventListener')),
1699
1700      shouldUseAttachEventDetachEvent = (
1701        areHostMethods(fabric.document.documentElement, 'attachEvent', 'detachEvent') &&
1702        areHostMethods(fabric.window, 'attachEvent', 'detachEvent')),
1703
1704      // IE branch
1705      listeners = { },
1706
1707      // DOM L0 branch
1708      handlers = { },
1709
1710      addListener, removeListener;
1711
1712  if (shouldUseAddListenerRemoveListener) {
1713    /** @ignore */
1714    addListener = function (element, eventName, handler) {
1715      element.addEventListener(eventName, handler, false);
1716    };
1717    /** @ignore */
1718    removeListener = function (element, eventName, handler) {
1719      element.removeEventListener(eventName, handler, false);
1720    };
1721  }
1722
1723  else if (shouldUseAttachEventDetachEvent) {
1724    /** @ignore */
1725    addListener = function (element, eventName, handler) {
1726      var uid = getUniqueId(element);
1727      setElement(uid, element);
1728      if (!listeners[uid]) {
1729        listeners[uid] = { };
1730      }
1731      if (!listeners[uid][eventName]) {
1732        listeners[uid][eventName] = [ ];
1733
1734      }
1735      var listener = createListener(uid, handler);
1736      listeners[uid][eventName].push(listener);
1737      element.attachEvent('on' + eventName, listener.wrappedHandler);
1738    };
1739    /** @ignore */
1740    removeListener = function (element, eventName, handler) {
1741      var uid = getUniqueId(element), listener;
1742      if (listeners[uid] && listeners[uid][eventName]) {
1743        for (var i = 0, len = listeners[uid][eventName].length; i < len; i++) {
1744          listener = listeners[uid][eventName][i];
1745          if (listener && listener.handler === handler) {
1746            element.detachEvent('on' + eventName, listener.wrappedHandler);
1747            listeners[uid][eventName][i] = null;
1748          }
1749        }
1750      }
1751    };
1752  }
1753  else {
1754    /** @ignore */
1755    addListener = function (element, eventName, handler) {
1756      var uid = getUniqueId(element);
1757      if (!handlers[uid]) {
1758        handlers[uid] = { };
1759      }
1760      if (!handlers[uid][eventName]) {
1761        handlers[uid][eventName] = [ ];
1762        var existingHandler = element['on' + eventName];
1763        if (existingHandler) {
1764          handlers[uid][eventName].push(existingHandler);
1765        }
1766        element['on' + eventName] = createDispatcher(uid, eventName);
1767      }
1768      handlers[uid][eventName].push(handler);
1769    };
1770    /** @ignore */
1771    removeListener = function (element, eventName, handler) {
1772      var uid = getUniqueId(element);
1773      if (handlers[uid] && handlers[uid][eventName]) {
1774        var handlersForEvent = handlers[uid][eventName];
1775        for (var i = 0, len = handlersForEvent.length; i < len; i++) {
1776          if (handlersForEvent[i] === handler) {
1777            handlersForEvent.splice(i, 1);
1778          }
1779        }
1780      }
1781    };
1782  }
1783
1784  /**
1785   * Adds an event listener to an element
1786   * @function
1787   * @memberOf fabric.util
1788   * @param {HTMLElement} element
1789   * @param {String} eventName
1790   * @param {Function} handler
1791   */
1792  fabric.util.addListener = addListener;
1793
1794  /**
1795   * Removes an event listener from an element
1796   * @function
1797   * @memberOf fabric.util
1798   * @param {HTMLElement} element
1799   * @param {String} eventName
1800   * @param {Function} handler
1801   */
1802  fabric.util.removeListener = removeListener;
1803
1804  /**
1805   * Cross-browser wrapper for getting event's coordinates
1806   * @memberOf fabric.util
1807   * @param {Event} event Event object
1808   * @param {HTMLCanvasElement} upperCanvasEl &lt;canvas> element on which object selection is drawn
1809   */
1810  function getPointer(event, upperCanvasEl) {
1811    event || (event = fabric.window.event);
1812
1813    var element = event.target ||
1814                  (typeof event.srcElement !== unknown ? event.srcElement : null),
1815
1816        scroll = fabric.util.getScrollLeftTop(element, upperCanvasEl);
1817
1818    return {
1819      x: pointerX(event) + scroll.left,
1820      y: pointerY(event) + scroll.top
1821    };
1822  }
1823
1824  var pointerX = function(event) {
1825    // looks like in IE (<9) clientX at certain point (apparently when mouseup fires on VML element)
1826    // is represented as COM object, with all the consequences, like "unknown" type and error on [[Get]]
1827    // need to investigate later
1828    return (typeof event.clientX !== unknown ? event.clientX : 0);
1829  },
1830
1831  pointerY = function(event) {
1832    return (typeof event.clientY !== unknown ? event.clientY : 0);
1833  };
1834
1835  function _getPointer(event, pageProp, clientProp) {
1836    var touchProp = event.type === 'touchend' ? 'changedTouches' : 'touches';
1837
1838    return (event[touchProp] && event[touchProp][0]
1839      ? (event[touchProp][0][pageProp] - (event[touchProp][0][pageProp] - event[touchProp][0][clientProp]))
1840        || event[clientProp]
1841      : event[clientProp]);
1842  }
1843
1844  if (fabric.isTouchSupported) {
1845    pointerX = function(event) {
1846      return _getPointer(event, 'pageX', 'clientX');
1847    };
1848    pointerY = function(event) {
1849      return _getPointer(event, 'pageY', 'clientY');
1850    };
1851  }
1852
1853  fabric.util.getPointer = getPointer;
1854
1855  fabric.util.object.extend(fabric.util, fabric.Observable);
1856
1857})();
1858
1859
1860(function () {
1861
1862  /**
1863   * Cross-browser wrapper for setting element's style
1864   * @memberOf fabric.util
1865   * @param {HTMLElement} element
1866   * @param {Object} styles
1867   * @return {HTMLElement} Element that was passed as a first argument
1868   */
1869  function setStyle(element, styles) {
1870    var elementStyle = element.style;
1871    if (!elementStyle) {
1872      return element;
1873    }
1874    if (typeof styles === 'string') {
1875      element.style.cssText += ';' + styles;
1876      return styles.indexOf('opacity') > -1
1877        ? setOpacity(element, styles.match(/opacity:\s*(\d?\.?\d*)/)[1])
1878        : element;
1879    }
1880    for (var property in styles) {
1881      if (property === 'opacity') {
1882        setOpacity(element, styles[property]);
1883      }
1884      else {
1885        var normalizedProperty = (property === 'float' || property === 'cssFloat')
1886          ? (typeof elementStyle.styleFloat === 'undefined' ? 'cssFloat' : 'styleFloat')
1887          : property;
1888        elementStyle[normalizedProperty] = styles[property];
1889      }
1890    }
1891    return element;
1892  }
1893
1894  var parseEl = fabric.document.createElement('div'),
1895      supportsOpacity = typeof parseEl.style.opacity === 'string',
1896      supportsFilters = typeof parseEl.style.filter === 'string',
1897      reOpacity = /alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/,
1898
1899      /** @ignore */
1900      setOpacity = function (element) { return element; };
1901
1902  if (supportsOpacity) {
1903    /** @ignore */
1904    setOpacity = function(element, value) {
1905      element.style.opacity = value;
1906      return element;
1907    };
1908  }
1909  else if (supportsFilters) {
1910    /** @ignore */
1911    setOpacity = function(element, value) {
1912      var es = element.style;
1913      if (element.currentStyle && !element.currentStyle.hasLayout) {
1914        es.zoom = 1;
1915      }
1916      if (reOpacity.test(es.filter)) {
1917        value = value >= 0.9999 ? '' : ('alpha(opacity=' + (value * 100) + ')');
1918        es.filter = es.filter.replace(reOpacity, value);
1919      }
1920      else {
1921        es.filter += ' alpha(opacity=' + (value * 100) + ')';
1922      }
1923      return element;
1924    };
1925  }
1926
1927  fabric.util.setStyle = setStyle;
1928
1929})();
1930
1931
1932(function() {
1933
1934  var _slice = Array.prototype.slice;
1935
1936  /**
1937   * Takes id and returns an element with that id (if one exists in a document)
1938   * @memberOf fabric.util
1939   * @param {String|HTMLElement} id
1940   * @return {HTMLElement|null}
1941   */
1942  function getById(id) {
1943    return typeof id === 'string' ? fabric.document.getElementById(id) : id;
1944  }
1945
1946  var sliceCanConvertNodelists,
1947      /**
1948       * Converts an array-like object (e.g. arguments or NodeList) to an array
1949       * @memberOf fabric.util
1950       * @param {Object} arrayLike
1951       * @return {Array}
1952       */
1953      toArray = function(arrayLike) {
1954        return _slice.call(arrayLike, 0);
1955      };
1956
1957  try {
1958    sliceCanConvertNodelists = toArray(fabric.document.childNodes) instanceof Array;
1959  }
1960  catch (err) { }
1961
1962  if (!sliceCanConvertNodelists) {
1963    toArray = function(arrayLike) {
1964      var arr = new Array(arrayLike.length), i = arrayLike.length;
1965      while (i--) {
1966        arr[i] = arrayLike[i];
1967      }
1968      return arr;
1969    };
1970  }
1971
1972  /**
1973   * Creates specified element with specified attributes
1974   * @memberOf fabric.util
1975   * @param {String} tagName Type of an element to create
1976   * @param {Object} [attributes] Attributes to set on an element
1977   * @return {HTMLElement} Newly created element
1978   */
1979  function makeElement(tagName, attributes) {
1980    var el = fabric.document.createElement(tagName);
1981    for (var prop in attributes) {
1982      if (prop === 'class') {
1983        el.className = attributes[prop];
1984      }
1985      else if (prop === 'for') {
1986        el.htmlFor = attributes[prop];
1987      }
1988      else {
1989        el.setAttribute(prop, attributes[prop]);
1990      }
1991    }
1992    return el;
1993  }
1994
1995  /**
1996   * Adds class to an element
1997   * @memberOf fabric.util
1998   * @param {HTMLElement} element Element to add class to
1999   * @param {String} className Class to add to an element
2000   */
2001  function addClass(element, className) {
2002    if (element && (' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
2003      element.className += (element.className ? ' ' : '') + className;
2004    }
2005  }
2006
2007  /**
2008   * Wraps element with another element
2009   * @memberOf fabric.util
2010   * @param {HTMLElement} element Element to wrap
2011   * @param {HTMLElement|String} wrapper Element to wrap with
2012   * @param {Object} [attributes] Attributes to set on a wrapper
2013   * @return {HTMLElement} wrapper
2014   */
2015  function wrapElement(element, wrapper, attributes) {
2016    if (typeof wrapper === 'string') {
2017      wrapper = makeElement(wrapper, attributes);
2018    }
2019    if (element.parentNode) {
2020      element.parentNode.replaceChild(wrapper, element);
2021    }
2022    wrapper.appendChild(element);
2023    return wrapper;
2024  }
2025
2026  /**
2027   * Returns element scroll offsets
2028   * @memberOf fabric.util
2029   * @param {HTMLElement} element Element to operate on
2030   * @param {HTMLElement} upperCanvasEl Upper canvas element
2031   * @return {Object} Object with left/top values
2032   */
2033  function getScrollLeftTop(element, upperCanvasEl) {
2034
2035    var firstFixedAncestor,
2036        origElement,
2037        left = 0,
2038        top = 0,
2039        docElement = fabric.document.documentElement,
2040        body = fabric.document.body || {
2041          scrollLeft: 0, scrollTop: 0
2042        };
2043
2044    origElement = element;
2045
2046    while (element && element.parentNode && !firstFixedAncestor) {
2047
2048      element = element.parentNode;
2049
2050      if (element.nodeType === 1 &&
2051          fabric.util.getElementStyle(element, 'position') === 'fixed') {
2052        firstFixedAncestor = element;
2053      }
2054
2055      if (element.nodeType === 1 &&
2056          origElement !== upperCanvasEl &&
2057          fabric.util.getElementStyle(element, 'position') === 'absolute') {
2058        left = 0;
2059        top = 0;
2060      }
2061      else if (element === fabric.document) {
2062        left = body.scrollLeft || docElement.scrollLeft || 0;
2063        top = body.scrollTop ||  docElement.scrollTop || 0;
2064      }
2065      else {
2066        left += element.scrollLeft || 0;
2067        top += element.scrollTop || 0;
2068      }
2069    }
2070
2071    return { left: left, top: top };
2072  }
2073
2074  /**
2075   * Returns offset for a given element
2076   * @function
2077   * @memberOf fabric.util
2078   * @param {HTMLElement} element Element to get offset for
2079   * @return {Object} Object with "left" and "top" properties
2080   */
2081  function getElementOffset(element) {
2082    var docElem,
2083        doc = element && element.ownerDocument,
2084        box = { left: 0, top: 0 },
2085        offset = { left: 0, top: 0 },
2086        scrollLeftTop,
2087        offsetAttributes = {
2088          borderLeftWidth: 'left',
2089          borderTopWidth:  'top',
2090          paddingLeft:     'left',
2091          paddingTop:      'top'
2092        };
2093
2094    if (!doc) {
2095      return { left: 0, top: 0 };
2096    }
2097
2098    for (var attr in offsetAttributes) {
2099      offset[offsetAttributes[attr]] += parseInt(getElementStyle(element, attr), 10) || 0;
2100    }
2101
2102    docElem = doc.documentElement;
2103    if ( typeof element.getBoundingClientRect !== 'undefined' ) {
2104      box = element.getBoundingClientRect();
2105    }
2106
2107    scrollLeftTop = fabric.util.getScrollLeftTop(element, null);
2108
2109    return {
2110      left: box.left + scrollLeftTop.left - (docElem.clientLeft || 0) + offset.left,
2111      top: box.top + scrollLeftTop.top - (docElem.clientTop || 0)  + offset.top
2112    };
2113  }
2114
2115  /**
2116  * Returns style attribute value of a given element
2117  * @memberOf fabric.util
2118  * @param {HTMLElement} element Element to get style attribute for
2119  * @param {String} attr Style attribute to get for element
2120  * @return {String} Style attribute value of the given element.
2121  */
2122  var getElementStyle;
2123  if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
2124    getElementStyle = function(element, attr) {
2125      var style = fabric.document.defaultView.getComputedStyle(element, null);
2126      return style ? style[attr] : undefined;
2127    };
2128  }
2129  else {
2130    getElementStyle = function(element, attr) {
2131      var value = element.style[attr];
2132      if (!value && element.currentStyle) {
2133        value = element.currentStyle[attr];
2134      }
2135      return value;
2136    };
2137  }
2138
2139  (function () {
2140    var style = fabric.document.documentElement.style,
2141        selectProp = 'userSelect' in style
2142          ? 'userSelect'
2143          : 'MozUserSelect' in style
2144            ? 'MozUserSelect'
2145            : 'WebkitUserSelect' in style
2146              ? 'WebkitUserSelect'
2147              : 'KhtmlUserSelect' in style
2148                ? 'KhtmlUserSelect'
2149                : '';
2150
2151    /**
2152     * Makes element unselectable
2153     * @memberOf fabric.util
2154     * @param {HTMLElement} element Element to make unselectable
2155     * @return {HTMLElement} Element that was passed in
2156     */
2157    function makeElementUnselectable(element) {
2158      if (typeof element.onselectstart !== 'undefined') {
2159        element.onselectstart = fabric.util.falseFunction;
2160      }
2161      if (selectProp) {
2162        element.style[selectProp] = 'none';
2163      }
2164      else if (typeof element.unselectable === 'string') {
2165        element.unselectable = 'on';
2166      }
2167      return element;
2168    }
2169
2170    /**
2171     * Makes element selectable
2172     * @memberOf fabric.util
2173     * @param {HTMLElement} element Element to make selectable
2174     * @return {HTMLElement} Element that was passed in
2175     */
2176    function makeElementSelectable(element) {
2177      if (typeof element.onselectstart !== 'undefined') {
2178        element.onselectstart = null;
2179      }
2180      if (selectProp) {
2181        element.style[selectProp] = '';
2182      }
2183      else if (typeof element.unselectable === 'string') {
2184        element.unselectable = '';
2185      }
2186      return element;
2187    }
2188
2189    fabric.util.makeElementUnselectable = makeElementUnselectable;
2190    fabric.util.makeElementSelectable = makeElementSelectable;
2191  })();
2192
2193  (function() {
2194
2195    /**
2196     * Inserts a script element with a given url into a document; invokes callback, when that script is finished loading
2197     * @memberOf fabric.util
2198     * @param {String} url URL of a script to load
2199     * @param {Function} callback Callback to execute when script is finished loading
2200     */
2201    function getScript(url, callback) {
2202      var headEl = fabric.document.getElementsByTagName('head')[0],
2203          scriptEl = fabric.document.createElement('script'),
2204          loading = true;
2205
2206      /** @ignore */
2207      scriptEl.onload = /** @ignore */ scriptEl.onreadystatechange = function(e) {
2208        if (loading) {
2209          if (typeof this.readyState === 'string' &&
2210              this.readyState !== 'loaded' &&
2211              this.readyState !== 'complete') {
2212            return;
2213          }
2214          loading = false;
2215          callback(e || fabric.window.event);
2216          scriptEl = scriptEl.onload = scriptEl.onreadystatechange = null;
2217        }
2218      };
2219      scriptEl.src = url;
2220      headEl.appendChild(scriptEl);
2221      // causes issue in Opera
2222      // headEl.removeChild(scriptEl);
2223    }
2224
2225    fabric.util.getScript = getScript;
2226  })();
2227
2228  fabric.util.getById = getById;
2229  fabric.util.toArray = toArray;
2230  fabric.util.makeElement = makeElement;
2231  fabric.util.addClass = addClass;
2232  fabric.util.wrapElement = wrapElement;
2233  fabric.util.getScrollLeftTop = getScrollLeftTop;
2234  fabric.util.getElementOffset = getElementOffset;
2235  fabric.util.getElementStyle = getElementStyle;
2236
2237})();
2238
2239
2240(function() {
2241
2242  function addParamToUrl(url, param) {
2243    return url + (/\?/.test(url) ? '&' : '?') + param;
2244  }
2245
2246  var makeXHR = (function() {
2247    var factories = [
2248      function() { return new ActiveXObject('Microsoft.XMLHTTP'); },
2249      function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
2250      function() { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); },
2251      function() { return new XMLHttpRequest(); }
2252    ];
2253    for (var i = factories.length; i--; ) {
2254      try {
2255        var req = factories[i]();
2256        if (req) {
2257          return factories[i];
2258        }
2259      }
2260      catch (err) { }
2261    }
2262  })();
2263
2264  function emptyFn() { }
2265
2266  /**
2267   * Cross-browser abstraction for sending XMLHttpRequest
2268   * @memberOf fabric.util
2269   * @param {String} url URL to send XMLHttpRequest to
2270   * @param {Object} [options] Options object
2271   * @param {String} [options.method="GET"]
2272   * @param {Function} options.onComplete Callback to invoke when request is completed
2273   * @return {XMLHttpRequest} request
2274   */
2275  function request(url, options) {
2276
2277    options || (options = { });
2278
2279    var method = options.method ? options.method.toUpperCase() : 'GET',
2280        onComplete = options.onComplete || function() { },
2281        xhr = makeXHR(),
2282        body;
2283
2284    /** @ignore */
2285    xhr.onreadystatechange = function() {
2286      if (xhr.readyState === 4) {
2287        onComplete(xhr);
2288        xhr.onreadystatechange = emptyFn;
2289      }
2290    };
2291
2292    if (method === 'GET') {
2293      body = null;
2294      if (typeof options.parameters === 'string') {
2295        url = addParamToUrl(url, options.parameters);
2296      }
2297    }
2298
2299    xhr.open(method, url, true);
2300
2301    if (method === 'POST' || method === 'PUT') {
2302      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
2303    }
2304
2305    xhr.send(body);
2306    return xhr;
2307  }
2308
2309  fabric.util.request = request;
2310})();
2311
2312
2313/**
2314 * Wrapper around `console.log` (when available)
2315 * @param {Any} [values] Values to log
2316 */
2317fabric.log = function() { };
2318
2319/**
2320 * Wrapper around `console.warn` (when available)
2321 * @param {Any} [values] Values to log as a warning
2322 */
2323fabric.warn = function() { };
2324
2325if (typeof console !== 'undefined') {
2326
2327  ['log', 'warn'].forEach(function(methodName) {
2328
2329    if (typeof console[methodName] !== 'undefined' &&
2330        typeof console[methodName].apply === 'function') {
2331
2332      fabric[methodName] = function() {
2333        return console[methodName].apply(console, arguments);
2334      };
2335    }
2336  });
2337}
2338
2339
2340(function() {
2341
2342  /**
2343   * Changes value from one to another within certain period of time, invoking callbacks as value is being changed.
2344   * @memberOf fabric.util
2345   * @param {Object} [options] Animation options
2346   * @param {Function} [options.onChange] Callback; invoked on every value change
2347   * @param {Function} [options.onComplete] Callback; invoked when value change is completed
2348   * @param {Number} [options.startValue=0] Starting value
2349   * @param {Number} [options.endValue=100] Ending value
2350   * @param {Number} [options.byValue=100] Value to modify the property by
2351   * @param {Function} [options.easing] Easing function
2352   * @param {Number} [options.duration=500] Duration of change (in ms)
2353   */
2354  function animate(options) {
2355
2356    requestAnimFrame(function(timestamp) {
2357      options || (options = { });
2358
2359      var start = timestamp || +new Date(),
2360          duration = options.duration || 500,
2361          finish = start + duration, time,
2362          onChange = options.onChange || function() { },
2363          abort = options.abort || function() { return false; },
2364          easing = options.easing || function(t, b, c, d) {return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;},
2365          startValue = 'startValue' in options ? options.startValue : 0,
2366          endValue = 'endValue' in options ? options.endValue : 100,
2367          byValue = options.byValue || endValue - startValue;
2368
2369      options.onStart && options.onStart();
2370
2371      (function tick(ticktime) {
2372        time = ticktime || +new Date();
2373        var currentTime = time > finish ? duration : (time - start);
2374        if (abort()) {
2375          options.onComplete && options.onComplete();
2376          return;
2377        }
2378        onChange(easing(currentTime, startValue, byValue, duration));
2379        if (time > finish) {
2380          options.onComplete && options.onComplete();
2381          return;
2382        }
2383        requestAnimFrame(tick);
2384      })(start);
2385    });
2386
2387  }
2388
2389  var _requestAnimFrame = fabric.window.requestAnimationFrame       ||
2390                          fabric.window.webkitRequestAnimationFrame ||
2391                          fabric.window.mozRequestAnimationFrame    ||
2392                          fabric.window.oRequestAnimationFrame      ||
2393                          fabric.window.msRequestAnimationFrame     ||
2394                          function(callback) {
2395                            fabric.window.setTimeout(callback, 1000 / 60);
2396                          };
2397  /**
2398    * requestAnimationFrame polyfill based on http://paulirish.com/2011/requestanimationframe-for-smart-animating/
2399    * In order to get a precise start time, `requestAnimFrame` should be called as an entry into the method
2400    * @memberOf fabric.util
2401    * @param {Function} callback Callback to invoke
2402    * @param {DOMElement} element optional Element to associate with animation
2403    */
2404  function requestAnimFrame() {
2405    return _requestAnimFrame.apply(fabric.window, arguments);
2406  }
2407
2408  fabric.util.animate = animate;
2409  fabric.util.requestAnimFrame = requestAnimFrame;
2410
2411})();
2412
2413
2414(function() {
2415
2416  function normalize(a, c, p, s) {
2417    if (a < Math.abs(c)) {
2418      a = c;
2419      s = p / 4;
2420    }
2421    else {
2422      s = p / (2 * Math.PI) * Math.asin(c / a);
2423    }
2424    return { a: a, c: c, p: p, s: s };
2425  }
2426
2427  function elastic(opts, t, d) {
2428    return opts.a *
2429      Math.pow(2, 10 * (t -= 1)) *
2430      Math.sin( (t * d - opts.s) * (2 * Math.PI) / opts.p );
2431  }
2432
2433  /**
2434   * Cubic easing out
2435   * @memberOf fabric.util.ease
2436   */
2437  function easeOutCubic(t, b, c, d) {
2438    return c * ((t = t / d - 1) * t * t + 1) + b;
2439  }
2440
2441  /**
2442   * Cubic easing in and out
2443   * @memberOf fabric.util.ease
2444   */
2445  function easeInOutCubic(t, b, c, d) {
2446    t /= d/2;
2447    if (t < 1) {
2448      return c / 2 * t * t * t + b;
2449    }
2450    return c / 2 * ((t -= 2) * t * t + 2) + b;
2451  }
2452
2453  /**
2454   * Quartic easing in
2455   * @memberOf fabric.util.ease
2456   */
2457  function easeInQuart(t, b, c, d) {
2458    return c * (t /= d) * t * t * t + b;
2459  }
2460
2461  /**
2462   * Quartic easing out
2463   * @memberOf fabric.util.ease
2464   */
2465  function easeOutQuart(t, b, c, d) {
2466    return -c * ((t = t / d - 1) * t * t * t - 1) + b;
2467  }
2468
2469  /**
2470   * Quartic easing in and out
2471   * @memberOf fabric.util.ease
2472   */
2473  function easeInOutQuart(t, b, c, d) {
2474    t /= d / 2;
2475    if (t < 1) {
2476      return c / 2 * t * t * t * t + b;
2477    }
2478    return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
2479  }
2480
2481  /**
2482   * Quintic easing in
2483   * @memberOf fabric.util.ease
2484   */
2485  function easeInQuint(t, b, c, d) {
2486    return c * (t /= d) * t * t * t * t + b;
2487  }
2488
2489  /**
2490   * Quintic easing out
2491   * @memberOf fabric.util.ease
2492   */
2493  function easeOutQuint(t, b, c, d) {
2494    return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
2495  }
2496
2497  /**
2498   * Quintic easing in and out
2499   * @memberOf fabric.util.ease
2500   */
2501  function easeInOutQuint(t, b, c, d) {
2502    t /= d / 2;
2503    if (t < 1) {
2504      return c / 2 * t * t * t * t * t + b;
2505    }
2506    return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
2507  }
2508
2509  /**
2510   * Sinusoidal easing in
2511   * @memberOf fabric.util.ease
2512   */
2513  function easeInSine(t, b, c, d) {
2514    return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
2515  }
2516
2517  /**
2518   * Sinusoidal easing out
2519   * @memberOf fabric.util.ease
2520   */
2521  function easeOutSine(t, b, c, d) {
2522    return c * Math.sin(t / d * (Math.PI / 2)) + b;
2523  }
2524
2525  /**
2526   * Sinusoidal easing in and out
2527   * @memberOf fabric.util.ease
2528   */
2529  function easeInOutSine(t, b, c, d) {
2530    return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
2531  }
2532
2533  /**
2534   * Exponential easing in
2535   * @memberOf fabric.util.ease
2536   */
2537  function easeInExpo(t, b, c, d) {
2538    return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
2539  }
2540
2541  /**
2542   * Exponential easing out
2543   * @memberOf fabric.util.ease
2544   */
2545  function easeOutExpo(t, b, c, d) {
2546    return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
2547  }
2548
2549  /**
2550   * Exponential easing in and out
2551   * @memberOf fabric.util.ease
2552   */
2553  function easeInOutExpo(t, b, c, d) {
2554    if (t === 0) {
2555      return b;
2556    }
2557    if (t === d) {
2558      return b + c;
2559    }
2560    t /= d / 2;
2561    if (t < 1) {
2562      return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
2563    }
2564    return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
2565  }
2566
2567  /**
2568   * Circular easing in
2569   * @memberOf fabric.util.ease
2570   */
2571  function easeInCirc(t, b, c, d) {
2572    return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
2573  }
2574
2575  /**
2576   * Circular easing out
2577   * @memberOf fabric.util.ease
2578   */
2579  function easeOutCirc(t, b, c, d) {
2580    return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
2581  }
2582
2583  /**
2584   * Circular easing in and out
2585   * @memberOf fabric.util.ease
2586   */
2587  function easeInOutCirc(t, b, c, d) {
2588    t /= d / 2;
2589    if (t < 1) {
2590      return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
2591    }
2592    return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
2593  }
2594
2595  /**
2596   * Elastic easing in
2597   * @memberOf fabric.util.ease
2598   */
2599  function easeInElastic(t, b, c, d) {
2600    var s = 1.70158, p = 0, a = c;
2601    if (t === 0) {
2602      return b;
2603    }
2604    t /= d;
2605    if (t === 1) {
2606      return b + c;
2607    }
2608    if (!p) {
2609      p = d * 0.3;
2610    }
2611    var opts = normalize(a, c, p, s);
2612    return -elastic(opts, t, d) + b;
2613  }
2614
2615  /**
2616   * Elastic easing out
2617   * @memberOf fabric.util.ease
2618   */
2619  function easeOutElastic(t, b, c, d) {
2620    var s = 1.70158, p = 0, a = c;
2621    if (t === 0) {
2622      return b;
2623    }
2624    t /= d;
2625    if (t === 1) {
2626      return b + c;
2627    }
2628    if (!p) {
2629      p = d * 0.3;
2630    }
2631    var opts = normalize(a, c, p, s);
2632    return opts.a * Math.pow(2, -10 * t) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) + opts.c + b;
2633  }
2634
2635  /**
2636   * Elastic easing in and out
2637   * @memberOf fabric.util.ease
2638   */
2639  function easeInOutElastic(t, b, c, d) {
2640    var s = 1.70158, p = 0, a = c;
2641    if (t === 0) {
2642      return b;
2643    }
2644    t /= d / 2;
2645    if (t === 2) {
2646      return b + c;
2647    }
2648    if (!p) {
2649      p = d * (0.3 * 1.5);
2650    }
2651    var opts = normalize(a, c, p, s);
2652    if (t < 1) {
2653      return -0.5 * elastic(opts, t, d) + b;
2654    }
2655    return opts.a * Math.pow(2, -10 * (t -= 1)) *
2656      Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) * 0.5 + opts.c + b;
2657  }
2658
2659  /**
2660   * Backwards easing in
2661   * @memberOf fabric.util.ease
2662   */
2663  function easeInBack(t, b, c, d, s) {
2664    if (s === undefined) {
2665      s = 1.70158;
2666    }
2667    return c * (t /= d) * t * ((s + 1) * t - s) + b;
2668  }
2669
2670  /**
2671   * Backwards easing out
2672   * @memberOf fabric.util.ease
2673   */
2674  function easeOutBack(t, b, c, d, s) {
2675    if (s === undefined) {
2676      s = 1.70158;
2677    }
2678    return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
2679  }
2680
2681  /**
2682   * Backwards easing in and out
2683   * @memberOf fabric.util.ease
2684   */
2685  function easeInOutBack(t, b, c, d, s) {
2686    if (s === undefined) {
2687      s = 1.70158;
2688    }
2689    t /= d / 2;
2690    if (t < 1) {
2691      return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
2692    }
2693    return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
2694  }
2695
2696  /**
2697   * Bouncing easing in
2698   * @memberOf fabric.util.ease
2699   */
2700  function easeInBounce(t, b, c, d) {
2701    return c - easeOutBounce (d - t, 0, c, d) + b;
2702  }
2703
2704  /**
2705   * Bouncing easing out
2706   * @memberOf fabric.util.ease
2707   */
2708  function easeOutBounce(t, b, c, d) {
2709    if ((t /= d) < (1 / 2.75)) {
2710      return c * (7.5625 * t * t) + b;
2711    }
2712    else if (t < (2/2.75)) {
2713      return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b;
2714    }
2715    else if (t < (2.5/2.75)) {
2716      return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b;
2717    }
2718    else {
2719      return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b;
2720    }
2721  }
2722
2723  /**
2724   * Bouncing easing in and out
2725   * @memberOf fabric.util.ease
2726   */
2727  function easeInOutBounce(t, b, c, d) {
2728    if (t < d / 2) {
2729      return easeInBounce (t * 2, 0, c, d) * 0.5 + b;
2730    }
2731    return easeOutBounce(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
2732  }
2733
2734  /**
2735   * Easing functions
2736   * See <a href="http://gizma.com/easing/">Easing Equations by Robert Penner</a>
2737   * @namespace fabric.util.ease
2738   */
2739  fabric.util.ease = {
2740
2741    /**
2742     * Quadratic easing in
2743     * @memberOf fabric.util.ease
2744     */
2745    easeInQuad: function(t, b, c, d) {
2746      return c * (t /= d) * t + b;
2747    },
2748
2749    /**
2750     * Quadratic easing out
2751     * @memberOf fabric.util.ease
2752     */
2753    easeOutQuad: function(t, b, c, d) {
2754      return -c * (t /= d) * (t - 2) + b;
2755    },
2756
2757    /**
2758     * Quadratic easing in and out
2759     * @memberOf fabric.util.ease
2760     */
2761    easeInOutQuad: function(t, b, c, d) {
2762      t /= (d / 2);
2763      if (t < 1) {
2764        return c / 2 * t * t + b;
2765      }
2766      return -c / 2 * ((--t) * (t - 2) - 1) + b;
2767    },
2768
2769    /**
2770     * Cubic easing in
2771     * @memberOf fabric.util.ease
2772     */
2773    easeInCubic: function(t, b, c, d) {
2774      return c * (t /= d) * t * t + b;
2775    },
2776
2777    easeOutCubic: easeOutCubic,
2778    easeInOutCubic: easeInOutCubic,
2779    easeInQuart: easeInQuart,
2780    easeOutQuart: easeOutQuart,
2781    easeInOutQuart: easeInOutQuart,
2782    easeInQuint: easeInQuint,
2783    easeOutQuint: easeOutQuint,
2784    easeInOutQuint: easeInOutQuint,
2785    easeInSine: easeInSine,
2786    easeOutSine: easeOutSine,
2787    easeInOutSine: easeInOutSine,
2788    easeInExpo: easeInExpo,
2789    easeOutExpo: easeOutExpo,
2790    easeInOutExpo: easeInOutExpo,
2791    easeInCirc: easeInCirc,
2792    easeOutCirc: easeOutCirc,
2793    easeInOutCirc: easeInOutCirc,
2794    easeInElastic: easeInElastic,
2795    easeOutElastic: easeOutElastic,
2796    easeInOutElastic: easeInOutElastic,
2797    easeInBack: easeInBack,
2798    easeOutBack: easeOutBack,
2799    easeInOutBack: easeInOutBack,
2800    easeInBounce: easeInBounce,
2801    easeOutBounce: easeOutBounce,
2802    easeInOutBounce: easeInOutBounce
2803  };
2804
2805}());
2806
2807
2808(function(global) {
2809
2810  'use strict';
2811
2812  /**
2813   * @name fabric
2814   * @namespace
2815   */
2816
2817  var fabric = global.fabric || (global.fabric = { }),
2818      extend = fabric.util.object.extend,
2819      capitalize = fabric.util.string.capitalize,
2820      clone = fabric.util.object.clone,
2821      toFixed = fabric.util.toFixed,
2822      parseUnit = fabric.util.parseUnit,
2823      multiplyTransformMatrices = fabric.util.multiplyTransformMatrices,
2824
2825      attributesMap = {
2826        cx:                   'left',
2827        x:                    'left',
2828        r:                    'radius',
2829        cy:                   'top',
2830        y:                    'top',
2831        display:              'visible',
2832        visibility:           'visible',
2833        transform:            'transformMatrix',
2834        'fill-opacity':       'fillOpacity',
2835        'fill-rule':          'fillRule',
2836        'font-family':        'fontFamily',
2837        'font-size':          'fontSize',
2838        'font-style':         'fontStyle',
2839        'font-weight':        'fontWeight',
2840        'stroke-dasharray':   'strokeDashArray',
2841        'stroke-linecap':     'strokeLineCap',
2842        'stroke-linejoin':    'strokeLineJoin',
2843        'stroke-miterlimit':  'strokeMiterLimit',
2844        'stroke-opacity':     'strokeOpacity',
2845        'stroke-width':       'strokeWidth',
2846        'text-decoration':    'textDecoration',
2847        'text-anchor':        'originX'
2848      },
2849
2850      colorAttributes = {
2851        stroke: 'strokeOpacity',
2852        fill:   'fillOpacity'
2853      };
2854
2855  fabric.cssRules = { };
2856  fabric.gradientDefs = { };
2857
2858  function normalizeAttr(attr) {
2859    // transform attribute names
2860    if (attr in attributesMap) {
2861      return attributesMap[attr];
2862    }
2863    return attr;
2864  }
2865
2866  function normalizeValue(attr, value, parentAttributes, fontSize) {
2867    var isArray = Object.prototype.toString.call(value) === '[object Array]',
2868        parsed;
2869
2870    if ((attr === 'fill' || attr === 'stroke') && value === 'none') {
2871      value = '';
2872    }
2873    else if (attr === 'strokeDashArray') {
2874      value = value.replace(/,/g, ' ').split(/\s+/).map(function(n) {
2875        return parseFloat(n);
2876      });
2877    }
2878    else if (attr === 'transformMatrix') {
2879      if (parentAttributes && parentAttributes.transformMatrix) {
2880        value = multiplyTransformMatrices(
2881          parentAttributes.transformMatrix, fabric.parseTransformAttribute(value));
2882      }
2883      else {
2884        value = fabric.parseTransformAttribute(value);
2885      }
2886    }
2887    else if (attr === 'visible') {
2888      value = (value === 'none' || value === 'hidden') ? false : true;
2889      // display=none on parent element always takes precedence over child element
2890      if (parentAttributes && parentAttributes.visible === false) {
2891        value = false;
2892      }
2893    }
2894    else if (attr === 'originX' /* text-anchor */) {
2895      value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
2896    }
2897    else {
2898      parsed = isArray ? value.map(parseUnit) : parseUnit(value, fontSize);
2899    }
2900
2901    return (!isArray && isNaN(parsed) ? value : parsed);
2902  }
2903
2904  /**
2905   * @private
2906   * @param {Object} attributes Array of attributes to parse
2907   */
2908  function _setStrokeFillOpacity(attributes) {
2909    for (var attr in colorAttributes) {
2910
2911      if (!attributes[attr] || typeof attributes[colorAttributes[attr]] === 'undefined') {
2912        continue;
2913      }
2914
2915      if (attributes[attr].indexOf('url(') === 0) {
2916        continue;
2917      }
2918
2919      var color = new fabric.Color(attributes[attr]);
2920      attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba();
2921    }
2922    return attributes;
2923  }
2924
2925  /**
2926   * Parses "transform" attribute, returning an array of values
2927   * @static
2928   * @function
2929   * @memberOf fabric
2930   * @param {String} attributeValue String containing attribute value
2931   * @return {Array} Array of 6 elements representing transformation matrix
2932   */
2933  fabric.parseTransformAttribute = (function() {
2934    function rotateMatrix(matrix, args) {
2935      var angle = args[0];
2936
2937      matrix[0] = Math.cos(angle);
2938      matrix[1] = Math.sin(angle);
2939      matrix[2] = -Math.sin(angle);
2940      matrix[3] = Math.cos(angle);
2941    }
2942
2943    function scaleMatrix(matrix, args) {
2944      var multiplierX = args[0],
2945          multiplierY = (args.length === 2) ? args[1] : args[0];
2946
2947      matrix[0] = multiplierX;
2948      matrix[3] = multiplierY;
2949    }
2950
2951    function skewXMatrix(matrix, args) {
2952      matrix[2] = Math.tan(fabric.util.degreesToRadians(args[0]));
2953    }
2954
2955    function skewYMatrix(matrix, args) {
2956      matrix[1] = Math.tan(fabric.util.degreesToRadians(args[0]));
2957    }
2958
2959    function translateMatrix(matrix, args) {
2960      matrix[4] = args[0];
2961      if (args.length === 2) {
2962        matrix[5] = args[1];
2963      }
2964    }
2965
2966    // identity matrix
2967    var iMatrix = [
2968          1, // a
2969          0, // b
2970          0, // c
2971          1, // d
2972          0, // e
2973          0  // f
2974        ],
2975
2976        // == begin transform regexp
2977        number = fabric.reNum,
2978
2979        commaWsp = '(?:\\s+,?\\s*|,\\s*)',
2980
2981        skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))',
2982
2983        skewY = '(?:(skewY)\\s*\\(\\s*(' + number + ')\\s*\\))',
2984
2985        rotate = '(?:(rotate)\\s*\\(\\s*(' + number + ')(?:' +
2986                    commaWsp + '(' + number + ')' +
2987                    commaWsp + '(' + number + '))?\\s*\\))',
2988
2989        scale = '(?:(scale)\\s*\\(\\s*(' + number + ')(?:' +
2990                    commaWsp + '(' + number + '))?\\s*\\))',
2991
2992        translate = '(?:(translate)\\s*\\(\\s*(' + number + ')(?:' +
2993                    commaWsp + '(' + number + '))?\\s*\\))',
2994
2995        matrix = '(?:(matrix)\\s*\\(\\s*' +
2996                  '(' + number + ')' + commaWsp +
2997                  '(' + number + ')' + commaWsp +
2998                  '(' + number + ')' + commaWsp +
2999                  '(' + number + ')' + commaWsp +
3000                  '(' + number + ')' + commaWsp +
3001                  '(' + number + ')' +
3002                  '\\s*\\))',
3003
3004        transform = '(?:' +
3005                    matrix + '|' +
3006                    translate + '|' +
3007                    scale + '|' +
3008                    rotate + '|' +
3009                    skewX + '|' +
3010                    skewY +
3011                    ')',
3012
3013        transforms = '(?:' + transform + '(?:' + commaWsp + transform + ')*' + ')',
3014
3015        transformList = '^\\s*(?:' + transforms + '?)\\s*$',
3016
3017        // http://www.w3.org/TR/SVG/coords.html#TransformAttribute
3018        reTransformList = new RegExp(transformList),
3019        // == end transform regexp
3020
3021        reTransform = new RegExp(transform, 'g');
3022
3023    return function(attributeValue) {
3024
3025      // start with identity matrix
3026      var matrix = iMatrix.concat(),
3027          matrices = [ ];
3028
3029      // return if no argument was given or
3030      // an argument does not match transform attribute regexp
3031      if (!attributeValue || (attributeValue && !reTransformList.test(attributeValue))) {
3032        return matrix;
3033      }
3034
3035      attributeValue.replace(reTransform, function(match) {
3036
3037        var m = new RegExp(transform).exec(match).filter(function (match) {
3038              return (match !== '' && match != null);
3039            }),
3040            operation = m[1],
3041            args = m.slice(2).map(parseFloat);
3042
3043        switch (operation) {
3044          case 'translate':
3045            translateMatrix(matrix, args);
3046            break;
3047          case 'rotate':
3048            args[0] = fabric.util.degreesToRadians(args[0]);
3049            rotateMatrix(matrix, args);
3050            break;
3051          case 'scale':
3052            scaleMatrix(matrix, args);
3053            break;
3054          case 'skewX':
3055            skewXMatrix(matrix, args);
3056            break;
3057          case 'skewY':
3058            skewYMatrix(matrix, args);
3059            break;
3060          case 'matrix':
3061            matrix = args;
3062            break;
3063        }
3064
3065        // snapshot current matrix into matrices array
3066        matrices.push(matrix.concat());
3067        // reset
3068        matrix = iMatrix.concat();
3069      });
3070
3071      var combinedMatrix = matrices[0];
3072      while (matrices.length > 1) {
3073        matrices.shift();
3074        combinedMatrix = fabric.util.multiplyTransformMatrices(combinedMatrix, matrices[0]);
3075      }
3076      return combinedMatrix;
3077    };
3078  })();
3079
3080  /**
3081   * @private
3082   */
3083  function parseStyleString(style, oStyle) {
3084    var attr, value;
3085    style.replace(/;$/, '').split(';').forEach(function (chunk) {
3086      var pair = chunk.split(':');
3087
3088      attr = normalizeAttr(pair[0].trim().toLowerCase());
3089      value = normalizeValue(attr, pair[1].trim());
3090
3091      oStyle[attr] = value;
3092    });
3093  }
3094
3095  /**
3096   * @private
3097   */
3098  function parseStyleObject(style, oStyle) {
3099    var attr, value;
3100    for (var prop in style) {
3101      if (typeof style[prop] === 'undefined') {
3102        continue;
3103      }
3104
3105      attr = normalizeAttr(prop.toLowerCase());
3106      value = normalizeValue(attr, style[prop]);
3107
3108      oStyle[attr] = value;
3109    }
3110  }
3111
3112  /**
3113   * @private
3114   */
3115  function getGlobalStylesForElement(element, svgUid) {
3116    var styles = { };
3117    for (var rule in fabric.cssRules[svgUid]) {
3118      if (elementMatchesRule(element, rule.split(' '))) {
3119        for (var property in fabric.cssRules[svgUid][rule]) {
3120          styles[property] = fabric.cssRules[svgUid][rule][property];
3121        }
3122      }
3123    }
3124    return styles;
3125  }
3126
3127  /**
3128   * @private
3129   */
3130  function elementMatchesRule(element, selectors) {
3131    var firstMatching, parentMatching = true;
3132    //start from rightmost selector.
3133    firstMatching = selectorMatches(element, selectors.pop());
3134    if (firstMatching && selectors.length) {
3135      parentMatching = doesSomeParentMatch(element, selectors);
3136    }
3137    return firstMatching && parentMatching && (selectors.length === 0);
3138  }
3139
3140  function doesSomeParentMatch(element, selectors) {
3141    var selector, parentMatching = true;
3142    while (element.parentNode && element.parentNode.nodeType === 1 && selectors.length) {
3143      if (parentMatching) {
3144        selector = selectors.pop();
3145      }
3146      element = element.parentNode;
3147      parentMatching = selectorMatches(element, selector);
3148    }
3149    return selectors.length === 0;
3150  }
3151  /**
3152   * @private
3153   */
3154  function selectorMatches(element, selector) {
3155    var nodeName = element.nodeName,
3156        classNames = element.getAttribute('class'),
3157        id = element.getAttribute('id'), matcher;
3158    // i check if a selector matches slicing away part from it.
3159    // if i get empty string i should match
3160    matcher = new RegExp('^' + nodeName, 'i');
3161    selector = selector.replace(matcher, '');
3162    if (id && selector.length) {
3163      matcher = new RegExp('#' + id + '(?![a-zA-Z\\-]+)', 'i');
3164      selector = selector.replace(matcher, '');
3165    }
3166    if (classNames && selector.length) {
3167      classNames = classNames.split(' ');
3168      for (var i = classNames.length; i--;) {
3169        matcher = new RegExp('\\.' + classNames[i] + '(?![a-zA-Z\\-]+)', 'i');
3170        selector = selector.replace(matcher, '');
3171      }
3172    }
3173    return selector.length === 0;
3174  }
3175
3176  /**
3177   * @private
3178   */
3179  function parseUseDirectives(doc) {
3180    var nodelist = doc.getElementsByTagName('use');
3181    while (nodelist.length) {
3182      var el = nodelist[0],
3183          xlink = el.getAttribute('xlink:href').substr(1),
3184          x = el.getAttribute('x') || 0,
3185          y = el.getAttribute('y') || 0,
3186          el2 = doc.getElementById(xlink).cloneNode(true),
3187          currentTrans = (el2.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')',
3188          parentNode;
3189
3190      for (var j = 0, attrs = el.attributes, l = attrs.length; j < l; j++) {
3191        var attr = attrs.item(j);
3192        if (attr.nodeName === 'x' || attr.nodeName === 'y' || attr.nodeName === 'xlink:href') {
3193          continue;
3194        }
3195
3196        if (attr.nodeName === 'transform') {
3197          currentTrans = attr.nodeValue + ' ' + currentTrans;
3198        }
3199        else {
3200          el2.setAttribute(attr.nodeName, attr.nodeValue);
3201        }
3202      }
3203
3204      el2.setAttribute('transform', currentTrans);
3205      el2.setAttribute('instantiated_by_use', '1');
3206      el2.removeAttribute('id');
3207      parentNode = el.parentNode;
3208      parentNode.replaceChild(el2, el);
3209    }
3210  }
3211
3212  // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
3213  // matches, e.g.: +14.56e-12, etc.
3214  var reViewBoxAttrValue = new RegExp(
3215    '^' +
3216    '\\s*(' + fabric.reNum + '+)\\s*,?' +
3217    '\\s*(' + fabric.reNum + '+)\\s*,?' +
3218    '\\s*(' + fabric.reNum + '+)\\s*,?' +
3219    '\\s*(' + fabric.reNum + '+)\\s*' +
3220    '$'
3221  );
3222
3223  /**
3224   * Add a <g> element that envelop all child elements and makes the viewbox transformMatrix descend on all elements
3225   */
3226  function addVBTransform(element, widthAttr, heightAttr) {
3227
3228    var viewBoxAttr = element.getAttribute('viewBox'),
3229        scaleX = 1,
3230        scaleY = 1,
3231        minX = 0,
3232        minY = 0,
3233        viewBoxWidth, viewBoxHeight, matrix, el;
3234
3235    if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
3236      minX = -parseFloat(viewBoxAttr[1]),
3237      minY = -parseFloat(viewBoxAttr[2]),
3238      viewBoxWidth = parseFloat(viewBoxAttr[3]),
3239      viewBoxHeight = parseFloat(viewBoxAttr[4]);
3240    }
3241    else {
3242      return;
3243    }
3244    if (widthAttr && widthAttr !== viewBoxWidth) {
3245      scaleX = widthAttr / viewBoxWidth;
3246    }
3247    if (heightAttr && heightAttr !== viewBoxHeight) {
3248      scaleY = heightAttr / viewBoxHeight;
3249    }
3250
3251    // default is to preserve aspect ratio
3252    // preserveAspectRatio attribute to be implemented
3253    scaleY = scaleX = (scaleX > scaleY ? scaleY : scaleX);
3254
3255    if (!(scaleX !== 1 || scaleY !== 1 || minX !== 0 || minY !== 0)) {
3256      return;
3257    }
3258    matrix = ' matrix(' + scaleX +
3259                  ' 0' +
3260                  ' 0 ' +
3261                  scaleY + ' ' +
3262                  (minX * scaleX) + ' ' +
3263                  (minY * scaleY) + ') ';
3264
3265    if (element.tagName === 'svg') {
3266      el = element.ownerDocument.createElement('g');
3267      while (element.firstChild != null) {
3268        el.appendChild(element.firstChild);
3269      }
3270      element.appendChild(el);
3271    }
3272    else {
3273      el = element;
3274      matrix = el.getAttribute('transform') + matrix;
3275    }
3276
3277    el.setAttribute('transform', matrix);
3278  }
3279
3280  /**
3281   * Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback
3282   * @static
3283   * @function
3284   * @memberOf fabric
3285   * @param {SVGDocument} doc SVG document to parse
3286   * @param {Function} callback Callback to call when parsing is finished; It's being passed an array of elements (parsed from a document).
3287   * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
3288   */
3289  fabric.parseSVGDocument = (function() {
3290
3291    var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,
3292        reViewBoxTagNames = /^(symbol|image|marker|pattern|view)$/;
3293
3294    function hasAncestorWithNodeName(element, nodeName) {
3295      while (element && (element = element.parentNode)) {
3296        if (nodeName.test(element.nodeName) && !element.getAttribute('instantiated_by_use')) {
3297          return true;
3298        }
3299      }
3300      return false;
3301    }
3302
3303    return function(doc, callback, reviver) {
3304      if (!doc) {
3305        return;
3306      }
3307
3308      parseUseDirectives(doc);
3309
3310      var startTime = new Date(),
3311          svgUid =  fabric.Object.__uid++,
3312          widthAttr, heightAttr, toBeParsed = false;
3313
3314      if (doc.getAttribute('width') && doc.getAttribute('width') !== '100%') {
3315        widthAttr = parseUnit(doc.getAttribute('width'));
3316      }
3317      if (doc.getAttribute('height') && doc.getAttribute('height') !== '100%') {
3318        heightAttr = parseUnit(doc.getAttribute('height'));
3319      }
3320
3321      if (!widthAttr || !heightAttr) {
3322        var viewBoxAttr = doc.getAttribute('viewBox');
3323        if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
3324          widthAttr = parseFloat(viewBoxAttr[3]),
3325          heightAttr = parseFloat(viewBoxAttr[4]);
3326        }
3327        else {
3328          toBeParsed = true;
3329        }
3330      }
3331
3332      addVBTransform(doc, widthAttr, heightAttr);
3333
3334      var descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
3335
3336      if (descendants.length === 0 && fabric.isLikelyNode) {
3337        // we're likely in node, where "o3-xml" library fails to gEBTN("*")
3338        // https://github.com/ajaxorg/node-o3-xml/issues/21
3339        descendants = doc.selectNodes('//*[name(.)!="svg"]');
3340        var arr = [ ];
3341        for (var i = 0, len = descendants.length; i < len; i++) {
3342          arr[i] = descendants[i];
3343        }
3344        descendants = arr;
3345      }
3346
3347      var elements = descendants.filter(function(el) {
3348        reViewBoxTagNames.test(el.tagName) && addVBTransform(el, 0, 0);
3349        return reAllowedSVGTagNames.test(el.tagName) &&
3350              !hasAncestorWithNodeName(el, /^(?:pattern|defs|symbol)$/); // http://www.w3.org/TR/SVG/struct.html#DefsElement
3351      });
3352
3353      if (!elements || (elements && !elements.length)) {
3354        callback && callback([], {});
3355        return;
3356      }
3357
3358      var options = {
3359        width: widthAttr,
3360        height: heightAttr,
3361        svgUid: svgUid,
3362        toBeParsed: toBeParsed
3363      };
3364
3365      fabric.gradientDefs[svgUid] = fabric.getGradientDefs(doc);
3366      fabric.cssRules[svgUid] = fabric.getCSSRules(doc);
3367      // Precedence of rules:   style > class > attribute
3368      fabric.parseElements(elements, function(instances) {
3369        fabric.documentParsingTime = new Date() - startTime;
3370        if (callback) {
3371          callback(instances, options);
3372        }
3373      }, clone(options), reviver);
3374    };
3375  })();
3376
3377  /**
3378   * Used for caching SVG documents (loaded via `fabric.Canvas#loadSVGFromURL`)
3379   * @namespace
3380   */
3381  var svgCache = {
3382
3383    /**
3384    * @param {String} name
3385    * @param {Function} callback
3386    */
3387    has: function (name, callback) {
3388      callback(false);
3389    },
3390
3391    get: function () {
3392      /* NOOP */
3393    },
3394
3395    set: function () {
3396      /* NOOP */
3397    }
3398  };
3399
3400  /**
3401   * @private
3402   */
3403  function _enlivenCachedObject(cachedObject) {
3404
3405    var objects = cachedObject.objects,
3406        options = cachedObject.options;
3407
3408    objects = objects.map(function (o) {
3409      return fabric[capitalize(o.type)].fromObject(o);
3410    });
3411
3412    return ({ objects: objects, options: options });
3413  }
3414
3415  /**
3416   * @private
3417   */
3418  function _createSVGPattern(markup, canvas, property) {
3419    if (canvas[property] && canvas[property].toSVG) {
3420      markup.push(
3421        '<pattern x="0" y="0" id="', property, 'Pattern" ',
3422          'width="', canvas[property].source.width,
3423          '" height="', canvas[property].source.height,
3424          '" patternUnits="userSpaceOnUse">',
3425        '<image x="0" y="0" ',
3426        'width="', canvas[property].source.width,
3427        '" height="', canvas[property].source.height,
3428        '" xlink:href="', canvas[property].source.src,
3429        '"></image></pattern>'
3430      );
3431    }
3432  }
3433
3434  var reFontDeclaration = new RegExp(
3435    '(normal|italic)?\\s*(normal|small-caps)?\\s*' +
3436    '(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*(' +
3437      fabric.reNum +
3438    '(?:px|cm|mm|em|pt|pc|in)*)(?:\\/(normal|' + fabric.reNum + '))?\\s+(.*)');
3439
3440  extend(fabric, {
3441    /**
3442     * Parses a short font declaration, building adding its properties to a style object
3443     * @static
3444     * @function
3445     * @memberOf fabric
3446     * @param {String} value font declaration
3447     * @param {Object} oStyle definition
3448     */
3449    parseFontDeclaration: function(value, oStyle) {
3450      var match = value.match(reFontDeclaration);
3451
3452      if (!match) {
3453        return;
3454      }
3455      var fontStyle = match[1],
3456          // font variant is not used
3457          // fontVariant = match[2],
3458          fontWeight = match[3],
3459          fontSize = match[4],
3460          lineHeight = match[5],
3461          fontFamily = match[6];
3462
3463      if (fontStyle) {
3464        oStyle.fontStyle = fontStyle;
3465      }
3466      if (fontWeight) {
3467        oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
3468      }
3469      if (fontSize) {
3470        oStyle.fontSize = parseUnit(fontSize);
3471      }
3472      if (fontFamily) {
3473        oStyle.fontFamily = fontFamily;
3474      }
3475      if (lineHeight) {
3476        oStyle.lineHeight = lineHeight === 'normal' ? 1 : lineHeight;
3477      }
3478    },
3479
3480    /**
3481     * Parses an SVG document, returning all of the gradient declarations found in it
3482     * @static
3483     * @function
3484     * @memberOf fabric
3485     * @param {SVGDocument} doc SVG document to parse
3486     * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element
3487     */
3488    getGradientDefs: function(doc) {
3489      var linearGradientEls = doc.getElementsByTagName('linearGradient'),
3490          radialGradientEls = doc.getElementsByTagName('radialGradient'),
3491          el, i, j = 0, id, xlink, elList = [ ],
3492          gradientDefs = { }, idsToXlinkMap = { };
3493
3494      elList.length = linearGradientEls.length + radialGradientEls.length;
3495      i = linearGradientEls.length;
3496      while (i--) {
3497        elList[j++] = linearGradientEls[i];
3498      }
3499      i = radialGradientEls.length;
3500      while (i--) {
3501        elList[j++] = radialGradientEls[i];
3502      }
3503
3504      while (j--) {
3505        el = elList[j];
3506        xlink = el.getAttribute('xlink:href');
3507        id = el.getAttribute('id');
3508        if (xlink) {
3509          idsToXlinkMap[id] = xlink.substr(1);
3510        }
3511        gradientDefs[id] = el;
3512      }
3513
3514      for (id in idsToXlinkMap) {
3515        var el2 = gradientDefs[idsToXlinkMap[id]].cloneNode(true);
3516        el = gradientDefs[id];
3517        while (el2.firstChild) {
3518          el.appendChild(el2.firstChild);
3519        }
3520      }
3521      return gradientDefs;
3522    },
3523
3524    /**
3525     * Returns an object of attributes' name/value, given element and an array of attribute names;
3526     * Parses parent "g" nodes recursively upwards.
3527     * @static
3528     * @memberOf fabric
3529     * @param {DOMElement} element Element to parse
3530     * @param {Array} attributes Array of attributes to parse
3531     * @return {Object} object containing parsed attributes' names/values
3532     */
3533    parseAttributes: function(element, attributes, svgUid) {
3534
3535      if (!element) {
3536        return;
3537      }
3538
3539      var value,
3540          parentAttributes = { },
3541          fontSize;
3542
3543      if (typeof svgUid === 'undefined') {
3544        svgUid = element.getAttribute('svgUid');
3545      }
3546      // if there's a parent container (`g` or `a` or `symbol` node), parse its attributes recursively upwards
3547      if (element.parentNode && /^symbol|[g|a]$/i.test(element.parentNode.nodeName)) {
3548        parentAttributes = fabric.parseAttributes(element.parentNode, attributes, svgUid);
3549      }
3550      fontSize = (parentAttributes && parentAttributes.fontSize ) ||
3551                 element.getAttribute('font-size') || fabric.Text.DEFAULT_SVG_FONT_SIZE;
3552
3553      var ownAttributes = attributes.reduce(function(memo, attr) {
3554        value = element.getAttribute(attr);
3555        if (value) {
3556          attr = normalizeAttr(attr);
3557          value = normalizeValue(attr, value, parentAttributes, fontSize);
3558
3559          memo[attr] = value;
3560        }
3561        return memo;
3562      }, { });
3563
3564      // add values parsed from style, which take precedence over attributes
3565      // (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes)
3566      ownAttributes = extend(ownAttributes,
3567        extend(getGlobalStylesForElement(element, svgUid), fabric.parseStyleAttribute(element)));
3568      if (ownAttributes.font) {
3569        fabric.parseFontDeclaration(ownAttributes.font, ownAttributes);
3570      }
3571      return _setStrokeFillOpacity(extend(parentAttributes, ownAttributes));
3572    },
3573
3574    /**
3575     * Transforms an array of svg elements to corresponding fabric.* instances
3576     * @static
3577     * @memberOf fabric
3578     * @param {Array} elements Array of elements to parse
3579     * @param {Function} callback Being passed an array of fabric instances (transformed from SVG elements)
3580     * @param {Object} [options] Options object
3581     * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
3582     */
3583    parseElements: function(elements, callback, options, reviver) {
3584      new fabric.ElementsParser(elements, callback, options, reviver).parse();
3585    },
3586
3587    /**
3588     * Parses "style" attribute, retuning an object with values
3589     * @static
3590     * @memberOf fabric
3591     * @param {SVGElement} element Element to parse
3592     * @return {Object} Objects with values parsed from style attribute of an element
3593     */
3594    parseStyleAttribute: function(element) {
3595      var oStyle = { },
3596          style = element.getAttribute('style');
3597
3598      if (!style) {
3599        return oStyle;
3600      }
3601
3602      if (typeof style === 'string') {
3603        parseStyleString(style, oStyle);
3604      }
3605      else {
3606        parseStyleObject(style, oStyle);
3607      }
3608
3609      return oStyle;
3610    },
3611
3612    /**
3613     * Parses "points" attribute, returning an array of values
3614     * @static
3615     * @memberOf fabric
3616     * @param {String} points points attribute string
3617     * @return {Array} array of points
3618     */
3619    parsePointsAttribute: function(points) {
3620
3621      // points attribute is required and must not be empty
3622      if (!points) {
3623        return null;
3624      }
3625
3626      // replace commas with whitespace and remove bookending whitespace
3627      points = points.replace(/,/g, ' ').trim();
3628
3629      points = points.split(/\s+/);
3630      var parsedPoints = [ ], i, len;
3631
3632      i = 0;
3633      len = points.length;
3634      for (; i < len; i+=2) {
3635        parsedPoints.push({
3636          x: parseFloat(points[i]),
3637          y: parseFloat(points[i + 1])
3638        });
3639      }
3640
3641      // odd number of points is an error
3642      // if (parsedPoints.length % 2 !== 0) {
3643      //   return null;
3644      // }
3645
3646      return parsedPoints;
3647    },
3648
3649    /**
3650     * Returns CSS rules for a given SVG document
3651     * @static
3652     * @function
3653     * @memberOf fabric
3654     * @param {SVGDocument} doc SVG document to parse
3655     * @return {Object} CSS rules of this document
3656     */
3657    getCSSRules: function(doc) {
3658      var styles = doc.getElementsByTagName('style'),
3659          allRules = { }, rules;
3660
3661      // very crude parsing of style contents
3662      for (var i = 0, len = styles.length; i < len; i++) {
3663        var styleContents = styles[i].textContent;
3664
3665        // remove comments
3666        styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, '');
3667        if (styleContents.trim() === '') {
3668          continue;
3669        }
3670        rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g);
3671        rules = rules.map(function(rule) { return rule.trim(); });
3672
3673        rules.forEach(function(rule) {
3674
3675          var match = rule.match(/([\s\S]*?)\s*\{([^}]*)\}/),
3676          ruleObj = { }, declaration = match[2].trim(),
3677          propertyValuePairs = declaration.replace(/;$/, '').split(/\s*;\s*/);
3678
3679          for (var i = 0, len = propertyValuePairs.length; i < len; i++) {
3680            var pair = propertyValuePairs[i].split(/\s*:\s*/),
3681                property = normalizeAttr(pair[0]),
3682                value = normalizeValue(property, pair[1], pair[0]);
3683            ruleObj[property] = value;
3684          }
3685          rule = match[1];
3686          rule.split(',').forEach(function(_rule) {
3687            _rule = _rule.replace(/^svg/i, '').trim();
3688            if (_rule === '') {
3689              return;
3690            }
3691            allRules[_rule] = fabric.util.object.clone(ruleObj);
3692          });
3693        });
3694      }
3695      return allRules;
3696    },
3697
3698    /**
3699     * Takes url corresponding to an SVG document, and parses it into a set of fabric objects. Note that SVG is fetched via XMLHttpRequest, so it needs to conform to SOP (Same Origin Policy)
3700     * @memberof fabric
3701     * @param {String} url
3702     * @param {Function} callback
3703     * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
3704     */
3705    loadSVGFromURL: function(url, callback, reviver) {
3706
3707      url = url.replace(/^\n\s*/, '').trim();
3708      svgCache.has(url, function (hasUrl) {
3709        if (hasUrl) {
3710          svgCache.get(url, function (value) {
3711            var enlivedRecord = _enlivenCachedObject(value);
3712            callback(enlivedRecord.objects, enlivedRecord.options);
3713          });
3714        }
3715        else {
3716          new fabric.util.request(url, {
3717            method: 'get',
3718            onComplete: onComplete
3719          });
3720        }
3721      });
3722
3723      function onComplete(r) {
3724
3725        var xml = r.responseXML;
3726        if (xml && !xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
3727          xml = new ActiveXObject('Microsoft.XMLDOM');
3728          xml.async = 'false';
3729          //IE chokes on DOCTYPE
3730          xml.loadXML(r.responseText.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i, ''));
3731        }
3732        if (!xml || !xml.documentElement) {
3733          return;
3734        }
3735
3736        fabric.parseSVGDocument(xml.documentElement, function (results, options) {
3737          svgCache.set(url, {
3738            objects: fabric.util.array.invoke(results, 'toObject'),
3739            options: options
3740          });
3741          callback(results, options);
3742        }, reviver);
3743      }
3744    },
3745
3746    /**
3747     * Takes string corresponding to an SVG document, and parses it into a set of fabric objects
3748     * @memberof fabric
3749     * @param {String} string
3750     * @param {Function} callback
3751     * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
3752     */
3753    loadSVGFromString: function(string, callback, reviver) {
3754      string = string.trim();
3755      var doc;
3756      if (typeof DOMParser !== 'undefined') {
3757        var parser = new DOMParser();
3758        if (parser && parser.parseFromString) {
3759          doc = parser.parseFromString(string, 'text/xml');
3760        }
3761      }
3762      else if (fabric.window.ActiveXObject) {
3763        doc = new ActiveXObject('Microsoft.XMLDOM');
3764        doc.async = 'false';
3765        // IE chokes on DOCTYPE
3766        doc.loadXML(string.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i, ''));
3767      }
3768
3769      fabric.parseSVGDocument(doc.documentElement, function (results, options) {
3770        callback(results, options);
3771      }, reviver);
3772    },
3773
3774    /**
3775     * Creates markup containing SVG font faces
3776     * @param {Array} objects Array of fabric objects
3777     * @return {String}
3778     */
3779    createSVGFontFacesMarkup: function(objects) {
3780      var markup = '';
3781
3782      for (var i = 0, len = objects.length; i < len; i++) {
3783        if (objects[i].type !== 'text' || !objects[i].path) {
3784          continue;
3785        }
3786
3787        markup += [
3788          //jscs:disable validateIndentation
3789          '@font-face {',
3790            'font-family: ', objects[i].fontFamily, '; ',
3791            'src: url(\'', objects[i].path, '\')',
3792          '}'
3793          //jscs:enable validateIndentation
3794        ].join('');
3795      }
3796
3797      if (markup) {
3798        markup = [
3799          //jscs:disable validateIndentation
3800          '<style type="text/css">',
3801            '<![CDATA[',
3802              markup,
3803            ']]>',
3804          '</style>'
3805          //jscs:enable validateIndentation
3806        ].join('');
3807      }
3808
3809      return markup;
3810    },
3811
3812    /**
3813     * Creates markup containing SVG referenced elements like patterns, gradients etc.
3814     * @param {fabric.Canvas} canvas instance of fabric.Canvas
3815     * @return {String}
3816     */
3817    createSVGRefElementsMarkup: function(canvas) {
3818      var markup = [ ];
3819
3820      _createSVGPattern(markup, canvas, 'backgroundColor');
3821      _createSVGPattern(markup, canvas, 'overlayColor');
3822
3823      return markup.join('');
3824    }
3825  });
3826
3827})(typeof exports !== 'undefined' ? exports : this);
3828
3829
3830fabric.ElementsParser = function(elements, callback, options, reviver) {
3831  this.elements = elements;
3832  this.callback = callback;
3833  this.options = options;
3834  this.reviver = reviver;
3835  this.svgUid = (options && options.svgUid) || 0;
3836};
3837
3838fabric.ElementsParser.prototype.parse = function() {
3839  this.instances = new Array(this.elements.length);
3840  this.numElements = this.elements.length;
3841
3842  this.createObjects();
3843};
3844
3845fabric.ElementsParser.prototype.createObjects = function() {
3846  for (var i = 0, len = this.elements.length; i < len; i++) {
3847    this.elements[i].setAttribute('svgUid', this.svgUid);
3848    (function(_this, i) {
3849      setTimeout(function() {
3850        _this.createObject(_this.elements[i], i);
3851      }, 0);
3852    })(this, i);
3853  }
3854};
3855
3856fabric.ElementsParser.prototype.createObject = function(el, index) {
3857  var klass = fabric[fabric.util.string.capitalize(el.tagName)];
3858  if (klass && klass.fromElement) {
3859    try {
3860      this._createObject(klass, el, index);
3861    }
3862    catch (err) {
3863      fabric.log(err);
3864    }
3865  }
3866  else {
3867    this.checkIfDone();
3868  }
3869};
3870
3871fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
3872  if (klass.async) {
3873    klass.fromElement(el, this.createCallback(index, el), this.options);
3874  }
3875  else {
3876    var obj = klass.fromElement(el, this.options);
3877    this.resolveGradient(obj, 'fill');
3878    this.resolveGradient(obj, 'stroke');
3879    this.reviver && this.reviver(el, obj);
3880    this.instances[index] = obj;
3881    this.checkIfDone();
3882  }
3883};
3884
3885fabric.ElementsParser.prototype.createCallback = function(index, el) {
3886  var _this = this;
3887  return function(obj) {
3888    _this.resolveGradient(obj, 'fill');
3889    _this.resolveGradient(obj, 'stroke');
3890    _this.reviver && _this.reviver(el, obj);
3891    _this.instances[index] = obj;
3892    _this.checkIfDone();
3893  };
3894};
3895
3896fabric.ElementsParser.prototype.resolveGradient = function(obj, property) {
3897
3898  var instanceFillValue = obj.get(property);
3899  if (!(/^url\(/).test(instanceFillValue)) {
3900    return;
3901  }
3902  var gradientId = instanceFillValue.slice(5, instanceFillValue.length - 1);
3903  if (fabric.gradientDefs[this.svgUid][gradientId]) {
3904    obj.set(property,
3905      fabric.Gradient.fromElement(fabric.gradientDefs[this.svgUid][gradientId], obj));
3906  }
3907};
3908
3909fabric.ElementsParser.prototype.checkIfDone = function() {
3910  if (--this.numElements === 0) {
3911    this.instances = this.instances.filter(function(el) {
3912      return el != null;
3913    });
3914    this.callback(this.instances);
3915  }
3916};
3917
3918
3919(function(global) {
3920
3921  'use strict';
3922
3923  /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
3924
3925  var fabric = global.fabric || (global.fabric = { });
3926
3927  if (fabric.Point) {
3928    fabric.warn('fabric.Point is already defined');
3929    return;
3930  }
3931
3932  fabric.Point = Point;
3933
3934  /**
3935   * Point class
3936   * @class fabric.Point
3937   * @memberOf fabric
3938   * @constructor
3939   * @param {Number} x
3940   * @param {Number} y
3941   * @return {fabric.Point} thisArg
3942   */
3943  function Point(x, y) {
3944    this.x = x;
3945    this.y = y;
3946  }
3947
3948  Point.prototype = /** @lends fabric.Point.prototype */ {
3949
3950    constructor: Point,
3951
3952    /**
3953     * Adds another point to this one and returns another one
3954     * @param {fabric.Point} that
3955     * @return {fabric.Point} new Point instance with added values
3956     */
3957    add: function (that) {
3958      return new Point(this.x + that.x, this.y + that.y);
3959    },
3960
3961    /**
3962     * Adds another point to this one
3963     * @param {fabric.Point} that
3964     * @return {fabric.Point} thisArg
3965     */
3966    addEquals: function (that) {
3967      this.x += that.x;
3968      this.y += that.y;
3969      return this;
3970    },
3971
3972    /**
3973     * Adds value to this point and returns a new one
3974     * @param {Number} scalar
3975     * @return {fabric.Point} new Point with added value
3976     */
3977    scalarAdd: function (scalar) {
3978      return new Point(this.x + scalar, this.y + scalar);
3979    },
3980
3981    /**
3982     * Adds value to this point
3983     * @param {Number} scalar
3984     * @return {fabric.Point} thisArg
3985     */
3986    scalarAddEquals: function (scalar) {
3987      this.x += scalar;
3988      this.y += scalar;
3989      return this;
3990    },
3991
3992    /**
3993     * Subtracts another point from this point and returns a new one
3994     * @param {fabric.Point} that
3995     * @return {fabric.Point} new Point object with subtracted values
3996     */
3997    subtract: function (that) {
3998      return new Point(this.x - that.x, this.y - that.y);
3999    },
4000
4001    /**
4002     * Subtracts another point from this point
4003     * @param {fabric.Point} that
4004     * @return {fabric.Point} thisArg
4005     */
4006    subtractEquals: function (that) {
4007      this.x -= that.x;
4008      this.y -= that.y;
4009      return this;
4010    },
4011
4012    /**
4013     * Subtracts value from this point and returns a new one
4014     * @param {Number} scalar
4015     * @return {fabric.Point}
4016     */
4017    scalarSubtract: function (scalar) {
4018      return new Point(this.x - scalar, this.y - scalar);
4019    },
4020
4021    /**
4022     * Subtracts value from this point
4023     * @param {Number} scalar
4024     * @return {fabric.Point} thisArg
4025     */
4026    scalarSubtractEquals: function (scalar) {
4027      this.x -= scalar;
4028      this.y -= scalar;
4029      return this;
4030    },
4031
4032    /**
4033     * Miltiplies this point by a value and returns a new one
4034     * @param {Number} scalar
4035     * @return {fabric.Point}
4036     */
4037    multiply: function (scalar) {
4038      return new Point(this.x * scalar, this.y * scalar);
4039    },
4040
4041    /**
4042     * Miltiplies this point by a value
4043     * @param {Number} scalar
4044     * @return {fabric.Point} thisArg
4045     */
4046    multiplyEquals: function (scalar) {
4047      this.x *= scalar;
4048      this.y *= scalar;
4049      return this;
4050    },
4051
4052    /**
4053     * Divides this point by a value and returns a new one
4054     * @param {Number} scalar
4055     * @return {fabric.Point}
4056     */
4057    divide: function (scalar) {
4058      return new Point(this.x / scalar, this.y / scalar);
4059    },
4060
4061    /**
4062     * Divides this point by a value
4063     * @param {Number} scalar
4064     * @return {fabric.Point} thisArg
4065     */
4066    divideEquals: function (scalar) {
4067      this.x /= scalar;
4068      this.y /= scalar;
4069      return this;
4070    },
4071
4072    /**
4073     * Returns true if this point is equal to another one
4074     * @param {fabric.Point} that
4075     * @return {Boolean}
4076     */
4077    eq: function (that) {
4078      return (this.x === that.x && this.y === that.y);
4079    },
4080
4081    /**
4082     * Returns true if this point is less than another one
4083     * @param {fabric.Point} that
4084     * @return {Boolean}
4085     */
4086    lt: function (that) {
4087      return (this.x < that.x && this.y < that.y);
4088    },
4089
4090    /**
4091     * Returns true if this point is less than or equal to another one
4092     * @param {fabric.Point} that
4093     * @return {Boolean}
4094     */
4095    lte: function (that) {
4096      return (this.x <= that.x && this.y <= that.y);
4097    },
4098
4099    /**
4100
4101     * Returns true if this point is greater another one
4102     * @param {fabric.Point} that
4103     * @return {Boolean}
4104     */
4105    gt: function (that) {
4106      return (this.x > that.x && this.y > that.y);
4107    },
4108
4109    /**
4110     * Returns true if this point is greater than or equal to another one
4111     * @param {fabric.Point} that
4112     * @return {Boolean}
4113     */
4114    gte: function (that) {
4115      return (this.x >= that.x && this.y >= that.y);
4116    },
4117
4118    /**
4119     * Returns new point which is the result of linear interpolation with this one and another one
4120     * @param {fabric.Point} that
4121     * @param {Number} t
4122     * @return {fabric.Point}
4123     */
4124    lerp: function (that, t) {
4125      return new Point(this.x + (that.x - this.x) * t, this.y + (that.y - this.y) * t);
4126    },
4127
4128    /**
4129     * Returns distance from this point and another one
4130     * @param {fabric.Point} that
4131     * @return {Number}