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}
4132     */
4133    distanceFrom: function (that) {
4134      var dx = this.x - that.x,
4135          dy = this.y - that.y;
4136      return Math.sqrt(dx * dx + dy * dy);
4137    },
4138
4139    /**
4140     * Returns the point between this point and another one
4141     * @param {fabric.Point} that
4142     * @return {fabric.Point}
4143     */
4144    midPointFrom: function (that) {
4145      return new Point(this.x + (that.x - this.x)/2, this.y + (that.y - this.y)/2);
4146    },
4147
4148    /**
4149     * Returns a new point which is the min of this and another one
4150     * @param {fabric.Point} that
4151     * @return {fabric.Point}
4152     */
4153    min: function (that) {
4154      return new Point(Math.min(this.x, that.x), Math.min(this.y, that.y));
4155    },
4156
4157    /**
4158     * Returns a new point which is the max of this and another one
4159     * @param {fabric.Point} that
4160     * @return {fabric.Point}
4161     */
4162    max: function (that) {
4163      return new Point(Math.max(this.x, that.x), Math.max(this.y, that.y));
4164    },
4165
4166    /**
4167     * Returns string representation of this point
4168     * @return {String}
4169     */
4170    toString: function () {
4171      return this.x + ',' + this.y;
4172    },
4173
4174    /**
4175     * Sets x/y of this point
4176     * @param {Number} x
4177     * @param {Number} y
4178     */
4179    setXY: function (x, y) {
4180      this.x = x;
4181      this.y = y;
4182    },
4183
4184    /**
4185     * Sets x/y of this point from another point
4186     * @param {fabric.Point} that
4187     */
4188    setFromPoint: function (that) {
4189      this.x = that.x;
4190      this.y = that.y;
4191    },
4192
4193    /**
4194     * Swaps x/y of this point and another point
4195     * @param {fabric.Point} that
4196     */
4197    swap: function (that) {
4198      var x = this.x,
4199          y = this.y;
4200      this.x = that.x;
4201      this.y = that.y;
4202      that.x = x;
4203      that.y = y;
4204    }
4205  };
4206
4207})(typeof exports !== 'undefined' ? exports : this);
4208
4209
4210(function(global) {
4211
4212  'use strict';
4213
4214  /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
4215  var fabric = global.fabric || (global.fabric = { });
4216
4217  if (fabric.Intersection) {
4218    fabric.warn('fabric.Intersection is already defined');
4219    return;
4220  }
4221
4222  /**
4223   * Intersection class
4224   * @class fabric.Intersection
4225   * @memberOf fabric
4226   * @constructor
4227   */
4228  function Intersection(status) {
4229    this.status = status;
4230    this.points = [];
4231  }
4232
4233  fabric.Intersection = Intersection;
4234
4235  fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
4236
4237    /**
4238     * Appends a point to intersection
4239     * @param {fabric.Point} point
4240     */
4241    appendPoint: function (point) {
4242      this.points.push(point);
4243    },
4244
4245    /**
4246     * Appends points to intersection
4247     * @param {Array} points
4248     */
4249    appendPoints: function (points) {
4250      this.points = this.points.concat(points);
4251    }
4252  };
4253
4254  /**
4255   * Checks if one line intersects another
4256   * @static
4257   * @param {fabric.Point} a1
4258   * @param {fabric.Point} a2
4259   * @param {fabric.Point} b1
4260   * @param {fabric.Point} b2
4261   * @return {fabric.Intersection}
4262   */
4263  fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
4264    var result,
4265        uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
4266        ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
4267        uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
4268    if (uB !== 0) {
4269      var ua = uaT / uB,
4270          ub = ubT / uB;
4271      if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
4272        result = new Intersection('Intersection');
4273        result.points.push(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
4274      }
4275      else {
4276        result = new Intersection();
4277      }
4278    }
4279    else {
4280      if (uaT === 0 || ubT === 0) {
4281        result = new Intersection('Coincident');
4282      }
4283      else {
4284        result = new Intersection('Parallel');
4285      }
4286    }
4287    return result;
4288  };
4289
4290  /**
4291   * Checks if line intersects polygon
4292   * @static
4293   * @param {fabric.Point} a1
4294   * @param {fabric.Point} a2
4295   * @param {Array} points
4296   * @return {fabric.Intersection}
4297   */
4298  fabric.Intersection.intersectLinePolygon = function(a1, a2, points) {
4299    var result = new Intersection(),
4300        length = points.length;
4301
4302    for (var i = 0; i < length; i++) {
4303      var b1 = points[i],
4304          b2 = points[(i + 1) % length],
4305          inter = Intersection.intersectLineLine(a1, a2, b1, b2);
4306
4307      result.appendPoints(inter.points);
4308    }
4309    if (result.points.length > 0) {
4310      result.status = 'Intersection';
4311    }
4312    return result;
4313  };
4314
4315  /**
4316   * Checks if polygon intersects another polygon
4317   * @static
4318   * @param {Array} points1
4319   * @param {Array} points2
4320   * @return {fabric.Intersection}
4321   */
4322  fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
4323    var result = new Intersection(),
4324        length = points1.length;
4325
4326    for (var i = 0; i < length; i++) {
4327      var a1 = points1[i],
4328          a2 = points1[(i + 1) % length],
4329          inter = Intersection.intersectLinePolygon(a1, a2, points2);
4330
4331      result.appendPoints(inter.points);
4332    }
4333    if (result.points.length > 0) {
4334      result.status = 'Intersection';
4335    }
4336    return result;
4337  };
4338
4339  /**
4340   * Checks if polygon intersects rectangle
4341   * @static
4342   * @param {Array} points
4343   * @param {Number} r1
4344   * @param {Number} r2
4345   * @return {fabric.Intersection}
4346   */
4347  fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
4348    var min = r1.min(r2),
4349        max = r1.max(r2),
4350        topRight = new fabric.Point(max.x, min.y),
4351        bottomLeft = new fabric.Point(min.x, max.y),
4352        inter1 = Intersection.intersectLinePolygon(min, topRight, points),
4353        inter2 = Intersection.intersectLinePolygon(topRight, max, points),
4354        inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
4355        inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
4356        result = new Intersection();
4357
4358    result.appendPoints(inter1.points);
4359    result.appendPoints(inter2.points);
4360    result.appendPoints(inter3.points);
4361    result.appendPoints(inter4.points);
4362
4363    if (result.points.length > 0) {
4364      result.status = 'Intersection';
4365    }
4366    return result;
4367  };
4368
4369})(typeof exports !== 'undefined' ? exports : this);
4370
4371
4372(function(global) {
4373
4374  'use strict';
4375
4376  var fabric = global.fabric || (global.fabric = { });
4377
4378  if (fabric.Color) {
4379    fabric.warn('fabric.Color is already defined.');
4380    return;
4381  }
4382
4383  /**
4384   * Color class
4385   * The purpose of {@link fabric.Color} is to abstract and encapsulate common color operations;
4386   * {@link fabric.Color} is a constructor and creates instances of {@link fabric.Color} objects.
4387   *
4388   * @class fabric.Color
4389   * @param {String} color optional in hex or rgb(a) format
4390   * @return {fabric.Color} thisArg
4391   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#colors}
4392   */
4393  function Color(color) {
4394    if (!color) {
4395      this.setSource([0, 0, 0, 1]);
4396    }
4397    else {
4398      this._tryParsingColor(color);
4399    }
4400  }
4401
4402  fabric.Color = Color;
4403
4404  fabric.Color.prototype = /** @lends fabric.Color.prototype */ {
4405
4406    /**
4407     * @private
4408     * @param {String|Array} color Color value to parse
4409     */
4410    _tryParsingColor: function(color) {
4411      var source;
4412
4413      if (color in Color.colorNameMap) {
4414        color = Color.colorNameMap[color];
4415      }
4416
4417      if (color === 'transparent') {
4418        this.setSource([255, 255, 255, 0]);
4419        return;
4420      }
4421
4422      source = Color.sourceFromHex(color);
4423
4424      if (!source) {
4425        source = Color.sourceFromRgb(color);
4426      }
4427      if (!source) {
4428        source = Color.sourceFromHsl(color);
4429      }
4430      if (source) {
4431        this.setSource(source);
4432      }
4433    },
4434
4435    /**
4436     * Adapted from <a href="https://rawgithub.com/mjijackson/mjijackson.github.com/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.html">https://github.com/mjijackson</a>
4437     * @private
4438     * @param {Number} r Red color value
4439     * @param {Number} g Green color value
4440     * @param {Number} b Blue color value
4441     * @return {Array} Hsl color
4442     */
4443    _rgbToHsl: function(r, g, b) {
4444      r /= 255, g /= 255, b /= 255;
4445
4446      var h, s, l,
4447          max = fabric.util.array.max([r, g, b]),
4448          min = fabric.util.array.min([r, g, b]);
4449
4450      l = (max + min) / 2;
4451
4452      if (max === min) {
4453        h = s = 0; // achromatic
4454      }
4455      else {
4456        var d = max - min;
4457        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
4458        switch (max) {
4459          case r:
4460            h = (g - b) / d + (g < b ? 6 : 0);
4461            break;
4462          case g:
4463            h = (b - r) / d + 2;
4464            break;
4465          case b:
4466            h = (r - g) / d + 4;
4467            break;
4468        }
4469        h /= 6;
4470      }
4471
4472      return [
4473        Math.round(h * 360),
4474        Math.round(s * 100),
4475        Math.round(l * 100)
4476      ];
4477    },
4478
4479    /**
4480     * Returns source of this color (where source is an array representation; ex: [200, 200, 100, 1])
4481     * @return {Array}
4482     */
4483    getSource: function() {
4484      return this._source;
4485    },
4486
4487    /**
4488     * Sets source of this color (where source is an array representation; ex: [200, 200, 100, 1])
4489     * @param {Array} source
4490     */
4491    setSource: function(source) {
4492      this._source = source;
4493    },
4494
4495    /**
4496     * Returns color represenation in RGB format
4497     * @return {String} ex: rgb(0-255,0-255,0-255)
4498     */
4499    toRgb: function() {
4500      var source = this.getSource();
4501      return 'rgb(' + source[0] + ',' + source[1] + ',' + source[2] + ')';
4502    },
4503
4504    /**
4505     * Returns color represenation in RGBA format
4506     * @return {String} ex: rgba(0-255,0-255,0-255,0-1)
4507     */
4508    toRgba: function() {
4509      var source = this.getSource();
4510      return 'rgba(' + source[0] + ',' + source[1] + ',' + source[2] + ',' + source[3] + ')';
4511    },
4512
4513    /**
4514     * Returns color represenation in HSL format
4515     * @return {String} ex: hsl(0-360,0%-100%,0%-100%)
4516     */
4517    toHsl: function() {
4518      var source = this.getSource(),
4519          hsl = this._rgbToHsl(source[0], source[1], source[2]);
4520
4521      return 'hsl(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%)';
4522    },
4523
4524    /**
4525     * Returns color represenation in HSLA format
4526     * @return {String} ex: hsla(0-360,0%-100%,0%-100%,0-1)
4527     */
4528    toHsla: function() {
4529      var source = this.getSource(),
4530          hsl = this._rgbToHsl(source[0], source[1], source[2]);
4531
4532      return 'hsla(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%,' + source[3] + ')';
4533    },
4534
4535    /**
4536     * Returns color represenation in HEX format
4537     * @return {String} ex: FF5555
4538     */
4539    toHex: function() {
4540      var source = this.getSource(), r, g, b;
4541
4542      r = source[0].toString(16);
4543      r = (r.length === 1) ? ('0' + r) : r;
4544
4545      g = source[1].toString(16);
4546      g = (g.length === 1) ? ('0' + g) : g;
4547
4548      b = source[2].toString(16);
4549      b = (b.length === 1) ? ('0' + b) : b;
4550
4551      return r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
4552    },
4553
4554    /**
4555     * Gets value of alpha channel for this color
4556     * @return {Number} 0-1
4557     */
4558    getAlpha: function() {
4559      return this.getSource()[3];
4560    },
4561
4562    /**
4563     * Sets value of alpha channel for this color
4564     * @param {Number} alpha Alpha value 0-1
4565     * @return {fabric.Color} thisArg
4566     */
4567    setAlpha: function(alpha) {
4568      var source = this.getSource();
4569      source[3] = alpha;
4570      this.setSource(source);
4571      return this;
4572    },
4573
4574    /**
4575     * Transforms color to its grayscale representation
4576     * @return {fabric.Color} thisArg
4577     */
4578    toGrayscale: function() {
4579      var source = this.getSource(),
4580          average = parseInt((source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0), 10),
4581          currentAlpha = source[3];
4582      this.setSource([average, average, average, currentAlpha]);
4583      return this;
4584    },
4585
4586    /**
4587     * Transforms color to its black and white representation
4588     * @param {Number} threshold
4589     * @return {fabric.Color} thisArg
4590     */
4591    toBlackWhite: function(threshold) {
4592      var source = this.getSource(),
4593          average = (source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0),
4594          currentAlpha = source[3];
4595
4596      threshold = threshold || 127;
4597
4598      average = (Number(average) < Number(threshold)) ? 0 : 255;
4599      this.setSource([average, average, average, currentAlpha]);
4600      return this;
4601    },
4602
4603    /**
4604     * Overlays color with another color
4605     * @param {String|fabric.Color} otherColor
4606     * @return {fabric.Color} thisArg
4607     */
4608    overlayWith: function(otherColor) {
4609      if (!(otherColor instanceof Color)) {
4610        otherColor = new Color(otherColor);
4611      }
4612
4613      var result = [],
4614          alpha = this.getAlpha(),
4615          otherAlpha = 0.5,
4616          source = this.getSource(),
4617          otherSource = otherColor.getSource();
4618
4619      for (var i = 0; i < 3; i++) {
4620        result.push(Math.round((source[i] * (1 - otherAlpha)) + (otherSource[i] * otherAlpha)));
4621      }
4622
4623      result[3] = alpha;
4624      this.setSource(result);
4625      return this;
4626    }
4627  };
4628
4629  /**
4630   * Regex matching color in RGB or RGBA formats (ex: rgb(0, 0, 0), rgba(255, 100, 10, 0.5), rgba( 255 , 100 , 10 , 0.5 ), rgb(1,1,1), rgba(100%, 60%, 10%, 0.5))
4631   * @static
4632   * @field
4633   * @memberOf fabric.Color
4634   */
4635  fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
4636
4637  /**
4638   * Regex matching color in HSL or HSLA formats (ex: hsl(200, 80%, 10%), hsla(300, 50%, 80%, 0.5), hsla( 300 , 50% , 80% , 0.5 ))
4639   * @static
4640   * @field
4641   * @memberOf fabric.Color
4642   */
4643  fabric.Color.reHSLa = /^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
4644
4645  /**
4646   * Regex matching color in HEX format (ex: #FF5555, 010155, aff)
4647   * @static
4648   * @field
4649   * @memberOf fabric.Color
4650   */
4651  fabric.Color.reHex = /^#?([0-9a-f]{6}|[0-9a-f]{3})$/i;
4652
4653  /**
4654   * Map of the 17 basic color names with HEX code
4655   * @static
4656   * @field
4657   * @memberOf fabric.Color
4658   * @see: http://www.w3.org/TR/CSS2/syndata.html#color-units
4659   */
4660  fabric.Color.colorNameMap = {
4661    aqua:    '#00FFFF',
4662    black:   '#000000',
4663    blue:    '#0000FF',
4664    fuchsia: '#FF00FF',
4665    gray:    '#808080',
4666    green:   '#008000',
4667    lime:    '#00FF00',
4668    maroon:  '#800000',
4669    navy:    '#000080',
4670    olive:   '#808000',
4671    orange:  '#FFA500',
4672    purple:  '#800080',
4673    red:     '#FF0000',
4674    silver:  '#C0C0C0',
4675    teal:    '#008080',
4676    white:   '#FFFFFF',
4677    yellow:  '#FFFF00'
4678  };
4679
4680  /**
4681   * @private
4682   * @param {Number} p
4683   * @param {Number} q
4684   * @param {Number} t
4685   * @return {Number}
4686   */
4687  function hue2rgb(p, q, t) {
4688    if (t < 0) {
4689      t += 1;
4690    }
4691    if (t > 1) {
4692      t -= 1;
4693    }
4694    if (t < 1/6) {
4695      return p + (q - p) * 6 * t;
4696    }
4697    if (t < 1/2) {
4698      return q;
4699    }
4700    if (t < 2/3) {
4701      return p + (q - p) * (2/3 - t) * 6;
4702    }
4703    return p;
4704  }
4705
4706  /**
4707   * Returns new color object, when given a color in RGB format
4708   * @memberOf fabric.Color
4709   * @param {String} color Color value ex: rgb(0-255,0-255,0-255)
4710   * @return {fabric.Color}
4711   */
4712  fabric.Color.fromRgb = function(color) {
4713    return Color.fromSource(Color.sourceFromRgb(color));
4714  };
4715
4716  /**
4717   * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in RGB or RGBA format
4718   * @memberOf fabric.Color
4719   * @param {String} color Color value ex: rgb(0-255,0-255,0-255), rgb(0%-100%,0%-100%,0%-100%)
4720   * @return {Array} source
4721   */
4722  fabric.Color.sourceFromRgb = function(color) {
4723    var match = color.match(Color.reRGBa);
4724    if (match) {
4725      var r = parseInt(match[1], 10) / (/%$/.test(match[1]) ? 100 : 1) * (/%$/.test(match[1]) ? 255 : 1),
4726          g = parseInt(match[2], 10) / (/%$/.test(match[2]) ? 100 : 1) * (/%$/.test(match[2]) ? 255 : 1),
4727          b = parseInt(match[3], 10) / (/%$/.test(match[3]) ? 100 : 1) * (/%$/.test(match[3]) ? 255 : 1);
4728
4729      return [
4730        parseInt(r, 10),
4731        parseInt(g, 10),
4732        parseInt(b, 10),
4733        match[4] ? parseFloat(match[4]) : 1
4734      ];
4735    }
4736  };
4737
4738  /**
4739   * Returns new color object, when given a color in RGBA format
4740   * @static
4741   * @function
4742   * @memberOf fabric.Color
4743   * @param {String} color
4744   * @return {fabric.Color}
4745   */
4746  fabric.Color.fromRgba = Color.fromRgb;
4747
4748  /**
4749   * Returns new color object, when given a color in HSL format
4750   * @param {String} color Color value ex: hsl(0-260,0%-100%,0%-100%)
4751   * @memberOf fabric.Color
4752   * @return {fabric.Color}
4753   */
4754  fabric.Color.fromHsl = function(color) {
4755    return Color.fromSource(Color.sourceFromHsl(color));
4756  };
4757
4758  /**
4759   * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in HSL or HSLA format.
4760   * Adapted from <a href="https://rawgithub.com/mjijackson/mjijackson.github.com/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.html">https://github.com/mjijackson</a>
4761   * @memberOf fabric.Color
4762   * @param {String} color Color value ex: hsl(0-360,0%-100%,0%-100%) or hsla(0-360,0%-100%,0%-100%, 0-1)
4763   * @return {Array} source
4764   * @see http://http://www.w3.org/TR/css3-color/#hsl-color
4765   */
4766  fabric.Color.sourceFromHsl = function(color) {
4767    var match = color.match(Color.reHSLa);
4768    if (!match) {
4769      return;
4770    }
4771
4772    var h = (((parseFloat(match[1]) % 360) + 360) % 360) / 360,
4773        s = parseFloat(match[2]) / (/%$/.test(match[2]) ? 100 : 1),
4774        l = parseFloat(match[3]) / (/%$/.test(match[3]) ? 100 : 1),
4775        r, g, b;
4776
4777    if (s === 0) {
4778      r = g = b = l;
4779    }
4780    else {
4781      var q = l <= 0.5 ? l * (s + 1) : l + s - l * s,
4782          p = l * 2 - q;
4783
4784      r = hue2rgb(p, q, h + 1/3);
4785      g = hue2rgb(p, q, h);
4786      b = hue2rgb(p, q, h - 1/3);
4787    }
4788
4789    return [
4790      Math.round(r * 255),
4791      Math.round(g * 255),
4792      Math.round(b * 255),
4793      match[4] ? parseFloat(match[4]) : 1
4794    ];
4795  };
4796
4797  /**
4798   * Returns new color object, when given a color in HSLA format
4799   * @static
4800   * @function
4801   * @memberOf fabric.Color
4802   * @param {String} color
4803   * @return {fabric.Color}
4804   */
4805  fabric.Color.fromHsla = Color.fromHsl;
4806
4807  /**
4808   * Returns new color object, when given a color in HEX format
4809   * @static
4810   * @memberOf fabric.Color
4811   * @param {String} color Color value ex: FF5555
4812   * @return {fabric.Color}
4813   */
4814  fabric.Color.fromHex = function(color) {
4815    return Color.fromSource(Color.sourceFromHex(color));
4816  };
4817
4818  /**
4819   * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in HEX format
4820   * @static
4821   * @memberOf fabric.Color
4822   * @param {String} color ex: FF5555
4823   * @return {Array} source
4824   */
4825  fabric.Color.sourceFromHex = function(color) {
4826    if (color.match(Color.reHex)) {
4827      var value = color.slice(color.indexOf('#') + 1),
4828          isShortNotation = (value.length === 3),
4829          r = isShortNotation ? (value.charAt(0) + value.charAt(0)) : value.substring(0, 2),
4830          g = isShortNotation ? (value.charAt(1) + value.charAt(1)) : value.substring(2, 4),
4831          b = isShortNotation ? (value.charAt(2) + value.charAt(2)) : value.substring(4, 6);
4832
4833      return [
4834        parseInt(r, 16),
4835        parseInt(g, 16),
4836        parseInt(b, 16),
4837        1
4838      ];
4839    }
4840  };
4841
4842  /**
4843   * Returns new color object, when given color in array representation (ex: [200, 100, 100, 0.5])
4844   * @static
4845   * @memberOf fabric.Color
4846   * @param {Array} source
4847   * @return {fabric.Color}
4848   */
4849  fabric.Color.fromSource = function(source) {
4850    var oColor = new Color();
4851    oColor.setSource(source);
4852    return oColor;
4853  };
4854
4855})(typeof exports !== 'undefined' ? exports : this);
4856
4857
4858(function() {
4859
4860  /* _FROM_SVG_START_ */
4861  function getColorStop(el) {
4862    var style = el.getAttribute('style'),
4863        offset = el.getAttribute('offset'),
4864        color, colorAlpha, opacity;
4865
4866    // convert percents to absolute values
4867    offset = parseFloat(offset) / (/%$/.test(offset) ? 100 : 1);
4868    offset = offset < 0 ? 0 : offset > 1 ? 1 : offset;
4869    if (style) {
4870      var keyValuePairs = style.split(/\s*;\s*/);
4871
4872      if (keyValuePairs[keyValuePairs.length - 1] === '') {
4873        keyValuePairs.pop();
4874      }
4875
4876      for (var i = keyValuePairs.length; i--; ) {
4877
4878        var split = keyValuePairs[i].split(/\s*:\s*/),
4879            key = split[0].trim(),
4880            value = split[1].trim();
4881
4882        if (key === 'stop-color') {
4883          color = value;
4884        }
4885        else if (key === 'stop-opacity') {
4886          opacity = value;
4887        }
4888      }
4889    }
4890
4891    if (!color) {
4892      color = el.getAttribute('stop-color') || 'rgb(0,0,0)';
4893    }
4894    if (!opacity) {
4895      opacity = el.getAttribute('stop-opacity');
4896    }
4897
4898    color = new fabric.Color(color);
4899    colorAlpha = color.getAlpha();
4900    opacity = isNaN(parseFloat(opacity)) ? 1 : parseFloat(opacity);
4901    opacity *= colorAlpha;
4902
4903    return {
4904      offset: offset,
4905      color: color.toRgb(),
4906      opacity: opacity
4907    };
4908  }
4909
4910  function getLinearCoords(el) {
4911    return {
4912      x1: el.getAttribute('x1') || 0,
4913      y1: el.getAttribute('y1') || 0,
4914      x2: el.getAttribute('x2') || '100%',
4915      y2: el.getAttribute('y2') || 0
4916    };
4917  }
4918
4919  function getRadialCoords(el) {
4920    return {
4921      x1: el.getAttribute('fx') || el.getAttribute('cx') || '50%',
4922      y1: el.getAttribute('fy') || el.getAttribute('cy') || '50%',
4923      r1: 0,
4924      x2: el.getAttribute('cx') || '50%',
4925      y2: el.getAttribute('cy') || '50%',
4926      r2: el.getAttribute('r') || '50%'
4927    };
4928  }
4929  /* _FROM_SVG_END_ */
4930
4931  /**
4932   * Gradient class
4933   * @class fabric.Gradient
4934   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#gradients}
4935   * @see {@link fabric.Gradient#initialize} for constructor definition
4936   */
4937  fabric.Gradient = fabric.util.createClass(/** @lends fabric.Gradient.prototype */ {
4938
4939    /**
4940     * Horizontal offset for aligning gradients coming from SVG when outside pathgroups
4941     * @type Number
4942     * @default 0
4943     */
4944    offsetX: 0,
4945
4946    /**
4947     * Vertical offset for aligning gradients coming from SVG when outside pathgroups
4948     * @type Number
4949     * @default 0
4950     */
4951    offsetY: 0,
4952
4953    /**
4954     * Constructor
4955     * @param {Object} [options] Options object with type, coords, gradientUnits and colorStops
4956     * @return {fabric.Gradient} thisArg
4957     */
4958    initialize: function(options) {
4959      options || (options = { });
4960
4961      var coords = { };
4962
4963      this.id = fabric.Object.__uid++;
4964      this.type = options.type || 'linear';
4965
4966      coords = {
4967        x1: options.coords.x1 || 0,
4968        y1: options.coords.y1 || 0,
4969        x2: options.coords.x2 || 0,
4970        y2: options.coords.y2 || 0
4971      };
4972
4973      if (this.type === 'radial') {
4974        coords.r1 = options.coords.r1 || 0;
4975        coords.r2 = options.coords.r2 || 0;
4976      }
4977      this.coords = coords;
4978      this.colorStops = options.colorStops.slice();
4979      if (options.gradientTransform) {
4980        this.gradientTransform = options.gradientTransform;
4981      }
4982      this.offsetX = options.offsetX || this.offsetX;
4983      this.offsetY = options.offsetY || this.offsetY;
4984    },
4985
4986    /**
4987     * Adds another colorStop
4988     * @param {Object} colorStop Object with offset and color
4989     * @return {fabric.Gradient} thisArg
4990     */
4991    addColorStop: function(colorStop) {
4992      for (var position in colorStop) {
4993        var color = new fabric.Color(colorStop[position]);
4994        this.colorStops.push({
4995          offset: position,
4996          color: color.toRgb(),
4997          opacity: color.getAlpha()
4998        });
4999      }
5000      return this;
5001    },
5002
5003    /**
5004     * Returns object representation of a gradient
5005     * @return {Object}
5006     */
5007    toObject: function() {
5008      return {
5009        type: this.type,
5010        coords: this.coords,
5011        colorStops: this.colorStops,
5012        offsetX: this.offsetX,
5013        offsetY: this.offsetY
5014      };
5015    },
5016
5017    /* _TO_SVG_START_ */
5018    /**
5019     * Returns SVG representation of an gradient
5020     * @param {Object} object Object to create a gradient for
5021     * @param {Boolean} normalize Whether coords should be normalized
5022     * @return {String} SVG representation of an gradient (linear/radial)
5023     */
5024    toSVG: function(object) {
5025      var coords = fabric.util.object.clone(this.coords),
5026          markup, commonAttributes;
5027
5028      // colorStops must be sorted ascending
5029      this.colorStops.sort(function(a, b) {
5030        return a.offset - b.offset;
5031      });
5032
5033      if (!(object.group && object.group.type === 'path-group')) {
5034        for (var prop in coords) {
5035          if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
5036            coords[prop] += this.offsetX - object.width / 2;
5037          }
5038          else if (prop === 'y1' || prop === 'y2') {
5039            coords[prop] += this.offsetY - object.height / 2;
5040          }
5041        }
5042      }
5043
5044      commonAttributes = 'id="SVGID_' + this.id +
5045                     '" gradientUnits="userSpaceOnUse"';
5046      if (this.gradientTransform) {
5047        commonAttributes += ' gradientTransform="matrix(' + this.gradientTransform.join(' ') + ')" ';
5048      }
5049      if (this.type === 'linear') {
5050        markup = [
5051          //jscs:disable validateIndentation
5052          '<linearGradient ',
5053            commonAttributes,
5054            ' x1="', coords.x1,
5055            '" y1="', coords.y1,
5056            '" x2="', coords.x2,
5057            '" y2="', coords.y2,
5058          '">\n'
5059          //jscs:enable validateIndentation
5060        ];
5061      }
5062      else if (this.type === 'radial') {
5063        markup = [
5064          //jscs:disable validateIndentation
5065          '<radialGradient ',
5066            commonAttributes,
5067            ' cx="', coords.x2,
5068            '" cy="', coords.y2,
5069            '" r="', coords.r2,
5070            '" fx="', coords.x1,
5071            '" fy="', coords.y1,
5072          '">\n'
5073          //jscs:enable validateIndentation
5074        ];
5075      }
5076
5077      for (var i = 0; i < this.colorStops.length; i++) {
5078        markup.push(
5079          //jscs:disable validateIndentation
5080          '<stop ',
5081            'offset="', (this.colorStops[i].offset * 100) + '%',
5082            '" style="stop-color:', this.colorStops[i].color,
5083            (this.colorStops[i].opacity != null ? ';stop-opacity: ' + this.colorStops[i].opacity : ';'),
5084          '"/>\n'
5085          //jscs:enable validateIndentation
5086        );
5087      }
5088
5089      markup.push((this.type === 'linear' ? '</linearGradient>\n' : '</radialGradient>\n'));
5090
5091      return markup.join('');
5092    },
5093    /* _TO_SVG_END_ */
5094
5095    /**
5096     * Returns an instance of CanvasGradient
5097     * @param {CanvasRenderingContext2D} ctx Context to render on
5098     * @return {CanvasGradient}
5099     */
5100    toLive: function(ctx, object) {
5101      var gradient, prop, coords = fabric.util.object.clone(this.coords);
5102
5103      if (!this.type) {
5104        return;
5105      }
5106
5107      if (object.group && object.group.type === 'path-group') {
5108        for (prop in coords) {
5109          if (prop === 'x1' || prop === 'x2') {
5110            coords[prop] += -this.offsetX + object.width / 2;
5111          }
5112          else if (prop === 'y1' || prop === 'y2') {
5113            coords[prop] += -this.offsetY + object.height / 2;
5114          }
5115        }
5116      }
5117
5118      if (object.type === 'text' || object.type === 'i-text') {
5119        for (prop in coords) {
5120          if (prop === 'x1' || prop === 'x2') {
5121            coords[prop] -= object.width / 2;
5122          }
5123          else if (prop === 'y1' || prop === 'y2') {
5124            coords[prop] -= object.height / 2;
5125          }
5126        }
5127      }
5128
5129      if (this.type === 'linear') {
5130        gradient = ctx.createLinearGradient(
5131          coords.x1, coords.y1, coords.x2, coords.y2);
5132      }
5133      else if (this.type === 'radial') {
5134        gradient = ctx.createRadialGradient(
5135          coords.x1, coords.y1, coords.r1, coords.x2, coords.y2, coords.r2);
5136      }
5137
5138      for (var i = 0, len = this.colorStops.length; i < len; i++) {
5139        var color = this.colorStops[i].color,
5140            opacity = this.colorStops[i].opacity,
5141            offset = this.colorStops[i].offset;
5142
5143        if (typeof opacity !== 'undefined') {
5144          color = new fabric.Color(color).setAlpha(opacity).toRgba();
5145        }
5146        gradient.addColorStop(parseFloat(offset), color);
5147      }
5148
5149      return gradient;
5150    }
5151  });
5152
5153  fabric.util.object.extend(fabric.Gradient, {
5154
5155    /* _FROM_SVG_START_ */
5156    /**
5157     * Returns {@link fabric.Gradient} instance from an SVG element
5158     * @static
5159     * @memberof fabric.Gradient
5160     * @param {SVGGradientElement} el SVG gradient element
5161     * @param {fabric.Object} instance
5162     * @return {fabric.Gradient} Gradient instance
5163     * @see http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement
5164     * @see http://www.w3.org/TR/SVG/pservers.html#RadialGradientElement
5165     */
5166    fromElement: function(el, instance) {
5167
5168      /**
5169       *  @example:
5170       *
5171       *  <linearGradient id="linearGrad1">
5172       *    <stop offset="0%" stop-color="white"/>
5173       *    <stop offset="100%" stop-color="black"/>
5174       *  </linearGradient>
5175       *
5176       *  OR
5177       *
5178       *  <linearGradient id="linearGrad2">
5179       *    <stop offset="0" style="stop-color:rgb(255,255,255)"/>
5180       *    <stop offset="1" style="stop-color:rgb(0,0,0)"/>
5181       *  </linearGradient>
5182       *
5183       *  OR
5184       *
5185       *  <radialGradient id="radialGrad1">
5186       *    <stop offset="0%" stop-color="white" stop-opacity="1" />
5187       *    <stop offset="50%" stop-color="black" stop-opacity="0.5" />
5188       *    <stop offset="100%" stop-color="white" stop-opacity="1" />
5189       *  </radialGradient>
5190       *
5191       *  OR
5192       *
5193       *  <radialGradient id="radialGrad2">
5194       *    <stop offset="0" stop-color="rgb(255,255,255)" />
5195       *    <stop offset="0.5" stop-color="rgb(0,0,0)" />
5196       *    <stop offset="1" stop-color="rgb(255,255,255)" />
5197       *  </radialGradient>
5198       *
5199       */
5200
5201      var colorStopEls = el.getElementsByTagName('stop'),
5202          type = (el.nodeName === 'linearGradient' ? 'linear' : 'radial'),
5203          gradientUnits = el.getAttribute('gradientUnits') || 'objectBoundingBox',
5204          gradientTransform = el.getAttribute('gradientTransform'),
5205          colorStops = [],
5206          coords = { }, ellipseMatrix;
5207
5208      if (type === 'linear') {
5209        coords = getLinearCoords(el);
5210      }
5211      else if (type === 'radial') {
5212        coords = getRadialCoords(el);
5213      }
5214
5215      for (var i = colorStopEls.length; i--; ) {
5216        colorStops.push(getColorStop(colorStopEls[i]));
5217      }
5218
5219      ellipseMatrix = _convertPercentUnitsToValues(instance, coords, gradientUnits);
5220
5221      var gradient = new fabric.Gradient({
5222        type: type,
5223        coords: coords,
5224        colorStops: colorStops,
5225        offsetX: -instance.left,
5226        offsetY: -instance.top
5227      });
5228
5229      if (gradientTransform || ellipseMatrix !== '') {
5230        gradient.gradientTransform = fabric.parseTransformAttribute((gradientTransform || '') + ellipseMatrix);
5231      }
5232      return gradient;
5233    },
5234    /* _FROM_SVG_END_ */
5235
5236    /**
5237     * Returns {@link fabric.Gradient} instance from its object representation
5238     * @static
5239     * @memberof fabric.Gradient
5240     * @param {Object} obj
5241     * @param {Object} [options] Options object
5242     */
5243    forObject: function(obj, options) {
5244      options || (options = { });
5245      _convertPercentUnitsToValues(obj, options.coords, 'userSpaceOnUse');
5246      return new fabric.Gradient(options);
5247    }
5248  });
5249
5250  /**
5251   * @private
5252   */
5253  function _convertPercentUnitsToValues(object, options, gradientUnits) {
5254    var propValue, addFactor = 0, multFactor = 1, ellipseMatrix = '';
5255    for (var prop in options) {
5256      propValue = parseFloat(options[prop], 10);
5257      if (typeof options[prop] === 'string' && /^\d+%$/.test(options[prop])) {
5258        multFactor = 0.01;
5259      }
5260      else {
5261        multFactor = 1;
5262      }
5263      if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
5264        multFactor *= gradientUnits === 'objectBoundingBox' ? object.width : 1;
5265        addFactor = gradientUnits === 'objectBoundingBox' ? object.left || 0 : 0;
5266      }
5267      else if (prop === 'y1' || prop === 'y2') {
5268        multFactor *= gradientUnits === 'objectBoundingBox' ? object.height : 1;
5269        addFactor = gradientUnits === 'objectBoundingBox' ? object.top || 0 : 0;
5270      }
5271      options[prop] = propValue * multFactor + addFactor;
5272    }
5273    if (object.type === 'ellipse' &&
5274        options.r2 !== null &&
5275        gradientUnits === 'objectBoundingBox' &&
5276        object.rx !== object.ry) {
5277
5278      var scaleFactor = object.ry/object.rx;
5279      ellipseMatrix = ' scale(1, ' + scaleFactor + ')';
5280      if (options.y1) {
5281        options.y1 /= scaleFactor;
5282      }
5283      if (options.y2) {
5284        options.y2 /= scaleFactor;
5285      }
5286    }
5287    return ellipseMatrix;
5288  }
5289})();
5290
5291
5292/**
5293 * Pattern class
5294 * @class fabric.Pattern
5295 * @see {@link http://fabricjs.com/patterns/|Pattern demo}
5296 * @see {@link http://fabricjs.com/dynamic-patterns/|DynamicPattern demo}
5297 * @see {@link fabric.Pattern#initialize} for constructor definition
5298 */
5299fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ {
5300
5301  /**
5302   * Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat)
5303   * @type String
5304   * @default
5305   */
5306  repeat: 'repeat',
5307
5308  /**
5309   * Pattern horizontal offset from object's left/top corner
5310   * @type Number
5311   * @default
5312   */
5313  offsetX: 0,
5314
5315  /**
5316   * Pattern vertical offset from object's left/top corner
5317   * @type Number
5318   * @default
5319   */
5320  offsetY: 0,
5321
5322  /**
5323   * Constructor
5324   * @param {Object} [options] Options object
5325   * @return {fabric.Pattern} thisArg
5326   */
5327  initialize: function(options) {
5328    options || (options = { });
5329
5330    this.id = fabric.Object.__uid++;
5331
5332    if (options.source) {
5333      if (typeof options.source === 'string') {
5334        // function string
5335        if (typeof fabric.util.getFunctionBody(options.source) !== 'undefined') {
5336          this.source = new Function(fabric.util.getFunctionBody(options.source));
5337        }
5338        else {
5339          // img src string
5340          var _this = this;
5341          this.source = fabric.util.createImage();
5342          fabric.util.loadImage(options.source, function(img) {
5343            _this.source = img;
5344          });
5345        }
5346      }
5347      else {
5348        // img element
5349        this.source = options.source;
5350      }
5351    }
5352    if (options.repeat) {
5353      this.repeat = options.repeat;
5354    }
5355    if (options.offsetX) {
5356      this.offsetX = options.offsetX;
5357    }
5358    if (options.offsetY) {
5359      this.offsetY = options.offsetY;
5360    }
5361  },
5362
5363  /**
5364   * Returns object representation of a pattern
5365   * @return {Object} Object representation of a pattern instance
5366   */
5367  toObject: function() {
5368
5369    var source;
5370
5371    // callback
5372    if (typeof this.source === 'function') {
5373      source = String(this.source);
5374    }
5375    // <img> element
5376    else if (typeof this.source.src === 'string') {
5377      source = this.source.src;
5378    }
5379
5380    return {
5381      source: source,
5382      repeat: this.repeat,
5383      offsetX: this.offsetX,
5384      offsetY: this.offsetY
5385    };
5386  },
5387
5388  /* _TO_SVG_START_ */
5389  /**
5390   * Returns SVG representation of a pattern
5391   * @param {fabric.Object} object
5392   * @return {String} SVG representation of a pattern
5393   */
5394  toSVG: function(object) {
5395    var patternSource = typeof this.source === 'function' ? this.source() : this.source,
5396        patternWidth = patternSource.width / object.getWidth(),
5397        patternHeight = patternSource.height / object.getHeight(),
5398        patternOffsetX = this.offsetX / object.getWidth(),
5399        patternOffsetY = this.offsetY / object.getHeight(),
5400        patternImgSrc = '';
5401    if (this.repeat === 'repeat-x' || this.repeat === 'no-repeat') {
5402      patternHeight = 1;
5403    }
5404    if (this.repeat === 'repeat-y' || this.repeat === 'no-repeat') {
5405      patternWidth = 1;
5406    }
5407    if (patternSource.src) {
5408      patternImgSrc = patternSource.src;
5409    }
5410    else if (patternSource.toDataURL) {
5411      patternImgSrc = patternSource.toDataURL();
5412    }
5413
5414    return '<pattern id="SVGID_' + this.id +
5415                  '" x="' + patternOffsetX +
5416                  '" y="' + patternOffsetY +
5417                  '" width="' + patternWidth +
5418                  '" height="' + patternHeight + '">\n' +
5419             '<image x="0" y="0"' +
5420                    ' width="' + patternSource.width +
5421                    '" height="' + patternSource.height +
5422                    '" xlink:href="' + patternImgSrc +
5423             '"></image>\n' +
5424           '</pattern>\n';
5425  },
5426  /* _TO_SVG_END_ */
5427
5428  /**
5429   * Returns an instance of CanvasPattern
5430   * @param {CanvasRenderingContext2D} ctx Context to create pattern
5431   * @return {CanvasPattern}
5432   */
5433  toLive: function(ctx) {
5434    var source = typeof this.source === 'function'
5435      ? this.source()
5436      : this.source;
5437
5438    // if the image failed to load, return, and allow rest to continue loading
5439    if (!source) {
5440      return '';
5441    }
5442
5443    // if an image
5444    if (typeof source.src !== 'undefined') {
5445      if (!source.complete) {
5446        return '';
5447      }
5448      if (source.naturalWidth === 0 || source.naturalHeight === 0) {
5449        return '';
5450      }
5451    }
5452    return ctx.createPattern(source, this.repeat);
5453  }
5454});
5455
5456
5457(function(global) {
5458
5459  'use strict';
5460
5461  var fabric = global.fabric || (global.fabric = { }),
5462      toFixed = fabric.util.toFixed;
5463
5464  if (fabric.Shadow) {
5465    fabric.warn('fabric.Shadow is already defined.');
5466    return;
5467  }
5468
5469  /**
5470   * Shadow class
5471   * @class fabric.Shadow
5472   * @see {@link http://fabricjs.com/shadows/|Shadow demo}
5473   * @see {@link fabric.Shadow#initialize} for constructor definition
5474   */
5475  fabric.Shadow = fabric.util.createClass(/** @lends fabric.Shadow.prototype */ {
5476
5477    /**
5478     * Shadow color
5479     * @type String
5480     * @default
5481     */
5482    color: 'rgb(0,0,0)',
5483
5484    /**
5485     * Shadow blur
5486     * @type Number
5487     */
5488    blur: 0,
5489
5490    /**
5491     * Shadow horizontal offset
5492     * @type Number
5493     * @default
5494     */
5495    offsetX: 0,
5496
5497    /**
5498     * Shadow vertical offset
5499     * @type Number
5500     * @default
5501     */
5502    offsetY: 0,
5503
5504    /**
5505     * Whether the shadow should affect stroke operations
5506     * @type Boolean
5507     * @default
5508     */
5509    affectStroke: false,
5510
5511    /**
5512     * Indicates whether toObject should include default values
5513     * @type Boolean
5514     * @default
5515     */
5516    includeDefaultValues: true,
5517
5518    /**
5519     * Constructor
5520     * @param {Object|String} [options] Options object with any of color, blur, offsetX, offsetX properties or string (e.g. "rgba(0,0,0,0.2) 2px 2px 10px, "2px 2px 10px rgba(0,0,0,0.2)")
5521     * @return {fabric.Shadow} thisArg
5522     */
5523    initialize: function(options) {
5524
5525      if (typeof options === 'string') {
5526        options = this._parseShadow(options);
5527      }
5528
5529      for (var prop in options) {
5530        this[prop] = options[prop];
5531      }
5532
5533      this.id = fabric.Object.__uid++;
5534    },
5535
5536    /**
5537     * @private
5538     * @param {String} shadow Shadow value to parse
5539     * @return {Object} Shadow object with color, offsetX, offsetY and blur
5540     */
5541    _parseShadow: function(shadow) {
5542      var shadowStr = shadow.trim(),
5543          offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [ ],
5544          color = shadowStr.replace(fabric.Shadow.reOffsetsAndBlur, '') || 'rgb(0,0,0)';
5545
5546      return {
5547        color: color.trim(),
5548        offsetX: parseInt(offsetsAndBlur[1], 10) || 0,
5549        offsetY: parseInt(offsetsAndBlur[2], 10) || 0,
5550        blur: parseInt(offsetsAndBlur[3], 10) || 0
5551      };
5552    },
5553
5554    /**
5555     * Returns a string representation of an instance
5556     * @see http://www.w3.org/TR/css-text-decor-3/#text-shadow
5557     * @return {String} Returns CSS3 text-shadow declaration
5558     */
5559    toString: function() {
5560      return [this.offsetX, this.offsetY, this.blur, this.color].join('px ');
5561    },
5562
5563    /* _TO_SVG_START_ */
5564    /**
5565     * Returns SVG representation of a shadow
5566     * @param {fabric.Object} object
5567     * @return {String} SVG representation of a shadow
5568     */
5569    toSVG: function(object) {
5570      var mode = 'SourceAlpha', fBoxX = 40, fBoxY = 40;
5571
5572      if (object && (object.fill === this.color || object.stroke === this.color)) {
5573        mode = 'SourceGraphic';
5574      }
5575
5576      if (object.width && object.height) {
5577        //http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
5578        // we add some extra space to filter box to contain the blur ( 20 )
5579        fBoxX = toFixed(Math.abs(this.offsetX / object.getWidth()), 2) * 100 + 20;
5580        fBoxY = toFixed(Math.abs(this.offsetY / object.getHeight()), 2) * 100 + 20;
5581      }
5582
5583      return (
5584        '<filter id="SVGID_' + this.id + '" y="-' + fBoxY + '%" height="' + (100 + 2 * fBoxY) + '%" ' +
5585          'x="-' + fBoxX + '%" width="' + (100 + 2 * fBoxX) + '%" ' + '>\n' +
5586          '\t<feGaussianBlur in="' + mode + '" stdDeviation="' +
5587            toFixed(this.blur ? this.blur / 2 : 0, 3) +
5588          '" result="blurOut"></feGaussianBlur>\n' +
5589          '\t<feColorMatrix result="matrixOut" in="blurOut" type="matrix" ' +
5590          'values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.30 0" ></feColorMatrix >\n' +
5591          '\t<feOffset dx="' + this.offsetX + '" dy="' + this.offsetY + '"></feOffset>\n' +
5592          '\t<feMerge>\n' +
5593            '\t\t<feMergeNode></feMergeNode>\n' +
5594            '\t\t<feMergeNode in="SourceGraphic"></feMergeNode>\n' +
5595          '\t</feMerge>\n' +
5596        '</filter>\n');
5597    },
5598    /* _TO_SVG_END_ */
5599
5600    /**
5601     * Returns object representation of a shadow
5602     * @return {Object} Object representation of a shadow instance
5603     */
5604    toObject: function() {
5605      if (this.includeDefaultValues) {
5606        return {
5607          color: this.color,
5608          blur: this.blur,
5609          offsetX: this.offsetX,
5610          offsetY: this.offsetY
5611        };
5612      }
5613      var obj = { }, proto = fabric.Shadow.prototype;
5614      if (this.color !== proto.color) {
5615        obj.color = this.color;
5616      }
5617      if (this.blur !== proto.blur) {
5618        obj.blur = this.blur;
5619      }
5620      if (this.offsetX !== proto.offsetX) {
5621        obj.offsetX = this.offsetX;
5622      }
5623      if (this.offsetY !== proto.offsetY) {
5624        obj.offsetY = this.offsetY;
5625      }
5626      return obj;
5627    }
5628  });
5629
5630  /**
5631   * Regex matching shadow offsetX, offsetY and blur (ex: "2px 2px 10px rgba(0,0,0,0.2)", "rgb(0,255,0) 2px 2px")
5632   * @static
5633   * @field
5634   * @memberOf fabric.Shadow
5635   */
5636  fabric.Shadow.reOffsetsAndBlur = /(?:\s|^)(-?\d+(?:px)?(?:\s?|$))?(-?\d+(?:px)?(?:\s?|$))?(\d+(?:px)?)?(?:\s?|$)(?:$|\s)/;
5637
5638})(typeof exports !== 'undefined' ? exports : this);
5639
5640
5641(function () {
5642
5643  'use strict';
5644
5645  if (fabric.StaticCanvas) {
5646    fabric.warn('fabric.StaticCanvas is already defined.');
5647    return;
5648  }
5649
5650  // aliases for faster resolution
5651  var extend = fabric.util.object.extend,
5652      getElementOffset = fabric.util.getElementOffset,
5653      removeFromArray = fabric.util.removeFromArray,
5654
5655      CANVAS_INIT_ERROR = new Error('Could not initialize `canvas` element');
5656
5657  /**
5658   * Static canvas class
5659   * @class fabric.StaticCanvas
5660   * @mixes fabric.Collection
5661   * @mixes fabric.Observable
5662   * @see {@link http://fabricjs.com/static_canvas/|StaticCanvas demo}
5663   * @see {@link fabric.StaticCanvas#initialize} for constructor definition
5664   * @fires before:render
5665   * @fires after:render
5666   * @fires canvas:cleared
5667   * @fires object:added
5668   * @fires object:removed
5669   */
5670  fabric.StaticCanvas = fabric.util.createClass(/** @lends fabric.StaticCanvas.prototype */ {
5671
5672    /**
5673     * Constructor
5674     * @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
5675     * @param {Object} [options] Options object
5676     * @return {Object} thisArg
5677     */
5678    initialize: function(el, options) {
5679      options || (options = { });
5680
5681      this._initStatic(el, options);
5682      fabric.StaticCanvas.activeInstance = this;
5683    },
5684
5685    /**
5686     * Background color of canvas instance.
5687     * Should be set via {@link fabric.StaticCanvas#setBackgroundColor}.
5688     * @type {(String|fabric.Pattern)}
5689     * @default
5690     */
5691    backgroundColor: '',
5692
5693    /**
5694     * Background image of canvas instance.
5695     * Should be set via {@link fabric.StaticCanvas#setBackgroundImage}.
5696     * <b>Backwards incompatibility note:</b> The "backgroundImageOpacity"
5697     * and "backgroundImageStretch" properties are deprecated since 1.3.9.
5698     * Use {@link fabric.Image#opacity}, {@link fabric.Image#width} and {@link fabric.Image#height}.
5699     * @type fabric.Image
5700     * @default
5701     */
5702    backgroundImage: null,
5703
5704    /**
5705     * Overlay color of canvas instance.
5706     * Should be set via {@link fabric.StaticCanvas#setOverlayColor}
5707     * @since 1.3.9
5708     * @type {(String|fabric.Pattern)}
5709     * @default
5710     */
5711    overlayColor: '',
5712
5713    /**
5714     * Overlay image of canvas instance.
5715     * Should be set via {@link fabric.StaticCanvas#setOverlayImage}.
5716     * <b>Backwards incompatibility note:</b> The "overlayImageLeft"
5717     * and "overlayImageTop" properties are deprecated since 1.3.9.
5718     * Use {@link fabric.Image#left} and {@link fabric.Image#top}.
5719     * @type fabric.Image
5720     * @default
5721     */
5722    overlayImage: null,
5723
5724    /**
5725     * Indicates whether toObject/toDatalessObject should include default values
5726     * @type Boolean
5727     * @default
5728     */
5729    includeDefaultValues: true,
5730
5731    /**
5732     * Indicates whether objects' state should be saved
5733     * @type Boolean
5734     * @default
5735     */
5736    stateful: true,
5737
5738    /**
5739     * Indicates whether {@link fabric.Collection.add}, {@link fabric.Collection.insertAt} and {@link fabric.Collection.remove} should also re-render canvas.
5740     * Disabling this option could give a great performance boost when adding/removing a lot of objects to/from canvas at once
5741     * (followed by a manual rendering after addition/deletion)
5742     * @type Boolean
5743     * @default
5744     */
5745    renderOnAddRemove: true,
5746
5747    /**
5748     * Function that determines clipping of entire canvas area
5749     * Being passed context as first argument. See clipping canvas area in {@link https://github.com/kangax/fabric.js/wiki/FAQ}
5750     * @type Function
5751     * @default
5752     */
5753    clipTo: null,
5754
5755    /**
5756     * Indicates whether object controls (borders/controls) are rendered above overlay image
5757     * @type Boolean
5758     * @default
5759     */
5760    controlsAboveOverlay: false,
5761
5762    /**
5763     * Indicates whether the browser can be scrolled when using a touchscreen and dragging on the canvas
5764     * @type Boolean
5765     * @default
5766     */
5767    allowTouchScrolling: false,
5768
5769    /**
5770     * Indicates whether this canvas will use image smoothing, this is on by default in browsers
5771     * @type Boolean
5772     * @default
5773     */
5774    imageSmoothingEnabled: true,
5775
5776    /**
5777     * Indicates whether objects should remain in current stack position when selected. When false objects are brought to top and rendered as part of the selection group
5778     * @type Boolean
5779     * @default
5780     */
5781    preserveObjectStacking: false,
5782
5783    /**
5784     * The transformation (in the format of Canvas transform) which focuses the viewport
5785     * @type Array
5786     * @default
5787     */
5788    viewportTransform: [1, 0, 0, 1, 0, 0],
5789
5790    /**
5791     * Callback; invoked right before object is about to be scaled/rotated
5792     */
5793    onBeforeScaleRotate: function () {
5794      /* NOOP */
5795    },
5796
5797    /**
5798     * @private
5799     * @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
5800     * @param {Object} [options] Options object
5801     */
5802    _initStatic: function(el, options) {
5803      this._objects = [];
5804
5805      this._createLowerCanvas(el);
5806      this._initOptions(options);
5807      this._setImageSmoothing();
5808
5809      if (options.overlayImage) {
5810        this.setOverlayImage(options.overlayImage, this.renderAll.bind(this));
5811      }
5812      if (options.backgroundImage) {
5813        this.setBackgroundImage(options.backgroundImage, this.renderAll.bind(this));
5814      }
5815      if (options.backgroundColor) {
5816        this.setBackgroundColor(options.backgroundColor, this.renderAll.bind(this));
5817      }
5818      if (options.overlayColor) {
5819        this.setOverlayColor(options.overlayColor, this.renderAll.bind(this));
5820      }
5821      this.calcOffset();
5822    },
5823
5824    /**
5825     * Calculates canvas element offset relative to the document
5826     * This method is also attached as "resize" event handler of window
5827     * @return {fabric.Canvas} instance
5828     * @chainable
5829     */
5830    calcOffset: function () {
5831      this._offset = getElementOffset(this.lowerCanvasEl);
5832      return this;
5833    },
5834
5835    /**
5836     * Sets {@link fabric.StaticCanvas#overlayImage|overlay image} for this canvas
5837     * @param {(fabric.Image|String)} image fabric.Image instance or URL of an image to set overlay to
5838     * @param {Function} callback callback to invoke when image is loaded and set as an overlay
5839     * @param {Object} [options] Optional options to set for the {@link fabric.Image|overlay image}.
5840     * @return {fabric.Canvas} thisArg
5841     * @chainable
5842     * @see {@link http://jsfiddle.net/fabricjs/MnzHT/|jsFiddle demo}
5843     * @example <caption>Normal overlayImage with left/top = 0</caption>
5844     * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
5845     *   // Needed to position overlayImage at 0/0
5846     *   originX: 'left',
5847     *   originY: 'top'
5848     * });
5849     * @example <caption>overlayImage with different properties</caption>
5850     * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
5851     *   opacity: 0.5,
5852     *   angle: 45,
5853     *   left: 400,
5854     *   top: 400,
5855     *   originX: 'left',
5856     *   originY: 'top'
5857     * });
5858     * @example <caption>Stretched overlayImage #1 - width/height correspond to canvas width/height</caption>
5859     * fabric.Image.fromURL('http://fabricjs.com/assets/jail_cell_bars.png', function(img) {
5860     *    img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});
5861     *    canvas.setOverlayImage(img, canvas.renderAll.bind(canvas));
5862     * });
5863     * @example <caption>Stretched overlayImage #2 - width/height correspond to canvas width/height</caption>
5864     * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
5865     *   width: canvas.width,
5866     *   height: canvas.height,
5867     *   // Needed to position overlayImage at 0/0
5868     *   originX: 'left',
5869     *   originY: 'top'
5870     * });
5871     * @example <caption>overlayImage loaded from cross-origin</caption>
5872     * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
5873     *   opacity: 0.5,
5874     *   angle: 45,
5875     *   left: 400,
5876     *   top: 400,
5877     *   originX: 'left',
5878     *   originY: 'top',
5879     *   crossOrigin: 'anonymous'
5880     * });
5881     */
5882    setOverlayImage: function (image, callback, options) {
5883      return this.__setBgOverlayImage('overlayImage', image, callback, options);
5884    },
5885
5886    /**
5887     * Sets {@link fabric.StaticCanvas#backgroundImage|background image} for this canvas
5888     * @param {(fabric.Image|String)} image fabric.Image instance or URL of an image to set background to
5889     * @param {Function} callback Callback to invoke when image is loaded and set as background
5890     * @param {Object} [options] Optional options to set for the {@link fabric.Image|background image}.
5891     * @return {fabric.Canvas} thisArg
5892     * @chainable
5893     * @see {@link http://jsfiddle.net/fabricjs/YH9yD/|jsFiddle demo}
5894     * @example <caption>Normal backgroundImage with left/top = 0</caption>
5895     * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
5896     *   // Needed to position backgroundImage at 0/0
5897     *   originX: 'left',
5898     *   originY: 'top'
5899     * });
5900     * @example <caption>backgroundImage with different properties</caption>
5901     * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
5902     *   opacity: 0.5,
5903     *   angle: 45,
5904     *   left: 400,
5905     *   top: 400,
5906     *   originX: 'left',
5907     *   originY: 'top'
5908     * });
5909     * @example <caption>Stretched backgroundImage #1 - width/height correspond to canvas width/height</caption>
5910     * fabric.Image.fromURL('http://fabricjs.com/assets/honey_im_subtle.png', function(img) {
5911     *    img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});
5912     *    canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
5913     * });
5914     * @example <caption>Stretched backgroundImage #2 - width/height correspond to canvas width/height</caption>
5915     * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
5916     *   width: canvas.width,
5917     *   height: canvas.height,
5918     *   // Needed to position backgroundImage at 0/0
5919     *   originX: 'left',
5920     *   originY: 'top'
5921     * });
5922     * @example <caption>backgroundImage loaded from cross-origin</caption>
5923     * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
5924     *   opacity: 0.5,
5925     *   angle: 45,
5926     *   left: 400,
5927     *   top: 400,
5928     *   originX: 'left',
5929     *   originY: 'top',
5930     *   crossOrigin: 'anonymous'
5931     * });
5932     */
5933    setBackgroundImage: function (image, callback, options) {
5934      return this.__setBgOverlayImage('backgroundImage', image, callback, options);
5935    },
5936
5937    /**
5938     * Sets {@link fabric.StaticCanvas#overlayColor|background color} for this canvas
5939     * @param {(String|fabric.Pattern)} overlayColor Color or pattern to set background color to
5940     * @param {Function} callback Callback to invoke when background color is set
5941     * @return {fabric.Canvas} thisArg
5942     * @chainable
5943     * @see {@link http://jsfiddle.net/fabricjs/pB55h/|jsFiddle demo}
5944     * @example <caption>Normal overlayColor - color value</caption>
5945     * canvas.setOverlayColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
5946     * @example <caption>fabric.Pattern used as overlayColor</caption>
5947     * canvas.setOverlayColor({
5948     *   source: 'http://fabricjs.com/assets/escheresque_ste.png'
5949     * }, canvas.renderAll.bind(canvas));
5950     * @example <caption>fabric.Pattern used as overlayColor with repeat and offset</caption>
5951     * canvas.setOverlayColor({
5952     *   source: 'http://fabricjs.com/assets/escheresque_ste.png',
5953     *   repeat: 'repeat',
5954     *   offsetX: 200,
5955     *   offsetY: 100
5956     * }, canvas.renderAll.bind(canvas));
5957     */
5958    setOverlayColor: function(overlayColor, callback) {
5959      return this.__setBgOverlayColor('overlayColor', overlayColor, callback);
5960    },
5961
5962    /**
5963     * Sets {@link fabric.StaticCanvas#backgroundColor|background color} for this canvas
5964     * @param {(String|fabric.Pattern)} backgroundColor Color or pattern to set background color to
5965     * @param {Function} callback Callback to invoke when background color is set
5966     * @return {fabric.Canvas} thisArg
5967     * @chainable
5968     * @see {@link http://jsfiddle.net/fabricjs/hXzvk/|jsFiddle demo}
5969     * @example <caption>Normal backgroundColor - color value</caption>
5970     * canvas.setBackgroundColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
5971     * @example <caption>fabric.Pattern used as backgroundColor</caption>
5972     * canvas.setBackgroundColor({
5973     *   source: 'http://fabricjs.com/assets/escheresque_ste.png'
5974     * }, canvas.renderAll.bind(canvas));
5975     * @example <caption>fabric.Pattern used as backgroundColor with repeat and offset</caption>
5976     * canvas.setBackgroundColor({
5977     *   source: 'http://fabricjs.com/assets/escheresque_ste.png',
5978     *   repeat: 'repeat',
5979     *   offsetX: 200,
5980     *   offsetY: 100
5981     * }, canvas.renderAll.bind(canvas));
5982     */
5983    setBackgroundColor: function(backgroundColor, callback) {
5984      return this.__setBgOverlayColor('backgroundColor', backgroundColor, callback);
5985    },
5986
5987    /**
5988     * @private
5989     * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-imagesmoothingenabled|WhatWG Canvas Standard}
5990     */
5991    _setImageSmoothing: function() {
5992      var ctx = this.getContext();
5993
5994      ctx.imageSmoothingEnabled       = this.imageSmoothingEnabled;
5995      ctx.webkitImageSmoothingEnabled = this.imageSmoothingEnabled;
5996      ctx.mozImageSmoothingEnabled    = this.imageSmoothingEnabled;
5997      ctx.msImageSmoothingEnabled     = this.imageSmoothingEnabled;
5998      ctx.oImageSmoothingEnabled      = this.imageSmoothingEnabled;
5999    },
6000
6001    /**
6002     * @private
6003     * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundImage|backgroundImage}
6004     * or {@link fabric.StaticCanvas#overlayImage|overlayImage})
6005     * @param {(fabric.Image|String|null)} image fabric.Image instance, URL of an image or null to set background or overlay to
6006     * @param {Function} callback Callback to invoke when image is loaded and set as background or overlay
6007     * @param {Object} [options] Optional options to set for the {@link fabric.Image|image}.
6008     */
6009    __setBgOverlayImage: function(property, image, callback, options) {
6010      if (typeof image === 'string') {
6011        fabric.util.loadImage(image, function(img) {
6012          this[property] = new fabric.Image(img, options);
6013          callback && callback();
6014        }, this, options && options.crossOrigin);
6015      }
6016      else {
6017        options && image.setOptions(options);
6018        this[property] = image;
6019        callback && callback();
6020      }
6021
6022      return this;
6023    },
6024
6025    /**
6026     * @private
6027     * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundColor|backgroundColor}
6028     * or {@link fabric.StaticCanvas#overlayColor|overlayColor})
6029     * @param {(Object|String|null)} color Object with pattern information, color value or null
6030     * @param {Function} [callback] Callback is invoked when color is set
6031     */
6032    __setBgOverlayColor: function(property, color, callback) {
6033      if (color && color.source) {
6034        var _this = this;
6035        fabric.util.loadImage(color.source, function(img) {
6036          _this[property] = new fabric.Pattern({
6037            source: img,
6038            repeat: color.repeat,
6039            offsetX: color.offsetX,
6040            offsetY: color.offsetY
6041          });
6042          callback && callback();
6043        });
6044      }
6045      else {
6046        this[property] = color;
6047        callback && callback();
6048      }
6049
6050      return this;
6051    },
6052
6053    /**
6054     * @private
6055     */
6056    _createCanvasElement: function() {
6057      var element = fabric.document.createElement('canvas');
6058      if (!element.style) {
6059        element.style = { };
6060      }
6061      if (!element) {
6062        throw CANVAS_INIT_ERROR;
6063      }
6064      this._initCanvasElement(element);
6065      return element;
6066    },
6067
6068    /**
6069     * @private
6070     * @param {HTMLElement} element
6071     */
6072    _initCanvasElement: function(element) {
6073      fabric.util.createCanvasElement(element);
6074
6075      if (typeof element.getContext === 'undefined') {
6076        throw CANVAS_INIT_ERROR;
6077      }
6078    },
6079
6080    /**
6081     * @private
6082     * @param {Object} [options] Options object
6083     */
6084    _initOptions: function (options) {
6085      for (var prop in options) {
6086        this[prop] = options[prop];
6087      }
6088
6089      this.width = this.width || parseInt(this.lowerCanvasEl.width, 10) || 0;
6090      this.height = this.height || parseInt(this.lowerCanvasEl.height, 10) || 0;
6091
6092      if (!this.lowerCanvasEl.style) {
6093        return;
6094      }
6095
6096      this.lowerCanvasEl.width = this.width;
6097      this.lowerCanvasEl.height = this.height;
6098
6099      this.lowerCanvasEl.style.width = this.width + 'px';
6100      this.lowerCanvasEl.style.height = this.height + 'px';
6101
6102      this.viewportTransform = this.viewportTransform.slice();
6103    },
6104
6105    /**
6106     * Creates a bottom canvas
6107     * @private
6108     * @param {HTMLElement} [canvasEl]
6109     */
6110    _createLowerCanvas: function (canvasEl) {
6111      this.lowerCanvasEl = fabric.util.getById(canvasEl) || this._createCanvasElement();
6112      this._initCanvasElement(this.lowerCanvasEl);
6113
6114      fabric.util.addClass(this.lowerCanvasEl, 'lower-canvas');
6115
6116      if (this.interactive) {
6117        this._applyCanvasStyle(this.lowerCanvasEl);
6118      }
6119
6120      this.contextContainer = this.lowerCanvasEl.getContext('2d');
6121    },
6122
6123    /**
6124     * Returns canvas width (in px)
6125     * @return {Number}
6126     */
6127    getWidth: function () {
6128      return this.width;
6129    },
6130
6131    /**
6132     * Returns canvas height (in px)
6133     * @return {Number}
6134     */
6135    getHeight: function () {
6136      return this.height;
6137    },
6138
6139    /**
6140     * Sets width of this canvas instance
6141     * @param {Number|String} value                         Value to set width to
6142     * @param {Object}        [options]                     Options object
6143     * @param {Boolean}       [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
6144     * @param {Boolean}       [options.cssOnly=false]       Set the given dimensions only as css dimensions
6145     * @return {fabric.Canvas} instance
6146     * @chainable true
6147     */
6148    setWidth: function (value, options) {
6149      return this.setDimensions({ width: value }, options);
6150    },
6151
6152    /**
6153     * Sets height of this canvas instance
6154     * @param {Number|String} value                         Value to set height to
6155     * @param {Object}        [options]                     Options object
6156     * @param {Boolean}       [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
6157     * @param {Boolean}       [options.cssOnly=false]       Set the given dimensions only as css dimensions
6158     * @return {fabric.Canvas} instance
6159     * @chainable true
6160     */
6161    setHeight: function (value, options) {
6162      return this.setDimensions({ height: value }, options);
6163    },
6164
6165    /**
6166     * Sets dimensions (width, height) of this canvas instance. when options.cssOnly flag active you should also supply the unit of measure (px/%/em)
6167     * @param {Object}        dimensions                    Object with width/height properties
6168     * @param {Number|String} [dimensions.width]            Width of canvas element
6169     * @param {Number|String} [dimensions.height]           Height of canvas element
6170     * @param {Object}        [options]                     Options object
6171     * @param {Boolean}       [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
6172     * @param {Boolean}       [options.cssOnly=false]       Set the given dimensions only as css dimensions
6173     * @return {fabric.Canvas} thisArg
6174     * @chainable
6175     */
6176    setDimensions: function (dimensions, options) {
6177      var cssValue;
6178
6179      options = options || {};
6180
6181      for (var prop in dimensions) {
6182        cssValue = dimensions[prop];
6183
6184        if (!options.cssOnly) {
6185          this._setBackstoreDimension(prop, dimensions[prop]);
6186          cssValue += 'px';
6187        }
6188
6189        if (!options.backstoreOnly) {
6190          this._setCssDimension(prop, cssValue);
6191        }
6192      }
6193
6194      if (!options.cssOnly) {
6195        this.renderAll();
6196      }
6197
6198      this.calcOffset();
6199
6200      return this;
6201    },
6202
6203    /**
6204     * Helper for setting width/height
6205     * @private
6206     * @param {String} prop property (width|height)
6207     * @param {Number} value value to set property to
6208     * @return {fabric.Canvas} instance
6209     * @chainable true
6210     */
6211    _setBackstoreDimension: function (prop, value) {
6212      this.lowerCanvasEl[prop] = value;
6213
6214      if (this.upperCanvasEl) {
6215        this.upperCanvasEl[prop] = value;
6216      }
6217
6218      if (this.cacheCanvasEl) {
6219        this.cacheCanvasEl[prop] = value;
6220      }
6221
6222      this[prop] = value;
6223
6224      return this;
6225    },
6226
6227    /**
6228     * Helper for setting css width/height
6229     * @private
6230     * @param {String} prop property (width|height)
6231     * @param {String} value value to set property to
6232     * @return {fabric.Canvas} instance
6233     * @chainable true
6234     */
6235    _setCssDimension: function (prop, value) {
6236      this.lowerCanvasEl.style[prop] = value;
6237
6238      if (this.upperCanvasEl) {
6239        this.upperCanvasEl.style[prop] = value;
6240      }
6241
6242      if (this.wrapperEl) {
6243        this.wrapperEl.style[prop] = value;
6244      }
6245
6246      return this;
6247    },
6248
6249    /**
6250     * Returns canvas zoom level
6251     * @return {Number}
6252     */
6253    getZoom: function () {
6254      return Math.sqrt(this.viewportTransform[0] * this.viewportTransform[3]);
6255    },
6256
6257    /**
6258     * Sets viewport transform of this canvas instance
6259     * @param {Array} vpt the transform in the form of context.transform
6260     * @return {fabric.Canvas} instance
6261     * @chainable true
6262     */
6263    setViewportTransform: function (vpt) {
6264      var activeGroup = this.getActiveGroup();
6265      this.viewportTransform = vpt;
6266      this.renderAll();
6267      for (var i = 0, len = this._objects.length; i < len; i++) {
6268        this._objects[i].setCoords();
6269      }
6270      if (activeGroup) {
6271        activeGroup.setCoords();
6272      }
6273      return this;
6274    },
6275
6276    /**
6277     * Sets zoom level of this canvas instance, zoom centered around point
6278     * @param {fabric.Point} point to zoom with respect to
6279     * @param {Number} value to set zoom to, less than 1 zooms out
6280     * @return {fabric.Canvas} instance
6281     * @chainable true
6282     */
6283    zoomToPoint: function (point, value) {
6284      // TODO: just change the scale, preserve other transformations
6285      var before = point;
6286      point = fabric.util.transformPoint(point, fabric.util.invertTransform(this.viewportTransform));
6287      this.viewportTransform[0] = value;
6288      this.viewportTransform[3] = value;
6289      var after = fabric.util.transformPoint(point, this.viewportTransform);
6290      this.viewportTransform[4] += before.x - after.x;
6291      this.viewportTransform[5] += before.y - after.y;
6292      this.renderAll();
6293      for (var i = 0, len = this._objects.length; i < len; i++) {
6294        this._objects[i].setCoords();
6295      }
6296      return this;
6297    },
6298
6299    /**
6300     * Sets zoom level of this canvas instance
6301     * @param {Number} value to set zoom to, less than 1 zooms out
6302     * @return {fabric.Canvas} instance
6303     * @chainable true
6304     */
6305    setZoom: function (value) {
6306      this.zoomToPoint(new fabric.Point(0, 0), value);
6307      return this;
6308    },
6309
6310    /**
6311     * Pan viewport so as to place point at top left corner of canvas
6312     * @param {fabric.Point} point to move to
6313     * @return {fabric.Canvas} instance
6314     * @chainable true
6315     */
6316    absolutePan: function (point) {
6317      this.viewportTransform[4] = -point.x;
6318      this.viewportTransform[5] = -point.y;
6319      this.renderAll();
6320      for (var i = 0, len = this._objects.length; i < len; i++) {
6321        this._objects[i].setCoords();
6322      }
6323      return this;
6324    },
6325
6326    /**
6327     * Pans viewpoint relatively
6328     * @param {fabric.Point} point (position vector) to move by
6329     * @return {fabric.Canvas} instance
6330     * @chainable true
6331     */
6332    relativePan: function (point) {
6333      return this.absolutePan(new fabric.Point(
6334        -point.x - this.viewportTransform[4],
6335        -point.y - this.viewportTransform[5]
6336      ));
6337    },
6338
6339    /**
6340     * Returns &lt;canvas> element corresponding to this instance
6341     * @return {HTMLCanvasElement}
6342     */
6343    getElement: function () {
6344      return this.lowerCanvasEl;
6345    },
6346
6347    /**
6348     * Returns currently selected object, if any
6349     * @return {fabric.Object}
6350     */
6351    getActiveObject: function() {
6352      return null;
6353    },
6354
6355    /**
6356     * Returns currently selected group of object, if any
6357     * @return {fabric.Group}
6358     */
6359    getActiveGroup: function() {
6360      return null;
6361    },
6362
6363    /**
6364     * Given a context, renders an object on that context
6365     * @param {CanvasRenderingContext2D} ctx Context to render object on
6366     * @param {fabric.Object} object Object to render
6367     * @private
6368     */
6369    _draw: function (ctx, object) {
6370      if (!object) {
6371        return;
6372      }
6373
6374      ctx.save();
6375      var v = this.viewportTransform;
6376      ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
6377      if (this._shouldRenderObject(object)) {
6378        object.render(ctx);
6379      }
6380      ctx.restore();
6381      if (!this.controlsAboveOverlay) {
6382        object._renderControls(ctx);
6383      }
6384    },
6385
6386    _shouldRenderObject: function(object) {
6387      if (!object) {
6388        return false;
6389      }
6390      return (object !== this.getActiveGroup() || !this.preserveObjectStacking);
6391    },
6392
6393    /**
6394     * @private
6395     * @param {fabric.Object} obj Object that was added
6396     */
6397    _onObjectAdded: function(obj) {
6398      this.stateful && obj.setupState();
6399      obj.canvas = this;
6400      obj.setCoords();
6401      this.fire('object:added', { target: obj });
6402      obj.fire('added');
6403    },
6404
6405    /**
6406     * @private
6407     * @param {fabric.Object} obj Object that was removed
6408     */
6409    _onObjectRemoved: function(obj) {
6410      // removing active object should fire "selection:cleared" events
6411      if (this.getActiveObject() === obj) {
6412        this.fire('before:selection:cleared', { target: obj });
6413        this._discardActiveObject();
6414        this.fire('selection:cleared');
6415      }
6416
6417      this.fire('object:removed', { target: obj });
6418      obj.fire('removed');
6419    },
6420
6421    /**
6422     * Clears specified context of canvas element
6423     * @param {CanvasRenderingContext2D} ctx Context to clear
6424     * @return {fabric.Canvas} thisArg
6425     * @chainable
6426     */
6427    clearContext: function(ctx) {
6428      ctx.clearRect(0, 0, this.width, this.height);
6429      return this;
6430    },
6431
6432    /**
6433     * Returns context of canvas where objects are drawn
6434     * @return {CanvasRenderingContext2D}
6435     */
6436    getContext: function () {
6437      return this.contextContainer;
6438    },
6439
6440    /**
6441     * Clears all contexts (background, main, top) of an instance
6442     * @return {fabric.Canvas} thisArg
6443     * @chainable
6444     */
6445    clear: function () {
6446      this._objects.length = 0;
6447      if (this.discardActiveGroup) {
6448        this.discardActiveGroup();
6449      }
6450      if (this.discardActiveObject) {
6451        this.discardActiveObject();
6452      }
6453      this.clearContext(this.contextContainer);
6454      if (this.contextTop) {
6455        this.clearContext(this.contextTop);
6456      }
6457      this.fire('canvas:cleared');
6458      this.renderAll();
6459      return this;
6460    },
6461
6462    /**
6463     * Renders both the top canvas and the secondary container canvas.
6464     * @param {Boolean} [allOnTop] Whether we want to force all images to be rendered on the top canvas
6465     * @return {fabric.Canvas} instance
6466     * @chainable
6467     */
6468    renderAll: function (allOnTop) {
6469      var canvasToDrawOn = this[(allOnTop === true && this.interactive) ? 'contextTop' : 'contextContainer'],
6470          activeGroup = this.getActiveGroup();
6471
6472      if (this.contextTop && this.selection && !this._groupSelector) {
6473        this.clearContext(this.contextTop);
6474      }
6475
6476      if (!allOnTop) {
6477        this.clearContext(canvasToDrawOn);
6478      }
6479
6480      this.fire('before:render');
6481
6482      if (this.clipTo) {
6483        fabric.util.clipContext(this, canvasToDrawOn);
6484      }
6485
6486      this._renderBackground(canvasToDrawOn);
6487      this._renderObjects(canvasToDrawOn, activeGroup);
6488      this._renderActiveGroup(canvasToDrawOn, activeGroup);
6489
6490      if (this.clipTo) {
6491        canvasToDrawOn.restore();
6492      }
6493
6494      this._renderOverlay(canvasToDrawOn);
6495
6496      if (this.controlsAboveOverlay && this.interactive) {
6497        this.drawControls(canvasToDrawOn);
6498      }
6499
6500      this.fire('after:render');
6501
6502      return this;
6503    },
6504
6505    /**
6506     * @private
6507     * @param {CanvasRenderingContext2D} ctx Context to render on
6508     * @param {fabric.Group} activeGroup
6509     */
6510    _renderObjects: function(ctx, activeGroup) {
6511      var i, length;
6512
6513      // fast path
6514      if (!activeGroup || this.preserveObjectStacking) {
6515        for (i = 0, length = this._objects.length; i < length; ++i) {
6516          this._draw(ctx, this._objects[i]);
6517        }
6518      }
6519      else {
6520        for (i = 0, length = this._objects.length; i < length; ++i) {
6521          if (this._objects[i] && !activeGroup.contains(this._objects[i])) {
6522            this._draw(ctx, this._objects[i]);
6523          }
6524        }
6525      }
6526    },
6527
6528    /**
6529     * @private
6530     * @param {CanvasRenderingContext2D} ctx Context to render on
6531     * @param {fabric.Group} activeGroup
6532     */
6533    _renderActiveGroup: function(ctx, activeGroup) {
6534
6535      // delegate rendering to group selection (if one exists)
6536      if (activeGroup) {
6537
6538        //Store objects in group preserving order, then replace
6539        var sortedObjects = [];
6540        this.forEachObject(function (object) {
6541          if (activeGroup.contains(object)) {
6542            sortedObjects.push(object);
6543          }
6544        });
6545        activeGroup._set('objects', sortedObjects);
6546        this._draw(ctx, activeGroup);
6547      }
6548    },
6549
6550    /**
6551     * @private
6552     * @param {CanvasRenderingContext2D} ctx Context to render on
6553     */
6554    _renderBackground: function(ctx) {
6555      if (this.backgroundColor) {
6556        ctx.fillStyle = this.backgroundColor.toLive
6557          ? this.backgroundColor.toLive(ctx)
6558          : this.backgroundColor;
6559
6560        ctx.fillRect(
6561          this.backgroundColor.offsetX || 0,
6562          this.backgroundColor.offsetY || 0,
6563          this.width,
6564          this.height);
6565      }
6566      if (this.backgroundImage) {
6567        this._draw(ctx, this.backgroundImage);
6568      }
6569    },
6570
6571    /**
6572     * @private
6573     * @param {CanvasRenderingContext2D} ctx Context to render on
6574     */
6575    _renderOverlay: function(ctx) {
6576      if (this.overlayColor) {
6577        ctx.fillStyle = this.overlayColor.toLive
6578          ? this.overlayColor.toLive(ctx)
6579          : this.overlayColor;
6580
6581        ctx.fillRect(
6582          this.overlayColor.offsetX || 0,
6583          this.overlayColor.offsetY || 0,
6584          this.width,
6585          this.height);
6586      }
6587      if (this.overlayImage) {
6588        this._draw(ctx, this.overlayImage);
6589      }
6590    },
6591
6592    /**
6593     * Method to render only the top canvas.
6594     * Also used to render the group selection box.
6595     * @return {fabric.Canvas} thisArg
6596     * @chainable
6597     */
6598    renderTop: function () {
6599      var ctx = this.contextTop || this.contextContainer;
6600      this.clearContext(ctx);
6601
6602      // we render the top context - last object
6603      if (this.selection && this._groupSelector) {
6604        this._drawSelection();
6605      }
6606
6607      // delegate rendering to group selection if one exists
6608      // used for drawing selection borders/controls
6609      var activeGroup = this.getActiveGroup();
6610      if (activeGroup) {
6611        activeGroup.render(ctx);
6612      }
6613
6614      this._renderOverlay(ctx);
6615
6616      this.fire('after:render');
6617
6618      return this;
6619    },
6620
6621    /**
6622     * Returns coordinates of a center of canvas.
6623     * Returned value is an object with top and left properties
6624     * @return {Object} object with "top" and "left" number values
6625     */
6626    getCenter: function () {
6627      return {
6628        top: this.getHeight() / 2,
6629        left: this.getWidth() / 2
6630      };
6631    },
6632
6633    /**
6634     * Centers object horizontally.
6635     * You might need to call `setCoords` on an object after centering, to update controls area.
6636     * @param {fabric.Object} object Object to center horizontally
6637     * @return {fabric.Canvas} thisArg
6638     */
6639    centerObjectH: function (object) {
6640      this._centerObject(object, new fabric.Point(this.getCenter().left, object.getCenterPoint().y));
6641      this.renderAll();
6642      return this;
6643    },
6644
6645    /**
6646     * Centers object vertically.
6647     * You might need to call `setCoords` on an object after centering, to update controls area.
6648     * @param {fabric.Object} object Object to center vertically
6649     * @return {fabric.Canvas} thisArg
6650     * @chainable
6651     */
6652    centerObjectV: function (object) {
6653      this._centerObject(object, new fabric.Point(object.getCenterPoint().x, this.getCenter().top));
6654      this.renderAll();
6655      return this;
6656    },
6657
6658    /**
6659     * Centers object vertically and horizontally.
6660     * You might need to call `setCoords` on an object after centering, to update controls area.
6661     * @param {fabric.Object} object Object to center vertically and horizontally
6662     * @return {fabric.Canvas} thisArg
6663     * @chainable
6664     */
6665    centerObject: function(object) {
6666      var center = this.getCenter();
6667
6668      this._centerObject(object, new fabric.Point(center.left, center.top));
6669      this.renderAll();
6670      return this;
6671    },
6672
6673    /**
6674     * @private
6675     * @param {fabric.Object} object Object to center
6676     * @param {fabric.Point} center Center point
6677     * @return {fabric.Canvas} thisArg
6678     * @chainable
6679     */
6680    _centerObject: function(object, center) {
6681      object.setPositionByOrigin(center, 'center', 'center');
6682      return this;
6683    },
6684
6685    /**
6686     * Returs dataless JSON representation of canvas
6687     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
6688     * @return {String} json string
6689     */
6690    toDatalessJSON: function (propertiesToInclude) {
6691      return this.toDatalessObject(propertiesToInclude);
6692    },
6693
6694    /**
6695     * Returns object representation of canvas
6696     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
6697     * @return {Object} object representation of an instance
6698     */
6699    toObject: function (propertiesToInclude) {
6700      return this._toObjectMethod('toObject', propertiesToInclude);
6701    },
6702
6703    /**
6704     * Returns dataless object representation of canvas
6705     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
6706     * @return {Object} object representation of an instance
6707     */
6708    toDatalessObject: function (propertiesToInclude) {
6709      return this._toObjectMethod('toDatalessObject', propertiesToInclude);
6710    },
6711
6712    /**
6713     * @private
6714     */
6715    _toObjectMethod: function (methodName, propertiesToInclude) {
6716
6717      var data = {
6718        objects: this._toObjects(methodName, propertiesToInclude)
6719      };
6720
6721      extend(data, this.__serializeBgOverlay());
6722
6723      fabric.util.populateWithProperties(this, data, propertiesToInclude);
6724
6725      return data;
6726    },
6727
6728    /**
6729     * @private
6730     */
6731    _toObjects: function(methodName, propertiesToInclude) {
6732      return this.getObjects().map(function(instance) {
6733        return this._toObject(instance, methodName, propertiesToInclude);
6734      }, this);
6735    },
6736
6737    /**
6738     * @private
6739     */
6740    _toObject: function(instance, methodName, propertiesToInclude) {
6741      var originalValue;
6742
6743      if (!this.includeDefaultValues) {
6744        originalValue = instance.includeDefaultValues;
6745        instance.includeDefaultValues = false;
6746      }
6747
6748      //If the object is part of the current selection group, it should
6749      //be transformed appropriately
6750      //i.e. it should be serialised as it would appear if the selection group
6751      //were to be destroyed.
6752      var originalProperties = this._realizeGroupTransformOnObject(instance),
6753          object = instance[methodName](propertiesToInclude);
6754      if (!this.includeDefaultValues) {
6755        instance.includeDefaultValues = originalValue;
6756      }
6757
6758      //Undo the damage we did by changing all of its properties
6759      this._unwindGroupTransformOnObject(instance, originalProperties);
6760
6761      return object;
6762    },
6763
6764    /**
6765     * Realises an object's group transformation on it
6766     * @private
6767     * @param {fabric.Object} [instance] the object to transform (gets mutated)
6768     * @returns the original values of instance which were changed
6769     */
6770    _realizeGroupTransformOnObject: function(instance) {
6771      var layoutProps = ['angle', 'flipX', 'flipY', 'height', 'left', 'scaleX', 'scaleY', 'top', 'width'];
6772      if (instance.group && instance.group === this.getActiveGroup()) {
6773        //Copy all the positionally relevant properties across now
6774        var originalValues = {};
6775        layoutProps.forEach(function(prop) {
6776          originalValues[prop] = instance[prop];
6777        });
6778        this.getActiveGroup().realizeTransform(instance);
6779        return originalValues;
6780      }
6781      else {
6782        return null;
6783      }
6784    },
6785
6786    /*
6787     * Restores the changed properties of instance
6788     * @private
6789     * @param {fabric.Object} [instance] the object to un-transform (gets mutated)
6790     * @param {Object} [originalValues] the original values of instance, as returned by _realizeGroupTransformOnObject
6791     */
6792    _unwindGroupTransformOnObject: function(instance, originalValues) {
6793      if (originalValues) {
6794        instance.set(originalValues);
6795      }
6796    },
6797
6798    /**
6799     * @private
6800     */
6801    __serializeBgOverlay: function() {
6802      var data = {
6803        background: (this.backgroundColor && this.backgroundColor.toObject)
6804          ? this.backgroundColor.toObject()
6805          : this.backgroundColor
6806      };
6807
6808      if (this.overlayColor) {
6809        data.overlay = this.overlayColor.toObject
6810          ? this.overlayColor.toObject()
6811          : this.overlayColor;
6812      }
6813      if (this.backgroundImage) {
6814        data.backgroundImage = this.backgroundImage.toObject();
6815      }
6816      if (this.overlayImage) {
6817        data.overlayImage = this.overlayImage.toObject();
6818      }
6819
6820      return data;
6821    },
6822
6823    /* _TO_SVG_START_ */
6824    /**
6825     * When true, getSvgTransform() will apply the StaticCanvas.viewportTransform to the SVG transformation. When true,
6826     * a zoomed canvas will then produce zoomed SVG output.
6827     * @type Boolean
6828     * @default
6829     */
6830    svgViewportTransformation: true,
6831
6832    /**
6833     * Returns SVG representation of canvas
6834     * @function
6835     * @param {Object} [options] Options object for SVG output
6836     * @param {Boolean} [options.suppressPreamble=false] If true xml tag is not included
6837     * @param {Object} [options.viewBox] SVG viewbox object
6838     * @param {Number} [options.viewBox.x] x-cooridnate of viewbox
6839     * @param {Number} [options.viewBox.y] y-coordinate of viewbox
6840     * @param {Number} [options.viewBox.width] Width of viewbox
6841     * @param {Number} [options.viewBox.height] Height of viewbox
6842     * @param {String} [options.encoding=UTF-8] Encoding of SVG output
6843     * @param {Function} [reviver] Method for further parsing of svg elements, called after each fabric object converted into svg representation.
6844     * @return {String} SVG string
6845     * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#serialization}
6846     * @see {@link http://jsfiddle.net/fabricjs/jQ3ZZ/|jsFiddle demo}
6847     * @example <caption>Normal SVG output</caption>
6848     * var svg = canvas.toSVG();
6849     * @example <caption>SVG output without preamble (without &lt;?xml ../>)</caption>
6850     * var svg = canvas.toSVG({suppressPreamble: true});
6851     * @example <caption>SVG output with viewBox attribute</caption>
6852     * var svg = canvas.toSVG({
6853     *   viewBox: {
6854     *     x: 100,
6855     *     y: 100,
6856     *     width: 200,
6857     *     height: 300
6858     *   }
6859     * });
6860     * @example <caption>SVG output with different encoding (default: UTF-8)</caption>
6861     * var svg = canvas.toSVG({encoding: 'ISO-8859-1'});
6862     * @example <caption>Modify SVG output with reviver function</caption>
6863     * var svg = canvas.toSVG(null, function(svg) {
6864     *   return svg.replace('stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; ', '');
6865     * });
6866     */
6867    toSVG: function(options, reviver) {
6868      options || (options = { });
6869
6870      var markup = [];
6871
6872      this._setSVGPreamble(markup, options);
6873      this._setSVGHeader(markup, options);
6874
6875      this._setSVGBgOverlayColor(markup, 'backgroundColor');
6876      this._setSVGBgOverlayImage(markup, 'backgroundImage');
6877
6878      this._setSVGObjects(markup, reviver);
6879
6880      this._setSVGBgOverlayColor(markup, 'overlayColor');
6881      this._setSVGBgOverlayImage(markup, 'overlayImage');
6882
6883      markup.push('</svg>');
6884
6885      return markup.join('');
6886    },
6887
6888    /**
6889     * @private
6890     */
6891    _setSVGPreamble: function(markup, options) {
6892      if (!options.suppressPreamble) {
6893        markup.push(
6894          '<?xml version="1.0" encoding="', (options.encoding || 'UTF-8'), '" standalone="no" ?>',
6895            '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" ',
6896              '"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n'
6897        );
6898      }
6899    },
6900
6901    /**
6902     * @private
6903     */
6904    _setSVGHeader: function(markup, options) {
6905      var width, height, vpt;
6906
6907      if (options.viewBox) {
6908        width = options.viewBox.width;
6909        height = options.viewBox.height;
6910      }
6911      else {
6912        width = this.width;
6913        height = this.height;
6914        if (!this.svgViewportTransformation) {
6915          vpt = this.viewportTransform;
6916          width /= vpt[0];
6917          height /= vpt[3];
6918        }
6919      }
6920
6921      markup.push(
6922        '<svg ',
6923          'xmlns="http://www.w3.org/2000/svg" ',
6924          'xmlns:xlink="http://www.w3.org/1999/xlink" ',
6925          'version="1.1" ',
6926          'width="', width, '" ',
6927          'height="', height, '" ',
6928          (this.backgroundColor && !this.backgroundColor.toLive
6929            ? 'style="background-color: ' + this.backgroundColor + '" '
6930            : null),
6931          (options.viewBox
6932              ? 'viewBox="' +
6933                options.viewBox.x + ' ' +
6934                options.viewBox.y + ' ' +
6935                options.viewBox.width + ' ' +
6936                options.viewBox.height + '" '
6937              : null),
6938          'xml:space="preserve">',
6939        '<desc>Created with Fabric.js ', fabric.version, '</desc>',
6940        '<defs>',
6941          fabric.createSVGFontFacesMarkup(this.getObjects()),
6942          fabric.createSVGRefElementsMarkup(this),
6943        '</defs>'
6944      );
6945    },
6946
6947    /**
6948     * @private
6949     */
6950    _setSVGObjects: function(markup, reviver) {
6951      for (var i = 0, objects = this.getObjects(), len = objects.length; i < len; i++) {
6952        var instance = objects[i],
6953            //If the object is in a selection group, simulate what would happen to that
6954            //object when the group is deselected
6955            originalProperties = this._realizeGroupTransformOnObject(instance);
6956        markup.push(instance.toSVG(reviver));
6957        this._unwindGroupTransformOnObject(instance, originalProperties);
6958      }
6959    },
6960
6961    /**
6962     * @private
6963     */
6964    _setSVGBgOverlayImage: function(markup, property) {
6965      if (this[property] && this[property].toSVG) {
6966        markup.push(this[property].toSVG());
6967      }
6968    },
6969
6970    /**
6971     * @private
6972     */
6973    _setSVGBgOverlayColor: function(markup, property) {
6974      if (this[property] && this[property].source) {
6975        markup.push(
6976          '<rect x="', this[property].offsetX, '" y="', this[property].offsetY, '" ',
6977            'width="',
6978              (this[property].repeat === 'repeat-y' || this[property].repeat === 'no-repeat'
6979                ? this[property].source.width
6980                : this.width),
6981            '" height="',
6982              (this[property].repeat === 'repeat-x' || this[property].repeat === 'no-repeat'
6983                ? this[property].source.height
6984                : this.height),
6985            '" fill="url(#' + property + 'Pattern)"',
6986          '></rect>'
6987        );
6988      }
6989      else if (this[property] && property === 'overlayColor') {
6990        markup.push(
6991          '<rect x="0" y="0" ',
6992            'width="', this.width,
6993            '" height="', this.height,
6994            '" fill="', this[property], '"',
6995          '></rect>'
6996        );
6997      }
6998    },
6999    /* _TO_SVG_END_ */
7000
7001    /**
7002     * Moves an object to the bottom of the stack of drawn objects
7003     * @param {fabric.Object} object Object to send to back
7004     * @return {fabric.Canvas} thisArg
7005     * @chainable
7006     */
7007    sendToBack: function (object) {
7008      removeFromArray(this._objects, object);
7009      this._objects.unshift(object);
7010      return this.renderAll && this.renderAll();
7011    },
7012
7013    /**
7014     * Moves an object to the top of the stack of drawn objects
7015     * @param {fabric.Object} object Object to send
7016     * @return {fabric.Canvas} thisArg
7017     * @chainable
7018     */
7019    bringToFront: function (object) {
7020      removeFromArray(this._objects, object);
7021      this._objects.push(object);
7022      return this.renderAll && this.renderAll();
7023    },
7024
7025    /**
7026     * Moves an object down in stack of drawn objects
7027     * @param {fabric.Object} object Object to send
7028     * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object
7029     * @return {fabric.Canvas} thisArg
7030     * @chainable
7031     */
7032    sendBackwards: function (object, intersecting) {
7033      var idx = this._objects.indexOf(object);
7034
7035      // if object is not on the bottom of stack
7036      if (idx !== 0) {
7037        var newIdx = this._findNewLowerIndex(object, idx, intersecting);
7038
7039        removeFromArray(this._objects, object);
7040        this._objects.splice(newIdx, 0, object);
7041        this.renderAll && this.renderAll();
7042      }
7043      return this;
7044    },
7045
7046    /**
7047     * @private
7048     */
7049    _findNewLowerIndex: function(object, idx, intersecting) {
7050      var newIdx;
7051
7052      if (intersecting) {
7053        newIdx = idx;
7054
7055        // traverse down the stack looking for the nearest intersecting object
7056        for (var i = idx - 1; i >= 0; --i) {
7057
7058          var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
7059                               object.isContainedWithinObject(this._objects[i]) ||
7060                               this._objects[i].isContainedWithinObject(object);
7061
7062          if (isIntersecting) {
7063            newIdx = i;
7064            break;
7065          }
7066        }
7067      }
7068      else {
7069        newIdx = idx - 1;
7070      }
7071
7072      return newIdx;
7073    },
7074
7075    /**
7076     * Moves an object up in stack of drawn objects
7077     * @param {fabric.Object} object Object to send
7078     * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object
7079     * @return {fabric.Canvas} thisArg
7080     * @chainable
7081     */
7082    bringForward: function (object, intersecting) {
7083      var idx = this._objects.indexOf(object);
7084
7085      // if object is not on top of stack (last item in an array)
7086      if (idx !== this._objects.length - 1) {
7087        var newIdx = this._findNewUpperIndex(object, idx, intersecting);
7088
7089        removeFromArray(this._objects, object);
7090        this._objects.splice(newIdx, 0, object);
7091        this.renderAll && this.renderAll();
7092      }
7093      return this;
7094    },
7095
7096    /**
7097     * @private
7098     */
7099    _findNewUpperIndex: function(object, idx, intersecting) {
7100      var newIdx;
7101
7102      if (intersecting) {
7103        newIdx = idx;
7104
7105        // traverse up the stack looking for the nearest intersecting object
7106        for (var i = idx + 1; i < this._objects.length; ++i) {
7107
7108          var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
7109                               object.isContainedWithinObject(this._objects[i]) ||
7110                               this._objects[i].isContainedWithinObject(object);
7111
7112          if (isIntersecting) {
7113            newIdx = i;
7114            break;
7115          }
7116        }
7117      }
7118      else {
7119        newIdx = idx + 1;
7120      }
7121
7122      return newIdx;
7123    },
7124
7125    /**
7126     * Moves an object to specified level in stack of drawn objects
7127     * @param {fabric.Object} object Object to send
7128     * @param {Number} index Position to move to
7129     * @return {fabric.Canvas} thisArg
7130     * @chainable
7131     */
7132    moveTo: function (object, index) {
7133      removeFromArray(this._objects, object);
7134      this._objects.splice(index, 0, object);
7135      return this.renderAll && this.renderAll();
7136    },
7137
7138    /**
7139     * Clears a canvas element and removes all event listeners
7140     * @return {fabric.Canvas} thisArg
7141     * @chainable
7142     */
7143    dispose: function () {
7144      this.clear();
7145      this.interactive && this.removeListeners();
7146      return this;
7147    },
7148
7149    /**
7150     * Returns a string representation of an instance
7151     * @return {String} string representation of an instance
7152     */
7153    toString: function () {
7154      return '#<fabric.Canvas (' + this.complexity() + '): ' +
7155               '{ objects: ' + this.getObjects().length + ' }>';
7156    }
7157  });
7158
7159  extend(fabric.StaticCanvas.prototype, fabric.Observable);
7160  extend(fabric.StaticCanvas.prototype, fabric.Collection);
7161  extend(fabric.StaticCanvas.prototype, fabric.DataURLExporter);
7162
7163  extend(fabric.StaticCanvas, /** @lends fabric.StaticCanvas */ {
7164
7165    /**
7166     * @static
7167     * @type String
7168     * @default
7169     */
7170    EMPTY_JSON: '{"objects": [], "background": "white"}',
7171
7172    /**
7173     * Provides a way to check support of some of the canvas methods
7174     * (either those of HTMLCanvasElement itself, or rendering context)
7175     *
7176     * @param {String} methodName Method to check support for;
7177     *                            Could be one of "getImageData", "toDataURL", "toDataURLWithQuality" or "setLineDash"
7178     * @return {Boolean | null} `true` if method is supported (or at least exists),
7179     *                          `null` if canvas element or context can not be initialized
7180     */
7181    supports: function (methodName) {
7182      var el = fabric.util.createCanvasElement();
7183
7184      if (!el || !el.getContext) {
7185        return null;
7186      }
7187
7188      var ctx = el.getContext('2d');
7189      if (!ctx) {
7190        return null;
7191      }
7192
7193      switch (methodName) {
7194
7195        case 'getImageData':
7196          return typeof ctx.getImageData !== 'undefined';
7197
7198        case 'setLineDash':
7199          return typeof ctx.setLineDash !== 'undefined';
7200
7201        case 'toDataURL':
7202          return typeof el.toDataURL !== 'undefined';
7203
7204        case 'toDataURLWithQuality':
7205          try {
7206            el.toDataURL('image/jpeg', 0);
7207            return true;
7208          }
7209          catch (e) { }
7210          return false;
7211
7212        default:
7213          return null;
7214      }
7215    }
7216  });
7217
7218  /**
7219   * Returns JSON representation of canvas
7220   * @function
7221   * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
7222   * @return {String} JSON string
7223   * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#serialization}
7224   * @see {@link http://jsfiddle.net/fabricjs/pec86/|jsFiddle demo}
7225   * @example <caption>JSON without additional properties</caption>
7226   * var json = canvas.toJSON();
7227   * @example <caption>JSON with additional properties included</caption>
7228   * var json = canvas.toJSON(['lockMovementX', 'lockMovementY', 'lockRotation', 'lockScalingX', 'lockScalingY', 'lockUniScaling']);
7229   * @example <caption>JSON without default values</caption>
7230   * canvas.includeDefaultValues = false;
7231   * var json = canvas.toJSON();
7232   */
7233  fabric.StaticCanvas.prototype.toJSON = fabric.StaticCanvas.prototype.toObject;
7234
7235})();
7236
7237
7238/**
7239 * BaseBrush class
7240 * @class fabric.BaseBrush
7241 * @see {@link http://fabricjs.com/freedrawing/|Freedrawing demo}
7242 */
7243fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype */ {
7244
7245  /**
7246   * Color of a brush
7247   * @type String
7248   * @default
7249   */
7250  color:            'rgb(0, 0, 0)',
7251
7252  /**
7253   * Width of a brush
7254   * @type Number
7255   * @default
7256   */
7257  width:            1,
7258
7259  /**
7260   * Shadow object representing shadow of this shape.
7261   * <b>Backwards incompatibility note:</b> This property replaces "shadowColor" (String), "shadowOffsetX" (Number),
7262   * "shadowOffsetY" (Number) and "shadowBlur" (Number) since v1.2.12
7263   * @type fabric.Shadow
7264   * @default
7265   */
7266  shadow:          null,
7267
7268  /**
7269   * Line endings style of a brush (one of "butt", "round", "square")
7270   * @type String
7271   * @default
7272   */
7273  strokeLineCap:    'round',
7274
7275  /**
7276   * Corner style of a brush (one of "bevil", "round", "miter")
7277   * @type String
7278   * @default
7279   */
7280  strokeLineJoin:   'round',
7281
7282  /**
7283   * Stroke Dash Array.
7284   * @type Array
7285   * @default
7286   */
7287  strokeDashArray:   null,
7288
7289  /**
7290   * Sets shadow of an object
7291   * @param {Object|String} [options] Options object or string (e.g. "2px 2px 10px rgba(0,0,0,0.2)")
7292   * @return {fabric.Object} thisArg
7293   * @chainable
7294   */
7295  setShadow: function(options) {
7296    this.shadow = new fabric.Shadow(options);
7297    return this;
7298  },
7299
7300  /**
7301   * Sets brush styles
7302   * @private
7303   */
7304  _setBrushStyles: function() {
7305    var ctx = this.canvas.contextTop;
7306
7307    ctx.strokeStyle = this.color;
7308    ctx.lineWidth = this.width;
7309    ctx.lineCap = this.strokeLineCap;
7310    ctx.lineJoin = this.strokeLineJoin;
7311    if (this.strokeDashArray && fabric.StaticCanvas.supports('setLineDash')) {
7312      ctx.setLineDash(this.strokeDashArray);
7313    }
7314  },
7315
7316  /**
7317   * Sets brush shadow styles
7318   * @private
7319   */
7320  _setShadow: function() {
7321    if (!this.shadow) {
7322      return;
7323    }
7324
7325    var ctx = this.canvas.contextTop;
7326
7327    ctx.shadowColor = this.shadow.color;
7328    ctx.shadowBlur = this.shadow.blur;
7329    ctx.shadowOffsetX = this.shadow.offsetX;
7330    ctx.shadowOffsetY = this.shadow.offsetY;
7331  },
7332
7333  /**
7334   * Removes brush shadow styles
7335   * @private
7336   */
7337  _resetShadow: function() {
7338    var ctx = this.canvas.contextTop;
7339
7340    ctx.shadowColor = '';
7341    ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
7342  }
7343});
7344
7345
7346(function() {
7347
7348  /**
7349   * PencilBrush class
7350   * @class fabric.PencilBrush
7351   * @extends fabric.BaseBrush
7352   */
7353  fabric.PencilBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.PencilBrush.prototype */ {
7354
7355    /**
7356     * Constructor
7357     * @param {fabric.Canvas} canvas
7358     * @return {fabric.PencilBrush} Instance of a pencil brush
7359     */
7360    initialize: function(canvas) {
7361      this.canvas = canvas;
7362      this._points = [ ];
7363    },
7364
7365    /**
7366     * Inovoked on mouse down
7367     * @param {Object} pointer
7368     */
7369    onMouseDown: function(pointer) {
7370      this._prepareForDrawing(pointer);
7371      // capture coordinates immediately
7372      // this allows to draw dots (when movement never occurs)
7373      this._captureDrawingPath(pointer);
7374      this._render();
7375    },
7376
7377    /**
7378     * Inovoked on mouse move
7379     * @param {Object} pointer
7380     */
7381    onMouseMove: function(pointer) {
7382      this._captureDrawingPath(pointer);
7383      // redraw curve
7384      // clear top canvas
7385      this.canvas.clearContext(this.canvas.contextTop);
7386      this._render();
7387    },
7388
7389    /**
7390     * Invoked on mouse up
7391     */
7392    onMouseUp: function() {
7393      this._finalizeAndAddPath();
7394    },
7395
7396    /**
7397     * @private
7398     * @param {Object} pointer Actual mouse position related to the canvas.
7399     */
7400    _prepareForDrawing: function(pointer) {
7401
7402      var p = new fabric.Point(pointer.x, pointer.y);
7403
7404      this._reset();
7405      this._addPoint(p);
7406
7407      this.canvas.contextTop.moveTo(p.x, p.y);
7408    },
7409
7410    /**
7411     * @private
7412     * @param {fabric.Point} point Point to be added to points array
7413     */
7414    _addPoint: function(point) {
7415      this._points.push(point);
7416    },
7417
7418    /**
7419     * Clear points array and set contextTop canvas style.
7420     * @private
7421     */
7422    _reset: function() {
7423      this._points.length = 0;
7424
7425      this._setBrushStyles();
7426      this._setShadow();
7427    },
7428
7429    /**
7430     * @private
7431     * @param {Object} pointer Actual mouse position related to the canvas.
7432     */
7433    _captureDrawingPath: function(pointer) {
7434      var pointerPoint = new fabric.Point(pointer.x, pointer.y);
7435      this._addPoint(pointerPoint);
7436    },
7437
7438    /**
7439     * Draw a smooth path on the topCanvas using quadraticCurveTo
7440     * @private
7441     */
7442    _render: function() {
7443      var ctx  = this.canvas.contextTop,
7444          v = this.canvas.viewportTransform,
7445          p1 = this._points[0],
7446          p2 = this._points[1];
7447
7448      ctx.save();
7449      ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
7450      ctx.beginPath();
7451
7452      //if we only have 2 points in the path and they are the same
7453      //it means that the user only clicked the canvas without moving the mouse
7454      //then we should be drawing a dot. A path isn't drawn between two identical dots
7455      //that's why we set them apart a bit
7456      if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) {
7457        p1.x -= 0.5;
7458        p2.x += 0.5;
7459      }
7460      ctx.moveTo(p1.x, p1.y);
7461
7462      for (var i = 1, len = this._points.length; i < len; i++) {
7463        // we pick the point between pi + 1 & pi + 2 as the
7464        // end point and p1 as our control point.
7465        var midPoint = p1.midPointFrom(p2);
7466        ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
7467
7468        p1 = this._points[i];
7469        p2 = this._points[i + 1];
7470      }
7471      // Draw last line as a straight line while
7472      // we wait for the next point to be able to calculate
7473      // the bezier control point
7474      ctx.lineTo(p1.x, p1.y);
7475      ctx.stroke();
7476      ctx.restore();
7477    },
7478
7479    /**
7480     * Converts points to SVG path
7481     * @param {Array} points Array of points
7482     * @param {Number} minX
7483     * @param {Number} minY
7484     * @return {String} SVG path
7485     */
7486    convertPointsToSVGPath: function(points) {
7487      var path = [],
7488          p1 = new fabric.Point(points[0].x, points[0].y),
7489          p2 = new fabric.Point(points[1].x, points[1].y);
7490
7491      path.push('M ', points[0].x, ' ', points[0].y, ' ');
7492      for (var i = 1, len = points.length; i < len; i++) {
7493        var midPoint = p1.midPointFrom(p2);
7494        // p1 is our bezier control point
7495        // midpoint is our endpoint
7496        // start point is p(i-1) value.
7497        path.push('Q ', p1.x, ' ', p1.y, ' ', midPoint.x, ' ', midPoint.y, ' ');
7498        p1 = new fabric.Point(points[i].x, points[i].y);
7499        if ((i + 1) < points.length) {
7500          p2 = new fabric.Point(points[i + 1].x, points[i + 1].y);
7501        }
7502      }
7503      path.push('L ', p1.x, ' ', p1.y, ' ');
7504      return path;
7505    },
7506
7507    /**
7508     * Creates fabric.Path object to add on canvas
7509     * @param {String} pathData Path data
7510     * @return {fabric.Path} Path to add on canvas
7511     */
7512    createPath: function(pathData) {
7513      var path = new fabric.Path(pathData, {
7514                   fill: null,
7515                   stroke: this.color,
7516                   strokeWidth: this.width,
7517                   strokeLineCap: this.strokeLineCap,
7518                   strokeLineJoin: this.strokeLineJoin,
7519                   strokeDashArray: this.strokeDashArray,
7520                   originX: 'center',
7521                   originY: 'center'
7522                 });
7523
7524      if (this.shadow) {
7525        this.shadow.affectStroke = true;
7526        path.setShadow(this.shadow);
7527      }
7528
7529      return path;
7530    },
7531
7532    /**
7533     * On mouseup after drawing the path on contextTop canvas
7534     * we use the points captured to create an new fabric path object
7535     * and add it to the fabric canvas.
7536     */
7537    _finalizeAndAddPath: function() {
7538      var ctx = this.canvas.contextTop;
7539      ctx.closePath();
7540
7541      var pathData = this.convertPointsToSVGPath(this._points).join('');
7542      if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') {
7543        // do not create 0 width/height paths, as they are
7544        // rendered inconsistently across browsers
7545        // Firefox 4, for example, renders a dot,
7546        // whereas Chrome 10 renders nothing
7547        this.canvas.renderAll();
7548        return;
7549      }
7550
7551      var path = this.createPath(pathData);
7552
7553      this.canvas.add(path);
7554      path.setCoords();
7555
7556      this.canvas.clearContext(this.canvas.contextTop);
7557      this._resetShadow();
7558      this.canvas.renderAll();
7559
7560      // fire event 'path' created
7561      this.canvas.fire('path:created', { path: path });
7562    }
7563  });
7564})();
7565
7566
7567/**
7568 * CircleBrush class
7569 * @class fabric.CircleBrush
7570 */
7571fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.CircleBrush.prototype */ {
7572
7573  /**
7574   * Width of a brush
7575   * @type Number
7576   * @default
7577   */
7578  width: 10,
7579
7580  /**
7581   * Constructor
7582   * @param {fabric.Canvas} canvas
7583   * @return {fabric.CircleBrush} Instance of a circle brush
7584   */
7585  initialize: function(canvas) {
7586    this.canvas = canvas;
7587    this.points = [ ];
7588  },
7589  /**
7590  * Invoked inside on mouse down and mouse move
7591  * @param {Object} pointer
7592  */
7593  drawDot: function(pointer) {
7594    var point = this.addPoint(pointer),
7595        ctx = this.canvas.contextTop,
7596        v = this.canvas.viewportTransform;
7597    ctx.save();
7598    ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
7599
7600    ctx.fillStyle = point.fill;
7601    ctx.beginPath();
7602    ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false);
7603    ctx.closePath();
7604    ctx.fill();
7605
7606    ctx.restore();
7607  },
7608
7609  /**
7610   * Invoked on mouse down
7611   */
7612  onMouseDown: function(pointer) {
7613    this.points.length = 0;
7614    this.canvas.clearContext(this.canvas.contextTop);
7615    this._setShadow();
7616    this.drawDot(pointer);
7617  },
7618
7619  /**
7620   * Invoked on mouse move
7621   * @param {Object} pointer
7622   */
7623  onMouseMove: function(pointer) {
7624    this.drawDot(pointer);
7625  },
7626
7627  /**
7628   * Invoked on mouse up
7629   */
7630  onMouseUp: function() {
7631    var originalRenderOnAddRemove = this.canvas.renderOnAddRemove;
7632    this.canvas.renderOnAddRemove = false;
7633
7634    var circles = [ ];
7635
7636    for (var i = 0, len = this.points.length; i < len; i++) {
7637      var point = this.points[i],
7638          circle = new fabric.Circle({
7639            radius: point.radius,
7640            left: point.x,
7641            top: point.y,
7642            originX: 'center',
7643            originY: 'center',
7644            fill: point.fill
7645          });
7646
7647      this.shadow && circle.setShadow(this.shadow);
7648
7649      circles.push(circle);
7650    }
7651    var group = new fabric.Group(circles, { originX: 'center', originY: 'center' });
7652    group.canvas = this.canvas;
7653
7654    this.canvas.add(group);
7655    this.canvas.fire('path:created', { path: group });
7656
7657    this.canvas.clearContext(this.canvas.contextTop);
7658    this._resetShadow();
7659    this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
7660    this.canvas.renderAll();
7661  },
7662
7663  /**
7664   * @param {Object} pointer
7665   * @return {fabric.Point} Just added pointer point
7666   */
7667  addPoint: function(pointer) {
7668    var pointerPoint = new fabric.Point(pointer.x, pointer.y),
7669
7670        circleRadius = fabric.util.getRandomInt(
7671                        Math.max(0, this.width - 20), this.width + 20) / 2,
7672
7673        circleColor = new fabric.Color(this.color)
7674                        .setAlpha(fabric.util.getRandomInt(0, 100) / 100)
7675                        .toRgba();
7676
7677    pointerPoint.radius = circleRadius;
7678    pointerPoint.fill = circleColor;
7679
7680    this.points.push(pointerPoint);
7681
7682    return pointerPoint;
7683  }
7684});
7685
7686
7687/**
7688 * SprayBrush class
7689 * @class fabric.SprayBrush
7690 */
7691fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric.SprayBrush.prototype */ {
7692
7693  /**
7694   * Width of a spray
7695   * @type Number
7696   * @default
7697   */
7698  width:              10,
7699
7700  /**
7701   * Density of a spray (number of dots per chunk)
7702   * @type Number
7703   * @default
7704   */
7705  density:            20,
7706
7707  /**
7708   * Width of spray dots
7709   * @type Number
7710   * @default
7711   */
7712  dotWidth:           1,
7713
7714  /**
7715   * Width variance of spray dots
7716   * @type Number
7717   * @default
7718   */
7719  dotWidthVariance:   1,
7720
7721  /**
7722   * Whether opacity of a dot should be random
7723   * @type Boolean
7724   * @default
7725   */
7726  randomOpacity:        false,
7727
7728  /**
7729   * Whether overlapping dots (rectangles) should be removed (for performance reasons)
7730   * @type Boolean
7731   * @default
7732   */
7733  optimizeOverlapping:  true,
7734
7735  /**
7736   * Constructor
7737   * @param {fabric.Canvas} canvas
7738   * @return {fabric.SprayBrush} Instance of a spray brush
7739   */
7740  initialize: function(canvas) {
7741    this.canvas = canvas;
7742    this.sprayChunks = [ ];
7743  },
7744
7745  /**
7746   * Invoked on mouse down
7747   * @param {Object} pointer
7748   */
7749  onMouseDown: function(pointer) {
7750    this.sprayChunks.length = 0;
7751    this.canvas.clearContext(this.canvas.contextTop);
7752    this._setShadow();
7753
7754    this.addSprayChunk(pointer);
7755    this.render();
7756  },
7757
7758  /**
7759   * Invoked on mouse move
7760   * @param {Object} pointer
7761   */
7762  onMouseMove: function(pointer) {
7763    this.addSprayChunk(pointer);
7764    this.render();
7765  },
7766
7767  /**
7768   * Invoked on mouse up
7769   */
7770  onMouseUp: function() {
7771    var originalRenderOnAddRemove = this.canvas.renderOnAddRemove;
7772    this.canvas.renderOnAddRemove = false;
7773
7774    var rects = [ ];
7775
7776    for (var i = 0, ilen = this.sprayChunks.length; i < ilen; i++) {
7777      var sprayChunk = this.sprayChunks[i];
7778
7779      for (var j = 0, jlen = sprayChunk.length; j < jlen; j++) {
7780
7781        var rect = new fabric.Rect({
7782          width: sprayChunk[j].width,
7783          height: sprayChunk[j].width,
7784          left: sprayChunk[j].x + 1,
7785          top: sprayChunk[j].y + 1,
7786          originX: 'center',
7787          originY: 'center',
7788          fill: this.color
7789        });
7790
7791        this.shadow && rect.setShadow(this.shadow);
7792        rects.push(rect);
7793      }
7794    }
7795
7796    if (this.optimizeOverlapping) {
7797      rects = this._getOptimizedRects(rects);
7798    }
7799
7800    var group = new fabric.Group(rects, { originX: 'center', originY: 'center' });
7801    group.canvas = this.canvas;
7802
7803    this.canvas.add(group);
7804    this.canvas.fire('path:created', { path: group });
7805
7806    this.canvas.clearContext(this.canvas.contextTop);
7807    this._resetShadow();
7808    this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
7809    this.canvas.renderAll();
7810  },
7811
7812  /**
7813   * @private
7814   * @param {Array} rects
7815   */
7816  _getOptimizedRects: function(rects) {
7817
7818    // avoid creating duplicate rects at the same coordinates
7819    var uniqueRects = { }, key;
7820
7821    for (var i = 0, len = rects.length; i < len; i++) {
7822      key = rects[i].left + '' + rects[i].top;
7823      if (!uniqueRects[key]) {
7824        uniqueRects[key] = rects[i];
7825      }
7826    }
7827    var uniqueRectsArray = [ ];
7828    for (key in uniqueRects) {
7829      uniqueRectsArray.push(uniqueRects[key]);
7830    }
7831
7832    return uniqueRectsArray;
7833  },
7834
7835  /**
7836   * Renders brush
7837   */
7838  render: function() {
7839    var ctx = this.canvas.contextTop;
7840    ctx.fillStyle = this.color;
7841
7842    var v = this.canvas.viewportTransform;
7843    ctx.save();
7844    ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
7845
7846    for (var i = 0, len = this.sprayChunkPoints.length; i < len; i++) {
7847      var point = this.sprayChunkPoints[i];
7848      if (typeof point.opacity !== 'undefined') {
7849        ctx.globalAlpha = point.opacity;
7850      }
7851      ctx.fillRect(point.x, point.y, point.width, point.width);
7852    }
7853    ctx.restore();
7854  },
7855
7856  /**
7857   * @param {Object} pointer
7858   */
7859  addSprayChunk: function(pointer) {
7860    this.sprayChunkPoints = [ ];
7861
7862    var x, y, width, radius = this.width / 2;
7863
7864    for (var i = 0; i < this.density; i++) {
7865
7866      x = fabric.util.getRandomInt(pointer.x - radius, pointer.x + radius);
7867      y = fabric.util.getRandomInt(pointer.y - radius, pointer.y + radius);
7868
7869      if (this.dotWidthVariance) {
7870        width = fabric.util.getRandomInt(
7871          // bottom clamp width to 1
7872          Math.max(1, this.dotWidth - this.dotWidthVariance),
7873          this.dotWidth + this.dotWidthVariance);
7874      }
7875      else {
7876        width = this.dotWidth;
7877      }
7878
7879      var point = new fabric.Point(x, y);
7880      point.width = width;
7881
7882      if (this.randomOpacity) {
7883        point.opacity = fabric.util.getRandomInt(0, 100) / 100;
7884      }
7885
7886      this.sprayChunkPoints.push(point);
7887    }
7888
7889    this.sprayChunks.push(this.sprayChunkPoints);
7890  }
7891});
7892
7893
7894/**
7895 * PatternBrush class
7896 * @class fabric.PatternBrush
7897 * @extends fabric.BaseBrush
7898 */
7899fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fabric.PatternBrush.prototype */ {
7900
7901  getPatternSrc: function() {
7902
7903    var dotWidth = 20,
7904        dotDistance = 5,
7905        patternCanvas = fabric.document.createElement('canvas'),
7906        patternCtx = patternCanvas.getContext('2d');
7907
7908    patternCanvas.width = patternCanvas.height = dotWidth + dotDistance;
7909
7910    patternCtx.fillStyle = this.color;
7911    patternCtx.beginPath();
7912    patternCtx.arc(dotWidth / 2, dotWidth / 2, dotWidth / 2, 0, Math.PI * 2, false);
7913    patternCtx.closePath();
7914    patternCtx.fill();
7915
7916    return patternCanvas;
7917  },
7918
7919  getPatternSrcFunction: function() {
7920    return String(this.getPatternSrc).replace('this.color', '"' + this.color + '"');
7921  },
7922
7923  /**
7924   * Creates "pattern" instance property
7925   */
7926  getPattern: function() {
7927    return this.canvas.contextTop.createPattern(this.source || this.getPatternSrc(), 'repeat');
7928  },
7929
7930  /**
7931   * Sets brush styles
7932   */
7933  _setBrushStyles: function() {
7934    this.callSuper('_setBrushStyles');
7935    this.canvas.contextTop.strokeStyle = this.getPattern();
7936  },
7937
7938  /**
7939   * Creates path
7940   */
7941  createPath: function(pathData) {
7942    var path = this.callSuper('createPath', pathData);
7943    path.stroke = new fabric.Pattern({
7944      source: this.source || this.getPatternSrcFunction()
7945    });
7946    return path;
7947  }
7948});
7949
7950
7951(function() {
7952
7953  var getPointer = fabric.util.getPointer,
7954      degreesToRadians = fabric.util.degreesToRadians,
7955      radiansToDegrees = fabric.util.radiansToDegrees,
7956      atan2 = Math.atan2,
7957      abs = Math.abs,
7958
7959      STROKE_OFFSET = 0.5;
7960
7961  /**
7962   * Canvas class
7963   * @class fabric.Canvas
7964   * @extends fabric.StaticCanvas
7965   * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#canvas}
7966   * @see {@link fabric.Canvas#initialize} for constructor definition
7967   *
7968   * @fires object:modified
7969   * @fires object:rotating
7970   * @fires object:scaling
7971   * @fires object:moving
7972   * @fires object:selected
7973   *
7974   * @fires before:selection:cleared
7975   * @fires selection:cleared
7976   * @fires selection:created
7977   *
7978   * @fires path:created
7979   * @fires mouse:down
7980   * @fires mouse:move
7981   * @fires mouse:up
7982   * @fires mouse:over
7983   * @fires mouse:out
7984   *
7985   */
7986  fabric.Canvas = fabric.util.createClass(fabric.StaticCanvas, /** @lends fabric.Canvas.prototype */ {
7987
7988    /**
7989     * Constructor
7990     * @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
7991     * @param {Object} [options] Options object
7992     * @return {Object} thisArg
7993     */
7994    initialize: function(el, options) {
7995      options || (options = { });
7996
7997      this._initStatic(el, options);
7998      this._initInteractive();
7999      this._createCacheCanvas();
8000
8001      fabric.Canvas.activeInstance = this;
8002    },
8003
8004    /**
8005     * When true, objects can be transformed by one side (unproportionally)
8006     * @type Boolean
8007     * @default
8008     */
8009    uniScaleTransform:      false,
8010
8011    /**
8012     * When true, objects use center point as the origin of scale transformation.
8013     * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
8014     * @since 1.3.4
8015     * @type Boolean
8016     * @default
8017     */
8018    centeredScaling:        false,
8019
8020    /**
8021     * When true, objects use center point as the origin of rotate transformation.
8022     * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
8023     * @since 1.3.4
8024     * @type Boolean
8025     * @default
8026     */
8027    centeredRotation:       false,
8028
8029    /**
8030     * Indicates that canvas is interactive. This property should not be changed.
8031     * @type Boolean
8032     * @default
8033     */
8034    interactive:            true,
8035
8036    /**
8037     * Indicates whether group selection should be enabled
8038     * @type Boolean
8039     * @default
8040     */
8041    selection:              true,
8042
8043    /**
8044     * Color of selection
8045     * @type String
8046     * @default
8047     */
8048    selectionColor:         'rgba(100, 100, 255, 0.3)', // blue
8049
8050    /**
8051     * Default dash array pattern
8052     * If not empty the selection border is dashed
8053     * @type Array
8054     */
8055    selectionDashArray:     [ ],
8056
8057    /**
8058     * Color of the border of selection (usually slightly darker than color of selection itself)
8059     * @type String
8060     * @default
8061     */
8062    selectionBorderColor:   'rgba(255, 255, 255, 0.3)',
8063
8064    /**
8065     * Width of a line used in object/group selection
8066     * @type Number
8067     * @default
8068     */
8069    selectionLineWidth:     1,
8070
8071    /**
8072     * Default cursor value used when hovering over an object on canvas
8073     * @type String
8074     * @default
8075     */
8076    hoverCursor:            'move',
8077
8078    /**
8079     * Default cursor value used when moving an object on canvas
8080     * @type String
8081     * @default
8082     */
8083    moveCursor:             'move',
8084
8085    /**
8086     * Default cursor value used for the entire canvas
8087     * @type String
8088     * @default
8089     */
8090    defaultCursor:          'default',
8091
8092    /**
8093     * Cursor value used during free drawing
8094     * @type String
8095     * @default
8096     */
8097    freeDrawingCursor:      'crosshair',
8098
8099    /**
8100     * Cursor value used for rotation point
8101     * @type String
8102     * @default
8103     */
8104    rotationCursor:         'crosshair',
8105
8106    /**
8107     * Default element class that's given to wrapper (div) element of canvas
8108     * @type String
8109     * @default
8110     */
8111    containerClass:         'canvas-container',
8112
8113    /**
8114     * When true, object detection happens on per-pixel basis rather than on per-bounding-box
8115     * @type Boolean
8116     * @default
8117     */
8118    perPixelTargetFind:     false,
8119
8120    /**
8121     * Number of pixels around target pixel to tolerate (consider active) during object detection
8122     * @type Number
8123     * @default
8124     */
8125    targetFindTolerance:    0,
8126
8127    /**
8128     * When true, target detection is skipped when hovering over canvas. This can be used to improve performance.
8129     * @type Boolean
8130     * @default
8131     */
8132    skipTargetFind:         false,
8133
8134    /**
8135     * @private
8136     */
8137    _initInteractive: function() {
8138      this._currentTransform = null;
8139      this._groupSelector = null;
8140      this._initWrapperElement();
8141      this._createUpperCanvas();
8142      this._initEventListeners();
8143
8144      this.freeDrawingBrush = fabric.PencilBrush && new fabric.PencilBrush(this);
8145
8146      this.calcOffset();
8147    },
8148
8149    /**
8150     * Resets the current transform to its original values and chooses the type of resizing based on the event
8151     * @private
8152     * @param {Event} e Event object fired on mousemove
8153     */
8154    _resetCurrentTransform: function(e) {
8155      var t = this._currentTransform;
8156
8157      t.target.set({
8158        scaleX: t.original.scaleX,
8159        scaleY: t.original.scaleY,
8160        left: t.original.left,
8161        top: t.original.top
8162      });
8163
8164      if (this._shouldCenterTransform(e, t.target)) {
8165        if (t.action === 'rotate') {
8166          this._setOriginToCenter(t.target);
8167        }
8168        else {
8169          if (t.originX !== 'center') {
8170            if (t.originX === 'right') {
8171              t.mouseXSign = -1;
8172            }
8173            else {
8174              t.mouseXSign = 1;
8175            }
8176          }
8177          if (t.originY !== 'center') {
8178            if (t.originY === 'bottom') {
8179              t.mouseYSign = -1;
8180            }
8181            else {
8182              t.mouseYSign = 1;
8183            }
8184          }
8185
8186          t.originX = 'center';
8187          t.originY = 'center';
8188        }
8189      }
8190      else {
8191        t.originX = t.original.originX;
8192        t.originY = t.original.originY;
8193      }
8194    },
8195
8196    /**
8197     * Checks if point is contained within an area of given object
8198     * @param {Event} e Event object
8199     * @param {fabric.Object} target Object to test against
8200     * @return {Boolean} true if point is contained within an area of given object
8201     */
8202    containsPoint: function (e, target) {
8203      var pointer = this.getPointer(e, true),
8204          xy = this._normalizePointer(target, pointer);
8205
8206      // http://www.geog.ubc.ca/courses/klink/gis.notes/ncgia/u32.html
8207      // http://idav.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html
8208      return (target.containsPoint(xy) || target._findTargetCorner(pointer));
8209    },
8210
8211    /**
8212     * @private
8213     */
8214    _normalizePointer: function (object, pointer) {
8215      var activeGroup = this.getActiveGroup(),
8216          x = pointer.x,
8217          y = pointer.y,
8218          isObjectInGroup = (
8219            activeGroup &&
8220            object.type !== 'group' &&
8221            activeGroup.contains(object)),
8222          lt;
8223
8224      if (isObjectInGroup) {
8225        lt = new fabric.Point(activeGroup.left, activeGroup.top);
8226        lt = fabric.util.transformPoint(lt, this.viewportTransform, true);
8227        x -= lt.x;
8228        y -= lt.y;
8229      }
8230      return { x: x, y: y };
8231    },
8232
8233    /**
8234     * Returns true if object is transparent at a certain location
8235     * @param {fabric.Object} target Object to check
8236     * @param {Number} x Left coordinate
8237     * @param {Number} y Top coordinate
8238     * @return {Boolean}
8239     */
8240    isTargetTransparent: function (target, x, y) {
8241      var hasBorders = target.hasBorders,
8242          transparentCorners = target.transparentCorners;
8243
8244      target.hasBorders = target.transparentCorners = false;
8245
8246      this._draw(this.contextCache, target);
8247
8248      target.hasBorders = hasBorders;
8249      target.transparentCorners = transparentCorners;
8250
8251      var isTransparent = fabric.util.isTransparent(
8252        this.contextCache, x, y, this.targetFindTolerance);
8253
8254      this.clearContext(this.contextCache);
8255
8256      return isTransparent;
8257    },
8258
8259    /**
8260     * @private
8261     * @param {Event} e Event object
8262     * @param {fabric.Object} target
8263     */
8264    _shouldClearSelection: function (e, target) {
8265      var activeGroup = this.getActiveGroup(),
8266          activeObject = this.getActiveObject();
8267
8268      return (
8269        !target
8270        ||
8271        (target &&
8272          activeGroup &&
8273          !activeGroup.contains(target) &&
8274          activeGroup !== target &&
8275          !e.shiftKey)
8276        ||
8277        (target && !target.evented)
8278        ||
8279        (target &&
8280          !target.selectable &&
8281          activeObject &&
8282          activeObject !== target)
8283      );
8284    },
8285
8286    /**
8287     * @private
8288     * @param {Event} e Event object
8289     * @param {fabric.Object} target
8290     */
8291    _shouldCenterTransform: function (e, target) {
8292      if (!target) {
8293        return;
8294      }
8295
8296      var t = this._currentTransform,
8297          centerTransform;
8298
8299      if (t.action === 'scale' || t.action === 'scaleX' || t.action === 'scaleY') {
8300        centerTransform = this.centeredScaling || target.centeredScaling;
8301      }
8302      else if (t.action === 'rotate') {
8303        centerTransform = this.centeredRotation || target.centeredRotation;
8304      }
8305
8306      return centerTransform ? !e.altKey : e.altKey;
8307    },
8308
8309    /**
8310     * @private
8311     */
8312    _getOriginFromCorner: function(target, corner) {
8313      var origin = {
8314        x: target.originX,
8315        y: target.originY
8316      };
8317
8318      if (corner === 'ml' || corner === 'tl' || corner === 'bl') {
8319        origin.x = 'right';
8320      }
8321      else if (corner === 'mr' || corner === 'tr' || corner === 'br') {
8322        origin.x = 'left';
8323      }
8324
8325      if (corner === 'tl' || corner === 'mt' || corner === 'tr') {
8326        origin.y = 'bottom';
8327      }
8328      else if (corner === 'bl' || corner === 'mb' || corner === 'br') {
8329        origin.y = 'top';
8330      }
8331
8332      return origin;
8333    },
8334
8335    /**
8336     * @private
8337     */
8338    _getActionFromCorner: function(target, corner) {
8339      var action = 'drag';
8340      if (corner) {
8341        action = (corner === 'ml' || corner === 'mr')
8342          ? 'scaleX'
8343          : (corner === 'mt' || corner === 'mb')
8344            ? 'scaleY'
8345            : corner === 'mtr'
8346              ? 'rotate'
8347              : 'scale';
8348      }
8349      return action;
8350    },
8351
8352    /**
8353     * @private
8354     * @param {Event} e Event object
8355     * @param {fabric.Object} target
8356     */
8357    _setupCurrentTransform: function (e, target) {
8358      if (!target) {
8359        return;
8360      }
8361
8362      var pointer = this.getPointer(e),
8363          corner = target._findTargetCorner(this.getPointer(e, true)),
8364          action = this._getActionFromCorner(target, corner),
8365          origin = this._getOriginFromCorner(target, corner);
8366
8367      this._currentTransform = {
8368        target: target,
8369        action: action,
8370        scaleX: target.scaleX,
8371        scaleY: target.scaleY,
8372        offsetX: pointer.x - target.left,
8373        offsetY: pointer.y - target.top,
8374        originX: origin.x,
8375        originY: origin.y,
8376        ex: pointer.x,
8377        ey: pointer.y,
8378        left: target.left,
8379        top: target.top,
8380        theta: degreesToRadians(target.angle),
8381        width: target.width * target.scaleX,
8382        mouseXSign: 1,
8383        mouseYSign: 1
8384      };
8385
8386      this._currentTransform.original = {
8387        left: target.left,
8388        top: target.top,
8389        scaleX: target.scaleX,
8390        scaleY: target.scaleY,
8391        originX: origin.x,
8392        originY: origin.y
8393      };
8394
8395      this._resetCurrentTransform(e);
8396    },
8397
8398    /**
8399     * Translates object by "setting" its left/top
8400     * @private
8401     * @param {Number} x pointer's x coordinate
8402     * @param {Number} y pointer's y coordinate
8403     */
8404    _translateObject: function (x, y) {
8405      var target = this._currentTransform.target;
8406
8407      if (!target.get('lockMovementX')) {
8408        target.set('left', x - this._currentTransform.offsetX);
8409      }
8410      if (!target.get('lockMovementY')) {
8411        target.set('top', y - this._currentTransform.offsetY);
8412      }
8413    },
8414
8415    /**
8416     * Scales object by invoking its scaleX/scaleY methods
8417     * @private
8418     * @param {Number} x pointer's x coordinate
8419     * @param {Number} y pointer's y coordinate
8420     * @param {String} by Either 'x' or 'y' - specifies dimension constraint by which to scale an object.
8421     *                    When not provided, an object is scaled by both dimensions equally
8422     */
8423    _scaleObject: function (x, y, by) {
8424      var t = this._currentTransform,
8425          target = t.target,
8426          lockScalingX = target.get('lockScalingX'),
8427          lockScalingY = target.get('lockScalingY'),
8428          lockScalingFlip = target.get('lockScalingFlip');
8429
8430      if (lockScalingX && lockScalingY) {
8431        return;
8432      }
8433
8434      // Get the constraint point
8435      var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY),
8436          localMouse = target.toLocalPoint(new fabric.Point(x, y), t.originX, t.originY);
8437
8438      this._setLocalMouse(localMouse, t);
8439
8440      // Actually scale the object
8441      this._setObjectScale(localMouse, t, lockScalingX, lockScalingY, by, lockScalingFlip);
8442
8443      // Make sure the constraints apply
8444      target.setPositionByOrigin(constraintPosition, t.originX, t.originY);
8445    },
8446
8447    /**
8448     * @private
8449     */
8450    _setObjectScale: function(localMouse, transform, lockScalingX, lockScalingY, by, lockScalingFlip) {
8451      var target = transform.target, forbidScalingX = false, forbidScalingY = false,
8452          strokeWidth = target.stroke ? target.strokeWidth : 0;
8453
8454      transform.newScaleX = localMouse.x / (target.width + strokeWidth / 2);
8455      transform.newScaleY = localMouse.y / (target.height + strokeWidth / 2);
8456
8457      if (lockScalingFlip && transform.newScaleX <= 0 && transform.newScaleX < target.scaleX) {
8458        forbidScalingX = true;
8459      }
8460
8461      if (lockScalingFlip && transform.newScaleY <= 0 && transform.newScaleY < target.scaleY) {
8462        forbidScalingY = true;
8463      }
8464
8465      if (by === 'equally' && !lockScalingX && !lockScalingY) {
8466        forbidScalingX || forbidScalingY || this._scaleObjectEqually(localMouse, target, transform);
8467      }
8468      else if (!by) {
8469        forbidScalingX || lockScalingX || target.set('scaleX', transform.newScaleX);
8470        forbidScalingY || lockScalingY || target.set('scaleY', transform.newScaleY);
8471      }
8472      else if (by === 'x' && !target.get('lockUniScaling')) {
8473        forbidScalingX || lockScalingX || target.set('scaleX', transform.newScaleX);
8474      }
8475      else if (by === 'y' && !target.get('lockUniScaling')) {
8476        forbidScalingY || lockScalingY || target.set('scaleY', transform.newScaleY);
8477      }
8478
8479      forbidScalingX || forbidScalingY || this._flipObject(transform, by);
8480
8481    },
8482
8483    /**
8484     * @private
8485     */
8486    _scaleObjectEqually: function(localMouse, target, transform) {
8487
8488      var dist = localMouse.y + localMouse.x,
8489          strokeWidth = target.stroke ? target.strokeWidth : 0,
8490          lastDist = (target.height + (strokeWidth / 2)) * transform.original.scaleY +
8491                     (target.width + (strokeWidth / 2)) * transform.original.scaleX;
8492
8493      // We use transform.scaleX/Y instead of target.scaleX/Y
8494      // because the object may have a min scale and we'll loose the proportions
8495      transform.newScaleX = transform.original.scaleX * dist / lastDist;
8496      transform.newScaleY = transform.original.scaleY * dist / lastDist;
8497
8498      target.set('scaleX', transform.newScaleX);
8499      target.set('scaleY', transform.newScaleY);
8500    },
8501
8502    /**
8503     * @private
8504     */
8505    _flipObject: function(transform, by) {
8506      if (transform.newScaleX < 0 && by !== 'y') {
8507        if (transform.originX === 'left') {
8508          transform.originX = 'right';
8509        }
8510        else if (transform.originX === 'right') {
8511          transform.originX = 'left';
8512        }
8513      }
8514
8515      if (transform.newScaleY < 0 && by !== 'x') {
8516        if (transform.originY === 'top') {
8517          transform.originY = 'bottom';
8518        }
8519        else if (transform.originY === 'bottom') {
8520          transform.originY = 'top';
8521        }
8522      }
8523    },
8524
8525    /**
8526     * @private
8527     */
8528    _setLocalMouse: function(localMouse, t) {
8529      var target = t.target;
8530
8531      if (t.originX === 'right') {
8532        localMouse.x *= -1;
8533      }
8534      else if (t.originX === 'center') {
8535        localMouse.x *= t.mouseXSign * 2;
8536
8537        if (localMouse.x < 0) {
8538          t.mouseXSign = -t.mouseXSign;
8539        }
8540      }
8541
8542      if (t.originY === 'bottom') {
8543        localMouse.y *= -1;
8544      }
8545      else if (t.originY === 'center') {
8546        localMouse.y *= t.mouseYSign * 2;
8547
8548        if (localMouse.y < 0) {
8549          t.mouseYSign = -t.mouseYSign;
8550        }
8551      }
8552
8553      // adjust the mouse coordinates when dealing with padding
8554      if (abs(localMouse.x) > target.padding) {
8555        if (localMouse.x < 0) {
8556          localMouse.x += target.padding;
8557        }
8558        else {
8559          localMouse.x -= target.padding;
8560        }
8561      }
8562      else { // mouse is within the padding, set to 0
8563        localMouse.x = 0;
8564      }
8565
8566      if (abs(localMouse.y) > target.padding) {
8567        if (localMouse.y < 0) {
8568          localMouse.y += target.padding;
8569        }
8570        else {
8571          localMouse.y -= target.padding;
8572        }
8573      }
8574      else {
8575        localMouse.y = 0;
8576      }
8577    },
8578
8579    /**
8580     * Rotates object by invoking its rotate method
8581     * @private
8582     * @param {Number} x pointer's x coordinate
8583     * @param {Number} y pointer's y coordinate
8584     */
8585    _rotateObject: function (x, y) {
8586
8587      var t = this._currentTransform;
8588
8589      if (t.target.get('lockRotation')) {
8590        return;
8591      }
8592
8593      var lastAngle = atan2(t.ey - t.top, t.ex - t.left),
8594          curAngle = atan2(y - t.top, x - t.left),
8595          angle = radiansToDegrees(curAngle - lastAngle + t.theta);
8596
8597      // normalize angle to positive value
8598      if (angle < 0) {
8599        angle = 360 + angle;
8600      }
8601
8602      t.target.angle = angle % 360;
8603    },
8604
8605    /**
8606     * Set the cursor type of the canvas element
8607     * @param {String} value Cursor type of the canvas element.
8608     * @see http://www.w3.org/TR/css3-ui/#cursor
8609     */
8610    setCursor: function (value) {
8611      this.upperCanvasEl.style.cursor = value;
8612    },
8613
8614    /**
8615     * @private
8616     */
8617    _resetObjectTransform: function (target) {
8618      target.scaleX = 1;
8619      target.scaleY = 1;
8620      target.setAngle(0);
8621    },
8622
8623    /**
8624     * @private
8625     */
8626    _drawSelection: function () {
8627      var ctx = this.contextTop,
8628          groupSelector = this._groupSelector,
8629          left = groupSelector.left,
8630          top = groupSelector.top,
8631          aleft = abs(left),
8632          atop = abs(top);
8633
8634      ctx.fillStyle = this.selectionColor;
8635
8636      ctx.fillRect(
8637        groupSelector.ex - ((left > 0) ? 0 : -left),
8638        groupSelector.ey - ((top > 0) ? 0 : -top),
8639        aleft,
8640        atop
8641      );
8642
8643      ctx.lineWidth = this.selectionLineWidth;
8644      ctx.strokeStyle = this.selectionBorderColor;
8645
8646      // selection border
8647      if (this.selectionDashArray.length > 1) {
8648
8649        var px = groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0: aleft),
8650            py = groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0: atop);
8651
8652        ctx.beginPath();
8653
8654        fabric.util.drawDashedLine(ctx, px, py, px + aleft, py, this.selectionDashArray);
8655        fabric.util.drawDashedLine(ctx, px, py + atop - 1, px + aleft, py + atop - 1, this.selectionDashArray);
8656        fabric.util.drawDashedLine(ctx, px, py, px, py + atop, this.selectionDashArray);
8657        fabric.util.drawDashedLine(ctx, px + aleft - 1, py, px + aleft - 1, py + atop, this.selectionDashArray);
8658
8659        ctx.closePath();
8660        ctx.stroke();
8661      }
8662      else {
8663        ctx.strokeRect(
8664          groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0 : aleft),
8665          groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0 : atop),
8666          aleft,
8667          atop
8668        );
8669      }
8670    },
8671
8672    /**
8673     * @private
8674     */
8675    _isLastRenderedObject: function(e) {
8676      return (
8677        this.controlsAboveOverlay &&
8678        this.lastRenderedObjectWithControlsAboveOverlay &&
8679        this.lastRenderedObjectWithControlsAboveOverlay.visible &&
8680        this.containsPoint(e, this.lastRenderedObjectWithControlsAboveOverlay) &&
8681        this.lastRenderedObjectWithControlsAboveOverlay._findTargetCorner(this.getPointer(e, true)));
8682    },
8683
8684    /**
8685     * Method that determines what object we are clicking on
8686     * @param {Event} e mouse event
8687     * @param {Boolean} skipGroup when true, group is skipped and only objects are traversed through
8688     */
8689    findTarget: function (e, skipGroup) {
8690      if (this.skipTargetFind) {
8691        return;
8692      }
8693
8694      if (this._isLastRenderedObject(e)) {
8695        return this.lastRenderedObjectWithControlsAboveOverlay;
8696      }
8697
8698      // first check current group (if one exists)
8699      var activeGroup = this.getActiveGroup();
8700      if (activeGroup && !skipGroup && this.containsPoint(e, activeGroup)) {
8701        return activeGroup;
8702      }
8703
8704      var target = this._searchPossibleTargets(e);
8705      this._fireOverOutEvents(target);
8706
8707      return target;
8708    },
8709
8710    /**
8711     * @private
8712     */
8713    _fireOverOutEvents: function(target) {
8714      if (target) {
8715        if (this._hoveredTarget !== target) {
8716          this.fire('mouse:over', { target: target });
8717          target.fire('mouseover');
8718          if (this._hoveredTarget) {
8719            this.fire('mouse:out', { target: this._hoveredTarget });
8720            this._hoveredTarget.fire('mouseout');
8721          }
8722          this._hoveredTarget = target;
8723        }
8724      }
8725      else if (this._hoveredTarget) {
8726        this.fire('mouse:out', { target: this._hoveredTarget });
8727        this._hoveredTarget.fire('mouseout');
8728        this._hoveredTarget = null;
8729      }
8730    },
8731
8732    /**
8733    * @private
8734    */
8735    _checkTarget: function(e, obj, pointer) {
8736      if (obj &&
8737          obj.visible &&
8738          obj.evented &&
8739          this.containsPoint(e, obj)){
8740        if ((this.perPixelTargetFind || obj.perPixelTargetFind) && !obj.isEditing) {
8741          var isTransparent = this.isTargetTransparent(obj, pointer.x, pointer.y);
8742          if (!isTransparent) {
8743            return true;
8744          }
8745        }
8746        else {
8747          return true;
8748        }
8749      }
8750    },
8751
8752    /**
8753     * @private
8754     */
8755    _searchPossibleTargets: function(e) {
8756
8757      // Cache all targets where their bounding box contains point.
8758      var target,
8759          pointer = this.getPointer(e, true),
8760          i = this._objects.length;
8761      // Do not check for currently grouped objects, since we check the parent group itself.
8762      while (i--) {
8763        if (!this._objects[i].group && this._checkTarget(e, this._objects[i], pointer)){
8764          this.relatedTarget = this._objects[i];
8765          target = this._objects[i];
8766          break;
8767        }
8768      }
8769
8770      return target;
8771    },
8772
8773    /**
8774     * Returns pointer coordinates relative to canvas.
8775     * @param {Event} e
8776     * @return {Object} object with "x" and "y" number values
8777     */
8778    getPointer: function (e, ignoreZoom, upperCanvasEl) {
8779      if (!upperCanvasEl) {
8780        upperCanvasEl = this.upperCanvasEl;
8781      }
8782      var pointer = getPointer(e, upperCanvasEl),
8783          bounds = upperCanvasEl.getBoundingClientRect(),
8784          boundsWidth = bounds.width || 0,
8785          boundsHeight = bounds.height || 0,
8786          cssScale;
8787
8788      if (!boundsWidth || !boundsHeight ) {
8789        if ('top' in bounds && 'bottom' in bounds) {
8790          boundsHeight = Math.abs( bounds.top - bounds.bottom );
8791        }
8792        if ('right' in bounds && 'left' in bounds) {
8793          boundsWidth = Math.abs( bounds.right - bounds.left );
8794        }
8795      }
8796
8797      this.calcOffset();
8798
8799      pointer.x = pointer.x - this._offset.left;
8800      pointer.y = pointer.y - this._offset.top;
8801      if (!ignoreZoom) {
8802        pointer = fabric.util.transformPoint(
8803          pointer,
8804          fabric.util.invertTransform(this.viewportTransform)
8805        );
8806      }
8807
8808      if (boundsWidth === 0 || boundsHeight === 0) {
8809        // If bounds are not available (i.e. not visible), do not apply scale.
8810        cssScale = { width: 1, height: 1 };
8811      }
8812      else {
8813        cssScale = {
8814          width: upperCanvasEl.width / boundsWidth,
8815          height: upperCanvasEl.height / boundsHeight
8816        };
8817      }
8818
8819      return {
8820        x: pointer.x * cssScale.width,
8821        y: pointer.y * cssScale.height
8822      };
8823    },
8824
8825    /**
8826     * @private
8827     * @throws {CANVAS_INIT_ERROR} If canvas can not be initialized
8828     */
8829    _createUpperCanvas: function () {
8830      var lowerCanvasClass = this.lowerCanvasEl.className.replace(/\s*lower-canvas\s*/, '');
8831
8832      this.upperCanvasEl = this._createCanvasElement();
8833      fabric.util.addClass(this.upperCanvasEl, 'upper-canvas ' + lowerCanvasClass);
8834
8835      this.wrapperEl.appendChild(this.upperCanvasEl);
8836
8837      this._copyCanvasStyle(this.lowerCanvasEl, this.upperCanvasEl);
8838      this._applyCanvasStyle(this.upperCanvasEl);
8839      this.contextTop = this.upperCanvasEl.getContext('2d');
8840    },
8841
8842    /**
8843     * @private
8844     */
8845    _createCacheCanvas: function () {
8846      this.cacheCanvasEl = this._createCanvasElement();
8847      this.cacheCanvasEl.setAttribute('width', this.width);
8848      this.cacheCanvasEl.setAttribute('height', this.height);
8849      this.contextCache = this.cacheCanvasEl.getContext('2d');
8850    },
8851
8852    /**
8853     * @private
8854     */
8855    _initWrapperElement: function () {
8856      this.wrapperEl = fabric.util.wrapElement(this.lowerCanvasEl, 'div', {
8857        'class': this.containerClass
8858      });
8859      fabric.util.setStyle(this.wrapperEl, {
8860        width: this.getWidth() + 'px',
8861        height: this.getHeight() + 'px',
8862        position: 'relative'
8863      });
8864      fabric.util.makeElementUnselectable(this.wrapperEl);
8865    },
8866
8867    /**
8868     * @private
8869     * @param {HTMLElement} element canvas element to apply styles on
8870     */
8871    _applyCanvasStyle: function (element) {
8872      var width = this.getWidth() || element.width,
8873          height = this.getHeight() || element.height;
8874
8875      fabric.util.setStyle(element, {
8876        position: 'absolute',
8877        width: width + 'px',
8878        height: height + 'px',
8879        left: 0,
8880        top: 0
8881      });
8882      element.width = width;
8883      element.height = height;
8884      fabric.util.makeElementUnselectable(element);
8885    },
8886
8887    /**
8888     * Copys the the entire inline style from one element (fromEl) to another (toEl)
8889     * @private
8890     * @param {Element} fromEl Element style is copied from
8891     * @param {Element} toEl Element copied style is applied to
8892     */
8893    _copyCanvasStyle: function (fromEl, toEl) {
8894      toEl.style.cssText = fromEl.style.cssText;
8895    },
8896
8897    /**
8898     * Returns context of canvas where object selection is drawn
8899     * @return {CanvasRenderingContext2D}
8900     */
8901    getSelectionContext: function() {
8902      return this.contextTop;
8903    },
8904
8905    /**
8906     * Returns &lt;canvas> element on which object selection is drawn
8907     * @return {HTMLCanvasElement}
8908     */
8909    getSelectionElement: function () {
8910      return this.upperCanvasEl;
8911    },
8912
8913    /**
8914     * @private
8915     * @param {Object} object
8916     */
8917    _setActiveObject: function(object) {
8918      if (this._activeObject) {
8919        this._activeObject.set('active', false);
8920      }
8921      this._activeObject = object;
8922      object.set('active', true);
8923    },
8924
8925    /**
8926     * Sets given object as the only active object on canvas
8927     * @param {fabric.Object} object Object to set as an active one
8928     * @param {Event} [e] Event (passed along when firing "object:selected")
8929     * @return {fabric.Canvas} thisArg
8930     * @chainable
8931     */
8932    setActiveObject: function (object, e) {
8933      this._setActiveObject(object);
8934      this.renderAll();
8935      this.fire('object:selected', { target: object, e: e });
8936      object.fire('selected', { e: e });
8937      return this;
8938    },
8939
8940    /**
8941     * Returns currently active object
8942     * @return {fabric.Object} active object
8943     */
8944    getActiveObject: function () {
8945      return this._activeObject;
8946    },
8947
8948    /**
8949     * @private
8950     */
8951    _discardActiveObject: function() {
8952      if (this._activeObject) {
8953        this._activeObject.set('active', false);
8954      }
8955      this._activeObject = null;
8956    },
8957
8958    /**
8959     * Discards currently active object
8960     * @return {fabric.Canvas} thisArg
8961     * @chainable
8962     */
8963    discardActiveObject: function (e) {
8964      this._discardActiveObject();
8965      this.renderAll();
8966      this.fire('selection:cleared', { e: e });
8967      return this;
8968    },
8969
8970    /**
8971     * @private
8972     * @param {fabric.Group} group
8973     */
8974    _setActiveGroup: function(group) {
8975      this._activeGroup = group;
8976      if (group) {
8977        group.set('active', true);
8978      }
8979    },
8980
8981    /**
8982     * Sets active group to a speicified one
8983     * @param {fabric.Group} group Group to set as a current one
8984     * @return {fabric.Canvas} thisArg
8985     * @chainable
8986     */
8987    setActiveGroup: function (group, e) {
8988      this._setActiveGroup(group);
8989      if (group) {
8990        this.fire('object:selected', { target: group, e: e });
8991        group.fire('selected', { e: e });
8992      }
8993      return this;
8994    },
8995
8996    /**
8997     * Returns currently active group
8998     * @return {fabric.Group} Current group
8999     */
9000    getActiveGroup: function () {
9001      return this._activeGroup;
9002    },
9003
9004    /**
9005     * @private
9006     */
9007    _discardActiveGroup: function() {
9008      var g = this.getActiveGroup();
9009      if (g) {
9010        g.destroy();
9011      }
9012      this.setActiveGroup(null);
9013    },
9014
9015    /**
9016     * Discards currently active group
9017     * @return {fabric.Canvas} thisArg
9018     */
9019    discardActiveGroup: function (e) {
9020      this._discardActiveGroup();
9021      this.fire('selection:cleared', { e: e });
9022      return this;
9023    },
9024
9025    /**
9026     * Deactivates all objects on canvas, removing any active group or object
9027     * @return {fabric.Canvas} thisArg
9028     */
9029    deactivateAll: function () {
9030      var allObjects = this.getObjects(),
9031          i = 0,
9032          len = allObjects.length;
9033      for ( ; i < len; i++) {
9034        allObjects[i].set('active', false);
9035      }
9036      this._discardActiveGroup();
9037      this._discardActiveObject();
9038      return this;
9039    },
9040
9041    /**
9042     * Deactivates all objects and dispatches appropriate events
9043     * @return {fabric.Canvas} thisArg
9044     */
9045    deactivateAllWithDispatch: function (e) {
9046      var activeObject = this.getActiveGroup() || this.getActiveObject();
9047      if (activeObject) {
9048        this.fire('before:selection:cleared', { target: activeObject, e: e });
9049      }
9050      this.deactivateAll();
9051      if (activeObject) {
9052        this.fire('selection:cleared', { e: e });
9053      }
9054      return this;
9055    },
9056
9057    /**
9058     * Draws objects' controls (borders/controls)
9059     * @param {CanvasRenderingContext2D} ctx Context to render controls on
9060     */
9061    drawControls: function(ctx) {
9062      var activeGroup = this.getActiveGroup();
9063      if (activeGroup) {
9064        this._drawGroupControls(ctx, activeGroup);
9065      }
9066      else {
9067        this._drawObjectsControls(ctx);
9068      }
9069    },
9070
9071    /**
9072     * @private
9073     */
9074    _drawGroupControls: function(ctx, activeGroup) {
9075      activeGroup._renderControls(ctx);
9076    },
9077
9078    /**
9079     * @private
9080     */
9081    _drawObjectsControls: function(ctx) {
9082      for (var i = 0, len = this._objects.length; i < len; ++i) {
9083        if (!this._objects[i] || !this._objects[i].active) {
9084          continue;
9085        }
9086        this._objects[i]._renderControls(ctx);
9087        this.lastRenderedObjectWithControlsAboveOverlay = this._objects[i];
9088      }
9089    }
9090  });
9091
9092  // copying static properties manually to work around Opera's bug,
9093  // where "prototype" property is enumerable and overrides existing prototype
9094  for (var prop in fabric.StaticCanvas) {
9095    if (prop !== 'prototype') {
9096      fabric.Canvas[prop] = fabric.StaticCanvas[prop];
9097    }
9098  }
9099
9100  if (fabric.isTouchSupported) {
9101    /** @ignore */
9102    fabric.Canvas.prototype._setCursorFromEvent = function() { };
9103  }
9104
9105  /**
9106   * @class fabric.Element
9107   * @alias fabric.Canvas
9108   * @deprecated Use {@link fabric.Canvas} instead.
9109   * @constructor
9110   */
9111  fabric.Element = fabric.Canvas;
9112})();
9113
9114
9115(function() {
9116
9117  var cursorOffset = {
9118    mt: 0, // n
9119    tr: 1, // ne
9120    mr: 2, // e
9121    br: 3, // se
9122    mb: 4, // s
9123    bl: 5, // sw
9124    ml: 6, // w
9125    tl: 7 // nw
9126  },
9127  addListener = fabric.util.addListener,
9128  removeListener = fabric.util.removeListener;
9129
9130  fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ {
9131
9132    /**
9133     * Map of cursor style values for each of the object controls
9134     * @private
9135     */
9136    cursorMap: [
9137      'n-resize',
9138      'ne-resize',
9139      'e-resize',
9140      'se-resize',
9141      's-resize',
9142      'sw-resize',
9143      'w-resize',
9144      'nw-resize'
9145    ],
9146
9147    /**
9148     * Adds mouse listeners to canvas
9149     * @private
9150     */
9151    _initEventListeners: function () {
9152
9153      this._bindEvents();
9154
9155      addListener(fabric.window, 'resize', this._onResize);
9156
9157      // mouse events
9158      addListener(this.upperCanvasEl, 'mousedown', this._onMouseDown);
9159      addListener(this.upperCanvasEl, 'mousemove', this._onMouseMove);
9160      addListener(this.upperCanvasEl, 'mousewheel', this._onMouseWheel);
9161
9162      // touch events
9163      addListener(this.upperCanvasEl, 'touchstart', this._onMouseDown);
9164      addListener(this.upperCanvasEl, 'touchmove', this._onMouseMove);
9165
9166      if (typeof eventjs !== 'undefined' && 'add' in eventjs) {
9167        eventjs.add(this.upperCanvasEl, 'gesture', this._onGesture);
9168        eventjs.add(this.upperCanvasEl, 'drag', this._onDrag);
9169        eventjs.add(this.upperCanvasEl, 'orientation', this._onOrientationChange);
9170        eventjs.add(this.upperCanvasEl, 'shake', this._onShake);
9171        eventjs.add(this.upperCanvasEl, 'longpress', this._onLongPress);
9172      }
9173    },
9174
9175    /**
9176     * @private
9177     */
9178    _bindEvents: function() {
9179      this._onMouseDown = this._onMouseDown.bind(this);
9180      this._onMouseMove = this._onMouseMove.bind(this);
9181      this._onMouseUp = this._onMouseUp.bind(this);
9182      this._onResize = this._onResize.bind(this);
9183      this._onGesture = this._onGesture.bind(this);
9184      this._onDrag = this._onDrag.bind(this);
9185      this._onShake = this._onShake.bind(this);
9186      this._onLongPress = this._onLongPress.bind(this);
9187      this._onOrientationChange = this._onOrientationChange.bind(this);
9188      this._onMouseWheel = this._onMouseWheel.bind(this);
9189    },
9190
9191    /**
9192     * Removes all event listeners
9193     */
9194    removeListeners: function() {
9195      removeListener(fabric.window, 'resize', this._onResize);
9196
9197      removeListener(this.upperCanvasEl, 'mousedown', this._onMouseDown);
9198      removeListener(this.upperCanvasEl, 'mousemove', this._onMouseMove);
9199      removeListener(this.upperCanvasEl, 'mousewheel', this._onMouseWheel);
9200
9201      removeListener(this.upperCanvasEl, 'touchstart', this._onMouseDown);
9202      removeListener(this.upperCanvasEl, 'touchmove', this._onMouseMove);
9203
9204      if (typeof eventjs !== 'undefined' && 'remove' in eventjs) {
9205        eventjs.remove(this.upperCanvasEl, 'gesture', this._onGesture);
9206        eventjs.remove(this.upperCanvasEl, 'drag', this._onDrag);
9207        eventjs.remove(this.upperCanvasEl, 'orientation', this._onOrientationChange);
9208        eventjs.remove(this.upperCanvasEl, 'shake', this._onShake);
9209        eventjs.remove(this.upperCanvasEl, 'longpress', this._onLongPress);
9210      }
9211    },
9212
9213    /**
9214     * @private
9215     * @param {Event} [e] Event object fired on Event.js gesture
9216     * @param {Event} [self] Inner Event object
9217     */
9218    _onGesture: function(e, self) {
9219      this.__onTransformGesture && this.__onTransformGesture(e, self);
9220    },
9221
9222    /**
9223     * @private
9224     * @param {Event} [e] Event object fired on Event.js drag
9225     * @param {Event} [self] Inner Event object
9226     */
9227    _onDrag: function(e, self) {
9228      this.__onDrag && this.__onDrag(e, self);
9229    },
9230
9231    /**
9232     * @private
9233     * @param {Event} [e] Event object fired on Event.js wheel event
9234     * @param {Event} [self] Inner Event object
9235     */
9236    _onMouseWheel: function(e, self) {
9237      this.__onMouseWheel && this.__onMouseWheel(e, self);
9238    },
9239
9240    /**
9241     * @private
9242     * @param {Event} [e] Event object fired on Event.js orientation change
9243     * @param {Event} [self] Inner Event object
9244     */
9245    _onOrientationChange: function(e, self) {
9246      this.__onOrientationChange && this.__onOrientationChange(e, self);
9247    },
9248
9249    /**
9250     * @private
9251     * @param {Event} [e] Event object fired on Event.js shake
9252     * @param {Event} [self] Inner Event object
9253     */
9254    _onShake: function(e, self) {
9255      this.__onShake && this.__onShake(e, self);
9256    },
9257    /**
9258     * @private
9259     * @param {Event} [e] Event object fired on Event.js shake
9260     * @param {Event} [self] Inner Event object
9261     */
9262    _onLongPress: function(e, self) {
9263      this.__onLongPress && this.__onLongPress(e, self);
9264    },
9265
9266    /**
9267     * @private
9268     * @param {Event} e Event object fired on mousedown
9269     */
9270    _onMouseDown: function (e) {
9271      this.__onMouseDown(e);
9272
9273      addListener(fabric.document, 'touchend', this._onMouseUp);
9274      addListener(fabric.document, 'touchmove', this._onMouseMove);
9275
9276      removeListener(this.upperCanvasEl, 'mousemove', this._onMouseMove);
9277      removeListener(this.upperCanvasEl, 'touchmove', this._onMouseMove);
9278
9279      if (e.type === 'touchstart') {
9280        // Unbind mousedown to prevent double triggers from touch devices
9281        removeListener(this.upperCanvasEl, 'mousedown', this._onMouseDown);
9282      }
9283      else {
9284        addListener(fabric.document, 'mouseup', this._onMouseUp);
9285        addListener(fabric.document, 'mousemove', this._onMouseMove);
9286      }
9287    },
9288
9289    /**
9290     * @private
9291     * @param {Event} e Event object fired on mouseup
9292     */
9293    _onMouseUp: function (e) {
9294      this.__onMouseUp(e);
9295
9296      removeListener(fabric.document, 'mouseup', this._onMouseUp);
9297      removeListener(fabric.document, 'touchend', this._onMouseUp);
9298
9299      removeListener(fabric.document, 'mousemove', this._onMouseMove);
9300      removeListener(fabric.document, 'touchmove', this._onMouseMove);
9301
9302      addListener(this.upperCanvasEl, 'mousemove', this._onMouseMove);
9303      addListener(this.upperCanvasEl, 'touchmove', this._onMouseMove);
9304
9305      if (e.type === 'touchend') {
9306        // Wait 400ms before rebinding mousedown to prevent double triggers
9307        // from touch devices
9308        var _this = this;
9309        setTimeout(function() {
9310          addListener(_this.upperCanvasEl, 'mousedown', _this._onMouseDown);
9311        }, 400);
9312      }
9313    },
9314
9315    /**
9316     * @private
9317     * @param {Event} e Event object fired on mousemove
9318     */
9319    _onMouseMove: function (e) {
9320      !this.allowTouchScrolling && e.preventDefault && e.preventDefault();
9321      this.__onMouseMove(e);
9322    },
9323
9324    /**
9325     * @private
9326     */
9327    _onResize: function () {
9328      this.calcOffset();
9329    },
9330
9331    /**
9332     * Decides whether the canvas should be redrawn in mouseup and mousedown events.
9333     * @private
9334     * @param {Object} target
9335     * @param {Object} pointer
9336     */
9337    _shouldRender: function(target, pointer) {
9338      var activeObject = this.getActiveGroup() || this.getActiveObject();
9339
9340      return !!(
9341        (target && (
9342          target.isMoving ||
9343          target !== activeObject))
9344        ||
9345        (!target && !!activeObject)
9346        ||
9347        (!target && !activeObject && !this._groupSelector)
9348        ||
9349        (pointer &&
9350          this._previousPointer &&
9351          this.selection && (
9352          pointer.x !== this._previousPointer.x ||
9353          pointer.y !== this._previousPointer.y))
9354      );
9355    },
9356
9357    /**
9358     * Method that defines the actions when mouse is released on canvas.
9359     * The method resets the currentTransform parameters, store the image corner
9360     * position in the image object and render the canvas on top.
9361     * @private
9362     * @param {Event} e Event object fired on mouseup
9363     */
9364    __onMouseUp: function (e) {
9365      var target;
9366
9367      if (this.isDrawingMode && this._isCurrentlyDrawing) {
9368        this._onMouseUpInDrawingMode(e);
9369        return;
9370      }
9371
9372      if (this._currentTransform) {
9373        this._finalizeCurrentTransform();
9374        target = this._currentTransform.target;
9375      }
9376      else {
9377        target = this.findTarget(e, true);
9378      }
9379
9380      var shouldRender = this._shouldRender(target, this.getPointer(e));
9381
9382      this._maybeGroupObjects(e);
9383
9384      if (target) {
9385        target.isMoving = false;
9386      }
9387
9388      shouldRender && this.renderAll();
9389
9390      this._handleCursorAndEvent(e, target);
9391    },
9392
9393    _handleCursorAndEvent: function(e, target) {
9394      this._setCursorFromEvent(e, target);
9395
9396      // TODO: why are we doing this?
9397      var _this = this;
9398      setTimeout(function () {
9399        _this._setCursorFromEvent(e, target);
9400      }, 50);
9401
9402      this.fire('mouse:up', { target: target, e: e });
9403      target && target.fire('mouseup', { e: e });
9404    },
9405
9406    /**
9407     * @private
9408     */
9409    _finalizeCurrentTransform: function() {
9410
9411      var transform = this._currentTransform,
9412          target = transform.target;
9413
9414      if (target._scaling) {
9415        target._scaling = false;
9416      }
9417
9418      target.setCoords();
9419
9420      // only fire :modified event if target coordinates were changed during mousedown-mouseup
9421      if (this.stateful && target.hasStateChanged()) {
9422        this.fire('object:modified', { target: target });
9423        target.fire('modified');
9424      }
9425
9426      this._restoreOriginXY(target);
9427    },
9428
9429    /**
9430     * @private
9431     * @param {Object} target Object to restore
9432     */
9433    _restoreOriginXY: function(target) {
9434      if (this._previousOriginX && this._previousOriginY) {
9435
9436        var originPoint = target.translateToOriginPoint(
9437          target.getCenterPoint(),
9438          this._previousOriginX,
9439          this._previousOriginY);
9440
9441        target.originX = this._previousOriginX;
9442        target.originY = this._previousOriginY;
9443
9444        target.left = originPoint.x;
9445        target.top = originPoint.y;
9446
9447        this._previousOriginX = null;
9448        this._previousOriginY = null;
9449      }
9450    },
9451
9452    /**
9453     * @private
9454     * @param {Event} e Event object fired on mousedown
9455     */
9456    _onMouseDownInDrawingMode: function(e) {
9457      this._isCurrentlyDrawing = true;
9458      this.discardActiveObject(e).renderAll();
9459      if (this.clipTo) {
9460        fabric.util.clipContext(this, this.contextTop);
9461      }
9462      var ivt = fabric.util.invertTransform(this.viewportTransform),
9463          pointer = fabric.util.transformPoint(this.getPointer(e, true), ivt);
9464      this.freeDrawingBrush.onMouseDown(pointer);
9465      this.fire('mouse:down', { e: e });
9466
9467      var target = this.findTarget(e);
9468      if (typeof target !== 'undefined') {
9469        target.fire('mousedown', { e: e, target: target });
9470      }
9471    },
9472
9473    /**
9474     * @private
9475     * @param {Event} e Event object fired on mousemove
9476     */
9477    _onMouseMoveInDrawingMode: function(e) {
9478      if (this._isCurrentlyDrawing) {
9479        var ivt = fabric.util.invertTransform(this.viewportTransform),
9480            pointer = fabric.util.transformPoint(this.getPointer(e, true), ivt);
9481        this.freeDrawingBrush.onMouseMove(pointer);
9482      }
9483      this.setCursor(this.freeDrawingCursor);
9484      this.fire('mouse:move', { e: e });
9485
9486      var target = this.findTarget(e);
9487      if (typeof target !== 'undefined') {
9488        target.fire('mousemove', { e: e, target: target });
9489      }
9490    },
9491
9492    /**
9493     * @private
9494     * @param {Event} e Event object fired on mouseup
9495     */
9496    _onMouseUpInDrawingMode: function(e) {
9497      this._isCurrentlyDrawing = false;
9498      if (this.clipTo) {
9499        this.contextTop.restore();
9500      }
9501      this.freeDrawingBrush.onMouseUp();
9502      this.fire('mouse:up', { e: e });
9503
9504      var target = this.findTarget(e);
9505      if (typeof target !== 'undefined') {
9506        target.fire('mouseup', { e: e, target: target });
9507      }
9508    },
9509
9510    /**
9511     * Method that defines the actions when mouse is clic ked on canvas.
9512     * The method inits the currentTransform parameters and renders all the
9513     * canvas so the current image can be placed on the top canvas and the rest
9514     * in on the container one.
9515     * @private
9516     * @param {Event} e Event object fired on mousedown
9517     */
9518    __onMouseDown: function (e) {
9519
9520      // accept only left clicks
9521      var isLeftClick  = 'which' in e ? e.which === 1 : e.button === 1;
9522      if (!isLeftClick && !fabric.isTouchSupported) {
9523        return;
9524      }
9525
9526      if (this.isDrawingMode) {
9527        this._onMouseDownInDrawingMode(e);
9528        return;
9529      }
9530
9531      // ignore if some object is being transformed at this moment
9532      if (this._currentTransform) {
9533        return;
9534      }
9535
9536      var target = this.findTarget(e),
9537          pointer = this.getPointer(e, true);
9538
9539      // save pointer for check in __onMouseUp event
9540      this._previousPointer = pointer;
9541
9542      var shouldRender = this._shouldRender(target, pointer),
9543          shouldGroup = this._shouldGroup(e, target);
9544
9545      if (this._shouldClearSelection(e, target)) {
9546        this._clearSelection(e, target, pointer);
9547      }
9548      else if (shouldGroup) {
9549        this._handleGrouping(e, target);
9550        target = this.getActiveGroup();
9551      }
9552
9553      if (target && target.selectable && !shouldGroup) {
9554        this._beforeTransform(e, target);
9555        this._setupCurrentTransform(e, target);
9556      }
9557      // we must renderAll so that active image is placed on the top canvas
9558      shouldRender && this.renderAll();
9559
9560      this.fire('mouse:down', { target: target, e: e });
9561      target && target.fire('mousedown', { e: e });
9562    },
9563
9564    /**
9565     * @private
9566     */
9567    _beforeTransform: function(e, target) {
9568      this.stateful && target.saveState();
9569
9570      // determine if it's a drag or rotate case
9571      if (target._findTargetCorner(this.getPointer(e))) {
9572        this.onBeforeScaleRotate(target);
9573      }
9574
9575      if (target !== this.getActiveGroup() && target !== this.getActiveObject()) {
9576        this.deactivateAll();
9577        this.setActiveObject(target, e);
9578      }
9579    },
9580
9581    /**
9582     * @private
9583     */
9584    _clearSelection: function(e, target, pointer) {
9585      this.deactivateAllWithDispatch(e);
9586
9587      if (target && target.selectable) {
9588        this.setActiveObject(target, e);
9589      }
9590      else if (this.selection) {
9591        this._groupSelector = {
9592          ex: pointer.x,
9593          ey: pointer.y,
9594          top: 0,
9595          left: 0
9596        };
9597      }
9598    },
9599
9600    /**
9601     * @private
9602     * @param {Object} target Object for that origin is set to center
9603     */
9604    _setOriginToCenter: function(target) {
9605      this._previousOriginX = this._currentTransform.target.originX;
9606      this._previousOriginY = this._currentTransform.target.originY;
9607
9608      var center = target.getCenterPoint();
9609
9610      target.originX = 'center';
9611      target.originY = 'center';
9612
9613      target.left = center.x;
9614      target.top = center.y;
9615
9616      this._currentTransform.left = target.left;
9617      this._currentTransform.top = target.top;
9618    },
9619
9620    /**
9621     * @private
9622     * @param {Object} target Object for that center is set to origin
9623     */
9624    _setCenterToOrigin: function(target) {
9625      var originPoint = target.translateToOriginPoint(
9626        target.getCenterPoint(),
9627        this._previousOriginX,
9628        this._previousOriginY);
9629
9630      target.originX = this._previousOriginX;
9631      target.originY = this._previousOriginY;
9632
9633      target.left = originPoint.x;
9634      target.top = originPoint.y;
9635
9636      this._previousOriginX = null;
9637      this._previousOriginY = null;
9638    },
9639
9640    /**
9641      * Method that defines the actions when mouse is hovering the canvas.
9642      * The currentTransform parameter will definde whether the user is rotating/scaling/translating
9643      * an image or neither of them (only hovering). A group selection is also possible and would cancel
9644      * all any other type of action.
9645      * In case of an image transformation only the top canvas will be rendered.
9646      * @private
9647      * @param {Event} e Event object fired on mousemove
9648      */
9649    __onMouseMove: function (e) {
9650
9651      var target, pointer;
9652
9653      if (this.isDrawingMode) {
9654        this._onMouseMoveInDrawingMode(e);
9655        return;
9656      }
9657      if (typeof e.touches !== 'undefined' && e.touches.length > 1) {
9658        return;
9659      }
9660
9661      var groupSelector = this._groupSelector;
9662
9663      // We initially clicked in an empty area, so we draw a box for multiple selection
9664      if (groupSelector) {
9665        pointer = this.getPointer(e, true);
9666
9667        groupSelector.left = pointer.x - groupSelector.ex;
9668        groupSelector.top = pointer.y - groupSelector.ey;
9669
9670        this.renderTop();
9671      }
9672      else if (!this._currentTransform) {
9673
9674        target = this.findTarget(e);
9675
9676        if (!target || target && !target.selectable) {
9677          this.setCursor(this.defaultCursor);
9678        }
9679        else {
9680          this._setCursorFromEvent(e, target);
9681        }
9682      }
9683      else {
9684        this._transformObject(e);
9685      }
9686
9687      this.fire('mouse:move', { target: target, e: e });
9688      target && target.fire('mousemove', { e: e });
9689    },
9690
9691    /**
9692     * @private
9693     * @param {Event} e Event fired on mousemove
9694     */
9695    _transformObject: function(e) {
9696      var pointer = this.getPointer(e),
9697          transform = this._currentTransform;
9698
9699      transform.reset = false,
9700      transform.target.isMoving = true;
9701
9702      this._beforeScaleTransform(e, transform);
9703      this._performTransformAction(e, transform, pointer);
9704
9705      this.renderAll();
9706    },
9707
9708    /**
9709     * @private
9710     */
9711    _performTransformAction: function(e, transform, pointer) {
9712      var x = pointer.x,
9713          y = pointer.y,
9714          target = transform.target,
9715          action = transform.action;
9716
9717      if (action === 'rotate') {
9718        this._rotateObject(x, y);
9719        this._fire('rotating', target, e);
9720      }
9721      else if (action === 'scale') {
9722        this._onScale(e, transform, x, y);
9723        this._fire('scaling', target, e);
9724      }
9725      else if (action === 'scaleX') {
9726        this._scaleObject(x, y, 'x');
9727        this._fire('scaling', target, e);
9728      }
9729      else if (action === 'scaleY') {
9730        this._scaleObject(x, y, 'y');
9731        this._fire('scaling', target, e);
9732      }
9733      else {
9734        this._translateObject(x, y);
9735        this._fire('moving', target, e);
9736        this.setCursor(this.moveCursor);
9737      }
9738    },
9739
9740    /**
9741     * @private
9742     */
9743    _fire: function(eventName, target, e) {
9744      this.fire('object:' + eventName, { target: target, e: e });
9745      target.fire(eventName, { e: e });
9746    },
9747
9748    /**
9749     * @private
9750     */
9751    _beforeScaleTransform: function(e, transform) {
9752      if (transform.action === 'scale' || transform.action === 'scaleX' || transform.action === 'scaleY') {
9753        var centerTransform = this._shouldCenterTransform(e, transform.target);
9754
9755        // Switch from a normal resize to center-based
9756        if ((centerTransform && (transform.originX !== 'center' || transform.originY !== 'center')) ||
9757           // Switch from center-based resize to normal one
9758           (!centerTransform && transform.originX === 'center' && transform.originY === 'center')
9759        ) {
9760          this._resetCurrentTransform(e);
9761          transform.reset = true;
9762        }
9763      }
9764    },
9765
9766    /**
9767     * @private
9768     */
9769    _onScale: function(e, transform, x, y) {
9770      // rotate object only if shift key is not pressed
9771      // and if it is not a group we are transforming
9772      if ((e.shiftKey || this.uniScaleTransform) && !transform.target.get('lockUniScaling')) {
9773        transform.currentAction = 'scale';
9774        this._scaleObject(x, y);
9775      }
9776      else {
9777        // Switch from a normal resize to proportional
9778        if (!transform.reset && transform.currentAction === 'scale') {
9779          this._resetCurrentTransform(e, transform.target);
9780        }
9781
9782        transform.currentAction = 'scaleEqually';
9783        this._scaleObject(x, y, 'equally');
9784      }
9785    },
9786
9787    /**
9788     * Sets the cursor depending on where the canvas is being hovered.
9789     * Note: very buggy in Opera
9790     * @param {Event} e Event object
9791     * @param {Object} target Object that the mouse is hovering, if so.
9792     */
9793    _setCursorFromEvent: function (e, target) {
9794      if (!target || !target.selectable) {
9795        this.setCursor(this.defaultCursor);
9796        return false;
9797      }
9798      else {
9799        var activeGroup = this.getActiveGroup(),
9800            // only show proper corner when group selection is not active
9801            corner = target._findTargetCorner
9802                      && (!activeGroup || !activeGroup.contains(target))
9803                      && target._findTargetCorner(this.getPointer(e, true));
9804
9805        if (!corner) {
9806          this.setCursor(target.hoverCursor || this.hoverCursor);
9807        }
9808        else {
9809          this._setCornerCursor(corner, target);
9810        }
9811      }
9812      return true;
9813    },
9814
9815    /**
9816     * @private
9817     */
9818    _setCornerCursor: function(corner, target) {
9819      if (corner in cursorOffset) {
9820        this.setCursor(this._getRotatedCornerCursor(corner, target));
9821      }
9822      else if (corner === 'mtr' && target.hasRotatingPoint) {
9823        this.setCursor(this.rotationCursor);
9824      }
9825      else {
9826        this.setCursor(this.defaultCursor);
9827        return false;
9828      }
9829    },
9830
9831    /**
9832     * @private
9833     */
9834    _getRotatedCornerCursor: function(corner, target) {
9835      var n = Math.round((target.getAngle() % 360) / 45);
9836
9837      if (n < 0) {
9838        n += 8; // full circle ahead
9839      }
9840      n += cursorOffset[corner];
9841      // normalize n to be from 0 to 7
9842      n %= 8;
9843
9844      return this.cursorMap[n];
9845    }
9846  });
9847})();
9848
9849
9850(function() {
9851
9852  var min = Math.min,
9853      max = Math.max;
9854
9855  fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ {
9856
9857    /**
9858     * @private
9859     * @param {Event} e Event object
9860     * @param {fabric.Object} target
9861     * @return {Boolean}
9862     */
9863    _shouldGroup: function(e, target) {
9864      var activeObject = this.getActiveObject();
9865      return e.shiftKey &&
9866            (this.getActiveGroup() || (activeObject && activeObject !== target))
9867            && this.selection;
9868    },
9869
9870    /**
9871     * @private
9872     * @param {Event} e Event object
9873     * @param {fabric.Object} target
9874     */
9875    _handleGrouping: function (e, target) {
9876
9877      if (target === this.getActiveGroup()) {
9878
9879        // if it's a group, find target again, this time skipping group
9880        target = this.findTarget(e, true);
9881
9882        // if even object is not found, bail out
9883        if (!target || target.isType('group')) {
9884          return;
9885        }
9886      }
9887      if (this.getActiveGroup()) {
9888        this._updateActiveGroup(target, e);
9889      }
9890      else {
9891        this._createActiveGroup(target, e);
9892      }
9893
9894      if (this._activeGroup) {
9895        this._activeGroup.saveCoords();
9896      }
9897    },
9898
9899    /**
9900     * @private
9901     */
9902    _updateActiveGroup: function(target, e) {
9903      var activeGroup = this.getActiveGroup();
9904
9905      if (activeGroup.contains(target)) {
9906
9907        activeGroup.removeWithUpdate(target);
9908        this._resetObjectTransform(activeGroup);
9909        target.set('active', false);
9910
9911        if (activeGroup.size() === 1) {
9912          // remove group alltogether if after removal it only contains 1 object
9913          this.discardActiveGroup(e);
9914          // activate last remaining object
9915          this.setActiveObject(activeGroup.item(0));
9916          return;
9917        }
9918      }
9919      else {
9920        activeGroup.addWithUpdate(target);
9921        this._resetObjectTransform(activeGroup);
9922      }
9923      this.fire('selection:created', { target: activeGroup, e: e });
9924      activeGroup.set('active', true);
9925    },
9926
9927    /**
9928     * @private
9929     */
9930    _createActiveGroup: function(target, e) {
9931
9932      if (this._activeObject && target !== this._activeObject) {
9933
9934        var group = this._createGroup(target);
9935        group.addWithUpdate();
9936
9937        this.setActiveGroup(group);
9938        this._activeObject = null;
9939
9940        this.fire('selection:created', { target: group, e: e });
9941      }
9942
9943      target.set('active', true);
9944    },
9945
9946    /**
9947     * @private
9948     * @param {Object} target
9949     */
9950    _createGroup: function(target) {
9951
9952      var objects = this.getObjects(),
9953          isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target),
9954          groupObjects = isActiveLower
9955            ? [ this._activeObject, target ]
9956            : [ target, this._activeObject ];
9957
9958      return new fabric.Group(groupObjects, {
9959        canvas: this
9960      });
9961    },
9962
9963    /**
9964     * @private
9965     * @param {Event} e mouse event
9966     */
9967    _groupSelectedObjects: function (e) {
9968
9969      var group = this._collectObjects();
9970
9971      // do not create group for 1 element only
9972      if (group.length === 1) {
9973        this.setActiveObject(group[0], e);
9974      }
9975      else if (group.length > 1) {
9976        group = new fabric.Group(group.reverse(), {
9977          canvas: this
9978        });
9979        group.addWithUpdate();
9980        this.setActiveGroup(group, e);
9981        group.saveCoords();
9982        this.fire('selection:created', { target: group });
9983        this.renderAll();
9984      }
9985    },
9986
9987    /**
9988     * @private
9989     */
9990    _collectObjects: function() {
9991      var group = [ ],
9992          currentObject,
9993          x1 = this._groupSelector.ex,
9994          y1 = this._groupSelector.ey,
9995          x2 = x1 + this._groupSelector.left,
9996          y2 = y1 + this._groupSelector.top,
9997          selectionX1Y1 = new fabric.Point(min(x1, x2), min(y1, y2)),
9998          selectionX2Y2 = new fabric.Point(max(x1, x2), max(y1, y2)),
9999          isClick = x1 === x2 && y1 === y2;
10000
10001      for (var i = this._objects.length; i--; ) {
10002        currentObject = this._objects[i];
10003
10004        if (!currentObject || !currentObject.selectable || !currentObject.visible) {
10005          continue;
10006        }
10007
10008        if (currentObject.intersectsWithRect(selectionX1Y1, selectionX2Y2) ||
10009            currentObject.isContainedWithinRect(selectionX1Y1, selectionX2Y2) ||
10010            currentObject.containsPoint(selectionX1Y1) ||
10011            currentObject.containsPoint(selectionX2Y2)
10012        ) {
10013          currentObject.set('active', true);
10014          group.push(currentObject);
10015
10016          // only add one object if it's a click
10017          if (isClick) {
10018            break;
10019          }
10020        }
10021      }
10022
10023      return group;
10024    },
10025
10026    /**
10027     * @private
10028     */
10029    _maybeGroupObjects: function(e) {
10030      if (this.selection && this._groupSelector) {
10031        this._groupSelectedObjects(e);
10032      }
10033
10034      var activeGroup = this.getActiveGroup();
10035      if (activeGroup) {
10036        activeGroup.setObjectsCoords().setCoords();
10037        activeGroup.isMoving = false;
10038        this.setCursor(this.defaultCursor);
10039      }
10040
10041      // clear selection and current transformation
10042      this._groupSelector = null;
10043      this._currentTransform = null;
10044    }
10045  });
10046
10047})();
10048
10049
10050fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
10051
10052  /**
10053   * Exports canvas element to a dataurl image. Note that when multiplier is used, cropping is scaled appropriately
10054   * @param {Object} [options] Options object
10055   * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
10056   * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
10057   * @param {Number} [options.multiplier=1] Multiplier to scale by
10058   * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
10059   * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
10060   * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
10061   * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
10062   * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
10063   * @see {@link http://jsfiddle.net/fabricjs/NfZVb/|jsFiddle demo}
10064   * @example <caption>Generate jpeg dataURL with lower quality</caption>
10065   * var dataURL = canvas.toDataURL({
10066   *   format: 'jpeg',
10067   *   quality: 0.8
10068   * });
10069   * @example <caption>Generate cropped png dataURL (clipping of canvas)</caption>
10070   * var dataURL = canvas.toDataURL({
10071   *   format: 'png',
10072   *   left: 100,
10073   *   top: 100,
10074   *   width: 200,
10075   *   height: 200
10076   * });
10077   * @example <caption>Generate double scaled png dataURL</caption>
10078   * var dataURL = canvas.toDataURL({
10079   *   format: 'png',
10080   *   multiplier: 2
10081   * });
10082   */
10083  toDataURL: function (options) {
10084    options || (options = { });
10085
10086    var format = options.format || 'png',
10087        quality = options.quality || 1,
10088        multiplier = options.multiplier || 1,
10089        cropping = {
10090          left: options.left,
10091          top: options.top,
10092          width: options.width,
10093          height: options.height
10094        };
10095
10096    if (multiplier !== 1) {
10097      return this.__toDataURLWithMultiplier(format, quality, cropping, multiplier);
10098    }
10099    else {
10100      return this.__toDataURL(format, quality, cropping);
10101    }
10102  },
10103
10104  /**
10105   * @private
10106   */
10107  __toDataURL: function(format, quality, cropping) {
10108
10109    this.renderAll(true);
10110
10111    var canvasEl = this.upperCanvasEl || this.lowerCanvasEl,
10112        croppedCanvasEl = this.__getCroppedCanvas(canvasEl, cropping);
10113
10114    // to avoid common confusion https://github.com/kangax/fabric.js/issues/806
10115    if (format === 'jpg') {
10116      format = 'jpeg';
10117    }
10118
10119    var data = (fabric.StaticCanvas.supports('toDataURLWithQuality'))
10120              ? (croppedCanvasEl || canvasEl).toDataURL('image/' + format, quality)
10121              : (croppedCanvasEl || canvasEl).toDataURL('image/' + format);
10122
10123    this.contextTop && this.clearContext(this.contextTop);
10124    this.renderAll();
10125
10126    if (croppedCanvasEl) {
10127      croppedCanvasEl = null;
10128    }
10129
10130    return data;
10131  },
10132
10133  /**
10134   * @private
10135   */
10136  __getCroppedCanvas: function(canvasEl, cropping) {
10137
10138    var croppedCanvasEl,
10139        croppedCtx,
10140        shouldCrop = 'left' in cropping ||
10141                     'top' in cropping ||
10142                     'width' in cropping ||
10143                     'height' in cropping;
10144
10145    if (shouldCrop) {
10146
10147      croppedCanvasEl = fabric.util.createCanvasElement();
10148      croppedCtx = croppedCanvasEl.getContext('2d');
10149
10150      croppedCanvasEl.width = cropping.width || this.width;
10151      croppedCanvasEl.height = cropping.height || this.height;
10152
10153      croppedCtx.drawImage(canvasEl, -cropping.left || 0, -cropping.top || 0);
10154    }
10155
10156    return croppedCanvasEl;
10157  },
10158
10159  /**
10160   * @private
10161   */
10162  __toDataURLWithMultiplier: function(format, quality, cropping, multiplier) {
10163
10164    var origWidth = this.getWidth(),
10165        origHeight = this.getHeight(),
10166        scaledWidth = origWidth * multiplier,
10167        scaledHeight = origHeight * multiplier,
10168        activeObject = this.getActiveObject(),
10169        activeGroup = this.getActiveGroup(),
10170
10171        ctx = this.contextTop || this.contextContainer;
10172
10173    if (multiplier > 1) {
10174      this.setWidth(scaledWidth).setHeight(scaledHeight);
10175    }
10176    ctx.scale(multiplier, multiplier);
10177
10178    if (cropping.left) {
10179      cropping.left *= multiplier;
10180    }
10181    if (cropping.top) {
10182      cropping.top *= multiplier;
10183    }
10184    if (cropping.width) {
10185      cropping.width *= multiplier;
10186    }
10187    else if (multiplier < 1) {
10188      cropping.width = scaledWidth;
10189    }
10190    if (cropping.height) {
10191      cropping.height *= multiplier;
10192    }
10193    else if (multiplier < 1) {
10194      cropping.height = scaledHeight;
10195    }
10196
10197    if (activeGroup) {
10198      // not removing group due to complications with restoring it with correct state afterwords
10199      this._tempRemoveBordersControlsFromGroup(activeGroup);
10200    }
10201    else if (activeObject && this.deactivateAll) {
10202      this.deactivateAll();
10203    }
10204
10205    this.renderAll(true);
10206
10207    var data = this.__toDataURL(format, quality, cropping);
10208
10209    // restoring width, height for `renderAll` to draw
10210    // background properly (while context is scaled)
10211    this.width = origWidth;
10212    this.height = origHeight;
10213
10214    ctx.scale(1 / multiplier,  1 / multiplier);
10215    this.setWidth(origWidth).setHeight(origHeight);
10216
10217    if (activeGroup) {
10218      this._restoreBordersControlsOnGroup(activeGroup);
10219    }
10220    else if (activeObject && this.setActiveObject) {
10221      this.setActiveObject(activeObject);
10222    }
10223
10224    this.contextTop && this.clearContext(this.contextTop);
10225    this.renderAll();
10226
10227    return data;
10228  },
10229
10230  /**
10231   * Exports canvas element to a dataurl image (allowing to change image size via multiplier).
10232   * @deprecated since 1.0.13
10233   * @param {String} format (png|jpeg)
10234   * @param {Number} multiplier
10235   * @param {Number} quality (0..1)
10236   * @return {String}
10237   */
10238  toDataURLWithMultiplier: function (format, multiplier, quality) {
10239    return this.toDataURL({
10240      format: format,
10241      multiplier: multiplier,
10242      quality: quality
10243    });
10244  },
10245
10246  /**
10247   * @private
10248   */
10249  _tempRemoveBordersControlsFromGroup: function(group) {
10250    group.origHasControls = group.hasControls;
10251    group.origBorderColor = group.borderColor;
10252
10253    group.hasControls = true;
10254    group.borderColor = 'rgba(0,0,0,0)';
10255
10256    group.forEachObject(function(o) {
10257      o.origBorderColor = o.borderColor;
10258      o.borderColor = 'rgba(0,0,0,0)';
10259    });
10260  },
10261
10262  /**
10263   * @private
10264   */
10265  _restoreBordersControlsOnGroup: function(group) {
10266    group.hideControls = group.origHideControls;
10267    group.borderColor = group.origBorderColor;
10268
10269    group.forEachObject(function(o) {
10270      o.borderColor = o.origBorderColor;
10271      delete o.origBorderColor;
10272    });
10273  }
10274});
10275
10276
10277fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
10278
10279  /**
10280   * Populates canvas with data from the specified dataless JSON.
10281   * JSON format must conform to the one of {@link fabric.Canvas#toDatalessJSON}
10282   * @deprecated since 1.2.2
10283   * @param {String|Object} json JSON string or object
10284   * @param {Function} callback Callback, invoked when json is parsed
10285   *                            and corresponding objects (e.g: {@link fabric.Image})
10286   *                            are initialized
10287   * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created.
10288   * @return {fabric.Canvas} instance
10289   * @chainable
10290   * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#deserialization}
10291   */
10292  loadFromDatalessJSON: function (json, callback, reviver) {
10293    return this.loadFromJSON(json, callback, reviver);
10294  },
10295
10296  /**
10297   * Populates canvas with data from the specified JSON.
10298   * JSON format must conform to the one of {@link fabric.Canvas#toJSON}
10299   * @param {String|Object} json JSON string or object
10300   * @param {Function} callback Callback, invoked when json is parsed
10301   *                            and corresponding objects (e.g: {@link fabric.Image})
10302   *                            are initialized
10303   * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created.
10304   * @return {fabric.Canvas} instance
10305   * @chainable
10306   * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#deserialization}
10307   * @see {@link http://jsfiddle.net/fabricjs/fmgXt/|jsFiddle demo}
10308   * @example <caption>loadFromJSON</caption>
10309   * canvas.loadFromJSON(json, canvas.renderAll.bind(canvas));
10310   * @example <caption>loadFromJSON with reviver</caption>
10311   * canvas.loadFromJSON(json, canvas.renderAll.bind(canvas), function(o, object) {
10312   *   // `o` = json object
10313   *   // `object` = fabric.Object instance
10314   *   // ... do some stuff ...
10315   * });
10316   */
10317  loadFromJSON: function (json, callback, reviver) {
10318    if (!json) {
10319      return;
10320    }
10321
10322    // serialize if it wasn't already
10323    var serialized = (typeof json === 'string')
10324      ? JSON.parse(json)
10325      : json;
10326
10327    this.clear();
10328
10329    var _this = this;
10330    this._enlivenObjects(serialized.objects, function () {
10331      _this._setBgOverlay(serialized, callback);
10332    }, reviver);
10333
10334    return this;
10335  },
10336
10337  /**
10338   * @private
10339   * @param {Object} serialized Object with background and overlay information
10340   * @param {Function} callback Invoked after all background and overlay images/patterns loaded
10341   */
10342  _setBgOverlay: function(serialized, callback) {
10343    var _this = this,
10344        loaded = {
10345          backgroundColor: false,
10346          overlayColor: false,
10347          backgroundImage: false,
10348          overlayImage: false
10349        };
10350
10351    if (!serialized.backgroundImage && !serialized.overlayImage && !serialized.background && !serialized.overlay) {
10352      callback && callback();
10353      return;
10354    }
10355
10356    var cbIfLoaded = function () {
10357      if (loaded.backgroundImage && loaded.overlayImage && loaded.backgroundColor && loaded.overlayColor) {
10358        _this.renderAll();
10359        callback && callback();
10360      }
10361    };
10362
10363    this.__setBgOverlay('backgroundImage', serialized.backgroundImage, loaded, cbIfLoaded);
10364    this.__setBgOverlay('overlayImage', serialized.overlayImage, loaded, cbIfLoaded);
10365    this.__setBgOverlay('backgroundColor', serialized.background, loaded, cbIfLoaded);
10366    this.__setBgOverlay('overlayColor', serialized.overlay, loaded, cbIfLoaded);
10367
10368    cbIfLoaded();
10369  },
10370
10371  /**
10372   * @private
10373   * @param {String} property Property to set (backgroundImage, overlayImage, backgroundColor, overlayColor)
10374   * @param {(Object|String)} value Value to set
10375   * @param {Object} loaded Set loaded property to true if property is set
10376   * @param {Object} callback Callback function to invoke after property is set
10377   */
10378  __setBgOverlay: function(property, value, loaded, callback) {
10379    var _this = this;
10380
10381    if (!value) {
10382      loaded[property] = true;
10383      return;
10384    }
10385
10386    if (property === 'backgroundImage' || property === 'overlayImage') {
10387      fabric.Image.fromObject(value, function(img) {
10388        _this[property] = img;
10389        loaded[property] = true;
10390        callback && callback();
10391      });
10392    }
10393    else {
10394      this['set' + fabric.util.string.capitalize(property, true)](value, function() {
10395        loaded[property] = true;
10396        callback && callback();
10397      });
10398    }
10399  },
10400
10401  /**
10402   * @private
10403   * @param {Array} objects
10404   * @param {Function} callback
10405   * @param {Function} [reviver]
10406   */
10407  _enlivenObjects: function (objects, callback, reviver) {
10408    var _this = this;
10409
10410    if (!objects || objects.length === 0) {
10411      callback && callback();
10412      return;
10413    }
10414
10415    var renderOnAddRemove = this.renderOnAddRemove;
10416    this.renderOnAddRemove = false;
10417
10418    fabric.util.enlivenObjects(objects, function(enlivenedObjects) {
10419      enlivenedObjects.forEach(function(obj, index) {
10420        _this.insertAt(obj, index, true);
10421      });
10422
10423      _this.renderOnAddRemove = renderOnAddRemove;
10424      callback && callback();
10425    }, null, reviver);
10426  },
10427
10428  /**
10429   * @private
10430   * @param {String} format
10431   * @param {Function} callback
10432   */
10433  _toDataURL: function (format, callback) {
10434    this.clone(function (clone) {
10435      callback(clone.toDataURL(format));
10436    });
10437  },
10438
10439  /**
10440   * @private
10441   * @param {String} format
10442   * @param {Number} multiplier
10443   * @param {Function} callback
10444   */
10445  _toDataURLWithMultiplier: function (format, multiplier, callback) {
10446    this.clone(function (clone) {
10447      callback(clone.toDataURLWithMultiplier(format, multiplier));
10448    });
10449  },
10450
10451  /**
10452   * Clones canvas instance
10453   * @param {Object} [callback] Receives cloned instance as a first argument
10454   * @param {Array} [properties] Array of properties to include in the cloned canvas and children
10455   */
10456  clone: function (callback, properties) {
10457    var data = JSON.stringify(this.toJSON(properties));
10458    this.cloneWithoutData(function(clone) {
10459      clone.loadFromJSON(data, function() {
10460        callback && callback(clone);
10461      });
10462    });
10463  },
10464
10465  /**
10466   * Clones canvas instance without cloning existing data.
10467   * This essentially copies canvas dimensions, clipping properties, etc.
10468   * but leaves data empty (so that you can populate it with your own)
10469   * @param {Object} [callback] Receives cloned instance as a first argument
10470   */
10471  cloneWithoutData: function(callback) {
10472    var el = fabric.document.createElement('canvas');
10473
10474    el.width = this.getWidth();
10475    el.height = this.getHeight();
10476
10477    var clone = new fabric.Canvas(el);
10478    clone.clipTo = this.clipTo;
10479    if (this.backgroundImage) {
10480      clone.setBackgroundImage(this.backgroundImage.src, function() {
10481        clone.renderAll();
10482        callback && callback(clone);
10483      });
10484      clone.backgroundImageOpacity = this.backgroundImageOpacity;
10485      clone.backgroundImageStretch = this.backgroundImageStretch;
10486    }
10487    else {
10488      callback && callback(clone);
10489    }
10490  }
10491});
10492
10493
10494(function(global) {
10495
10496  'use strict';
10497
10498  var fabric = global.fabric || (global.fabric = { }),
10499      extend = fabric.util.object.extend,
10500      toFixed = fabric.util.toFixed,
10501      capitalize = fabric.util.string.capitalize,
10502      degreesToRadians = fabric.util.degreesToRadians,
10503      supportsLineDash = fabric.StaticCanvas.supports('setLineDash');
10504
10505  if (fabric.Object) {
10506    return;
10507  }
10508
10509  /**
10510   * Root object class from which all 2d shape classes inherit from
10511   * @class fabric.Object
10512   * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#objects}
10513   * @see {@link fabric.Object#initialize} for constructor definition
10514   *
10515   * @fires added
10516   * @fires removed
10517   *
10518   * @fires selected
10519   * @fires modified
10520   * @fires rotating
10521   * @fires scaling
10522   * @fires moving
10523   *
10524   * @fires mousedown
10525   * @fires mouseup
10526   */
10527  fabric.Object = fabric.util.createClass(/** @lends fabric.Object.prototype */ {
10528
10529    /**
10530     * Retrieves object's {@link fabric.Object#clipTo|clipping function}
10531     * @method getClipTo
10532     * @memberOf fabric.Object.prototype
10533     * @return {Function}
10534     */
10535
10536    /**
10537     * Sets object's {@link fabric.Object#clipTo|clipping function}
10538     * @method setClipTo
10539     * @memberOf fabric.Object.prototype
10540     * @param {Function} clipTo Clipping function
10541     * @return {fabric.Object} thisArg
10542     * @chainable
10543     */
10544
10545    /**
10546     * Retrieves object's {@link fabric.Object#transformMatrix|transformMatrix}
10547     * @method getTransformMatrix
10548     * @memberOf fabric.Object.prototype
10549     * @return {Array} transformMatrix
10550     */
10551
10552    /**
10553     * Sets object's {@link fabric.Object#transformMatrix|transformMatrix}
10554     * @method setTransformMatrix
10555     * @memberOf fabric.Object.prototype
10556     * @param {Array} transformMatrix
10557     * @return {fabric.Object} thisArg
10558     * @chainable
10559     */
10560
10561    /**
10562     * Retrieves object's {@link fabric.Object#visible|visible} state
10563     * @method getVisible
10564     * @memberOf fabric.Object.prototype
10565     * @return {Boolean} True if visible
10566     */
10567
10568    /**
10569     * Sets object's {@link fabric.Object#visible|visible} state
10570     * @method setVisible
10571     * @memberOf fabric.Object.prototype
10572     * @param {Boolean} value visible value
10573     * @return {fabric.Object} thisArg
10574     * @chainable
10575     */
10576
10577    /**
10578     * Retrieves object's {@link fabric.Object#shadow|shadow}
10579     * @method getShadow
10580     * @memberOf fabric.Object.prototype
10581     * @return {Object} Shadow instance
10582     */
10583
10584    /**
10585     * Retrieves object's {@link fabric.Object#stroke|stroke}
10586     * @method getStroke
10587     * @memberOf fabric.Object.prototype
10588     * @return {String} stroke value
10589     */
10590
10591    /**
10592     * Sets object's {@link fabric.Object#stroke|stroke}
10593     * @method setStroke
10594     * @memberOf fabric.Object.prototype
10595     * @param {String} value stroke value
10596     * @return {fabric.Object} thisArg
10597     * @chainable
10598     */
10599
10600    /**
10601     * Retrieves object's {@link fabric.Object#strokeWidth|strokeWidth}
10602     * @method getStrokeWidth
10603     * @memberOf fabric.Object.prototype
10604     * @return {Number} strokeWidth value
10605     */
10606
10607    /**
10608     * Sets object's {@link fabric.Object#strokeWidth|strokeWidth}
10609     * @method setStrokeWidth
10610     * @memberOf fabric.Object.prototype
10611     * @param {Number} value strokeWidth value
10612     * @return {fabric.Object} thisArg
10613     * @chainable
10614     */
10615
10616    /**
10617     * Retrieves object's {@link fabric.Object#originX|originX}
10618     * @method getOriginX
10619     * @memberOf fabric.Object.prototype
10620     * @return {String} originX value
10621     */
10622
10623    /**
10624     * Sets object's {@link fabric.Object#originX|originX}
10625     * @method setOriginX
10626     * @memberOf fabric.Object.prototype
10627     * @param {String} value originX value
10628     * @return {fabric.Object} thisArg
10629     * @chainable
10630     */
10631
10632    /**
10633     * Retrieves object's {@link fabric.Object#originY|originY}
10634     * @method getOriginY
10635     * @memberOf fabric.Object.prototype
10636     * @return {String} originY value
10637     */
10638
10639    /**
10640     * Sets object's {@link fabric.Object#originY|originY}
10641     * @method setOriginY
10642     * @memberOf fabric.Object.prototype
10643     * @param {String} value originY value
10644     * @return {fabric.Object} thisArg
10645     * @chainable
10646     */
10647
10648    /**
10649     * Retrieves object's {@link fabric.Object#fill|fill}
10650     * @method getFill
10651     * @memberOf fabric.Object.prototype
10652     * @return {String} Fill value
10653     */
10654
10655    /**
10656     * Sets object's {@link fabric.Object#fill|fill}
10657     * @method setFill
10658     * @memberOf fabric.Object.prototype
10659     * @param {String} value Fill value
10660     * @return {fabric.Object} thisArg
10661     * @chainable
10662     */
10663
10664    /**
10665     * Retrieves object's {@link fabric.Object#opacity|opacity}
10666     * @method getOpacity
10667     * @memberOf fabric.Object.prototype
10668     * @return {Number} Opacity value (0-1)
10669     */
10670
10671    /**
10672     * Sets object's {@link fabric.Object#opacity|opacity}
10673     * @method setOpacity
10674     * @memberOf fabric.Object.prototype
10675     * @param {Number} value Opacity value (0-1)
10676     * @return {fabric.Object} thisArg
10677     * @chainable
10678     */
10679
10680    /**
10681     * Retrieves object's {@link fabric.Object#angle|angle} (in degrees)
10682     * @method getAngle
10683     * @memberOf fabric.Object.prototype
10684     * @return {Number}
10685     */
10686
10687    /**
10688     * Retrieves object's {@link fabric.Object#top|top position}
10689     * @method getTop
10690     * @memberOf fabric.Object.prototype
10691     * @return {Number} Top value (in pixels)
10692     */
10693
10694    /**
10695     * Sets object's {@link fabric.Object#top|top position}
10696     * @method setTop
10697     * @memberOf fabric.Object.prototype
10698     * @param {Number} value Top value (in pixels)
10699     * @return {fabric.Object} thisArg
10700     * @chainable
10701     */
10702
10703    /**
10704     * Retrieves object's {@link fabric.Object#left|left position}
10705     * @method getLeft
10706     * @memberOf fabric.Object.prototype
10707     * @return {Number} Left value (in pixels)
10708     */
10709
10710    /**
10711     * Sets object's {@link fabric.Object#left|left position}
10712     * @method setLeft
10713     * @memberOf fabric.Object.prototype
10714     * @param {Number} value Left value (in pixels)
10715     * @return {fabric.Object} thisArg
10716     * @chainable
10717     */
10718
10719    /**
10720     * Retrieves object's {@link fabric.Object#scaleX|scaleX} value
10721     * @method getScaleX
10722     * @memberOf fabric.Object.prototype
10723     * @return {Number} scaleX value
10724     */
10725
10726    /**
10727     * Sets object's {@link fabric.Object#scaleX|scaleX} value
10728     * @method setScaleX
10729     * @memberOf fabric.Object.prototype
10730     * @param {Number} value scaleX value
10731     * @return {fabric.Object} thisArg
10732     * @chainable
10733     */
10734
10735    /**
10736     * Retrieves object's {@link fabric.Object#scaleY|scaleY} value
10737     * @method getScaleY
10738     * @memberOf fabric.Object.prototype
10739     * @return {Number} scaleY value
10740     */
10741
10742    /**
10743     * Sets object's {@link fabric.Object#scaleY|scaleY} value
10744     * @method setScaleY
10745     * @memberOf fabric.Object.prototype
10746     * @param {Number} value scaleY value
10747     * @return {fabric.Object} thisArg
10748     * @chainable
10749     */
10750
10751    /**
10752     * Retrieves object's {@link fabric.Object#flipX|flipX} value
10753     * @method getFlipX
10754     * @memberOf fabric.Object.prototype
10755     * @return {Boolean} flipX value
10756     */
10757
10758    /**
10759     * Sets object's {@link fabric.Object#flipX|flipX} value
10760     * @method setFlipX
10761     * @memberOf fabric.Object.prototype
10762     * @param {Boolean} value flipX value
10763     * @return {fabric.Object} thisArg
10764     * @chainable
10765     */
10766
10767    /**
10768     * Retrieves object's {@link fabric.Object#flipY|flipY} value
10769     * @method getFlipY
10770     * @memberOf fabric.Object.prototype
10771     * @return {Boolean} flipY value
10772     */
10773
10774    /**
10775     * Sets object's {@link fabric.Object#flipY|flipY} value
10776     * @method setFlipY
10777     * @memberOf fabric.Object.prototype
10778     * @param {Boolean} value flipY value
10779     * @return {fabric.Object} thisArg
10780     * @chainable
10781     */
10782
10783    /**
10784     * Type of an object (rect, circle, path, etc.).
10785     * Note that this property is meant to be read-only and not meant to be modified.
10786     * If you modify, certain parts of Fabric (such as JSON loading) won't work correctly.
10787     * @type String
10788     * @default
10789     */
10790    type:                     'object',
10791
10792    /**
10793     * Horizontal origin of transformation of an object (one of "left", "right", "center")
10794     * See http://jsfiddle.net/1ow02gea/40/ on how originX/originY affect objects in groups
10795     * @type String
10796     * @default
10797     */
10798    originX:                  'left',
10799
10800    /**
10801     * Vertical origin of transformation of an object (one of "top", "bottom", "center")
10802     * See http://jsfiddle.net/1ow02gea/40/ on how originX/originY affect objects in groups
10803     * @type String
10804     * @default
10805     */
10806    originY:                  'top',
10807
10808    /**
10809     * Top position of an object. Note that by default it's relative to object center. You can change this by setting originY={top/center/bottom}
10810     * @type Number
10811     * @default
10812     */
10813    top:                      0,
10814
10815    /**
10816     * Left position of an object. Note that by default it's relative to object center. You can change this by setting originX={left/center/right}
10817     * @type Number
10818     * @default
10819     */
10820    left:                     0,
10821
10822    /**
10823     * Object width
10824     * @type Number
10825     * @default
10826     */
10827    width:                    0,
10828
10829    /**
10830     * Object height
10831     * @type Number
10832     * @default
10833     */
10834    height:                   0,
10835
10836    /**
10837     * Object scale factor (horizontal)
10838     * @type Number
10839     * @default
10840     */
10841    scaleX:                   1,
10842
10843    /**
10844     * Object scale factor (vertical)
10845     * @type Number
10846     * @default
10847     */
10848    scaleY:                   1,
10849
10850    /**
10851     * When true, an object is rendered as flipped horizontally
10852     * @type Boolean
10853     * @default
10854     */
10855    flipX:                    false,
10856
10857    /**
10858     * When true, an object is rendered as flipped vertically
10859     * @type Boolean
10860     * @default
10861     */
10862    flipY:                    false,
10863
10864    /**
10865     * Opacity of an object
10866     * @type Number
10867     * @default
10868     */
10869    opacity:                  1,
10870
10871    /**
10872     * Angle of rotation of an object (in degrees)
10873     * @type Number
10874     * @default
10875     */
10876    angle:                    0,
10877
10878    /**
10879     * Size of object's controlling corners (in pixels)
10880     * @type Number
10881     * @default
10882     */
10883    cornerSize:               12,
10884
10885    /**
10886     * When true, object's controlling corners are rendered as transparent inside (i.e. stroke instead of fill)
10887     * @type Boolean
10888     * @default
10889     */
10890    transparentCorners:       true,
10891
10892    /**
10893     * Default cursor value used when hovering over this object on canvas
10894     * @type String
10895     * @default
10896     */
10897    hoverCursor:              null,
10898
10899    /**
10900     * Padding between object and its controlling borders (in pixels)
10901     * @type Number
10902     * @default
10903     */
10904    padding:                  0,
10905
10906    /**
10907     * Color of controlling borders of an object (when it's active)
10908     * @type String
10909     * @default
10910     */
10911    borderColor:              'rgba(102,153,255,0.75)',
10912
10913    /**
10914     * Color of controlling corners of an object (when it's active)
10915     * @type String
10916     * @default
10917     */
10918    cornerColor:              'rgba(102,153,255,0.5)',
10919
10920    /**
10921     * When true, this object will use center point as the origin of transformation
10922     * when being scaled via the controls.
10923     * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
10924     * @since 1.3.4
10925     * @type Boolean
10926     * @default
10927     */
10928    centeredScaling:          false,
10929
10930    /**
10931     * When true, this object will use center point as the origin of transformation
10932     * when being rotated via the controls.
10933     * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
10934     * @since 1.3.4
10935     * @type Boolean
10936     * @default
10937     */
10938    centeredRotation:         true,
10939
10940    /**
10941     * Color of object's fill
10942     * @type String
10943     * @default
10944     */
10945    fill:                     'rgb(0,0,0)',
10946
10947    /**
10948     * Fill rule used to fill an object
10949     * accepted values are nonzero, evenodd
10950     * <b>Backwards incompatibility note:</b> This property was used for setting globalCompositeOperation until v1.4.12 (use `fabric.Object#globalCompositeOperation` instead)
10951     * @type String
10952     * @default
10953     */
10954    fillRule:                 'nonzero',
10955
10956    /**
10957     * Composite rule used for canvas globalCompositeOperation
10958     * @type String
10959     * @default
10960     */
10961    globalCompositeOperation: 'source-over',
10962
10963    /**
10964     * Background color of an object. Only works with text objects at the moment.
10965     * @type String
10966     * @default
10967     */
10968    backgroundColor:          '',
10969
10970    /**
10971     * When defined, an object is rendered via stroke and this property specifies its color
10972     * @type String
10973     * @default
10974     */
10975    stroke:                   null,
10976
10977    /**
10978     * Width of a stroke used to render this object
10979     * @type Number
10980     * @default
10981     */
10982    strokeWidth:              1,
10983
10984    /**
10985     * Array specifying dash pattern of an object's stroke (stroke must be defined)
10986     * @type Array
10987     */
10988    strokeDashArray:          null,
10989
10990    /**
10991     * Line endings style of an object's stroke (one of "butt", "round", "square")
10992     * @type String
10993     * @default
10994     */
10995    strokeLineCap:            'butt',
10996
10997    /**
10998     * Corner style of an object's stroke (one of "bevil", "round", "miter")
10999     * @type String
11000     * @default
11001     */
11002    strokeLineJoin:           'miter',
11003
11004    /**
11005     * Maximum miter length (used for strokeLineJoin = "miter") of an object's stroke
11006     * @type Number
11007     * @default
11008     */
11009    strokeMiterLimit:         10,
11010
11011    /**
11012     * Shadow object representing shadow of this shape
11013     * @type fabric.Shadow
11014     * @default
11015     */
11016    shadow:                   null,
11017
11018    /**
11019     * Opacity of object's controlling borders when object is active and moving
11020     * @type Number
11021     * @default
11022     */
11023    borderOpacityWhenMoving:  0.4,
11024
11025    /**
11026     * Scale factor of object's controlling borders
11027     * @type Number
11028     * @default
11029     */
11030    borderScaleFactor:        1,
11031
11032    /**
11033     * Transform matrix (similar to SVG's transform matrix)
11034     * @type Array
11035     */
11036    transformMatrix:          null,
11037
11038    /**
11039     * Minimum allowed scale value of an object
11040     * @type Number
11041     * @default
11042     */
11043    minScaleLimit:            0.01,
11044
11045    /**
11046     * When set to `false`, an object can not be selected for modification (using either point-click-based or group-based selection).
11047     * But events still fire on it.
11048     * @type Boolean
11049     * @default
11050     */
11051    selectable:               true,
11052
11053    /**
11054     * When set to `false`, an object can not be a target of events. All events propagate through it. Introduced in v1.3.4
11055     * @type Boolean
11056     * @default
11057     */
11058    evented:                  true,
11059
11060    /**
11061     * When set to `false`, an object is not rendered on canvas
11062     * @type Boolean
11063     * @default
11064     */
11065    visible:                  true,
11066
11067    /**
11068     * When set to `false`, object's controls are not displayed and can not be used to manipulate object
11069     * @type Boolean
11070     * @default
11071     */
11072    hasControls:              true,
11073
11074    /**
11075     * When set to `false`, object's controlling borders are not rendered
11076     * @type Boolean
11077     * @default
11078     */
11079    hasBorders:               true,
11080
11081    /**
11082     * When set to `false`, object's controlling rotating point will not be visible or selectable
11083     * @type Boolean
11084     * @default
11085     */
11086    hasRotatingPoint:         true,
11087
11088    /**
11089     * Offset for object's controlling rotating point (when enabled via `hasRotatingPoint`)
11090     * @type Number
11091     * @default
11092     */
11093    rotatingPointOffset:      40,
11094
11095    /**
11096     * When set to `true`, objects are "found" on canvas on per-pixel basis rather than according to bounding box
11097     * @type Boolean
11098     * @default
11099     */
11100    perPixelTargetFind:       false,
11101
11102    /**
11103     * When `false`, default object's values are not included in its serialization
11104     * @type Boolean
11105     * @default
11106     */
11107    includeDefaultValues:     true,
11108
11109    /**
11110     * Function that determines clipping of an object (context is passed as a first argument)
11111     * Note that context origin is at the object's center point (not left/top corner)
11112     * @type Function
11113     */
11114    clipTo:                   null,
11115
11116    /**
11117     * When `true`, object horizontal movement is locked
11118     * @type Boolean
11119     * @default
11120     */
11121    lockMovementX:            false,
11122
11123    /**
11124     * When `true`, object vertical movement is locked
11125     * @type Boolean
11126     * @default
11127     */
11128    lockMovementY:            false,
11129
11130    /**
11131     * When `true`, object rotation is locked
11132     * @type Boolean
11133     * @default
11134     */
11135    lockRotation:             false,
11136
11137    /**
11138     * When `true`, object horizontal scaling is locked
11139     * @type Boolean
11140     * @default
11141     */
11142    lockScalingX:             false,
11143
11144    /**
11145     * When `true`, object vertical scaling is locked
11146     * @type Boolean
11147     * @default
11148     */
11149    lockScalingY:             false,
11150
11151    /**
11152     * When `true`, object non-uniform scaling is locked
11153     * @type Boolean
11154     * @default
11155     */
11156    lockUniScaling:           false,
11157
11158    /**
11159     * When `true`, object cannot be flipped by scaling into negative values
11160     * @type Boolean
11161     * @default
11162     */
11163
11164    lockScalingFlip:          false,
11165    /**
11166     * List of properties to consider when checking if state
11167     * of an object is changed (fabric.Object#hasStateChanged)
11168     * as well as for history (undo/redo) purposes
11169     * @type Array
11170     */
11171    stateProperties:  (
11172      'top left width height scaleX scaleY flipX flipY originX originY transformMatrix ' +
11173      'stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit ' +
11174      'angle opacity fill fillRule globalCompositeOperation shadow clipTo visible backgroundColor'
11175    ).split(' '),
11176
11177    /**
11178     * Constructor
11179     * @param {Object} [options] Options object
11180     */
11181    initialize: function(options) {
11182      if (options) {
11183        this.setOptions(options);
11184      }
11185    },
11186
11187    /**
11188     * @private
11189     * @param {Object} [options] Options object
11190     */
11191    _initGradient: function(options) {
11192      if (options.fill && options.fill.colorStops && !(options.fill instanceof fabric.Gradient)) {
11193        this.set('fill', new fabric.Gradient(options.fill));
11194      }
11195    },
11196
11197    /**
11198     * @private
11199     * @param {Object} [options] Options object
11200     */
11201    _initPattern: function(options) {
11202      if (options.fill && options.fill.source && !(options.fill instanceof fabric.Pattern)) {
11203        this.set('fill', new fabric.Pattern(options.fill));
11204      }
11205      if (options.stroke && options.stroke.source && !(options.stroke instanceof fabric.Pattern)) {
11206        this.set('stroke', new fabric.Pattern(options.stroke));
11207      }
11208    },
11209
11210    /**
11211     * @private
11212     * @param {Object} [options] Options object
11213     */
11214    _initClipping: function(options) {
11215      if (!options.clipTo || typeof options.clipTo !== 'string') {
11216        return;
11217      }
11218
11219      var functionBody = fabric.util.getFunctionBody(options.clipTo);
11220      if (typeof functionBody !== 'undefined') {
11221        this.clipTo = new Function('ctx', functionBody);
11222      }
11223    },
11224
11225    /**
11226     * Sets object's properties from options
11227     * @param {Object} [options] Options object
11228     */
11229    setOptions: function(options) {
11230      for (var prop in options) {
11231        this.set(prop, options[prop]);
11232      }
11233      this._initGradient(options);
11234      this._initPattern(options);
11235      this._initClipping(options);
11236    },
11237
11238    /**
11239     * Transforms context when rendering an object
11240     * @param {CanvasRenderingContext2D} ctx Context
11241     * @param {Boolean} fromLeft When true, context is transformed to object's top/left corner. This is used when rendering text on Node
11242     */
11243    transform: function(ctx, fromLeft) {
11244      var center = fromLeft ? this._getLeftTopCoords() : this.getCenterPoint();
11245      ctx.translate(center.x, center.y);
11246      ctx.rotate(degreesToRadians(this.angle));
11247      ctx.scale(
11248        this.scaleX * (this.flipX ? -1 : 1),
11249        this.scaleY * (this.flipY ? -1 : 1)
11250      );
11251    },
11252
11253    /**
11254     * Returns an object representation of an instance
11255     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
11256     * @return {Object} Object representation of an instance
11257     */
11258    toObject: function(propertiesToInclude) {
11259      var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
11260
11261          object = {
11262            type:                     this.type,
11263            originX:                  this.originX,
11264            originY:                  this.originY,
11265            left:                     toFixed(this.left, NUM_FRACTION_DIGITS),
11266            top:                      toFixed(this.top, NUM_FRACTION_DIGITS),
11267            width:                    toFixed(this.width, NUM_FRACTION_DIGITS),
11268            height:                   toFixed(this.height, NUM_FRACTION_DIGITS),
11269            fill:                     (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
11270            stroke:                   (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
11271            strokeWidth:              toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
11272            strokeDashArray:          this.strokeDashArray,
11273            strokeLineCap:            this.strokeLineCap,
11274            strokeLineJoin:           this.strokeLineJoin,
11275            strokeMiterLimit:         toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
11276            scaleX:                   toFixed(this.scaleX, NUM_FRACTION_DIGITS),
11277            scaleY:                   toFixed(this.scaleY, NUM_FRACTION_DIGITS),
11278            angle:                    toFixed(this.getAngle(), NUM_FRACTION_DIGITS),
11279            flipX:                    this.flipX,
11280            flipY:                    this.flipY,
11281            opacity:                  toFixed(this.opacity, NUM_FRACTION_DIGITS),
11282            shadow:                   (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
11283            visible:                  this.visible,
11284            clipTo:                   this.clipTo && String(this.clipTo),
11285            backgroundColor:          this.backgroundColor,
11286            fillRule:                 this.fillRule,
11287            globalCompositeOperation: this.globalCompositeOperation
11288          };
11289
11290      if (!this.includeDefaultValues) {
11291        object = this._removeDefaultValues(object);
11292      }
11293
11294      fabric.util.populateWithProperties(this, object, propertiesToInclude);
11295
11296      return object;
11297    },
11298
11299    /**
11300     * Returns (dataless) object representation of an instance
11301     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
11302     * @return {Object} Object representation of an instance
11303     */
11304    toDatalessObject: function(propertiesToInclude) {
11305      // will be overwritten by subclasses
11306      return this.toObject(propertiesToInclude);
11307    },
11308
11309    /**
11310     * @private
11311     * @param {Object} object
11312     */
11313    _removeDefaultValues: function(object) {
11314      var prototype = fabric.util.getKlass(object.type).prototype,
11315          stateProperties = prototype.stateProperties;
11316
11317      stateProperties.forEach(function(prop) {
11318        if (object[prop] === prototype[prop]) {
11319          delete object[prop];
11320        }
11321      });
11322
11323      return object;
11324    },
11325
11326    /**
11327     * Returns a string representation of an instance
11328     * @return {String}
11329     */
11330    toString: function() {
11331      return '#<fabric.' + capitalize(this.type) + '>';
11332    },
11333
11334    /**
11335     * Basic getter
11336     * @param {String} property Property name
11337     * @return {Any} value of a property
11338     */
11339    get: function(property) {
11340      return this[property];
11341    },
11342
11343    /**
11344     * @private
11345     */
11346    _setObject: function(obj) {
11347      for (var prop in obj) {
11348        this._set(prop, obj[prop]);
11349      }
11350    },
11351
11352    /**
11353     * Sets property to a given value. When changing position/dimension -related properties (left, top, scale, angle, etc.) `set` does not update position of object's borders/controls. If you need to update those, call `setCoords()`.
11354     * @param {String|Object} key Property name or object (if object, iterate over the object properties)
11355     * @param {Object|Function} value Property value (if function, the value is passed into it and its return value is used as a new one)
11356     * @return {fabric.Object} thisArg
11357     * @chainable
11358     */
11359    set: function(key, value) {
11360      if (typeof key === 'object') {
11361        this._setObject(key);
11362      }
11363      else {
11364        if (typeof value === 'function' && key !== 'clipTo') {
11365          this._set(key, value(this.get(key)));
11366        }
11367        else {
11368          this._set(key, value);
11369        }
11370      }
11371      return this;
11372    },
11373
11374    /**
11375     * @private
11376     * @param {String} key
11377     * @param {Any} value
11378     * @return {fabric.Object} thisArg
11379     */
11380    _set: function(key, value) {
11381      var shouldConstrainValue = (key === 'scaleX' || key === 'scaleY');
11382
11383      if (shouldConstrainValue) {
11384        value = this._constrainScale(value);
11385      }
11386      if (key === 'scaleX' && value < 0) {
11387        this.flipX = !this.flipX;
11388        value *= -1;
11389      }
11390      else if (key === 'scaleY' && value < 0) {
11391        this.flipY = !this.flipY;
11392        value *= -1;
11393      }
11394      else if (key === 'width' || key === 'height') {
11395        this.minScaleLimit = toFixed(Math.min(0.1, 1/Math.max(this.width, this.height)), 2);
11396      }
11397      else if (key === 'shadow' && value && !(value instanceof fabric.Shadow)) {
11398        value = new fabric.Shadow(value);
11399      }
11400
11401      this[key] = value;
11402
11403      return this;
11404    },
11405
11406    /**
11407     * Toggles specified property from `true` to `false` or from `false` to `true`
11408     * @param {String} property Property to toggle
11409     * @return {fabric.Object} thisArg
11410     * @chainable
11411     */
11412    toggle: function(property) {
11413      var value = this.get(property);
11414      if (typeof value === 'boolean') {
11415        this.set(property, !value);
11416      }
11417      return this;
11418    },
11419
11420    /**
11421     * Sets sourcePath of an object
11422     * @param {String} value Value to set sourcePath to
11423     * @return {fabric.Object} thisArg
11424     * @chainable
11425     */
11426    setSourcePath: function(value) {
11427      this.sourcePath = value;
11428      return this;
11429    },
11430
11431    /**
11432     * Retrieves viewportTransform from Object's canvas if possible
11433     * @method getViewportTransform
11434     * @memberOf fabric.Object.prototype
11435     * @return {Boolean} flipY value // TODO
11436     */
11437    getViewportTransform: function() {
11438      if (this.canvas && this.canvas.viewportTransform) {
11439        return this.canvas.viewportTransform;
11440      }
11441      return [1, 0, 0, 1, 0, 0];
11442    },
11443
11444    /**
11445     * Renders an object on a specified context
11446     * @param {CanvasRenderingContext2D} ctx Context to render on
11447     * @param {Boolean} [noTransform] When true, context is not transformed
11448     */
11449    render: function(ctx, noTransform) {
11450      // do not render if width/height are zeros or object is not visible
11451      if ((this.width === 0 && this.height === 0) || !this.visible) {
11452        return;
11453      }
11454
11455      ctx.save();
11456
11457      //setup fill rule for current object
11458      this._setupCompositeOperation(ctx);
11459      if (!noTransform) {
11460        this.transform(ctx);
11461      }
11462      this._setStrokeStyles(ctx);
11463      this._setFillStyles(ctx);
11464      if (this.transformMatrix) {
11465        ctx.transform.apply(ctx, this.transformMatrix);
11466      }
11467      this._setOpacity(ctx);
11468      this._setShadow(ctx);
11469      this.clipTo && fabric.util.clipContext(this, ctx);
11470      this._render(ctx, noTransform);
11471      this.clipTo && ctx.restore();
11472      this._removeShadow(ctx);
11473      this._restoreCompositeOperation(ctx);
11474
11475      ctx.restore();
11476    },
11477
11478    /* @private
11479     * @param {CanvasRenderingContext2D} ctx Context to render on
11480     */
11481    _setOpacity: function(ctx) {
11482      if (this.group) {
11483        this.group._setOpacity(ctx);
11484      }
11485      ctx.globalAlpha *= this.opacity;
11486    },
11487
11488    _setStrokeStyles: function(ctx) {
11489      if (this.stroke) {
11490        ctx.lineWidth = this.strokeWidth;
11491        ctx.lineCap = this.strokeLineCap;
11492        ctx.lineJoin = this.strokeLineJoin;
11493        ctx.miterLimit = this.strokeMiterLimit;
11494        ctx.strokeStyle = this.stroke.toLive
11495          ? this.stroke.toLive(ctx, this)
11496          : this.stroke;
11497      }
11498    },
11499
11500    _setFillStyles: function(ctx) {
11501      if (this.fill) {
11502        ctx.fillStyle = this.fill.toLive
11503          ? this.fill.toLive(ctx, this)
11504          : this.fill;
11505      }
11506    },
11507
11508    /**
11509     * Renders controls and borders for the object
11510     * @param {CanvasRenderingContext2D} ctx Context to render on
11511     * @param {Boolean} [noTransform] When true, context is not transformed
11512     */
11513    _renderControls: function(ctx, noTransform) {
11514      if (!this.active || noTransform) {
11515        return;
11516      }
11517      var vpt = this.getViewportTransform();
11518      ctx.save();
11519      var center;
11520      if (this.group) {
11521        center = fabric.util.transformPoint(this.group.getCenterPoint(), vpt);
11522        ctx.translate(center.x, center.y);
11523        ctx.rotate(degreesToRadians(this.group.angle));
11524      }
11525      center = fabric.util.transformPoint(this.getCenterPoint(), vpt, null != this.group);
11526      if (this.group) {
11527        center.x *= this.group.scaleX;
11528        center.y *= this.group.scaleY;
11529      }
11530      ctx.translate(center.x, center.y);
11531      ctx.rotate(degreesToRadians(this.angle));
11532      this.drawBorders(ctx);
11533      this.drawControls(ctx);
11534      ctx.restore();
11535    },
11536
11537    /**
11538     * @private
11539     * @param {CanvasRenderingContext2D} ctx Context to render on
11540     */
11541    _setShadow: function(ctx) {
11542      if (!this.shadow) {
11543        return;
11544      }
11545
11546      var multX = (this.canvas && this.canvas.viewportTransform[0]) || 1,
11547          multY = (this.canvas && this.canvas.viewportTransform[3]) || 1;
11548
11549      ctx.shadowColor = this.shadow.color;
11550      ctx.shadowBlur = this.shadow.blur * (multX + multY) * (this.scaleX + this.scaleY) / 4;
11551      ctx.shadowOffsetX = this.shadow.offsetX * multX * this.scaleX;
11552      ctx.shadowOffsetY = this.shadow.offsetY * multY * this.scaleY;
11553    },
11554
11555    /**
11556     * @private
11557     * @param {CanvasRenderingContext2D} ctx Context to render on
11558     */
11559    _removeShadow: function(ctx) {
11560      if (!this.shadow) {
11561        return;
11562      }
11563
11564      ctx.shadowColor = '';
11565      ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
11566    },
11567
11568    /**
11569     * @private
11570     * @param {CanvasRenderingContext2D} ctx Context to render on
11571     */
11572    _renderFill: function(ctx) {
11573      if (!this.fill) {
11574        return;
11575      }
11576
11577      ctx.save();
11578      if (this.fill.gradientTransform) {
11579        var g = this.fill.gradientTransform;
11580        ctx.transform.apply(ctx, g);
11581      }
11582      if (this.fill.toLive) {
11583        ctx.translate(
11584          -this.width / 2 + this.fill.offsetX || 0,
11585          -this.height / 2 + this.fill.offsetY || 0);
11586      }
11587      if (this.fillRule === 'evenodd') {
11588        ctx.fill('evenodd');
11589      }
11590      else {
11591        ctx.fill();
11592      }
11593      ctx.restore();
11594      if (this.shadow && !this.shadow.affectStroke) {
11595        this._removeShadow(ctx);
11596      }
11597    },
11598
11599    /**
11600     * @private
11601     * @param {CanvasRenderingContext2D} ctx Context to render on
11602     */
11603    _renderStroke: function(ctx) {
11604      if (!this.stroke || this.strokeWidth === 0) {
11605        return;
11606      }
11607
11608      ctx.save();
11609      if (this.strokeDashArray) {
11610        // Spec requires the concatenation of two copies the dash list when the number of elements is odd
11611        if (1 & this.strokeDashArray.length) {
11612          this.strokeDashArray.push.apply(this.strokeDashArray, this.strokeDashArray);
11613        }
11614        if (supportsLineDash) {
11615          ctx.setLineDash(this.strokeDashArray);
11616          this._stroke && this._stroke(ctx);
11617        }
11618        else {
11619          this._renderDashedStroke && this._renderDashedStroke(ctx);
11620        }
11621        ctx.stroke();
11622      }
11623      else {
11624        if (this.stroke.gradientTransform) {
11625          var g = this.stroke.gradientTransform;
11626          ctx.transform.apply(ctx, g);
11627        }
11628        this._stroke ? this._stroke(ctx) : ctx.stroke();
11629      }
11630      this._removeShadow(ctx);
11631      ctx.restore();
11632    },
11633
11634    /**
11635     * Clones an instance
11636     * @param {Function} callback Callback is invoked with a clone as a first argument
11637     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
11638     * @return {fabric.Object} clone of an instance
11639     */
11640    clone: function(callback, propertiesToInclude) {
11641      if (this.constructor.fromObject) {
11642        return this.constructor.fromObject(this.toObject(propertiesToInclude), callback);
11643      }
11644      return new fabric.Object(this.toObject(propertiesToInclude));
11645    },
11646
11647    /**
11648     * Creates an instance of fabric.Image out of an object
11649     * @param {Function} callback callback, invoked with an instance as a first argument
11650     * @return {fabric.Object} thisArg
11651     */
11652    cloneAsImage: function(callback) {
11653      var dataUrl = this.toDataURL();
11654      fabric.util.loadImage(dataUrl, function(img) {
11655        if (callback) {
11656          callback(new fabric.Image(img));
11657        }
11658      });
11659      return this;
11660    },
11661
11662    /**
11663     * Converts an object into a data-url-like string
11664     * @param {Object} options Options object
11665     * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
11666     * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
11667     * @param {Number} [options.multiplier=1] Multiplier to scale by
11668     * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
11669     * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
11670     * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
11671     * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
11672     * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
11673     */
11674    toDataURL: function(options) {
11675      options || (options = { });
11676
11677      var el = fabric.util.createCanvasElement(),
11678          boundingRect = this.getBoundingRect();
11679
11680      el.width = boundingRect.width;
11681      el.height = boundingRect.height;
11682
11683      fabric.util.wrapElement(el, 'div');
11684      var canvas = new fabric.StaticCanvas(el);
11685
11686      // to avoid common confusion https://github.com/kangax/fabric.js/issues/806
11687      if (options.format === 'jpg') {
11688        options.format = 'jpeg';
11689      }
11690
11691      if (options.format === 'jpeg') {
11692        canvas.backgroundColor = '#fff';
11693      }
11694
11695      var origParams = {
11696        active: this.get('active'),
11697        left: this.getLeft(),
11698        top: this.getTop()
11699      };
11700
11701      this.set('active', false);
11702      this.setPositionByOrigin(new fabric.Point(el.width / 2, el.height / 2), 'center', 'center');
11703
11704      var originalCanvas = this.canvas;
11705      canvas.add(this);
11706      var data = canvas.toDataURL(options);
11707
11708      this.set(origParams).setCoords();
11709      this.canvas = originalCanvas;
11710
11711      canvas.dispose();
11712      canvas = null;
11713
11714      return data;
11715    },
11716
11717    /**
11718     * Returns true if specified type is identical to the type of an instance
11719     * @param {String} type Type to check against
11720     * @return {Boolean}
11721     */
11722    isType: function(type) {
11723      return this.type === type;
11724    },
11725
11726    /**
11727     * Returns complexity of an instance
11728     * @return {Number} complexity of this instance
11729     */
11730    complexity: function() {
11731      return 0;
11732    },
11733
11734    /**
11735     * Returns a JSON representation of an instance
11736     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
11737     * @return {Object} JSON
11738     */
11739    toJSON: function(propertiesToInclude) {
11740      // delegate, not alias
11741      return this.toObject(propertiesToInclude);
11742    },
11743
11744    /**
11745     * Sets gradient (fill or stroke) of an object
11746     * <b>Backwards incompatibility note:</b> This method was named "setGradientFill" until v1.1.0
11747     * @param {String} property Property name 'stroke' or 'fill'
11748     * @param {Object} [options] Options object
11749     * @param {String} [options.type] Type of gradient 'radial' or 'linear'
11750     * @param {Number} [options.x1=0] x-coordinate of start point
11751     * @param {Number} [options.y1=0] y-coordinate of start point
11752     * @param {Number} [options.x2=0] x-coordinate of end point
11753     * @param {Number} [options.y2=0] y-coordinate of end point
11754     * @param {Number} [options.r1=0] Radius of start point (only for radial gradients)
11755     * @param {Number} [options.r2=0] Radius of end point (only for radial gradients)
11756     * @param {Object} [options.colorStops] Color stops object eg. {0: 'ff0000', 1: '000000'}
11757     * @return {fabric.Object} thisArg
11758     * @chainable
11759     * @see {@link http://jsfiddle.net/fabricjs/58y8b/|jsFiddle demo}
11760     * @example <caption>Set linear gradient</caption>
11761     * object.setGradient('fill', {
11762     *   type: 'linear',
11763     *   x1: -object.width / 2,
11764     *   y1: 0,
11765     *   x2: object.width / 2,
11766     *   y2: 0,
11767     *   colorStops: {
11768     *     0: 'red',
11769     *     0.5: '#005555',
11770     *     1: 'rgba(0,0,255,0.5)'
11771     *   }
11772     * });
11773     * canvas.renderAll();
11774     * @example <caption>Set radial gradient</caption>
11775     * object.setGradient('fill', {
11776     *   type: 'radial',
11777     *   x1: 0,
11778     *   y1: 0,
11779     *   x2: 0,
11780     *   y2: 0,
11781     *   r1: object.width / 2,
11782     *   r2: 10,
11783     *   colorStops: {
11784     *     0: 'red',
11785     *     0.5: '#005555',
11786     *     1: 'rgba(0,0,255,0.5)'
11787     *   }
11788     * });
11789     * canvas.renderAll();
11790     */
11791    setGradient: function(property, options) {
11792      options || (options = { });
11793
11794      var gradient = { colorStops: [] };
11795
11796      gradient.type = options.type || (options.r1 || options.r2 ? 'radial' : 'linear');
11797      gradient.coords = {
11798        x1: options.x1,
11799        y1: options.y1,
11800        x2: options.x2,
11801        y2: options.y2
11802      };
11803
11804      if (options.r1 || options.r2) {
11805        gradient.coords.r1 = options.r1;
11806        gradient.coords.r2 = options.r2;
11807      }
11808
11809      for (var position in options.colorStops) {
11810        var color = new fabric.Color(options.colorStops[position]);
11811        gradient.colorStops.push({
11812          offset: position,
11813          color: color.toRgb(),
11814          opacity: color.getAlpha()
11815        });
11816      }
11817
11818      return this.set(property, fabric.Gradient.forObject(this, gradient));
11819    },
11820
11821    /**
11822     * Sets pattern fill of an object
11823     * @param {Object} options Options object
11824     * @param {(String|HTMLImageElement)} options.source Pattern source
11825     * @param {String} [options.repeat=repeat] Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat)
11826     * @param {Number} [options.offsetX=0] Pattern horizontal offset from object's left/top corner
11827     * @param {Number} [options.offsetY=0] Pattern vertical offset from object's left/top corner
11828     * @return {fabric.Object} thisArg
11829     * @chainable
11830     * @see {@link http://jsfiddle.net/fabricjs/QT3pa/|jsFiddle demo}
11831     * @example <caption>Set pattern</caption>
11832     * fabric.util.loadImage('http://fabricjs.com/assets/escheresque_ste.png', function(img) {
11833     *   object.setPatternFill({
11834     *     source: img,
11835     *     repeat: 'repeat'
11836     *   });
11837     *   canvas.renderAll();
11838     * });
11839     */
11840    setPatternFill: function(options) {
11841      return this.set('fill', new fabric.Pattern(options));
11842    },
11843
11844    /**
11845     * Sets {@link fabric.Object#shadow|shadow} of an object
11846     * @param {Object|String} [options] Options object or string (e.g. "2px 2px 10px rgba(0,0,0,0.2)")
11847     * @param {String} [options.color=rgb(0,0,0)] Shadow color
11848     * @param {Number} [options.blur=0] Shadow blur
11849     * @param {Number} [options.offsetX=0] Shadow horizontal offset
11850     * @param {Number} [options.offsetY=0] Shadow vertical offset
11851     * @return {fabric.Object} thisArg
11852     * @chainable
11853     * @see {@link http://jsfiddle.net/fabricjs/7gvJG/|jsFiddle demo}
11854     * @example <caption>Set shadow with string notation</caption>
11855     * object.setShadow('2px 2px 10px rgba(0,0,0,0.2)');
11856     * canvas.renderAll();
11857     * @example <caption>Set shadow with object notation</caption>
11858     * object.setShadow({
11859     *   color: 'red',
11860     *   blur: 10,
11861     *   offsetX: 20,
11862     *   offsetY: 20
11863     * });
11864     * canvas.renderAll();
11865     */
11866    setShadow: function(options) {
11867      return this.set('shadow', options ? new fabric.Shadow(options) : null);
11868    },
11869
11870    /**
11871     * Sets "color" of an instance (alias of `set('fill', &hellip;)`)
11872     * @param {String} color Color value
11873     * @return {fabric.Object} thisArg
11874     * @chainable
11875     */
11876    setColor: function(color) {
11877      this.set('fill', color);
11878      return this;
11879    },
11880
11881    /**
11882     * Sets "angle" of an instance
11883     * @param {Number} angle Angle value (in degrees)
11884     * @return {fabric.Object} thisArg
11885     * @chainable
11886     */
11887    setAngle: function(angle) {
11888      var shouldCenterOrigin = (this.originX !== 'center' || this.originY !== 'center') && this.centeredRotation;
11889
11890      if (shouldCenterOrigin) {
11891        this._setOriginToCenter();
11892      }
11893
11894      this.set('angle', angle);
11895
11896      if (shouldCenterOrigin) {
11897        this._resetOrigin();
11898      }
11899
11900      return this;
11901    },
11902
11903    /**
11904     * Centers object horizontally on canvas to which it was added last.
11905     * You might need to call `setCoords` on an object after centering, to update controls area.
11906     * @return {fabric.Object} thisArg
11907     * @chainable
11908     */
11909    centerH: function () {
11910      this.canvas.centerObjectH(this);
11911      return this;
11912    },
11913
11914    /**
11915     * Centers object vertically on canvas to which it was added last.
11916     * You might need to call `setCoords` on an object after centering, to update controls area.
11917     * @return {fabric.Object} thisArg
11918     * @chainable
11919     */
11920    centerV: function () {
11921      this.canvas.centerObjectV(this);
11922      return this;
11923    },
11924
11925    /**
11926     * Centers object vertically and horizontally on canvas to which is was added last
11927     * You might need to call `setCoords` on an object after centering, to update controls area.
11928     * @return {fabric.Object} thisArg
11929     * @chainable
11930     */
11931    center: function () {
11932      this.canvas.centerObject(this);
11933      return this;
11934    },
11935
11936    /**
11937     * Removes object from canvas to which it was added last
11938     * @return {fabric.Object} thisArg
11939     * @chainable
11940     */
11941    remove: function() {
11942      this.canvas.remove(this);
11943      return this;
11944    },
11945
11946    /**
11947     * Returns coordinates of a pointer relative to an object
11948     * @param {Event} e Event to operate upon
11949     * @param {Object} [pointer] Pointer to operate upon (instead of event)
11950     * @return {Object} Coordinates of a pointer (x, y)
11951     */
11952    getLocalPointer: function(e, pointer) {
11953      pointer = pointer || this.canvas.getPointer(e);
11954      var objectLeftTop = this.translateToOriginPoint(this.getCenterPoint(), 'left', 'top');
11955      return {
11956        x: pointer.x - objectLeftTop.x,
11957        y: pointer.y - objectLeftTop.y
11958      };
11959    },
11960
11961    /**
11962     * Sets canvas globalCompositeOperation for specific object
11963     * custom composition operation for the particular object can be specifed using globalCompositeOperation property
11964     * @param {CanvasRenderingContext2D} ctx Rendering canvas context
11965     */
11966    _setupCompositeOperation: function (ctx) {
11967      if (this.globalCompositeOperation) {
11968        this._prevGlobalCompositeOperation = ctx.globalCompositeOperation;
11969        ctx.globalCompositeOperation = this.globalCompositeOperation;
11970      }
11971    },
11972
11973    /**
11974     * Restores previously saved canvas globalCompositeOperation after obeject rendering
11975     * @param {CanvasRenderingContext2D} ctx Rendering canvas context
11976     */
11977    _restoreCompositeOperation: function (ctx) {
11978      if (this.globalCompositeOperation && this._prevGlobalCompositeOperation) {
11979        ctx.globalCompositeOperation = this._prevGlobalCompositeOperation;
11980      }
11981    }
11982  });
11983
11984  fabric.util.createAccessors(fabric.Object);
11985
11986  /**
11987   * Alias for {@link fabric.Object.prototype.setAngle}
11988   * @alias rotate -> setAngle
11989   * @memberof fabric.Object
11990   */
11991  fabric.Object.prototype.rotate = fabric.Object.prototype.setAngle;
11992
11993  extend(fabric.Object.prototype, fabric.Observable);
11994
11995  /**
11996   * Defines the number of fraction digits to use when serializing object values.
11997   * You can use it to increase/decrease precision of such values like left, top, scaleX, scaleY, etc.
11998   * @static
11999   * @memberof fabric.Object
12000   * @constant
12001   * @type Number
12002   */
12003  fabric.Object.NUM_FRACTION_DIGITS = 2;
12004
12005  /**
12006   * Unique id used internally when creating SVG elements
12007   * @static
12008   * @memberof fabric.Object
12009   * @type Number
12010   */
12011  fabric.Object.__uid = 0;
12012
12013})(typeof exports !== 'undefined' ? exports : this);
12014
12015
12016(function() {
12017
12018  var degreesToRadians = fabric.util.degreesToRadians;
12019
12020  fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
12021
12022    /**
12023     * Translates the coordinates from origin to center coordinates (based on the object's dimensions)
12024     * @param {fabric.Point} point The point which corresponds to the originX and originY params
12025     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
12026     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
12027     * @return {fabric.Point}
12028     */
12029    translateToCenterPoint: function(point, originX, originY) {
12030      var cx = point.x,
12031          cy = point.y,
12032          strokeWidth = this.stroke ? this.strokeWidth : 0;
12033
12034      if (originX === 'left') {
12035        cx = point.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
12036      }
12037      else if (originX === 'right') {
12038        cx = point.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
12039      }
12040
12041      if (originY === 'top') {
12042        cy = point.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
12043      }
12044      else if (originY === 'bottom') {
12045        cy = point.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
12046      }
12047
12048      // Apply the reverse rotation to the point (it's already scaled properly)
12049      return fabric.util.rotatePoint(new fabric.Point(cx, cy), point, degreesToRadians(this.angle));
12050    },
12051
12052    /**
12053     * Translates the coordinates from center to origin coordinates (based on the object's dimensions)
12054     * @param {fabric.Point} center The point which corresponds to center of the object
12055     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
12056     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
12057     * @return {fabric.Point}
12058     */
12059    translateToOriginPoint: function(center, originX, originY) {
12060      var x = center.x,
12061          y = center.y,
12062          strokeWidth = this.stroke ? this.strokeWidth : 0;
12063
12064      // Get the point coordinates
12065      if (originX === 'left') {
12066        x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
12067      }
12068      else if (originX === 'right') {
12069        x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
12070      }
12071      if (originY === 'top') {
12072        y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
12073      }
12074      else if (originY === 'bottom') {
12075        y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
12076      }
12077
12078      // Apply the rotation to the point (it's already scaled properly)
12079      return fabric.util.rotatePoint(new fabric.Point(x, y), center, degreesToRadians(this.angle));
12080    },
12081
12082    /**
12083     * Returns the real center coordinates of the object
12084     * @return {fabric.Point}
12085     */
12086    getCenterPoint: function() {
12087      var leftTop = new fabric.Point(this.left, this.top);
12088      return this.translateToCenterPoint(leftTop, this.originX, this.originY);
12089    },
12090
12091    /**
12092     * Returns the coordinates of the object based on center coordinates
12093     * @param {fabric.Point} point The point which corresponds to the originX and originY params
12094     * @return {fabric.Point}
12095     */
12096    // getOriginPoint: function(center) {
12097    //   return this.translateToOriginPoint(center, this.originX, this.originY);
12098    // },
12099
12100    /**
12101     * Returns the coordinates of the object as if it has a different origin
12102     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
12103     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
12104     * @return {fabric.Point}
12105     */
12106    getPointByOrigin: function(originX, originY) {
12107      var center = this.getCenterPoint();
12108      return this.translateToOriginPoint(center, originX, originY);
12109    },
12110
12111    /**
12112     * Returns the point in local coordinates
12113     * @param {fabric.Point} point The point relative to the global coordinate system
12114     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
12115     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
12116     * @return {fabric.Point}
12117     */
12118    toLocalPoint: function(point, originX, originY) {
12119      var center = this.getCenterPoint(),
12120          strokeWidth = this.stroke ? this.strokeWidth : 0,
12121          x, y;
12122
12123      if (originX && originY) {
12124        if (originX === 'left') {
12125          x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
12126        }
12127        else if (originX === 'right') {
12128          x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
12129        }
12130        else {
12131          x = center.x;
12132        }
12133
12134        if (originY === 'top') {
12135          y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
12136        }
12137        else if (originY === 'bottom') {
12138          y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
12139        }
12140        else {
12141          y = center.y;
12142        }
12143      }
12144      else {
12145        x = this.left;
12146        y = this.top;
12147      }
12148
12149      return fabric.util.rotatePoint(new fabric.Point(point.x, point.y), center, -degreesToRadians(this.angle))
12150        .subtractEquals(new fabric.Point(x, y));
12151    },
12152
12153    /**
12154     * Returns the point in global coordinates
12155     * @param {fabric.Point} The point relative to the local coordinate system
12156     * @return {fabric.Point}
12157     */
12158    // toGlobalPoint: function(point) {
12159    //   return fabric.util.rotatePoint(point, this.getCenterPoint(), degreesToRadians(this.angle)).addEquals(new fabric.Point(this.left, this.top));
12160    // },
12161
12162    /**
12163     * Sets the position of the object taking into consideration the object's origin
12164     * @param {fabric.Point} pos The new position of the object
12165     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
12166     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
12167     * @return {void}
12168     */
12169    setPositionByOrigin: function(pos, originX, originY) {
12170      var center = this.translateToCenterPoint(pos, originX, originY),
12171          position = this.translateToOriginPoint(center, this.originX, this.originY);
12172
12173      this.set('left', position.x);
12174      this.set('top', position.y);
12175    },
12176
12177    /**
12178     * @param {String} to One of 'left', 'center', 'right'
12179     */
12180    adjustPosition: function(to) {
12181      var angle = degreesToRadians(this.angle),
12182          hypotHalf = this.getWidth() / 2,
12183          xHalf = Math.cos(angle) * hypotHalf,
12184          yHalf = Math.sin(angle) * hypotHalf,
12185          hypotFull = this.getWidth(),
12186          xFull = Math.cos(angle) * hypotFull,
12187          yFull = Math.sin(angle) * hypotFull;
12188
12189      if (this.originX === 'center' && to === 'left' ||
12190          this.originX === 'right' && to === 'center') {
12191        // move half left
12192        this.left -= xHalf;
12193        this.top -= yHalf;
12194      }
12195      else if (this.originX === 'left' && to === 'center' ||
12196               this.originX === 'center' && to === 'right') {
12197        // move half right
12198        this.left += xHalf;
12199        this.top += yHalf;
12200      }
12201      else if (this.originX === 'left' && to === 'right') {
12202        // move full right
12203        this.left += xFull;
12204        this.top += yFull;
12205      }
12206      else if (this.originX === 'right' && to === 'left') {
12207        // move full left
12208        this.left -= xFull;
12209        this.top -= yFull;
12210      }
12211
12212      this.setCoords();
12213      this.originX = to;
12214    },
12215
12216    /**
12217     * Sets the origin/position of the object to it's center point
12218     * @private
12219     * @return {void}
12220     */
12221    _setOriginToCenter: function() {
12222      this._originalOriginX = this.originX;
12223      this._originalOriginY = this.originY;
12224
12225      var center = this.getCenterPoint();
12226
12227      this.originX = 'center';
12228      this.originY = 'center';
12229
12230      this.left = center.x;
12231      this.top = center.y;
12232    },
12233
12234    /**
12235     * Resets the origin/position of the object to it's original origin
12236     * @private
12237     * @return {void}
12238     */
12239    _resetOrigin: function() {
12240      var originPoint = this.translateToOriginPoint(
12241        this.getCenterPoint(),
12242        this._originalOriginX,
12243        this._originalOriginY);
12244
12245      this.originX = this._originalOriginX;
12246      this.originY = this._originalOriginY;
12247
12248      this.left = originPoint.x;
12249      this.top = originPoint.y;
12250
12251      this._originalOriginX = null;
12252      this._originalOriginY = null;
12253    },
12254
12255    /**
12256     * @private
12257     */
12258    _getLeftTopCoords: function() {
12259      return this.translateToOriginPoint(this.getCenterPoint(), 'left', 'center');
12260    }
12261  });
12262
12263})();
12264
12265
12266(function() {
12267
12268  var degreesToRadians = fabric.util.degreesToRadians;
12269
12270  fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
12271
12272    /**
12273     * Object containing coordinates of object's controls
12274     * @type Object
12275     * @default
12276     */
12277    oCoords: null,
12278
12279    /**
12280     * Checks if object intersects with an area formed by 2 points
12281     * @param {Object} pointTL top-left point of area
12282     * @param {Object} pointBR bottom-right point of area
12283     * @return {Boolean} true if object intersects with an area formed by 2 points
12284     */
12285    intersectsWithRect: function(pointTL, pointBR) {
12286      var oCoords = this.oCoords,
12287          tl = new fabric.Point(oCoords.tl.x, oCoords.tl.y),
12288          tr = new fabric.Point(oCoords.tr.x, oCoords.tr.y),
12289          bl = new fabric.Point(oCoords.bl.x, oCoords.bl.y),
12290          br = new fabric.Point(oCoords.br.x, oCoords.br.y),
12291          intersection = fabric.Intersection.intersectPolygonRectangle(
12292            [tl, tr, br, bl],
12293            pointTL,
12294            pointBR
12295          );
12296      return intersection.status === 'Intersection';
12297    },
12298
12299    /**
12300     * Checks if object intersects with another object
12301     * @param {Object} other Object to test
12302     * @return {Boolean} true if object intersects with another object
12303     */
12304    intersectsWithObject: function(other) {
12305      // extracts coords
12306      function getCoords(oCoords) {
12307        return {
12308          tl: new fabric.Point(oCoords.tl.x, oCoords.tl.y),
12309          tr: new fabric.Point(oCoords.tr.x, oCoords.tr.y),
12310          bl: new fabric.Point(oCoords.bl.x, oCoords.bl.y),
12311          br: new fabric.Point(oCoords.br.x, oCoords.br.y)
12312        };
12313      }
12314      var thisCoords = getCoords(this.oCoords),
12315          otherCoords = getCoords(other.oCoords),
12316          intersection = fabric.Intersection.intersectPolygonPolygon(
12317            [thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl],
12318            [otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl]
12319          );
12320
12321      return intersection.status === 'Intersection';
12322    },
12323
12324    /**
12325     * Checks if object is fully contained within area of another object
12326     * @param {Object} other Object to test
12327     * @return {Boolean} true if object is fully contained within area of another object
12328     */
12329    isContainedWithinObject: function(other) {
12330      var boundingRect = other.getBoundingRect(),
12331          point1 = new fabric.Point(boundingRect.left, boundingRect.top),
12332          point2 = new fabric.Point(boundingRect.left + boundingRect.width, boundingRect.top + boundingRect.height);
12333
12334      return this.isContainedWithinRect(point1, point2);
12335    },
12336
12337    /**
12338     * Checks if object is fully contained within area formed by 2 points
12339     * @param {Object} pointTL top-left point of area
12340     * @param {Object} pointBR bottom-right point of area
12341     * @return {Boolean} true if object is fully contained within area formed by 2 points
12342     */
12343    isContainedWithinRect: function(pointTL, pointBR) {
12344      var boundingRect = this.getBoundingRect();
12345
12346      return (
12347        boundingRect.left >= pointTL.x &&
12348        boundingRect.left + boundingRect.width <= pointBR.x &&
12349        boundingRect.top >= pointTL.y &&
12350        boundingRect.top + boundingRect.height <= pointBR.y
12351      );
12352    },
12353
12354    /**
12355     * Checks if point is inside the object
12356     * @param {fabric.Point} point Point to check against
12357     * @return {Boolean} true if point is inside the object
12358     */
12359    containsPoint: function(point) {
12360      var lines = this._getImageLines(this.oCoords),
12361          xPoints = this._findCrossPoints(point, lines);
12362
12363      // if xPoints is odd then point is inside the object
12364      return (xPoints !== 0 && xPoints % 2 === 1);
12365    },
12366
12367    /**
12368     * Method that returns an object with the object edges in it, given the coordinates of the corners
12369     * @private
12370     * @param {Object} oCoords Coordinates of the object corners
12371     */
12372    _getImageLines: function(oCoords) {
12373      return {
12374        topline: {
12375          o: oCoords.tl,
12376          d: oCoords.tr
12377        },
12378        rightline: {
12379          o: oCoords.tr,
12380          d: oCoords.br
12381        },
12382        bottomline: {
12383          o: oCoords.br,
12384          d: oCoords.bl
12385        },
12386        leftline: {
12387          o: oCoords.bl,
12388          d: oCoords.tl
12389        }
12390      };
12391    },
12392
12393    /**
12394     * Helper method to determine how many cross points are between the 4 object edges
12395     * and the horizontal line determined by a point on canvas
12396     * @private
12397     * @param {fabric.Point} point Point to check
12398     * @param {Object} oCoords Coordinates of the object being evaluated
12399     */
12400    _findCrossPoints: function(point, oCoords) {
12401      var b1, b2, a1, a2, xi, yi,
12402          xcount = 0,
12403          iLine;
12404
12405      for (var lineKey in oCoords) {
12406        iLine = oCoords[lineKey];
12407        // optimisation 1: line below point. no cross
12408        if ((iLine.o.y < point.y) && (iLine.d.y < point.y)) {
12409          continue;
12410        }
12411        // optimisation 2: line above point. no cross
12412        if ((iLine.o.y >= point.y) && (iLine.d.y >= point.y)) {
12413          continue;
12414        }
12415        // optimisation 3: vertical line case
12416        if ((iLine.o.x === iLine.d.x) && (iLine.o.x >= point.x)) {
12417          xi = iLine.o.x;
12418          yi = point.y;
12419        }
12420        // calculate the intersection point
12421        else {
12422          b1 = 0;
12423          b2 = (iLine.d.y - iLine.o.y) / (iLine.d.x - iLine.o.x);
12424          a1 = point.y - b1 * point.x;
12425          a2 = iLine.o.y - b2 * iLine.o.x;
12426
12427          xi = - (a1 - a2) / (b1 - b2);
12428          yi = a1 + b1 * xi;
12429        }
12430        // dont count xi < point.x cases
12431        if (xi >= point.x) {
12432          xcount += 1;
12433        }
12434        // optimisation 4: specific for square images
12435        if (xcount === 2) {
12436          break;
12437        }
12438      }
12439      return xcount;
12440    },
12441
12442    /**
12443     * Returns width of an object's bounding rectangle
12444     * @deprecated since 1.0.4
12445     * @return {Number} width value
12446     */
12447    getBoundingRectWidth: function() {
12448      return this.getBoundingRect().width;
12449    },
12450
12451    /**
12452     * Returns height of an object's bounding rectangle
12453     * @deprecated since 1.0.4
12454     * @return {Number} height value
12455     */
12456    getBoundingRectHeight: function() {
12457      return this.getBoundingRect().height;
12458    },
12459
12460    /**
12461     * Returns coordinates of object's bounding rectangle (left, top, width, height)
12462     * @return {Object} Object with left, top, width, height properties
12463     */
12464    getBoundingRect: function() {
12465      this.oCoords || this.setCoords();
12466
12467      var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x],
12468          minX = fabric.util.array.min(xCoords),
12469          maxX = fabric.util.array.max(xCoords),
12470          width = Math.abs(minX - maxX),
12471
12472          yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y],
12473          minY = fabric.util.array.min(yCoords),
12474          maxY = fabric.util.array.max(yCoords),
12475          height = Math.abs(minY - maxY);
12476
12477      return {
12478        left: minX,
12479        top: minY,
12480        width: width,
12481        height: height
12482      };
12483    },
12484
12485    /**
12486     * Returns width of an object
12487     * @return {Number} width value
12488     */
12489    getWidth: function() {
12490      return this.width * this.scaleX;
12491    },
12492
12493    /**
12494     * Returns height of an object
12495     * @return {Number} height value
12496     */
12497    getHeight: function() {
12498      return this.height * this.scaleY;
12499    },
12500
12501    /**
12502     * Makes sure the scale is valid and modifies it if necessary
12503     * @private
12504     * @param {Number} value
12505     * @return {Number}
12506     */
12507    _constrainScale: function(value) {
12508      if (Math.abs(value) < this.minScaleLimit) {
12509        if (value < 0) {
12510          return -this.minScaleLimit;
12511        }
12512        else {
12513          return this.minScaleLimit;
12514        }
12515      }
12516      return value;
12517    },
12518
12519    /**
12520     * Scales an object (equally by x and y)
12521     * @param {Number} value Scale factor
12522     * @return {fabric.Object} thisArg
12523     * @chainable
12524     */
12525    scale: function(value) {
12526      value = this._constrainScale(value);
12527
12528      if (value < 0) {
12529        this.flipX = !this.flipX;
12530        this.flipY = !this.flipY;
12531        value *= -1;
12532      }
12533
12534      this.scaleX = value;
12535      this.scaleY = value;
12536      this.setCoords();
12537      return this;
12538    },
12539
12540    /**
12541     * Scales an object to a given width, with respect to bounding box (scaling by x/y equally)
12542     * @param {Number} value New width value
12543     * @return {fabric.Object} thisArg
12544     * @chainable
12545     */
12546    scaleToWidth: function(value) {
12547      // adjust to bounding rect factor so that rotated shapes would fit as well
12548      var boundingRectFactor = this.getBoundingRectWidth() / this.getWidth();
12549      return this.scale(value / this.width / boundingRectFactor);
12550    },
12551
12552    /**
12553     * Scales an object to a given height, with respect to bounding box (scaling by x/y equally)
12554     * @param {Number} value New height value
12555     * @return {fabric.Object} thisArg
12556     * @chainable
12557     */
12558    scaleToHeight: function(value) {
12559      // adjust to bounding rect factor so that rotated shapes would fit as well
12560      var boundingRectFactor = this.getBoundingRectHeight() / this.getHeight();
12561      return this.scale(value / this.height / boundingRectFactor);
12562    },
12563
12564    /**
12565     * Sets corner position coordinates based on current angle, width and height
12566     * See https://github.com/kangax/fabric.js/wiki/When-to-call-setCoords
12567     * @return {fabric.Object} thisArg
12568     * @chainable
12569     */
12570    setCoords: function() {
12571      var theta = degreesToRadians(this.angle),
12572          vpt = this.getViewportTransform(),
12573          f = function (p) {
12574            return fabric.util.transformPoint(p, vpt);
12575          },
12576          p = this._calculateCurrentDimensions(false),
12577          currentWidth = p.x, currentHeight = p.y;
12578
12579      // If width is negative, make postive. Fixes path selection issue
12580      if (currentWidth < 0) {
12581        currentWidth = Math.abs(currentWidth);
12582      }
12583
12584      var _hypotenuse = Math.sqrt(
12585            Math.pow(currentWidth / 2, 2) +
12586            Math.pow(currentHeight / 2, 2)),
12587
12588          _angle = Math.atan(
12589            isFinite(currentHeight / currentWidth)
12590              ? currentHeight / currentWidth
12591              : 0),
12592
12593          // offset added for rotate and scale actions
12594          offsetX = Math.cos(_angle + theta) * _hypotenuse,
12595          offsetY = Math.sin(_angle + theta) * _hypotenuse,
12596          sinTh = Math.sin(theta),
12597          cosTh = Math.cos(theta),
12598          coords = this.getCenterPoint(),
12599          wh = new fabric.Point(currentWidth, currentHeight),
12600          _tl =   new fabric.Point(coords.x - offsetX, coords.y - offsetY),
12601          _tr =   new fabric.Point(_tl.x + (wh.x * cosTh),   _tl.y + (wh.x * sinTh)),
12602          bl =  f(new fabric.Point(_tl.x - (wh.y * sinTh),   _tl.y + (wh.y * cosTh))),
12603          br  = f(new fabric.Point(_tr.x - (wh.y * sinTh),   _tr.y + (wh.y * cosTh))),
12604          tl  = f(_tl),
12605          tr  = f(_tr),
12606          ml  = new fabric.Point((tl.x + bl.x)/2, (tl.y + bl.y)/2),
12607          mt  = new fabric.Point((tr.x + tl.x)/2, (tr.y + tl.y)/2),
12608          mr  = new fabric.Point((br.x + tr.x)/2, (br.y + tr.y)/2),
12609          mb  = new fabric.Point((br.x + bl.x)/2, (br.y + bl.y)/2),
12610          mtr = new fabric.Point(mt.x + sinTh * this.rotatingPointOffset, mt.y - cosTh * this.rotatingPointOffset);
12611      // debugging
12612
12613      /* setTimeout(function() {
12614         canvas.contextTop.fillStyle = 'green';
12615         canvas.contextTop.fillRect(mb.x, mb.y, 3, 3);
12616         canvas.contextTop.fillRect(bl.x, bl.y, 3, 3);
12617         canvas.contextTop.fillRect(br.x, br.y, 3, 3);
12618         canvas.contextTop.fillRect(tl.x, tl.y, 3, 3);
12619         canvas.contextTop.fillRect(tr.x, tr.y, 3, 3);
12620         canvas.contextTop.fillRect(ml.x, ml.y, 3, 3);
12621         canvas.contextTop.fillRect(mr.x, mr.y, 3, 3);
12622         canvas.contextTop.fillRect(mt.x, mt.y, 3, 3);
12623         canvas.contextTop.fillRect(mtr.x, mtr.y, 3, 3);
12624       }, 50); */
12625
12626      this.oCoords = {
12627        // corners
12628        tl: tl, tr: tr, br: br, bl: bl,
12629        // middle
12630        ml: ml, mt: mt, mr: mr, mb: mb,
12631        // rotating point
12632        mtr: mtr
12633      };
12634
12635      // set coordinates of the draggable boxes in the corners used to scale/rotate the image
12636      this._setCornerCoords && this._setCornerCoords();
12637
12638      return this;
12639    }
12640  });
12641})();
12642
12643
12644fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
12645
12646  /**
12647   * Moves an object to the bottom of the stack of drawn objects
12648   * @return {fabric.Object} thisArg
12649   * @chainable
12650   */
12651  sendToBack: function() {
12652    if (this.group) {
12653      fabric.StaticCanvas.prototype.sendToBack.call(this.group, this);
12654    }
12655    else {
12656      this.canvas.sendToBack(this);
12657    }
12658    return this;
12659  },
12660
12661  /**
12662   * Moves an object to the top of the stack of drawn objects
12663   * @return {fabric.Object} thisArg
12664   * @chainable
12665   */
12666  bringToFront: function() {
12667    if (this.group) {
12668      fabric.StaticCanvas.prototype.bringToFront.call(this.group, this);
12669    }
12670    else {
12671      this.canvas.bringToFront(this);
12672    }
12673    return this;
12674  },
12675
12676  /**
12677   * Moves an object down in stack of drawn objects
12678   * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object
12679   * @return {fabric.Object} thisArg
12680   * @chainable
12681   */
12682  sendBackwards: function(intersecting) {
12683    if (this.group) {
12684      fabric.StaticCanvas.prototype.sendBackwards.call(this.group, this, intersecting);
12685    }
12686    else {
12687      this.canvas.sendBackwards(this, intersecting);
12688    }
12689    return this;
12690  },
12691
12692  /**
12693   * Moves an object up in stack of drawn objects
12694   * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object
12695   * @return {fabric.Object} thisArg
12696   * @chainable
12697   */
12698  bringForward: function(intersecting) {
12699    if (this.group) {
12700      fabric.StaticCanvas.prototype.bringForward.call(this.group, this, intersecting);
12701    }
12702    else {
12703      this.canvas.bringForward(this, intersecting);
12704    }
12705    return this;
12706  },
12707
12708  /**
12709   * Moves an object to specified level in stack of drawn objects
12710   * @param {Number} index New position of object
12711   * @return {fabric.Object} thisArg
12712   * @chainable
12713   */
12714  moveTo: function(index) {
12715    if (this.group) {
12716      fabric.StaticCanvas.prototype.moveTo.call(this.group, this, index);
12717    }
12718    else {
12719      this.canvas.moveTo(this, index);
12720    }
12721    return this;
12722  }
12723});
12724
12725
12726/* _TO_SVG_START_ */
12727fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
12728
12729  /**
12730   * Returns styles-string for svg-export
12731   * @return {String}
12732   */
12733  getSvgStyles: function() {
12734
12735    var fill = this.fill
12736          ? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
12737          : 'none',
12738        fillRule = this.fillRule,
12739        stroke = this.stroke
12740          ? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
12741          : 'none',
12742
12743        strokeWidth = this.strokeWidth ? this.strokeWidth : '0',
12744        strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : '',
12745        strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt',
12746        strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter',
12747        strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4',
12748        opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1',
12749
12750        visibility = this.visible ? '' : ' visibility: hidden;',
12751        filter = this.shadow ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
12752
12753    return [
12754      'stroke: ', stroke, '; ',
12755      'stroke-width: ', strokeWidth, '; ',
12756      'stroke-dasharray: ', strokeDashArray, '; ',
12757      'stroke-linecap: ', strokeLineCap, '; ',
12758      'stroke-linejoin: ', strokeLineJoin, '; ',
12759      'stroke-miterlimit: ', strokeMiterLimit, '; ',
12760      'fill: ', fill, '; ',
12761      'fill-rule: ', fillRule, '; ',
12762      'opacity: ', opacity, ';',
12763      filter,
12764      visibility
12765    ].join('');
12766  },
12767
12768  /**
12769   * Returns transform-string for svg-export
12770   * @return {String}
12771   */
12772  getSvgTransform: function() {
12773    if (this.group && this.group.type === 'path-group') {
12774      return '';
12775    }
12776    var toFixed = fabric.util.toFixed,
12777        angle = this.getAngle(),
12778        vpt = !this.canvas || this.canvas.svgViewportTransformation ? this.getViewportTransform() : [1, 0, 0, 1, 0, 0],
12779        center = fabric.util.transformPoint(this.getCenterPoint(), vpt),
12780
12781        NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
12782
12783        translatePart = this.type === 'path-group' ? '' : 'translate(' +
12784                          toFixed(center.x, NUM_FRACTION_DIGITS) +
12785                          ' ' +
12786                          toFixed(center.y, NUM_FRACTION_DIGITS) +
12787                        ')',
12788
12789        anglePart = angle !== 0
12790          ? (' rotate(' + toFixed(angle, NUM_FRACTION_DIGITS) + ')')
12791          : '',
12792
12793        scalePart = (this.scaleX === 1 && this.scaleY === 1 && vpt[0] === 1 && vpt[3] === 1)
12794          ? '' :
12795          (' scale(' +
12796            toFixed(this.scaleX * vpt[0], NUM_FRACTION_DIGITS) +
12797            ' ' +
12798            toFixed(this.scaleY * vpt[3], NUM_FRACTION_DIGITS) +
12799          ')'),
12800
12801        addTranslateX = this.type === 'path-group' ? this.width * vpt[0] : 0,
12802
12803        flipXPart = this.flipX ? ' matrix(-1 0 0 1 ' + addTranslateX + ' 0) ' : '',
12804
12805        addTranslateY = this.type === 'path-group' ? this.height * vpt[3] : 0,
12806
12807        flipYPart = this.flipY ? ' matrix(1 0 0 -1 0 ' + addTranslateY + ')' : '';
12808
12809    return [
12810      translatePart, anglePart, scalePart, flipXPart, flipYPart
12811    ].join('');
12812  },
12813
12814  /**
12815   * Returns transform-string for svg-export from the transform matrix of single elements
12816   * @return {String}
12817   */
12818  getSvgTransformMatrix: function() {
12819    return this.transformMatrix ? ' matrix(' + this.transformMatrix.join(' ') + ') ' : '';
12820  },
12821
12822  /**
12823   * @private
12824   */
12825  _createBaseSVGMarkup: function() {
12826    var markup = [ ];
12827
12828    if (this.fill && this.fill.toLive) {
12829      markup.push(this.fill.toSVG(this, false));
12830    }
12831    if (this.stroke && this.stroke.toLive) {
12832      markup.push(this.stroke.toSVG(this, false));
12833    }
12834    if (this.shadow) {
12835      markup.push(this.shadow.toSVG(this));
12836    }
12837    return markup;
12838  }
12839});
12840/* _TO_SVG_END_ */
12841
12842
12843/*
12844  Depends on `stateProperties`
12845*/
12846fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
12847
12848  /**
12849   * Returns true if object state (one of its state properties) was changed
12850   * @return {Boolean} true if instance' state has changed since `{@link fabric.Object#saveState}` was called
12851   */
12852  hasStateChanged: function() {
12853    return this.stateProperties.some(function(prop) {
12854      return this.get(prop) !== this.originalState[prop];
12855    }, this);
12856  },
12857
12858  /**
12859   * Saves state of an object
12860   * @param {Object} [options] Object with additional `stateProperties` array to include when saving state
12861   * @return {fabric.Object} thisArg
12862   */
12863  saveState: function(options) {
12864    this.stateProperties.forEach(function(prop) {
12865      this.originalState[prop] = this.get(prop);
12866    }, this);
12867
12868    if (options && options.stateProperties) {
12869      options.stateProperties.forEach(function(prop) {
12870        this.originalState[prop] = this.get(prop);
12871      }, this);
12872    }
12873
12874    return this;
12875  },
12876
12877  /**
12878   * Setups state of an object
12879   * @return {fabric.Object} thisArg
12880   */
12881  setupState: function() {
12882    this.originalState = { };
12883    this.saveState();
12884
12885    return this;
12886  }
12887});
12888
12889
12890(function() {
12891
12892  var degreesToRadians = fabric.util.degreesToRadians,
12893      //jscs:disable requireCamelCaseOrUpperCaseIdentifiers
12894      isVML = function() { return typeof G_vmlCanvasManager !== 'undefined'; };
12895  //jscs:enable requireCamelCaseOrUpperCaseIdentifiers
12896
12897  fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
12898
12899    /**
12900     * The object interactivity controls.
12901     * @private
12902     */
12903    _controlsVisibility: null,
12904
12905    /**
12906     * Determines which corner has been clicked
12907     * @private
12908     * @param {Object} pointer The pointer indicating the mouse position
12909     * @return {String|Boolean} corner code (tl, tr, bl, br, etc.), or false if nothing is found
12910     */
12911    _findTargetCorner: function(pointer) {
12912      if (!this.hasControls || !this.active) {
12913        return false;
12914      }
12915
12916      var ex = pointer.x,
12917          ey = pointer.y,
12918          xPoints,
12919          lines;
12920
12921      for (var i in this.oCoords) {
12922
12923        if (!this.isControlVisible(i)) {
12924          continue;
12925        }
12926
12927        if (i === 'mtr' && !this.hasRotatingPoint) {
12928          continue;
12929        }
12930
12931        if (this.get('lockUniScaling') &&
12932           (i === 'mt' || i === 'mr' || i === 'mb' || i === 'ml')) {
12933          continue;
12934        }
12935
12936        lines = this._getImageLines(this.oCoords[i].corner);
12937
12938        // debugging
12939
12940        // canvas.contextTop.fillRect(lines.bottomline.d.x, lines.bottomline.d.y, 2, 2);
12941        // canvas.contextTop.fillRect(lines.bottomline.o.x, lines.bottomline.o.y, 2, 2);
12942
12943        // canvas.contextTop.fillRect(lines.leftline.d.x, lines.leftline.d.y, 2, 2);
12944        // canvas.contextTop.fillRect(lines.leftline.o.x, lines.leftline.o.y, 2, 2);
12945
12946        // canvas.contextTop.fillRect(lines.topline.d.x, lines.topline.d.y, 2, 2);
12947        // canvas.contextTop.fillRect(lines.topline.o.x, lines.topline.o.y, 2, 2);
12948
12949        // canvas.contextTop.fillRect(lines.rightline.d.x, lines.rightline.d.y, 2, 2);
12950        // canvas.contextTop.fillRect(lines.rightline.o.x, lines.rightline.o.y, 2, 2);
12951
12952        xPoints = this._findCrossPoints({ x: ex, y: ey }, lines);
12953        if (xPoints !== 0 && xPoints % 2 === 1) {
12954          this.__corner = i;
12955          return i;
12956        }
12957      }
12958      return false;
12959    },
12960
12961    /**
12962     * Sets the coordinates of the draggable boxes in the corners of
12963     * the image used to scale/rotate it.
12964     * @private
12965     */
12966    _setCornerCoords: function() {
12967      var coords = this.oCoords,
12968          newTheta = degreesToRadians(45 - this.angle),
12969          cornerHypotenuse = Math.sqrt(2 * Math.pow(this.cornerSize, 2)) / 2,
12970          cosHalfOffset = cornerHypotenuse * Math.cos(newTheta),
12971          sinHalfOffset = cornerHypotenuse * Math.sin(newTheta),
12972          x, y;
12973
12974      for (var point in coords) {
12975        x = coords[point].x;
12976        y = coords[point].y;
12977        coords[point].corner = {
12978          tl: {
12979            x: x - sinHalfOffset,
12980            y: y - cosHalfOffset
12981          },
12982          tr: {
12983            x: x + cosHalfOffset,
12984            y: y - sinHalfOffset
12985          },
12986          bl: {
12987            x: x - cosHalfOffset,
12988            y: y + sinHalfOffset
12989          },
12990          br: {
12991            x: x + sinHalfOffset,
12992            y: y + cosHalfOffset
12993          }
12994        };
12995      }
12996    },
12997
12998    _calculateCurrentDimensions: function(shouldTransform)  {
12999      var vpt = this.getViewportTransform(),
13000          strokeWidth = this.strokeWidth,
13001          w = this.width,
13002          h = this.height,
13003          capped = this.strokeLineCap === 'round' || this.strokeLineCap === 'square',
13004          vLine = this.type === 'line' && this.width === 0,
13005          hLine = this.type === 'line' && this.height === 0,
13006          sLine = vLine || hLine,
13007          strokeW = (capped && hLine) || !sLine,
13008          strokeH = (capped && vLine) || !sLine;
13009
13010      if (vLine) {
13011        w = strokeWidth;
13012      }
13013      else if (hLine) {
13014        h = strokeWidth;
13015      }
13016      if (strokeW) {
13017        w += (w < 0 ? -strokeWidth : strokeWidth);
13018      }
13019      if (strokeH) {
13020        h += (h < 0 ? -strokeWidth : strokeWidth);
13021      }
13022
13023      w = w * this.scaleX + 2 * this.padding;
13024      h = h * this.scaleY + 2 * this.padding;
13025
13026      if (shouldTransform) {
13027        return fabric.util.transformPoint(new fabric.Point(w, h), vpt, true);
13028      }
13029      return { x: w, y: h };
13030    },
13031
13032    /**
13033     * Draws borders of an object's bounding box.
13034     * Requires public properties: width, height
13035     * Requires public options: padding, borderColor
13036     * @param {CanvasRenderingContext2D} ctx Context to draw on
13037     * @return {fabric.Object} thisArg
13038     * @chainable
13039     */
13040    drawBorders: function(ctx) {
13041      if (!this.hasBorders) {
13042        return this;
13043      }
13044
13045      ctx.save();
13046
13047      ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
13048      ctx.strokeStyle = this.borderColor;
13049      ctx.lineWidth = 1 / this.borderScaleFactor;
13050
13051      var wh = this._calculateCurrentDimensions(true),
13052          width = wh.x,
13053          height = wh.y;
13054      if (this.group) {
13055        width = width * this.group.scaleX;
13056        height = height * this.group.scaleY;
13057      }
13058
13059      ctx.strokeRect(
13060        ~~(-(width / 2)) - 0.5, // offset needed to make lines look sharper
13061        ~~(-(height / 2)) - 0.5,
13062        ~~(width) + 1, // double offset needed to make lines look sharper
13063        ~~(height) + 1
13064      );
13065
13066      if (this.hasRotatingPoint && this.isControlVisible('mtr') && !this.get('lockRotation') && this.hasControls) {
13067
13068        var rotateHeight = -height / 2;
13069
13070        ctx.beginPath();
13071        ctx.moveTo(0, rotateHeight);
13072        ctx.lineTo(0, rotateHeight - this.rotatingPointOffset);
13073        ctx.closePath();
13074        ctx.stroke();
13075      }
13076
13077      ctx.restore();
13078      return this;
13079    },
13080
13081    /**
13082     * Draws corners of an object's bounding box.
13083     * Requires public properties: width, height
13084     * Requires public options: cornerSize, padding
13085     * @param {CanvasRenderingContext2D} ctx Context to draw on
13086     * @return {fabric.Object} thisArg
13087     * @chainable
13088     */
13089    drawControls: function(ctx) {
13090      if (!this.hasControls) {
13091        return this;
13092      }
13093
13094      var wh = this._calculateCurrentDimensions(true),
13095          width = wh.x,
13096          height = wh.y,
13097          left = -(width / 2),
13098          top = -(height / 2),
13099          scaleOffset = this.cornerSize / 2,
13100          methodName = this.transparentCorners ? 'strokeRect' : 'fillRect';
13101
13102      ctx.save();
13103
13104      ctx.lineWidth = 1;
13105
13106      ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
13107      ctx.strokeStyle = ctx.fillStyle = this.cornerColor;
13108
13109      // top-left
13110      this._drawControl('tl', ctx, methodName,
13111        left - scaleOffset,
13112        top - scaleOffset);
13113
13114      // top-right
13115      this._drawControl('tr', ctx, methodName,
13116        left + width - scaleOffset,
13117        top - scaleOffset);
13118
13119      // bottom-left
13120      this._drawControl('bl', ctx, methodName,
13121        left - scaleOffset,
13122        top + height - scaleOffset);
13123
13124      // bottom-right
13125      this._drawControl('br', ctx, methodName,
13126        left + width - scaleOffset,
13127        top + height - scaleOffset);
13128
13129      if (!this.get('lockUniScaling')) {
13130
13131        // middle-top
13132        this._drawControl('mt', ctx, methodName,
13133          left + width/2 - scaleOffset,
13134          top - scaleOffset);
13135
13136        // middle-bottom
13137        this._drawControl('mb', ctx, methodName,
13138          left + width/2 - scaleOffset,
13139          top + height - scaleOffset);
13140
13141        // middle-right
13142        this._drawControl('mr', ctx, methodName,
13143          left + width - scaleOffset,
13144          top + height/2 - scaleOffset);
13145
13146        // middle-left
13147        this._drawControl('ml', ctx, methodName,
13148          left - scaleOffset,
13149          top + height/2 - scaleOffset);
13150      }
13151
13152      // middle-top-rotate
13153      if (this.hasRotatingPoint) {
13154        this._drawControl('mtr', ctx, methodName,
13155          left + width/2 - scaleOffset,
13156          top - this.rotatingPointOffset - scaleOffset);
13157      }
13158
13159      ctx.restore();
13160
13161      return this;
13162    },
13163
13164    /**
13165     * @private
13166     */
13167    _drawControl: function(control, ctx, methodName, left, top) {
13168      if (!this.isControlVisible(control)) {
13169        return;
13170      }
13171      var size = this.cornerSize;
13172      isVML() || this.transparentCorners || ctx.clearRect(left, top, size, size);
13173      ctx[methodName](left, top, size, size);
13174    },
13175
13176    /**
13177     * Returns true if the specified control is visible, false otherwise.
13178     * @param {String} controlName The name of the control. Possible values are 'tl', 'tr', 'br', 'bl', 'ml', 'mt', 'mr', 'mb', 'mtr'.
13179     * @returns {Boolean} true if the specified control is visible, false otherwise
13180     */
13181    isControlVisible: function(controlName) {
13182      return this._getControlsVisibility()[controlName];
13183    },
13184
13185    /**
13186     * Sets the visibility of the specified control.
13187     * @param {String} controlName The name of the control. Possible values are 'tl', 'tr', 'br', 'bl', 'ml', 'mt', 'mr', 'mb', 'mtr'.
13188     * @param {Boolean} visible true to set the specified control visible, false otherwise
13189     * @return {fabric.Object} thisArg
13190     * @chainable
13191     */
13192    setControlVisible: function(controlName, visible) {
13193      this._getControlsVisibility()[controlName] = visible;
13194      return this;
13195    },
13196
13197    /**
13198     * Sets the visibility state of object controls.
13199     * @param {Object} [options] Options object
13200     * @param {Boolean} [options.bl] true to enable the bottom-left control, false to disable it
13201     * @param {Boolean} [options.br] true to enable the bottom-right control, false to disable it
13202     * @param {Boolean} [options.mb] true to enable the middle-bottom control, false to disable it
13203     * @param {Boolean} [options.ml] true to enable the middle-left control, false to disable it
13204     * @param {Boolean} [options.mr] true to enable the middle-right control, false to disable it
13205     * @param {Boolean} [options.mt] true to enable the middle-top control, false to disable it
13206     * @param {Boolean} [options.tl] true to enable the top-left control, false to disable it
13207     * @param {Boolean} [options.tr] true to enable the top-right control, false to disable it
13208     * @param {Boolean} [options.mtr] true to enable the middle-top-rotate control, false to disable it
13209     * @return {fabric.Object} thisArg
13210     * @chainable
13211     */
13212    setControlsVisibility: function(options) {
13213      options || (options = { });
13214
13215      for (var p in options) {
13216        this.setControlVisible(p, options[p]);
13217      }
13218      return this;
13219    },
13220
13221    /**
13222     * Returns the instance of the control visibility set for this object.
13223     * @private
13224     * @returns {Object}
13225     */
13226    _getControlsVisibility: function() {
13227      if (!this._controlsVisibility) {
13228        this._controlsVisibility = {
13229          tl: true,
13230          tr: true,
13231          br: true,
13232          bl: true,
13233          ml: true,
13234          mt: true,
13235          mr: true,
13236          mb: true,
13237          mtr: true
13238        };
13239      }
13240      return this._controlsVisibility;
13241    }
13242  });
13243})();
13244
13245
13246fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
13247
13248  /**
13249   * Animation duration (in ms) for fx* methods
13250   * @type Number
13251   * @default
13252   */
13253  FX_DURATION: 500,
13254
13255  /**
13256   * Centers object horizontally with animation.
13257   * @param {fabric.Object} object Object to center
13258   * @param {Object} [callbacks] Callbacks object with optional "onComplete" and/or "onChange" properties
13259   * @param {Function} [callbacks.onComplete] Invoked on completion
13260   * @param {Function} [callbacks.onChange] Invoked on every step of animation
13261   * @return {fabric.Canvas} thisArg
13262   * @chainable
13263   */
13264  fxCenterObjectH: function (object, callbacks) {
13265    callbacks = callbacks || { };
13266
13267    var empty = function() { },
13268        onComplete = callbacks.onComplete || empty,
13269        onChange = callbacks.onChange || empty,
13270        _this = this;
13271
13272    fabric.util.animate({
13273      startValue: object.get('left'),
13274      endValue: this.getCenter().left,
13275      duration: this.FX_DURATION,
13276      onChange: function(value) {
13277        object.set('left', value);
13278        _this.renderAll();
13279        onChange();
13280      },
13281      onComplete: function() {
13282        object.setCoords();
13283        onComplete();
13284      }
13285    });
13286
13287    return this;
13288  },
13289
13290  /**
13291   * Centers object vertically with animation.
13292   * @param {fabric.Object} object Object to center
13293   * @param {Object} [callbacks] Callbacks object with optional "onComplete" and/or "onChange" properties
13294   * @param {Function} [callbacks.onComplete] Invoked on completion
13295   * @param {Function} [callbacks.onChange] Invoked on every step of animation
13296   * @return {fabric.Canvas} thisArg
13297   * @chainable
13298   */
13299  fxCenterObjectV: function (object, callbacks) {
13300    callbacks = callbacks || { };
13301
13302    var empty = function() { },
13303        onComplete = callbacks.onComplete || empty,
13304        onChange = callbacks.onChange || empty,
13305        _this = this;
13306
13307    fabric.util.animate({
13308      startValue: object.get('top'),
13309      endValue: this.getCenter().top,
13310      duration: this.FX_DURATION,
13311      onChange: function(value) {
13312        object.set('top', value);
13313        _this.renderAll();
13314        onChange();
13315      },
13316      onComplete: function() {
13317        object.setCoords();
13318        onComplete();
13319      }
13320    });
13321
13322    return this;
13323  },
13324
13325  /**
13326   * Same as `fabric.Canvas#remove` but animated
13327   * @param {fabric.Object} object Object to remove
13328   * @param {Object} [callbacks] Callbacks object with optional "onComplete" and/or "onChange" properties
13329   * @param {Function} [callbacks.onComplete] Invoked on completion
13330   * @param {Function} [callbacks.onChange] Invoked on every step of animation
13331   * @return {fabric.Canvas} thisArg
13332   * @chainable
13333   */
13334  fxRemove: function (object, callbacks) {
13335    callbacks = callbacks || { };
13336
13337    var empty = function() { },
13338        onComplete = callbacks.onComplete || empty,
13339        onChange = callbacks.onChange || empty,
13340        _this = this;
13341
13342    fabric.util.animate({
13343      startValue: object.get('opacity'),
13344      endValue: 0,
13345      duration: this.FX_DURATION,
13346      onStart: function() {
13347        object.set('active', false);
13348      },
13349      onChange: function(value) {
13350        object.set('opacity', value);
13351        _this.renderAll();
13352        onChange();
13353      },
13354      onComplete: function () {
13355        _this.remove(object);
13356        onComplete();
13357      }
13358    });
13359
13360    return this;
13361  }
13362});
13363
13364fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
13365  /**
13366   * Animates object's properties
13367   * @param {String|Object} property Property to animate (if string) or properties to animate (if object)
13368   * @param {Number|Object} value Value to animate property to (if string was given first) or options object
13369   * @return {fabric.Object} thisArg
13370   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#animation}
13371   * @chainable
13372   *
13373   * As object — multiple properties
13374   *
13375   * object.animate({ left: ..., top: ... });
13376   * object.animate({ left: ..., top: ... }, { duration: ... });
13377   *
13378   * As string — one property
13379   *
13380   * object.animate('left', ...);
13381   * object.animate('left', { duration: ... });
13382   *
13383   */
13384  animate: function() {
13385    if (arguments[0] && typeof arguments[0] === 'object') {
13386      var propsToAnimate = [ ], prop, skipCallbacks;
13387      for (prop in arguments[0]) {
13388        propsToAnimate.push(prop);
13389      }
13390      for (var i = 0, len = propsToAnimate.length; i < len; i++) {
13391        prop = propsToAnimate[i];
13392        skipCallbacks = i !== len - 1;
13393        this._animate(prop, arguments[0][prop], arguments[1], skipCallbacks);
13394      }
13395    }
13396    else {
13397      this._animate.apply(this, arguments);
13398    }
13399    return this;
13400  },
13401
13402  /**
13403   * @private
13404   * @param {String} property Property to animate
13405   * @param {String} to Value to animate to
13406   * @param {Object} [options] Options object
13407   * @param {Boolean} [skipCallbacks] When true, callbacks like onchange and oncomplete are not invoked
13408   */
13409  _animate: function(property, to, options, skipCallbacks) {
13410    var _this = this, propPair;
13411
13412    to = to.toString();
13413
13414    if (!options) {
13415      options = { };
13416    }
13417    else {
13418      options = fabric.util.object.clone(options);
13419    }
13420
13421    if (~property.indexOf('.')) {
13422      propPair = property.split('.');
13423    }
13424
13425    var currentValue = propPair
13426      ? this.get(propPair[0])[propPair[1]]
13427      : this.get(property);
13428
13429    if (!('from' in options)) {
13430      options.from = currentValue;
13431    }
13432
13433    if (~to.indexOf('=')) {
13434      to = currentValue + parseFloat(to.replace('=', ''));
13435    }
13436    else {
13437      to = parseFloat(to);
13438    }
13439
13440    fabric.util.animate({
13441      startValue: options.from,
13442      endValue: to,
13443      byValue: options.by,
13444      easing: options.easing,
13445      duration: options.duration,
13446      abort: options.abort && function() {
13447        return options.abort.call(_this);
13448      },
13449      onChange: function(value) {
13450        if (propPair) {
13451          _this[propPair[0]][propPair[1]] = value;
13452        }
13453        else {
13454          _this.set(property, value);
13455        }
13456        if (skipCallbacks) {
13457          return;
13458        }
13459        options.onChange && options.onChange();
13460      },
13461      onComplete: function() {
13462        if (skipCallbacks) {
13463          return;
13464        }
13465
13466        _this.setCoords();
13467        options.onComplete && options.onComplete();
13468      }
13469    });
13470  }
13471});
13472
13473
13474(function(global) {
13475
13476  'use strict';
13477
13478  var fabric = global.fabric || (global.fabric = { }),
13479      extend = fabric.util.object.extend,
13480      coordProps = { x1: 1, x2: 1, y1: 1, y2: 1 },
13481      supportsLineDash = fabric.StaticCanvas.supports('setLineDash');
13482
13483  if (fabric.Line) {
13484    fabric.warn('fabric.Line is already defined');
13485    return;
13486  }
13487
13488  /**
13489   * Line class
13490   * @class fabric.Line
13491   * @extends fabric.Object
13492   * @see {@link fabric.Line#initialize} for constructor definition
13493   */
13494  fabric.Line = fabric.util.createClass(fabric.Object, /** @lends fabric.Line.prototype */ {
13495
13496    /**
13497     * Type of an object
13498     * @type String
13499     * @default
13500     */
13501    type: 'line',
13502
13503    /**
13504     * x value or first line edge
13505     * @type Number
13506     * @default
13507     */
13508    x1: 0,
13509
13510    /**
13511     * y value or first line edge
13512     * @type Number
13513     * @default
13514     */
13515    y1: 0,
13516
13517    /**
13518     * x value or second line edge
13519     * @type Number
13520     * @default
13521     */
13522    x2: 0,
13523
13524    /**
13525     * y value or second line edge
13526     * @type Number
13527     * @default
13528     */
13529    y2: 0,
13530
13531    /**
13532     * Constructor
13533     * @param {Array} [points] Array of points
13534     * @param {Object} [options] Options object
13535     * @return {fabric.Line} thisArg
13536     */
13537    initialize: function(points, options) {
13538      options = options || { };
13539
13540      if (!points) {
13541        points = [0, 0, 0, 0];
13542      }
13543
13544      this.callSuper('initialize', options);
13545
13546      this.set('x1', points[0]);
13547      this.set('y1', points[1]);
13548      this.set('x2', points[2]);
13549      this.set('y2', points[3]);
13550
13551      this._setWidthHeight(options);
13552    },
13553
13554    /**
13555     * @private
13556     * @param {Object} [options] Options
13557     */
13558    _setWidthHeight: function(options) {
13559      options || (options = { });
13560
13561      this.width = Math.abs(this.x2 - this.x1);
13562      this.height = Math.abs(this.y2 - this.y1);
13563
13564      this.left = 'left' in options
13565        ? options.left
13566        : this._getLeftToOriginX();
13567
13568      this.top = 'top' in options
13569        ? options.top
13570        : this._getTopToOriginY();
13571    },
13572
13573    /**
13574     * @private
13575     * @param {String} key
13576     * @param {Any} value
13577     */
13578    _set: function(key, value) {
13579      this.callSuper('_set', key, value);
13580      if (typeof coordProps[key] !== 'undefined') {
13581        this._setWidthHeight();
13582      }
13583      return this;
13584    },
13585
13586    /**
13587     * @private
13588     * @return {Number} leftToOriginX Distance from left edge of canvas to originX of Line.
13589     */
13590    _getLeftToOriginX: makeEdgeToOriginGetter(
13591      { // property names
13592        origin: 'originX',
13593        axis1: 'x1',
13594        axis2: 'x2',
13595        dimension: 'width'
13596      },
13597      { // possible values of origin
13598        nearest: 'left',
13599        center: 'center',
13600        farthest: 'right'
13601      }
13602    ),
13603
13604    /**
13605     * @private
13606     * @return {Number} topToOriginY Distance from top edge of canvas to originY of Line.
13607     */
13608    _getTopToOriginY: makeEdgeToOriginGetter(
13609      { // property names
13610        origin: 'originY',
13611        axis1: 'y1',
13612        axis2: 'y2',
13613        dimension: 'height'
13614      },
13615      { // possible values of origin
13616        nearest: 'top',
13617        center: 'center',
13618        farthest: 'bottom'
13619      }
13620    ),
13621
13622    /**
13623     * @private
13624     * @param {CanvasRenderingContext2D} ctx Context to render on
13625     */
13626    _render: function(ctx, noTransform) {
13627      ctx.beginPath();
13628
13629      if (noTransform) {
13630        //  Line coords are distances from left-top of canvas to origin of line.
13631        //  To render line in a path-group, we need to translate them to
13632        //  distances from center of path-group to center of line.
13633        var cp = this.getCenterPoint();
13634        ctx.translate(
13635          cp.x - this.strokeWidth / 2,
13636          cp.y - this.strokeWidth / 2
13637        );
13638      }
13639
13640      if (!this.strokeDashArray || this.strokeDashArray && supportsLineDash) {
13641        // move from center (of virtual box) to its left/top corner
13642        // we can't assume x1, y1 is top left and x2, y2 is bottom right
13643        var p = this.calcLinePoints();
13644        ctx.moveTo(p.x1, p.y1);
13645        ctx.lineTo(p.x2, p.y2);
13646      }
13647
13648      ctx.lineWidth = this.strokeWidth;
13649
13650      // TODO: test this
13651      // make sure setting "fill" changes color of a line
13652      // (by copying fillStyle to strokeStyle, since line is stroked, not filled)
13653      var origStrokeStyle = ctx.strokeStyle;
13654      ctx.strokeStyle = this.stroke || ctx.fillStyle;
13655      this.stroke && this._renderStroke(ctx);
13656      ctx.strokeStyle = origStrokeStyle;
13657    },
13658
13659    /**
13660     * @private
13661     * @param {CanvasRenderingContext2D} ctx Context to render on
13662     */
13663    _renderDashedStroke: function(ctx) {
13664      var p = this.calcLinePoints();
13665
13666      ctx.beginPath();
13667      fabric.util.drawDashedLine(ctx, p.x1, p.y1, p.x2, p.y2, this.strokeDashArray);
13668      ctx.closePath();
13669    },
13670
13671    /**
13672     * Returns object representation of an instance
13673     * @methd toObject
13674     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
13675     * @return {Object} object representation of an instance
13676     */
13677    toObject: function(propertiesToInclude) {
13678      return extend(this.callSuper('toObject', propertiesToInclude), this.calcLinePoints());
13679    },
13680
13681    /**
13682     * Recalculates line points given width and height
13683     * @private
13684     */
13685    calcLinePoints: function() {
13686      var xMult = this.x1 <= this.x2 ? -1 : 1,
13687          yMult = this.y1 <= this.y2 ? -1 : 1,
13688          x1 = (xMult * this.width * 0.5),
13689          y1 = (yMult * this.height * 0.5),
13690          x2 = (xMult * this.width * -0.5),
13691          y2 = (yMult * this.height * -0.5);
13692
13693      return {
13694        x1: x1,
13695        x2: x2,
13696        y1: y1,
13697        y2: y2
13698      };
13699    },
13700
13701    /* _TO_SVG_START_ */
13702    /**
13703     * Returns SVG representation of an instance
13704     * @param {Function} [reviver] Method for further parsing of svg representation.
13705     * @return {String} svg representation of an instance
13706     */
13707    toSVG: function(reviver) {
13708      var markup = this._createBaseSVGMarkup(),
13709          p = { x1: this.x1, x2: this.x2, y1: this.y1, y2: this.y2 };
13710
13711      if (!(this.group && this.group.type === 'path-group')) {
13712        p = this.calcLinePoints();
13713      }
13714      markup.push(
13715        '<line ',
13716          'x1="', p.x1,
13717          '" y1="', p.y1,
13718          '" x2="', p.x2,
13719          '" y2="', p.y2,
13720          '" style="', this.getSvgStyles(),
13721          '" transform="', this.getSvgTransform(),
13722          this.getSvgTransformMatrix(),
13723        '"/>\n'
13724      );
13725
13726      return reviver ? reviver(markup.join('')) : markup.join('');
13727    },
13728    /* _TO_SVG_END_ */
13729
13730    /**
13731     * Returns complexity of an instance
13732     * @return {Number} complexity
13733     */
13734    complexity: function() {
13735      return 1;
13736    }
13737  });
13738
13739  /* _FROM_SVG_START_ */
13740  /**
13741   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Line.fromElement})
13742   * @static
13743   * @memberOf fabric.Line
13744   * @see http://www.w3.org/TR/SVG/shapes.html#LineElement
13745   */
13746  fabric.Line.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x1 y1 x2 y2'.split(' '));
13747
13748  /**
13749   * Returns fabric.Line instance from an SVG element
13750   * @static
13751   * @memberOf fabric.Line
13752   * @param {SVGElement} element Element to parse
13753   * @param {Object} [options] Options object
13754   * @return {fabric.Line} instance of fabric.Line
13755   */
13756  fabric.Line.fromElement = function(element, options) {
13757    var parsedAttributes = fabric.parseAttributes(element, fabric.Line.ATTRIBUTE_NAMES),
13758        points = [
13759          parsedAttributes.x1 || 0,
13760          parsedAttributes.y1 || 0,
13761          parsedAttributes.x2 || 0,
13762          parsedAttributes.y2 || 0
13763        ];
13764    return new fabric.Line(points, extend(parsedAttributes, options));
13765  };
13766  /* _FROM_SVG_END_ */
13767
13768  /**
13769   * Returns fabric.Line instance from an object representation
13770   * @static
13771   * @memberOf fabric.Line
13772   * @param {Object} object Object to create an instance from
13773   * @return {fabric.Line} instance of fabric.Line
13774   */
13775  fabric.Line.fromObject = function(object) {
13776    var points = [object.x1, object.y1, object.x2, object.y2];
13777    return new fabric.Line(points, object);
13778  };
13779
13780  /**
13781   * Produces a function that calculates distance from canvas edge to Line origin.
13782   */
13783  function makeEdgeToOriginGetter(propertyNames, originValues) {
13784    var origin = propertyNames.origin,
13785        axis1 = propertyNames.axis1,
13786        axis2 = propertyNames.axis2,
13787        dimension = propertyNames.dimension,
13788        nearest = originValues.nearest,
13789        center = originValues.center,
13790        farthest = originValues.farthest;
13791
13792    return function() {
13793      switch (this.get(origin)) {
13794      case nearest:
13795        return Math.min(this.get(axis1), this.get(axis2));
13796      case center:
13797        return Math.min(this.get(axis1), this.get(axis2)) + (0.5 * this.get(dimension));
13798      case farthest:
13799        return Math.max(this.get(axis1), this.get(axis2));
13800      }
13801    };
13802
13803  }
13804
13805})(typeof exports !== 'undefined' ? exports : this);
13806
13807
13808(function(global) {
13809
13810  'use strict';
13811
13812  var fabric = global.fabric || (global.fabric = { }),
13813      pi = Math.PI,
13814      extend = fabric.util.object.extend;
13815
13816  if (fabric.Circle) {
13817    fabric.warn('fabric.Circle is already defined.');
13818    return;
13819  }
13820
13821  /**
13822   * Circle class
13823   * @class fabric.Circle
13824   * @extends fabric.Object
13825   * @see {@link fabric.Circle#initialize} for constructor definition
13826   */
13827  fabric.Circle = fabric.util.createClass(fabric.Object, /** @lends fabric.Circle.prototype */ {
13828
13829    /**
13830     * Type of an object
13831     * @type String
13832     * @default
13833     */
13834    type: 'circle',
13835
13836    /**
13837     * Radius of this circle
13838     * @type Number
13839     * @default
13840     */
13841    radius: 0,
13842
13843    /**
13844     * Start angle of the circle, moving clockwise
13845     * @type Number
13846     * @default 0
13847     */
13848    startAngle: 0,
13849
13850    /**
13851     * End angle of the circle
13852     * @type Number
13853     * @default 2Pi
13854     */
13855    endAngle: pi * 2,
13856
13857    /**
13858     * Constructor
13859     * @param {Object} [options] Options object
13860     * @return {fabric.Circle} thisArg
13861     */
13862    initialize: function(options) {
13863      options = options || { };
13864
13865      this.callSuper('initialize', options);
13866      this.set('radius', options.radius || 0);
13867      this.startAngle = options.startAngle || this.startAngle;
13868      this.endAngle = options.endAngle || this.endAngle;
13869    },
13870
13871    /**
13872     * @private
13873     * @param {String} key
13874     * @param {Any} value
13875     * @return {fabric.Circle} thisArg
13876     */
13877    _set: function(key, value) {
13878      this.callSuper('_set', key, value);
13879
13880      if (key === 'radius') {
13881        this.setRadius(value);
13882      }
13883
13884      return this;
13885    },
13886
13887    /**
13888     * Returns object representation of an instance
13889     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
13890     * @return {Object} object representation of an instance
13891     */
13892    toObject: function(propertiesToInclude) {
13893      return extend(this.callSuper('toObject', propertiesToInclude), {
13894        radius: this.get('radius'),
13895        startAngle: this.startAngle,
13896        endAngle: this.endAngle
13897      });
13898    },
13899
13900    /* _TO_SVG_START_ */
13901    /**
13902     * Returns svg representation of an instance
13903     * @param {Function} [reviver] Method for further parsing of svg representation.
13904     * @return {String} svg representation of an instance
13905     */
13906    toSVG: function(reviver) {
13907      var markup = this._createBaseSVGMarkup(), x = 0, y = 0,
13908      angle = (this.endAngle - this.startAngle) % ( 2 * pi);
13909
13910      if (angle === 0) {
13911        if (this.group && this.group.type === 'path-group') {
13912          x = this.left + this.radius;
13913          y = this.top + this.radius;
13914        }
13915        markup.push(
13916          '<circle ',
13917            'cx="' + x + '" cy="' + y + '" ',
13918            'r="', this.radius,
13919            '" style="', this.getSvgStyles(),
13920            '" transform="', this.getSvgTransform(),
13921            ' ', this.getSvgTransformMatrix(),
13922          '"/>\n'
13923        );
13924      }
13925      else {
13926        var startX = Math.cos(this.startAngle) * this.radius,
13927            startY = Math.sin(this.startAngle) * this.radius,
13928            endX = Math.cos(this.endAngle) * this.radius,
13929            endY = Math.sin(this.endAngle) * this.radius,
13930            largeFlag = angle > pi ? '1' : '0';
13931
13932        markup.push(
13933          '<path d="M ' + startX + ' ' + startY,
13934          ' A ' + this.radius + ' ' + this.radius,
13935          ' 0 ', + largeFlag + ' 1', ' ' + endX + ' ' + endY,
13936          '" style="', this.getSvgStyles(),
13937          '" transform="', this.getSvgTransform(),
13938          ' ', this.getSvgTransformMatrix(),
13939          '"/>\n'
13940        );
13941      }
13942
13943      return reviver ? reviver(markup.join('')) : markup.join('');
13944    },
13945    /* _TO_SVG_END_ */
13946
13947    /**
13948     * @private
13949     * @param {CanvasRenderingContext2D} ctx context to render on
13950     * @param {Boolean} [noTransform] When true, context is not transformed
13951     */
13952    _render: function(ctx, noTransform) {
13953      ctx.beginPath();
13954      ctx.arc(noTransform ? this.left + this.radius : 0,
13955              noTransform ? this.top + this.radius : 0,
13956              this.radius,
13957              this.startAngle,
13958              this.endAngle, false);
13959      this._renderFill(ctx);
13960      this._renderStroke(ctx);
13961    },
13962
13963    /**
13964     * Returns horizontal radius of an object (according to how an object is scaled)
13965     * @return {Number}
13966     */
13967    getRadiusX: function() {
13968      return this.get('radius') * this.get('scaleX');
13969    },
13970
13971    /**
13972     * Returns vertical radius of an object (according to how an object is scaled)
13973     * @return {Number}
13974     */
13975    getRadiusY: function() {
13976      return this.get('radius') * this.get('scaleY');
13977    },
13978
13979    /**
13980     * Sets radius of an object (and updates width accordingly)
13981     * @return {Number}
13982     */
13983    setRadius: function(value) {
13984      this.radius = value;
13985      this.set('width', value * 2).set('height', value * 2);
13986    },
13987
13988    /**
13989     * Returns complexity of an instance
13990     * @return {Number} complexity of this instance
13991     */
13992    complexity: function() {
13993      return 1;
13994    }
13995  });
13996
13997  /* _FROM_SVG_START_ */
13998  /**
13999   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Circle.fromElement})
14000   * @static
14001   * @memberOf fabric.Circle
14002   * @see: http://www.w3.org/TR/SVG/shapes.html#CircleElement
14003   */
14004  fabric.Circle.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('cx cy r'.split(' '));
14005
14006  /**
14007   * Returns {@link fabric.Circle} instance from an SVG element
14008   * @static
14009   * @memberOf fabric.Circle
14010   * @param {SVGElement} element Element to parse
14011   * @param {Object} [options] Options object
14012   * @throws {Error} If value of `r` attribute is missing or invalid
14013   * @return {fabric.Circle} Instance of fabric.Circle
14014   */
14015  fabric.Circle.fromElement = function(element, options) {
14016    options || (options = { });
14017
14018    var parsedAttributes = fabric.parseAttributes(element, fabric.Circle.ATTRIBUTE_NAMES);
14019
14020    if (!isValidRadius(parsedAttributes)) {
14021      throw new Error('value of `r` attribute is required and can not be negative');
14022    }
14023
14024    parsedAttributes.left = parsedAttributes.left || 0;
14025    parsedAttributes.top = parsedAttributes.top || 0;
14026
14027    var obj = new fabric.Circle(extend(parsedAttributes, options));
14028
14029    obj.left -= obj.radius;
14030    obj.top -= obj.radius;
14031    return obj;
14032  };
14033
14034  /**
14035   * @private
14036   */
14037  function isValidRadius(attributes) {
14038    return (('radius' in attributes) && (attributes.radius >= 0));
14039  }
14040  /* _FROM_SVG_END_ */
14041
14042  /**
14043   * Returns {@link fabric.Circle} instance from an object representation
14044   * @static
14045   * @memberOf fabric.Circle
14046   * @param {Object} object Object to create an instance from
14047   * @return {Object} Instance of fabric.Circle
14048   */
14049  fabric.Circle.fromObject = function(object) {
14050    return new fabric.Circle(object);
14051  };
14052
14053})(typeof exports !== 'undefined' ? exports : this);
14054
14055
14056(function(global) {
14057
14058  'use strict';
14059
14060  var fabric = global.fabric || (global.fabric = { });
14061
14062  if (fabric.Triangle) {
14063    fabric.warn('fabric.Triangle is already defined');
14064    return;
14065  }
14066
14067  /**
14068   * Triangle class
14069   * @class fabric.Triangle
14070   * @extends fabric.Object
14071   * @return {fabric.Triangle} thisArg
14072   * @see {@link fabric.Triangle#initialize} for constructor definition
14073   */
14074  fabric.Triangle = fabric.util.createClass(fabric.Object, /** @lends fabric.Triangle.prototype */ {
14075
14076    /**
14077     * Type of an object
14078     * @type String
14079     * @default
14080     */
14081    type: 'triangle',
14082
14083    /**
14084     * Constructor
14085     * @param {Object} [options] Options object
14086     * @return {Object} thisArg
14087     */
14088    initialize: function(options) {
14089      options = options || { };
14090
14091      this.callSuper('initialize', options);
14092
14093      this.set('width', options.width || 100)
14094          .set('height', options.height || 100);
14095    },
14096
14097    /**
14098     * @private
14099     * @param {CanvasRenderingContext2D} ctx Context to render on
14100     */
14101    _render: function(ctx) {
14102      var widthBy2 = this.width / 2,
14103          heightBy2 = this.height / 2;
14104
14105      ctx.beginPath();
14106      ctx.moveTo(-widthBy2, heightBy2);
14107      ctx.lineTo(0, -heightBy2);
14108      ctx.lineTo(widthBy2, heightBy2);
14109      ctx.closePath();
14110
14111      this._renderFill(ctx);
14112      this._renderStroke(ctx);
14113    },
14114
14115    /**
14116     * @private
14117     * @param {CanvasRenderingContext2D} ctx Context to render on
14118     */
14119    _renderDashedStroke: function(ctx) {
14120      var widthBy2 = this.width / 2,
14121          heightBy2 = this.height / 2;
14122
14123      ctx.beginPath();
14124      fabric.util.drawDashedLine(ctx, -widthBy2, heightBy2, 0, -heightBy2, this.strokeDashArray);
14125      fabric.util.drawDashedLine(ctx, 0, -heightBy2, widthBy2, heightBy2, this.strokeDashArray);
14126      fabric.util.drawDashedLine(ctx, widthBy2, heightBy2, -widthBy2, heightBy2, this.strokeDashArray);
14127      ctx.closePath();
14128    },
14129
14130    /* _TO_SVG_START_ */
14131    /**
14132     * Returns SVG representation of an instance
14133     * @param {Function} [reviver] Method for further parsing of svg representation.
14134     * @return {String} svg representation of an instance
14135     */
14136    toSVG: function(reviver) {
14137      var markup = this._createBaseSVGMarkup(),
14138          widthBy2 = this.width / 2,
14139          heightBy2 = this.height / 2,
14140          points = [
14141            -widthBy2 + ' ' + heightBy2,
14142            '0 ' + -heightBy2,
14143            widthBy2 + ' ' + heightBy2
14144          ]
14145          .join(',');
14146
14147      markup.push(
14148        '<polygon ',
14149          'points="', points,
14150          '" style="', this.getSvgStyles(),
14151          '" transform="', this.getSvgTransform(),
14152        '"/>'
14153      );
14154
14155      return reviver ? reviver(markup.join('')) : markup.join('');
14156    },
14157    /* _TO_SVG_END_ */
14158
14159    /**
14160     * Returns complexity of an instance
14161     * @return {Number} complexity of this instance
14162     */
14163    complexity: function() {
14164      return 1;
14165    }
14166  });
14167
14168  /**
14169   * Returns fabric.Triangle instance from an object representation
14170   * @static
14171   * @memberOf fabric.Triangle
14172   * @param {Object} object Object to create an instance from
14173   * @return {Object} instance of Canvas.Triangle
14174   */
14175  fabric.Triangle.fromObject = function(object) {
14176    return new fabric.Triangle(object);
14177  };
14178
14179})(typeof exports !== 'undefined' ? exports : this);
14180
14181
14182(function(global) {
14183
14184  'use strict';
14185
14186  var fabric = global.fabric || (global.fabric = { }),
14187      piBy2   = Math.PI * 2,
14188      extend = fabric.util.object.extend;
14189
14190  if (fabric.Ellipse) {
14191    fabric.warn('fabric.Ellipse is already defined.');
14192    return;
14193  }
14194
14195  /**
14196   * Ellipse class
14197   * @class fabric.Ellipse
14198   * @extends fabric.Object
14199   * @return {fabric.Ellipse} thisArg
14200   * @see {@link fabric.Ellipse#initialize} for constructor definition
14201   */
14202  fabric.Ellipse = fabric.util.createClass(fabric.Object, /** @lends fabric.Ellipse.prototype */ {
14203
14204    /**
14205     * Type of an object
14206     * @type String
14207     * @default
14208     */
14209    type: 'ellipse',
14210
14211    /**
14212     * Horizontal radius
14213     * @type Number
14214     * @default
14215     */
14216    rx:   0,
14217
14218    /**
14219     * Vertical radius
14220     * @type Number
14221     * @default
14222     */
14223    ry:   0,
14224
14225    /**
14226     * Constructor
14227     * @param {Object} [options] Options object
14228     * @return {fabric.Ellipse} thisArg
14229     */
14230    initialize: function(options) {
14231      options = options || { };
14232
14233      this.callSuper('initialize', options);
14234
14235      this.set('rx', options.rx || 0);
14236      this.set('ry', options.ry || 0);
14237    },
14238
14239    /**
14240     * @private
14241     * @param {String} key
14242     * @param {Any} value
14243     * @return {fabric.Ellipse} thisArg
14244     */
14245    _set: function(key, value) {
14246      this.callSuper('_set', key, value);
14247      switch (key) {
14248
14249        case 'rx':
14250          this.rx = value;
14251          this.set('width', value * 2);
14252          break;
14253
14254        case 'ry':
14255          this.ry = value;
14256          this.set('height', value * 2);
14257          break;
14258
14259      }
14260      return this;
14261    },
14262
14263    /**
14264     * Returns horizontal radius of an object (according to how an object is scaled)
14265     * @return {Number}
14266     */
14267    getRx: function() {
14268      return this.get('rx') * this.get('scaleX');
14269    },
14270
14271    /**
14272     * Returns Vertical radius of an object (according to how an object is scaled)
14273     * @return {Number}
14274     */
14275    getRy: function() {
14276      return this.get('ry') * this.get('scaleY');
14277    },
14278
14279    /**
14280     * Returns object representation of an instance
14281     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
14282     * @return {Object} object representation of an instance
14283     */
14284    toObject: function(propertiesToInclude) {
14285      return extend(this.callSuper('toObject', propertiesToInclude), {
14286        rx: this.get('rx'),
14287        ry: this.get('ry')
14288      });
14289    },
14290
14291    /* _TO_SVG_START_ */
14292    /**
14293     * Returns svg representation of an instance
14294     * @param {Function} [reviver] Method for further parsing of svg representation.
14295     * @return {String} svg representation of an instance
14296     */
14297    toSVG: function(reviver) {
14298      var markup = this._createBaseSVGMarkup(), x = 0, y = 0;
14299      if (this.group && this.group.type === 'path-group') {
14300        x = this.left + this.rx;
14301        y = this.top + this.ry;
14302      }
14303      markup.push(
14304        '<ellipse ',
14305          'cx="', x, '" cy="', y, '" ',
14306          'rx="', this.rx,
14307          '" ry="', this.ry,
14308          '" style="', this.getSvgStyles(),
14309          '" transform="', this.getSvgTransform(),
14310          this.getSvgTransformMatrix(),
14311        '"/>\n'
14312      );
14313
14314      return reviver ? reviver(markup.join('')) : markup.join('');
14315    },
14316    /* _TO_SVG_END_ */
14317
14318    /**
14319     * @private
14320     * @param {CanvasRenderingContext2D} ctx context to render on
14321     * @param {Boolean} [noTransform] When true, context is not transformed
14322     */
14323    _render: function(ctx, noTransform) {
14324      ctx.beginPath();
14325      ctx.save();
14326      ctx.transform(1, 0, 0, this.ry/this.rx, 0, 0);
14327      ctx.arc(
14328        noTransform ? this.left + this.rx : 0,
14329        noTransform ? (this.top + this.ry) * this.rx/this.ry : 0,
14330        this.rx,
14331        0,
14332        piBy2,
14333        false);
14334      ctx.restore();
14335      this._renderFill(ctx);
14336      this._renderStroke(ctx);
14337    },
14338
14339    /**
14340     * Returns complexity of an instance
14341     * @return {Number} complexity
14342     */
14343    complexity: function() {
14344      return 1;
14345    }
14346  });
14347
14348  /* _FROM_SVG_START_ */
14349  /**
14350   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Ellipse.fromElement})
14351   * @static
14352   * @memberOf fabric.Ellipse
14353   * @see http://www.w3.org/TR/SVG/shapes.html#EllipseElement
14354   */
14355  fabric.Ellipse.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('cx cy rx ry'.split(' '));
14356
14357  /**
14358   * Returns {@link fabric.Ellipse} instance from an SVG element
14359   * @static
14360   * @memberOf fabric.Ellipse
14361   * @param {SVGElement} element Element to parse
14362   * @param {Object} [options] Options object
14363   * @return {fabric.Ellipse}
14364   */
14365  fabric.Ellipse.fromElement = function(element, options) {
14366    options || (options = { });
14367
14368    var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES);
14369
14370    parsedAttributes.left = parsedAttributes.left || 0;
14371    parsedAttributes.top = parsedAttributes.top || 0;
14372
14373    var ellipse = new fabric.Ellipse(extend(parsedAttributes, options));
14374
14375    ellipse.top -= ellipse.ry;
14376    ellipse.left -= ellipse.rx;
14377    return ellipse;
14378  };
14379  /* _FROM_SVG_END_ */
14380
14381  /**
14382   * Returns {@link fabric.Ellipse} instance from an object representation
14383   * @static
14384   * @memberOf fabric.Ellipse
14385   * @param {Object} object Object to create an instance from
14386   * @return {fabric.Ellipse}
14387   */
14388  fabric.Ellipse.fromObject = function(object) {
14389    return new fabric.Ellipse(object);
14390  };
14391
14392})(typeof exports !== 'undefined' ? exports : this);
14393
14394
14395(function(global) {
14396
14397  'use strict';
14398
14399  var fabric = global.fabric || (global.fabric = { }),
14400      extend = fabric.util.object.extend;
14401
14402  if (fabric.Rect) {
14403    console.warn('fabric.Rect is already defined');
14404    return;
14405  }
14406
14407  var stateProperties = fabric.Object.prototype.stateProperties.concat();
14408  stateProperties.push('rx', 'ry', 'x', 'y');
14409
14410  /**
14411   * Rectangle class
14412   * @class fabric.Rect
14413   * @extends fabric.Object
14414   * @return {fabric.Rect} thisArg
14415   * @see {@link fabric.Rect#initialize} for constructor definition
14416   */
14417  fabric.Rect = fabric.util.createClass(fabric.Object, /** @lends fabric.Rect.prototype */ {
14418
14419    /**
14420     * List of properties to consider when checking if state of an object is changed ({@link fabric.Object#hasStateChanged})
14421     * as well as for history (undo/redo) purposes
14422     * @type Array
14423     */
14424    stateProperties: stateProperties,
14425
14426    /**
14427     * Type of an object
14428     * @type String
14429     * @default
14430     */
14431    type: 'rect',
14432
14433    /**
14434     * Horizontal border radius
14435     * @type Number
14436     * @default
14437     */
14438    rx:   0,
14439
14440    /**
14441     * Vertical border radius
14442     * @type Number
14443     * @default
14444     */
14445    ry:   0,
14446
14447    /**
14448     * Used to specify dash pattern for stroke on this object
14449     * @type Array
14450     */
14451    strokeDashArray: null,
14452
14453    /**
14454     * Constructor
14455     * @param {Object} [options] Options object
14456     * @return {Object} thisArg
14457     */
14458    initialize: function(options) {
14459      options = options || { };
14460
14461      this.callSuper('initialize', options);
14462      this._initRxRy();
14463
14464    },
14465
14466    /**
14467     * Initializes rx/ry attributes
14468     * @private
14469     */
14470    _initRxRy: function() {
14471      if (this.rx && !this.ry) {
14472        this.ry = this.rx;
14473      }
14474      else if (this.ry && !this.rx) {
14475        this.rx = this.ry;
14476      }
14477    },
14478
14479    /**
14480     * @private
14481     * @param {CanvasRenderingContext2D} ctx Context to render on
14482     */
14483    _render: function(ctx, noTransform) {
14484
14485      // optimize 1x1 case (used in spray brush)
14486      if (this.width === 1 && this.height === 1) {
14487        ctx.fillRect(0, 0, 1, 1);
14488        return;
14489      }
14490
14491      var rx = this.rx ? Math.min(this.rx, this.width / 2) : 0,
14492          ry = this.ry ? Math.min(this.ry, this.height / 2) : 0,
14493          w = this.width,
14494          h = this.height,
14495          x = noTransform ? this.left : -this.width / 2,
14496          y = noTransform ? this.top : -this.height / 2,
14497          isRounded = rx !== 0 || ry !== 0,
14498          k = 1 - 0.5522847498 /* "magic number" for bezier approximations of arcs (http://itc.ktu.lt/itc354/Riskus354.pdf) */;
14499
14500      ctx.beginPath();
14501
14502      ctx.moveTo(x + rx, y);
14503
14504      ctx.lineTo(x + w - rx, y);
14505      isRounded && ctx.bezierCurveTo(x + w - k * rx, y, x + w, y + k * ry, x + w, y + ry);
14506
14507      ctx.lineTo(x + w, y + h - ry);
14508      isRounded && ctx.bezierCurveTo(x + w, y + h - k * ry, x + w - k * rx, y + h, x + w - rx, y + h);
14509
14510      ctx.lineTo(x + rx, y + h);
14511      isRounded && ctx.bezierCurveTo(x + k * rx, y + h, x, y + h - k * ry, x, y + h - ry);
14512
14513      ctx.lineTo(x, y + ry);
14514      isRounded && ctx.bezierCurveTo(x, y + k * ry, x + k * rx, y, x + rx, y);
14515
14516      ctx.closePath();
14517
14518      this._renderFill(ctx);
14519      this._renderStroke(ctx);
14520    },
14521
14522    /**
14523     * @private
14524     * @param {CanvasRenderingContext2D} ctx Context to render on
14525     */
14526    _renderDashedStroke: function(ctx) {
14527      var x = -this.width / 2,
14528          y = -this.height / 2,
14529          w = this.width,
14530          h = this.height;
14531
14532      ctx.beginPath();
14533      fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
14534      fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
14535      fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
14536      fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
14537      ctx.closePath();
14538    },
14539
14540    /**
14541     * Returns object representation of an instance
14542     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
14543     * @return {Object} object representation of an instance
14544     */
14545    toObject: function(propertiesToInclude) {
14546      var object = extend(this.callSuper('toObject', propertiesToInclude), {
14547        rx: this.get('rx') || 0,
14548        ry: this.get('ry') || 0
14549      });
14550      if (!this.includeDefaultValues) {
14551        this._removeDefaultValues(object);
14552      }
14553      return object;
14554    },
14555
14556    /* _TO_SVG_START_ */
14557    /**
14558     * Returns svg representation of an instance
14559     * @param {Function} [reviver] Method for further parsing of svg representation.
14560     * @return {String} svg representation of an instance
14561     */
14562    toSVG: function(reviver) {
14563      var markup = this._createBaseSVGMarkup(), x = this.left, y = this.top;
14564      if (!(this.group && this.group.type === 'path-group')) {
14565        x = -this.width / 2;
14566        y = -this.height / 2;
14567      }
14568      markup.push(
14569        '<rect ',
14570          'x="', x, '" y="', y,
14571          '" rx="', this.get('rx'), '" ry="', this.get('ry'),
14572          '" width="', this.width, '" height="', this.height,
14573          '" style="', this.getSvgStyles(),
14574          '" transform="', this.getSvgTransform(),
14575          this.getSvgTransformMatrix(),
14576        '"/>\n');
14577
14578      return reviver ? reviver(markup.join('')) : markup.join('');
14579    },
14580    /* _TO_SVG_END_ */
14581
14582    /**
14583     * Returns complexity of an instance
14584     * @return {Number} complexity
14585     */
14586    complexity: function() {
14587      return 1;
14588    }
14589  });
14590
14591  /* _FROM_SVG_START_ */
14592  /**
14593   * List of attribute names to account for when parsing SVG element (used by `fabric.Rect.fromElement`)
14594   * @static
14595   * @memberOf fabric.Rect
14596   * @see: http://www.w3.org/TR/SVG/shapes.html#RectElement
14597   */
14598  fabric.Rect.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x y rx ry width height'.split(' '));
14599
14600  /**
14601   * Returns {@link fabric.Rect} instance from an SVG element
14602   * @static
14603   * @memberOf fabric.Rect
14604   * @param {SVGElement} element Element to parse
14605   * @param {Object} [options] Options object
14606   * @return {fabric.Rect} Instance of fabric.Rect
14607   */
14608  fabric.Rect.fromElement = function(element, options) {
14609    if (!element) {
14610      return null;
14611    }
14612    options = options || { };
14613
14614    var parsedAttributes = fabric.parseAttributes(element, fabric.Rect.ATTRIBUTE_NAMES);
14615
14616    parsedAttributes.left = parsedAttributes.left || 0;
14617    parsedAttributes.top  = parsedAttributes.top  || 0;
14618    var rect = new fabric.Rect(extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
14619    rect.visible = rect.width > 0 && rect.height > 0;
14620    return rect;
14621  };
14622  /* _FROM_SVG_END_ */
14623
14624  /**
14625   * Returns {@link fabric.Rect} instance from an object representation
14626   * @static
14627   * @memberOf fabric.Rect
14628   * @param {Object} object Object to create an instance from
14629   * @return {Object} instance of fabric.Rect
14630   */
14631  fabric.Rect.fromObject = function(object) {
14632    return new fabric.Rect(object);
14633  };
14634
14635})(typeof exports !== 'undefined' ? exports : this);
14636
14637
14638(function(global) {
14639
14640  'use strict';
14641
14642  var fabric = global.fabric || (global.fabric = { });
14643
14644  if (fabric.Polyline) {
14645    fabric.warn('fabric.Polyline is already defined');
14646    return;
14647  }
14648
14649  /**
14650   * Polyline class
14651   * @class fabric.Polyline
14652   * @extends fabric.Object
14653   * @see {@link fabric.Polyline#initialize} for constructor definition
14654   */
14655  fabric.Polyline = fabric.util.createClass(fabric.Object, /** @lends fabric.Polyline.prototype */ {
14656
14657    /**
14658     * Type of an object
14659     * @type String
14660     * @default
14661     */
14662    type: 'polyline',
14663
14664    /**
14665     * Points array
14666     * @type Array
14667     * @default
14668     */
14669    points: null,
14670
14671    /**
14672     * Minimum X from points values, necessary to offset points
14673     * @type Number
14674     * @default
14675     */
14676    minX: 0,
14677
14678    /**
14679     * Minimum Y from points values, necessary to offset points
14680     * @type Number
14681     * @default
14682     */
14683    minY: 0,
14684
14685    /**
14686     * Constructor
14687     * @param {Array} points Array of points (where each point is an object with x and y)
14688     * @param {Object} [options] Options object
14689     * @param {Boolean} [skipOffset] Whether points offsetting should be skipped
14690     * @return {fabric.Polyline} thisArg
14691     * @example
14692     * var poly = new fabric.Polyline([
14693     *     { x: 10, y: 10 },
14694     *     { x: 50, y: 30 },
14695     *     { x: 40, y: 70 },
14696     *     { x: 60, y: 50 },
14697     *     { x: 100, y: 150 },
14698     *     { x: 40, y: 100 }
14699     *   ], {
14700     *   stroke: 'red',
14701     *   left: 100,
14702     *   top: 100
14703     * });
14704     */
14705    initialize: function(points, options) {
14706      return fabric.Polygon.prototype.initialize.call(this, points, options);
14707    },
14708
14709    /**
14710     * @private
14711     */
14712    _calcDimensions: function() {
14713      return fabric.Polygon.prototype._calcDimensions.call(this);
14714    },
14715
14716    /**
14717     * @private
14718     */
14719    _applyPointOffset: function() {
14720      return fabric.Polygon.prototype._applyPointOffset.call(this);
14721    },
14722
14723    /**
14724     * Returns object representation of an instance
14725     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
14726     * @return {Object} Object representation of an instance
14727     */
14728    toObject: function(propertiesToInclude) {
14729      return fabric.Polygon.prototype.toObject.call(this, propertiesToInclude);
14730    },
14731
14732    /* _TO_SVG_START_ */
14733    /**
14734     * Returns SVG representation of an instance
14735     * @param {Function} [reviver] Method for further parsing of svg representation.
14736     * @return {String} svg representation of an instance
14737     */
14738    toSVG: function(reviver) {
14739      return fabric.Polygon.prototype.toSVG.call(this, reviver);
14740    },
14741    /* _TO_SVG_END_ */
14742
14743    /**
14744     * @private
14745     * @param {CanvasRenderingContext2D} ctx Context to render on
14746     */
14747    _render: function(ctx) {
14748      if (!fabric.Polygon.prototype.commonRender.call(this, ctx)) {
14749        return;
14750      }
14751      this._renderFill(ctx);
14752      this._renderStroke(ctx);
14753    },
14754
14755    /**
14756     * @private
14757     * @param {CanvasRenderingContext2D} ctx Context to render on
14758     */
14759    _renderDashedStroke: function(ctx) {
14760      var p1, p2;
14761
14762      ctx.beginPath();
14763      for (var i = 0, len = this.points.length; i < len; i++) {
14764        p1 = this.points[i];
14765        p2 = this.points[i + 1] || p1;
14766        fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
14767      }
14768    },
14769
14770    /**
14771     * Returns complexity of an instance
14772     * @return {Number} complexity of this instance
14773     */
14774    complexity: function() {
14775      return this.get('points').length;
14776    }
14777  });
14778
14779  /* _FROM_SVG_START_ */
14780  /**
14781   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Polyline.fromElement})
14782   * @static
14783   * @memberOf fabric.Polyline
14784   * @see: http://www.w3.org/TR/SVG/shapes.html#PolylineElement
14785   */
14786  fabric.Polyline.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat();
14787
14788  /**
14789   * Returns fabric.Polyline instance from an SVG element
14790   * @static
14791   * @memberOf fabric.Polyline
14792   * @param {SVGElement} element Element to parse
14793   * @param {Object} [options] Options object
14794   * @return {fabric.Polyline} Instance of fabric.Polyline
14795   */
14796  fabric.Polyline.fromElement = function(element, options) {
14797    if (!element) {
14798      return null;
14799    }
14800    options || (options = { });
14801
14802    var points = fabric.parsePointsAttribute(element.getAttribute('points')),
14803        parsedAttributes = fabric.parseAttributes(element, fabric.Polyline.ATTRIBUTE_NAMES);
14804
14805    return new fabric.Polyline(points, fabric.util.object.extend(parsedAttributes, options));
14806  };
14807  /* _FROM_SVG_END_ */
14808
14809  /**
14810   * Returns fabric.Polyline instance from an object representation
14811   * @static
14812   * @memberOf fabric.Polyline
14813   * @param {Object} object Object to create an instance from
14814   * @return {fabric.Polyline} Instance of fabric.Polyline
14815   */
14816  fabric.Polyline.fromObject = function(object) {
14817    var points = object.points;
14818    return new fabric.Polyline(points, object, true);
14819  };
14820
14821})(typeof exports !== 'undefined' ? exports : this);
14822
14823
14824(function(global) {
14825
14826  'use strict';
14827
14828  var fabric = global.fabric || (global.fabric = { }),
14829      extend = fabric.util.object.extend,
14830      min = fabric.util.array.min,
14831      max = fabric.util.array.max,
14832      toFixed = fabric.util.toFixed;
14833
14834  if (fabric.Polygon) {
14835    fabric.warn('fabric.Polygon is already defined');
14836    return;
14837  }
14838
14839  /**
14840   * Polygon class
14841   * @class fabric.Polygon
14842   * @extends fabric.Object
14843   * @see {@link fabric.Polygon#initialize} for constructor definition
14844   */
14845  fabric.Polygon = fabric.util.createClass(fabric.Object, /** @lends fabric.Polygon.prototype */ {
14846
14847    /**
14848     * Type of an object
14849     * @type String
14850     * @default
14851     */
14852    type: 'polygon',
14853
14854    /**
14855     * Points array
14856     * @type Array
14857     * @default
14858     */
14859    points: null,
14860
14861    /**
14862     * Minimum X from points values, necessary to offset points
14863     * @type Number
14864     * @default
14865     */
14866    minX: 0,
14867
14868    /**
14869     * Minimum Y from points values, necessary to offset points
14870     * @type Number
14871     * @default
14872     */
14873    minY: 0,
14874
14875    /**
14876     * Constructor
14877     * @param {Array} points Array of points
14878     * @param {Object} [options] Options object
14879     * @return {fabric.Polygon} thisArg
14880     */
14881    initialize: function(points, options) {
14882      options = options || { };
14883      this.points = points || [ ];
14884      this.callSuper('initialize', options);
14885      this._calcDimensions();
14886      if (!('top' in options)) {
14887        this.top = this.minY;
14888      }
14889      if (!('left' in options)) {
14890        this.left = this.minX;
14891      }
14892    },
14893
14894    /**
14895     * @private
14896     */
14897    _calcDimensions: function() {
14898
14899      var points = this.points,
14900          minX = min(points, 'x'),
14901          minY = min(points, 'y'),
14902          maxX = max(points, 'x'),
14903          maxY = max(points, 'y');
14904
14905      this.width = (maxX - minX) || 0;
14906      this.height = (maxY - minY) || 0;
14907
14908      this.minX = minX || 0,
14909      this.minY = minY || 0;
14910    },
14911
14912    /**
14913     * @private
14914     */
14915    _applyPointOffset: function() {
14916      // change points to offset polygon into a bounding box
14917      // executed one time
14918      this.points.forEach(function(p) {
14919        p.x -= (this.minX + this.width / 2);
14920        p.y -= (this.minY + this.height / 2);
14921      }, this);
14922    },
14923
14924    /**
14925     * Returns object representation of an instance
14926     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
14927     * @return {Object} Object representation of an instance
14928     */
14929    toObject: function(propertiesToInclude) {
14930      return extend(this.callSuper('toObject', propertiesToInclude), {
14931        points: this.points.concat()
14932      });
14933    },
14934
14935    /* _TO_SVG_START_ */
14936    /**
14937     * Returns svg representation of an instance
14938     * @param {Function} [reviver] Method for further parsing of svg representation.
14939     * @return {String} svg representation of an instance
14940     */
14941    toSVG: function(reviver) {
14942      var points = [],
14943          markup = this._createBaseSVGMarkup();
14944
14945      for (var i = 0, len = this.points.length; i < len; i++) {
14946        points.push(toFixed(this.points[i].x, 2), ',', toFixed(this.points[i].y, 2), ' ');
14947      }
14948
14949      markup.push(
14950        '<', this.type, ' ',
14951          'points="', points.join(''),
14952          '" style="', this.getSvgStyles(),
14953          '" transform="', this.getSvgTransform(),
14954          ' ', this.getSvgTransformMatrix(),
14955        '"/>\n'
14956      );
14957
14958      return reviver ? reviver(markup.join('')) : markup.join('');
14959    },
14960    /* _TO_SVG_END_ */
14961
14962    /**
14963     * @private
14964     * @param {CanvasRenderingContext2D} ctx Context to render on
14965     */
14966    _render: function(ctx) {
14967      if (!this.commonRender(ctx)) {
14968        return;
14969      }
14970      this._renderFill(ctx);
14971      if (this.stroke || this.strokeDashArray) {
14972        ctx.closePath();
14973        this._renderStroke(ctx);
14974      }
14975    },
14976
14977    /**
14978     * @private
14979     * @param {CanvasRenderingContext2D} ctx Context to render on
14980     */
14981    commonRender: function(ctx) {
14982      var point, len = this.points.length;
14983
14984      if (!len || isNaN(this.points[len - 1].y)) {
14985        // do not draw if no points or odd points
14986        // NaN comes from parseFloat of a empty string in parser
14987        return false;
14988      }
14989
14990      ctx.beginPath();
14991
14992      if (this._applyPointOffset) {
14993        if (!(this.group && this.group.type === 'path-group')) {
14994          this._applyPointOffset();
14995        }
14996        this._applyPointOffset = null;
14997      }
14998
14999      ctx.moveTo(this.points[0].x, this.points[0].y);
15000      for (var i = 0; i < len; i++) {
15001        point = this.points[i];
15002        ctx.lineTo(point.x, point.y);
15003      }
15004      return true;
15005    },
15006
15007    /**
15008     * @private
15009     * @param {CanvasRenderingContext2D} ctx Context to render on
15010     */
15011    _renderDashedStroke: function(ctx) {
15012      fabric.Polyline.prototype._renderDashedStroke.call(this, ctx);
15013      ctx.closePath();
15014    },
15015
15016    /**
15017     * Returns complexity of an instance
15018     * @return {Number} complexity of this instance
15019     */
15020    complexity: function() {
15021      return this.points.length;
15022    }
15023  });
15024
15025  /* _FROM_SVG_START_ */
15026  /**
15027   * List of attribute names to account for when parsing SVG element (used by `fabric.Polygon.fromElement`)
15028   * @static
15029   * @memberOf fabric.Polygon
15030   * @see: http://www.w3.org/TR/SVG/shapes.html#PolygonElement
15031   */
15032  fabric.Polygon.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat();
15033
15034  /**
15035   * Returns {@link fabric.Polygon} instance from an SVG element
15036   * @static
15037   * @memberOf fabric.Polygon
15038   * @param {SVGElement} element Element to parse
15039   * @param {Object} [options] Options object
15040   * @return {fabric.Polygon} Instance of fabric.Polygon
15041   */
15042  fabric.Polygon.fromElement = function(element, options) {
15043    if (!element) {
15044      return null;
15045    }
15046
15047    options || (options = { });
15048
15049    var points = fabric.parsePointsAttribute(element.getAttribute('points')),
15050        parsedAttributes = fabric.parseAttributes(element, fabric.Polygon.ATTRIBUTE_NAMES);
15051
15052    return new fabric.Polygon(points, extend(parsedAttributes, options));
15053  };
15054  /* _FROM_SVG_END_ */
15055
15056  /**
15057   * Returns fabric.Polygon instance from an object representation
15058   * @static
15059   * @memberOf fabric.Polygon
15060   * @param {Object} object Object to create an instance from
15061   * @return {fabric.Polygon} Instance of fabric.Polygon
15062   */
15063  fabric.Polygon.fromObject = function(object) {
15064    return new fabric.Polygon(object.points, object, true);
15065  };
15066
15067})(typeof exports !== 'undefined' ? exports : this);
15068
15069
15070(function(global) {
15071
15072  'use strict';
15073
15074  var fabric = global.fabric || (global.fabric = { }),
15075      min = fabric.util.array.min,
15076      max = fabric.util.array.max,
15077      extend = fabric.util.object.extend,
15078      _toString = Object.prototype.toString,
15079      drawArc = fabric.util.drawArc,
15080      commandLengths = {
15081        m: 2,
15082        l: 2,
15083        h: 1,
15084        v: 1,
15085        c: 6,
15086        s: 4,
15087        q: 4,
15088        t: 2,
15089        a: 7
15090      },
15091      repeatedCommands = {
15092        m: 'l',
15093        M: 'L'
15094      };
15095
15096  if (fabric.Path) {
15097    fabric.warn('fabric.Path is already defined');
15098    return;
15099  }
15100
15101  /**
15102   * Path class
15103   * @class fabric.Path
15104   * @extends fabric.Object
15105   * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#path_and_pathgroup}
15106   * @see {@link fabric.Path#initialize} for constructor definition
15107   */
15108  fabric.Path = fabric.util.createClass(fabric.Object, /** @lends fabric.Path.prototype */ {
15109
15110    /**
15111     * Type of an object
15112     * @type String
15113     * @default
15114     */
15115    type: 'path',
15116
15117    /**
15118     * Array of path points
15119     * @type Array
15120     * @default
15121     */
15122    path: null,
15123
15124    /**
15125     * Minimum X from points values, necessary to offset points
15126     * @type Number
15127     * @default
15128     */
15129    minX: 0,
15130
15131    /**
15132     * Minimum Y from points values, necessary to offset points
15133     * @type Number
15134     * @default
15135     */
15136    minY: 0,
15137
15138    /**
15139     * Constructor
15140     * @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
15141     * @param {Object} [options] Options object
15142     * @return {fabric.Path} thisArg
15143     */
15144    initialize: function(path, options) {
15145      options = options || { };
15146
15147      this.setOptions(options);
15148
15149      if (!path) {
15150        throw new Error('`path` argument is required');
15151      }
15152
15153      var fromArray = _toString.call(path) === '[object Array]';
15154
15155      this.path = fromArray
15156        ? path
15157        // one of commands (m,M,l,L,q,Q,c,C,etc.) followed by non-command characters (i.e. command values)
15158        : path.match && path.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi);
15159
15160      if (!this.path) {
15161        return;
15162      }
15163
15164      if (!fromArray) {
15165        this.path = this._parsePath();
15166      }
15167
15168      this._setPositionDimensions();
15169
15170      if (options.sourcePath) {
15171        this.setSourcePath(options.sourcePath);
15172      }
15173    },
15174
15175    /**
15176     * @private
15177     */
15178    _setPositionDimensions: function() {
15179      var calcDim = this._parseDimensions();
15180
15181      this.minX = calcDim.left;
15182      this.minY = calcDim.top;
15183      this.width = calcDim.width;
15184      this.height = calcDim.height;
15185
15186      calcDim.left += this.originX === 'center'
15187        ? this.width / 2
15188        : this.originX === 'right'
15189          ? this.width
15190          : 0;
15191
15192      calcDim.top += this.originY === 'center'
15193        ? this.height / 2
15194        : this.originY === 'bottom'
15195          ? this.height
15196          : 0;
15197
15198      this.top = this.top || calcDim.top;
15199      this.left = this.left || calcDim.left;
15200
15201      this.pathOffset = this.pathOffset || {
15202        x: this.minX + this.width / 2,
15203        y: this.minY + this.height / 2
15204      };
15205    },
15206
15207    /**
15208     * @private
15209     * @param {CanvasRenderingContext2D} ctx context to render path on
15210     */
15211    _render: function(ctx) {
15212      var current, // current instruction
15213          previous = null,
15214          subpathStartX = 0,
15215          subpathStartY = 0,
15216          x = 0, // current x
15217          y = 0, // current y
15218          controlX = 0, // current control point x
15219          controlY = 0, // current control point y
15220          tempX,
15221          tempY,
15222          l = -this.pathOffset.x,
15223          t = -this.pathOffset.y;
15224
15225      if (this.group && this.group.type === 'path-group') {
15226        l = 0;
15227        t = 0;
15228      }
15229
15230      ctx.beginPath();
15231
15232      for (var i = 0, len = this.path.length; i < len; ++i) {
15233
15234        current = this.path[i];
15235
15236        switch (current[0]) { // first letter
15237
15238          case 'l': // lineto, relative
15239            x += current[1];
15240            y += current[2];
15241            ctx.lineTo(x + l, y + t);
15242            break;
15243
15244          case 'L': // lineto, absolute
15245            x = current[1];
15246            y = current[2];
15247            ctx.lineTo(x + l, y + t);
15248            break;
15249
15250          case 'h': // horizontal lineto, relative
15251            x += current[1];
15252            ctx.lineTo(x + l, y + t);
15253            break;
15254
15255          case 'H': // horizontal lineto, absolute
15256            x = current[1];
15257            ctx.lineTo(x + l, y + t);
15258            break;
15259
15260          case 'v': // vertical lineto, relative
15261            y += current[1];
15262            ctx.lineTo(x + l, y + t);
15263            break;
15264
15265          case 'V': // verical lineto, absolute
15266            y = current[1];
15267            ctx.lineTo(x + l, y + t);
15268            break;
15269
15270          case 'm': // moveTo, relative
15271            x += current[1];
15272            y += current[2];
15273            subpathStartX = x;
15274            subpathStartY = y;
15275            ctx.moveTo(x + l, y + t);
15276            break;
15277
15278          case 'M': // moveTo, absolute
15279            x = current[1];
15280            y = current[2];
15281            subpathStartX = x;
15282            subpathStartY = y;
15283            ctx.moveTo(x + l, y + t);
15284            break;
15285
15286          case 'c': // bezierCurveTo, relative
15287            tempX = x + current[5];
15288            tempY = y + current[6];
15289            controlX = x + current[3];
15290            controlY = y + current[4];
15291            ctx.bezierCurveTo(
15292              x + current[1] + l, // x1
15293              y + current[2] + t, // y1
15294              controlX + l, // x2
15295              controlY + t, // y2
15296              tempX + l,
15297              tempY + t
15298            );
15299            x = tempX;
15300            y = tempY;
15301            break;
15302
15303          case 'C': // bezierCurveTo, absolute
15304            x = current[5];
15305            y = current[6];
15306            controlX = current[3];
15307            controlY = current[4];
15308            ctx.bezierCurveTo(
15309              current[1] + l,
15310              current[2] + t,
15311              controlX + l,
15312              controlY + t,
15313              x + l,
15314              y + t
15315            );
15316            break;
15317
15318          case 's': // shorthand cubic bezierCurveTo, relative
15319
15320            // transform to absolute x,y
15321            tempX = x + current[3];
15322            tempY = y + current[4];
15323
15324            if (previous[0].match(/[CcSs]/) === null) {
15325              // If there is no previous command or if the previous command was not a C, c, S, or s,
15326              // the control point is coincident with the current point
15327              controlX = x;
15328              controlY = y;
15329            }
15330            else {
15331              // calculate reflection of previous control points
15332              controlX = 2 * x - controlX;
15333              controlY = 2 * y - controlY;
15334            }
15335
15336            ctx.bezierCurveTo(
15337              controlX + l,
15338              controlY + t,
15339              x + current[1] + l,
15340              y + current[2] + t,
15341              tempX + l,
15342              tempY + t
15343            );
15344            // set control point to 2nd one of this command
15345            // "... the first control point is assumed to be
15346            // the reflection of the second control point on
15347            // the previous command relative to the current point."
15348            controlX = x + current[1];
15349            controlY = y + current[2];
15350
15351            x = tempX;
15352            y = tempY;
15353            break;
15354
15355          case 'S': // shorthand cubic bezierCurveTo, absolute
15356            tempX = current[3];
15357            tempY = current[4];
15358            if (previous[0].match(/[CcSs]/) === null) {
15359              // If there is no previous command or if the previous command was not a C, c, S, or s,
15360              // the control point is coincident with the current point
15361              controlX = x;
15362              controlY = y;
15363            }
15364            else {
15365              // calculate reflection of previous control points
15366              controlX = 2 * x - controlX;
15367              controlY = 2 * y - controlY;
15368            }
15369            ctx.bezierCurveTo(
15370              controlX + l,
15371              controlY + t,
15372              current[1] + l,
15373              current[2] + t,
15374              tempX + l,
15375              tempY + t
15376            );
15377            x = tempX;
15378            y = tempY;
15379
15380            // set control point to 2nd one of this command
15381            // "... the first control point is assumed to be
15382            // the reflection of the second control point on
15383            // the previous command relative to the current point."
15384            controlX = current[1];
15385            controlY = current[2];
15386
15387            break;
15388
15389          case 'q': // quadraticCurveTo, relative
15390            // transform to absolute x,y
15391            tempX = x + current[3];
15392            tempY = y + current[4];
15393
15394            controlX = x + current[1];
15395            controlY = y + current[2];
15396
15397            ctx.quadraticCurveTo(
15398              controlX + l,
15399              controlY + t,
15400              tempX + l,
15401              tempY + t
15402            );
15403            x = tempX;
15404            y = tempY;
15405            break;
15406
15407          case 'Q': // quadraticCurveTo, absolute
15408            tempX = current[3];
15409            tempY = current[4];
15410
15411            ctx.quadraticCurveTo(
15412              current[1] + l,
15413              current[2] + t,
15414              tempX + l,
15415              tempY + t
15416            );
15417            x = tempX;
15418            y = tempY;
15419            controlX = current[1];
15420            controlY = current[2];
15421            break;
15422
15423          case 't': // shorthand quadraticCurveTo, relative
15424
15425            // transform to absolute x,y
15426            tempX = x + current[1];
15427            tempY = y + current[2];
15428
15429            if (previous[0].match(/[QqTt]/) === null) {
15430              // If there is no previous command or if the previous command was not a Q, q, T or t,
15431              // assume the control point is coincident with the current point
15432              controlX = x;
15433              controlY = y;
15434            }
15435            else {
15436              // calculate reflection of previous control point
15437              controlX = 2 * x - controlX;
15438              controlY = 2 * y - controlY;
15439            }
15440
15441            ctx.quadraticCurveTo(
15442              controlX + l,
15443              controlY + t,
15444              tempX + l,
15445              tempY + t
15446            );
15447            x = tempX;
15448            y = tempY;
15449
15450            break;
15451
15452          case 'T':
15453            tempX = current[1];
15454            tempY = current[2];
15455
15456            if (previous[0].match(/[QqTt]/) === null) {
15457              // If there is no previous command or if the previous command was not a Q, q, T or t,
15458              // assume the control point is coincident with the current point
15459              controlX = x;
15460              controlY = y;
15461            }
15462            else {
15463              // calculate reflection of previous control point
15464              controlX = 2 * x - controlX;
15465              controlY = 2 * y - controlY;
15466            }
15467            ctx.quadraticCurveTo(
15468              controlX + l,
15469              controlY + t,
15470              tempX + l,
15471              tempY + t
15472            );
15473            x = tempX;
15474            y = tempY;
15475            break;
15476
15477          case 'a':
15478            // TODO: optimize this
15479            drawArc(ctx, x + l, y + t, [
15480              current[1],
15481              current[2],
15482              current[3],
15483              current[4],
15484              current[5],
15485              current[6] + x + l,
15486              current[7] + y + t
15487            ]);
15488            x += current[6];
15489            y += current[7];
15490            break;
15491
15492          case 'A':
15493            // TODO: optimize this
15494            drawArc(ctx, x + l, y + t, [
15495              current[1],
15496              current[2],
15497              current[3],
15498              current[4],
15499              current[5],
15500              current[6] + l,
15501              current[7] + t
15502            ]);
15503            x = current[6];
15504            y = current[7];
15505            break;
15506
15507          case 'z':
15508          case 'Z':
15509            x = subpathStartX;
15510            y = subpathStartY;
15511            ctx.closePath();
15512            break;
15513        }
15514        previous = current;
15515      }
15516      this._renderFill(ctx);
15517      this._renderStroke(ctx);
15518    },
15519
15520    /**
15521     * Returns string representation of an instance
15522     * @return {String} string representation of an instance
15523     */
15524    toString: function() {
15525      return '#<fabric.Path (' + this.complexity() +
15526        '): { "top": ' + this.top + ', "left": ' + this.left + ' }>';
15527    },
15528
15529    /**
15530     * Returns object representation of an instance
15531     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
15532     * @return {Object} object representation of an instance
15533     */
15534    toObject: function(propertiesToInclude) {
15535      var o = extend(this.callSuper('toObject', propertiesToInclude), {
15536        path: this.path.map(function(item) { return item.slice() }),
15537        pathOffset: this.pathOffset
15538      });
15539      if (this.sourcePath) {
15540        o.sourcePath = this.sourcePath;
15541      }
15542      if (this.transformMatrix) {
15543        o.transformMatrix = this.transformMatrix;
15544      }
15545      return o;
15546    },
15547
15548    /**
15549     * Returns dataless object representation of an instance
15550     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
15551     * @return {Object} object representation of an instance
15552     */
15553    toDatalessObject: function(propertiesToInclude) {
15554      var o = this.toObject(propertiesToInclude);
15555      if (this.sourcePath) {
15556        o.path = this.sourcePath;
15557      }
15558      delete o.sourcePath;
15559      return o;
15560    },
15561
15562    /* _TO_SVG_START_ */
15563    /**
15564     * Returns svg representation of an instance
15565     * @param {Function} [reviver] Method for further parsing of svg representation.
15566     * @return {String} svg representation of an instance
15567     */
15568    toSVG: function(reviver) {
15569      var chunks = [],
15570          markup = this._createBaseSVGMarkup(), addTransform = '';
15571
15572      for (var i = 0, len = this.path.length; i < len; i++) {
15573        chunks.push(this.path[i].join(' '));
15574      }
15575      var path = chunks.join(' ');
15576      if (!(this.group && this.group.type === 'path-group')) {
15577        addTransform = ' translate(' + (-this.pathOffset.x) + ', ' + (-this.pathOffset.y) + ') ';
15578      }
15579      markup.push(
15580        //jscs:disable validateIndentation
15581        '<path ',
15582          'd="', path,
15583          '" style="', this.getSvgStyles(),
15584          '" transform="', this.getSvgTransform(), addTransform,
15585          this.getSvgTransformMatrix(), '" stroke-linecap="round" ',
15586        '/>\n'
15587        //jscs:enable validateIndentation
15588      );
15589
15590      return reviver ? reviver(markup.join('')) : markup.join('');
15591    },
15592    /* _TO_SVG_END_ */
15593
15594    /**
15595     * Returns number representation of an instance complexity
15596     * @return {Number} complexity of this instance
15597     */
15598    complexity: function() {
15599      return this.path.length;
15600    },
15601
15602    /**
15603     * @private
15604     */
15605    _parsePath: function() {
15606      var result = [ ],
15607          coords = [ ],
15608          currentPath,
15609          parsed,
15610          re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig,
15611          match,
15612          coordsStr;
15613
15614      for (var i = 0, coordsParsed, len = this.path.length; i < len; i++) {
15615        currentPath = this.path[i];
15616
15617        coordsStr = currentPath.slice(1).trim();
15618        coords.length = 0;
15619
15620        while ((match = re.exec(coordsStr))) {
15621          coords.push(match[0]);
15622        }
15623
15624        coordsParsed = [ currentPath.charAt(0) ];
15625
15626        for (var j = 0, jlen = coords.length; j < jlen; j++) {
15627          parsed = parseFloat(coords[j]);
15628          if (!isNaN(parsed)) {
15629            coordsParsed.push(parsed);
15630          }
15631        }
15632
15633        var command = coordsParsed[0],
15634            commandLength = commandLengths[command.toLowerCase()],
15635            repeatedCommand = repeatedCommands[command] || command;
15636
15637        if (coordsParsed.length - 1 > commandLength) {
15638          for (var k = 1, klen = coordsParsed.length; k < klen; k += commandLength) {
15639            result.push([ command ].concat(coordsParsed.slice(k, k + commandLength)));
15640            command = repeatedCommand;
15641          }
15642        }
15643        else {
15644          result.push(coordsParsed);
15645        }
15646      }
15647
15648      return result;
15649    },
15650
15651    /**
15652     * @private
15653     */
15654    _parseDimensions: function() {
15655
15656      var aX = [],
15657          aY = [],
15658          current, // current instruction
15659          previous = null,
15660          subpathStartX = 0,
15661          subpathStartY = 0,
15662          x = 0, // current x
15663          y = 0, // current y
15664          controlX = 0, // current control point x
15665          controlY = 0, // current control point y
15666          tempX,
15667          tempY,
15668          bounds;
15669
15670      for (var i = 0, len = this.path.length; i < len; ++i) {
15671
15672        current = this.path[i];
15673
15674        switch (current[0]) { // first letter
15675
15676          case 'l': // lineto, relative
15677            x += current[1];
15678            y += current[2];
15679            bounds = [ ];
15680            break;
15681
15682          case 'L': // lineto, absolute
15683            x = current[1];
15684            y = current[2];
15685            bounds = [ ];
15686            break;
15687
15688          case 'h': // horizontal lineto, relative
15689            x += current[1];
15690            bounds = [ ];
15691            break;
15692
15693          case 'H': // horizontal lineto, absolute
15694            x = current[1];
15695            bounds = [ ];
15696            break;
15697
15698          case 'v': // vertical lineto, relative
15699            y += current[1];
15700            bounds = [ ];
15701            break;
15702
15703          case 'V': // verical lineto, absolute
15704            y = current[1];
15705            bounds = [ ];
15706            break;
15707
15708          case 'm': // moveTo, relative
15709            x += current[1];
15710            y += current[2];
15711            subpathStartX = x;
15712            subpathStartY = y;
15713            bounds = [ ];
15714            break;
15715
15716          case 'M': // moveTo, absolute
15717            x = current[1];
15718            y = current[2];
15719            subpathStartX = x;
15720            subpathStartY = y;
15721            bounds = [ ];
15722            break;
15723
15724          case 'c': // bezierCurveTo, relative
15725            tempX = x + current[5];
15726            tempY = y + current[6];
15727            controlX = x + current[3];
15728            controlY = y + current[4];
15729            bounds = fabric.util.getBoundsOfCurve(x, y,
15730              x + current[1], // x1
15731              y + current[2], // y1
15732              controlX, // x2
15733              controlY, // y2
15734              tempX,
15735              tempY
15736            );
15737            x = tempX;
15738            y = tempY;
15739            break;
15740
15741          case 'C': // bezierCurveTo, absolute
15742            x = current[5];
15743            y = current[6];
15744            controlX = current[3];
15745            controlY = current[4];
15746            bounds = fabric.util.getBoundsOfCurve(x, y,
15747              current[1],
15748              current[2],
15749              controlX,
15750              controlY,
15751              x,
15752              y
15753            );
15754            break;
15755
15756          case 's': // shorthand cubic bezierCurveTo, relative
15757
15758            // transform to absolute x,y
15759            tempX = x + current[3];
15760            tempY = y + current[4];
15761
15762            if (previous[0].match(/[CcSs]/) === null) {
15763              // If there is no previous command or if the previous command was not a C, c, S, or s,
15764              // the control point is coincident with the current point
15765              controlX = x;
15766              controlY = y;
15767            }
15768            else {
15769              // calculate reflection of previous control points
15770              controlX = 2 * x - controlX;
15771              controlY = 2 * y - controlY;
15772            }
15773
15774            bounds = fabric.util.getBoundsOfCurve(x, y,
15775              controlX,
15776              controlY,
15777              x + current[1],
15778              y + current[2],
15779              tempX,
15780              tempY
15781            );
15782            // set control point to 2nd one of this command
15783            // "... the first control point is assumed to be
15784            // the reflection of the second control point on
15785            // the previous command relative to the current point."
15786            controlX = x + current[1];
15787            controlY = y + current[2];
15788            x = tempX;
15789            y = tempY;
15790            break;
15791
15792          case 'S': // shorthand cubic bezierCurveTo, absolute
15793            tempX = current[3];
15794            tempY = current[4];
15795            if (previous[0].match(/[CcSs]/) === null) {
15796              // If there is no previous command or if the previous command was not a C, c, S, or s,
15797              // the control point is coincident with the current point
15798              controlX = x;
15799              controlY = y;
15800            }
15801            else {
15802              // calculate reflection of previous control points
15803              controlX = 2 * x - controlX;
15804              controlY = 2 * y - controlY;
15805            }
15806            bounds = fabric.util.getBoundsOfCurve(x, y,
15807              controlX,
15808              controlY,
15809              current[1],
15810              current[2],
15811              tempX,
15812              tempY
15813            );
15814            x = tempX;
15815            y = tempY;
15816            // set control point to 2nd one of this command
15817            // "... the first control point is assumed to be
15818            // the reflection of the second control point on
15819            // the previous command relative to the current point."
15820            controlX = current[1];
15821            controlY = current[2];
15822            break;
15823
15824          case 'q': // quadraticCurveTo, relative
15825            // transform to absolute x,y
15826            tempX = x + current[3];
15827            tempY = y + current[4];
15828            controlX = x + current[1];
15829            controlY = y + current[2];
15830            bounds = fabric.util.getBoundsOfCurve(x, y,
15831              controlX,
15832              controlY,
15833              controlX,
15834              controlY,
15835              tempX,
15836              tempY
15837            );
15838            x = tempX;
15839            y = tempY;
15840            break;
15841
15842          case 'Q': // quadraticCurveTo, absolute
15843            controlX = current[1];
15844            controlY = current[2];
15845            bounds = fabric.util.getBoundsOfCurve(x, y,
15846              controlX,
15847              controlY,
15848              controlX,
15849              controlY,
15850              current[3],
15851              current[4]
15852            );
15853            x = current[3];
15854            y = current[4];
15855            break;
15856
15857          case 't': // shorthand quadraticCurveTo, relative
15858            // transform to absolute x,y
15859            tempX = x + current[1];
15860            tempY = y + current[2];
15861            if (previous[0].match(/[QqTt]/) === null) {
15862              // If there is no previous command or if the previous command was not a Q, q, T or t,
15863              // assume the control point is coincident with the current point
15864              controlX = x;
15865              controlY = y;
15866            }
15867            else {
15868              // calculate reflection of previous control point
15869              controlX = 2 * x - controlX;
15870              controlY = 2 * y - controlY;
15871            }
15872
15873            bounds = fabric.util.getBoundsOfCurve(x, y,
15874              controlX,
15875              controlY,
15876              controlX,
15877              controlY,
15878              tempX,
15879              tempY
15880            );
15881            x = tempX;
15882            y = tempY;
15883
15884            break;
15885
15886          case 'T':
15887            tempX = current[1];
15888            tempY = current[2];
15889
15890            if (previous[0].match(/[QqTt]/) === null) {
15891              // If there is no previous command or if the previous command was not a Q, q, T or t,
15892              // assume the control point is coincident with the current point
15893              controlX = x;
15894              controlY = y;
15895            }
15896            else {
15897              // calculate reflection of previous control point
15898              controlX = 2 * x - controlX;
15899              controlY = 2 * y - controlY;
15900            }
15901            bounds = fabric.util.getBoundsOfCurve(x, y,
15902              controlX,
15903              controlY,
15904              controlX,
15905              controlY,
15906              tempX,
15907              tempY
15908            );
15909            x = tempX;
15910            y = tempY;
15911            break;
15912
15913          case 'a':
15914            // TODO: optimize this
15915            bounds = fabric.util.getBoundsOfArc(x, y,
15916              current[1],
15917              current[2],
15918              current[3],
15919              current[4],
15920              current[5],
15921              current[6] + x,
15922              current[7] + y
15923            );
15924            x += current[6];
15925            y += current[7];
15926            break;
15927
15928          case 'A':
15929            // TODO: optimize this
15930            bounds = fabric.util.getBoundsOfArc(x, y,
15931              current[1],
15932              current[2],
15933              current[3],
15934              current[4],
15935              current[5],
15936              current[6],
15937              current[7]
15938            );
15939            x = current[6];
15940            y = current[7];
15941            break;
15942
15943          case 'z':
15944          case 'Z':
15945            x = subpathStartX;
15946            y = subpathStartY;
15947            break;
15948        }
15949        previous = current;
15950        bounds.forEach(function (point) {
15951          aX.push(point.x);
15952          aY.push(point.y);
15953        });
15954        aX.push(x);
15955        aY.push(y);
15956      }
15957
15958      var minX = min(aX),
15959          minY = min(aY),
15960          maxX = max(aX),
15961          maxY = max(aY),
15962          deltaX = maxX - minX,
15963          deltaY = maxY - minY,
15964
15965          o = {
15966            left: minX,
15967            top: minY,
15968            width: deltaX,
15969            height: deltaY
15970          };
15971
15972      return o;
15973    }
15974  });
15975
15976  /**
15977   * Creates an instance of fabric.Path from an object
15978   * @static
15979   * @memberOf fabric.Path
15980   * @param {Object} object
15981   * @param {Function} callback Callback to invoke when an fabric.Path instance is created
15982   */
15983  fabric.Path.fromObject = function(object, callback) {
15984    if (typeof object.path === 'string') {
15985      fabric.loadSVGFromURL(object.path, function (elements) {
15986        var path = elements[0],
15987            pathUrl = object.path;
15988
15989        delete object.path;
15990
15991        fabric.util.object.extend(path, object);
15992        path.setSourcePath(pathUrl);
15993
15994        callback(path);
15995      });
15996    }
15997    else {
15998      callback(new fabric.Path(object.path, object));
15999    }
16000  };
16001
16002  /* _FROM_SVG_START_ */
16003  /**
16004   * List of attribute names to account for when parsing SVG element (used by `fabric.Path.fromElement`)
16005   * @static
16006   * @memberOf fabric.Path
16007   * @see http://www.w3.org/TR/SVG/paths.html#PathElement
16008   */
16009  fabric.Path.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(['d']);
16010
16011  /**
16012   * Creates an instance of fabric.Path from an SVG <path> element
16013   * @static
16014   * @memberOf fabric.Path
16015   * @param {SVGElement} element to parse
16016   * @param {Function} callback Callback to invoke when an fabric.Path instance is created
16017   * @param {Object} [options] Options object
16018   */
16019  fabric.Path.fromElement = function(element, callback, options) {
16020    var parsedAttributes = fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES);
16021    callback && callback(new fabric.Path(parsedAttributes.d, extend(parsedAttributes, options)));
16022  };
16023  /* _FROM_SVG_END_ */
16024
16025  /**
16026   * Indicates that instances of this type are async
16027   * @static
16028   * @memberOf fabric.Path
16029   * @type Boolean
16030   * @default
16031   */
16032  fabric.Path.async = true;
16033
16034})(typeof exports !== 'undefined' ? exports : this);
16035
16036
16037(function(global) {
16038
16039  'use strict';
16040
16041  var fabric = global.fabric || (global.fabric = { }),
16042      extend = fabric.util.object.extend,
16043      invoke = fabric.util.array.invoke,
16044      parentToObject = fabric.Object.prototype.toObject;
16045
16046  if (fabric.PathGroup) {
16047    fabric.warn('fabric.PathGroup is already defined');
16048    return;
16049  }
16050
16051  /**
16052   * Path group class
16053   * @class fabric.PathGroup
16054   * @extends fabric.Path
16055   * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#path_and_pathgroup}
16056   * @see {@link fabric.PathGroup#initialize} for constructor definition
16057   */
16058  fabric.PathGroup = fabric.util.createClass(fabric.Path, /** @lends fabric.PathGroup.prototype */ {
16059
16060    /**
16061     * Type of an object
16062     * @type String
16063     * @default
16064     */
16065    type: 'path-group',
16066
16067    /**
16068     * Fill value
16069     * @type String
16070     * @default
16071     */
16072    fill: '',
16073
16074    /**
16075     * Constructor
16076     * @param {Array} paths
16077     * @param {Object} [options] Options object
16078     * @return {fabric.PathGroup} thisArg
16079     */
16080    initialize: function(paths, options) {
16081
16082      options = options || { };
16083      this.paths = paths || [ ];
16084
16085      for (var i = this.paths.length; i--;) {
16086        this.paths[i].group = this;
16087      }
16088
16089      if (options.toBeParsed) {
16090        this.parseDimensionsFromPaths(options);
16091        delete options.toBeParsed;
16092      }
16093      this.setOptions(options);
16094      this.setCoords();
16095
16096      if (options.sourcePath) {
16097        this.setSourcePath(options.sourcePath);
16098      }
16099    },
16100
16101    /**
16102     * Calculate width and height based on paths contained
16103     */
16104    parseDimensionsFromPaths: function(options) {
16105      var points, p, xC = [ ], yC = [ ], path, height, width,
16106          m = this.transformMatrix;
16107      for (var j = this.paths.length; j--;) {
16108        path = this.paths[j];
16109        height = path.height + path.strokeWidth;
16110        width = path.width + path.strokeWidth;
16111        points = [
16112          { x: path.left, y: path.top },
16113          { x: path.left + width, y: path.top },
16114          { x: path.left, y: path.top + height },
16115          { x: path.left + width, y: path.top + height }
16116        ];
16117        for (var i = 0; i < points.length; i++) {
16118          p = points[i];
16119          if (m) {
16120            p = fabric.util.transformPoint(p, m, false);
16121          }
16122          xC.push(p.x);
16123          yC.push(p.y);
16124        }
16125      }
16126      options.width = Math.max.apply(null, xC);
16127      options.height = Math.max.apply(null, yC);
16128    },
16129
16130    /**
16131     * Renders this group on a specified context
16132     * @param {CanvasRenderingContext2D} ctx Context to render this instance on
16133     */
16134    render: function(ctx) {
16135      // do not render if object is not visible
16136      if (!this.visible) {
16137        return;
16138      }
16139
16140      ctx.save();
16141
16142      if (this.transformMatrix) {
16143        ctx.transform.apply(ctx, this.transformMatrix);
16144      }
16145      this.transform(ctx);
16146
16147      this._setShadow(ctx);
16148      this.clipTo && fabric.util.clipContext(this, ctx);
16149      ctx.translate(-this.width/2, -this.height/2);
16150      for (var i = 0, l = this.paths.length; i < l; ++i) {
16151        this.paths[i].render(ctx, true);
16152      }
16153      this.clipTo && ctx.restore();
16154      this._removeShadow(ctx);
16155      ctx.restore();
16156    },
16157
16158    /**
16159     * Sets certain property to a certain value
16160     * @param {String} prop
16161     * @param {Any} value
16162     * @return {fabric.PathGroup} thisArg
16163     */
16164    _set: function(prop, value) {
16165
16166      if (prop === 'fill' && value && this.isSameColor()) {
16167        var i = this.paths.length;
16168        while (i--) {
16169          this.paths[i]._set(prop, value);
16170        }
16171      }
16172
16173      return this.callSuper('_set', prop, value);
16174    },
16175
16176    /**
16177     * Returns object representation of this path group
16178     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
16179     * @return {Object} object representation of an instance
16180     */
16181    toObject: function(propertiesToInclude) {
16182      var o = extend(parentToObject.call(this, propertiesToInclude), {
16183        paths: invoke(this.getObjects(), 'toObject', propertiesToInclude)
16184      });
16185      if (this.sourcePath) {
16186        o.sourcePath = this.sourcePath;
16187      }
16188      return o;
16189    },
16190
16191    /**
16192     * Returns dataless object representation of this path group
16193     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
16194     * @return {Object} dataless object representation of an instance
16195     */
16196    toDatalessObject: function(propertiesToInclude) {
16197      var o = this.toObject(propertiesToInclude);
16198      if (this.sourcePath) {
16199        o.paths = this.sourcePath;
16200      }
16201      return o;
16202    },
16203
16204    /* _TO_SVG_START_ */
16205    /**
16206     * Returns svg representation of an instance
16207     * @param {Function} [reviver] Method for further parsing of svg representation.
16208     * @return {String} svg representation of an instance
16209     */
16210    toSVG: function(reviver) {
16211      var objects = this.getObjects(),
16212          p = this.getPointByOrigin('left', 'top'),
16213          translatePart = 'translate(' + p.x + ' ' + p.y + ')',
16214          markup = [
16215            //jscs:disable validateIndentation
16216            '<g ',
16217              'style="', this.getSvgStyles(), '" ',
16218              'transform="', this.getSvgTransformMatrix(), translatePart, this.getSvgTransform(), '" ',
16219            '>\n'
16220            //jscs:enable validateIndentation
16221          ];
16222
16223      for (var i = 0, len = objects.length; i < len; i++) {
16224        markup.push(objects[i].toSVG(reviver));
16225      }
16226      markup.push('</g>\n');
16227
16228      return reviver ? reviver(markup.join('')) : markup.join('');
16229    },
16230    /* _TO_SVG_END_ */
16231
16232    /**
16233     * Returns a string representation of this path group
16234     * @return {String} string representation of an object
16235     */
16236    toString: function() {
16237      return '#<fabric.PathGroup (' + this.complexity() +
16238        '): { top: ' + this.top + ', left: ' + this.left + ' }>';
16239    },
16240
16241    /**
16242     * Returns true if all paths in this group are of same color
16243     * @return {Boolean} true if all paths are of the same color (`fill`)
16244     */
16245    isSameColor: function() {
16246      var firstPathFill = (this.getObjects()[0].get('fill') || '').toLowerCase();
16247      return this.getObjects().every(function(path) {
16248        return (path.get('fill') || '').toLowerCase() === firstPathFill;
16249      });
16250    },
16251
16252    /**
16253     * Returns number representation of object's complexity
16254     * @return {Number} complexity
16255     */
16256    complexity: function() {
16257      return this.paths.reduce(function(total, path) {
16258        return total + ((path && path.complexity) ? path.complexity() : 0);
16259      }, 0);
16260    },
16261
16262    /**
16263     * Returns all paths in this path group
16264     * @return {Array} array of path objects included in this path group
16265     */
16266    getObjects: function() {
16267      return this.paths;
16268    }
16269  });
16270
16271  /**
16272   * Creates fabric.PathGroup instance from an object representation
16273   * @static
16274   * @memberOf fabric.PathGroup
16275   * @param {Object} object Object to create an instance from
16276   * @param {Function} callback Callback to invoke when an fabric.PathGroup instance is created
16277   */
16278  fabric.PathGroup.fromObject = function(object, callback) {
16279    if (typeof object.paths === 'string') {
16280      fabric.loadSVGFromURL(object.paths, function (elements) {
16281
16282        var pathUrl = object.paths;
16283        delete object.paths;
16284
16285        var pathGroup = fabric.util.groupSVGElements(elements, object, pathUrl);
16286
16287        callback(pathGroup);
16288      });
16289    }
16290    else {
16291      fabric.util.enlivenObjects(object.paths, function(enlivenedObjects) {
16292        delete object.paths;
16293        callback(new fabric.PathGroup(enlivenedObjects, object));
16294      });
16295    }
16296  };
16297
16298  /**
16299   * Indicates that instances of this type are async
16300   * @static
16301   * @memberOf fabric.PathGroup
16302   * @type Boolean
16303   * @default
16304   */
16305  fabric.PathGroup.async = true;
16306
16307})(typeof exports !== 'undefined' ? exports : this);
16308
16309
16310(function(global) {
16311
16312  'use strict';
16313
16314  var fabric = global.fabric || (global.fabric = { }),
16315      extend = fabric.util.object.extend,
16316      min = fabric.util.array.min,
16317      max = fabric.util.array.max,
16318      invoke = fabric.util.array.invoke;
16319
16320  if (fabric.Group) {
16321    return;
16322  }
16323
16324  // lock-related properties, for use in fabric.Group#get
16325  // to enable locking behavior on group
16326  // when one of its objects has lock-related properties set
16327  var _lockProperties = {
16328    lockMovementX:  true,
16329    lockMovementY:  true,
16330    lockRotation:   true,
16331    lockScalingX:   true,
16332    lockScalingY:   true,
16333    lockUniScaling: true
16334  };
16335
16336  /**
16337   * Group class
16338   * @class fabric.Group
16339   * @extends fabric.Object
16340   * @mixes fabric.Collection
16341   * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#groups}
16342   * @see {@link fabric.Group#initialize} for constructor definition
16343   */
16344  fabric.Group = fabric.util.createClass(fabric.Object, fabric.Collection, /** @lends fabric.Group.prototype */ {
16345
16346    /**
16347     * Type of an object
16348     * @type String
16349     * @default
16350     */
16351    type: 'group',
16352
16353    /**
16354     * Constructor
16355     * @param {Object} objects Group objects
16356     * @param {Object} [options] Options object
16357     * @return {Object} thisArg
16358     */
16359    initialize: function(objects, options) {
16360      options = options || { };
16361
16362      this._objects = objects || [];
16363      for (var i = this._objects.length; i--; ) {
16364        this._objects[i].group = this;
16365      }
16366
16367      this.originalState = { };
16368      this.callSuper('initialize');
16369
16370      if (options.originX) {
16371        this.originX = options.originX;
16372      }
16373
16374      if (options.originY) {
16375        this.originY = options.originY;
16376      }
16377
16378      this._calcBounds();
16379      this._updateObjectsCoords();
16380
16381      this.callSuper('initialize', options);
16382
16383      this.setCoords();
16384      this.saveCoords();
16385    },
16386
16387    /**
16388     * @private
16389     */
16390    _updateObjectsCoords: function() {
16391      this.forEachObject(this._updateObjectCoords, this);
16392    },
16393
16394    /**
16395     * @private
16396     */
16397    _updateObjectCoords: function(object) {
16398      var objectLeft = object.getLeft(),
16399          objectTop = object.getTop(),
16400          center = this.getCenterPoint();
16401
16402      object.set({
16403        originalLeft: objectLeft,
16404        originalTop: objectTop,
16405        left: objectLeft - center.x,
16406        top: objectTop - center.y
16407      });
16408
16409      object.setCoords();
16410
16411      // do not display corners of objects enclosed in a group
16412      object.__origHasControls = object.hasControls;
16413      object.hasControls = false;
16414    },
16415
16416    /**
16417     * Returns string represenation of a group
16418     * @return {String}
16419     */
16420    toString: function() {
16421      return '#<fabric.Group: (' + this.complexity() + ')>';
16422    },
16423
16424    /**
16425     * Adds an object to a group; Then recalculates group's dimension, position.
16426     * @param {Object} object
16427     * @return {fabric.Group} thisArg
16428     * @chainable
16429     */
16430    addWithUpdate: function(object) {
16431      this._restoreObjectsState();
16432      if (object) {
16433        this._objects.push(object);
16434        object.group = this;
16435      }
16436      // since _restoreObjectsState set objects inactive
16437      this.forEachObject(this._setObjectActive, this);
16438      this._calcBounds();
16439      this._updateObjectsCoords();
16440      return this;
16441    },
16442
16443    /**
16444     * @private
16445     */
16446    _setObjectActive: function(object) {
16447      object.set('active', true);
16448      object.group = this;
16449    },
16450
16451    /**
16452     * Removes an object from a group; Then recalculates group's dimension, position.
16453     * @param {Object} object
16454     * @return {fabric.Group} thisArg
16455     * @chainable
16456     */
16457    removeWithUpdate: function(object) {
16458      this._moveFlippedObject(object);
16459      this._restoreObjectsState();
16460
16461      // since _restoreObjectsState set objects inactive
16462      this.forEachObject(this._setObjectActive, this);
16463
16464      this.remove(object);
16465      this._calcBounds();
16466      this._updateObjectsCoords();
16467
16468      return this;
16469    },
16470
16471    /**
16472     * @private
16473     */
16474    _onObjectAdded: function(object) {
16475      object.group = this;
16476    },
16477
16478    /**
16479     * @private
16480     */
16481    _onObjectRemoved: function(object) {
16482      delete object.group;
16483      object.set('active', false);
16484    },
16485
16486    /**
16487     * Properties that are delegated to group objects when reading/writing
16488     * @param {Object} delegatedProperties
16489     */
16490    delegatedProperties: {
16491      fill:             true,
16492      opacity:          true,
16493      fontFamily:       true,
16494      fontWeight:       true,
16495      fontSize:         true,
16496      fontStyle:        true,
16497      lineHeight:       true,
16498      textDecoration:   true,
16499      textAlign:        true,
16500      backgroundColor:  true
16501    },
16502
16503    /**
16504     * @private
16505     */
16506    _set: function(key, value) {
16507      if (key in this.delegatedProperties) {
16508        var i = this._objects.length;
16509        while (i--) {
16510          this._objects[i].set(key, value);
16511        }
16512      }
16513      this.callSuper('_set', key, value);
16514    },
16515
16516    /**
16517     * Returns object representation of an instance
16518     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
16519     * @return {Object} object representation of an instance
16520     */
16521    toObject: function(propertiesToInclude) {
16522      return extend(this.callSuper('toObject', propertiesToInclude), {
16523        objects: invoke(this._objects, 'toObject', propertiesToInclude)
16524      });
16525    },
16526
16527    /**
16528     * Renders instance on a given context
16529     * @param {CanvasRenderingContext2D} ctx context to render instance on
16530     */
16531    render: function(ctx) {
16532      // do not render if object is not visible
16533      if (!this.visible) {
16534        return;
16535      }
16536
16537      ctx.save();
16538      this.clipTo && fabric.util.clipContext(this, ctx);
16539      this.transform(ctx);
16540      // the array is now sorted in order of highest first, so start from end
16541      for (var i = 0, len = this._objects.length; i < len; i++) {
16542        this._renderObject(this._objects[i], ctx);
16543      }
16544
16545      this.clipTo && ctx.restore();
16546
16547      ctx.restore();
16548    },
16549
16550    /**
16551     * Renders controls and borders for the object
16552     * @param {CanvasRenderingContext2D} ctx Context to render on
16553     * @param {Boolean} [noTransform] When true, context is not transformed
16554     */
16555    _renderControls: function(ctx, noTransform) {
16556      this.callSuper('_renderControls', ctx, noTransform);
16557      for (var i = 0, len = this._objects.length; i < len; i++) {
16558        this._objects[i]._renderControls(ctx);
16559      }
16560    },
16561
16562    /**
16563     * @private
16564     */
16565    _renderObject: function(object, ctx) {
16566      var originalHasRotatingPoint = object.hasRotatingPoint;
16567
16568      // do not render if object is not visible
16569      if (!object.visible) {
16570        return;
16571      }
16572
16573      object.hasRotatingPoint = false;
16574
16575      object.render(ctx);
16576
16577      object.hasRotatingPoint = originalHasRotatingPoint;
16578    },
16579
16580    /**
16581     * Retores original state of each of group objects (original state is that which was before group was created).
16582     * @private
16583     * @return {fabric.Group} thisArg
16584     * @chainable
16585     */
16586    _restoreObjectsState: function() {
16587      this._objects.forEach(this._restoreObjectState, this);
16588      return this;
16589    },
16590
16591    /**
16592     * Realises the transform from this group onto the supplied object
16593     * i.e. it tells you what would happen if the supplied object was in
16594     * the group, and then the group was destroyed. It mutates the supplied
16595     * object.
16596     * @param {fabric.Object} object
16597     * @return {fabric.Object} transformedObject
16598    */
16599    realizeTransform: function(object) {
16600      this._moveFlippedObject(object);
16601      this._setObjectPosition(object);
16602      return object;
16603    },
16604    /**
16605     * Moves a flipped object to the position where it's displayed
16606     * @private
16607     * @param {fabric.Object} object
16608     * @return {fabric.Group} thisArg
16609     */
16610    _moveFlippedObject: function(object) {
16611      var oldOriginX = object.get('originX'),
16612          oldOriginY = object.get('originY'),
16613          center = object.getCenterPoint();
16614
16615      object.set({
16616        originX: 'center',
16617        originY: 'center',
16618        left: center.x,
16619        top: center.y
16620      });
16621
16622      this._toggleFlipping(object);
16623
16624      var newOrigin = object.getPointByOrigin(oldOriginX, oldOriginY);
16625
16626      object.set({
16627        originX: oldOriginX,
16628        originY: oldOriginY,
16629        left: newOrigin.x,
16630        top: newOrigin.y
16631      });
16632
16633      return this;
16634    },
16635
16636    /**
16637     * @private
16638     */
16639    _toggleFlipping: function(object) {
16640      if (this.flipX) {
16641        object.toggle('flipX');
16642        object.set('left', -object.get('left'));
16643        object.setAngle(-object.getAngle());
16644      }
16645      if (this.flipY) {
16646        object.toggle('flipY');
16647        object.set('top', -object.get('top'));
16648        object.setAngle(-object.getAngle());
16649      }
16650    },
16651
16652    /**
16653     * Restores original state of a specified object in group
16654     * @private
16655     * @param {fabric.Object} object
16656     * @return {fabric.Group} thisArg
16657     */
16658    _restoreObjectState: function(object) {
16659      this._setObjectPosition(object);
16660
16661      object.setCoords();
16662      object.hasControls = object.__origHasControls;
16663      delete object.__origHasControls;
16664      object.set('active', false);
16665      object.setCoords();
16666      delete object.group;
16667
16668      return this;
16669    },
16670
16671    /**
16672     * @private
16673     */
16674    _setObjectPosition: function(object) {
16675      var center = this.getCenterPoint(),
16676          rotated = this._getRotatedLeftTop(object);
16677
16678      object.set({
16679        angle: object.getAngle() + this.getAngle(),
16680        left: center.x + rotated.left,
16681        top: center.y + rotated.top,
16682        scaleX: object.get('scaleX') * this.get('scaleX'),
16683        scaleY: object.get('scaleY') * this.get('scaleY')
16684      });
16685    },
16686
16687    /**
16688     * @private
16689     */
16690    _getRotatedLeftTop: function(object) {
16691      var groupAngle = this.getAngle() * (Math.PI / 180);
16692      return {
16693        left: (-Math.sin(groupAngle) * object.getTop() * this.get('scaleY') +
16694                Math.cos(groupAngle) * object.getLeft() * this.get('scaleX')),
16695
16696        top:  (Math.cos(groupAngle) * object.getTop() * this.get('scaleY') +
16697               Math.sin(groupAngle) * object.getLeft() * this.get('scaleX'))
16698      };
16699    },
16700
16701    /**
16702     * Destroys a group (restoring state of its objects)
16703     * @return {fabric.Group} thisArg
16704     * @chainable
16705     */
16706    destroy: function() {
16707      this._objects.forEach(this._moveFlippedObject, this);
16708      return this._restoreObjectsState();
16709    },
16710
16711    /**
16712     * Saves coordinates of this instance (to be used together with `hasMoved`)
16713     * @saveCoords
16714     * @return {fabric.Group} thisArg
16715     * @chainable
16716     */
16717    saveCoords: function() {
16718      this._originalLeft = this.get('left');
16719      this._originalTop = this.get('top');
16720      return this;
16721    },
16722
16723    /**
16724     * Checks whether this group was moved (since `saveCoords` was called last)
16725     * @return {Boolean} true if an object was moved (since fabric.Group#saveCoords was called)
16726     */
16727    hasMoved: function() {
16728      return this._originalLeft !== this.get('left') ||
16729             this._originalTop !== this.get('top');
16730    },
16731
16732    /**
16733     * Sets coordinates of all group objects
16734     * @return {fabric.Group} thisArg
16735     * @chainable
16736     */
16737    setObjectsCoords: function() {
16738      this.forEachObject(function(object) {
16739        object.setCoords();
16740      });
16741      return this;
16742    },
16743
16744    /**
16745     * @private
16746     */
16747    _calcBounds: function(onlyWidthHeight) {
16748      var aX = [],
16749          aY = [],
16750          o, prop,
16751          props = ['tr', 'br', 'bl', 'tl'];
16752
16753      for (var i = 0, len = this._objects.length; i < len; ++i) {
16754        o = this._objects[i];
16755        o.setCoords();
16756        for (var j = 0; j < props.length; j++) {
16757          prop = props[j];
16758          aX.push(o.oCoords[prop].x);
16759          aY.push(o.oCoords[prop].y);
16760        }
16761      }
16762
16763      this.set(this._getBounds(aX, aY, onlyWidthHeight));
16764    },
16765
16766    /**
16767     * @private
16768     */
16769    _getBounds: function(aX, aY, onlyWidthHeight) {
16770      var ivt = fabric.util.invertTransform(this.getViewportTransform()),
16771          minXY = fabric.util.transformPoint(new fabric.Point(min(aX), min(aY)), ivt),
16772          maxXY = fabric.util.transformPoint(new fabric.Point(max(aX), max(aY)), ivt),
16773          obj = {
16774            width: (maxXY.x - minXY.x) || 0,
16775            height: (maxXY.y - minXY.y) || 0
16776          };
16777
16778      if (!onlyWidthHeight) {
16779        obj.left = minXY.x || 0;
16780        obj.top = minXY.y || 0;
16781        if (this.originX === 'center') {
16782          obj.left += obj.width / 2;
16783        }
16784        if (this.originX === 'right') {
16785          obj.left += obj.width;
16786        }
16787        if (this.originY === 'center') {
16788          obj.top += obj.height / 2;
16789        }
16790        if (this.originY === 'bottom') {
16791          obj.top += obj.height;
16792        }
16793      }
16794      return obj;
16795    },
16796
16797    /* _TO_SVG_START_ */
16798    /**
16799     * Returns svg representation of an instance
16800     * @param {Function} [reviver] Method for further parsing of svg representation.
16801     * @return {String} svg representation of an instance
16802     */
16803    toSVG: function(reviver) {
16804      var markup = [
16805        //jscs:disable validateIndentation
16806        '<g ',
16807          'transform="', this.getSvgTransform(),
16808        '">\n'
16809        //jscs:enable validateIndentation
16810      ];
16811
16812      for (var i = 0, len = this._objects.length; i < len; i++) {
16813        markup.push(this._objects[i].toSVG(reviver));
16814      }
16815
16816      markup.push('</g>\n');
16817
16818      return reviver ? reviver(markup.join('')) : markup.join('');
16819    },
16820    /* _TO_SVG_END_ */
16821
16822    /**
16823     * Returns requested property
16824     * @param {String} prop Property to get
16825     * @return {Any}
16826     */
16827    get: function(prop) {
16828      if (prop in _lockProperties) {
16829        if (this[prop]) {
16830          return this[prop];
16831        }
16832        else {
16833          for (var i = 0, len = this._objects.length; i < len; i++) {
16834            if (this._objects[i][prop]) {
16835              return true;
16836            }
16837          }
16838          return false;
16839        }
16840      }
16841      else {
16842        if (prop in this.delegatedProperties) {
16843          return this._objects[0] && this._objects[0].get(prop);
16844        }
16845        return this[prop];
16846      }
16847    }
16848  });
16849
16850  /**
16851   * Returns {@link fabric.Group} instance from an object representation
16852   * @static
16853   * @memberOf fabric.Group
16854   * @param {Object} object Object to create a group from
16855   * @param {Function} [callback] Callback to invoke when an group instance is created
16856   * @return {fabric.Group} An instance of fabric.Group
16857   */
16858  fabric.Group.fromObject = function(object, callback) {
16859    fabric.util.enlivenObjects(object.objects, function(enlivenedObjects) {
16860      delete object.objects;
16861      callback && callback(new fabric.Group(enlivenedObjects, object));
16862    });
16863  };
16864
16865  /**
16866   * Indicates that instances of this type are async
16867   * @static
16868   * @memberOf fabric.Group
16869   * @type Boolean
16870   * @default
16871   */
16872  fabric.Group.async = true;
16873
16874})(typeof exports !== 'undefined' ? exports : this);
16875
16876
16877(function(global) {
16878
16879  'use strict';
16880
16881  var extend = fabric.util.object.extend;
16882
16883  if (!global.fabric) {
16884    global.fabric = { };
16885  }
16886
16887  if (global.fabric.Image) {
16888    fabric.warn('fabric.Image is already defined.');
16889    return;
16890  }
16891
16892  /**
16893   * Image class
16894   * @class fabric.Image
16895   * @extends fabric.Object
16896   * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#images}
16897   * @see {@link fabric.Image#initialize} for constructor definition
16898   */
16899  fabric.Image = fabric.util.createClass(fabric.Object, /** @lends fabric.Image.prototype */ {
16900
16901    /**
16902     * Type of an object
16903     * @type String
16904     * @default
16905     */
16906    type: 'image',
16907
16908    /**
16909     * crossOrigin value (one of "", "anonymous", "allow-credentials")
16910     * @see https://developer.mozilla.org/en-US/docs/HTML/CORS_settings_attributes
16911     * @type String
16912     * @default
16913     */
16914    crossOrigin: '',
16915
16916    /**
16917     * AlignX value, part of preserveAspectRatio (one of "none", "mid", "min", "max")
16918     * @see http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
16919     * This parameter defines how the picture is aligned to its viewport when image element width differs from image width.
16920     * @type String
16921     * @default
16922     */
16923    alignX: 'none',
16924
16925    /**
16926     * AlignY value, part of preserveAspectRatio (one of "none", "mid", "min", "max")
16927     * @see http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
16928     * This parameter defines how the picture is aligned to its viewport when image element height differs from image height.
16929     * @type String
16930     * @default
16931     */
16932    alignY: 'none',
16933
16934    /**
16935     * meetOrSlice value, part of preserveAspectRatio  (one of "meet", "slice").
16936     * if meet the image is always fully visibile, if slice the viewport is always filled with image.
16937     * @see http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
16938     * @type String
16939     * @default
16940     */
16941    meetOrSlice: 'meet',
16942
16943    /**
16944     * private
16945     * contains last value of scaleX to detect
16946     * if the Image got resized after the last Render
16947     * @type Number
16948     */
16949    _lastScaleX: 1,
16950
16951    /**
16952     * private
16953     * contains last value of scaleY to detect
16954     * if the Image got resized after the last Render
16955     * @type Number
16956     */
16957    _lastScaleY: 1,
16958
16959    /**
16960     * Constructor
16961     * @param {HTMLImageElement | String} element Image element
16962     * @param {Object} [options] Options object
16963     * @return {fabric.Image} thisArg
16964     */
16965    initialize: function(element, options) {
16966      options || (options = { });
16967
16968      this.filters = [ ];
16969      this.resizeFilters = [ ];
16970      this.callSuper('initialize', options);
16971
16972      this._initElement(element, options);
16973      this._initConfig(options);
16974
16975      if (options.filters) {
16976        this.filters = options.filters;
16977        this.applyFilters();
16978      }
16979    },
16980
16981    /**
16982     * Returns image element which this instance if based on
16983     * @return {HTMLImageElement} Image element
16984     */
16985    getElement: function() {
16986      return this._element;
16987    },
16988
16989    /**
16990     * Sets image element for this instance to a specified one.
16991     * If filters defined they are applied to new image.
16992     * You might need to call `canvas.renderAll` and `object.setCoords` after replacing, to render new image and update controls area.
16993     * @param {HTMLImageElement} element
16994     * @param {Function} [callback] Callback is invoked when all filters have been applied and new image is generated
16995     * @param {Object} [options] Options object
16996     * @return {fabric.Image} thisArg
16997     * @chainable
16998     */
16999    setElement: function(element, callback, options) {
17000      this._element = element;
17001      this._originalElement = element;
17002      this._initConfig(options);
17003
17004      if (this.filters.length !== 0) {
17005        this.applyFilters(callback);
17006      }
17007      else if (callback) {
17008        callback();
17009      }
17010
17011      return this;
17012    },
17013
17014    /**
17015     * Sets crossOrigin value (on an instance and corresponding image element)
17016     * @return {fabric.Image} thisArg
17017     * @chainable
17018     */
17019    setCrossOrigin: function(value) {
17020      this.crossOrigin = value;
17021      this._element.crossOrigin = value;
17022
17023      return this;
17024    },
17025
17026    /**
17027     * Returns original size of an image
17028     * @return {Object} Object with "width" and "height" properties
17029     */
17030    getOriginalSize: function() {
17031      var element = this.getElement();
17032      return {
17033        width: element.width,
17034        height: element.height
17035      };
17036    },
17037
17038    /**
17039     * @private
17040     * @param {CanvasRenderingContext2D} ctx Context to render on
17041     */
17042    _stroke: function(ctx) {
17043      ctx.save();
17044      this._setStrokeStyles(ctx);
17045      ctx.beginPath();
17046      ctx.strokeRect(-this.width / 2, -this.height / 2, this.width, this.height);
17047      ctx.closePath();
17048      ctx.restore();
17049    },
17050
17051    /**
17052     * @private
17053     * @param {CanvasRenderingContext2D} ctx Context to render on
17054     */
17055    _renderDashedStroke: function(ctx) {
17056      var x = -this.width / 2,
17057          y = -this.height / 2,
17058          w = this.width,
17059          h = this.height;
17060
17061      ctx.save();
17062      this._setStrokeStyles(ctx);
17063
17064      ctx.beginPath();
17065      fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
17066      fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
17067      fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
17068      fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
17069      ctx.closePath();
17070      ctx.restore();
17071    },
17072
17073    /**
17074     * Returns object representation of an instance
17075     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
17076     * @return {Object} Object representation of an instance
17077     */
17078    toObject: function(propertiesToInclude) {
17079      return extend(this.callSuper('toObject', propertiesToInclude), {
17080        src: this._originalElement.src || this._originalElement._src,
17081        filters: this.filters.map(function(filterObj) {
17082          return filterObj && filterObj.toObject();
17083        }),
17084        crossOrigin: this.crossOrigin,
17085        alignX: this.alignX,
17086        alignY: this.alignY,
17087        meetOrSlice: this.meetOrSlice
17088      });
17089    },
17090
17091    /* _TO_SVG_START_ */
17092    /**
17093     * Returns SVG representation of an instance
17094     * @param {Function} [reviver] Method for further parsing of svg representation.
17095     * @return {String} svg representation of an instance
17096     */
17097    toSVG: function(reviver) {
17098      var markup = [], x = -this.width / 2, y = -this.height / 2,
17099          preserveAspectRatio = 'none';
17100      if (this.group && this.group.type === 'path-group') {
17101        x = this.left;
17102        y = this.top;
17103      }
17104      if (this.alignX !== 'none' && this.alignY !== 'none') {
17105        preserveAspectRatio = 'x' + this.alignX + 'Y' + this.alignY + ' ' + this.meetOrSlice;
17106      }
17107      markup.push(
17108        '<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
17109          '<image xlink:href="', this.getSvgSrc(),
17110            '" x="', x, '" y="', y,
17111            '" style="', this.getSvgStyles(),
17112            // we're essentially moving origin of transformation from top/left corner to the center of the shape
17113            // by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
17114            // so that object's center aligns with container's left/top
17115            '" width="', this.width,
17116            '" height="', this.height,
17117            '" preserveAspectRatio="', preserveAspectRatio, '"',
17118          '></image>\n'
17119      );
17120
17121      if (this.stroke || this.strokeDashArray) {
17122        var origFill = this.fill;
17123        this.fill = null;
17124        markup.push(
17125          '<rect ',
17126            'x="', x, '" y="', y,
17127            '" width="', this.width, '" height="', this.height,
17128            '" style="', this.getSvgStyles(),
17129          '"/>\n'
17130        );
17131        this.fill = origFill;
17132      }
17133
17134      markup.push('</g>\n');
17135
17136      return reviver ? reviver(markup.join('')) : markup.join('');
17137    },
17138    /* _TO_SVG_END_ */
17139
17140    /**
17141     * Returns source of an image
17142     * @return {String} Source of an image
17143     */
17144    getSrc: function() {
17145      if (this.getElement()) {
17146        return this.getElement().src || this.getElement()._src;
17147      }
17148    },
17149
17150    /**
17151     * Sets source of an image
17152     * @param {String} src Source string (URL)
17153     * @param {Function} [callback] Callback is invoked when image has been loaded (and all filters have been applied)
17154     * @param {Object} [options] Options object
17155     * @return {fabric.Image} thisArg
17156     * @chainable
17157     */
17158    setSrc: function(src, callback, options) {
17159      fabric.util.loadImage(src, function(img) {
17160        return this.setElement(img, callback, options);
17161      }, this, options && options.crossOrigin);
17162    },
17163
17164    /**
17165     * Returns string representation of an instance
17166     * @return {String} String representation of an instance
17167     */
17168    toString: function() {
17169      return '#<fabric.Image: { src: "' + this.getSrc() + '" }>';
17170    },
17171
17172    /**
17173     * Returns a clone of an instance
17174     * @param {Function} callback Callback is invoked with a clone as a first argument
17175     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
17176     */
17177    clone: function(callback, propertiesToInclude) {
17178      this.constructor.fromObject(this.toObject(propertiesToInclude), callback);
17179    },
17180
17181    /**
17182     * Applies filters assigned to this image (from "filters" array)
17183     * @method applyFilters
17184     * @param {Function} callback Callback is invoked when all filters have been applied and new image is generated
17185     * @return {fabric.Image} thisArg
17186     * @chainable
17187     */
17188    applyFilters: function(callback, filters, imgElement, forResizing) {
17189
17190      filters = filters || this.filters;
17191      imgElement = imgElement || this._originalElement;
17192
17193      if (!imgElement) {
17194        return;
17195      }
17196
17197      var imgEl = imgElement,
17198          canvasEl = fabric.util.createCanvasElement(),
17199          replacement = fabric.util.createImage(),
17200          _this = this;
17201
17202      canvasEl.width = imgEl.width;
17203      canvasEl.height = imgEl.height;
17204      canvasEl.getContext('2d').drawImage(imgEl, 0, 0, imgEl.width, imgEl.height);
17205
17206      if (filters.length === 0) {
17207        this._element = imgElement;
17208        callback && callback();
17209        return canvasEl;
17210      }
17211      filters.forEach(function(filter) {
17212        filter && filter.applyTo(canvasEl, filter.scaleX || _this.scaleX, filter.scaleY || _this.scaleY);
17213        if (!forResizing && filter && filter.type === 'Resize') {
17214          _this.width *= filter.scaleX;
17215          _this.height *= filter.scaleY;
17216        }
17217      });
17218
17219      /** @ignore */
17220      replacement.width = canvasEl.width;
17221      replacement.height = canvasEl.height;
17222
17223      if (fabric.isLikelyNode) {
17224        replacement.src = canvasEl.toBuffer(undefined, fabric.Image.pngCompression);
17225        // onload doesn't fire in some node versions, so we invoke callback manually
17226        _this._element = replacement;
17227        !forResizing && (_this._filteredEl = replacement);
17228        callback && callback();
17229      }
17230      else {
17231        replacement.onload = function() {
17232          _this._element = replacement;
17233          !forResizing && (_this._filteredEl = replacement);
17234          callback && callback();
17235          replacement.onload = canvasEl = imgEl = null;
17236        };
17237        replacement.src = canvasEl.toDataURL('image/png');
17238      }
17239      return canvasEl;
17240    },
17241    /**
17242     * @private
17243     * @param {CanvasRenderingContext2D} ctx Context to render on
17244     */
17245    _render: function(ctx, noTransform) {
17246      var x, y, imageMargins = this._findMargins(), elementToDraw;
17247
17248      x = (noTransform ? this.left : -this.width / 2);
17249      y = (noTransform ? this.top : -this.height / 2);
17250
17251      if (this.meetOrSlice === 'slice') {
17252        ctx.beginPath();
17253        ctx.rect(x, y, this.width, this.height);
17254        ctx.clip();
17255      }
17256
17257      if (this.isMoving === false && this.resizeFilters.length && this._needsResize()) {
17258        this._lastScaleX = this.scaleX;
17259        this._lastScaleY = this.scaleY;
17260        elementToDraw = this.applyFilters(null, this.resizeFilters, this._filteredEl || this._originalElement, true);
17261      }
17262      else {
17263        elementToDraw = this._element;
17264      }
17265      elementToDraw && ctx.drawImage(elementToDraw,
17266                                     x + imageMargins.marginX,
17267                                     y + imageMargins.marginY,
17268                                     imageMargins.width,
17269                                     imageMargins.height
17270                                    );
17271
17272      this._renderStroke(ctx);
17273    },
17274    /**
17275     * @private, needed to check if image needs resize
17276     */
17277    _needsResize: function() {
17278      return (this.scaleX !== this._lastScaleX || this.scaleY !== this._lastScaleY);
17279    },
17280
17281    /**
17282     * @private
17283     */
17284    _findMargins: function() {
17285      var width = this.width, height = this.height, scales,
17286          scale, marginX = 0, marginY = 0;
17287
17288      if (this.alignX !== 'none' || this.alignY !== 'none') {
17289        scales = [this.width / this._element.width, this.height / this._element.height];
17290        scale = this.meetOrSlice === 'meet'
17291                ? Math.min.apply(null, scales) : Math.max.apply(null, scales);
17292        width = this._element.width * scale;
17293        height = this._element.height * scale;
17294        if (this.alignX === 'Mid') {
17295          marginX = (this.width - width) / 2;
17296        }
17297        if (this.alignX === 'Max') {
17298          marginX = this.width - width;
17299        }
17300        if (this.alignY === 'Mid') {
17301          marginY = (this.height - height) / 2;
17302        }
17303        if (this.alignY === 'Max') {
17304          marginY = this.height - height;
17305        }
17306      }
17307      return {
17308        width:  width,
17309        height: height,
17310        marginX: marginX,
17311        marginY: marginY
17312      };
17313    },
17314
17315    /**
17316     * @private
17317     */
17318    _resetWidthHeight: function() {
17319      var element = this.getElement();
17320
17321      this.set('width', element.width);
17322      this.set('height', element.height);
17323    },
17324
17325    /**
17326     * The Image class's initialization method. This method is automatically
17327     * called by the constructor.
17328     * @private
17329     * @param {HTMLImageElement|String} element The element representing the image
17330     */
17331    _initElement: function(element) {
17332      this.setElement(fabric.util.getById(element));
17333      fabric.util.addClass(this.getElement(), fabric.Image.CSS_CANVAS);
17334    },
17335
17336    /**
17337     * @private
17338     * @param {Object} [options] Options object
17339     */
17340    _initConfig: function(options) {
17341      options || (options = { });
17342      this.setOptions(options);
17343      this._setWidthHeight(options);
17344      if (this._element && this.crossOrigin) {
17345        this._element.crossOrigin = this.crossOrigin;
17346      }
17347    },
17348
17349    /**
17350     * @private
17351     * @param {Object} object Object with filters property
17352     * @param {Function} callback Callback to invoke when all fabric.Image.filters instances are created
17353     */
17354    _initFilters: function(object, callback) {
17355      if (object.filters && object.filters.length) {
17356        fabric.util.enlivenObjects(object.filters, function(enlivenedObjects) {
17357          callback && callback(enlivenedObjects);
17358        }, 'fabric.Image.filters');
17359      }
17360      else {
17361        callback && callback();
17362      }
17363    },
17364
17365    /**
17366     * @private
17367     * @param {Object} [options] Object with width/height properties
17368     */
17369    _setWidthHeight: function(options) {
17370      this.width = 'width' in options
17371        ? options.width
17372        : (this.getElement()
17373            ? this.getElement().width || 0
17374            : 0);
17375
17376      this.height = 'height' in options
17377        ? options.height
17378        : (this.getElement()
17379            ? this.getElement().height || 0
17380            : 0);
17381    },
17382
17383    /**
17384     * Returns complexity of an instance
17385     * @return {Number} complexity of this instance
17386     */
17387    complexity: function() {
17388      return 1;
17389    }
17390  });
17391
17392  /**
17393   * Default CSS class name for canvas
17394   * @static
17395   * @type String
17396   * @default
17397   */
17398  fabric.Image.CSS_CANVAS = 'canvas-img';
17399
17400  /**
17401   * Alias for getSrc
17402   * @static
17403   */
17404  fabric.Image.prototype.getSvgSrc = fabric.Image.prototype.getSrc;
17405
17406  /**
17407   * Creates an instance of fabric.Image from its object representation
17408   * @static
17409   * @param {Object} object Object to create an instance from
17410   * @param {Function} [callback] Callback to invoke when an image instance is created
17411   */
17412  fabric.Image.fromObject = function(object, callback) {
17413    fabric.util.loadImage(object.src, function(img) {
17414      fabric.Image.prototype._initFilters.call(object, object, function(filters) {
17415        object.filters = filters || [ ];
17416        var instance = new fabric.Image(img, object);
17417        callback && callback(instance);
17418      });
17419    }, null, object.crossOrigin);
17420  };
17421
17422  /**
17423   * Creates an instance of fabric.Image from an URL string
17424   * @static
17425   * @param {String} url URL to create an image from
17426   * @param {Function} [callback] Callback to invoke when image is created (newly created image is passed as a first argument)
17427   * @param {Object} [imgOptions] Options object
17428   */
17429  fabric.Image.fromURL = function(url, callback, imgOptions) {
17430    fabric.util.loadImage(url, function(img) {
17431      callback && callback(new fabric.Image(img, imgOptions));
17432    }, null, imgOptions && imgOptions.crossOrigin);
17433  };
17434
17435  /* _FROM_SVG_START_ */
17436  /**
17437   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Image.fromElement})
17438   * @static
17439   * @see {@link http://www.w3.org/TR/SVG/struct.html#ImageElement}
17440   */
17441  fabric.Image.ATTRIBUTE_NAMES =
17442    fabric.SHARED_ATTRIBUTES.concat('x y width height preserveAspectRatio xlink:href'.split(' '));
17443
17444  /**
17445   * Returns {@link fabric.Image} instance from an SVG element
17446   * @static
17447   * @param {SVGElement} element Element to parse
17448   * @param {Function} callback Callback to execute when fabric.Image object is created
17449   * @param {Object} [options] Options object
17450   * @return {fabric.Image} Instance of fabric.Image
17451   */
17452  fabric.Image.fromElement = function(element, callback, options) {
17453    var parsedAttributes = fabric.parseAttributes(element, fabric.Image.ATTRIBUTE_NAMES),
17454        align = 'xMidYMid', meetOrSlice = 'meet', alignX, alignY, aspectRatioAttrs;
17455
17456    if (parsedAttributes.preserveAspectRatio) {
17457      aspectRatioAttrs = parsedAttributes.preserveAspectRatio.split(' ');
17458    }
17459
17460    if (aspectRatioAttrs && aspectRatioAttrs.length) {
17461      meetOrSlice = aspectRatioAttrs.pop();
17462      if (meetOrSlice !== 'meet' && meetOrSlice !== 'slice') {
17463        align = meetOrSlice;
17464        meetOrSlice = 'meet';
17465      }
17466      else if (aspectRatioAttrs.length) {
17467        align = aspectRatioAttrs.pop();
17468      }
17469    }
17470    //divide align in alignX and alignY
17471    alignX = align !== 'none' ? align.slice(1, 4) : 'none';
17472    alignY = align !== 'none' ? align.slice(5, 8) : 'none';
17473    parsedAttributes.alignX = alignX;
17474    parsedAttributes.alignY = alignY;
17475    parsedAttributes.meetOrSlice = meetOrSlice;
17476    fabric.Image.fromURL(parsedAttributes['xlink:href'], callback,
17477      extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
17478  };
17479  /* _FROM_SVG_END_ */
17480
17481  /**
17482   * Indicates that instances of this type are async
17483   * @static
17484   * @type Boolean
17485   * @default
17486   */
17487  fabric.Image.async = true;
17488
17489  /**
17490   * Indicates compression level used when generating PNG under Node (in applyFilters). Any of 0-9
17491   * @static
17492   * @type Number
17493   * @default
17494   */
17495  fabric.Image.pngCompression = 1;
17496
17497})(typeof exports !== 'undefined' ? exports : this);
17498
17499
17500fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
17501
17502  /**
17503   * @private
17504   * @return {Number} angle value
17505   */
17506  _getAngleValueForStraighten: function() {
17507    var angle = this.getAngle() % 360;
17508    if (angle > 0) {
17509      return Math.round((angle - 1) / 90) * 90;
17510    }
17511    return Math.round(angle / 90) * 90;
17512  },
17513
17514  /**
17515   * Straightens an object (rotating it from current angle to one of 0, 90, 180, 270, etc. depending on which is closer)
17516   * @return {fabric.Object} thisArg
17517   * @chainable
17518   */
17519  straighten: function() {
17520    this.setAngle(this._getAngleValueForStraighten());
17521    return this;
17522  },
17523
17524  /**
17525   * Same as {@link fabric.Object.prototype.straighten} but with animation
17526   * @param {Object} callbacks Object with callback functions
17527   * @param {Function} [callbacks.onComplete] Invoked on completion
17528   * @param {Function} [callbacks.onChange] Invoked on every step of animation
17529   * @return {fabric.Object} thisArg
17530   * @chainable
17531   */
17532  fxStraighten: function(callbacks) {
17533    callbacks = callbacks || { };
17534
17535    var empty = function() { },
17536        onComplete = callbacks.onComplete || empty,
17537        onChange = callbacks.onChange || empty,
17538        _this = this;
17539
17540    fabric.util.animate({
17541      startValue: this.get('angle'),
17542      endValue: this._getAngleValueForStraighten(),
17543      duration: this.FX_DURATION,
17544      onChange: function(value) {
17545        _this.setAngle(value);
17546        onChange();
17547      },
17548      onComplete: function() {
17549        _this.setCoords();
17550        onComplete();
17551      },
17552      onStart: function() {
17553        _this.set('active', false);
17554      }
17555    });
17556
17557    return this;
17558  }
17559});
17560
17561fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
17562
17563  /**
17564   * Straightens object, then rerenders canvas
17565   * @param {fabric.Object} object Object to straighten
17566   * @return {fabric.Canvas} thisArg
17567   * @chainable
17568   */
17569  straightenObject: function (object) {
17570    object.straighten();
17571    this.renderAll();
17572    return this;
17573  },
17574
17575  /**
17576   * Same as {@link fabric.Canvas.prototype.straightenObject}, but animated
17577   * @param {fabric.Object} object Object to straighten
17578   * @return {fabric.Canvas} thisArg
17579   * @chainable
17580   */
17581  fxStraightenObject: function (object) {
17582    object.fxStraighten({
17583      onChange: this.renderAll.bind(this)
17584    });
17585    return this;
17586  }
17587});
17588
17589
17590/**
17591 * @namespace fabric.Image.filters
17592 * @memberOf fabric.Image
17593 * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#image_filters}
17594 * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
17595 */
17596fabric.Image.filters = fabric.Image.filters || { };
17597
17598/**
17599 * Root filter class from which all filter classes inherit from
17600 * @class fabric.Image.filters.BaseFilter
17601 * @memberOf fabric.Image.filters
17602 */
17603fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Image.filters.BaseFilter.prototype */ {
17604
17605  /**
17606   * Filter type
17607   * @param {String} type
17608   * @default
17609   */
17610  type: 'BaseFilter',
17611
17612  /**
17613   * Constructor
17614   * @param {Object} [options] Options object
17615   */
17616  initialize: function(options) {
17617    if (options) {
17618      this.setOptions(options);
17619    }
17620  },
17621
17622  /**
17623   * Sets filter's properties from options
17624   * @param {Object} [options] Options object
17625   */
17626  setOptions: function(options) {
17627    for (var prop in options) {
17628      this[prop] = options[prop];
17629    }
17630  },
17631
17632  /**
17633   * Returns object representation of an instance
17634   * @return {Object} Object representation of an instance
17635   */
17636  toObject: function() {
17637    return { type: this.type };
17638  },
17639
17640  /**
17641   * Returns a JSON representation of an instance
17642   * @return {Object} JSON
17643   */
17644  toJSON: function() {
17645    // delegate, not alias
17646    return this.toObject();
17647  }
17648});
17649
17650
17651(function(global) {
17652
17653  'use strict';
17654
17655  var fabric  = global.fabric || (global.fabric = { }),
17656      extend = fabric.util.object.extend;
17657
17658  /**
17659   * Brightness filter class
17660   * @class fabric.Image.filters.Brightness
17661   * @memberOf fabric.Image.filters
17662   * @extends fabric.Image.filters.BaseFilter
17663   * @see {@link fabric.Image.filters.Brightness#initialize} for constructor definition
17664   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
17665   * @example
17666   * var filter = new fabric.Image.filters.Brightness({
17667   *   brightness: 200
17668   * });
17669   * object.filters.push(filter);
17670   * object.applyFilters(canvas.renderAll.bind(canvas));
17671   */
17672  fabric.Image.filters.Brightness = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Brightness.prototype */ {
17673
17674    /**
17675     * Filter type
17676     * @param {String} type
17677     * @default
17678     */
17679    type: 'Brightness',
17680
17681    /**
17682     * Constructor
17683     * @memberOf fabric.Image.filters.Brightness.prototype
17684     * @param {Object} [options] Options object
17685     * @param {Number} [options.brightness=0] Value to brighten the image up (0..255)
17686     */
17687    initialize: function(options) {
17688      options = options || { };
17689      this.brightness = options.brightness || 0;
17690    },
17691
17692    /**
17693     * Applies filter to canvas element
17694     * @param {Object} canvasEl Canvas element to apply filter to
17695     */
17696    applyTo: function(canvasEl) {
17697      var context = canvasEl.getContext('2d'),
17698          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
17699          data = imageData.data,
17700          brightness = this.brightness;
17701
17702      for (var i = 0, len = data.length; i < len; i += 4) {
17703        data[i] += brightness;
17704        data[i + 1] += brightness;
17705        data[i + 2] += brightness;
17706      }
17707
17708      context.putImageData(imageData, 0, 0);
17709    },
17710
17711    /**
17712     * Returns object representation of an instance
17713     * @return {Object} Object representation of an instance
17714     */
17715    toObject: function() {
17716      return extend(this.callSuper('toObject'), {
17717        brightness: this.brightness
17718      });
17719    }
17720  });
17721
17722  /**
17723   * Returns filter instance from an object representation
17724   * @static
17725   * @param {Object} object Object to create an instance from
17726   * @return {fabric.Image.filters.Brightness} Instance of fabric.Image.filters.Brightness
17727   */
17728  fabric.Image.filters.Brightness.fromObject = function(object) {
17729    return new fabric.Image.filters.Brightness(object);
17730  };
17731
17732})(typeof exports !== 'undefined' ? exports : this);
17733
17734
17735(function(global) {
17736
17737  'use strict';
17738
17739  var fabric  = global.fabric || (global.fabric = { }),
17740      extend = fabric.util.object.extend;
17741
17742  /**
17743   * Adapted from <a href="http://www.html5rocks.com/en/tutorials/canvas/imagefilters/">html5rocks article</a>
17744   * @class fabric.Image.filters.Convolute
17745   * @memberOf fabric.Image.filters
17746   * @extends fabric.Image.filters.BaseFilter
17747   * @see {@link fabric.Image.filters.Convolute#initialize} for constructor definition
17748   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
17749   * @example <caption>Sharpen filter</caption>
17750   * var filter = new fabric.Image.filters.Convolute({
17751   *   matrix: [ 0, -1,  0,
17752   *            -1,  5, -1,
17753   *             0, -1,  0 ]
17754   * });
17755   * object.filters.push(filter);
17756   * object.applyFilters(canvas.renderAll.bind(canvas));
17757   * @example <caption>Blur filter</caption>
17758   * var filter = new fabric.Image.filters.Convolute({
17759   *   matrix: [ 1/9, 1/9, 1/9,
17760   *             1/9, 1/9, 1/9,
17761   *             1/9, 1/9, 1/9 ]
17762   * });
17763   * object.filters.push(filter);
17764   * object.applyFilters(canvas.renderAll.bind(canvas));
17765   * @example <caption>Emboss filter</caption>
17766   * var filter = new fabric.Image.filters.Convolute({
17767   *   matrix: [ 1,   1,  1,
17768   *             1, 0.7, -1,
17769   *            -1,  -1, -1 ]
17770   * });
17771   * object.filters.push(filter);
17772   * object.applyFilters(canvas.renderAll.bind(canvas));
17773   * @example <caption>Emboss filter with opaqueness</caption>
17774   * var filter = new fabric.Image.filters.Convolute({
17775   *   opaque: true,
17776   *   matrix: [ 1,   1,  1,
17777   *             1, 0.7, -1,
17778   *            -1,  -1, -1 ]
17779   * });
17780   * object.filters.push(filter);
17781   * object.applyFilters(canvas.renderAll.bind(canvas));
17782   */
17783  fabric.Image.filters.Convolute = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Convolute.prototype */ {
17784
17785    /**
17786     * Filter type
17787     * @param {String} type
17788     * @default
17789     */
17790    type: 'Convolute',
17791
17792    /**
17793     * Constructor
17794     * @memberOf fabric.Image.filters.Convolute.prototype
17795     * @param {Object} [options] Options object
17796     * @param {Boolean} [options.opaque=false] Opaque value (true/false)
17797     * @param {Array} [options.matrix] Filter matrix
17798     */
17799    initialize: function(options) {
17800      options = options || { };
17801
17802      this.opaque = options.opaque;
17803      this.matrix = options.matrix || [
17804        0, 0, 0,
17805        0, 1, 0,
17806        0, 0, 0
17807      ];
17808
17809      var canvasEl = fabric.util.createCanvasElement();
17810      this.tmpCtx = canvasEl.getContext('2d');
17811    },
17812
17813    /**
17814     * @private
17815     */
17816    _createImageData: function(w, h) {
17817      return this.tmpCtx.createImageData(w, h);
17818    },
17819
17820    /**
17821     * Applies filter to canvas element
17822     * @param {Object} canvasEl Canvas element to apply filter to
17823     */
17824    applyTo: function(canvasEl) {
17825
17826      var weights = this.matrix,
17827          context = canvasEl.getContext('2d'),
17828          pixels = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
17829
17830          side = Math.round(Math.sqrt(weights.length)),
17831          halfSide = Math.floor(side/2),
17832          src = pixels.data,
17833          sw = pixels.width,
17834          sh = pixels.height,
17835
17836          // pad output by the convolution matrix
17837          w = sw,
17838          h = sh,
17839          output = this._createImageData(w, h),
17840
17841          dst = output.data,
17842
17843          // go through the destination image pixels
17844          alphaFac = this.opaque ? 1 : 0;
17845
17846      for (var y = 0; y < h; y++) {
17847        for (var x = 0; x < w; x++) {
17848          var sy = y,
17849              sx = x,
17850              dstOff = (y * w + x) * 4,
17851              // calculate the weighed sum of the source image pixels that
17852              // fall under the convolution matrix
17853              r = 0, g = 0, b = 0, a = 0;
17854
17855          for (var cy = 0; cy < side; cy++) {
17856            for (var cx = 0; cx < side; cx++) {
17857
17858              var scy = sy + cy - halfSide,
17859                  scx = sx + cx - halfSide;
17860
17861              /* jshint maxdepth:5 */
17862              if (scy < 0 || scy > sh || scx < 0 || scx > sw) {
17863                continue;
17864              }
17865
17866              var srcOff = (scy * sw + scx) * 4,
17867                  wt = weights[cy * side + cx];
17868
17869              r += src[srcOff] * wt;
17870              g += src[srcOff + 1] * wt;
17871              b += src[srcOff + 2] * wt;
17872              a += src[srcOff + 3] * wt;
17873            }
17874          }
17875          dst[dstOff] = r;
17876          dst[dstOff + 1] = g;
17877          dst[dstOff + 2] = b;
17878          dst[dstOff + 3] = a + alphaFac * (255 - a);
17879        }
17880      }
17881
17882      context.putImageData(output, 0, 0);
17883    },
17884
17885    /**
17886     * Returns object representation of an instance
17887     * @return {Object} Object representation of an instance
17888     */
17889    toObject: function() {
17890      return extend(this.callSuper('toObject'), {
17891        opaque: this.opaque,
17892        matrix: this.matrix
17893      });
17894    }
17895  });
17896
17897  /**
17898   * Returns filter instance from an object representation
17899   * @static
17900   * @param {Object} object Object to create an instance from
17901   * @return {fabric.Image.filters.Convolute} Instance of fabric.Image.filters.Convolute
17902   */
17903  fabric.Image.filters.Convolute.fromObject = function(object) {
17904    return new fabric.Image.filters.Convolute(object);
17905  };
17906
17907})(typeof exports !== 'undefined' ? exports : this);
17908
17909
17910(function(global) {
17911
17912  'use strict';
17913
17914  var fabric  = global.fabric || (global.fabric = { }),
17915      extend = fabric.util.object.extend;
17916
17917  /**
17918   * GradientTransparency filter class
17919   * @class fabric.Image.filters.GradientTransparency
17920   * @memberOf fabric.Image.filters
17921   * @extends fabric.Image.filters.BaseFilter
17922   * @see {@link fabric.Image.filters.GradientTransparency#initialize} for constructor definition
17923   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
17924   * @example
17925   * var filter = new fabric.Image.filters.GradientTransparency({
17926   *   threshold: 200
17927   * });
17928   * object.filters.push(filter);
17929   * object.applyFilters(canvas.renderAll.bind(canvas));
17930   */
17931  fabric.Image.filters.GradientTransparency = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.GradientTransparency.prototype */ {
17932
17933    /**
17934     * Filter type
17935     * @param {String} type
17936     * @default
17937     */
17938    type: 'GradientTransparency',
17939
17940    /**
17941     * Constructor
17942     * @memberOf fabric.Image.filters.GradientTransparency.prototype
17943     * @param {Object} [options] Options object
17944     * @param {Number} [options.threshold=100] Threshold value
17945     */
17946    initialize: function(options) {
17947      options = options || { };
17948      this.threshold = options.threshold || 100;
17949    },
17950
17951    /**
17952     * Applies filter to canvas element
17953     * @param {Object} canvasEl Canvas element to apply filter to
17954     */
17955    applyTo: function(canvasEl) {
17956      var context = canvasEl.getContext('2d'),
17957          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
17958          data = imageData.data,
17959          threshold = this.threshold,
17960          total = data.length;
17961
17962      for (var i = 0, len = data.length; i < len; i += 4) {
17963        data[i + 3] = threshold + 255 * (total - i) / total;
17964      }
17965
17966      context.putImageData(imageData, 0, 0);
17967    },
17968
17969    /**
17970     * Returns object representation of an instance
17971     * @return {Object} Object representation of an instance
17972     */
17973    toObject: function() {
17974      return extend(this.callSuper('toObject'), {
17975        threshold: this.threshold
17976      });
17977    }
17978  });
17979
17980  /**
17981   * Returns filter instance from an object representation
17982   * @static
17983   * @param {Object} object Object to create an instance from
17984   * @return {fabric.Image.filters.GradientTransparency} Instance of fabric.Image.filters.GradientTransparency
17985   */
17986  fabric.Image.filters.GradientTransparency.fromObject = function(object) {
17987    return new fabric.Image.filters.GradientTransparency(object);
17988  };
17989
17990})(typeof exports !== 'undefined' ? exports : this);
17991
17992
17993(function(global) {
17994
17995  'use strict';
17996
17997  var fabric  = global.fabric || (global.fabric = { });
17998
17999  /**
18000   * Grayscale image filter class
18001   * @class fabric.Image.filters.Grayscale
18002   * @memberOf fabric.Image.filters
18003   * @extends fabric.Image.filters.BaseFilter
18004   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
18005   * @example
18006   * var filter = new fabric.Image.filters.Grayscale();
18007   * object.filters.push(filter);
18008   * object.applyFilters(canvas.renderAll.bind(canvas));
18009   */
18010  fabric.Image.filters.Grayscale = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Grayscale.prototype */ {
18011
18012    /**
18013     * Filter type
18014     * @param {String} type
18015     * @default
18016     */
18017    type: 'Grayscale',
18018
18019    /**
18020     * Applies filter to canvas element
18021     * @memberOf fabric.Image.filters.Grayscale.prototype
18022     * @param {Object} canvasEl Canvas element to apply filter to
18023     */
18024    applyTo: function(canvasEl) {
18025      var context = canvasEl.getContext('2d'),
18026          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
18027          data = imageData.data,
18028          len = imageData.width * imageData.height * 4,
18029          index = 0,
18030          average;
18031
18032      while (index < len) {
18033        average = (data[index] + data[index + 1] + data[index + 2]) / 3;
18034        data[index]     = average;
18035        data[index + 1] = average;
18036        data[index + 2] = average;
18037        index += 4;
18038      }
18039
18040      context.putImageData(imageData, 0, 0);
18041    }
18042  });
18043
18044  /**
18045   * Returns filter instance from an object representation
18046   * @static
18047   * @return {fabric.Image.filters.Grayscale} Instance of fabric.Image.filters.Grayscale
18048   */
18049  fabric.Image.filters.Grayscale.fromObject = function() {
18050    return new fabric.Image.filters.Grayscale();
18051  };
18052
18053})(typeof exports !== 'undefined' ? exports : this);
18054
18055
18056(function(global) {
18057
18058  'use strict';
18059
18060  var fabric  = global.fabric || (global.fabric = { });
18061
18062  /**
18063   * Invert filter class
18064   * @class fabric.Image.filters.Invert
18065   * @memberOf fabric.Image.filters
18066   * @extends fabric.Image.filters.BaseFilter
18067   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
18068   * @example
18069   * var filter = new fabric.Image.filters.Invert();
18070   * object.filters.push(filter);
18071   * object.applyFilters(canvas.renderAll.bind(canvas));
18072   */
18073  fabric.Image.filters.Invert = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Invert.prototype */ {
18074
18075    /**
18076     * Filter type
18077     * @param {String} type
18078     * @default
18079     */
18080    type: 'Invert',
18081
18082    /**
18083     * Applies filter to canvas element
18084     * @memberOf fabric.Image.filters.Invert.prototype
18085     * @param {Object} canvasEl Canvas element to apply filter to
18086     */
18087    applyTo: function(canvasEl) {
18088      var context = canvasEl.getContext('2d'),
18089          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
18090          data = imageData.data,
18091          iLen = data.length, i;
18092
18093      for (i = 0; i < iLen; i+=4) {
18094        data[i] = 255 - data[i];
18095        data[i + 1] = 255 - data[i + 1];
18096        data[i + 2] = 255 - data[i + 2];
18097      }
18098
18099      context.putImageData(imageData, 0, 0);
18100    }
18101  });
18102
18103  /**
18104   * Returns filter instance from an object representation
18105   * @static
18106   * @return {fabric.Image.filters.Invert} Instance of fabric.Image.filters.Invert
18107   */
18108  fabric.Image.filters.Invert.fromObject = function() {
18109    return new fabric.Image.filters.Invert();
18110  };
18111
18112})(typeof exports !== 'undefined' ? exports : this);
18113
18114
18115(function(global) {
18116
18117  'use strict';
18118
18119  var fabric  = global.fabric || (global.fabric = { }),
18120      extend = fabric.util.object.extend;
18121
18122  /**
18123   * Mask filter class
18124   * See http://resources.aleph-1.com/mask/
18125   * @class fabric.Image.filters.Mask
18126   * @memberOf fabric.Image.filters
18127   * @extends fabric.Image.filters.BaseFilter
18128   * @see {@link fabric.Image.filters.Mask#initialize} for constructor definition
18129   */
18130  fabric.Image.filters.Mask = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Mask.prototype */ {
18131
18132    /**
18133     * Filter type
18134     * @param {String} type
18135     * @default
18136     */
18137    type: 'Mask',
18138
18139    /**
18140     * Constructor
18141     * @memberOf fabric.Image.filters.Mask.prototype
18142     * @param {Object} [options] Options object
18143     * @param {fabric.Image} [options.mask] Mask image object
18144     * @param {Number} [options.channel=0] Rgb channel (0, 1, 2 or 3)
18145     */
18146    initialize: function(options) {
18147      options = options || { };
18148
18149      this.mask = options.mask;
18150      this.channel = [ 0, 1, 2, 3 ].indexOf(options.channel) > -1 ? options.channel : 0;
18151    },
18152
18153    /**
18154     * Applies filter to canvas element
18155     * @param {Object} canvasEl Canvas element to apply filter to
18156     */
18157    applyTo: function(canvasEl) {
18158      if (!this.mask) {
18159        return;
18160      }
18161
18162      var context = canvasEl.getContext('2d'),
18163          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
18164          data = imageData.data,
18165          maskEl = this.mask.getElement(),
18166          maskCanvasEl = fabric.util.createCanvasElement(),
18167          channel = this.channel,
18168          i,
18169          iLen = imageData.width * imageData.height * 4;
18170
18171      maskCanvasEl.width = maskEl.width;
18172      maskCanvasEl.height = maskEl.height;
18173
18174      maskCanvasEl.getContext('2d').drawImage(maskEl, 0, 0, maskEl.width, maskEl.height);
18175
18176      var maskImageData = maskCanvasEl.getContext('2d').getImageData(0, 0, maskEl.width, maskEl.height),
18177          maskData = maskImageData.data;
18178
18179      for (i = 0; i < iLen; i += 4) {
18180        data[i + 3] = maskData[i + channel];
18181      }
18182
18183      context.putImageData(imageData, 0, 0);
18184    },
18185
18186    /**
18187     * Returns object representation of an instance
18188     * @return {Object} Object representation of an instance
18189     */
18190    toObject: function() {
18191      return extend(this.callSuper('toObject'), {
18192        mask: this.mask.toObject(),
18193        channel: this.channel
18194      });
18195    }
18196  });
18197
18198  /**
18199   * Returns filter instance from an object representation
18200   * @static
18201   * @param {Object} object Object to create an instance from
18202   * @param {Function} [callback] Callback to invoke when a mask filter instance is created
18203   */
18204  fabric.Image.filters.Mask.fromObject = function(object, callback) {
18205    fabric.util.loadImage(object.mask.src, function(img) {
18206      object.mask = new fabric.Image(img, object.mask);
18207      callback && callback(new fabric.Image.filters.Mask(object));
18208    });
18209  };
18210
18211  /**
18212   * Indicates that instances of this type are async
18213   * @static
18214   * @type Boolean
18215   * @default
18216   */
18217  fabric.Image.filters.Mask.async = true;
18218
18219})(typeof exports !== 'undefined' ? exports : this);
18220
18221
18222(function(global) {
18223
18224  'use strict';
18225
18226  var fabric  = global.fabric || (global.fabric = { }),
18227      extend = fabric.util.object.extend;
18228
18229  /**
18230   * Noise filter class
18231   * @class fabric.Image.filters.Noise
18232   * @memberOf fabric.Image.filters
18233   * @extends fabric.Image.filters.BaseFilter
18234   * @see {@link fabric.Image.filters.Noise#initialize} for constructor definition
18235   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
18236   * @example
18237   * var filter = new fabric.Image.filters.Noise({
18238   *   noise: 700
18239   * });
18240   * object.filters.push(filter);
18241   * object.applyFilters(canvas.renderAll.bind(canvas));
18242   */
18243  fabric.Image.filters.Noise = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Noise.prototype */ {
18244
18245    /**
18246     * Filter type
18247     * @param {String} type
18248     * @default
18249     */
18250    type: 'Noise',
18251
18252    /**
18253     * Constructor
18254     * @memberOf fabric.Image.filters.Noise.prototype
18255     * @param {Object} [options] Options object
18256     * @param {Number} [options.noise=0] Noise value
18257     */
18258    initialize: function(options) {
18259      options = options || { };
18260      this.noise = options.noise || 0;
18261    },
18262
18263    /**
18264     * Applies filter to canvas element
18265     * @param {Object} canvasEl Canvas element to apply filter to
18266     */
18267    applyTo: function(canvasEl) {
18268      var context = canvasEl.getContext('2d'),
18269          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
18270          data = imageData.data,
18271          noise = this.noise, rand;
18272
18273      for (var i = 0, len = data.length; i < len; i += 4) {
18274
18275        rand = (0.5 - Math.random()) * noise;
18276
18277        data[i] += rand;
18278        data[i + 1] += rand;
18279        data[i + 2] += rand;
18280      }
18281
18282      context.putImageData(imageData, 0, 0);
18283    },
18284
18285    /**
18286     * Returns object representation of an instance
18287     * @return {Object} Object representation of an instance
18288     */
18289    toObject: function() {
18290      return extend(this.callSuper('toObject'), {
18291        noise: this.noise
18292      });
18293    }
18294  });
18295
18296  /**
18297   * Returns filter instance from an object representation
18298   * @static
18299   * @param {Object} object Object to create an instance from
18300   * @return {fabric.Image.filters.Noise} Instance of fabric.Image.filters.Noise
18301   */
18302  fabric.Image.filters.Noise.fromObject = function(object) {
18303    return new fabric.Image.filters.Noise(object);
18304  };
18305
18306})(typeof exports !== 'undefined' ? exports : this);
18307
18308
18309(function(global) {
18310
18311  'use strict';
18312
18313  var fabric  = global.fabric || (global.fabric = { }),
18314      extend = fabric.util.object.extend;
18315
18316  /**
18317   * Pixelate filter class
18318   * @class fabric.Image.filters.Pixelate
18319   * @memberOf fabric.Image.filters
18320   * @extends fabric.Image.filters.BaseFilter
18321   * @see {@link fabric.Image.filters.Pixelate#initialize} for constructor definition
18322   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
18323   * @example
18324   * var filter = new fabric.Image.filters.Pixelate({
18325   *   blocksize: 8
18326   * });
18327   * object.filters.push(filter);
18328   * object.applyFilters(canvas.renderAll.bind(canvas));
18329   */
18330  fabric.Image.filters.Pixelate = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Pixelate.prototype */ {
18331
18332    /**
18333     * Filter type
18334     * @param {String} type
18335     * @default
18336     */
18337    type: 'Pixelate',
18338
18339    /**
18340     * Constructor
18341     * @memberOf fabric.Image.filters.Pixelate.prototype
18342     * @param {Object} [options] Options object
18343     * @param {Number} [options.blocksize=4] Blocksize for pixelate
18344     */
18345    initialize: function(options) {
18346      options = options || { };
18347      this.blocksize = options.blocksize || 4;
18348    },
18349
18350    /**
18351     * Applies filter to canvas element
18352     * @param {Object} canvasEl Canvas element to apply filter to
18353     */
18354    applyTo: function(canvasEl) {
18355      var context = canvasEl.getContext('2d'),
18356          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
18357          data = imageData.data,
18358          iLen = imageData.height,
18359          jLen = imageData.width,
18360          index, i, j, r, g, b, a;
18361
18362      for (i = 0; i < iLen; i += this.blocksize) {
18363        for (j = 0; j < jLen; j += this.blocksize) {
18364
18365          index = (i * 4) * jLen + (j * 4);
18366
18367          r = data[index];
18368          g = data[index + 1];
18369          b = data[index + 2];
18370          a = data[index + 3];
18371
18372          /*
18373           blocksize: 4
18374
18375           [1,x,x,x,1]
18376           [x,x,x,x,1]
18377           [x,x,x,x,1]
18378           [x,x,x,x,1]
18379           [1,1,1,1,1]
18380           */
18381
18382          for (var _i = i, _ilen = i + this.blocksize; _i < _ilen; _i++) {
18383            for (var _j = j, _jlen = j + this.blocksize; _j < _jlen; _j++) {
18384              index = (_i * 4) * jLen + (_j * 4);
18385              data[index] = r;
18386              data[index + 1] = g;
18387              data[index + 2] = b;
18388              data[index + 3] = a;
18389            }
18390          }
18391        }
18392      }
18393
18394      context.putImageData(imageData, 0, 0);
18395    },
18396
18397    /**
18398     * Returns object representation of an instance
18399     * @return {Object} Object representation of an instance
18400     */
18401    toObject: function() {
18402      return extend(this.callSuper('toObject'), {
18403        blocksize: this.blocksize
18404      });
18405    }
18406  });
18407
18408  /**
18409   * Returns filter instance from an object representation
18410   * @static
18411   * @param {Object} object Object to create an instance from
18412   * @return {fabric.Image.filters.Pixelate} Instance of fabric.Image.filters.Pixelate
18413   */
18414  fabric.Image.filters.Pixelate.fromObject = function(object) {
18415    return new fabric.Image.filters.Pixelate(object);
18416  };
18417
18418})(typeof exports !== 'undefined' ? exports : this);
18419
18420
18421(function(global) {
18422
18423  'use strict';
18424
18425  var fabric  = global.fabric || (global.fabric = { }),
18426      extend = fabric.util.object.extend;
18427
18428  /**
18429   * Remove white filter class
18430   * @class fabric.Image.filters.RemoveWhite
18431   * @memberOf fabric.Image.filters
18432   * @extends fabric.Image.filters.BaseFilter
18433   * @see {@link fabric.Image.filters.RemoveWhite#initialize} for constructor definition
18434   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
18435   * @example
18436   * var filter = new fabric.Image.filters.RemoveWhite({
18437   *   threshold: 40,
18438   *   distance: 140
18439   * });
18440   * object.filters.push(filter);
18441   * object.applyFilters(canvas.renderAll.bind(canvas));
18442   */
18443  fabric.Image.filters.RemoveWhite = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.RemoveWhite.prototype */ {
18444
18445    /**
18446     * Filter type
18447     * @param {String} type
18448     * @default
18449     */
18450    type: 'RemoveWhite',
18451
18452    /**
18453     * Constructor
18454     * @memberOf fabric.Image.filters.RemoveWhite.prototype
18455     * @param {Object} [options] Options object
18456     * @param {Number} [options.threshold=30] Threshold value
18457     * @param {Number} [options.distance=20] Distance value
18458     */
18459    initialize: function(options) {
18460      options = options || { };
18461      this.threshold = options.threshold || 30;
18462      this.distance = options.distance || 20;
18463    },
18464
18465    /**
18466     * Applies filter to canvas element
18467     * @param {Object} canvasEl Canvas element to apply filter to
18468     */
18469    applyTo: function(canvasEl) {
18470      var context = canvasEl.getContext('2d'),
18471          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
18472          data = imageData.data,
18473          threshold = this.threshold,
18474          distance = this.distance,
18475          limit = 255 - threshold,
18476          abs = Math.abs,
18477          r, g, b;
18478
18479      for (var i = 0, len = data.length; i < len; i += 4) {
18480        r = data[i];
18481        g = data[i + 1];
18482        b = data[i + 2];
18483
18484        if (r > limit &&
18485            g > limit &&
18486            b > limit &&
18487            abs(r - g) < distance &&
18488            abs(r - b) < distance &&
18489            abs(g - b) < distance
18490        ) {
18491          data[i + 3] = 1;
18492        }
18493      }
18494
18495      context.putImageData(imageData, 0, 0);
18496    },
18497
18498    /**
18499     * Returns object representation of an instance
18500     * @return {Object} Object representation of an instance
18501     */
18502    toObject: function() {
18503      return extend(this.callSuper('toObject'), {
18504        threshold: this.threshold,
18505        distance: this.distance
18506      });
18507    }
18508  });
18509
18510  /**
18511   * Returns filter instance from an object representation
18512   * @static
18513   * @param {Object} object Object to create an instance from
18514   * @return {fabric.Image.filters.RemoveWhite} Instance of fabric.Image.filters.RemoveWhite
18515   */
18516  fabric.Image.filters.RemoveWhite.fromObject = function(object) {
18517    return new fabric.Image.filters.RemoveWhite(object);
18518  };
18519
18520})(typeof exports !== 'undefined' ? exports : this);
18521
18522
18523(function(global) {
18524
18525  'use strict';
18526
18527  var fabric  = global.fabric || (global.fabric = { });
18528
18529  /**
18530   * Sepia filter class
18531   * @class fabric.Image.filters.Sepia
18532   * @memberOf fabric.Image.filters
18533   * @extends fabric.Image.filters.BaseFilter
18534   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
18535   * @example
18536   * var filter = new fabric.Image.filters.Sepia();
18537   * object.filters.push(filter);
18538   * object.applyFilters(canvas.renderAll.bind(canvas));
18539   */
18540  fabric.Image.filters.Sepia = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Sepia.prototype */ {
18541
18542    /**
18543     * Filter type
18544     * @param {String} type
18545     * @default
18546     */
18547    type: 'Sepia',
18548
18549    /**
18550     * Applies filter to canvas element
18551     * @memberOf fabric.Image.filters.Sepia.prototype
18552     * @param {Object} canvasEl Canvas element to apply filter to
18553     */
18554    applyTo: function(canvasEl) {
18555      var context = canvasEl.getContext('2d'),
18556          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
18557          data = imageData.data,
18558          iLen = data.length, i, avg;
18559
18560      for (i = 0; i < iLen; i+=4) {
18561        avg = 0.3  * data[i] + 0.59 * data[i + 1] + 0.11 * data[i + 2];
18562        data[i] = avg + 100;
18563        data[i + 1] = avg + 50;
18564        data[i + 2] = avg + 255;
18565      }
18566
18567      context.putImageData(imageData, 0, 0);
18568    }
18569  });
18570
18571  /**
18572   * Returns filter instance from an object representation
18573   * @static
18574   * @return {fabric.Image.filters.Sepia} Instance of fabric.Image.filters.Sepia
18575   */
18576  fabric.Image.filters.Sepia.fromObject = function() {
18577    return new fabric.Image.filters.Sepia();
18578  };
18579
18580})(typeof exports !== 'undefined' ? exports : this);
18581
18582
18583(function(global) {
18584
18585  'use strict';
18586
18587  var fabric  = global.fabric || (global.fabric = { });
18588
18589  /**
18590   * Sepia2 filter class
18591   * @class fabric.Image.filters.Sepia2
18592   * @memberOf fabric.Image.filters
18593   * @extends fabric.Image.filters.BaseFilter
18594   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
18595   * @example
18596   * var filter = new fabric.Image.filters.Sepia2();
18597   * object.filters.push(filter);
18598   * object.applyFilters(canvas.renderAll.bind(canvas));
18599   */
18600  fabric.Image.filters.Sepia2 = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Sepia2.prototype */ {
18601
18602    /**
18603     * Filter type
18604     * @param {String} type
18605     * @default
18606     */
18607    type: 'Sepia2',
18608
18609    /**
18610     * Applies filter to canvas element
18611     * @memberOf fabric.Image.filters.Sepia.prototype
18612     * @param {Object} canvasEl Canvas element to apply filter to
18613     */
18614    applyTo: function(canvasEl) {
18615      var context = canvasEl.getContext('2d'),
18616          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
18617          data = imageData.data,
18618          iLen = data.length, i, r, g, b;
18619
18620      for (i = 0; i < iLen; i+=4) {
18621        r = data[i];
18622        g = data[i + 1];
18623        b = data[i + 2];
18624
18625        data[i] = (r * 0.393 + g * 0.769 + b * 0.189 ) / 1.351;
18626        data[i + 1] = (r * 0.349 + g * 0.686 + b * 0.168 ) / 1.203;
18627        data[i + 2] = (r * 0.272 + g * 0.534 + b * 0.131 ) / 2.140;
18628      }
18629
18630      context.putImageData(imageData, 0, 0);
18631    }
18632  });
18633
18634  /**
18635   * Returns filter instance from an object representation
18636   * @static
18637   * @return {fabric.Image.filters.Sepia2} Instance of fabric.Image.filters.Sepia2
18638   */
18639  fabric.Image.filters.Sepia2.fromObject = function() {
18640    return new fabric.Image.filters.Sepia2();
18641  };
18642
18643})(typeof exports !== 'undefined' ? exports : this);
18644
18645
18646(function(global) {
18647
18648  'use strict';
18649
18650  var fabric  = global.fabric || (global.fabric = { }),
18651      extend = fabric.util.object.extend;
18652
18653  /**
18654   * Tint filter class
18655   * Adapted from <a href="https://github.com/mezzoblue/PaintbrushJS">https://github.com/mezzoblue/PaintbrushJS</a>
18656   * @class fabric.Image.filters.Tint
18657   * @memberOf fabric.Image.filters
18658   * @extends fabric.Image.filters.BaseFilter
18659   * @see {@link fabric.Image.filters.Tint#initialize} for constructor definition
18660   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
18661   * @example <caption>Tint filter with hex color and opacity</caption>
18662   * var filter = new fabric.Image.filters.Tint({
18663   *   color: '#3513B0',
18664   *   opacity: 0.5
18665   * });
18666   * object.filters.push(filter);
18667   * object.applyFilters(canvas.renderAll.bind(canvas));
18668   * @example <caption>Tint filter with rgba color</caption>
18669   * var filter = new fabric.Image.filters.Tint({
18670   *   color: 'rgba(53, 21, 176, 0.5)'
18671   * });
18672   * object.filters.push(filter);
18673   * object.applyFilters(canvas.renderAll.bind(canvas));
18674   */
18675  fabric.Image.filters.Tint = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Tint.prototype */ {
18676
18677    /**
18678     * Filter type
18679     * @param {String} type
18680     * @default
18681     */
18682    type: 'Tint',
18683
18684    /**
18685     * Constructor
18686     * @memberOf fabric.Image.filters.Tint.prototype
18687     * @param {Object} [options] Options object
18688     * @param {String} [options.color=#000000] Color to tint the image with
18689     * @param {Number} [options.opacity] Opacity value that controls the tint effect's transparency (0..1)
18690     */
18691    initialize: function(options) {
18692      options = options || { };
18693
18694      this.color = options.color || '#000000';
18695      this.opacity = typeof options.opacity !== 'undefined'
18696                      ? options.opacity
18697                      : new fabric.Color(this.color).getAlpha();
18698    },
18699
18700    /**
18701     * Applies filter to canvas element
18702     * @param {Object} canvasEl Canvas element to apply filter to
18703     */
18704    applyTo: function(canvasEl) {
18705      var context = canvasEl.getContext('2d'),
18706          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
18707          data = imageData.data,
18708          iLen = data.length, i,
18709          tintR, tintG, tintB,
18710          r, g, b, alpha1,
18711          source;
18712
18713      source = new fabric.Color(this.color).getSource();
18714
18715      tintR = source[0] * this.opacity;
18716      tintG = source[1] * this.opacity;
18717      tintB = source[2] * this.opacity;
18718
18719      alpha1 = 1 - this.opacity;
18720
18721      for (i = 0; i < iLen; i+=4) {
18722        r = data[i];
18723        g = data[i + 1];
18724        b = data[i + 2];
18725
18726        // alpha compositing
18727        data[i] = tintR + r * alpha1;
18728        data[i + 1] = tintG + g * alpha1;
18729        data[i + 2] = tintB + b * alpha1;
18730      }
18731
18732      context.putImageData(imageData, 0, 0);
18733    },
18734
18735    /**
18736     * Returns object representation of an instance
18737     * @return {Object} Object representation of an instance
18738     */
18739    toObject: function() {
18740      return extend(this.callSuper('toObject'), {
18741        color: this.color,
18742        opacity: this.opacity
18743      });
18744    }
18745  });
18746
18747  /**
18748   * Returns filter instance from an object representation
18749   * @static
18750   * @param {Object} object Object to create an instance from
18751   * @return {fabric.Image.filters.Tint} Instance of fabric.Image.filters.Tint
18752   */
18753  fabric.Image.filters.Tint.fromObject = function(object) {
18754    return new fabric.Image.filters.Tint(object);
18755  };
18756
18757})(typeof exports !== 'undefined' ? exports : this);
18758
18759
18760(function(global) {
18761
18762  'use strict';
18763
18764  var fabric  = global.fabric || (global.fabric = { }),
18765      extend = fabric.util.object.extend;
18766
18767  /**
18768   * Multiply filter class
18769   * Adapted from <a href="http://www.laurenscorijn.com/articles/colormath-basics">http://www.laurenscorijn.com/articles/colormath-basics</a>
18770   * @class fabric.Image.filters.Multiply
18771   * @memberOf fabric.Image.filters
18772   * @extends fabric.Image.filters.BaseFilter
18773   * @example <caption>Multiply filter with hex color</caption>
18774   * var filter = new fabric.Image.filters.Multiply({
18775   *   color: '#F0F'
18776   * });
18777   * object.filters.push(filter);
18778   * object.applyFilters(canvas.renderAll.bind(canvas));
18779   * @example <caption>Multiply filter with rgb color</caption>
18780   * var filter = new fabric.Image.filters.Multiply({
18781   *   color: 'rgb(53, 21, 176)'
18782   * });
18783   * object.filters.push(filter);
18784   * object.applyFilters(canvas.renderAll.bind(canvas));
18785   */
18786  fabric.Image.filters.Multiply = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Multiply.prototype */ {
18787
18788    /**
18789     * Filter type
18790     * @param {String} type
18791     * @default
18792     */
18793    type: 'Multiply',
18794
18795    /**
18796     * Constructor
18797     * @memberOf fabric.Image.filters.Multiply.prototype
18798     * @param {Object} [options] Options object
18799     * @param {String} [options.color=#000000] Color to multiply the image pixels with
18800     */
18801    initialize: function(options) {
18802      options = options || { };
18803
18804      this.color = options.color || '#000000';
18805    },
18806
18807    /**
18808     * Applies filter to canvas element
18809     * @param {Object} canvasEl Canvas element to apply filter to
18810     */
18811    applyTo: function(canvasEl) {
18812      var context = canvasEl.getContext('2d'),
18813          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
18814          data = imageData.data,
18815          iLen = data.length, i,
18816          source;
18817
18818      source = new fabric.Color(this.color).getSource();
18819
18820      for (i = 0; i < iLen; i+=4) {
18821        data[i] *= source[0] / 255;
18822        data[i + 1] *= source[1] / 255;
18823        data[i + 2] *= source[2] / 255;
18824      }
18825
18826      context.putImageData(imageData, 0, 0);
18827    },
18828
18829    /**
18830     * Returns object representation of an instance
18831     * @return {Object} Object representation of an instance
18832     */
18833    toObject: function() {
18834      return extend(this.callSuper('toObject'), {
18835        color: this.color
18836      });
18837    }
18838  });
18839
18840  /**
18841   * Returns filter instance from an object representation
18842   * @static
18843   * @param {Object} object Object to create an instance from
18844   * @return {fabric.Image.filters.Multiply} Instance of fabric.Image.filters.Multiply
18845   */
18846  fabric.Image.filters.Multiply.fromObject = function(object) {
18847    return new fabric.Image.filters.Multiply(object);
18848  };
18849
18850})(typeof exports !== 'undefined' ? exports : this);
18851
18852
18853(function(global) {
18854  'use strict';
18855
18856  var fabric = global.fabric;
18857
18858  /**
18859   * Color Blend filter class
18860   * @class fabric.Image.filter.Blend
18861   * @memberOf fabric.Image.filters
18862   * @extends fabric.Image.filters.BaseFilter
18863   * @example
18864   * var filter = new fabric.Image.filters.Blend({
18865   *  color: '#000',
18866   *  mode: 'multiply'
18867   * });
18868   *
18869   * var filter = new fabric.Image.filters.Blend({
18870   *  image: fabricImageObject,
18871   *  mode: 'multiply',
18872   *  alpha: 0.5
18873   * });
18874
18875   * object.filters.push(filter);
18876   * object.applyFilters(canvas.renderAll.bind(canvas));
18877   */
18878  fabric.Image.filters.Blend = fabric.util.createClass({
18879    type: 'Blend',
18880
18881    initialize: function(options) {
18882      options = options || {};
18883      this.color = options.color || '#000';
18884      this.image = options.image || false;
18885      this.mode = options.mode || 'multiply';
18886      this.alpha = options.alpha || 1;
18887    },
18888
18889    applyTo: function(canvasEl) {
18890      var context = canvasEl.getContext('2d'),
18891          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
18892          data = imageData.data,
18893          tr, tg, tb,
18894          r, g, b,
18895          _r, _g, _b,
18896          source,
18897          isImage = false;
18898
18899      if (this.image) {
18900        // Blend images
18901        isImage = true;
18902
18903        var _el = fabric.util.createCanvasElement();
18904        _el.width = this.image.width;
18905        _el.height = this.image.height;
18906
18907        var tmpCanvas = new fabric.StaticCanvas(_el);
18908        tmpCanvas.add(this.image);
18909        var context2 =  tmpCanvas.getContext('2d');
18910        source = context2.getImageData(0, 0, tmpCanvas.width, tmpCanvas.height).data;
18911      }
18912      else {
18913        // Blend color
18914        source = new fabric.Color(this.color).getSource();
18915
18916        tr = source[0] * this.alpha;
18917        tg = source[1] * this.alpha;
18918        tb = source[2] * this.alpha;
18919      }
18920
18921      for (var i = 0, len = data.length; i < len; i += 4) {
18922
18923        r = data[i];
18924        g = data[i + 1];
18925        b = data[i + 2];
18926
18927        if (isImage) {
18928          tr = source[i] * this.alpha;
18929          tg = source[i + 1] * this.alpha;
18930          tb = source[i + 2] * this.alpha;
18931        }
18932
18933        switch (this.mode) {
18934          case 'multiply':
18935            data[i] = r * tr / 255;
18936            data[i + 1] = g * tg / 255;
18937            data[i + 2] = b * tb / 255;
18938            break;
18939          case 'screen':
18940            data[i] = 1 - (1 - r) * (1 - tr);
18941            data[i + 1] = 1 - (1 - g) * (1 - tg);
18942            data[i + 2] = 1 - (1 - b) * (1 - tb);
18943            break;
18944          case 'add':
18945            data[i] = Math.min(255, r + tr);
18946            data[i + 1] = Math.min(255, g + tg);
18947            data[i + 2] = Math.min(255, b + tb);
18948            break;
18949          case 'diff':
18950          case 'difference':
18951            data[i] = Math.abs(r - tr);
18952            data[i + 1] = Math.abs(g - tg);
18953            data[i + 2] = Math.abs(b - tb);
18954            break;
18955          case 'subtract':
18956            _r = r - tr;
18957            _g = g - tg;
18958            _b = b - tb;
18959
18960            data[i] = (_r < 0) ? 0 : _r;
18961            data[i + 1] = (_g < 0) ? 0 : _g;
18962            data[i + 2] = (_b < 0) ? 0 : _b;
18963            break;
18964          case 'darken':
18965            data[i] = Math.min(r, tr);
18966            data[i + 1] = Math.min(g, tg);
18967            data[i + 2] = Math.min(b, tb);
18968            break;
18969          case 'lighten':
18970            data[i] = Math.max(r, tr);
18971            data[i + 1] = Math.max(g, tg);
18972            data[i + 2] = Math.max(b, tb);
18973            break;
18974        }
18975      }
18976
18977      context.putImageData(imageData, 0, 0);
18978    },
18979
18980    /**
18981     * Returns object representation of an instance
18982     * @return {Object} Object representation of an instance
18983     */
18984    toObject: function() {
18985      return {
18986        color: this.color,
18987        image: this.image,
18988        mode: this.mode,
18989        alpha: this.alpha
18990      };
18991    }
18992  });
18993
18994  fabric.Image.filters.Blend.fromObject = function(object) {
18995    return new fabric.Image.filters.Blend(object);
18996  };
18997})(typeof exports !== 'undefined' ? exports : this);
18998
18999
19000(function(global) {
19001
19002  'use strict';
19003
19004  var fabric  = global.fabric || (global.fabric = { }), pow = Math.pow, floor = Math.floor,
19005      sqrt = Math.sqrt, abs = Math.abs, max = Math.max, round = Math.round, sin = Math.sin,
19006      ceil = Math.ceil;
19007
19008  /**
19009   * Resize image filter class
19010   * @class fabric.Image.filters.Resize
19011   * @memberOf fabric.Image.filters
19012   * @extends fabric.Image.filters.BaseFilter
19013   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
19014   * @example
19015   * var filter = new fabric.Image.filters.Resize();
19016   * object.filters.push(filter);
19017   * object.applyFilters(canvas.renderAll.bind(canvas));
19018   */
19019  fabric.Image.filters.Resize = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Resize.prototype */ {
19020
19021    /**
19022     * Filter type
19023     * @param {String} type
19024     * @default
19025     */
19026    type: 'Resize',
19027
19028    /**
19029     * Resize type
19030     * @param {String} resizeType
19031     * @default
19032     */
19033    resizeType: 'hermite',
19034
19035    /**
19036     * Scale factor for resizing, x axis
19037     * @param {Number} scaleX
19038     * @default
19039     */
19040    scaleX: 0,
19041
19042    /**
19043     * Scale factor for resizing, y axis
19044     * @param {Number} scaleY
19045     * @default
19046     */
19047    scaleY: 0,
19048
19049    /**
19050     * LanczosLobes parameter for lanczos filter
19051     * @param {Number} lanczosLobes
19052     * @default
19053     */
19054    lanczosLobes: 3,
19055
19056    /**
19057     * Applies filter to canvas element
19058     * @memberOf fabric.Image.filters.Resize.prototype
19059     * @param {Object} canvasEl Canvas element to apply filter to
19060     */
19061    applyTo: function(canvasEl, scaleX, scaleY) {
19062
19063      this.rcpScaleX = 1 / scaleX;
19064      this.rcpScaleY = 1 / scaleY;
19065
19066      var oW = canvasEl.width, oH = canvasEl.height,
19067          dW = round(oW * scaleX), dH = round(oH * scaleY),
19068          imageData;
19069
19070      if (this.resizeType === 'sliceHack') {
19071        imageData = this.sliceByTwo(canvasEl, oW, oH, dW, dH);
19072      }
19073      if (this.resizeType === 'hermite') {
19074        imageData = this.hermiteFastResize(canvasEl, oW, oH, dW, dH);
19075      }
19076      if (this.resizeType === 'bilinear') {
19077        imageData = this.bilinearFiltering(canvasEl, oW, oH, dW, dH);
19078      }
19079      if (this.resizeType === 'lanczos') {
19080        imageData = this.lanczosResize(canvasEl, oW, oH, dW, dH);
19081      }
19082      canvasEl.width = dW;
19083      canvasEl.height = dH;
19084      canvasEl.getContext('2d').putImageData(imageData, 0, 0);
19085    },
19086
19087    sliceByTwo: function(canvasEl, width, height, newWidth, newHeight) {
19088      var context = canvasEl.getContext('2d'), imageData,
19089          multW = 0.5, multH = 0.5, signW = 1, signH = 1,
19090          doneW = false, doneH = false, stepW = width, stepH = height,
19091          tmpCanvas = fabric.util.createCanvasElement(),
19092          tmpCtx = tmpCanvas.getContext('2d');
19093      newWidth = floor(newWidth);
19094      newHeight = floor(newHeight);
19095      tmpCanvas.width = max(newWidth, width);
19096      tmpCanvas.height = max(newHeight, height);
19097
19098      if (newWidth > width) {
19099        multW = 2;
19100        signW = -1;
19101      }
19102      if (newHeight > height) {
19103        multH = 2;
19104        signH = -1;
19105      }
19106      imageData = context.getImageData(0, 0, width, height);
19107      canvasEl.width = max(newWidth, width);
19108      canvasEl.height = max(newHeight, height);
19109      context.putImageData(imageData, 0, 0);
19110
19111      while (!doneW || !doneH) {
19112        width = stepW;
19113        height = stepH;
19114        if (newWidth * signW < floor(stepW * multW * signW)) {
19115          stepW = floor(stepW * multW);
19116        }
19117        else {
19118          stepW = newWidth;
19119          doneW = true;
19120        }
19121        if (newHeight * signH < floor(stepH * multH * signH)) {
19122          stepH = floor(stepH * multH);
19123        }
19124        else {
19125          stepH = newHeight;
19126          doneH = true;
19127        }
19128        imageData = context.getImageData(0, 0, width, height);
19129        tmpCtx.putImageData(imageData, 0, 0);
19130        context.clearRect(0, 0, stepW, stepH);
19131        context.drawImage(tmpCanvas, 0, 0, width, height, 0, 0, stepW, stepH);
19132      }
19133      return context.getImageData(0, 0, newWidth, newHeight);
19134    },
19135
19136    lanczosResize: function(canvasEl, oW, oH, dW, dH) {
19137
19138      function lanczosCreate(lobes) {
19139        return function(x) {
19140          if (x > lobes) {
19141            return 0;
19142          }
19143          x *= Math.PI;
19144          if (abs(x) < 1e-16) {
19145            return 1;
19146          }
19147          var xx = x / lobes;
19148          return sin(x) * sin(xx) / x / xx;
19149        };
19150      }
19151
19152      function process(u) {
19153        var v, i, weight, idx, a, red, green,
19154            blue, alpha, fX, fY;
19155        center.x = (u + 0.5) * ratioX;
19156        icenter.x = floor(center.x);
19157        for (v = 0; v < dH; v++) {
19158          center.y = (v + 0.5) * ratioY;
19159          icenter.y = floor(center.y);
19160          a = 0, red = 0, green = 0, blue = 0, alpha = 0;
19161          for (i = icenter.x - range2X; i <= icenter.x + range2X; i++) {
19162            if (i < 0 || i >= oW) {
19163              continue;
19164            }
19165            fX = floor(1000 * abs(i - center.x));
19166            if (!cacheLanc[fX]) {
19167              cacheLanc[fX] = { };
19168            }
19169            for (var j = icenter.y - range2Y; j <= icenter.y + range2Y; j++) {
19170              if (j < 0 || j >= oH) {
19171                continue;
19172              }
19173              fY = floor(1000 * abs(j - center.y));
19174              if (!cacheLanc[fX][fY]) {
19175                cacheLanc[fX][fY] = lanczos(sqrt(pow(fX * rcpRatioX, 2) + pow(fY * rcpRatioY, 2)) / 1000);
19176              }
19177              weight = cacheLanc[fX][fY];
19178              if (weight > 0) {
19179                idx = (j * oW + i) * 4;
19180                a += weight;
19181                red += weight * srcData[idx];
19182                green += weight * srcData[idx + 1];
19183                blue += weight * srcData[idx + 2];
19184                alpha += weight * srcData[idx + 3];
19185              }
19186            }
19187          }
19188          idx = (v * dW + u) * 4;
19189          destData[idx] = red / a;
19190          destData[idx + 1] = green / a;
19191          destData[idx + 2] = blue / a;
19192          destData[idx + 3] = alpha / a;
19193        }
19194
19195        if (++u < dW) {
19196          return process(u);
19197        }
19198        else {
19199          return destImg;
19200        }
19201      }
19202
19203      var context = canvasEl.getContext('2d'),
19204          srcImg = context.getImageData(0, 0, oW, oH),
19205          destImg = context.getImageData(0, 0, dW, dH),
19206          srcData = srcImg.data, destData = destImg.data,
19207          lanczos = lanczosCreate(this.lanczosLobes),
19208          ratioX = this.rcpScaleX, ratioY = this.rcpScaleY,
19209          rcpRatioX = 2 / this.rcpScaleX, rcpRatioY = 2 / this.rcpScaleY,
19210          range2X = ceil(ratioX * this.lanczosLobes / 2),
19211          range2Y = ceil(ratioY * this.lanczosLobes / 2),
19212          cacheLanc = { }, center = { }, icenter = { };
19213
19214      return process(0);
19215    },
19216
19217    bilinearFiltering: function(canvasEl, w, h, w2, h2) {
19218      var a, b, c, d, x, y, i, j, xDiff, yDiff, chnl,
19219          color, offset = 0, origPix, ratioX = this.rcpScaleX,
19220          ratioY = this.rcpScaleY, context = canvasEl.getContext('2d'),
19221          w4 = 4 * (w - 1), img = context.getImageData(0, 0, w, h),
19222          pixels = img.data, destImage = context.getImageData(0, 0, w2, h2),
19223          destPixels = destImage.data;
19224      for (i = 0; i < h2; i++) {
19225        for (j = 0; j < w2; j++) {
19226          x = floor(ratioX * j);
19227          y = floor(ratioY * i);
19228          xDiff = ratioX * j - x;
19229          yDiff = ratioY * i - y;
19230          origPix = 4 * (y * w + x);
19231
19232          for (chnl = 0; chnl < 4; chnl++) {
19233            a = pixels[origPix + chnl];
19234            b = pixels[origPix + 4 + chnl];
19235            c = pixels[origPix + w4 + chnl];
19236            d = pixels[origPix + w4 + 4 + chnl];
19237            color = a * (1 - xDiff) * (1 - yDiff) + b * xDiff * (1 - yDiff) +
19238                    c * yDiff * (1 - xDiff) + d * xDiff * yDiff;
19239            destPixels[offset++] = color;
19240          }
19241        }
19242      }
19243      return destImage;
19244    },
19245
19246    hermiteFastResize: function(canvasEl, oW, oH, dW, dH) {
19247      var ratioW = this.rcpScaleX, ratioH = this.rcpScaleY,
19248          ratioWHalf = ceil(ratioW / 2),
19249          ratioHHalf = ceil(ratioH / 2),
19250          context = canvasEl.getContext('2d'),
19251          img = context.getImageData(0, 0, oW, oH), data = img.data,
19252          img2 = context.getImageData(0, 0, dW, dH), data2 = img2.data;
19253      for (var j = 0; j < dH; j++) {
19254        for (var i = 0; i < dW; i++) {
19255          var x2 = (i + j * dW) * 4, weight = 0, weights = 0, weightsAlpha = 0,
19256              gxR = 0, gxG = 0, gxB = 0, gxA = 0, centerY = (j + 0.5) * ratioH;
19257          for (var yy = floor(j * ratioH); yy < (j + 1) * ratioH; yy++) {
19258            var dy = abs(centerY - (yy + 0.5)) / ratioHHalf,
19259                centerX = (i + 0.5) * ratioW, w0 = dy * dy;
19260            for (var xx = floor(i * ratioW); xx < (i + 1) * ratioW; xx++) {
19261              var dx = abs(centerX - (xx + 0.5)) / ratioWHalf,
19262                  w = sqrt(w0 + dx * dx);
19263              /*jshint maxdepth:5 */
19264              if (w > 1 && w < -1) {
19265                continue;
19266              }
19267              //hermite filter
19268              weight = 2 * w * w * w - 3 * w * w + 1;
19269              if (weight > 0) {
19270                dx = 4 * (xx + yy * oW);
19271                //alpha
19272                gxA += weight * data[dx + 3];
19273                weightsAlpha += weight;
19274                //colors
19275                /*jshint maxdepth:6 */
19276                if (data[dx + 3] < 255) {
19277                  weight = weight * data[dx + 3] / 250;
19278                }
19279                /*jshint maxdepth:5 */
19280                gxR += weight * data[dx];
19281                gxG += weight * data[dx + 1];
19282                gxB += weight * data[dx + 2];
19283                weights += weight;
19284              }
19285              /*jshint maxdepth:4 */
19286            }
19287          }
19288          data2[x2] = gxR / weights;
19289          data2[x2 + 1] = gxG / weights;
19290          data2[x2 + 2] = gxB / weights;
19291          data2[x2 + 3] = gxA / weightsAlpha;
19292        }
19293      }
19294      return img2;
19295    },
19296
19297    /**
19298     * Returns object representation of an instance
19299     * @return {Object} Object representation of an instance
19300     */
19301    toObject: function() {
19302      return {
19303        type: this.type,
19304        scaleX: this.scaleX,
19305        scaley: this.scaleY,
19306        resizeType: this.resizeType,
19307        lanczosLobes: this.lanczosLobes
19308      };
19309    }
19310  });
19311
19312  /**
19313   * Returns filter instance from an object representation
19314   * @static
19315   * @return {fabric.Image.filters.Resize} Instance of fabric.Image.filters.Resize
19316   */
19317  fabric.Image.filters.Resize.fromObject = function() {
19318    return new fabric.Image.filters.Resize();
19319  };
19320
19321})(typeof exports !== 'undefined' ? exports : this);
19322
19323
19324(function(global) {
19325
19326  'use strict';
19327
19328  var fabric = global.fabric || (global.fabric = { }),
19329      extend = fabric.util.object.extend,
19330      clone = fabric.util.object.clone,
19331      toFixed = fabric.util.toFixed,
19332      supportsLineDash = fabric.StaticCanvas.supports('setLineDash');
19333
19334  if (fabric.Text) {
19335    fabric.warn('fabric.Text is already defined');
19336    return;
19337  }
19338
19339  var stateProperties = fabric.Object.prototype.stateProperties.concat();
19340  stateProperties.push(
19341    'fontFamily',
19342    'fontWeight',
19343    'fontSize',
19344    'text',
19345    'textDecoration',
19346    'textAlign',
19347    'fontStyle',
19348    'lineHeight',
19349    'textBackgroundColor'
19350  );
19351
19352  /**
19353   * Text class
19354   * @class fabric.Text
19355   * @extends fabric.Object
19356   * @return {fabric.Text} thisArg
19357   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#text}
19358   * @see {@link fabric.Text#initialize} for constructor definition
19359   */
19360  fabric.Text = fabric.util.createClass(fabric.Object, /** @lends fabric.Text.prototype */ {
19361
19362    /**
19363     * Properties which when set cause object to change dimensions
19364     * @type Object
19365     * @private
19366     */
19367    _dimensionAffectingProps: {
19368      fontSize: true,
19369      fontWeight: true,
19370      fontFamily: true,
19371      fontStyle: true,
19372      lineHeight: true,
19373      stroke: true,
19374      strokeWidth: true,
19375      text: true,
19376      textAlign: true
19377    },
19378
19379    /**
19380     * @private
19381     */
19382    _reNewline: /\r?\n/,
19383
19384    /**
19385     * Retrieves object's fontSize
19386     * @method getFontSize
19387     * @memberOf fabric.Text.prototype
19388     * @return {String} Font size (in pixels)
19389     */
19390
19391    /**
19392     * Sets object's fontSize
19393     * @method setFontSize
19394     * @memberOf fabric.Text.prototype
19395     * @param {Number} fontSize Font size (in pixels)
19396     * @return {fabric.Text}
19397     * @chainable
19398     */
19399
19400    /**
19401     * Retrieves object's fontWeight
19402     * @method getFontWeight
19403     * @memberOf fabric.Text.prototype
19404     * @return {(String|Number)} Font weight
19405     */
19406
19407    /**
19408     * Sets object's fontWeight
19409     * @method setFontWeight
19410     * @memberOf fabric.Text.prototype
19411     * @param {(Number|String)} fontWeight Font weight
19412     * @return {fabric.Text}
19413     * @chainable
19414     */
19415
19416    /**
19417     * Retrieves object's fontFamily
19418     * @method getFontFamily
19419     * @memberOf fabric.Text.prototype
19420     * @return {String} Font family
19421     */
19422
19423    /**
19424     * Sets object's fontFamily
19425     * @method setFontFamily
19426     * @memberOf fabric.Text.prototype
19427     * @param {String} fontFamily Font family
19428     * @return {fabric.Text}
19429     * @chainable
19430     */
19431
19432    /**
19433     * Retrieves object's text
19434     * @method getText
19435     * @memberOf fabric.Text.prototype
19436     * @return {String} text
19437     */
19438
19439    /**
19440     * Sets object's text
19441     * @method setText
19442     * @memberOf fabric.Text.prototype
19443     * @param {String} text Text
19444     * @return {fabric.Text}
19445     * @chainable
19446     */
19447
19448    /**
19449     * Retrieves object's textDecoration
19450     * @method getTextDecoration
19451     * @memberOf fabric.Text.prototype
19452     * @return {String} Text decoration
19453     */
19454
19455    /**
19456     * Sets object's textDecoration
19457     * @method setTextDecoration
19458     * @memberOf fabric.Text.prototype
19459     * @param {String} textDecoration Text decoration
19460     * @return {fabric.Text}
19461     * @chainable
19462     */
19463
19464    /**
19465     * Retrieves object's fontStyle
19466     * @method getFontStyle
19467     * @memberOf fabric.Text.prototype
19468     * @return {String} Font style
19469     */
19470
19471    /**
19472     * Sets object's fontStyle
19473     * @method setFontStyle
19474     * @memberOf fabric.Text.prototype
19475     * @param {String} fontStyle Font style
19476     * @return {fabric.Text}
19477     * @chainable
19478     */
19479
19480    /**
19481     * Retrieves object's lineHeight
19482     * @method getLineHeight
19483     * @memberOf fabric.Text.prototype
19484     * @return {Number} Line height
19485     */
19486
19487    /**
19488     * Sets object's lineHeight
19489     * @method setLineHeight
19490     * @memberOf fabric.Text.prototype
19491     * @param {Number} lineHeight Line height
19492     * @return {fabric.Text}
19493     * @chainable
19494     */
19495
19496    /**
19497     * Retrieves object's textAlign
19498     * @method getTextAlign
19499     * @memberOf fabric.Text.prototype
19500     * @return {String} Text alignment
19501     */
19502
19503    /**
19504     * Sets object's textAlign
19505     * @method setTextAlign
19506     * @memberOf fabric.Text.prototype
19507     * @param {String} textAlign Text alignment
19508     * @return {fabric.Text}
19509     * @chainable
19510     */
19511
19512    /**
19513     * Retrieves object's textBackgroundColor
19514     * @method getTextBackgroundColor
19515     * @memberOf fabric.Text.prototype
19516     * @return {String} Text background color
19517     */
19518
19519    /**
19520     * Sets object's textBackgroundColor
19521     * @method setTextBackgroundColor
19522     * @memberOf fabric.Text.prototype
19523     * @param {String} textBackgroundColor Text background color
19524     * @return {fabric.Text}
19525     * @chainable
19526     */
19527
19528    /**
19529     * Type of an object
19530     * @type String
19531     * @default
19532     */
19533    type:                 'text',
19534
19535    /**
19536     * Font size (in pixels)
19537     * @type Number
19538     * @default
19539     */
19540    fontSize:             40,
19541
19542    /**
19543     * Font weight (e.g. bold, normal, 400, 600, 800)
19544     * @type {(Number|String)}
19545     * @default
19546     */
19547    fontWeight:           'normal',
19548
19549    /**
19550     * Font family
19551     * @type String
19552     * @default
19553     */
19554    fontFamily:           'Times New Roman',
19555
19556    /**
19557     * Text decoration Possible values: "", "underline", "overline" or "line-through".
19558     * @type String
19559     * @default
19560     */
19561    textDecoration:       '',
19562
19563    /**
19564     * Text alignment. Possible values: "left", "center", or "right".
19565     * @type String
19566     * @default
19567     */
19568    textAlign:            'left',
19569
19570    /**
19571     * Font style . Possible values: "", "normal", "italic" or "oblique".
19572     * @type String
19573     * @default
19574     */
19575    fontStyle:            '',
19576
19577    /**
19578     * Line height
19579     * @type Number
19580     * @default
19581     */
19582    lineHeight:           1.16,
19583
19584    /**
19585     * Background color of text lines
19586     * @type String
19587     * @default
19588     */
19589    textBackgroundColor:  '',
19590
19591    /**
19592     * List of properties to consider when checking if
19593     * state of an object is changed ({@link fabric.Object#hasStateChanged})
19594     * as well as for history (undo/redo) purposes
19595     * @type Array
19596     */
19597    stateProperties:      stateProperties,
19598
19599    /**
19600     * When defined, an object is rendered via stroke and this property specifies its color.
19601     * <b>Backwards incompatibility note:</b> This property was named "strokeStyle" until v1.1.6
19602     * @type String
19603     * @default
19604     */
19605    stroke:               null,
19606
19607    /**
19608     * Shadow object representing shadow of this shape.
19609     * <b>Backwards incompatibility note:</b> This property was named "textShadow" (String) until v1.2.11
19610     * @type fabric.Shadow
19611     * @default
19612     */
19613    shadow:               null,
19614
19615    /**
19616     * @private
19617     */
19618    _fontSizeFraction: 0.25,
19619
19620    /**
19621     * Text Line proportion to font Size (in pixels)
19622     * @type Number
19623     * @default
19624     */
19625    _fontSizeMult:             1.13,
19626
19627    /**
19628     * Constructor
19629     * @param {String} text Text string
19630     * @param {Object} [options] Options object
19631     * @return {fabric.Text} thisArg
19632     */
19633    initialize: function(text, options) {
19634      options = options || { };
19635      this.text = text;
19636      this.__skipDimension = true;
19637      this.setOptions(options);
19638      this.__skipDimension = false;
19639      this._initDimensions();
19640    },
19641
19642    /**
19643     * Renders text object on offscreen canvas, so that it would get dimensions
19644     * @private
19645     */
19646    _initDimensions: function(ctx) {
19647      if (this.__skipDimension) {
19648        return;
19649      }
19650      if (!ctx) {
19651        ctx = fabric.util.createCanvasElement().getContext('2d');
19652        this._setTextStyles(ctx);
19653      }
19654      this._textLines = this.text.split(this._reNewline);
19655      this._clearCache();
19656      var currentTextAlign = this.textAlign;
19657      this.textAlign = 'left';
19658      this.width = this._getTextWidth(ctx);
19659      this.textAlign = currentTextAlign;
19660      this.height = this._getTextHeight(ctx);
19661    },
19662
19663    /**
19664     * Returns string representation of an instance
19665     * @return {String} String representation of text object
19666     */
19667    toString: function() {
19668      return '#<fabric.Text (' + this.complexity() +
19669        '): { "text": "' + this.text + '", "fontFamily": "' + this.fontFamily + '" }>';
19670    },
19671
19672    /**
19673     * @private
19674     * @param {CanvasRenderingContext2D} ctx Context to render on
19675     */
19676    _render: function(ctx) {
19677
19678      this.clipTo && fabric.util.clipContext(this, ctx);
19679
19680      this._renderTextBackground(ctx);
19681      this._renderText(ctx);
19682
19683      this._renderTextDecoration(ctx);
19684      this.clipTo && ctx.restore();
19685    },
19686
19687    /**
19688     * @private
19689     * @param {CanvasRenderingContext2D} ctx Context to render on
19690     */
19691    _renderText: function(ctx) {
19692      ctx.save();
19693      this._translateForTextAlign(ctx);
19694      this._setOpacity(ctx);
19695      this._setShadow(ctx);
19696      this._setupCompositeOperation(ctx);
19697      this._renderTextFill(ctx);
19698      this._renderTextStroke(ctx);
19699      this._restoreCompositeOperation(ctx);
19700      this._removeShadow(ctx);
19701      ctx.restore();
19702    },
19703
19704    /**
19705     * @private
19706     * @param {CanvasRenderingContext2D} ctx Context to render on
19707     */
19708    _translateForTextAlign: function(ctx) {
19709      if (this.textAlign !== 'left' && this.textAlign !== 'justify') {
19710        ctx.translate(this.textAlign === 'center' ? (this.width / 2) : this.width, 0);
19711      }
19712    },
19713
19714    /**
19715     * @private
19716     * @param {CanvasRenderingContext2D} ctx Context to render on
19717     */
19718    _setTextStyles: function(ctx) {
19719      ctx.textBaseline = 'alphabetic';
19720      if (!this.skipTextAlign) {
19721        ctx.textAlign = this.textAlign;
19722      }
19723      ctx.font = this._getFontDeclaration();
19724    },
19725
19726    /**
19727     * @private
19728     * @param {CanvasRenderingContext2D} ctx Context to render on
19729     * @return {Number} Height of fabric.Text object
19730     */
19731    _getTextHeight: function() {
19732      return this._textLines.length * this._getHeightOfLine();
19733    },
19734
19735    /**
19736     * @private
19737     * @param {CanvasRenderingContext2D} ctx Context to render on
19738     * @return {Number} Maximum width of fabric.Text object
19739     */
19740    _getTextWidth: function(ctx) {
19741      var maxWidth = this._getLineWidth(ctx, 0);
19742
19743      for (var i = 1, len = this._textLines.length; i < len; i++) {
19744        var currentLineWidth = this._getLineWidth(ctx, i);
19745        if (currentLineWidth > maxWidth) {
19746          maxWidth = currentLineWidth;
19747        }
19748      }
19749      return maxWidth;
19750    },
19751
19752    /**
19753     * @private
19754     * @param {String} method Method name ("fillText" or "strokeText")
19755     * @param {CanvasRenderingContext2D} ctx Context to render on
19756     * @param {String} chars Chars to render
19757     * @param {Number} left Left position of text
19758     * @param {Number} top Top position of text
19759     */
19760    _renderChars: function(method, ctx, chars, left, top) {
19761      ctx[method](chars, left, top);
19762    },
19763
19764    /**
19765     * @private
19766     * @param {String} method Method name ("fillText" or "strokeText")
19767     * @param {CanvasRenderingContext2D} ctx Context to render on
19768     * @param {String} line Text to render
19769     * @param {Number} left Left position of text
19770     * @param {Number} top Top position of text
19771     * @param {Number} lineIndex Index of a line in a text
19772     */
19773    _renderTextLine: function(method, ctx, line, left, top, lineIndex) {
19774      // lift the line by quarter of fontSize
19775      top -= this.fontSize * this._fontSizeFraction;
19776
19777      // short-circuit
19778      if (this.textAlign !== 'justify') {
19779        this._renderChars(method, ctx, line, left, top, lineIndex);
19780        return;
19781      }
19782
19783      var lineWidth = this._getLineWidth(ctx, lineIndex),
19784          totalWidth = this.width;
19785      if (totalWidth >= lineWidth) {
19786        // stretch the line
19787        var words = line.split(/\s+/),
19788            wordsWidth = this._getWidthOfWords(ctx, line, lineIndex),
19789            widthDiff = totalWidth - wordsWidth,
19790            numSpaces = words.length - 1,
19791            spaceWidth = widthDiff / numSpaces,
19792            leftOffset = 0;
19793
19794        for (var i = 0, len = words.length; i < len; i++) {
19795          this._renderChars(method, ctx, words[i], left + leftOffset, top, lineIndex);
19796          leftOffset += ctx.measureText(words[i]).width + spaceWidth;
19797        }
19798      }
19799      else {
19800        this._renderChars(method, ctx, line, left, top, lineIndex);
19801      }
19802    },
19803
19804    /**
19805     * @private
19806     * @param {CanvasRenderingContext2D} ctx Context to render on
19807     * @param {Number} line
19808     */
19809    _getWidthOfWords: function (ctx, line) {
19810      return ctx.measureText(line.replace(/\s+/g, '')).width;
19811    },
19812
19813    /**
19814     * @private
19815     * @return {Number} Left offset
19816     */
19817    _getLeftOffset: function() {
19818      return -this.width / 2;
19819    },
19820
19821    /**
19822     * @private
19823     * @return {Number} Top offset
19824     */
19825    _getTopOffset: function() {
19826      return -this.height / 2;
19827    },
19828
19829    /**
19830     * @private
19831     * @param {CanvasRenderingContext2D} ctx Context to render on
19832     */
19833    _renderTextFill: function(ctx) {
19834      if (!this.fill && !this._skipFillStrokeCheck) {
19835        return;
19836      }
19837
19838      var lineHeights = 0;
19839
19840      for (var i = 0, len = this._textLines.length; i < len; i++) {
19841        var heightOfLine = this._getHeightOfLine(ctx, i),
19842            maxHeight = heightOfLine / this.lineHeight;
19843
19844        this._renderTextLine(
19845          'fillText',
19846          ctx,
19847          this._textLines[i],
19848          this._getLeftOffset(),
19849          this._getTopOffset() + lineHeights + maxHeight,
19850          i
19851        );
19852        lineHeights += heightOfLine;
19853      }
19854      if (this.shadow && !this.shadow.affectStroke) {
19855        this._removeShadow(ctx);
19856      }
19857    },
19858
19859    /**
19860     * @private
19861     * @param {CanvasRenderingContext2D} ctx Context to render on
19862     */
19863    _renderTextStroke: function(ctx) {
19864      if ((!this.stroke || this.strokeWidth === 0) && !this._skipFillStrokeCheck) {
19865        return;
19866      }
19867
19868      var lineHeights = 0;
19869
19870      ctx.save();
19871
19872      if (this.strokeDashArray) {
19873        // Spec requires the concatenation of two copies the dash list when the number of elements is odd
19874        if (1 & this.strokeDashArray.length) {
19875          this.strokeDashArray.push.apply(this.strokeDashArray, this.strokeDashArray);
19876        }
19877        supportsLineDash && ctx.setLineDash(this.strokeDashArray);
19878      }
19879
19880      ctx.beginPath();
19881      for (var i = 0, len = this._textLines.length; i < len; i++) {
19882        var heightOfLine = this._getHeightOfLine(ctx, i),
19883            maxHeight = heightOfLine / this.lineHeight;
19884
19885        this._renderTextLine(
19886          'strokeText',
19887          ctx,
19888          this._textLines[i],
19889          this._getLeftOffset(),
19890          this._getTopOffset() + lineHeights + maxHeight,
19891          i
19892        );
19893        lineHeights += heightOfLine;
19894      }
19895      ctx.closePath();
19896      ctx.restore();
19897    },
19898
19899    _getHeightOfLine: function() {
19900      return this.fontSize * this._fontSizeMult * this.lineHeight;
19901    },
19902
19903    /**
19904     * @private
19905     * @param {CanvasRenderingContext2D} ctx Context to render on
19906     * @param {Array} textLines Array of all text lines
19907     */
19908    _renderTextBackground: function(ctx) {
19909      this._renderTextBoxBackground(ctx);
19910      this._renderTextLinesBackground(ctx);
19911    },
19912
19913    /**
19914     * @private
19915     * @param {CanvasRenderingContext2D} ctx Context to render on
19916     */
19917    _renderTextBoxBackground: function(ctx) {
19918      if (!this.backgroundColor) {
19919        return;
19920      }
19921
19922      ctx.save();
19923      ctx.fillStyle = this.backgroundColor;
19924
19925      ctx.fillRect(
19926        this._getLeftOffset(),
19927        this._getTopOffset(),
19928        this.width,
19929        this.height
19930      );
19931
19932      ctx.restore();
19933    },
19934
19935    /**
19936     * @private
19937     * @param {CanvasRenderingContext2D} ctx Context to render on
19938     */
19939    _renderTextLinesBackground: function(ctx) {
19940      var lineTopOffset = 0, heightOfLine = this._getHeightOfLine();
19941      if (!this.textBackgroundColor) {
19942        return;
19943      }
19944
19945      ctx.save();
19946      ctx.fillStyle = this.textBackgroundColor;
19947
19948      for (var i = 0, len = this._textLines.length; i < len; i++) {
19949
19950        if (this._textLines[i] !== '') {
19951
19952          var lineWidth = this._getLineWidth(ctx, i),
19953              lineLeftOffset = this._getLineLeftOffset(lineWidth);
19954
19955          ctx.fillRect(
19956            this._getLeftOffset() + lineLeftOffset,
19957            this._getTopOffset() + lineTopOffset,
19958            lineWidth,
19959            this.fontSize * this._fontSizeMult
19960          );
19961        }
19962        lineTopOffset += heightOfLine;
19963      }
19964      ctx.restore();
19965    },
19966
19967    /**
19968     * @private
19969     * @param {Number} lineWidth Width of text line
19970     * @return {Number} Line left offset
19971     */
19972    _getLineLeftOffset: function(lineWidth) {
19973      if (this.textAlign === 'center') {
19974        return (this.width - lineWidth) / 2;
19975      }
19976      if (this.textAlign === 'right') {
19977        return this.width - lineWidth;
19978      }
19979      return 0;
19980    },
19981
19982    /**
19983     * @private
19984     */
19985    _clearCache: function() {
19986      this.__lineWidths = [ ];
19987      this.__lineHeights = [ ];
19988      this.__lineOffsets = [ ];
19989    },
19990
19991    /**
19992     * @private
19993     */
19994    _shouldClearCache: function() {
19995      var shouldClear = false;
19996      for (var prop in this._dimensionAffectingProps) {
19997        if (this['__' + prop] !== this[prop]) {
19998          this['__' + prop] = this[prop];
19999          shouldClear = true;
20000        }
20001      }
20002      return shouldClear;
20003    },
20004
20005    /**
20006     * @private
20007     * @param {CanvasRenderingContext2D} ctx Context to render on
20008     * @return {Number} Line width
20009     */
20010    _getLineWidth: function(ctx, lineIndex) {
20011      if (this.__lineWidths[lineIndex]) {
20012        return this.__lineWidths[lineIndex];
20013      }
20014      this.__lineWidths[lineIndex] = ctx.measureText(this._textLines[lineIndex]).width;
20015      return this.__lineWidths[lineIndex];
20016    },
20017
20018    /**
20019     * @private
20020     * @param {CanvasRenderingContext2D} ctx Context to render on
20021     */
20022    _renderTextDecoration: function(ctx) {
20023      if (!this.textDecoration) {
20024        return;
20025      }
20026
20027      var halfOfVerticalBox = this.height / 2,
20028          _this = this, offsets = [];
20029
20030      /** @ignore */
20031      function renderLinesAtOffset(offsets) {
20032        var i, lineHeight = 0, len, j, oLen;
20033        for (i = 0, len = _this._textLines.length; i < len; i++) {
20034
20035          var lineWidth = _this._getLineWidth(ctx, i),
20036              lineLeftOffset = _this._getLineLeftOffset(lineWidth),
20037              heightOfLine = _this._getHeightOfLine(ctx, i);
20038
20039          for (j = 0, oLen = offsets.length; j < oLen; j++) {
20040            ctx.fillRect(
20041              _this._getLeftOffset() + lineLeftOffset,
20042              lineHeight + (_this._fontSizeMult - 1 + offsets[j] ) * _this.fontSize - halfOfVerticalBox,
20043              lineWidth,
20044              _this.fontSize / 15);
20045          }
20046          lineHeight += heightOfLine;
20047        }
20048      }
20049
20050      if (this.textDecoration.indexOf('underline') > -1) {
20051        offsets.push(0.85); // 1 - 3/16
20052      }
20053      if (this.textDecoration.indexOf('line-through') > -1) {
20054        offsets.push(0.43);
20055      }
20056      if (this.textDecoration.indexOf('overline') > -1) {
20057        offsets.push(-0.12);
20058      }
20059
20060      if (offsets.length > 0) {
20061        renderLinesAtOffset(offsets);
20062      }
20063    },
20064
20065    /**
20066     * @private
20067     */
20068    _getFontDeclaration: function() {
20069      return [
20070        // node-canvas needs "weight style", while browsers need "style weight"
20071        (fabric.isLikelyNode ? this.fontWeight : this.fontStyle),
20072        (fabric.isLikelyNode ? this.fontStyle : this.fontWeight),
20073        this.fontSize + 'px',
20074        (fabric.isLikelyNode ? ('"' + this.fontFamily + '"') : this.fontFamily)
20075      ].join(' ');
20076    },
20077
20078    /**
20079     * Renders text instance on a specified context
20080     * @param {CanvasRenderingContext2D} ctx Context to render on
20081     */
20082    render: function(ctx, noTransform) {
20083      // do not render if object is not visible
20084      if (!this.visible) {
20085        return;
20086      }
20087
20088      ctx.save();
20089      this._setTextStyles(ctx);
20090
20091      if (this._shouldClearCache()) {
20092        this._initDimensions(ctx);
20093      }
20094      if (!noTransform) {
20095        this.transform(ctx);
20096      }
20097      this._setStrokeStyles(ctx);
20098      this._setFillStyles(ctx);
20099      if (this.transformMatrix) {
20100        ctx.transform.apply(ctx, this.transformMatrix);
20101      }
20102      if (this.group && this.group.type === 'path-group') {
20103        ctx.translate(this.left, this.top);
20104      }
20105      this._render(ctx);
20106      ctx.restore();
20107    },
20108
20109    /**
20110     * Returns object representation of an instance
20111     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
20112     * @return {Object} Object representation of an instance
20113     */
20114    toObject: function(propertiesToInclude) {
20115      var object = extend(this.callSuper('toObject', propertiesToInclude), {
20116        text:                 this.text,
20117        fontSize:             this.fontSize,
20118        fontWeight:           this.fontWeight,
20119        fontFamily:           this.fontFamily,
20120        fontStyle:            this.fontStyle,
20121        lineHeight:           this.lineHeight,
20122        textDecoration:       this.textDecoration,
20123        textAlign:            this.textAlign,
20124        textBackgroundColor:  this.textBackgroundColor
20125      });
20126      if (!this.includeDefaultValues) {
20127        this._removeDefaultValues(object);
20128      }
20129      return object;
20130    },
20131
20132    /* _TO_SVG_START_ */
20133    /**
20134     * Returns SVG representation of an instance
20135     * @param {Function} [reviver] Method for further parsing of svg representation.
20136     * @return {String} svg representation of an instance
20137     */
20138    toSVG: function(reviver) {
20139      var markup = this._createBaseSVGMarkup(),
20140          offsets = this._getSVGLeftTopOffsets(this.ctx),
20141          textAndBg = this._getSVGTextAndBg(offsets.textTop, offsets.textLeft);
20142      this._wrapSVGTextAndBg(markup, textAndBg);
20143
20144      return reviver ? reviver(markup.join('')) : markup.join('');
20145    },
20146
20147    /**
20148     * @private
20149     */
20150    _getSVGLeftTopOffsets: function(ctx) {
20151      var lineTop = this._getHeightOfLine(ctx, 0),
20152          textLeft = -this.width / 2,
20153          textTop = 0;
20154
20155      return {
20156        textLeft: textLeft + (this.group && this.group.type === 'path-group' ? this.left : 0),
20157        textTop: textTop + (this.group && this.group.type === 'path-group' ? -this.top : 0),
20158        lineTop: lineTop
20159      };
20160    },
20161
20162    /**
20163     * @private
20164     */
20165    _wrapSVGTextAndBg: function(markup, textAndBg) {
20166      markup.push(
20167        '\t<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
20168          textAndBg.textBgRects.join(''),
20169          '\t\t<text ',
20170            (this.fontFamily ? 'font-family="' + this.fontFamily.replace(/"/g, '\'') + '" ': ''),
20171            (this.fontSize ? 'font-size="' + this.fontSize + '" ': ''),
20172            (this.fontStyle ? 'font-style="' + this.fontStyle + '" ': ''),
20173            (this.fontWeight ? 'font-weight="' + this.fontWeight + '" ': ''),
20174            (this.textDecoration ? 'text-decoration="' + this.textDecoration + '" ': ''),
20175            'style="', this.getSvgStyles(), '" >',
20176            textAndBg.textSpans.join(''),
20177          '</text>\n',
20178        '\t</g>\n'
20179      );
20180    },
20181
20182    /**
20183     * @private
20184     * @param {Number} textTopOffset Text top offset
20185     * @param {Number} textLeftOffset Text left offset
20186     * @return {Object}
20187     */
20188    _getSVGTextAndBg: function(textTopOffset, textLeftOffset) {
20189      var textSpans = [ ],
20190          textBgRects = [ ],
20191          height = 0;
20192      // bounding-box background
20193      this._setSVGBg(textBgRects);
20194
20195      // text and text-background
20196      for (var i = 0, len = this._textLines.length; i < len; i++) {
20197        if (this.textBackgroundColor) {
20198          this._setSVGTextLineBg(textBgRects, i, textLeftOffset, textTopOffset, height);
20199        }
20200        this._setSVGTextLineText(i, textSpans, height, textLeftOffset, textTopOffset, textBgRects);
20201        height += this._getHeightOfLine(this.ctx, i);
20202      }
20203
20204      return {
20205        textSpans: textSpans,
20206        textBgRects: textBgRects
20207      };
20208    },
20209
20210    _setSVGTextLineText: function(i, textSpans, height, textLeftOffset, textTopOffset) {
20211      var yPos = this.fontSize * (this._fontSizeMult - this._fontSizeFraction)
20212        - textTopOffset + height - this.height / 2;
20213      textSpans.push(
20214        '<tspan x="',
20215          toFixed(textLeftOffset + this._getLineLeftOffset(this.__lineWidths[i]), 4), '" ',
20216          'y="',
20217          toFixed(yPos, 4),
20218          '" ',
20219          // doing this on <tspan> elements since setting opacity
20220          // on containing <text> one doesn't work in Illustrator
20221          this._getFillAttributes(this.fill), '>',
20222          fabric.util.string.escapeXml(this._textLines[i]),
20223        '</tspan>'
20224      );
20225    },
20226
20227    _setSVGTextLineBg: function(textBgRects, i, textLeftOffset, textTopOffset, height) {
20228      textBgRects.push(
20229        '\t\t<rect ',
20230          this._getFillAttributes(this.textBackgroundColor),
20231          ' x="',
20232          toFixed(textLeftOffset + this._getLineLeftOffset(this.__lineWidths[i]), 4),
20233          '" y="',
20234          toFixed(height - this.height / 2, 4),
20235          '" width="',
20236          toFixed(this.__lineWidths[i], 4),
20237          '" height="',
20238          toFixed(this._getHeightOfLine(this.ctx, i) / this.lineHeight, 4),
20239        '"></rect>\n');
20240    },
20241
20242    _setSVGBg: function(textBgRects) {
20243      if (this.backgroundColor) {
20244        textBgRects.push(
20245          '\t\t<rect ',
20246            this._getFillAttributes(this.backgroundColor),
20247            ' x="',
20248            toFixed(-this.width / 2, 4),
20249            '" y="',
20250            toFixed(-this.height / 2, 4),
20251            '" width="',
20252            toFixed(this.width, 4),
20253            '" height="',
20254            toFixed(this.height, 4),
20255          '"></rect>\n');
20256      }
20257    },
20258
20259    /**
20260     * Adobe Illustrator (at least CS5) is unable to render rgba()-based fill values
20261     * we work around it by "moving" alpha channel into opacity attribute and setting fill's alpha to 1
20262     *
20263     * @private
20264     * @param {Any} value
20265     * @return {String}
20266     */
20267    _getFillAttributes: function(value) {
20268      var fillColor = (value && typeof value === 'string') ? new fabric.Color(value) : '';
20269      if (!fillColor || !fillColor.getSource() || fillColor.getAlpha() === 1) {
20270        return 'fill="' + value + '"';
20271      }
20272      return 'opacity="' + fillColor.getAlpha() + '" fill="' + fillColor.setAlpha(1).toRgb() + '"';
20273    },
20274    /* _TO_SVG_END_ */
20275
20276    /**
20277     * Sets specified property to a specified value
20278     * @param {String} key
20279     * @param {Any} value
20280     * @return {fabric.Text} thisArg
20281     * @chainable
20282     */
20283    _set: function(key, value) {
20284      this.callSuper('_set', key, value);
20285
20286      if (key in this._dimensionAffectingProps) {
20287        this._initDimensions();
20288        this.setCoords();
20289      }
20290    },
20291
20292    /**
20293     * Returns complexity of an instance
20294     * @return {Number} complexity
20295     */
20296    complexity: function() {
20297      return 1;
20298    }
20299  });
20300
20301  /* _FROM_SVG_START_ */
20302  /**
20303   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Text.fromElement})
20304   * @static
20305   * @memberOf fabric.Text
20306   * @see: http://www.w3.org/TR/SVG/text.html#TextElement
20307   */
20308  fabric.Text.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(
20309    'x y dx dy font-family font-style font-weight font-size text-decoration text-anchor'.split(' '));
20310
20311  /**
20312   * Default SVG font size
20313   * @static
20314   * @memberOf fabric.Text
20315   */
20316  fabric.Text.DEFAULT_SVG_FONT_SIZE = 16;
20317
20318  /**
20319   * Returns fabric.Text instance from an SVG element (<b>not yet implemented</b>)
20320   * @static
20321   * @memberOf fabric.Text
20322   * @param {SVGElement} element Element to parse
20323   * @param {Object} [options] Options object
20324   * @return {fabric.Text} Instance of fabric.Text
20325   */
20326  fabric.Text.fromElement = function(element, options) {
20327    if (!element) {
20328      return null;
20329    }
20330
20331    var parsedAttributes = fabric.parseAttributes(element, fabric.Text.ATTRIBUTE_NAMES);
20332    options = fabric.util.object.extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes);
20333
20334    options.top = options.top || 0;
20335    options.left = options.left || 0;
20336    if ('dx' in parsedAttributes) {
20337      options.left += parsedAttributes.dx;
20338    }
20339    if ('dy' in parsedAttributes) {
20340      options.top += parsedAttributes.dy;
20341    }
20342    if (!('fontSize' in options)) {
20343      options.fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
20344    }
20345
20346    if (!options.originX) {
20347      options.originX = 'left';
20348    }
20349    var textContent = element.textContent.replace(/^\s+|\s+$|\n+/g, '').replace(/\s+/g, ' '),
20350        text = new fabric.Text(textContent, options),
20351        /*
20352          Adjust positioning:
20353            x/y attributes in SVG correspond to the bottom-left corner of text bounding box
20354            top/left properties in Fabric correspond to center point of text bounding box
20355        */
20356        offX = 0;
20357
20358    if (text.originX === 'left') {
20359      offX = text.getWidth() / 2;
20360    }
20361    if (text.originX === 'right') {
20362      offX = -text.getWidth() / 2;
20363    }
20364    text.set({
20365      left: text.getLeft() + offX,
20366      top: text.getTop() - text.getHeight() / 2 + text.fontSize * (0.18 + text._fontSizeFraction) /* 0.3 is the old lineHeight */
20367    });
20368
20369    return text;
20370  };
20371  /* _FROM_SVG_END_ */
20372
20373  /**
20374   * Returns fabric.Text instance from an object representation
20375   * @static
20376   * @memberOf fabric.Text
20377   * @param {Object} object Object to create an instance from
20378   * @return {fabric.Text} Instance of fabric.Text
20379   */
20380  fabric.Text.fromObject = function(object) {
20381    return new fabric.Text(object.text, clone(object));
20382  };
20383
20384  fabric.util.createAccessors(fabric.Text);
20385
20386})(typeof exports !== 'undefined' ? exports : this);
20387
20388
20389(function() {
20390
20391  var clone = fabric.util.object.clone;
20392
20393  /**
20394   * IText class (introduced in <b>v1.4</b>) Events are also fired with "text:"
20395   * prefix when observing canvas.
20396   * @class fabric.IText
20397   * @extends fabric.Text
20398   * @mixes fabric.Observable
20399   *
20400   * @fires changed
20401   * @fires selection:changed
20402   * @fires editing:entered
20403   * @fires editing:exited
20404   *
20405   * @return {fabric.IText} thisArg
20406   * @see {@link fabric.IText#initialize} for constructor definition
20407   *
20408   * <p>Supported key combinations:</p>
20409   * <pre>
20410   *   Move cursor:                    left, right, up, down
20411   *   Select character:               shift + left, shift + right
20412   *   Select text vertically:         shift + up, shift + down
20413   *   Move cursor by word:            alt + left, alt + right
20414   *   Select words:                   shift + alt + left, shift + alt + right
20415   *   Move cursor to line start/end:  cmd + left, cmd + right or home, end
20416   *   Select till start/end of line:  cmd + shift + left, cmd + shift + right or shift + home, shift + end
20417   *   Jump to start/end of text:      cmd + up, cmd + down
20418   *   Select till start/end of text:  cmd + shift + up, cmd + shift + down or shift + pgUp, shift + pgDown
20419   *   Delete character:               backspace
20420   *   Delete word:                    alt + backspace
20421   *   Delete line:                    cmd + backspace
20422   *   Forward delete:                 delete
20423   *   Copy text:                      ctrl/cmd + c
20424   *   Paste text:                     ctrl/cmd + v
20425   *   Cut text:                       ctrl/cmd + x
20426   *   Select entire text:             ctrl/cmd + a
20427   *   Quit editing                    tab or esc
20428   * </pre>
20429   *
20430   * <p>Supported mouse/touch combination</p>
20431   * <pre>
20432   *   Position cursor:                click/touch
20433   *   Create selection:               click/touch & drag
20434   *   Create selection:               click & shift + click
20435   *   Select word:                    double click
20436   *   Select line:                    triple click
20437   * </pre>
20438   */
20439  fabric.IText = fabric.util.createClass(fabric.Text, fabric.Observable, /** @lends fabric.IText.prototype */ {
20440
20441    /**
20442     * Type of an object
20443     * @type String
20444     * @default
20445     */
20446    type: 'i-text',
20447
20448    /**
20449     * Index where text selection starts (or where cursor is when there is no selection)
20450     * @type Nubmer
20451     * @default
20452     */
20453    selectionStart: 0,
20454
20455    /**
20456     * Index where text selection ends
20457     * @type Nubmer
20458     * @default
20459     */
20460    selectionEnd: 0,
20461
20462    /**
20463     * Color of text selection
20464     * @type String
20465     * @default
20466     */
20467    selectionColor: 'rgba(17,119,255,0.3)',
20468
20469    /**
20470     * Indicates whether text is in editing mode
20471     * @type Boolean
20472     * @default
20473     */
20474    isEditing: false,
20475
20476    /**
20477     * Indicates whether a text can be edited
20478     * @type Boolean
20479     * @default
20480     */
20481    editable: true,
20482
20483    /**
20484     * Border color of text object while it's in editing mode
20485     * @type String
20486     * @default
20487     */
20488    editingBorderColor: 'rgba(102,153,255,0.25)',
20489
20490    /**
20491     * Width of cursor (in px)
20492     * @type Number
20493     * @default
20494     */
20495    cursorWidth: 2,
20496
20497    /**
20498     * Color of default cursor (when not overwritten by character style)
20499     * @type String
20500     * @default
20501     */
20502    cursorColor: '#333',
20503
20504    /**
20505     * Delay between cursor blink (in ms)
20506     * @type Number
20507     * @default
20508     */
20509    cursorDelay: 1000,
20510
20511    /**
20512     * Duration of cursor fadein (in ms)
20513     * @type Number
20514     * @default
20515     */
20516    cursorDuration: 600,
20517
20518    /**
20519     * Object containing character styles
20520     * (where top-level properties corresponds to line number and 2nd-level properties -- to char number in a line)
20521     * @type Object
20522     * @default
20523     */
20524    styles: null,
20525
20526    /**
20527     * Indicates whether internal text char widths can be cached
20528     * @type Boolean
20529     * @default
20530     */
20531    caching: true,
20532
20533    /**
20534     * @private
20535     * @type Boolean
20536     * @default
20537     */
20538    _skipFillStrokeCheck: false,
20539
20540    /**
20541     * @private
20542     */
20543    _reSpace: /\s|\n/,
20544
20545    /**
20546     * @private
20547     */
20548    _currentCursorOpacity: 0,
20549
20550    /**
20551     * @private
20552     */
20553    _selectionDirection: null,
20554
20555    /**
20556     * @private
20557     */
20558    _abortCursorAnimation: false,
20559
20560    /**
20561     * @private
20562     */
20563    _charWidthsCache: { },
20564
20565    /**
20566     * Constructor
20567     * @param {String} text Text string
20568     * @param {Object} [options] Options object
20569     * @return {fabric.IText} thisArg
20570     */
20571    initialize: function(text, options) {
20572      this.styles = options ? (options.styles || { }) : { };
20573      this.callSuper('initialize', text, options);
20574      this.initBehavior();
20575    },
20576
20577    /**
20578     * @private
20579     */
20580    _clearCache: function() {
20581      this.callSuper('_clearCache');
20582      this.__maxFontHeights = [ ];
20583      this.__widthOfSpace = [ ];
20584    },
20585
20586    /**
20587     * Returns true if object has no styling
20588     */
20589    isEmptyStyles: function() {
20590      if (!this.styles) {
20591        return true;
20592      }
20593      var obj = this.styles;
20594
20595      for (var p1 in obj) {
20596        for (var p2 in obj[p1]) {
20597          /*jshint unused:false */
20598          for (var p3 in obj[p1][p2]) {
20599            return false;
20600          }
20601        }
20602      }
20603      return true;
20604    },
20605
20606    /**
20607     * Sets selection start (left boundary of a selection)
20608     * @param {Number} index Index to set selection start to
20609     */
20610    setSelectionStart: function(index) {
20611      index = Math.max(index, 0);
20612      if (this.selectionStart !== index) {
20613        this.fire('selection:changed');
20614        this.canvas && this.canvas.fire('text:selection:changed', { target: this });
20615        this.selectionStart = index;
20616      }
20617      this._updateTextarea();
20618    },
20619
20620    /**
20621     * Sets selection end (right boundary of a selection)
20622     * @param {Number} index Index to set selection end to
20623     */
20624    setSelectionEnd: function(index) {
20625      index = Math.min(index, this.text.length);
20626      if (this.selectionEnd !== index) {
20627        this.fire('selection:changed');
20628        this.canvas && this.canvas.fire('text:selection:changed', { target: this });
20629        this.selectionEnd = index;
20630      }
20631      this._updateTextarea();
20632    },
20633
20634    /**
20635     * Gets style of a current selection/cursor (at the start position)
20636     * @param {Number} [startIndex] Start index to get styles at
20637     * @param {Number} [endIndex] End index to get styles at
20638     * @return {Object} styles Style object at a specified (or current) index
20639     */
20640    getSelectionStyles: function(startIndex, endIndex) {
20641
20642      if (arguments.length === 2) {
20643        var styles = [ ];
20644        for (var i = startIndex; i < endIndex; i++) {
20645          styles.push(this.getSelectionStyles(i));
20646        }
20647        return styles;
20648      }
20649
20650      var loc = this.get2DCursorLocation(startIndex);
20651      if (this.styles[loc.lineIndex]) {
20652        return this.styles[loc.lineIndex][loc.charIndex] || { };
20653      }
20654
20655      return { };
20656    },
20657
20658    /**
20659     * Sets style of a current selection
20660     * @param {Object} [styles] Styles object
20661     * @return {fabric.IText} thisArg
20662     * @chainable
20663     */
20664    setSelectionStyles: function(styles) {
20665      if (this.selectionStart === this.selectionEnd) {
20666        this._extendStyles(this.selectionStart, styles);
20667      }
20668      else {
20669        for (var i = this.selectionStart; i < this.selectionEnd; i++) {
20670          this._extendStyles(i, styles);
20671        }
20672      }
20673      /* not included in _extendStyles to avoid clearing cache more than once */
20674      this._clearCache();
20675      return this;
20676    },
20677
20678    /**
20679     * @private
20680     */
20681    _extendStyles: function(index, styles) {
20682      var loc = this.get2DCursorLocation(index);
20683
20684      if (!this.styles[loc.lineIndex]) {
20685        this.styles[loc.lineIndex] = { };
20686      }
20687      if (!this.styles[loc.lineIndex][loc.charIndex]) {
20688        this.styles[loc.lineIndex][loc.charIndex] = { };
20689      }
20690      fabric.util.object.extend(this.styles[loc.lineIndex][loc.charIndex], styles);
20691    },
20692
20693    /**
20694    * @private
20695    * @param {CanvasRenderingContext2D} ctx Context to render on
20696    */
20697    _render: function(ctx) {
20698      this.callSuper('_render', ctx);
20699      this.ctx = ctx;
20700      this.isEditing && this.renderCursorOrSelection();
20701    },
20702
20703    /**
20704     * Renders cursor or selection (depending on what exists)
20705     */
20706    renderCursorOrSelection: function() {
20707      if (!this.active) {
20708        return;
20709      }
20710
20711      var chars = this.text.split(''),
20712          boundaries, ctx;
20713
20714      if (this.canvas.contextTop) {
20715        ctx = this.canvas.contextTop;
20716        ctx.save();
20717        ctx.transform.apply(ctx, this.canvas.viewportTransform);
20718        this.transform(ctx);
20719      }
20720      else {
20721        ctx = this.ctx;
20722        ctx.save();
20723      }
20724
20725      if (this.selectionStart === this.selectionEnd) {
20726        boundaries = this._getCursorBoundaries(chars, 'cursor');
20727        this.renderCursor(boundaries, ctx);
20728      }
20729      else {
20730        boundaries = this._getCursorBoundaries(chars, 'selection');
20731        this.renderSelection(chars, boundaries, ctx);
20732      }
20733
20734      ctx.restore();
20735    },
20736
20737    /**
20738     * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start)
20739     * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used.
20740     */
20741    get2DCursorLocation: function(selectionStart) {
20742      if (typeof selectionStart === 'undefined') {
20743        selectionStart = this.selectionStart;
20744      }
20745      var textBeforeCursor = this.text.slice(0, selectionStart),
20746          linesBeforeCursor = textBeforeCursor.split(this._reNewline);
20747
20748      return {
20749        lineIndex: linesBeforeCursor.length - 1,
20750        charIndex: linesBeforeCursor[linesBeforeCursor.length - 1].length
20751      };
20752    },
20753
20754    /**
20755     * Returns complete style of char at the current cursor
20756     * @param {Number} lineIndex Line index
20757     * @param {Number} charIndex Char index
20758    * @return {Object} Character style
20759     */
20760    getCurrentCharStyle: function(lineIndex, charIndex) {
20761      var style = this.styles[lineIndex] && this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)];
20762
20763      return {
20764        fontSize: style && style.fontSize || this.fontSize,
20765        fill: style && style.fill || this.fill,
20766        textBackgroundColor: style && style.textBackgroundColor || this.textBackgroundColor,
20767        textDecoration: style && style.textDecoration || this.textDecoration,
20768        fontFamily: style && style.fontFamily || this.fontFamily,
20769        fontWeight: style && style.fontWeight || this.fontWeight,
20770        fontStyle: style && style.fontStyle || this.fontStyle,
20771        stroke: style && style.stroke || this.stroke,
20772        strokeWidth: style && style.strokeWidth || this.strokeWidth
20773      };
20774    },
20775
20776    /**
20777     * Returns fontSize of char at the current cursor
20778     * @param {Number} lineIndex Line index
20779     * @param {Number} charIndex Char index
20780     * @return {Number} Character font size
20781     */
20782    getCurrentCharFontSize: function(lineIndex, charIndex) {
20783      return (
20784        this.styles[lineIndex] &&
20785        this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)] &&
20786        this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)].fontSize) || this.fontSize;
20787    },
20788
20789    /**
20790     * Returns color (fill) of char at the current cursor
20791     * @param {Number} lineIndex Line index
20792     * @param {Number} charIndex Char index
20793     * @return {String} Character color (fill)
20794     */
20795    getCurrentCharColor: function(lineIndex, charIndex) {
20796      return (
20797        this.styles[lineIndex] &&
20798        this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)] &&
20799        this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)].fill) || this.cursorColor;
20800    },
20801
20802    /**
20803     * Returns cursor boundaries (left, top, leftOffset, topOffset)
20804     * @private
20805     * @param {Array} chars Array of characters
20806     * @param {String} typeOfBoundaries
20807     */
20808    _getCursorBoundaries: function(chars, typeOfBoundaries) {
20809
20810      // left/top are left/top of entire text box
20811      // leftOffset/topOffset are offset from that left/top point of a text box
20812
20813      var left = Math.round(this._getLeftOffset()),
20814          top = this._getTopOffset(),
20815
20816          offsets = this._getCursorBoundariesOffsets(
20817                      chars, typeOfBoundaries);
20818
20819      return {
20820        left: left,
20821        top: top,
20822        leftOffset: offsets.left + offsets.lineLeft,
20823        topOffset: offsets.top
20824      };
20825    },
20826
20827    /**
20828     * @private
20829     */
20830    _getCursorBoundariesOffsets: function(chars, typeOfBoundaries) {
20831
20832      var lineLeftOffset = 0,
20833
20834          lineIndex = 0,
20835          charIndex = 0,
20836          topOffset = 0,
20837          leftOffset = 0;
20838
20839      for (var i = 0; i < this.selectionStart; i++) {
20840        if (chars[i] === '\n') {
20841          leftOffset = 0;
20842          topOffset += this._getHeightOfLine(this.ctx, lineIndex);
20843
20844          lineIndex++;
20845          charIndex = 0;
20846        }
20847        else {
20848          leftOffset += this._getWidthOfChar(this.ctx, chars[i], lineIndex, charIndex);
20849          charIndex++;
20850        }
20851
20852        lineLeftOffset = this._getCachedLineOffset(lineIndex);
20853      }
20854      if (typeOfBoundaries === 'cursor') {
20855        topOffset += (1 - this._fontSizeFraction) * this._getHeightOfLine(this.ctx, lineIndex) / this.lineHeight
20856          - this.getCurrentCharFontSize(lineIndex, charIndex) * (1 - this._fontSizeFraction);
20857      }
20858
20859      return {
20860        top: topOffset,
20861        left: leftOffset,
20862        lineLeft: lineLeftOffset
20863      };
20864    },
20865
20866    /**
20867     * @private
20868     */
20869    _getCachedLineOffset: function(lineIndex) {
20870      var widthOfLine = this._getLineWidth(this.ctx, lineIndex);
20871
20872      return this.__lineOffsets[lineIndex] ||
20873        (this.__lineOffsets[lineIndex] = this._getLineLeftOffset(widthOfLine));
20874    },
20875
20876    /**
20877     * Renders cursor
20878     * @param {Object} boundaries
20879     * @param {CanvasRenderingContext2D} ctx transformed context to draw on
20880     */
20881    renderCursor: function(boundaries, ctx) {
20882
20883      var cursorLocation = this.get2DCursorLocation(),
20884          lineIndex = cursorLocation.lineIndex,
20885          charIndex = cursorLocation.charIndex,
20886          charHeight = this.getCurrentCharFontSize(lineIndex, charIndex),
20887          leftOffset = (lineIndex === 0 && charIndex === 0)
20888                    ? this._getCachedLineOffset(lineIndex)
20889                    : boundaries.leftOffset;
20890
20891      ctx.fillStyle = this.getCurrentCharColor(lineIndex, charIndex);
20892      ctx.globalAlpha = this.__isMousedown ? 1 : this._currentCursorOpacity;
20893
20894      ctx.fillRect(
20895        boundaries.left + leftOffset,
20896        boundaries.top + boundaries.topOffset,
20897        this.cursorWidth / this.scaleX,
20898        charHeight);
20899
20900    },
20901
20902    /**
20903     * Renders text selection
20904     * @param {Array} chars Array of characters
20905     * @param {Object} boundaries Object with left/top/leftOffset/topOffset
20906     * @param {CanvasRenderingContext2D} ctx transformed context to draw on
20907     */
20908    renderSelection: function(chars, boundaries, ctx) {
20909
20910      ctx.fillStyle = this.selectionColor;
20911
20912      var start = this.get2DCursorLocation(this.selectionStart),
20913          end = this.get2DCursorLocation(this.selectionEnd),
20914          startLine = start.lineIndex,
20915          endLine = end.lineIndex;
20916
20917      for (var i = startLine; i <= endLine; i++) {
20918        var lineOffset = this._getCachedLineOffset(i) || 0,
20919            lineHeight = this._getHeightOfLine(this.ctx, i),
20920            boxWidth = 0, line = this._textLines[i];
20921
20922        if (i === startLine) {
20923          for (var j = 0, len = line.length; j < len; j++) {
20924            if (j >= start.charIndex && (i !== endLine || j < end.charIndex)) {
20925              boxWidth += this._getWidthOfChar(ctx, line[j], i, j);
20926            }
20927            if (j < start.charIndex) {
20928              lineOffset += this._getWidthOfChar(ctx, line[j], i, j);
20929            }
20930          }
20931        }
20932        else if (i > startLine && i < endLine) {
20933          boxWidth += this._getLineWidth(ctx, i) || 5;
20934        }
20935        else if (i === endLine) {
20936          for (var j2 = 0, j2len = end.charIndex; j2 < j2len; j2++) {
20937            boxWidth += this._getWidthOfChar(ctx, line[j2], i, j2);
20938          }
20939        }
20940
20941        ctx.fillRect(
20942          boundaries.left + lineOffset,
20943          boundaries.top + boundaries.topOffset,
20944          boxWidth,
20945          lineHeight);
20946
20947        boundaries.topOffset += lineHeight;
20948      }
20949    },
20950
20951    /**
20952     * @private
20953     * @param {String} method
20954     * @param {CanvasRenderingContext2D} ctx Context to render on
20955     */
20956    _renderChars: function(method, ctx, line, left, top, lineIndex) {
20957
20958      if (this.isEmptyStyles()) {
20959        return this._renderCharsFast(method, ctx, line, left, top);
20960      }
20961
20962      this.skipTextAlign = true;
20963
20964      // set proper box offset
20965      left -= this.textAlign === 'center'
20966        ? (this.width / 2)
20967        : (this.textAlign === 'right')
20968          ? this.width
20969          : 0;
20970
20971      // set proper line offset
20972      var lineHeight = this._getHeightOfLine(ctx, lineIndex),
20973          lineLeftOffset = this._getCachedLineOffset(lineIndex),
20974          chars = line.split(''),
20975          prevStyle,
20976          charsToRender = '';
20977
20978      left += lineLeftOffset || 0;
20979
20980      ctx.save();
20981      top -= lineHeight / this.lineHeight * this._fontSizeFraction;
20982      for (var i = 0, len = chars.length; i <= len; i++) {
20983        prevStyle = prevStyle || this.getCurrentCharStyle(lineIndex, i);
20984        var thisStyle = this.getCurrentCharStyle(lineIndex, i + 1);
20985
20986        if (this._hasStyleChanged(prevStyle, thisStyle) || i === len) {
20987          this._renderChar(method, ctx, lineIndex, i - 1, charsToRender, left, top, lineHeight);
20988          charsToRender = '';
20989          prevStyle = thisStyle;
20990        }
20991        charsToRender += chars[i];
20992      }
20993
20994      ctx.restore();
20995    },
20996
20997    /**
20998     * @private
20999     * @param {String} method
21000     * @param {CanvasRenderingContext2D} ctx Context to render on
21001     * @param {String} line Content of the line
21002     * @param {Number} left Left coordinate
21003     * @param {Number} top Top coordinate
21004     */
21005    _renderCharsFast: function(method, ctx, line, left, top) {
21006      this.skipTextAlign = false;
21007
21008      if (method === 'fillText' && this.fill) {
21009        this.callSuper('_renderChars', method, ctx, line, left, top);
21010      }
21011      if (method === 'strokeText' && ((this.stroke && this.strokeWidth > 0) || this.skipFillStrokeCheck)) {
21012        this.callSuper('_renderChars', method, ctx, line, left, top);
21013      }
21014    },
21015
21016    /**
21017     * @private
21018     * @param {String} method
21019     * @param {CanvasRenderingContext2D} ctx Context to render on
21020     * @param {Number} lineIndex
21021     * @param {Number} i
21022     * @param {String} _char
21023     * @param {Number} left Left coordinate
21024     * @param {Number} top Top coordinate
21025     * @param {Number} lineHeight Height of the line
21026     */
21027    _renderChar: function(method, ctx, lineIndex, i, _char, left, top, lineHeight) {
21028      var decl, charWidth, charHeight,
21029          offset = this._fontSizeFraction * lineHeight / this.lineHeight;
21030
21031      if (this.styles && this.styles[lineIndex] && (decl = this.styles[lineIndex][i])) {
21032
21033        var shouldStroke = decl.stroke || this.stroke,
21034            shouldFill = decl.fill || this.fill;
21035
21036        ctx.save();
21037        charWidth = this._applyCharStylesGetWidth(ctx, _char, lineIndex, i, decl);
21038        charHeight = this._getHeightOfChar(ctx, _char, lineIndex, i);
21039
21040        if (shouldFill) {
21041          ctx.fillText(_char, left, top);
21042        }
21043        if (shouldStroke) {
21044          ctx.strokeText(_char, left, top);
21045        }
21046
21047        this._renderCharDecoration(ctx, decl, left, top, offset, charWidth, charHeight);
21048        ctx.restore();
21049
21050        ctx.translate(charWidth, 0);
21051      }
21052      else {
21053        if (method === 'strokeText' && this.stroke) {
21054          ctx[method](_char, left, top);
21055        }
21056        if (method === 'fillText' && this.fill) {
21057          ctx[method](_char, left, top);
21058        }
21059        charWidth = this._applyCharStylesGetWidth(ctx, _char, lineIndex, i);
21060        this._renderCharDecoration(ctx, null, left, top, offset, charWidth, this.fontSize);
21061
21062        ctx.translate(ctx.measureText(_char).width, 0);
21063      }
21064    },
21065
21066    /**
21067     * @private
21068     * @param {Object} prevStyle
21069     * @param {Object} thisStyle
21070     */
21071    _hasStyleChanged: function(prevStyle, thisStyle) {
21072      return (prevStyle.fill !== thisStyle.fill ||
21073              prevStyle.fontSize !== thisStyle.fontSize ||
21074              prevStyle.textBackgroundColor !== thisStyle.textBackgroundColor ||
21075              prevStyle.textDecoration !== thisStyle.textDecoration ||
21076              prevStyle.fontFamily !== thisStyle.fontFamily ||
21077              prevStyle.fontWeight !== thisStyle.fontWeight ||
21078              prevStyle.fontStyle !== thisStyle.fontStyle ||
21079              prevStyle.stroke !== thisStyle.stroke ||
21080              prevStyle.strokeWidth !== thisStyle.strokeWidth
21081      );
21082    },
21083
21084    /**
21085     * @private
21086     * @param {CanvasRenderingContext2D} ctx Context to render on
21087     */
21088    _renderCharDecoration: function(ctx, styleDeclaration, left, top, offset, charWidth, charHeight) {
21089
21090      var textDecoration = styleDeclaration
21091            ? (styleDeclaration.textDecoration || this.textDecoration)
21092            : this.textDecoration;
21093
21094      if (!textDecoration) {
21095        return;
21096      }
21097
21098      if (textDecoration.indexOf('underline') > -1) {
21099        ctx.fillRect(
21100          left,
21101          top + charHeight / 10,
21102          charWidth ,
21103          charHeight / 15
21104        );
21105      }
21106      if (textDecoration.indexOf('line-through') > -1) {
21107        ctx.fillRect(
21108          left,
21109          top - charHeight * (this._fontSizeFraction + this._fontSizeMult - 1) + charHeight / 15,
21110          charWidth,
21111          charHeight / 15
21112        );
21113      }
21114      if (textDecoration.indexOf('overline') > -1) {
21115        ctx.fillRect(
21116          left,
21117          top - (this._fontSizeMult - this._fontSizeFraction) * charHeight,
21118          charWidth,
21119          charHeight / 15
21120        );
21121      }
21122    },
21123
21124    /**
21125     * @private
21126     * @param {String} method
21127     * @param {CanvasRenderingContext2D} ctx Context to render on
21128     * @param {String} line
21129     */
21130    _renderTextLine: function(method, ctx, line, left, top, lineIndex) {
21131      // to "cancel" this.fontSize subtraction in fabric.Text#_renderTextLine
21132      // the adding 0.03 is just to align text with itext by overlap test
21133      if (!this.isEmptyStyles()) {
21134        top += this.fontSize * (this._fontSizeFraction + 0.03);
21135      }
21136      this.callSuper('_renderTextLine', method, ctx, line, left, top, lineIndex);
21137    },
21138
21139    /**
21140     * @private
21141     * @param {CanvasRenderingContext2D} ctx Context to render on
21142     */
21143    _renderTextDecoration: function(ctx) {
21144      if (this.isEmptyStyles()) {
21145        return this.callSuper('_renderTextDecoration', ctx);
21146      }
21147    },
21148
21149    /**
21150     * @private
21151     * @param {CanvasRenderingContext2D} ctx Context to render on
21152     */
21153    _renderTextLinesBackground: function(ctx) {
21154      if (!this.textBackgroundColor && !this.styles) {
21155        return;
21156      }
21157
21158      ctx.save();
21159
21160      if (this.textBackgroundColor) {
21161        ctx.fillStyle = this.textBackgroundColor;
21162      }
21163
21164      var lineHeights = 0;
21165
21166      for (var i = 0, len = this._textLines.length; i < len; i++) {
21167
21168        var heightOfLine = this._getHeightOfLine(ctx, i);
21169        if (this._textLines[i] === '') {
21170          lineHeights += heightOfLine;
21171          continue;
21172        }
21173
21174        var lineWidth = this._getLineWidth(ctx, i),
21175            lineLeftOffset = this._getCachedLineOffset(i);
21176
21177        if (this.textBackgroundColor) {
21178          ctx.fillStyle = this.textBackgroundColor;
21179
21180          ctx.fillRect(
21181            this._getLeftOffset() + lineLeftOffset,
21182            this._getTopOffset() + lineHeights,
21183            lineWidth,
21184            heightOfLine / this.lineHeight
21185          );
21186        }
21187        if (this.styles[i]) {
21188          for (var j = 0, jlen = this._textLines[i].length; j < jlen; j++) {
21189            if (this.styles[i] && this.styles[i][j] && this.styles[i][j].textBackgroundColor) {
21190
21191              var _char = this._textLines[i][j];
21192
21193              ctx.fillStyle = this.styles[i][j].textBackgroundColor;
21194
21195              ctx.fillRect(
21196                this._getLeftOffset() + lineLeftOffset + this._getWidthOfCharsAt(ctx, i, j),
21197                this._getTopOffset() + lineHeights,
21198                this._getWidthOfChar(ctx, _char, i, j) + 1,
21199                heightOfLine / this.lineHeight
21200              );
21201            }
21202          }
21203        }
21204        lineHeights += heightOfLine;
21205      }
21206      ctx.restore();
21207    },
21208
21209    /**
21210     * @private
21211     */
21212    _getCacheProp: function(_char, styleDeclaration) {
21213      return _char +
21214             styleDeclaration.fontFamily +
21215             styleDeclaration.fontSize +
21216             styleDeclaration.fontWeight +
21217             styleDeclaration.fontStyle +
21218             styleDeclaration.shadow;
21219    },
21220
21221    /**
21222     * @private
21223     * @param {CanvasRenderingContext2D} ctx Context to render on
21224     * @param {String} _char
21225     * @param {Number} lineIndex
21226     * @param {Number} charIndex
21227     * @param {Object} [decl]
21228     */
21229    _applyCharStylesGetWidth: function(ctx, _char, lineIndex, charIndex, decl) {
21230      var styleDeclaration = decl ||
21231                            (this.styles[lineIndex] &&
21232                             this.styles[lineIndex][charIndex]);
21233
21234      if (styleDeclaration) {
21235        // cloning so that original style object is not polluted with following font declarations
21236        styleDeclaration = clone(styleDeclaration);
21237      }
21238      else {
21239        styleDeclaration = { };
21240      }
21241
21242      this._applyFontStyles(styleDeclaration);
21243
21244      var cacheProp = this._getCacheProp(_char, styleDeclaration);
21245
21246      // short-circuit if no styles
21247      if (this.isEmptyStyles() && this._charWidthsCache[cacheProp] && this.caching) {
21248        return this._charWidthsCache[cacheProp];
21249      }
21250
21251      if (typeof styleDeclaration.shadow === 'string') {
21252        styleDeclaration.shadow = new fabric.Shadow(styleDeclaration.shadow);
21253      }
21254
21255      var fill = styleDeclaration.fill || this.fill;
21256      ctx.fillStyle = fill.toLive
21257        ? fill.toLive(ctx, this)
21258        : fill;
21259
21260      if (styleDeclaration.stroke) {
21261        ctx.strokeStyle = (styleDeclaration.stroke && styleDeclaration.stroke.toLive)
21262          ? styleDeclaration.stroke.toLive(ctx, this)
21263          : styleDeclaration.stroke;
21264      }
21265
21266      ctx.lineWidth = styleDeclaration.strokeWidth || this.strokeWidth;
21267      ctx.font = this._getFontDeclaration.call(styleDeclaration);
21268      this._setShadow.call(styleDeclaration, ctx);
21269
21270      if (!this.caching) {
21271        return ctx.measureText(_char).width;
21272      }
21273
21274      if (!this._charWidthsCache[cacheProp]) {
21275        this._charWidthsCache[cacheProp] = ctx.measureText(_char).width;
21276      }
21277
21278      return this._charWidthsCache[cacheProp];
21279    },
21280
21281    /**
21282     * @private
21283     * @param {Object} styleDeclaration
21284     */
21285    _applyFontStyles: function(styleDeclaration) {
21286      if (!styleDeclaration.fontFamily) {
21287        styleDeclaration.fontFamily = this.fontFamily;
21288      }
21289      if (!styleDeclaration.fontSize) {
21290        styleDeclaration.fontSize = this.fontSize;
21291      }
21292      if (!styleDeclaration.fontWeight) {
21293        styleDeclaration.fontWeight = this.fontWeight;
21294      }
21295      if (!styleDeclaration.fontStyle) {
21296        styleDeclaration.fontStyle = this.fontStyle;
21297      }
21298    },
21299
21300    /**
21301     * @private
21302     * @param {Number} lineIndex
21303     * @param {Number} charIndex
21304     */
21305    _getStyleDeclaration: function(lineIndex, charIndex) {
21306      return (this.styles[lineIndex] && this.styles[lineIndex][charIndex])
21307        ? clone(this.styles[lineIndex][charIndex])
21308        : { };
21309    },
21310
21311    /**
21312     * @private
21313     * @param {CanvasRenderingContext2D} ctx Context to render on
21314     */
21315    _getWidthOfChar: function(ctx, _char, lineIndex, charIndex) {
21316      if (this.textAlign === 'justify' && /\s/.test(_char)) {
21317        return this._getWidthOfSpace(ctx, lineIndex);
21318      }
21319
21320      var styleDeclaration = this._getStyleDeclaration(lineIndex, charIndex);
21321      this._applyFontStyles(styleDeclaration);
21322      var cacheProp = this._getCacheProp(_char, styleDeclaration);
21323
21324      if (this._charWidthsCache[cacheProp] && this.caching) {
21325        return this._charWidthsCache[cacheProp];
21326      }
21327      else if (ctx) {
21328        ctx.save();
21329        var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex);
21330        ctx.restore();
21331        return width;
21332      }
21333    },
21334
21335    /**
21336     * @private
21337     * @param {CanvasRenderingContext2D} ctx Context to render on
21338     */
21339    _getHeightOfChar: function(ctx, _char, lineIndex, charIndex) {
21340      if (this.styles[lineIndex] && this.styles[lineIndex][charIndex]) {
21341        return this.styles[lineIndex][charIndex].fontSize || this.fontSize;
21342      }
21343      return this.fontSize;
21344    },
21345
21346    /**
21347     * @private
21348     * @param {CanvasRenderingContext2D} ctx Context to render on
21349     */
21350    _getHeightOfCharAt: function(ctx, lineIndex, charIndex) {
21351      var _char = this._textLines[lineIndex][charIndex];
21352      return this._getHeightOfChar(ctx, _char, lineIndex, charIndex);
21353    },
21354
21355    /**
21356     * @private
21357     * @param {CanvasRenderingContext2D} ctx Context to render on
21358     */
21359    _getWidthOfCharsAt: function(ctx, lineIndex, charIndex) {
21360      var width = 0, i, _char;
21361      for (i = 0; i < charIndex; i++) {
21362        _char = this._textLines[lineIndex][i];
21363        width += this._getWidthOfChar(ctx, _char, lineIndex, i);
21364      }
21365      return width;
21366    },
21367
21368    /**
21369     * @private
21370     * @param {CanvasRenderingContext2D} ctx Context to render on
21371     */
21372    _getLineWidth: function(ctx, lineIndex) {
21373      if (this.__lineWidths[lineIndex]) {
21374        return this.__lineWidths[lineIndex];
21375      }
21376      this.__lineWidths[lineIndex] = this._getWidthOfCharsAt(ctx, lineIndex, this._textLines[lineIndex].length);
21377      return this.__lineWidths[lineIndex];
21378    },
21379
21380    /**
21381     * @private
21382     * @param {CanvasRenderingContext2D} ctx Context to render on
21383     * @param {Number} lineIndex
21384     */
21385    _getWidthOfSpace: function (ctx, lineIndex) {
21386      if (this.__widthOfSpace[lineIndex]) {
21387        return this.__widthOfSpace[lineIndex];
21388      }
21389      var line = this._textLines[lineIndex],
21390          wordsWidth = this._getWidthOfWords(ctx, line, lineIndex),
21391          widthDiff = this.width - wordsWidth,
21392          numSpaces = line.length - line.replace(/\s+/g, '').length,
21393          width = widthDiff / numSpaces;
21394      this.__widthOfSpace[lineIndex] = width;
21395      return width;
21396    },
21397
21398    /**
21399     * @private
21400     * @param {CanvasRenderingContext2D} ctx Context to render on
21401     * @param {Number} line
21402     * @param {Number} lineIndex
21403     */
21404    _getWidthOfWords: function (ctx, line, lineIndex) {
21405      var width = 0;
21406
21407      for (var charIndex = 0; charIndex < line.length; charIndex++) {
21408        var _char = line[charIndex];
21409
21410        if (!_char.match(/\s/)) {
21411          width += this._getWidthOfChar(ctx, _char, lineIndex, charIndex);
21412        }
21413      }
21414
21415      return width;
21416    },
21417
21418    /**
21419     * @private
21420     * @param {CanvasRenderingContext2D} ctx Context to render on
21421     */
21422    _getHeightOfLine: function(ctx, lineIndex) {
21423      if (this.__lineHeights[lineIndex]) {
21424        return this.__lineHeights[lineIndex];
21425      }
21426
21427      var line = this._textLines[lineIndex],
21428          maxHeight = this._getHeightOfChar(ctx, line[0], lineIndex, 0);
21429
21430      for (var i = 1, len = line.length; i < len; i++) {
21431        var currentCharHeight = this._getHeightOfChar(ctx, line[i], lineIndex, i);
21432        if (currentCharHeight > maxHeight) {
21433          maxHeight = currentCharHeight;
21434        }
21435      }
21436      this.__maxFontHeights[lineIndex] = maxHeight;
21437      this.__lineHeights[lineIndex] = maxHeight * this.lineHeight * this._fontSizeMult;
21438      return this.__lineHeights[lineIndex];
21439    },
21440
21441    /**
21442     * @private
21443     * @param {CanvasRenderingContext2D} ctx Context to render on
21444     */
21445    _getTextHeight: function(ctx) {
21446      var height = 0;
21447      for (var i = 0, len = this._textLines.length; i < len; i++) {
21448        height += this._getHeightOfLine(ctx, i);
21449      }
21450      return height;
21451    },
21452
21453    /**
21454     * This method is overwritten to account for different top offset
21455     * @private
21456     */
21457    _renderTextBoxBackground: function(ctx) {
21458      if (!this.backgroundColor) {
21459        return;
21460      }
21461
21462      ctx.save();
21463      ctx.fillStyle = this.backgroundColor;
21464
21465      ctx.fillRect(
21466        this._getLeftOffset(),
21467        this._getTopOffset(),
21468        this.width,
21469        this.height
21470      );
21471
21472      ctx.restore();
21473    },
21474
21475    /**
21476     * Returns object representation of an instance
21477     * @method toObject
21478     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
21479     * @return {Object} object representation of an instance
21480     */
21481    toObject: function(propertiesToInclude) {
21482      return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), {
21483        styles: clone(this.styles)
21484      });
21485    }
21486  });
21487
21488  /**
21489   * Returns fabric.IText instance from an object representation
21490   * @static
21491   * @memberOf fabric.IText
21492   * @param {Object} object Object to create an instance from
21493   * @return {fabric.IText} instance of fabric.IText
21494   */
21495  fabric.IText.fromObject = function(object) {
21496    return new fabric.IText(object.text, clone(object));
21497  };
21498})();
21499
21500
21501(function() {
21502
21503  var clone = fabric.util.object.clone;
21504
21505  fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ {
21506
21507    /**
21508     * Initializes all the interactive behavior of IText
21509     */
21510    initBehavior: function() {
21511      this.initAddedHandler();
21512      this.initRemovedHandler();
21513      this.initCursorSelectionHandlers();
21514      this.initDoubleClickSimulation();
21515    },
21516
21517    /**
21518     * Initializes "selected" event handler
21519     */
21520    initSelectedHandler: function() {
21521      this.on('selected', function() {
21522
21523        var _this = this;
21524        setTimeout(function() {
21525          _this.selected = true;
21526        }, 100);
21527      });
21528    },
21529
21530    /**
21531     * Initializes "added" event handler
21532     */
21533    initAddedHandler: function() {
21534      var _this = this;
21535      this.on('added', function() {
21536        if (this.canvas && !this.canvas._hasITextHandlers) {
21537          this.canvas._hasITextHandlers = true;
21538          this._initCanvasHandlers();
21539        }
21540
21541        // Track IText instances per-canvas. Only register in this array once added
21542        // to a canvas; we don't want to leak a reference to the instance forever
21543        // simply because it existed at some point.
21544        //
21545        // (Might be added to a collection, but not on a canvas.)
21546        if (_this.canvas) {
21547          _this.canvas._iTextInstances = _this.canvas._iTextInstances || [];
21548          _this.canvas._iTextInstances.push(_this);
21549        }
21550      });
21551    },
21552
21553    initRemovedHandler: function() {
21554      var _this = this;
21555      this.on('removed', function() {
21556        // (Might be removed from a collection, but not on a canvas.)
21557        if (_this.canvas) {
21558          _this.canvas._iTextInstances = _this.canvas._iTextInstances || [];
21559          fabric.util.removeFromArray(_this.canvas._iTextInstances, _this);
21560        }
21561      });
21562    },
21563
21564    /**
21565     * @private
21566     */
21567    _initCanvasHandlers: function() {
21568      var _this = this;
21569
21570      this.canvas.on('selection:cleared', function() {
21571        fabric.IText.prototype.exitEditingOnOthers(_this.canvas);
21572      });
21573
21574      this.canvas.on('mouse:up', function() {
21575        if (_this.canvas._iTextInstances) {
21576          _this.canvas._iTextInstances.forEach(function(obj) {
21577            obj.__isMousedown = false;
21578          });
21579        }
21580      });
21581
21582      this.canvas.on('object:selected', function() {
21583        fabric.IText.prototype.exitEditingOnOthers(_this.canvas);
21584      });
21585    },
21586
21587    /**
21588     * @private
21589     */
21590    _tick: function() {
21591      this._currentTickState = this._animateCursor(this, 1, this.cursorDuration, '_onTickComplete');
21592    },
21593
21594    /**
21595     * @private
21596     */
21597    _animateCursor: function(obj, targetOpacity, duration, completeMethod) {
21598
21599      var tickState;
21600
21601      tickState = {
21602        isAborted: false,
21603        abort: function() {
21604          this.isAborted = true;
21605        },
21606      };
21607
21608      obj.animate('_currentCursorOpacity', targetOpacity, {
21609        duration: duration,
21610        onComplete: function() {
21611          if (!tickState.isAborted) {
21612            obj[completeMethod]();
21613          }
21614        },
21615        onChange: function() {
21616          if (obj.canvas) {
21617            obj.canvas.clearContext(obj.canvas.contextTop || obj.ctx);
21618            obj.renderCursorOrSelection();
21619          }
21620        },
21621        abort: function() {
21622          return tickState.isAborted;
21623        }
21624      });
21625      return tickState;
21626    },
21627
21628    /**
21629     * @private
21630     */
21631    _onTickComplete: function() {
21632
21633      var _this = this;
21634
21635      if (this._cursorTimeout1) {
21636        clearTimeout(this._cursorTimeout1);
21637      }
21638      this._cursorTimeout1 = setTimeout(function() {
21639        _this._currentTickCompleteState = _this._animateCursor(_this, 0, this.cursorDuration / 2, '_tick');
21640      }, 100);
21641    },
21642
21643    /**
21644     * Initializes delayed cursor
21645     */
21646    initDelayedCursor: function(restart) {
21647      var _this = this,
21648          delay = restart ? 0 : this.cursorDelay;
21649
21650      this._currentTickState && this._currentTickState.abort();
21651      this._currentTickCompleteState && this._currentTickCompleteState.abort();
21652      clearTimeout(this._cursorTimeout1);
21653      this._currentCursorOpacity = 1;
21654      if (this.canvas) {
21655        this.canvas.clearContext(this.canvas.contextTop || this.ctx);
21656        this.renderCursorOrSelection();
21657      }
21658      if (this._cursorTimeout2) {
21659        clearTimeout(this._cursorTimeout2);
21660      }
21661      this._cursorTimeout2 = setTimeout(function() {
21662        _this._tick();
21663      }, delay);
21664    },
21665
21666    /**
21667     * Aborts cursor animation and clears all timeouts
21668     */
21669    abortCursorAnimation: function() {
21670      this._currentTickState && this._currentTickState.abort();
21671      this._currentTickCompleteState && this._currentTickCompleteState.abort();
21672
21673      clearTimeout(this._cursorTimeout1);
21674      clearTimeout(this._cursorTimeout2);
21675
21676      this._currentCursorOpacity = 0;
21677      this.canvas && this.canvas.clearContext(this.canvas.contextTop || this.ctx);
21678    },
21679
21680    /**
21681     * Selects entire text
21682     */
21683    selectAll: function() {
21684      this.setSelectionStart(0);
21685      this.setSelectionEnd(this.text.length);
21686    },
21687
21688    /**
21689     * Returns selected text
21690     * @return {String}
21691     */
21692    getSelectedText: function() {
21693      return this.text.slice(this.selectionStart, this.selectionEnd);
21694    },
21695
21696    /**
21697     * Find new selection index representing start of current word according to current selection index
21698     * @param {Number} startFrom Surrent selection index
21699     * @return {Number} New selection index
21700     */
21701    findWordBoundaryLeft: function(startFrom) {
21702      var offset = 0, index = startFrom - 1;
21703
21704      // remove space before cursor first
21705      if (this._reSpace.test(this.text.charAt(index))) {
21706        while (this._reSpace.test(this.text.charAt(index))) {
21707          offset++;
21708          index--;
21709        }
21710      }
21711      while (/\S/.test(this.text.charAt(index)) && index > -1) {
21712        offset++;
21713        index--;
21714      }
21715
21716      return startFrom - offset;
21717    },
21718
21719    /**
21720     * Find new selection index representing end of current word according to current selection index
21721     * @param {Number} startFrom Current selection index
21722     * @return {Number} New selection index
21723     */
21724    findWordBoundaryRight: function(startFrom) {
21725      var offset = 0, index = startFrom;
21726
21727      // remove space after cursor first
21728      if (this._reSpace.test(this.text.charAt(index))) {
21729        while (this._reSpace.test(this.text.charAt(index))) {
21730          offset++;
21731          index++;
21732        }
21733      }
21734      while (/\S/.test(this.text.charAt(index)) && index < this.text.length) {
21735        offset++;
21736        index++;
21737      }
21738
21739      return startFrom + offset;
21740    },
21741
21742    /**
21743     * Find new selection index representing start of current line according to current selection index
21744     * @param {Number} startFrom Current selection index
21745     * @return {Number} New selection index
21746     */
21747    findLineBoundaryLeft: function(startFrom) {
21748      var offset = 0, index = startFrom - 1;
21749
21750      while (!/\n/.test(this.text.charAt(index)) && index > -1) {
21751        offset++;
21752        index--;
21753      }
21754
21755      return startFrom - offset;
21756    },
21757
21758    /**
21759     * Find new selection index representing end of current line according to current selection index
21760     * @param {Number} startFrom Current selection index
21761     * @return {Number} New selection index
21762     */
21763    findLineBoundaryRight: function(startFrom) {
21764      var offset = 0, index = startFrom;
21765
21766      while (!/\n/.test(this.text.charAt(index)) && index < this.text.length) {
21767        offset++;
21768        index++;
21769      }
21770
21771      return startFrom + offset;
21772    },
21773
21774    /**
21775     * Returns number of newlines in selected text
21776     * @return {Number} Number of newlines in selected text
21777     */
21778    getNumNewLinesInSelectedText: function() {
21779      var selectedText = this.getSelectedText(),
21780          numNewLines = 0;
21781
21782      for (var i = 0, chars = selectedText.split(''), len = chars.length; i < len; i++) {
21783        if (chars[i] === '\n') {
21784          numNewLines++;
21785        }
21786      }
21787      return numNewLines;
21788    },
21789
21790    /**
21791     * Finds index corresponding to beginning or end of a word
21792     * @param {Number} selectionStart Index of a character
21793     * @param {Number} direction: 1 or -1
21794     * @return {Number} Index of the beginning or end of a word
21795     */
21796    searchWordBoundary: function(selectionStart, direction) {
21797      var index = this._reSpace.test(this.text.charAt(selectionStart)) ? selectionStart - 1 : selectionStart,
21798          _char = this.text.charAt(index),
21799          reNonWord = /[ \n\.,;!\?\-]/;
21800
21801      while (!reNonWord.test(_char) && index > 0 && index < this.text.length) {
21802        index += direction;
21803        _char = this.text.charAt(index);
21804      }
21805      if (reNonWord.test(_char) && _char !== '\n') {
21806        index += direction === 1 ? 0 : 1;
21807      }
21808      return index;
21809    },
21810
21811    /**
21812     * Selects a word based on the index
21813     * @param {Number} selectionStart Index of a character
21814     */
21815    selectWord: function(selectionStart) {
21816      var newSelectionStart = this.searchWordBoundary(selectionStart, -1), /* search backwards */
21817          newSelectionEnd = this.searchWordBoundary(selectionStart, 1); /* search forward */
21818
21819      this.setSelectionStart(newSelectionStart);
21820      this.setSelectionEnd(newSelectionEnd);
21821    },
21822
21823    /**
21824     * Selects a line based on the index
21825     * @param {Number} selectionStart Index of a character
21826     */
21827    selectLine: function(selectionStart) {
21828      var newSelectionStart = this.findLineBoundaryLeft(selectionStart),
21829          newSelectionEnd = this.findLineBoundaryRight(selectionStart);
21830
21831      this.setSelectionStart(newSelectionStart);
21832      this.setSelectionEnd(newSelectionEnd);
21833    },
21834
21835    /**
21836     * Enters editing state
21837     * @return {fabric.IText} thisArg
21838     * @chainable
21839     */
21840    enterEditing: function() {
21841      if (this.isEditing || !this.editable) {
21842        return;
21843      }
21844
21845      if (this.canvas) {
21846        this.exitEditingOnOthers(this.canvas);
21847      }
21848
21849      this.isEditing = true;
21850
21851      this.initHiddenTextarea();
21852      this.hiddenTextarea.focus();
21853      this._updateTextarea();
21854      this._saveEditingProps();
21855      this._setEditingProps();
21856
21857      this._tick();
21858      this.fire('editing:entered');
21859
21860      if (!this.canvas) {
21861        return this;
21862      }
21863
21864      this.canvas.renderAll();
21865      this.canvas.fire('text:editing:entered', { target: this });
21866      this.initMouseMoveHandler();
21867      return this;
21868    },
21869
21870    exitEditingOnOthers: function(canvas) {
21871      if (canvas._iTextInstances) {
21872        canvas._iTextInstances.forEach(function(obj) {
21873          obj.selected = false;
21874          if (obj.isEditing) {
21875            obj.exitEditing();
21876          }
21877        });
21878      }
21879    },
21880
21881    /**
21882    * Initializes "mousemove" event handler
21883    */
21884    initMouseMoveHandler: function() {
21885      var _this = this;
21886      this.canvas.on('mouse:move',  function(options) {
21887        if (!_this.__isMousedown || !_this.isEditing) {
21888          return;
21889        }
21890
21891        var newSelectionStart = _this.getSelectionStartFromPointer(options.e);
21892        if (newSelectionStart >= _this.__selectionStartOnMouseDown) {
21893          _this.setSelectionStart(_this.__selectionStartOnMouseDown);
21894          _this.setSelectionEnd(newSelectionStart);
21895        }
21896        else {
21897          _this.setSelectionStart(newSelectionStart);
21898          _this.setSelectionEnd(_this.__selectionStartOnMouseDown);
21899        }
21900      });
21901    },
21902
21903    /**
21904     * @private
21905     */
21906    _setEditingProps: function() {
21907      this.hoverCursor = 'text';
21908
21909      if (this.canvas) {
21910        this.canvas.defaultCursor = this.canvas.moveCursor = 'text';
21911      }
21912
21913      this.borderColor = this.editingBorderColor;
21914
21915      this.hasControls = this.selectable = false;
21916      this.lockMovementX = this.lockMovementY = true;
21917    },
21918
21919    /**
21920     * @private
21921     */
21922    _updateTextarea: function() {
21923      if (!this.hiddenTextarea) {
21924        return;
21925      }
21926
21927      this.hiddenTextarea.value = this.text;
21928      this.hiddenTextarea.selectionStart = this.selectionStart;
21929      this.hiddenTextarea.selectionEnd = this.selectionEnd;
21930    },
21931
21932    /**
21933     * @private
21934     */
21935    _saveEditingProps: function() {
21936      this._savedProps = {
21937        hasControls: this.hasControls,
21938        borderColor: this.borderColor,
21939        lockMovementX: this.lockMovementX,
21940        lockMovementY: this.lockMovementY,
21941        hoverCursor: this.hoverCursor,
21942        defaultCursor: this.canvas && this.canvas.defaultCursor,
21943        moveCursor: this.canvas && this.canvas.moveCursor
21944      };
21945    },
21946
21947    /**
21948     * @private
21949     */
21950    _restoreEditingProps: function() {
21951      if (!this._savedProps) {
21952        return;
21953      }
21954
21955      this.hoverCursor = this._savedProps.overCursor;
21956      this.hasControls = this._savedProps.hasControls;
21957      this.borderColor = this._savedProps.borderColor;
21958      this.lockMovementX = this._savedProps.lockMovementX;
21959      this.lockMovementY = this._savedProps.lockMovementY;
21960
21961      if (this.canvas) {
21962        this.canvas.defaultCursor = this._savedProps.defaultCursor;
21963        this.canvas.moveCursor = this._savedProps.moveCursor;
21964      }
21965    },
21966
21967    /**
21968     * Exits from editing state
21969     * @return {fabric.IText} thisArg
21970     * @chainable
21971     */
21972    exitEditing: function() {
21973
21974      this.selected = false;
21975      this.isEditing = false;
21976      this.selectable = true;
21977
21978      this.selectionEnd = this.selectionStart;
21979      this.hiddenTextarea && this.canvas && this.hiddenTextarea.parentNode.removeChild(this.hiddenTextarea);
21980      this.hiddenTextarea = null;
21981
21982      this.abortCursorAnimation();
21983      this._restoreEditingProps();
21984      this._currentCursorOpacity = 0;
21985
21986      this.fire('editing:exited');
21987      this.canvas && this.canvas.fire('text:editing:exited', { target: this });
21988
21989      return this;
21990    },
21991
21992    /**
21993     * @private
21994     */
21995    _removeExtraneousStyles: function() {
21996      for (var prop in this.styles) {
21997        if (!this._textLines[prop]) {
21998          delete this.styles[prop];
21999        }
22000      }
22001    },
22002
22003    /**
22004     * @private
22005     */
22006    _removeCharsFromTo: function(start, end) {
22007
22008      var i = end;
22009      while (i !== start) {
22010
22011        var prevIndex = this.get2DCursorLocation(i).charIndex;
22012        i--;
22013
22014        var index = this.get2DCursorLocation(i).charIndex,
22015            isNewline = index > prevIndex;
22016
22017        if (isNewline) {
22018          this.removeStyleObject(isNewline, i + 1);
22019        }
22020        else {
22021          this.removeStyleObject(this.get2DCursorLocation(i).charIndex === 0, i);
22022        }
22023
22024      }
22025
22026      this.text = this.text.slice(0, start) +
22027                  this.text.slice(end);
22028      this._clearCache();
22029    },
22030
22031    /**
22032     * Inserts a character where cursor is (replacing selection if one exists)
22033     * @param {String} _chars Characters to insert
22034     */
22035    insertChars: function(_chars, useCopiedStyle) {
22036      var isEndOfLine = this.text.slice(this.selectionStart, this.selectionStart + 1) === '\n';
22037
22038      this.text = this.text.slice(0, this.selectionStart) +
22039                    _chars +
22040                  this.text.slice(this.selectionEnd);
22041
22042      if (this.selectionStart === this.selectionEnd) {
22043        this.insertStyleObjects(_chars, isEndOfLine, useCopiedStyle);
22044      }
22045      // else if (this.selectionEnd - this.selectionStart > 1) {
22046      // TODO: replace styles properly
22047      // console.log('replacing MORE than 1 char');
22048      // }
22049      this.setSelectionStart(this.selectionStart + _chars.length);
22050      this.setSelectionEnd(this.selectionStart);
22051      this._clearCache();
22052      this.canvas && this.canvas.renderAll();
22053
22054      this.setCoords();
22055      this.fire('changed');
22056      this.canvas && this.canvas.fire('text:changed', { target: this });
22057    },
22058
22059    /**
22060     * Inserts new style object
22061     * @param {Number} lineIndex Index of a line
22062     * @param {Number} charIndex Index of a char
22063     * @param {Boolean} isEndOfLine True if it's end of line
22064     */
22065    insertNewlineStyleObject: function(lineIndex, charIndex, isEndOfLine) {
22066
22067      this.shiftLineStyles(lineIndex, +1);
22068
22069      if (!this.styles[lineIndex + 1]) {
22070        this.styles[lineIndex + 1] = { };
22071      }
22072
22073      var currentCharStyle = this.styles[lineIndex][charIndex - 1],
22074          newLineStyles = { };
22075
22076      // if there's nothing after cursor,
22077      // we clone current char style onto the next (otherwise empty) line
22078      if (isEndOfLine) {
22079        newLineStyles[0] = clone(currentCharStyle);
22080        this.styles[lineIndex + 1] = newLineStyles;
22081      }
22082      // otherwise we clone styles of all chars
22083      // after cursor onto the next line, from the beginning
22084      else {
22085        for (var index in this.styles[lineIndex]) {
22086          if (parseInt(index, 10) >= charIndex) {
22087            newLineStyles[parseInt(index, 10) - charIndex] = this.styles[lineIndex][index];
22088            // remove lines from the previous line since they're on a new line now
22089            delete this.styles[lineIndex][index];
22090          }
22091        }
22092        this.styles[lineIndex + 1] = newLineStyles;
22093      }
22094      this._clearCache();
22095    },
22096
22097    /**
22098     * Inserts style object for a given line/char index
22099     * @param {Number} lineIndex Index of a line
22100     * @param {Number} charIndex Index of a char
22101     * @param {Object} [style] Style object to insert, if given
22102     */
22103    insertCharStyleObject: function(lineIndex, charIndex, style) {
22104
22105      var currentLineStyles = this.styles[lineIndex],
22106          currentLineStylesCloned = clone(currentLineStyles);
22107
22108      if (charIndex === 0 && !style) {
22109        charIndex = 1;
22110      }
22111
22112      // shift all char styles by 1 forward
22113      // 0,1,2,3 -> (charIndex=2) -> 0,1,3,4 -> (insert 2) -> 0,1,2,3,4
22114      for (var index in currentLineStylesCloned) {
22115        var numericIndex = parseInt(index, 10);
22116        if (numericIndex >= charIndex) {
22117          currentLineStyles[numericIndex + 1] = currentLineStylesCloned[numericIndex];
22118          //delete currentLineStyles[index];
22119        }
22120      }
22121
22122      this.styles[lineIndex][charIndex] =
22123        style || clone(currentLineStyles[charIndex - 1]);
22124      this._clearCache();
22125    },
22126
22127    /**
22128     * Inserts style object(s)
22129     * @param {String} _chars Characters at the location where style is inserted
22130     * @param {Boolean} isEndOfLine True if it's end of line
22131     * @param {Boolean} [useCopiedStyle] Style to insert
22132     */
22133    insertStyleObjects: function(_chars, isEndOfLine, useCopiedStyle) {
22134      // removed shortcircuit over isEmptyStyles
22135
22136      var cursorLocation = this.get2DCursorLocation(),
22137          lineIndex = cursorLocation.lineIndex,
22138          charIndex = cursorLocation.charIndex;
22139
22140      if (!this.styles[lineIndex]) {
22141        this.styles[lineIndex] = { };
22142      }
22143
22144      if (_chars === '\n') {
22145        this.insertNewlineStyleObject(lineIndex, charIndex, isEndOfLine);
22146      }
22147      else {
22148        if (useCopiedStyle) {
22149          this._insertStyles(this.copiedStyles);
22150        }
22151        else {
22152          // TODO: support multiple style insertion if _chars.length > 1
22153          this.insertCharStyleObject(lineIndex, charIndex);
22154        }
22155      }
22156    },
22157
22158    /**
22159     * @private
22160     */
22161    _insertStyles: function(styles) {
22162      for (var i = 0, len = styles.length; i < len; i++) {
22163
22164        var cursorLocation = this.get2DCursorLocation(this.selectionStart + i),
22165            lineIndex = cursorLocation.lineIndex,
22166            charIndex = cursorLocation.charIndex;
22167
22168        this.insertCharStyleObject(lineIndex, charIndex, styles[i]);
22169      }
22170    },
22171
22172    /**
22173     * Shifts line styles up or down
22174     * @param {Number} lineIndex Index of a line
22175     * @param {Number} offset Can be -1 or +1
22176     */
22177    shiftLineStyles: function(lineIndex, offset) {
22178      // shift all line styles by 1 upward
22179      var clonedStyles = clone(this.styles);
22180      for (var line in this.styles) {
22181        var numericLine = parseInt(line, 10);
22182        if (numericLine > lineIndex) {
22183          this.styles[numericLine + offset] = clonedStyles[numericLine];
22184        }
22185      }
22186    },
22187
22188    /**
22189     * Removes style object
22190     * @param {Boolean} isBeginningOfLine True if cursor is at the beginning of line
22191     * @param {Number} [index] Optional index. When not given, current selectionStart is used.
22192     */
22193    removeStyleObject: function(isBeginningOfLine, index) {
22194
22195      var cursorLocation = this.get2DCursorLocation(index),
22196          lineIndex = cursorLocation.lineIndex,
22197          charIndex = cursorLocation.charIndex;
22198
22199      if (isBeginningOfLine) {
22200
22201        var textOnPreviousLine = this._textLines[lineIndex - 1],
22202            newCharIndexOnPrevLine = textOnPreviousLine
22203              ? textOnPreviousLine.length
22204              : 0;
22205
22206        if (!this.styles[lineIndex - 1]) {
22207          this.styles[lineIndex - 1] = { };
22208        }
22209
22210        for (charIndex in this.styles[lineIndex]) {
22211          this.styles[lineIndex - 1][parseInt(charIndex, 10) + newCharIndexOnPrevLine]
22212            = this.styles[lineIndex][charIndex];
22213        }
22214
22215        this.shiftLineStyles(lineIndex, -1);
22216      }
22217      else {
22218        var currentLineStyles = this.styles[lineIndex];
22219
22220        if (currentLineStyles) {
22221          var offset = this.selectionStart === this.selectionEnd ? -1 : 0;
22222          delete currentLineStyles[charIndex + offset];
22223          // console.log('deleting', lineIndex, charIndex + offset);
22224        }
22225
22226        var currentLineStylesCloned = clone(currentLineStyles);
22227
22228        // shift all styles by 1 backwards
22229        for (var i in currentLineStylesCloned) {
22230          var numericIndex = parseInt(i, 10);
22231          if (numericIndex >= charIndex && numericIndex !== 0) {
22232            currentLineStyles[numericIndex - 1] = currentLineStylesCloned[numericIndex];
22233            delete currentLineStyles[numericIndex];
22234          }
22235        }
22236      }
22237    },
22238
22239    /**
22240     * Inserts new line
22241     */
22242    insertNewline: function() {
22243      this.insertChars('\n');
22244    }
22245  });
22246})();
22247
22248
22249fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ {
22250  /**
22251   * Initializes "dbclick" event handler
22252   */
22253  initDoubleClickSimulation: function() {
22254
22255    // for double click
22256    this.__lastClickTime = +new Date();
22257
22258    // for triple click
22259    this.__lastLastClickTime = +new Date();
22260
22261    this.__lastPointer = { };
22262
22263    this.on('mousedown', this.onMouseDown.bind(this));
22264  },
22265
22266  onMouseDown: function(options) {
22267
22268    this.__newClickTime = +new Date();
22269    var newPointer = this.canvas.getPointer(options.e);
22270
22271    if (this.isTripleClick(newPointer)) {
22272      this.fire('tripleclick', options);
22273      this._stopEvent(options.e);
22274    }
22275    else if (this.isDoubleClick(newPointer)) {
22276      this.fire('dblclick', options);
22277      this._stopEvent(options.e);
22278    }
22279
22280    this.__lastLastClickTime = this.__lastClickTime;
22281    this.__lastClickTime = this.__newClickTime;
22282    this.__lastPointer = newPointer;
22283    this.__lastIsEditing = this.isEditing;
22284    this.__lastSelected = this.selected;
22285  },
22286
22287  isDoubleClick: function(newPointer) {
22288    return this.__newClickTime - this.__lastClickTime < 500 &&
22289        this.__lastPointer.x === newPointer.x &&
22290        this.__lastPointer.y === newPointer.y && this.__lastIsEditing;
22291  },
22292
22293  isTripleClick: function(newPointer) {
22294    return this.__newClickTime - this.__lastClickTime < 500 &&
22295        this.__lastClickTime - this.__lastLastClickTime < 500 &&
22296        this.__lastPointer.x === newPointer.x &&
22297        this.__lastPointer.y === newPointer.y;
22298  },
22299
22300  /**
22301   * @private
22302   */
22303  _stopEvent: function(e) {
22304    e.preventDefault && e.preventDefault();
22305    e.stopPropagation && e.stopPropagation();
22306  },
22307
22308  /**
22309   * Initializes event handlers related to cursor or selection
22310   */
22311  initCursorSelectionHandlers: function() {
22312    this.initSelectedHandler();
22313    this.initMousedownHandler();
22314    this.initMouseupHandler();
22315    this.initClicks();
22316  },
22317
22318  /**
22319   * Initializes double and triple click event handlers
22320   */
22321  initClicks: function() {
22322    this.on('dblclick', function(options) {
22323      this.selectWord(this.getSelectionStartFromPointer(options.e));
22324    });
22325    this.on('tripleclick', function(options) {
22326      this.selectLine(this.getSelectionStartFromPointer(options.e));
22327    });
22328  },
22329
22330  /**
22331   * Initializes "mousedown" event handler
22332   */
22333  initMousedownHandler: function() {
22334    this.on('mousedown', function(options) {
22335
22336      var pointer = this.canvas.getPointer(options.e);
22337
22338      this.__mousedownX = pointer.x;
22339      this.__mousedownY = pointer.y;
22340      this.__isMousedown = true;
22341
22342      if (this.hiddenTextarea && this.canvas) {
22343        this.canvas.wrapperEl.appendChild(this.hiddenTextarea);
22344      }
22345
22346      if (this.selected) {
22347        this.setCursorByClick(options.e);
22348      }
22349
22350      if (this.isEditing) {
22351        this.__selectionStartOnMouseDown = this.selectionStart;
22352        this.initDelayedCursor(true);
22353      }
22354    });
22355  },
22356
22357  /**
22358   * @private
22359   */
22360  _isObjectMoved: function(e) {
22361    var pointer = this.canvas.getPointer(e);
22362
22363    return this.__mousedownX !== pointer.x ||
22364           this.__mousedownY !== pointer.y;
22365  },
22366
22367  /**
22368   * Initializes "mouseup" event handler
22369   */
22370  initMouseupHandler: function() {
22371    this.on('mouseup', function(options) {
22372      this.__isMousedown = false;
22373      if (this._isObjectMoved(options.e)) {
22374        return;
22375      }
22376
22377      if (this.__lastSelected) {
22378        this.enterEditing();
22379        this.initDelayedCursor(true);
22380      }
22381      this.selected = true;
22382    });
22383  },
22384
22385  /**
22386   * Changes cursor location in a text depending on passed pointer (x/y) object
22387   * @param {Event} e Event object
22388   */
22389  setCursorByClick: function(e) {
22390    var newSelectionStart = this.getSelectionStartFromPointer(e);
22391
22392    if (e.shiftKey) {
22393      if (newSelectionStart < this.selectionStart) {
22394        this.setSelectionEnd(this.selectionStart);
22395        this.setSelectionStart(newSelectionStart);
22396      }
22397      else {
22398        this.setSelectionEnd(newSelectionStart);
22399      }
22400    }
22401    else {
22402      this.setSelectionStart(newSelectionStart);
22403      this.setSelectionEnd(newSelectionStart);
22404    }
22405  },
22406
22407  /**
22408   * @private
22409   * @param {Event} e Event object
22410   * @return {Object} Coordinates of a pointer (x, y)
22411   */
22412  _getLocalRotatedPointer: function(e) {
22413    var pointer = this.canvas.getPointer(e),
22414
22415        pClicked = new fabric.Point(pointer.x, pointer.y),
22416        pLeftTop = new fabric.Point(this.left, this.top),
22417
22418        rotated = fabric.util.rotatePoint(
22419          pClicked, pLeftTop, fabric.util.degreesToRadians(-this.angle));
22420
22421    return this.getLocalPointer(e, rotated);
22422  },
22423
22424  /**
22425   * Returns index of a character corresponding to where an object was clicked
22426   * @param {Event} e Event object
22427   * @return {Number} Index of a character
22428   */
22429  getSelectionStartFromPointer: function(e) {
22430    var mouseOffset = this._getLocalRotatedPointer(e),
22431        prevWidth = 0,
22432        width = 0,
22433        height = 0,
22434        charIndex = 0,
22435        newSelectionStart,
22436        line;
22437
22438    for (var i = 0, len = this._textLines.length; i < len; i++) {
22439      line = this._textLines[i].split('');
22440      height += this._getHeightOfLine(this.ctx, i) * this.scaleY;
22441
22442      var widthOfLine = this._getLineWidth(this.ctx, i),
22443          lineLeftOffset = this._getLineLeftOffset(widthOfLine);
22444
22445      width = lineLeftOffset * this.scaleX;
22446
22447      if (this.flipX) {
22448        // when oject is horizontally flipped we reverse chars
22449        this._textLines[i] = line.reverse().join('');
22450      }
22451
22452      for (var j = 0, jlen = line.length; j < jlen; j++) {
22453
22454        var _char = line[j];
22455        prevWidth = width;
22456
22457        width += this._getWidthOfChar(this.ctx, _char, i, this.flipX ? jlen - j : j) *
22458                 this.scaleX;
22459
22460        if (height <= mouseOffset.y || width <= mouseOffset.x) {
22461          charIndex++;
22462          continue;
22463        }
22464
22465        return this._getNewSelectionStartFromOffset(
22466          mouseOffset, prevWidth, width, charIndex + i, jlen);
22467      }
22468
22469      if (mouseOffset.y < height) {
22470        return this._getNewSelectionStartFromOffset(
22471          mouseOffset, prevWidth, width, charIndex + i, jlen);
22472      }
22473    }
22474
22475    // clicked somewhere after all chars, so set at the end
22476    if (typeof newSelectionStart === 'undefined') {
22477      return this.text.length;
22478    }
22479  },
22480
22481  /**
22482   * @private
22483   */
22484  _getNewSelectionStartFromOffset: function(mouseOffset, prevWidth, width, index, jlen) {
22485
22486    var distanceBtwLastCharAndCursor = mouseOffset.x - prevWidth,
22487        distanceBtwNextCharAndCursor = width - mouseOffset.x,
22488        offset = distanceBtwNextCharAndCursor > distanceBtwLastCharAndCursor ? 0 : 1,
22489        newSelectionStart = index + offset;
22490
22491    // if object is horizontally flipped, mirror cursor location from the end
22492    if (this.flipX) {
22493      newSelectionStart = jlen - newSelectionStart;
22494    }
22495
22496    if (newSelectionStart > this.text.length) {
22497      newSelectionStart = this.text.length;
22498    }
22499
22500    return newSelectionStart;
22501  }
22502});
22503
22504
22505fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ {
22506
22507  /**
22508   * Initializes hidden textarea (needed to bring up keyboard in iOS)
22509   */
22510  initHiddenTextarea: function() {
22511    this.hiddenTextarea = fabric.document.createElement('textarea');
22512
22513    this.hiddenTextarea.setAttribute('autocapitalize', 'off');
22514    this.hiddenTextarea.style.cssText = 'position: fixed; bottom: 20px; left: 0px; opacity: 0;'
22515                                        + ' width: 0px; height: 0px; z-index: -999;';
22516    fabric.document.body.appendChild(this.hiddenTextarea);
22517
22518    fabric.util.addListener(this.hiddenTextarea, 'keydown', this.onKeyDown.bind(this));
22519    fabric.util.addListener(this.hiddenTextarea, 'keypress', this.onKeyPress.bind(this));
22520    fabric.util.addListener(this.hiddenTextarea, 'copy', this.copy.bind(this));
22521    fabric.util.addListener(this.hiddenTextarea, 'paste', this.paste.bind(this));
22522
22523    if (!this._clickHandlerInitialized && this.canvas) {
22524      fabric.util.addListener(this.canvas.upperCanvasEl, 'click', this.onClick.bind(this));
22525      this._clickHandlerInitialized = true;
22526    }
22527  },
22528
22529  /**
22530   * @private
22531   */
22532  _keysMap: {
22533    8:  'removeChars',
22534    9:  'exitEditing',
22535    27: 'exitEditing',
22536    13: 'insertNewline',
22537    33: 'moveCursorUp',
22538    34: 'moveCursorDown',
22539    35: 'moveCursorRight',
22540    36: 'moveCursorLeft',
22541    37: 'moveCursorLeft',
22542    38: 'moveCursorUp',
22543    39: 'moveCursorRight',
22544    40: 'moveCursorDown',
22545    46: 'forwardDelete'
22546  },
22547
22548  /**
22549   * @private
22550   */
22551  _ctrlKeysMap: {
22552    65: 'selectAll',
22553    88: 'cut'
22554  },
22555
22556  onClick: function() {
22557    // No need to trigger click event here, focus is enough to have the keyboard appear on Android
22558    this.hiddenTextarea && this.hiddenTextarea.focus();
22559  },
22560
22561  /**
22562   * Handles keyup event
22563   * @param {Event} e Event object
22564   */
22565  onKeyDown: function(e) {
22566    if (!this.isEditing) {
22567      return;
22568    }
22569    if (e.keyCode in this._keysMap) {
22570      this[this._keysMap[e.keyCode]](e);
22571    }
22572    else if ((e.keyCode in this._ctrlKeysMap) && (e.ctrlKey || e.metaKey)) {
22573      this[this._ctrlKeysMap[e.keyCode]](e);
22574    }
22575    else {
22576      return;
22577    }
22578    e.stopImmediatePropagation();
22579    e.preventDefault();
22580    this.canvas && this.canvas.renderAll();
22581  },
22582
22583  /**
22584   * Forward delete
22585   */
22586  forwardDelete: function(e) {
22587    if (this.selectionStart === this.selectionEnd) {
22588      this.moveCursorRight(e);
22589    }
22590    this.removeChars(e);
22591  },
22592
22593  /**
22594   * Copies selected text
22595   * @param {Event} e Event object
22596   */
22597  copy: function(e) {
22598    var selectedText = this.getSelectedText(),
22599        clipboardData = this._getClipboardData(e);
22600
22601    // Check for backward compatibility with old browsers
22602    if (clipboardData) {
22603      clipboardData.setData('text', selectedText);
22604    }
22605
22606    this.copiedText = selectedText;
22607    this.copiedStyles = this.getSelectionStyles(
22608                          this.selectionStart,
22609                          this.selectionEnd);
22610  },
22611
22612  /**
22613   * Pastes text
22614   * @param {Event} e Event object
22615   */
22616  paste: function(e) {
22617    var copiedText = null,
22618        clipboardData = this._getClipboardData(e);
22619
22620    // Check for backward compatibility with old browsers
22621    if (clipboardData) {
22622      copiedText = clipboardData.getData('text');
22623    }
22624    else {
22625      copiedText = this.copiedText;
22626    }
22627
22628    if (copiedText) {
22629      this.insertChars(copiedText, true);
22630    }
22631  },
22632
22633  /**
22634   * Cuts text
22635   * @param {Event} e Event object
22636   */
22637  cut: function(e) {
22638    if (this.selectionStart === this.selectionEnd) {
22639      return;
22640    }
22641
22642    this.copy();
22643    this.removeChars(e);
22644  },
22645
22646  /**
22647   * @private
22648   * @param {Event} e Event object
22649   * @return {Object} Clipboard data object
22650   */
22651  _getClipboardData: function(e) {
22652    return e && (e.clipboardData || fabric.window.clipboardData);
22653  },
22654
22655  /**
22656   * Handles keypress event
22657   * @param {Event} e Event object
22658   */
22659  onKeyPress: function(e) {
22660    if (!this.isEditing || e.metaKey || e.ctrlKey) {
22661      return;
22662    }
22663    if (e.which !== 0) {
22664      this.insertChars(String.fromCharCode(e.which));
22665    }
22666    e.stopPropagation();
22667  },
22668
22669  /**
22670   * Gets start offset of a selection
22671   * @param {Event} e Event object
22672   * @param {Boolean} isRight
22673   * @return {Number}
22674   */
22675  getDownCursorOffset: function(e, isRight) {
22676    var selectionProp = isRight ? this.selectionEnd : this.selectionStart,
22677        _char, lineLeftOffset,
22678        textBeforeCursor = this.text.slice(0, selectionProp),
22679        textAfterCursor = this.text.slice(selectionProp),
22680
22681        textOnSameLineBeforeCursor = textBeforeCursor.slice(textBeforeCursor.lastIndexOf('\n') + 1),
22682        textOnSameLineAfterCursor = textAfterCursor.match(/(.*)\n?/)[1],
22683        textOnNextLine = (textAfterCursor.match(/.*\n(.*)\n?/) || { })[1] || '',
22684
22685        cursorLocation = this.get2DCursorLocation(selectionProp);
22686
22687    // if on last line, down cursor goes to end of line
22688    if (cursorLocation.lineIndex === this._textLines.length - 1 || e.metaKey || e.keyCode === 34) {
22689
22690      // move to the end of a text
22691      return this.text.length - selectionProp;
22692    }
22693
22694    var widthOfSameLineBeforeCursor = this._getLineWidth(this.ctx, cursorLocation.lineIndex);
22695    lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor);
22696
22697    var widthOfCharsOnSameLineBeforeCursor = lineLeftOffset,
22698        lineIndex = cursorLocation.lineIndex;
22699
22700    for (var i = 0, len = textOnSameLineBeforeCursor.length; i < len; i++) {
22701      _char = textOnSameLineBeforeCursor[i];
22702      widthOfCharsOnSameLineBeforeCursor += this._getWidthOfChar(this.ctx, _char, lineIndex, i);
22703    }
22704
22705    var indexOnNextLine = this._getIndexOnNextLine(
22706      cursorLocation, textOnNextLine, widthOfCharsOnSameLineBeforeCursor);
22707
22708    return textOnSameLineAfterCursor.length + 1 + indexOnNextLine;
22709  },
22710
22711  /**
22712   * @private
22713   */
22714  _getIndexOnNextLine: function(cursorLocation, textOnNextLine, widthOfCharsOnSameLineBeforeCursor) {
22715    var lineIndex = cursorLocation.lineIndex + 1,
22716        widthOfNextLine = this._getLineWidth(this.ctx, lineIndex),
22717        lineLeftOffset = this._getLineLeftOffset(widthOfNextLine),
22718        widthOfCharsOnNextLine = lineLeftOffset,
22719        indexOnNextLine = 0,
22720        foundMatch;
22721
22722    for (var j = 0, jlen = textOnNextLine.length; j < jlen; j++) {
22723
22724      var _char = textOnNextLine[j],
22725          widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
22726
22727      widthOfCharsOnNextLine += widthOfChar;
22728
22729      if (widthOfCharsOnNextLine > widthOfCharsOnSameLineBeforeCursor) {
22730
22731        foundMatch = true;
22732
22733        var leftEdge = widthOfCharsOnNextLine - widthOfChar,
22734            rightEdge = widthOfCharsOnNextLine,
22735            offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor),
22736            offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
22737
22738        indexOnNextLine = offsetFromRightEdge < offsetFromLeftEdge ? j + 1 : j;
22739
22740        break;
22741      }
22742    }
22743
22744    // reached end
22745    if (!foundMatch) {
22746      indexOnNextLine = textOnNextLine.length;
22747    }
22748
22749    return indexOnNextLine;
22750  },
22751
22752  /**
22753   * Moves cursor down
22754   * @param {Event} e Event object
22755   */
22756  moveCursorDown: function(e) {
22757    this.abortCursorAnimation();
22758    this._currentCursorOpacity = 1;
22759
22760    var offset = this.getDownCursorOffset(e, this._selectionDirection === 'right');
22761
22762    if (e.shiftKey) {
22763      this.moveCursorDownWithShift(offset);
22764    }
22765    else {
22766      this.moveCursorDownWithoutShift(offset);
22767    }
22768
22769    this.initDelayedCursor();
22770  },
22771
22772  /**
22773   * Moves cursor down without keeping selection
22774   * @param {Number} offset
22775   */
22776  moveCursorDownWithoutShift: function(offset) {
22777    this._selectionDirection = 'right';
22778    this.setSelectionStart(this.selectionStart + offset);
22779    this.setSelectionEnd(this.selectionStart);
22780  },
22781
22782  /**
22783   * private
22784   */
22785  swapSelectionPoints: function() {
22786    var swapSel = this.selectionEnd;
22787    this.setSelectionEnd(this.selectionStart);
22788    this.setSelectionStart(swapSel);
22789  },
22790
22791  /**
22792   * Moves cursor down while keeping selection
22793   * @param {Number} offset
22794   */
22795  moveCursorDownWithShift: function(offset) {
22796    if (this.selectionEnd === this.selectionStart) {
22797      this._selectionDirection = 'right';
22798    }
22799    if (this._selectionDirection === 'right') {
22800      this.setSelectionEnd(this.selectionEnd + offset);
22801    }
22802    else {
22803      this.setSelectionStart(this.selectionStart + offset);
22804    }
22805    if (this.selectionEnd < this.selectionStart  && this._selectionDirection === 'left') {
22806      this.swapSelectionPoints();
22807      this._selectionDirection = 'right';
22808    }
22809    if (this.selectionEnd > this.text.length) {
22810      this.setSelectionEnd(this.text.length);
22811    }
22812  },
22813
22814  /**
22815   * @param {Event} e Event object
22816   * @param {Boolean} isRight
22817   * @return {Number}
22818   */
22819  getUpCursorOffset: function(e, isRight) {
22820    var selectionProp = isRight ? this.selectionEnd : this.selectionStart,
22821        cursorLocation = this.get2DCursorLocation(selectionProp);
22822    // if on first line, up cursor goes to start of line
22823    if (cursorLocation.lineIndex === 0 || e.metaKey || e.keyCode === 33) {
22824      return selectionProp;
22825    }
22826
22827    var textBeforeCursor = this.text.slice(0, selectionProp),
22828        textOnSameLineBeforeCursor = textBeforeCursor.slice(textBeforeCursor.lastIndexOf('\n') + 1),
22829        textOnPreviousLine = (textBeforeCursor.match(/\n?(.*)\n.*$/) || {})[1] || '',
22830        _char,
22831        widthOfSameLineBeforeCursor = this._getLineWidth(this.ctx, cursorLocation.lineIndex),
22832        lineLeftOffset = this._getLineLeftOffset(widthOfSameLineBeforeCursor),
22833        widthOfCharsOnSameLineBeforeCursor = lineLeftOffset,
22834        lineIndex = cursorLocation.lineIndex;
22835
22836    for (var i = 0, len = textOnSameLineBeforeCursor.length; i < len; i++) {
22837      _char = textOnSameLineBeforeCursor[i];
22838      widthOfCharsOnSameLineBeforeCursor += this._getWidthOfChar(this.ctx, _char, lineIndex, i);
22839    }
22840
22841    var indexOnPrevLine = this._getIndexOnPrevLine(
22842      cursorLocation, textOnPreviousLine, widthOfCharsOnSameLineBeforeCursor);
22843
22844    return textOnPreviousLine.length - indexOnPrevLine + textOnSameLineBeforeCursor.length;
22845  },
22846
22847  /**
22848   * @private
22849   */
22850  _getIndexOnPrevLine: function(cursorLocation, textOnPreviousLine, widthOfCharsOnSameLineBeforeCursor) {
22851
22852    var lineIndex = cursorLocation.lineIndex - 1,
22853        widthOfPreviousLine = this._getLineWidth(this.ctx, lineIndex),
22854        lineLeftOffset = this._getLineLeftOffset(widthOfPreviousLine),
22855        widthOfCharsOnPreviousLine = lineLeftOffset,
22856        indexOnPrevLine = 0,
22857        foundMatch;
22858
22859    for (var j = 0, jlen = textOnPreviousLine.length; j < jlen; j++) {
22860
22861      var _char = textOnPreviousLine[j],
22862          widthOfChar = this._getWidthOfChar(this.ctx, _char, lineIndex, j);
22863
22864      widthOfCharsOnPreviousLine += widthOfChar;
22865
22866      if (widthOfCharsOnPreviousLine > widthOfCharsOnSameLineBeforeCursor) {
22867
22868        foundMatch = true;
22869
22870        var leftEdge = widthOfCharsOnPreviousLine - widthOfChar,
22871            rightEdge = widthOfCharsOnPreviousLine,
22872            offsetFromLeftEdge = Math.abs(leftEdge - widthOfCharsOnSameLineBeforeCursor),
22873            offsetFromRightEdge = Math.abs(rightEdge - widthOfCharsOnSameLineBeforeCursor);
22874
22875        indexOnPrevLine = offsetFromRightEdge < offsetFromLeftEdge ? j : (j - 1);
22876
22877        break;
22878      }
22879    }
22880
22881    // reached end
22882    if (!foundMatch) {
22883      indexOnPrevLine = textOnPreviousLine.length - 1;
22884    }
22885
22886    return indexOnPrevLine;
22887  },
22888
22889  /**
22890   * Moves cursor up
22891   * @param {Event} e Event object
22892   */
22893  moveCursorUp: function(e) {
22894
22895    this.abortCursorAnimation();
22896    this._currentCursorOpacity = 1;
22897
22898    var offset = this.getUpCursorOffset(e, this._selectionDirection === 'right');
22899    if (e.shiftKey) {
22900      this.moveCursorUpWithShift(offset);
22901    }
22902    else {
22903      this.moveCursorUpWithoutShift(offset);
22904    }
22905
22906    this.initDelayedCursor();
22907  },
22908
22909  /**
22910   * Moves cursor up with shift
22911   * @param {Number} offset
22912   */
22913  moveCursorUpWithShift: function(offset) {
22914    if (this.selectionEnd === this.selectionStart) {
22915      this._selectionDirection = 'left';
22916    }
22917    if (this._selectionDirection === 'right') {
22918      this.setSelectionEnd(this.selectionEnd - offset);
22919    }
22920    else {
22921      this.setSelectionStart(this.selectionStart - offset);
22922    }
22923    if (this.selectionEnd < this.selectionStart && this._selectionDirection === 'right') {
22924      this.swapSelectionPoints();
22925      this._selectionDirection = 'left';
22926    }
22927  },
22928
22929  /**
22930   * Moves cursor up without shift
22931   * @param {Number} offset
22932   */
22933  moveCursorUpWithoutShift: function(offset) {
22934    if (this.selectionStart === this.selectionEnd) {
22935      this.setSelectionStart(this.selectionStart - offset);
22936    }
22937    this.setSelectionEnd(this.selectionStart);
22938
22939    this._selectionDirection = 'left';
22940  },
22941
22942  /**
22943   * Moves cursor left
22944   * @param {Event} e Event object
22945   */
22946  moveCursorLeft: function(e) {
22947    if (this.selectionStart === 0 && this.selectionEnd === 0) {
22948      return;
22949    }
22950
22951    this.abortCursorAnimation();
22952    this._currentCursorOpacity = 1;
22953
22954    if (e.shiftKey) {
22955      this.moveCursorLeftWithShift(e);
22956    }
22957    else {
22958      this.moveCursorLeftWithoutShift(e);
22959    }
22960
22961    this.initDelayedCursor();
22962  },
22963
22964  /**
22965   * @private
22966   */
22967  _move: function(e, prop, direction) {
22968    var propMethod = (prop === 'selectionStart' ? 'setSelectionStart' : 'setSelectionEnd');
22969    if (e.altKey) {
22970      this[propMethod](this['findWordBoundary' + direction](this[prop]));
22971    }
22972    else if (e.metaKey || e.keyCode === 35 ||  e.keyCode === 36 ) {
22973      this[propMethod](this['findLineBoundary' + direction](this[prop]));
22974    }
22975    else {
22976      this[propMethod](this[prop] + (direction === 'Left' ? -1 : 1));
22977    }
22978  },
22979
22980  /**
22981   * @private
22982   */
22983  _moveLeft: function(e, prop) {
22984    this._move(e, prop, 'Left');
22985  },
22986
22987  /**
22988   * @private
22989   */
22990  _moveRight: function(e, prop) {
22991    this._move(e, prop, 'Right');
22992  },
22993
22994  /**
22995   * Moves cursor left without keeping selection
22996   * @param {Event} e
22997   */
22998  moveCursorLeftWithoutShift: function(e) {
22999    this._selectionDirection = 'left';
23000
23001    // only move cursor when there is no selection,
23002    // otherwise we discard it, and leave cursor on same place
23003    if (this.selectionEnd === this.selectionStart) {
23004      this._moveLeft(e, 'selectionStart');
23005    }
23006    this.setSelectionEnd(this.selectionStart);
23007  },
23008
23009  /**
23010   * Moves cursor left while keeping selection
23011   * @param {Event} e
23012   */
23013  moveCursorLeftWithShift: function(e) {
23014    if (this._selectionDirection === 'right' && this.selectionStart !== this.selectionEnd) {
23015      this._moveLeft(e, 'selectionEnd');
23016    }
23017    else {
23018      this._selectionDirection = 'left';
23019      this._moveLeft(e, 'selectionStart');
23020
23021      // increase selection by one if it's a newline
23022      if (this.text.charAt(this.selectionStart) === '\n') {
23023        this.setSelectionStart(this.selectionStart - 1);
23024      }
23025    }
23026  },
23027
23028  /**
23029   * Moves cursor right
23030   * @param {Event} e Event object
23031   */
23032  moveCursorRight: function(e) {
23033    if (this.selectionStart >= this.text.length && this.selectionEnd >= this.text.length) {
23034      return;
23035    }
23036
23037    this.abortCursorAnimation();
23038    this._currentCursorOpacity = 1;
23039
23040    if (e.shiftKey) {
23041      this.moveCursorRightWithShift(e);
23042    }
23043    else {
23044      this.moveCursorRightWithoutShift(e);
23045    }
23046
23047    this.initDelayedCursor();
23048  },
23049
23050  /**
23051   * Moves cursor right while keeping selection
23052   * @param {Event} e
23053   */
23054  moveCursorRightWithShift: function(e) {
23055    if (this._selectionDirection === 'left' && this.selectionStart !== this.selectionEnd) {
23056      this._moveRight(e, 'selectionStart');
23057    }
23058    else {
23059      this._selectionDirection = 'right';
23060      this._moveRight(e, 'selectionEnd');
23061
23062      // increase selection by one if it's a newline
23063      if (this.text.charAt(this.selectionEnd - 1) === '\n') {
23064        this.setSelectionEnd(this.selectionEnd + 1);
23065      }
23066    }
23067  },
23068
23069  /**
23070   * Moves cursor right without keeping selection
23071   * @param {Event} e Event object
23072   */
23073  moveCursorRightWithoutShift: function(e) {
23074    this._selectionDirection = 'right';
23075
23076    if (this.selectionStart === this.selectionEnd) {
23077      this._moveRight(e, 'selectionStart');
23078      this.setSelectionEnd(this.selectionStart);
23079    }
23080    else {
23081      this.setSelectionEnd(this.selectionEnd + this.getNumNewLinesInSelectedText());
23082      this.setSelectionStart(this.selectionEnd);
23083    }
23084  },
23085
23086  /**
23087   * Removes characters selected by selection
23088   * @param {Event} e Event object
23089   */
23090  removeChars: function(e) {
23091    if (this.selectionStart === this.selectionEnd) {
23092      this._removeCharsNearCursor(e);
23093    }
23094    else {
23095      this._removeCharsFromTo(this.selectionStart, this.selectionEnd);
23096    }
23097
23098    this.setSelectionEnd(this.selectionStart);
23099
23100    this._removeExtraneousStyles();
23101
23102    this._clearCache();
23103    this.canvas && this.canvas.renderAll();
23104
23105    this.setCoords();
23106    this.fire('changed');
23107    this.canvas && this.canvas.fire('text:changed', { target: this });
23108  },
23109
23110  /**
23111   * @private
23112   * @param {Event} e Event object
23113   */
23114  _removeCharsNearCursor: function(e) {
23115    if (this.selectionStart !== 0) {
23116
23117      if (e.metaKey) {
23118        // remove all till the start of current line
23119        var leftLineBoundary = this.findLineBoundaryLeft(this.selectionStart);
23120
23121        this._removeCharsFromTo(leftLineBoundary, this.selectionStart);
23122        this.setSelectionStart(leftLineBoundary);
23123      }
23124      else if (e.altKey) {
23125        // remove all till the start of current word
23126        var leftWordBoundary = this.findWordBoundaryLeft(this.selectionStart);
23127
23128        this._removeCharsFromTo(leftWordBoundary, this.selectionStart);
23129        this.setSelectionStart(leftWordBoundary);
23130      }
23131      else {
23132        var isBeginningOfLine = this.text.slice(this.selectionStart - 1, this.selectionStart) === '\n';
23133        this.removeStyleObject(isBeginningOfLine);
23134        this.setSelectionStart(this.selectionStart - 1);
23135        this.text = this.text.slice(0, this.selectionStart) +
23136                    this.text.slice(this.selectionStart + 1);
23137      }
23138    }
23139  }
23140});
23141
23142
23143/* _TO_SVG_START_ */
23144fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ {
23145
23146  /**
23147   * @private
23148   */
23149  _setSVGTextLineText: function(lineIndex, textSpans, height, textLeftOffset, textTopOffset, textBgRects) {
23150    if (!this.styles[lineIndex]) {
23151      this.callSuper('_setSVGTextLineText',
23152        lineIndex, textSpans, height, textLeftOffset, textTopOffset);
23153    }
23154    else {
23155      this._setSVGTextLineChars(
23156        lineIndex, textSpans, height, textLeftOffset, textBgRects);
23157    }
23158  },
23159
23160  /**
23161   * @private
23162   */
23163  _setSVGTextLineChars: function(lineIndex, textSpans, height, textLeftOffset, textBgRects) {
23164
23165    var chars = this._textLines[lineIndex].split(''),
23166        charOffset = 0,
23167        lineLeftOffset = this._getSVGLineLeftOffset(lineIndex) - this.width / 2,
23168        lineOffset = this._getSVGLineTopOffset(lineIndex),
23169        heightOfLine = this._getHeightOfLine(this.ctx, lineIndex);
23170
23171    for (var i = 0, len = chars.length; i < len; i++) {
23172      var styleDecl = this.styles[lineIndex][i] || { };
23173
23174      textSpans.push(
23175        this._createTextCharSpan(
23176          chars[i], styleDecl, lineLeftOffset, lineOffset.lineTop + lineOffset.offset, charOffset));
23177
23178      var charWidth = this._getWidthOfChar(this.ctx, chars[i], lineIndex, i);
23179
23180      if (styleDecl.textBackgroundColor) {
23181        textBgRects.push(
23182          this._createTextCharBg(
23183            styleDecl, lineLeftOffset, lineOffset.lineTop, heightOfLine, charWidth, charOffset));
23184      }
23185
23186      charOffset += charWidth;
23187    }
23188  },
23189
23190  /**
23191   * @private
23192   */
23193  _getSVGLineLeftOffset: function(lineIndex) {
23194    return fabric.util.toFixed(this._getLineLeftOffset(this.__lineWidths[lineIndex]), 2);
23195  },
23196
23197  /**
23198   * @private
23199   */
23200  _getSVGLineTopOffset: function(lineIndex) {
23201    var lineTopOffset = 0, lastHeight = 0;
23202    for (var j = 0; j < lineIndex; j++) {
23203      lineTopOffset += this._getHeightOfLine(this.ctx, j);
23204    }
23205    lastHeight = this._getHeightOfLine(this.ctx, j);
23206    return {
23207      lineTop: lineTopOffset,
23208      offset: (this._fontSizeMult - this._fontSizeFraction) * lastHeight / (this.lineHeight * this._fontSizeMult)
23209    };
23210  },
23211
23212  /**
23213   * @private
23214   */
23215  _createTextCharBg: function(styleDecl, lineLeftOffset, lineTopOffset, heightOfLine, charWidth, charOffset) {
23216    return [
23217      //jscs:disable validateIndentation
23218      '<rect fill="', styleDecl.textBackgroundColor,
23219      '" x="', lineLeftOffset + charOffset,
23220      '" y="', lineTopOffset - this.height/2,
23221      '" width="', charWidth,
23222      '" height="', heightOfLine / this.lineHeight,
23223      '"></rect>'
23224      //jscs:enable validateIndentation
23225    ].join('');
23226  },
23227
23228  /**
23229   * @private
23230   */
23231  _createTextCharSpan: function(_char, styleDecl, lineLeftOffset, lineTopOffset, charOffset) {
23232
23233    var fillStyles = this.getSvgStyles.call(fabric.util.object.extend({
23234      visible: true,
23235      fill: this.fill,
23236      stroke: this.stroke,
23237      type: 'text'
23238    }, styleDecl));
23239
23240    return [
23241      //jscs:disable validateIndentation
23242      '<tspan x="', lineLeftOffset + charOffset, '" y="',
23243        lineTopOffset - this.height/2, '" ',
23244        (styleDecl.fontFamily ? 'font-family="' + styleDecl.fontFamily.replace(/"/g, '\'') + '" ': ''),
23245        (styleDecl.fontSize ? 'font-size="' + styleDecl.fontSize + '" ': ''),
23246        (styleDecl.fontStyle ? 'font-style="' + styleDecl.fontStyle + '" ': ''),
23247        (styleDecl.fontWeight ? 'font-weight="' + styleDecl.fontWeight + '" ': ''),
23248        (styleDecl.textDecoration ? 'text-decoration="' + styleDecl.textDecoration + '" ': ''),
23249        'style="', fillStyles, '">',
23250        fabric.util.string.escapeXml(_char),
23251      '</tspan>'
23252      //jscs:enable validateIndentation
23253    ].join('');
23254  }
23255});
23256/* _TO_SVG_END_ */
23257
23258
23259(function() {
23260
23261  if (typeof document !== 'undefined' && typeof window !== 'undefined') {
23262    return;
23263  }
23264
23265  var DOMParser = require('xmldom').DOMParser,
23266      URL = require('url'),
23267      HTTP = require('http'),
23268      HTTPS = require('https'),
23269
23270      Canvas = require('canvas'),
23271      Image = require('canvas').Image;
23272
23273  /** @private */
23274  function request(url, encoding, callback) {
23275    var oURL = URL.parse(url);
23276
23277    // detect if http or https is used
23278    if ( !oURL.port ) {
23279      oURL.port = ( oURL.protocol.indexOf('https:') === 0 ) ? 443 : 80;
23280    }
23281
23282    // assign request handler based on protocol
23283    var reqHandler = (oURL.protocol.indexOf('https:') === 0 ) ? HTTPS : HTTP,
23284        req = reqHandler.request({
23285          hostname: oURL.hostname,
23286          port: oURL.port,
23287          path: oURL.path,
23288          method: 'GET'
23289        }, function(response) {
23290          var body = '';
23291          if (encoding) {
23292            response.setEncoding(encoding);
23293          }
23294          response.on('end', function () {
23295            callback(body);
23296          });
23297          response.on('data', function (chunk) {
23298            if (response.statusCode === 200) {
23299              body += chunk;
23300            }
23301          });
23302        });
23303
23304    req.on('error', function(err) {
23305      if (err.errno === process.ECONNREFUSED) {
23306        fabric.log('ECONNREFUSED: connection refused to ' + oURL.hostname + ':' + oURL.port);
23307      }
23308      else {
23309        fabric.log(err.message);
23310      }
23311    });
23312
23313    req.end();
23314  }
23315
23316  /** @private */
23317  function requestFs(path, callback) {
23318    var fs = require('fs');
23319    fs.readFile(path, function (err, data) {
23320      if (err) {
23321        fabric.log(err);
23322        throw err;
23323      }
23324      else {
23325        callback(data);
23326      }
23327    });
23328  }
23329
23330  fabric.util.loadImage = function(url, callback, context) {
23331    function createImageAndCallBack(data) {
23332      img.src = new Buffer(data, 'binary');
23333      // preserving original url, which seems to be lost in node-canvas
23334      img._src = url;
23335      callback && callback.call(context, img);
23336    }
23337    var img = new Image();
23338    if (url && (url instanceof Buffer || url.indexOf('data') === 0)) {
23339      img.src = img._src = url;
23340      callback && callback.call(context, img);
23341    }
23342    else if (url && url.indexOf('http') !== 0) {
23343      requestFs(url, createImageAndCallBack);
23344    }
23345    else if (url) {
23346      request(url, 'binary', createImageAndCallBack);
23347    }
23348    else {
23349      callback && callback.call(context, url);
23350    }
23351  };
23352
23353  fabric.loadSVGFromURL = function(url, callback, reviver) {
23354    url = url.replace(/^\n\s*/, '').replace(/\?.*$/, '').trim();
23355    if (url.indexOf('http') !== 0) {
23356      requestFs(url, function(body) {
23357        fabric.loadSVGFromString(body.toString(), callback, reviver);
23358      });
23359    }
23360    else {
23361      request(url, '', function(body) {
23362        fabric.loadSVGFromString(body, callback, reviver);
23363      });
23364    }
23365  };
23366
23367  fabric.loadSVGFromString = function(string, callback, reviver) {
23368    var doc = new DOMParser().parseFromString(string);
23369    fabric.parseSVGDocument(doc.documentElement, function(results, options) {
23370      callback && callback(results, options);
23371    }, reviver);
23372  };
23373
23374  fabric.util.getScript = function(url, callback) {
23375    request(url, '', function(body) {
23376      eval(body);
23377      callback && callback();
23378    });
23379  };
23380
23381  fabric.Image.fromObject = function(object, callback) {
23382    fabric.util.loadImage(object.src, function(img) {
23383      var oImg = new fabric.Image(img);
23384
23385      oImg._initConfig(object);
23386      oImg._initFilters(object, function(filters) {
23387        oImg.filters = filters || [ ];
23388        callback && callback(oImg);
23389      });
23390    });
23391  };
23392
23393  /**
23394   * Only available when running fabric on node.js
23395   * @param {Number} width Canvas width
23396   * @param {Number} height Canvas height
23397   * @param {Object} [options] Options to pass to FabricCanvas.
23398   * @param {Object} [nodeCanvasOptions] Options to pass to NodeCanvas.
23399   * @return {Object} wrapped canvas instance
23400   */
23401  fabric.createCanvasForNode = function(width, height, options, nodeCanvasOptions) {
23402    nodeCanvasOptions = nodeCanvasOptions || options;
23403
23404    var canvasEl = fabric.document.createElement('canvas'),
23405        nodeCanvas = new Canvas(width || 600, height || 600, nodeCanvasOptions);
23406
23407    // jsdom doesn't create style on canvas element, so here be temp. workaround
23408    canvasEl.style = { };
23409
23410    canvasEl.width = nodeCanvas.width;
23411    canvasEl.height = nodeCanvas.height;
23412
23413    var FabricCanvas = fabric.Canvas || fabric.StaticCanvas,
23414        fabricCanvas = new FabricCanvas(canvasEl, options);
23415
23416    fabricCanvas.contextContainer = nodeCanvas.getContext('2d');
23417    fabricCanvas.nodeCanvas = nodeCanvas;
23418    fabricCanvas.Font = Canvas.Font;
23419
23420    return fabricCanvas;
23421  };
23422
23423  /** @ignore */
23424  fabric.StaticCanvas.prototype.createPNGStream = function() {
23425    return this.nodeCanvas.createPNGStream();
23426  };
23427
23428  fabric.StaticCanvas.prototype.createJPEGStream = function(opts) {
23429    return this.nodeCanvas.createJPEGStream(opts);
23430  };
23431
23432  var origSetWidth = fabric.StaticCanvas.prototype.setWidth;
23433  fabric.StaticCanvas.prototype.setWidth = function(width, options) {
23434    origSetWidth.call(this, width, options);
23435    this.nodeCanvas.width = width;
23436    return this;
23437  };
23438  if (fabric.Canvas) {
23439    fabric.Canvas.prototype.setWidth = fabric.StaticCanvas.prototype.setWidth;
23440  }
23441
23442  var origSetHeight = fabric.StaticCanvas.prototype.setHeight;
23443  fabric.StaticCanvas.prototype.setHeight = function(height, options) {
23444    origSetHeight.call(this, height, options);
23445    this.nodeCanvas.height = height;
23446    return this;
23447  };
23448  if (fabric.Canvas) {
23449    fabric.Canvas.prototype.setHeight = fabric.StaticCanvas.prototype.setHeight;
23450  }
23451
23452})();
23453