Browse code

Created repository.

DoubleBastionAdmin authored on 26/01/2022 20:32:42
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,29109 @@
1
+/**
2
+ *  Copyright (C) 2021  Double Bastion LLC
3
+ *
4
+ *  This file is part of Roundpin, which is licensed under the
5
+ *  GNU Affero General Public License Version 3.0. The license terms
6
+ *  are detailed in the "LICENSE.txt" file located in the root directory.
7
+ *
8
+ *  The file content from below is identical with that of the
9
+ *  original file "fabric.js" released under the MIT License:
10
+ *  https://github.com/fabricjs/fabric.js/blob/master/LICENSE .
11
+ *  The copyright notice for the original content follows:
12
+ */
13
+
14
+/* build: `node build.js modules=ALL exclude=gestures,accessors requirejs minifier=uglifyjs` */
15
+/*! Fabric.js Copyright 2008-2015, Printio (Juriy Zaytsev, Maxim Chernyak) */
16
+
17
+var fabric = fabric || { version: '2.4.6' };
18
+if (typeof exports !== 'undefined') {
19
+  exports.fabric = fabric;
20
+}
21
+/* _AMD_START_ */
22
+else if (typeof define === 'function' && define.amd) {
23
+  define([], function() { return fabric; });
24
+}
25
+/* _AMD_END_ */
26
+if (typeof document !== 'undefined' && typeof window !== 'undefined') {
27
+  fabric.document = document;
28
+  fabric.window = window;
29
+}
30
+else {
31
+  // assume we're running under node.js when document/window are not present
32
+  fabric.document = require('jsdom')
33
+    .jsdom(
34
+      decodeURIComponent('%3C!DOCTYPE%20html%3E%3Chtml%3E%3Chead%3E%3C%2Fhead%3E%3Cbody%3E%3C%2Fbody%3E%3C%2Fhtml%3E'),
35
+      { features: {
36
+        FetchExternalResources: ['img']
37
+      }
38
+      });
39
+  fabric.jsdomImplForWrapper = require('jsdom/lib/jsdom/living/generated/utils').implForWrapper;
40
+  fabric.nodeCanvas = require('jsdom/lib/jsdom/utils').Canvas;
41
+  fabric.window = fabric.document.defaultView;
42
+  DOMParser = require('xmldom').DOMParser;
43
+}
44
+
45
+/**
46
+ * True when in environment that supports touch events
47
+ * @type boolean
48
+ */
49
+fabric.isTouchSupported = 'ontouchstart' in fabric.window || 'ontouchstart' in fabric.document ||
50
+  (fabric.window && fabric.window.navigator && fabric.window.navigator.maxTouchPoints > 0);
51
+
52
+/**
53
+ * True when in environment that's probably Node.js
54
+ * @type boolean
55
+ */
56
+fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
57
+                      typeof window === 'undefined';
58
+
59
+/* _FROM_SVG_START_ */
60
+/**
61
+ * Attributes parsed from all SVG elements
62
+ * @type array
63
+ */
64
+fabric.SHARED_ATTRIBUTES = [
65
+  'display',
66
+  'transform',
67
+  'fill', 'fill-opacity', 'fill-rule',
68
+  'opacity',
69
+  'stroke', 'stroke-dasharray', 'stroke-linecap', 'stroke-dashoffset',
70
+  'stroke-linejoin', 'stroke-miterlimit',
71
+  'stroke-opacity', 'stroke-width',
72
+  'id', 'paint-order',
73
+  'instantiated_by_use', 'clip-path'
74
+];
75
+/* _FROM_SVG_END_ */
76
+
77
+/**
78
+ * Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion.
79
+ */
80
+fabric.DPI = 96;
81
+fabric.reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)';
82
+fabric.fontPaths = { };
83
+fabric.iMatrix = [1, 0, 0, 1, 0, 0];
84
+fabric.canvasModule = 'canvas';
85
+
86
+/**
87
+ * Pixel limit for cache canvases. 1Mpx , 4Mpx should be fine.
88
+ * @since 1.7.14
89
+ * @type Number
90
+ * @default
91
+ */
92
+fabric.perfLimitSizeTotal = 2097152;
93
+
94
+/**
95
+ * Pixel limit for cache canvases width or height. IE fixes the maximum at 5000
96
+ * @since 1.7.14
97
+ * @type Number
98
+ * @default
99
+ */
100
+fabric.maxCacheSideLimit = 4096;
101
+
102
+/**
103
+ * Lowest pixel limit for cache canvases, set at 256PX
104
+ * @since 1.7.14
105
+ * @type Number
106
+ * @default
107
+ */
108
+fabric.minCacheSideLimit = 256;
109
+
110
+/**
111
+ * Cache Object for widths of chars in text rendering.
112
+ */
113
+fabric.charWidthsCache = { };
114
+
115
+/**
116
+ * if webgl is enabled and available, textureSize will determine the size
117
+ * of the canvas backend
118
+ * @since 2.0.0
119
+ * @type Number
120
+ * @default
121
+ */
122
+fabric.textureSize = 2048;
123
+
124
+/**
125
+ * Enable webgl for filtering picture is available
126
+ * A filtering backend will be initialized, this will both take memory and
127
+ * time since a default 2048x2048 canvas will be created for the gl context
128
+ * @since 2.0.0
129
+ * @type Boolean
130
+ * @default
131
+ */
132
+fabric.enableGLFiltering = true;
133
+
134
+/**
135
+ * Device Pixel Ratio
136
+ * @see https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/HTML-canvas-guide/SettingUptheCanvas/SettingUptheCanvas.html
137
+ */
138
+fabric.devicePixelRatio = fabric.window.devicePixelRatio ||
139
+                          fabric.window.webkitDevicePixelRatio ||
140
+                          fabric.window.mozDevicePixelRatio ||
141
+                          1;
142
+/**
143
+ * Browser-specific constant to adjust CanvasRenderingContext2D.shadowBlur value,
144
+ * which is unitless and not rendered equally across browsers.
145
+ *
146
+ * Values that work quite well (as of October 2017) are:
147
+ * - Chrome: 1.5
148
+ * - Edge: 1.75
149
+ * - Firefox: 0.9
150
+ * - Safari: 0.95
151
+ *
152
+ * @since 2.0.0
153
+ * @type Number
154
+ * @default 1
155
+ */
156
+fabric.browserShadowBlurConstant = 1;
157
+
158
+/**
159
+ * This object contains the result of arc to beizer conversion for faster retrieving if the same arc needs to be converted again.
160
+ * It was an internal variable, is accessible since version 2.3.4
161
+ */
162
+fabric.arcToSegmentsCache = { };
163
+
164
+/**
165
+ * This object keeps the results of the boundsOfCurve calculation mapped by the joined arguments necessary to calculate it.
166
+ * It does speed up calculation, if you parse and add always the same paths, but in case of heavy usage of freedrawing
167
+ * you do not get any speed benefit and you get a big object in memory.
168
+ * The object was a private variable before, while now is appended to the lib so that you have access to it and you
169
+ * can eventually clear it.
170
+ * It was an internal variable, is accessible since version 2.3.4
171
+ */
172
+fabric.boundsOfCurveCache = { };
173
+
174
+/**
175
+ * If disabled boundsOfCurveCache is not used. For apps that make heavy usage of pencil drawing probably disabling it is better
176
+ * @default true
177
+ */
178
+fabric.cachesBoundsOfCurve = true;
179
+
180
+fabric.initFilterBackend = function() {
181
+  if (fabric.enableGLFiltering && fabric.isWebglSupported && fabric.isWebglSupported(fabric.textureSize)) {
182
+    console.log('max texture size: ' + fabric.maxTextureSize);
183
+    return (new fabric.WebglFilterBackend({ tileSize: fabric.textureSize }));
184
+  }
185
+  else if (fabric.Canvas2dFilterBackend) {
186
+    return (new fabric.Canvas2dFilterBackend());
187
+  }
188
+};
189
+
190
+
191
+if (typeof document !== 'undefined' && typeof window !== 'undefined') {
192
+  // ensure globality even if entire library were function wrapped (as in Meteor.js packaging system)
193
+  window.fabric = fabric;
194
+}
195
+
196
+
197
+(function() {
198
+
199
+  /**
200
+   * @private
201
+   * @param {String} eventName
202
+   * @param {Function} handler
203
+   */
204
+  function _removeEventListener(eventName, handler) {
205
+    if (!this.__eventListeners[eventName]) {
206
+      return;
207
+    }
208
+    var eventListener = this.__eventListeners[eventName];
209
+    if (handler) {
210
+      eventListener[eventListener.indexOf(handler)] = false;
211
+    }
212
+    else {
213
+      fabric.util.array.fill(eventListener, false);
214
+    }
215
+  }
216
+
217
+  /**
218
+   * Observes specified event
219
+   * @deprecated `observe` deprecated since 0.8.34 (use `on` instead)
220
+   * @memberOf fabric.Observable
221
+   * @alias on
222
+   * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
223
+   * @param {Function} handler Function that receives a notification when an event of the specified type occurs
224
+   * @return {Self} thisArg
225
+   * @chainable
226
+   */
227
+  function observe(eventName, handler) {
228
+    if (!this.__eventListeners) {
229
+      this.__eventListeners = { };
230
+    }
231
+    // one object with key/value pairs was passed
232
+    if (arguments.length === 1) {
233
+      for (var prop in eventName) {
234
+        this.on(prop, eventName[prop]);
235
+      }
236
+    }
237
+    else {
238
+      if (!this.__eventListeners[eventName]) {
239
+        this.__eventListeners[eventName] = [];
240
+      }
241
+      this.__eventListeners[eventName].push(handler);
242
+    }
243
+    return this;
244
+  }
245
+
246
+  /**
247
+   * Stops event observing for a particular event handler. Calling this method
248
+   * without arguments removes all handlers for all events
249
+   * @deprecated `stopObserving` deprecated since 0.8.34 (use `off` instead)
250
+   * @memberOf fabric.Observable
251
+   * @alias off
252
+   * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
253
+   * @param {Function} handler Function to be deleted from EventListeners
254
+   * @return {Self} thisArg
255
+   * @chainable
256
+   */
257
+  function stopObserving(eventName, handler) {
258
+    if (!this.__eventListeners) {
259
+      return;
260
+    }
261
+
262
+    // remove all key/value pairs (event name -> event handler)
263
+    if (arguments.length === 0) {
264
+      for (eventName in this.__eventListeners) {
265
+        _removeEventListener.call(this, eventName);
266
+      }
267
+    }
268
+    // one object with key/value pairs was passed
269
+    else if (arguments.length === 1 && typeof arguments[0] === 'object') {
270
+      for (var prop in eventName) {
271
+        _removeEventListener.call(this, prop, eventName[prop]);
272
+      }
273
+    }
274
+    else {
275
+      _removeEventListener.call(this, eventName, handler);
276
+    }
277
+    return this;
278
+  }
279
+
280
+  /**
281
+   * Fires event with an optional options object
282
+   * @deprecated `fire` deprecated since 1.0.7 (use `trigger` instead)
283
+   * @memberOf fabric.Observable
284
+   * @alias trigger
285
+   * @param {String} eventName Event name to fire
286
+   * @param {Object} [options] Options object
287
+   * @return {Self} thisArg
288
+   * @chainable
289
+   */
290
+  function fire(eventName, options) {
291
+    if (!this.__eventListeners) {
292
+      return;
293
+    }
294
+
295
+    var listenersForEvent = this.__eventListeners[eventName];
296
+    if (!listenersForEvent) {
297
+      return;
298
+    }
299
+
300
+    for (var i = 0, len = listenersForEvent.length; i < len; i++) {
301
+      listenersForEvent[i] && listenersForEvent[i].call(this, options || { });
302
+    }
303
+    this.__eventListeners[eventName] = listenersForEvent.filter(function(value) {
304
+      return value !== false;
305
+    });
306
+    return this;
307
+  }
308
+
309
+  /**
310
+   * @namespace fabric.Observable
311
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#events}
312
+   * @see {@link http://fabricjs.com/events|Events demo}
313
+   */
314
+  fabric.Observable = {
315
+    observe: observe,
316
+    stopObserving: stopObserving,
317
+    fire: fire,
318
+
319
+    on: observe,
320
+    off: stopObserving,
321
+    trigger: fire
322
+  };
323
+})();
324
+
325
+
326
+/**
327
+ * @namespace fabric.Collection
328
+ */
329
+fabric.Collection = {
330
+
331
+  _objects: [],
332
+
333
+  /**
334
+   * Adds objects to collection, Canvas or Group, then renders canvas
335
+   * (if `renderOnAddRemove` is not `false`).
336
+   * in case of Group no changes to bounding box are made.
337
+   * Objects should be instances of (or inherit from) fabric.Object
338
+   * Use of this function is highly discouraged for groups.
339
+   * you can add a bunch of objects with the add method but then you NEED
340
+   * to run a addWithUpdate call for the Group class or position/bbox will be wrong.
341
+   * @param {...fabric.Object} object Zero or more fabric instances
342
+   * @return {Self} thisArg
343
+   * @chainable
344
+   */
345
+  add: function () {
346
+    this._objects.push.apply(this._objects, arguments);
347
+    if (this._onObjectAdded) {
348
+      for (var i = 0, length = arguments.length; i < length; i++) {
349
+        this._onObjectAdded(arguments[i]);
350
+      }
351
+    }
352
+    this.renderOnAddRemove && this.requestRenderAll();
353
+    return this;
354
+  },
355
+
356
+  /**
357
+   * Inserts an object into collection at specified index, then renders canvas (if `renderOnAddRemove` is not `false`)
358
+   * An object should be an instance of (or inherit from) fabric.Object
359
+   * Use of this function is highly discouraged for groups.
360
+   * you can add a bunch of objects with the insertAt method but then you NEED
361
+   * to run a addWithUpdate call for the Group class or position/bbox will be wrong.
362
+   * @param {Object} object Object to insert
363
+   * @param {Number} index Index to insert object at
364
+   * @param {Boolean} nonSplicing When `true`, no splicing (shifting) of objects occurs
365
+   * @return {Self} thisArg
366
+   * @chainable
367
+   */
368
+  insertAt: function (object, index, nonSplicing) {
369
+    var objects = this._objects;
370
+    if (nonSplicing) {
371
+      objects[index] = object;
372
+    }
373
+    else {
374
+      objects.splice(index, 0, object);
375
+    }
376
+    this._onObjectAdded && this._onObjectAdded(object);
377
+    this.renderOnAddRemove && this.requestRenderAll();
378
+    return this;
379
+  },
380
+
381
+  /**
382
+   * Removes objects from a collection, then renders canvas (if `renderOnAddRemove` is not `false`)
383
+   * @param {...fabric.Object} object Zero or more fabric instances
384
+   * @return {Self} thisArg
385
+   * @chainable
386
+   */
387
+  remove: function() {
388
+    var objects = this._objects,
389
+        index, somethingRemoved = false;
390
+
391
+    for (var i = 0, length = arguments.length; i < length; i++) {
392
+      index = objects.indexOf(arguments[i]);
393
+
394
+      // only call onObjectRemoved if an object was actually removed
395
+      if (index !== -1) {
396
+        somethingRemoved = true;
397
+        objects.splice(index, 1);
398
+        this._onObjectRemoved && this._onObjectRemoved(arguments[i]);
399
+      }
400
+    }
401
+
402
+    this.renderOnAddRemove && somethingRemoved && this.requestRenderAll();
403
+    return this;
404
+  },
405
+
406
+  /**
407
+   * Executes given function for each object in this group
408
+   * @param {Function} callback
409
+   *                   Callback invoked with current object as first argument,
410
+   *                   index - as second and an array of all objects - as third.
411
+   *                   Callback is invoked in a context of Global Object (e.g. `window`)
412
+   *                   when no `context` argument is given
413
+   *
414
+   * @param {Object} context Context (aka thisObject)
415
+   * @return {Self} thisArg
416
+   * @chainable
417
+   */
418
+  forEachObject: function(callback, context) {
419
+    var objects = this.getObjects();
420
+    for (var i = 0, len = objects.length; i < len; i++) {
421
+      callback.call(context, objects[i], i, objects);
422
+    }
423
+    return this;
424
+  },
425
+
426
+  /**
427
+   * Returns an array of children objects of this instance
428
+   * Type parameter introduced in 1.3.10
429
+   * since 2.3.5 this method return always a COPY of the array;
430
+   * @param {String} [type] When specified, only objects of this type are returned
431
+   * @return {Array}
432
+   */
433
+  getObjects: function(type) {
434
+    if (typeof type === 'undefined') {
435
+      return this._objects.concat();
436
+    }
437
+    return this._objects.filter(function(o) {
438
+      return o.type === type;
439
+    });
440
+  },
441
+
442
+  /**
443
+   * Returns object at specified index
444
+   * @param {Number} index
445
+   * @return {Self} thisArg
446
+   */
447
+  item: function (index) {
448
+    return this._objects[index];
449
+  },
450
+
451
+  /**
452
+   * Returns true if collection contains no objects
453
+   * @return {Boolean} true if collection is empty
454
+   */
455
+  isEmpty: function () {
456
+    return this._objects.length === 0;
457
+  },
458
+
459
+  /**
460
+   * Returns a size of a collection (i.e: length of an array containing its objects)
461
+   * @return {Number} Collection size
462
+   */
463
+  size: function() {
464
+    return this._objects.length;
465
+  },
466
+
467
+  /**
468
+   * Returns true if collection contains an object
469
+   * @param {Object} object Object to check against
470
+   * @return {Boolean} `true` if collection contains an object
471
+   */
472
+  contains: function(object) {
473
+    return this._objects.indexOf(object) > -1;
474
+  },
475
+
476
+  /**
477
+   * Returns number representation of a collection complexity
478
+   * @return {Number} complexity
479
+   */
480
+  complexity: function () {
481
+    return this._objects.reduce(function (memo, current) {
482
+      memo += current.complexity ? current.complexity() : 0;
483
+      return memo;
484
+    }, 0);
485
+  }
486
+};
487
+
488
+
489
+/**
490
+ * @namespace fabric.CommonMethods
491
+ */
492
+fabric.CommonMethods = {
493
+
494
+  /**
495
+   * Sets object's properties from options
496
+   * @param {Object} [options] Options object
497
+   */
498
+  _setOptions: function(options) {
499
+    for (var prop in options) {
500
+      this.set(prop, options[prop]);
501
+    }
502
+  },
503
+
504
+  /**
505
+   * @private
506
+   * @param {Object} [filler] Options object
507
+   * @param {String} [property] property to set the Gradient to
508
+   */
509
+  _initGradient: function(filler, property) {
510
+    if (filler && filler.colorStops && !(filler instanceof fabric.Gradient)) {
511
+      this.set(property, new fabric.Gradient(filler));
512
+    }
513
+  },
514
+
515
+  /**
516
+   * @private
517
+   * @param {Object} [filler] Options object
518
+   * @param {String} [property] property to set the Pattern to
519
+   * @param {Function} [callback] callback to invoke after pattern load
520
+   */
521
+  _initPattern: function(filler, property, callback) {
522
+    if (filler && filler.source && !(filler instanceof fabric.Pattern)) {
523
+      this.set(property, new fabric.Pattern(filler, callback));
524
+    }
525
+    else {
526
+      callback && callback();
527
+    }
528
+  },
529
+
530
+  /**
531
+   * @private
532
+   * @param {Object} [options] Options object
533
+   */
534
+  _initClipping: function(options) {
535
+    if (!options.clipTo || typeof options.clipTo !== 'string') {
536
+      return;
537
+    }
538
+
539
+    var functionBody = fabric.util.getFunctionBody(options.clipTo);
540
+    if (typeof functionBody !== 'undefined') {
541
+      this.clipTo = new Function('ctx', functionBody);
542
+    }
543
+  },
544
+
545
+  /**
546
+   * @private
547
+   */
548
+  _setObject: function(obj) {
549
+    for (var prop in obj) {
550
+      this._set(prop, obj[prop]);
551
+    }
552
+  },
553
+
554
+  /**
555
+   * 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()`.
556
+   * @param {String|Object} key Property name or object (if object, iterate over the object properties)
557
+   * @param {Object|Function} value Property value (if function, the value is passed into it and its return value is used as a new one)
558
+   * @return {fabric.Object} thisArg
559
+   * @chainable
560
+   */
561
+  set: function(key, value) {
562
+    if (typeof key === 'object') {
563
+      this._setObject(key);
564
+    }
565
+    else {
566
+      if (typeof value === 'function' && key !== 'clipTo') {
567
+        this._set(key, value(this.get(key)));
568
+      }
569
+      else {
570
+        this._set(key, value);
571
+      }
572
+    }
573
+    return this;
574
+  },
575
+
576
+  _set: function(key, value) {
577
+    this[key] = value;
578
+  },
579
+
580
+  /**
581
+   * Toggles specified property from `true` to `false` or from `false` to `true`
582
+   * @param {String} property Property to toggle
583
+   * @return {fabric.Object} thisArg
584
+   * @chainable
585
+   */
586
+  toggle: function(property) {
587
+    var value = this.get(property);
588
+    if (typeof value === 'boolean') {
589
+      this.set(property, !value);
590
+    }
591
+    return this;
592
+  },
593
+
594
+  /**
595
+   * Basic getter
596
+   * @param {String} property Property name
597
+   * @return {*} value of a property
598
+   */
599
+  get: function(property) {
600
+    return this[property];
601
+  }
602
+};
603
+
604
+
605
+(function(global) {
606
+
607
+  var sqrt = Math.sqrt,
608
+      atan2 = Math.atan2,
609
+      pow = Math.pow,
610
+      abs = Math.abs,
611
+      PiBy180 = Math.PI / 180,
612
+      PiBy2 = Math.PI / 2;
613
+
614
+  /**
615
+   * @namespace fabric.util
616
+   */
617
+  fabric.util = {
618
+
619
+    /**
620
+     * Calculate the cos of an angle, avoiding returning floats for known results
621
+     * @static
622
+     * @memberOf fabric.util
623
+     * @param {Number} angle the angle in radians or in degree
624
+     * @return {Number}
625
+     */
626
+    cos: function(angle) {
627
+      if (angle === 0) { return 1; }
628
+      if (angle < 0) {
629
+        // cos(a) = cos(-a)
630
+        angle = -angle;
631
+      }
632
+      var angleSlice = angle / PiBy2;
633
+      switch (angleSlice) {
634
+        case 1: case 3: return 0;
635
+        case 2: return -1;
636
+      }
637
+      return Math.cos(angle);
638
+    },
639
+
640
+    /**
641
+     * Calculate the sin of an angle, avoiding returning floats for known results
642
+     * @static
643
+     * @memberOf fabric.util
644
+     * @param {Number} angle the angle in radians or in degree
645
+     * @return {Number}
646
+     */
647
+    sin: function(angle) {
648
+      if (angle === 0) { return 0; }
649
+      var angleSlice = angle / PiBy2, sign = 1;
650
+      if (angle < 0) {
651
+        // sin(-a) = -sin(a)
652
+        sign = -1;
653
+      }
654
+      switch (angleSlice) {
655
+        case 1: return sign;
656
+        case 2: return 0;
657
+        case 3: return -sign;
658
+      }
659
+      return Math.sin(angle);
660
+    },
661
+
662
+    /**
663
+     * Removes value from an array.
664
+     * Presence of value (and its position in an array) is determined via `Array.prototype.indexOf`
665
+     * @static
666
+     * @memberOf fabric.util
667
+     * @param {Array} array
668
+     * @param {*} value
669
+     * @return {Array} original array
670
+     */
671
+    removeFromArray: function(array, value) {
672
+      var idx = array.indexOf(value);
673
+      if (idx !== -1) {
674
+        array.splice(idx, 1);
675
+      }
676
+      return array;
677
+    },
678
+
679
+    /**
680
+     * Returns random number between 2 specified ones.
681
+     * @static
682
+     * @memberOf fabric.util
683
+     * @param {Number} min lower limit
684
+     * @param {Number} max upper limit
685
+     * @return {Number} random value (between min and max)
686
+     */
687
+    getRandomInt: function(min, max) {
688
+      return Math.floor(Math.random() * (max - min + 1)) + min;
689
+    },
690
+
691
+    /**
692
+     * Transforms degrees to radians.
693
+     * @static
694
+     * @memberOf fabric.util
695
+     * @param {Number} degrees value in degrees
696
+     * @return {Number} value in radians
697
+     */
698
+    degreesToRadians: function(degrees) {
699
+      return degrees * PiBy180;
700
+    },
701
+
702
+    /**
703
+     * Transforms radians to degrees.
704
+     * @static
705
+     * @memberOf fabric.util
706
+     * @param {Number} radians value in radians
707
+     * @return {Number} value in degrees
708
+     */
709
+    radiansToDegrees: function(radians) {
710
+      return radians / PiBy180;
711
+    },
712
+
713
+    /**
714
+     * Rotates `point` around `origin` with `radians`
715
+     * @static
716
+     * @memberOf fabric.util
717
+     * @param {fabric.Point} point The point to rotate
718
+     * @param {fabric.Point} origin The origin of the rotation
719
+     * @param {Number} radians The radians of the angle for the rotation
720
+     * @return {fabric.Point} The new rotated point
721
+     */
722
+    rotatePoint: function(point, origin, radians) {
723
+      point.subtractEquals(origin);
724
+      var v = fabric.util.rotateVector(point, radians);
725
+      return new fabric.Point(v.x, v.y).addEquals(origin);
726
+    },
727
+
728
+    /**
729
+     * Rotates `vector` with `radians`
730
+     * @static
731
+     * @memberOf fabric.util
732
+     * @param {Object} vector The vector to rotate (x and y)
733
+     * @param {Number} radians The radians of the angle for the rotation
734
+     * @return {Object} The new rotated point
735
+     */
736
+    rotateVector: function(vector, radians) {
737
+      var sin = fabric.util.sin(radians),
738
+          cos = fabric.util.cos(radians),
739
+          rx = vector.x * cos - vector.y * sin,
740
+          ry = vector.x * sin + vector.y * cos;
741
+      return {
742
+        x: rx,
743
+        y: ry
744
+      };
745
+    },
746
+
747
+    /**
748
+     * Apply transform t to point p
749
+     * @static
750
+     * @memberOf fabric.util
751
+     * @param  {fabric.Point} p The point to transform
752
+     * @param  {Array} t The transform
753
+     * @param  {Boolean} [ignoreOffset] Indicates that the offset should not be applied
754
+     * @return {fabric.Point} The transformed point
755
+     */
756
+    transformPoint: function(p, t, ignoreOffset) {
757
+      if (ignoreOffset) {
758
+        return new fabric.Point(
759
+          t[0] * p.x + t[2] * p.y,
760
+          t[1] * p.x + t[3] * p.y
761
+        );
762
+      }
763
+      return new fabric.Point(
764
+        t[0] * p.x + t[2] * p.y + t[4],
765
+        t[1] * p.x + t[3] * p.y + t[5]
766
+      );
767
+    },
768
+
769
+    /**
770
+     * Returns coordinates of points's bounding rectangle (left, top, width, height)
771
+     * @param {Array} points 4 points array
772
+     * @return {Object} Object with left, top, width, height properties
773
+     */
774
+    makeBoundingBoxFromPoints: function(points) {
775
+      var xPoints = [points[0].x, points[1].x, points[2].x, points[3].x],
776
+          minX = fabric.util.array.min(xPoints),
777
+          maxX = fabric.util.array.max(xPoints),
778
+          width = maxX - minX,
779
+          yPoints = [points[0].y, points[1].y, points[2].y, points[3].y],
780
+          minY = fabric.util.array.min(yPoints),
781
+          maxY = fabric.util.array.max(yPoints),
782
+          height = maxY - minY;
783
+
784
+      return {
785
+        left: minX,
786
+        top: minY,
787
+        width: width,
788
+        height: height
789
+      };
790
+    },
791
+
792
+    /**
793
+     * Invert transformation t
794
+     * @static
795
+     * @memberOf fabric.util
796
+     * @param {Array} t The transform
797
+     * @return {Array} The inverted transform
798
+     */
799
+    invertTransform: function(t) {
800
+      var a = 1 / (t[0] * t[3] - t[1] * t[2]),
801
+          r = [a * t[3], -a * t[1], -a * t[2], a * t[0]],
802
+          o = fabric.util.transformPoint({ x: t[4], y: t[5] }, r, true);
803
+      r[4] = -o.x;
804
+      r[5] = -o.y;
805
+      return r;
806
+    },
807
+
808
+    /**
809
+     * A wrapper around Number#toFixed, which contrary to native method returns number, not string.
810
+     * @static
811
+     * @memberOf fabric.util
812
+     * @param {Number|String} number number to operate on
813
+     * @param {Number} fractionDigits number of fraction digits to "leave"
814
+     * @return {Number}
815
+     */
816
+    toFixed: function(number, fractionDigits) {
817
+      return parseFloat(Number(number).toFixed(fractionDigits));
818
+    },
819
+
820
+    /**
821
+     * Converts from attribute value to pixel value if applicable.
822
+     * Returns converted pixels or original value not converted.
823
+     * @param {Number|String} value number to operate on
824
+     * @param {Number} fontSize
825
+     * @return {Number|String}
826
+     */
827
+    parseUnit: function(value, fontSize) {
828
+      var unit = /\D{0,2}$/.exec(value),
829
+          number = parseFloat(value);
830
+      if (!fontSize) {
831
+        fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
832
+      }
833
+      switch (unit[0]) {
834
+        case 'mm':
835
+          return number * fabric.DPI / 25.4;
836
+
837
+        case 'cm':
838
+          return number * fabric.DPI / 2.54;
839
+
840
+        case 'in':
841
+          return number * fabric.DPI;
842
+
843
+        case 'pt':
844
+          return number * fabric.DPI / 72; // or * 4 / 3
845
+
846
+        case 'pc':
847
+          return number * fabric.DPI / 72 * 12; // or * 16
848
+
849
+        case 'em':
850
+          return number * fontSize;
851
+
852
+        default:
853
+          return number;
854
+      }
855
+    },
856
+
857
+    /**
858
+     * Function which always returns `false`.
859
+     * @static
860
+     * @memberOf fabric.util
861
+     * @return {Boolean}
862
+     */
863
+    falseFunction: function() {
864
+      return false;
865
+    },
866
+
867
+    /**
868
+     * Returns klass "Class" object of given namespace
869
+     * @memberOf fabric.util
870
+     * @param {String} type Type of object (eg. 'circle')
871
+     * @param {String} namespace Namespace to get klass "Class" object from
872
+     * @return {Object} klass "Class"
873
+     */
874
+    getKlass: function(type, namespace) {
875
+      // capitalize first letter only
876
+      type = fabric.util.string.camelize(type.charAt(0).toUpperCase() + type.slice(1));
877
+      return fabric.util.resolveNamespace(namespace)[type];
878
+    },
879
+
880
+    /**
881
+     * Returns array of attributes for given svg that fabric parses
882
+     * @memberOf fabric.util
883
+     * @param {String} type Type of svg element (eg. 'circle')
884
+     * @return {Array} string names of supported attributes
885
+     */
886
+    getSvgAttributes: function(type) {
887
+      var attributes = [
888
+        'instantiated_by_use',
889
+        'style',
890
+        'id',
891
+        'class'
892
+      ];
893
+      switch (type) {
894
+        case 'linearGradient':
895
+          attributes = attributes.concat(['x1', 'y1', 'x2', 'y2', 'gradientUnits', 'gradientTransform']);
896
+          break;
897
+        case 'radialGradient':
898
+          attributes = attributes.concat(['gradientUnits', 'gradientTransform', 'cx', 'cy', 'r', 'fx', 'fy', 'fr']);
899
+          break;
900
+        case 'stop':
901
+          attributes = attributes.concat(['offset', 'stop-color', 'stop-opacity']);
902
+          break;
903
+      }
904
+      return attributes;
905
+    },
906
+
907
+    /**
908
+     * Returns object of given namespace
909
+     * @memberOf fabric.util
910
+     * @param {String} namespace Namespace string e.g. 'fabric.Image.filter' or 'fabric'
911
+     * @return {Object} Object for given namespace (default fabric)
912
+     */
913
+    resolveNamespace: function(namespace) {
914
+      if (!namespace) {
915
+        return fabric;
916
+      }
917
+
918
+      var parts = namespace.split('.'),
919
+          len = parts.length, i,
920
+          obj = global || fabric.window;
921
+
922
+      for (i = 0; i < len; ++i) {
923
+        obj = obj[parts[i]];
924
+      }
925
+
926
+      return obj;
927
+    },
928
+
929
+    /**
930
+     * Loads image element from given url and passes it to a callback
931
+     * @memberOf fabric.util
932
+     * @param {String} url URL representing an image
933
+     * @param {Function} callback Callback; invoked with loaded image
934
+     * @param {*} [context] Context to invoke callback in
935
+     * @param {Object} [crossOrigin] crossOrigin value to set image element to
936
+     */
937
+    loadImage: function(url, callback, context, crossOrigin) {
938
+      if (!url) {
939
+        callback && callback.call(context, url);
940
+        return;
941
+      }
942
+
943
+      var img = fabric.util.createImage();
944
+
945
+      /** @ignore */
946
+      var onLoadCallback = function () {
947
+        callback && callback.call(context, img);
948
+        img = img.onload = img.onerror = null;
949
+      };
950
+
951
+      img.onload = onLoadCallback;
952
+      /** @ignore */
953
+      img.onerror = function() {
954
+        fabric.log('Error loading ' + img.src);
955
+        callback && callback.call(context, null, true);
956
+        img = img.onload = img.onerror = null;
957
+      };
958
+
959
+      // data-urls appear to be buggy with crossOrigin
960
+      // https://github.com/kangax/fabric.js/commit/d0abb90f1cd5c5ef9d2a94d3fb21a22330da3e0a#commitcomment-4513767
961
+      // see https://code.google.com/p/chromium/issues/detail?id=315152
962
+      //     https://bugzilla.mozilla.org/show_bug.cgi?id=935069
963
+      if (url.indexOf('data') !== 0 && crossOrigin) {
964
+        img.crossOrigin = crossOrigin;
965
+      }
966
+
967
+      // IE10 / IE11-Fix: SVG contents from data: URI
968
+      // will only be available if the IMG is present
969
+      // in the DOM (and visible)
970
+      if (url.substring(0,14) === 'data:image/svg') {
971
+        img.onload = null;
972
+        fabric.util.loadImageInDom(img, onLoadCallback);
973
+      }
974
+
975
+      img.src = url;
976
+    },
977
+
978
+    /**
979
+     * Attaches SVG image with data: URL to the dom
980
+     * @memberOf fabric.util
981
+     * @param {Object} img Image object with data:image/svg src
982
+     * @param {Function} callback Callback; invoked with loaded image
983
+     * @return {Object} DOM element (div containing the SVG image)
984
+     */
985
+    loadImageInDom: function(img, onLoadCallback) {
986
+      var div = fabric.document.createElement('div');
987
+      div.style.width = div.style.height = '1px';
988
+      div.style.left = div.style.top = '-100%';
989
+      div.style.position = 'absolute';
990
+      div.appendChild(img);
991
+      fabric.document.querySelector('body').appendChild(div);
992
+      /**
993
+       * Wrap in function to:
994
+       *   1. Call existing callback
995
+       *   2. Cleanup DOM
996
+       */
997
+      img.onload = function () {
998
+        onLoadCallback();
999
+        div.parentNode.removeChild(div);
1000
+        div = null;
1001
+      };
1002
+    },
1003
+
1004
+    /**
1005
+     * Creates corresponding fabric instances from their object representations
1006
+     * @static
1007
+     * @memberOf fabric.util
1008
+     * @param {Array} objects Objects to enliven
1009
+     * @param {Function} callback Callback to invoke when all objects are created
1010
+     * @param {String} namespace Namespace to get klass "Class" object from
1011
+     * @param {Function} reviver Method for further parsing of object elements,
1012
+     * called after each fabric object created.
1013
+     */
1014
+    enlivenObjects: function(objects, callback, namespace, reviver) {
1015
+      objects = objects || [];
1016
+
1017
+      function onLoaded() {
1018
+        if (++numLoadedObjects === numTotalObjects) {
1019
+          callback && callback(enlivenedObjects);
1020
+        }
1021
+      }
1022
+
1023
+      var enlivenedObjects = [],
1024
+          numLoadedObjects = 0,
1025
+          numTotalObjects = objects.length;
1026
+
1027
+      if (!numTotalObjects) {
1028
+        callback && callback(enlivenedObjects);
1029
+        return;
1030
+      }
1031
+
1032
+      objects.forEach(function (o, index) {
1033
+        // if sparse array
1034
+        if (!o || !o.type) {
1035
+          onLoaded();
1036
+          return;
1037
+        }
1038
+        var klass = fabric.util.getKlass(o.type, namespace);
1039
+        klass.fromObject(o, function (obj, error) {
1040
+          error || (enlivenedObjects[index] = obj);
1041
+          reviver && reviver(o, obj, error);
1042
+          onLoaded();
1043
+        });
1044
+      });
1045
+    },
1046
+
1047
+    /**
1048
+     * Create and wait for loading of patterns
1049
+     * @static
1050
+     * @memberOf fabric.util
1051
+     * @param {Array} patterns Objects to enliven
1052
+     * @param {Function} callback Callback to invoke when all objects are created
1053
+     * called after each fabric object created.
1054
+     */
1055
+    enlivenPatterns: function(patterns, callback) {
1056
+      patterns = patterns || [];
1057
+
1058
+      function onLoaded() {
1059
+        if (++numLoadedPatterns === numPatterns) {
1060
+          callback && callback(enlivenedPatterns);
1061
+        }
1062
+      }
1063
+
1064
+      var enlivenedPatterns = [],
1065
+          numLoadedPatterns = 0,
1066
+          numPatterns = patterns.length;
1067
+
1068
+      if (!numPatterns) {
1069
+        callback && callback(enlivenedPatterns);
1070
+        return;
1071
+      }
1072
+
1073
+      patterns.forEach(function (p, index) {
1074
+        if (p && p.source) {
1075
+          new fabric.Pattern(p, function(pattern) {
1076
+            enlivenedPatterns[index] = pattern;
1077
+            onLoaded();
1078
+          });
1079
+        }
1080
+        else {
1081
+          enlivenedPatterns[index] = p;
1082
+          onLoaded();
1083
+        }
1084
+      });
1085
+    },
1086
+
1087
+    /**
1088
+     * Groups SVG elements (usually those retrieved from SVG document)
1089
+     * @static
1090
+     * @memberOf fabric.util
1091
+     * @param {Array} elements SVG elements to group
1092
+     * @param {Object} [options] Options object
1093
+     * @param {String} path Value to set sourcePath to
1094
+     * @return {fabric.Object|fabric.Group}
1095
+     */
1096
+    groupSVGElements: function(elements, options, path) {
1097
+      var object;
1098
+      if (elements && elements.length === 1) {
1099
+        return elements[0];
1100
+      }
1101
+      if (options) {
1102
+        if (options.width && options.height) {
1103
+          options.centerPoint = {
1104
+            x: options.width / 2,
1105
+            y: options.height / 2
1106
+          };
1107
+        }
1108
+        else {
1109
+          delete options.width;
1110
+          delete options.height;
1111
+        }
1112
+      }
1113
+      object = new fabric.Group(elements, options);
1114
+      if (typeof path !== 'undefined') {
1115
+        object.sourcePath = path;
1116
+      }
1117
+      return object;
1118
+    },
1119
+
1120
+    /**
1121
+     * Populates an object with properties of another object
1122
+     * @static
1123
+     * @memberOf fabric.util
1124
+     * @param {Object} source Source object
1125
+     * @param {Object} destination Destination object
1126
+     * @return {Array} properties Properties names to include
1127
+     */
1128
+    populateWithProperties: function(source, destination, properties) {
1129
+      if (properties && Object.prototype.toString.call(properties) === '[object Array]') {
1130
+        for (var i = 0, len = properties.length; i < len; i++) {
1131
+          if (properties[i] in source) {
1132
+            destination[properties[i]] = source[properties[i]];
1133
+          }
1134
+        }
1135
+      }
1136
+    },
1137
+
1138
+    /**
1139
+     * Draws a dashed line between two points
1140
+     *
1141
+     * This method is used to draw dashed line around selection area.
1142
+     * See <a href="http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas">dotted stroke in canvas</a>
1143
+     *
1144
+     * @param {CanvasRenderingContext2D} ctx context
1145
+     * @param {Number} x  start x coordinate
1146
+     * @param {Number} y start y coordinate
1147
+     * @param {Number} x2 end x coordinate
1148
+     * @param {Number} y2 end y coordinate
1149
+     * @param {Array} da dash array pattern
1150
+     */
1151
+    drawDashedLine: function(ctx, x, y, x2, y2, da) {
1152
+      var dx = x2 - x,
1153
+          dy = y2 - y,
1154
+          len = sqrt(dx * dx + dy * dy),
1155
+          rot = atan2(dy, dx),
1156
+          dc = da.length,
1157
+          di = 0,
1158
+          draw = true;
1159
+
1160
+      ctx.save();
1161
+      ctx.translate(x, y);
1162
+      ctx.moveTo(0, 0);
1163
+      ctx.rotate(rot);
1164
+
1165
+      x = 0;
1166
+      while (len > x) {
1167
+        x += da[di++ % dc];
1168
+        if (x > len) {
1169
+          x = len;
1170
+        }
1171
+        ctx[draw ? 'lineTo' : 'moveTo'](x, 0);
1172
+        draw = !draw;
1173
+      }
1174
+
1175
+      ctx.restore();
1176
+    },
1177
+
1178
+    /**
1179
+     * Creates canvas element
1180
+     * @static
1181
+     * @memberOf fabric.util
1182
+     * @return {CanvasElement} initialized canvas element
1183
+     */
1184
+    createCanvasElement: function() {
1185
+      return fabric.document.createElement('canvas');
1186
+    },
1187
+
1188
+    /**
1189
+     * Creates a canvas element that is a copy of another and is also painted
1190
+     * @static
1191
+     * @memberOf fabric.util
1192
+     * @return {CanvasElement} initialized canvas element
1193
+     */
1194
+    copyCanvasElement: function(canvas) {
1195
+      var newCanvas = fabric.util.createCanvasElement();
1196
+      newCanvas.width = canvas.width;
1197
+      newCanvas.height = canvas.height;
1198
+      newCanvas.getContext('2d').drawImage(canvas, 0, 0);
1199
+      return newCanvas;
1200
+    },
1201
+
1202
+    /**
1203
+     * Creates image element (works on client and node)
1204
+     * @static
1205
+     * @memberOf fabric.util
1206
+     * @return {HTMLImageElement} HTML image element
1207
+     */
1208
+    createImage: function() {
1209
+      return fabric.document.createElement('img');
1210
+    },
1211
+
1212
+    /**
1213
+     * @static
1214
+     * @memberOf fabric.util
1215
+     * @deprecated since 2.0.0
1216
+     * @param {fabric.Object} receiver Object implementing `clipTo` method
1217
+     * @param {CanvasRenderingContext2D} ctx Context to clip
1218
+     */
1219
+    clipContext: function(receiver, ctx) {
1220
+      ctx.save();
1221
+      ctx.beginPath();
1222
+      receiver.clipTo(ctx);
1223
+      ctx.clip();
1224
+    },
1225
+
1226
+    /**
1227
+     * Multiply matrix A by matrix B to nest transformations
1228
+     * @static
1229
+     * @memberOf fabric.util
1230
+     * @param  {Array} a First transformMatrix
1231
+     * @param  {Array} b Second transformMatrix
1232
+     * @param  {Boolean} is2x2 flag to multiply matrices as 2x2 matrices
1233
+     * @return {Array} The product of the two transform matrices
1234
+     */
1235
+    multiplyTransformMatrices: function(a, b, is2x2) {
1236
+      // Matrix multiply a * b
1237
+      return [
1238
+        a[0] * b[0] + a[2] * b[1],
1239
+        a[1] * b[0] + a[3] * b[1],
1240
+        a[0] * b[2] + a[2] * b[3],
1241
+        a[1] * b[2] + a[3] * b[3],
1242
+        is2x2 ? 0 : a[0] * b[4] + a[2] * b[5] + a[4],
1243
+        is2x2 ? 0 : a[1] * b[4] + a[3] * b[5] + a[5]
1244
+      ];
1245
+    },
1246
+
1247
+    /**
1248
+     * Decomposes standard 2x2 matrix into transform componentes
1249
+     * @static
1250
+     * @memberOf fabric.util
1251
+     * @param  {Array} a transformMatrix
1252
+     * @return {Object} Components of transform
1253
+     */
1254
+    qrDecompose: function(a) {
1255
+      var angle = atan2(a[1], a[0]),
1256
+          denom = pow(a[0], 2) + pow(a[1], 2),
1257
+          scaleX = sqrt(denom),
1258
+          scaleY = (a[0] * a[3] - a[2] * a [1]) / scaleX,
1259
+          skewX = atan2(a[0] * a[2] + a[1] * a [3], denom);
1260
+      return {
1261
+        angle: angle  / PiBy180,
1262
+        scaleX: scaleX,
1263
+        scaleY: scaleY,
1264
+        skewX: skewX / PiBy180,
1265
+        skewY: 0,
1266
+        translateX: a[4],
1267
+        translateY: a[5]
1268
+      };
1269
+    },
1270
+
1271
+    customTransformMatrix: function(scaleX, scaleY, skewX) {
1272
+      var skewMatrixX = [1, 0, abs(Math.tan(skewX * PiBy180)), 1],
1273
+          scaleMatrix = [abs(scaleX), 0, 0, abs(scaleY)];
1274
+      return fabric.util.multiplyTransformMatrices(scaleMatrix, skewMatrixX, true);
1275
+    },
1276
+
1277
+    /**
1278
+     * reset an object transform state to neutral. Top and left are not accounted for
1279
+     * @static
1280
+     * @memberOf fabric.util
1281
+     * @param  {fabric.Object} target object to transform
1282
+     */
1283
+    resetObjectTransform: function (target) {
1284
+      target.scaleX = 1;
1285
+      target.scaleY = 1;
1286
+      target.skewX = 0;
1287
+      target.skewY = 0;
1288
+      target.flipX = false;
1289
+      target.flipY = false;
1290
+      target.rotate(0);
1291
+    },
1292
+
1293
+    /**
1294
+     * Extract Object transform values
1295
+     * @static
1296
+     * @memberOf fabric.util
1297
+     * @param  {fabric.Object} target object to read from
1298
+     * @return {Object} Components of transform
1299
+     */
1300
+    saveObjectTransform: function (target) {
1301
+      return {
1302
+        scaleX: target.scaleX,
1303
+        scaleY: target.scaleY,
1304
+        skewX: target.skewX,
1305
+        skewY: target.skewY,
1306
+        angle: target.angle,
1307
+        left: target.left,
1308
+        flipX: target.flipX,
1309
+        flipY: target.flipY,
1310
+        top: target.top
1311
+      };
1312
+    },
1313
+
1314
+    /**
1315
+     * Returns string representation of function body
1316
+     * @param {Function} fn Function to get body of
1317
+     * @return {String} Function body
1318
+     */
1319
+    getFunctionBody: function(fn) {
1320
+      return (String(fn).match(/function[^{]*\{([\s\S]*)\}/) || {})[1];
1321
+    },
1322
+
1323
+    /**
1324
+     * Returns true if context has transparent pixel
1325
+     * at specified location (taking tolerance into account)
1326
+     * @param {CanvasRenderingContext2D} ctx context
1327
+     * @param {Number} x x coordinate
1328
+     * @param {Number} y y coordinate
1329
+     * @param {Number} tolerance Tolerance
1330
+     */
1331
+    isTransparent: function(ctx, x, y, tolerance) {
1332
+
1333
+      // If tolerance is > 0 adjust start coords to take into account.
1334
+      // If moves off Canvas fix to 0
1335
+      if (tolerance > 0) {
1336
+        if (x > tolerance) {
1337
+          x -= tolerance;
1338
+        }
1339
+        else {
1340
+          x = 0;
1341
+        }
1342
+        if (y > tolerance) {
1343
+          y -= tolerance;
1344
+        }
1345
+        else {
1346
+          y = 0;
1347
+        }
1348
+      }
1349
+
1350
+      var _isTransparent = true, i, temp,
1351
+          imageData = ctx.getImageData(x, y, (tolerance * 2) || 1, (tolerance * 2) || 1),
1352
+          l = imageData.data.length;
1353
+
1354
+      // Split image data - for tolerance > 1, pixelDataSize = 4;
1355
+      for (i = 3; i < l; i += 4) {
1356
+        temp = imageData.data[i];
1357
+        _isTransparent = temp <= 0;
1358
+        if (_isTransparent === false) {
1359
+          break; // Stop if colour found
1360
+        }
1361
+      }
1362
+
1363
+      imageData = null;
1364
+
1365
+      return _isTransparent;
1366
+    },
1367
+
1368
+    /**
1369
+     * Parse preserveAspectRatio attribute from element
1370
+     * @param {string} attribute to be parsed
1371
+     * @return {Object} an object containing align and meetOrSlice attribute
1372
+     */
1373
+    parsePreserveAspectRatioAttribute: function(attribute) {
1374
+      var meetOrSlice = 'meet', alignX = 'Mid', alignY = 'Mid',
1375
+          aspectRatioAttrs = attribute.split(' '), align;
1376
+
1377
+      if (aspectRatioAttrs && aspectRatioAttrs.length) {
1378
+        meetOrSlice = aspectRatioAttrs.pop();
1379
+        if (meetOrSlice !== 'meet' && meetOrSlice !== 'slice') {
1380
+          align = meetOrSlice;
1381
+          meetOrSlice = 'meet';
1382
+        }
1383
+        else if (aspectRatioAttrs.length) {
1384
+          align = aspectRatioAttrs.pop();
1385
+        }
1386
+      }
1387
+      //divide align in alignX and alignY
1388
+      alignX = align !== 'none' ? align.slice(1, 4) : 'none';
1389
+      alignY = align !== 'none' ? align.slice(5, 8) : 'none';
1390
+      return {
1391
+        meetOrSlice: meetOrSlice,
1392
+        alignX: alignX,
1393
+        alignY: alignY
1394
+      };
1395
+    },
1396
+
1397
+    /**
1398
+     * Clear char widths cache for the given font family or all the cache if no
1399
+     * fontFamily is specified.
1400
+     * Use it if you know you are loading fonts in a lazy way and you are not waiting
1401
+     * for custom fonts to load properly when adding text objects to the canvas.
1402
+     * If a text object is added when its own font is not loaded yet, you will get wrong
1403
+     * measurement and so wrong bounding boxes.
1404
+     * After the font cache is cleared, either change the textObject text content or call
1405
+     * initDimensions() to trigger a recalculation
1406
+     * @memberOf fabric.util
1407
+     * @param {String} [fontFamily] font family to clear
1408
+     */
1409
+    clearFabricFontCache: function(fontFamily) {
1410
+      fontFamily = (fontFamily || '').toLowerCase();
1411
+      if (!fontFamily) {
1412
+        fabric.charWidthsCache = { };
1413
+      }
1414
+      else if (fabric.charWidthsCache[fontFamily]) {
1415
+        delete fabric.charWidthsCache[fontFamily];
1416
+      }
1417
+    },
1418
+
1419
+    /**
1420
+     * Given current aspect ratio, determines the max width and height that can
1421
+     * respect the total allowed area for the cache.
1422
+     * @memberOf fabric.util
1423
+     * @param {Number} ar aspect ratio
1424
+     * @param {Number} maximumArea Maximum area you want to achieve
1425
+     * @return {Object.x} Limited dimensions by X
1426
+     * @return {Object.y} Limited dimensions by Y
1427
+     */
1428
+    limitDimsByArea: function(ar, maximumArea) {
1429
+      var roughWidth = Math.sqrt(maximumArea * ar),
1430
+          perfLimitSizeY = Math.floor(maximumArea / roughWidth);
1431
+      return { x: Math.floor(roughWidth), y: perfLimitSizeY };
1432
+    },
1433
+
1434
+    capValue: function(min, value, max) {
1435
+      return Math.max(min, Math.min(value, max));
1436
+    },
1437
+
1438
+    findScaleToFit: function(source, destination) {
1439
+      return Math.min(destination.width / source.width, destination.height / source.height);
1440
+    },
1441
+
1442
+    findScaleToCover: function(source, destination) {
1443
+      return Math.max(destination.width / source.width, destination.height / source.height);
1444
+    }
1445
+  };
1446
+})(typeof exports !== 'undefined' ? exports : this);
1447
+
1448
+
1449
+(function() {
1450
+
1451
+  var _join = Array.prototype.join;
1452
+
1453
+  /* Adapted from http://dxr.mozilla.org/mozilla-central/source/content/svg/content/src/nsSVGPathDataParser.cpp
1454
+   * by Andrea Bogazzi code is under MPL. if you don't have a copy of the license you can take it here
1455
+   * http://mozilla.org/MPL/2.0/
1456
+   */
1457
+  function arcToSegments(toX, toY, rx, ry, large, sweep, rotateX) {
1458
+    var argsString = _join.call(arguments);
1459
+    if (fabric.arcToSegmentsCache[argsString]) {
1460
+      return fabric.arcToSegmentsCache[argsString];
1461
+    }
1462
+
1463
+    var PI = Math.PI, th = rotateX * PI / 180,
1464
+        sinTh = fabric.util.sin(th),
1465
+        cosTh = fabric.util.cos(th),
1466
+        fromX = 0, fromY = 0;
1467
+
1468
+    rx = Math.abs(rx);
1469
+    ry = Math.abs(ry);
1470
+
1471
+    var px = -cosTh * toX * 0.5 - sinTh * toY * 0.5,
1472
+        py = -cosTh * toY * 0.5 + sinTh * toX * 0.5,
1473
+        rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px,
1474
+        pl = rx2 * ry2 - rx2 * py2 - ry2 * px2,
1475
+        root = 0;
1476
+
1477
+    if (pl < 0) {
1478
+      var s = Math.sqrt(1 - pl / (rx2 * ry2));
1479
+      rx *= s;
1480
+      ry *= s;
1481
+    }
1482
+    else {
1483
+      root = (large === sweep ? -1.0 : 1.0) *
1484
+              Math.sqrt( pl / (rx2 * py2 + ry2 * px2));
1485
+    }
1486
+
1487
+    var cx = root * rx * py / ry,
1488
+        cy = -root * ry * px / rx,
1489
+        cx1 = cosTh * cx - sinTh * cy + toX * 0.5,
1490
+        cy1 = sinTh * cx + cosTh * cy + toY * 0.5,
1491
+        mTheta = calcVectorAngle(1, 0, (px - cx) / rx, (py - cy) / ry),
1492
+        dtheta = calcVectorAngle((px - cx) / rx, (py - cy) / ry, (-px - cx) / rx, (-py - cy) / ry);
1493
+
1494
+    if (sweep === 0 && dtheta > 0) {
1495
+      dtheta -= 2 * PI;
1496
+    }
1497
+    else if (sweep === 1 && dtheta < 0) {
1498
+      dtheta += 2 * PI;
1499
+    }
1500
+
1501
+    // Convert into cubic bezier segments <= 90deg
1502
+    var segments = Math.ceil(Math.abs(dtheta / PI * 2)),
1503
+        result = [], mDelta = dtheta / segments,
1504
+        mT = 8 / 3 * Math.sin(mDelta / 4) * Math.sin(mDelta / 4) / Math.sin(mDelta / 2),
1505
+        th3 = mTheta + mDelta;
1506
+
1507
+    for (var i = 0; i < segments; i++) {
1508
+      result[i] = segmentToBezier(mTheta, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY);
1509
+      fromX = result[i][4];
1510
+      fromY = result[i][5];
1511
+      mTheta = th3;
1512
+      th3 += mDelta;
1513
+    }
1514
+    fabric.arcToSegmentsCache[argsString] = result;
1515
+    return result;
1516
+  }
1517
+
1518
+  function segmentToBezier(th2, th3, cosTh, sinTh, rx, ry, cx1, cy1, mT, fromX, fromY) {
1519
+    var costh2 = fabric.util.cos(th2),
1520
+        sinth2 = fabric.util.sin(th2),
1521
+        costh3 = fabric.util.cos(th3),
1522
+        sinth3 = fabric.util.sin(th3),
1523
+        toX = cosTh * rx * costh3 - sinTh * ry * sinth3 + cx1,
1524
+        toY = sinTh * rx * costh3 + cosTh * ry * sinth3 + cy1,
1525
+        cp1X = fromX + mT * ( -cosTh * rx * sinth2 - sinTh * ry * costh2),
1526
+        cp1Y = fromY + mT * ( -sinTh * rx * sinth2 + cosTh * ry * costh2),
1527
+        cp2X = toX + mT * ( cosTh * rx * sinth3 + sinTh * ry * costh3),
1528
+        cp2Y = toY + mT * ( sinTh * rx * sinth3 - cosTh * ry * costh3);
1529
+
1530
+    return [
1531
+      cp1X, cp1Y,
1532
+      cp2X, cp2Y,
1533
+      toX, toY
1534
+    ];
1535
+  }
1536
+
1537
+  /*
1538
+   * Private
1539
+   */
1540
+  function calcVectorAngle(ux, uy, vx, vy) {
1541
+    var ta = Math.atan2(uy, ux),
1542
+        tb = Math.atan2(vy, vx);
1543
+    if (tb >= ta) {
1544
+      return tb - ta;
1545
+    }
1546
+    else {
1547
+      return 2 * Math.PI - (ta - tb);
1548
+    }
1549
+  }
1550
+
1551
+  /**
1552
+   * Draws arc
1553
+   * @param {CanvasRenderingContext2D} ctx
1554
+   * @param {Number} fx
1555
+   * @param {Number} fy
1556
+   * @param {Array} coords
1557
+   */
1558
+  fabric.util.drawArc = function(ctx, fx, fy, coords) {
1559
+    var rx = coords[0],
1560
+        ry = coords[1],
1561
+        rot = coords[2],
1562
+        large = coords[3],
1563
+        sweep = coords[4],
1564
+        tx = coords[5],
1565
+        ty = coords[6],
1566
+        segs = [[], [], [], []],
1567
+        segsNorm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
1568
+
1569
+    for (var i = 0, len = segsNorm.length; i < len; i++) {
1570
+      segs[i][0] = segsNorm[i][0] + fx;
1571
+      segs[i][1] = segsNorm[i][1] + fy;
1572
+      segs[i][2] = segsNorm[i][2] + fx;
1573
+      segs[i][3] = segsNorm[i][3] + fy;
1574
+      segs[i][4] = segsNorm[i][4] + fx;
1575
+      segs[i][5] = segsNorm[i][5] + fy;
1576
+      ctx.bezierCurveTo.apply(ctx, segs[i]);
1577
+    }
1578
+  };
1579
+
1580
+  /**
1581
+   * Calculate bounding box of a elliptic-arc
1582
+   * @param {Number} fx start point of arc
1583
+   * @param {Number} fy
1584
+   * @param {Number} rx horizontal radius
1585
+   * @param {Number} ry vertical radius
1586
+   * @param {Number} rot angle of horizontal axe
1587
+   * @param {Number} large 1 or 0, whatever the arc is the big or the small on the 2 points
1588
+   * @param {Number} sweep 1 or 0, 1 clockwise or counterclockwise direction
1589
+   * @param {Number} tx end point of arc
1590
+   * @param {Number} ty
1591
+   */
1592
+  fabric.util.getBoundsOfArc = function(fx, fy, rx, ry, rot, large, sweep, tx, ty) {
1593
+
1594
+    var fromX = 0, fromY = 0, bound, bounds = [],
1595
+        segs = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
1596
+
1597
+    for (var i = 0, len = segs.length; i < len; i++) {
1598
+      bound = getBoundsOfCurve(fromX, fromY, segs[i][0], segs[i][1], segs[i][2], segs[i][3], segs[i][4], segs[i][5]);
1599
+      bounds.push({ x: bound[0].x + fx, y: bound[0].y + fy });
1600
+      bounds.push({ x: bound[1].x + fx, y: bound[1].y + fy });
1601
+      fromX = segs[i][4];
1602
+      fromY = segs[i][5];
1603
+    }
1604
+    return bounds;
1605
+  };
1606
+
1607
+  /**
1608
+   * Calculate bounding box of a beziercurve
1609
+   * @param {Number} x0 starting point
1610
+   * @param {Number} y0
1611
+   * @param {Number} x1 first control point
1612
+   * @param {Number} y1
1613
+   * @param {Number} x2 secondo control point
1614
+   * @param {Number} y2
1615
+   * @param {Number} x3 end of beizer
1616
+   * @param {Number} y3
1617
+   */
1618
+  // taken from http://jsbin.com/ivomiq/56/edit  no credits available for that.
1619
+  function getBoundsOfCurve(x0, y0, x1, y1, x2, y2, x3, y3) {
1620
+    var argsString;
1621
+    if (fabric.cachesBoundsOfCurve) {
1622
+      argsString = _join.call(arguments);
1623
+      if (fabric.boundsOfCurveCache[argsString]) {
1624
+        return fabric.boundsOfCurveCache[argsString];
1625
+      }
1626
+    }
1627
+
1628
+    var sqrt = Math.sqrt,
1629
+        min = Math.min, max = Math.max,
1630
+        abs = Math.abs, tvalues = [],
1631
+        bounds = [[], []],
1632
+        a, b, c, t, t1, t2, b2ac, sqrtb2ac;
1633
+
1634
+    b = 6 * x0 - 12 * x1 + 6 * x2;
1635
+    a = -3 * x0 + 9 * x1 - 9 * x2 + 3 * x3;
1636
+    c = 3 * x1 - 3 * x0;
1637
+
1638
+    for (var i = 0; i < 2; ++i) {
1639
+      if (i > 0) {
1640
+        b = 6 * y0 - 12 * y1 + 6 * y2;
1641
+        a = -3 * y0 + 9 * y1 - 9 * y2 + 3 * y3;
1642
+        c = 3 * y1 - 3 * y0;
1643
+      }
1644
+
1645
+      if (abs(a) < 1e-12) {
1646
+        if (abs(b) < 1e-12) {
1647
+          continue;
1648
+        }
1649
+        t = -c / b;
1650
+        if (0 < t && t < 1) {
1651
+          tvalues.push(t);
1652
+        }
1653
+        continue;
1654
+      }
1655
+      b2ac = b * b - 4 * c * a;
1656
+      if (b2ac < 0) {
1657
+        continue;
1658
+      }
1659
+      sqrtb2ac = sqrt(b2ac);
1660
+      t1 = (-b + sqrtb2ac) / (2 * a);
1661
+      if (0 < t1 && t1 < 1) {
1662
+        tvalues.push(t1);
1663
+      }
1664
+      t2 = (-b - sqrtb2ac) / (2 * a);
1665
+      if (0 < t2 && t2 < 1) {
1666
+        tvalues.push(t2);
1667
+      }
1668
+    }
1669
+
1670
+    var x, y, j = tvalues.length, jlen = j, mt;
1671
+    while (j--) {
1672
+      t = tvalues[j];
1673
+      mt = 1 - t;
1674
+      x = (mt * mt * mt * x0) + (3 * mt * mt * t * x1) + (3 * mt * t * t * x2) + (t * t * t * x3);
1675
+      bounds[0][j] = x;
1676
+
1677
+      y = (mt * mt * mt * y0) + (3 * mt * mt * t * y1) + (3 * mt * t * t * y2) + (t * t * t * y3);
1678
+      bounds[1][j] = y;
1679
+    }
1680
+
1681
+    bounds[0][jlen] = x0;
1682
+    bounds[1][jlen] = y0;
1683
+    bounds[0][jlen + 1] = x3;
1684
+    bounds[1][jlen + 1] = y3;
1685
+    var result = [
1686
+      {
1687
+        x: min.apply(null, bounds[0]),
1688
+        y: min.apply(null, bounds[1])
1689
+      },
1690
+      {
1691
+        x: max.apply(null, bounds[0]),
1692
+        y: max.apply(null, bounds[1])
1693
+      }
1694
+    ];
1695
+    if (fabric.cachesBoundsOfCurve) {
1696
+      fabric.boundsOfCurveCache[argsString] = result;
1697
+    }
1698
+    return result;
1699
+  }
1700
+
1701
+  fabric.util.getBoundsOfCurve = getBoundsOfCurve;
1702
+
1703
+})();
1704
+
1705
+
1706
+(function() {
1707
+
1708
+  var slice = Array.prototype.slice;
1709
+
1710
+  /**
1711
+   * Invokes method on all items in a given array
1712
+   * @memberOf fabric.util.array
1713
+   * @param {Array} array Array to iterate over
1714
+   * @param {String} method Name of a method to invoke
1715
+   * @return {Array}
1716
+   */
1717
+  function invoke(array, method) {
1718
+    var args = slice.call(arguments, 2), result = [];
1719
+    for (var i = 0, len = array.length; i < len; i++) {
1720
+      result[i] = args.length ? array[i][method].apply(array[i], args) : array[i][method].call(array[i]);
1721
+    }
1722
+    return result;
1723
+  }
1724
+
1725
+  /**
1726
+   * Finds maximum value in array (not necessarily "first" one)
1727
+   * @memberOf fabric.util.array
1728
+   * @param {Array} array Array to iterate over
1729
+   * @param {String} byProperty
1730
+   * @return {*}
1731
+   */
1732
+  function max(array, byProperty) {
1733
+    return find(array, byProperty, function(value1, value2) {
1734
+      return value1 >= value2;
1735
+    });
1736
+  }
1737
+
1738
+  /**
1739
+   * Finds minimum value in array (not necessarily "first" one)
1740
+   * @memberOf fabric.util.array
1741
+   * @param {Array} array Array to iterate over
1742
+   * @param {String} byProperty
1743
+   * @return {*}
1744
+   */
1745
+  function min(array, byProperty) {
1746
+    return find(array, byProperty, function(value1, value2) {
1747
+      return value1 < value2;
1748
+    });
1749
+  }
1750
+
1751
+  /**
1752
+   * @private
1753
+   */
1754
+  function fill(array, value) {
1755
+    var k = array.length;
1756
+    while (k--) {
1757
+      array[k] = value;
1758
+    }
1759
+    return array;
1760
+  }
1761
+
1762
+  /**
1763
+   * @private
1764
+   */
1765
+  function find(array, byProperty, condition) {
1766
+    if (!array || array.length === 0) {
1767
+      return;
1768
+    }
1769
+
1770
+    var i = array.length - 1,
1771
+        result = byProperty ? array[i][byProperty] : array[i];
1772
+    if (byProperty) {
1773
+      while (i--) {
1774
+        if (condition(array[i][byProperty], result)) {
1775
+          result = array[i][byProperty];
1776
+        }
1777
+      }
1778
+    }
1779
+    else {
1780
+      while (i--) {
1781
+        if (condition(array[i], result)) {
1782
+          result = array[i];
1783
+        }
1784
+      }
1785
+    }
1786
+    return result;
1787
+  }
1788
+
1789
+  /**
1790
+   * @namespace fabric.util.array
1791
+   */
1792
+  fabric.util.array = {
1793
+    fill: fill,
1794
+    invoke: invoke,
1795
+    min: min,
1796
+    max: max
1797
+  };
1798
+
1799
+})();
1800
+
1801
+
1802
+(function() {
1803
+  /**
1804
+   * Copies all enumerable properties of one js object to another
1805
+   * this does not and cannot compete with generic utils.
1806
+   * Does not clone or extend fabric.Object subclasses.
1807
+   * This is mostly for internal use and has extra handling for fabricJS objects
1808
+   * it skips the canvas property in deep cloning.
1809
+   * @memberOf fabric.util.object
1810
+   * @param {Object} destination Where to copy to
1811
+   * @param {Object} source Where to copy from
1812
+   * @return {Object}
1813
+   */
1814
+
1815
+  function extend(destination, source, deep) {
1816
+    // JScript DontEnum bug is not taken care of
1817
+    // the deep clone is for internal use, is not meant to avoid
1818
+    // javascript traps or cloning html element or self referenced objects.
1819
+    if (deep) {
1820
+      if (!fabric.isLikelyNode && source instanceof Element) {
1821
+        // avoid cloning deep images, canvases,
1822
+        destination = source;
1823
+      }
1824
+      else if (source instanceof Array) {
1825
+        destination = [];
1826
+        for (var i = 0, len = source.length; i < len; i++) {
1827
+          destination[i] = extend({ }, source[i], deep);
1828
+        }
1829
+      }
1830
+      else if (source && typeof source === 'object') {
1831
+        for (var property in source) {
1832
+          if (property === 'canvas') {
1833
+            destination[property] = extend({ }, source[property]);
1834
+          }
1835
+          else if (source.hasOwnProperty(property)) {
1836
+            destination[property] = extend({ }, source[property], deep);
1837
+          }
1838
+        }
1839
+      }
1840
+      else {
1841
+        // this sounds odd for an extend but is ok for recursive use
1842
+        destination = source;
1843
+      }
1844
+    }
1845
+    else {
1846
+      for (var property in source) {
1847
+        destination[property] = source[property];
1848
+      }
1849
+    }
1850
+    return destination;
1851
+  }
1852
+
1853
+  /**
1854
+   * Creates an empty object and copies all enumerable properties of another object to it
1855
+   * @memberOf fabric.util.object
1856
+   * TODO: this function return an empty object if you try to clone null
1857
+   * @param {Object} object Object to clone
1858
+   * @return {Object}
1859
+   */
1860
+  function clone(object, deep) {
1861
+    return extend({ }, object, deep);
1862
+  }
1863
+
1864
+  /** @namespace fabric.util.object */
1865
+  fabric.util.object = {
1866
+    extend: extend,
1867
+    clone: clone
1868
+  };
1869
+  fabric.util.object.extend(fabric.util, fabric.Observable);
1870
+})();
1871
+
1872
+
1873
+(function() {
1874
+
1875
+  /**
1876
+   * Camelizes a string
1877
+   * @memberOf fabric.util.string
1878
+   * @param {String} string String to camelize
1879
+   * @return {String} Camelized version of a string
1880
+   */
1881
+  function camelize(string) {
1882
+    return string.replace(/-+(.)?/g, function(match, character) {
1883
+      return character ? character.toUpperCase() : '';
1884
+    });
1885
+  }
1886
+
1887
+  /**
1888
+   * Capitalizes a string
1889
+   * @memberOf fabric.util.string
1890
+   * @param {String} string String to capitalize
1891
+   * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
1892
+   * and other letters stay untouched, if false first letter is capitalized
1893
+   * and other letters are converted to lowercase.
1894
+   * @return {String} Capitalized version of a string
1895
+   */
1896
+  function capitalize(string, firstLetterOnly) {
1897
+    return string.charAt(0).toUpperCase() +
1898
+      (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
1899
+  }
1900
+
1901
+  /**
1902
+   * Escapes XML in a string
1903
+   * @memberOf fabric.util.string
1904
+   * @param {String} string String to escape
1905
+   * @return {String} Escaped version of a string
1906
+   */
1907
+  function escapeXml(string) {
1908
+    return string.replace(/&/g, '&amp;')
1909
+      .replace(/"/g, '&quot;')
1910
+      .replace(/'/g, '&apos;')
1911
+      .replace(/</g, '&lt;')
1912
+      .replace(/>/g, '&gt;');
1913
+  }
1914
+
1915
+  /**
1916
+   * Divide a string in the user perceived single units
1917
+   * @memberOf fabric.util.string
1918
+   * @param {String} textstring String to escape
1919
+   * @return {Array} array containing the graphemes
1920
+   */
1921
+  function graphemeSplit(textstring) {
1922
+    var i = 0, chr, graphemes = [];
1923
+    for (i = 0, chr; i < textstring.length; i++) {
1924
+      if ((chr = getWholeChar(textstring, i)) === false) {
1925
+        continue;
1926
+      }
1927
+      graphemes.push(chr);
1928
+    }
1929
+    return graphemes;
1930
+  }
1931
+
1932
+  // taken from mdn in the charAt doc page.
1933
+  function getWholeChar(str, i) {
1934
+    var code = str.charCodeAt(i);
1935
+
1936
+    if (isNaN(code)) {
1937
+      return ''; // Position not found
1938
+    }
1939
+    if (code < 0xD800 || code > 0xDFFF) {
1940
+      return str.charAt(i);
1941
+    }
1942
+
1943
+    // High surrogate (could change last hex to 0xDB7F to treat high private
1944
+    // surrogates as single characters)
1945
+    if (0xD800 <= code && code <= 0xDBFF) {
1946
+      if (str.length <= (i + 1)) {
1947
+        throw 'High surrogate without following low surrogate';
1948
+      }
1949
+      var next = str.charCodeAt(i + 1);
1950
+      if (0xDC00 > next || next > 0xDFFF) {
1951
+        throw 'High surrogate without following low surrogate';
1952
+      }
1953
+      return str.charAt(i) + str.charAt(i + 1);
1954
+    }
1955
+    // Low surrogate (0xDC00 <= code && code <= 0xDFFF)
1956
+    if (i === 0) {
1957
+      throw 'Low surrogate without preceding high surrogate';
1958
+    }
1959
+    var prev = str.charCodeAt(i - 1);
1960
+
1961
+    // (could change last hex to 0xDB7F to treat high private
1962
+    // surrogates as single characters)
1963
+    if (0xD800 > prev || prev > 0xDBFF) {
1964
+      throw 'Low surrogate without preceding high surrogate';
1965
+    }
1966
+    // We can pass over low surrogates now as the second component
1967
+    // in a pair which we have already processed
1968
+    return false;
1969
+  }
1970
+
1971
+
1972
+  /**
1973
+   * String utilities
1974
+   * @namespace fabric.util.string
1975
+   */
1976
+  fabric.util.string = {
1977
+    camelize: camelize,
1978
+    capitalize: capitalize,
1979
+    escapeXml: escapeXml,
1980
+    graphemeSplit: graphemeSplit
1981
+  };
1982
+})();
1983
+
1984
+
1985
+(function() {
1986
+
1987
+  var slice = Array.prototype.slice, emptyFunction = function() { },
1988
+
1989
+      IS_DONTENUM_BUGGY = (function() {
1990
+        for (var p in { toString: 1 }) {
1991
+          if (p === 'toString') {
1992
+            return false;
1993
+          }
1994
+        }
1995
+        return true;
1996
+      })(),
1997
+
1998
+      /** @ignore */
1999
+      addMethods = function(klass, source, parent) {
2000
+        for (var property in source) {
2001
+
2002
+          if (property in klass.prototype &&
2003
+              typeof klass.prototype[property] === 'function' &&
2004
+              (source[property] + '').indexOf('callSuper') > -1) {
2005
+
2006
+            klass.prototype[property] = (function(property) {
2007
+              return function() {
2008
+
2009
+                var superclass = this.constructor.superclass;
2010
+                this.constructor.superclass = parent;
2011
+                var returnValue = source[property].apply(this, arguments);
2012
+                this.constructor.superclass = superclass;
2013
+
2014
+                if (property !== 'initialize') {
2015
+                  return returnValue;
2016
+                }
2017
+              };
2018
+            })(property);
2019
+          }
2020
+          else {
2021
+            klass.prototype[property] = source[property];
2022
+          }
2023
+
2024
+          if (IS_DONTENUM_BUGGY) {
2025
+            if (source.toString !== Object.prototype.toString) {
2026
+              klass.prototype.toString = source.toString;
2027
+            }
2028
+            if (source.valueOf !== Object.prototype.valueOf) {
2029
+              klass.prototype.valueOf = source.valueOf;
2030
+            }
2031
+          }
2032
+        }
2033
+      };
2034
+
2035
+  function Subclass() { }
2036
+
2037
+  function callSuper(methodName) {
2038
+    var parentMethod = null,
2039
+        _this = this;
2040
+
2041
+    // climb prototype chain to find method not equal to callee's method
2042
+    while (_this.constructor.superclass) {
2043
+      var superClassMethod = _this.constructor.superclass.prototype[methodName];
2044
+      if (_this[methodName] !== superClassMethod) {
2045
+        parentMethod = superClassMethod;
2046
+        break;
2047
+      }
2048
+      // eslint-disable-next-line
2049
+      _this = _this.constructor.superclass.prototype;
2050
+    }
2051
+
2052
+    if (!parentMethod) {
2053
+      return console.log('tried to callSuper ' + methodName + ', method not found in prototype chain', this);
2054
+    }
2055
+
2056
+    return (arguments.length > 1)
2057
+      ? parentMethod.apply(this, slice.call(arguments, 1))
2058
+      : parentMethod.call(this);
2059
+  }
2060
+
2061
+  /**
2062
+   * Helper for creation of "classes".
2063
+   * @memberOf fabric.util
2064
+   * @param {Function} [parent] optional "Class" to inherit from
2065
+   * @param {Object} [properties] Properties shared by all instances of this class
2066
+   *                  (be careful modifying objects defined here as this would affect all instances)
2067
+   */
2068
+  function createClass() {
2069
+    var parent = null,
2070
+        properties = slice.call(arguments, 0);
2071
+
2072
+    if (typeof properties[0] === 'function') {
2073
+      parent = properties.shift();
2074
+    }
2075
+    function klass() {
2076
+      this.initialize.apply(this, arguments);
2077
+    }
2078
+
2079
+    klass.superclass = parent;
2080
+    klass.subclasses = [];
2081
+
2082
+    if (parent) {
2083
+      Subclass.prototype = parent.prototype;
2084
+      klass.prototype = new Subclass();
2085
+      parent.subclasses.push(klass);
2086
+    }
2087
+    for (var i = 0, length = properties.length; i < length; i++) {
2088
+      addMethods(klass, properties[i], parent);
2089
+    }
2090
+    if (!klass.prototype.initialize) {
2091
+      klass.prototype.initialize = emptyFunction;
2092
+    }
2093
+    klass.prototype.constructor = klass;
2094
+    klass.prototype.callSuper = callSuper;
2095
+    return klass;
2096
+  }
2097
+
2098
+  fabric.util.createClass = createClass;
2099
+})();
2100
+
2101
+
2102
+(function () {
2103
+
2104
+  var unknown = 'unknown';
2105
+
2106
+  /* EVENT HANDLING */
2107
+
2108
+  function areHostMethods(object) {
2109
+    var methodNames = Array.prototype.slice.call(arguments, 1),
2110
+        t, i, len = methodNames.length;
2111
+    for (i = 0; i < len; i++) {
2112
+      t = typeof object[methodNames[i]];
2113
+      if (!(/^(?:function|object|unknown)$/).test(t)) {
2114
+        return false;
2115
+      }
2116
+    }
2117
+    return true;
2118
+  }
2119
+
2120
+  /** @ignore */
2121
+  var getElement,
2122
+      setElement,
2123
+      getUniqueId = (function () {
2124
+        var uid = 0;
2125
+        return function (element) {
2126
+          return element.__uniqueID || (element.__uniqueID = 'uniqueID__' + uid++);
2127
+        };
2128
+      })();
2129
+
2130
+  (function () {
2131
+    var elements = { };
2132
+    /** @ignore */
2133
+    getElement = function (uid) {
2134
+      return elements[uid];
2135
+    };
2136
+    /** @ignore */
2137
+    setElement = function (uid, element) {
2138
+      elements[uid] = element;
2139
+    };
2140
+  })();
2141
+
2142
+  function createListener(uid, handler) {
2143
+    return {
2144
+      handler: handler,
2145
+      wrappedHandler: createWrappedHandler(uid, handler)
2146
+    };
2147
+  }
2148
+
2149
+  function createWrappedHandler(uid, handler) {
2150
+    return function (e) {
2151
+      handler.call(getElement(uid), e || fabric.window.event);
2152
+    };
2153
+  }
2154
+
2155
+  function createDispatcher(uid, eventName) {
2156
+    return function (e) {
2157
+      if (handlers[uid] && handlers[uid][eventName]) {
2158
+        var handlersForEvent = handlers[uid][eventName];
2159
+        for (var i = 0, len = handlersForEvent.length; i < len; i++) {
2160
+          handlersForEvent[i].call(this, e || fabric.window.event);
2161
+        }
2162
+      }
2163
+    };
2164
+  }
2165
+
2166
+  var shouldUseAddListenerRemoveListener = (
2167
+        areHostMethods(fabric.document.documentElement, 'addEventListener', 'removeEventListener') &&
2168
+        areHostMethods(fabric.window, 'addEventListener', 'removeEventListener')),
2169
+
2170
+      shouldUseAttachEventDetachEvent = (
2171
+        areHostMethods(fabric.document.documentElement, 'attachEvent', 'detachEvent') &&
2172
+        areHostMethods(fabric.window, 'attachEvent', 'detachEvent')),
2173
+
2174
+      // IE branch
2175
+      listeners = { },
2176
+
2177
+      // DOM L0 branch
2178
+      handlers = { },
2179
+
2180
+      addListener, removeListener;
2181
+
2182
+  if (shouldUseAddListenerRemoveListener) {
2183
+    /** @ignore */
2184
+    addListener = function (element, eventName, handler, options) {
2185
+      // since ie10 or ie9 can use addEventListener but they do not support options, i need to check
2186
+      element && element.addEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options);
2187
+    };
2188
+    /** @ignore */
2189
+    removeListener = function (element, eventName, handler, options) {
2190
+      element && element.removeEventListener(eventName, handler, shouldUseAttachEventDetachEvent ? false : options);
2191
+    };
2192
+  }
2193
+
2194
+  else if (shouldUseAttachEventDetachEvent) {
2195
+    /** @ignore */
2196
+    addListener = function (element, eventName, handler) {
2197
+      if (!element) {
2198
+        return;
2199
+      }
2200
+      var uid = getUniqueId(element);
2201
+      setElement(uid, element);
2202
+      if (!listeners[uid]) {
2203
+        listeners[uid] = { };
2204
+      }
2205
+      if (!listeners[uid][eventName]) {
2206
+        listeners[uid][eventName] = [];
2207
+
2208
+      }
2209
+      var listener = createListener(uid, handler);
2210
+      listeners[uid][eventName].push(listener);
2211
+      element.attachEvent('on' + eventName, listener.wrappedHandler);
2212
+    };
2213
+    /** @ignore */
2214
+    removeListener = function (element, eventName, handler) {
2215
+      if (!element) {
2216
+        return;
2217
+      }
2218
+      var uid = getUniqueId(element), listener;
2219
+      if (listeners[uid] && listeners[uid][eventName]) {
2220
+        for (var i = 0, len = listeners[uid][eventName].length; i < len; i++) {
2221
+          listener = listeners[uid][eventName][i];
2222
+          if (listener && listener.handler === handler) {
2223
+            element.detachEvent('on' + eventName, listener.wrappedHandler);
2224
+            listeners[uid][eventName][i] = null;
2225
+          }
2226
+        }
2227
+      }
2228
+    };
2229
+  }
2230
+  else {
2231
+    /** @ignore */
2232
+    addListener = function (element, eventName, handler) {
2233
+      if (!element) {
2234
+        return;
2235
+      }
2236
+      var uid = getUniqueId(element);
2237
+      if (!handlers[uid]) {
2238
+        handlers[uid] = { };
2239
+      }
2240
+      if (!handlers[uid][eventName]) {
2241
+        handlers[uid][eventName] = [];
2242
+        var existingHandler = element['on' + eventName];
2243
+        if (existingHandler) {
2244
+          handlers[uid][eventName].push(existingHandler);
2245
+        }
2246
+        element['on' + eventName] = createDispatcher(uid, eventName);
2247
+      }
2248
+      handlers[uid][eventName].push(handler);
2249
+    };
2250
+    /** @ignore */
2251
+    removeListener = function (element, eventName, handler) {
2252
+      if (!element) {
2253
+        return;
2254
+      }
2255
+      var uid = getUniqueId(element);
2256
+      if (handlers[uid] && handlers[uid][eventName]) {
2257
+        var handlersForEvent = handlers[uid][eventName];
2258
+        for (var i = 0, len = handlersForEvent.length; i < len; i++) {
2259
+          if (handlersForEvent[i] === handler) {
2260
+            handlersForEvent.splice(i, 1);
2261
+          }
2262
+        }
2263
+      }
2264
+    };
2265
+  }
2266
+
2267
+  /**
2268
+   * Adds an event listener to an element
2269
+   * @function
2270
+   * @memberOf fabric.util
2271
+   * @param {HTMLElement} element
2272
+   * @param {String} eventName
2273
+   * @param {Function} handler
2274
+   */
2275
+  fabric.util.addListener = addListener;
2276
+
2277
+  /**
2278
+   * Removes an event listener from an element
2279
+   * @function
2280
+   * @memberOf fabric.util
2281
+   * @param {HTMLElement} element
2282
+   * @param {String} eventName
2283
+   * @param {Function} handler
2284
+   */
2285
+  fabric.util.removeListener = removeListener;
2286
+
2287
+  /**
2288
+   * Cross-browser wrapper for getting event's coordinates
2289
+   * @memberOf fabric.util
2290
+   * @param {Event} event Event object
2291
+   */
2292
+  function getPointer(event) {
2293
+    event || (event = fabric.window.event);
2294
+
2295
+    var element = event.target ||
2296
+                  (typeof event.srcElement !== unknown ? event.srcElement : null),
2297
+
2298
+        scroll = fabric.util.getScrollLeftTop(element);
2299
+    return {
2300
+      x: pointerX(event) + scroll.left,
2301
+      y: pointerY(event) + scroll.top
2302
+    };
2303
+  }
2304
+
2305
+  var pointerX = function(event) {
2306
+        return event.clientX;
2307
+      },
2308
+
2309
+      pointerY = function(event) {
2310
+        return event.clientY;
2311
+      };
2312
+
2313
+  function _getPointer(event, pageProp, clientProp) {
2314
+    var touchProp = event.type === 'touchend' ? 'changedTouches' : 'touches';
2315
+    var pointer, eventTouchProp = event[touchProp];
2316
+
2317
+    if (eventTouchProp && eventTouchProp[0]) {
2318
+      pointer = eventTouchProp[0][clientProp];
2319
+    }
2320
+
2321
+    if (typeof pointer === 'undefined') {
2322
+      pointer = event[clientProp];
2323
+    }
2324
+
2325
+    return pointer;
2326
+  }
2327
+
2328
+  if (fabric.isTouchSupported) {
2329
+    pointerX = function(event) {
2330
+      return _getPointer(event, 'pageX', 'clientX');
2331
+    };
2332
+    pointerY = function(event) {
2333
+      return _getPointer(event, 'pageY', 'clientY');
2334
+    };
2335
+  }
2336
+
2337
+  fabric.util.getPointer = getPointer;
2338
+
2339
+})();
2340
+
2341
+
2342
+(function () {
2343
+
2344
+  /**
2345
+   * Cross-browser wrapper for setting element's style
2346
+   * @memberOf fabric.util
2347
+   * @param {HTMLElement} element
2348
+   * @param {Object} styles
2349
+   * @return {HTMLElement} Element that was passed as a first argument
2350
+   */
2351
+  function setStyle(element, styles) {
2352
+    var elementStyle = element.style;
2353
+    if (!elementStyle) {
2354
+      return element;
2355
+    }
2356
+    if (typeof styles === 'string') {
2357
+      element.style.cssText += ';' + styles;
2358
+      return styles.indexOf('opacity') > -1
2359
+        ? setOpacity(element, styles.match(/opacity:\s*(\d?\.?\d*)/)[1])
2360
+        : element;
2361
+    }
2362
+    for (var property in styles) {
2363
+      if (property === 'opacity') {
2364
+        setOpacity(element, styles[property]);
2365
+      }
2366
+      else {
2367
+        var normalizedProperty = (property === 'float' || property === 'cssFloat')
2368
+          ? (typeof elementStyle.styleFloat === 'undefined' ? 'cssFloat' : 'styleFloat')
2369
+          : property;
2370
+        elementStyle[normalizedProperty] = styles[property];
2371
+      }
2372
+    }
2373
+    return element;
2374
+  }
2375
+
2376
+  var parseEl = fabric.document.createElement('div'),
2377
+      supportsOpacity = typeof parseEl.style.opacity === 'string',
2378
+      supportsFilters = typeof parseEl.style.filter === 'string',
2379
+      reOpacity = /alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/,
2380
+
2381
+      /** @ignore */
2382
+      setOpacity = function (element) { return element; };
2383
+
2384
+  if (supportsOpacity) {
2385
+    /** @ignore */
2386
+    setOpacity = function(element, value) {
2387
+      element.style.opacity = value;
2388
+      return element;
2389
+    };
2390
+  }
2391
+  else if (supportsFilters) {
2392
+    /** @ignore */
2393
+    setOpacity = function(element, value) {
2394
+      var es = element.style;
2395
+      if (element.currentStyle && !element.currentStyle.hasLayout) {
2396
+        es.zoom = 1;
2397
+      }
2398
+      if (reOpacity.test(es.filter)) {
2399
+        value = value >= 0.9999 ? '' : ('alpha(opacity=' + (value * 100) + ')');
2400
+        es.filter = es.filter.replace(reOpacity, value);
2401
+      }
2402
+      else {
2403
+        es.filter += ' alpha(opacity=' + (value * 100) + ')';
2404
+      }
2405
+      return element;
2406
+    };
2407
+  }
2408
+
2409
+  fabric.util.setStyle = setStyle;
2410
+
2411
+})();
2412
+
2413
+
2414
+(function() {
2415
+
2416
+  var _slice = Array.prototype.slice;
2417
+
2418
+  /**
2419
+   * Takes id and returns an element with that id (if one exists in a document)
2420
+   * @memberOf fabric.util
2421
+   * @param {String|HTMLElement} id
2422
+   * @return {HTMLElement|null}
2423
+   */
2424
+  function getById(id) {
2425
+    return typeof id === 'string' ? fabric.document.getElementById(id) : id;
2426
+  }
2427
+
2428
+  var sliceCanConvertNodelists,
2429
+      /**
2430
+       * Converts an array-like object (e.g. arguments or NodeList) to an array
2431
+       * @memberOf fabric.util
2432
+       * @param {Object} arrayLike
2433
+       * @return {Array}
2434
+       */
2435
+      toArray = function(arrayLike) {
2436
+        return _slice.call(arrayLike, 0);
2437
+      };
2438
+
2439
+  try {
2440
+    sliceCanConvertNodelists = toArray(fabric.document.childNodes) instanceof Array;
2441
+  }
2442
+  catch (err) { }
2443
+
2444
+  if (!sliceCanConvertNodelists) {
2445
+    toArray = function(arrayLike) {
2446
+      var arr = new Array(arrayLike.length), i = arrayLike.length;
2447
+      while (i--) {
2448
+        arr[i] = arrayLike[i];
2449
+      }
2450
+      return arr;
2451
+    };
2452
+  }
2453
+
2454
+  /**
2455
+   * Creates specified element with specified attributes
2456
+   * @memberOf fabric.util
2457
+   * @param {String} tagName Type of an element to create
2458
+   * @param {Object} [attributes] Attributes to set on an element
2459
+   * @return {HTMLElement} Newly created element
2460
+   */
2461
+  function makeElement(tagName, attributes) {
2462
+    var el = fabric.document.createElement(tagName);
2463
+    for (var prop in attributes) {
2464
+      if (prop === 'class') {
2465
+        el.className = attributes[prop];
2466
+      }
2467
+      else if (prop === 'for') {
2468
+        el.htmlFor = attributes[prop];
2469
+      }
2470
+      else {
2471
+        el.setAttribute(prop, attributes[prop]);
2472
+      }
2473
+    }
2474
+    return el;
2475
+  }
2476
+
2477
+  /**
2478
+   * Adds class to an element
2479
+   * @memberOf fabric.util
2480
+   * @param {HTMLElement} element Element to add class to
2481
+   * @param {String} className Class to add to an element
2482
+   */
2483
+  function addClass(element, className) {
2484
+    if (element && (' ' + element.className + ' ').indexOf(' ' + className + ' ') === -1) {
2485
+      element.className += (element.className ? ' ' : '') + className;
2486
+    }
2487
+  }
2488
+
2489
+  /**
2490
+   * Wraps element with another element
2491
+   * @memberOf fabric.util
2492
+   * @param {HTMLElement} element Element to wrap
2493
+   * @param {HTMLElement|String} wrapper Element to wrap with
2494
+   * @param {Object} [attributes] Attributes to set on a wrapper
2495
+   * @return {HTMLElement} wrapper
2496
+   */
2497
+  function wrapElement(element, wrapper, attributes) {
2498
+    if (typeof wrapper === 'string') {
2499
+      wrapper = makeElement(wrapper, attributes);
2500
+    }
2501
+    if (element.parentNode) {
2502
+      element.parentNode.replaceChild(wrapper, element);
2503
+    }
2504
+    wrapper.appendChild(element);
2505
+    return wrapper;
2506
+  }
2507
+
2508
+  /**
2509
+   * Returns element scroll offsets
2510
+   * @memberOf fabric.util
2511
+   * @param {HTMLElement} element Element to operate on
2512
+   * @return {Object} Object with left/top values
2513
+   */
2514
+  function getScrollLeftTop(element) {
2515
+
2516
+    var left = 0,
2517
+        top = 0,
2518
+        docElement = fabric.document.documentElement,
2519
+        body = fabric.document.body || {
2520
+          scrollLeft: 0, scrollTop: 0
2521
+        };
2522
+
2523
+    // While loop checks (and then sets element to) .parentNode OR .host
2524
+    //  to account for ShadowDOM. We still want to traverse up out of ShadowDOM,
2525
+    //  but the .parentNode of a root ShadowDOM node will always be null, instead
2526
+    //  it should be accessed through .host. See http://stackoverflow.com/a/24765528/4383938
2527
+    while (element && (element.parentNode || element.host)) {
2528
+
2529
+      // Set element to element parent, or 'host' in case of ShadowDOM
2530
+      element = element.parentNode || element.host;
2531
+
2532
+      if (element === fabric.document) {
2533
+        left = body.scrollLeft || docElement.scrollLeft || 0;
2534
+        top = body.scrollTop ||  docElement.scrollTop || 0;
2535
+      }
2536
+      else {
2537
+        left += element.scrollLeft || 0;
2538
+        top += element.scrollTop || 0;
2539
+      }
2540
+
2541
+      if (element.nodeType === 1 && element.style.position === 'fixed') {
2542
+        break;
2543
+      }
2544
+    }
2545
+
2546
+    return { left: left, top: top };
2547
+  }
2548
+
2549
+  /**
2550
+   * Returns offset for a given element
2551
+   * @function
2552
+   * @memberOf fabric.util
2553
+   * @param {HTMLElement} element Element to get offset for
2554
+   * @return {Object} Object with "left" and "top" properties
2555
+   */
2556
+  function getElementOffset(element) {
2557
+    var docElem,
2558
+        doc = element && element.ownerDocument,
2559
+        box = { left: 0, top: 0 },
2560
+        offset = { left: 0, top: 0 },
2561
+        scrollLeftTop,
2562
+        offsetAttributes = {
2563
+          borderLeftWidth: 'left',
2564
+          borderTopWidth:  'top',
2565
+          paddingLeft:     'left',
2566
+          paddingTop:      'top'
2567
+        };
2568
+
2569
+    if (!doc) {
2570
+      return offset;
2571
+    }
2572
+
2573
+    for (var attr in offsetAttributes) {
2574
+      offset[offsetAttributes[attr]] += parseInt(getElementStyle(element, attr), 10) || 0;
2575
+    }
2576
+
2577
+    docElem = doc.documentElement;
2578
+    if ( typeof element.getBoundingClientRect !== 'undefined' ) {
2579
+      box = element.getBoundingClientRect();
2580
+    }
2581
+
2582
+    scrollLeftTop = getScrollLeftTop(element);
2583
+
2584
+    return {
2585
+      left: box.left + scrollLeftTop.left - (docElem.clientLeft || 0) + offset.left,
2586
+      top: box.top + scrollLeftTop.top - (docElem.clientTop || 0)  + offset.top
2587
+    };
2588
+  }
2589
+
2590
+  /**
2591
+   * Returns style attribute value of a given element
2592
+   * @memberOf fabric.util
2593
+   * @param {HTMLElement} element Element to get style attribute for
2594
+   * @param {String} attr Style attribute to get for element
2595
+   * @return {String} Style attribute value of the given element.
2596
+   */
2597
+  var getElementStyle;
2598
+  if (fabric.document.defaultView && fabric.document.defaultView.getComputedStyle) {
2599
+    getElementStyle = function(element, attr) {
2600
+      var style = fabric.document.defaultView.getComputedStyle(element, null);
2601
+      return style ? style[attr] : undefined;
2602
+    };
2603
+  }
2604
+  else {
2605
+    getElementStyle = function(element, attr) {
2606
+      var value = element.style[attr];
2607
+      if (!value && element.currentStyle) {
2608
+        value = element.currentStyle[attr];
2609
+      }
2610
+      return value;
2611
+    };
2612
+  }
2613
+
2614
+  (function () {
2615
+    var style = fabric.document.documentElement.style,
2616
+        selectProp = 'userSelect' in style
2617
+          ? 'userSelect'
2618
+          : 'MozUserSelect' in style
2619
+            ? 'MozUserSelect'
2620
+            : 'WebkitUserSelect' in style
2621
+              ? 'WebkitUserSelect'
2622
+              : 'KhtmlUserSelect' in style
2623
+                ? 'KhtmlUserSelect'
2624
+                : '';
2625
+
2626
+    /**
2627
+     * Makes element unselectable
2628
+     * @memberOf fabric.util
2629
+     * @param {HTMLElement} element Element to make unselectable
2630
+     * @return {HTMLElement} Element that was passed in
2631
+     */
2632
+    function makeElementUnselectable(element) {
2633
+      if (typeof element.onselectstart !== 'undefined') {
2634
+        element.onselectstart = fabric.util.falseFunction;
2635
+      }
2636
+      if (selectProp) {
2637
+        element.style[selectProp] = 'none';
2638
+      }
2639
+      else if (typeof element.unselectable === 'string') {
2640
+        element.unselectable = 'on';
2641
+      }
2642
+      return element;
2643
+    }
2644
+
2645
+    /**
2646
+     * Makes element selectable
2647
+     * @memberOf fabric.util
2648
+     * @param {HTMLElement} element Element to make selectable
2649
+     * @return {HTMLElement} Element that was passed in
2650
+     */
2651
+    function makeElementSelectable(element) {
2652
+      if (typeof element.onselectstart !== 'undefined') {
2653
+        element.onselectstart = null;
2654
+      }
2655
+      if (selectProp) {
2656
+        element.style[selectProp] = '';
2657
+      }
2658
+      else if (typeof element.unselectable === 'string') {
2659
+        element.unselectable = '';
2660
+      }
2661
+      return element;
2662
+    }
2663
+
2664
+    fabric.util.makeElementUnselectable = makeElementUnselectable;
2665
+    fabric.util.makeElementSelectable = makeElementSelectable;
2666
+  })();
2667
+
2668
+  (function() {
2669
+
2670
+    /**
2671
+     * Inserts a script element with a given url into a document; invokes callback, when that script is finished loading
2672
+     * @memberOf fabric.util
2673
+     * @param {String} url URL of a script to load
2674
+     * @param {Function} callback Callback to execute when script is finished loading
2675
+     */
2676
+    function getScript(url, callback) {
2677
+      var headEl = fabric.document.getElementsByTagName('head')[0],
2678
+          scriptEl = fabric.document.createElement('script'),
2679
+          loading = true;
2680
+
2681
+      /** @ignore */
2682
+      scriptEl.onload = /** @ignore */ scriptEl.onreadystatechange = function(e) {
2683
+        if (loading) {
2684
+          if (typeof this.readyState === 'string' &&
2685
+              this.readyState !== 'loaded' &&
2686
+              this.readyState !== 'complete') {
2687
+            return;
2688
+          }
2689
+          loading = false;
2690
+          callback(e || fabric.window.event);
2691
+          scriptEl = scriptEl.onload = scriptEl.onreadystatechange = null;
2692
+        }
2693
+      };
2694
+      scriptEl.src = url;
2695
+      headEl.appendChild(scriptEl);
2696
+      // causes issue in Opera
2697
+      // headEl.removeChild(scriptEl);
2698
+    }
2699
+
2700
+    fabric.util.getScript = getScript;
2701
+  })();
2702
+
2703
+  function getNodeCanvas(element) {
2704
+    var impl = fabric.jsdomImplForWrapper(element);
2705
+    return impl._canvas || impl._image;
2706
+  };
2707
+
2708
+  function cleanUpJsdomNode(element) {
2709
+    if (!fabric.isLikelyNode) {
2710
+      return;
2711
+    }
2712
+    var impl = fabric.jsdomImplForWrapper(element);
2713
+    if (impl) {
2714
+      impl._image = null;
2715
+      impl._canvas = null;
2716
+      // unsure if necessary
2717
+      impl._currentSrc = null;
2718
+      impl._attributes = null;
2719
+      impl._classList = null;
2720
+    }
2721
+  }
2722
+
2723
+  fabric.util.getById = getById;
2724
+  fabric.util.toArray = toArray;
2725
+  fabric.util.makeElement = makeElement;
2726
+  fabric.util.addClass = addClass;
2727
+  fabric.util.wrapElement = wrapElement;
2728
+  fabric.util.getScrollLeftTop = getScrollLeftTop;
2729
+  fabric.util.getElementOffset = getElementOffset;
2730
+  fabric.util.getElementStyle = getElementStyle;
2731
+  fabric.util.getNodeCanvas = getNodeCanvas;
2732
+  fabric.util.cleanUpJsdomNode = cleanUpJsdomNode;
2733
+
2734
+})();
2735
+
2736
+
2737
+(function() {
2738
+
2739
+  function addParamToUrl(url, param) {
2740
+    return url + (/\?/.test(url) ? '&' : '?') + param;
2741
+  }
2742
+
2743
+  function emptyFn() { }
2744
+
2745
+  /**
2746
+   * Cross-browser abstraction for sending XMLHttpRequest
2747
+   * @memberOf fabric.util
2748
+   * @param {String} url URL to send XMLHttpRequest to
2749
+   * @param {Object} [options] Options object
2750
+   * @param {String} [options.method="GET"]
2751
+   * @param {String} [options.parameters] parameters to append to url in GET or in body
2752
+   * @param {String} [options.body] body to send with POST or PUT request
2753
+   * @param {Function} options.onComplete Callback to invoke when request is completed
2754
+   * @return {XMLHttpRequest} request
2755
+   */
2756
+  function request(url, options) {
2757
+    options || (options = { });
2758
+
2759
+    var method = options.method ? options.method.toUpperCase() : 'GET',
2760
+        onComplete = options.onComplete || function() { },
2761
+        xhr = new fabric.window.XMLHttpRequest(),
2762
+        body = options.body || options.parameters;
2763
+
2764
+    /** @ignore */
2765
+    xhr.onreadystatechange = function() {
2766
+      if (xhr.readyState === 4) {
2767
+        onComplete(xhr);
2768
+        xhr.onreadystatechange = emptyFn;
2769
+      }
2770
+    };
2771
+
2772
+    if (method === 'GET') {
2773
+      body = null;
2774
+      if (typeof options.parameters === 'string') {
2775
+        url = addParamToUrl(url, options.parameters);
2776
+      }
2777
+    }
2778
+
2779
+    xhr.open(method, url, true);
2780
+
2781
+    if (method === 'POST' || method === 'PUT') {
2782
+      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
2783
+    }
2784
+
2785
+    xhr.send(body);
2786
+    return xhr;
2787
+  }
2788
+
2789
+  fabric.util.request = request;
2790
+})();
2791
+
2792
+
2793
+/**
2794
+ * Wrapper around `console.log` (when available)
2795
+ * @param {*} [values] Values to log
2796
+ */
2797
+fabric.log = function() { };
2798
+
2799
+/**
2800
+ * Wrapper around `console.warn` (when available)
2801
+ * @param {*} [values] Values to log as a warning
2802
+ */
2803
+fabric.warn = function() { };
2804
+
2805
+/* eslint-disable */
2806
+if (typeof console !== 'undefined') {
2807
+
2808
+  ['log', 'warn'].forEach(function(methodName) {
2809
+
2810
+    if (typeof console[methodName] !== 'undefined' &&
2811
+        typeof console[methodName].apply === 'function') {
2812
+
2813
+      fabric[methodName] = function() {
2814
+        return console[methodName].apply(console, arguments);
2815
+      };
2816
+    }
2817
+  });
2818
+}
2819
+/* eslint-enable */
2820
+
2821
+
2822
+(function() {
2823
+
2824
+  function noop() {
2825
+    return false;
2826
+  }
2827
+
2828
+  /**
2829
+   * Changes value from one to another within certain period of time, invoking callbacks as value is being changed.
2830
+   * @memberOf fabric.util
2831
+   * @param {Object} [options] Animation options
2832
+   * @param {Function} [options.onChange] Callback; invoked on every value change
2833
+   * @param {Function} [options.onComplete] Callback; invoked when value change is completed
2834
+   * @param {Number} [options.startValue=0] Starting value
2835
+   * @param {Number} [options.endValue=100] Ending value
2836
+   * @param {Number} [options.byValue=100] Value to modify the property by
2837
+   * @param {Function} [options.easing] Easing function
2838
+   * @param {Number} [options.duration=500] Duration of change (in ms)
2839
+   */
2840
+  function animate(options) {
2841
+
2842
+    requestAnimFrame(function(timestamp) {
2843
+      options || (options = { });
2844
+
2845
+      var start = timestamp || +new Date(),
2846
+          duration = options.duration || 500,
2847
+          finish = start + duration, time,
2848
+          onChange = options.onChange || noop,
2849
+          abort = options.abort || noop,
2850
+          onComplete = options.onComplete || noop,
2851
+          easing = options.easing || function(t, b, c, d) {return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;},
2852
+          startValue = 'startValue' in options ? options.startValue : 0,
2853
+          endValue = 'endValue' in options ? options.endValue : 100,
2854
+          byValue = options.byValue || endValue - startValue;
2855
+
2856
+      options.onStart && options.onStart();
2857
+
2858
+      (function tick(ticktime) {
2859
+        if (abort()) {
2860
+          onComplete(endValue, 1, 1);
2861
+          return;
2862
+        }
2863
+        time = ticktime || +new Date();
2864
+        var currentTime = time > finish ? duration : (time - start),
2865
+            timePerc = currentTime / duration,
2866
+            current = easing(currentTime, startValue, byValue, duration),
2867
+            valuePerc = Math.abs((current - startValue) / byValue);
2868
+        onChange(current, valuePerc, timePerc);
2869
+        if (time > finish) {
2870
+          options.onComplete && options.onComplete();
2871
+          return;
2872
+        }
2873
+        requestAnimFrame(tick);
2874
+      })(start);
2875
+    });
2876
+
2877
+  }
2878
+
2879
+  var _requestAnimFrame = fabric.window.requestAnimationFrame       ||
2880
+                          fabric.window.webkitRequestAnimationFrame ||
2881
+                          fabric.window.mozRequestAnimationFrame    ||
2882
+                          fabric.window.oRequestAnimationFrame      ||
2883
+                          fabric.window.msRequestAnimationFrame     ||
2884
+                          function(callback) {
2885
+                            return fabric.window.setTimeout(callback, 1000 / 60);
2886
+                          };
2887
+
2888
+  var _cancelAnimFrame = fabric.window.cancelAnimationFrame || fabric.window.clearTimeout;
2889
+
2890
+  /**
2891
+   * requestAnimationFrame polyfill based on http://paulirish.com/2011/requestanimationframe-for-smart-animating/
2892
+   * In order to get a precise start time, `requestAnimFrame` should be called as an entry into the method
2893
+   * @memberOf fabric.util
2894
+   * @param {Function} callback Callback to invoke
2895
+   * @param {DOMElement} element optional Element to associate with animation
2896
+   */
2897
+  function requestAnimFrame() {
2898
+    return _requestAnimFrame.apply(fabric.window, arguments);
2899
+  }
2900
+
2901
+  function cancelAnimFrame() {
2902
+    return _cancelAnimFrame.apply(fabric.window, arguments);
2903
+  }
2904
+
2905
+  fabric.util.animate = animate;
2906
+  fabric.util.requestAnimFrame = requestAnimFrame;
2907
+  fabric.util.cancelAnimFrame = cancelAnimFrame;
2908
+})();
2909
+
2910
+
2911
+(function() {
2912
+  // Calculate an in-between color. Returns a "rgba()" string.
2913
+  // Credit: Edwin Martin <edwin@bitstorm.org>
2914
+  //         http://www.bitstorm.org/jquery/color-animation/jquery.animate-colors.js
2915
+  function calculateColor(begin, end, pos) {
2916
+    var color = 'rgba('
2917
+        + parseInt((begin[0] + pos * (end[0] - begin[0])), 10) + ','
2918
+        + parseInt((begin[1] + pos * (end[1] - begin[1])), 10) + ','
2919
+        + parseInt((begin[2] + pos * (end[2] - begin[2])), 10);
2920
+
2921
+    color += ',' + (begin && end ? parseFloat(begin[3] + pos * (end[3] - begin[3])) : 1);
2922
+    color += ')';
2923
+    return color;
2924
+  }
2925
+
2926
+  /**
2927
+   * Changes the color from one to another within certain period of time, invoking callbacks as value is being changed.
2928
+   * @memberOf fabric.util
2929
+   * @param {String} fromColor The starting color in hex or rgb(a) format.
2930
+   * @param {String} toColor The starting color in hex or rgb(a) format.
2931
+   * @param {Number} [duration] Duration of change (in ms).
2932
+   * @param {Object} [options] Animation options
2933
+   * @param {Function} [options.onChange] Callback; invoked on every value change
2934
+   * @param {Function} [options.onComplete] Callback; invoked when value change is completed
2935
+   * @param {Function} [options.colorEasing] Easing function. Note that this function only take two arguments (currentTime, duration). Thus the regular animation easing functions cannot be used.
2936
+   */
2937
+  function animateColor(fromColor, toColor, duration, options) {
2938
+    var startColor = new fabric.Color(fromColor).getSource(),
2939
+        endColor = new fabric.Color(toColor).getSource();
2940
+
2941
+    options = options || {};
2942
+
2943
+    fabric.util.animate(fabric.util.object.extend(options, {
2944
+      duration: duration || 500,
2945
+      startValue: startColor,
2946
+      endValue: endColor,
2947
+      byValue: endColor,
2948
+      easing: function (currentTime, startValue, byValue, duration) {
2949
+        var posValue = options.colorEasing
2950
+          ? options.colorEasing(currentTime, duration)
2951
+          : 1 - Math.cos(currentTime / duration * (Math.PI / 2));
2952
+        return calculateColor(startValue, byValue, posValue);
2953
+      }
2954
+    }));
2955
+  }
2956
+
2957
+  fabric.util.animateColor = animateColor;
2958
+
2959
+})();
2960
+
2961
+
2962
+(function() {
2963
+
2964
+  function normalize(a, c, p, s) {
2965
+    if (a < Math.abs(c)) {
2966
+      a = c;
2967
+      s = p / 4;
2968
+    }
2969
+    else {
2970
+      //handle the 0/0 case:
2971
+      if (c === 0 && a === 0) {
2972
+        s = p / (2 * Math.PI) * Math.asin(1);
2973
+      }
2974
+      else {
2975
+        s = p / (2 * Math.PI) * Math.asin(c / a);
2976
+      }
2977
+    }
2978
+    return { a: a, c: c, p: p, s: s };
2979
+  }
2980
+
2981
+  function elastic(opts, t, d) {
2982
+    return opts.a *
2983
+      Math.pow(2, 10 * (t -= 1)) *
2984
+      Math.sin( (t * d - opts.s) * (2 * Math.PI) / opts.p );
2985
+  }
2986
+
2987
+  /**
2988
+   * Cubic easing out
2989
+   * @memberOf fabric.util.ease
2990
+   */
2991
+  function easeOutCubic(t, b, c, d) {
2992
+    return c * ((t = t / d - 1) * t * t + 1) + b;
2993
+  }
2994
+
2995
+  /**
2996
+   * Cubic easing in and out
2997
+   * @memberOf fabric.util.ease
2998
+   */
2999
+  function easeInOutCubic(t, b, c, d) {
3000
+    t /= d / 2;
3001
+    if (t < 1) {
3002
+      return c / 2 * t * t * t + b;
3003
+    }
3004
+    return c / 2 * ((t -= 2) * t * t + 2) + b;
3005
+  }
3006
+
3007
+  /**
3008
+   * Quartic easing in
3009
+   * @memberOf fabric.util.ease
3010
+   */
3011
+  function easeInQuart(t, b, c, d) {
3012
+    return c * (t /= d) * t * t * t + b;
3013
+  }
3014
+
3015
+  /**
3016
+   * Quartic easing out
3017
+   * @memberOf fabric.util.ease
3018
+   */
3019
+  function easeOutQuart(t, b, c, d) {
3020
+    return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3021
+  }
3022
+
3023
+  /**
3024
+   * Quartic easing in and out
3025
+   * @memberOf fabric.util.ease
3026
+   */
3027
+  function easeInOutQuart(t, b, c, d) {
3028
+    t /= d / 2;
3029
+    if (t < 1) {
3030
+      return c / 2 * t * t * t * t + b;
3031
+    }
3032
+    return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3033
+  }
3034
+
3035
+  /**
3036
+   * Quintic easing in
3037
+   * @memberOf fabric.util.ease
3038
+   */
3039
+  function easeInQuint(t, b, c, d) {
3040
+    return c * (t /= d) * t * t * t * t + b;
3041
+  }
3042
+
3043
+  /**
3044
+   * Quintic easing out
3045
+   * @memberOf fabric.util.ease
3046
+   */
3047
+  function easeOutQuint(t, b, c, d) {
3048
+    return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
3049
+  }
3050
+
3051
+  /**
3052
+   * Quintic easing in and out
3053
+   * @memberOf fabric.util.ease
3054
+   */
3055
+  function easeInOutQuint(t, b, c, d) {
3056
+    t /= d / 2;
3057
+    if (t < 1) {
3058
+      return c / 2 * t * t * t * t * t + b;
3059
+    }
3060
+    return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
3061
+  }
3062
+
3063
+  /**
3064
+   * Sinusoidal easing in
3065
+   * @memberOf fabric.util.ease
3066
+   */
3067
+  function easeInSine(t, b, c, d) {
3068
+    return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
3069
+  }
3070
+
3071
+  /**
3072
+   * Sinusoidal easing out
3073
+   * @memberOf fabric.util.ease
3074
+   */
3075
+  function easeOutSine(t, b, c, d) {
3076
+    return c * Math.sin(t / d * (Math.PI / 2)) + b;
3077
+  }
3078
+
3079
+  /**
3080
+   * Sinusoidal easing in and out
3081
+   * @memberOf fabric.util.ease
3082
+   */
3083
+  function easeInOutSine(t, b, c, d) {
3084
+    return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
3085
+  }
3086
+
3087
+  /**
3088
+   * Exponential easing in
3089
+   * @memberOf fabric.util.ease
3090
+   */
3091
+  function easeInExpo(t, b, c, d) {
3092
+    return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
3093
+  }
3094
+
3095
+  /**
3096
+   * Exponential easing out
3097
+   * @memberOf fabric.util.ease
3098
+   */
3099
+  function easeOutExpo(t, b, c, d) {
3100
+    return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
3101
+  }
3102
+
3103
+  /**
3104
+   * Exponential easing in and out
3105
+   * @memberOf fabric.util.ease
3106
+   */
3107
+  function easeInOutExpo(t, b, c, d) {
3108
+    if (t === 0) {
3109
+      return b;
3110
+    }
3111
+    if (t === d) {
3112
+      return b + c;
3113
+    }
3114
+    t /= d / 2;
3115
+    if (t < 1) {
3116
+      return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
3117
+    }
3118
+    return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
3119
+  }
3120
+
3121
+  /**
3122
+   * Circular easing in
3123
+   * @memberOf fabric.util.ease
3124
+   */
3125
+  function easeInCirc(t, b, c, d) {
3126
+    return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
3127
+  }
3128
+
3129
+  /**
3130
+   * Circular easing out
3131
+   * @memberOf fabric.util.ease
3132
+   */
3133
+  function easeOutCirc(t, b, c, d) {
3134
+    return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
3135
+  }
3136
+
3137
+  /**
3138
+   * Circular easing in and out
3139
+   * @memberOf fabric.util.ease
3140
+   */
3141
+  function easeInOutCirc(t, b, c, d) {
3142
+    t /= d / 2;
3143
+    if (t < 1) {
3144
+      return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
3145
+    }
3146
+    return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
3147
+  }
3148
+
3149
+  /**
3150
+   * Elastic easing in
3151
+   * @memberOf fabric.util.ease
3152
+   */
3153
+  function easeInElastic(t, b, c, d) {
3154
+    var s = 1.70158, p = 0, a = c;
3155
+    if (t === 0) {
3156
+      return b;
3157
+    }
3158
+    t /= d;
3159
+    if (t === 1) {
3160
+      return b + c;
3161
+    }
3162
+    if (!p) {
3163
+      p = d * 0.3;
3164
+    }
3165
+    var opts = normalize(a, c, p, s);
3166
+    return -elastic(opts, t, d) + b;
3167
+  }
3168
+
3169
+  /**
3170
+   * Elastic easing out
3171
+   * @memberOf fabric.util.ease
3172
+   */
3173
+  function easeOutElastic(t, b, c, d) {
3174
+    var s = 1.70158, p = 0, a = c;
3175
+    if (t === 0) {
3176
+      return b;
3177
+    }
3178
+    t /= d;
3179
+    if (t === 1) {
3180
+      return b + c;
3181
+    }
3182
+    if (!p) {
3183
+      p = d * 0.3;
3184
+    }
3185
+    var opts = normalize(a, c, p, s);
3186
+    return opts.a * Math.pow(2, -10 * t) * Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) + opts.c + b;
3187
+  }
3188
+
3189
+  /**
3190
+   * Elastic easing in and out
3191
+   * @memberOf fabric.util.ease
3192
+   */
3193
+  function easeInOutElastic(t, b, c, d) {
3194
+    var s = 1.70158, p = 0, a = c;
3195
+    if (t === 0) {
3196
+      return b;
3197
+    }
3198
+    t /= d / 2;
3199
+    if (t === 2) {
3200
+      return b + c;
3201
+    }
3202
+    if (!p) {
3203
+      p = d * (0.3 * 1.5);
3204
+    }
3205
+    var opts = normalize(a, c, p, s);
3206
+    if (t < 1) {
3207
+      return -0.5 * elastic(opts, t, d) + b;
3208
+    }
3209
+    return opts.a * Math.pow(2, -10 * (t -= 1)) *
3210
+      Math.sin((t * d - opts.s) * (2 * Math.PI) / opts.p ) * 0.5 + opts.c + b;
3211
+  }
3212
+
3213
+  /**
3214
+   * Backwards easing in
3215
+   * @memberOf fabric.util.ease
3216
+   */
3217
+  function easeInBack(t, b, c, d, s) {
3218
+    if (s === undefined) {
3219
+      s = 1.70158;
3220
+    }
3221
+    return c * (t /= d) * t * ((s + 1) * t - s) + b;
3222
+  }
3223
+
3224
+  /**
3225
+   * Backwards easing out
3226
+   * @memberOf fabric.util.ease
3227
+   */
3228
+  function easeOutBack(t, b, c, d, s) {
3229
+    if (s === undefined) {
3230
+      s = 1.70158;
3231
+    }
3232
+    return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3233
+  }
3234
+
3235
+  /**
3236
+   * Backwards easing in and out
3237
+   * @memberOf fabric.util.ease
3238
+   */
3239
+  function easeInOutBack(t, b, c, d, s) {
3240
+    if (s === undefined) {
3241
+      s = 1.70158;
3242
+    }
3243
+    t /= d / 2;
3244
+    if (t < 1) {
3245
+      return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3246
+    }
3247
+    return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3248
+  }
3249
+
3250
+  /**
3251
+   * Bouncing easing in
3252
+   * @memberOf fabric.util.ease
3253
+   */
3254
+  function easeInBounce(t, b, c, d) {
3255
+    return c - easeOutBounce (d - t, 0, c, d) + b;
3256
+  }
3257
+
3258
+  /**
3259
+   * Bouncing easing out
3260
+   * @memberOf fabric.util.ease
3261
+   */
3262
+  function easeOutBounce(t, b, c, d) {
3263
+    if ((t /= d) < (1 / 2.75)) {
3264
+      return c * (7.5625 * t * t) + b;
3265
+    }
3266
+    else if (t < (2 / 2.75)) {
3267
+      return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b;
3268
+    }
3269
+    else if (t < (2.5 / 2.75)) {
3270
+      return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b;
3271
+    }
3272
+    else {
3273
+      return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b;
3274
+    }
3275
+  }
3276
+
3277
+  /**
3278
+   * Bouncing easing in and out
3279
+   * @memberOf fabric.util.ease
3280
+   */
3281
+  function easeInOutBounce(t, b, c, d) {
3282
+    if (t < d / 2) {
3283
+      return easeInBounce (t * 2, 0, c, d) * 0.5 + b;
3284
+    }
3285
+    return easeOutBounce(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
3286
+  }
3287
+
3288
+  /**
3289
+   * Easing functions
3290
+   * See <a href="http://gizma.com/easing/">Easing Equations by Robert Penner</a>
3291
+   * @namespace fabric.util.ease
3292
+   */
3293
+  fabric.util.ease = {
3294
+
3295
+    /**
3296
+     * Quadratic easing in
3297
+     * @memberOf fabric.util.ease
3298
+     */
3299
+    easeInQuad: function(t, b, c, d) {
3300
+      return c * (t /= d) * t + b;
3301
+    },
3302
+
3303
+    /**
3304
+     * Quadratic easing out
3305
+     * @memberOf fabric.util.ease
3306
+     */
3307
+    easeOutQuad: function(t, b, c, d) {
3308
+      return -c * (t /= d) * (t - 2) + b;
3309
+    },
3310
+
3311
+    /**
3312
+     * Quadratic easing in and out
3313
+     * @memberOf fabric.util.ease
3314
+     */
3315
+    easeInOutQuad: function(t, b, c, d) {
3316
+      t /= (d / 2);
3317
+      if (t < 1) {
3318
+        return c / 2 * t * t + b;
3319
+      }
3320
+      return -c / 2 * ((--t) * (t - 2) - 1) + b;
3321
+    },
3322
+
3323
+    /**
3324
+     * Cubic easing in
3325
+     * @memberOf fabric.util.ease
3326
+     */
3327
+    easeInCubic: function(t, b, c, d) {
3328
+      return c * (t /= d) * t * t + b;
3329
+    },
3330
+
3331
+    easeOutCubic: easeOutCubic,
3332
+    easeInOutCubic: easeInOutCubic,
3333
+    easeInQuart: easeInQuart,
3334
+    easeOutQuart: easeOutQuart,
3335
+    easeInOutQuart: easeInOutQuart,
3336
+    easeInQuint: easeInQuint,
3337
+    easeOutQuint: easeOutQuint,
3338
+    easeInOutQuint: easeInOutQuint,
3339
+    easeInSine: easeInSine,
3340
+    easeOutSine: easeOutSine,
3341
+    easeInOutSine: easeInOutSine,
3342
+    easeInExpo: easeInExpo,
3343
+    easeOutExpo: easeOutExpo,
3344
+    easeInOutExpo: easeInOutExpo,
3345
+    easeInCirc: easeInCirc,
3346
+    easeOutCirc: easeOutCirc,
3347
+    easeInOutCirc: easeInOutCirc,
3348
+    easeInElastic: easeInElastic,
3349
+    easeOutElastic: easeOutElastic,
3350
+    easeInOutElastic: easeInOutElastic,
3351
+    easeInBack: easeInBack,
3352
+    easeOutBack: easeOutBack,
3353
+    easeInOutBack: easeInOutBack,
3354
+    easeInBounce: easeInBounce,
3355
+    easeOutBounce: easeOutBounce,
3356
+    easeInOutBounce: easeInOutBounce
3357
+  };
3358
+
3359
+})();
3360
+
3361
+
3362
+(function(global) {
3363
+
3364
+  'use strict';
3365
+
3366
+  /**
3367
+   * @name fabric
3368
+   * @namespace
3369
+   */
3370
+
3371
+  var fabric = global.fabric || (global.fabric = { }),
3372
+      extend = fabric.util.object.extend,
3373
+      clone = fabric.util.object.clone,
3374
+      toFixed = fabric.util.toFixed,
3375
+      parseUnit = fabric.util.parseUnit,
3376
+      multiplyTransformMatrices = fabric.util.multiplyTransformMatrices,
3377
+
3378
+      svgValidTagNames = ['path', 'circle', 'polygon', 'polyline', 'ellipse', 'rect', 'line',
3379
+        'image', 'text'],
3380
+      svgViewBoxElements = ['symbol', 'image', 'marker', 'pattern', 'view', 'svg'],
3381
+      svgInvalidAncestors = ['pattern', 'defs', 'symbol', 'metadata', 'clipPath', 'mask', 'desc'],
3382
+      svgValidParents = ['symbol', 'g', 'a', 'svg', 'clipPath', 'defs'],
3383
+
3384
+      attributesMap = {
3385
+        cx:                   'left',
3386
+        x:                    'left',
3387
+        r:                    'radius',
3388
+        cy:                   'top',
3389
+        y:                    'top',
3390
+        display:              'visible',
3391
+        visibility:           'visible',
3392
+        transform:            'transformMatrix',
3393
+        'fill-opacity':       'fillOpacity',
3394
+        'fill-rule':          'fillRule',
3395
+        'font-family':        'fontFamily',
3396
+        'font-size':          'fontSize',
3397
+        'font-style':         'fontStyle',
3398
+        'font-weight':        'fontWeight',
3399
+        'letter-spacing':     'charSpacing',
3400
+        'paint-order':        'paintFirst',
3401
+        'stroke-dasharray':   'strokeDashArray',
3402
+        'stroke-dashoffset':  'strokeDashOffset',
3403
+        'stroke-linecap':     'strokeLineCap',
3404
+        'stroke-linejoin':    'strokeLineJoin',
3405
+        'stroke-miterlimit':  'strokeMiterLimit',
3406
+        'stroke-opacity':     'strokeOpacity',
3407
+        'stroke-width':       'strokeWidth',
3408
+        'text-decoration':    'textDecoration',
3409
+        'text-anchor':        'textAnchor',
3410
+        opacity:              'opacity',
3411
+        'clip-path':          'clipPath',
3412
+        'clip-rule':          'clipRule',
3413
+      },
3414
+
3415
+      colorAttributes = {
3416
+        stroke: 'strokeOpacity',
3417
+        fill:   'fillOpacity'
3418
+      };
3419
+
3420
+  fabric.svgValidTagNamesRegEx = getSvgRegex(svgValidTagNames);
3421
+  fabric.svgViewBoxElementsRegEx = getSvgRegex(svgViewBoxElements);
3422
+  fabric.svgInvalidAncestorsRegEx = getSvgRegex(svgInvalidAncestors);
3423
+  fabric.svgValidParentsRegEx = getSvgRegex(svgValidParents);
3424
+
3425
+  fabric.cssRules = { };
3426
+  fabric.gradientDefs = { };
3427
+  fabric.clipPaths = { };
3428
+
3429
+  function normalizeAttr(attr) {
3430
+    // transform attribute names
3431
+    if (attr in attributesMap) {
3432
+      return attributesMap[attr];
3433
+    }
3434
+    return attr;
3435
+  }
3436
+
3437
+  function normalizeValue(attr, value, parentAttributes, fontSize) {
3438
+    var isArray = Object.prototype.toString.call(value) === '[object Array]',
3439
+        parsed;
3440
+
3441
+    if ((attr === 'fill' || attr === 'stroke') && value === 'none') {
3442
+      value = '';
3443
+    }
3444
+    else if (attr === 'strokeDashArray') {
3445
+      if (value === 'none') {
3446
+        value = null;
3447
+      }
3448
+      else {
3449
+        value = value.replace(/,/g, ' ').split(/\s+/).map(parseFloat);
3450
+      }
3451
+    }
3452
+    else if (attr === 'transformMatrix') {
3453
+      if (parentAttributes && parentAttributes.transformMatrix) {
3454
+        value = multiplyTransformMatrices(
3455
+          parentAttributes.transformMatrix, fabric.parseTransformAttribute(value));
3456
+      }
3457
+      else {
3458
+        value = fabric.parseTransformAttribute(value);
3459
+      }
3460
+    }
3461
+    else if (attr === 'visible') {
3462
+      value = value !== 'none' && value !== 'hidden';
3463
+      // display=none on parent element always takes precedence over child element
3464
+      if (parentAttributes && parentAttributes.visible === false) {
3465
+        value = false;
3466
+      }
3467
+    }
3468
+    else if (attr === 'opacity') {
3469
+      value = parseFloat(value);
3470
+      if (parentAttributes && typeof parentAttributes.opacity !== 'undefined') {
3471
+        value *= parentAttributes.opacity;
3472
+      }
3473
+    }
3474
+    else if (attr === 'textAnchor' /* text-anchor */) {
3475
+      value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
3476
+    }
3477
+    else if (attr === 'charSpacing') {
3478
+      // parseUnit returns px and we convert it to em
3479
+      parsed = parseUnit(value, fontSize) / fontSize * 1000;
3480
+    }
3481
+    else if (attr === 'paintFirst') {
3482
+      var fillIndex = value.indexOf('fill');
3483
+      var strokeIndex = value.indexOf('stroke');
3484
+      var value = 'fill';
3485
+      if (fillIndex > -1 && strokeIndex > -1 && strokeIndex < fillIndex) {
3486
+        value = 'stroke';
3487
+      }
3488
+      else if (fillIndex === -1 && strokeIndex > -1) {
3489
+        value = 'stroke';
3490
+      }
3491
+    }
3492
+    else if (attr === 'href' || attr === 'xlink:href') {
3493
+      return value;
3494
+    }
3495
+    else {
3496
+      parsed = isArray ? value.map(parseUnit) : parseUnit(value, fontSize);
3497
+    }
3498
+
3499
+    return (!isArray && isNaN(parsed) ? value : parsed);
3500
+  }
3501
+
3502
+  /**
3503
+    * @private
3504
+    */
3505
+  function getSvgRegex(arr) {
3506
+    return new RegExp('^(' + arr.join('|') + ')\\b', 'i');
3507
+  }
3508
+
3509
+  /**
3510
+   * @private
3511
+   * @param {Object} attributes Array of attributes to parse
3512
+   */
3513
+  function _setStrokeFillOpacity(attributes) {
3514
+    for (var attr in colorAttributes) {
3515
+
3516
+      if (typeof attributes[colorAttributes[attr]] === 'undefined' || attributes[attr] === '') {
3517
+        continue;
3518
+      }
3519
+
3520
+      if (typeof attributes[attr] === 'undefined') {
3521
+        if (!fabric.Object.prototype[attr]) {
3522
+          continue;
3523
+        }
3524
+        attributes[attr] = fabric.Object.prototype[attr];
3525
+      }
3526
+
3527
+      if (attributes[attr].indexOf('url(') === 0) {
3528
+        continue;
3529
+      }
3530
+
3531
+      var color = new fabric.Color(attributes[attr]);
3532
+      attributes[attr] = color.setAlpha(toFixed(color.getAlpha() * attributes[colorAttributes[attr]], 2)).toRgba();
3533
+    }
3534
+    return attributes;
3535
+  }
3536
+
3537
+  /**
3538
+   * @private
3539
+   */
3540
+  function _getMultipleNodes(doc, nodeNames) {
3541
+    var nodeName, nodeArray = [], nodeList, i, len;
3542
+    for (i = 0, len = nodeNames.length; i < len; i++) {
3543
+      nodeName = nodeNames[i];
3544
+      nodeList = doc.getElementsByTagName(nodeName);
3545
+      nodeArray = nodeArray.concat(Array.prototype.slice.call(nodeList));
3546
+    }
3547
+    return nodeArray;
3548
+  }
3549
+
3550
+  /**
3551
+   * Parses "transform" attribute, returning an array of values
3552
+   * @static
3553
+   * @function
3554
+   * @memberOf fabric
3555
+   * @param {String} attributeValue String containing attribute value
3556
+   * @return {Array} Array of 6 elements representing transformation matrix
3557
+   */
3558
+  fabric.parseTransformAttribute = (function() {
3559
+    function rotateMatrix(matrix, args) {
3560
+      var cos = fabric.util.cos(args[0]), sin = fabric.util.sin(args[0]),
3561
+          x = 0, y = 0;
3562
+      if (args.length === 3) {
3563
+        x = args[1];
3564
+        y = args[2];
3565
+      }
3566
+
3567
+      matrix[0] = cos;
3568
+      matrix[1] = sin;
3569
+      matrix[2] = -sin;
3570
+      matrix[3] = cos;
3571
+      matrix[4] = x - (cos * x - sin * y);
3572
+      matrix[5] = y - (sin * x + cos * y);
3573
+    }
3574
+
3575
+    function scaleMatrix(matrix, args) {
3576
+      var multiplierX = args[0],
3577
+          multiplierY = (args.length === 2) ? args[1] : args[0];
3578
+
3579
+      matrix[0] = multiplierX;
3580
+      matrix[3] = multiplierY;
3581
+    }
3582
+
3583
+    function skewMatrix(matrix, args, pos) {
3584
+      matrix[pos] = Math.tan(fabric.util.degreesToRadians(args[0]));
3585
+    }
3586
+
3587
+    function translateMatrix(matrix, args) {
3588
+      matrix[4] = args[0];
3589
+      if (args.length === 2) {
3590
+        matrix[5] = args[1];
3591
+      }
3592
+    }
3593
+
3594
+    // identity matrix
3595
+    var iMatrix = fabric.iMatrix,
3596
+
3597
+        // == begin transform regexp
3598
+        number = fabric.reNum,
3599
+
3600
+        commaWsp = '(?:\\s+,?\\s*|,\\s*)',
3601
+
3602
+        skewX = '(?:(skewX)\\s*\\(\\s*(' + number + ')\\s*\\))',
3603
+
3604
+        skewY = '(?:(skewY)\\s*\\(\\s*(' + number + ')\\s*\\))',
3605
+
3606
+        rotate = '(?:(rotate)\\s*\\(\\s*(' + number + ')(?:' +
3607
+                    commaWsp + '(' + number + ')' +
3608
+                    commaWsp + '(' + number + '))?\\s*\\))',
3609
+
3610
+        scale = '(?:(scale)\\s*\\(\\s*(' + number + ')(?:' +
3611
+                    commaWsp + '(' + number + '))?\\s*\\))',
3612
+
3613
+        translate = '(?:(translate)\\s*\\(\\s*(' + number + ')(?:' +
3614
+                    commaWsp + '(' + number + '))?\\s*\\))',
3615
+
3616
+        matrix = '(?:(matrix)\\s*\\(\\s*' +
3617
+                  '(' + number + ')' + commaWsp +
3618
+                  '(' + number + ')' + commaWsp +
3619
+                  '(' + number + ')' + commaWsp +
3620
+                  '(' + number + ')' + commaWsp +
3621
+                  '(' + number + ')' + commaWsp +
3622
+                  '(' + number + ')' +
3623
+                  '\\s*\\))',
3624
+
3625
+        transform = '(?:' +
3626
+                    matrix + '|' +
3627
+                    translate + '|' +
3628
+                    scale + '|' +
3629
+                    rotate + '|' +
3630
+                    skewX + '|' +
3631
+                    skewY +
3632
+                    ')',
3633
+
3634
+        transforms = '(?:' + transform + '(?:' + commaWsp + '*' + transform + ')*' + ')',
3635
+
3636
+        transformList = '^\\s*(?:' + transforms + '?)\\s*$',
3637
+
3638
+        // http://www.w3.org/TR/SVG/coords.html#TransformAttribute
3639
+        reTransformList = new RegExp(transformList),
3640
+        // == end transform regexp
3641
+
3642
+        reTransform = new RegExp(transform, 'g');
3643
+
3644
+    return function(attributeValue) {
3645
+
3646
+      // start with identity matrix
3647
+      var matrix = iMatrix.concat(),
3648
+          matrices = [];
3649
+
3650
+      // return if no argument was given or
3651
+      // an argument does not match transform attribute regexp
3652
+      if (!attributeValue || (attributeValue && !reTransformList.test(attributeValue))) {
3653
+        return matrix;
3654
+      }
3655
+
3656
+      attributeValue.replace(reTransform, function(match) {
3657
+
3658
+        var m = new RegExp(transform).exec(match).filter(function (match) {
3659
+              // match !== '' && match != null
3660
+              return (!!match);
3661
+            }),
3662
+            operation = m[1],
3663
+            args = m.slice(2).map(parseFloat);
3664
+
3665
+        switch (operation) {
3666
+          case 'translate':
3667
+            translateMatrix(matrix, args);
3668
+            break;
3669
+          case 'rotate':
3670
+            args[0] = fabric.util.degreesToRadians(args[0]);
3671
+            rotateMatrix(matrix, args);
3672
+            break;
3673
+          case 'scale':
3674
+            scaleMatrix(matrix, args);
3675
+            break;
3676
+          case 'skewX':
3677
+            skewMatrix(matrix, args, 2);
3678
+            break;
3679
+          case 'skewY':
3680
+            skewMatrix(matrix, args, 1);
3681
+            break;
3682
+          case 'matrix':
3683
+            matrix = args;
3684
+            break;
3685
+        }
3686
+
3687
+        // snapshot current matrix into matrices array
3688
+        matrices.push(matrix.concat());
3689
+        // reset
3690
+        matrix = iMatrix.concat();
3691
+      });
3692
+
3693
+      var combinedMatrix = matrices[0];
3694
+      while (matrices.length > 1) {
3695
+        matrices.shift();
3696
+        combinedMatrix = fabric.util.multiplyTransformMatrices(combinedMatrix, matrices[0]);
3697
+      }
3698
+      return combinedMatrix;
3699
+    };
3700
+  })();
3701
+
3702
+  /**
3703
+   * @private
3704
+   */
3705
+  function parseStyleString(style, oStyle) {
3706
+    var attr, value;
3707
+    style.replace(/;\s*$/, '').split(';').forEach(function (chunk) {
3708
+      var pair = chunk.split(':');
3709
+
3710
+      attr = pair[0].trim().toLowerCase();
3711
+      value =  pair[1].trim();
3712
+
3713
+      oStyle[attr] = value;
3714
+    });
3715
+  }
3716
+
3717
+  /**
3718
+   * @private
3719
+   */
3720
+  function parseStyleObject(style, oStyle) {
3721
+    var attr, value;
3722
+    for (var prop in style) {
3723
+      if (typeof style[prop] === 'undefined') {
3724
+        continue;
3725
+      }
3726
+
3727
+      attr = prop.toLowerCase();
3728
+      value = style[prop];
3729
+
3730
+      oStyle[attr] = value;
3731
+    }
3732
+  }
3733
+
3734
+  /**
3735
+   * @private
3736
+   */
3737
+  function getGlobalStylesForElement(element, svgUid) {
3738
+    var styles = { };
3739
+    for (var rule in fabric.cssRules[svgUid]) {
3740
+      if (elementMatchesRule(element, rule.split(' '))) {
3741
+        for (var property in fabric.cssRules[svgUid][rule]) {
3742
+          styles[property] = fabric.cssRules[svgUid][rule][property];
3743
+        }
3744
+      }
3745
+    }
3746
+    return styles;
3747
+  }
3748
+
3749
+  /**
3750
+   * @private
3751
+   */
3752
+  function elementMatchesRule(element, selectors) {
3753
+    var firstMatching, parentMatching = true;
3754
+    //start from rightmost selector.
3755
+    firstMatching = selectorMatches(element, selectors.pop());
3756
+    if (firstMatching && selectors.length) {
3757
+      parentMatching = doesSomeParentMatch(element, selectors);
3758
+    }
3759
+    return firstMatching && parentMatching && (selectors.length === 0);
3760
+  }
3761
+
3762
+  function doesSomeParentMatch(element, selectors) {
3763
+    var selector, parentMatching = true;
3764
+    while (element.parentNode && element.parentNode.nodeType === 1 && selectors.length) {
3765
+      if (parentMatching) {
3766
+        selector = selectors.pop();
3767
+      }
3768
+      element = element.parentNode;
3769
+      parentMatching = selectorMatches(element, selector);
3770
+    }
3771
+    return selectors.length === 0;
3772
+  }
3773
+
3774
+  /**
3775
+   * @private
3776
+   */
3777
+  function selectorMatches(element, selector) {
3778
+    var nodeName = element.nodeName,
3779
+        classNames = element.getAttribute('class'),
3780
+        id = element.getAttribute('id'), matcher, i;
3781
+    // i check if a selector matches slicing away part from it.
3782
+    // if i get empty string i should match
3783
+    matcher = new RegExp('^' + nodeName, 'i');
3784
+    selector = selector.replace(matcher, '');
3785
+    if (id && selector.length) {
3786
+      matcher = new RegExp('#' + id + '(?![a-zA-Z\\-]+)', 'i');
3787
+      selector = selector.replace(matcher, '');
3788
+    }
3789
+    if (classNames && selector.length) {
3790
+      classNames = classNames.split(' ');
3791
+      for (i = classNames.length; i--;) {
3792
+        matcher = new RegExp('\\.' + classNames[i] + '(?![a-zA-Z\\-]+)', 'i');
3793
+        selector = selector.replace(matcher, '');
3794
+      }
3795
+    }
3796
+    return selector.length === 0;
3797
+  }
3798
+
3799
+  /**
3800
+   * @private
3801
+   * to support IE8 missing getElementById on SVGdocument and on node xmlDOM
3802
+   */
3803
+  function elementById(doc, id) {
3804
+    var el;
3805
+    doc.getElementById && (el = doc.getElementById(id));
3806
+    if (el) {
3807
+      return el;
3808
+    }
3809
+    var node, i, len, nodelist = doc.getElementsByTagName('*');
3810
+    for (i = 0, len = nodelist.length; i < len; i++) {
3811
+      node = nodelist[i];
3812
+      if (id === node.getAttribute('id')) {
3813
+        return node;
3814
+      }
3815
+    }
3816
+  }
3817
+
3818
+  /**
3819
+   * @private
3820
+   */
3821
+  function parseUseDirectives(doc) {
3822
+    var nodelist = _getMultipleNodes(doc, ['use', 'svg:use']), i = 0;
3823
+    while (nodelist.length && i < nodelist.length) {
3824
+      var el = nodelist[i],
3825
+          xlink = (el.getAttribute('xlink:href') || el.getAttribute('href')).substr(1),
3826
+          x = el.getAttribute('x') || 0,
3827
+          y = el.getAttribute('y') || 0,
3828
+          el2 = elementById(doc, xlink).cloneNode(true),
3829
+          currentTrans = (el2.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')',
3830
+          parentNode, oldLength = nodelist.length, attr, j, attrs, len;
3831
+
3832
+      applyViewboxTransform(el2);
3833
+      if (/^svg$/i.test(el2.nodeName)) {
3834
+        var el3 = el2.ownerDocument.createElement('g');
3835
+        for (j = 0, attrs = el2.attributes, len = attrs.length; j < len; j++) {
3836
+          attr = attrs.item(j);
3837
+          el3.setAttribute(attr.nodeName, attr.nodeValue);
3838
+        }
3839
+        // el2.firstChild != null
3840
+        while (el2.firstChild) {
3841
+          el3.appendChild(el2.firstChild);
3842
+        }
3843
+        el2 = el3;
3844
+      }
3845
+
3846
+      for (j = 0, attrs = el.attributes, len = attrs.length; j < len; j++) {
3847
+        attr = attrs.item(j);
3848
+        if (attr.nodeName === 'x' || attr.nodeName === 'y' ||
3849
+          attr.nodeName === 'xlink:href' || attr.nodeName === 'href') {
3850
+          continue;
3851
+        }
3852
+
3853
+        if (attr.nodeName === 'transform') {
3854
+          currentTrans = attr.nodeValue + ' ' + currentTrans;
3855
+        }
3856
+        else {
3857
+          el2.setAttribute(attr.nodeName, attr.nodeValue);
3858
+        }
3859
+      }
3860
+
3861
+      el2.setAttribute('transform', currentTrans);
3862
+      el2.setAttribute('instantiated_by_use', '1');
3863
+      el2.removeAttribute('id');
3864
+      parentNode = el.parentNode;
3865
+      parentNode.replaceChild(el2, el);
3866
+      // some browsers do not shorten nodelist after replaceChild (IE8)
3867
+      if (nodelist.length === oldLength) {
3868
+        i++;
3869
+      }
3870
+    }
3871
+  }
3872
+
3873
+  // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
3874
+  // matches, e.g.: +14.56e-12, etc.
3875
+  var reViewBoxAttrValue = new RegExp(
3876
+    '^' +
3877
+    '\\s*(' + fabric.reNum + '+)\\s*,?' +
3878
+    '\\s*(' + fabric.reNum + '+)\\s*,?' +
3879
+    '\\s*(' + fabric.reNum + '+)\\s*,?' +
3880
+    '\\s*(' + fabric.reNum + '+)\\s*' +
3881
+    '$'
3882
+  );
3883
+
3884
+  /**
3885
+   * Add a <g> element that envelop all child elements and makes the viewbox transformMatrix descend on all elements
3886
+   */
3887
+  function applyViewboxTransform(element) {
3888
+
3889
+    var viewBoxAttr = element.getAttribute('viewBox'),
3890
+        scaleX = 1,
3891
+        scaleY = 1,
3892
+        minX = 0,
3893
+        minY = 0,
3894
+        viewBoxWidth, viewBoxHeight, matrix, el,
3895
+        widthAttr = element.getAttribute('width'),
3896
+        heightAttr = element.getAttribute('height'),
3897
+        x = element.getAttribute('x') || 0,
3898
+        y = element.getAttribute('y') || 0,
3899
+        preserveAspectRatio = element.getAttribute('preserveAspectRatio') || '',
3900
+        missingViewBox = (!viewBoxAttr || !fabric.svgViewBoxElementsRegEx.test(element.nodeName)
3901
+                           || !(viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))),
3902
+        missingDimAttr = (!widthAttr || !heightAttr || widthAttr === '100%' || heightAttr === '100%'),
3903
+        toBeParsed = missingViewBox && missingDimAttr,
3904
+        parsedDim = { }, translateMatrix = '', widthDiff = 0, heightDiff = 0;
3905
+
3906
+    parsedDim.width = 0;
3907
+    parsedDim.height = 0;
3908
+    parsedDim.toBeParsed = toBeParsed;
3909
+
3910
+    if (toBeParsed) {
3911
+      return parsedDim;
3912
+    }
3913
+
3914
+    if (missingViewBox) {
3915
+      parsedDim.width = parseUnit(widthAttr);
3916
+      parsedDim.height = parseUnit(heightAttr);
3917
+      return parsedDim;
3918
+    }
3919
+
3920
+    minX = -parseFloat(viewBoxAttr[1]);
3921
+    minY = -parseFloat(viewBoxAttr[2]);
3922
+    viewBoxWidth = parseFloat(viewBoxAttr[3]);
3923
+    viewBoxHeight = parseFloat(viewBoxAttr[4]);
3924
+
3925
+    if (!missingDimAttr) {
3926
+      parsedDim.width = parseUnit(widthAttr);
3927
+      parsedDim.height = parseUnit(heightAttr);
3928
+      scaleX = parsedDim.width / viewBoxWidth;
3929
+      scaleY = parsedDim.height / viewBoxHeight;
3930
+    }
3931
+    else {
3932
+      parsedDim.width = viewBoxWidth;
3933
+      parsedDim.height = viewBoxHeight;
3934
+    }
3935
+
3936
+    // default is to preserve aspect ratio
3937
+    preserveAspectRatio = fabric.util.parsePreserveAspectRatioAttribute(preserveAspectRatio);
3938
+    if (preserveAspectRatio.alignX !== 'none') {
3939
+      //translate all container for the effect of Mid, Min, Max
3940
+      if (preserveAspectRatio.meetOrSlice === 'meet') {
3941
+        scaleY = scaleX = (scaleX > scaleY ? scaleY : scaleX);
3942
+        // calculate additional translation to move the viewbox
3943
+      }
3944
+      if (preserveAspectRatio.meetOrSlice === 'slice') {
3945
+        scaleY = scaleX = (scaleX > scaleY ? scaleX : scaleY);
3946
+        // calculate additional translation to move the viewbox
3947
+      }
3948
+      widthDiff = parsedDim.width - viewBoxWidth * scaleX;
3949
+      heightDiff = parsedDim.height - viewBoxHeight * scaleX;
3950
+      if (preserveAspectRatio.alignX === 'Mid') {
3951
+        widthDiff /= 2;
3952
+      }
3953
+      if (preserveAspectRatio.alignY === 'Mid') {
3954
+        heightDiff /= 2;
3955
+      }
3956
+      if (preserveAspectRatio.alignX === 'Min') {
3957
+        widthDiff = 0;
3958
+      }
3959
+      if (preserveAspectRatio.alignY === 'Min') {
3960
+        heightDiff = 0;
3961
+      }
3962
+    }
3963
+
3964
+    if (scaleX === 1 && scaleY === 1 && minX === 0 && minY === 0 && x === 0 && y === 0) {
3965
+      return parsedDim;
3966
+    }
3967
+
3968
+    if (x || y) {
3969
+      translateMatrix = ' translate(' + parseUnit(x) + ' ' + parseUnit(y) + ') ';
3970
+    }
3971
+
3972
+    matrix = translateMatrix + ' matrix(' + scaleX +
3973
+                  ' 0' +
3974
+                  ' 0 ' +
3975
+                  scaleY + ' ' +
3976
+                  (minX * scaleX + widthDiff) + ' ' +
3977
+                  (minY * scaleY + heightDiff) + ') ';
3978
+    parsedDim.viewboxTransform = fabric.parseTransformAttribute(matrix);
3979
+    if (element.nodeName === 'svg') {
3980
+      el = element.ownerDocument.createElement('g');
3981
+      // element.firstChild != null
3982
+      while (element.firstChild) {
3983
+        el.appendChild(element.firstChild);
3984
+      }
3985
+      element.appendChild(el);
3986
+    }
3987
+    else {
3988
+      el = element;
3989
+      matrix = el.getAttribute('transform') + matrix;
3990
+    }
3991
+    el.setAttribute('transform', matrix);
3992
+    return parsedDim;
3993
+  }
3994
+
3995
+  function hasAncestorWithNodeName(element, nodeName) {
3996
+    while (element && (element = element.parentNode)) {
3997
+      if (element.nodeName && nodeName.test(element.nodeName.replace('svg:', ''))
3998
+        && !element.getAttribute('instantiated_by_use')) {
3999
+        return true;
4000
+      }
4001
+    }
4002
+    return false;
4003
+  }
4004
+
4005
+  /**
4006
+   * Parses an SVG document, converts it to an array of corresponding fabric.* instances and passes them to a callback
4007
+   * @static
4008
+   * @function
4009
+   * @memberOf fabric
4010
+   * @param {SVGDocument} doc SVG document to parse
4011
+   * @param {Function} callback Callback to call when parsing is finished;
4012
+   * It's being passed an array of elements (parsed from a document).
4013
+   * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
4014
+   * @param {Object} [parsingOptions] options for parsing document
4015
+   * @param {String} [parsingOptions.crossOrigin] crossOrigin settings
4016
+   */
4017
+  fabric.parseSVGDocument = function(doc, callback, reviver, parsingOptions) {
4018
+    if (!doc) {
4019
+      return;
4020
+    }
4021
+
4022
+    parseUseDirectives(doc);
4023
+
4024
+    var svgUid =  fabric.Object.__uid++, i, len,
4025
+        options = applyViewboxTransform(doc),
4026
+        descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
4027
+    options.crossOrigin = parsingOptions && parsingOptions.crossOrigin;
4028
+    options.svgUid = svgUid;
4029
+
4030
+    if (descendants.length === 0 && fabric.isLikelyNode) {
4031
+      // we're likely in node, where "o3-xml" library fails to gEBTN("*")
4032
+      // https://github.com/ajaxorg/node-o3-xml/issues/21
4033
+      descendants = doc.selectNodes('//*[name(.)!="svg"]');
4034
+      var arr = [];
4035
+      for (i = 0, len = descendants.length; i < len; i++) {
4036
+        arr[i] = descendants[i];
4037
+      }
4038
+      descendants = arr;
4039
+    }
4040
+
4041
+    var elements = descendants.filter(function(el) {
4042
+      applyViewboxTransform(el);
4043
+      return fabric.svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', '')) &&
4044
+            !hasAncestorWithNodeName(el, fabric.svgInvalidAncestorsRegEx); // http://www.w3.org/TR/SVG/struct.html#DefsElement
4045
+    });
4046
+
4047
+    if (!elements || (elements && !elements.length)) {
4048
+      callback && callback([], {});
4049
+      return;
4050
+    }
4051
+    var clipPaths = { };
4052
+    descendants.filter(function(el) {
4053
+      return el.nodeName.replace('svg:', '') === 'clipPath';
4054
+    }).forEach(function(el) {
4055
+      var id = el.getAttribute('id');
4056
+      clipPaths[id] = fabric.util.toArray(el.getElementsByTagName('*')).filter(function(el) {
4057
+        return fabric.svgValidTagNamesRegEx.test(el.nodeName.replace('svg:', ''));
4058
+      });
4059
+    });
4060
+    fabric.gradientDefs[svgUid] = fabric.getGradientDefs(doc);
4061
+    fabric.cssRules[svgUid] = fabric.getCSSRules(doc);
4062
+    fabric.clipPaths[svgUid] = clipPaths;
4063
+    // Precedence of rules:   style > class > attribute
4064
+    fabric.parseElements(elements, function(instances, elements) {
4065
+      if (callback) {
4066
+        callback(instances, options, elements, descendants);
4067
+        delete fabric.gradientDefs[svgUid];
4068
+        delete fabric.cssRules[svgUid];
4069
+        delete fabric.clipPaths[svgUid];
4070
+      }
4071
+    }, clone(options), reviver, parsingOptions);
4072
+  };
4073
+
4074
+  function recursivelyParseGradientsXlink(doc, gradient) {
4075
+    var gradientsAttrs = ['gradientTransform', 'x1', 'x2', 'y1', 'y2', 'gradientUnits', 'cx', 'cy', 'r', 'fx', 'fy'],
4076
+        xlinkAttr = 'xlink:href',
4077
+        xLink = gradient.getAttribute(xlinkAttr).substr(1),
4078
+        referencedGradient = elementById(doc, xLink);
4079
+    if (referencedGradient && referencedGradient.getAttribute(xlinkAttr)) {
4080
+      recursivelyParseGradientsXlink(doc, referencedGradient);
4081
+    }
4082
+    gradientsAttrs.forEach(function(attr) {
4083
+      if (!gradient.hasAttribute(attr)) {
4084
+        gradient.setAttribute(attr, referencedGradient.getAttribute(attr));
4085
+      }
4086
+    });
4087
+    if (!gradient.children.length) {
4088
+      var referenceClone = referencedGradient.cloneNode(true);
4089
+      while (referenceClone.firstChild) {
4090
+        gradient.appendChild(referenceClone.firstChild);
4091
+      }
4092
+    }
4093
+    gradient.removeAttribute(xlinkAttr);
4094
+  }
4095
+
4096
+  var reFontDeclaration = new RegExp(
4097
+    '(normal|italic)?\\s*(normal|small-caps)?\\s*' +
4098
+    '(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\\s*(' +
4099
+      fabric.reNum +
4100
+    '(?:px|cm|mm|em|pt|pc|in)*)(?:\\/(normal|' + fabric.reNum + '))?\\s+(.*)');
4101
+
4102
+  extend(fabric, {
4103
+    /**
4104
+     * Parses a short font declaration, building adding its properties to a style object
4105
+     * @static
4106
+     * @function
4107
+     * @memberOf fabric
4108
+     * @param {String} value font declaration
4109
+     * @param {Object} oStyle definition
4110
+     */
4111
+    parseFontDeclaration: function(value, oStyle) {
4112
+      var match = value.match(reFontDeclaration);
4113
+
4114
+      if (!match) {
4115
+        return;
4116
+      }
4117
+      var fontStyle = match[1],
4118
+          // font variant is not used
4119
+          // fontVariant = match[2],
4120
+          fontWeight = match[3],
4121
+          fontSize = match[4],
4122
+          lineHeight = match[5],
4123
+          fontFamily = match[6];
4124
+
4125
+      if (fontStyle) {
4126
+        oStyle.fontStyle = fontStyle;
4127
+      }
4128
+      if (fontWeight) {
4129
+        oStyle.fontWeight = isNaN(parseFloat(fontWeight)) ? fontWeight : parseFloat(fontWeight);
4130
+      }
4131
+      if (fontSize) {
4132
+        oStyle.fontSize = parseUnit(fontSize);
4133
+      }
4134
+      if (fontFamily) {
4135
+        oStyle.fontFamily = fontFamily;
4136
+      }
4137
+      if (lineHeight) {
4138
+        oStyle.lineHeight = lineHeight === 'normal' ? 1 : lineHeight;
4139
+      }
4140
+    },
4141
+
4142
+    /**
4143
+     * Parses an SVG document, returning all of the gradient declarations found in it
4144
+     * @static
4145
+     * @function
4146
+     * @memberOf fabric
4147
+     * @param {SVGDocument} doc SVG document to parse
4148
+     * @return {Object} Gradient definitions; key corresponds to element id, value -- to gradient definition element
4149
+     */
4150
+    getGradientDefs: function(doc) {
4151
+      var tagArray = [
4152
+            'linearGradient',
4153
+            'radialGradient',
4154
+            'svg:linearGradient',
4155
+            'svg:radialGradient'],
4156
+          elList = _getMultipleNodes(doc, tagArray),
4157
+          el, j = 0, gradientDefs = { };
4158
+      j = elList.length;
4159
+      while (j--) {
4160
+        el = elList[j];
4161
+        if (el.getAttribute('xlink:href')) {
4162
+          recursivelyParseGradientsXlink(doc, el);
4163
+        }
4164
+        gradientDefs[el.getAttribute('id')] = el;
4165
+      }
4166
+      return gradientDefs;
4167
+    },
4168
+
4169
+    /**
4170
+     * Returns an object of attributes' name/value, given element and an array of attribute names;
4171
+     * Parses parent "g" nodes recursively upwards.
4172
+     * @static
4173
+     * @memberOf fabric
4174
+     * @param {DOMElement} element Element to parse
4175
+     * @param {Array} attributes Array of attributes to parse
4176
+     * @return {Object} object containing parsed attributes' names/values
4177
+     */
4178
+    parseAttributes: function(element, attributes, svgUid) {
4179
+
4180
+      if (!element) {
4181
+        return;
4182
+      }
4183
+
4184
+      var value,
4185
+          parentAttributes = { },
4186
+          fontSize, parentFontSize;
4187
+
4188
+      if (typeof svgUid === 'undefined') {
4189
+        svgUid = element.getAttribute('svgUid');
4190
+      }
4191
+      // if there's a parent container (`g` or `a` or `symbol` node), parse its attributes recursively upwards
4192
+      if (element.parentNode && fabric.svgValidParentsRegEx.test(element.parentNode.nodeName)) {
4193
+        parentAttributes = fabric.parseAttributes(element.parentNode, attributes, svgUid);
4194
+      }
4195
+
4196
+      var ownAttributes = attributes.reduce(function(memo, attr) {
4197
+        value = element.getAttribute(attr);
4198
+        if (value) { // eslint-disable-line
4199
+          memo[attr] = value;
4200
+        }
4201
+        return memo;
4202
+      }, { });
4203
+      // add values parsed from style, which take precedence over attributes
4204
+      // (see: http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes)
4205
+      ownAttributes = extend(ownAttributes,
4206
+        extend(getGlobalStylesForElement(element, svgUid), fabric.parseStyleAttribute(element)));
4207
+
4208
+      fontSize = parentFontSize = parentAttributes.fontSize || fabric.Text.DEFAULT_SVG_FONT_SIZE;
4209
+      if (ownAttributes['font-size']) {
4210
+        // looks like the minimum should be 9px when dealing with ems. this is what looks like in browsers.
4211
+        ownAttributes['font-size'] = fontSize = parseUnit(ownAttributes['font-size'], parentFontSize);
4212
+      }
4213
+
4214
+      var normalizedAttr, normalizedValue, normalizedStyle = {};
4215
+      for (var attr in ownAttributes) {
4216
+        normalizedAttr = normalizeAttr(attr);
4217
+        normalizedValue = normalizeValue(normalizedAttr, ownAttributes[attr], parentAttributes, fontSize);
4218
+        normalizedStyle[normalizedAttr] = normalizedValue;
4219
+      }
4220
+      if (normalizedStyle && normalizedStyle.font) {
4221
+        fabric.parseFontDeclaration(normalizedStyle.font, normalizedStyle);
4222
+      }
4223
+      var mergedAttrs = extend(parentAttributes, normalizedStyle);
4224
+      return fabric.svgValidParentsRegEx.test(element.nodeName) ? mergedAttrs : _setStrokeFillOpacity(mergedAttrs);
4225
+    },
4226
+
4227
+    /**
4228
+     * Transforms an array of svg elements to corresponding fabric.* instances
4229
+     * @static
4230
+     * @memberOf fabric
4231
+     * @param {Array} elements Array of elements to parse
4232
+     * @param {Function} callback Being passed an array of fabric instances (transformed from SVG elements)
4233
+     * @param {Object} [options] Options object
4234
+     * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
4235
+     */
4236
+    parseElements: function(elements, callback, options, reviver, parsingOptions) {
4237
+      new fabric.ElementsParser(elements, callback, options, reviver, parsingOptions).parse();
4238
+    },
4239
+
4240
+    /**
4241
+     * Parses "style" attribute, retuning an object with values
4242
+     * @static
4243
+     * @memberOf fabric
4244
+     * @param {SVGElement} element Element to parse
4245
+     * @return {Object} Objects with values parsed from style attribute of an element
4246
+     */
4247
+    parseStyleAttribute: function(element) {
4248
+      var oStyle = { },
4249
+          style = element.getAttribute('style');
4250
+
4251
+      if (!style) {
4252
+        return oStyle;
4253
+      }
4254
+
4255
+      if (typeof style === 'string') {
4256
+        parseStyleString(style, oStyle);
4257
+      }
4258
+      else {
4259
+        parseStyleObject(style, oStyle);
4260
+      }
4261
+
4262
+      return oStyle;
4263
+    },
4264
+
4265
+    /**
4266
+     * Parses "points" attribute, returning an array of values
4267
+     * @static
4268
+     * @memberOf fabric
4269
+     * @param {String} points points attribute string
4270
+     * @return {Array} array of points
4271
+     */
4272
+    parsePointsAttribute: function(points) {
4273
+
4274
+      // points attribute is required and must not be empty
4275
+      if (!points) {
4276
+        return null;
4277
+      }
4278
+
4279
+      // replace commas with whitespace and remove bookending whitespace
4280
+      points = points.replace(/,/g, ' ').trim();
4281
+
4282
+      points = points.split(/\s+/);
4283
+      var parsedPoints = [], i, len;
4284
+
4285
+      for (i = 0, len = points.length; i < len; i += 2) {
4286
+        parsedPoints.push({
4287
+          x: parseFloat(points[i]),
4288
+          y: parseFloat(points[i + 1])
4289
+        });
4290
+      }
4291
+
4292
+      // odd number of points is an error
4293
+      // if (parsedPoints.length % 2 !== 0) {
4294
+      //   return null;
4295
+      // }
4296
+
4297
+      return parsedPoints;
4298
+    },
4299
+
4300
+    /**
4301
+     * Returns CSS rules for a given SVG document
4302
+     * @static
4303
+     * @function
4304
+     * @memberOf fabric
4305
+     * @param {SVGDocument} doc SVG document to parse
4306
+     * @return {Object} CSS rules of this document
4307
+     */
4308
+    getCSSRules: function(doc) {
4309
+      var styles = doc.getElementsByTagName('style'), i, len,
4310
+          allRules = { }, rules;
4311
+
4312
+      // very crude parsing of style contents
4313
+      for (i = 0, len = styles.length; i < len; i++) {
4314
+        // IE9 doesn't support textContent, but provides text instead.
4315
+        var styleContents = styles[i].textContent || styles[i].text;
4316
+
4317
+        // remove comments
4318
+        styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, '');
4319
+        if (styleContents.trim() === '') {
4320
+          continue;
4321
+        }
4322
+        rules = styleContents.match(/[^{]*\{[\s\S]*?\}/g);
4323
+        rules = rules.map(function(rule) { return rule.trim(); });
4324
+        // eslint-disable-next-line no-loop-func
4325
+        rules.forEach(function(rule) {
4326
+
4327
+          var match = rule.match(/([\s\S]*?)\s*\{([^}]*)\}/),
4328
+              ruleObj = { }, declaration = match[2].trim(),
4329
+              propertyValuePairs = declaration.replace(/;$/, '').split(/\s*;\s*/);
4330
+
4331
+          for (i = 0, len = propertyValuePairs.length; i < len; i++) {
4332
+            var pair = propertyValuePairs[i].split(/\s*:\s*/),
4333
+                property = pair[0],
4334
+                value = pair[1];
4335
+            ruleObj[property] = value;
4336
+          }
4337
+          rule = match[1];
4338
+          rule.split(',').forEach(function(_rule) {
4339
+            _rule = _rule.replace(/^svg/i, '').trim();
4340
+            if (_rule === '') {
4341
+              return;
4342
+            }
4343
+            if (allRules[_rule]) {
4344
+              fabric.util.object.extend(allRules[_rule], ruleObj);
4345
+            }
4346
+            else {
4347
+              allRules[_rule] = fabric.util.object.clone(ruleObj);
4348
+            }
4349
+          });
4350
+        });
4351
+      }
4352
+      return allRules;
4353
+    },
4354
+
4355
+    /**
4356
+     * Takes url corresponding to an SVG document, and parses it into a set of fabric objects.
4357
+     * Note that SVG is fetched via XMLHttpRequest, so it needs to conform to SOP (Same Origin Policy)
4358
+     * @memberOf fabric
4359
+     * @param {String} url
4360
+     * @param {Function} callback
4361
+     * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
4362
+     * @param {Object} [options] Object containing options for parsing
4363
+     * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources
4364
+     */
4365
+    loadSVGFromURL: function(url, callback, reviver, options) {
4366
+
4367
+      url = url.replace(/^\n\s*/, '').trim();
4368
+      new fabric.util.request(url, {
4369
+        method: 'get',
4370
+        onComplete: onComplete
4371
+      });
4372
+
4373
+      function onComplete(r) {
4374
+
4375
+        var xml = r.responseXML;
4376
+        if (xml && !xml.documentElement && fabric.window.ActiveXObject && r.responseText) {
4377
+          xml = new ActiveXObject('Microsoft.XMLDOM');
4378
+          xml.async = 'false';
4379
+          //IE chokes on DOCTYPE
4380
+          xml.loadXML(r.responseText.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i, ''));
4381
+        }
4382
+        if (!xml || !xml.documentElement) {
4383
+          callback && callback(null);
4384
+          return false;
4385
+        }
4386
+
4387
+        fabric.parseSVGDocument(xml.documentElement, function (results, _options, elements, allElements) {
4388
+          callback && callback(results, _options, elements, allElements);
4389
+        }, reviver, options);
4390
+      }
4391
+    },
4392
+
4393
+    /**
4394
+     * Takes string corresponding to an SVG document, and parses it into a set of fabric objects
4395
+     * @memberOf fabric
4396
+     * @param {String} string
4397
+     * @param {Function} callback
4398
+     * @param {Function} [reviver] Method for further parsing of SVG elements, called after each fabric object created.
4399
+     * @param {Object} [options] Object containing options for parsing
4400
+     * @param {String} [options.crossOrigin] crossOrigin crossOrigin setting to use for external resources
4401
+     */
4402
+    loadSVGFromString: function(string, callback, reviver, options) {
4403
+      string = string.trim();
4404
+      var doc;
4405
+      if (typeof DOMParser !== 'undefined') {
4406
+        var parser = new DOMParser();
4407
+        if (parser && parser.parseFromString) {
4408
+          doc = parser.parseFromString(string, 'text/xml');
4409
+        }
4410
+      }
4411
+      else if (fabric.window.ActiveXObject) {
4412
+        doc = new ActiveXObject('Microsoft.XMLDOM');
4413
+        doc.async = 'false';
4414
+        // IE chokes on DOCTYPE
4415
+        doc.loadXML(string.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i, ''));
4416
+      }
4417
+
4418
+      fabric.parseSVGDocument(doc.documentElement, function (results, _options, elements, allElements) {
4419
+        callback(results, _options, elements, allElements);
4420
+      }, reviver, options);
4421
+    }
4422
+  });
4423
+
4424
+})(typeof exports !== 'undefined' ? exports : this);
4425
+
4426
+
4427
+fabric.ElementsParser = function(elements, callback, options, reviver, parsingOptions) {
4428
+  this.elements = elements;
4429
+  this.callback = callback;
4430
+  this.options = options;
4431
+  this.reviver = reviver;
4432
+  this.svgUid = (options && options.svgUid) || 0;
4433
+  this.parsingOptions = parsingOptions;
4434
+  this.regexUrl = /^url\(['"]?#([^'"]+)['"]?\)/g;
4435
+};
4436
+
4437
+(function(proto) {
4438
+  proto.parse = function() {
4439
+    this.instances = new Array(this.elements.length);
4440
+    this.numElements = this.elements.length;
4441
+    this.createObjects();
4442
+  };
4443
+
4444
+  proto.createObjects = function() {
4445
+    var _this = this;
4446
+    this.elements.forEach(function(element, i) {
4447
+      element.setAttribute('svgUid', _this.svgUid);
4448
+      _this.createObject(element, i);
4449
+    });
4450
+  };
4451
+
4452
+  proto.findTag = function(el) {
4453
+    return fabric[fabric.util.string.capitalize(el.tagName.replace('svg:', ''))];
4454
+  };
4455
+
4456
+  proto.createObject = function(el, index) {
4457
+    var klass = this.findTag(el);
4458
+    if (klass && klass.fromElement) {
4459
+      try {
4460
+        klass.fromElement(el, this.createCallback(index, el), this.options);
4461
+      }
4462
+      catch (err) {
4463
+        fabric.log(err);
4464
+      }
4465
+    }
4466
+    else {
4467
+      this.checkIfDone();
4468
+    }
4469
+  };
4470
+
4471
+  proto.createCallback = function(index, el) {
4472
+    var _this = this;
4473
+    return function(obj) {
4474
+      var _options;
4475
+      _this.resolveGradient(obj, 'fill');
4476
+      _this.resolveGradient(obj, 'stroke');
4477
+      if (obj instanceof fabric.Image && obj._originalElement) {
4478
+        _options = obj.parsePreserveAspectRatioAttribute(el);
4479
+      }
4480
+      obj._removeTransformMatrix(_options);
4481
+      _this.resolveClipPath(obj);
4482
+      _this.reviver && _this.reviver(el, obj);
4483
+      _this.instances[index] = obj;
4484
+      _this.checkIfDone();
4485
+    };
4486
+  };
4487
+
4488
+  proto.extractPropertyDefinition = function(obj, property, storage) {
4489
+    var value = obj[property];
4490
+    if (!(/^url\(/).test(value)) {
4491
+      return;
4492
+    }
4493
+    var id = this.regexUrl.exec(value)[1];
4494
+    this.regexUrl.lastIndex = 0;
4495
+    return fabric[storage][this.svgUid][id];
4496
+  };
4497
+
4498
+  proto.resolveGradient = function(obj, property) {
4499
+    var gradientDef = this.extractPropertyDefinition(obj, property, 'gradientDefs');
4500
+    if (gradientDef) {
4501
+      obj.set(property, fabric.Gradient.fromElement(gradientDef, obj));
4502
+    }
4503
+  };
4504
+
4505
+  proto.createClipPathCallback = function(obj, container) {
4506
+    return function(_newObj) {
4507
+      _newObj._removeTransformMatrix();
4508
+      _newObj.fillRule = _newObj.clipRule;
4509
+      container.push(_newObj);
4510
+    };
4511
+  };
4512
+
4513
+  proto.resolveClipPath = function(obj) {
4514
+    var clipPath = this.extractPropertyDefinition(obj, 'clipPath', 'clipPaths'),
4515
+        element, klass, objTransformInv, container, gTransform, options;
4516
+    if (clipPath) {
4517
+      container = [];
4518
+      objTransformInv = fabric.util.invertTransform(obj.calcTransformMatrix());
4519
+      for (var i = 0; i < clipPath.length; i++) {
4520
+        element = clipPath[i];
4521
+        klass = this.findTag(element);
4522
+        klass.fromElement(
4523
+          element,
4524
+          this.createClipPathCallback(obj, container),
4525
+          this.options
4526
+        );
4527
+      }
4528
+      if (container.length === 1) {
4529
+        clipPath = container[0];
4530
+      }
4531
+      else {
4532
+        clipPath = new fabric.Group(container);
4533
+      }
4534
+      gTransform = fabric.util.multiplyTransformMatrices(
4535
+        objTransformInv,
4536
+        clipPath.calcTransformMatrix()
4537
+      );
4538
+      var options = fabric.util.qrDecompose(gTransform);
4539
+      clipPath.flipX = false;
4540
+      clipPath.flipY = false;
4541
+      clipPath.set('scaleX', options.scaleX);
4542
+      clipPath.set('scaleY', options.scaleY);
4543
+      clipPath.angle = options.angle;
4544
+      clipPath.skewX = options.skewX;
4545
+      clipPath.skewY = 0;
4546
+      clipPath.setPositionByOrigin({ x: options.translateX, y: options.translateY }, 'center', 'center');
4547
+      obj.clipPath = clipPath;
4548
+    }
4549
+  };
4550
+
4551
+  proto.checkIfDone = function() {
4552
+    if (--this.numElements === 0) {
4553
+      this.instances = this.instances.filter(function(el) {
4554
+        // eslint-disable-next-line no-eq-null, eqeqeq
4555
+        return el != null;
4556
+      });
4557
+      this.callback(this.instances, this.elements);
4558
+    }
4559
+  };
4560
+})(fabric.ElementsParser.prototype);
4561
+
4562
+
4563
+(function(global) {
4564
+
4565
+  'use strict';
4566
+
4567
+  /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
4568
+
4569
+  var fabric = global.fabric || (global.fabric = { });
4570
+
4571
+  if (fabric.Point) {
4572
+    fabric.warn('fabric.Point is already defined');
4573
+    return;
4574
+  }
4575
+
4576
+  fabric.Point = Point;
4577
+
4578
+  /**
4579
+   * Point class
4580
+   * @class fabric.Point
4581
+   * @memberOf fabric
4582
+   * @constructor
4583
+   * @param {Number} x
4584
+   * @param {Number} y
4585
+   * @return {fabric.Point} thisArg
4586
+   */
4587
+  function Point(x, y) {
4588
+    this.x = x;
4589
+    this.y = y;
4590
+  }
4591
+
4592
+  Point.prototype = /** @lends fabric.Point.prototype */ {
4593
+
4594
+    type: 'point',
4595
+
4596
+    constructor: Point,
4597
+
4598
+    /**
4599
+     * Adds another point to this one and returns another one
4600
+     * @param {fabric.Point} that
4601
+     * @return {fabric.Point} new Point instance with added values
4602
+     */
4603
+    add: function (that) {
4604
+      return new Point(this.x + that.x, this.y + that.y);
4605
+    },
4606
+
4607
+    /**
4608
+     * Adds another point to this one
4609
+     * @param {fabric.Point} that
4610
+     * @return {fabric.Point} thisArg
4611
+     * @chainable
4612
+     */
4613
+    addEquals: function (that) {
4614
+      this.x += that.x;
4615
+      this.y += that.y;
4616
+      return this;
4617
+    },
4618
+
4619
+    /**
4620
+     * Adds value to this point and returns a new one
4621
+     * @param {Number} scalar
4622
+     * @return {fabric.Point} new Point with added value
4623
+     */
4624
+    scalarAdd: function (scalar) {
4625
+      return new Point(this.x + scalar, this.y + scalar);
4626
+    },
4627
+
4628
+    /**
4629
+     * Adds value to this point
4630
+     * @param {Number} scalar
4631
+     * @return {fabric.Point} thisArg
4632
+     * @chainable
4633
+     */
4634
+    scalarAddEquals: function (scalar) {
4635
+      this.x += scalar;
4636
+      this.y += scalar;
4637
+      return this;
4638
+    },
4639
+
4640
+    /**
4641
+     * Subtracts another point from this point and returns a new one
4642
+     * @param {fabric.Point} that
4643
+     * @return {fabric.Point} new Point object with subtracted values
4644
+     */
4645
+    subtract: function (that) {
4646
+      return new Point(this.x - that.x, this.y - that.y);
4647
+    },
4648
+
4649
+    /**
4650
+     * Subtracts another point from this point
4651
+     * @param {fabric.Point} that
4652
+     * @return {fabric.Point} thisArg
4653
+     * @chainable
4654
+     */
4655
+    subtractEquals: function (that) {
4656
+      this.x -= that.x;
4657
+      this.y -= that.y;
4658
+      return this;
4659
+    },
4660
+
4661
+    /**
4662
+     * Subtracts value from this point and returns a new one
4663
+     * @param {Number} scalar
4664
+     * @return {fabric.Point}
4665
+     */
4666
+    scalarSubtract: function (scalar) {
4667
+      return new Point(this.x - scalar, this.y - scalar);
4668
+    },
4669
+
4670
+    /**
4671
+     * Subtracts value from this point
4672
+     * @param {Number} scalar
4673
+     * @return {fabric.Point} thisArg
4674
+     * @chainable
4675
+     */
4676
+    scalarSubtractEquals: function (scalar) {
4677
+      this.x -= scalar;
4678
+      this.y -= scalar;
4679
+      return this;
4680
+    },
4681
+
4682
+    /**
4683
+     * Multiplies this point by a value and returns a new one
4684
+     * TODO: rename in scalarMultiply in 2.0
4685
+     * @param {Number} scalar
4686
+     * @return {fabric.Point}
4687
+     */
4688
+    multiply: function (scalar) {
4689
+      return new Point(this.x * scalar, this.y * scalar);
4690
+    },
4691
+
4692
+    /**
4693
+     * Multiplies this point by a value
4694
+     * TODO: rename in scalarMultiplyEquals in 2.0
4695
+     * @param {Number} scalar
4696
+     * @return {fabric.Point} thisArg
4697
+     * @chainable
4698
+     */
4699
+    multiplyEquals: function (scalar) {
4700
+      this.x *= scalar;
4701
+      this.y *= scalar;
4702
+      return this;
4703
+    },
4704
+
4705
+    /**
4706
+     * Divides this point by a value and returns a new one
4707
+     * TODO: rename in scalarDivide in 2.0
4708
+     * @param {Number} scalar
4709
+     * @return {fabric.Point}
4710
+     */
4711
+    divide: function (scalar) {
4712
+      return new Point(this.x / scalar, this.y / scalar);
4713
+    },
4714
+
4715
+    /**
4716
+     * Divides this point by a value
4717
+     * TODO: rename in scalarDivideEquals in 2.0
4718
+     * @param {Number} scalar
4719
+     * @return {fabric.Point} thisArg
4720
+     * @chainable
4721
+     */
4722
+    divideEquals: function (scalar) {
4723
+      this.x /= scalar;
4724
+      this.y /= scalar;
4725
+      return this;
4726
+    },
4727
+
4728
+    /**
4729
+     * Returns true if this point is equal to another one
4730
+     * @param {fabric.Point} that
4731
+     * @return {Boolean}
4732
+     */
4733
+    eq: function (that) {
4734
+      return (this.x === that.x && this.y === that.y);
4735
+    },
4736
+
4737
+    /**
4738
+     * Returns true if this point is less than another one
4739
+     * @param {fabric.Point} that
4740
+     * @return {Boolean}
4741
+     */
4742
+    lt: function (that) {
4743
+      return (this.x < that.x && this.y < that.y);
4744
+    },
4745
+
4746
+    /**
4747
+     * Returns true if this point is less than or equal to another one
4748
+     * @param {fabric.Point} that
4749
+     * @return {Boolean}
4750
+     */
4751
+    lte: function (that) {
4752
+      return (this.x <= that.x && this.y <= that.y);
4753
+    },
4754
+
4755
+    /**
4756
+
4757
+     * Returns true if this point is greater another one
4758
+     * @param {fabric.Point} that
4759
+     * @return {Boolean}
4760
+     */
4761
+    gt: function (that) {
4762
+      return (this.x > that.x && this.y > that.y);
4763
+    },
4764
+
4765
+    /**
4766
+     * Returns true if this point is greater than or equal to another one
4767
+     * @param {fabric.Point} that
4768
+     * @return {Boolean}
4769
+     */
4770
+    gte: function (that) {
4771
+      return (this.x >= that.x && this.y >= that.y);
4772
+    },
4773
+
4774
+    /**
4775
+     * Returns new point which is the result of linear interpolation with this one and another one
4776
+     * @param {fabric.Point} that
4777
+     * @param {Number} t , position of interpolation, between 0 and 1 default 0.5
4778
+     * @return {fabric.Point}
4779
+     */
4780
+    lerp: function (that, t) {
4781
+      if (typeof t === 'undefined') {
4782
+        t = 0.5;
4783
+      }
4784
+      t = Math.max(Math.min(1, t), 0);
4785
+      return new Point(this.x + (that.x - this.x) * t, this.y + (that.y - this.y) * t);
4786
+    },
4787
+
4788
+    /**
4789
+     * Returns distance from this point and another one
4790
+     * @param {fabric.Point} that
4791
+     * @return {Number}
4792
+     */
4793
+    distanceFrom: function (that) {
4794
+      var dx = this.x - that.x,
4795
+          dy = this.y - that.y;
4796
+      return Math.sqrt(dx * dx + dy * dy);
4797
+    },
4798
+
4799
+    /**
4800
+     * Returns the point between this point and another one
4801
+     * @param {fabric.Point} that
4802
+     * @return {fabric.Point}
4803
+     */
4804
+    midPointFrom: function (that) {
4805
+      return this.lerp(that);
4806
+    },
4807
+
4808
+    /**
4809
+     * Returns a new point which is the min of this and another one
4810
+     * @param {fabric.Point} that
4811
+     * @return {fabric.Point}
4812
+     */
4813
+    min: function (that) {
4814
+      return new Point(Math.min(this.x, that.x), Math.min(this.y, that.y));
4815
+    },
4816
+
4817
+    /**
4818
+     * Returns a new point which is the max of this and another one
4819
+     * @param {fabric.Point} that
4820
+     * @return {fabric.Point}
4821
+     */
4822
+    max: function (that) {
4823
+      return new Point(Math.max(this.x, that.x), Math.max(this.y, that.y));
4824
+    },
4825
+
4826
+    /**
4827
+     * Returns string representation of this point
4828
+     * @return {String}
4829
+     */
4830
+    toString: function () {
4831
+      return this.x + ',' + this.y;
4832
+    },
4833
+
4834
+    /**
4835
+     * Sets x/y of this point
4836
+     * @param {Number} x
4837
+     * @param {Number} y
4838
+     * @chainable
4839
+     */
4840
+    setXY: function (x, y) {
4841
+      this.x = x;
4842
+      this.y = y;
4843
+      return this;
4844
+    },
4845
+
4846
+    /**
4847
+     * Sets x of this point
4848
+     * @param {Number} x
4849
+     * @chainable
4850
+     */
4851
+    setX: function (x) {
4852
+      this.x = x;
4853
+      return this;
4854
+    },
4855
+
4856
+    /**
4857
+     * Sets y of this point
4858
+     * @param {Number} y
4859
+     * @chainable
4860
+     */
4861
+    setY: function (y) {
4862
+      this.y = y;
4863
+      return this;
4864
+    },
4865
+
4866
+    /**
4867
+     * Sets x/y of this point from another point
4868
+     * @param {fabric.Point} that
4869
+     * @chainable
4870
+     */
4871
+    setFromPoint: function (that) {
4872
+      this.x = that.x;
4873
+      this.y = that.y;
4874
+      return this;
4875
+    },
4876
+
4877
+    /**
4878
+     * Swaps x/y of this point and another point
4879
+     * @param {fabric.Point} that
4880
+     */
4881
+    swap: function (that) {
4882
+      var x = this.x,
4883
+          y = this.y;
4884
+      this.x = that.x;
4885
+      this.y = that.y;
4886
+      that.x = x;
4887
+      that.y = y;
4888
+    },
4889
+
4890
+    /**
4891
+     * return a cloned instance of the point
4892
+     * @return {fabric.Point}
4893
+     */
4894
+    clone: function () {
4895
+      return new Point(this.x, this.y);
4896
+    }
4897
+  };
4898
+
4899
+})(typeof exports !== 'undefined' ? exports : this);
4900
+
4901
+
4902
+(function(global) {
4903
+
4904
+  'use strict';
4905
+
4906
+  /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
4907
+  var fabric = global.fabric || (global.fabric = { });
4908
+
4909
+  if (fabric.Intersection) {
4910
+    fabric.warn('fabric.Intersection is already defined');
4911
+    return;
4912
+  }
4913
+
4914
+  /**
4915
+   * Intersection class
4916
+   * @class fabric.Intersection
4917
+   * @memberOf fabric
4918
+   * @constructor
4919
+   */
4920
+  function Intersection(status) {
4921
+    this.status = status;
4922
+    this.points = [];
4923
+  }
4924
+
4925
+  fabric.Intersection = Intersection;
4926
+
4927
+  fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
4928
+
4929
+    constructor: Intersection,
4930
+
4931
+    /**
4932
+     * Appends a point to intersection
4933
+     * @param {fabric.Point} point
4934
+     * @return {fabric.Intersection} thisArg
4935
+     * @chainable
4936
+     */
4937
+    appendPoint: function (point) {
4938
+      this.points.push(point);
4939
+      return this;
4940
+    },
4941
+
4942
+    /**
4943
+     * Appends points to intersection
4944
+     * @param {Array} points
4945
+     * @return {fabric.Intersection} thisArg
4946
+     * @chainable
4947
+     */
4948
+    appendPoints: function (points) {
4949
+      this.points = this.points.concat(points);
4950
+      return this;
4951
+    }
4952
+  };
4953
+
4954
+  /**
4955
+   * Checks if one line intersects another
4956
+   * TODO: rename in intersectSegmentSegment
4957
+   * @static
4958
+   * @param {fabric.Point} a1
4959
+   * @param {fabric.Point} a2
4960
+   * @param {fabric.Point} b1
4961
+   * @param {fabric.Point} b2
4962
+   * @return {fabric.Intersection}
4963
+   */
4964
+  fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
4965
+    var result,
4966
+        uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
4967
+        ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
4968
+        uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
4969
+    if (uB !== 0) {
4970
+      var ua = uaT / uB,
4971
+          ub = ubT / uB;
4972
+      if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
4973
+        result = new Intersection('Intersection');
4974
+        result.appendPoint(new fabric.Point(a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)));
4975
+      }
4976
+      else {
4977
+        result = new Intersection();
4978
+      }
4979
+    }
4980
+    else {
4981
+      if (uaT === 0 || ubT === 0) {
4982
+        result = new Intersection('Coincident');
4983
+      }
4984
+      else {
4985
+        result = new Intersection('Parallel');
4986
+      }
4987
+    }
4988
+    return result;
4989
+  };
4990
+
4991
+  /**
4992
+   * Checks if line intersects polygon
4993
+   * TODO: rename in intersectSegmentPolygon
4994
+   * fix detection of coincident
4995
+   * @static
4996
+   * @param {fabric.Point} a1
4997
+   * @param {fabric.Point} a2
4998
+   * @param {Array} points
4999
+   * @return {fabric.Intersection}
5000
+   */
5001
+  fabric.Intersection.intersectLinePolygon = function(a1, a2, points) {
5002
+    var result = new Intersection(),
5003
+        length = points.length,
5004
+        b1, b2, inter, i;
5005
+
5006
+    for (i = 0; i < length; i++) {
5007
+      b1 = points[i];
5008
+      b2 = points[(i + 1) % length];
5009
+      inter = Intersection.intersectLineLine(a1, a2, b1, b2);
5010
+
5011
+      result.appendPoints(inter.points);
5012
+    }
5013
+    if (result.points.length > 0) {
5014
+      result.status = 'Intersection';
5015
+    }
5016
+    return result;
5017
+  };
5018
+
5019
+  /**
5020
+   * Checks if polygon intersects another polygon
5021
+   * @static
5022
+   * @param {Array} points1
5023
+   * @param {Array} points2
5024
+   * @return {fabric.Intersection}
5025
+   */
5026
+  fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
5027
+    var result = new Intersection(),
5028
+        length = points1.length, i;
5029
+
5030
+    for (i = 0; i < length; i++) {
5031
+      var a1 = points1[i],
5032
+          a2 = points1[(i + 1) % length],
5033
+          inter = Intersection.intersectLinePolygon(a1, a2, points2);
5034
+
5035
+      result.appendPoints(inter.points);
5036
+    }
5037
+    if (result.points.length > 0) {
5038
+      result.status = 'Intersection';
5039
+    }
5040
+    return result;
5041
+  };
5042
+
5043
+  /**
5044
+   * Checks if polygon intersects rectangle
5045
+   * @static
5046
+   * @param {Array} points
5047
+   * @param {fabric.Point} r1
5048
+   * @param {fabric.Point} r2
5049
+   * @return {fabric.Intersection}
5050
+   */
5051
+  fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
5052
+    var min = r1.min(r2),
5053
+        max = r1.max(r2),
5054
+        topRight = new fabric.Point(max.x, min.y),
5055
+        bottomLeft = new fabric.Point(min.x, max.y),
5056
+        inter1 = Intersection.intersectLinePolygon(min, topRight, points),
5057
+        inter2 = Intersection.intersectLinePolygon(topRight, max, points),
5058
+        inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
5059
+        inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
5060
+        result = new Intersection();
5061
+
5062
+    result.appendPoints(inter1.points);
5063
+    result.appendPoints(inter2.points);
5064
+    result.appendPoints(inter3.points);
5065
+    result.appendPoints(inter4.points);
5066
+
5067
+    if (result.points.length > 0) {
5068
+      result.status = 'Intersection';
5069
+    }
5070
+    return result;
5071
+  };
5072
+
5073
+})(typeof exports !== 'undefined' ? exports : this);
5074
+
5075
+
5076
+(function(global) {
5077
+
5078
+  'use strict';
5079
+
5080
+  var fabric = global.fabric || (global.fabric = { });
5081
+
5082
+  if (fabric.Color) {
5083
+    fabric.warn('fabric.Color is already defined.');
5084
+    return;
5085
+  }
5086
+
5087
+  /**
5088
+   * Color class
5089
+   * The purpose of {@link fabric.Color} is to abstract and encapsulate common color operations;
5090
+   * {@link fabric.Color} is a constructor and creates instances of {@link fabric.Color} objects.
5091
+   *
5092
+   * @class fabric.Color
5093
+   * @param {String} color optional in hex or rgb(a) or hsl format or from known color list
5094
+   * @return {fabric.Color} thisArg
5095
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#colors}
5096
+   */
5097
+  function Color(color) {
5098
+    if (!color) {
5099
+      this.setSource([0, 0, 0, 1]);
5100
+    }
5101
+    else {
5102
+      this._tryParsingColor(color);
5103
+    }
5104
+  }
5105
+
5106
+  fabric.Color = Color;
5107
+
5108
+  fabric.Color.prototype = /** @lends fabric.Color.prototype */ {
5109
+
5110
+    /**
5111
+     * @private
5112
+     * @param {String|Array} color Color value to parse
5113
+     */
5114
+    _tryParsingColor: function(color) {
5115
+      var source;
5116
+
5117
+      if (color in Color.colorNameMap) {
5118
+        color = Color.colorNameMap[color];
5119
+      }
5120
+
5121
+      if (color === 'transparent') {
5122
+        source = [255, 255, 255, 0];
5123
+      }
5124
+
5125
+      if (!source) {
5126
+        source = Color.sourceFromHex(color);
5127
+      }
5128
+      if (!source) {
5129
+        source = Color.sourceFromRgb(color);
5130
+      }
5131
+      if (!source) {
5132
+        source = Color.sourceFromHsl(color);
5133
+      }
5134
+      if (!source) {
5135
+        //if color is not recognize let's make black as canvas does
5136
+        source = [0, 0, 0, 1];
5137
+      }
5138
+      if (source) {
5139
+        this.setSource(source);
5140
+      }
5141
+    },
5142
+
5143
+    /**
5144
+     * 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>
5145
+     * @private
5146
+     * @param {Number} r Red color value
5147
+     * @param {Number} g Green color value
5148
+     * @param {Number} b Blue color value
5149
+     * @return {Array} Hsl color
5150
+     */
5151
+    _rgbToHsl: function(r, g, b) {
5152
+      r /= 255; g /= 255; b /= 255;
5153
+
5154
+      var h, s, l,
5155
+          max = fabric.util.array.max([r, g, b]),
5156
+          min = fabric.util.array.min([r, g, b]);
5157
+
5158
+      l = (max + min) / 2;
5159
+
5160
+      if (max === min) {
5161
+        h = s = 0; // achromatic
5162
+      }
5163
+      else {
5164
+        var d = max - min;
5165
+        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
5166
+        switch (max) {
5167
+          case r:
5168
+            h = (g - b) / d + (g < b ? 6 : 0);
5169
+            break;
5170
+          case g:
5171
+            h = (b - r) / d + 2;
5172
+            break;
5173
+          case b:
5174
+            h = (r - g) / d + 4;
5175
+            break;
5176
+        }
5177
+        h /= 6;
5178
+      }
5179
+
5180
+      return [
5181
+        Math.round(h * 360),
5182
+        Math.round(s * 100),
5183
+        Math.round(l * 100)
5184
+      ];
5185
+    },
5186
+
5187
+    /**
5188
+     * Returns source of this color (where source is an array representation; ex: [200, 200, 100, 1])
5189
+     * @return {Array}
5190
+     */
5191
+    getSource: function() {
5192
+      return this._source;
5193
+    },
5194
+
5195
+    /**
5196
+     * Sets source of this color (where source is an array representation; ex: [200, 200, 100, 1])
5197
+     * @param {Array} source
5198
+     */
5199
+    setSource: function(source) {
5200
+      this._source = source;
5201
+    },
5202
+
5203
+    /**
5204
+     * Returns color representation in RGB format
5205
+     * @return {String} ex: rgb(0-255,0-255,0-255)
5206
+     */
5207
+    toRgb: function() {
5208
+      var source = this.getSource();
5209
+      return 'rgb(' + source[0] + ',' + source[1] + ',' + source[2] + ')';
5210
+    },
5211
+
5212
+    /**
5213
+     * Returns color representation in RGBA format
5214
+     * @return {String} ex: rgba(0-255,0-255,0-255,0-1)
5215
+     */
5216
+    toRgba: function() {
5217
+      var source = this.getSource();
5218
+      return 'rgba(' + source[0] + ',' + source[1] + ',' + source[2] + ',' + source[3] + ')';
5219
+    },
5220
+
5221
+    /**
5222
+     * Returns color representation in HSL format
5223
+     * @return {String} ex: hsl(0-360,0%-100%,0%-100%)
5224
+     */
5225
+    toHsl: function() {
5226
+      var source = this.getSource(),
5227
+          hsl = this._rgbToHsl(source[0], source[1], source[2]);
5228
+
5229
+      return 'hsl(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%)';
5230
+    },
5231
+
5232
+    /**
5233
+     * Returns color representation in HSLA format
5234
+     * @return {String} ex: hsla(0-360,0%-100%,0%-100%,0-1)
5235
+     */
5236
+    toHsla: function() {
5237
+      var source = this.getSource(),
5238
+          hsl = this._rgbToHsl(source[0], source[1], source[2]);
5239
+
5240
+      return 'hsla(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%,' + source[3] + ')';
5241
+    },
5242
+
5243
+    /**
5244
+     * Returns color representation in HEX format
5245
+     * @return {String} ex: FF5555
5246
+     */
5247
+    toHex: function() {
5248
+      var source = this.getSource(), r, g, b;
5249
+
5250
+      r = source[0].toString(16);
5251
+      r = (r.length === 1) ? ('0' + r) : r;
5252
+
5253
+      g = source[1].toString(16);
5254
+      g = (g.length === 1) ? ('0' + g) : g;
5255
+
5256
+      b = source[2].toString(16);
5257
+      b = (b.length === 1) ? ('0' + b) : b;
5258
+
5259
+      return r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
5260
+    },
5261
+
5262
+    /**
5263
+     * Returns color representation in HEXA format
5264
+     * @return {String} ex: FF5555CC
5265
+     */
5266
+    toHexa: function() {
5267
+      var source = this.getSource(), a;
5268
+
5269
+      a = Math.round(source[3] * 255);
5270
+      a = a.toString(16);
5271
+      a = (a.length === 1) ? ('0' + a) : a;
5272
+
5273
+      return this.toHex() + a.toUpperCase();
5274
+    },
5275
+
5276
+    /**
5277
+     * Gets value of alpha channel for this color
5278
+     * @return {Number} 0-1
5279
+     */
5280
+    getAlpha: function() {
5281
+      return this.getSource()[3];
5282
+    },
5283
+
5284
+    /**
5285
+     * Sets value of alpha channel for this color
5286
+     * @param {Number} alpha Alpha value 0-1
5287
+     * @return {fabric.Color} thisArg
5288
+     */
5289
+    setAlpha: function(alpha) {
5290
+      var source = this.getSource();
5291
+      source[3] = alpha;
5292
+      this.setSource(source);
5293
+      return this;
5294
+    },
5295
+
5296
+    /**
5297
+     * Transforms color to its grayscale representation
5298
+     * @return {fabric.Color} thisArg
5299
+     */
5300
+    toGrayscale: function() {
5301
+      var source = this.getSource(),
5302
+          average = parseInt((source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0), 10),
5303
+          currentAlpha = source[3];
5304
+      this.setSource([average, average, average, currentAlpha]);
5305
+      return this;
5306
+    },
5307
+
5308
+    /**
5309
+     * Transforms color to its black and white representation
5310
+     * @param {Number} threshold
5311
+     * @return {fabric.Color} thisArg
5312
+     */
5313
+    toBlackWhite: function(threshold) {
5314
+      var source = this.getSource(),
5315
+          average = (source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0),
5316
+          currentAlpha = source[3];
5317
+
5318
+      threshold = threshold || 127;
5319
+
5320
+      average = (Number(average) < Number(threshold)) ? 0 : 255;
5321
+      this.setSource([average, average, average, currentAlpha]);
5322
+      return this;
5323
+    },
5324
+
5325
+    /**
5326
+     * Overlays color with another color
5327
+     * @param {String|fabric.Color} otherColor
5328
+     * @return {fabric.Color} thisArg
5329
+     */
5330
+    overlayWith: function(otherColor) {
5331
+      if (!(otherColor instanceof Color)) {
5332
+        otherColor = new Color(otherColor);
5333
+      }
5334
+
5335
+      var result = [],
5336
+          alpha = this.getAlpha(),
5337
+          otherAlpha = 0.5,
5338
+          source = this.getSource(),
5339
+          otherSource = otherColor.getSource(), i;
5340
+
5341
+      for (i = 0; i < 3; i++) {
5342
+        result.push(Math.round((source[i] * (1 - otherAlpha)) + (otherSource[i] * otherAlpha)));
5343
+      }
5344
+
5345
+      result[3] = alpha;
5346
+      this.setSource(result);
5347
+      return this;
5348
+    }
5349
+  };
5350
+
5351
+  /**
5352
+   * 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))
5353
+   * @static
5354
+   * @field
5355
+   * @memberOf fabric.Color
5356
+   */
5357
+  // eslint-disable-next-line max-len
5358
+  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*)?\)$/i;
5359
+
5360
+  /**
5361
+   * 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 ))
5362
+   * @static
5363
+   * @field
5364
+   * @memberOf fabric.Color
5365
+   */
5366
+  fabric.Color.reHSLa = /^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/i;
5367
+
5368
+  /**
5369
+   * Regex matching color in HEX format (ex: #FF5544CC, #FF5555, 010155, aff)
5370
+   * @static
5371
+   * @field
5372
+   * @memberOf fabric.Color
5373
+   */
5374
+  fabric.Color.reHex = /^#?([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})$/i;
5375
+
5376
+  /**
5377
+   * Map of the 148 color names with HEX code
5378
+   * @static
5379
+   * @field
5380
+   * @memberOf fabric.Color
5381
+   * @see: https://www.w3.org/TR/css3-color/#svg-color
5382
+   */
5383
+  fabric.Color.colorNameMap = {
5384
+    aliceblue:            '#F0F8FF',
5385
+    antiquewhite:         '#FAEBD7',
5386
+    aqua:                 '#00FFFF',
5387
+    aquamarine:           '#7FFFD4',
5388
+    azure:                '#F0FFFF',
5389
+    beige:                '#F5F5DC',
5390
+    bisque:               '#FFE4C4',
5391
+    black:                '#000000',
5392
+    blanchedalmond:       '#FFEBCD',
5393
+    blue:                 '#0000FF',
5394
+    blueviolet:           '#8A2BE2',
5395
+    brown:                '#A52A2A',
5396
+    burlywood:            '#DEB887',
5397
+    cadetblue:            '#5F9EA0',
5398
+    chartreuse:           '#7FFF00',
5399
+    chocolate:            '#D2691E',
5400
+    coral:                '#FF7F50',
5401
+    cornflowerblue:       '#6495ED',
5402
+    cornsilk:             '#FFF8DC',
5403
+    crimson:              '#DC143C',
5404
+    cyan:                 '#00FFFF',
5405
+    darkblue:             '#00008B',
5406
+    darkcyan:             '#008B8B',
5407
+    darkgoldenrod:        '#B8860B',
5408
+    darkgray:             '#A9A9A9',
5409
+    darkgrey:             '#A9A9A9',
5410
+    darkgreen:            '#006400',
5411
+    darkkhaki:            '#BDB76B',
5412
+    darkmagenta:          '#8B008B',
5413
+    darkolivegreen:       '#556B2F',
5414
+    darkorange:           '#FF8C00',
5415
+    darkorchid:           '#9932CC',
5416
+    darkred:              '#8B0000',
5417
+    darksalmon:           '#E9967A',
5418
+    darkseagreen:         '#8FBC8F',
5419
+    darkslateblue:        '#483D8B',
5420
+    darkslategray:        '#2F4F4F',
5421
+    darkslategrey:        '#2F4F4F',
5422
+    darkturquoise:        '#00CED1',
5423
+    darkviolet:           '#9400D3',
5424
+    deeppink:             '#FF1493',
5425
+    deepskyblue:          '#00BFFF',
5426
+    dimgray:              '#696969',
5427
+    dimgrey:              '#696969',
5428
+    dodgerblue:           '#1E90FF',
5429
+    firebrick:            '#B22222',
5430
+    floralwhite:          '#FFFAF0',
5431
+    forestgreen:          '#228B22',
5432
+    fuchsia:              '#FF00FF',
5433
+    gainsboro:            '#DCDCDC',
5434
+    ghostwhite:           '#F8F8FF',
5435
+    gold:                 '#FFD700',
5436
+    goldenrod:            '#DAA520',
5437
+    gray:                 '#808080',
5438
+    grey:                 '#808080',
5439
+    green:                '#008000',
5440
+    greenyellow:          '#ADFF2F',
5441
+    honeydew:             '#F0FFF0',
5442
+    hotpink:              '#FF69B4',
5443
+    indianred:            '#CD5C5C',
5444
+    indigo:               '#4B0082',
5445
+    ivory:                '#FFFFF0',
5446
+    khaki:                '#F0E68C',
5447
+    lavender:             '#E6E6FA',
5448
+    lavenderblush:        '#FFF0F5',
5449
+    lawngreen:            '#7CFC00',
5450
+    lemonchiffon:         '#FFFACD',
5451
+    lightblue:            '#ADD8E6',
5452
+    lightcoral:           '#F08080',
5453
+    lightcyan:            '#E0FFFF',
5454
+    lightgoldenrodyellow: '#FAFAD2',
5455
+    lightgray:            '#D3D3D3',
5456
+    lightgrey:            '#D3D3D3',
5457
+    lightgreen:           '#90EE90',
5458
+    lightpink:            '#FFB6C1',
5459
+    lightsalmon:          '#FFA07A',
5460
+    lightseagreen:        '#20B2AA',
5461
+    lightskyblue:         '#87CEFA',
5462
+    lightslategray:       '#778899',
5463
+    lightslategrey:       '#778899',
5464
+    lightsteelblue:       '#B0C4DE',
5465
+    lightyellow:          '#FFFFE0',
5466
+    lime:                 '#00FF00',
5467
+    limegreen:            '#32CD32',
5468
+    linen:                '#FAF0E6',
5469
+    magenta:              '#FF00FF',
5470
+    maroon:               '#800000',
5471
+    mediumaquamarine:     '#66CDAA',
5472
+    mediumblue:           '#0000CD',
5473
+    mediumorchid:         '#BA55D3',
5474
+    mediumpurple:         '#9370DB',
5475
+    mediumseagreen:       '#3CB371',
5476
+    mediumslateblue:      '#7B68EE',
5477
+    mediumspringgreen:    '#00FA9A',
5478
+    mediumturquoise:      '#48D1CC',
5479
+    mediumvioletred:      '#C71585',
5480
+    midnightblue:         '#191970',
5481
+    mintcream:            '#F5FFFA',
5482
+    mistyrose:            '#FFE4E1',
5483
+    moccasin:             '#FFE4B5',
5484
+    navajowhite:          '#FFDEAD',
5485
+    navy:                 '#000080',
5486
+    oldlace:              '#FDF5E6',
5487
+    olive:                '#808000',
5488
+    olivedrab:            '#6B8E23',
5489
+    orange:               '#FFA500',
5490
+    orangered:            '#FF4500',
5491
+    orchid:               '#DA70D6',
5492
+    palegoldenrod:        '#EEE8AA',
5493
+    palegreen:            '#98FB98',
5494
+    paleturquoise:        '#AFEEEE',
5495
+    palevioletred:        '#DB7093',
5496
+    papayawhip:           '#FFEFD5',
5497
+    peachpuff:            '#FFDAB9',
5498
+    peru:                 '#CD853F',
5499
+    pink:                 '#FFC0CB',
5500
+    plum:                 '#DDA0DD',
5501
+    powderblue:           '#B0E0E6',
5502
+    purple:               '#800080',
5503
+    rebeccapurple:        '#663399',
5504
+    red:                  '#FF0000',
5505
+    rosybrown:            '#BC8F8F',
5506
+    royalblue:            '#4169E1',
5507
+    saddlebrown:          '#8B4513',
5508
+    salmon:               '#FA8072',
5509
+    sandybrown:           '#F4A460',
5510
+    seagreen:             '#2E8B57',
5511
+    seashell:             '#FFF5EE',
5512
+    sienna:               '#A0522D',
5513
+    silver:               '#C0C0C0',
5514
+    skyblue:              '#87CEEB',
5515
+    slateblue:            '#6A5ACD',
5516
+    slategray:            '#708090',
5517
+    slategrey:            '#708090',
5518
+    snow:                 '#FFFAFA',
5519
+    springgreen:          '#00FF7F',
5520
+    steelblue:            '#4682B4',
5521
+    tan:                  '#D2B48C',
5522
+    teal:                 '#008080',
5523
+    thistle:              '#D8BFD8',
5524
+    tomato:               '#FF6347',
5525
+    turquoise:            '#40E0D0',
5526
+    violet:               '#EE82EE',
5527
+    wheat:                '#F5DEB3',
5528
+    white:                '#FFFFFF',
5529
+    whitesmoke:           '#F5F5F5',
5530
+    yellow:               '#FFFF00',
5531
+    yellowgreen:          '#9ACD32'
5532
+  };
5533
+
5534
+  /**
5535
+   * @private
5536
+   * @param {Number} p
5537
+   * @param {Number} q
5538
+   * @param {Number} t
5539
+   * @return {Number}
5540
+   */
5541
+  function hue2rgb(p, q, t) {
5542
+    if (t < 0) {
5543
+      t += 1;
5544
+    }
5545
+    if (t > 1) {
5546
+      t -= 1;
5547
+    }
5548
+    if (t < 1 / 6) {
5549
+      return p + (q - p) * 6 * t;
5550
+    }
5551
+    if (t < 1 / 2) {
5552
+      return q;
5553
+    }
5554
+    if (t < 2 / 3) {
5555
+      return p + (q - p) * (2 / 3 - t) * 6;
5556
+    }
5557
+    return p;
5558
+  }
5559
+
5560
+  /**
5561
+   * Returns new color object, when given a color in RGB format
5562
+   * @memberOf fabric.Color
5563
+   * @param {String} color Color value ex: rgb(0-255,0-255,0-255)
5564
+   * @return {fabric.Color}
5565
+   */
5566
+  fabric.Color.fromRgb = function(color) {
5567
+    return Color.fromSource(Color.sourceFromRgb(color));
5568
+  };
5569
+
5570
+  /**
5571
+   * Returns array representation (ex: [100, 100, 200, 1]) of a color that's in RGB or RGBA format
5572
+   * @memberOf fabric.Color
5573
+   * @param {String} color Color value ex: rgb(0-255,0-255,0-255), rgb(0%-100%,0%-100%,0%-100%)
5574
+   * @return {Array} source
5575
+   */
5576
+  fabric.Color.sourceFromRgb = function(color) {
5577
+    var match = color.match(Color.reRGBa);
5578
+    if (match) {
5579
+      var r = parseInt(match[1], 10) / (/%$/.test(match[1]) ? 100 : 1) * (/%$/.test(match[1]) ? 255 : 1),
5580
+          g = parseInt(match[2], 10) / (/%$/.test(match[2]) ? 100 : 1) * (/%$/.test(match[2]) ? 255 : 1),
5581
+          b = parseInt(match[3], 10) / (/%$/.test(match[3]) ? 100 : 1) * (/%$/.test(match[3]) ? 255 : 1);
5582
+
5583
+      return [
5584
+        parseInt(r, 10),
5585
+        parseInt(g, 10),
5586
+        parseInt(b, 10),
5587
+        match[4] ? parseFloat(match[4]) : 1
5588
+      ];
5589
+    }
5590
+  };
5591
+
5592
+  /**
5593
+   * Returns new color object, when given a color in RGBA format
5594
+   * @static
5595
+   * @function
5596
+   * @memberOf fabric.Color
5597
+   * @param {String} color
5598
+   * @return {fabric.Color}
5599
+   */
5600
+  fabric.Color.fromRgba = Color.fromRgb;
5601
+
5602
+  /**
5603
+   * Returns new color object, when given a color in HSL format
5604
+   * @param {String} color Color value ex: hsl(0-260,0%-100%,0%-100%)
5605
+   * @memberOf fabric.Color
5606
+   * @return {fabric.Color}
5607
+   */
5608
+  fabric.Color.fromHsl = function(color) {
5609
+    return Color.fromSource(Color.sourceFromHsl(color));
5610
+  };
5611
+
5612
+  /**
5613
+   * Returns array representation (ex: [100, 100, 200, 1]) of a color that's in HSL or HSLA format.
5614
+   * 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>
5615
+   * @memberOf fabric.Color
5616
+   * @param {String} color Color value ex: hsl(0-360,0%-100%,0%-100%) or hsla(0-360,0%-100%,0%-100%, 0-1)
5617
+   * @return {Array} source
5618
+   * @see http://http://www.w3.org/TR/css3-color/#hsl-color
5619
+   */
5620
+  fabric.Color.sourceFromHsl = function(color) {
5621
+    var match = color.match(Color.reHSLa);
5622
+    if (!match) {
5623
+      return;
5624
+    }
5625
+
5626
+    var h = (((parseFloat(match[1]) % 360) + 360) % 360) / 360,
5627
+        s = parseFloat(match[2]) / (/%$/.test(match[2]) ? 100 : 1),
5628
+        l = parseFloat(match[3]) / (/%$/.test(match[3]) ? 100 : 1),
5629
+        r, g, b;
5630
+
5631
+    if (s === 0) {
5632
+      r = g = b = l;
5633
+    }
5634
+    else {
5635
+      var q = l <= 0.5 ? l * (s + 1) : l + s - l * s,
5636
+          p = l * 2 - q;
5637
+
5638
+      r = hue2rgb(p, q, h + 1 / 3);
5639
+      g = hue2rgb(p, q, h);
5640
+      b = hue2rgb(p, q, h - 1 / 3);
5641
+    }
5642
+
5643
+    return [
5644
+      Math.round(r * 255),
5645
+      Math.round(g * 255),
5646
+      Math.round(b * 255),
5647
+      match[4] ? parseFloat(match[4]) : 1
5648
+    ];
5649
+  };
5650
+
5651
+  /**
5652
+   * Returns new color object, when given a color in HSLA format
5653
+   * @static
5654
+   * @function
5655
+   * @memberOf fabric.Color
5656
+   * @param {String} color
5657
+   * @return {fabric.Color}
5658
+   */
5659
+  fabric.Color.fromHsla = Color.fromHsl;
5660
+
5661
+  /**
5662
+   * Returns new color object, when given a color in HEX format
5663
+   * @static
5664
+   * @memberOf fabric.Color
5665
+   * @param {String} color Color value ex: FF5555
5666
+   * @return {fabric.Color}
5667
+   */
5668
+  fabric.Color.fromHex = function(color) {
5669
+    return Color.fromSource(Color.sourceFromHex(color));
5670
+  };
5671
+
5672
+  /**
5673
+   * Returns array representation (ex: [100, 100, 200, 1]) of a color that's in HEX format
5674
+   * @static
5675
+   * @memberOf fabric.Color
5676
+   * @param {String} color ex: FF5555 or FF5544CC (RGBa)
5677
+   * @return {Array} source
5678
+   */
5679
+  fabric.Color.sourceFromHex = function(color) {
5680
+    if (color.match(Color.reHex)) {
5681
+      var value = color.slice(color.indexOf('#') + 1),
5682
+          isShortNotation = (value.length === 3 || value.length === 4),
5683
+          isRGBa = (value.length === 8 || value.length === 4),
5684
+          r = isShortNotation ? (value.charAt(0) + value.charAt(0)) : value.substring(0, 2),
5685
+          g = isShortNotation ? (value.charAt(1) + value.charAt(1)) : value.substring(2, 4),
5686
+          b = isShortNotation ? (value.charAt(2) + value.charAt(2)) : value.substring(4, 6),
5687
+          a = isRGBa ? (isShortNotation ? (value.charAt(3) + value.charAt(3)) : value.substring(6, 8)) : 'FF';
5688
+
5689
+      return [
5690
+        parseInt(r, 16),
5691
+        parseInt(g, 16),
5692
+        parseInt(b, 16),
5693
+        parseFloat((parseInt(a, 16) / 255).toFixed(2))
5694
+      ];
5695
+    }
5696
+  };
5697
+
5698
+  /**
5699
+   * Returns new color object, when given color in array representation (ex: [200, 100, 100, 0.5])
5700
+   * @static
5701
+   * @memberOf fabric.Color
5702
+   * @param {Array} source
5703
+   * @return {fabric.Color}
5704
+   */
5705
+  fabric.Color.fromSource = function(source) {
5706
+    var oColor = new Color();
5707
+    oColor.setSource(source);
5708
+    return oColor;
5709
+  };
5710
+
5711
+})(typeof exports !== 'undefined' ? exports : this);
5712
+
5713
+
5714
+(function() {
5715
+
5716
+  /* _FROM_SVG_START_ */
5717
+  function getColorStop(el) {
5718
+    var style = el.getAttribute('style'),
5719
+        offset = el.getAttribute('offset') || 0,
5720
+        color, colorAlpha, opacity, i;
5721
+
5722
+    // convert percents to absolute values
5723
+    offset = parseFloat(offset) / (/%$/.test(offset) ? 100 : 1);
5724
+    offset = offset < 0 ? 0 : offset > 1 ? 1 : offset;
5725
+    if (style) {
5726
+      var keyValuePairs = style.split(/\s*;\s*/);
5727
+
5728
+      if (keyValuePairs[keyValuePairs.length - 1] === '') {
5729
+        keyValuePairs.pop();
5730
+      }
5731
+
5732
+      for (i = keyValuePairs.length; i--; ) {
5733
+
5734
+        var split = keyValuePairs[i].split(/\s*:\s*/),
5735
+            key = split[0].trim(),
5736
+            value = split[1].trim();
5737
+
5738
+        if (key === 'stop-color') {
5739
+          color = value;
5740
+        }
5741
+        else if (key === 'stop-opacity') {
5742
+          opacity = value;
5743
+        }
5744
+      }
5745
+    }
5746
+
5747
+    if (!color) {
5748
+      color = el.getAttribute('stop-color') || 'rgb(0,0,0)';
5749
+    }
5750
+    if (!opacity) {
5751
+      opacity = el.getAttribute('stop-opacity');
5752
+    }
5753
+
5754
+    color = new fabric.Color(color);
5755
+    colorAlpha = color.getAlpha();
5756
+    opacity = isNaN(parseFloat(opacity)) ? 1 : parseFloat(opacity);
5757
+    opacity *= colorAlpha;
5758
+
5759
+    return {
5760
+      offset: offset,
5761
+      color: color.toRgb(),
5762
+      opacity: opacity
5763
+    };
5764
+  }
5765
+
5766
+  function getLinearCoords(el) {
5767
+    return {
5768
+      x1: el.getAttribute('x1') || 0,
5769
+      y1: el.getAttribute('y1') || 0,
5770
+      x2: el.getAttribute('x2') || '100%',
5771
+      y2: el.getAttribute('y2') || 0
5772
+    };
5773
+  }
5774
+
5775
+  function getRadialCoords(el) {
5776
+    return {
5777
+      x1: el.getAttribute('fx') || el.getAttribute('cx') || '50%',
5778
+      y1: el.getAttribute('fy') || el.getAttribute('cy') || '50%',
5779
+      r1: 0,
5780
+      x2: el.getAttribute('cx') || '50%',
5781
+      y2: el.getAttribute('cy') || '50%',
5782
+      r2: el.getAttribute('r') || '50%'
5783
+    };
5784
+  }
5785
+  /* _FROM_SVG_END_ */
5786
+
5787
+  var clone = fabric.util.object.clone;
5788
+
5789
+  /**
5790
+   * Gradient class
5791
+   * @class fabric.Gradient
5792
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#gradients}
5793
+   * @see {@link fabric.Gradient#initialize} for constructor definition
5794
+   */
5795
+  fabric.Gradient = fabric.util.createClass(/** @lends fabric.Gradient.prototype */ {
5796
+
5797
+    /**
5798
+     * Horizontal offset for aligning gradients coming from SVG when outside pathgroups
5799
+     * @type Number
5800
+     * @default 0
5801
+     */
5802
+    offsetX: 0,
5803
+
5804
+    /**
5805
+     * Vertical offset for aligning gradients coming from SVG when outside pathgroups
5806
+     * @type Number
5807
+     * @default 0
5808
+     */
5809
+    offsetY: 0,
5810
+
5811
+    /**
5812
+     * Constructor
5813
+     * @param {Object} [options] Options object with type, coords, gradientUnits and colorStops
5814
+     * @return {fabric.Gradient} thisArg
5815
+     */
5816
+    initialize: function(options) {
5817
+      options || (options = { });
5818
+
5819
+      var coords = { };
5820
+
5821
+      this.id = fabric.Object.__uid++;
5822
+      this.type = options.type || 'linear';
5823
+
5824
+      coords = {
5825
+        x1: options.coords.x1 || 0,
5826
+        y1: options.coords.y1 || 0,
5827
+        x2: options.coords.x2 || 0,
5828
+        y2: options.coords.y2 || 0
5829
+      };
5830
+
5831
+      if (this.type === 'radial') {
5832
+        coords.r1 = options.coords.r1 || 0;
5833
+        coords.r2 = options.coords.r2 || 0;
5834
+      }
5835
+      this.coords = coords;
5836
+      this.colorStops = options.colorStops.slice();
5837
+      if (options.gradientTransform) {
5838
+        this.gradientTransform = options.gradientTransform;
5839
+      }
5840
+      this.offsetX = options.offsetX || this.offsetX;
5841
+      this.offsetY = options.offsetY || this.offsetY;
5842
+    },
5843
+
5844
+    /**
5845
+     * Adds another colorStop
5846
+     * @param {Object} colorStop Object with offset and color
5847
+     * @return {fabric.Gradient} thisArg
5848
+     */
5849
+    addColorStop: function(colorStops) {
5850
+      for (var position in colorStops) {
5851
+        var color = new fabric.Color(colorStops[position]);
5852
+        this.colorStops.push({
5853
+          offset: parseFloat(position),
5854
+          color: color.toRgb(),
5855
+          opacity: color.getAlpha()
5856
+        });
5857
+      }
5858
+      return this;
5859
+    },
5860
+
5861
+    /**
5862
+     * Returns object representation of a gradient
5863
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
5864
+     * @return {Object}
5865
+     */
5866
+    toObject: function(propertiesToInclude) {
5867
+      var object = {
5868
+        type: this.type,
5869
+        coords: this.coords,
5870
+        colorStops: this.colorStops,
5871
+        offsetX: this.offsetX,
5872
+        offsetY: this.offsetY,
5873
+        gradientTransform: this.gradientTransform ? this.gradientTransform.concat() : this.gradientTransform
5874
+      };
5875
+      fabric.util.populateWithProperties(this, object, propertiesToInclude);
5876
+
5877
+      return object;
5878
+    },
5879
+
5880
+    /* _TO_SVG_START_ */
5881
+    /**
5882
+     * Returns SVG representation of an gradient
5883
+     * @param {Object} object Object to create a gradient for
5884
+     * @return {String} SVG representation of an gradient (linear/radial)
5885
+     */
5886
+    toSVG: function(object) {
5887
+      var coords = clone(this.coords, true), i, len,
5888
+          markup, commonAttributes, colorStops = clone(this.colorStops, true),
5889
+          needsSwap = coords.r1 > coords.r2,
5890
+          offsetX = object.width / 2, offsetY = object.height / 2;
5891
+      // colorStops must be sorted ascending
5892
+      colorStops.sort(function(a, b) {
5893
+        return a.offset - b.offset;
5894
+      });
5895
+      if (object.type === 'path') {
5896
+        offsetX -= object.pathOffset.x;
5897
+        offsetY -= object.pathOffset.y;
5898
+      }
5899
+      for (var prop in coords) {
5900
+        if (prop === 'x1' || prop === 'x2') {
5901
+          coords[prop] += this.offsetX - offsetX;
5902
+        }
5903
+        else if (prop === 'y1' || prop === 'y2') {
5904
+          coords[prop] += this.offsetY - offsetY;
5905
+        }
5906
+      }
5907
+
5908
+      commonAttributes = 'id="SVGID_' + this.id +
5909
+                     '" gradientUnits="userSpaceOnUse"';
5910
+      if (this.gradientTransform) {
5911
+        commonAttributes += ' gradientTransform="matrix(' + this.gradientTransform.join(' ') + ')" ';
5912
+      }
5913
+      if (this.type === 'linear') {
5914
+        markup = [
5915
+          '<linearGradient ',
5916
+          commonAttributes,
5917
+          ' x1="', coords.x1,
5918
+          '" y1="', coords.y1,
5919
+          '" x2="', coords.x2,
5920
+          '" y2="', coords.y2,
5921
+          '">\n'
5922
+        ];
5923
+      }
5924
+      else if (this.type === 'radial') {
5925
+        // svg radial gradient has just 1 radius. the biggest.
5926
+        markup = [
5927
+          '<radialGradient ',
5928
+          commonAttributes,
5929
+          ' cx="', needsSwap ? coords.x1 : coords.x2,
5930
+          '" cy="', needsSwap ? coords.y1 : coords.y2,
5931
+          '" r="', needsSwap ? coords.r1 : coords.r2,
5932
+          '" fx="', needsSwap ? coords.x2 : coords.x1,
5933
+          '" fy="', needsSwap ? coords.y2 : coords.y1,
5934
+          '">\n'
5935
+        ];
5936
+      }
5937
+
5938
+      if (this.type === 'radial') {
5939
+        if (needsSwap) {
5940
+          // svg goes from internal to external radius. if radius are inverted, swap color stops.
5941
+          colorStops = colorStops.concat();
5942
+          colorStops.reverse();
5943
+          for (i = 0, len = colorStops.length; i < len; i++) {
5944
+            colorStops[i].offset = 1 - colorStops[i].offset;
5945
+          }
5946
+        }
5947
+        var minRadius = Math.min(coords.r1, coords.r2);
5948
+        if (minRadius > 0) {
5949
+          // i have to shift all colorStops and add new one in 0.
5950
+          var maxRadius = Math.max(coords.r1, coords.r2),
5951
+              percentageShift = minRadius / maxRadius;
5952
+          for (i = 0, len = colorStops.length; i < len; i++) {
5953
+            colorStops[i].offset += percentageShift * (1 - colorStops[i].offset);
5954
+          }
5955
+        }
5956
+      }
5957
+
5958
+      for (i = 0, len = colorStops.length; i < len; i++) {
5959
+        var colorStop = colorStops[i];
5960
+        markup.push(
5961
+          '<stop ',
5962
+          'offset="', (colorStop.offset * 100) + '%',
5963
+          '" style="stop-color:', colorStop.color,
5964
+          (typeof colorStop.opacity !== 'undefined' ? ';stop-opacity: ' + colorStop.opacity : ';'),
5965
+          '"/>\n'
5966
+        );
5967
+      }
5968
+
5969
+      markup.push((this.type === 'linear' ? '</linearGradient>\n' : '</radialGradient>\n'));
5970
+
5971
+      return markup.join('');
5972
+    },
5973
+    /* _TO_SVG_END_ */
5974
+
5975
+    /**
5976
+     * Returns an instance of CanvasGradient
5977
+     * @param {CanvasRenderingContext2D} ctx Context to render on
5978
+     * @return {CanvasGradient}
5979
+     */
5980
+    toLive: function(ctx) {
5981
+      var gradient, coords = fabric.util.object.clone(this.coords), i, len;
5982
+
5983
+      if (!this.type) {
5984
+        return;
5985
+      }
5986
+
5987
+      if (this.type === 'linear') {
5988
+        gradient = ctx.createLinearGradient(
5989
+          coords.x1, coords.y1, coords.x2, coords.y2);
5990
+      }
5991
+      else if (this.type === 'radial') {
5992
+        gradient = ctx.createRadialGradient(
5993
+          coords.x1, coords.y1, coords.r1, coords.x2, coords.y2, coords.r2);
5994
+      }
5995
+
5996
+      for (i = 0, len = this.colorStops.length; i < len; i++) {
5997
+        var color = this.colorStops[i].color,
5998
+            opacity = this.colorStops[i].opacity,
5999
+            offset = this.colorStops[i].offset;
6000
+
6001
+        if (typeof opacity !== 'undefined') {
6002
+          color = new fabric.Color(color).setAlpha(opacity).toRgba();
6003
+        }
6004
+        gradient.addColorStop(offset, color);
6005
+      }
6006
+
6007
+      return gradient;
6008
+    }
6009
+  });
6010
+
6011
+  fabric.util.object.extend(fabric.Gradient, {
6012
+
6013
+    /* _FROM_SVG_START_ */
6014
+    /**
6015
+     * Returns {@link fabric.Gradient} instance from an SVG element
6016
+     * @static
6017
+     * @memberOf fabric.Gradient
6018
+     * @param {SVGGradientElement} el SVG gradient element
6019
+     * @param {fabric.Object} instance
6020
+     * @return {fabric.Gradient} Gradient instance
6021
+     * @see http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement
6022
+     * @see http://www.w3.org/TR/SVG/pservers.html#RadialGradientElement
6023
+     */
6024
+    fromElement: function(el, instance) {
6025
+      /**
6026
+       *  @example:
6027
+       *
6028
+       *  <linearGradient id="linearGrad1">
6029
+       *    <stop offset="0%" stop-color="white"/>
6030
+       *    <stop offset="100%" stop-color="black"/>
6031
+       *  </linearGradient>
6032
+       *
6033
+       *  OR
6034
+       *
6035
+       *  <linearGradient id="linearGrad2">
6036
+       *    <stop offset="0" style="stop-color:rgb(255,255,255)"/>
6037
+       *    <stop offset="1" style="stop-color:rgb(0,0,0)"/>
6038
+       *  </linearGradient>
6039
+       *
6040
+       *  OR
6041
+       *
6042
+       *  <radialGradient id="radialGrad1">
6043
+       *    <stop offset="0%" stop-color="white" stop-opacity="1" />
6044
+       *    <stop offset="50%" stop-color="black" stop-opacity="0.5" />
6045
+       *    <stop offset="100%" stop-color="white" stop-opacity="1" />
6046
+       *  </radialGradient>
6047
+       *
6048
+       *  OR
6049
+       *
6050
+       *  <radialGradient id="radialGrad2">
6051
+       *    <stop offset="0" stop-color="rgb(255,255,255)" />
6052
+       *    <stop offset="0.5" stop-color="rgb(0,0,0)" />
6053
+       *    <stop offset="1" stop-color="rgb(255,255,255)" />
6054
+       *  </radialGradient>
6055
+       *
6056
+       */
6057
+
6058
+      var colorStopEls = el.getElementsByTagName('stop'),
6059
+          type,
6060
+          gradientUnits = el.getAttribute('gradientUnits') || 'objectBoundingBox',
6061
+          gradientTransform = el.getAttribute('gradientTransform'),
6062
+          colorStops = [],
6063
+          coords, ellipseMatrix, i;
6064
+
6065
+      if (el.nodeName === 'linearGradient' || el.nodeName === 'LINEARGRADIENT') {
6066
+        type = 'linear';
6067
+      }
6068
+      else {
6069
+        type = 'radial';
6070
+      }
6071
+
6072
+      if (type === 'linear') {
6073
+        coords = getLinearCoords(el);
6074
+      }
6075
+      else if (type === 'radial') {
6076
+        coords = getRadialCoords(el);
6077
+      }
6078
+
6079
+      for (i = colorStopEls.length; i--; ) {
6080
+        colorStops.push(getColorStop(colorStopEls[i]));
6081
+      }
6082
+
6083
+      ellipseMatrix = _convertPercentUnitsToValues(instance, coords, gradientUnits);
6084
+
6085
+      var gradient = new fabric.Gradient({
6086
+        type: type,
6087
+        coords: coords,
6088
+        colorStops: colorStops,
6089
+        offsetX: -instance.left,
6090
+        offsetY: -instance.top
6091
+      });
6092
+
6093
+      if (gradientTransform || ellipseMatrix !== '') {
6094
+        gradient.gradientTransform = fabric.parseTransformAttribute((gradientTransform || '') + ellipseMatrix);
6095
+      }
6096
+
6097
+      return gradient;
6098
+    },
6099
+    /* _FROM_SVG_END_ */
6100
+
6101
+    /**
6102
+     * Returns {@link fabric.Gradient} instance from its object representation
6103
+     * @static
6104
+     * @memberOf fabric.Gradient
6105
+     * @param {Object} obj
6106
+     * @param {Object} [options] Options object
6107
+     */
6108
+    forObject: function(obj, options) {
6109
+      options || (options = { });
6110
+      _convertPercentUnitsToValues(obj, options.coords, 'userSpaceOnUse');
6111
+      return new fabric.Gradient(options);
6112
+    }
6113
+  });
6114
+
6115
+  /**
6116
+   * @private
6117
+   */
6118
+  function _convertPercentUnitsToValues(object, options, gradientUnits) {
6119
+    var propValue, addFactor = 0, multFactor = 1, ellipseMatrix = '';
6120
+    for (var prop in options) {
6121
+      if (options[prop] === 'Infinity') {
6122
+        options[prop] = 1;
6123
+      }
6124
+      else if (options[prop] === '-Infinity') {
6125
+        options[prop] = 0;
6126
+      }
6127
+      propValue = parseFloat(options[prop], 10);
6128
+      if (typeof options[prop] === 'string' && /^(\d+\.\d+)%|(\d+)%$/.test(options[prop])) {
6129
+        multFactor = 0.01;
6130
+      }
6131
+      else {
6132
+        multFactor = 1;
6133
+      }
6134
+      if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
6135
+        multFactor *= gradientUnits === 'objectBoundingBox' ? object.width : 1;
6136
+        addFactor = gradientUnits === 'objectBoundingBox' ? object.left || 0 : 0;
6137
+      }
6138
+      else if (prop === 'y1' || prop === 'y2') {
6139
+        multFactor *= gradientUnits === 'objectBoundingBox' ? object.height : 1;
6140
+        addFactor = gradientUnits === 'objectBoundingBox' ? object.top || 0 : 0;
6141
+      }
6142
+      options[prop] = propValue * multFactor + addFactor;
6143
+    }
6144
+    if (object.type === 'ellipse' &&
6145
+        options.r2 !== null &&
6146
+        gradientUnits === 'objectBoundingBox' &&
6147
+        object.rx !== object.ry) {
6148
+
6149
+      var scaleFactor = object.ry / object.rx;
6150
+      ellipseMatrix = ' scale(1, ' + scaleFactor + ')';
6151
+      if (options.y1) {
6152
+        options.y1 /= scaleFactor;
6153
+      }
6154
+      if (options.y2) {
6155
+        options.y2 /= scaleFactor;
6156
+      }
6157
+    }
6158
+    return ellipseMatrix;
6159
+  }
6160
+})();
6161
+
6162
+
6163
+(function() {
6164
+
6165
+  'use strict';
6166
+
6167
+  var toFixed = fabric.util.toFixed;
6168
+
6169
+  /**
6170
+   * Pattern class
6171
+   * @class fabric.Pattern
6172
+   * @see {@link http://fabricjs.com/patterns|Pattern demo}
6173
+   * @see {@link http://fabricjs.com/dynamic-patterns|DynamicPattern demo}
6174
+   * @see {@link fabric.Pattern#initialize} for constructor definition
6175
+   */
6176
+
6177
+
6178
+  fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ {
6179
+
6180
+    /**
6181
+     * Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat)
6182
+     * @type String
6183
+     * @default
6184
+     */
6185
+    repeat: 'repeat',
6186
+
6187
+    /**
6188
+     * Pattern horizontal offset from object's left/top corner
6189
+     * @type Number
6190
+     * @default
6191
+     */
6192
+    offsetX: 0,
6193
+
6194
+    /**
6195
+     * Pattern vertical offset from object's left/top corner
6196
+     * @type Number
6197
+     * @default
6198
+     */
6199
+    offsetY: 0,
6200
+
6201
+    /**
6202
+     * crossOrigin value (one of "", "anonymous", "use-credentials")
6203
+     * @see https://developer.mozilla.org/en-US/docs/HTML/CORS_settings_attributes
6204
+     * @type String
6205
+     * @default
6206
+     */
6207
+    crossOrigin: '',
6208
+
6209
+    /**
6210
+     * transform matrix to change the pattern, imported from svgs.
6211
+     * @type Array
6212
+     * @default
6213
+     */
6214
+    patternTransform: null,
6215
+
6216
+    /**
6217
+     * Constructor
6218
+     * @param {Object} [options] Options object
6219
+     * @param {Function} [callback] function to invoke after callback init.
6220
+     * @return {fabric.Pattern} thisArg
6221
+     */
6222
+    initialize: function(options, callback) {
6223
+      options || (options = { });
6224
+
6225
+      this.id = fabric.Object.__uid++;
6226
+      this.setOptions(options);
6227
+      if (!options.source || (options.source && typeof options.source !== 'string')) {
6228
+        callback && callback(this);
6229
+        return;
6230
+      }
6231
+      // function string
6232
+      if (typeof fabric.util.getFunctionBody(options.source) !== 'undefined') {
6233
+        this.source = new Function(fabric.util.getFunctionBody(options.source));
6234
+        callback && callback(this);
6235
+      }
6236
+      else {
6237
+        // img src string
6238
+        var _this = this;
6239
+        this.source = fabric.util.createImage();
6240
+        fabric.util.loadImage(options.source, function(img) {
6241
+          _this.source = img;
6242
+          callback && callback(_this);
6243
+        }, null, this.crossOrigin);
6244
+      }
6245
+    },
6246
+
6247
+    /**
6248
+     * Returns object representation of a pattern
6249
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
6250
+     * @return {Object} Object representation of a pattern instance
6251
+     */
6252
+    toObject: function(propertiesToInclude) {
6253
+      var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
6254
+          source, object;
6255
+
6256
+      // callback
6257
+      if (typeof this.source === 'function') {
6258
+        source = String(this.source);
6259
+      }
6260
+      // <img> element
6261
+      else if (typeof this.source.src === 'string') {
6262
+        source = this.source.src;
6263
+      }
6264
+      // <canvas> element
6265
+      else if (typeof this.source === 'object' && this.source.toDataURL) {
6266
+        source = this.source.toDataURL();
6267
+      }
6268
+
6269
+      object = {
6270
+        type: 'pattern',
6271
+        source: source,
6272
+        repeat: this.repeat,
6273
+        crossOrigin: this.crossOrigin,
6274
+        offsetX: toFixed(this.offsetX, NUM_FRACTION_DIGITS),
6275
+        offsetY: toFixed(this.offsetY, NUM_FRACTION_DIGITS),
6276
+        patternTransform: this.patternTransform ? this.patternTransform.concat() : null
6277
+      };
6278
+      fabric.util.populateWithProperties(this, object, propertiesToInclude);
6279
+
6280
+      return object;
6281
+    },
6282
+
6283
+    /* _TO_SVG_START_ */
6284
+    /**
6285
+     * Returns SVG representation of a pattern
6286
+     * @param {fabric.Object} object
6287
+     * @return {String} SVG representation of a pattern
6288
+     */
6289
+    toSVG: function(object) {
6290
+      var patternSource = typeof this.source === 'function' ? this.source() : this.source,
6291
+          patternWidth = patternSource.width / object.width,
6292
+          patternHeight = patternSource.height / object.height,
6293
+          patternOffsetX = this.offsetX / object.width,
6294
+          patternOffsetY = this.offsetY / object.height,
6295
+          patternImgSrc = '';
6296
+      if (this.repeat === 'repeat-x' || this.repeat === 'no-repeat') {
6297
+        patternHeight = 1;
6298
+        if (patternOffsetY) {
6299
+          patternHeight += Math.abs(patternOffsetY);
6300
+        }
6301
+      }
6302
+      if (this.repeat === 'repeat-y' || this.repeat === 'no-repeat') {
6303
+        patternWidth = 1;
6304
+        if (patternOffsetX) {
6305
+          patternWidth += Math.abs(patternOffsetX);
6306
+        }
6307
+
6308
+      }
6309
+      if (patternSource.src) {
6310
+        patternImgSrc = patternSource.src;
6311
+      }
6312
+      else if (patternSource.toDataURL) {
6313
+        patternImgSrc = patternSource.toDataURL();
6314
+      }
6315
+
6316
+      return '<pattern id="SVGID_' + this.id +
6317
+                    '" x="' + patternOffsetX +
6318
+                    '" y="' + patternOffsetY +
6319
+                    '" width="' + patternWidth +
6320
+                    '" height="' + patternHeight + '">\n' +
6321
+               '<image x="0" y="0"' +
6322
+                      ' width="' + patternSource.width +
6323
+                      '" height="' + patternSource.height +
6324
+                      '" xlink:href="' + patternImgSrc +
6325
+               '"></image>\n' +
6326
+             '</pattern>\n';
6327
+    },
6328
+    /* _TO_SVG_END_ */
6329
+
6330
+    setOptions: function(options) {
6331
+      for (var prop in options) {
6332
+        this[prop] = options[prop];
6333
+      }
6334
+    },
6335
+
6336
+    /**
6337
+     * Returns an instance of CanvasPattern
6338
+     * @param {CanvasRenderingContext2D} ctx Context to create pattern
6339
+     * @return {CanvasPattern}
6340
+     */
6341
+    toLive: function(ctx) {
6342
+      var source = typeof this.source === 'function' ? this.source() : this.source;
6343
+
6344
+      // if the image failed to load, return, and allow rest to continue loading
6345
+      if (!source) {
6346
+        return '';
6347
+      }
6348
+
6349
+      // if an image
6350
+      if (typeof source.src !== 'undefined') {
6351
+        if (!source.complete) {
6352
+          return '';
6353
+        }
6354
+        if (source.naturalWidth === 0 || source.naturalHeight === 0) {
6355
+          return '';
6356
+        }
6357
+      }
6358
+      return ctx.createPattern(source, this.repeat);
6359
+    }
6360
+  });
6361
+})();
6362
+
6363
+
6364
+(function(global) {
6365
+
6366
+  'use strict';
6367
+
6368
+  var fabric = global.fabric || (global.fabric = { }),
6369
+      toFixed = fabric.util.toFixed;
6370
+
6371
+  if (fabric.Shadow) {
6372
+    fabric.warn('fabric.Shadow is already defined.');
6373
+    return;
6374
+  }
6375
+
6376
+  /**
6377
+   * Shadow class
6378
+   * @class fabric.Shadow
6379
+   * @see {@link http://fabricjs.com/shadows|Shadow demo}
6380
+   * @see {@link fabric.Shadow#initialize} for constructor definition
6381
+   */
6382
+  fabric.Shadow = fabric.util.createClass(/** @lends fabric.Shadow.prototype */ {
6383
+
6384
+    /**
6385
+     * Shadow color
6386
+     * @type String
6387
+     * @default
6388
+     */
6389
+    color: 'rgb(0,0,0)',
6390
+
6391
+    /**
6392
+     * Shadow blur
6393
+     * @type Number
6394
+     */
6395
+    blur: 0,
6396
+
6397
+    /**
6398
+     * Shadow horizontal offset
6399
+     * @type Number
6400
+     * @default
6401
+     */
6402
+    offsetX: 0,
6403
+
6404
+    /**
6405
+     * Shadow vertical offset
6406
+     * @type Number
6407
+     * @default
6408
+     */
6409
+    offsetY: 0,
6410
+
6411
+    /**
6412
+     * Whether the shadow should affect stroke operations
6413
+     * @type Boolean
6414
+     * @default
6415
+     */
6416
+    affectStroke: false,
6417
+
6418
+    /**
6419
+     * Indicates whether toObject should include default values
6420
+     * @type Boolean
6421
+     * @default
6422
+     */
6423
+    includeDefaultValues: true,
6424
+
6425
+    /**
6426
+     * Constructor
6427
+     * @param {Object|String} [options] Options object with any of color, blur, offsetX, offsetY properties or string (e.g. "rgba(0,0,0,0.2) 2px 2px 10px")
6428
+     * @return {fabric.Shadow} thisArg
6429
+     */
6430
+    initialize: function(options) {
6431
+
6432
+      if (typeof options === 'string') {
6433
+        options = this._parseShadow(options);
6434
+      }
6435
+
6436
+      for (var prop in options) {
6437
+        this[prop] = options[prop];
6438
+      }
6439
+
6440
+      this.id = fabric.Object.__uid++;
6441
+    },
6442
+
6443
+    /**
6444
+     * @private
6445
+     * @param {String} shadow Shadow value to parse
6446
+     * @return {Object} Shadow object with color, offsetX, offsetY and blur
6447
+     */
6448
+    _parseShadow: function(shadow) {
6449
+      var shadowStr = shadow.trim(),
6450
+          offsetsAndBlur = fabric.Shadow.reOffsetsAndBlur.exec(shadowStr) || [],
6451
+          color = shadowStr.replace(fabric.Shadow.reOffsetsAndBlur, '') || 'rgb(0,0,0)';
6452
+
6453
+      return {
6454
+        color: color.trim(),
6455
+        offsetX: parseInt(offsetsAndBlur[1], 10) || 0,
6456
+        offsetY: parseInt(offsetsAndBlur[2], 10) || 0,
6457
+        blur: parseInt(offsetsAndBlur[3], 10) || 0
6458
+      };
6459
+    },
6460
+
6461
+    /**
6462
+     * Returns a string representation of an instance
6463
+     * @see http://www.w3.org/TR/css-text-decor-3/#text-shadow
6464
+     * @return {String} Returns CSS3 text-shadow declaration
6465
+     */
6466
+    toString: function() {
6467
+      return [this.offsetX, this.offsetY, this.blur, this.color].join('px ');
6468
+    },
6469
+
6470
+    /* _TO_SVG_START_ */
6471
+    /**
6472
+     * Returns SVG representation of a shadow
6473
+     * @param {fabric.Object} object
6474
+     * @return {String} SVG representation of a shadow
6475
+     */
6476
+    toSVG: function(object) {
6477
+      var fBoxX = 40, fBoxY = 40, NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
6478
+          offset = fabric.util.rotateVector(
6479
+            { x: this.offsetX, y: this.offsetY },
6480
+            fabric.util.degreesToRadians(-object.angle)),
6481
+          BLUR_BOX = 20, color = new fabric.Color(this.color);
6482
+
6483
+      if (object.width && object.height) {
6484
+        //http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
6485
+        // we add some extra space to filter box to contain the blur ( 20 )
6486
+        fBoxX = toFixed((Math.abs(offset.x) + this.blur) / object.width, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
6487
+        fBoxY = toFixed((Math.abs(offset.y) + this.blur) / object.height, NUM_FRACTION_DIGITS) * 100 + BLUR_BOX;
6488
+      }
6489
+      if (object.flipX) {
6490
+        offset.x *= -1;
6491
+      }
6492
+      if (object.flipY) {
6493
+        offset.y *= -1;
6494
+      }
6495
+
6496
+      return (
6497
+        '<filter id="SVGID_' + this.id + '" y="-' + fBoxY + '%" height="' + (100 + 2 * fBoxY) + '%" ' +
6498
+          'x="-' + fBoxX + '%" width="' + (100 + 2 * fBoxX) + '%" ' + '>\n' +
6499
+          '\t<feGaussianBlur in="SourceAlpha" stdDeviation="' +
6500
+            toFixed(this.blur ? this.blur / 2 : 0, NUM_FRACTION_DIGITS) + '"></feGaussianBlur>\n' +
6501
+          '\t<feOffset dx="' + toFixed(offset.x, NUM_FRACTION_DIGITS) +
6502
+          '" dy="' + toFixed(offset.y, NUM_FRACTION_DIGITS) + '" result="oBlur" ></feOffset>\n' +
6503
+          '\t<feFlood flood-color="' + color.toRgb() + '" flood-opacity="' + color.getAlpha() + '"/>\n' +
6504
+          '\t<feComposite in2="oBlur" operator="in" />\n' +
6505
+          '\t<feMerge>\n' +
6506
+            '\t\t<feMergeNode></feMergeNode>\n' +
6507
+            '\t\t<feMergeNode in="SourceGraphic"></feMergeNode>\n' +
6508
+          '\t</feMerge>\n' +
6509
+        '</filter>\n');
6510
+    },
6511
+    /* _TO_SVG_END_ */
6512
+
6513
+    /**
6514
+     * Returns object representation of a shadow
6515
+     * @return {Object} Object representation of a shadow instance
6516
+     */
6517
+    toObject: function() {
6518
+      if (this.includeDefaultValues) {
6519
+        return {
6520
+          color: this.color,
6521
+          blur: this.blur,
6522
+          offsetX: this.offsetX,
6523
+          offsetY: this.offsetY,
6524
+          affectStroke: this.affectStroke
6525
+        };
6526
+      }
6527
+      var obj = { }, proto = fabric.Shadow.prototype;
6528
+
6529
+      ['color', 'blur', 'offsetX', 'offsetY', 'affectStroke'].forEach(function(prop) {
6530
+        if (this[prop] !== proto[prop]) {
6531
+          obj[prop] = this[prop];
6532
+        }
6533
+      }, this);
6534
+
6535
+      return obj;
6536
+    }
6537
+  });
6538
+
6539
+  /**
6540
+   * Regex matching shadow offsetX, offsetY and blur (ex: "2px 2px 10px rgba(0,0,0,0.2)", "rgb(0,255,0) 2px 2px")
6541
+   * @static
6542
+   * @field
6543
+   * @memberOf fabric.Shadow
6544
+   */
6545
+  // eslint-disable-next-line max-len
6546
+  fabric.Shadow.reOffsetsAndBlur = /(?:\s|^)(-?\d+(?:px)?(?:\s?|$))?(-?\d+(?:px)?(?:\s?|$))?(\d+(?:px)?)?(?:\s?|$)(?:$|\s)/;
6547
+
6548
+})(typeof exports !== 'undefined' ? exports : this);
6549
+
6550
+
6551
+(function () {
6552
+
6553
+  'use strict';
6554
+
6555
+  if (fabric.StaticCanvas) {
6556
+    fabric.warn('fabric.StaticCanvas is already defined.');
6557
+    return;
6558
+  }
6559
+
6560
+  // aliases for faster resolution
6561
+  var extend = fabric.util.object.extend,
6562
+      getElementOffset = fabric.util.getElementOffset,
6563
+      removeFromArray = fabric.util.removeFromArray,
6564
+      toFixed = fabric.util.toFixed,
6565
+      transformPoint = fabric.util.transformPoint,
6566
+      invertTransform = fabric.util.invertTransform,
6567
+      getNodeCanvas = fabric.util.getNodeCanvas,
6568
+      createCanvasElement = fabric.util.createCanvasElement,
6569
+
6570
+      CANVAS_INIT_ERROR = new Error('Could not initialize `canvas` element');
6571
+
6572
+  /**
6573
+   * Static canvas class
6574
+   * @class fabric.StaticCanvas
6575
+   * @mixes fabric.Collection
6576
+   * @mixes fabric.Observable
6577
+   * @see {@link http://fabricjs.com/static_canvas|StaticCanvas demo}
6578
+   * @see {@link fabric.StaticCanvas#initialize} for constructor definition
6579
+   * @fires before:render
6580
+   * @fires after:render
6581
+   * @fires canvas:cleared
6582
+   * @fires object:added
6583
+   * @fires object:removed
6584
+   */
6585
+  fabric.StaticCanvas = fabric.util.createClass(fabric.CommonMethods, /** @lends fabric.StaticCanvas.prototype */ {
6586
+
6587
+    /**
6588
+     * Constructor
6589
+     * @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
6590
+     * @param {Object} [options] Options object
6591
+     * @return {Object} thisArg
6592
+     */
6593
+    initialize: function(el, options) {
6594
+      options || (options = { });
6595
+      this.renderAndResetBound = this.renderAndReset.bind(this);
6596
+      this.requestRenderAllBound = this.requestRenderAll.bind(this);
6597
+      this._initStatic(el, options);
6598
+    },
6599
+
6600
+    /**
6601
+     * Background color of canvas instance.
6602
+     * Should be set via {@link fabric.StaticCanvas#setBackgroundColor}.
6603
+     * @type {(String|fabric.Pattern)}
6604
+     * @default
6605
+     */
6606
+    backgroundColor: '',
6607
+
6608
+    /**
6609
+     * Background image of canvas instance.
6610
+     * Should be set via {@link fabric.StaticCanvas#setBackgroundImage}.
6611
+     * <b>Backwards incompatibility note:</b> The "backgroundImageOpacity"
6612
+     * and "backgroundImageStretch" properties are deprecated since 1.3.9.
6613
+     * Use {@link fabric.Image#opacity}, {@link fabric.Image#width} and {@link fabric.Image#height}.
6614
+     * since 2.4.0 image caching is active, please when putting an image as background, add to the
6615
+     * canvas property a reference to the canvas it is on. Otherwise the image cannot detect the zoom
6616
+     * vale. As an alternative you can disable image objectCaching
6617
+     * @type fabric.Image
6618
+     * @default
6619
+     */
6620
+    backgroundImage: null,
6621
+
6622
+    /**
6623
+     * Overlay color of canvas instance.
6624
+     * Should be set via {@link fabric.StaticCanvas#setOverlayColor}
6625
+     * @since 1.3.9
6626
+     * @type {(String|fabric.Pattern)}
6627
+     * @default
6628
+     */
6629
+    overlayColor: '',
6630
+
6631
+    /**
6632
+     * Overlay image of canvas instance.
6633
+     * Should be set via {@link fabric.StaticCanvas#setOverlayImage}.
6634
+     * <b>Backwards incompatibility note:</b> The "overlayImageLeft"
6635
+     * and "overlayImageTop" properties are deprecated since 1.3.9.
6636
+     * Use {@link fabric.Image#left} and {@link fabric.Image#top}.
6637
+     * since 2.4.0 image caching is active, please when putting an image as overlay, add to the
6638
+     * canvas property a reference to the canvas it is on. Otherwise the image cannot detect the zoom
6639
+     * vale. As an alternative you can disable image objectCaching
6640
+     * @type fabric.Image
6641
+     * @default
6642
+     */
6643
+    overlayImage: null,
6644
+
6645
+    /**
6646
+     * Indicates whether toObject/toDatalessObject should include default values
6647
+     * @type Boolean
6648
+     * @default
6649
+     */
6650
+    includeDefaultValues: true,
6651
+
6652
+    /**
6653
+     * Indicates whether objects' state should be saved
6654
+     * @type Boolean
6655
+     * @default
6656
+     */
6657
+    stateful: false,
6658
+
6659
+    /**
6660
+     * Indicates whether {@link fabric.Collection.add}, {@link fabric.Collection.insertAt} and {@link fabric.Collection.remove},
6661
+     * {@link fabric.StaticCanvas.moveTo}, {@link fabric.StaticCanvas.clear} and many more, should also re-render canvas.
6662
+     * Disabling this option will not give a performance boost when adding/removing a lot of objects to/from canvas at once
6663
+     * since the renders are quequed and executed one per frame.
6664
+     * Disabling is suggested anyway and managing the renders of the app manually is not a big effort ( canvas.requestRenderAll() )
6665
+     * Left default to true to do not break documentation and old app, fiddles.
6666
+     * @type Boolean
6667
+     * @default
6668
+     */
6669
+    renderOnAddRemove: true,
6670
+
6671
+    /**
6672
+     * Function that determines clipping of entire canvas area
6673
+     * Being passed context as first argument.
6674
+     * If you are using code minification, ctx argument can be minified/manglied you should use
6675
+     * as a workaround `var ctx = arguments[0];` in the function;
6676
+     * See clipping canvas area in {@link https://github.com/kangax/fabric.js/wiki/FAQ}
6677
+     * @deprecated since 2.0.0
6678
+     * @type Function
6679
+     * @default
6680
+     */
6681
+    clipTo: null,
6682
+
6683
+    /**
6684
+     * Indicates whether object controls (borders/controls) are rendered above overlay image
6685
+     * @type Boolean
6686
+     * @default
6687
+     */
6688
+    controlsAboveOverlay: false,
6689
+
6690
+    /**
6691
+     * Indicates whether the browser can be scrolled when using a touchscreen and dragging on the canvas
6692
+     * @type Boolean
6693
+     * @default
6694
+     */
6695
+    allowTouchScrolling: false,
6696
+
6697
+    /**
6698
+     * Indicates whether this canvas will use image smoothing, this is on by default in browsers
6699
+     * @type Boolean
6700
+     * @default
6701
+     */
6702
+    imageSmoothingEnabled: true,
6703
+
6704
+    /**
6705
+     * The transformation (in the format of Canvas transform) which focuses the viewport
6706
+     * @type Array
6707
+     * @default
6708
+     */
6709
+    viewportTransform: fabric.iMatrix.concat(),
6710
+
6711
+    /**
6712
+     * if set to false background image is not affected by viewport transform
6713
+     * @since 1.6.3
6714
+     * @type Boolean
6715
+     * @default
6716
+     */
6717
+    backgroundVpt: true,
6718
+
6719
+    /**
6720
+     * if set to false overlya image is not affected by viewport transform
6721
+     * @since 1.6.3
6722
+     * @type Boolean
6723
+     * @default
6724
+     */
6725
+    overlayVpt: true,
6726
+
6727
+    /**
6728
+     * Callback; invoked right before object is about to be scaled/rotated
6729
+     * @deprecated since 2.3.0
6730
+     * Use before:transform event
6731
+     */
6732
+    onBeforeScaleRotate: function () {
6733
+      /* NOOP */
6734
+    },
6735
+
6736
+    /**
6737
+     * When true, canvas is scaled by devicePixelRatio for better rendering on retina screens
6738
+     * @type Boolean
6739
+     * @default
6740
+     */
6741
+    enableRetinaScaling: true,
6742
+
6743
+    /**
6744
+     * Describe canvas element extension over design
6745
+     * properties are tl,tr,bl,br.
6746
+     * if canvas is not zoomed/panned those points are the four corner of canvas
6747
+     * if canvas is viewportTransformed you those points indicate the extension
6748
+     * of canvas element in plain untrasformed coordinates
6749
+     * The coordinates get updated with @method calcViewportBoundaries.
6750
+     * @memberOf fabric.StaticCanvas.prototype
6751
+     */
6752
+    vptCoords: { },
6753
+
6754
+    /**
6755
+     * Based on vptCoords and object.aCoords, skip rendering of objects that
6756
+     * are not included in current viewport.
6757
+     * May greatly help in applications with crowded canvas and use of zoom/pan
6758
+     * If One of the corner of the bounding box of the object is on the canvas
6759
+     * the objects get rendered.
6760
+     * @memberOf fabric.StaticCanvas.prototype
6761
+     * @type Boolean
6762
+     * @default
6763
+     */
6764
+    skipOffscreen: true,
6765
+
6766
+    /**
6767
+     * a fabricObject that, without stroke define a clipping area with their shape. filled in black
6768
+     * the clipPath object gets used when the canvas has rendered, and the context is placed in the
6769
+     * top left corner of the canvas.
6770
+     * clipPath will clip away controls, if you do not want this to happen use controlsAboveOverlay = true
6771
+     * @type fabric.Object
6772
+     */
6773
+    clipPath: undefined,
6774
+
6775
+    /**
6776
+     * @private
6777
+     * @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
6778
+     * @param {Object} [options] Options object
6779
+     */
6780
+    _initStatic: function(el, options) {
6781
+      var cb = this.requestRenderAllBound;
6782
+      this._objects = [];
6783
+      this._createLowerCanvas(el);
6784
+      this._initOptions(options);
6785
+      this._setImageSmoothing();
6786
+      // only initialize retina scaling once
6787
+      if (!this.interactive) {
6788
+        this._initRetinaScaling();
6789
+      }
6790
+
6791
+      if (options.overlayImage) {
6792
+        this.setOverlayImage(options.overlayImage, cb);
6793
+      }
6794
+      if (options.backgroundImage) {
6795
+        this.setBackgroundImage(options.backgroundImage, cb);
6796
+      }
6797
+      if (options.backgroundColor) {
6798
+        this.setBackgroundColor(options.backgroundColor, cb);
6799
+      }
6800
+      if (options.overlayColor) {
6801
+        this.setOverlayColor(options.overlayColor, cb);
6802
+      }
6803
+      this.calcOffset();
6804
+    },
6805
+
6806
+    /**
6807
+     * @private
6808
+     */
6809
+    _isRetinaScaling: function() {
6810
+      return (fabric.devicePixelRatio !== 1 && this.enableRetinaScaling);
6811
+    },
6812
+
6813
+    /**
6814
+     * @private
6815
+     * @return {Number} retinaScaling if applied, otherwise 1;
6816
+     */
6817
+    getRetinaScaling: function() {
6818
+      return this._isRetinaScaling() ? fabric.devicePixelRatio : 1;
6819
+    },
6820
+
6821
+    /**
6822
+     * @private
6823
+     */
6824
+    _initRetinaScaling: function() {
6825
+      if (!this._isRetinaScaling()) {
6826
+        return;
6827
+      }
6828
+      this.lowerCanvasEl.setAttribute('width', this.width * fabric.devicePixelRatio);
6829
+      this.lowerCanvasEl.setAttribute('height', this.height * fabric.devicePixelRatio);
6830
+
6831
+      this.contextContainer.scale(fabric.devicePixelRatio, fabric.devicePixelRatio);
6832
+    },
6833
+
6834
+    /**
6835
+     * Calculates canvas element offset relative to the document
6836
+     * This method is also attached as "resize" event handler of window
6837
+     * @return {fabric.Canvas} instance
6838
+     * @chainable
6839
+     */
6840
+    calcOffset: function () {
6841
+      this._offset = getElementOffset(this.lowerCanvasEl);
6842
+      return this;
6843
+    },
6844
+
6845
+    /**
6846
+     * Sets {@link fabric.StaticCanvas#overlayImage|overlay image} for this canvas
6847
+     * @param {(fabric.Image|String)} image fabric.Image instance or URL of an image to set overlay to
6848
+     * @param {Function} callback callback to invoke when image is loaded and set as an overlay
6849
+     * @param {Object} [options] Optional options to set for the {@link fabric.Image|overlay image}.
6850
+     * @return {fabric.Canvas} thisArg
6851
+     * @chainable
6852
+     * @see {@link http://jsfiddle.net/fabricjs/MnzHT/|jsFiddle demo}
6853
+     * @example <caption>Normal overlayImage with left/top = 0</caption>
6854
+     * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
6855
+     *   // Needed to position overlayImage at 0/0
6856
+     *   originX: 'left',
6857
+     *   originY: 'top'
6858
+     * });
6859
+     * @example <caption>overlayImage with different properties</caption>
6860
+     * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
6861
+     *   opacity: 0.5,
6862
+     *   angle: 45,
6863
+     *   left: 400,
6864
+     *   top: 400,
6865
+     *   originX: 'left',
6866
+     *   originY: 'top'
6867
+     * });
6868
+     * @example <caption>Stretched overlayImage #1 - width/height correspond to canvas width/height</caption>
6869
+     * fabric.Image.fromURL('http://fabricjs.com/assets/jail_cell_bars.png', function(img) {
6870
+     *    img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});
6871
+     *    canvas.setOverlayImage(img, canvas.renderAll.bind(canvas));
6872
+     * });
6873
+     * @example <caption>Stretched overlayImage #2 - width/height correspond to canvas width/height</caption>
6874
+     * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
6875
+     *   width: canvas.width,
6876
+     *   height: canvas.height,
6877
+     *   // Needed to position overlayImage at 0/0
6878
+     *   originX: 'left',
6879
+     *   originY: 'top'
6880
+     * });
6881
+     * @example <caption>overlayImage loaded from cross-origin</caption>
6882
+     * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
6883
+     *   opacity: 0.5,
6884
+     *   angle: 45,
6885
+     *   left: 400,
6886
+     *   top: 400,
6887
+     *   originX: 'left',
6888
+     *   originY: 'top',
6889
+     *   crossOrigin: 'anonymous'
6890
+     * });
6891
+     */
6892
+    setOverlayImage: function (image, callback, options) {
6893
+      return this.__setBgOverlayImage('overlayImage', image, callback, options);
6894
+    },
6895
+
6896
+    /**
6897
+     * Sets {@link fabric.StaticCanvas#backgroundImage|background image} for this canvas
6898
+     * @param {(fabric.Image|String)} image fabric.Image instance or URL of an image to set background to
6899
+     * @param {Function} callback Callback to invoke when image is loaded and set as background
6900
+     * @param {Object} [options] Optional options to set for the {@link fabric.Image|background image}.
6901
+     * @return {fabric.Canvas} thisArg
6902
+     * @chainable
6903
+     * @see {@link http://jsfiddle.net/djnr8o7a/28/|jsFiddle demo}
6904
+     * @example <caption>Normal backgroundImage with left/top = 0</caption>
6905
+     * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
6906
+     *   // Needed to position backgroundImage at 0/0
6907
+     *   originX: 'left',
6908
+     *   originY: 'top'
6909
+     * });
6910
+     * @example <caption>backgroundImage with different properties</caption>
6911
+     * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
6912
+     *   opacity: 0.5,
6913
+     *   angle: 45,
6914
+     *   left: 400,
6915
+     *   top: 400,
6916
+     *   originX: 'left',
6917
+     *   originY: 'top'
6918
+     * });
6919
+     * @example <caption>Stretched backgroundImage #1 - width/height correspond to canvas width/height</caption>
6920
+     * fabric.Image.fromURL('http://fabricjs.com/assets/honey_im_subtle.png', function(img) {
6921
+     *    img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});
6922
+     *    canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
6923
+     * });
6924
+     * @example <caption>Stretched backgroundImage #2 - width/height correspond to canvas width/height</caption>
6925
+     * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
6926
+     *   width: canvas.width,
6927
+     *   height: canvas.height,
6928
+     *   // Needed to position backgroundImage at 0/0
6929
+     *   originX: 'left',
6930
+     *   originY: 'top'
6931
+     * });
6932
+     * @example <caption>backgroundImage loaded from cross-origin</caption>
6933
+     * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
6934
+     *   opacity: 0.5,
6935
+     *   angle: 45,
6936
+     *   left: 400,
6937
+     *   top: 400,
6938
+     *   originX: 'left',
6939
+     *   originY: 'top',
6940
+     *   crossOrigin: 'anonymous'
6941
+     * });
6942
+     */
6943
+    // TODO: fix stretched examples
6944
+    setBackgroundImage: function (image, callback, options) {
6945
+      return this.__setBgOverlayImage('backgroundImage', image, callback, options);
6946
+    },
6947
+
6948
+    /**
6949
+     * Sets {@link fabric.StaticCanvas#overlayColor|foreground color} for this canvas
6950
+     * @param {(String|fabric.Pattern)} overlayColor Color or pattern to set foreground color to
6951
+     * @param {Function} callback Callback to invoke when foreground color is set
6952
+     * @return {fabric.Canvas} thisArg
6953
+     * @chainable
6954
+     * @see {@link http://jsfiddle.net/fabricjs/pB55h/|jsFiddle demo}
6955
+     * @example <caption>Normal overlayColor - color value</caption>
6956
+     * canvas.setOverlayColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
6957
+     * @example <caption>fabric.Pattern used as overlayColor</caption>
6958
+     * canvas.setOverlayColor({
6959
+     *   source: 'http://fabricjs.com/assets/escheresque_ste.png'
6960
+     * }, canvas.renderAll.bind(canvas));
6961
+     * @example <caption>fabric.Pattern used as overlayColor with repeat and offset</caption>
6962
+     * canvas.setOverlayColor({
6963
+     *   source: 'http://fabricjs.com/assets/escheresque_ste.png',
6964
+     *   repeat: 'repeat',
6965
+     *   offsetX: 200,
6966
+     *   offsetY: 100
6967
+     * }, canvas.renderAll.bind(canvas));
6968
+     */
6969
+    setOverlayColor: function(overlayColor, callback) {
6970
+      return this.__setBgOverlayColor('overlayColor', overlayColor, callback);
6971
+    },
6972
+
6973
+    /**
6974
+     * Sets {@link fabric.StaticCanvas#backgroundColor|background color} for this canvas
6975
+     * @param {(String|fabric.Pattern)} backgroundColor Color or pattern to set background color to
6976
+     * @param {Function} callback Callback to invoke when background color is set
6977
+     * @return {fabric.Canvas} thisArg
6978
+     * @chainable
6979
+     * @see {@link http://jsfiddle.net/fabricjs/hXzvk/|jsFiddle demo}
6980
+     * @example <caption>Normal backgroundColor - color value</caption>
6981
+     * canvas.setBackgroundColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
6982
+     * @example <caption>fabric.Pattern used as backgroundColor</caption>
6983
+     * canvas.setBackgroundColor({
6984
+     *   source: 'http://fabricjs.com/assets/escheresque_ste.png'
6985
+     * }, canvas.renderAll.bind(canvas));
6986
+     * @example <caption>fabric.Pattern used as backgroundColor with repeat and offset</caption>
6987
+     * canvas.setBackgroundColor({
6988
+     *   source: 'http://fabricjs.com/assets/escheresque_ste.png',
6989
+     *   repeat: 'repeat',
6990
+     *   offsetX: 200,
6991
+     *   offsetY: 100
6992
+     * }, canvas.renderAll.bind(canvas));
6993
+     */
6994
+    setBackgroundColor: function(backgroundColor, callback) {
6995
+      return this.__setBgOverlayColor('backgroundColor', backgroundColor, callback);
6996
+    },
6997
+
6998
+    /**
6999
+     * @private
7000
+     * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-imagesmoothingenabled|WhatWG Canvas Standard}
7001
+     */
7002
+    _setImageSmoothing: function() {
7003
+      var ctx = this.getContext();
7004
+
7005
+      ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled || ctx.webkitImageSmoothingEnabled
7006
+        || ctx.mozImageSmoothingEnabled || ctx.msImageSmoothingEnabled || ctx.oImageSmoothingEnabled;
7007
+      ctx.imageSmoothingEnabled = this.imageSmoothingEnabled;
7008
+    },
7009
+
7010
+    /**
7011
+     * @private
7012
+     * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundImage|backgroundImage}
7013
+     * or {@link fabric.StaticCanvas#overlayImage|overlayImage})
7014
+     * @param {(fabric.Image|String|null)} image fabric.Image instance, URL of an image or null to set background or overlay to
7015
+     * @param {Function} callback Callback to invoke when image is loaded and set as background or overlay
7016
+     * @param {Object} [options] Optional options to set for the {@link fabric.Image|image}.
7017
+     */
7018
+    __setBgOverlayImage: function(property, image, callback, options) {
7019
+      if (typeof image === 'string') {
7020
+        fabric.util.loadImage(image, function(img) {
7021
+          if (img) {
7022
+            var instance = new fabric.Image(img, options);
7023
+            this[property] = instance;
7024
+            instance.canvas = this;
7025
+          }
7026
+          callback && callback(img);
7027
+        }, this, options && options.crossOrigin);
7028
+      }
7029
+      else {
7030
+        options && image.setOptions(options);
7031
+        this[property] = image;
7032
+        image && (image.canvas = this);
7033
+        callback && callback(image);
7034
+      }
7035
+
7036
+      return this;
7037
+    },
7038
+
7039
+    /**
7040
+     * @private
7041
+     * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundColor|backgroundColor}
7042
+     * or {@link fabric.StaticCanvas#overlayColor|overlayColor})
7043
+     * @param {(Object|String|null)} color Object with pattern information, color value or null
7044
+     * @param {Function} [callback] Callback is invoked when color is set
7045
+     */
7046
+    __setBgOverlayColor: function(property, color, callback) {
7047
+      this[property] = color;
7048
+      this._initGradient(color, property);
7049
+      this._initPattern(color, property, callback);
7050
+      return this;
7051
+    },
7052
+
7053
+    /**
7054
+     * @private
7055
+     */
7056
+    _createCanvasElement: function() {
7057
+      var element = createCanvasElement();
7058
+      if (!element) {
7059
+        throw CANVAS_INIT_ERROR;
7060
+      }
7061
+      if (!element.style) {
7062
+        element.style = { };
7063
+      }
7064
+      if (typeof element.getContext === 'undefined') {
7065
+        throw CANVAS_INIT_ERROR;
7066
+      }
7067
+      return element;
7068
+    },
7069
+
7070
+    /**
7071
+     * @private
7072
+     * @param {Object} [options] Options object
7073
+     */
7074
+    _initOptions: function (options) {
7075
+      var lowerCanvasEl = this.lowerCanvasEl;
7076
+      this._setOptions(options);
7077
+
7078
+      this.width = this.width || parseInt(lowerCanvasEl.width, 10) || 0;
7079
+      this.height = this.height || parseInt(lowerCanvasEl.height, 10) || 0;
7080
+
7081
+      if (!this.lowerCanvasEl.style) {
7082
+        return;
7083
+      }
7084
+
7085
+      lowerCanvasEl.width = this.width;
7086
+      lowerCanvasEl.height = this.height;
7087
+
7088
+      lowerCanvasEl.style.width = this.width + 'px';
7089
+      lowerCanvasEl.style.height = this.height + 'px';
7090
+
7091
+      this.viewportTransform = this.viewportTransform.slice();
7092
+    },
7093
+
7094
+    /**
7095
+     * Creates a bottom canvas
7096
+     * @private
7097
+     * @param {HTMLElement} [canvasEl]
7098
+     */
7099
+    _createLowerCanvas: function (canvasEl) {
7100
+      // canvasEl === 'HTMLCanvasElement' does not work on jsdom/node
7101
+      if (canvasEl && canvasEl.getContext) {
7102
+        this.lowerCanvasEl = canvasEl;
7103
+      }
7104
+      else {
7105
+        this.lowerCanvasEl = fabric.util.getById(canvasEl) || this._createCanvasElement();
7106
+      }
7107
+
7108
+      fabric.util.addClass(this.lowerCanvasEl, 'lower-canvas');
7109
+
7110
+      if (this.interactive) {
7111
+        this._applyCanvasStyle(this.lowerCanvasEl);
7112
+      }
7113
+
7114
+      this.contextContainer = this.lowerCanvasEl.getContext('2d');
7115
+    },
7116
+
7117
+    /**
7118
+     * Returns canvas width (in px)
7119
+     * @return {Number}
7120
+     */
7121
+    getWidth: function () {
7122
+      return this.width;
7123
+    },
7124
+
7125
+    /**
7126
+     * Returns canvas height (in px)
7127
+     * @return {Number}
7128
+     */
7129
+    getHeight: function () {
7130
+      return this.height;
7131
+    },
7132
+
7133
+    /**
7134
+     * Sets width of this canvas instance
7135
+     * @param {Number|String} value                         Value to set width to
7136
+     * @param {Object}        [options]                     Options object
7137
+     * @param {Boolean}       [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
7138
+     * @param {Boolean}       [options.cssOnly=false]       Set the given dimensions only as css dimensions
7139
+     * @return {fabric.Canvas} instance
7140
+     * @chainable true
7141
+     */
7142
+    setWidth: function (value, options) {
7143
+      return this.setDimensions({ width: value }, options);
7144
+    },
7145
+
7146
+    /**
7147
+     * Sets height of this canvas instance
7148
+     * @param {Number|String} value                         Value to set height to
7149
+     * @param {Object}        [options]                     Options object
7150
+     * @param {Boolean}       [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
7151
+     * @param {Boolean}       [options.cssOnly=false]       Set the given dimensions only as css dimensions
7152
+     * @return {fabric.Canvas} instance
7153
+     * @chainable true
7154
+     */
7155
+    setHeight: function (value, options) {
7156
+      return this.setDimensions({ height: value }, options);
7157
+    },
7158
+
7159
+    /**
7160
+     * Sets dimensions (width, height) of this canvas instance. when options.cssOnly flag active you should also supply the unit of measure (px/%/em)
7161
+     * @param {Object}        dimensions                    Object with width/height properties
7162
+     * @param {Number|String} [dimensions.width]            Width of canvas element
7163
+     * @param {Number|String} [dimensions.height]           Height of canvas element
7164
+     * @param {Object}        [options]                     Options object
7165
+     * @param {Boolean}       [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
7166
+     * @param {Boolean}       [options.cssOnly=false]       Set the given dimensions only as css dimensions
7167
+     * @return {fabric.Canvas} thisArg
7168
+     * @chainable
7169
+     */
7170
+    setDimensions: function (dimensions, options) {
7171
+      var cssValue;
7172
+
7173
+      options = options || {};
7174
+
7175
+      for (var prop in dimensions) {
7176
+        cssValue = dimensions[prop];
7177
+
7178
+        if (!options.cssOnly) {
7179
+          this._setBackstoreDimension(prop, dimensions[prop]);
7180
+          cssValue += 'px';
7181
+          this.hasLostContext = true;
7182
+        }
7183
+
7184
+        if (!options.backstoreOnly) {
7185
+          this._setCssDimension(prop, cssValue);
7186
+        }
7187
+      }
7188
+      if (this._isCurrentlyDrawing) {
7189
+        this.freeDrawingBrush && this.freeDrawingBrush._setBrushStyles();
7190
+      }
7191
+      this._initRetinaScaling();
7192
+      this._setImageSmoothing();
7193
+      this.calcOffset();
7194
+
7195
+      if (!options.cssOnly) {
7196
+        this.requestRenderAll();
7197
+      }
7198
+
7199
+      return this;
7200
+    },
7201
+
7202
+    /**
7203
+     * Helper for setting width/height
7204
+     * @private
7205
+     * @param {String} prop property (width|height)
7206
+     * @param {Number} value value to set property to
7207
+     * @return {fabric.Canvas} instance
7208
+     * @chainable true
7209
+     */
7210
+    _setBackstoreDimension: function (prop, value) {
7211
+      this.lowerCanvasEl[prop] = value;
7212
+
7213
+      if (this.upperCanvasEl) {
7214
+        this.upperCanvasEl[prop] = value;
7215
+      }
7216
+
7217
+      if (this.cacheCanvasEl) {
7218
+        this.cacheCanvasEl[prop] = value;
7219
+      }
7220
+
7221
+      this[prop] = value;
7222
+
7223
+      return this;
7224
+    },
7225
+
7226
+    /**
7227
+     * Helper for setting css width/height
7228
+     * @private
7229
+     * @param {String} prop property (width|height)
7230
+     * @param {String} value value to set property to
7231
+     * @return {fabric.Canvas} instance
7232
+     * @chainable true
7233
+     */
7234
+    _setCssDimension: function (prop, value) {
7235
+      this.lowerCanvasEl.style[prop] = value;
7236
+
7237
+      if (this.upperCanvasEl) {
7238
+        this.upperCanvasEl.style[prop] = value;
7239
+      }
7240
+
7241
+      if (this.wrapperEl) {
7242
+        this.wrapperEl.style[prop] = value;
7243
+      }
7244
+
7245
+      return this;
7246
+    },
7247
+
7248
+    /**
7249
+     * Returns canvas zoom level
7250
+     * @return {Number}
7251
+     */
7252
+    getZoom: function () {
7253
+      return this.viewportTransform[0];
7254
+    },
7255
+
7256
+    /**
7257
+     * Sets viewport transform of this canvas instance
7258
+     * @param {Array} vpt the transform in the form of context.transform
7259
+     * @return {fabric.Canvas} instance
7260
+     * @chainable true
7261
+     */
7262
+    setViewportTransform: function (vpt) {
7263
+      var activeObject = this._activeObject, object, ignoreVpt = false, skipAbsolute = true, i, len;
7264
+      this.viewportTransform = vpt;
7265
+      for (i = 0, len = this._objects.length; i < len; i++) {
7266
+        object = this._objects[i];
7267
+        object.group || object.setCoords(ignoreVpt, skipAbsolute);
7268
+      }
7269
+      if (activeObject && activeObject.type === 'activeSelection') {
7270
+        activeObject.setCoords(ignoreVpt, skipAbsolute);
7271
+      }
7272
+      this.calcViewportBoundaries();
7273
+      this.renderOnAddRemove && this.requestRenderAll();
7274
+      return this;
7275
+    },
7276
+
7277
+    /**
7278
+     * Sets zoom level of this canvas instance, zoom centered around point
7279
+     * @param {fabric.Point} point to zoom with respect to
7280
+     * @param {Number} value to set zoom to, less than 1 zooms out
7281
+     * @return {fabric.Canvas} instance
7282
+     * @chainable true
7283
+     */
7284
+    zoomToPoint: function (point, value) {
7285
+      // TODO: just change the scale, preserve other transformations
7286
+      var before = point, vpt = this.viewportTransform.slice(0);
7287
+      point = transformPoint(point, invertTransform(this.viewportTransform));
7288
+      vpt[0] = value;
7289
+      vpt[3] = value;
7290
+      var after = transformPoint(point, vpt);
7291
+      vpt[4] += before.x - after.x;
7292
+      vpt[5] += before.y - after.y;
7293
+      return this.setViewportTransform(vpt);
7294
+    },
7295
+
7296
+    /**
7297
+     * Sets zoom level of this canvas instance
7298
+     * @param {Number} value to set zoom to, less than 1 zooms out
7299
+     * @return {fabric.Canvas} instance
7300
+     * @chainable true
7301
+     */
7302
+    setZoom: function (value) {
7303
+      this.zoomToPoint(new fabric.Point(0, 0), value);
7304
+      return this;
7305
+    },
7306
+
7307
+    /**
7308
+     * Pan viewport so as to place point at top left corner of canvas
7309
+     * @param {fabric.Point} point to move to
7310
+     * @return {fabric.Canvas} instance
7311
+     * @chainable true
7312
+     */
7313
+    absolutePan: function (point) {
7314
+      var vpt = this.viewportTransform.slice(0);
7315
+      vpt[4] = -point.x;
7316
+      vpt[5] = -point.y;
7317
+      return this.setViewportTransform(vpt);
7318
+    },
7319
+
7320
+    /**
7321
+     * Pans viewpoint relatively
7322
+     * @param {fabric.Point} point (position vector) to move by
7323
+     * @return {fabric.Canvas} instance
7324
+     * @chainable true
7325
+     */
7326
+    relativePan: function (point) {
7327
+      return this.absolutePan(new fabric.Point(
7328
+        -point.x - this.viewportTransform[4],
7329
+        -point.y - this.viewportTransform[5]
7330
+      ));
7331
+    },
7332
+
7333
+    /**
7334
+     * Returns &lt;canvas> element corresponding to this instance
7335
+     * @return {HTMLCanvasElement}
7336
+     */
7337
+    getElement: function () {
7338
+      return this.lowerCanvasEl;
7339
+    },
7340
+
7341
+    /**
7342
+     * @private
7343
+     * @param {fabric.Object} obj Object that was added
7344
+     */
7345
+    _onObjectAdded: function(obj) {
7346
+      this.stateful && obj.setupState();
7347
+      obj._set('canvas', this);
7348
+      obj.setCoords();
7349
+      this.fire('object:added', { target: obj });
7350
+      obj.fire('added');
7351
+    },
7352
+
7353
+    /**
7354
+     * @private
7355
+     * @param {fabric.Object} obj Object that was removed
7356
+     */
7357
+    _onObjectRemoved: function(obj) {
7358
+      this.fire('object:removed', { target: obj });
7359
+      obj.fire('removed');
7360
+      delete obj.canvas;
7361
+    },
7362
+
7363
+    /**
7364
+     * Clears specified context of canvas element
7365
+     * @param {CanvasRenderingContext2D} ctx Context to clear
7366
+     * @return {fabric.Canvas} thisArg
7367
+     * @chainable
7368
+     */
7369
+    clearContext: function(ctx) {
7370
+      ctx.clearRect(0, 0, this.width, this.height);
7371
+      return this;
7372
+    },
7373
+
7374
+    /**
7375
+     * Returns context of canvas where objects are drawn
7376
+     * @return {CanvasRenderingContext2D}
7377
+     */
7378
+    getContext: function () {
7379
+      return this.contextContainer;
7380
+    },
7381
+
7382
+    /**
7383
+     * Clears all contexts (background, main, top) of an instance
7384
+     * @return {fabric.Canvas} thisArg
7385
+     * @chainable
7386
+     */
7387
+    clear: function () {
7388
+      this._objects.length = 0;
7389
+      this.backgroundImage = null;
7390
+      this.overlayImage = null;
7391
+      this.backgroundColor = '';
7392
+      this.overlayColor = '';
7393
+      if (this._hasITextHandlers) {
7394
+        this.off('mouse:up', this._mouseUpITextHandler);
7395
+        this._iTextInstances = null;
7396
+        this._hasITextHandlers = false;
7397
+      }
7398
+      this.clearContext(this.contextContainer);
7399
+      this.fire('canvas:cleared');
7400
+      this.renderOnAddRemove && this.requestRenderAll();
7401
+      return this;
7402
+    },
7403
+
7404
+    /**
7405
+     * Renders the canvas
7406
+     * @return {fabric.Canvas} instance
7407
+     * @chainable
7408
+     */
7409
+    renderAll: function () {
7410
+      var canvasToDrawOn = this.contextContainer;
7411
+      this.renderCanvas(canvasToDrawOn, this._objects);
7412
+      return this;
7413
+    },
7414
+
7415
+    /**
7416
+     * Function created to be instance bound at initialization
7417
+     * used in requestAnimationFrame rendering
7418
+     * Let the fabricJS call it. If you call it manually you could have more
7419
+     * animationFrame stacking on to of each other
7420
+     * for an imperative rendering, use canvas.renderAll
7421
+     * @private
7422
+     * @return {fabric.Canvas} instance
7423
+     * @chainable
7424
+     */
7425
+    renderAndReset: function() {
7426
+      this.isRendering = 0;
7427
+      this.renderAll();
7428
+    },
7429
+
7430
+    /**
7431
+     * Append a renderAll request to next animation frame.
7432
+     * unless one is already in progress, in that case nothing is done
7433
+     * a boolean flag will avoid appending more.
7434
+     * @return {fabric.Canvas} instance
7435
+     * @chainable
7436
+     */
7437
+    requestRenderAll: function () {
7438
+      if (!this.isRendering) {
7439
+        this.isRendering = fabric.util.requestAnimFrame(this.renderAndResetBound);
7440
+      }
7441
+      return this;
7442
+    },
7443
+
7444
+    /**
7445
+     * Calculate the position of the 4 corner of canvas with current viewportTransform.
7446
+     * helps to determinate when an object is in the current rendering viewport using
7447
+     * object absolute coordinates ( aCoords )
7448
+     * @return {Object} points.tl
7449
+     * @chainable
7450
+     */
7451
+    calcViewportBoundaries: function() {
7452
+      var points = { }, width = this.width, height = this.height,
7453
+          iVpt = invertTransform(this.viewportTransform);
7454
+      points.tl = transformPoint({ x: 0, y: 0 }, iVpt);
7455
+      points.br = transformPoint({ x: width, y: height }, iVpt);
7456
+      points.tr = new fabric.Point(points.br.x, points.tl.y);
7457
+      points.bl = new fabric.Point(points.tl.x, points.br.y);
7458
+      this.vptCoords = points;
7459
+      return points;
7460
+    },
7461
+
7462
+    cancelRequestedRender: function() {
7463
+      if (this.isRendering) {
7464
+        fabric.util.cancelAnimFrame(this.isRendering);
7465
+        this.isRendering = 0;
7466
+      }
7467
+    },
7468
+
7469
+    /**
7470
+     * Renders background, objects, overlay and controls.
7471
+     * @param {CanvasRenderingContext2D} ctx
7472
+     * @param {Array} objects to render
7473
+     * @return {fabric.Canvas} instance
7474
+     * @chainable
7475
+     */
7476
+    renderCanvas: function(ctx, objects) {
7477
+      var v = this.viewportTransform, path = this.clipPath;
7478
+      this.cancelRequestedRender();
7479
+      this.calcViewportBoundaries();
7480
+      this.clearContext(ctx);
7481
+      this.fire('before:render', { ctx: ctx, });
7482
+      if (this.clipTo) {
7483
+        fabric.util.clipContext(this, ctx);
7484
+      }
7485
+      this._renderBackground(ctx);
7486
+
7487
+      ctx.save();
7488
+      //apply viewport transform once for all rendering process
7489
+      ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
7490
+      this._renderObjects(ctx, objects);
7491
+      ctx.restore();
7492
+      if (!this.controlsAboveOverlay && this.interactive) {
7493
+        this.drawControls(ctx);
7494
+      }
7495
+      if (this.clipTo) {
7496
+        ctx.restore();
7497
+      }
7498
+      if (path) {
7499
+        path.canvas = this;
7500
+        // needed to setup a couple of variables
7501
+        path.shouldCache();
7502
+        path._transformDone = true;
7503
+        path.renderCache({ forClipping: true });
7504
+        this.drawClipPathOnCanvas(ctx);
7505
+      }
7506
+      this._renderOverlay(ctx);
7507
+      if (this.controlsAboveOverlay && this.interactive) {
7508
+        this.drawControls(ctx);
7509
+      }
7510
+      this.fire('after:render', { ctx: ctx, });
7511
+    },
7512
+
7513
+    /**
7514
+     * Paint the cached clipPath on the lowerCanvasEl
7515
+     * @param {CanvasRenderingContext2D} ctx Context to render on
7516
+     */
7517
+    drawClipPathOnCanvas: function(ctx) {
7518
+      var v = this.viewportTransform, path = this.clipPath;
7519
+      ctx.save();
7520
+      ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
7521
+      // DEBUG: uncomment this line, comment the following
7522
+      // ctx.globalAlpha = 0.4;
7523
+      ctx.globalCompositeOperation = 'destination-in';
7524
+      path.transform(ctx);
7525
+      ctx.scale(1 / path.zoomX, 1 / path.zoomY);
7526
+      ctx.drawImage(path._cacheCanvas, -path.cacheTranslationX, -path.cacheTranslationY);
7527
+      ctx.restore();
7528
+    },
7529
+
7530
+    /**
7531
+     * @private
7532
+     * @param {CanvasRenderingContext2D} ctx Context to render on
7533
+     * @param {Array} objects to render
7534
+     */
7535
+    _renderObjects: function(ctx, objects) {
7536
+      var i, len;
7537
+      for (i = 0, len = objects.length; i < len; ++i) {
7538
+        objects[i] && objects[i].render(ctx);
7539
+      }
7540
+    },
7541
+
7542
+    /**
7543
+     * @private
7544
+     * @param {CanvasRenderingContext2D} ctx Context to render on
7545
+     * @param {string} property 'background' or 'overlay'
7546
+     */
7547
+    _renderBackgroundOrOverlay: function(ctx, property) {
7548
+      var object = this[property + 'Color'], v;
7549
+      if (object) {
7550
+        ctx.fillStyle = object.toLive
7551
+          ? object.toLive(ctx, this)
7552
+          : object;
7553
+
7554
+        ctx.fillRect(
7555
+          object.offsetX || 0,
7556
+          object.offsetY || 0,
7557
+          this.width,
7558
+          this.height);
7559
+      }
7560
+      object = this[property + 'Image'];
7561
+      if (object) {
7562
+        if (this[property + 'Vpt']) {
7563
+          v = this.viewportTransform;
7564
+          ctx.save();
7565
+          ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
7566
+        }
7567
+        object.render(ctx);
7568
+        this[property + 'Vpt'] && ctx.restore();
7569
+      }
7570
+    },
7571
+
7572
+    /**
7573
+     * @private
7574
+     * @param {CanvasRenderingContext2D} ctx Context to render on
7575
+     */
7576
+    _renderBackground: function(ctx) {
7577
+      this._renderBackgroundOrOverlay(ctx, 'background');
7578
+    },
7579
+
7580
+    /**
7581
+     * @private
7582
+     * @param {CanvasRenderingContext2D} ctx Context to render on
7583
+     */
7584
+    _renderOverlay: function(ctx) {
7585
+      this._renderBackgroundOrOverlay(ctx, 'overlay');
7586
+    },
7587
+
7588
+    /**
7589
+     * Returns coordinates of a center of canvas.
7590
+     * Returned value is an object with top and left properties
7591
+     * @return {Object} object with "top" and "left" number values
7592
+     */
7593
+    getCenter: function () {
7594
+      return {
7595
+        top: this.height / 2,
7596
+        left: this.width / 2
7597
+      };
7598
+    },
7599
+
7600
+    /**
7601
+     * Centers object horizontally in the canvas
7602
+     * @param {fabric.Object} object Object to center horizontally
7603
+     * @return {fabric.Canvas} thisArg
7604
+     */
7605
+    centerObjectH: function (object) {
7606
+      return this._centerObject(object, new fabric.Point(this.getCenter().left, object.getCenterPoint().y));
7607
+    },
7608
+
7609
+    /**
7610
+     * Centers object vertically in the canvas
7611
+     * @param {fabric.Object} object Object to center vertically
7612
+     * @return {fabric.Canvas} thisArg
7613
+     * @chainable
7614
+     */
7615
+    centerObjectV: function (object) {
7616
+      return this._centerObject(object, new fabric.Point(object.getCenterPoint().x, this.getCenter().top));
7617
+    },
7618
+
7619
+    /**
7620
+     * Centers object vertically and horizontally in the canvas
7621
+     * @param {fabric.Object} object Object to center vertically and horizontally
7622
+     * @return {fabric.Canvas} thisArg
7623
+     * @chainable
7624
+     */
7625
+    centerObject: function(object) {
7626
+      var center = this.getCenter();
7627
+
7628
+      return this._centerObject(object, new fabric.Point(center.left, center.top));
7629
+    },
7630
+
7631
+    /**
7632
+     * Centers object vertically and horizontally in the viewport
7633
+     * @param {fabric.Object} object Object to center vertically and horizontally
7634
+     * @return {fabric.Canvas} thisArg
7635
+     * @chainable
7636
+     */
7637
+    viewportCenterObject: function(object) {
7638
+      var vpCenter = this.getVpCenter();
7639
+
7640
+      return this._centerObject(object, vpCenter);
7641
+    },
7642
+
7643
+    /**
7644
+     * Centers object horizontally in the viewport, object.top is unchanged
7645
+     * @param {fabric.Object} object Object to center vertically and horizontally
7646
+     * @return {fabric.Canvas} thisArg
7647
+     * @chainable
7648
+     */
7649
+    viewportCenterObjectH: function(object) {
7650
+      var vpCenter = this.getVpCenter();
7651
+      this._centerObject(object, new fabric.Point(vpCenter.x, object.getCenterPoint().y));
7652
+      return this;
7653
+    },
7654
+
7655
+    /**
7656
+     * Centers object Vertically in the viewport, object.top is unchanged
7657
+     * @param {fabric.Object} object Object to center vertically and horizontally
7658
+     * @return {fabric.Canvas} thisArg
7659
+     * @chainable
7660
+     */
7661
+    viewportCenterObjectV: function(object) {
7662
+      var vpCenter = this.getVpCenter();
7663
+
7664
+      return this._centerObject(object, new fabric.Point(object.getCenterPoint().x, vpCenter.y));
7665
+    },
7666
+
7667
+    /**
7668
+     * Calculate the point in canvas that correspond to the center of actual viewport.
7669
+     * @return {fabric.Point} vpCenter, viewport center
7670
+     * @chainable
7671
+     */
7672
+    getVpCenter: function() {
7673
+      var center = this.getCenter(),
7674
+          iVpt = invertTransform(this.viewportTransform);
7675
+      return transformPoint({ x: center.left, y: center.top }, iVpt);
7676
+    },
7677
+
7678
+    /**
7679
+     * @private
7680
+     * @param {fabric.Object} object Object to center
7681
+     * @param {fabric.Point} center Center point
7682
+     * @return {fabric.Canvas} thisArg
7683
+     * @chainable
7684
+     */
7685
+    _centerObject: function(object, center) {
7686
+      object.setPositionByOrigin(center, 'center', 'center');
7687
+      object.setCoords();
7688
+      this.renderOnAddRemove && this.requestRenderAll();
7689
+      return this;
7690
+    },
7691
+
7692
+    /**
7693
+     * Returs dataless JSON representation of canvas
7694
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
7695
+     * @return {String} json string
7696
+     */
7697
+    toDatalessJSON: function (propertiesToInclude) {
7698
+      return this.toDatalessObject(propertiesToInclude);
7699
+    },
7700
+
7701
+    /**
7702
+     * Returns object representation of canvas
7703
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
7704
+     * @return {Object} object representation of an instance
7705
+     */
7706
+    toObject: function (propertiesToInclude) {
7707
+      return this._toObjectMethod('toObject', propertiesToInclude);
7708
+    },
7709
+
7710
+    /**
7711
+     * Returns dataless object representation of canvas
7712
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
7713
+     * @return {Object} object representation of an instance
7714
+     */
7715
+    toDatalessObject: function (propertiesToInclude) {
7716
+      return this._toObjectMethod('toDatalessObject', propertiesToInclude);
7717
+    },
7718
+
7719
+    /**
7720
+     * @private
7721
+     */
7722
+    _toObjectMethod: function (methodName, propertiesToInclude) {
7723
+
7724
+      var clipPath = this.clipPath, data = {
7725
+        version: fabric.version,
7726
+        objects: this._toObjects(methodName, propertiesToInclude),
7727
+      };
7728
+      if (clipPath) {
7729
+        clipPath = clipPath.toObject(propertiesToInclude);
7730
+      }
7731
+      extend(data, this.__serializeBgOverlay(methodName, propertiesToInclude));
7732
+
7733
+      fabric.util.populateWithProperties(this, data, propertiesToInclude);
7734
+
7735
+      return data;
7736
+    },
7737
+
7738
+    /**
7739
+     * @private
7740
+     */
7741
+    _toObjects: function(methodName, propertiesToInclude) {
7742
+      return this._objects.filter(function(object) {
7743
+        return !object.excludeFromExport;
7744
+      }).map(function(instance) {
7745
+        return this._toObject(instance, methodName, propertiesToInclude);
7746
+      }, this);
7747
+    },
7748
+
7749
+    /**
7750
+     * @private
7751
+     */
7752
+    _toObject: function(instance, methodName, propertiesToInclude) {
7753
+      var originalValue;
7754
+
7755
+      if (!this.includeDefaultValues) {
7756
+        originalValue = instance.includeDefaultValues;
7757
+        instance.includeDefaultValues = false;
7758
+      }
7759
+
7760
+      var object = instance[methodName](propertiesToInclude);
7761
+      if (!this.includeDefaultValues) {
7762
+        instance.includeDefaultValues = originalValue;
7763
+      }
7764
+      return object;
7765
+    },
7766
+
7767
+    /**
7768
+     * @private
7769
+     */
7770
+    __serializeBgOverlay: function(methodName, propertiesToInclude) {
7771
+      var data = { }, bgImage = this.backgroundImage, overlay = this.overlayImage;
7772
+
7773
+      if (this.backgroundColor) {
7774
+        data.background = this.backgroundColor.toObject
7775
+          ? this.backgroundColor.toObject(propertiesToInclude)
7776
+          : this.backgroundColor;
7777
+      }
7778
+
7779
+      if (this.overlayColor) {
7780
+        data.overlay = this.overlayColor.toObject
7781
+          ? this.overlayColor.toObject(propertiesToInclude)
7782
+          : this.overlayColor;
7783
+      }
7784
+      if (bgImage && !bgImage.excludeFromExport) {
7785
+        data.backgroundImage = this._toObject(bgImage, methodName, propertiesToInclude);
7786
+      }
7787
+      if (overlay && !overlay.excludeFromExport) {
7788
+        data.overlayImage = this._toObject(overlay, methodName, propertiesToInclude);
7789
+      }
7790
+
7791
+      return data;
7792
+    },
7793
+
7794
+    /* _TO_SVG_START_ */
7795
+    /**
7796
+     * When true, getSvgTransform() will apply the StaticCanvas.viewportTransform to the SVG transformation. When true,
7797
+     * a zoomed canvas will then produce zoomed SVG output.
7798
+     * @type Boolean
7799
+     * @default
7800
+     */
7801
+    svgViewportTransformation: true,
7802
+
7803
+    /**
7804
+     * Returns SVG representation of canvas
7805
+     * @function
7806
+     * @param {Object} [options] Options object for SVG output
7807
+     * @param {Boolean} [options.suppressPreamble=false] If true xml tag is not included
7808
+     * @param {Object} [options.viewBox] SVG viewbox object
7809
+     * @param {Number} [options.viewBox.x] x-cooridnate of viewbox
7810
+     * @param {Number} [options.viewBox.y] y-coordinate of viewbox
7811
+     * @param {Number} [options.viewBox.width] Width of viewbox
7812
+     * @param {Number} [options.viewBox.height] Height of viewbox
7813
+     * @param {String} [options.encoding=UTF-8] Encoding of SVG output
7814
+     * @param {String} [options.width] desired width of svg with or without units
7815
+     * @param {String} [options.height] desired height of svg with or without units
7816
+     * @param {Function} [reviver] Method for further parsing of svg elements, called after each fabric object converted into svg representation.
7817
+     * @return {String} SVG string
7818
+     * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#serialization}
7819
+     * @see {@link http://jsfiddle.net/fabricjs/jQ3ZZ/|jsFiddle demo}
7820
+     * @example <caption>Normal SVG output</caption>
7821
+     * var svg = canvas.toSVG();
7822
+     * @example <caption>SVG output without preamble (without &lt;?xml ../>)</caption>
7823
+     * var svg = canvas.toSVG({suppressPreamble: true});
7824
+     * @example <caption>SVG output with viewBox attribute</caption>
7825
+     * var svg = canvas.toSVG({
7826
+     *   viewBox: {
7827
+     *     x: 100,
7828
+     *     y: 100,
7829
+     *     width: 200,
7830
+     *     height: 300
7831
+     *   }
7832
+     * });
7833
+     * @example <caption>SVG output with different encoding (default: UTF-8)</caption>
7834
+     * var svg = canvas.toSVG({encoding: 'ISO-8859-1'});
7835
+     * @example <caption>Modify SVG output with reviver function</caption>
7836
+     * var svg = canvas.toSVG(null, function(svg) {
7837
+     *   return svg.replace('stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; ', '');
7838
+     * });
7839
+     */
7840
+    toSVG: function(options, reviver) {
7841
+      options || (options = { });
7842
+      options.reviver = reviver;
7843
+      var markup = [];
7844
+
7845
+      this._setSVGPreamble(markup, options);
7846
+      this._setSVGHeader(markup, options);
7847
+      if (this.clipPath) {
7848
+        markup.push('<g clip-path="url(#' + this.clipPath.clipPathId + ')" >\n');
7849
+      }
7850
+      this._setSVGBgOverlayColor(markup, 'backgroundColor');
7851
+      this._setSVGBgOverlayImage(markup, 'backgroundImage', reviver);
7852
+      this._setSVGObjects(markup, reviver);
7853
+      if (this.clipPath) {
7854
+        markup.push('</g>\n');
7855
+      }
7856
+      this._setSVGBgOverlayColor(markup, 'overlayColor');
7857
+      this._setSVGBgOverlayImage(markup, 'overlayImage', reviver);
7858
+
7859
+      markup.push('</svg>');
7860
+
7861
+      return markup.join('');
7862
+    },
7863
+
7864
+    /**
7865
+     * @private
7866
+     */
7867
+    _setSVGPreamble: function(markup, options) {
7868
+      if (options.suppressPreamble) {
7869
+        return;
7870
+      }
7871
+      markup.push(
7872
+        '<?xml version="1.0" encoding="', (options.encoding || 'UTF-8'), '" standalone="no" ?>\n',
7873
+        '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" ',
7874
+        '"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n'
7875
+      );
7876
+    },
7877
+
7878
+    /**
7879
+     * @private
7880
+     */
7881
+    _setSVGHeader: function(markup, options) {
7882
+      var width = options.width || this.width,
7883
+          height = options.height || this.height,
7884
+          vpt, viewBox = 'viewBox="0 0 ' + this.width + ' ' + this.height + '" ',
7885
+          NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
7886
+
7887
+      if (options.viewBox) {
7888
+        viewBox = 'viewBox="' +
7889
+                options.viewBox.x + ' ' +
7890
+                options.viewBox.y + ' ' +
7891
+                options.viewBox.width + ' ' +
7892
+                options.viewBox.height + '" ';
7893
+      }
7894
+      else {
7895
+        if (this.svgViewportTransformation) {
7896
+          vpt = this.viewportTransform;
7897
+          viewBox = 'viewBox="' +
7898
+                  toFixed(-vpt[4] / vpt[0], NUM_FRACTION_DIGITS) + ' ' +
7899
+                  toFixed(-vpt[5] / vpt[3], NUM_FRACTION_DIGITS) + ' ' +
7900
+                  toFixed(this.width / vpt[0], NUM_FRACTION_DIGITS) + ' ' +
7901
+                  toFixed(this.height / vpt[3], NUM_FRACTION_DIGITS) + '" ';
7902
+        }
7903
+      }
7904
+
7905
+      markup.push(
7906
+        '<svg ',
7907
+        'xmlns="http://www.w3.org/2000/svg" ',
7908
+        'xmlns:xlink="http://www.w3.org/1999/xlink" ',
7909
+        'version="1.1" ',
7910
+        'width="', width, '" ',
7911
+        'height="', height, '" ',
7912
+        viewBox,
7913
+        'xml:space="preserve">\n',
7914
+        '<desc>Created with Fabric.js ', fabric.version, '</desc>\n',
7915
+        '<defs>\n',
7916
+        this.createSVGFontFacesMarkup(),
7917
+        this.createSVGRefElementsMarkup(),
7918
+        this.createSVGClipPathMarkup(options),
7919
+        '</defs>\n'
7920
+      );
7921
+    },
7922
+
7923
+    createSVGClipPathMarkup: function(options) {
7924
+      var clipPath = this.clipPath;
7925
+      if (clipPath) {
7926
+        clipPath.clipPathId = 'CLIPPATH_' + fabric.Object.__uid++;
7927
+        return  '<clipPath id="' + clipPath.clipPathId + '" >\n' +
7928
+          this.clipPath.toClipPathSVG(options.reviver) +
7929
+          '</clipPath>\n';
7930
+      }
7931
+      return '';
7932
+    },
7933
+
7934
+    /**
7935
+     * Creates markup containing SVG referenced elements like patterns, gradients etc.
7936
+     * @return {String}
7937
+     */
7938
+    createSVGRefElementsMarkup: function() {
7939
+      var _this = this,
7940
+          markup = ['backgroundColor', 'overlayColor'].map(function(prop) {
7941
+            var fill = _this[prop];
7942
+            if (fill && fill.toLive) {
7943
+              return fill.toSVG(_this, false);
7944
+            }
7945
+          });
7946
+      return markup.join('');
7947
+    },
7948
+
7949
+    /**
7950
+     * Creates markup containing SVG font faces,
7951
+     * font URLs for font faces must be collected by developers
7952
+     * and are not extracted from the DOM by fabricjs
7953
+     * @param {Array} objects Array of fabric objects
7954
+     * @return {String}
7955
+     */
7956
+    createSVGFontFacesMarkup: function() {
7957
+      var markup = '', fontList = { }, obj, fontFamily,
7958
+          style, row, rowIndex, _char, charIndex, i, len,
7959
+          fontPaths = fabric.fontPaths, objects = this._objects;
7960
+
7961
+      for (i = 0, len = objects.length; i < len; i++) {
7962
+        obj = objects[i];
7963
+        fontFamily = obj.fontFamily;
7964
+        if (obj.type.indexOf('text') === -1 || fontList[fontFamily] || !fontPaths[fontFamily]) {
7965
+          continue;
7966
+        }
7967
+        fontList[fontFamily] = true;
7968
+        if (!obj.styles) {
7969
+          continue;
7970
+        }
7971
+        style = obj.styles;
7972
+        for (rowIndex in style) {
7973
+          row = style[rowIndex];
7974
+          for (charIndex in row) {
7975
+            _char = row[charIndex];
7976
+            fontFamily = _char.fontFamily;
7977
+            if (!fontList[fontFamily] && fontPaths[fontFamily]) {
7978
+              fontList[fontFamily] = true;
7979
+            }
7980
+          }
7981
+        }
7982
+      }
7983
+
7984
+      for (var j in fontList) {
7985
+        markup += [
7986
+          '\t\t@font-face {\n',
7987
+          '\t\t\tfont-family: \'', j, '\';\n',
7988
+          '\t\t\tsrc: url(\'', fontPaths[j], '\');\n',
7989
+          '\t\t}\n'
7990
+        ].join('');
7991
+      }
7992
+
7993
+      if (markup) {
7994
+        markup = [
7995
+          '\t<style type="text/css">',
7996
+          '<![CDATA[\n',
7997
+          markup,
7998
+          ']]>',
7999
+          '</style>\n'
8000
+        ].join('');
8001
+      }
8002
+
8003
+      return markup;
8004
+    },
8005
+
8006
+    /**
8007
+     * @private
8008
+     */
8009
+    _setSVGObjects: function(markup, reviver) {
8010
+      var instance, i, len, objects = this._objects;
8011
+      for (i = 0, len = objects.length; i < len; i++) {
8012
+        instance = objects[i];
8013
+        if (instance.excludeFromExport) {
8014
+          continue;
8015
+        }
8016
+        this._setSVGObject(markup, instance, reviver);
8017
+      }
8018
+    },
8019
+
8020
+    /**
8021
+     * @private
8022
+     */
8023
+    _setSVGObject: function(markup, instance, reviver) {
8024
+      markup.push(instance.toSVG(reviver));
8025
+    },
8026
+
8027
+    /**
8028
+     * @private
8029
+     */
8030
+    _setSVGBgOverlayImage: function(markup, property, reviver) {
8031
+      if (this[property] && !this[property].excludeFromExport && this[property].toSVG) {
8032
+        markup.push(this[property].toSVG(reviver));
8033
+      }
8034
+    },
8035
+
8036
+    /**
8037
+     * @private
8038
+     */
8039
+    _setSVGBgOverlayColor: function(markup, property) {
8040
+      var filler = this[property], vpt = this.viewportTransform, finalWidth = this.width / vpt[0],
8041
+          finalHeight = this.height / vpt[3];
8042
+      if (!filler) {
8043
+        return;
8044
+      }
8045
+      if (filler.toLive) {
8046
+        var repeat = filler.repeat;
8047
+        markup.push(
8048
+          '<rect transform="translate(', finalWidth / 2, ',', finalHeight / 2, ')"',
8049
+          ' x="', filler.offsetX - finalWidth / 2, '" y="', filler.offsetY - finalHeight / 2, '" ',
8050
+          'width="',
8051
+          (repeat === 'repeat-y' || repeat === 'no-repeat'
8052
+            ? filler.source.width
8053
+            : finalWidth ),
8054
+          '" height="',
8055
+          (repeat === 'repeat-x' || repeat === 'no-repeat'
8056
+            ? filler.source.height
8057
+            : finalHeight),
8058
+          '" fill="url(#SVGID_' + filler.id + ')"',
8059
+          '></rect>\n'
8060
+        );
8061
+      }
8062
+      else {
8063
+        markup.push(
8064
+          '<rect x="0" y="0" width="100%" height="100%" ',
8065
+          'fill="', this[property], '"',
8066
+          '></rect>\n'
8067
+        );
8068
+      }
8069
+    },
8070
+    /* _TO_SVG_END_ */
8071
+
8072
+    /**
8073
+     * Moves an object or the objects of a multiple selection
8074
+     * to the bottom of the stack of drawn objects
8075
+     * @param {fabric.Object} object Object to send to back
8076
+     * @return {fabric.Canvas} thisArg
8077
+     * @chainable
8078
+     */
8079
+    sendToBack: function (object) {
8080
+      if (!object) {
8081
+        return this;
8082
+      }
8083
+      var activeSelection = this._activeObject,
8084
+          i, obj, objs;
8085
+      if (object === activeSelection && object.type === 'activeSelection') {
8086
+        objs = activeSelection._objects;
8087
+        for (i = objs.length; i--;) {
8088
+          obj = objs[i];
8089
+          removeFromArray(this._objects, obj);
8090
+          this._objects.unshift(obj);
8091
+        }
8092
+      }
8093
+      else {
8094
+        removeFromArray(this._objects, object);
8095
+        this._objects.unshift(object);
8096
+      }
8097
+      this.renderOnAddRemove && this.requestRenderAll();
8098
+      return this;
8099
+    },
8100
+
8101
+    /**
8102
+     * Moves an object or the objects of a multiple selection
8103
+     * to the top of the stack of drawn objects
8104
+     * @param {fabric.Object} object Object to send
8105
+     * @return {fabric.Canvas} thisArg
8106
+     * @chainable
8107
+     */
8108
+    bringToFront: function (object) {
8109
+      if (!object) {
8110
+        return this;
8111
+      }
8112
+      var activeSelection = this._activeObject,
8113
+          i, obj, objs;
8114
+      if (object === activeSelection && object.type === 'activeSelection') {
8115
+        objs = activeSelection._objects;
8116
+        for (i = 0; i < objs.length; i++) {
8117
+          obj = objs[i];
8118
+          removeFromArray(this._objects, obj);
8119
+          this._objects.push(obj);
8120
+        }
8121
+      }
8122
+      else {
8123
+        removeFromArray(this._objects, object);
8124
+        this._objects.push(object);
8125
+      }
8126
+      this.renderOnAddRemove && this.requestRenderAll();
8127
+      return this;
8128
+    },
8129
+
8130
+    /**
8131
+     * Moves an object or a selection down in stack of drawn objects
8132
+     * An optional paramter, intersecting allowes to move the object in behind
8133
+     * the first intersecting object. Where intersection is calculated with
8134
+     * bounding box. If no intersection is found, there will not be change in the
8135
+     * stack.
8136
+     * @param {fabric.Object} object Object to send
8137
+     * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object
8138
+     * @return {fabric.Canvas} thisArg
8139
+     * @chainable
8140
+     */
8141
+    sendBackwards: function (object, intersecting) {
8142
+      if (!object) {
8143
+        return this;
8144
+      }
8145
+      var activeSelection = this._activeObject,
8146
+          i, obj, idx, newIdx, objs, objsMoved = 0;
8147
+
8148
+      if (object === activeSelection && object.type === 'activeSelection') {
8149
+        objs = activeSelection._objects;
8150
+        for (i = 0; i < objs.length; i++) {
8151
+          obj = objs[i];
8152
+          idx = this._objects.indexOf(obj);
8153
+          if (idx > 0 + objsMoved) {
8154
+            newIdx = idx - 1;
8155
+            removeFromArray(this._objects, obj);
8156
+            this._objects.splice(newIdx, 0, obj);
8157
+          }
8158
+          objsMoved++;
8159
+        }
8160
+      }
8161
+      else {
8162
+        idx = this._objects.indexOf(object);
8163
+        if (idx !== 0) {
8164
+          // if object is not on the bottom of stack
8165
+          newIdx = this._findNewLowerIndex(object, idx, intersecting);
8166
+          removeFromArray(this._objects, object);
8167
+          this._objects.splice(newIdx, 0, object);
8168
+        }
8169
+      }
8170
+      this.renderOnAddRemove && this.requestRenderAll();
8171
+      return this;
8172
+    },
8173
+
8174
+    /**
8175
+     * @private
8176
+     */
8177
+    _findNewLowerIndex: function(object, idx, intersecting) {
8178
+      var newIdx, i;
8179
+
8180
+      if (intersecting) {
8181
+        newIdx = idx;
8182
+
8183
+        // traverse down the stack looking for the nearest intersecting object
8184
+        for (i = idx - 1; i >= 0; --i) {
8185
+
8186
+          var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
8187
+                               object.isContainedWithinObject(this._objects[i]) ||
8188
+                               this._objects[i].isContainedWithinObject(object);
8189
+
8190
+          if (isIntersecting) {
8191
+            newIdx = i;
8192
+            break;
8193
+          }
8194
+        }
8195
+      }
8196
+      else {
8197
+        newIdx = idx - 1;
8198
+      }
8199
+
8200
+      return newIdx;
8201
+    },
8202
+
8203
+    /**
8204
+     * Moves an object or a selection up in stack of drawn objects
8205
+     * An optional paramter, intersecting allowes to move the object in front
8206
+     * of the first intersecting object. Where intersection is calculated with
8207
+     * bounding box. If no intersection is found, there will not be change in the
8208
+     * stack.
8209
+     * @param {fabric.Object} object Object to send
8210
+     * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object
8211
+     * @return {fabric.Canvas} thisArg
8212
+     * @chainable
8213
+     */
8214
+    bringForward: function (object, intersecting) {
8215
+      if (!object) {
8216
+        return this;
8217
+      }
8218
+      var activeSelection = this._activeObject,
8219
+          i, obj, idx, newIdx, objs, objsMoved = 0;
8220
+
8221
+      if (object === activeSelection && object.type === 'activeSelection') {
8222
+        objs = activeSelection._objects;
8223
+        for (i = objs.length; i--;) {
8224
+          obj = objs[i];
8225
+          idx = this._objects.indexOf(obj);
8226
+          if (idx < this._objects.length - 1 - objsMoved) {
8227
+            newIdx = idx + 1;
8228
+            removeFromArray(this._objects, obj);
8229
+            this._objects.splice(newIdx, 0, obj);
8230
+          }
8231
+          objsMoved++;
8232
+        }
8233
+      }
8234
+      else {
8235
+        idx = this._objects.indexOf(object);
8236
+        if (idx !== this._objects.length - 1) {
8237
+          // if object is not on top of stack (last item in an array)
8238
+          newIdx = this._findNewUpperIndex(object, idx, intersecting);
8239
+          removeFromArray(this._objects, object);
8240
+          this._objects.splice(newIdx, 0, object);
8241
+        }
8242
+      }
8243
+      this.renderOnAddRemove && this.requestRenderAll();
8244
+      return this;
8245
+    },
8246
+
8247
+    /**
8248
+     * @private
8249
+     */
8250
+    _findNewUpperIndex: function(object, idx, intersecting) {
8251
+      var newIdx, i, len;
8252
+
8253
+      if (intersecting) {
8254
+        newIdx = idx;
8255
+
8256
+        // traverse up the stack looking for the nearest intersecting object
8257
+        for (i = idx + 1, len = this._objects.length; i < len; ++i) {
8258
+
8259
+          var isIntersecting = object.intersectsWithObject(this._objects[i]) ||
8260
+                               object.isContainedWithinObject(this._objects[i]) ||
8261
+                               this._objects[i].isContainedWithinObject(object);
8262
+
8263
+          if (isIntersecting) {
8264
+            newIdx = i;
8265
+            break;
8266
+          }
8267
+        }
8268
+      }
8269
+      else {
8270
+        newIdx = idx + 1;
8271
+      }
8272
+
8273
+      return newIdx;
8274
+    },
8275
+
8276
+    /**
8277
+     * Moves an object to specified level in stack of drawn objects
8278
+     * @param {fabric.Object} object Object to send
8279
+     * @param {Number} index Position to move to
8280
+     * @return {fabric.Canvas} thisArg
8281
+     * @chainable
8282
+     */
8283
+    moveTo: function (object, index) {
8284
+      removeFromArray(this._objects, object);
8285
+      this._objects.splice(index, 0, object);
8286
+      return this.renderOnAddRemove && this.requestRenderAll();
8287
+    },
8288
+
8289
+    /**
8290
+     * Clears a canvas element and dispose objects
8291
+     * @return {fabric.Canvas} thisArg
8292
+     * @chainable
8293
+     */
8294
+    dispose: function () {
8295
+      // cancel eventually ongoing renders
8296
+      if (this.isRendering) {
8297
+        fabric.util.cancelAnimFrame(this.isRendering);
8298
+        this.isRendering = 0;
8299
+      }
8300
+      this.forEachObject(function(object) {
8301
+        object.dispose && object.dispose();
8302
+      });
8303
+      this._objects = [];
8304
+      if (this.backgroundImage && this.backgroundImage.dispose) {
8305
+        this.backgroundImage.dispose();
8306
+      }
8307
+      this.backgroundImage = null;
8308
+      if (this.overlayImage && this.overlayImage.dispose) {
8309
+        this.overlayImage.dispose();
8310
+      }
8311
+      this.overlayImage = null;
8312
+      this._iTextInstances = null;
8313
+      this.contextContainer = null;
8314
+      fabric.util.cleanUpJsdomNode(this.lowerCanvasEl);
8315
+      this.lowerCanvasEl = undefined;
8316
+      return this;
8317
+    },
8318
+
8319
+    /**
8320
+     * Returns a string representation of an instance
8321
+     * @return {String} string representation of an instance
8322
+     */
8323
+    toString: function () {
8324
+      return '#<fabric.Canvas (' + this.complexity() + '): ' +
8325
+               '{ objects: ' + this._objects.length + ' }>';
8326
+    }
8327
+  });
8328
+
8329
+  extend(fabric.StaticCanvas.prototype, fabric.Observable);
8330
+  extend(fabric.StaticCanvas.prototype, fabric.Collection);
8331
+  extend(fabric.StaticCanvas.prototype, fabric.DataURLExporter);
8332
+
8333
+  extend(fabric.StaticCanvas, /** @lends fabric.StaticCanvas */ {
8334
+
8335
+    /**
8336
+     * @static
8337
+     * @type String
8338
+     * @default
8339
+     */
8340
+    EMPTY_JSON: '{"objects": [], "background": "white"}',
8341
+
8342
+    /**
8343
+     * Provides a way to check support of some of the canvas methods
8344
+     * (either those of HTMLCanvasElement itself, or rendering context)
8345
+     *
8346
+     * @param {String} methodName Method to check support for;
8347
+     *                            Could be one of "getImageData", "toDataURL", "toDataURLWithQuality" or "setLineDash"
8348
+     * @return {Boolean | null} `true` if method is supported (or at least exists),
8349
+     *                          `null` if canvas element or context can not be initialized
8350
+     */
8351
+    supports: function (methodName) {
8352
+      var el = createCanvasElement();
8353
+
8354
+      if (!el || !el.getContext) {
8355
+        return null;
8356
+      }
8357
+
8358
+      var ctx = el.getContext('2d');
8359
+      if (!ctx) {
8360
+        return null;
8361
+      }
8362
+
8363
+      switch (methodName) {
8364
+
8365
+        case 'getImageData':
8366
+          return typeof ctx.getImageData !== 'undefined';
8367
+
8368
+        case 'setLineDash':
8369
+          return typeof ctx.setLineDash !== 'undefined';
8370
+
8371
+        case 'toDataURL':
8372
+          return typeof el.toDataURL !== 'undefined';
8373
+
8374
+        case 'toDataURLWithQuality':
8375
+          try {
8376
+            el.toDataURL('image/jpeg', 0);
8377
+            return true;
8378
+          }
8379
+          catch (e) { }
8380
+          return false;
8381
+
8382
+        default:
8383
+          return null;
8384
+      }
8385
+    }
8386
+  });
8387
+
8388
+  /**
8389
+   * Returns JSON representation of canvas
8390
+   * @function
8391
+   * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
8392
+   * @return {String} JSON string
8393
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#serialization}
8394
+   * @see {@link http://jsfiddle.net/fabricjs/pec86/|jsFiddle demo}
8395
+   * @example <caption>JSON without additional properties</caption>
8396
+   * var json = canvas.toJSON();
8397
+   * @example <caption>JSON with additional properties included</caption>
8398
+   * var json = canvas.toJSON(['lockMovementX', 'lockMovementY', 'lockRotation', 'lockScalingX', 'lockScalingY', 'lockUniScaling']);
8399
+   * @example <caption>JSON without default values</caption>
8400
+   * canvas.includeDefaultValues = false;
8401
+   * var json = canvas.toJSON();
8402
+   */
8403
+  fabric.StaticCanvas.prototype.toJSON = fabric.StaticCanvas.prototype.toObject;
8404
+
8405
+  if (fabric.isLikelyNode) {
8406
+    fabric.StaticCanvas.prototype.createPNGStream = function() {
8407
+      var impl = getNodeCanvas(this.lowerCanvasEl);
8408
+      return impl && impl.createPNGStream();
8409
+    };
8410
+    fabric.StaticCanvas.prototype.createJPEGStream = function(opts) {
8411
+      var impl = getNodeCanvas(this.lowerCanvasEl);
8412
+      return impl && impl.createJPEGStream(opts);
8413
+    };
8414
+  }
8415
+})();
8416
+
8417
+
8418
+/**
8419
+ * BaseBrush class
8420
+ * @class fabric.BaseBrush
8421
+ * @see {@link http://fabricjs.com/freedrawing|Freedrawing demo}
8422
+ */
8423
+fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype */ {
8424
+
8425
+  /**
8426
+   * Color of a brush
8427
+   * @type String
8428
+   * @default
8429
+   */
8430
+  color: 'rgb(0, 0, 0)',
8431
+
8432
+  /**
8433
+   * Width of a brush, has to be a Number, no string literals
8434
+   * @type Number
8435
+   * @default
8436
+   */
8437
+  width: 1,
8438
+
8439
+  /**
8440
+   * Shadow object representing shadow of this shape.
8441
+   * <b>Backwards incompatibility note:</b> This property replaces "shadowColor" (String), "shadowOffsetX" (Number),
8442
+   * "shadowOffsetY" (Number) and "shadowBlur" (Number) since v1.2.12
8443
+   * @type fabric.Shadow
8444
+   * @default
8445
+   */
8446
+  shadow: null,
8447
+
8448
+  /**
8449
+   * Line endings style of a brush (one of "butt", "round", "square")
8450
+   * @type String
8451
+   * @default
8452
+   */
8453
+  strokeLineCap: 'round',
8454
+
8455
+  /**
8456
+   * Corner style of a brush (one of "bevel", "round", "miter")
8457
+   * @type String
8458
+   * @default
8459
+   */
8460
+  strokeLineJoin: 'round',
8461
+
8462
+  /**
8463
+   * Maximum miter length (used for strokeLineJoin = "miter") of a brush's
8464
+   * @type Number
8465
+   * @default
8466
+   */
8467
+  strokeMiterLimit:         10,
8468
+
8469
+  /**
8470
+   * Stroke Dash Array.
8471
+   * @type Array
8472
+   * @default
8473
+   */
8474
+  strokeDashArray: null,
8475
+
8476
+  /**
8477
+   * Sets shadow of an object
8478
+   * @param {Object|String} [options] Options object or string (e.g. "2px 2px 10px rgba(0,0,0,0.2)")
8479
+   * @return {fabric.Object} thisArg
8480
+   * @chainable
8481
+   */
8482
+  setShadow: function(options) {
8483
+    this.shadow = new fabric.Shadow(options);
8484
+    return this;
8485
+  },
8486
+
8487
+  /**
8488
+   * Sets brush styles
8489
+   * @private
8490
+   */
8491
+  _setBrushStyles: function() {
8492
+    var ctx = this.canvas.contextTop;
8493
+    ctx.strokeStyle = this.color;
8494
+    ctx.lineWidth = this.width;
8495
+    ctx.lineCap = this.strokeLineCap;
8496
+    ctx.miterLimit = this.strokeMiterLimit;
8497
+    ctx.lineJoin = this.strokeLineJoin;
8498
+    if (fabric.StaticCanvas.supports('setLineDash')) {
8499
+      ctx.setLineDash(this.strokeDashArray || []);
8500
+    }
8501
+  },
8502
+
8503
+  /**
8504
+   * Sets the transformation on given context
8505
+   * @param {RenderingContext2d} ctx context to render on
8506
+   * @private
8507
+   */
8508
+  _saveAndTransform: function(ctx) {
8509
+    var v = this.canvas.viewportTransform;
8510
+    ctx.save();
8511
+    ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
8512
+  },
8513
+
8514
+  /**
8515
+   * Sets brush shadow styles
8516
+   * @private
8517
+   */
8518
+  _setShadow: function() {
8519
+    if (!this.shadow) {
8520
+      return;
8521
+    }
8522
+
8523
+    var ctx = this.canvas.contextTop,
8524
+        zoom = this.canvas.getZoom();
8525
+
8526
+    ctx.shadowColor = this.shadow.color;
8527
+    ctx.shadowBlur = this.shadow.blur * zoom;
8528
+    ctx.shadowOffsetX = this.shadow.offsetX * zoom;
8529
+    ctx.shadowOffsetY = this.shadow.offsetY * zoom;
8530
+  },
8531
+
8532
+  /**
8533
+   * Removes brush shadow styles
8534
+   * @private
8535
+   */
8536
+  _resetShadow: function() {
8537
+    var ctx = this.canvas.contextTop;
8538
+
8539
+    ctx.shadowColor = '';
8540
+    ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
8541
+  }
8542
+});
8543
+
8544
+
8545
+(function() {
8546
+
8547
+  /**
8548
+   * PencilBrush class
8549
+   * @class fabric.PencilBrush
8550
+   * @extends fabric.BaseBrush
8551
+   */
8552
+  fabric.PencilBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.PencilBrush.prototype */ {
8553
+
8554
+    /**
8555
+     * Constructor
8556
+     * @param {fabric.Canvas} canvas
8557
+     * @return {fabric.PencilBrush} Instance of a pencil brush
8558
+     */
8559
+    initialize: function(canvas) {
8560
+      this.canvas = canvas;
8561
+      this._points = [];
8562
+    },
8563
+
8564
+    /**
8565
+     * Invoked inside on mouse down and mouse move
8566
+     * @param {Object} pointer
8567
+     */
8568
+    _drawSegment: function (ctx, p1, p2) {
8569
+      var midPoint = p1.midPointFrom(p2);
8570
+      ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
8571
+      return midPoint;
8572
+    },
8573
+
8574
+    /**
8575
+     * Inovoked on mouse down
8576
+     * @param {Object} pointer
8577
+     */
8578
+    onMouseDown: function(pointer) {
8579
+      this._prepareForDrawing(pointer);
8580
+      // capture coordinates immediately
8581
+      // this allows to draw dots (when movement never occurs)
8582
+      this._captureDrawingPath(pointer);
8583
+      this._render();
8584
+    },
8585
+
8586
+    /**
8587
+     * Inovoked on mouse move
8588
+     * @param {Object} pointer
8589
+     */
8590
+    onMouseMove: function(pointer) {
8591
+      if (this._captureDrawingPath(pointer) && this._points.length > 1) {
8592
+        if (this.needsFullRender) {
8593
+          // redraw curve
8594
+          // clear top canvas
8595
+          this.canvas.clearContext(this.canvas.contextTop);
8596
+          this._render();
8597
+        }
8598
+        else {
8599
+          var points = this._points, length = points.length, ctx = this.canvas.contextTop;
8600
+          // draw the curve update
8601
+          this._saveAndTransform(ctx);
8602
+          if (this.oldEnd) {
8603
+            ctx.beginPath();
8604
+            ctx.moveTo(this.oldEnd.x, this.oldEnd.y);
8605
+          }
8606
+          this.oldEnd = this._drawSegment(ctx, points[length - 2], points[length - 1], true);
8607
+          ctx.stroke();
8608
+          ctx.restore();
8609
+        }
8610
+      }
8611
+    },
8612
+
8613
+    /**
8614
+     * Invoked on mouse up
8615
+     */
8616
+    onMouseUp: function() {
8617
+      this.oldEnd = undefined;
8618
+      this._finalizeAndAddPath();
8619
+    },
8620
+
8621
+    /**
8622
+     * @private
8623
+     * @param {Object} pointer Actual mouse position related to the canvas.
8624
+     */
8625
+    _prepareForDrawing: function(pointer) {
8626
+
8627
+      var p = new fabric.Point(pointer.x, pointer.y);
8628
+
8629
+      this._reset();
8630
+      this._addPoint(p);
8631
+      this.canvas.contextTop.moveTo(p.x, p.y);
8632
+    },
8633
+
8634
+    /**
8635
+     * @private
8636
+     * @param {fabric.Point} point Point to be added to points array
8637
+     */
8638
+    _addPoint: function(point) {
8639
+      if (this._points.length > 1 && point.eq(this._points[this._points.length - 1])) {
8640
+        return false;
8641
+      }
8642
+      this._points.push(point);
8643
+      return true;
8644
+    },
8645
+
8646
+    /**
8647
+     * Clear points array and set contextTop canvas style.
8648
+     * @private
8649
+     */
8650
+    _reset: function() {
8651
+      this._points.length = 0;
8652
+      this._setBrushStyles();
8653
+      var color = new fabric.Color(this.color);
8654
+      this.needsFullRender = (color.getAlpha() < 1);
8655
+      this._setShadow();
8656
+    },
8657
+
8658
+    /**
8659
+     * @private
8660
+     * @param {Object} pointer Actual mouse position related to the canvas.
8661
+     */
8662
+    _captureDrawingPath: function(pointer) {
8663
+      var pointerPoint = new fabric.Point(pointer.x, pointer.y);
8664
+      return this._addPoint(pointerPoint);
8665
+    },
8666
+
8667
+    /**
8668
+     * Draw a smooth path on the topCanvas using quadraticCurveTo
8669
+     * @private
8670
+     */
8671
+    _render: function() {
8672
+      var ctx  = this.canvas.contextTop, i, len,
8673
+          p1 = this._points[0],
8674
+          p2 = this._points[1];
8675
+
8676
+      this._saveAndTransform(ctx);
8677
+      ctx.beginPath();
8678
+      //if we only have 2 points in the path and they are the same
8679
+      //it means that the user only clicked the canvas without moving the mouse
8680
+      //then we should be drawing a dot. A path isn't drawn between two identical dots
8681
+      //that's why we set them apart a bit
8682
+      if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) {
8683
+        var width = this.width / 1000;
8684
+        p1 = new fabric.Point(p1.x, p1.y);
8685
+        p2 = new fabric.Point(p2.x, p2.y);
8686
+        p1.x -= width;
8687
+        p2.x += width;
8688
+      }
8689
+      ctx.moveTo(p1.x, p1.y);
8690
+
8691
+      for (i = 1, len = this._points.length; i < len; i++) {
8692
+        // we pick the point between pi + 1 & pi + 2 as the
8693
+        // end point and p1 as our control point.
8694
+        this._drawSegment(ctx, p1, p2);
8695
+        p1 = this._points[i];
8696
+        p2 = this._points[i + 1];
8697
+      }
8698
+      // Draw last line as a straight line while
8699
+      // we wait for the next point to be able to calculate
8700
+      // the bezier control point
8701
+      ctx.lineTo(p1.x, p1.y);
8702
+      ctx.stroke();
8703
+      ctx.restore();
8704
+    },
8705
+
8706
+    /**
8707
+     * Converts points to SVG path
8708
+     * @param {Array} points Array of points
8709
+     * @return {String} SVG path
8710
+     */
8711
+    convertPointsToSVGPath: function(points) {
8712
+      var path = [], i, width = this.width / 1000,
8713
+          p1 = new fabric.Point(points[0].x, points[0].y),
8714
+          p2 = new fabric.Point(points[1].x, points[1].y),
8715
+          len = points.length, multSignX = 1, multSignY = 1, manyPoints = len > 2;
8716
+
8717
+      if (manyPoints) {
8718
+        multSignX = points[2].x < p2.x ? -1 : points[2].x === p2.x ? 0 : 1;
8719
+        multSignY = points[2].y < p2.y ? -1 : points[2].y === p2.y ? 0 : 1;
8720
+      }
8721
+      path.push('M ', p1.x - multSignX * width, ' ', p1.y - multSignY * width, ' ');
8722
+      for (i = 1; i < len; i++) {
8723
+        if (!p1.eq(p2)) {
8724
+          var midPoint = p1.midPointFrom(p2);
8725
+          // p1 is our bezier control point
8726
+          // midpoint is our endpoint
8727
+          // start point is p(i-1) value.
8728
+          path.push('Q ', p1.x, ' ', p1.y, ' ', midPoint.x, ' ', midPoint.y, ' ');
8729
+        }
8730
+        p1 = points[i];
8731
+        if ((i + 1) < points.length) {
8732
+          p2 = points[i + 1];
8733
+        }
8734
+      }
8735
+      if (manyPoints) {
8736
+        multSignX = p1.x > points[i - 2].x ? 1 : p1.x === points[i - 2].x ? 0 : -1;
8737
+        multSignY = p1.y > points[i - 2].y ? 1 : p1.y === points[i - 2].y ? 0 : -1;
8738
+      }
8739
+      path.push('L ', p1.x + multSignX * width, ' ', p1.y + multSignY * width);
8740
+      return path;
8741
+    },
8742
+
8743
+    /**
8744
+     * Creates fabric.Path object to add on canvas
8745
+     * @param {String} pathData Path data
8746
+     * @return {fabric.Path} Path to add on canvas
8747
+     */
8748
+    createPath: function(pathData) {
8749
+      var path = new fabric.Path(pathData, {
8750
+        fill: null,
8751
+        stroke: this.color,
8752
+        strokeWidth: this.width,
8753
+        strokeLineCap: this.strokeLineCap,
8754
+        strokeMiterLimit: this.strokeMiterLimit,
8755
+        strokeLineJoin: this.strokeLineJoin,
8756
+        strokeDashArray: this.strokeDashArray,
8757
+      });
8758
+      var position = new fabric.Point(path.left + path.width / 2, path.top + path.height / 2);
8759
+      position = path.translateToGivenOrigin(position, 'center', 'center', path.originX, path.originY);
8760
+      path.top = position.y;
8761
+      path.left = position.x;
8762
+      if (this.shadow) {
8763
+        this.shadow.affectStroke = true;
8764
+        path.setShadow(this.shadow);
8765
+      }
8766
+
8767
+      return path;
8768
+    },
8769
+
8770
+    /**
8771
+     * On mouseup after drawing the path on contextTop canvas
8772
+     * we use the points captured to create an new fabric path object
8773
+     * and add it to the fabric canvas.
8774
+     */
8775
+    _finalizeAndAddPath: function() {
8776
+      var ctx = this.canvas.contextTop;
8777
+      ctx.closePath();
8778
+
8779
+      var pathData = this.convertPointsToSVGPath(this._points).join('');
8780
+      if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') {
8781
+        // do not create 0 width/height paths, as they are
8782
+        // rendered inconsistently across browsers
8783
+        // Firefox 4, for example, renders a dot,
8784
+        // whereas Chrome 10 renders nothing
8785
+        this.canvas.requestRenderAll();
8786
+        return;
8787
+      }
8788
+
8789
+      var path = this.createPath(pathData);
8790
+      this.canvas.clearContext(this.canvas.contextTop);
8791
+      this.canvas.add(path);
8792
+      this.canvas.renderAll();
8793
+      path.setCoords();
8794
+      this._resetShadow();
8795
+
8796
+
8797
+      // fire event 'path' created
8798
+      this.canvas.fire('path:created', { path: path });
8799
+    }
8800
+  });
8801
+})();
8802
+
8803
+
8804
+/**
8805
+ * CircleBrush class
8806
+ * @class fabric.CircleBrush
8807
+ */
8808
+fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.CircleBrush.prototype */ {
8809
+
8810
+  /**
8811
+   * Width of a brush
8812
+   * @type Number
8813
+   * @default
8814
+   */
8815
+  width: 10,
8816
+
8817
+  /**
8818
+   * Constructor
8819
+   * @param {fabric.Canvas} canvas
8820
+   * @return {fabric.CircleBrush} Instance of a circle brush
8821
+   */
8822
+  initialize: function(canvas) {
8823
+    this.canvas = canvas;
8824
+    this.points = [];
8825
+  },
8826
+
8827
+  /**
8828
+   * Invoked inside on mouse down and mouse move
8829
+   * @param {Object} pointer
8830
+   */
8831
+  drawDot: function(pointer) {
8832
+    var point = this.addPoint(pointer),
8833
+        ctx = this.canvas.contextTop;
8834
+    this._saveAndTransform(ctx);
8835
+    ctx.fillStyle = point.fill;
8836
+    ctx.beginPath();
8837
+    ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false);
8838
+    ctx.closePath();
8839
+    ctx.fill();
8840
+
8841
+    ctx.restore();
8842
+  },
8843
+
8844
+  /**
8845
+   * Invoked on mouse down
8846
+   */
8847
+  onMouseDown: function(pointer) {
8848
+    this.points.length = 0;
8849
+    this.canvas.clearContext(this.canvas.contextTop);
8850
+    this._setShadow();
8851
+    this.drawDot(pointer);
8852
+  },
8853
+
8854
+  /**
8855
+   * Render the full state of the brush
8856
+   * @private
8857
+   */
8858
+  _render: function() {
8859
+    var ctx  = this.canvas.contextTop, i, len,
8860
+        points = this.points, point;
8861
+    this._saveAndTransform(ctx);
8862
+    for (i = 0, len = points.length; i < len; i++) {
8863
+      point = points[i];
8864
+      ctx.fillStyle = point.fill;
8865
+      ctx.beginPath();
8866
+      ctx.arc(point.x, point.y, point.radius, 0, Math.PI * 2, false);
8867
+      ctx.closePath();
8868
+      ctx.fill();
8869
+    }
8870
+    ctx.restore();
8871
+  },
8872
+
8873
+  /**
8874
+   * Invoked on mouse move
8875
+   * @param {Object} pointer
8876
+   */
8877
+  onMouseMove: function(pointer) {
8878
+    this.drawDot(pointer);
8879
+  },
8880
+
8881
+  /**
8882
+   * Invoked on mouse up
8883
+   */
8884
+  onMouseUp: function() {
8885
+    var originalRenderOnAddRemove = this.canvas.renderOnAddRemove, i, len;
8886
+    this.canvas.renderOnAddRemove = false;
8887
+
8888
+    var circles = [];
8889
+
8890
+    for (i = 0, len = this.points.length; i < len; i++) {
8891
+      var point = this.points[i],
8892
+          circle = new fabric.Circle({
8893
+            radius: point.radius,
8894
+            left: point.x,
8895
+            top: point.y,
8896
+            originX: 'center',
8897
+            originY: 'center',
8898
+            fill: point.fill
8899
+          });
8900
+
8901
+      this.shadow && circle.setShadow(this.shadow);
8902
+
8903
+      circles.push(circle);
8904
+    }
8905
+    var group = new fabric.Group(circles);
8906
+    group.canvas = this.canvas;
8907
+
8908
+    this.canvas.add(group);
8909
+    this.canvas.fire('path:created', { path: group });
8910
+
8911
+    this.canvas.clearContext(this.canvas.contextTop);
8912
+    this._resetShadow();
8913
+    this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
8914
+    this.canvas.requestRenderAll();
8915
+  },
8916
+
8917
+  /**
8918
+   * @param {Object} pointer
8919
+   * @return {fabric.Point} Just added pointer point
8920
+   */
8921
+  addPoint: function(pointer) {
8922
+    var pointerPoint = new fabric.Point(pointer.x, pointer.y),
8923
+
8924
+        circleRadius = fabric.util.getRandomInt(
8925
+          Math.max(0, this.width - 20), this.width + 20) / 2,
8926
+
8927
+        circleColor = new fabric.Color(this.color)
8928
+          .setAlpha(fabric.util.getRandomInt(0, 100) / 100)
8929
+          .toRgba();
8930
+
8931
+    pointerPoint.radius = circleRadius;
8932
+    pointerPoint.fill = circleColor;
8933
+
8934
+    this.points.push(pointerPoint);
8935
+
8936
+    return pointerPoint;
8937
+  }
8938
+});
8939
+
8940
+
8941
+/**
8942
+ * SprayBrush class
8943
+ * @class fabric.SprayBrush
8944
+ */
8945
+fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric.SprayBrush.prototype */ {
8946
+
8947
+  /**
8948
+   * Width of a spray
8949
+   * @type Number
8950
+   * @default
8951
+   */
8952
+  width:              10,
8953
+
8954
+  /**
8955
+   * Density of a spray (number of dots per chunk)
8956
+   * @type Number
8957
+   * @default
8958
+   */
8959
+  density:            20,
8960
+
8961
+  /**
8962
+   * Width of spray dots
8963
+   * @type Number
8964
+   * @default
8965
+   */
8966
+  dotWidth:           1,
8967
+
8968
+  /**
8969
+   * Width variance of spray dots
8970
+   * @type Number
8971
+   * @default
8972
+   */
8973
+  dotWidthVariance:   1,
8974
+
8975
+  /**
8976
+   * Whether opacity of a dot should be random
8977
+   * @type Boolean
8978
+   * @default
8979
+   */
8980
+  randomOpacity:        false,
8981
+
8982
+  /**
8983
+   * Whether overlapping dots (rectangles) should be removed (for performance reasons)
8984
+   * @type Boolean
8985
+   * @default
8986
+   */
8987
+  optimizeOverlapping:  true,
8988
+
8989
+  /**
8990
+   * Constructor
8991
+   * @param {fabric.Canvas} canvas
8992
+   * @return {fabric.SprayBrush} Instance of a spray brush
8993
+   */
8994
+  initialize: function(canvas) {
8995
+    this.canvas = canvas;
8996
+    this.sprayChunks = [];
8997
+  },
8998
+
8999
+  /**
9000
+   * Invoked on mouse down
9001
+   * @param {Object} pointer
9002
+   */
9003
+  onMouseDown: function(pointer) {
9004
+    this.sprayChunks.length = 0;
9005
+    this.canvas.clearContext(this.canvas.contextTop);
9006
+    this._setShadow();
9007
+
9008
+    this.addSprayChunk(pointer);
9009
+    this.render(this.sprayChunkPoints);
9010
+  },
9011
+
9012
+  /**
9013
+   * Invoked on mouse move
9014
+   * @param {Object} pointer
9015
+   */
9016
+  onMouseMove: function(pointer) {
9017
+    this.addSprayChunk(pointer);
9018
+    this.render(this.sprayChunkPoints);
9019
+  },
9020
+
9021
+  /**
9022
+   * Invoked on mouse up
9023
+   */
9024
+  onMouseUp: function() {
9025
+    var originalRenderOnAddRemove = this.canvas.renderOnAddRemove;
9026
+    this.canvas.renderOnAddRemove = false;
9027
+
9028
+    var rects = [];
9029
+
9030
+    for (var i = 0, ilen = this.sprayChunks.length; i < ilen; i++) {
9031
+      var sprayChunk = this.sprayChunks[i];
9032
+
9033
+      for (var j = 0, jlen = sprayChunk.length; j < jlen; j++) {
9034
+
9035
+        var rect = new fabric.Rect({
9036
+          width: sprayChunk[j].width,
9037
+          height: sprayChunk[j].width,
9038
+          left: sprayChunk[j].x + 1,
9039
+          top: sprayChunk[j].y + 1,
9040
+          originX: 'center',
9041
+          originY: 'center',
9042
+          fill: this.color
9043
+        });
9044
+        rects.push(rect);
9045
+      }
9046
+    }
9047
+
9048
+    if (this.optimizeOverlapping) {
9049
+      rects = this._getOptimizedRects(rects);
9050
+    }
9051
+
9052
+    var group = new fabric.Group(rects);
9053
+    this.shadow && group.setShadow(this.shadow);
9054
+    this.canvas.add(group);
9055
+    this.canvas.fire('path:created', { path: group });
9056
+
9057
+    this.canvas.clearContext(this.canvas.contextTop);
9058
+    this._resetShadow();
9059
+    this.canvas.renderOnAddRemove = originalRenderOnAddRemove;
9060
+    this.canvas.requestRenderAll();
9061
+  },
9062
+
9063
+  /**
9064
+   * @private
9065
+   * @param {Array} rects
9066
+   */
9067
+  _getOptimizedRects: function(rects) {
9068
+
9069
+    // avoid creating duplicate rects at the same coordinates
9070
+    var uniqueRects = { }, key, i, len;
9071
+
9072
+    for (i = 0, len = rects.length; i < len; i++) {
9073
+      key = rects[i].left + '' + rects[i].top;
9074
+      if (!uniqueRects[key]) {
9075
+        uniqueRects[key] = rects[i];
9076
+      }
9077
+    }
9078
+    var uniqueRectsArray = [];
9079
+    for (key in uniqueRects) {
9080
+      uniqueRectsArray.push(uniqueRects[key]);
9081
+    }
9082
+
9083
+    return uniqueRectsArray;
9084
+  },
9085
+
9086
+  /**
9087
+   * Render new chunk of spray brush
9088
+   */
9089
+  render: function(sprayChunk) {
9090
+    var ctx = this.canvas.contextTop, i, len;
9091
+    ctx.fillStyle = this.color;
9092
+
9093
+    this._saveAndTransform(ctx);
9094
+
9095
+    for (i = 0, len = sprayChunk.length; i < len; i++) {
9096
+      var point = sprayChunk[i];
9097
+      if (typeof point.opacity !== 'undefined') {
9098
+        ctx.globalAlpha = point.opacity;
9099
+      }
9100
+      ctx.fillRect(point.x, point.y, point.width, point.width);
9101
+    }
9102
+    ctx.restore();
9103
+  },
9104
+
9105
+  /**
9106
+   * Render all spray chunks
9107
+   */
9108
+  _render: function() {
9109
+    var ctx = this.canvas.contextTop, i, ilen;
9110
+    ctx.fillStyle = this.color;
9111
+
9112
+    this._saveAndTransform(ctx);
9113
+
9114
+    for (i = 0, ilen = this.sprayChunks.length; i < ilen; i++) {
9115
+      this.render(this.sprayChunks[i]);
9116
+    }
9117
+    ctx.restore();
9118
+  },
9119
+
9120
+  /**
9121
+   * @param {Object} pointer
9122
+   */
9123
+  addSprayChunk: function(pointer) {
9124
+    this.sprayChunkPoints = [];
9125
+
9126
+    var x, y, width, radius = this.width / 2, i;
9127
+
9128
+    for (i = 0; i < this.density; i++) {
9129
+
9130
+      x = fabric.util.getRandomInt(pointer.x - radius, pointer.x + radius);
9131
+      y = fabric.util.getRandomInt(pointer.y - radius, pointer.y + radius);
9132
+
9133
+      if (this.dotWidthVariance) {
9134
+        width = fabric.util.getRandomInt(
9135
+          // bottom clamp width to 1
9136
+          Math.max(1, this.dotWidth - this.dotWidthVariance),
9137
+          this.dotWidth + this.dotWidthVariance);
9138
+      }
9139
+      else {
9140
+        width = this.dotWidth;
9141
+      }
9142
+
9143
+      var point = new fabric.Point(x, y);
9144
+      point.width = width;
9145
+
9146
+      if (this.randomOpacity) {
9147
+        point.opacity = fabric.util.getRandomInt(0, 100) / 100;
9148
+      }
9149
+
9150
+      this.sprayChunkPoints.push(point);
9151
+    }
9152
+
9153
+    this.sprayChunks.push(this.sprayChunkPoints);
9154
+  }
9155
+});
9156
+
9157
+
9158
+/**
9159
+ * PatternBrush class
9160
+ * @class fabric.PatternBrush
9161
+ * @extends fabric.BaseBrush
9162
+ */
9163
+fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fabric.PatternBrush.prototype */ {
9164
+
9165
+  getPatternSrc: function() {
9166
+
9167
+    var dotWidth = 20,
9168
+        dotDistance = 5,
9169
+        patternCanvas = fabric.util.createCanvasElement(),
9170
+        patternCtx = patternCanvas.getContext('2d');
9171
+
9172
+    patternCanvas.width = patternCanvas.height = dotWidth + dotDistance;
9173
+
9174
+    patternCtx.fillStyle = this.color;
9175
+    patternCtx.beginPath();
9176
+    patternCtx.arc(dotWidth / 2, dotWidth / 2, dotWidth / 2, 0, Math.PI * 2, false);
9177
+    patternCtx.closePath();
9178
+    patternCtx.fill();
9179
+
9180
+    return patternCanvas;
9181
+  },
9182
+
9183
+  getPatternSrcFunction: function() {
9184
+    return String(this.getPatternSrc).replace('this.color', '"' + this.color + '"');
9185
+  },
9186
+
9187
+  /**
9188
+   * Creates "pattern" instance property
9189
+   */
9190
+  getPattern: function() {
9191
+    return this.canvas.contextTop.createPattern(this.source || this.getPatternSrc(), 'repeat');
9192
+  },
9193
+
9194
+  /**
9195
+   * Sets brush styles
9196
+   */
9197
+  _setBrushStyles: function() {
9198
+    this.callSuper('_setBrushStyles');
9199
+    this.canvas.contextTop.strokeStyle = this.getPattern();
9200
+  },
9201
+
9202
+  /**
9203
+   * Creates path
9204
+   */
9205
+  createPath: function(pathData) {
9206
+    var path = this.callSuper('createPath', pathData),
9207
+        topLeft = path._getLeftTopCoords().scalarAdd(path.strokeWidth / 2);
9208
+
9209
+    path.stroke = new fabric.Pattern({
9210
+      source: this.source || this.getPatternSrcFunction(),
9211
+      offsetX: -topLeft.x,
9212
+      offsetY: -topLeft.y
9213
+    });
9214
+    return path;
9215
+  }
9216
+});
9217
+
9218
+
9219
+(function() {
9220
+
9221
+  var getPointer = fabric.util.getPointer,
9222
+      degreesToRadians = fabric.util.degreesToRadians,
9223
+      radiansToDegrees = fabric.util.radiansToDegrees,
9224
+      atan2 = Math.atan2,
9225
+      abs = Math.abs,
9226
+      supportLineDash = fabric.StaticCanvas.supports('setLineDash'),
9227
+
9228
+      STROKE_OFFSET = 0.5;
9229
+
9230
+  /**
9231
+   * Canvas class
9232
+   * @class fabric.Canvas
9233
+   * @extends fabric.StaticCanvas
9234
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-1#canvas}
9235
+   * @see {@link fabric.Canvas#initialize} for constructor definition
9236
+   *
9237
+   * @fires object:modified
9238
+   * @fires object:rotated
9239
+   * @fires object:scaled
9240
+   * @fires object:moved
9241
+   * @fires object:skewed
9242
+   * @fires object:rotating
9243
+   * @fires object:scaling
9244
+   * @fires object:moving
9245
+   * @fires object:skewing
9246
+   * @fires object:selected this event is deprecated. use selection:created
9247
+   *
9248
+   * @fires before:transform
9249
+   * @fires before:selection:cleared
9250
+   * @fires selection:cleared
9251
+   * @fires selection:updated
9252
+   * @fires selection:created
9253
+   *
9254
+   * @fires path:created
9255
+   * @fires mouse:down
9256
+   * @fires mouse:move
9257
+   * @fires mouse:up
9258
+   * @fires mouse:down:before
9259
+   * @fires mouse:move:before
9260
+   * @fires mouse:up:before
9261
+   * @fires mouse:over
9262
+   * @fires mouse:out
9263
+   * @fires mouse:dblclick
9264
+   *
9265
+   * @fires dragover
9266
+   * @fires dragenter
9267
+   * @fires dragleave
9268
+   * @fires drop
9269
+   *
9270
+   */
9271
+  fabric.Canvas = fabric.util.createClass(fabric.StaticCanvas, /** @lends fabric.Canvas.prototype */ {
9272
+
9273
+    /**
9274
+     * Constructor
9275
+     * @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
9276
+     * @param {Object} [options] Options object
9277
+     * @return {Object} thisArg
9278
+     */
9279
+    initialize: function(el, options) {
9280
+      options || (options = { });
9281
+      this.renderAndResetBound = this.renderAndReset.bind(this);
9282
+      this.requestRenderAllBound = this.requestRenderAll.bind(this);
9283
+      this._initStatic(el, options);
9284
+      this._initInteractive();
9285
+      this._createCacheCanvas();
9286
+    },
9287
+
9288
+    /**
9289
+     * When true, objects can be transformed by one side (unproportionally)
9290
+     * @type Boolean
9291
+     * @default
9292
+     */
9293
+    uniScaleTransform:      false,
9294
+
9295
+    /**
9296
+     * Indicates which key enable unproportional scaling
9297
+     * values: 'altKey', 'shiftKey', 'ctrlKey'.
9298
+     * If `null` or 'none' or any other string that is not a modifier key
9299
+     * feature is disabled feature disabled.
9300
+     * @since 1.6.2
9301
+     * @type String
9302
+     * @default
9303
+     */
9304
+    uniScaleKey:           'shiftKey',
9305
+
9306
+    /**
9307
+     * When true, objects use center point as the origin of scale transformation.
9308
+     * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
9309
+     * @since 1.3.4
9310
+     * @type Boolean
9311
+     * @default
9312
+     */
9313
+    centeredScaling:        false,
9314
+
9315
+    /**
9316
+     * When true, objects use center point as the origin of rotate transformation.
9317
+     * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
9318
+     * @since 1.3.4
9319
+     * @type Boolean
9320
+     * @default
9321
+     */
9322
+    centeredRotation:       false,
9323
+
9324
+    /**
9325
+     * Indicates which key enable centered Transform
9326
+     * values: 'altKey', 'shiftKey', 'ctrlKey'.
9327
+     * If `null` or 'none' or any other string that is not a modifier key
9328
+     * feature is disabled feature disabled.
9329
+     * @since 1.6.2
9330
+     * @type String
9331
+     * @default
9332
+     */
9333
+    centeredKey:           'altKey',
9334
+
9335
+    /**
9336
+     * Indicates which key enable alternate action on corner
9337
+     * values: 'altKey', 'shiftKey', 'ctrlKey'.
9338
+     * If `null` or 'none' or any other string that is not a modifier key
9339
+     * feature is disabled feature disabled.
9340
+     * @since 1.6.2
9341
+     * @type String
9342
+     * @default
9343
+     */
9344
+    altActionKey:           'shiftKey',
9345
+
9346
+    /**
9347
+     * Indicates that canvas is interactive. This property should not be changed.
9348
+     * @type Boolean
9349
+     * @default
9350
+     */
9351
+    interactive:            true,
9352
+
9353
+    /**
9354
+     * Indicates whether group selection should be enabled
9355
+     * @type Boolean
9356
+     * @default
9357
+     */
9358
+    selection:              true,
9359
+
9360
+    /**
9361
+     * Indicates which key or keys enable multiple click selection
9362
+     * Pass value as a string or array of strings
9363
+     * values: 'altKey', 'shiftKey', 'ctrlKey'.
9364
+     * If `null` or empty or containing any other string that is not a modifier key
9365
+     * feature is disabled.
9366
+     * @since 1.6.2
9367
+     * @type String|Array
9368
+     * @default
9369
+     */
9370
+    selectionKey:           'shiftKey',
9371
+
9372
+    /**
9373
+     * Indicates which key enable alternative selection
9374
+     * in case of target overlapping with active object
9375
+     * values: 'altKey', 'shiftKey', 'ctrlKey'.
9376
+     * For a series of reason that come from the general expectations on how
9377
+     * things should work, this feature works only for preserveObjectStacking true.
9378
+     * If `null` or 'none' or any other string that is not a modifier key
9379
+     * feature is disabled.
9380
+     * @since 1.6.5
9381
+     * @type null|String
9382
+     * @default
9383
+     */
9384
+    altSelectionKey:           null,
9385
+
9386
+    /**
9387
+     * Color of selection
9388
+     * @type String
9389
+     * @default
9390
+     */
9391
+    selectionColor:         'rgba(100, 100, 255, 0.3)', // blue
9392
+
9393
+    /**
9394
+     * Default dash array pattern
9395
+     * If not empty the selection border is dashed
9396
+     * @type Array
9397
+     */
9398
+    selectionDashArray:     [],
9399
+
9400
+    /**
9401
+     * Color of the border of selection (usually slightly darker than color of selection itself)
9402
+     * @type String
9403
+     * @default
9404
+     */
9405
+    selectionBorderColor:   'rgba(255, 255, 255, 0.3)',
9406
+
9407
+    /**
9408
+     * Width of a line used in object/group selection
9409
+     * @type Number
9410
+     * @default
9411
+     */
9412
+    selectionLineWidth:     1,
9413
+
9414
+    /**
9415
+     * Select only shapes that are fully contained in the dragged selection rectangle.
9416
+     * @type Boolean
9417
+     * @default
9418
+     */
9419
+    selectionFullyContained: false,
9420
+
9421
+    /**
9422
+     * Default cursor value used when hovering over an object on canvas
9423
+     * @type String
9424
+     * @default
9425
+     */
9426
+    hoverCursor:            'move',
9427
+
9428
+    /**
9429
+     * Default cursor value used when moving an object on canvas
9430
+     * @type String
9431
+     * @default
9432
+     */
9433
+    moveCursor:             'move',
9434
+
9435
+    /**
9436
+     * Default cursor value used for the entire canvas
9437
+     * @type String
9438
+     * @default
9439
+     */
9440
+    defaultCursor:          'default',
9441
+
9442
+    /**
9443
+     * Cursor value used during free drawing
9444
+     * @type String
9445
+     * @default
9446
+     */
9447
+    freeDrawingCursor:      'crosshair',
9448
+
9449
+    /**
9450
+     * Cursor value used for rotation point
9451
+     * @type String
9452
+     * @default
9453
+     */
9454
+    rotationCursor:         'crosshair',
9455
+
9456
+    /**
9457
+     * Cursor value used for disabled elements ( corners with disabled action )
9458
+     * @type String
9459
+     * @since 2.0.0
9460
+     * @default
9461
+     */
9462
+    notAllowedCursor:         'not-allowed',
9463
+
9464
+    /**
9465
+     * Default element class that's given to wrapper (div) element of canvas
9466
+     * @type String
9467
+     * @default
9468
+     */
9469
+    containerClass:         'canvas-container',
9470
+
9471
+    /**
9472
+     * When true, object detection happens on per-pixel basis rather than on per-bounding-box
9473
+     * @type Boolean
9474
+     * @default
9475
+     */
9476
+    perPixelTargetFind:     false,
9477
+
9478
+    /**
9479
+     * Number of pixels around target pixel to tolerate (consider active) during object detection
9480
+     * @type Number
9481
+     * @default
9482
+     */
9483
+    targetFindTolerance:    0,
9484
+
9485
+    /**
9486
+     * When true, target detection is skipped when hovering over canvas. This can be used to improve performance.
9487
+     * @type Boolean
9488
+     * @default
9489
+     */
9490
+    skipTargetFind:         false,
9491
+
9492
+    /**
9493
+     * When true, mouse events on canvas (mousedown/mousemove/mouseup) result in free drawing.
9494
+     * After mousedown, mousemove creates a shape,
9495
+     * and then mouseup finalizes it and adds an instance of `fabric.Path` onto canvas.
9496
+     * @tutorial {@link http://fabricjs.com/fabric-intro-part-4#free_drawing}
9497
+     * @type Boolean
9498
+     * @default
9499
+     */
9500
+    isDrawingMode:          false,
9501
+
9502
+    /**
9503
+     * Indicates whether objects should remain in current stack position when selected.
9504
+     * When false objects are brought to top and rendered as part of the selection group
9505
+     * @type Boolean
9506
+     * @default
9507
+     */
9508
+    preserveObjectStacking: false,
9509
+
9510
+    /**
9511
+     * Indicates the angle that an object will lock to while rotating.
9512
+     * @type Number
9513
+     * @since 1.6.7
9514
+     * @default
9515
+     */
9516
+    snapAngle: 0,
9517
+
9518
+    /**
9519
+     * Indicates the distance from the snapAngle the rotation will lock to the snapAngle.
9520
+     * When `null`, the snapThreshold will default to the snapAngle.
9521
+     * @type null|Number
9522
+     * @since 1.6.7
9523
+     * @default
9524
+     */
9525
+    snapThreshold: null,
9526
+
9527
+    /**
9528
+     * Indicates if the right click on canvas can output the context menu or not
9529
+     * @type Boolean
9530
+     * @since 1.6.5
9531
+     * @default
9532
+     */
9533
+    stopContextMenu: false,
9534
+
9535
+    /**
9536
+     * Indicates if the canvas can fire right click events
9537
+     * @type Boolean
9538
+     * @since 1.6.5
9539
+     * @default
9540
+     */
9541
+    fireRightClick: false,
9542
+
9543
+    /**
9544
+     * Indicates if the canvas can fire middle click events
9545
+     * @type Boolean
9546
+     * @since 1.7.8
9547
+     * @default
9548
+     */
9549
+    fireMiddleClick: false,
9550
+
9551
+    /**
9552
+     * @private
9553
+     */
9554
+    _initInteractive: function() {
9555
+      this._currentTransform = null;
9556
+      this._groupSelector = null;
9557
+      this._initWrapperElement();
9558
+      this._createUpperCanvas();
9559
+      this._initEventListeners();
9560
+
9561
+      this._initRetinaScaling();
9562
+
9563
+      this.freeDrawingBrush = fabric.PencilBrush && new fabric.PencilBrush(this);
9564
+
9565
+      this.calcOffset();
9566
+    },
9567
+
9568
+    /**
9569
+     * Divides objects in two groups, one to render immediately
9570
+     * and one to render as activeGroup.
9571
+     * @return {Array} objects to render immediately and pushes the other in the activeGroup.
9572
+     */
9573
+    _chooseObjectsToRender: function() {
9574
+      var activeObjects = this.getActiveObjects(),
9575
+          object, objsToRender, activeGroupObjects;
9576
+
9577
+      if (activeObjects.length > 0 && !this.preserveObjectStacking) {
9578
+        objsToRender = [];
9579
+        activeGroupObjects = [];
9580
+        for (var i = 0, length = this._objects.length; i < length; i++) {
9581
+          object = this._objects[i];
9582
+          if (activeObjects.indexOf(object) === -1 ) {
9583
+            objsToRender.push(object);
9584
+          }
9585
+          else {
9586
+            activeGroupObjects.push(object);
9587
+          }
9588
+        }
9589
+        if (activeObjects.length > 1) {
9590
+          this._activeObject._objects = activeGroupObjects;
9591
+        }
9592
+        objsToRender.push.apply(objsToRender, activeGroupObjects);
9593
+      }
9594
+      else {
9595
+        objsToRender = this._objects;
9596
+      }
9597
+      return objsToRender;
9598
+    },
9599
+
9600
+    /**
9601
+     * Renders both the top canvas and the secondary container canvas.
9602
+     * @return {fabric.Canvas} instance
9603
+     * @chainable
9604
+     */
9605
+    renderAll: function () {
9606
+      if (this.contextTopDirty && !this._groupSelector && !this.isDrawingMode) {
9607
+        this.clearContext(this.contextTop);
9608
+        this.contextTopDirty = false;
9609
+      }
9610
+      if (this.hasLostContext) {
9611
+        this.renderTopLayer(this.contextTop);
9612
+      }
9613
+      var canvasToDrawOn = this.contextContainer;
9614
+      this.renderCanvas(canvasToDrawOn, this._chooseObjectsToRender());
9615
+      return this;
9616
+    },
9617
+
9618
+    renderTopLayer: function(ctx) {
9619
+      ctx.save();
9620
+      if (this.isDrawingMode && this._isCurrentlyDrawing) {
9621
+        this.freeDrawingBrush && this.freeDrawingBrush._render();
9622
+        this.contextTopDirty = true;
9623
+      }
9624
+      // we render the top context - last object
9625
+      if (this.selection && this._groupSelector) {
9626
+        this._drawSelection(ctx);
9627
+        this.contextTopDirty = true;
9628
+      }
9629
+      ctx.restore();
9630
+    },
9631
+
9632
+    /**
9633
+     * Method to render only the top canvas.
9634
+     * Also used to render the group selection box.
9635
+     * @return {fabric.Canvas} thisArg
9636
+     * @chainable
9637
+     */
9638
+    renderTop: function () {
9639
+      var ctx = this.contextTop;
9640
+      this.clearContext(ctx);
9641
+      this.renderTopLayer(ctx);
9642
+      this.fire('after:render');
9643
+      return this;
9644
+    },
9645
+
9646
+    /**
9647
+     * Resets the current transform to its original values and chooses the type of resizing based on the event
9648
+     * @private
9649
+     */
9650
+    _resetCurrentTransform: function() {
9651
+      var t = this._currentTransform;
9652
+
9653
+      t.target.set({
9654
+        scaleX: t.original.scaleX,
9655
+        scaleY: t.original.scaleY,
9656
+        skewX: t.original.skewX,
9657
+        skewY: t.original.skewY,
9658
+        left: t.original.left,
9659
+        top: t.original.top
9660
+      });
9661
+
9662
+      if (this._shouldCenterTransform(t.target)) {
9663
+        if (t.originX !== 'center') {
9664
+          if (t.originX === 'right') {
9665
+            t.mouseXSign = -1;
9666
+          }
9667
+          else {
9668
+            t.mouseXSign = 1;
9669
+          }
9670
+        }
9671
+        if (t.originY !== 'center') {
9672
+          if (t.originY === 'bottom') {
9673
+            t.mouseYSign = -1;
9674
+          }
9675
+          else {
9676
+            t.mouseYSign = 1;
9677
+          }
9678
+        }
9679
+
9680
+        t.originX = 'center';
9681
+        t.originY = 'center';
9682
+      }
9683
+      else {
9684
+        t.originX = t.original.originX;
9685
+        t.originY = t.original.originY;
9686
+      }
9687
+    },
9688
+
9689
+    /**
9690
+     * Checks if point is contained within an area of given object
9691
+     * @param {Event} e Event object
9692
+     * @param {fabric.Object} target Object to test against
9693
+     * @param {Object} [point] x,y object of point coordinates we want to check.
9694
+     * @return {Boolean} true if point is contained within an area of given object
9695
+     */
9696
+    containsPoint: function (e, target, point) {
9697
+      var ignoreZoom = true,
9698
+          pointer = point || this.getPointer(e, ignoreZoom),
9699
+          xy;
9700
+
9701
+      if (target.group && target.group === this._activeObject && target.group.type === 'activeSelection') {
9702
+        xy = this._normalizePointer(target.group, pointer);
9703
+      }
9704
+      else {
9705
+        xy = { x: pointer.x, y: pointer.y };
9706
+      }
9707
+      // http://www.geog.ubc.ca/courses/klink/gis.notes/ncgia/u32.html
9708
+      // http://idav.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html
9709
+      return (target.containsPoint(xy) || target._findTargetCorner(pointer));
9710
+    },
9711
+
9712
+    /**
9713
+     * @private
9714
+     */
9715
+    _normalizePointer: function (object, pointer) {
9716
+      var m = object.calcTransformMatrix(),
9717
+          invertedM = fabric.util.invertTransform(m),
9718
+          vptPointer = this.restorePointerVpt(pointer);
9719
+      return fabric.util.transformPoint(vptPointer, invertedM);
9720
+    },
9721
+
9722
+    /**
9723
+     * Returns true if object is transparent at a certain location
9724
+     * @param {fabric.Object} target Object to check
9725
+     * @param {Number} x Left coordinate
9726
+     * @param {Number} y Top coordinate
9727
+     * @return {Boolean}
9728
+     */
9729
+    isTargetTransparent: function (target, x, y) {
9730
+      if (target.shouldCache() && target._cacheCanvas) {
9731
+        var normalizedPointer = this._normalizePointer(target, {x: x, y: y}),
9732
+            targetRelativeX = target.cacheTranslationX + (normalizedPointer.x * target.zoomX),
9733
+            targetRelativeY = target.cacheTranslationY + (normalizedPointer.y * target.zoomY);
9734
+
9735
+        var isTransparent = fabric.util.isTransparent(
9736
+          target._cacheContext, targetRelativeX, targetRelativeY, this.targetFindTolerance);
9737
+
9738
+        return isTransparent;
9739
+      }
9740
+
9741
+      var ctx = this.contextCache,
9742
+          originalColor = target.selectionBackgroundColor, v = this.viewportTransform;
9743
+
9744
+      target.selectionBackgroundColor = '';
9745
+
9746
+      this.clearContext(ctx);
9747
+
9748
+      ctx.save();
9749
+      ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
9750
+      target.render(ctx);
9751
+      ctx.restore();
9752
+
9753
+      target === this._activeObject && target._renderControls(ctx, {
9754
+        hasBorders: false,
9755
+        transparentCorners: false
9756
+      }, {
9757
+        hasBorders: false,
9758
+      });
9759
+
9760
+      target.selectionBackgroundColor = originalColor;
9761
+
9762
+      var isTransparent = fabric.util.isTransparent(
9763
+        ctx, x, y, this.targetFindTolerance);
9764
+
9765
+      return isTransparent;
9766
+    },
9767
+
9768
+    /**
9769
+     * takes an event and determins if selection key has been pressed
9770
+     * @private
9771
+     * @param {Event} e Event object
9772
+     */
9773
+    _isSelectionKeyPressed: function(e) {
9774
+      var selectionKeyPressed = false;
9775
+
9776
+      if (Object.prototype.toString.call(this.selectionKey) === '[object Array]') {
9777
+        selectionKeyPressed = !!this.selectionKey.find(function(key) { return e[key] === true; });
9778
+      }
9779
+      else {
9780
+        selectionKeyPressed = e[this.selectionKey];
9781
+      }
9782
+
9783
+      return selectionKeyPressed;
9784
+    },
9785
+
9786
+    /**
9787
+     * @private
9788
+     * @param {Event} e Event object
9789
+     * @param {fabric.Object} target
9790
+     */
9791
+    _shouldClearSelection: function (e, target) {
9792
+      var activeObjects = this.getActiveObjects(),
9793
+          activeObject = this._activeObject;
9794
+
9795
+      return (
9796
+        !target
9797
+        ||
9798
+        (target &&
9799
+          activeObject &&
9800
+          activeObjects.length > 1 &&
9801
+          activeObjects.indexOf(target) === -1 &&
9802
+          activeObject !== target &&
9803
+          !this._isSelectionKeyPressed(e))
9804
+        ||
9805
+        (target && !target.evented)
9806
+        ||
9807
+        (target &&
9808
+          !target.selectable &&
9809
+          activeObject &&
9810
+          activeObject !== target)
9811
+      );
9812
+    },
9813
+
9814
+    /**
9815
+     * centeredScaling from object can't override centeredScaling from canvas.
9816
+     * this should be fixed, since object setting should take precedence over canvas.
9817
+     * @private
9818
+     * @param {fabric.Object} target
9819
+     */
9820
+    _shouldCenterTransform: function (target) {
9821
+      if (!target) {
9822
+        return;
9823
+      }
9824
+
9825
+      var t = this._currentTransform,
9826
+          centerTransform;
9827
+
9828
+      if (t.action === 'scale' || t.action === 'scaleX' || t.action === 'scaleY') {
9829
+        centerTransform = this.centeredScaling || target.centeredScaling;
9830
+      }
9831
+      else if (t.action === 'rotate') {
9832
+        centerTransform = this.centeredRotation || target.centeredRotation;
9833
+      }
9834
+
9835
+      return centerTransform ? !t.altKey : t.altKey;
9836
+    },
9837
+
9838
+    /**
9839
+     * @private
9840
+     */
9841
+    _getOriginFromCorner: function(target, corner) {
9842
+      var origin = {
9843
+        x: target.originX,
9844
+        y: target.originY
9845
+      };
9846
+
9847
+      if (corner === 'ml' || corner === 'tl' || corner === 'bl') {
9848
+        origin.x = 'right';
9849
+      }
9850
+      else if (corner === 'mr' || corner === 'tr' || corner === 'br') {
9851
+        origin.x = 'left';
9852
+      }
9853
+
9854
+      if (corner === 'tl' || corner === 'mt' || corner === 'tr') {
9855
+        origin.y = 'bottom';
9856
+      }
9857
+      else if (corner === 'bl' || corner === 'mb' || corner === 'br') {
9858
+        origin.y = 'top';
9859
+      }
9860
+
9861
+      return origin;
9862
+    },
9863
+
9864
+    /**
9865
+     * @private
9866
+     */
9867
+    _getActionFromCorner: function(alreadySelected, corner, e) {
9868
+      if (!corner || !alreadySelected) {
9869
+        return 'drag';
9870
+      }
9871
+
9872
+      switch (corner) {
9873
+        case 'mtr':
9874
+          return 'rotate';
9875
+        case 'ml':
9876
+        case 'mr':
9877
+          return e[this.altActionKey] ? 'skewY' : 'scaleX';
9878
+        case 'mt':
9879
+        case 'mb':
9880
+          return e[this.altActionKey] ? 'skewX' : 'scaleY';
9881
+        default:
9882
+          return 'scale';
9883
+      }
9884
+    },
9885
+
9886
+    /**
9887
+     * @private
9888
+     * @param {Event} e Event object
9889
+     * @param {fabric.Object} target
9890
+     */
9891
+    _setupCurrentTransform: function (e, target, alreadySelected) {
9892
+      if (!target) {
9893
+        return;
9894
+      }
9895
+
9896
+      var pointer = this.getPointer(e),
9897
+          corner = target._findTargetCorner(this.getPointer(e, true)),
9898
+          action = this._getActionFromCorner(alreadySelected, corner, e),
9899
+          origin = this._getOriginFromCorner(target, corner);
9900
+
9901
+      this._currentTransform = {
9902
+        target: target,
9903
+        action: action,
9904
+        corner: corner,
9905
+        scaleX: target.scaleX,
9906
+        scaleY: target.scaleY,
9907
+        skewX: target.skewX,
9908
+        skewY: target.skewY,
9909
+        // used by transation
9910
+        offsetX: pointer.x - target.left,
9911
+        offsetY: pointer.y - target.top,
9912
+        originX: origin.x,
9913
+        originY: origin.y,
9914
+        ex: pointer.x,
9915
+        ey: pointer.y,
9916
+        lastX: pointer.x,
9917
+        lastY: pointer.y,
9918
+        // unsure they are usefull anymore.
9919
+        // left: target.left,
9920
+        // top: target.top,
9921
+        theta: degreesToRadians(target.angle),
9922
+        // end of unsure
9923
+        width: target.width * target.scaleX,
9924
+        mouseXSign: 1,
9925
+        mouseYSign: 1,
9926
+        shiftKey: e.shiftKey,
9927
+        altKey: e[this.centeredKey],
9928
+        original: fabric.util.saveObjectTransform(target),
9929
+      };
9930
+
9931
+      this._currentTransform.original.originX = origin.x;
9932
+      this._currentTransform.original.originY = origin.y;
9933
+
9934
+      this._resetCurrentTransform();
9935
+      this._beforeTransform(e);
9936
+    },
9937
+
9938
+    /**
9939
+     * Translates object by "setting" its left/top
9940
+     * @private
9941
+     * @param {Number} x pointer's x coordinate
9942
+     * @param {Number} y pointer's y coordinate
9943
+     * @return {Boolean} true if the translation occurred
9944
+     */
9945
+    _translateObject: function (x, y) {
9946
+      var transform = this._currentTransform,
9947
+          target = transform.target,
9948
+          newLeft = x - transform.offsetX,
9949
+          newTop = y - transform.offsetY,
9950
+          moveX = !target.get('lockMovementX') && target.left !== newLeft,
9951
+          moveY = !target.get('lockMovementY') && target.top !== newTop;
9952
+
9953
+      moveX && target.set('left', newLeft);
9954
+      moveY && target.set('top', newTop);
9955
+      return moveX || moveY;
9956
+    },
9957
+
9958
+    /**
9959
+     * Check if we are increasing a positive skew or lower it,
9960
+     * checking mouse direction and pressed corner.
9961
+     * @private
9962
+     */
9963
+    _changeSkewTransformOrigin: function(mouseMove, t, by) {
9964
+      var property = 'originX', origins = { 0: 'center' },
9965
+          skew = t.target.skewX, originA = 'left', originB = 'right',
9966
+          corner = t.corner === 'mt' || t.corner === 'ml' ? 1 : -1,
9967
+          flipSign = 1;
9968
+
9969
+      mouseMove = mouseMove > 0 ? 1 : -1;
9970
+      if (by === 'y') {
9971
+        skew = t.target.skewY;
9972
+        originA = 'top';
9973
+        originB = 'bottom';
9974
+        property = 'originY';
9975
+      }
9976
+      origins[-1] = originA;
9977
+      origins[1] = originB;
9978
+
9979
+      t.target.flipX && (flipSign *= -1);
9980
+      t.target.flipY && (flipSign *= -1);
9981
+
9982
+      if (skew === 0) {
9983
+        t.skewSign = -corner * mouseMove * flipSign;
9984
+        t[property] = origins[-mouseMove];
9985
+      }
9986
+      else {
9987
+        skew = skew > 0 ? 1 : -1;
9988
+        t.skewSign = skew;
9989
+        t[property] = origins[skew * corner * flipSign];
9990
+      }
9991
+    },
9992
+
9993
+    /**
9994
+     * Skew object by mouse events
9995
+     * @private
9996
+     * @param {Number} x pointer's x coordinate
9997
+     * @param {Number} y pointer's y coordinate
9998
+     * @param {String} by Either 'x' or 'y'
9999
+     * @return {Boolean} true if the skewing occurred
10000
+     */
10001
+    _skewObject: function (x, y, by) {
10002
+      var t = this._currentTransform,
10003
+          target = t.target, skewed = false,
10004
+          lockSkewingX = target.get('lockSkewingX'),
10005
+          lockSkewingY = target.get('lockSkewingY');
10006
+
10007
+      if ((lockSkewingX && by === 'x') || (lockSkewingY && by === 'y')) {
10008
+        return false;
10009
+      }
10010
+
10011
+      // Get the constraint point
10012
+      var center = target.getCenterPoint(),
10013
+          actualMouseByCenter = target.toLocalPoint(new fabric.Point(x, y), 'center', 'center')[by],
10014
+          lastMouseByCenter = target.toLocalPoint(new fabric.Point(t.lastX, t.lastY), 'center', 'center')[by],
10015
+          actualMouseByOrigin, constraintPosition, dim = target._getTransformedDimensions();
10016
+
10017
+      this._changeSkewTransformOrigin(actualMouseByCenter - lastMouseByCenter, t, by);
10018
+      actualMouseByOrigin = target.toLocalPoint(new fabric.Point(x, y), t.originX, t.originY)[by];
10019
+      constraintPosition = target.translateToOriginPoint(center, t.originX, t.originY);
10020
+      // Actually skew the object
10021
+      skewed = this._setObjectSkew(actualMouseByOrigin, t, by, dim);
10022
+      t.lastX = x;
10023
+      t.lastY = y;
10024
+      // Make sure the constraints apply
10025
+      target.setPositionByOrigin(constraintPosition, t.originX, t.originY);
10026
+      return skewed;
10027
+    },
10028
+
10029
+    /**
10030
+     * Set object skew
10031
+     * @private
10032
+     * @return {Boolean} true if the skewing occurred
10033
+     */
10034
+    _setObjectSkew: function(localMouse, transform, by, _dim) {
10035
+      var target = transform.target, newValue, skewed = false,
10036
+          skewSign = transform.skewSign, newDim, dimNoSkew,
10037
+          otherBy, _otherBy, _by, newDimMouse, skewX, skewY;
10038
+
10039
+      if (by === 'x') {
10040
+        otherBy = 'y';
10041
+        _otherBy = 'Y';
10042
+        _by = 'X';
10043
+        skewX = 0;
10044
+        skewY = target.skewY;
10045
+      }
10046
+      else {
10047
+        otherBy = 'x';
10048
+        _otherBy = 'X';
10049
+        _by = 'Y';
10050
+        skewX = target.skewX;
10051
+        skewY = 0;
10052
+      }
10053
+
10054
+      dimNoSkew = target._getTransformedDimensions(skewX, skewY);
10055
+      newDimMouse = 2 * Math.abs(localMouse) - dimNoSkew[by];
10056
+      if (newDimMouse <= 2) {
10057
+        newValue = 0;
10058
+      }
10059
+      else {
10060
+        newValue = skewSign * Math.atan((newDimMouse / target['scale' + _by]) /
10061
+                                        (dimNoSkew[otherBy] / target['scale' + _otherBy]));
10062
+        newValue = fabric.util.radiansToDegrees(newValue);
10063
+      }
10064
+      skewed = target['skew' + _by] !== newValue;
10065
+      target.set('skew' + _by, newValue);
10066
+      if (target['skew' + _otherBy] !== 0) {
10067
+        newDim = target._getTransformedDimensions();
10068
+        newValue = (_dim[otherBy] / newDim[otherBy]) * target['scale' + _otherBy];
10069
+        target.set('scale' + _otherBy, newValue);
10070
+      }
10071
+      return skewed;
10072
+    },
10073
+
10074
+    /**
10075
+     * Scales object by invoking its scaleX/scaleY methods
10076
+     * @private
10077
+     * @param {Number} x pointer's x coordinate
10078
+     * @param {Number} y pointer's y coordinate
10079
+     * @param {String} by Either 'x' or 'y' - specifies dimension constraint by which to scale an object.
10080
+     *                    When not provided, an object is scaled by both dimensions equally
10081
+     * @return {Boolean} true if the scaling occurred
10082
+     */
10083
+    _scaleObject: function (x, y, by) {
10084
+      var t = this._currentTransform,
10085
+          target = t.target,
10086
+          lockScalingX = target.lockScalingX,
10087
+          lockScalingY = target.lockScalingY,
10088
+          lockScalingFlip = target.lockScalingFlip;
10089
+
10090
+      if (lockScalingX && lockScalingY) {
10091
+        return false;
10092
+      }
10093
+
10094
+      // Get the constraint point
10095
+      var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY),
10096
+          localMouse = target.toLocalPoint(new fabric.Point(x, y), t.originX, t.originY),
10097
+          dim = target._getTransformedDimensions(), scaled = false;
10098
+
10099
+      this._setLocalMouse(localMouse, t);
10100
+
10101
+      // Actually scale the object
10102
+      scaled = this._setObjectScale(localMouse, t, lockScalingX, lockScalingY, by, lockScalingFlip, dim);
10103
+
10104
+      // Make sure the constraints apply
10105
+      target.setPositionByOrigin(constraintPosition, t.originX, t.originY);
10106
+      return scaled;
10107
+    },
10108
+
10109
+    /**
10110
+     * @private
10111
+     * @return {Boolean} true if the scaling occurred
10112
+     */
10113
+    _setObjectScale: function(localMouse, transform, lockScalingX, lockScalingY, by, lockScalingFlip, _dim) {
10114
+      var target = transform.target, forbidScalingX = false, forbidScalingY = false, scaled = false,
10115
+          changeX, changeY, scaleX, scaleY;
10116
+
10117
+      scaleX = localMouse.x * target.scaleX / _dim.x;
10118
+      scaleY = localMouse.y * target.scaleY / _dim.y;
10119
+      changeX = target.scaleX !== scaleX;
10120
+      changeY = target.scaleY !== scaleY;
10121
+
10122
+      if (lockScalingFlip && scaleX <= 0 && scaleX < target.scaleX) {
10123
+        forbidScalingX = true;
10124
+        localMouse.x = 0;
10125
+      }
10126
+
10127
+      if (lockScalingFlip && scaleY <= 0 && scaleY < target.scaleY) {
10128
+        forbidScalingY = true;
10129
+        localMouse.y = 0;
10130
+      }
10131
+
10132
+      if (by === 'equally' && !lockScalingX && !lockScalingY) {
10133
+        scaled = this._scaleObjectEqually(localMouse, target, transform, _dim);
10134
+      }
10135
+      else if (!by) {
10136
+        forbidScalingX || lockScalingX || (target.set('scaleX', scaleX) && (scaled = scaled || changeX));
10137
+        forbidScalingY || lockScalingY || (target.set('scaleY', scaleY) && (scaled = scaled || changeY));
10138
+      }
10139
+      else if (by === 'x' && !target.get('lockUniScaling')) {
10140
+        forbidScalingX || lockScalingX || (target.set('scaleX', scaleX) && (scaled = scaled || changeX));
10141
+      }
10142
+      else if (by === 'y' && !target.get('lockUniScaling')) {
10143
+        forbidScalingY || lockScalingY || (target.set('scaleY', scaleY) && (scaled = scaled || changeY));
10144
+      }
10145
+      transform.newScaleX = scaleX;
10146
+      transform.newScaleY = scaleY;
10147
+      forbidScalingX || forbidScalingY || this._flipObject(transform, by);
10148
+      return scaled;
10149
+    },
10150
+
10151
+    /**
10152
+     * @private
10153
+     * @return {Boolean} true if the scaling occurred
10154
+     */
10155
+    _scaleObjectEqually: function(localMouse, target, transform, _dim) {
10156
+
10157
+      var dist = localMouse.y + localMouse.x,
10158
+          lastDist = _dim.y * transform.original.scaleY / target.scaleY +
10159
+                     _dim.x * transform.original.scaleX / target.scaleX,
10160
+          scaled, signX = localMouse.x < 0 ? -1 : 1,
10161
+          signY = localMouse.y < 0 ? -1 : 1;
10162
+
10163
+      // We use transform.scaleX/Y instead of target.scaleX/Y
10164
+      // because the object may have a min scale and we'll loose the proportions
10165
+      transform.newScaleX = signX * Math.abs(transform.original.scaleX * dist / lastDist);
10166
+      transform.newScaleY = signY * Math.abs(transform.original.scaleY * dist / lastDist);
10167
+      scaled = transform.newScaleX !== target.scaleX || transform.newScaleY !== target.scaleY;
10168
+      target.set('scaleX', transform.newScaleX);
10169
+      target.set('scaleY', transform.newScaleY);
10170
+      return scaled;
10171
+    },
10172
+
10173
+    /**
10174
+     * @private
10175
+     */
10176
+    _flipObject: function(transform, by) {
10177
+      if (transform.newScaleX < 0 && by !== 'y') {
10178
+        if (transform.originX === 'left') {
10179
+          transform.originX = 'right';
10180
+        }
10181
+        else if (transform.originX === 'right') {
10182
+          transform.originX = 'left';
10183
+        }
10184
+      }
10185
+
10186
+      if (transform.newScaleY < 0 && by !== 'x') {
10187
+        if (transform.originY === 'top') {
10188
+          transform.originY = 'bottom';
10189
+        }
10190
+        else if (transform.originY === 'bottom') {
10191
+          transform.originY = 'top';
10192
+        }
10193
+      }
10194
+    },
10195
+
10196
+    /**
10197
+     * @private
10198
+     */
10199
+    _setLocalMouse: function(localMouse, t) {
10200
+      var target = t.target, zoom = this.getZoom(),
10201
+          padding = target.padding / zoom;
10202
+
10203
+      if (t.originX === 'right') {
10204
+        localMouse.x *= -1;
10205
+      }
10206
+      else if (t.originX === 'center') {
10207
+        localMouse.x *= t.mouseXSign * 2;
10208
+        if (localMouse.x < 0) {
10209
+          t.mouseXSign = -t.mouseXSign;
10210
+        }
10211
+      }
10212
+
10213
+      if (t.originY === 'bottom') {
10214
+        localMouse.y *= -1;
10215
+      }
10216
+      else if (t.originY === 'center') {
10217
+        localMouse.y *= t.mouseYSign * 2;
10218
+        if (localMouse.y < 0) {
10219
+          t.mouseYSign = -t.mouseYSign;
10220
+        }
10221
+      }
10222
+
10223
+      // adjust the mouse coordinates when dealing with padding
10224
+      if (abs(localMouse.x) > padding) {
10225
+        if (localMouse.x < 0) {
10226
+          localMouse.x += padding;
10227
+        }
10228
+        else {
10229
+          localMouse.x -= padding;
10230
+        }
10231
+      }
10232
+      else { // mouse is within the padding, set to 0
10233
+        localMouse.x = 0;
10234
+      }
10235
+
10236
+      if (abs(localMouse.y) > padding) {
10237
+        if (localMouse.y < 0) {
10238
+          localMouse.y += padding;
10239
+        }
10240
+        else {
10241
+          localMouse.y -= padding;
10242
+        }
10243
+      }
10244
+      else {
10245
+        localMouse.y = 0;
10246
+      }
10247
+    },
10248
+
10249
+    /**
10250
+     * Rotates object by invoking its rotate method
10251
+     * @private
10252
+     * @param {Number} x pointer's x coordinate
10253
+     * @param {Number} y pointer's y coordinate
10254
+     * @return {Boolean} true if the rotation occurred
10255
+     */
10256
+    _rotateObject: function (x, y) {
10257
+
10258
+      var t = this._currentTransform,
10259
+          target = t.target, constraintPosition,
10260
+          constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY);
10261
+
10262
+      if (target.lockRotation) {
10263
+        return false;
10264
+      }
10265
+
10266
+      var lastAngle = atan2(t.ey - constraintPosition.y, t.ex - constraintPosition.x),
10267
+          curAngle = atan2(y - constraintPosition.y, x - constraintPosition.x),
10268
+          angle = radiansToDegrees(curAngle - lastAngle + t.theta),
10269
+          hasRotated = true;
10270
+
10271
+      if (target.snapAngle > 0) {
10272
+        var snapAngle  = target.snapAngle,
10273
+            snapThreshold  = target.snapThreshold || snapAngle,
10274
+            rightAngleLocked = Math.ceil(angle / snapAngle) * snapAngle,
10275
+            leftAngleLocked = Math.floor(angle / snapAngle) * snapAngle;
10276
+
10277
+        if (Math.abs(angle - leftAngleLocked) < snapThreshold) {
10278
+          angle = leftAngleLocked;
10279
+        }
10280
+        else if (Math.abs(angle - rightAngleLocked) < snapThreshold) {
10281
+          angle = rightAngleLocked;
10282
+        }
10283
+      }
10284
+
10285
+      // normalize angle to positive value
10286
+      if (angle < 0) {
10287
+        angle = 360 + angle;
10288
+      }
10289
+      angle %= 360;
10290
+
10291
+      if (target.angle === angle) {
10292
+        hasRotated = false;
10293
+      }
10294
+      else {
10295
+        // rotation only happen here
10296
+        target.angle = angle;
10297
+        // Make sure the constraints apply
10298
+        target.setPositionByOrigin(constraintPosition, t.originX, t.originY);
10299
+      }
10300
+
10301
+      return hasRotated;
10302
+    },
10303
+
10304
+    /**
10305
+     * Set the cursor type of the canvas element
10306
+     * @param {String} value Cursor type of the canvas element.
10307
+     * @see http://www.w3.org/TR/css3-ui/#cursor
10308
+     */
10309
+    setCursor: function (value) {
10310
+      this.upperCanvasEl.style.cursor = value;
10311
+    },
10312
+
10313
+    /**
10314
+     * @private
10315
+     * @param {CanvasRenderingContext2D} ctx to draw the selection on
10316
+     */
10317
+    _drawSelection: function (ctx) {
10318
+      var groupSelector = this._groupSelector,
10319
+          left = groupSelector.left,
10320
+          top = groupSelector.top,
10321
+          aleft = abs(left),
10322
+          atop = abs(top);
10323
+
10324
+      if (this.selectionColor) {
10325
+        ctx.fillStyle = this.selectionColor;
10326
+
10327
+        ctx.fillRect(
10328
+          groupSelector.ex - ((left > 0) ? 0 : -left),
10329
+          groupSelector.ey - ((top > 0) ? 0 : -top),
10330
+          aleft,
10331
+          atop
10332
+        );
10333
+      }
10334
+
10335
+      if (!this.selectionLineWidth || !this.selectionBorderColor) {
10336
+        return;
10337
+      }
10338
+      ctx.lineWidth = this.selectionLineWidth;
10339
+      ctx.strokeStyle = this.selectionBorderColor;
10340
+
10341
+      // selection border
10342
+      if (this.selectionDashArray.length > 1 && !supportLineDash) {
10343
+
10344
+        var px = groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0 : aleft),
10345
+            py = groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0 : atop);
10346
+
10347
+        ctx.beginPath();
10348
+
10349
+        fabric.util.drawDashedLine(ctx, px, py, px + aleft, py, this.selectionDashArray);
10350
+        fabric.util.drawDashedLine(ctx, px, py + atop - 1, px + aleft, py + atop - 1, this.selectionDashArray);
10351
+        fabric.util.drawDashedLine(ctx, px, py, px, py + atop, this.selectionDashArray);
10352
+        fabric.util.drawDashedLine(ctx, px + aleft - 1, py, px + aleft - 1, py + atop, this.selectionDashArray);
10353
+
10354
+        ctx.closePath();
10355
+        ctx.stroke();
10356
+      }
10357
+      else {
10358
+        fabric.Object.prototype._setLineDash.call(this, ctx, this.selectionDashArray);
10359
+        ctx.strokeRect(
10360
+          groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0 : aleft),
10361
+          groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0 : atop),
10362
+          aleft,
10363
+          atop
10364
+        );
10365
+      }
10366
+    },
10367
+
10368
+    /**
10369
+     * Method that determines what object we are clicking on
10370
+     * the skipGroup parameter is for internal use, is needed for shift+click action
10371
+     * 11/09/2018 TODO: would be cool if findTarget could discern between being a full target
10372
+     * or the outside part of the corner.
10373
+     * @param {Event} e mouse event
10374
+     * @param {Boolean} skipGroup when true, activeGroup is skipped and only objects are traversed through
10375
+     * @return {fabric.Object} the target found
10376
+     */
10377
+    findTarget: function (e, skipGroup) {
10378
+      if (this.skipTargetFind) {
10379
+        return;
10380
+      }
10381
+
10382
+      var ignoreZoom = true,
10383
+          pointer = this.getPointer(e, ignoreZoom),
10384
+          activeObject = this._activeObject,
10385
+          aObjects = this.getActiveObjects(),
10386
+          activeTarget, activeTargetSubs;
10387
+
10388
+      // first check current group (if one exists)
10389
+      // active group does not check sub targets like normal groups.
10390
+      // if active group just exits.
10391
+      this.targets = [];
10392
+
10393
+      if (aObjects.length > 1 && !skipGroup && activeObject === this._searchPossibleTargets([activeObject], pointer)) {
10394
+        return activeObject;
10395
+      }
10396
+      // if we hit the corner of an activeObject, let's return that.
10397
+      if (aObjects.length === 1 && activeObject._findTargetCorner(pointer)) {
10398
+        return activeObject;
10399
+      }
10400
+      if (aObjects.length === 1 &&
10401
+        activeObject === this._searchPossibleTargets([activeObject], pointer)) {
10402
+        if (!this.preserveObjectStacking) {
10403
+          return activeObject;
10404
+        }
10405
+        else {
10406
+          activeTarget = activeObject;
10407
+          activeTargetSubs = this.targets;
10408
+          this.targets = [];
10409
+        }
10410
+      }
10411
+      var target = this._searchPossibleTargets(this._objects, pointer);
10412
+      if (e[this.altSelectionKey] && target && activeTarget && target !== activeTarget) {
10413
+        target = activeTarget;
10414
+        this.targets = activeTargetSubs;
10415
+      }
10416
+      return target;
10417
+    },
10418
+
10419
+    /**
10420
+     * Checks point is inside the object.
10421
+     * @param {Object} [pointer] x,y object of point coordinates we want to check.
10422
+     * @param {fabric.Object} obj Object to test against
10423
+     * @param {Object} [globalPointer] x,y object of point coordinates relative to canvas used to search per pixel target.
10424
+     * @return {Boolean} true if point is contained within an area of given object
10425
+     * @private
10426
+     */
10427
+    _checkTarget: function(pointer, obj, globalPointer) {
10428
+      if (obj &&
10429
+          obj.visible &&
10430
+          obj.evented &&
10431
+          this.containsPoint(null, obj, pointer)){
10432
+        if ((this.perPixelTargetFind || obj.perPixelTargetFind) && !obj.isEditing) {
10433
+          var isTransparent = this.isTargetTransparent(obj, globalPointer.x, globalPointer.y);
10434
+          if (!isTransparent) {
10435
+            return true;
10436
+          }
10437
+        }
10438
+        else {
10439
+          return true;
10440
+        }
10441
+      }
10442
+    },
10443
+
10444
+    /**
10445
+     * Function used to search inside objects an object that contains pointer in bounding box or that contains pointerOnCanvas when painted
10446
+     * @param {Array} [objects] objects array to look into
10447
+     * @param {Object} [pointer] x,y object of point coordinates we want to check.
10448
+     * @return {fabric.Object} object that contains pointer
10449
+     * @private
10450
+     */
10451
+    _searchPossibleTargets: function(objects, pointer) {
10452
+      // Cache all targets where their bounding box contains point.
10453
+      var target, i = objects.length, subTarget;
10454
+      // Do not check for currently grouped objects, since we check the parent group itself.
10455
+      // until we call this function specifically to search inside the activeGroup
10456
+      while (i--) {
10457
+        var objToCheck = objects[i];
10458
+        var pointerToUse = objToCheck.group && objToCheck.group.type !== 'activeSelection' ?
10459
+          this._normalizePointer(objToCheck.group, pointer) : pointer;
10460
+        if (this._checkTarget(pointerToUse, objToCheck, pointer)) {
10461
+          target = objects[i];
10462
+          if (target.subTargetCheck && target instanceof fabric.Group) {
10463
+            subTarget = this._searchPossibleTargets(target._objects, pointer);
10464
+            subTarget && this.targets.push(subTarget);
10465
+          }
10466
+          break;
10467
+        }
10468
+      }
10469
+      return target;
10470
+    },
10471
+
10472
+    /**
10473
+     * Returns pointer coordinates without the effect of the viewport
10474
+     * @param {Object} pointer with "x" and "y" number values
10475
+     * @return {Object} object with "x" and "y" number values
10476
+     */
10477
+    restorePointerVpt: function(pointer) {
10478
+      return fabric.util.transformPoint(
10479
+        pointer,
10480
+        fabric.util.invertTransform(this.viewportTransform)
10481
+      );
10482
+    },
10483
+
10484
+    /**
10485
+     * Returns pointer coordinates relative to canvas.
10486
+     * Can return coordinates with or without viewportTransform.
10487
+     * ignoreZoom false gives back coordinates that represent
10488
+     * the point clicked on canvas element.
10489
+     * ignoreZoom true gives back coordinates after being processed
10490
+     * by the viewportTransform ( sort of coordinates of what is displayed
10491
+     * on the canvas where you are clicking.
10492
+     * ignoreZoom true = HTMLElement coordinates relative to top,left
10493
+     * ignoreZoom false, default = fabric space coordinates, the same used for shape position
10494
+     * To interact with your shapes top and left you want to use ignoreZoom true
10495
+     * most of the time, while ignoreZoom false will give you coordinates
10496
+     * compatible with the object.oCoords system.
10497
+     * of the time.
10498
+     * @param {Event} e
10499
+     * @param {Boolean} ignoreZoom
10500
+     * @return {Object} object with "x" and "y" number values
10501
+     */
10502
+    getPointer: function (e, ignoreZoom) {
10503
+      // return cached values if we are in the event processing chain
10504
+      if (this._absolutePointer && !ignoreZoom) {
10505
+        return this._absolutePointer;
10506
+      }
10507
+      if (this._pointer && ignoreZoom) {
10508
+        return this._pointer;
10509
+      }
10510
+
10511
+      var pointer = getPointer(e),
10512
+          upperCanvasEl = this.upperCanvasEl,
10513
+          bounds = upperCanvasEl.getBoundingClientRect(),
10514
+          boundsWidth = bounds.width || 0,
10515
+          boundsHeight = bounds.height || 0,
10516
+          cssScale;
10517
+
10518
+      if (!boundsWidth || !boundsHeight ) {
10519
+        if ('top' in bounds && 'bottom' in bounds) {
10520
+          boundsHeight = Math.abs( bounds.top - bounds.bottom );
10521
+        }
10522
+        if ('right' in bounds && 'left' in bounds) {
10523
+          boundsWidth = Math.abs( bounds.right - bounds.left );
10524
+        }
10525
+      }
10526
+
10527
+      this.calcOffset();
10528
+      pointer.x = pointer.x - this._offset.left;
10529
+      pointer.y = pointer.y - this._offset.top;
10530
+      if (!ignoreZoom) {
10531
+        pointer = this.restorePointerVpt(pointer);
10532
+      }
10533
+
10534
+      if (boundsWidth === 0 || boundsHeight === 0) {
10535
+        // If bounds are not available (i.e. not visible), do not apply scale.
10536
+        cssScale = { width: 1, height: 1 };
10537
+      }
10538
+      else {
10539
+        cssScale = {
10540
+          width: upperCanvasEl.width / boundsWidth,
10541
+          height: upperCanvasEl.height / boundsHeight
10542
+        };
10543
+      }
10544
+
10545
+      return {
10546
+        x: pointer.x * cssScale.width,
10547
+        y: pointer.y * cssScale.height
10548
+      };
10549
+    },
10550
+
10551
+    /**
10552
+     * @private
10553
+     * @throws {CANVAS_INIT_ERROR} If canvas can not be initialized
10554
+     */
10555
+    _createUpperCanvas: function () {
10556
+      var lowerCanvasClass = this.lowerCanvasEl.className.replace(/\s*lower-canvas\s*/, '');
10557
+
10558
+      // there is no need to create a new upperCanvas element if we have already one.
10559
+      if (this.upperCanvasEl) {
10560
+        this.upperCanvasEl.className = '';
10561
+      }
10562
+      else {
10563
+        this.upperCanvasEl = this._createCanvasElement();
10564
+      }
10565
+      fabric.util.addClass(this.upperCanvasEl, 'upper-canvas ' + lowerCanvasClass);
10566
+
10567
+      this.wrapperEl.appendChild(this.upperCanvasEl);
10568
+
10569
+      this._copyCanvasStyle(this.lowerCanvasEl, this.upperCanvasEl);
10570
+      this._applyCanvasStyle(this.upperCanvasEl);
10571
+      this.contextTop = this.upperCanvasEl.getContext('2d');
10572
+    },
10573
+
10574
+    /**
10575
+     * @private
10576
+     */
10577
+    _createCacheCanvas: function () {
10578
+      this.cacheCanvasEl = this._createCanvasElement();
10579
+      this.cacheCanvasEl.setAttribute('width', this.width);
10580
+      this.cacheCanvasEl.setAttribute('height', this.height);
10581
+      this.contextCache = this.cacheCanvasEl.getContext('2d');
10582
+    },
10583
+
10584
+    /**
10585
+     * @private
10586
+     */
10587
+    _initWrapperElement: function () {
10588
+      this.wrapperEl = fabric.util.wrapElement(this.lowerCanvasEl, 'div', {
10589
+        'class': this.containerClass
10590
+      });
10591
+      fabric.util.setStyle(this.wrapperEl, {
10592
+        width: this.width + 'px',
10593
+        height: this.height + 'px',
10594
+        position: 'relative'
10595
+      });
10596
+      fabric.util.makeElementUnselectable(this.wrapperEl);
10597
+    },
10598
+
10599
+    /**
10600
+     * @private
10601
+     * @param {HTMLElement} element canvas element to apply styles on
10602
+     */
10603
+    _applyCanvasStyle: function (element) {
10604
+      var width = this.width || element.width,
10605
+          height = this.height || element.height;
10606
+
10607
+      fabric.util.setStyle(element, {
10608
+        position: 'absolute',
10609
+        width: width + 'px',
10610
+        height: height + 'px',
10611
+        left: 0,
10612
+        top: 0,
10613
+        'touch-action': this.allowTouchScrolling ? 'manipulation' : 'none'
10614
+      });
10615
+      element.width = width;
10616
+      element.height = height;
10617
+      fabric.util.makeElementUnselectable(element);
10618
+    },
10619
+
10620
+    /**
10621
+     * Copy the entire inline style from one element (fromEl) to another (toEl)
10622
+     * @private
10623
+     * @param {Element} fromEl Element style is copied from
10624
+     * @param {Element} toEl Element copied style is applied to
10625
+     */
10626
+    _copyCanvasStyle: function (fromEl, toEl) {
10627
+      toEl.style.cssText = fromEl.style.cssText;
10628
+    },
10629
+
10630
+    /**
10631
+     * Returns context of canvas where object selection is drawn
10632
+     * @return {CanvasRenderingContext2D}
10633
+     */
10634
+    getSelectionContext: function() {
10635
+      return this.contextTop;
10636
+    },
10637
+
10638
+    /**
10639
+     * Returns &lt;canvas> element on which object selection is drawn
10640
+     * @return {HTMLCanvasElement}
10641
+     */
10642
+    getSelectionElement: function () {
10643
+      return this.upperCanvasEl;
10644
+    },
10645
+
10646
+    /**
10647
+     * Returns currently active object
10648
+     * @return {fabric.Object} active object
10649
+     */
10650
+    getActiveObject: function () {
10651
+      return this._activeObject;
10652
+    },
10653
+
10654
+    /**
10655
+     * Returns an array with the current selected objects
10656
+     * @return {fabric.Object} active object
10657
+     */
10658
+    getActiveObjects: function () {
10659
+      var active = this._activeObject;
10660
+      if (active) {
10661
+        if (active.type === 'activeSelection' && active._objects) {
10662
+          return active._objects.slice(0);
10663
+        }
10664
+        else {
10665
+          return [active];
10666
+        }
10667
+      }
10668
+      return [];
10669
+    },
10670
+
10671
+    /**
10672
+     * @private
10673
+     * @param {fabric.Object} obj Object that was removed
10674
+     */
10675
+    _onObjectRemoved: function(obj) {
10676
+      // removing active object should fire "selection:cleared" events
10677
+      if (obj === this._activeObject) {
10678
+        this.fire('before:selection:cleared', { target: obj });
10679
+        this._discardActiveObject();
10680
+        this.fire('selection:cleared', { target: obj });
10681
+        obj.fire('deselected');
10682
+      }
10683
+      if (this._hoveredTarget === obj) {
10684
+        this._hoveredTarget = null;
10685
+      }
10686
+      this.callSuper('_onObjectRemoved', obj);
10687
+    },
10688
+
10689
+    /**
10690
+     * @private
10691
+     * Compares the old activeObject with the current one and fires correct events
10692
+     * @param {fabric.Object} obj old activeObject
10693
+     */
10694
+    _fireSelectionEvents: function(oldObjects, e) {
10695
+      var somethingChanged = false, objects = this.getActiveObjects(),
10696
+          added = [], removed = [], opt = { e: e };
10697
+      oldObjects.forEach(function(oldObject) {
10698
+        if (objects.indexOf(oldObject) === -1) {
10699
+          somethingChanged = true;
10700
+          oldObject.fire('deselected', opt);
10701
+          removed.push(oldObject);
10702
+        }
10703
+      });
10704
+      objects.forEach(function(object) {
10705
+        if (oldObjects.indexOf(object) === -1) {
10706
+          somethingChanged = true;
10707
+          object.fire('selected', opt);
10708
+          added.push(object);
10709
+        }
10710
+      });
10711
+      if (oldObjects.length > 0 && objects.length > 0) {
10712
+        opt.selected = added;
10713
+        opt.deselected = removed;
10714
+        // added for backward compatibility
10715
+        opt.updated = added[0] || removed[0];
10716
+        opt.target = this._activeObject;
10717
+        somethingChanged && this.fire('selection:updated', opt);
10718
+      }
10719
+      else if (objects.length > 0) {
10720
+        // deprecated event
10721
+        if (objects.length === 1) {
10722
+          opt.target = added[0];
10723
+          this.fire('object:selected', opt);
10724
+        }
10725
+        opt.selected = added;
10726
+        // added for backward compatibility
10727
+        opt.target = this._activeObject;
10728
+        this.fire('selection:created', opt);
10729
+      }
10730
+      else if (oldObjects.length > 0) {
10731
+        opt.deselected = removed;
10732
+        this.fire('selection:cleared', opt);
10733
+      }
10734
+    },
10735
+
10736
+    /**
10737
+     * Sets given object as the only active object on canvas
10738
+     * @param {fabric.Object} object Object to set as an active one
10739
+     * @param {Event} [e] Event (passed along when firing "object:selected")
10740
+     * @return {fabric.Canvas} thisArg
10741
+     * @chainable
10742
+     */
10743
+    setActiveObject: function (object, e) {
10744
+      var currentActives = this.getActiveObjects();
10745
+      this._setActiveObject(object, e);
10746
+      this._fireSelectionEvents(currentActives, e);
10747
+      return this;
10748
+    },
10749
+
10750
+    /**
10751
+     * @private
10752
+     * @param {Object} object to set as active
10753
+     * @param {Event} [e] Event (passed along when firing "object:selected")
10754
+     * @return {Boolean} true if the selection happened
10755
+     */
10756
+    _setActiveObject: function(object, e) {
10757
+      if (this._activeObject === object) {
10758
+        return false;
10759
+      }
10760
+      if (!this._discardActiveObject(e, object)) {
10761
+        return false;
10762
+      }
10763
+      if (object.onSelect({ e: e })) {
10764
+        return false;
10765
+      }
10766
+      this._activeObject = object;
10767
+      return true;
10768
+    },
10769
+
10770
+    /**
10771
+     * @private
10772
+     */
10773
+    _discardActiveObject: function(e, object) {
10774
+      var obj = this._activeObject;
10775
+      if (obj) {
10776
+        // onDeselect return TRUE to cancel selection;
10777
+        if (obj.onDeselect({ e: e, object: object })) {
10778
+          return false;
10779
+        }
10780
+        this._activeObject = null;
10781
+      }
10782
+      return true;
10783
+    },
10784
+
10785
+    /**
10786
+     * Discards currently active object and fire events. If the function is called by fabric
10787
+     * as a consequence of a mouse event, the event is passed as a parameter and
10788
+     * sent to the fire function for the custom events. When used as a method the
10789
+     * e param does not have any application.
10790
+     * @param {event} e
10791
+     * @return {fabric.Canvas} thisArg
10792
+     * @chainable
10793
+     */
10794
+    discardActiveObject: function (e) {
10795
+      var currentActives = this.getActiveObjects();
10796
+      if (currentActives.length) {
10797
+        this.fire('before:selection:cleared', { target: currentActives[0], e: e });
10798
+      }
10799
+      this._discardActiveObject(e);
10800
+      this._fireSelectionEvents(currentActives, e);
10801
+      return this;
10802
+    },
10803
+
10804
+    /**
10805
+     * Clears a canvas element and removes all event listeners
10806
+     * @return {fabric.Canvas} thisArg
10807
+     * @chainable
10808
+     */
10809
+    dispose: function () {
10810
+      var wrapper = this.wrapperEl;
10811
+      this.removeListeners();
10812
+      wrapper.removeChild(this.upperCanvasEl);
10813
+      wrapper.removeChild(this.lowerCanvasEl);
10814
+      this.contextCache = null;
10815
+      this.contextTop = null;
10816
+      ['upperCanvasEl', 'cacheCanvasEl'].forEach((function(element) {
10817
+        fabric.util.cleanUpJsdomNode(this[element]);
10818
+        this[element] = undefined;
10819
+      }).bind(this));
10820
+      if (wrapper.parentNode) {
10821
+        wrapper.parentNode.replaceChild(this.lowerCanvasEl, this.wrapperEl);
10822
+      }
10823
+      delete this.wrapperEl;
10824
+      fabric.StaticCanvas.prototype.dispose.call(this);
10825
+      return this;
10826
+    },
10827
+
10828
+    /**
10829
+     * Clears all contexts (background, main, top) of an instance
10830
+     * @return {fabric.Canvas} thisArg
10831
+     * @chainable
10832
+     */
10833
+    clear: function () {
10834
+      // this.discardActiveGroup();
10835
+      this.discardActiveObject();
10836
+      this.clearContext(this.contextTop);
10837
+      return this.callSuper('clear');
10838
+    },
10839
+
10840
+    /**
10841
+     * Draws objects' controls (borders/controls)
10842
+     * @param {CanvasRenderingContext2D} ctx Context to render controls on
10843
+     */
10844
+    drawControls: function(ctx) {
10845
+      var activeObject = this._activeObject;
10846
+
10847
+      if (activeObject) {
10848
+        activeObject._renderControls(ctx);
10849
+      }
10850
+    },
10851
+
10852
+    /**
10853
+     * @private
10854
+     */
10855
+    _toObject: function(instance, methodName, propertiesToInclude) {
10856
+      //If the object is part of the current selection group, it should
10857
+      //be transformed appropriately
10858
+      //i.e. it should be serialised as it would appear if the selection group
10859
+      //were to be destroyed.
10860
+      var originalProperties = this._realizeGroupTransformOnObject(instance),
10861
+          object = this.callSuper('_toObject', instance, methodName, propertiesToInclude);
10862
+      //Undo the damage we did by changing all of its properties
10863
+      this._unwindGroupTransformOnObject(instance, originalProperties);
10864
+      return object;
10865
+    },
10866
+
10867
+    /**
10868
+     * Realises an object's group transformation on it
10869
+     * @private
10870
+     * @param {fabric.Object} [instance] the object to transform (gets mutated)
10871
+     * @returns the original values of instance which were changed
10872
+     */
10873
+    _realizeGroupTransformOnObject: function(instance) {
10874
+      if (instance.group && instance.group.type === 'activeSelection' && this._activeObject === instance.group) {
10875
+        var layoutProps = ['angle', 'flipX', 'flipY', 'left', 'scaleX', 'scaleY', 'skewX', 'skewY', 'top'];
10876
+        //Copy all the positionally relevant properties across now
10877
+        var originalValues = {};
10878
+        layoutProps.forEach(function(prop) {
10879
+          originalValues[prop] = instance[prop];
10880
+        });
10881
+        this._activeObject.realizeTransform(instance);
10882
+        return originalValues;
10883
+      }
10884
+      else {
10885
+        return null;
10886
+      }
10887
+    },
10888
+
10889
+    /**
10890
+     * Restores the changed properties of instance
10891
+     * @private
10892
+     * @param {fabric.Object} [instance] the object to un-transform (gets mutated)
10893
+     * @param {Object} [originalValues] the original values of instance, as returned by _realizeGroupTransformOnObject
10894
+     */
10895
+    _unwindGroupTransformOnObject: function(instance, originalValues) {
10896
+      if (originalValues) {
10897
+        instance.set(originalValues);
10898
+      }
10899
+    },
10900
+
10901
+    /**
10902
+     * @private
10903
+     */
10904
+    _setSVGObject: function(markup, instance, reviver) {
10905
+      //If the object is in a selection group, simulate what would happen to that
10906
+      //object when the group is deselected
10907
+      var originalProperties = this._realizeGroupTransformOnObject(instance);
10908
+      this.callSuper('_setSVGObject', markup, instance, reviver);
10909
+      this._unwindGroupTransformOnObject(instance, originalProperties);
10910
+    },
10911
+
10912
+    setViewportTransform: function (vpt) {
10913
+      if (this.renderOnAddRemove && this._activeObject && this._activeObject.isEditing) {
10914
+        this._activeObject.clearContextTop();
10915
+      }
10916
+      fabric.StaticCanvas.prototype.setViewportTransform.call(this, vpt);
10917
+    }
10918
+  });
10919
+
10920
+  // copying static properties manually to work around Opera's bug,
10921
+  // where "prototype" property is enumerable and overrides existing prototype
10922
+  for (var prop in fabric.StaticCanvas) {
10923
+    if (prop !== 'prototype') {
10924
+      fabric.Canvas[prop] = fabric.StaticCanvas[prop];
10925
+    }
10926
+  }
10927
+
10928
+  if (fabric.isTouchSupported) {
10929
+    /** @ignore */
10930
+    fabric.Canvas.prototype._setCursorFromEvent = function() { };
10931
+  }
10932
+})();
10933
+
10934
+
10935
+(function() {
10936
+
10937
+  var cursorOffset = {
10938
+        mt: 0, // n
10939
+        tr: 1, // ne
10940
+        mr: 2, // e
10941
+        br: 3, // se
10942
+        mb: 4, // s
10943
+        bl: 5, // sw
10944
+        ml: 6, // w
10945
+        tl: 7 // nw
10946
+      },
10947
+      addListener = fabric.util.addListener,
10948
+      removeListener = fabric.util.removeListener,
10949
+      RIGHT_CLICK = 3, MIDDLE_CLICK = 2, LEFT_CLICK = 1,
10950
+      addEventOptions = { passive: false };
10951
+
10952
+  function checkClick(e, value) {
10953
+    return 'which' in e ? e.which === value : e.button === value - 1;
10954
+  }
10955
+
10956
+  fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ {
10957
+
10958
+    /**
10959
+     * Map of cursor style values for each of the object controls
10960
+     * @private
10961
+     */
10962
+    cursorMap: [
10963
+      'n-resize',
10964
+      'ne-resize',
10965
+      'e-resize',
10966
+      'se-resize',
10967
+      's-resize',
10968
+      'sw-resize',
10969
+      'w-resize',
10970
+      'nw-resize'
10971
+    ],
10972
+
10973
+    /**
10974
+     * Adds mouse listeners to canvas
10975
+     * @private
10976
+     */
10977
+    _initEventListeners: function () {
10978
+      // in case we initialized the class twice. This should not happen normally
10979
+      // but in some kind of applications where the canvas element may be changed
10980
+      // this is a workaround to having double listeners.
10981
+      this.removeListeners();
10982
+      this._bindEvents();
10983
+      this.addOrRemove(addListener, 'add');
10984
+    },
10985
+
10986
+    addOrRemove: function(functor, eventjsFunctor) {
10987
+      functor(fabric.window, 'resize', this._onResize);
10988
+      functor(this.upperCanvasEl, 'mousedown', this._onMouseDown);
10989
+      functor(this.upperCanvasEl, 'mousemove', this._onMouseMove, addEventOptions);
10990
+      functor(this.upperCanvasEl, 'mouseout', this._onMouseOut);
10991
+      functor(this.upperCanvasEl, 'mouseenter', this._onMouseEnter);
10992
+      functor(this.upperCanvasEl, 'wheel', this._onMouseWheel);
10993
+      functor(this.upperCanvasEl, 'contextmenu', this._onContextMenu);
10994
+      functor(this.upperCanvasEl, 'dblclick', this._onDoubleClick);
10995
+      functor(this.upperCanvasEl, 'touchstart', this._onMouseDown, addEventOptions);
10996
+      functor(this.upperCanvasEl, 'touchmove', this._onMouseMove, addEventOptions);
10997
+      functor(this.upperCanvasEl, 'dragover', this._onDragOver);
10998
+      functor(this.upperCanvasEl, 'dragenter', this._onDragEnter);
10999
+      functor(this.upperCanvasEl, 'dragleave', this._onDragLeave);
11000
+      functor(this.upperCanvasEl, 'drop', this._onDrop);
11001
+      if (typeof eventjs !== 'undefined' && eventjsFunctor in eventjs) {
11002
+        eventjs[eventjsFunctor](this.upperCanvasEl, 'gesture', this._onGesture);
11003
+        eventjs[eventjsFunctor](this.upperCanvasEl, 'drag', this._onDrag);
11004
+        eventjs[eventjsFunctor](this.upperCanvasEl, 'orientation', this._onOrientationChange);
11005
+        eventjs[eventjsFunctor](this.upperCanvasEl, 'shake', this._onShake);
11006
+        eventjs[eventjsFunctor](this.upperCanvasEl, 'longpress', this._onLongPress);
11007
+      }
11008
+    },
11009
+
11010
+    /**
11011
+     * Removes all event listeners
11012
+     */
11013
+    removeListeners: function() {
11014
+      this.addOrRemove(removeListener, 'remove');
11015
+      // if you dispose on a mouseDown, before mouse up, you need to clean document to...
11016
+      removeListener(fabric.document, 'mouseup', this._onMouseUp);
11017
+      removeListener(fabric.document, 'touchend', this._onMouseUp, addEventOptions);
11018
+      removeListener(fabric.document, 'mousemove', this._onMouseMove, addEventOptions);
11019
+      removeListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);
11020
+    },
11021
+
11022
+    /**
11023
+     * @private
11024
+     */
11025
+    _bindEvents: function() {
11026
+      if (this.eventsBound) {
11027
+        // for any reason we pass here twice we do not want to bind events twice.
11028
+        return;
11029
+      }
11030
+      this._onMouseDown = this._onMouseDown.bind(this);
11031
+      this._onMouseMove = this._onMouseMove.bind(this);
11032
+      this._onMouseUp = this._onMouseUp.bind(this);
11033
+      this._onResize = this._onResize.bind(this);
11034
+      this._onGesture = this._onGesture.bind(this);
11035
+      this._onDrag = this._onDrag.bind(this);
11036
+      this._onShake = this._onShake.bind(this);
11037
+      this._onLongPress = this._onLongPress.bind(this);
11038
+      this._onOrientationChange = this._onOrientationChange.bind(this);
11039
+      this._onMouseWheel = this._onMouseWheel.bind(this);
11040
+      this._onMouseOut = this._onMouseOut.bind(this);
11041
+      this._onMouseEnter = this._onMouseEnter.bind(this);
11042
+      this._onContextMenu = this._onContextMenu.bind(this);
11043
+      this._onDoubleClick = this._onDoubleClick.bind(this);
11044
+      this._onDragOver = this._onDragOver.bind(this);
11045
+      this._onDragEnter = this._simpleEventHandler.bind(this, 'dragenter');
11046
+      this._onDragLeave = this._simpleEventHandler.bind(this, 'dragleave');
11047
+      this._onDrop = this._simpleEventHandler.bind(this, 'drop');
11048
+      this.eventsBound = true;
11049
+    },
11050
+
11051
+    /**
11052
+     * @private
11053
+     * @param {Event} [e] Event object fired on Event.js gesture
11054
+     * @param {Event} [self] Inner Event object
11055
+     */
11056
+    _onGesture: function(e, self) {
11057
+      this.__onTransformGesture && this.__onTransformGesture(e, self);
11058
+    },
11059
+
11060
+    /**
11061
+     * @private
11062
+     * @param {Event} [e] Event object fired on Event.js drag
11063
+     * @param {Event} [self] Inner Event object
11064
+     */
11065
+    _onDrag: function(e, self) {
11066
+      this.__onDrag && this.__onDrag(e, self);
11067
+    },
11068
+
11069
+    /**
11070
+     * @private
11071
+     * @param {Event} [e] Event object fired on wheel event
11072
+     */
11073
+    _onMouseWheel: function(e) {
11074
+      this.__onMouseWheel(e);
11075
+    },
11076
+
11077
+    /**
11078
+     * @private
11079
+     * @param {Event} e Event object fired on mousedown
11080
+     */
11081
+    _onMouseOut: function(e) {
11082
+      var target = this._hoveredTarget;
11083
+      this.fire('mouse:out', { target: target, e: e });
11084
+      this._hoveredTarget = null;
11085
+      target && target.fire('mouseout', { e: e });
11086
+      if (this._iTextInstances) {
11087
+        this._iTextInstances.forEach(function(obj) {
11088
+          if (obj.isEditing) {
11089
+            obj.hiddenTextarea.focus();
11090
+          }
11091
+        });
11092
+      }
11093
+    },
11094
+
11095
+    /**
11096
+     * @private
11097
+     * @param {Event} e Event object fired on mouseenter
11098
+     */
11099
+    _onMouseEnter: function(e) {
11100
+      if (!this.findTarget(e)) {
11101
+        this.fire('mouse:over', { target: null, e: e });
11102
+        this._hoveredTarget = null;
11103
+      }
11104
+    },
11105
+
11106
+    /**
11107
+     * @private
11108
+     * @param {Event} [e] Event object fired on Event.js orientation change
11109
+     * @param {Event} [self] Inner Event object
11110
+     */
11111
+    _onOrientationChange: function(e, self) {
11112
+      this.__onOrientationChange && this.__onOrientationChange(e, self);
11113
+    },
11114
+
11115
+    /**
11116
+     * @private
11117
+     * @param {Event} [e] Event object fired on Event.js shake
11118
+     * @param {Event} [self] Inner Event object
11119
+     */
11120
+    _onShake: function(e, self) {
11121
+      this.__onShake && this.__onShake(e, self);
11122
+    },
11123
+
11124
+    /**
11125
+     * @private
11126
+     * @param {Event} [e] Event object fired on Event.js shake
11127
+     * @param {Event} [self] Inner Event object
11128
+     */
11129
+    _onLongPress: function(e, self) {
11130
+      this.__onLongPress && this.__onLongPress(e, self);
11131
+    },
11132
+
11133
+    /**
11134
+     * prevent default to allow drop event to be fired
11135
+     * @private
11136
+     * @param {Event} [e] Event object fired on Event.js shake
11137
+     */
11138
+    _onDragOver: function(e) {
11139
+      e.preventDefault();
11140
+      var target = this._simpleEventHandler('dragover', e);
11141
+      this._fireEnterLeaveEvents(target, e);
11142
+    },
11143
+
11144
+    /**
11145
+     * @private
11146
+     * @param {Event} e Event object fired on mousedown
11147
+     */
11148
+    _onContextMenu: function (e) {
11149
+      if (this.stopContextMenu) {
11150
+        e.stopPropagation();
11151
+        e.preventDefault();
11152
+      }
11153
+      return false;
11154
+    },
11155
+
11156
+    /**
11157
+     * @private
11158
+     * @param {Event} e Event object fired on mousedown
11159
+     */
11160
+    _onDoubleClick: function (e) {
11161
+      this._cacheTransformEventData(e);
11162
+      this._handleEvent(e, 'dblclick');
11163
+      this._resetTransformEventData(e);
11164
+    },
11165
+
11166
+    /**
11167
+     * @private
11168
+     * @param {Event} e Event object fired on mousedown
11169
+     */
11170
+    _onMouseDown: function (e) {
11171
+      this.__onMouseDown(e);
11172
+      this._resetTransformEventData();
11173
+      addListener(fabric.document, 'touchend', this._onMouseUp, addEventOptions);
11174
+      addListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);
11175
+
11176
+      removeListener(this.upperCanvasEl, 'mousemove', this._onMouseMove, addEventOptions);
11177
+      removeListener(this.upperCanvasEl, 'touchmove', this._onMouseMove, addEventOptions);
11178
+
11179
+      if (e.type === 'touchstart') {
11180
+        // Unbind mousedown to prevent double triggers from touch devices
11181
+        removeListener(this.upperCanvasEl, 'mousedown', this._onMouseDown);
11182
+      }
11183
+      else {
11184
+        addListener(fabric.document, 'mouseup', this._onMouseUp);
11185
+        addListener(fabric.document, 'mousemove', this._onMouseMove, addEventOptions);
11186
+      }
11187
+    },
11188
+
11189
+    /**
11190
+     * @private
11191
+     * @param {Event} e Event object fired on mouseup
11192
+     */
11193
+    _onMouseUp: function (e) {
11194
+      this.__onMouseUp(e);
11195
+      this._resetTransformEventData();
11196
+      removeListener(fabric.document, 'mouseup', this._onMouseUp);
11197
+      removeListener(fabric.document, 'touchend', this._onMouseUp, addEventOptions);
11198
+
11199
+      removeListener(fabric.document, 'mousemove', this._onMouseMove, addEventOptions);
11200
+      removeListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);
11201
+
11202
+      addListener(this.upperCanvasEl, 'mousemove', this._onMouseMove, addEventOptions);
11203
+      addListener(this.upperCanvasEl, 'touchmove', this._onMouseMove, addEventOptions);
11204
+
11205
+      if (e.type === 'touchend') {
11206
+        // Wait 400ms before rebinding mousedown to prevent double triggers
11207
+        // from touch devices
11208
+        var _this = this;
11209
+        setTimeout(function() {
11210
+          addListener(_this.upperCanvasEl, 'mousedown', _this._onMouseDown);
11211
+        }, 400);
11212
+      }
11213
+    },
11214
+
11215
+    /**
11216
+     * @private
11217
+     * @param {Event} e Event object fired on mousemove
11218
+     */
11219
+    _onMouseMove: function (e) {
11220
+      !this.allowTouchScrolling && e.preventDefault && e.preventDefault();
11221
+      this.__onMouseMove(e);
11222
+    },
11223
+
11224
+    /**
11225
+     * @private
11226
+     */
11227
+    _onResize: function () {
11228
+      this.calcOffset();
11229
+    },
11230
+
11231
+    /**
11232
+     * Decides whether the canvas should be redrawn in mouseup and mousedown events.
11233
+     * @private
11234
+     * @param {Object} target
11235
+     */
11236
+    _shouldRender: function(target) {
11237
+      var activeObject = this._activeObject;
11238
+
11239
+      if (
11240
+        !!activeObject !== !!target ||
11241
+        (activeObject && target && (activeObject !== target))
11242
+      ) {
11243
+        // this covers: switch of target, from target to no target, selection of target
11244
+        // multiSelection with key and mouse
11245
+        return true;
11246
+      }
11247
+      else if (activeObject && activeObject.isEditing) {
11248
+        // if we mouse up/down over a editing textbox a cursor change,
11249
+        // there is no need to re render
11250
+        return false;
11251
+      }
11252
+      return false;
11253
+    },
11254
+
11255
+    /**
11256
+     * Method that defines the actions when mouse is released on canvas.
11257
+     * The method resets the currentTransform parameters, store the image corner
11258
+     * position in the image object and render the canvas on top.
11259
+     * @private
11260
+     * @param {Event} e Event object fired on mouseup
11261
+     */
11262
+    __onMouseUp: function (e) {
11263
+      var target, transform = this._currentTransform,
11264
+          groupSelector = this._groupSelector, shouldRender = false,
11265
+          isClick = (!groupSelector || (groupSelector.left === 0 && groupSelector.top === 0));
11266
+      this._cacheTransformEventData(e);
11267
+      target = this._target;
11268
+      this._handleEvent(e, 'up:before');
11269
+      // if right/middle click just fire events and return
11270
+      // target undefined will make the _handleEvent search the target
11271
+      if (checkClick(e, RIGHT_CLICK)) {
11272
+        if (this.fireRightClick) {
11273
+          this._handleEvent(e, 'up', RIGHT_CLICK, isClick);
11274
+        }
11275
+        return;
11276
+      }
11277
+
11278
+      if (checkClick(e, MIDDLE_CLICK)) {
11279
+        if (this.fireMiddleClick) {
11280
+          this._handleEvent(e, 'up', MIDDLE_CLICK, isClick);
11281
+        }
11282
+        this._resetTransformEventData();
11283
+        return;
11284
+      }
11285
+
11286
+      if (this.isDrawingMode && this._isCurrentlyDrawing) {
11287
+        this._onMouseUpInDrawingMode(e);
11288
+        return;
11289
+      }
11290
+
11291
+      if (transform) {
11292
+        this._finalizeCurrentTransform(e);
11293
+        shouldRender = transform.actionPerformed;
11294
+      }
11295
+
11296
+      if (!isClick) {
11297
+        this._maybeGroupObjects(e);
11298
+        shouldRender || (shouldRender = this._shouldRender(target));
11299
+      }
11300
+      if (target) {
11301
+        target.isMoving = false;
11302
+      }
11303
+      this._setCursorFromEvent(e, target);
11304
+      this._handleEvent(e, 'up', LEFT_CLICK, isClick);
11305
+      this._groupSelector = null;
11306
+      this._currentTransform = null;
11307
+      // reset the target information about which corner is selected
11308
+      target && (target.__corner = 0);
11309
+      if (shouldRender) {
11310
+        this.requestRenderAll();
11311
+      }
11312
+      else if (!isClick) {
11313
+        this.renderTop();
11314
+      }
11315
+    },
11316
+
11317
+    /**
11318
+     * @private
11319
+     * Handle event firing for target and subtargets
11320
+     * @param {Event} e event from mouse
11321
+     * @param {String} eventType event to fire (up, down or move)
11322
+     * @return {Fabric.Object} target return the the target found, for internal reasons.
11323
+     */
11324
+    _simpleEventHandler: function(eventType, e) {
11325
+      var target = this.findTarget(e),
11326
+          targets = this.targets,
11327
+          options = {
11328
+            e: e,
11329
+            target: target,
11330
+            subTargets: targets,
11331
+          };
11332
+      this.fire(eventType, options);
11333
+      target && target.fire(eventType, options);
11334
+      if (!targets) {
11335
+        return target;
11336
+      }
11337
+      for (var i = 0; i < targets.length; i++) {
11338
+        targets[i].fire(eventType, options);
11339
+      }
11340
+      return target;
11341
+    },
11342
+
11343
+    /**
11344
+     * @private
11345
+     * Handle event firing for target and subtargets
11346
+     * @param {Event} e event from mouse
11347
+     * @param {String} eventType event to fire (up, down or move)
11348
+     * @param {fabric.Object} targetObj receiving event
11349
+     * @param {Number} [button] button used in the event 1 = left, 2 = middle, 3 = right
11350
+     * @param {Boolean} isClick for left button only, indicates that the mouse up happened without move.
11351
+     */
11352
+    _handleEvent: function(e, eventType, button, isClick) {
11353
+      var target = this._target,
11354
+          targets = this.targets || [],
11355
+          options = {
11356
+            e: e,
11357
+            target: target,
11358
+            subTargets: targets,
11359
+            button: button || LEFT_CLICK,
11360
+            isClick: isClick || false,
11361
+            pointer: this._pointer,
11362
+            absolutePointer: this._absolutePointer,
11363
+            transform: this._currentTransform
11364
+          };
11365
+      this.fire('mouse:' + eventType, options);
11366
+      target && target.fire('mouse' + eventType, options);
11367
+      for (var i = 0; i < targets.length; i++) {
11368
+        targets[i].fire('mouse' + eventType, options);
11369
+      }
11370
+    },
11371
+
11372
+    /**
11373
+     * @private
11374
+     * @param {Event} e send the mouse event that generate the finalize down, so it can be used in the event
11375
+     */
11376
+    _finalizeCurrentTransform: function(e) {
11377
+
11378
+      var transform = this._currentTransform,
11379
+          target = transform.target,
11380
+          eventName,
11381
+          options = {
11382
+            e: e,
11383
+            target: target,
11384
+            transform: transform,
11385
+          };
11386
+
11387
+      if (target._scaling) {
11388
+        target._scaling = false;
11389
+      }
11390
+
11391
+      target.setCoords();
11392
+
11393
+      if (transform.actionPerformed || (this.stateful && target.hasStateChanged())) {
11394
+        if (transform.actionPerformed) {
11395
+          eventName = this._addEventOptions(options, transform);
11396
+          this._fire(eventName, options);
11397
+        }
11398
+        this._fire('modified', options);
11399
+      }
11400
+    },
11401
+
11402
+    /**
11403
+     * Mutate option object in order to add by property and give back the event name.
11404
+     * @private
11405
+     * @param {Object} options to mutate
11406
+     * @param {Object} transform to inspect action from
11407
+     */
11408
+    _addEventOptions: function(options, transform) {
11409
+      // we can probably add more details at low cost
11410
+      // scale change, rotation changes, translation changes
11411
+      var eventName, by;
11412
+      switch (transform.action) {
11413
+        case 'scaleX':
11414
+          eventName = 'scaled';
11415
+          by = 'x';
11416
+          break;
11417
+        case 'scaleY':
11418
+          eventName = 'scaled';
11419
+          by = 'y';
11420
+          break;
11421
+        case 'skewX':
11422
+          eventName = 'skewed';
11423
+          by = 'x';
11424
+          break;
11425
+        case 'skewY':
11426
+          eventName = 'skewed';
11427
+          by = 'y';
11428
+          break;
11429
+        case 'scale':
11430
+          eventName = 'scaled';
11431
+          by = 'equally';
11432
+          break;
11433
+        case 'rotate':
11434
+          eventName = 'rotated';
11435
+          break;
11436
+        case 'drag':
11437
+          eventName = 'moved';
11438
+          break;
11439
+      }
11440
+      options.by = by;
11441
+      return eventName;
11442
+    },
11443
+
11444
+    /**
11445
+     * @private
11446
+     * @param {Event} e Event object fired on mousedown
11447
+     */
11448
+    _onMouseDownInDrawingMode: function(e) {
11449
+      this._isCurrentlyDrawing = true;
11450
+      if (this.getActiveObject()) {
11451
+        this.discardActiveObject(e).requestRenderAll();
11452
+      }
11453
+      if (this.clipTo) {
11454
+        fabric.util.clipContext(this, this.contextTop);
11455
+      }
11456
+      var pointer = this.getPointer(e);
11457
+      this.freeDrawingBrush.onMouseDown(pointer);
11458
+      this._handleEvent(e, 'down');
11459
+    },
11460
+
11461
+    /**
11462
+     * @private
11463
+     * @param {Event} e Event object fired on mousemove
11464
+     */
11465
+    _onMouseMoveInDrawingMode: function(e) {
11466
+      if (this._isCurrentlyDrawing) {
11467
+        var pointer = this.getPointer(e);
11468
+        this.freeDrawingBrush.onMouseMove(pointer);
11469
+      }
11470
+      this.setCursor(this.freeDrawingCursor);
11471
+      this._handleEvent(e, 'move');
11472
+    },
11473
+
11474
+    /**
11475
+     * @private
11476
+     * @param {Event} e Event object fired on mouseup
11477
+     */
11478
+    _onMouseUpInDrawingMode: function(e) {
11479
+      this._isCurrentlyDrawing = false;
11480
+      if (this.clipTo) {
11481
+        this.contextTop.restore();
11482
+      }
11483
+      this.freeDrawingBrush.onMouseUp();
11484
+      this._handleEvent(e, 'up');
11485
+    },
11486
+
11487
+    /**
11488
+     * Method that defines the actions when mouse is clicked on canvas.
11489
+     * The method inits the currentTransform parameters and renders all the
11490
+     * canvas so the current image can be placed on the top canvas and the rest
11491
+     * in on the container one.
11492
+     * @private
11493
+     * @param {Event} e Event object fired on mousedown
11494
+     */
11495
+    __onMouseDown: function (e) {
11496
+      this._cacheTransformEventData(e);
11497
+      this._handleEvent(e, 'down:before');
11498
+      var target = this._target;
11499
+      // if right click just fire events
11500
+      if (checkClick(e, RIGHT_CLICK)) {
11501
+        if (this.fireRightClick) {
11502
+          this._handleEvent(e, 'down', RIGHT_CLICK);
11503
+        }
11504
+        return;
11505
+      }
11506
+
11507
+      if (checkClick(e, MIDDLE_CLICK)) {
11508
+        if (this.fireMiddleClick) {
11509
+          this._handleEvent(e, 'down', MIDDLE_CLICK);
11510
+        }
11511
+        return;
11512
+      }
11513
+
11514
+      if (this.isDrawingMode) {
11515
+        this._onMouseDownInDrawingMode(e);
11516
+        return;
11517
+      }
11518
+
11519
+      // ignore if some object is being transformed at this moment
11520
+      if (this._currentTransform) {
11521
+        return;
11522
+      }
11523
+
11524
+      var pointer = this._pointer;
11525
+      // save pointer for check in __onMouseUp event
11526
+      this._previousPointer = pointer;
11527
+      var shouldRender = this._shouldRender(target),
11528
+          shouldGroup = this._shouldGroup(e, target);
11529
+      if (this._shouldClearSelection(e, target)) {
11530
+        this.discardActiveObject(e);
11531
+      }
11532
+      else if (shouldGroup) {
11533
+        this._handleGrouping(e, target);
11534
+        target = this._activeObject;
11535
+      }
11536
+
11537
+      if (this.selection && (!target ||
11538
+        (!target.selectable && !target.isEditing && target !== this._activeObject))) {
11539
+        this._groupSelector = {
11540
+          ex: pointer.x,
11541
+          ey: pointer.y,
11542
+          top: 0,
11543
+          left: 0
11544
+        };
11545
+      }
11546
+
11547
+      if (target) {
11548
+        var alreadySelected = target === this._activeObject;
11549
+        if (target.selectable) {
11550
+          this.setActiveObject(target, e);
11551
+        }
11552
+        if (target === this._activeObject && (target.__corner || !shouldGroup)) {
11553
+          this._setupCurrentTransform(e, target, alreadySelected);
11554
+        }
11555
+      }
11556
+      this._handleEvent(e, 'down');
11557
+      // we must renderAll so that we update the visuals
11558
+      (shouldRender || shouldGroup) && this.requestRenderAll();
11559
+    },
11560
+
11561
+    /**
11562
+     * reset cache form common information needed during event processing
11563
+     * @private
11564
+     */
11565
+    _resetTransformEventData: function() {
11566
+      this._target = null;
11567
+      this._pointer = null;
11568
+      this._absolutePointer = null;
11569
+    },
11570
+
11571
+    /**
11572
+     * Cache common information needed during event processing
11573
+     * @private
11574
+     * @param {Event} e Event object fired on event
11575
+     */
11576
+    _cacheTransformEventData: function(e) {
11577
+      // reset in order to avoid stale caching
11578
+      this._resetTransformEventData();
11579
+      this._pointer = this.getPointer(e, true);
11580
+      this._absolutePointer = this.restorePointerVpt(this._pointer);
11581
+      this._target = this._currentTransform ? this._currentTransform.target : this.findTarget(e) || null;
11582
+    },
11583
+
11584
+    /**
11585
+     * @private
11586
+     */
11587
+    _beforeTransform: function(e) {
11588
+      var t = this._currentTransform;
11589
+      this.stateful && t.target.saveState();
11590
+      this.fire('before:transform', {
11591
+        e: e,
11592
+        transform: t,
11593
+      });
11594
+      // determine if it's a drag or rotate case
11595
+      if (t.corner) {
11596
+        this.onBeforeScaleRotate(t.target);
11597
+      }
11598
+    },
11599
+
11600
+    /**
11601
+     * Method that defines the actions when mouse is hovering the canvas.
11602
+     * The currentTransform parameter will definde whether the user is rotating/scaling/translating
11603
+     * an image or neither of them (only hovering). A group selection is also possible and would cancel
11604
+     * all any other type of action.
11605
+     * In case of an image transformation only the top canvas will be rendered.
11606
+     * @private
11607
+     * @param {Event} e Event object fired on mousemove
11608
+     */
11609
+    __onMouseMove: function (e) {
11610
+      this._handleEvent(e, 'move:before');
11611
+      this._cacheTransformEventData(e);
11612
+      var target, pointer;
11613
+
11614
+      if (this.isDrawingMode) {
11615
+        this._onMouseMoveInDrawingMode(e);
11616
+        return;
11617
+      }
11618
+      if (typeof e.touches !== 'undefined' && e.touches.length > 1) {
11619
+        return;
11620
+      }
11621
+
11622
+      var groupSelector = this._groupSelector;
11623
+
11624
+      // We initially clicked in an empty area, so we draw a box for multiple selection
11625
+      if (groupSelector) {
11626
+        pointer = this._pointer;
11627
+
11628
+        groupSelector.left = pointer.x - groupSelector.ex;
11629
+        groupSelector.top = pointer.y - groupSelector.ey;
11630
+
11631
+        this.renderTop();
11632
+      }
11633
+      else if (!this._currentTransform) {
11634
+        target = this.findTarget(e) || null;
11635
+        this._setCursorFromEvent(e, target);
11636
+        this._fireOverOutEvents(target, e);
11637
+      }
11638
+      else {
11639
+        this._transformObject(e);
11640
+      }
11641
+      this._handleEvent(e, 'move');
11642
+      this._resetTransformEventData();
11643
+    },
11644
+
11645
+    /**
11646
+     * Manage the mouseout, mouseover events for the fabric object on the canvas
11647
+     * @param {Fabric.Object} target the target where the target from the mousemove event
11648
+     * @param {Event} e Event object fired on mousemove
11649
+     * @private
11650
+     */
11651
+    _fireOverOutEvents: function(target, e) {
11652
+      this.fireSynteticInOutEvents(target, e, {
11653
+        targetName: '_hoveredTarget',
11654
+        canvasEvtOut: 'mouse:out',
11655
+        evtOut: 'mouseout',
11656
+        canvasEvtIn: 'mouse:over',
11657
+        evtIn: 'mouseover',
11658
+      });
11659
+    },
11660
+
11661
+    /**
11662
+     * Manage the dragEnter, dragLeave events for the fabric objects on the canvas
11663
+     * @param {Fabric.Object} target the target where the target from the onDrag event
11664
+     * @param {Event} e Event object fired on ondrag
11665
+     * @private
11666
+     */
11667
+    _fireEnterLeaveEvents: function(target, e) {
11668
+      this.fireSynteticInOutEvents(target, e, {
11669
+        targetName: '_draggedoverTarget',
11670
+        evtOut: 'dragleave',
11671
+        evtIn: 'dragenter',
11672
+      });
11673
+    },
11674
+
11675
+    /**
11676
+     * Manage the syntetic in/out events for the fabric objects on the canvas
11677
+     * @param {Fabric.Object} target the target where the target from the supported events
11678
+     * @param {Event} e Event object fired
11679
+     * @param {Object} config configuration for the function to work
11680
+     * @param {String} config.targetName property on the canvas where the old target is stored
11681
+     * @param {String} [config.canvasEvtOut] name of the event to fire at canvas level for out
11682
+     * @param {String} config.evtOut name of the event to fire for out
11683
+     * @param {String} [config.canvasEvtIn] name of the event to fire at canvas level for in
11684
+     * @param {String} config.evtIn name of the event to fire for in
11685
+     * @private
11686
+     */
11687
+    fireSynteticInOutEvents: function(target, e, config) {
11688
+      var inOpt, outOpt, oldTarget = this[config.targetName], outFires, inFires,
11689
+          targetChanged = oldTarget !== target, canvasEvtIn = config.canvasEvtIn, canvasEvtOut = config.canvasEvtOut;
11690
+      if (targetChanged) {
11691
+        inOpt = { e: e, target: target, previousTarget: oldTarget };
11692
+        outOpt = { e: e, target: oldTarget, nextTarget: target };
11693
+        this[config.targetName] = target;
11694
+      }
11695
+      inFires = target && targetChanged;
11696
+      outFires = oldTarget && targetChanged;
11697
+      if (outFires) {
11698
+        canvasEvtOut && this.fire(canvasEvtOut, outOpt);
11699
+        oldTarget.fire(config.evtOut, outOpt);
11700
+      }
11701
+      if (inFires) {
11702
+        canvasEvtIn && this.fire(canvasEvtIn, inOpt);
11703
+        target.fire(config.evtIn, inOpt);
11704
+      }
11705
+    },
11706
+
11707
+    /**
11708
+     * Method that defines actions when an Event Mouse Wheel
11709
+     * @param {Event} e Event object fired on mouseup
11710
+     */
11711
+    __onMouseWheel: function(e) {
11712
+      this._cacheTransformEventData(e);
11713
+      this._handleEvent(e, 'wheel');
11714
+      this._resetTransformEventData();
11715
+    },
11716
+
11717
+    /**
11718
+     * @private
11719
+     * @param {Event} e Event fired on mousemove
11720
+     */
11721
+    _transformObject: function(e) {
11722
+      var pointer = this.getPointer(e),
11723
+          transform = this._currentTransform;
11724
+
11725
+      transform.reset = false;
11726
+      transform.target.isMoving = true;
11727
+      transform.shiftKey = e.shiftKey;
11728
+      transform.altKey = e[this.centeredKey];
11729
+
11730
+      this._beforeScaleTransform(e, transform);
11731
+      this._performTransformAction(e, transform, pointer);
11732
+
11733
+      transform.actionPerformed && this.requestRenderAll();
11734
+    },
11735
+
11736
+    /**
11737
+     * @private
11738
+     */
11739
+    _performTransformAction: function(e, transform, pointer) {
11740
+      var x = pointer.x,
11741
+          y = pointer.y,
11742
+          action = transform.action,
11743
+          actionPerformed = false,
11744
+          options = {
11745
+            target: transform.target,
11746
+            e: e,
11747
+            transform: transform,
11748
+            pointer: pointer
11749
+          };
11750
+
11751
+      if (action === 'rotate') {
11752
+        (actionPerformed = this._rotateObject(x, y)) && this._fire('rotating', options);
11753
+      }
11754
+      else if (action === 'scale') {
11755
+        (actionPerformed = this._onScale(e, transform, x, y)) && this._fire('scaling', options);
11756
+      }
11757
+      else if (action === 'scaleX') {
11758
+        (actionPerformed = this._scaleObject(x, y, 'x')) && this._fire('scaling', options);
11759
+      }
11760
+      else if (action === 'scaleY') {
11761
+        (actionPerformed = this._scaleObject(x, y, 'y')) && this._fire('scaling', options);
11762
+      }
11763
+      else if (action === 'skewX') {
11764
+        (actionPerformed = this._skewObject(x, y, 'x')) && this._fire('skewing', options);
11765
+      }
11766
+      else if (action === 'skewY') {
11767
+        (actionPerformed = this._skewObject(x, y, 'y')) && this._fire('skewing', options);
11768
+      }
11769
+      else {
11770
+        actionPerformed = this._translateObject(x, y);
11771
+        if (actionPerformed) {
11772
+          this._fire('moving', options);
11773
+          this.setCursor(options.target.moveCursor || this.moveCursor);
11774
+        }
11775
+      }
11776
+      transform.actionPerformed = transform.actionPerformed || actionPerformed;
11777
+    },
11778
+
11779
+    /**
11780
+     * @private
11781
+     */
11782
+    _fire: function(eventName, options) {
11783
+      this.fire('object:' + eventName, options);
11784
+      options.target.fire(eventName, options);
11785
+    },
11786
+
11787
+    /**
11788
+     * @private
11789
+     */
11790
+    _beforeScaleTransform: function(e, transform) {
11791
+      if (transform.action === 'scale' || transform.action === 'scaleX' || transform.action === 'scaleY') {
11792
+        var centerTransform = this._shouldCenterTransform(transform.target);
11793
+
11794
+        // Switch from a normal resize to center-based
11795
+        if ((centerTransform && (transform.originX !== 'center' || transform.originY !== 'center')) ||
11796
+           // Switch from center-based resize to normal one
11797
+           (!centerTransform && transform.originX === 'center' && transform.originY === 'center')
11798
+        ) {
11799
+          this._resetCurrentTransform();
11800
+          transform.reset = true;
11801
+        }
11802
+      }
11803
+    },
11804
+
11805
+    /**
11806
+     * @private
11807
+     * @param {Event} e Event object
11808
+     * @param {Object} transform current tranform
11809
+     * @param {Number} x mouse position x from origin
11810
+     * @param {Number} y mouse poistion y from origin
11811
+     * @return {Boolean} true if the scaling occurred
11812
+     */
11813
+    _onScale: function(e, transform, x, y) {
11814
+      if (this._isUniscalePossible(e, transform.target)) {
11815
+        transform.currentAction = 'scale';
11816
+        return this._scaleObject(x, y);
11817
+      }
11818
+      else {
11819
+        // Switch from a normal resize to proportional
11820
+        if (!transform.reset && transform.currentAction === 'scale') {
11821
+          this._resetCurrentTransform();
11822
+        }
11823
+
11824
+        transform.currentAction = 'scaleEqually';
11825
+        return this._scaleObject(x, y, 'equally');
11826
+      }
11827
+    },
11828
+
11829
+    /**
11830
+     * @private
11831
+     * @param {Event} e Event object
11832
+     * @param {fabric.Object} target current target
11833
+     * @return {Boolean} true if unproportional scaling is possible
11834
+     */
11835
+    _isUniscalePossible: function(e, target) {
11836
+      return (e[this.uniScaleKey] || this.uniScaleTransform) && !target.get('lockUniScaling');
11837
+    },
11838
+
11839
+    /**
11840
+     * Sets the cursor depending on where the canvas is being hovered.
11841
+     * Note: very buggy in Opera
11842
+     * @param {Event} e Event object
11843
+     * @param {Object} target Object that the mouse is hovering, if so.
11844
+     */
11845
+    _setCursorFromEvent: function (e, target) {
11846
+      if (!target) {
11847
+        this.setCursor(this.defaultCursor);
11848
+        return false;
11849
+      }
11850
+
11851
+      var hoverCursor = target.hoverCursor || this.hoverCursor,
11852
+          activeSelection = this._activeObject && this._activeObject.type === 'activeSelection' ?
11853
+            this._activeObject : null,
11854
+          // only show proper corner when group selection is not active
11855
+          corner = (!activeSelection || !activeSelection.contains(target))
11856
+                    && target._findTargetCorner(this.getPointer(e, true));
11857
+
11858
+      if (!corner) {
11859
+        this.setCursor(hoverCursor);
11860
+      }
11861
+      else {
11862
+        this.setCursor(this.getCornerCursor(corner, target, e));
11863
+      }
11864
+    },
11865
+
11866
+    /**
11867
+     * @private
11868
+     */
11869
+    getCornerCursor: function(corner, target, e) {
11870
+      if (this.actionIsDisabled(corner, target, e)) {
11871
+        return this.notAllowedCursor;
11872
+      }
11873
+      else if (corner in cursorOffset) {
11874
+        return this._getRotatedCornerCursor(corner, target, e);
11875
+      }
11876
+      else if (corner === 'mtr' && target.hasRotatingPoint) {
11877
+        return this.rotationCursor;
11878
+      }
11879
+      else {
11880
+        return this.defaultCursor;
11881
+      }
11882
+    },
11883
+
11884
+    actionIsDisabled: function(corner, target, e) {
11885
+      if (corner === 'mt' || corner === 'mb') {
11886
+        return e[this.altActionKey] ? target.lockSkewingX : target.lockScalingY;
11887
+      }
11888
+      else if (corner === 'ml' || corner === 'mr') {
11889
+        return e[this.altActionKey] ? target.lockSkewingY : target.lockScalingX;
11890
+      }
11891
+      else if (corner === 'mtr') {
11892
+        return target.lockRotation;
11893
+      }
11894
+      else {
11895
+        return this._isUniscalePossible(e, target) ?
11896
+          target.lockScalingX && target.lockScalingY : target.lockScalingX || target.lockScalingY;
11897
+      }
11898
+    },
11899
+
11900
+    /**
11901
+     * @private
11902
+     */
11903
+    _getRotatedCornerCursor: function(corner, target, e) {
11904
+      var n = Math.round((target.angle % 360) / 45);
11905
+
11906
+      if (n < 0) {
11907
+        n += 8; // full circle ahead
11908
+      }
11909
+      n += cursorOffset[corner];
11910
+      if (e[this.altActionKey] && cursorOffset[corner] % 2 === 0) {
11911
+        //if we are holding shift and we are on a mx corner...
11912
+        n += 2;
11913
+      }
11914
+      // normalize n to be from 0 to 7
11915
+      n %= 8;
11916
+
11917
+      return this.cursorMap[n];
11918
+    }
11919
+  });
11920
+})();
11921
+
11922
+
11923
+(function() {
11924
+
11925
+  var min = Math.min,
11926
+      max = Math.max;
11927
+
11928
+  fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ {
11929
+
11930
+    /**
11931
+     * @private
11932
+     * @param {Event} e Event object
11933
+     * @param {fabric.Object} target
11934
+     * @return {Boolean}
11935
+     */
11936
+    _shouldGroup: function(e, target) {
11937
+      var activeObject = this._activeObject;
11938
+      return activeObject && this._isSelectionKeyPressed(e) && target && target.selectable && this.selection &&
11939
+            (activeObject !== target || activeObject.type === 'activeSelection') && !target.onSelect({ e: e });
11940
+    },
11941
+
11942
+    /**
11943
+     * @private
11944
+     * @param {Event} e Event object
11945
+     * @param {fabric.Object} target
11946
+     */
11947
+    _handleGrouping: function (e, target) {
11948
+      var activeObject = this._activeObject;
11949
+      // avoid multi select when shift click on a corner
11950
+      if (activeObject.__corner) {
11951
+        return;
11952
+      }
11953
+      if (target === activeObject) {
11954
+        // if it's a group, find target again, using activeGroup objects
11955
+        target = this.findTarget(e, true);
11956
+        // if even object is not found or we are on activeObjectCorner, bail out
11957
+        if (!target || !target.selectable) {
11958
+          return;
11959
+        }
11960
+      }
11961
+      if (activeObject && activeObject.type === 'activeSelection') {
11962
+        this._updateActiveSelection(target, e);
11963
+      }
11964
+      else {
11965
+        this._createActiveSelection(target, e);
11966
+      }
11967
+    },
11968
+
11969
+    /**
11970
+     * @private
11971
+     */
11972
+    _updateActiveSelection: function(target, e) {
11973
+      var activeSelection = this._activeObject,
11974
+          currentActiveObjects = activeSelection._objects.slice(0);
11975
+      if (activeSelection.contains(target)) {
11976
+        activeSelection.removeWithUpdate(target);
11977
+        this._hoveredTarget = target;
11978
+        if (activeSelection.size() === 1) {
11979
+          // activate last remaining object
11980
+          this._setActiveObject(activeSelection.item(0), e);
11981
+        }
11982
+      }
11983
+      else {
11984
+        activeSelection.addWithUpdate(target);
11985
+        this._hoveredTarget = activeSelection;
11986
+      }
11987
+      this._fireSelectionEvents(currentActiveObjects, e);
11988
+    },
11989
+
11990
+    /**
11991
+     * @private
11992
+     */
11993
+    _createActiveSelection: function(target, e) {
11994
+      var currentActives = this.getActiveObjects(), group = this._createGroup(target);
11995
+      this._hoveredTarget = group;
11996
+      this._setActiveObject(group, e);
11997
+      this._fireSelectionEvents(currentActives, e);
11998
+    },
11999
+
12000
+    /**
12001
+     * @private
12002
+     * @param {Object} target
12003
+     */
12004
+    _createGroup: function(target) {
12005
+      var objects = this._objects,
12006
+          isActiveLower = objects.indexOf(this._activeObject) < objects.indexOf(target),
12007
+          groupObjects = isActiveLower
12008
+            ? [this._activeObject, target]
12009
+            : [target, this._activeObject];
12010
+      this._activeObject.isEditing && this._activeObject.exitEditing();
12011
+      return new fabric.ActiveSelection(groupObjects, {
12012
+        canvas: this
12013
+      });
12014
+    },
12015
+
12016
+    /**
12017
+     * @private
12018
+     * @param {Event} e mouse event
12019
+     */
12020
+    _groupSelectedObjects: function (e) {
12021
+
12022
+      var group = this._collectObjects(e),
12023
+          aGroup;
12024
+
12025
+      // do not create group for 1 element only
12026
+      if (group.length === 1) {
12027
+        this.setActiveObject(group[0], e);
12028
+      }
12029
+      else if (group.length > 1) {
12030
+        aGroup = new fabric.ActiveSelection(group.reverse(), {
12031
+          canvas: this
12032
+        });
12033
+        this.setActiveObject(aGroup, e);
12034
+      }
12035
+    },
12036
+
12037
+    /**
12038
+     * @private
12039
+     */
12040
+    _collectObjects: function(e) {
12041
+      var group = [],
12042
+          currentObject,
12043
+          x1 = this._groupSelector.ex,
12044
+          y1 = this._groupSelector.ey,
12045
+          x2 = x1 + this._groupSelector.left,
12046
+          y2 = y1 + this._groupSelector.top,
12047
+          selectionX1Y1 = new fabric.Point(min(x1, x2), min(y1, y2)),
12048
+          selectionX2Y2 = new fabric.Point(max(x1, x2), max(y1, y2)),
12049
+          allowIntersect = !this.selectionFullyContained,
12050
+          isClick = x1 === x2 && y1 === y2;
12051
+      // we iterate reverse order to collect top first in case of click.
12052
+      for (var i = this._objects.length; i--; ) {
12053
+        currentObject = this._objects[i];
12054
+
12055
+        if (!currentObject || !currentObject.selectable || !currentObject.visible || currentObject.onSelect({ e: e })) {
12056
+          continue;
12057
+        }
12058
+
12059
+        if ((allowIntersect && currentObject.intersectsWithRect(selectionX1Y1, selectionX2Y2)) ||
12060
+            currentObject.isContainedWithinRect(selectionX1Y1, selectionX2Y2) ||
12061
+            (allowIntersect && currentObject.containsPoint(selectionX1Y1)) ||
12062
+            (allowIntersect && currentObject.containsPoint(selectionX2Y2))
12063
+        ) {
12064
+          group.push(currentObject);
12065
+
12066
+          // only add one object if it's a click
12067
+          if (isClick) {
12068
+            break;
12069
+          }
12070
+        }
12071
+      }
12072
+
12073
+      return group;
12074
+    },
12075
+
12076
+    /**
12077
+     * @private
12078
+     */
12079
+    _maybeGroupObjects: function(e) {
12080
+      if (this.selection && this._groupSelector) {
12081
+        this._groupSelectedObjects(e);
12082
+      }
12083
+      this.setCursor(this.defaultCursor);
12084
+      // clear selection and current transformation
12085
+      this._groupSelector = null;
12086
+    }
12087
+  });
12088
+
12089
+})();
12090
+
12091
+
12092
+(function () {
12093
+
12094
+  var supportQuality = fabric.StaticCanvas.supports('toDataURLWithQuality');
12095
+
12096
+  fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
12097
+
12098
+    /**
12099
+     * Exports canvas element to a dataurl image. Note that when multiplier is used, cropping is scaled appropriately
12100
+     * @param {Object} [options] Options object
12101
+     * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
12102
+     * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
12103
+     * @param {Number} [options.multiplier=1] Multiplier to scale by, to have consistent
12104
+     * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
12105
+     * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
12106
+     * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
12107
+     * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
12108
+     * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 2.0.0
12109
+     * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
12110
+     * @see {@link http://jsfiddle.net/fabricjs/NfZVb/|jsFiddle demo}
12111
+     * @example <caption>Generate jpeg dataURL with lower quality</caption>
12112
+     * var dataURL = canvas.toDataURL({
12113
+     *   format: 'jpeg',
12114
+     *   quality: 0.8
12115
+     * });
12116
+     * @example <caption>Generate cropped png dataURL (clipping of canvas)</caption>
12117
+     * var dataURL = canvas.toDataURL({
12118
+     *   format: 'png',
12119
+     *   left: 100,
12120
+     *   top: 100,
12121
+     *   width: 200,
12122
+     *   height: 200
12123
+     * });
12124
+     * @example <caption>Generate double scaled png dataURL</caption>
12125
+     * var dataURL = canvas.toDataURL({
12126
+     *   format: 'png',
12127
+     *   multiplier: 2
12128
+     * });
12129
+     */
12130
+    toDataURL: function (options) {
12131
+      options || (options = { });
12132
+
12133
+      var format = options.format || 'png',
12134
+          quality = options.quality || 1,
12135
+          multiplier = (options.multiplier || 1) * (options.enableRetinaScaling ? 1 : 1 / this.getRetinaScaling()),
12136
+          cropping = {
12137
+            left: options.left || 0,
12138
+            top: options.top || 0,
12139
+            width: options.width || 0,
12140
+            height: options.height || 0,
12141
+          };
12142
+      return this.__toDataURLWithMultiplier(format, quality, cropping, multiplier);
12143
+    },
12144
+
12145
+    /**
12146
+     * @private
12147
+     */
12148
+    __toDataURLWithMultiplier: function(format, quality, cropping, multiplier) {
12149
+
12150
+      var origWidth = this.width,
12151
+          origHeight = this.height,
12152
+          scaledWidth = (cropping.width || this.width) * multiplier,
12153
+          scaledHeight = (cropping.height || this.height) * multiplier,
12154
+          zoom = this.getZoom(),
12155
+          newZoom = zoom * multiplier,
12156
+          vp = this.viewportTransform,
12157
+          translateX = (vp[4] - cropping.left) * multiplier,
12158
+          translateY = (vp[5] - cropping.top) * multiplier,
12159
+          newVp = [newZoom, 0, 0, newZoom, translateX, translateY],
12160
+          originalInteractive = this.interactive,
12161
+          originalSkipOffScreen = this.skipOffscreen,
12162
+          needsResize = origWidth !== scaledWidth || origHeight !== scaledHeight;
12163
+
12164
+      this.viewportTransform = newVp;
12165
+      this.skipOffscreen = false;
12166
+      // setting interactive to false avoid exporting controls
12167
+      this.interactive = false;
12168
+      if (needsResize) {
12169
+        this.setDimensions({ width: scaledWidth, height: scaledHeight }, { backstoreOnly: true });
12170
+      }
12171
+      // call a renderAll to force sync update. This will cancel the scheduled requestRenderAll
12172
+      // from setDimensions
12173
+      this.renderAll();
12174
+      var data = this.__toDataURL(format, quality, cropping);
12175
+      this.interactive = originalInteractive;
12176
+      this.skipOffscreen = originalSkipOffScreen;
12177
+      this.viewportTransform = vp;
12178
+      //setDimensions with no option object is taking care of:
12179
+      //this.width, this.height, this.requestRenderAll()
12180
+      if (needsResize) {
12181
+        this.setDimensions({ width: origWidth, height: origHeight }, { backstoreOnly: true });
12182
+      }
12183
+      this.renderAll();
12184
+      return data;
12185
+    },
12186
+
12187
+    /**
12188
+     * @private
12189
+     */
12190
+    __toDataURL: function(format, quality) {
12191
+
12192
+      var canvasEl = this.contextContainer.canvas;
12193
+      var data = supportQuality
12194
+        ? canvasEl.toDataURL('image/' + format, quality)
12195
+        : canvasEl.toDataURL('image/' + format);
12196
+
12197
+      return data;
12198
+    },
12199
+  });
12200
+
12201
+})();
12202
+
12203
+
12204
+fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
12205
+
12206
+  /**
12207
+   * Populates canvas with data from the specified dataless JSON.
12208
+   * JSON format must conform to the one of {@link fabric.Canvas#toDatalessJSON}
12209
+   * @deprecated since 1.2.2
12210
+   * @param {String|Object} json JSON string or object
12211
+   * @param {Function} callback Callback, invoked when json is parsed
12212
+   *                            and corresponding objects (e.g: {@link fabric.Image})
12213
+   *                            are initialized
12214
+   * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created.
12215
+   * @return {fabric.Canvas} instance
12216
+   * @chainable
12217
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#deserialization}
12218
+   */
12219
+  loadFromDatalessJSON: function (json, callback, reviver) {
12220
+    return this.loadFromJSON(json, callback, reviver);
12221
+  },
12222
+
12223
+  /**
12224
+   * Populates canvas with data from the specified JSON.
12225
+   * JSON format must conform to the one of {@link fabric.Canvas#toJSON}
12226
+   * @param {String|Object} json JSON string or object
12227
+   * @param {Function} callback Callback, invoked when json is parsed
12228
+   *                            and corresponding objects (e.g: {@link fabric.Image})
12229
+   *                            are initialized
12230
+   * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created.
12231
+   * @return {fabric.Canvas} instance
12232
+   * @chainable
12233
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#deserialization}
12234
+   * @see {@link http://jsfiddle.net/fabricjs/fmgXt/|jsFiddle demo}
12235
+   * @example <caption>loadFromJSON</caption>
12236
+   * canvas.loadFromJSON(json, canvas.renderAll.bind(canvas));
12237
+   * @example <caption>loadFromJSON with reviver</caption>
12238
+   * canvas.loadFromJSON(json, canvas.renderAll.bind(canvas), function(o, object) {
12239
+   *   // `o` = json object
12240
+   *   // `object` = fabric.Object instance
12241
+   *   // ... do some stuff ...
12242
+   * });
12243
+   */
12244
+  loadFromJSON: function (json, callback, reviver) {
12245
+    if (!json) {
12246
+      return;
12247
+    }
12248
+
12249
+    // serialize if it wasn't already
12250
+    var serialized = (typeof json === 'string')
12251
+      ? JSON.parse(json)
12252
+      : fabric.util.object.clone(json);
12253
+
12254
+    var _this = this,
12255
+        renderOnAddRemove = this.renderOnAddRemove;
12256
+    this.renderOnAddRemove = false;
12257
+
12258
+    this._enlivenObjects(serialized.objects, function (enlivenedObjects) {
12259
+      _this.clear();
12260
+      _this._setBgOverlay(serialized, function () {
12261
+        enlivenedObjects.forEach(function(obj, index) {
12262
+          // we splice the array just in case some custom classes restored from JSON
12263
+          // will add more object to canvas at canvas init.
12264
+          _this.insertAt(obj, index);
12265
+        });
12266
+        _this.renderOnAddRemove = renderOnAddRemove;
12267
+        // remove parts i cannot set as options
12268
+        delete serialized.objects;
12269
+        delete serialized.backgroundImage;
12270
+        delete serialized.overlayImage;
12271
+        delete serialized.background;
12272
+        delete serialized.overlay;
12273
+        // this._initOptions does too many things to just
12274
+        // call it. Normally loading an Object from JSON
12275
+        // create the Object instance. Here the Canvas is
12276
+        // already an instance and we are just loading things over it
12277
+        _this._setOptions(serialized);
12278
+        _this.renderAll();
12279
+        callback && callback();
12280
+      });
12281
+    }, reviver);
12282
+    return this;
12283
+  },
12284
+
12285
+  /**
12286
+   * @private
12287
+   * @param {Object} serialized Object with background and overlay information
12288
+   * @param {Function} callback Invoked after all background and overlay images/patterns loaded
12289
+   */
12290
+  _setBgOverlay: function(serialized, callback) {
12291
+    var loaded = {
12292
+      backgroundColor: false,
12293
+      overlayColor: false,
12294
+      backgroundImage: false,
12295
+      overlayImage: false
12296
+    };
12297
+
12298
+    if (!serialized.backgroundImage && !serialized.overlayImage && !serialized.background && !serialized.overlay) {
12299
+      callback && callback();
12300
+      return;
12301
+    }
12302
+
12303
+    var cbIfLoaded = function () {
12304
+      if (loaded.backgroundImage && loaded.overlayImage && loaded.backgroundColor && loaded.overlayColor) {
12305
+        callback && callback();
12306
+      }
12307
+    };
12308
+
12309
+    this.__setBgOverlay('backgroundImage', serialized.backgroundImage, loaded, cbIfLoaded);
12310
+    this.__setBgOverlay('overlayImage', serialized.overlayImage, loaded, cbIfLoaded);
12311
+    this.__setBgOverlay('backgroundColor', serialized.background, loaded, cbIfLoaded);
12312
+    this.__setBgOverlay('overlayColor', serialized.overlay, loaded, cbIfLoaded);
12313
+  },
12314
+
12315
+  /**
12316
+   * @private
12317
+   * @param {String} property Property to set (backgroundImage, overlayImage, backgroundColor, overlayColor)
12318
+   * @param {(Object|String)} value Value to set
12319
+   * @param {Object} loaded Set loaded property to true if property is set
12320
+   * @param {Object} callback Callback function to invoke after property is set
12321
+   */
12322
+  __setBgOverlay: function(property, value, loaded, callback) {
12323
+    var _this = this;
12324
+
12325
+    if (!value) {
12326
+      loaded[property] = true;
12327
+      callback && callback();
12328
+      return;
12329
+    }
12330
+
12331
+    if (property === 'backgroundImage' || property === 'overlayImage') {
12332
+      fabric.util.enlivenObjects([value], function(enlivedObject){
12333
+        _this[property] = enlivedObject[0];
12334
+        loaded[property] = true;
12335
+        callback && callback();
12336
+      });
12337
+    }
12338
+    else {
12339
+      this['set' + fabric.util.string.capitalize(property, true)](value, function() {
12340
+        loaded[property] = true;
12341
+        callback && callback();
12342
+      });
12343
+    }
12344
+  },
12345
+
12346
+  /**
12347
+   * @private
12348
+   * @param {Array} objects
12349
+   * @param {Function} callback
12350
+   * @param {Function} [reviver]
12351
+   */
12352
+  _enlivenObjects: function (objects, callback, reviver) {
12353
+    if (!objects || objects.length === 0) {
12354
+      callback && callback([]);
12355
+      return;
12356
+    }
12357
+
12358
+    fabric.util.enlivenObjects(objects, function(enlivenedObjects) {
12359
+      callback && callback(enlivenedObjects);
12360
+    }, null, reviver);
12361
+  },
12362
+
12363
+  /**
12364
+   * @private
12365
+   * @param {String} format
12366
+   * @param {Function} callback
12367
+   */
12368
+  _toDataURL: function (format, callback) {
12369
+    this.clone(function (clone) {
12370
+      callback(clone.toDataURL(format));
12371
+    });
12372
+  },
12373
+
12374
+  /**
12375
+   * @private
12376
+   * @param {String} format
12377
+   * @param {Number} multiplier
12378
+   * @param {Function} callback
12379
+   */
12380
+  _toDataURLWithMultiplier: function (format, multiplier, callback) {
12381
+    this.clone(function (clone) {
12382
+      callback(clone.toDataURLWithMultiplier(format, multiplier));
12383
+    });
12384
+  },
12385
+
12386
+  /**
12387
+   * Clones canvas instance
12388
+   * @param {Object} [callback] Receives cloned instance as a first argument
12389
+   * @param {Array} [properties] Array of properties to include in the cloned canvas and children
12390
+   */
12391
+  clone: function (callback, properties) {
12392
+    var data = JSON.stringify(this.toJSON(properties));
12393
+    this.cloneWithoutData(function(clone) {
12394
+      clone.loadFromJSON(data, function() {
12395
+        callback && callback(clone);
12396
+      });
12397
+    });
12398
+  },
12399
+
12400
+  /**
12401
+   * Clones canvas instance without cloning existing data.
12402
+   * This essentially copies canvas dimensions, clipping properties, etc.
12403
+   * but leaves data empty (so that you can populate it with your own)
12404
+   * @param {Object} [callback] Receives cloned instance as a first argument
12405
+   */
12406
+  cloneWithoutData: function(callback) {
12407
+    var el = fabric.util.createCanvasElement();
12408
+
12409
+    el.width = this.width;
12410
+    el.height = this.height;
12411
+
12412
+    var clone = new fabric.Canvas(el);
12413
+    clone.clipTo = this.clipTo;
12414
+    if (this.backgroundImage) {
12415
+      clone.setBackgroundImage(this.backgroundImage.src, function() {
12416
+        clone.renderAll();
12417
+        callback && callback(clone);
12418
+      });
12419
+      clone.backgroundImageOpacity = this.backgroundImageOpacity;
12420
+      clone.backgroundImageStretch = this.backgroundImageStretch;
12421
+    }
12422
+    else {
12423
+      callback && callback(clone);
12424
+    }
12425
+  }
12426
+});
12427
+
12428
+
12429
+(function(global) {
12430
+
12431
+  'use strict';
12432
+
12433
+  var fabric = global.fabric || (global.fabric = { }),
12434
+      extend = fabric.util.object.extend,
12435
+      clone = fabric.util.object.clone,
12436
+      toFixed = fabric.util.toFixed,
12437
+      capitalize = fabric.util.string.capitalize,
12438
+      degreesToRadians = fabric.util.degreesToRadians,
12439
+      supportsLineDash = fabric.StaticCanvas.supports('setLineDash'),
12440
+      objectCaching = !fabric.isLikelyNode,
12441
+      ALIASING_LIMIT = 2;
12442
+
12443
+  if (fabric.Object) {
12444
+    return;
12445
+  }
12446
+
12447
+  /**
12448
+   * Root object class from which all 2d shape classes inherit from
12449
+   * @class fabric.Object
12450
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-1#objects}
12451
+   * @see {@link fabric.Object#initialize} for constructor definition
12452
+   *
12453
+   * @fires added
12454
+   * @fires removed
12455
+   *
12456
+   * @fires selected
12457
+   * @fires deselected
12458
+   * @fires modified
12459
+   * @fires modified
12460
+   * @fires moved
12461
+   * @fires scaled
12462
+   * @fires rotated
12463
+   * @fires skewed
12464
+   *
12465
+   * @fires rotating
12466
+   * @fires scaling
12467
+   * @fires moving
12468
+   * @fires skewing
12469
+   *
12470
+   * @fires mousedown
12471
+   * @fires mouseup
12472
+   * @fires mouseover
12473
+   * @fires mouseout
12474
+   * @fires mousewheel
12475
+   * @fires mousedblclick
12476
+   *
12477
+   * @fires dragover
12478
+   * @fires dragenter
12479
+   * @fires dragleave
12480
+   * @fires drop
12481
+   */
12482
+  fabric.Object = fabric.util.createClass(fabric.CommonMethods, /** @lends fabric.Object.prototype */ {
12483
+
12484
+    /**
12485
+     * Type of an object (rect, circle, path, etc.).
12486
+     * Note that this property is meant to be read-only and not meant to be modified.
12487
+     * If you modify, certain parts of Fabric (such as JSON loading) won't work correctly.
12488
+     * @type String
12489
+     * @default
12490
+     */
12491
+    type:                     'object',
12492
+
12493
+    /**
12494
+     * Horizontal origin of transformation of an object (one of "left", "right", "center")
12495
+     * See http://jsfiddle.net/1ow02gea/244/ on how originX/originY affect objects in groups
12496
+     * @type String
12497
+     * @default
12498
+     */
12499
+    originX:                  'left',
12500
+
12501
+    /**
12502
+     * Vertical origin of transformation of an object (one of "top", "bottom", "center")
12503
+     * See http://jsfiddle.net/1ow02gea/244/ on how originX/originY affect objects in groups
12504
+     * @type String
12505
+     * @default
12506
+     */
12507
+    originY:                  'top',
12508
+
12509
+    /**
12510
+     * Top position of an object. Note that by default it's relative to object top. You can change this by setting originY={top/center/bottom}
12511
+     * @type Number
12512
+     * @default
12513
+     */
12514
+    top:                      0,
12515
+
12516
+    /**
12517
+     * Left position of an object. Note that by default it's relative to object left. You can change this by setting originX={left/center/right}
12518
+     * @type Number
12519
+     * @default
12520
+     */
12521
+    left:                     0,
12522
+
12523
+    /**
12524
+     * Object width
12525
+     * @type Number
12526
+     * @default
12527
+     */
12528
+    width:                    0,
12529
+
12530
+    /**
12531
+     * Object height
12532
+     * @type Number
12533
+     * @default
12534
+     */
12535
+    height:                   0,
12536
+
12537
+    /**
12538
+     * Object scale factor (horizontal)
12539
+     * @type Number
12540
+     * @default
12541
+     */
12542
+    scaleX:                   1,
12543
+
12544
+    /**
12545
+     * Object scale factor (vertical)
12546
+     * @type Number
12547
+     * @default
12548
+     */
12549
+    scaleY:                   1,
12550
+
12551
+    /**
12552
+     * When true, an object is rendered as flipped horizontally
12553
+     * @type Boolean
12554
+     * @default
12555
+     */
12556
+    flipX:                    false,
12557
+
12558
+    /**
12559
+     * When true, an object is rendered as flipped vertically
12560
+     * @type Boolean
12561
+     * @default
12562
+     */
12563
+    flipY:                    false,
12564
+
12565
+    /**
12566
+     * Opacity of an object
12567
+     * @type Number
12568
+     * @default
12569
+     */
12570
+    opacity:                  1,
12571
+
12572
+    /**
12573
+     * Angle of rotation of an object (in degrees)
12574
+     * @type Number
12575
+     * @default
12576
+     */
12577
+    angle:                    0,
12578
+
12579
+    /**
12580
+     * Angle of skew on x axes of an object (in degrees)
12581
+     * @type Number
12582
+     * @default
12583
+     */
12584
+    skewX:                    0,
12585
+
12586
+    /**
12587
+     * Angle of skew on y axes of an object (in degrees)
12588
+     * @type Number
12589
+     * @default
12590
+     */
12591
+    skewY:                    0,
12592
+
12593
+    /**
12594
+     * Size of object's controlling corners (in pixels)
12595
+     * @type Number
12596
+     * @default
12597
+     */
12598
+    cornerSize:               13,
12599
+
12600
+    /**
12601
+     * When true, object's controlling corners are rendered as transparent inside (i.e. stroke instead of fill)
12602
+     * @type Boolean
12603
+     * @default
12604
+     */
12605
+    transparentCorners:       true,
12606
+
12607
+    /**
12608
+     * Default cursor value used when hovering over this object on canvas
12609
+     * @type String
12610
+     * @default
12611
+     */
12612
+    hoverCursor:              null,
12613
+
12614
+    /**
12615
+     * Default cursor value used when moving this object on canvas
12616
+     * @type String
12617
+     * @default
12618
+     */
12619
+    moveCursor:               null,
12620
+
12621
+    /**
12622
+     * Padding between object and its controlling borders (in pixels)
12623
+     * @type Number
12624
+     * @default
12625
+     */
12626
+    padding:                  0,
12627
+
12628
+    /**
12629
+     * Color of controlling borders of an object (when it's active)
12630
+     * @type String
12631
+     * @default
12632
+     */
12633
+    borderColor:              'rgba(102,153,255,0.75)',
12634
+
12635
+    /**
12636
+     * Array specifying dash pattern of an object's borders (hasBorder must be true)
12637
+     * @since 1.6.2
12638
+     * @type Array
12639
+     */
12640
+    borderDashArray:          null,
12641
+
12642
+    /**
12643
+     * Color of controlling corners of an object (when it's active)
12644
+     * @type String
12645
+     * @default
12646
+     */
12647
+    cornerColor:              'rgba(102,153,255,0.5)',
12648
+
12649
+    /**
12650
+     * Color of controlling corners of an object (when it's active and transparentCorners false)
12651
+     * @since 1.6.2
12652
+     * @type String
12653
+     * @default
12654
+     */
12655
+    cornerStrokeColor:        null,
12656
+
12657
+    /**
12658
+     * Specify style of control, 'rect' or 'circle'
12659
+     * @since 1.6.2
12660
+     * @type String
12661
+     */
12662
+    cornerStyle:          'rect',
12663
+
12664
+    /**
12665
+     * Array specifying dash pattern of an object's control (hasBorder must be true)
12666
+     * @since 1.6.2
12667
+     * @type Array
12668
+     */
12669
+    cornerDashArray:          null,
12670
+
12671
+    /**
12672
+     * When true, this object will use center point as the origin of transformation
12673
+     * when being scaled via the controls.
12674
+     * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
12675
+     * @since 1.3.4
12676
+     * @type Boolean
12677
+     * @default
12678
+     */
12679
+    centeredScaling:          false,
12680
+
12681
+    /**
12682
+     * When true, this object will use center point as the origin of transformation
12683
+     * when being rotated via the controls.
12684
+     * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
12685
+     * @since 1.3.4
12686
+     * @type Boolean
12687
+     * @default
12688
+     */
12689
+    centeredRotation:         true,
12690
+
12691
+    /**
12692
+     * Color of object's fill
12693
+     * takes css colors https://www.w3.org/TR/css-color-3/
12694
+     * @type String
12695
+     * @default
12696
+     */
12697
+    fill:                     'rgb(0,0,0)',
12698
+
12699
+    /**
12700
+     * Fill rule used to fill an object
12701
+     * accepted values are nonzero, evenodd
12702
+     * <b>Backwards incompatibility note:</b> This property was used for setting globalCompositeOperation until v1.4.12 (use `fabric.Object#globalCompositeOperation` instead)
12703
+     * @type String
12704
+     * @default
12705
+     */
12706
+    fillRule:                 'nonzero',
12707
+
12708
+    /**
12709
+     * Composite rule used for canvas globalCompositeOperation
12710
+     * @type String
12711
+     * @default
12712
+     */
12713
+    globalCompositeOperation: 'source-over',
12714
+
12715
+    /**
12716
+     * Background color of an object.
12717
+     * takes css colors https://www.w3.org/TR/css-color-3/
12718
+     * @type String
12719
+     * @default
12720
+     */
12721
+    backgroundColor:          '',
12722
+
12723
+    /**
12724
+     * Selection Background color of an object. colored layer behind the object when it is active.
12725
+     * does not mix good with globalCompositeOperation methods.
12726
+     * @type String
12727
+     * @default
12728
+     */
12729
+    selectionBackgroundColor:          '',
12730
+
12731
+    /**
12732
+     * When defined, an object is rendered via stroke and this property specifies its color
12733
+     * takes css colors https://www.w3.org/TR/css-color-3/
12734
+     * @type String
12735
+     * @default
12736
+     */
12737
+    stroke:                   null,
12738
+
12739
+    /**
12740
+     * Width of a stroke used to render this object
12741
+     * @type Number
12742
+     * @default
12743
+     */
12744
+    strokeWidth:              1,
12745
+
12746
+    /**
12747
+     * Array specifying dash pattern of an object's stroke (stroke must be defined)
12748
+     * @type Array
12749
+     */
12750
+    strokeDashArray:          null,
12751
+
12752
+    /**
12753
+     * Line offset of an object's stroke
12754
+     * @type Number
12755
+     * @default
12756
+     */
12757
+    strokeDashOffset: 0,
12758
+
12759
+    /**
12760
+     * Line endings style of an object's stroke (one of "butt", "round", "square")
12761
+     * @type String
12762
+     * @default
12763
+     */
12764
+    strokeLineCap:            'butt',
12765
+
12766
+    /**
12767
+     * Corner style of an object's stroke (one of "bevil", "round", "miter")
12768
+     * @type String
12769
+     * @default
12770
+     */
12771
+    strokeLineJoin:           'miter',
12772
+
12773
+    /**
12774
+     * Maximum miter length (used for strokeLineJoin = "miter") of an object's stroke
12775
+     * @type Number
12776
+     * @default
12777
+     */
12778
+    strokeMiterLimit:         4,
12779
+
12780
+    /**
12781
+     * Shadow object representing shadow of this shape
12782
+     * @type fabric.Shadow
12783
+     * @default
12784
+     */
12785
+    shadow:                   null,
12786
+
12787
+    /**
12788
+     * Opacity of object's controlling borders when object is active and moving
12789
+     * @type Number
12790
+     * @default
12791
+     */
12792
+    borderOpacityWhenMoving:  0.4,
12793
+
12794
+    /**
12795
+     * Scale factor of object's controlling borders
12796
+     * @type Number
12797
+     * @default
12798
+     */
12799
+    borderScaleFactor:        1,
12800
+
12801
+    /**
12802
+     * Transform matrix (similar to SVG's transform matrix)
12803
+     * @type Array
12804
+     */
12805
+    transformMatrix:          null,
12806
+
12807
+    /**
12808
+     * Minimum allowed scale value of an object
12809
+     * @type Number
12810
+     * @default
12811
+     */
12812
+    minScaleLimit:            0,
12813
+
12814
+    /**
12815
+     * When set to `false`, an object can not be selected for modification (using either point-click-based or group-based selection).
12816
+     * But events still fire on it.
12817
+     * @type Boolean
12818
+     * @default
12819
+     */
12820
+    selectable:               true,
12821
+
12822
+    /**
12823
+     * When set to `false`, an object can not be a target of events. All events propagate through it. Introduced in v1.3.4
12824
+     * @type Boolean
12825
+     * @default
12826
+     */
12827
+    evented:                  true,
12828
+
12829
+    /**
12830
+     * When set to `false`, an object is not rendered on canvas
12831
+     * @type Boolean
12832
+     * @default
12833
+     */
12834
+    visible:                  true,
12835
+
12836
+    /**
12837
+     * When set to `false`, object's controls are not displayed and can not be used to manipulate object
12838
+     * @type Boolean
12839
+     * @default
12840
+     */
12841
+    hasControls:              true,
12842
+
12843
+    /**
12844
+     * When set to `false`, object's controlling borders are not rendered
12845
+     * @type Boolean
12846
+     * @default
12847
+     */
12848
+    hasBorders:               true,
12849
+
12850
+    /**
12851
+     * When set to `false`, object's controlling rotating point will not be visible or selectable
12852
+     * @type Boolean
12853
+     * @default
12854
+     */
12855
+    hasRotatingPoint:         true,
12856
+
12857
+    /**
12858
+     * Offset for object's controlling rotating point (when enabled via `hasRotatingPoint`)
12859
+     * @type Number
12860
+     * @default
12861
+     */
12862
+    rotatingPointOffset:      40,
12863
+
12864
+    /**
12865
+     * When set to `true`, objects are "found" on canvas on per-pixel basis rather than according to bounding box
12866
+     * @type Boolean
12867
+     * @default
12868
+     */
12869
+    perPixelTargetFind:       false,
12870
+
12871
+    /**
12872
+     * When `false`, default object's values are not included in its serialization
12873
+     * @type Boolean
12874
+     * @default
12875
+     */
12876
+    includeDefaultValues:     true,
12877
+
12878
+    /**
12879
+     * Function that determines clipping of an object (context is passed as a first argument).
12880
+     * If you are using code minification, ctx argument can be minified/manglied you should use
12881
+     * as a workaround `var ctx = arguments[0];` in the function;
12882
+     * Note that context origin is at the object's center point (not left/top corner)
12883
+     * @deprecated since 2.0.0
12884
+     * @type Function
12885
+     */
12886
+    clipTo:                   null,
12887
+
12888
+    /**
12889
+     * When `true`, object horizontal movement is locked
12890
+     * @type Boolean
12891
+     * @default
12892
+     */
12893
+    lockMovementX:            false,
12894
+
12895
+    /**
12896
+     * When `true`, object vertical movement is locked
12897
+     * @type Boolean
12898
+     * @default
12899
+     */
12900
+    lockMovementY:            false,
12901
+
12902
+    /**
12903
+     * When `true`, object rotation is locked
12904
+     * @type Boolean
12905
+     * @default
12906
+     */
12907
+    lockRotation:             false,
12908
+
12909
+    /**
12910
+     * When `true`, object horizontal scaling is locked
12911
+     * @type Boolean
12912
+     * @default
12913
+     */
12914
+    lockScalingX:             false,
12915
+
12916
+    /**
12917
+     * When `true`, object vertical scaling is locked
12918
+     * @type Boolean
12919
+     * @default
12920
+     */
12921
+    lockScalingY:             false,
12922
+
12923
+    /**
12924
+     * When `true`, object non-uniform scaling is locked
12925
+     * @type Boolean
12926
+     * @default
12927
+     */
12928
+    lockUniScaling:           false,
12929
+
12930
+    /**
12931
+     * When `true`, object horizontal skewing is locked
12932
+     * @type Boolean
12933
+     * @default
12934
+     */
12935
+    lockSkewingX:             false,
12936
+
12937
+    /**
12938
+     * When `true`, object vertical skewing is locked
12939
+     * @type Boolean
12940
+     * @default
12941
+     */
12942
+    lockSkewingY:             false,
12943
+
12944
+    /**
12945
+     * When `true`, object cannot be flipped by scaling into negative values
12946
+     * @type Boolean
12947
+     * @default
12948
+     */
12949
+    lockScalingFlip:          false,
12950
+
12951
+    /**
12952
+     * When `true`, object is not exported in OBJECT/JSON
12953
+     * since 1.6.3
12954
+     * @type Boolean
12955
+     * @default
12956
+     */
12957
+    excludeFromExport:        false,
12958
+
12959
+    /**
12960
+     * When `true`, object is cached on an additional canvas.
12961
+     * default to true
12962
+     * since 1.7.0
12963
+     * @type Boolean
12964
+     * @default true
12965
+     */
12966
+    objectCaching:            objectCaching,
12967
+
12968
+    /**
12969
+     * When `true`, object properties are checked for cache invalidation. In some particular
12970
+     * situation you may want this to be disabled ( spray brush, very big, groups)
12971
+     * or if your application does not allow you to modify properties for groups child you want
12972
+     * to disable it for groups.
12973
+     * default to false
12974
+     * since 1.7.0
12975
+     * @type Boolean
12976
+     * @default false
12977
+     */
12978
+    statefullCache:            false,
12979
+
12980
+    /**
12981
+     * When `true`, cache does not get updated during scaling. The picture will get blocky if scaled
12982
+     * too much and will be redrawn with correct details at the end of scaling.
12983
+     * this setting is performance and application dependant.
12984
+     * default to true
12985
+     * since 1.7.0
12986
+     * @type Boolean
12987
+     * @default true
12988
+     */
12989
+    noScaleCache:              true,
12990
+
12991
+    /**
12992
+     * When set to `true`, object's cache will be rerendered next render call.
12993
+     * since 1.7.0
12994
+     * @type Boolean
12995
+     * @default true
12996
+     */
12997
+    dirty:                true,
12998
+
12999
+    /**
13000
+     * keeps the value of the last hovered corner during mouse move.
13001
+     * 0 is no corner, or 'mt', 'ml', 'mtr' etc..
13002
+     * It should be private, but there is no harm in using it as
13003
+     * a read-only property.
13004
+     * @type number|string|any
13005
+     * @default 0
13006
+     */
13007
+    __corner: 0,
13008
+
13009
+    /**
13010
+     * Determines if the fill or the stroke is drawn first (one of "fill" or "stroke")
13011
+     * @type String
13012
+     * @default
13013
+     */
13014
+    paintFirst:           'fill',
13015
+
13016
+    /**
13017
+     * List of properties to consider when checking if state
13018
+     * of an object is changed (fabric.Object#hasStateChanged)
13019
+     * as well as for history (undo/redo) purposes
13020
+     * @type Array
13021
+     */
13022
+    stateProperties: (
13023
+      'top left width height scaleX scaleY flipX flipY originX originY transformMatrix ' +
13024
+      'stroke strokeWidth strokeDashArray strokeLineCap strokeDashOffset strokeLineJoin strokeMiterLimit ' +
13025
+      'angle opacity fill globalCompositeOperation shadow clipTo visible backgroundColor ' +
13026
+      'skewX skewY fillRule paintFirst clipPath'
13027
+    ).split(' '),
13028
+
13029
+    /**
13030
+     * List of properties to consider when checking if cache needs refresh
13031
+     * Those properties are checked by statefullCache ON ( or lazy mode if we want ) or from single
13032
+     * calls to Object.set(key, value). If the key is in this list, the object is marked as dirty
13033
+     * and refreshed at the next render
13034
+     * @type Array
13035
+     */
13036
+    cacheProperties: (
13037
+      'fill stroke strokeWidth strokeDashArray width height paintFirst' +
13038
+      ' strokeLineCap strokeDashOffset strokeLineJoin strokeMiterLimit backgroundColor clipPath'
13039
+    ).split(' '),
13040
+
13041
+    /**
13042
+     * a fabricObject that, without stroke define a clipping area with their shape. filled in black
13043
+     * the clipPath object gets used when the object has rendered, and the context is placed in the center
13044
+     * of the object cacheCanvas.
13045
+     * If you want 0,0 of a clipPath to align with an object center, use clipPath.originX/Y to 'center'
13046
+     * @type fabric.Object
13047
+     */
13048
+    clipPath: undefined,
13049
+
13050
+    /**
13051
+     * Meaningful ONLY when the object is used as clipPath.
13052
+     * if true, the clipPath will make the object clip to the outside of the clipPath
13053
+     * since 2.4.0
13054
+     * @type boolean
13055
+     * @default false
13056
+     */
13057
+    inverted: false,
13058
+
13059
+    /**
13060
+     * Meaningful ONLY when the object is used as clipPath.
13061
+     * if true, the clipPath will have its top and left relative to canvas, and will
13062
+     * not be influenced by the object transform. This will make the clipPath relative
13063
+     * to the canvas, but clipping just a particular object.
13064
+     * WARNING this is beta, this feature may change or be renamed.
13065
+     * since 2.4.0
13066
+     * @type boolean
13067
+     * @default false
13068
+     */
13069
+    absolutePositioned: false,
13070
+
13071
+    /**
13072
+     * Constructor
13073
+     * @param {Object} [options] Options object
13074
+     */
13075
+    initialize: function(options) {
13076
+      if (options) {
13077
+        this.setOptions(options);
13078
+      }
13079
+    },
13080
+
13081
+    /**
13082
+     * Create a the canvas used to keep the cached copy of the object
13083
+     * @private
13084
+     */
13085
+    _createCacheCanvas: function() {
13086
+      this._cacheProperties = {};
13087
+      this._cacheCanvas = fabric.util.createCanvasElement();
13088
+      this._cacheContext = this._cacheCanvas.getContext('2d');
13089
+      this._updateCacheCanvas();
13090
+      // if canvas gets created, is empty, so dirty.
13091
+      this.dirty = true;
13092
+    },
13093
+
13094
+    /**
13095
+     * Limit the cache dimensions so that X * Y do not cross fabric.perfLimitSizeTotal
13096
+     * and each side do not cross fabric.cacheSideLimit
13097
+     * those numbers are configurable so that you can get as much detail as you want
13098
+     * making bargain with performances.
13099
+     * @param {Object} dims
13100
+     * @param {Object} dims.width width of canvas
13101
+     * @param {Object} dims.height height of canvas
13102
+     * @param {Object} dims.zoomX zoomX zoom value to unscale the canvas before drawing cache
13103
+     * @param {Object} dims.zoomY zoomY zoom value to unscale the canvas before drawing cache
13104
+     * @return {Object}.width width of canvas
13105
+     * @return {Object}.height height of canvas
13106
+     * @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache
13107
+     * @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache
13108
+     */
13109
+    _limitCacheSize: function(dims) {
13110
+      var perfLimitSizeTotal = fabric.perfLimitSizeTotal,
13111
+          width = dims.width, height = dims.height,
13112
+          max = fabric.maxCacheSideLimit, min = fabric.minCacheSideLimit;
13113
+      if (width <= max && height <= max && width * height <= perfLimitSizeTotal) {
13114
+        if (width < min) {
13115
+          dims.width = min;
13116
+        }
13117
+        if (height < min) {
13118
+          dims.height = min;
13119
+        }
13120
+        return dims;
13121
+      }
13122
+      var ar = width / height, limitedDims = fabric.util.limitDimsByArea(ar, perfLimitSizeTotal),
13123
+          capValue = fabric.util.capValue,
13124
+          x = capValue(min, limitedDims.x, max),
13125
+          y = capValue(min, limitedDims.y, max);
13126
+      if (width > x) {
13127
+        dims.zoomX /= width / x;
13128
+        dims.width = x;
13129
+        dims.capped = true;
13130
+      }
13131
+      if (height > y) {
13132
+        dims.zoomY /= height / y;
13133
+        dims.height = y;
13134
+        dims.capped = true;
13135
+      }
13136
+      return dims;
13137
+    },
13138
+
13139
+    /**
13140
+     * Return the dimension and the zoom level needed to create a cache canvas
13141
+     * big enough to host the object to be cached.
13142
+     * @private
13143
+     * @return {Object}.x width of object to be cached
13144
+     * @return {Object}.y height of object to be cached
13145
+     * @return {Object}.width width of canvas
13146
+     * @return {Object}.height height of canvas
13147
+     * @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache
13148
+     * @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache
13149
+     */
13150
+    _getCacheCanvasDimensions: function() {
13151
+      var objectScale = this.getTotalObjectScaling(),
13152
+          dim = this._getNonTransformedDimensions(),
13153
+          zoomX = objectScale.scaleX,
13154
+          zoomY = objectScale.scaleY,
13155
+          width = dim.x * zoomX,
13156
+          height = dim.y * zoomY;
13157
+      return {
13158
+        // for sure this ALIASING_LIMIT is slightly crating problem
13159
+        // in situation in wich the cache canvas gets an upper limit
13160
+        width: width + ALIASING_LIMIT,
13161
+        height: height + ALIASING_LIMIT,
13162
+        zoomX: zoomX,
13163
+        zoomY: zoomY,
13164
+        x: dim.x,
13165
+        y: dim.y
13166
+      };
13167
+    },
13168
+
13169
+    /**
13170
+     * Update width and height of the canvas for cache
13171
+     * returns true or false if canvas needed resize.
13172
+     * @private
13173
+     * @return {Boolean} true if the canvas has been resized
13174
+     */
13175
+    _updateCacheCanvas: function() {
13176
+      var targetCanvas = this.canvas;
13177
+      if (this.noScaleCache && targetCanvas && targetCanvas._currentTransform) {
13178
+        var target = targetCanvas._currentTransform.target,
13179
+            action = targetCanvas._currentTransform.action;
13180
+        if (this === target && action.slice && action.slice(0, 5) === 'scale') {
13181
+          return false;
13182
+        }
13183
+      }
13184
+      var canvas = this._cacheCanvas,
13185
+          dims = this._limitCacheSize(this._getCacheCanvasDimensions()),
13186
+          minCacheSize = fabric.minCacheSideLimit,
13187
+          width = dims.width, height = dims.height, drawingWidth, drawingHeight,
13188
+          zoomX = dims.zoomX, zoomY = dims.zoomY,
13189
+          dimensionsChanged = width !== this.cacheWidth || height !== this.cacheHeight,
13190
+          zoomChanged = this.zoomX !== zoomX || this.zoomY !== zoomY,
13191
+          shouldRedraw = dimensionsChanged || zoomChanged,
13192
+          additionalWidth = 0, additionalHeight = 0, shouldResizeCanvas = false;
13193
+      if (dimensionsChanged) {
13194
+        var canvasWidth = this._cacheCanvas.width,
13195
+            canvasHeight = this._cacheCanvas.height,
13196
+            sizeGrowing = width > canvasWidth || height > canvasHeight,
13197
+            sizeShrinking = (width < canvasWidth * 0.9 || height < canvasHeight * 0.9) &&
13198
+              canvasWidth > minCacheSize && canvasHeight > minCacheSize;
13199
+        shouldResizeCanvas = sizeGrowing || sizeShrinking;
13200
+        if (sizeGrowing && !dims.capped && (width > minCacheSize || height > minCacheSize)) {
13201
+          additionalWidth = width * 0.1;
13202
+          additionalHeight = height * 0.1;
13203
+        }
13204
+      }
13205
+      if (shouldRedraw) {
13206
+        if (shouldResizeCanvas) {
13207
+          canvas.width = Math.ceil(width + additionalWidth);
13208
+          canvas.height = Math.ceil(height + additionalHeight);
13209
+        }
13210
+        else {
13211
+          this._cacheContext.setTransform(1, 0, 0, 1, 0, 0);
13212
+          this._cacheContext.clearRect(0, 0, canvas.width, canvas.height);
13213
+        }
13214
+        drawingWidth = dims.x * zoomX / 2;
13215
+        drawingHeight = dims.y * zoomY / 2;
13216
+        this.cacheTranslationX = Math.round(canvas.width / 2 - drawingWidth) + drawingWidth;
13217
+        this.cacheTranslationY = Math.round(canvas.height / 2 - drawingHeight) + drawingHeight;
13218
+        this.cacheWidth = width;
13219
+        this.cacheHeight = height;
13220
+        this._cacheContext.translate(this.cacheTranslationX, this.cacheTranslationY);
13221
+        this._cacheContext.scale(zoomX, zoomY);
13222
+        this.zoomX = zoomX;
13223
+        this.zoomY = zoomY;
13224
+        return true;
13225
+      }
13226
+      return false;
13227
+    },
13228
+
13229
+    /**
13230
+     * Sets object's properties from options
13231
+     * @param {Object} [options] Options object
13232
+     */
13233
+    setOptions: function(options) {
13234
+      this._setOptions(options);
13235
+      this._initGradient(options.fill, 'fill');
13236
+      this._initGradient(options.stroke, 'stroke');
13237
+      this._initClipping(options);
13238
+      this._initPattern(options.fill, 'fill');
13239
+      this._initPattern(options.stroke, 'stroke');
13240
+    },
13241
+
13242
+    /**
13243
+     * Transforms context when rendering an object
13244
+     * @param {CanvasRenderingContext2D} ctx Context
13245
+     */
13246
+    transform: function(ctx) {
13247
+      var m;
13248
+      if (this.group && !this.group._transformDone) {
13249
+        m = this.calcTransformMatrix();
13250
+      }
13251
+      else {
13252
+        m = this.calcOwnMatrix();
13253
+      }
13254
+      ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
13255
+    },
13256
+
13257
+    /**
13258
+     * Returns an object representation of an instance
13259
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
13260
+     * @return {Object} Object representation of an instance
13261
+     */
13262
+    toObject: function(propertiesToInclude) {
13263
+      var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
13264
+
13265
+          object = {
13266
+            type:                     this.type,
13267
+            version:                  fabric.version,
13268
+            originX:                  this.originX,
13269
+            originY:                  this.originY,
13270
+            left:                     toFixed(this.left, NUM_FRACTION_DIGITS),
13271
+            top:                      toFixed(this.top, NUM_FRACTION_DIGITS),
13272
+            width:                    toFixed(this.width, NUM_FRACTION_DIGITS),
13273
+            height:                   toFixed(this.height, NUM_FRACTION_DIGITS),
13274
+            fill:                     (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
13275
+            stroke:                   (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
13276
+            strokeWidth:              toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
13277
+            strokeDashArray:          this.strokeDashArray ? this.strokeDashArray.concat() : this.strokeDashArray,
13278
+            strokeLineCap:            this.strokeLineCap,
13279
+            strokeDashOffset:         this.strokeDashOffset,
13280
+            strokeLineJoin:           this.strokeLineJoin,
13281
+            strokeMiterLimit:         toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
13282
+            scaleX:                   toFixed(this.scaleX, NUM_FRACTION_DIGITS),
13283
+            scaleY:                   toFixed(this.scaleY, NUM_FRACTION_DIGITS),
13284
+            angle:                    toFixed(this.angle, NUM_FRACTION_DIGITS),
13285
+            flipX:                    this.flipX,
13286
+            flipY:                    this.flipY,
13287
+            opacity:                  toFixed(this.opacity, NUM_FRACTION_DIGITS),
13288
+            shadow:                   (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
13289
+            visible:                  this.visible,
13290
+            clipTo:                   this.clipTo && String(this.clipTo),
13291
+            backgroundColor:          this.backgroundColor,
13292
+            fillRule:                 this.fillRule,
13293
+            paintFirst:               this.paintFirst,
13294
+            globalCompositeOperation: this.globalCompositeOperation,
13295
+            transformMatrix:          this.transformMatrix ? this.transformMatrix.concat() : null,
13296
+            skewX:                    toFixed(this.skewX, NUM_FRACTION_DIGITS),
13297
+            skewY:                    toFixed(this.skewY, NUM_FRACTION_DIGITS),
13298
+          };
13299
+
13300
+      if (this.clipPath) {
13301
+        object.clipPath = this.clipPath.toObject(propertiesToInclude);
13302
+        object.clipPath.inverted = this.clipPath.inverted;
13303
+        object.clipPath.absolutePositioned = this.clipPath.absolutePositioned;
13304
+      }
13305
+
13306
+      fabric.util.populateWithProperties(this, object, propertiesToInclude);
13307
+      if (!this.includeDefaultValues) {
13308
+        object = this._removeDefaultValues(object);
13309
+      }
13310
+
13311
+      return object;
13312
+    },
13313
+
13314
+    /**
13315
+     * Returns (dataless) object representation of an instance
13316
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
13317
+     * @return {Object} Object representation of an instance
13318
+     */
13319
+    toDatalessObject: function(propertiesToInclude) {
13320
+      // will be overwritten by subclasses
13321
+      return this.toObject(propertiesToInclude);
13322
+    },
13323
+
13324
+    /**
13325
+     * @private
13326
+     * @param {Object} object
13327
+     */
13328
+    _removeDefaultValues: function(object) {
13329
+      var prototype = fabric.util.getKlass(object.type).prototype,
13330
+          stateProperties = prototype.stateProperties;
13331
+      stateProperties.forEach(function(prop) {
13332
+        if (object[prop] === prototype[prop]) {
13333
+          delete object[prop];
13334
+        }
13335
+        var isArray = Object.prototype.toString.call(object[prop]) === '[object Array]' &&
13336
+                      Object.prototype.toString.call(prototype[prop]) === '[object Array]';
13337
+
13338
+        // basically a check for [] === []
13339
+        if (isArray && object[prop].length === 0 && prototype[prop].length === 0) {
13340
+          delete object[prop];
13341
+        }
13342
+      });
13343
+
13344
+      return object;
13345
+    },
13346
+
13347
+    /**
13348
+     * Returns a string representation of an instance
13349
+     * @return {String}
13350
+     */
13351
+    toString: function() {
13352
+      return '#<fabric.' + capitalize(this.type) + '>';
13353
+    },
13354
+
13355
+    /**
13356
+     * Return the object scale factor counting also the group scaling
13357
+     * @return {Object} object with scaleX and scaleY properties
13358
+     */
13359
+    getObjectScaling: function() {
13360
+      var scaleX = this.scaleX, scaleY = this.scaleY;
13361
+      if (this.group) {
13362
+        var scaling = this.group.getObjectScaling();
13363
+        scaleX *= scaling.scaleX;
13364
+        scaleY *= scaling.scaleY;
13365
+      }
13366
+      return { scaleX: scaleX, scaleY: scaleY };
13367
+    },
13368
+
13369
+    /**
13370
+     * Return the object scale factor counting also the group scaling, zoom and retina
13371
+     * @return {Object} object with scaleX and scaleY properties
13372
+     */
13373
+    getTotalObjectScaling: function() {
13374
+      var scale = this.getObjectScaling(), scaleX = scale.scaleX, scaleY = scale.scaleY;
13375
+      if (this.canvas) {
13376
+        var zoom = this.canvas.getZoom();
13377
+        var retina = this.canvas.getRetinaScaling();
13378
+        scaleX *= zoom * retina;
13379
+        scaleY *= zoom * retina;
13380
+      }
13381
+      return { scaleX: scaleX, scaleY: scaleY };
13382
+    },
13383
+
13384
+    /**
13385
+     * Return the object opacity counting also the group property
13386
+     * @return {Number}
13387
+     */
13388
+    getObjectOpacity: function() {
13389
+      var opacity = this.opacity;
13390
+      if (this.group) {
13391
+        opacity *= this.group.getObjectOpacity();
13392
+      }
13393
+      return opacity;
13394
+    },
13395
+
13396
+    /**
13397
+     * @private
13398
+     * @param {String} key
13399
+     * @param {*} value
13400
+     * @return {fabric.Object} thisArg
13401
+     */
13402
+    _set: function(key, value) {
13403
+      var shouldConstrainValue = (key === 'scaleX' || key === 'scaleY'),
13404
+          isChanged = this[key] !== value, groupNeedsUpdate = false;
13405
+
13406
+      if (shouldConstrainValue) {
13407
+        value = this._constrainScale(value);
13408
+      }
13409
+      if (key === 'scaleX' && value < 0) {
13410
+        this.flipX = !this.flipX;
13411
+        value *= -1;
13412
+      }
13413
+      else if (key === 'scaleY' && value < 0) {
13414
+        this.flipY = !this.flipY;
13415
+        value *= -1;
13416
+      }
13417
+      else if (key === 'shadow' && value && !(value instanceof fabric.Shadow)) {
13418
+        value = new fabric.Shadow(value);
13419
+      }
13420
+      else if (key === 'dirty' && this.group) {
13421
+        this.group.set('dirty', value);
13422
+      }
13423
+
13424
+      this[key] = value;
13425
+
13426
+      if (isChanged) {
13427
+        groupNeedsUpdate = this.group && this.group.isOnACache();
13428
+        if (this.cacheProperties.indexOf(key) > -1) {
13429
+          this.dirty = true;
13430
+          groupNeedsUpdate && this.group.set('dirty', true);
13431
+        }
13432
+        else if (groupNeedsUpdate && this.stateProperties.indexOf(key) > -1) {
13433
+          this.group.set('dirty', true);
13434
+        }
13435
+      }
13436
+
13437
+      return this;
13438
+    },
13439
+
13440
+    /**
13441
+     * This callback function is called by the parent group of an object every
13442
+     * time a non-delegated property changes on the group. It is passed the key
13443
+     * and value as parameters. Not adding in this function's signature to avoid
13444
+     * Travis build error about unused variables.
13445
+     */
13446
+    setOnGroup: function() {
13447
+      // implemented by sub-classes, as needed.
13448
+    },
13449
+
13450
+    /**
13451
+     * Retrieves viewportTransform from Object's canvas if possible
13452
+     * @method getViewportTransform
13453
+     * @memberOf fabric.Object.prototype
13454
+     * @return {Array}
13455
+     */
13456
+    getViewportTransform: function() {
13457
+      if (this.canvas && this.canvas.viewportTransform) {
13458
+        return this.canvas.viewportTransform;
13459
+      }
13460
+      return fabric.iMatrix.concat();
13461
+    },
13462
+
13463
+    /*
13464
+     * @private
13465
+     * return if the object would be visible in rendering
13466
+     * @memberOf fabric.Object.prototype
13467
+     * @return {Boolean}
13468
+     */
13469
+    isNotVisible: function() {
13470
+      return this.opacity === 0 ||
13471
+        (this.width === 0 && this.height === 0 && this.strokeWidth === 0) ||
13472
+        !this.visible;
13473
+    },
13474
+
13475
+    /**
13476
+     * Renders an object on a specified context
13477
+     * @param {CanvasRenderingContext2D} ctx Context to render on
13478
+     */
13479
+    render: function(ctx) {
13480
+      // do not render if width/height are zeros or object is not visible
13481
+      if (this.isNotVisible()) {
13482
+        return;
13483
+      }
13484
+      if (this.canvas && this.canvas.skipOffscreen && !this.group && !this.isOnScreen()) {
13485
+        return;
13486
+      }
13487
+      ctx.save();
13488
+      this._setupCompositeOperation(ctx);
13489
+      this.drawSelectionBackground(ctx);
13490
+      this.transform(ctx);
13491
+      this._setOpacity(ctx);
13492
+      this._setShadow(ctx, this);
13493
+      if (this.transformMatrix) {
13494
+        ctx.transform.apply(ctx, this.transformMatrix);
13495
+      }
13496
+      this.clipTo && fabric.util.clipContext(this, ctx);
13497
+      if (this.shouldCache()) {
13498
+        this.renderCache();
13499
+        this.drawCacheOnCanvas(ctx);
13500
+      }
13501
+      else {
13502
+        this._removeCacheCanvas();
13503
+        this.dirty = false;
13504
+        this.drawObject(ctx);
13505
+        if (this.objectCaching && this.statefullCache) {
13506
+          this.saveState({ propertySet: 'cacheProperties' });
13507
+        }
13508
+      }
13509
+      this.clipTo && ctx.restore();
13510
+      ctx.restore();
13511
+    },
13512
+
13513
+    renderCache: function(options) {
13514
+      options = options || {};
13515
+      if (!this._cacheCanvas) {
13516
+        this._createCacheCanvas();
13517
+      }
13518
+      if (this.isCacheDirty()) {
13519
+        this.statefullCache && this.saveState({ propertySet: 'cacheProperties' });
13520
+        this.drawObject(this._cacheContext, options.forClipping);
13521
+        this.dirty = false;
13522
+      }
13523
+    },
13524
+
13525
+    /**
13526
+     * Remove cacheCanvas and its dimensions from the objects
13527
+     */
13528
+    _removeCacheCanvas: function() {
13529
+      this._cacheCanvas = null;
13530
+      this.cacheWidth = 0;
13531
+      this.cacheHeight = 0;
13532
+    },
13533
+
13534
+    /**
13535
+     * When set to `true`, force the object to have its own cache, even if it is inside a group
13536
+     * it may be needed when your object behave in a particular way on the cache and always needs
13537
+     * its own isolated canvas to render correctly.
13538
+     * Created to be overridden
13539
+     * since 1.7.12
13540
+     * @returns false
13541
+     */
13542
+    needsItsOwnCache: function() {
13543
+      if (this.paintFirst === 'stroke' && typeof this.shadow === 'object') {
13544
+        return true;
13545
+      }
13546
+      if (this.clipPath) {
13547
+        return true;
13548
+      }
13549
+      return false;
13550
+    },
13551
+
13552
+    /**
13553
+     * Decide if the object should cache or not. Create its own cache level
13554
+     * objectCaching is a global flag, wins over everything
13555
+     * needsItsOwnCache should be used when the object drawing method requires
13556
+     * a cache step. None of the fabric classes requires it.
13557
+     * Generally you do not cache objects in groups because the group outside is cached.
13558
+     * @return {Boolean}
13559
+     */
13560
+    shouldCache: function() {
13561
+      this.ownCaching = this.objectCaching &&
13562
+      (!this.group || this.needsItsOwnCache() || !this.group.isOnACache());
13563
+      return this.ownCaching;
13564
+    },
13565
+
13566
+    /**
13567
+     * Check if this object or a child object will cast a shadow
13568
+     * used by Group.shouldCache to know if child has a shadow recursively
13569
+     * @return {Boolean}
13570
+     */
13571
+    willDrawShadow: function() {
13572
+      return !!this.shadow && (this.shadow.offsetX !== 0 || this.shadow.offsetY !== 0);
13573
+    },
13574
+
13575
+    /**
13576
+     * Execute the drawing operation for an object clipPath
13577
+     * @param {CanvasRenderingContext2D} ctx Context to render on
13578
+     */
13579
+    drawClipPathOnCache: function(ctx) {
13580
+      var path = this.clipPath;
13581
+      ctx.save();
13582
+      // DEBUG: uncomment this line, comment the following
13583
+      // ctx.globalAlpha = 0.4
13584
+      if (path.inverted) {
13585
+        ctx.globalCompositeOperation = 'destination-out';
13586
+      }
13587
+      else {
13588
+        ctx.globalCompositeOperation = 'destination-in';
13589
+      }
13590
+      //ctx.scale(1 / 2, 1 / 2);
13591
+      if (path.absolutePositioned) {
13592
+        var m = fabric.util.invertTransform(this.calcTransformMatrix());
13593
+        ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
13594
+      }
13595
+      path.transform(ctx);
13596
+      ctx.scale(1 / path.zoomX, 1 / path.zoomY);
13597
+      ctx.drawImage(path._cacheCanvas, -path.cacheTranslationX, -path.cacheTranslationY);
13598
+      ctx.restore();
13599
+    },
13600
+
13601
+    /**
13602
+     * Execute the drawing operation for an object on a specified context
13603
+     * @param {CanvasRenderingContext2D} ctx Context to render on
13604
+     */
13605
+    drawObject: function(ctx, forClipping) {
13606
+
13607
+      if (forClipping) {
13608
+        this._setClippingProperties(ctx);
13609
+      }
13610
+      else {
13611
+        this._renderBackground(ctx);
13612
+        this._setStrokeStyles(ctx, this);
13613
+        this._setFillStyles(ctx, this);
13614
+      }
13615
+      this._render(ctx);
13616
+      this._drawClipPath(ctx);
13617
+    },
13618
+
13619
+    _drawClipPath: function(ctx) {
13620
+      var path = this.clipPath;
13621
+      if (!path) { return; }
13622
+      // needed to setup a couple of variables
13623
+      // path canvas gets overridden with this one.
13624
+      // TODO find a better solution?
13625
+      path.canvas = this.canvas;
13626
+      path.shouldCache();
13627
+      path._transformDone = true;
13628
+      path.renderCache({ forClipping: true });
13629
+      this.drawClipPathOnCache(ctx);
13630
+    },
13631
+
13632
+    /**
13633
+     * Paint the cached copy of the object on the target context.
13634
+     * @param {CanvasRenderingContext2D} ctx Context to render on
13635
+     */
13636
+    drawCacheOnCanvas: function(ctx) {
13637
+      ctx.scale(1 / this.zoomX, 1 / this.zoomY);
13638
+      ctx.drawImage(this._cacheCanvas, -this.cacheTranslationX, -this.cacheTranslationY);
13639
+    },
13640
+
13641
+    /**
13642
+     * Check if cache is dirty
13643
+     * @param {Boolean} skipCanvas skip canvas checks because this object is painted
13644
+     * on parent canvas.
13645
+     */
13646
+    isCacheDirty: function(skipCanvas) {
13647
+      if (this.isNotVisible()) {
13648
+        return false;
13649
+      }
13650
+      if (this._cacheCanvas && !skipCanvas && this._updateCacheCanvas()) {
13651
+        // in this case the context is already cleared.
13652
+        return true;
13653
+      }
13654
+      else {
13655
+        if (this.dirty ||
13656
+          (this.clipPath && this.clipPath.absolutePositioned) ||
13657
+          (this.statefullCache && this.hasStateChanged('cacheProperties'))
13658
+        ) {
13659
+          if (this._cacheCanvas && !skipCanvas) {
13660
+            var width = this.cacheWidth / this.zoomX;
13661
+            var height = this.cacheHeight / this.zoomY;
13662
+            this._cacheContext.clearRect(-width / 2, -height / 2, width, height);
13663
+          }
13664
+          return true;
13665
+        }
13666
+      }
13667
+      return false;
13668
+    },
13669
+
13670
+    /**
13671
+     * Draws a background for the object big as its untransformed dimensions
13672
+     * @private
13673
+     * @param {CanvasRenderingContext2D} ctx Context to render on
13674
+     */
13675
+    _renderBackground: function(ctx) {
13676
+      if (!this.backgroundColor) {
13677
+        return;
13678
+      }
13679
+      var dim = this._getNonTransformedDimensions();
13680
+      ctx.fillStyle = this.backgroundColor;
13681
+
13682
+      ctx.fillRect(
13683
+        -dim.x / 2,
13684
+        -dim.y / 2,
13685
+        dim.x,
13686
+        dim.y
13687
+      );
13688
+      // if there is background color no other shadows
13689
+      // should be casted
13690
+      this._removeShadow(ctx);
13691
+    },
13692
+
13693
+    /**
13694
+     * @private
13695
+     * @param {CanvasRenderingContext2D} ctx Context to render on
13696
+     */
13697
+    _setOpacity: function(ctx) {
13698
+      if (this.group && !this.group._transformDone) {
13699
+        ctx.globalAlpha = this.getObjectOpacity();
13700
+      }
13701
+      else {
13702
+        ctx.globalAlpha *= this.opacity;
13703
+      }
13704
+    },
13705
+
13706
+    _setStrokeStyles: function(ctx, decl) {
13707
+      if (decl.stroke) {
13708
+        ctx.lineWidth = decl.strokeWidth;
13709
+        ctx.lineCap = decl.strokeLineCap;
13710
+        ctx.lineDashOffset = decl.strokeDashOffset;
13711
+        ctx.lineJoin = decl.strokeLineJoin;
13712
+        ctx.miterLimit = decl.strokeMiterLimit;
13713
+        ctx.strokeStyle = decl.stroke.toLive
13714
+          ? decl.stroke.toLive(ctx, this)
13715
+          : decl.stroke;
13716
+      }
13717
+    },
13718
+
13719
+    _setFillStyles: function(ctx, decl) {
13720
+      if (decl.fill) {
13721
+        ctx.fillStyle = decl.fill.toLive
13722
+          ? decl.fill.toLive(ctx, this)
13723
+          : decl.fill;
13724
+      }
13725
+    },
13726
+
13727
+    _setClippingProperties: function(ctx) {
13728
+      ctx.globalAlpha = 1;
13729
+      ctx.strokeStyle = 'transparent';
13730
+      ctx.fillStyle = '#000000';
13731
+    },
13732
+
13733
+    /**
13734
+     * @private
13735
+     * Sets line dash
13736
+     * @param {CanvasRenderingContext2D} ctx Context to set the dash line on
13737
+     * @param {Array} dashArray array representing dashes
13738
+     * @param {Function} alternative function to call if browser does not support lineDash
13739
+     */
13740
+    _setLineDash: function(ctx, dashArray, alternative) {
13741
+      if (!dashArray) {
13742
+        return;
13743
+      }
13744
+      // Spec requires the concatenation of two copies the dash list when the number of elements is odd
13745
+      if (1 & dashArray.length) {
13746
+        dashArray.push.apply(dashArray, dashArray);
13747
+      }
13748
+      if (supportsLineDash) {
13749
+        ctx.setLineDash(dashArray);
13750
+      }
13751
+      else {
13752
+        alternative && alternative(ctx);
13753
+      }
13754
+    },
13755
+
13756
+    /**
13757
+     * Renders controls and borders for the object
13758
+     * @param {CanvasRenderingContext2D} ctx Context to render on
13759
+     * @param {Object} [styleOverride] properties to override the object style
13760
+     */
13761
+    _renderControls: function(ctx, styleOverride) {
13762
+      var vpt = this.getViewportTransform(),
13763
+          matrix = this.calcTransformMatrix(),
13764
+          options, drawBorders, drawControls;
13765
+      styleOverride = styleOverride || { };
13766
+      drawBorders = typeof styleOverride.hasBorders !== 'undefined' ? styleOverride.hasBorders : this.hasBorders;
13767
+      drawControls = typeof styleOverride.hasControls !== 'undefined' ? styleOverride.hasControls : this.hasControls;
13768
+      matrix = fabric.util.multiplyTransformMatrices(vpt, matrix);
13769
+      options = fabric.util.qrDecompose(matrix);
13770
+      ctx.save();
13771
+      ctx.translate(options.translateX, options.translateY);
13772
+      ctx.lineWidth = 1 * this.borderScaleFactor;
13773
+      if (!this.group) {
13774
+        ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
13775
+      }
13776
+      if (styleOverride.forActiveSelection) {
13777
+        ctx.rotate(degreesToRadians(options.angle));
13778
+        drawBorders && this.drawBordersInGroup(ctx, options, styleOverride);
13779
+      }
13780
+      else {
13781
+        ctx.rotate(degreesToRadians(this.angle));
13782
+        drawBorders && this.drawBorders(ctx, styleOverride);
13783
+      }
13784
+      drawControls && this.drawControls(ctx, styleOverride);
13785
+      ctx.restore();
13786
+    },
13787
+
13788
+    /**
13789
+     * @private
13790
+     * @param {CanvasRenderingContext2D} ctx Context to render on
13791
+     */
13792
+    _setShadow: function(ctx) {
13793
+      if (!this.shadow) {
13794
+        return;
13795
+      }
13796
+
13797
+      var multX = (this.canvas && this.canvas.viewportTransform[0]) || 1,
13798
+          multY = (this.canvas && this.canvas.viewportTransform[3]) || 1,
13799
+          scaling = this.getObjectScaling();
13800
+      if (this.canvas && this.canvas._isRetinaScaling()) {
13801
+        multX *= fabric.devicePixelRatio;
13802
+        multY *= fabric.devicePixelRatio;
13803
+      }
13804
+      ctx.shadowColor = this.shadow.color;
13805
+      ctx.shadowBlur = this.shadow.blur * fabric.browserShadowBlurConstant *
13806
+        (multX + multY) * (scaling.scaleX + scaling.scaleY) / 4;
13807
+      ctx.shadowOffsetX = this.shadow.offsetX * multX * scaling.scaleX;
13808
+      ctx.shadowOffsetY = this.shadow.offsetY * multY * scaling.scaleY;
13809
+    },
13810
+
13811
+    /**
13812
+     * @private
13813
+     * @param {CanvasRenderingContext2D} ctx Context to render on
13814
+     */
13815
+    _removeShadow: function(ctx) {
13816
+      if (!this.shadow) {
13817
+        return;
13818
+      }
13819
+
13820
+      ctx.shadowColor = '';
13821
+      ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
13822
+    },
13823
+
13824
+    /**
13825
+     * @private
13826
+     * @param {CanvasRenderingContext2D} ctx Context to render on
13827
+     * @param {Object} filler fabric.Pattern or fabric.Gradient
13828
+     */
13829
+    _applyPatternGradientTransform: function(ctx, filler) {
13830
+      if (!filler || !filler.toLive) {
13831
+        return { offsetX: 0, offsetY: 0 };
13832
+      }
13833
+      var t = filler.gradientTransform || filler.patternTransform;
13834
+      var offsetX = -this.width / 2 + filler.offsetX || 0,
13835
+          offsetY = -this.height / 2 + filler.offsetY || 0;
13836
+      ctx.translate(offsetX, offsetY);
13837
+      if (t) {
13838
+        ctx.transform(t[0], t[1], t[2], t[3], t[4], t[5]);
13839
+      }
13840
+      return { offsetX: offsetX, offsetY: offsetY };
13841
+    },
13842
+
13843
+    /**
13844
+     * @private
13845
+     * @param {CanvasRenderingContext2D} ctx Context to render on
13846
+     */
13847
+    _renderPaintInOrder: function(ctx) {
13848
+      if (this.paintFirst === 'stroke') {
13849
+        this._renderStroke(ctx);
13850
+        this._renderFill(ctx);
13851
+      }
13852
+      else {
13853
+        this._renderFill(ctx);
13854
+        this._renderStroke(ctx);
13855
+      }
13856
+    },
13857
+
13858
+    /**
13859
+     * @private
13860
+     * @param {CanvasRenderingContext2D} ctx Context to render on
13861
+     */
13862
+    _renderFill: function(ctx) {
13863
+      if (!this.fill) {
13864
+        return;
13865
+      }
13866
+
13867
+      ctx.save();
13868
+      this._applyPatternGradientTransform(ctx, this.fill);
13869
+      if (this.fillRule === 'evenodd') {
13870
+        ctx.fill('evenodd');
13871
+      }
13872
+      else {
13873
+        ctx.fill();
13874
+      }
13875
+      ctx.restore();
13876
+    },
13877
+
13878
+    _renderStroke: function(ctx) {
13879
+      if (!this.stroke || this.strokeWidth === 0) {
13880
+        return;
13881
+      }
13882
+
13883
+      if (this.shadow && !this.shadow.affectStroke) {
13884
+        this._removeShadow(ctx);
13885
+      }
13886
+
13887
+      ctx.save();
13888
+      this._setLineDash(ctx, this.strokeDashArray, this._renderDashedStroke);
13889
+      this._applyPatternGradientTransform(ctx, this.stroke);
13890
+      ctx.stroke();
13891
+      ctx.restore();
13892
+    },
13893
+
13894
+    /**
13895
+     * This function is an helper for svg import. it returns the center of the object in the svg
13896
+     * untransformed coordinates
13897
+     * @private
13898
+     * @return {Object} center point from element coordinates
13899
+     */
13900
+    _findCenterFromElement: function() {
13901
+      return { x: this.left + this.width / 2, y: this.top + this.height / 2 };
13902
+    },
13903
+
13904
+    /**
13905
+     * This function is an helper for svg import. it decompose the transformMatrix
13906
+     * and assign properties to object.
13907
+     * untransformed coordinates
13908
+     * @private
13909
+     * @chainable
13910
+     */
13911
+    _assignTransformMatrixProps: function() {
13912
+      if (this.transformMatrix) {
13913
+        var options = fabric.util.qrDecompose(this.transformMatrix);
13914
+        this.flipX = false;
13915
+        this.flipY = false;
13916
+        this.set('scaleX', options.scaleX);
13917
+        this.set('scaleY', options.scaleY);
13918
+        this.angle = options.angle;
13919
+        this.skewX = options.skewX;
13920
+        this.skewY = 0;
13921
+      }
13922
+    },
13923
+
13924
+    /**
13925
+     * This function is an helper for svg import. it removes the transform matrix
13926
+     * and set to object properties that fabricjs can handle
13927
+     * @private
13928
+     * @param {Object} preserveAspectRatioOptions
13929
+     * @return {thisArg}
13930
+     */
13931
+    _removeTransformMatrix: function(preserveAspectRatioOptions) {
13932
+      var center = this._findCenterFromElement();
13933
+      if (this.transformMatrix) {
13934
+        this._assignTransformMatrixProps();
13935
+        center = fabric.util.transformPoint(center, this.transformMatrix);
13936
+      }
13937
+      this.transformMatrix = null;
13938
+      if (preserveAspectRatioOptions) {
13939
+        this.scaleX *= preserveAspectRatioOptions.scaleX;
13940
+        this.scaleY *= preserveAspectRatioOptions.scaleY;
13941
+        this.cropX = preserveAspectRatioOptions.cropX;
13942
+        this.cropY = preserveAspectRatioOptions.cropY;
13943
+        center.x += preserveAspectRatioOptions.offsetLeft;
13944
+        center.y += preserveAspectRatioOptions.offsetTop;
13945
+        this.width = preserveAspectRatioOptions.width;
13946
+        this.height = preserveAspectRatioOptions.height;
13947
+      }
13948
+      this.setPositionByOrigin(center, 'center', 'center');
13949
+    },
13950
+
13951
+    /**
13952
+     * Clones an instance, using a callback method will work for every object.
13953
+     * @param {Function} callback Callback is invoked with a clone as a first argument
13954
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
13955
+     */
13956
+    clone: function(callback, propertiesToInclude) {
13957
+      var objectForm = this.toObject(propertiesToInclude);
13958
+      if (this.constructor.fromObject) {
13959
+        this.constructor.fromObject(objectForm, callback);
13960
+      }
13961
+      else {
13962
+        fabric.Object._fromObject('Object', objectForm, callback);
13963
+      }
13964
+    },
13965
+
13966
+    /**
13967
+     * Creates an instance of fabric.Image out of an object
13968
+     * @param {Function} callback callback, invoked with an instance as a first argument
13969
+     * @param {Object} [options] for clone as image, passed to toDataURL
13970
+     * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
13971
+     * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
13972
+     * @param {Number} [options.multiplier=1] Multiplier to scale by
13973
+     * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
13974
+     * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
13975
+     * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
13976
+     * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
13977
+     * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 1.6.4
13978
+     * @param {Boolean} [options.withoutTransform] Remove current object transform ( no scale , no angle, no flip, no skew ). Introduced in 2.3.4
13979
+     * @param {Boolean} [options.withoutShadow] Remove current object shadow. Introduced in 2.4.2
13980
+     * @return {fabric.Object} thisArg
13981
+     */
13982
+    cloneAsImage: function(callback, options) {
13983
+      var dataUrl = this.toDataURL(options);
13984
+      fabric.util.loadImage(dataUrl, function(img) {
13985
+        if (callback) {
13986
+          callback(new fabric.Image(img));
13987
+        }
13988
+      });
13989
+      return this;
13990
+    },
13991
+
13992
+    /**
13993
+     * Converts an object into a data-url-like string
13994
+     * @param {Object} options Options object
13995
+     * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
13996
+     * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
13997
+     * @param {Number} [options.multiplier=1] Multiplier to scale by
13998
+     * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
13999
+     * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
14000
+     * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
14001
+     * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
14002
+     * @param {Boolean} [options.enableRetinaScaling] Enable retina scaling for clone image. Introduce in 1.6.4
14003
+     * @param {Boolean} [options.withoutTransform] Remove current object transform ( no scale , no angle, no flip, no skew ). Introduced in 2.3.4
14004
+     * @param {Boolean} [options.withoutShadow] Remove current object shadow. Introduced in 2.4.2
14005
+     * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
14006
+     */
14007
+    toDataURL: function(options) {
14008
+      options || (options = { });
14009
+
14010
+      var utils = fabric.util, origParams = utils.saveObjectTransform(this),
14011
+          originalShadow = this.shadow, abs = Math.abs;
14012
+
14013
+      if (options.withoutTransform) {
14014
+        utils.resetObjectTransform(this);
14015
+      }
14016
+      if (options.withoutShadow) {
14017
+        this.shadow = null;
14018
+      }
14019
+
14020
+      var el = fabric.util.createCanvasElement(),
14021
+          // skip canvas zoom and calculate with setCoords now.
14022
+          boundingRect = this.getBoundingRect(true, true),
14023
+          shadow = this.shadow, scaling,
14024
+          shadowOffset = { x: 0, y: 0 }, shadowBlur;
14025
+
14026
+      if (shadow) {
14027
+        shadowBlur = shadow.blur;
14028
+        scaling = this.getObjectScaling();
14029
+        shadowOffset.x = 2 * Math.round((abs(shadow.offsetX) + shadowBlur) * abs(scaling.scaleX));
14030
+        shadowOffset.y = 2 * Math.round((abs(shadow.offsetY) + shadowBlur) * abs(scaling.scaleY));
14031
+      }
14032
+      el.width = boundingRect.width + shadowOffset.x;
14033
+      el.height = boundingRect.height + shadowOffset.y;
14034
+      el.width += el.width % 2 ? 2 - el.width % 2 : 0;
14035
+      el.height += el.height % 2 ? 2 - el.height % 2 : 0;
14036
+      var canvas = new fabric.StaticCanvas(el, {
14037
+        enableRetinaScaling: options.enableRetinaScaling,
14038
+        renderOnAddRemove: false,
14039
+        skipOffscreen: false,
14040
+      });
14041
+      if (options.format === 'jpeg') {
14042
+        canvas.backgroundColor = '#fff';
14043
+      }
14044
+      this.setPositionByOrigin(new fabric.Point(canvas.width / 2, canvas.height / 2), 'center', 'center');
14045
+
14046
+      var originalCanvas = this.canvas;
14047
+      canvas.add(this);
14048
+      var data = canvas.toDataURL(options);
14049
+      this.shadow = originalShadow;
14050
+      this.set(origParams).setCoords();
14051
+      this.canvas = originalCanvas;
14052
+      // canvas.dispose will call image.dispose that will nullify the elements
14053
+      // since this canvas is a simple element for the process, we remove references
14054
+      // to objects in this way in order to avoid object trashing.
14055
+      canvas._objects = [];
14056
+      canvas.dispose();
14057
+      canvas = null;
14058
+
14059
+      return data;
14060
+    },
14061
+
14062
+    /**
14063
+     * Returns true if specified type is identical to the type of an instance
14064
+     * @param {String} type Type to check against
14065
+     * @return {Boolean}
14066
+     */
14067
+    isType: function(type) {
14068
+      return this.type === type;
14069
+    },
14070
+
14071
+    /**
14072
+     * Returns complexity of an instance
14073
+     * @return {Number} complexity of this instance (is 1 unless subclassed)
14074
+     */
14075
+    complexity: function() {
14076
+      return 1;
14077
+    },
14078
+
14079
+    /**
14080
+     * Returns a JSON representation of an instance
14081
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
14082
+     * @return {Object} JSON
14083
+     */
14084
+    toJSON: function(propertiesToInclude) {
14085
+      // delegate, not alias
14086
+      return this.toObject(propertiesToInclude);
14087
+    },
14088
+
14089
+    /**
14090
+     * Sets gradient (fill or stroke) of an object
14091
+     * <b>Backwards incompatibility note:</b> This method was named "setGradientFill" until v1.1.0
14092
+     * @param {String} property Property name 'stroke' or 'fill'
14093
+     * @param {Object} [options] Options object
14094
+     * @param {String} [options.type] Type of gradient 'radial' or 'linear'
14095
+     * @param {Number} [options.x1=0] x-coordinate of start point
14096
+     * @param {Number} [options.y1=0] y-coordinate of start point
14097
+     * @param {Number} [options.x2=0] x-coordinate of end point
14098
+     * @param {Number} [options.y2=0] y-coordinate of end point
14099
+     * @param {Number} [options.r1=0] Radius of start point (only for radial gradients)
14100
+     * @param {Number} [options.r2=0] Radius of end point (only for radial gradients)
14101
+     * @param {Object} [options.colorStops] Color stops object eg. {0: 'ff0000', 1: '000000'}
14102
+     * @param {Object} [options.gradientTransform] transformMatrix for gradient
14103
+     * @return {fabric.Object} thisArg
14104
+     * @chainable
14105
+     * @see {@link http://jsfiddle.net/fabricjs/58y8b/|jsFiddle demo}
14106
+     * @example <caption>Set linear gradient</caption>
14107
+     * object.setGradient('fill', {
14108
+     *   type: 'linear',
14109
+     *   x1: -object.width / 2,
14110
+     *   y1: 0,
14111
+     *   x2: object.width / 2,
14112
+     *   y2: 0,
14113
+     *   colorStops: {
14114
+     *     0: 'red',
14115
+     *     0.5: '#005555',
14116
+     *     1: 'rgba(0,0,255,0.5)'
14117
+     *   }
14118
+     * });
14119
+     * canvas.renderAll();
14120
+     * @example <caption>Set radial gradient</caption>
14121
+     * object.setGradient('fill', {
14122
+     *   type: 'radial',
14123
+     *   x1: 0,
14124
+     *   y1: 0,
14125
+     *   x2: 0,
14126
+     *   y2: 0,
14127
+     *   r1: object.width / 2,
14128
+     *   r2: 10,
14129
+     *   colorStops: {
14130
+     *     0: 'red',
14131
+     *     0.5: '#005555',
14132
+     *     1: 'rgba(0,0,255,0.5)'
14133
+     *   }
14134
+     * });
14135
+     * canvas.renderAll();
14136
+     */
14137
+    setGradient: function(property, options) {
14138
+      options || (options = { });
14139
+
14140
+      var gradient = { colorStops: [] };
14141
+
14142
+      gradient.type = options.type || (options.r1 || options.r2 ? 'radial' : 'linear');
14143
+      gradient.coords = {
14144
+        x1: options.x1,
14145
+        y1: options.y1,
14146
+        x2: options.x2,
14147
+        y2: options.y2
14148
+      };
14149
+
14150
+      if (options.r1 || options.r2) {
14151
+        gradient.coords.r1 = options.r1;
14152
+        gradient.coords.r2 = options.r2;
14153
+      }
14154
+
14155
+      gradient.gradientTransform = options.gradientTransform;
14156
+      fabric.Gradient.prototype.addColorStop.call(gradient, options.colorStops);
14157
+
14158
+      return this.set(property, fabric.Gradient.forObject(this, gradient));
14159
+    },
14160
+
14161
+    /**
14162
+     * Sets pattern fill of an object
14163
+     * @param {Object} options Options object
14164
+     * @param {(String|HTMLImageElement)} options.source Pattern source
14165
+     * @param {String} [options.repeat=repeat] Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat)
14166
+     * @param {Number} [options.offsetX=0] Pattern horizontal offset from object's left/top corner
14167
+     * @param {Number} [options.offsetY=0] Pattern vertical offset from object's left/top corner
14168
+     * @param {Function} [callback] Callback to invoke when image set as a pattern
14169
+     * @return {fabric.Object} thisArg
14170
+     * @chainable
14171
+     * @see {@link http://jsfiddle.net/fabricjs/QT3pa/|jsFiddle demo}
14172
+     * @example <caption>Set pattern</caption>
14173
+     * object.setPatternFill({
14174
+     *   source: 'http://fabricjs.com/assets/escheresque_ste.png',
14175
+     *   repeat: 'repeat'
14176
+     * },canvas.renderAll.bind(canvas));
14177
+     */
14178
+    setPatternFill: function(options, callback) {
14179
+      return this.set('fill', new fabric.Pattern(options, callback));
14180
+    },
14181
+
14182
+    /**
14183
+     * Sets {@link fabric.Object#shadow|shadow} of an object
14184
+     * @param {Object|String} [options] Options object or string (e.g. "2px 2px 10px rgba(0,0,0,0.2)")
14185
+     * @param {String} [options.color=rgb(0,0,0)] Shadow color
14186
+     * @param {Number} [options.blur=0] Shadow blur
14187
+     * @param {Number} [options.offsetX=0] Shadow horizontal offset
14188
+     * @param {Number} [options.offsetY=0] Shadow vertical offset
14189
+     * @return {fabric.Object} thisArg
14190
+     * @chainable
14191
+     * @see {@link http://jsfiddle.net/fabricjs/7gvJG/|jsFiddle demo}
14192
+     * @example <caption>Set shadow with string notation</caption>
14193
+     * object.setShadow('2px 2px 10px rgba(0,0,0,0.2)');
14194
+     * canvas.renderAll();
14195
+     * @example <caption>Set shadow with object notation</caption>
14196
+     * object.setShadow({
14197
+     *   color: 'red',
14198
+     *   blur: 10,
14199
+     *   offsetX: 20,
14200
+     *   offsetY: 20
14201
+     * });
14202
+     * canvas.renderAll();
14203
+     */
14204
+    setShadow: function(options) {
14205
+      return this.set('shadow', options ? new fabric.Shadow(options) : null);
14206
+    },
14207
+
14208
+    /**
14209
+     * Sets "color" of an instance (alias of `set('fill', &hellip;)`)
14210
+     * @param {String} color Color value
14211
+     * @return {fabric.Object} thisArg
14212
+     * @chainable
14213
+     */
14214
+    setColor: function(color) {
14215
+      this.set('fill', color);
14216
+      return this;
14217
+    },
14218
+
14219
+    /**
14220
+     * Sets "angle" of an instance with centered rotation
14221
+     * @param {Number} angle Angle value (in degrees)
14222
+     * @return {fabric.Object} thisArg
14223
+     * @chainable
14224
+     */
14225
+    rotate: function(angle) {
14226
+      var shouldCenterOrigin = (this.originX !== 'center' || this.originY !== 'center') && this.centeredRotation;
14227
+
14228
+      if (shouldCenterOrigin) {
14229
+        this._setOriginToCenter();
14230
+      }
14231
+
14232
+      this.set('angle', angle);
14233
+
14234
+      if (shouldCenterOrigin) {
14235
+        this._resetOrigin();
14236
+      }
14237
+
14238
+      return this;
14239
+    },
14240
+
14241
+    /**
14242
+     * Centers object horizontally on canvas to which it was added last.
14243
+     * You might need to call `setCoords` on an object after centering, to update controls area.
14244
+     * @return {fabric.Object} thisArg
14245
+     * @chainable
14246
+     */
14247
+    centerH: function () {
14248
+      this.canvas && this.canvas.centerObjectH(this);
14249
+      return this;
14250
+    },
14251
+
14252
+    /**
14253
+     * Centers object horizontally on current viewport of canvas to which it was added last.
14254
+     * You might need to call `setCoords` on an object after centering, to update controls area.
14255
+     * @return {fabric.Object} thisArg
14256
+     * @chainable
14257
+     */
14258
+    viewportCenterH: function () {
14259
+      this.canvas && this.canvas.viewportCenterObjectH(this);
14260
+      return this;
14261
+    },
14262
+
14263
+    /**
14264
+     * Centers object vertically on canvas to which it was added last.
14265
+     * You might need to call `setCoords` on an object after centering, to update controls area.
14266
+     * @return {fabric.Object} thisArg
14267
+     * @chainable
14268
+     */
14269
+    centerV: function () {
14270
+      this.canvas && this.canvas.centerObjectV(this);
14271
+      return this;
14272
+    },
14273
+
14274
+    /**
14275
+     * Centers object vertically on current viewport of canvas to which it was added last.
14276
+     * You might need to call `setCoords` on an object after centering, to update controls area.
14277
+     * @return {fabric.Object} thisArg
14278
+     * @chainable
14279
+     */
14280
+    viewportCenterV: function () {
14281
+      this.canvas && this.canvas.viewportCenterObjectV(this);
14282
+      return this;
14283
+    },
14284
+
14285
+    /**
14286
+     * Centers object vertically and horizontally on canvas to which is was added last
14287
+     * You might need to call `setCoords` on an object after centering, to update controls area.
14288
+     * @return {fabric.Object} thisArg
14289
+     * @chainable
14290
+     */
14291
+    center: function () {
14292
+      this.canvas && this.canvas.centerObject(this);
14293
+      return this;
14294
+    },
14295
+
14296
+    /**
14297
+     * Centers object on current viewport of canvas to which it was added last.
14298
+     * You might need to call `setCoords` on an object after centering, to update controls area.
14299
+     * @return {fabric.Object} thisArg
14300
+     * @chainable
14301
+     */
14302
+    viewportCenter: function () {
14303
+      this.canvas && this.canvas.viewportCenterObject(this);
14304
+      return this;
14305
+    },
14306
+
14307
+    /**
14308
+     * Returns coordinates of a pointer relative to an object
14309
+     * @param {Event} e Event to operate upon
14310
+     * @param {Object} [pointer] Pointer to operate upon (instead of event)
14311
+     * @return {Object} Coordinates of a pointer (x, y)
14312
+     */
14313
+    getLocalPointer: function(e, pointer) {
14314
+      pointer = pointer || this.canvas.getPointer(e);
14315
+      var pClicked = new fabric.Point(pointer.x, pointer.y),
14316
+          objectLeftTop = this._getLeftTopCoords();
14317
+      if (this.angle) {
14318
+        pClicked = fabric.util.rotatePoint(
14319
+          pClicked, objectLeftTop, degreesToRadians(-this.angle));
14320
+      }
14321
+      return {
14322
+        x: pClicked.x - objectLeftTop.x,
14323
+        y: pClicked.y - objectLeftTop.y
14324
+      };
14325
+    },
14326
+
14327
+    /**
14328
+     * Sets canvas globalCompositeOperation for specific object
14329
+     * custom composition operation for the particular object can be specified using globalCompositeOperation property
14330
+     * @param {CanvasRenderingContext2D} ctx Rendering canvas context
14331
+     */
14332
+    _setupCompositeOperation: function (ctx) {
14333
+      if (this.globalCompositeOperation) {
14334
+        ctx.globalCompositeOperation = this.globalCompositeOperation;
14335
+      }
14336
+    }
14337
+  });
14338
+
14339
+  fabric.util.createAccessors && fabric.util.createAccessors(fabric.Object);
14340
+
14341
+  extend(fabric.Object.prototype, fabric.Observable);
14342
+
14343
+  /**
14344
+   * Defines the number of fraction digits to use when serializing object values.
14345
+   * You can use it to increase/decrease precision of such values like left, top, scaleX, scaleY, etc.
14346
+   * @static
14347
+   * @memberOf fabric.Object
14348
+   * @constant
14349
+   * @type Number
14350
+   */
14351
+  fabric.Object.NUM_FRACTION_DIGITS = 2;
14352
+
14353
+  fabric.Object._fromObject = function(className, object, callback, extraParam) {
14354
+    var klass = fabric[className];
14355
+    object = clone(object, true);
14356
+    fabric.util.enlivenPatterns([object.fill, object.stroke], function(patterns) {
14357
+      if (typeof patterns[0] !== 'undefined') {
14358
+        object.fill = patterns[0];
14359
+      }
14360
+      if (typeof patterns[1] !== 'undefined') {
14361
+        object.stroke = patterns[1];
14362
+      }
14363
+      fabric.util.enlivenObjects([object.clipPath], function(enlivedProps) {
14364
+        object.clipPath = enlivedProps[0];
14365
+        var instance = extraParam ? new klass(object[extraParam], object) : new klass(object);
14366
+        callback && callback(instance);
14367
+      });
14368
+    });
14369
+  };
14370
+
14371
+  /**
14372
+   * Unique id used internally when creating SVG elements
14373
+   * @static
14374
+   * @memberOf fabric.Object
14375
+   * @type Number
14376
+   */
14377
+  fabric.Object.__uid = 0;
14378
+
14379
+})(typeof exports !== 'undefined' ? exports : this);
14380
+
14381
+
14382
+(function() {
14383
+
14384
+  var degreesToRadians = fabric.util.degreesToRadians,
14385
+      originXOffset = {
14386
+        left: -0.5,
14387
+        center: 0,
14388
+        right: 0.5
14389
+      },
14390
+      originYOffset = {
14391
+        top: -0.5,
14392
+        center: 0,
14393
+        bottom: 0.5
14394
+      };
14395
+
14396
+  fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
14397
+
14398
+    /**
14399
+     * Translates the coordinates from a set of origin to another (based on the object's dimensions)
14400
+     * @param {fabric.Point} point The point which corresponds to the originX and originY params
14401
+     * @param {String} fromOriginX Horizontal origin: 'left', 'center' or 'right'
14402
+     * @param {String} fromOriginY Vertical origin: 'top', 'center' or 'bottom'
14403
+     * @param {String} toOriginX Horizontal origin: 'left', 'center' or 'right'
14404
+     * @param {String} toOriginY Vertical origin: 'top', 'center' or 'bottom'
14405
+     * @return {fabric.Point}
14406
+     */
14407
+    translateToGivenOrigin: function(point, fromOriginX, fromOriginY, toOriginX, toOriginY) {
14408
+      var x = point.x,
14409
+          y = point.y,
14410
+          offsetX, offsetY, dim;
14411
+
14412
+      if (typeof fromOriginX === 'string') {
14413
+        fromOriginX = originXOffset[fromOriginX];
14414
+      }
14415
+      else {
14416
+        fromOriginX -= 0.5;
14417
+      }
14418
+
14419
+      if (typeof toOriginX === 'string') {
14420
+        toOriginX = originXOffset[toOriginX];
14421
+      }
14422
+      else {
14423
+        toOriginX -= 0.5;
14424
+      }
14425
+
14426
+      offsetX = toOriginX - fromOriginX;
14427
+
14428
+      if (typeof fromOriginY === 'string') {
14429
+        fromOriginY = originYOffset[fromOriginY];
14430
+      }
14431
+      else {
14432
+        fromOriginY -= 0.5;
14433
+      }
14434
+
14435
+      if (typeof toOriginY === 'string') {
14436
+        toOriginY = originYOffset[toOriginY];
14437
+      }
14438
+      else {
14439
+        toOriginY -= 0.5;
14440
+      }
14441
+
14442
+      offsetY = toOriginY - fromOriginY;
14443
+
14444
+      if (offsetX || offsetY) {
14445
+        dim = this._getTransformedDimensions();
14446
+        x = point.x + offsetX * dim.x;
14447
+        y = point.y + offsetY * dim.y;
14448
+      }
14449
+
14450
+      return new fabric.Point(x, y);
14451
+    },
14452
+
14453
+    /**
14454
+     * Translates the coordinates from origin to center coordinates (based on the object's dimensions)
14455
+     * @param {fabric.Point} point The point which corresponds to the originX and originY params
14456
+     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
14457
+     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
14458
+     * @return {fabric.Point}
14459
+     */
14460
+    translateToCenterPoint: function(point, originX, originY) {
14461
+      var p = this.translateToGivenOrigin(point, originX, originY, 'center', 'center');
14462
+      if (this.angle) {
14463
+        return fabric.util.rotatePoint(p, point, degreesToRadians(this.angle));
14464
+      }
14465
+      return p;
14466
+    },
14467
+
14468
+    /**
14469
+     * Translates the coordinates from center to origin coordinates (based on the object's dimensions)
14470
+     * @param {fabric.Point} center The point which corresponds to center of the object
14471
+     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
14472
+     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
14473
+     * @return {fabric.Point}
14474
+     */
14475
+    translateToOriginPoint: function(center, originX, originY) {
14476
+      var p = this.translateToGivenOrigin(center, 'center', 'center', originX, originY);
14477
+      if (this.angle) {
14478
+        return fabric.util.rotatePoint(p, center, degreesToRadians(this.angle));
14479
+      }
14480
+      return p;
14481
+    },
14482
+
14483
+    /**
14484
+     * Returns the real center coordinates of the object
14485
+     * @return {fabric.Point}
14486
+     */
14487
+    getCenterPoint: function() {
14488
+      var leftTop = new fabric.Point(this.left, this.top);
14489
+      return this.translateToCenterPoint(leftTop, this.originX, this.originY);
14490
+    },
14491
+
14492
+    /**
14493
+     * Returns the coordinates of the object based on center coordinates
14494
+     * @param {fabric.Point} point The point which corresponds to the originX and originY params
14495
+     * @return {fabric.Point}
14496
+     */
14497
+    // getOriginPoint: function(center) {
14498
+    //   return this.translateToOriginPoint(center, this.originX, this.originY);
14499
+    // },
14500
+
14501
+    /**
14502
+     * Returns the coordinates of the object as if it has a different origin
14503
+     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
14504
+     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
14505
+     * @return {fabric.Point}
14506
+     */
14507
+    getPointByOrigin: function(originX, originY) {
14508
+      var center = this.getCenterPoint();
14509
+      return this.translateToOriginPoint(center, originX, originY);
14510
+    },
14511
+
14512
+    /**
14513
+     * Returns the point in local coordinates
14514
+     * @param {fabric.Point} point The point relative to the global coordinate system
14515
+     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
14516
+     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
14517
+     * @return {fabric.Point}
14518
+     */
14519
+    toLocalPoint: function(point, originX, originY) {
14520
+      var center = this.getCenterPoint(),
14521
+          p, p2;
14522
+
14523
+      if (typeof originX !== 'undefined' && typeof originY !== 'undefined' ) {
14524
+        p = this.translateToGivenOrigin(center, 'center', 'center', originX, originY);
14525
+      }
14526
+      else {
14527
+        p = new fabric.Point(this.left, this.top);
14528
+      }
14529
+
14530
+      p2 = new fabric.Point(point.x, point.y);
14531
+      if (this.angle) {
14532
+        p2 = fabric.util.rotatePoint(p2, center, -degreesToRadians(this.angle));
14533
+      }
14534
+      return p2.subtractEquals(p);
14535
+    },
14536
+
14537
+    /**
14538
+     * Returns the point in global coordinates
14539
+     * @param {fabric.Point} The point relative to the local coordinate system
14540
+     * @return {fabric.Point}
14541
+     */
14542
+    // toGlobalPoint: function(point) {
14543
+    //   return fabric.util.rotatePoint(point, this.getCenterPoint(), degreesToRadians(this.angle)).addEquals(new fabric.Point(this.left, this.top));
14544
+    // },
14545
+
14546
+    /**
14547
+     * Sets the position of the object taking into consideration the object's origin
14548
+     * @param {fabric.Point} pos The new position of the object
14549
+     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
14550
+     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
14551
+     * @return {void}
14552
+     */
14553
+    setPositionByOrigin: function(pos, originX, originY) {
14554
+      var center = this.translateToCenterPoint(pos, originX, originY),
14555
+          position = this.translateToOriginPoint(center, this.originX, this.originY);
14556
+      this.set('left', position.x);
14557
+      this.set('top', position.y);
14558
+    },
14559
+
14560
+    /**
14561
+     * @param {String} to One of 'left', 'center', 'right'
14562
+     */
14563
+    adjustPosition: function(to) {
14564
+      var angle = degreesToRadians(this.angle),
14565
+          hypotFull = this.getScaledWidth(),
14566
+          xFull = fabric.util.cos(angle) * hypotFull,
14567
+          yFull = fabric.util.sin(angle) * hypotFull,
14568
+          offsetFrom, offsetTo;
14569
+
14570
+      //TODO: this function does not consider mixed situation like top, center.
14571
+      if (typeof this.originX === 'string') {
14572
+        offsetFrom = originXOffset[this.originX];
14573
+      }
14574
+      else {
14575
+        offsetFrom = this.originX - 0.5;
14576
+      }
14577
+      if (typeof to === 'string') {
14578
+        offsetTo = originXOffset[to];
14579
+      }
14580
+      else {
14581
+        offsetTo = to - 0.5;
14582
+      }
14583
+      this.left += xFull * (offsetTo - offsetFrom);
14584
+      this.top += yFull * (offsetTo - offsetFrom);
14585
+      this.setCoords();
14586
+      this.originX = to;
14587
+    },
14588
+
14589
+    /**
14590
+     * Sets the origin/position of the object to it's center point
14591
+     * @private
14592
+     * @return {void}
14593
+     */
14594
+    _setOriginToCenter: function() {
14595
+      this._originalOriginX = this.originX;
14596
+      this._originalOriginY = this.originY;
14597
+
14598
+      var center = this.getCenterPoint();
14599
+
14600
+      this.originX = 'center';
14601
+      this.originY = 'center';
14602
+
14603
+      this.left = center.x;
14604
+      this.top = center.y;
14605
+    },
14606
+
14607
+    /**
14608
+     * Resets the origin/position of the object to it's original origin
14609
+     * @private
14610
+     * @return {void}
14611
+     */
14612
+    _resetOrigin: function() {
14613
+      var originPoint = this.translateToOriginPoint(
14614
+        this.getCenterPoint(),
14615
+        this._originalOriginX,
14616
+        this._originalOriginY);
14617
+
14618
+      this.originX = this._originalOriginX;
14619
+      this.originY = this._originalOriginY;
14620
+
14621
+      this.left = originPoint.x;
14622
+      this.top = originPoint.y;
14623
+
14624
+      this._originalOriginX = null;
14625
+      this._originalOriginY = null;
14626
+    },
14627
+
14628
+    /**
14629
+     * @private
14630
+     */
14631
+    _getLeftTopCoords: function() {
14632
+      return this.translateToOriginPoint(this.getCenterPoint(), 'left', 'top');
14633
+    },
14634
+  });
14635
+
14636
+})();
14637
+
14638
+
14639
+(function() {
14640
+
14641
+  function getCoords(coords) {
14642
+    return [
14643
+      new fabric.Point(coords.tl.x, coords.tl.y),
14644
+      new fabric.Point(coords.tr.x, coords.tr.y),
14645
+      new fabric.Point(coords.br.x, coords.br.y),
14646
+      new fabric.Point(coords.bl.x, coords.bl.y)
14647
+    ];
14648
+  }
14649
+
14650
+  var degreesToRadians = fabric.util.degreesToRadians,
14651
+      multiplyMatrices = fabric.util.multiplyTransformMatrices,
14652
+      transformPoint = fabric.util.transformPoint;
14653
+
14654
+  fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
14655
+
14656
+    /**
14657
+     * Describe object's corner position in canvas element coordinates.
14658
+     * properties are tl,mt,tr,ml,mr,bl,mb,br,mtr for the main controls.
14659
+     * each property is an object with x, y and corner.
14660
+     * The `corner` property contains in a similar manner the 4 points of the
14661
+     * interactive area of the corner.
14662
+     * The coordinates depends from this properties: width, height, scaleX, scaleY
14663
+     * skewX, skewY, angle, strokeWidth, viewportTransform, top, left, padding.
14664
+     * The coordinates get updated with @method setCoords.
14665
+     * You can calculate them without updating with @method calcCoords;
14666
+     * @memberOf fabric.Object.prototype
14667
+     */
14668
+    oCoords: null,
14669
+
14670
+    /**
14671
+     * Describe object's corner position in canvas object absolute coordinates
14672
+     * properties are tl,tr,bl,br and describe the four main corner.
14673
+     * each property is an object with x, y, instance of Fabric.Point.
14674
+     * The coordinates depends from this properties: width, height, scaleX, scaleY
14675
+     * skewX, skewY, angle, strokeWidth, top, left.
14676
+     * Those coordinates are usefull to understand where an object is. They get updated
14677
+     * with oCoords but they do not need to be updated when zoom or panning change.
14678
+     * The coordinates get updated with @method setCoords.
14679
+     * You can calculate them without updating with @method calcCoords(true);
14680
+     * @memberOf fabric.Object.prototype
14681
+     */
14682
+    aCoords: null,
14683
+
14684
+    /**
14685
+     * storage for object transform matrix
14686
+     */
14687
+    ownMatrixCache: null,
14688
+
14689
+    /**
14690
+     * storage for object full transform matrix
14691
+     */
14692
+    matrixCache: null,
14693
+
14694
+    /**
14695
+     * return correct set of coordinates for intersection
14696
+     */
14697
+    getCoords: function(absolute, calculate) {
14698
+      if (!this.oCoords) {
14699
+        this.setCoords();
14700
+      }
14701
+      var coords = absolute ? this.aCoords : this.oCoords;
14702
+      return getCoords(calculate ? this.calcCoords(absolute) : coords);
14703
+    },
14704
+
14705
+    /**
14706
+     * Checks if object intersects with an area formed by 2 points
14707
+     * @param {Object} pointTL top-left point of area
14708
+     * @param {Object} pointBR bottom-right point of area
14709
+     * @param {Boolean} [absolute] use coordinates without viewportTransform
14710
+     * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
14711
+     * @return {Boolean} true if object intersects with an area formed by 2 points
14712
+     */
14713
+    intersectsWithRect: function(pointTL, pointBR, absolute, calculate) {
14714
+      var coords = this.getCoords(absolute, calculate),
14715
+          intersection = fabric.Intersection.intersectPolygonRectangle(
14716
+            coords,
14717
+            pointTL,
14718
+            pointBR
14719
+          );
14720
+      return intersection.status === 'Intersection';
14721
+    },
14722
+
14723
+    /**
14724
+     * Checks if object intersects with another object
14725
+     * @param {Object} other Object to test
14726
+     * @param {Boolean} [absolute] use coordinates without viewportTransform
14727
+     * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
14728
+     * @return {Boolean} true if object intersects with another object
14729
+     */
14730
+    intersectsWithObject: function(other, absolute, calculate) {
14731
+      var intersection = fabric.Intersection.intersectPolygonPolygon(
14732
+        this.getCoords(absolute, calculate),
14733
+        other.getCoords(absolute, calculate)
14734
+      );
14735
+
14736
+      return intersection.status === 'Intersection'
14737
+        || other.isContainedWithinObject(this, absolute, calculate)
14738
+        || this.isContainedWithinObject(other, absolute, calculate);
14739
+    },
14740
+
14741
+    /**
14742
+     * Checks if object is fully contained within area of another object
14743
+     * @param {Object} other Object to test
14744
+     * @param {Boolean} [absolute] use coordinates without viewportTransform
14745
+     * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
14746
+     * @return {Boolean} true if object is fully contained within area of another object
14747
+     */
14748
+    isContainedWithinObject: function(other, absolute, calculate) {
14749
+      var points = this.getCoords(absolute, calculate),
14750
+          i = 0, lines = other._getImageLines(
14751
+            calculate ? other.calcCoords(absolute) : absolute ? other.aCoords : other.oCoords
14752
+          );
14753
+      for (; i < 4; i++) {
14754
+        if (!other.containsPoint(points[i], lines)) {
14755
+          return false;
14756
+        }
14757
+      }
14758
+      return true;
14759
+    },
14760
+
14761
+    /**
14762
+     * Checks if object is fully contained within area formed by 2 points
14763
+     * @param {Object} pointTL top-left point of area
14764
+     * @param {Object} pointBR bottom-right point of area
14765
+     * @param {Boolean} [absolute] use coordinates without viewportTransform
14766
+     * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
14767
+     * @return {Boolean} true if object is fully contained within area formed by 2 points
14768
+     */
14769
+    isContainedWithinRect: function(pointTL, pointBR, absolute, calculate) {
14770
+      var boundingRect = this.getBoundingRect(absolute, calculate);
14771
+
14772
+      return (
14773
+        boundingRect.left >= pointTL.x &&
14774
+        boundingRect.left + boundingRect.width <= pointBR.x &&
14775
+        boundingRect.top >= pointTL.y &&
14776
+        boundingRect.top + boundingRect.height <= pointBR.y
14777
+      );
14778
+    },
14779
+
14780
+    /**
14781
+     * Checks if point is inside the object
14782
+     * @param {fabric.Point} point Point to check against
14783
+     * @param {Object} [lines] object returned from @method _getImageLines
14784
+     * @param {Boolean} [absolute] use coordinates without viewportTransform
14785
+     * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
14786
+     * @return {Boolean} true if point is inside the object
14787
+     */
14788
+    containsPoint: function(point, lines, absolute, calculate) {
14789
+      var lines = lines || this._getImageLines(
14790
+            calculate ? this.calcCoords(absolute) : absolute ? this.aCoords : this.oCoords
14791
+          ),
14792
+          xPoints = this._findCrossPoints(point, lines);
14793
+
14794
+      // if xPoints is odd then point is inside the object
14795
+      return (xPoints !== 0 && xPoints % 2 === 1);
14796
+    },
14797
+
14798
+    /**
14799
+     * Checks if object is contained within the canvas with current viewportTransform
14800
+     * the check is done stopping at first point that appears on screen
14801
+     * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
14802
+     * @return {Boolean} true if object is fully or partially contained within canvas
14803
+     */
14804
+    isOnScreen: function(calculate) {
14805
+      if (!this.canvas) {
14806
+        return false;
14807
+      }
14808
+      var pointTL = this.canvas.vptCoords.tl, pointBR = this.canvas.vptCoords.br;
14809
+      var points = this.getCoords(true, calculate), point;
14810
+      for (var i = 0; i < 4; i++) {
14811
+        point = points[i];
14812
+        if (point.x <= pointBR.x && point.x >= pointTL.x && point.y <= pointBR.y && point.y >= pointTL.y) {
14813
+          return true;
14814
+        }
14815
+      }
14816
+      // no points on screen, check intersection with absolute coordinates
14817
+      if (this.intersectsWithRect(pointTL, pointBR, true, calculate)) {
14818
+        return true;
14819
+      }
14820
+      return this._containsCenterOfCanvas(pointTL, pointBR, calculate);
14821
+    },
14822
+
14823
+    /**
14824
+     * Checks if the object contains the midpoint between canvas extremities
14825
+     * Does not make sense outside the context of isOnScreen and isPartiallyOnScreen
14826
+     * @private
14827
+     * @param {Fabric.Point} pointTL Top Left point
14828
+     * @param {Fabric.Point} pointBR Top Right point
14829
+     * @param {Boolean} calculate use coordinates of current position instead of .oCoords
14830
+     * @return {Boolean} true if the objects containe the point
14831
+     */
14832
+    _containsCenterOfCanvas: function(pointTL, pointBR, calculate) {
14833
+      // worst case scenario the object is so big that contains the screen
14834
+      var centerPoint = { x: (pointTL.x + pointBR.x) / 2, y: (pointTL.y + pointBR.y) / 2 };
14835
+      if (this.containsPoint(centerPoint, null, true, calculate)) {
14836
+        return true;
14837
+      }
14838
+      return false;
14839
+    },
14840
+
14841
+    /**
14842
+     * Checks if object is partially contained within the canvas with current viewportTransform
14843
+     * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords
14844
+     * @return {Boolean} true if object is partially contained within canvas
14845
+     */
14846
+    isPartiallyOnScreen: function(calculate) {
14847
+      if (!this.canvas) {
14848
+        return false;
14849
+      }
14850
+      var pointTL = this.canvas.vptCoords.tl, pointBR = this.canvas.vptCoords.br;
14851
+      if (this.intersectsWithRect(pointTL, pointBR, true, calculate)) {
14852
+        return true;
14853
+      }
14854
+      return this._containsCenterOfCanvas(pointTL, pointBR, calculate);
14855
+    },
14856
+
14857
+    /**
14858
+     * Method that returns an object with the object edges in it, given the coordinates of the corners
14859
+     * @private
14860
+     * @param {Object} oCoords Coordinates of the object corners
14861
+     */
14862
+    _getImageLines: function(oCoords) {
14863
+      return {
14864
+        topline: {
14865
+          o: oCoords.tl,
14866
+          d: oCoords.tr
14867
+        },
14868
+        rightline: {
14869
+          o: oCoords.tr,
14870
+          d: oCoords.br
14871
+        },
14872
+        bottomline: {
14873
+          o: oCoords.br,
14874
+          d: oCoords.bl
14875
+        },
14876
+        leftline: {
14877
+          o: oCoords.bl,
14878
+          d: oCoords.tl
14879
+        }
14880
+      };
14881
+    },
14882
+
14883
+    /**
14884
+     * Helper method to determine how many cross points are between the 4 object edges
14885
+     * and the horizontal line determined by a point on canvas
14886
+     * @private
14887
+     * @param {fabric.Point} point Point to check
14888
+     * @param {Object} lines Coordinates of the object being evaluated
14889
+     */
14890
+    // remove yi, not used but left code here just in case.
14891
+    _findCrossPoints: function(point, lines) {
14892
+      var b1, b2, a1, a2, xi, // yi,
14893
+          xcount = 0,
14894
+          iLine;
14895
+
14896
+      for (var lineKey in lines) {
14897
+        iLine = lines[lineKey];
14898
+        // optimisation 1: line below point. no cross
14899
+        if ((iLine.o.y < point.y) && (iLine.d.y < point.y)) {
14900
+          continue;
14901
+        }
14902
+        // optimisation 2: line above point. no cross
14903
+        if ((iLine.o.y >= point.y) && (iLine.d.y >= point.y)) {
14904
+          continue;
14905
+        }
14906
+        // optimisation 3: vertical line case
14907
+        if ((iLine.o.x === iLine.d.x) && (iLine.o.x >= point.x)) {
14908
+          xi = iLine.o.x;
14909
+          // yi = point.y;
14910
+        }
14911
+        // calculate the intersection point
14912
+        else {
14913
+          b1 = 0;
14914
+          b2 = (iLine.d.y - iLine.o.y) / (iLine.d.x - iLine.o.x);
14915
+          a1 = point.y - b1 * point.x;
14916
+          a2 = iLine.o.y - b2 * iLine.o.x;
14917
+
14918
+          xi = -(a1 - a2) / (b1 - b2);
14919
+          // yi = a1 + b1 * xi;
14920
+        }
14921
+        // dont count xi < point.x cases
14922
+        if (xi >= point.x) {
14923
+          xcount += 1;
14924
+        }
14925
+        // optimisation 4: specific for square images
14926
+        if (xcount === 2) {
14927
+          break;
14928
+        }
14929
+      }
14930
+      return xcount;
14931
+    },
14932
+
14933
+    /**
14934
+     * Returns coordinates of object's bounding rectangle (left, top, width, height)
14935
+     * the box is intented as aligned to axis of canvas.
14936
+     * @param {Boolean} [absolute] use coordinates without viewportTransform
14937
+     * @param {Boolean} [calculate] use coordinates of current position instead of .oCoords / .aCoords
14938
+     * @return {Object} Object with left, top, width, height properties
14939
+     */
14940
+    getBoundingRect: function(absolute, calculate) {
14941
+      var coords = this.getCoords(absolute, calculate);
14942
+      return fabric.util.makeBoundingBoxFromPoints(coords);
14943
+    },
14944
+
14945
+    /**
14946
+     * Returns width of an object bounding box counting transformations
14947
+     * before 2.0 it was named getWidth();
14948
+     * @return {Number} width value
14949
+     */
14950
+    getScaledWidth: function() {
14951
+      return this._getTransformedDimensions().x;
14952
+    },
14953
+
14954
+    /**
14955
+     * Returns height of an object bounding box counting transformations
14956
+     * before 2.0 it was named getHeight();
14957
+     * @return {Number} height value
14958
+     */
14959
+    getScaledHeight: function() {
14960
+      return this._getTransformedDimensions().y;
14961
+    },
14962
+
14963
+    /**
14964
+     * Makes sure the scale is valid and modifies it if necessary
14965
+     * @private
14966
+     * @param {Number} value
14967
+     * @return {Number}
14968
+     */
14969
+    _constrainScale: function(value) {
14970
+      if (Math.abs(value) < this.minScaleLimit) {
14971
+        if (value < 0) {
14972
+          return -this.minScaleLimit;
14973
+        }
14974
+        else {
14975
+          return this.minScaleLimit;
14976
+        }
14977
+      }
14978
+      else if (value === 0) {
14979
+        return 0.0001;
14980
+      }
14981
+      return value;
14982
+    },
14983
+
14984
+    /**
14985
+     * Scales an object (equally by x and y)
14986
+     * @param {Number} value Scale factor
14987
+     * @return {fabric.Object} thisArg
14988
+     * @chainable
14989
+     */
14990
+    scale: function(value) {
14991
+      this._set('scaleX', value);
14992
+      this._set('scaleY', value);
14993
+      return this.setCoords();
14994
+    },
14995
+
14996
+    /**
14997
+     * Scales an object to a given width, with respect to bounding box (scaling by x/y equally)
14998
+     * @param {Number} value New width value
14999
+     * @param {Boolean} absolute ignore viewport
15000
+     * @return {fabric.Object} thisArg
15001
+     * @chainable
15002
+     */
15003
+    scaleToWidth: function(value, absolute) {
15004
+      // adjust to bounding rect factor so that rotated shapes would fit as well
15005
+      var boundingRectFactor = this.getBoundingRect(absolute).width / this.getScaledWidth();
15006
+      return this.scale(value / this.width / boundingRectFactor);
15007
+    },
15008
+
15009
+    /**
15010
+     * Scales an object to a given height, with respect to bounding box (scaling by x/y equally)
15011
+     * @param {Number} value New height value
15012
+     * @param {Boolean} absolute ignore viewport
15013
+     * @return {fabric.Object} thisArg
15014
+     * @chainable
15015
+     */
15016
+    scaleToHeight: function(value, absolute) {
15017
+      // adjust to bounding rect factor so that rotated shapes would fit as well
15018
+      var boundingRectFactor = this.getBoundingRect(absolute).height / this.getScaledHeight();
15019
+      return this.scale(value / this.height / boundingRectFactor);
15020
+    },
15021
+
15022
+    /**
15023
+     * Calculate and returns the .coords of an object.
15024
+     * @return {Object} Object with tl, tr, br, bl ....
15025
+     * @chainable
15026
+     */
15027
+    calcCoords: function(absolute) {
15028
+      var rotateMatrix = this._calcRotateMatrix(),
15029
+          translateMatrix = this._calcTranslateMatrix(),
15030
+          startMatrix = multiplyMatrices(translateMatrix, rotateMatrix),
15031
+          vpt = this.getViewportTransform(),
15032
+          finalMatrix = absolute ? startMatrix : multiplyMatrices(vpt, startMatrix),
15033
+          dim = this._getTransformedDimensions(),
15034
+          w = dim.x / 2, h = dim.y / 2,
15035
+          tl = transformPoint({ x: -w, y: -h }, finalMatrix),
15036
+          tr = transformPoint({ x: w, y: -h }, finalMatrix),
15037
+          bl = transformPoint({ x: -w, y: h }, finalMatrix),
15038
+          br = transformPoint({ x: w, y: h }, finalMatrix);
15039
+      if (!absolute) {
15040
+        var padding = this.padding, angle = degreesToRadians(this.angle),
15041
+            cos = fabric.util.cos(angle), sin = fabric.util.sin(angle),
15042
+            cosP = cos * padding, sinP = sin * padding, cosPSinP = cosP + sinP,
15043
+            cosPMinusSinP = cosP - sinP;
15044
+        if (padding) {
15045
+          tl.x -= cosPMinusSinP;
15046
+          tl.y -= cosPSinP;
15047
+          tr.x += cosPSinP;
15048
+          tr.y -= cosPMinusSinP;
15049
+          bl.x -= cosPSinP;
15050
+          bl.y += cosPMinusSinP;
15051
+          br.x += cosPMinusSinP;
15052
+          br.y += cosPSinP;
15053
+        }
15054
+        var ml  = new fabric.Point((tl.x + bl.x) / 2, (tl.y + bl.y) / 2),
15055
+            mt  = new fabric.Point((tr.x + tl.x) / 2, (tr.y + tl.y) / 2),
15056
+            mr  = new fabric.Point((br.x + tr.x) / 2, (br.y + tr.y) / 2),
15057
+            mb  = new fabric.Point((br.x + bl.x) / 2, (br.y + bl.y) / 2),
15058
+            mtr = new fabric.Point(mt.x + sin * this.rotatingPointOffset, mt.y - cos * this.rotatingPointOffset);
15059
+      }
15060
+
15061
+      // if (!absolute) {
15062
+      //   var canvas = this.canvas;
15063
+      //   setTimeout(function() {
15064
+      //     canvas.contextTop.clearRect(0, 0, 700, 700);
15065
+      //     canvas.contextTop.fillStyle = 'green';
15066
+      //     canvas.contextTop.fillRect(mb.x, mb.y, 3, 3);
15067
+      //     canvas.contextTop.fillRect(bl.x, bl.y, 3, 3);
15068
+      //     canvas.contextTop.fillRect(br.x, br.y, 3, 3);
15069
+      //     canvas.contextTop.fillRect(tl.x, tl.y, 3, 3);
15070
+      //     canvas.contextTop.fillRect(tr.x, tr.y, 3, 3);
15071
+      //     canvas.contextTop.fillRect(ml.x, ml.y, 3, 3);
15072
+      //     canvas.contextTop.fillRect(mr.x, mr.y, 3, 3);
15073
+      //     canvas.contextTop.fillRect(mt.x, mt.y, 3, 3);
15074
+      //     canvas.contextTop.fillRect(mtr.x, mtr.y, 3, 3);
15075
+      //   }, 50);
15076
+      // }
15077
+
15078
+      var coords = {
15079
+        // corners
15080
+        tl: tl, tr: tr, br: br, bl: bl,
15081
+      };
15082
+      if (!absolute) {
15083
+        // middle
15084
+        coords.ml = ml;
15085
+        coords.mt = mt;
15086
+        coords.mr = mr;
15087
+        coords.mb = mb;
15088
+        // rotating point
15089
+        coords.mtr = mtr;
15090
+      }
15091
+      return coords;
15092
+    },
15093
+
15094
+    /**
15095
+     * Sets corner position coordinates based on current angle, width and height.
15096
+     * See {@link https://github.com/kangax/fabric.js/wiki/When-to-call-setCoords|When-to-call-setCoords}
15097
+     * @param {Boolean} [ignoreZoom] set oCoords with or without the viewport transform.
15098
+     * @param {Boolean} [skipAbsolute] skip calculation of aCoords, usefull in setViewportTransform
15099
+     * @return {fabric.Object} thisArg
15100
+     * @chainable
15101
+     */
15102
+    setCoords: function(ignoreZoom, skipAbsolute) {
15103
+      this.oCoords = this.calcCoords(ignoreZoom);
15104
+      if (!skipAbsolute) {
15105
+        this.aCoords = this.calcCoords(true);
15106
+      }
15107
+
15108
+      // set coordinates of the draggable boxes in the corners used to scale/rotate the image
15109
+      ignoreZoom || (this._setCornerCoords && this._setCornerCoords());
15110
+
15111
+      return this;
15112
+    },
15113
+
15114
+    /**
15115
+     * calculate rotation matrix of an object
15116
+     * @return {Array} rotation matrix for the object
15117
+     */
15118
+    _calcRotateMatrix: function() {
15119
+      if (this.angle) {
15120
+        var theta = degreesToRadians(this.angle), cos = fabric.util.cos(theta), sin = fabric.util.sin(theta);
15121
+        return [cos, sin, -sin, cos, 0, 0];
15122
+      }
15123
+      return fabric.iMatrix.concat();
15124
+    },
15125
+
15126
+    /**
15127
+     * calculate the translation matrix for an object transform
15128
+     * @return {Array} rotation matrix for the object
15129
+     */
15130
+    _calcTranslateMatrix: function() {
15131
+      var center = this.getCenterPoint();
15132
+      return [1, 0, 0, 1, center.x, center.y];
15133
+    },
15134
+
15135
+    transformMatrixKey: function(skipGroup) {
15136
+      var sep = '_', prefix = '';
15137
+      if (!skipGroup && this.group) {
15138
+        prefix = this.group.transformMatrixKey(skipGroup) + sep;
15139
+      };
15140
+      return prefix + this.top + sep + this.left + sep + this.scaleX + sep + this.scaleY +
15141
+        sep + this.skewX + sep + this.skewY + sep + this.angle + sep + this.originX + sep + this.originY +
15142
+        sep + this.width + sep + this.height + sep + this.strokeWidth + this.flipX + this.flipY;
15143
+    },
15144
+
15145
+    /**
15146
+     * calculate trasform Matrix that represent current transformation from
15147
+     * object properties.
15148
+     * @param {Boolean} [skipGroup] return transformMatrix for object and not go upward with parents
15149
+     * @return {Array} matrix Transform Matrix for the object
15150
+     */
15151
+    calcTransformMatrix: function(skipGroup) {
15152
+      if (skipGroup) {
15153
+        return this.calcOwnMatrix();
15154
+      }
15155
+      var key = this.transformMatrixKey(), cache = this.matrixCache || (this.matrixCache = {});
15156
+      if (cache.key === key) {
15157
+        return cache.value;
15158
+      }
15159
+      var matrix = this.calcOwnMatrix();
15160
+      if (this.group) {
15161
+        matrix = multiplyMatrices(this.group.calcTransformMatrix(), matrix);
15162
+      }
15163
+      cache.key = key;
15164
+      cache.value = matrix;
15165
+      return matrix;
15166
+    },
15167
+
15168
+    calcOwnMatrix: function() {
15169
+      var key = this.transformMatrixKey(true), cache = this.ownMatrixCache || (this.ownMatrixCache = {});
15170
+      if (cache.key === key) {
15171
+        return cache.value;
15172
+      }
15173
+      var matrix = this._calcTranslateMatrix(),
15174
+          rotateMatrix,
15175
+          dimensionMatrix = this._calcDimensionsTransformMatrix(this.skewX, this.skewY, true);
15176
+      if (this.angle) {
15177
+        rotateMatrix = this._calcRotateMatrix();
15178
+        matrix = multiplyMatrices(matrix, rotateMatrix);
15179
+      }
15180
+      matrix = multiplyMatrices(matrix, dimensionMatrix);
15181
+      cache.key = key;
15182
+      cache.value = matrix;
15183
+      return matrix;
15184
+    },
15185
+
15186
+    _calcDimensionsTransformMatrix: function(skewX, skewY, flipping) {
15187
+      var skewMatrix,
15188
+          scaleX = this.scaleX * (flipping && this.flipX ? -1 : 1),
15189
+          scaleY = this.scaleY * (flipping && this.flipY ? -1 : 1),
15190
+          scaleMatrix = [scaleX, 0, 0, scaleY, 0, 0];
15191
+      if (skewX) {
15192
+        skewMatrix = [1, 0, Math.tan(degreesToRadians(skewX)), 1];
15193
+        scaleMatrix = multiplyMatrices(scaleMatrix, skewMatrix, true);
15194
+      }
15195
+      if (skewY) {
15196
+        skewMatrix = [1, Math.tan(degreesToRadians(skewY)), 0, 1];
15197
+        scaleMatrix = multiplyMatrices(scaleMatrix, skewMatrix, true);
15198
+      }
15199
+      return scaleMatrix;
15200
+    },
15201
+
15202
+
15203
+    /*
15204
+     * Calculate object dimensions from its properties
15205
+     * @private
15206
+     * @return {Object} .x width dimension
15207
+     * @return {Object} .y height dimension
15208
+     */
15209
+    _getNonTransformedDimensions: function() {
15210
+      var strokeWidth = this.strokeWidth,
15211
+          w = this.width + strokeWidth,
15212
+          h = this.height + strokeWidth;
15213
+      return { x: w, y: h };
15214
+    },
15215
+
15216
+    /*
15217
+     * Calculate object bounding boxdimensions from its properties scale, skew.
15218
+     * @private
15219
+     * @return {Object} .x width dimension
15220
+     * @return {Object} .y height dimension
15221
+     */
15222
+    _getTransformedDimensions: function(skewX, skewY) {
15223
+      if (typeof skewX === 'undefined') {
15224
+        skewX = this.skewX;
15225
+      }
15226
+      if (typeof skewY === 'undefined') {
15227
+        skewY = this.skewY;
15228
+      }
15229
+      var dimensions = this._getNonTransformedDimensions();
15230
+      if (skewX === 0 && skewY === 0) {
15231
+        return { x: dimensions.x * this.scaleX, y: dimensions.y * this.scaleY };
15232
+      }
15233
+      var dimX = dimensions.x / 2, dimY = dimensions.y / 2,
15234
+          points = [
15235
+            {
15236
+              x: -dimX,
15237
+              y: -dimY
15238
+            },
15239
+            {
15240
+              x: dimX,
15241
+              y: -dimY
15242
+            },
15243
+            {
15244
+              x: -dimX,
15245
+              y: dimY
15246
+            },
15247
+            {
15248
+              x: dimX,
15249
+              y: dimY
15250
+            }],
15251
+          i, transformMatrix = this._calcDimensionsTransformMatrix(skewX, skewY, false),
15252
+          bbox;
15253
+      for (i = 0; i < points.length; i++) {
15254
+        points[i] = fabric.util.transformPoint(points[i], transformMatrix);
15255
+      }
15256
+      bbox = fabric.util.makeBoundingBoxFromPoints(points);
15257
+      return { x: bbox.width, y: bbox.height };
15258
+    },
15259
+
15260
+    /*
15261
+     * Calculate object dimensions for controls. include padding and canvas zoom
15262
+     * private
15263
+     */
15264
+    _calculateCurrentDimensions: function()  {
15265
+      var vpt = this.getViewportTransform(),
15266
+          dim = this._getTransformedDimensions(),
15267
+          p = fabric.util.transformPoint(dim, vpt, true);
15268
+
15269
+      return p.scalarAdd(2 * this.padding);
15270
+    },
15271
+  });
15272
+})();
15273
+
15274
+
15275
+fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
15276
+
15277
+  /**
15278
+   * Moves an object to the bottom of the stack of drawn objects
15279
+   * @return {fabric.Object} thisArg
15280
+   * @chainable
15281
+   */
15282
+  sendToBack: function() {
15283
+    if (this.group) {
15284
+      fabric.StaticCanvas.prototype.sendToBack.call(this.group, this);
15285
+    }
15286
+    else {
15287
+      this.canvas.sendToBack(this);
15288
+    }
15289
+    return this;
15290
+  },
15291
+
15292
+  /**
15293
+   * Moves an object to the top of the stack of drawn objects
15294
+   * @return {fabric.Object} thisArg
15295
+   * @chainable
15296
+   */
15297
+  bringToFront: function() {
15298
+    if (this.group) {
15299
+      fabric.StaticCanvas.prototype.bringToFront.call(this.group, this);
15300
+    }
15301
+    else {
15302
+      this.canvas.bringToFront(this);
15303
+    }
15304
+    return this;
15305
+  },
15306
+
15307
+  /**
15308
+   * Moves an object down in stack of drawn objects
15309
+   * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object
15310
+   * @return {fabric.Object} thisArg
15311
+   * @chainable
15312
+   */
15313
+  sendBackwards: function(intersecting) {
15314
+    if (this.group) {
15315
+      fabric.StaticCanvas.prototype.sendBackwards.call(this.group, this, intersecting);
15316
+    }
15317
+    else {
15318
+      this.canvas.sendBackwards(this, intersecting);
15319
+    }
15320
+    return this;
15321
+  },
15322
+
15323
+  /**
15324
+   * Moves an object up in stack of drawn objects
15325
+   * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object
15326
+   * @return {fabric.Object} thisArg
15327
+   * @chainable
15328
+   */
15329
+  bringForward: function(intersecting) {
15330
+    if (this.group) {
15331
+      fabric.StaticCanvas.prototype.bringForward.call(this.group, this, intersecting);
15332
+    }
15333
+    else {
15334
+      this.canvas.bringForward(this, intersecting);
15335
+    }
15336
+    return this;
15337
+  },
15338
+
15339
+  /**
15340
+   * Moves an object to specified level in stack of drawn objects
15341
+   * @param {Number} index New position of object
15342
+   * @return {fabric.Object} thisArg
15343
+   * @chainable
15344
+   */
15345
+  moveTo: function(index) {
15346
+    if (this.group && this.group.type !== 'activeSelection') {
15347
+      fabric.StaticCanvas.prototype.moveTo.call(this.group, this, index);
15348
+    }
15349
+    else {
15350
+      this.canvas.moveTo(this, index);
15351
+    }
15352
+    return this;
15353
+  }
15354
+});
15355
+
15356
+
15357
+/* _TO_SVG_START_ */
15358
+(function() {
15359
+  function getSvgColorString(prop, value) {
15360
+    if (!value) {
15361
+      return prop + ': none; ';
15362
+    }
15363
+    else if (value.toLive) {
15364
+      return prop + ': url(#SVGID_' + value.id + '); ';
15365
+    }
15366
+    else {
15367
+      var color = new fabric.Color(value),
15368
+          str = prop + ': ' + color.toRgb() + '; ',
15369
+          opacity = color.getAlpha();
15370
+      if (opacity !== 1) {
15371
+        //change the color in rgb + opacity
15372
+        str += prop + '-opacity: ' + opacity.toString() + '; ';
15373
+      }
15374
+      return str;
15375
+    }
15376
+  }
15377
+
15378
+  var toFixed = fabric.util.toFixed;
15379
+
15380
+  fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
15381
+    /**
15382
+     * Returns styles-string for svg-export
15383
+     * @param {Boolean} skipShadow a boolean to skip shadow filter output
15384
+     * @return {String}
15385
+     */
15386
+    getSvgStyles: function(skipShadow) {
15387
+
15388
+      var fillRule = this.fillRule ? this.fillRule : 'nonzero',
15389
+          strokeWidth = this.strokeWidth ? this.strokeWidth : '0',
15390
+          strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : 'none',
15391
+          strokeDashOffset = this.strokeDashOffset ? this.strokeDashOffset : '0',
15392
+          strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt',
15393
+          strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter',
15394
+          strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4',
15395
+          opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1',
15396
+          visibility = this.visible ? '' : ' visibility: hidden;',
15397
+          filter = skipShadow ? '' : this.getSvgFilter(),
15398
+          fill = getSvgColorString('fill', this.fill),
15399
+          stroke = getSvgColorString('stroke', this.stroke);
15400
+
15401
+      return [
15402
+        stroke,
15403
+        'stroke-width: ', strokeWidth, '; ',
15404
+        'stroke-dasharray: ', strokeDashArray, '; ',
15405
+        'stroke-linecap: ', strokeLineCap, '; ',
15406
+        'stroke-dashoffset: ', strokeDashOffset, '; ',
15407
+        'stroke-linejoin: ', strokeLineJoin, '; ',
15408
+        'stroke-miterlimit: ', strokeMiterLimit, '; ',
15409
+        fill,
15410
+        'fill-rule: ', fillRule, '; ',
15411
+        'opacity: ', opacity, ';',
15412
+        filter,
15413
+        visibility
15414
+      ].join('');
15415
+    },
15416
+
15417
+    /**
15418
+     * Returns styles-string for svg-export
15419
+     * @param {Object} style the object from which to retrieve style properties
15420
+     * @param {Boolean} useWhiteSpace a boolean to include an additional attribute in the style.
15421
+     * @return {String}
15422
+     */
15423
+    getSvgSpanStyles: function(style, useWhiteSpace) {
15424
+      var term = '; ';
15425
+      var fontFamily = style.fontFamily ?
15426
+        'font-family: ' + (((style.fontFamily.indexOf('\'') === -1 && style.fontFamily.indexOf('"') === -1) ?
15427
+          '\'' + style.fontFamily  + '\'' : style.fontFamily)) + term : '';
15428
+      var strokeWidth = style.strokeWidth ? 'stroke-width: ' + style.strokeWidth + term : '',
15429
+          fontFamily = fontFamily,
15430
+          fontSize = style.fontSize ? 'font-size: ' + style.fontSize + 'px' + term : '',
15431
+          fontStyle = style.fontStyle ? 'font-style: ' + style.fontStyle + term : '',
15432
+          fontWeight = style.fontWeight ? 'font-weight: ' + style.fontWeight + term : '',
15433
+          fill = style.fill ? getSvgColorString('fill', style.fill) : '',
15434
+          stroke = style.stroke ? getSvgColorString('stroke', style.stroke) : '',
15435
+          textDecoration = this.getSvgTextDecoration(style),
15436
+          deltaY = style.deltaY ? 'baseline-shift: ' + (-style.deltaY) + '; ' : '';
15437
+      if (textDecoration) {
15438
+        textDecoration = 'text-decoration: ' + textDecoration + term;
15439
+      }
15440
+
15441
+      return [
15442
+        stroke,
15443
+        strokeWidth,
15444
+        fontFamily,
15445
+        fontSize,
15446
+        fontStyle,
15447
+        fontWeight,
15448
+        textDecoration,
15449
+        fill,
15450
+        deltaY,
15451
+        useWhiteSpace ? 'white-space: pre; ' : ''
15452
+      ].join('');
15453
+    },
15454
+
15455
+    /**
15456
+     * Returns text-decoration property for svg-export
15457
+     * @param {Object} style the object from which to retrieve style properties
15458
+     * @return {String}
15459
+     */
15460
+    getSvgTextDecoration: function(style) {
15461
+      if ('overline' in style || 'underline' in style || 'linethrough' in style) {
15462
+        return (style.overline ? 'overline ' : '') +
15463
+          (style.underline ? 'underline ' : '') + (style.linethrough ? 'line-through ' : '');
15464
+      }
15465
+      return '';
15466
+    },
15467
+
15468
+    /**
15469
+     * Returns filter for svg shadow
15470
+     * @return {String}
15471
+     */
15472
+    getSvgFilter: function() {
15473
+      return this.shadow ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
15474
+    },
15475
+
15476
+    /**
15477
+     * Returns id attribute for svg output
15478
+     * @return {String}
15479
+     */
15480
+    getSvgCommons: function() {
15481
+      return [
15482
+        this.id ? 'id="' + this.id + '" ' : '',
15483
+        this.clipPath ? 'clip-path="url(#' + this.clipPath.clipPathId + ')" ' : '',
15484
+      ].join('');
15485
+    },
15486
+
15487
+    /**
15488
+     * Returns transform-string for svg-export
15489
+     * @param {Boolean} use the full transform or the single object one.
15490
+     * @return {String}
15491
+     */
15492
+    getSvgTransform: function(full, additionalTransform) {
15493
+      var transform = full ? this.calcTransformMatrix() : this.calcOwnMatrix(),
15494
+          svgTransform = transform.map(function(value) {
15495
+            return toFixed(value, fabric.Object.NUM_FRACTION_DIGITS);
15496
+          }).join(' ');
15497
+      return 'transform="matrix(' + svgTransform + ')' +
15498
+        (additionalTransform || '') + this.getSvgTransformMatrix() + '" ';
15499
+    },
15500
+
15501
+    /**
15502
+     * Returns transform-string for svg-export from the transform matrix of single elements
15503
+     * @return {String}
15504
+     */
15505
+    getSvgTransformMatrix: function() {
15506
+      return this.transformMatrix ? ' matrix(' + this.transformMatrix.join(' ') + ')' : '';
15507
+    },
15508
+
15509
+    _setSVGBg: function(textBgRects) {
15510
+      if (this.backgroundColor) {
15511
+        var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
15512
+        textBgRects.push(
15513
+          '\t\t<rect ',
15514
+          this._getFillAttributes(this.backgroundColor),
15515
+          ' x="',
15516
+          toFixed(-this.width / 2, NUM_FRACTION_DIGITS),
15517
+          '" y="',
15518
+          toFixed(-this.height / 2, NUM_FRACTION_DIGITS),
15519
+          '" width="',
15520
+          toFixed(this.width, NUM_FRACTION_DIGITS),
15521
+          '" height="',
15522
+          toFixed(this.height, NUM_FRACTION_DIGITS),
15523
+          '"></rect>\n');
15524
+      }
15525
+    },
15526
+
15527
+    /**
15528
+     * Returns svg representation of an instance
15529
+     * @param {Function} [reviver] Method for further parsing of svg representation.
15530
+     * @return {String} svg representation of an instance
15531
+     */
15532
+    toSVG: function(reviver) {
15533
+      return this._createBaseSVGMarkup(this._toSVG(), { reviver: reviver });
15534
+    },
15535
+
15536
+    /**
15537
+     * Returns svg clipPath representation of an instance
15538
+     * @param {Function} [reviver] Method for further parsing of svg representation.
15539
+     * @return {String} svg representation of an instance
15540
+     */
15541
+    toClipPathSVG: function(reviver) {
15542
+      return '\t' + this._createBaseClipPathSVGMarkup(this._toSVG(), { reviver: reviver });
15543
+    },
15544
+
15545
+    /**
15546
+     * @private
15547
+     */
15548
+    _createBaseClipPathSVGMarkup: function(objectMarkup, options) {
15549
+      options = options || {};
15550
+      var reviver = options.reviver,
15551
+          additionalTransform = options.additionalTransform || '',
15552
+          commonPieces = [
15553
+            this.getSvgTransform(true, additionalTransform),
15554
+            this.getSvgCommons(),
15555
+          ].join(''),
15556
+          // insert commons in the markup, style and svgCommons
15557
+          index = objectMarkup.indexOf('COMMON_PARTS');
15558
+      objectMarkup[index] = commonPieces;
15559
+      return reviver ? reviver(objectMarkup.join('')) : objectMarkup.join('');
15560
+    },
15561
+
15562
+    /**
15563
+     * @private
15564
+     */
15565
+    _createBaseSVGMarkup: function(objectMarkup, options) {
15566
+      options = options || {};
15567
+      var noStyle = options.noStyle, withShadow = options.withShadow,
15568
+          reviver = options.reviver,
15569
+          styleInfo = noStyle ? '' : 'style="' + this.getSvgStyles() + '" ',
15570
+          shadowInfo = withShadow ? 'style="' + this.getSvgFilter() + '" ' : '',
15571
+          clipPath = this.clipPath,
15572
+          absoluteClipPath = this.clipPath && this.clipPath.absolutePositioned,
15573
+          commonPieces, markup = [], clipPathMarkup,
15574
+          // insert commons in the markup, style and svgCommons
15575
+          index = objectMarkup.indexOf('COMMON_PARTS'),
15576
+          additionalTransform = options.additionalTransform;
15577
+      if (clipPath) {
15578
+        clipPath.clipPathId = 'CLIPPATH_' + fabric.Object.__uid++;
15579
+        clipPathMarkup = '<clipPath id="' + clipPath.clipPathId + '" >\n' +
15580
+          this.clipPath.toClipPathSVG(reviver) +
15581
+          '</clipPath>\n';
15582
+      }
15583
+      if (absoluteClipPath) {
15584
+        markup.push(
15585
+          '<g ', shadowInfo, this.getSvgCommons(), ' >\n'
15586
+        );
15587
+      }
15588
+      markup.push(
15589
+        '<g ',
15590
+        this.getSvgTransform(false),
15591
+        !absoluteClipPath ? shadowInfo + this.getSvgCommons() : '',
15592
+        ' >\n'
15593
+      );
15594
+      commonPieces = [
15595
+        styleInfo,
15596
+        noStyle ? '' : this.addPaintOrder(), ' ',
15597
+        additionalTransform ? 'transform="' + additionalTransform + '" ' : '',
15598
+      ].join('');
15599
+      objectMarkup[index] = commonPieces;
15600
+      if (this.fill && this.fill.toLive) {
15601
+        markup.push(this.fill.toSVG(this, false));
15602
+      }
15603
+      if (this.stroke && this.stroke.toLive) {
15604
+        markup.push(this.stroke.toSVG(this, false));
15605
+      }
15606
+      if (this.shadow) {
15607
+        markup.push(this.shadow.toSVG(this));
15608
+      }
15609
+      if (clipPath) {
15610
+        markup.push(clipPathMarkup);
15611
+      }
15612
+      markup.push(objectMarkup.join(''));
15613
+      markup.push('</g>\n');
15614
+      absoluteClipPath && markup.push('</g>\n');
15615
+      return reviver ? reviver(markup.join('')) : markup.join('');
15616
+    },
15617
+
15618
+    addPaintOrder: function() {
15619
+      return this.paintFirst !== 'fill' ? ' paint-order="' + this.paintFirst + '" ' : '';
15620
+    }
15621
+  });
15622
+})();
15623
+/* _TO_SVG_END_ */
15624
+
15625
+
15626
+(function() {
15627
+
15628
+  var extend = fabric.util.object.extend,
15629
+      originalSet = 'stateProperties';
15630
+
15631
+  /*
15632
+    Depends on `stateProperties`
15633
+  */
15634
+  function saveProps(origin, destination, props) {
15635
+    var tmpObj = { }, deep = true;
15636
+    props.forEach(function(prop) {
15637
+      tmpObj[prop] = origin[prop];
15638
+    });
15639
+    extend(origin[destination], tmpObj, deep);
15640
+  }
15641
+
15642
+  function _isEqual(origValue, currentValue, firstPass) {
15643
+    if (origValue === currentValue) {
15644
+      // if the objects are identical, return
15645
+      return true;
15646
+    }
15647
+    else if (Array.isArray(origValue)) {
15648
+      if (!Array.isArray(currentValue) || origValue.length !== currentValue.length) {
15649
+        return false;
15650
+      }
15651
+      for (var i = 0, len = origValue.length; i < len; i++) {
15652
+        if (!_isEqual(origValue[i], currentValue[i])) {
15653
+          return false;
15654
+        }
15655
+      }
15656
+      return true;
15657
+    }
15658
+    else if (origValue && typeof origValue === 'object') {
15659
+      var keys = Object.keys(origValue), key;
15660
+      if (!currentValue ||
15661
+          typeof currentValue !== 'object' ||
15662
+          (!firstPass && keys.length !== Object.keys(currentValue).length)
15663
+      ) {
15664
+        return false;
15665
+      }
15666
+      for (var i = 0, len = keys.length; i < len; i++) {
15667
+        key = keys[i];
15668
+        // since clipPath is in the statefull cache list and the clipPath objects
15669
+        // would be iterated as an object, this would lead to possible infinite recursion
15670
+        if (key === 'canvas') {
15671
+          continue;
15672
+        }
15673
+        if (!_isEqual(origValue[key], currentValue[key])) {
15674
+          return false;
15675
+        }
15676
+      }
15677
+      return true;
15678
+    }
15679
+  }
15680
+
15681
+
15682
+  fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
15683
+
15684
+    /**
15685
+     * Returns true if object state (one of its state properties) was changed
15686
+     * @param {String} [propertySet] optional name for the set of property we want to save
15687
+     * @return {Boolean} true if instance' state has changed since `{@link fabric.Object#saveState}` was called
15688
+     */
15689
+    hasStateChanged: function(propertySet) {
15690
+      propertySet = propertySet || originalSet;
15691
+      var dashedPropertySet = '_' + propertySet;
15692
+      if (Object.keys(this[dashedPropertySet]).length < this[propertySet].length) {
15693
+        return true;
15694
+      }
15695
+      return !_isEqual(this[dashedPropertySet], this, true);
15696
+    },
15697
+
15698
+    /**
15699
+     * Saves state of an object
15700
+     * @param {Object} [options] Object with additional `stateProperties` array to include when saving state
15701
+     * @return {fabric.Object} thisArg
15702
+     */
15703
+    saveState: function(options) {
15704
+      var propertySet = options && options.propertySet || originalSet,
15705
+          destination = '_' + propertySet;
15706
+      if (!this[destination]) {
15707
+        return this.setupState(options);
15708
+      }
15709
+      saveProps(this, destination, this[propertySet]);
15710
+      if (options && options.stateProperties) {
15711
+        saveProps(this, destination, options.stateProperties);
15712
+      }
15713
+      return this;
15714
+    },
15715
+
15716
+    /**
15717
+     * Setups state of an object
15718
+     * @param {Object} [options] Object with additional `stateProperties` array to include when saving state
15719
+     * @return {fabric.Object} thisArg
15720
+     */
15721
+    setupState: function(options) {
15722
+      options = options || { };
15723
+      var propertySet = options.propertySet || originalSet;
15724
+      options.propertySet = propertySet;
15725
+      this['_' + propertySet] = { };
15726
+      this.saveState(options);
15727
+      return this;
15728
+    }
15729
+  });
15730
+})();
15731
+
15732
+
15733
+(function() {
15734
+
15735
+  var degreesToRadians = fabric.util.degreesToRadians;
15736
+
15737
+  fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
15738
+
15739
+    /**
15740
+     * The object interactivity controls.
15741
+     * @private
15742
+     */
15743
+    _controlsVisibility: null,
15744
+
15745
+    /**
15746
+     * Determines which corner has been clicked
15747
+     * @private
15748
+     * @param {Object} pointer The pointer indicating the mouse position
15749
+     * @return {String|Boolean} corner code (tl, tr, bl, br, etc.), or false if nothing is found
15750
+     */
15751
+    _findTargetCorner: function(pointer) {
15752
+      // objects in group, anykind, are not self modificable,
15753
+      // must not return an hovered corner.
15754
+      if (!this.hasControls || this.group || (!this.canvas || this.canvas._activeObject !== this)) {
15755
+        return false;
15756
+      }
15757
+
15758
+      var ex = pointer.x,
15759
+          ey = pointer.y,
15760
+          xPoints,
15761
+          lines;
15762
+      this.__corner = 0;
15763
+      for (var i in this.oCoords) {
15764
+
15765
+        if (!this.isControlVisible(i)) {
15766
+          continue;
15767
+        }
15768
+
15769
+        if (i === 'mtr' && !this.hasRotatingPoint) {
15770
+          continue;
15771
+        }
15772
+
15773
+        if (this.get('lockUniScaling') &&
15774
+           (i === 'mt' || i === 'mr' || i === 'mb' || i === 'ml')) {
15775
+          continue;
15776
+        }
15777
+
15778
+        lines = this._getImageLines(this.oCoords[i].corner);
15779
+
15780
+        // debugging
15781
+
15782
+        // canvas.contextTop.fillRect(lines.bottomline.d.x, lines.bottomline.d.y, 2, 2);
15783
+        // canvas.contextTop.fillRect(lines.bottomline.o.x, lines.bottomline.o.y, 2, 2);
15784
+
15785
+        // canvas.contextTop.fillRect(lines.leftline.d.x, lines.leftline.d.y, 2, 2);
15786
+        // canvas.contextTop.fillRect(lines.leftline.o.x, lines.leftline.o.y, 2, 2);
15787
+
15788
+        // canvas.contextTop.fillRect(lines.topline.d.x, lines.topline.d.y, 2, 2);
15789
+        // canvas.contextTop.fillRect(lines.topline.o.x, lines.topline.o.y, 2, 2);
15790
+
15791
+        // canvas.contextTop.fillRect(lines.rightline.d.x, lines.rightline.d.y, 2, 2);
15792
+        // canvas.contextTop.fillRect(lines.rightline.o.x, lines.rightline.o.y, 2, 2);
15793
+
15794
+        xPoints = this._findCrossPoints({ x: ex, y: ey }, lines);
15795
+        if (xPoints !== 0 && xPoints % 2 === 1) {
15796
+          this.__corner = i;
15797
+          return i;
15798
+        }
15799
+      }
15800
+      return false;
15801
+    },
15802
+
15803
+    /**
15804
+     * Sets the coordinates of the draggable boxes in the corners of
15805
+     * the image used to scale/rotate it.
15806
+     * @private
15807
+     */
15808
+    _setCornerCoords: function() {
15809
+      var coords = this.oCoords,
15810
+          newTheta = degreesToRadians(45 - this.angle),
15811
+          /* Math.sqrt(2 * Math.pow(this.cornerSize, 2)) / 2, */
15812
+          /* 0.707106 stands for sqrt(2)/2 */
15813
+          cornerHypotenuse = this.cornerSize * 0.707106,
15814
+          cosHalfOffset = cornerHypotenuse * fabric.util.cos(newTheta),
15815
+          sinHalfOffset = cornerHypotenuse * fabric.util.sin(newTheta),
15816
+          x, y;
15817
+
15818
+      for (var point in coords) {
15819
+        x = coords[point].x;
15820
+        y = coords[point].y;
15821
+        coords[point].corner = {
15822
+          tl: {
15823
+            x: x - sinHalfOffset,
15824
+            y: y - cosHalfOffset
15825
+          },
15826
+          tr: {
15827
+            x: x + cosHalfOffset,
15828
+            y: y - sinHalfOffset
15829
+          },
15830
+          bl: {
15831
+            x: x - cosHalfOffset,
15832
+            y: y + sinHalfOffset
15833
+          },
15834
+          br: {
15835
+            x: x + sinHalfOffset,
15836
+            y: y + cosHalfOffset
15837
+          }
15838
+        };
15839
+      }
15840
+    },
15841
+
15842
+    /**
15843
+     * Draws a colored layer behind the object, inside its selection borders.
15844
+     * Requires public options: padding, selectionBackgroundColor
15845
+     * this function is called when the context is transformed
15846
+     * has checks to be skipped when the object is on a staticCanvas
15847
+     * @param {CanvasRenderingContext2D} ctx Context to draw on
15848
+     * @return {fabric.Object} thisArg
15849
+     * @chainable
15850
+     */
15851
+    drawSelectionBackground: function(ctx) {
15852
+      if (!this.selectionBackgroundColor ||
15853
+        (this.canvas && !this.canvas.interactive) ||
15854
+        (this.canvas && this.canvas._activeObject !== this)
15855
+      ) {
15856
+        return this;
15857
+      }
15858
+      ctx.save();
15859
+      var center = this.getCenterPoint(), wh = this._calculateCurrentDimensions(),
15860
+          vpt = this.canvas.viewportTransform;
15861
+      ctx.translate(center.x, center.y);
15862
+      ctx.scale(1 / vpt[0], 1 / vpt[3]);
15863
+      ctx.rotate(degreesToRadians(this.angle));
15864
+      ctx.fillStyle = this.selectionBackgroundColor;
15865
+      ctx.fillRect(-wh.x / 2, -wh.y / 2, wh.x, wh.y);
15866
+      ctx.restore();
15867
+      return this;
15868
+    },
15869
+
15870
+    /**
15871
+     * Draws borders of an object's bounding box.
15872
+     * Requires public properties: width, height
15873
+     * Requires public options: padding, borderColor
15874
+     * @param {CanvasRenderingContext2D} ctx Context to draw on
15875
+     * @param {Object} styleOverride object to override the object style
15876
+     * @return {fabric.Object} thisArg
15877
+     * @chainable
15878
+     */
15879
+    drawBorders: function(ctx, styleOverride) {
15880
+      styleOverride = styleOverride || {};
15881
+      var wh = this._calculateCurrentDimensions(),
15882
+          strokeWidth = 1 / this.borderScaleFactor,
15883
+          width = wh.x + strokeWidth,
15884
+          height = wh.y + strokeWidth,
15885
+          drawRotatingPoint = typeof styleOverride.hasRotatingPoint !== 'undefined' ?
15886
+            styleOverride.hasRotatingPoint : this.hasRotatingPoint,
15887
+          hasControls = typeof styleOverride.hasControls !== 'undefined' ?
15888
+            styleOverride.hasControls : this.hasControls,
15889
+          rotatingPointOffset = typeof styleOverride.rotatingPointOffset !== 'undefined' ?
15890
+            styleOverride.rotatingPointOffset : this.rotatingPointOffset;
15891
+
15892
+      ctx.save();
15893
+      ctx.strokeStyle = styleOverride.borderColor || this.borderColor;
15894
+      this._setLineDash(ctx, styleOverride.borderDashArray || this.borderDashArray, null);
15895
+
15896
+      ctx.strokeRect(
15897
+        -width / 2,
15898
+        -height / 2,
15899
+        width,
15900
+        height
15901
+      );
15902
+
15903
+      if (drawRotatingPoint && this.isControlVisible('mtr') && hasControls) {
15904
+
15905
+        var rotateHeight = -height / 2;
15906
+
15907
+        ctx.beginPath();
15908
+        ctx.moveTo(0, rotateHeight);
15909
+        ctx.lineTo(0, rotateHeight - rotatingPointOffset);
15910
+        ctx.stroke();
15911
+      }
15912
+
15913
+      ctx.restore();
15914
+      return this;
15915
+    },
15916
+
15917
+    /**
15918
+     * Draws borders of an object's bounding box when it is inside a group.
15919
+     * Requires public properties: width, height
15920
+     * Requires public options: padding, borderColor
15921
+     * @param {CanvasRenderingContext2D} ctx Context to draw on
15922
+     * @param {object} options object representing current object parameters
15923
+     * @param {Object} styleOverride object to override the object style
15924
+     * @return {fabric.Object} thisArg
15925
+     * @chainable
15926
+     */
15927
+    drawBordersInGroup: function(ctx, options, styleOverride) {
15928
+      styleOverride = styleOverride || {};
15929
+      var p = this._getNonTransformedDimensions(),
15930
+          matrix = fabric.util.customTransformMatrix(options.scaleX, options.scaleY, options.skewX),
15931
+          wh = fabric.util.transformPoint(p, matrix),
15932
+          strokeWidth = 1 / this.borderScaleFactor,
15933
+          width = wh.x + strokeWidth,
15934
+          height = wh.y + strokeWidth;
15935
+
15936
+      ctx.save();
15937
+      this._setLineDash(ctx, styleOverride.borderDashArray || this.borderDashArray, null);
15938
+      ctx.strokeStyle = styleOverride.borderColor || this.borderColor;
15939
+
15940
+      ctx.strokeRect(
15941
+        -width / 2,
15942
+        -height / 2,
15943
+        width,
15944
+        height
15945
+      );
15946
+
15947
+      ctx.restore();
15948
+      return this;
15949
+    },
15950
+
15951
+    /**
15952
+     * Draws corners of an object's bounding box.
15953
+     * Requires public properties: width, height
15954
+     * Requires public options: cornerSize, padding
15955
+     * @param {CanvasRenderingContext2D} ctx Context to draw on
15956
+     * @param {Object} styleOverride object to override the object style
15957
+     * @return {fabric.Object} thisArg
15958
+     * @chainable
15959
+     */
15960
+    drawControls: function(ctx, styleOverride) {
15961
+      styleOverride = styleOverride || {};
15962
+      var wh = this._calculateCurrentDimensions(),
15963
+          width = wh.x,
15964
+          height = wh.y,
15965
+          scaleOffset = styleOverride.cornerSize || this.cornerSize,
15966
+          left = -(width + scaleOffset) / 2,
15967
+          top = -(height + scaleOffset) / 2,
15968
+          transparentCorners = typeof styleOverride.transparentCorners !== 'undefined' ?
15969
+            styleOverride.transparentCorners : this.transparentCorners,
15970
+          hasRotatingPoint = typeof styleOverride.hasRotatingPoint !== 'undefined' ?
15971
+            styleOverride.hasRotatingPoint : this.hasRotatingPoint,
15972
+          methodName = transparentCorners ? 'stroke' : 'fill';
15973
+
15974
+      ctx.save();
15975
+      ctx.strokeStyle = ctx.fillStyle = styleOverride.cornerColor || this.cornerColor;
15976
+      if (!this.transparentCorners) {
15977
+        ctx.strokeStyle = styleOverride.cornerStrokeColor || this.cornerStrokeColor;
15978
+      }
15979
+      this._setLineDash(ctx, styleOverride.cornerDashArray || this.cornerDashArray, null);
15980
+
15981
+      // top-left
15982
+      this._drawControl('tl', ctx, methodName,
15983
+        left,
15984
+        top, styleOverride);
15985
+
15986
+      // top-right
15987
+      this._drawControl('tr', ctx, methodName,
15988
+        left + width,
15989
+        top, styleOverride);
15990
+
15991
+      // bottom-left
15992
+      this._drawControl('bl', ctx, methodName,
15993
+        left,
15994
+        top + height, styleOverride);
15995
+
15996
+      // bottom-right
15997
+      this._drawControl('br', ctx, methodName,
15998
+        left + width,
15999
+        top + height, styleOverride);
16000
+
16001
+      if (!this.get('lockUniScaling')) {
16002
+
16003
+        // middle-top
16004
+        this._drawControl('mt', ctx, methodName,
16005
+          left + width / 2,
16006
+          top, styleOverride);
16007
+
16008
+        // middle-bottom
16009
+        this._drawControl('mb', ctx, methodName,
16010
+          left + width / 2,
16011
+          top + height, styleOverride);
16012
+
16013
+        // middle-right
16014
+        this._drawControl('mr', ctx, methodName,
16015
+          left + width,
16016
+          top + height / 2, styleOverride);
16017
+
16018
+        // middle-left
16019
+        this._drawControl('ml', ctx, methodName,
16020
+          left,
16021
+          top + height / 2, styleOverride);
16022
+      }
16023
+
16024
+      // middle-top-rotate
16025
+      if (hasRotatingPoint) {
16026
+        this._drawControl('mtr', ctx, methodName,
16027
+          left + width / 2,
16028
+          top - this.rotatingPointOffset, styleOverride);
16029
+      }
16030
+
16031
+      ctx.restore();
16032
+
16033
+      return this;
16034
+    },
16035
+
16036
+    /**
16037
+     * @private
16038
+     */
16039
+    _drawControl: function(control, ctx, methodName, left, top, styleOverride) {
16040
+      styleOverride = styleOverride || {};
16041
+      if (!this.isControlVisible(control)) {
16042
+        return;
16043
+      }
16044
+      var size = this.cornerSize, stroke = !this.transparentCorners && this.cornerStrokeColor;
16045
+      switch (styleOverride.cornerStyle || this.cornerStyle) {
16046
+        case 'circle':
16047
+          ctx.beginPath();
16048
+          ctx.arc(left + size / 2, top + size / 2, size / 2, 0, 2 * Math.PI, false);
16049
+          ctx[methodName]();
16050
+          if (stroke) {
16051
+            ctx.stroke();
16052
+          }
16053
+          break;
16054
+        default:
16055
+          this.transparentCorners || ctx.clearRect(left, top, size, size);
16056
+          ctx[methodName + 'Rect'](left, top, size, size);
16057
+          if (stroke) {
16058
+            ctx.strokeRect(left, top, size, size);
16059
+          }
16060
+      }
16061
+    },
16062
+
16063
+    /**
16064
+     * Returns true if the specified control is visible, false otherwise.
16065
+     * @param {String} controlName The name of the control. Possible values are 'tl', 'tr', 'br', 'bl', 'ml', 'mt', 'mr', 'mb', 'mtr'.
16066
+     * @returns {Boolean} true if the specified control is visible, false otherwise
16067
+     */
16068
+    isControlVisible: function(controlName) {
16069
+      return this._getControlsVisibility()[controlName];
16070
+    },
16071
+
16072
+    /**
16073
+     * Sets the visibility of the specified control.
16074
+     * @param {String} controlName The name of the control. Possible values are 'tl', 'tr', 'br', 'bl', 'ml', 'mt', 'mr', 'mb', 'mtr'.
16075
+     * @param {Boolean} visible true to set the specified control visible, false otherwise
16076
+     * @return {fabric.Object} thisArg
16077
+     * @chainable
16078
+     */
16079
+    setControlVisible: function(controlName, visible) {
16080
+      this._getControlsVisibility()[controlName] = visible;
16081
+      return this;
16082
+    },
16083
+
16084
+    /**
16085
+     * Sets the visibility state of object controls.
16086
+     * @param {Object} [options] Options object
16087
+     * @param {Boolean} [options.bl] true to enable the bottom-left control, false to disable it
16088
+     * @param {Boolean} [options.br] true to enable the bottom-right control, false to disable it
16089
+     * @param {Boolean} [options.mb] true to enable the middle-bottom control, false to disable it
16090
+     * @param {Boolean} [options.ml] true to enable the middle-left control, false to disable it
16091
+     * @param {Boolean} [options.mr] true to enable the middle-right control, false to disable it
16092
+     * @param {Boolean} [options.mt] true to enable the middle-top control, false to disable it
16093
+     * @param {Boolean} [options.tl] true to enable the top-left control, false to disable it
16094
+     * @param {Boolean} [options.tr] true to enable the top-right control, false to disable it
16095
+     * @param {Boolean} [options.mtr] true to enable the middle-top-rotate control, false to disable it
16096
+     * @return {fabric.Object} thisArg
16097
+     * @chainable
16098
+     */
16099
+    setControlsVisibility: function(options) {
16100
+      options || (options = { });
16101
+
16102
+      for (var p in options) {
16103
+        this.setControlVisible(p, options[p]);
16104
+      }
16105
+      return this;
16106
+    },
16107
+
16108
+    /**
16109
+     * Returns the instance of the control visibility set for this object.
16110
+     * @private
16111
+     * @returns {Object}
16112
+     */
16113
+    _getControlsVisibility: function() {
16114
+      if (!this._controlsVisibility) {
16115
+        this._controlsVisibility = {
16116
+          tl: true,
16117
+          tr: true,
16118
+          br: true,
16119
+          bl: true,
16120
+          ml: true,
16121
+          mt: true,
16122
+          mr: true,
16123
+          mb: true,
16124
+          mtr: true
16125
+        };
16126
+      }
16127
+      return this._controlsVisibility;
16128
+    },
16129
+
16130
+    /**
16131
+     * This callback function is called every time _discardActiveObject or _setActiveObject
16132
+     * try to to deselect this object. If the function returns true, the process is cancelled
16133
+     * @param {Object} [options] options sent from the upper functions
16134
+     * @param {Event} [options.e] event if the process is generated by an event
16135
+     */
16136
+    onDeselect: function() {
16137
+      // implemented by sub-classes, as needed.
16138
+    },
16139
+
16140
+
16141
+    /**
16142
+     * This callback function is called every time _discardActiveObject or _setActiveObject
16143
+     * try to to select this object. If the function returns true, the process is cancelled
16144
+     * @param {Object} [options] options sent from the upper functions
16145
+     * @param {Event} [options.e] event if the process is generated by an event
16146
+     */
16147
+    onSelect: function() {
16148
+      // implemented by sub-classes, as needed.
16149
+    }
16150
+  });
16151
+})();
16152
+
16153
+
16154
+fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
16155
+
16156
+  /**
16157
+   * Animation duration (in ms) for fx* methods
16158
+   * @type Number
16159
+   * @default
16160
+   */
16161
+  FX_DURATION: 500,
16162
+
16163
+  /**
16164
+   * Centers object horizontally with animation.
16165
+   * @param {fabric.Object} object Object to center
16166
+   * @param {Object} [callbacks] Callbacks object with optional "onComplete" and/or "onChange" properties
16167
+   * @param {Function} [callbacks.onComplete] Invoked on completion
16168
+   * @param {Function} [callbacks.onChange] Invoked on every step of animation
16169
+   * @return {fabric.Canvas} thisArg
16170
+   * @chainable
16171
+   */
16172
+  fxCenterObjectH: function (object, callbacks) {
16173
+    callbacks = callbacks || { };
16174
+
16175
+    var empty = function() { },
16176
+        onComplete = callbacks.onComplete || empty,
16177
+        onChange = callbacks.onChange || empty,
16178
+        _this = this;
16179
+
16180
+    fabric.util.animate({
16181
+      startValue: object.left,
16182
+      endValue: this.getCenter().left,
16183
+      duration: this.FX_DURATION,
16184
+      onChange: function(value) {
16185
+        object.set('left', value);
16186
+        _this.requestRenderAll();
16187
+        onChange();
16188
+      },
16189
+      onComplete: function() {
16190
+        object.setCoords();
16191
+        onComplete();
16192
+      }
16193
+    });
16194
+
16195
+    return this;
16196
+  },
16197
+
16198
+  /**
16199
+   * Centers object vertically with animation.
16200
+   * @param {fabric.Object} object Object to center
16201
+   * @param {Object} [callbacks] Callbacks object with optional "onComplete" and/or "onChange" properties
16202
+   * @param {Function} [callbacks.onComplete] Invoked on completion
16203
+   * @param {Function} [callbacks.onChange] Invoked on every step of animation
16204
+   * @return {fabric.Canvas} thisArg
16205
+   * @chainable
16206
+   */
16207
+  fxCenterObjectV: function (object, callbacks) {
16208
+    callbacks = callbacks || { };
16209
+
16210
+    var empty = function() { },
16211
+        onComplete = callbacks.onComplete || empty,
16212
+        onChange = callbacks.onChange || empty,
16213
+        _this = this;
16214
+
16215
+    fabric.util.animate({
16216
+      startValue: object.top,
16217
+      endValue: this.getCenter().top,
16218
+      duration: this.FX_DURATION,
16219
+      onChange: function(value) {
16220
+        object.set('top', value);
16221
+        _this.requestRenderAll();
16222
+        onChange();
16223
+      },
16224
+      onComplete: function() {
16225
+        object.setCoords();
16226
+        onComplete();
16227
+      }
16228
+    });
16229
+
16230
+    return this;
16231
+  },
16232
+
16233
+  /**
16234
+   * Same as `fabric.Canvas#remove` but animated
16235
+   * @param {fabric.Object} object Object to remove
16236
+   * @param {Object} [callbacks] Callbacks object with optional "onComplete" and/or "onChange" properties
16237
+   * @param {Function} [callbacks.onComplete] Invoked on completion
16238
+   * @param {Function} [callbacks.onChange] Invoked on every step of animation
16239
+   * @return {fabric.Canvas} thisArg
16240
+   * @chainable
16241
+   */
16242
+  fxRemove: function (object, callbacks) {
16243
+    callbacks = callbacks || { };
16244
+
16245
+    var empty = function() { },
16246
+        onComplete = callbacks.onComplete || empty,
16247
+        onChange = callbacks.onChange || empty,
16248
+        _this = this;
16249
+
16250
+    fabric.util.animate({
16251
+      startValue: object.opacity,
16252
+      endValue: 0,
16253
+      duration: this.FX_DURATION,
16254
+      onChange: function(value) {
16255
+        object.set('opacity', value);
16256
+        _this.requestRenderAll();
16257
+        onChange();
16258
+      },
16259
+      onComplete: function () {
16260
+        _this.remove(object);
16261
+        onComplete();
16262
+      }
16263
+    });
16264
+
16265
+    return this;
16266
+  }
16267
+});
16268
+
16269
+fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
16270
+  /**
16271
+   * Animates object's properties
16272
+   * @param {String|Object} property Property to animate (if string) or properties to animate (if object)
16273
+   * @param {Number|Object} value Value to animate property to (if string was given first) or options object
16274
+   * @return {fabric.Object} thisArg
16275
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#animation}
16276
+   * @chainable
16277
+   *
16278
+   * As object — multiple properties
16279
+   *
16280
+   * object.animate({ left: ..., top: ... });
16281
+   * object.animate({ left: ..., top: ... }, { duration: ... });
16282
+   *
16283
+   * As string — one property
16284
+   *
16285
+   * object.animate('left', ...);
16286
+   * object.animate('left', { duration: ... });
16287
+   *
16288
+   */
16289
+  animate: function() {
16290
+    if (arguments[0] && typeof arguments[0] === 'object') {
16291
+      var propsToAnimate = [], prop, skipCallbacks;
16292
+      for (prop in arguments[0]) {
16293
+        propsToAnimate.push(prop);
16294
+      }
16295
+      for (var i = 0, len = propsToAnimate.length; i < len; i++) {
16296
+        prop = propsToAnimate[i];
16297
+        skipCallbacks = i !== len - 1;
16298
+        this._animate(prop, arguments[0][prop], arguments[1], skipCallbacks);
16299
+      }
16300
+    }
16301
+    else {
16302
+      this._animate.apply(this, arguments);
16303
+    }
16304
+    return this;
16305
+  },
16306
+
16307
+  /**
16308
+   * @private
16309
+   * @param {String} property Property to animate
16310
+   * @param {String} to Value to animate to
16311
+   * @param {Object} [options] Options object
16312
+   * @param {Boolean} [skipCallbacks] When true, callbacks like onchange and oncomplete are not invoked
16313
+   */
16314
+  _animate: function(property, to, options, skipCallbacks) {
16315
+    var _this = this, propPair;
16316
+
16317
+    to = to.toString();
16318
+
16319
+    if (!options) {
16320
+      options = { };
16321
+    }
16322
+    else {
16323
+      options = fabric.util.object.clone(options);
16324
+    }
16325
+
16326
+    if (~property.indexOf('.')) {
16327
+      propPair = property.split('.');
16328
+    }
16329
+
16330
+    var currentValue = propPair
16331
+      ? this.get(propPair[0])[propPair[1]]
16332
+      : this.get(property);
16333
+
16334
+    if (!('from' in options)) {
16335
+      options.from = currentValue;
16336
+    }
16337
+
16338
+    if (~to.indexOf('=')) {
16339
+      to = currentValue + parseFloat(to.replace('=', ''));
16340
+    }
16341
+    else {
16342
+      to = parseFloat(to);
16343
+    }
16344
+
16345
+    fabric.util.animate({
16346
+      startValue: options.from,
16347
+      endValue: to,
16348
+      byValue: options.by,
16349
+      easing: options.easing,
16350
+      duration: options.duration,
16351
+      abort: options.abort && function() {
16352
+        return options.abort.call(_this);
16353
+      },
16354
+      onChange: function(value, valueProgress, timeProgress) {
16355
+        if (propPair) {
16356
+          _this[propPair[0]][propPair[1]] = value;
16357
+        }
16358
+        else {
16359
+          _this.set(property, value);
16360
+        }
16361
+        if (skipCallbacks) {
16362
+          return;
16363
+        }
16364
+        options.onChange && options.onChange(value, valueProgress, timeProgress);
16365
+      },
16366
+      onComplete: function(value, valueProgress, timeProgress) {
16367
+        if (skipCallbacks) {
16368
+          return;
16369
+        }
16370
+
16371
+        _this.setCoords();
16372
+        options.onComplete && options.onComplete(value, valueProgress, timeProgress);
16373
+      }
16374
+    });
16375
+  }
16376
+});
16377
+
16378
+
16379
+(function(global) {
16380
+
16381
+  'use strict';
16382
+
16383
+  var fabric = global.fabric || (global.fabric = { }),
16384
+      extend = fabric.util.object.extend,
16385
+      clone = fabric.util.object.clone,
16386
+      coordProps = { x1: 1, x2: 1, y1: 1, y2: 1 },
16387
+      supportsLineDash = fabric.StaticCanvas.supports('setLineDash');
16388
+
16389
+  if (fabric.Line) {
16390
+    fabric.warn('fabric.Line is already defined');
16391
+    return;
16392
+  }
16393
+
16394
+  /**
16395
+   * Line class
16396
+   * @class fabric.Line
16397
+   * @extends fabric.Object
16398
+   * @see {@link fabric.Line#initialize} for constructor definition
16399
+   */
16400
+  fabric.Line = fabric.util.createClass(fabric.Object, /** @lends fabric.Line.prototype */ {
16401
+
16402
+    /**
16403
+     * Type of an object
16404
+     * @type String
16405
+     * @default
16406
+     */
16407
+    type: 'line',
16408
+
16409
+    /**
16410
+     * x value or first line edge
16411
+     * @type Number
16412
+     * @default
16413
+     */
16414
+    x1: 0,
16415
+
16416
+    /**
16417
+     * y value or first line edge
16418
+     * @type Number
16419
+     * @default
16420
+     */
16421
+    y1: 0,
16422
+
16423
+    /**
16424
+     * x value or second line edge
16425
+     * @type Number
16426
+     * @default
16427
+     */
16428
+    x2: 0,
16429
+
16430
+    /**
16431
+     * y value or second line edge
16432
+     * @type Number
16433
+     * @default
16434
+     */
16435
+    y2: 0,
16436
+
16437
+    cacheProperties: fabric.Object.prototype.cacheProperties.concat('x1', 'x2', 'y1', 'y2'),
16438
+
16439
+    /**
16440
+     * Constructor
16441
+     * @param {Array} [points] Array of points
16442
+     * @param {Object} [options] Options object
16443
+     * @return {fabric.Line} thisArg
16444
+     */
16445
+    initialize: function(points, options) {
16446
+      if (!points) {
16447
+        points = [0, 0, 0, 0];
16448
+      }
16449
+
16450
+      this.callSuper('initialize', options);
16451
+
16452
+      this.set('x1', points[0]);
16453
+      this.set('y1', points[1]);
16454
+      this.set('x2', points[2]);
16455
+      this.set('y2', points[3]);
16456
+
16457
+      this._setWidthHeight(options);
16458
+    },
16459
+
16460
+    /**
16461
+     * @private
16462
+     * @param {Object} [options] Options
16463
+     */
16464
+    _setWidthHeight: function(options) {
16465
+      options || (options = { });
16466
+
16467
+      this.width = Math.abs(this.x2 - this.x1);
16468
+      this.height = Math.abs(this.y2 - this.y1);
16469
+
16470
+      this.left = 'left' in options
16471
+        ? options.left
16472
+        : this._getLeftToOriginX();
16473
+
16474
+      this.top = 'top' in options
16475
+        ? options.top
16476
+        : this._getTopToOriginY();
16477
+    },
16478
+
16479
+    /**
16480
+     * @private
16481
+     * @param {String} key
16482
+     * @param {*} value
16483
+     */
16484
+    _set: function(key, value) {
16485
+      this.callSuper('_set', key, value);
16486
+      if (typeof coordProps[key] !== 'undefined') {
16487
+        this._setWidthHeight();
16488
+      }
16489
+      return this;
16490
+    },
16491
+
16492
+    /**
16493
+     * @private
16494
+     * @return {Number} leftToOriginX Distance from left edge of canvas to originX of Line.
16495
+     */
16496
+    _getLeftToOriginX: makeEdgeToOriginGetter(
16497
+      { // property names
16498
+        origin: 'originX',
16499
+        axis1: 'x1',
16500
+        axis2: 'x2',
16501
+        dimension: 'width'
16502
+      },
16503
+      { // possible values of origin
16504
+        nearest: 'left',
16505
+        center: 'center',
16506
+        farthest: 'right'
16507
+      }
16508
+    ),
16509
+
16510
+    /**
16511
+     * @private
16512
+     * @return {Number} topToOriginY Distance from top edge of canvas to originY of Line.
16513
+     */
16514
+    _getTopToOriginY: makeEdgeToOriginGetter(
16515
+      { // property names
16516
+        origin: 'originY',
16517
+        axis1: 'y1',
16518
+        axis2: 'y2',
16519
+        dimension: 'height'
16520
+      },
16521
+      { // possible values of origin
16522
+        nearest: 'top',
16523
+        center: 'center',
16524
+        farthest: 'bottom'
16525
+      }
16526
+    ),
16527
+
16528
+    /**
16529
+     * @private
16530
+     * @param {CanvasRenderingContext2D} ctx Context to render on
16531
+     */
16532
+    _render: function(ctx) {
16533
+      ctx.beginPath();
16534
+
16535
+      if (!this.strokeDashArray || this.strokeDashArray && supportsLineDash) {
16536
+        // move from center (of virtual box) to its left/top corner
16537
+        // we can't assume x1, y1 is top left and x2, y2 is bottom right
16538
+        var p = this.calcLinePoints();
16539
+        ctx.moveTo(p.x1, p.y1);
16540
+        ctx.lineTo(p.x2, p.y2);
16541
+      }
16542
+
16543
+      ctx.lineWidth = this.strokeWidth;
16544
+
16545
+      // TODO: test this
16546
+      // make sure setting "fill" changes color of a line
16547
+      // (by copying fillStyle to strokeStyle, since line is stroked, not filled)
16548
+      var origStrokeStyle = ctx.strokeStyle;
16549
+      ctx.strokeStyle = this.stroke || ctx.fillStyle;
16550
+      this.stroke && this._renderStroke(ctx);
16551
+      ctx.strokeStyle = origStrokeStyle;
16552
+    },
16553
+
16554
+    /**
16555
+     * @private
16556
+     * @param {CanvasRenderingContext2D} ctx Context to render on
16557
+     */
16558
+    _renderDashedStroke: function(ctx) {
16559
+      var p = this.calcLinePoints();
16560
+
16561
+      ctx.beginPath();
16562
+      fabric.util.drawDashedLine(ctx, p.x1, p.y1, p.x2, p.y2, this.strokeDashArray);
16563
+      ctx.closePath();
16564
+    },
16565
+
16566
+    /**
16567
+     * This function is an helper for svg import. it returns the center of the object in the svg
16568
+     * untransformed coordinates
16569
+     * @private
16570
+     * @return {Object} center point from element coordinates
16571
+     */
16572
+    _findCenterFromElement: function() {
16573
+      return {
16574
+        x: (this.x1 + this.x2) / 2,
16575
+        y: (this.y1 + this.y2) / 2,
16576
+      };
16577
+    },
16578
+
16579
+    /**
16580
+     * Returns object representation of an instance
16581
+     * @methd toObject
16582
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
16583
+     * @return {Object} object representation of an instance
16584
+     */
16585
+    toObject: function(propertiesToInclude) {
16586
+      return extend(this.callSuper('toObject', propertiesToInclude), this.calcLinePoints());
16587
+    },
16588
+
16589
+    /*
16590
+     * Calculate object dimensions from its properties
16591
+     * @private
16592
+     */
16593
+    _getNonTransformedDimensions: function() {
16594
+      var dim = this.callSuper('_getNonTransformedDimensions');
16595
+      if (this.strokeLineCap === 'butt') {
16596
+        if (this.width === 0) {
16597
+          dim.y -= this.strokeWidth;
16598
+        }
16599
+        if (this.height === 0) {
16600
+          dim.x -= this.strokeWidth;
16601
+        }
16602
+      }
16603
+      return dim;
16604
+    },
16605
+
16606
+    /**
16607
+     * Recalculates line points given width and height
16608
+     * @private
16609
+     */
16610
+    calcLinePoints: function() {
16611
+      var xMult = this.x1 <= this.x2 ? -1 : 1,
16612
+          yMult = this.y1 <= this.y2 ? -1 : 1,
16613
+          x1 = (xMult * this.width * 0.5),
16614
+          y1 = (yMult * this.height * 0.5),
16615
+          x2 = (xMult * this.width * -0.5),
16616
+          y2 = (yMult * this.height * -0.5);
16617
+
16618
+      return {
16619
+        x1: x1,
16620
+        x2: x2,
16621
+        y1: y1,
16622
+        y2: y2
16623
+      };
16624
+    },
16625
+
16626
+    /* _TO_SVG_START_ */
16627
+    /**
16628
+     * Returns svg representation of an instance
16629
+     * @return {Array} an array of strings with the specific svg representation
16630
+     * of the instance
16631
+     */
16632
+    _toSVG: function() {
16633
+      var p = this.calcLinePoints();
16634
+      return [
16635
+        '<line ', 'COMMON_PARTS',
16636
+        'x1="', p.x1,
16637
+        '" y1="', p.y1,
16638
+        '" x2="', p.x2,
16639
+        '" y2="', p.y2,
16640
+        '" />\n'
16641
+      ];
16642
+    },
16643
+    /* _TO_SVG_END_ */
16644
+  });
16645
+
16646
+  /* _FROM_SVG_START_ */
16647
+  /**
16648
+   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Line.fromElement})
16649
+   * @static
16650
+   * @memberOf fabric.Line
16651
+   * @see http://www.w3.org/TR/SVG/shapes.html#LineElement
16652
+   */
16653
+  fabric.Line.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x1 y1 x2 y2'.split(' '));
16654
+
16655
+  /**
16656
+   * Returns fabric.Line instance from an SVG element
16657
+   * @static
16658
+   * @memberOf fabric.Line
16659
+   * @param {SVGElement} element Element to parse
16660
+   * @param {Object} [options] Options object
16661
+   * @param {Function} [callback] callback function invoked after parsing
16662
+   */
16663
+  fabric.Line.fromElement = function(element, callback, options) {
16664
+    options = options || { };
16665
+    var parsedAttributes = fabric.parseAttributes(element, fabric.Line.ATTRIBUTE_NAMES),
16666
+        points = [
16667
+          parsedAttributes.x1 || 0,
16668
+          parsedAttributes.y1 || 0,
16669
+          parsedAttributes.x2 || 0,
16670
+          parsedAttributes.y2 || 0
16671
+        ];
16672
+    callback(new fabric.Line(points, extend(parsedAttributes, options)));
16673
+  };
16674
+  /* _FROM_SVG_END_ */
16675
+
16676
+  /**
16677
+   * Returns fabric.Line instance from an object representation
16678
+   * @static
16679
+   * @memberOf fabric.Line
16680
+   * @param {Object} object Object to create an instance from
16681
+   * @param {function} [callback] invoked with new instance as first argument
16682
+   */
16683
+  fabric.Line.fromObject = function(object, callback) {
16684
+    function _callback(instance) {
16685
+      delete instance.points;
16686
+      callback && callback(instance);
16687
+    };
16688
+    var options = clone(object, true);
16689
+    options.points = [object.x1, object.y1, object.x2, object.y2];
16690
+    fabric.Object._fromObject('Line', options, _callback, 'points');
16691
+  };
16692
+
16693
+  /**
16694
+   * Produces a function that calculates distance from canvas edge to Line origin.
16695
+   */
16696
+  function makeEdgeToOriginGetter(propertyNames, originValues) {
16697
+    var origin = propertyNames.origin,
16698
+        axis1 = propertyNames.axis1,
16699
+        axis2 = propertyNames.axis2,
16700
+        dimension = propertyNames.dimension,
16701
+        nearest = originValues.nearest,
16702
+        center = originValues.center,
16703
+        farthest = originValues.farthest;
16704
+
16705
+    return function() {
16706
+      switch (this.get(origin)) {
16707
+        case nearest:
16708
+          return Math.min(this.get(axis1), this.get(axis2));
16709
+        case center:
16710
+          return Math.min(this.get(axis1), this.get(axis2)) + (0.5 * this.get(dimension));
16711
+        case farthest:
16712
+          return Math.max(this.get(axis1), this.get(axis2));
16713
+      }
16714
+    };
16715
+
16716
+  }
16717
+
16718
+})(typeof exports !== 'undefined' ? exports : this);
16719
+
16720
+
16721
+(function(global) {
16722
+
16723
+  'use strict';
16724
+
16725
+  var fabric = global.fabric || (global.fabric = { }),
16726
+      pi = Math.PI;
16727
+
16728
+  if (fabric.Circle) {
16729
+    fabric.warn('fabric.Circle is already defined.');
16730
+    return;
16731
+  }
16732
+
16733
+  /**
16734
+   * Circle class
16735
+   * @class fabric.Circle
16736
+   * @extends fabric.Object
16737
+   * @see {@link fabric.Circle#initialize} for constructor definition
16738
+   */
16739
+  fabric.Circle = fabric.util.createClass(fabric.Object, /** @lends fabric.Circle.prototype */ {
16740
+
16741
+    /**
16742
+     * Type of an object
16743
+     * @type String
16744
+     * @default
16745
+     */
16746
+    type: 'circle',
16747
+
16748
+    /**
16749
+     * Radius of this circle
16750
+     * @type Number
16751
+     * @default
16752
+     */
16753
+    radius: 0,
16754
+
16755
+    /**
16756
+     * Start angle of the circle, moving clockwise
16757
+     * deprectated type, this should be in degree, this was an oversight.
16758
+     * probably will change to degrees in next major version
16759
+     * @type Number
16760
+     * @default 0
16761
+     */
16762
+    startAngle: 0,
16763
+
16764
+    /**
16765
+     * End angle of the circle
16766
+     * deprectated type, this should be in degree, this was an oversight.
16767
+     * probably will change to degrees in next major version
16768
+     * @type Number
16769
+     * @default 2Pi
16770
+     */
16771
+    endAngle: pi * 2,
16772
+
16773
+    cacheProperties: fabric.Object.prototype.cacheProperties.concat('radius', 'startAngle', 'endAngle'),
16774
+
16775
+    /**
16776
+     * @private
16777
+     * @param {String} key
16778
+     * @param {*} value
16779
+     * @return {fabric.Circle} thisArg
16780
+     */
16781
+    _set: function(key, value) {
16782
+      this.callSuper('_set', key, value);
16783
+
16784
+      if (key === 'radius') {
16785
+        this.setRadius(value);
16786
+      }
16787
+
16788
+      return this;
16789
+    },
16790
+
16791
+    /**
16792
+     * Returns object representation of an instance
16793
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
16794
+     * @return {Object} object representation of an instance
16795
+     */
16796
+    toObject: function(propertiesToInclude) {
16797
+      return this.callSuper('toObject', ['radius', 'startAngle', 'endAngle'].concat(propertiesToInclude));
16798
+    },
16799
+
16800
+    /* _TO_SVG_START_ */
16801
+
16802
+    /**
16803
+     * Returns svg representation of an instance
16804
+     * @return {Array} an array of strings with the specific svg representation
16805
+     * of the instance
16806
+     */
16807
+    _toSVG: function() {
16808
+      var svgString, x = 0, y = 0,
16809
+          angle = (this.endAngle - this.startAngle) % ( 2 * pi);
16810
+
16811
+      if (angle === 0) {
16812
+        svgString = [
16813
+          '<circle ', 'COMMON_PARTS',
16814
+          'cx="' + x + '" cy="' + y + '" ',
16815
+          'r="', this.radius,
16816
+          '" />\n'
16817
+        ];
16818
+      }
16819
+      else {
16820
+        var startX = fabric.util.cos(this.startAngle) * this.radius,
16821
+            startY = fabric.util.sin(this.startAngle) * this.radius,
16822
+            endX = fabric.util.cos(this.endAngle) * this.radius,
16823
+            endY = fabric.util.sin(this.endAngle) * this.radius,
16824
+            largeFlag = angle > pi ? '1' : '0';
16825
+        svgString = [
16826
+          '<path d="M ' + startX + ' ' + startY,
16827
+          ' A ' + this.radius + ' ' + this.radius,
16828
+          ' 0 ', +largeFlag + ' 1', ' ' + endX + ' ' + endY,
16829
+          '"', 'COMMON_PARTS', ' />\n'
16830
+        ];
16831
+      }
16832
+      return svgString;
16833
+    },
16834
+    /* _TO_SVG_END_ */
16835
+
16836
+    /**
16837
+     * @private
16838
+     * @param {CanvasRenderingContext2D} ctx context to render on
16839
+     */
16840
+    _render: function(ctx) {
16841
+      ctx.beginPath();
16842
+      ctx.arc(
16843
+        0,
16844
+        0,
16845
+        this.radius,
16846
+        this.startAngle,
16847
+        this.endAngle, false);
16848
+      this._renderPaintInOrder(ctx);
16849
+    },
16850
+
16851
+    /**
16852
+     * Returns horizontal radius of an object (according to how an object is scaled)
16853
+     * @return {Number}
16854
+     */
16855
+    getRadiusX: function() {
16856
+      return this.get('radius') * this.get('scaleX');
16857
+    },
16858
+
16859
+    /**
16860
+     * Returns vertical radius of an object (according to how an object is scaled)
16861
+     * @return {Number}
16862
+     */
16863
+    getRadiusY: function() {
16864
+      return this.get('radius') * this.get('scaleY');
16865
+    },
16866
+
16867
+    /**
16868
+     * Sets radius of an object (and updates width accordingly)
16869
+     * @return {fabric.Circle} thisArg
16870
+     */
16871
+    setRadius: function(value) {
16872
+      this.radius = value;
16873
+      return this.set('width', value * 2).set('height', value * 2);
16874
+    },
16875
+  });
16876
+
16877
+  /* _FROM_SVG_START_ */
16878
+  /**
16879
+   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Circle.fromElement})
16880
+   * @static
16881
+   * @memberOf fabric.Circle
16882
+   * @see: http://www.w3.org/TR/SVG/shapes.html#CircleElement
16883
+   */
16884
+  fabric.Circle.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('cx cy r'.split(' '));
16885
+
16886
+  /**
16887
+   * Returns {@link fabric.Circle} instance from an SVG element
16888
+   * @static
16889
+   * @memberOf fabric.Circle
16890
+   * @param {SVGElement} element Element to parse
16891
+   * @param {Function} [callback] Options callback invoked after parsing is finished
16892
+   * @param {Object} [options] Options object
16893
+   * @throws {Error} If value of `r` attribute is missing or invalid
16894
+   */
16895
+  fabric.Circle.fromElement = function(element, callback) {
16896
+    var parsedAttributes = fabric.parseAttributes(element, fabric.Circle.ATTRIBUTE_NAMES);
16897
+
16898
+    if (!isValidRadius(parsedAttributes)) {
16899
+      throw new Error('value of `r` attribute is required and can not be negative');
16900
+    }
16901
+
16902
+    parsedAttributes.left = (parsedAttributes.left || 0) - parsedAttributes.radius;
16903
+    parsedAttributes.top = (parsedAttributes.top || 0) - parsedAttributes.radius;
16904
+    callback(new fabric.Circle(parsedAttributes));
16905
+  };
16906
+
16907
+  /**
16908
+   * @private
16909
+   */
16910
+  function isValidRadius(attributes) {
16911
+    return (('radius' in attributes) && (attributes.radius >= 0));
16912
+  }
16913
+  /* _FROM_SVG_END_ */
16914
+
16915
+  /**
16916
+   * Returns {@link fabric.Circle} instance from an object representation
16917
+   * @static
16918
+   * @memberOf fabric.Circle
16919
+   * @param {Object} object Object to create an instance from
16920
+   * @param {function} [callback] invoked with new instance as first argument
16921
+   * @return {Object} Instance of fabric.Circle
16922
+   */
16923
+  fabric.Circle.fromObject = function(object, callback) {
16924
+    return fabric.Object._fromObject('Circle', object, callback);
16925
+  };
16926
+
16927
+})(typeof exports !== 'undefined' ? exports : this);
16928
+
16929
+
16930
+(function(global) {
16931
+
16932
+  'use strict';
16933
+
16934
+  var fabric = global.fabric || (global.fabric = { });
16935
+
16936
+  if (fabric.Triangle) {
16937
+    fabric.warn('fabric.Triangle is already defined');
16938
+    return;
16939
+  }
16940
+
16941
+  /**
16942
+   * Triangle class
16943
+   * @class fabric.Triangle
16944
+   * @extends fabric.Object
16945
+   * @return {fabric.Triangle} thisArg
16946
+   * @see {@link fabric.Triangle#initialize} for constructor definition
16947
+   */
16948
+  fabric.Triangle = fabric.util.createClass(fabric.Object, /** @lends fabric.Triangle.prototype */ {
16949
+
16950
+    /**
16951
+     * Type of an object
16952
+     * @type String
16953
+     * @default
16954
+     */
16955
+    type: 'triangle',
16956
+
16957
+    /**
16958
+     * Width is set to 100 to compensate the old initialize code that was setting it to 100
16959
+     * @type Number
16960
+     * @default
16961
+     */
16962
+    width: 100,
16963
+
16964
+    /**
16965
+     * Height is set to 100 to compensate the old initialize code that was setting it to 100
16966
+     * @type Number
16967
+     * @default
16968
+     */
16969
+    height: 100,
16970
+
16971
+    /**
16972
+     * @private
16973
+     * @param {CanvasRenderingContext2D} ctx Context to render on
16974
+     */
16975
+    _render: function(ctx) {
16976
+      var widthBy2 = this.width / 2,
16977
+          heightBy2 = this.height / 2;
16978
+
16979
+      ctx.beginPath();
16980
+      ctx.moveTo(-widthBy2, heightBy2);
16981
+      ctx.lineTo(0, -heightBy2);
16982
+      ctx.lineTo(widthBy2, heightBy2);
16983
+      ctx.closePath();
16984
+
16985
+      this._renderPaintInOrder(ctx);
16986
+    },
16987
+
16988
+    /**
16989
+     * @private
16990
+     * @param {CanvasRenderingContext2D} ctx Context to render on
16991
+     */
16992
+    _renderDashedStroke: function(ctx) {
16993
+      var widthBy2 = this.width / 2,
16994
+          heightBy2 = this.height / 2;
16995
+
16996
+      ctx.beginPath();
16997
+      fabric.util.drawDashedLine(ctx, -widthBy2, heightBy2, 0, -heightBy2, this.strokeDashArray);
16998
+      fabric.util.drawDashedLine(ctx, 0, -heightBy2, widthBy2, heightBy2, this.strokeDashArray);
16999
+      fabric.util.drawDashedLine(ctx, widthBy2, heightBy2, -widthBy2, heightBy2, this.strokeDashArray);
17000
+      ctx.closePath();
17001
+    },
17002
+
17003
+    /* _TO_SVG_START_ */
17004
+    /**
17005
+     * Returns svg representation of an instance
17006
+     * @return {Array} an array of strings with the specific svg representation
17007
+     * of the instance
17008
+     */
17009
+    _toSVG: function() {
17010
+      var widthBy2 = this.width / 2,
17011
+          heightBy2 = this.height / 2,
17012
+          points = [
17013
+            -widthBy2 + ' ' + heightBy2,
17014
+            '0 ' + -heightBy2,
17015
+            widthBy2 + ' ' + heightBy2
17016
+          ].join(',');
17017
+      return [
17018
+        '<polygon ', 'COMMON_PARTS',
17019
+        'points="', points,
17020
+        '" />'
17021
+      ];
17022
+    },
17023
+    /* _TO_SVG_END_ */
17024
+  });
17025
+
17026
+  /**
17027
+   * Returns {@link fabric.Triangle} instance from an object representation
17028
+   * @static
17029
+   * @memberOf fabric.Triangle
17030
+   * @param {Object} object Object to create an instance from
17031
+   * @param {function} [callback] invoked with new instance as first argument
17032
+   */
17033
+  fabric.Triangle.fromObject = function(object, callback) {
17034
+    return fabric.Object._fromObject('Triangle', object, callback);
17035
+  };
17036
+
17037
+})(typeof exports !== 'undefined' ? exports : this);
17038
+
17039
+
17040
+(function(global) {
17041
+
17042
+  'use strict';
17043
+
17044
+  var fabric = global.fabric || (global.fabric = { }),
17045
+      piBy2   = Math.PI * 2;
17046
+
17047
+  if (fabric.Ellipse) {
17048
+    fabric.warn('fabric.Ellipse is already defined.');
17049
+    return;
17050
+  }
17051
+
17052
+  /**
17053
+   * Ellipse class
17054
+   * @class fabric.Ellipse
17055
+   * @extends fabric.Object
17056
+   * @return {fabric.Ellipse} thisArg
17057
+   * @see {@link fabric.Ellipse#initialize} for constructor definition
17058
+   */
17059
+  fabric.Ellipse = fabric.util.createClass(fabric.Object, /** @lends fabric.Ellipse.prototype */ {
17060
+
17061
+    /**
17062
+     * Type of an object
17063
+     * @type String
17064
+     * @default
17065
+     */
17066
+    type: 'ellipse',
17067
+
17068
+    /**
17069
+     * Horizontal radius
17070
+     * @type Number
17071
+     * @default
17072
+     */
17073
+    rx:   0,
17074
+
17075
+    /**
17076
+     * Vertical radius
17077
+     * @type Number
17078
+     * @default
17079
+     */
17080
+    ry:   0,
17081
+
17082
+    cacheProperties: fabric.Object.prototype.cacheProperties.concat('rx', 'ry'),
17083
+
17084
+    /**
17085
+     * Constructor
17086
+     * @param {Object} [options] Options object
17087
+     * @return {fabric.Ellipse} thisArg
17088
+     */
17089
+    initialize: function(options) {
17090
+      this.callSuper('initialize', options);
17091
+      this.set('rx', options && options.rx || 0);
17092
+      this.set('ry', options && options.ry || 0);
17093
+    },
17094
+
17095
+    /**
17096
+     * @private
17097
+     * @param {String} key
17098
+     * @param {*} value
17099
+     * @return {fabric.Ellipse} thisArg
17100
+     */
17101
+    _set: function(key, value) {
17102
+      this.callSuper('_set', key, value);
17103
+      switch (key) {
17104
+
17105
+        case 'rx':
17106
+          this.rx = value;
17107
+          this.set('width', value * 2);
17108
+          break;
17109
+
17110
+        case 'ry':
17111
+          this.ry = value;
17112
+          this.set('height', value * 2);
17113
+          break;
17114
+
17115
+      }
17116
+      return this;
17117
+    },
17118
+
17119
+    /**
17120
+     * Returns horizontal radius of an object (according to how an object is scaled)
17121
+     * @return {Number}
17122
+     */
17123
+    getRx: function() {
17124
+      return this.get('rx') * this.get('scaleX');
17125
+    },
17126
+
17127
+    /**
17128
+     * Returns Vertical radius of an object (according to how an object is scaled)
17129
+     * @return {Number}
17130
+     */
17131
+    getRy: function() {
17132
+      return this.get('ry') * this.get('scaleY');
17133
+    },
17134
+
17135
+    /**
17136
+     * Returns object representation of an instance
17137
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
17138
+     * @return {Object} object representation of an instance
17139
+     */
17140
+    toObject: function(propertiesToInclude) {
17141
+      return this.callSuper('toObject', ['rx', 'ry'].concat(propertiesToInclude));
17142
+    },
17143
+
17144
+    /* _TO_SVG_START_ */
17145
+    /**
17146
+     * Returns svg representation of an instance
17147
+     * @return {Array} an array of strings with the specific svg representation
17148
+     * of the instance
17149
+     */
17150
+    _toSVG: function() {
17151
+      return [
17152
+        '<ellipse ', 'COMMON_PARTS',
17153
+        'cx="0" cy="0" ',
17154
+        'rx="', this.rx,
17155
+        '" ry="', this.ry,
17156
+        '" />\n'
17157
+      ];
17158
+    },
17159
+    /* _TO_SVG_END_ */
17160
+
17161
+    /**
17162
+     * @private
17163
+     * @param {CanvasRenderingContext2D} ctx context to render on
17164
+     */
17165
+    _render: function(ctx) {
17166
+      ctx.beginPath();
17167
+      ctx.save();
17168
+      ctx.transform(1, 0, 0, this.ry / this.rx, 0, 0);
17169
+      ctx.arc(
17170
+        0,
17171
+        0,
17172
+        this.rx,
17173
+        0,
17174
+        piBy2,
17175
+        false);
17176
+      ctx.restore();
17177
+      this._renderPaintInOrder(ctx);
17178
+    },
17179
+  });
17180
+
17181
+  /* _FROM_SVG_START_ */
17182
+  /**
17183
+   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Ellipse.fromElement})
17184
+   * @static
17185
+   * @memberOf fabric.Ellipse
17186
+   * @see http://www.w3.org/TR/SVG/shapes.html#EllipseElement
17187
+   */
17188
+  fabric.Ellipse.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('cx cy rx ry'.split(' '));
17189
+
17190
+  /**
17191
+   * Returns {@link fabric.Ellipse} instance from an SVG element
17192
+   * @static
17193
+   * @memberOf fabric.Ellipse
17194
+   * @param {SVGElement} element Element to parse
17195
+   * @param {Function} [callback] Options callback invoked after parsing is finished
17196
+   * @return {fabric.Ellipse}
17197
+   */
17198
+  fabric.Ellipse.fromElement = function(element, callback) {
17199
+
17200
+    var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES);
17201
+
17202
+    parsedAttributes.left = (parsedAttributes.left || 0) - parsedAttributes.rx;
17203
+    parsedAttributes.top = (parsedAttributes.top || 0) - parsedAttributes.ry;
17204
+    callback(new fabric.Ellipse(parsedAttributes));
17205
+  };
17206
+  /* _FROM_SVG_END_ */
17207
+
17208
+  /**
17209
+   * Returns {@link fabric.Ellipse} instance from an object representation
17210
+   * @static
17211
+   * @memberOf fabric.Ellipse
17212
+   * @param {Object} object Object to create an instance from
17213
+   * @param {function} [callback] invoked with new instance as first argument
17214
+   * @return {fabric.Ellipse}
17215
+   */
17216
+  fabric.Ellipse.fromObject = function(object, callback) {
17217
+    return fabric.Object._fromObject('Ellipse', object, callback);
17218
+  };
17219
+
17220
+})(typeof exports !== 'undefined' ? exports : this);
17221
+
17222
+
17223
+(function(global) {
17224
+
17225
+  'use strict';
17226
+
17227
+  var fabric = global.fabric || (global.fabric = { }),
17228
+      extend = fabric.util.object.extend;
17229
+
17230
+  if (fabric.Rect) {
17231
+    fabric.warn('fabric.Rect is already defined');
17232
+    return;
17233
+  }
17234
+
17235
+  /**
17236
+   * Rectangle class
17237
+   * @class fabric.Rect
17238
+   * @extends fabric.Object
17239
+   * @return {fabric.Rect} thisArg
17240
+   * @see {@link fabric.Rect#initialize} for constructor definition
17241
+   */
17242
+  fabric.Rect = fabric.util.createClass(fabric.Object, /** @lends fabric.Rect.prototype */ {
17243
+
17244
+    /**
17245
+     * List of properties to consider when checking if state of an object is changed ({@link fabric.Object#hasStateChanged})
17246
+     * as well as for history (undo/redo) purposes
17247
+     * @type Array
17248
+     */
17249
+    stateProperties: fabric.Object.prototype.stateProperties.concat('rx', 'ry'),
17250
+
17251
+    /**
17252
+     * Type of an object
17253
+     * @type String
17254
+     * @default
17255
+     */
17256
+    type: 'rect',
17257
+
17258
+    /**
17259
+     * Horizontal border radius
17260
+     * @type Number
17261
+     * @default
17262
+     */
17263
+    rx:   0,
17264
+
17265
+    /**
17266
+     * Vertical border radius
17267
+     * @type Number
17268
+     * @default
17269
+     */
17270
+    ry:   0,
17271
+
17272
+    cacheProperties: fabric.Object.prototype.cacheProperties.concat('rx', 'ry'),
17273
+
17274
+    /**
17275
+     * Constructor
17276
+     * @param {Object} [options] Options object
17277
+     * @return {Object} thisArg
17278
+     */
17279
+    initialize: function(options) {
17280
+      this.callSuper('initialize', options);
17281
+      this._initRxRy();
17282
+    },
17283
+
17284
+    /**
17285
+     * Initializes rx/ry attributes
17286
+     * @private
17287
+     */
17288
+    _initRxRy: function() {
17289
+      if (this.rx && !this.ry) {
17290
+        this.ry = this.rx;
17291
+      }
17292
+      else if (this.ry && !this.rx) {
17293
+        this.rx = this.ry;
17294
+      }
17295
+    },
17296
+
17297
+    /**
17298
+     * @private
17299
+     * @param {CanvasRenderingContext2D} ctx Context to render on
17300
+     */
17301
+    _render: function(ctx) {
17302
+
17303
+      // 1x1 case (used in spray brush) optimization was removed because
17304
+      // with caching and higher zoom level this makes more damage than help
17305
+
17306
+      var rx = this.rx ? Math.min(this.rx, this.width / 2) : 0,
17307
+          ry = this.ry ? Math.min(this.ry, this.height / 2) : 0,
17308
+          w = this.width,
17309
+          h = this.height,
17310
+          x = -this.width / 2,
17311
+          y = -this.height / 2,
17312
+          isRounded = rx !== 0 || ry !== 0,
17313
+          /* "magic number" for bezier approximations of arcs (http://itc.ktu.lt/itc354/Riskus354.pdf) */
17314
+          k = 1 - 0.5522847498;
17315
+      ctx.beginPath();
17316
+
17317
+      ctx.moveTo(x + rx, y);
17318
+
17319
+      ctx.lineTo(x + w - rx, y);
17320
+      isRounded && ctx.bezierCurveTo(x + w - k * rx, y, x + w, y + k * ry, x + w, y + ry);
17321
+
17322
+      ctx.lineTo(x + w, y + h - ry);
17323
+      isRounded && ctx.bezierCurveTo(x + w, y + h - k * ry, x + w - k * rx, y + h, x + w - rx, y + h);
17324
+
17325
+      ctx.lineTo(x + rx, y + h);
17326
+      isRounded && ctx.bezierCurveTo(x + k * rx, y + h, x, y + h - k * ry, x, y + h - ry);
17327
+
17328
+      ctx.lineTo(x, y + ry);
17329
+      isRounded && ctx.bezierCurveTo(x, y + k * ry, x + k * rx, y, x + rx, y);
17330
+
17331
+      ctx.closePath();
17332
+
17333
+      this._renderPaintInOrder(ctx);
17334
+    },
17335
+
17336
+    /**
17337
+     * @private
17338
+     * @param {CanvasRenderingContext2D} ctx Context to render on
17339
+     */
17340
+    _renderDashedStroke: function(ctx) {
17341
+      var x = -this.width / 2,
17342
+          y = -this.height / 2,
17343
+          w = this.width,
17344
+          h = this.height;
17345
+
17346
+      ctx.beginPath();
17347
+      fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
17348
+      fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
17349
+      fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
17350
+      fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
17351
+      ctx.closePath();
17352
+    },
17353
+
17354
+    /**
17355
+     * Returns object representation of an instance
17356
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
17357
+     * @return {Object} object representation of an instance
17358
+     */
17359
+    toObject: function(propertiesToInclude) {
17360
+      return this.callSuper('toObject', ['rx', 'ry'].concat(propertiesToInclude));
17361
+    },
17362
+
17363
+    /* _TO_SVG_START_ */
17364
+    /**
17365
+     * Returns svg representation of an instance
17366
+     * @return {Array} an array of strings with the specific svg representation
17367
+     * of the instance
17368
+     */
17369
+    _toSVG: function() {
17370
+      var x = -this.width / 2, y = -this.height / 2;
17371
+      return [
17372
+        '<rect ', 'COMMON_PARTS',
17373
+        'x="', x, '" y="', y,
17374
+        '" rx="', this.rx, '" ry="', this.ry,
17375
+        '" width="', this.width, '" height="', this.height,
17376
+        '" />\n'
17377
+      ];
17378
+    },
17379
+    /* _TO_SVG_END_ */
17380
+  });
17381
+
17382
+  /* _FROM_SVG_START_ */
17383
+  /**
17384
+   * List of attribute names to account for when parsing SVG element (used by `fabric.Rect.fromElement`)
17385
+   * @static
17386
+   * @memberOf fabric.Rect
17387
+   * @see: http://www.w3.org/TR/SVG/shapes.html#RectElement
17388
+   */
17389
+  fabric.Rect.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x y rx ry width height'.split(' '));
17390
+
17391
+  /**
17392
+   * Returns {@link fabric.Rect} instance from an SVG element
17393
+   * @static
17394
+   * @memberOf fabric.Rect
17395
+   * @param {SVGElement} element Element to parse
17396
+   * @param {Function} callback callback function invoked after parsing
17397
+   * @param {Object} [options] Options object
17398
+   */
17399
+  fabric.Rect.fromElement = function(element, callback, options) {
17400
+    if (!element) {
17401
+      return callback(null);
17402
+    }
17403
+    options = options || { };
17404
+
17405
+    var parsedAttributes = fabric.parseAttributes(element, fabric.Rect.ATTRIBUTE_NAMES);
17406
+
17407
+    parsedAttributes.left = parsedAttributes.left || 0;
17408
+    parsedAttributes.top  = parsedAttributes.top  || 0;
17409
+    var rect = new fabric.Rect(extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
17410
+    rect.visible = rect.visible && rect.width > 0 && rect.height > 0;
17411
+    callback(rect);
17412
+  };
17413
+  /* _FROM_SVG_END_ */
17414
+
17415
+  /**
17416
+   * Returns {@link fabric.Rect} instance from an object representation
17417
+   * @static
17418
+   * @memberOf fabric.Rect
17419
+   * @param {Object} object Object to create an instance from
17420
+   * @param {Function} [callback] Callback to invoke when an fabric.Rect instance is created
17421
+   */
17422
+  fabric.Rect.fromObject = function(object, callback) {
17423
+    return fabric.Object._fromObject('Rect', object, callback);
17424
+  };
17425
+
17426
+})(typeof exports !== 'undefined' ? exports : this);
17427
+
17428
+
17429
+(function(global) {
17430
+
17431
+  'use strict';
17432
+
17433
+  var fabric = global.fabric || (global.fabric = { }),
17434
+      extend = fabric.util.object.extend,
17435
+      min = fabric.util.array.min,
17436
+      max = fabric.util.array.max,
17437
+      toFixed = fabric.util.toFixed;
17438
+
17439
+  if (fabric.Polyline) {
17440
+    fabric.warn('fabric.Polyline is already defined');
17441
+    return;
17442
+  }
17443
+
17444
+  /**
17445
+   * Polyline class
17446
+   * @class fabric.Polyline
17447
+   * @extends fabric.Object
17448
+   * @see {@link fabric.Polyline#initialize} for constructor definition
17449
+   */
17450
+  fabric.Polyline = fabric.util.createClass(fabric.Object, /** @lends fabric.Polyline.prototype */ {
17451
+
17452
+    /**
17453
+     * Type of an object
17454
+     * @type String
17455
+     * @default
17456
+     */
17457
+    type: 'polyline',
17458
+
17459
+    /**
17460
+     * Points array
17461
+     * @type Array
17462
+     * @default
17463
+     */
17464
+    points: null,
17465
+
17466
+    cacheProperties: fabric.Object.prototype.cacheProperties.concat('points'),
17467
+
17468
+    /**
17469
+     * Constructor
17470
+     * @param {Array} points Array of points (where each point is an object with x and y)
17471
+     * @param {Object} [options] Options object
17472
+     * @return {fabric.Polyline} thisArg
17473
+     * @example
17474
+     * var poly = new fabric.Polyline([
17475
+     *     { x: 10, y: 10 },
17476
+     *     { x: 50, y: 30 },
17477
+     *     { x: 40, y: 70 },
17478
+     *     { x: 60, y: 50 },
17479
+     *     { x: 100, y: 150 },
17480
+     *     { x: 40, y: 100 }
17481
+     *   ], {
17482
+     *   stroke: 'red',
17483
+     *   left: 100,
17484
+     *   top: 100
17485
+     * });
17486
+     */
17487
+    initialize: function(points, options) {
17488
+      options = options || {};
17489
+      this.points = points || [];
17490
+      this.callSuper('initialize', options);
17491
+      var calcDim = this._calcDimensions();
17492
+      if (typeof options.left === 'undefined') {
17493
+        this.left = calcDim.left;
17494
+      }
17495
+      if (typeof options.top === 'undefined') {
17496
+        this.top = calcDim.top;
17497
+      }
17498
+      this.width = calcDim.width;
17499
+      this.height = calcDim.height;
17500
+      this.pathOffset = {
17501
+        x: calcDim.left + this.width / 2,
17502
+        y: calcDim.top + this.height / 2
17503
+      };
17504
+    },
17505
+
17506
+    /**
17507
+     * Calculate the polygon min and max point from points array,
17508
+     * returning an object with left, top, widht, height to measure the
17509
+     * polygon size
17510
+     * @return {Object} object.left X coordinate of the polygon leftmost point
17511
+     * @return {Object} object.top Y coordinate of the polygon topmost point
17512
+     * @return {Object} object.width distance between X coordinates of the polygon leftmost and rightmost point
17513
+     * @return {Object} object.height distance between Y coordinates of the polygon topmost and bottommost point
17514
+     * @private
17515
+     */
17516
+    _calcDimensions: function() {
17517
+
17518
+      var points = this.points,
17519
+          minX = min(points, 'x') || 0,
17520
+          minY = min(points, 'y') || 0,
17521
+          maxX = max(points, 'x') || 0,
17522
+          maxY = max(points, 'y') || 0,
17523
+          width = (maxX - minX),
17524
+          height = (maxY - minY);
17525
+
17526
+      return {
17527
+        left: minX,
17528
+        top: minY,
17529
+        width: width,
17530
+        height: height
17531
+      };
17532
+    },
17533
+
17534
+    /**
17535
+     * Returns object representation of an instance
17536
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
17537
+     * @return {Object} Object representation of an instance
17538
+     */
17539
+    toObject: function(propertiesToInclude) {
17540
+      return extend(this.callSuper('toObject', propertiesToInclude), {
17541
+        points: this.points.concat()
17542
+      });
17543
+    },
17544
+
17545
+    /* _TO_SVG_START_ */
17546
+    /**
17547
+     * Returns svg representation of an instance
17548
+     * @return {Array} an array of strings with the specific svg representation
17549
+     * of the instance
17550
+     */
17551
+    _toSVG: function() {
17552
+      var points = [], diffX = this.pathOffset.x, diffY = this.pathOffset.y,
17553
+          NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
17554
+
17555
+      for (var i = 0, len = this.points.length; i < len; i++) {
17556
+        points.push(
17557
+          toFixed(this.points[i].x - diffX, NUM_FRACTION_DIGITS), ',',
17558
+          toFixed(this.points[i].y - diffY, NUM_FRACTION_DIGITS), ' '
17559
+        );
17560
+      }
17561
+      return [
17562
+        '<' + this.type + ' ', 'COMMON_PARTS',
17563
+        'points="', points.join(''),
17564
+        '" />\n'
17565
+      ];
17566
+    },
17567
+    /* _TO_SVG_END_ */
17568
+
17569
+
17570
+    /**
17571
+     * @private
17572
+     * @param {CanvasRenderingContext2D} ctx Context to render on
17573
+     */
17574
+    commonRender: function(ctx) {
17575
+      var point, len = this.points.length,
17576
+          x = this.pathOffset.x,
17577
+          y = this.pathOffset.y;
17578
+
17579
+      if (!len || isNaN(this.points[len - 1].y)) {
17580
+        // do not draw if no points or odd points
17581
+        // NaN comes from parseFloat of a empty string in parser
17582
+        return false;
17583
+      }
17584
+      ctx.beginPath();
17585
+      ctx.moveTo(this.points[0].x - x, this.points[0].y - y);
17586
+      for (var i = 0; i < len; i++) {
17587
+        point = this.points[i];
17588
+        ctx.lineTo(point.x - x, point.y - y);
17589
+      }
17590
+      return true;
17591
+    },
17592
+
17593
+    /**
17594
+     * @private
17595
+     * @param {CanvasRenderingContext2D} ctx Context to render on
17596
+     */
17597
+    _render: function(ctx) {
17598
+      if (!this.commonRender(ctx)) {
17599
+        return;
17600
+      }
17601
+      this._renderPaintInOrder(ctx);
17602
+    },
17603
+
17604
+    /**
17605
+     * @private
17606
+     * @param {CanvasRenderingContext2D} ctx Context to render on
17607
+     */
17608
+    _renderDashedStroke: function(ctx) {
17609
+      var p1, p2;
17610
+
17611
+      ctx.beginPath();
17612
+      for (var i = 0, len = this.points.length; i < len; i++) {
17613
+        p1 = this.points[i];
17614
+        p2 = this.points[i + 1] || p1;
17615
+        fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
17616
+      }
17617
+    },
17618
+
17619
+    /**
17620
+     * Returns complexity of an instance
17621
+     * @return {Number} complexity of this instance
17622
+     */
17623
+    complexity: function() {
17624
+      return this.get('points').length;
17625
+    }
17626
+  });
17627
+
17628
+  /* _FROM_SVG_START_ */
17629
+  /**
17630
+   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Polyline.fromElement})
17631
+   * @static
17632
+   * @memberOf fabric.Polyline
17633
+   * @see: http://www.w3.org/TR/SVG/shapes.html#PolylineElement
17634
+   */
17635
+  fabric.Polyline.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat();
17636
+
17637
+  /**
17638
+   * Returns fabric.Polyline instance from an SVG element
17639
+   * @static
17640
+   * @memberOf fabric.Polyline
17641
+   * @param {SVGElement} element Element to parser
17642
+   * @param {Function} callback callback function invoked after parsing
17643
+   * @param {Object} [options] Options object
17644
+   */
17645
+  fabric.Polyline.fromElement = function(element, callback, options) {
17646
+    if (!element) {
17647
+      return callback(null);
17648
+    }
17649
+    options || (options = { });
17650
+
17651
+    var points = fabric.parsePointsAttribute(element.getAttribute('points')),
17652
+        parsedAttributes = fabric.parseAttributes(element, fabric.Polyline.ATTRIBUTE_NAMES);
17653
+
17654
+    callback(new fabric.Polyline(points, fabric.util.object.extend(parsedAttributes, options)));
17655
+  };
17656
+  /* _FROM_SVG_END_ */
17657
+
17658
+  /**
17659
+   * Returns fabric.Polyline instance from an object representation
17660
+   * @static
17661
+   * @memberOf fabric.Polyline
17662
+   * @param {Object} object Object to create an instance from
17663
+   * @param {Function} [callback] Callback to invoke when an fabric.Path instance is created
17664
+   */
17665
+  fabric.Polyline.fromObject = function(object, callback) {
17666
+    return fabric.Object._fromObject('Polyline', object, callback, 'points');
17667
+  };
17668
+
17669
+})(typeof exports !== 'undefined' ? exports : this);
17670
+
17671
+
17672
+(function(global) {
17673
+
17674
+  'use strict';
17675
+
17676
+  var fabric = global.fabric || (global.fabric = { }),
17677
+      extend = fabric.util.object.extend;
17678
+
17679
+  if (fabric.Polygon) {
17680
+    fabric.warn('fabric.Polygon is already defined');
17681
+    return;
17682
+  }
17683
+
17684
+  /**
17685
+   * Polygon class
17686
+   * @class fabric.Polygon
17687
+   * @extends fabric.Polyline
17688
+   * @see {@link fabric.Polygon#initialize} for constructor definition
17689
+   */
17690
+  fabric.Polygon = fabric.util.createClass(fabric.Polyline, /** @lends fabric.Polygon.prototype */ {
17691
+
17692
+    /**
17693
+     * Type of an object
17694
+     * @type String
17695
+     * @default
17696
+     */
17697
+    type: 'polygon',
17698
+
17699
+    /**
17700
+     * @private
17701
+     * @param {CanvasRenderingContext2D} ctx Context to render on
17702
+     */
17703
+    _render: function(ctx) {
17704
+      if (!this.commonRender(ctx)) {
17705
+        return;
17706
+      }
17707
+      ctx.closePath();
17708
+      this._renderPaintInOrder(ctx);
17709
+    },
17710
+
17711
+    /**
17712
+     * @private
17713
+     * @param {CanvasRenderingContext2D} ctx Context to render on
17714
+     */
17715
+    _renderDashedStroke: function(ctx) {
17716
+      this.callSuper('_renderDashedStroke', ctx);
17717
+      ctx.closePath();
17718
+    },
17719
+  });
17720
+
17721
+  /* _FROM_SVG_START_ */
17722
+  /**
17723
+   * List of attribute names to account for when parsing SVG element (used by `fabric.Polygon.fromElement`)
17724
+   * @static
17725
+   * @memberOf fabric.Polygon
17726
+   * @see: http://www.w3.org/TR/SVG/shapes.html#PolygonElement
17727
+   */
17728
+  fabric.Polygon.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat();
17729
+
17730
+  /**
17731
+   * Returns {@link fabric.Polygon} instance from an SVG element
17732
+   * @static
17733
+   * @memberOf fabric.Polygon
17734
+   * @param {SVGElement} element Element to parse
17735
+   * @param {Function} callback callback function invoked after parsing
17736
+   * @param {Object} [options] Options object
17737
+   */
17738
+  fabric.Polygon.fromElement = function(element, callback, options) {
17739
+    if (!element) {
17740
+      return callback(null);
17741
+    }
17742
+
17743
+    options || (options = { });
17744
+
17745
+    var points = fabric.parsePointsAttribute(element.getAttribute('points')),
17746
+        parsedAttributes = fabric.parseAttributes(element, fabric.Polygon.ATTRIBUTE_NAMES);
17747
+
17748
+    callback(new fabric.Polygon(points, extend(parsedAttributes, options)));
17749
+  };
17750
+  /* _FROM_SVG_END_ */
17751
+
17752
+  /**
17753
+   * Returns fabric.Polygon instance from an object representation
17754
+   * @static
17755
+   * @memberOf fabric.Polygon
17756
+   * @param {Object} object Object to create an instance from
17757
+   * @param {Function} [callback] Callback to invoke when an fabric.Path instance is created
17758
+   */
17759
+  fabric.Polygon.fromObject = function(object, callback) {
17760
+    return fabric.Object._fromObject('Polygon', object, callback, 'points');
17761
+  };
17762
+
17763
+})(typeof exports !== 'undefined' ? exports : this);
17764
+
17765
+
17766
+(function(global) {
17767
+
17768
+  'use strict';
17769
+
17770
+  var fabric = global.fabric || (global.fabric = { }),
17771
+      min = fabric.util.array.min,
17772
+      max = fabric.util.array.max,
17773
+      extend = fabric.util.object.extend,
17774
+      _toString = Object.prototype.toString,
17775
+      drawArc = fabric.util.drawArc,
17776
+      toFixed = fabric.util.toFixed,
17777
+      commandLengths = {
17778
+        m: 2,
17779
+        l: 2,
17780
+        h: 1,
17781
+        v: 1,
17782
+        c: 6,
17783
+        s: 4,
17784
+        q: 4,
17785
+        t: 2,
17786
+        a: 7
17787
+      },
17788
+      repeatedCommands = {
17789
+        m: 'l',
17790
+        M: 'L'
17791
+      };
17792
+
17793
+  if (fabric.Path) {
17794
+    fabric.warn('fabric.Path is already defined');
17795
+    return;
17796
+  }
17797
+
17798
+  /**
17799
+   * Path class
17800
+   * @class fabric.Path
17801
+   * @extends fabric.Object
17802
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-1#path_and_pathgroup}
17803
+   * @see {@link fabric.Path#initialize} for constructor definition
17804
+   */
17805
+  fabric.Path = fabric.util.createClass(fabric.Object, /** @lends fabric.Path.prototype */ {
17806
+
17807
+    /**
17808
+     * Type of an object
17809
+     * @type String
17810
+     * @default
17811
+     */
17812
+    type: 'path',
17813
+
17814
+    /**
17815
+     * Array of path points
17816
+     * @type Array
17817
+     * @default
17818
+     */
17819
+    path: null,
17820
+
17821
+    cacheProperties: fabric.Object.prototype.cacheProperties.concat('path', 'fillRule'),
17822
+
17823
+    stateProperties: fabric.Object.prototype.stateProperties.concat('path'),
17824
+
17825
+    /**
17826
+     * Constructor
17827
+     * @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
17828
+     * @param {Object} [options] Options object
17829
+     * @return {fabric.Path} thisArg
17830
+     */
17831
+    initialize: function(path, options) {
17832
+      options = options || { };
17833
+      this.callSuper('initialize', options);
17834
+
17835
+      if (!path) {
17836
+        path = [];
17837
+      }
17838
+
17839
+      var fromArray = _toString.call(path) === '[object Array]';
17840
+
17841
+      this.path = fromArray
17842
+        ? path
17843
+        // one of commands (m,M,l,L,q,Q,c,C,etc.) followed by non-command characters (i.e. command values)
17844
+        : path.match && path.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi);
17845
+
17846
+      if (!this.path) {
17847
+        return;
17848
+      }
17849
+
17850
+      if (!fromArray) {
17851
+        this.path = this._parsePath();
17852
+      }
17853
+
17854
+      this._setPositionDimensions(options);
17855
+    },
17856
+
17857
+    /**
17858
+     * @private
17859
+     * @param {Object} options Options object
17860
+     */
17861
+    _setPositionDimensions: function(options) {
17862
+      var calcDim = this._parseDimensions();
17863
+
17864
+      this.width = calcDim.width;
17865
+      this.height = calcDim.height;
17866
+
17867
+      if (typeof options.left === 'undefined') {
17868
+        this.left = calcDim.left;
17869
+      }
17870
+
17871
+      if (typeof options.top === 'undefined') {
17872
+        this.top = calcDim.top;
17873
+      }
17874
+
17875
+      this.pathOffset = this.pathOffset || {
17876
+        x: calcDim.left + this.width / 2,
17877
+        y: calcDim.top + this.height / 2
17878
+      };
17879
+    },
17880
+
17881
+    /**
17882
+     * @private
17883
+     * @param {CanvasRenderingContext2D} ctx context to render path on
17884
+     */
17885
+    _renderPathCommands: function(ctx) {
17886
+      var current, // current instruction
17887
+          previous = null,
17888
+          subpathStartX = 0,
17889
+          subpathStartY = 0,
17890
+          x = 0, // current x
17891
+          y = 0, // current y
17892
+          controlX = 0, // current control point x
17893
+          controlY = 0, // current control point y
17894
+          tempX,
17895
+          tempY,
17896
+          l = -this.pathOffset.x,
17897
+          t = -this.pathOffset.y;
17898
+
17899
+      ctx.beginPath();
17900
+
17901
+      for (var i = 0, len = this.path.length; i < len; ++i) {
17902
+
17903
+        current = this.path[i];
17904
+
17905
+        switch (current[0]) { // first letter
17906
+
17907
+          case 'l': // lineto, relative
17908
+            x += current[1];
17909
+            y += current[2];
17910
+            ctx.lineTo(x + l, y + t);
17911
+            break;
17912
+
17913
+          case 'L': // lineto, absolute
17914
+            x = current[1];
17915
+            y = current[2];
17916
+            ctx.lineTo(x + l, y + t);
17917
+            break;
17918
+
17919
+          case 'h': // horizontal lineto, relative
17920
+            x += current[1];
17921
+            ctx.lineTo(x + l, y + t);
17922
+            break;
17923
+
17924
+          case 'H': // horizontal lineto, absolute
17925
+            x = current[1];
17926
+            ctx.lineTo(x + l, y + t);
17927
+            break;
17928
+
17929
+          case 'v': // vertical lineto, relative
17930
+            y += current[1];
17931
+            ctx.lineTo(x + l, y + t);
17932
+            break;
17933
+
17934
+          case 'V': // verical lineto, absolute
17935
+            y = current[1];
17936
+            ctx.lineTo(x + l, y + t);
17937
+            break;
17938
+
17939
+          case 'm': // moveTo, relative
17940
+            x += current[1];
17941
+            y += current[2];
17942
+            subpathStartX = x;
17943
+            subpathStartY = y;
17944
+            ctx.moveTo(x + l, y + t);
17945
+            break;
17946
+
17947
+          case 'M': // moveTo, absolute
17948
+            x = current[1];
17949
+            y = current[2];
17950
+            subpathStartX = x;
17951
+            subpathStartY = y;
17952
+            ctx.moveTo(x + l, y + t);
17953
+            break;
17954
+
17955
+          case 'c': // bezierCurveTo, relative
17956
+            tempX = x + current[5];
17957
+            tempY = y + current[6];
17958
+            controlX = x + current[3];
17959
+            controlY = y + current[4];
17960
+            ctx.bezierCurveTo(
17961
+              x + current[1] + l, // x1
17962
+              y + current[2] + t, // y1
17963
+              controlX + l, // x2
17964
+              controlY + t, // y2
17965
+              tempX + l,
17966
+              tempY + t
17967
+            );
17968
+            x = tempX;
17969
+            y = tempY;
17970
+            break;
17971
+
17972
+          case 'C': // bezierCurveTo, absolute
17973
+            x = current[5];
17974
+            y = current[6];
17975
+            controlX = current[3];
17976
+            controlY = current[4];
17977
+            ctx.bezierCurveTo(
17978
+              current[1] + l,
17979
+              current[2] + t,
17980
+              controlX + l,
17981
+              controlY + t,
17982
+              x + l,
17983
+              y + t
17984
+            );
17985
+            break;
17986
+
17987
+          case 's': // shorthand cubic bezierCurveTo, relative
17988
+
17989
+            // transform to absolute x,y
17990
+            tempX = x + current[3];
17991
+            tempY = y + current[4];
17992
+
17993
+            if (previous[0].match(/[CcSs]/) === null) {
17994
+              // If there is no previous command or if the previous command was not a C, c, S, or s,
17995
+              // the control point is coincident with the current point
17996
+              controlX = x;
17997
+              controlY = y;
17998
+            }
17999
+            else {
18000
+              // calculate reflection of previous control points
18001
+              controlX = 2 * x - controlX;
18002
+              controlY = 2 * y - controlY;
18003
+            }
18004
+
18005
+            ctx.bezierCurveTo(
18006
+              controlX + l,
18007
+              controlY + t,
18008
+              x + current[1] + l,
18009
+              y + current[2] + t,
18010
+              tempX + l,
18011
+              tempY + t
18012
+            );
18013
+            // set control point to 2nd one of this command
18014
+            // "... the first control point is assumed to be
18015
+            // the reflection of the second control point on
18016
+            // the previous command relative to the current point."
18017
+            controlX = x + current[1];
18018
+            controlY = y + current[2];
18019
+
18020
+            x = tempX;
18021
+            y = tempY;
18022
+            break;
18023
+
18024
+          case 'S': // shorthand cubic bezierCurveTo, absolute
18025
+            tempX = current[3];
18026
+            tempY = current[4];
18027
+            if (previous[0].match(/[CcSs]/) === null) {
18028
+              // If there is no previous command or if the previous command was not a C, c, S, or s,
18029
+              // the control point is coincident with the current point
18030
+              controlX = x;
18031
+              controlY = y;
18032
+            }
18033
+            else {
18034
+              // calculate reflection of previous control points
18035
+              controlX = 2 * x - controlX;
18036
+              controlY = 2 * y - controlY;
18037
+            }
18038
+            ctx.bezierCurveTo(
18039
+              controlX + l,
18040
+              controlY + t,
18041
+              current[1] + l,
18042
+              current[2] + t,
18043
+              tempX + l,
18044
+              tempY + t
18045
+            );
18046
+            x = tempX;
18047
+            y = tempY;
18048
+
18049
+            // set control point to 2nd one of this command
18050
+            // "... the first control point is assumed to be
18051
+            // the reflection of the second control point on
18052
+            // the previous command relative to the current point."
18053
+            controlX = current[1];
18054
+            controlY = current[2];
18055
+
18056
+            break;
18057
+
18058
+          case 'q': // quadraticCurveTo, relative
18059
+            // transform to absolute x,y
18060
+            tempX = x + current[3];
18061
+            tempY = y + current[4];
18062
+
18063
+            controlX = x + current[1];
18064
+            controlY = y + current[2];
18065
+
18066
+            ctx.quadraticCurveTo(
18067
+              controlX + l,
18068
+              controlY + t,
18069
+              tempX + l,
18070
+              tempY + t
18071
+            );
18072
+            x = tempX;
18073
+            y = tempY;
18074
+            break;
18075
+
18076
+          case 'Q': // quadraticCurveTo, absolute
18077
+            tempX = current[3];
18078
+            tempY = current[4];
18079
+
18080
+            ctx.quadraticCurveTo(
18081
+              current[1] + l,
18082
+              current[2] + t,
18083
+              tempX + l,
18084
+              tempY + t
18085
+            );
18086
+            x = tempX;
18087
+            y = tempY;
18088
+            controlX = current[1];
18089
+            controlY = current[2];
18090
+            break;
18091
+
18092
+          case 't': // shorthand quadraticCurveTo, relative
18093
+
18094
+            // transform to absolute x,y
18095
+            tempX = x + current[1];
18096
+            tempY = y + current[2];
18097
+
18098
+            if (previous[0].match(/[QqTt]/) === null) {
18099
+              // If there is no previous command or if the previous command was not a Q, q, T or t,
18100
+              // assume the control point is coincident with the current point
18101
+              controlX = x;
18102
+              controlY = y;
18103
+            }
18104
+            else {
18105
+              // calculate reflection of previous control point
18106
+              controlX = 2 * x - controlX;
18107
+              controlY = 2 * y - controlY;
18108
+            }
18109
+
18110
+            ctx.quadraticCurveTo(
18111
+              controlX + l,
18112
+              controlY + t,
18113
+              tempX + l,
18114
+              tempY + t
18115
+            );
18116
+            x = tempX;
18117
+            y = tempY;
18118
+
18119
+            break;
18120
+
18121
+          case 'T':
18122
+            tempX = current[1];
18123
+            tempY = current[2];
18124
+
18125
+            if (previous[0].match(/[QqTt]/) === null) {
18126
+              // If there is no previous command or if the previous command was not a Q, q, T or t,
18127
+              // assume the control point is coincident with the current point
18128
+              controlX = x;
18129
+              controlY = y;
18130
+            }
18131
+            else {
18132
+              // calculate reflection of previous control point
18133
+              controlX = 2 * x - controlX;
18134
+              controlY = 2 * y - controlY;
18135
+            }
18136
+            ctx.quadraticCurveTo(
18137
+              controlX + l,
18138
+              controlY + t,
18139
+              tempX + l,
18140
+              tempY + t
18141
+            );
18142
+            x = tempX;
18143
+            y = tempY;
18144
+            break;
18145
+
18146
+          case 'a':
18147
+            // TODO: optimize this
18148
+            drawArc(ctx, x + l, y + t, [
18149
+              current[1],
18150
+              current[2],
18151
+              current[3],
18152
+              current[4],
18153
+              current[5],
18154
+              current[6] + x + l,
18155
+              current[7] + y + t
18156
+            ]);
18157
+            x += current[6];
18158
+            y += current[7];
18159
+            break;
18160
+
18161
+          case 'A':
18162
+            // TODO: optimize this
18163
+            drawArc(ctx, x + l, y + t, [
18164
+              current[1],
18165
+              current[2],
18166
+              current[3],
18167
+              current[4],
18168
+              current[5],
18169
+              current[6] + l,
18170
+              current[7] + t
18171
+            ]);
18172
+            x = current[6];
18173
+            y = current[7];
18174
+            break;
18175
+
18176
+          case 'z':
18177
+          case 'Z':
18178
+            x = subpathStartX;
18179
+            y = subpathStartY;
18180
+            ctx.closePath();
18181
+            break;
18182
+        }
18183
+        previous = current;
18184
+      }
18185
+    },
18186
+
18187
+    /**
18188
+     * @private
18189
+     * @param {CanvasRenderingContext2D} ctx context to render path on
18190
+     */
18191
+    _render: function(ctx) {
18192
+      this._renderPathCommands(ctx);
18193
+      this._renderPaintInOrder(ctx);
18194
+    },
18195
+
18196
+    /**
18197
+     * Returns string representation of an instance
18198
+     * @return {String} string representation of an instance
18199
+     */
18200
+    toString: function() {
18201
+      return '#<fabric.Path (' + this.complexity() +
18202
+        '): { "top": ' + this.top + ', "left": ' + this.left + ' }>';
18203
+    },
18204
+
18205
+    /**
18206
+     * Returns object representation of an instance
18207
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
18208
+     * @return {Object} object representation of an instance
18209
+     */
18210
+    toObject: function(propertiesToInclude) {
18211
+      var o = extend(this.callSuper('toObject', propertiesToInclude), {
18212
+        path: this.path.map(function(item) { return item.slice(); }),
18213
+        top: this.top,
18214
+        left: this.left,
18215
+      });
18216
+      return o;
18217
+    },
18218
+
18219
+    /**
18220
+     * Returns dataless object representation of an instance
18221
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
18222
+     * @return {Object} object representation of an instance
18223
+     */
18224
+    toDatalessObject: function(propertiesToInclude) {
18225
+      var o = this.toObject(['sourcePath'].concat(propertiesToInclude));
18226
+      if (o.sourcePath) {
18227
+        delete o.path;
18228
+      }
18229
+      return o;
18230
+    },
18231
+
18232
+    /* _TO_SVG_START_ */
18233
+    /**
18234
+     * Returns svg representation of an instance
18235
+     * @return {Array} an array of strings with the specific svg representation
18236
+     * of the instance
18237
+     */
18238
+    _toSVG: function() {
18239
+      var path = this.path.map(function(path) {
18240
+        return path.join(' ');
18241
+      }).join(' ');
18242
+      return [
18243
+        '<path ', 'COMMON_PARTS',
18244
+        'd="', path,
18245
+        '" stroke-linecap="round" ',
18246
+        '/>\n'
18247
+      ];
18248
+    },
18249
+
18250
+    _getOffsetTransform: function() {
18251
+      var digits = fabric.Object.NUM_FRACTION_DIGITS;
18252
+      return ' translate(' + toFixed(-this.pathOffset.x, digits) + ', ' +
18253
+          toFixed(-this.pathOffset.y, digits) + ')';
18254
+    },
18255
+
18256
+    /**
18257
+     * Returns svg clipPath representation of an instance
18258
+     * @param {Function} [reviver] Method for further parsing of svg representation.
18259
+     * @return {String} svg representation of an instance
18260
+     */
18261
+    toClipPathSVG: function(reviver) {
18262
+      var additionalTransform = this._getOffsetTransform();
18263
+      return '\t' + this._createBaseClipPathSVGMarkup(
18264
+        this._toSVG(), { reviver: reviver, additionalTransform: additionalTransform }
18265
+      );
18266
+    },
18267
+
18268
+    /**
18269
+     * Returns svg representation of an instance
18270
+     * @param {Function} [reviver] Method for further parsing of svg representation.
18271
+     * @return {String} svg representation of an instance
18272
+     */
18273
+    toSVG: function(reviver) {
18274
+      var additionalTransform = this._getOffsetTransform();
18275
+      return this._createBaseSVGMarkup(this._toSVG(), { reviver: reviver, additionalTransform: additionalTransform  });
18276
+    },
18277
+    /* _TO_SVG_END_ */
18278
+
18279
+    /**
18280
+     * Returns number representation of an instance complexity
18281
+     * @return {Number} complexity of this instance
18282
+     */
18283
+    complexity: function() {
18284
+      return this.path.length;
18285
+    },
18286
+
18287
+    /**
18288
+     * @private
18289
+     */
18290
+    _parsePath: function() {
18291
+      var result = [],
18292
+          coords = [],
18293
+          currentPath,
18294
+          parsed,
18295
+          re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig,
18296
+          match,
18297
+          coordsStr;
18298
+
18299
+      for (var i = 0, coordsParsed, len = this.path.length; i < len; i++) {
18300
+        currentPath = this.path[i];
18301
+
18302
+        coordsStr = currentPath.slice(1).trim();
18303
+        coords.length = 0;
18304
+
18305
+        while ((match = re.exec(coordsStr))) {
18306
+          coords.push(match[0]);
18307
+        }
18308
+
18309
+        coordsParsed = [currentPath.charAt(0)];
18310
+
18311
+        for (var j = 0, jlen = coords.length; j < jlen; j++) {
18312
+          parsed = parseFloat(coords[j]);
18313
+          if (!isNaN(parsed)) {
18314
+            coordsParsed.push(parsed);
18315
+          }
18316
+        }
18317
+
18318
+        var command = coordsParsed[0],
18319
+            commandLength = commandLengths[command.toLowerCase()],
18320
+            repeatedCommand = repeatedCommands[command] || command;
18321
+
18322
+        if (coordsParsed.length - 1 > commandLength) {
18323
+          for (var k = 1, klen = coordsParsed.length; k < klen; k += commandLength) {
18324
+            result.push([command].concat(coordsParsed.slice(k, k + commandLength)));
18325
+            command = repeatedCommand;
18326
+          }
18327
+        }
18328
+        else {
18329
+          result.push(coordsParsed);
18330
+        }
18331
+      }
18332
+
18333
+      return result;
18334
+    },
18335
+
18336
+    /**
18337
+     * @private
18338
+     */
18339
+    _parseDimensions: function() {
18340
+
18341
+      var aX = [],
18342
+          aY = [],
18343
+          current, // current instruction
18344
+          previous = null,
18345
+          subpathStartX = 0,
18346
+          subpathStartY = 0,
18347
+          x = 0, // current x
18348
+          y = 0, // current y
18349
+          controlX = 0, // current control point x
18350
+          controlY = 0, // current control point y
18351
+          tempX,
18352
+          tempY,
18353
+          bounds;
18354
+
18355
+      for (var i = 0, len = this.path.length; i < len; ++i) {
18356
+
18357
+        current = this.path[i];
18358
+
18359
+        switch (current[0]) { // first letter
18360
+
18361
+          case 'l': // lineto, relative
18362
+            x += current[1];
18363
+            y += current[2];
18364
+            bounds = [];
18365
+            break;
18366
+
18367
+          case 'L': // lineto, absolute
18368
+            x = current[1];
18369
+            y = current[2];
18370
+            bounds = [];
18371
+            break;
18372
+
18373
+          case 'h': // horizontal lineto, relative
18374
+            x += current[1];
18375
+            bounds = [];
18376
+            break;
18377
+
18378
+          case 'H': // horizontal lineto, absolute
18379
+            x = current[1];
18380
+            bounds = [];
18381
+            break;
18382
+
18383
+          case 'v': // vertical lineto, relative
18384
+            y += current[1];
18385
+            bounds = [];
18386
+            break;
18387
+
18388
+          case 'V': // verical lineto, absolute
18389
+            y = current[1];
18390
+            bounds = [];
18391
+            break;
18392
+
18393
+          case 'm': // moveTo, relative
18394
+            x += current[1];
18395
+            y += current[2];
18396
+            subpathStartX = x;
18397
+            subpathStartY = y;
18398
+            bounds = [];
18399
+            break;
18400
+
18401
+          case 'M': // moveTo, absolute
18402
+            x = current[1];
18403
+            y = current[2];
18404
+            subpathStartX = x;
18405
+            subpathStartY = y;
18406
+            bounds = [];
18407
+            break;
18408
+
18409
+          case 'c': // bezierCurveTo, relative
18410
+            tempX = x + current[5];
18411
+            tempY = y + current[6];
18412
+            controlX = x + current[3];
18413
+            controlY = y + current[4];
18414
+            bounds = fabric.util.getBoundsOfCurve(x, y,
18415
+              x + current[1], // x1
18416
+              y + current[2], // y1
18417
+              controlX, // x2
18418
+              controlY, // y2
18419
+              tempX,
18420
+              tempY
18421
+            );
18422
+            x = tempX;
18423
+            y = tempY;
18424
+            break;
18425
+
18426
+          case 'C': // bezierCurveTo, absolute
18427
+            controlX = current[3];
18428
+            controlY = current[4];
18429
+            bounds = fabric.util.getBoundsOfCurve(x, y,
18430
+              current[1],
18431
+              current[2],
18432
+              controlX,
18433
+              controlY,
18434
+              current[5],
18435
+              current[6]
18436
+            );
18437
+            x = current[5];
18438
+            y = current[6];
18439
+            break;
18440
+
18441
+          case 's': // shorthand cubic bezierCurveTo, relative
18442
+
18443
+            // transform to absolute x,y
18444
+            tempX = x + current[3];
18445
+            tempY = y + current[4];
18446
+
18447
+            if (previous[0].match(/[CcSs]/) === null) {
18448
+              // If there is no previous command or if the previous command was not a C, c, S, or s,
18449
+              // the control point is coincident with the current point
18450
+              controlX = x;
18451
+              controlY = y;
18452
+            }
18453
+            else {
18454
+              // calculate reflection of previous control points
18455
+              controlX = 2 * x - controlX;
18456
+              controlY = 2 * y - controlY;
18457
+            }
18458
+
18459
+            bounds = fabric.util.getBoundsOfCurve(x, y,
18460
+              controlX,
18461
+              controlY,
18462
+              x + current[1],
18463
+              y + current[2],
18464
+              tempX,
18465
+              tempY
18466
+            );
18467
+            // set control point to 2nd one of this command
18468
+            // "... the first control point is assumed to be
18469
+            // the reflection of the second control point on
18470
+            // the previous command relative to the current point."
18471
+            controlX = x + current[1];
18472
+            controlY = y + current[2];
18473
+            x = tempX;
18474
+            y = tempY;
18475
+            break;
18476
+
18477
+          case 'S': // shorthand cubic bezierCurveTo, absolute
18478
+            tempX = current[3];
18479
+            tempY = current[4];
18480
+            if (previous[0].match(/[CcSs]/) === null) {
18481
+              // If there is no previous command or if the previous command was not a C, c, S, or s,
18482
+              // the control point is coincident with the current point
18483
+              controlX = x;
18484
+              controlY = y;
18485
+            }
18486
+            else {
18487
+              // calculate reflection of previous control points
18488
+              controlX = 2 * x - controlX;
18489
+              controlY = 2 * y - controlY;
18490
+            }
18491
+            bounds = fabric.util.getBoundsOfCurve(x, y,
18492
+              controlX,
18493
+              controlY,
18494
+              current[1],
18495
+              current[2],
18496
+              tempX,
18497
+              tempY
18498
+            );
18499
+            x = tempX;
18500
+            y = tempY;
18501
+            // set control point to 2nd one of this command
18502
+            // "... the first control point is assumed to be
18503
+            // the reflection of the second control point on
18504
+            // the previous command relative to the current point."
18505
+            controlX = current[1];
18506
+            controlY = current[2];
18507
+            break;
18508
+
18509
+          case 'q': // quadraticCurveTo, relative
18510
+            // transform to absolute x,y
18511
+            tempX = x + current[3];
18512
+            tempY = y + current[4];
18513
+            controlX = x + current[1];
18514
+            controlY = y + current[2];
18515
+            bounds = fabric.util.getBoundsOfCurve(x, y,
18516
+              controlX,
18517
+              controlY,
18518
+              controlX,
18519
+              controlY,
18520
+              tempX,
18521
+              tempY
18522
+            );
18523
+            x = tempX;
18524
+            y = tempY;
18525
+            break;
18526
+
18527
+          case 'Q': // quadraticCurveTo, absolute
18528
+            controlX = current[1];
18529
+            controlY = current[2];
18530
+            bounds = fabric.util.getBoundsOfCurve(x, y,
18531
+              controlX,
18532
+              controlY,
18533
+              controlX,
18534
+              controlY,
18535
+              current[3],
18536
+              current[4]
18537
+            );
18538
+            x = current[3];
18539
+            y = current[4];
18540
+            break;
18541
+
18542
+          case 't': // shorthand quadraticCurveTo, relative
18543
+            // transform to absolute x,y
18544
+            tempX = x + current[1];
18545
+            tempY = y + current[2];
18546
+            if (previous[0].match(/[QqTt]/) === null) {
18547
+              // If there is no previous command or if the previous command was not a Q, q, T or t,
18548
+              // assume the control point is coincident with the current point
18549
+              controlX = x;
18550
+              controlY = y;
18551
+            }
18552
+            else {
18553
+              // calculate reflection of previous control point
18554
+              controlX = 2 * x - controlX;
18555
+              controlY = 2 * y - controlY;
18556
+            }
18557
+
18558
+            bounds = fabric.util.getBoundsOfCurve(x, y,
18559
+              controlX,
18560
+              controlY,
18561
+              controlX,
18562
+              controlY,
18563
+              tempX,
18564
+              tempY
18565
+            );
18566
+            x = tempX;
18567
+            y = tempY;
18568
+
18569
+            break;
18570
+
18571
+          case 'T':
18572
+            tempX = current[1];
18573
+            tempY = current[2];
18574
+
18575
+            if (previous[0].match(/[QqTt]/) === null) {
18576
+              // If there is no previous command or if the previous command was not a Q, q, T or t,
18577
+              // assume the control point is coincident with the current point
18578
+              controlX = x;
18579
+              controlY = y;
18580
+            }
18581
+            else {
18582
+              // calculate reflection of previous control point
18583
+              controlX = 2 * x - controlX;
18584
+              controlY = 2 * y - controlY;
18585
+            }
18586
+            bounds = fabric.util.getBoundsOfCurve(x, y,
18587
+              controlX,
18588
+              controlY,
18589
+              controlX,
18590
+              controlY,
18591
+              tempX,
18592
+              tempY
18593
+            );
18594
+            x = tempX;
18595
+            y = tempY;
18596
+            break;
18597
+
18598
+          case 'a':
18599
+            // TODO: optimize this
18600
+            bounds = fabric.util.getBoundsOfArc(x, y,
18601
+              current[1],
18602
+              current[2],
18603
+              current[3],
18604
+              current[4],
18605
+              current[5],
18606
+              current[6] + x,
18607
+              current[7] + y
18608
+            );
18609
+            x += current[6];
18610
+            y += current[7];
18611
+            break;
18612
+
18613
+          case 'A':
18614
+            // TODO: optimize this
18615
+            bounds = fabric.util.getBoundsOfArc(x, y,
18616
+              current[1],
18617
+              current[2],
18618
+              current[3],
18619
+              current[4],
18620
+              current[5],
18621
+              current[6],
18622
+              current[7]
18623
+            );
18624
+            x = current[6];
18625
+            y = current[7];
18626
+            break;
18627
+
18628
+          case 'z':
18629
+          case 'Z':
18630
+            x = subpathStartX;
18631
+            y = subpathStartY;
18632
+            break;
18633
+        }
18634
+        previous = current;
18635
+        bounds.forEach(function (point) {
18636
+          aX.push(point.x);
18637
+          aY.push(point.y);
18638
+        });
18639
+        aX.push(x);
18640
+        aY.push(y);
18641
+      }
18642
+
18643
+      var minX = min(aX) || 0,
18644
+          minY = min(aY) || 0,
18645
+          maxX = max(aX) || 0,
18646
+          maxY = max(aY) || 0,
18647
+          deltaX = maxX - minX,
18648
+          deltaY = maxY - minY,
18649
+
18650
+          o = {
18651
+            left: minX,
18652
+            top: minY,
18653
+            width: deltaX,
18654
+            height: deltaY
18655
+          };
18656
+
18657
+      return o;
18658
+    }
18659
+  });
18660
+
18661
+  /**
18662
+   * Creates an instance of fabric.Path from an object
18663
+   * @static
18664
+   * @memberOf fabric.Path
18665
+   * @param {Object} object
18666
+   * @param {Function} [callback] Callback to invoke when an fabric.Path instance is created
18667
+   */
18668
+  fabric.Path.fromObject = function(object, callback) {
18669
+    if (typeof object.sourcePath === 'string') {
18670
+      var pathUrl = object.sourcePath;
18671
+      fabric.loadSVGFromURL(pathUrl, function (elements) {
18672
+        var path = elements[0];
18673
+        path.setOptions(object);
18674
+        callback && callback(path);
18675
+      });
18676
+    }
18677
+    else {
18678
+      fabric.Object._fromObject('Path', object, callback, 'path');
18679
+    }
18680
+  };
18681
+
18682
+  /* _FROM_SVG_START_ */
18683
+  /**
18684
+   * List of attribute names to account for when parsing SVG element (used by `fabric.Path.fromElement`)
18685
+   * @static
18686
+   * @memberOf fabric.Path
18687
+   * @see http://www.w3.org/TR/SVG/paths.html#PathElement
18688
+   */
18689
+  fabric.Path.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(['d']);
18690
+
18691
+  /**
18692
+   * Creates an instance of fabric.Path from an SVG <path> element
18693
+   * @static
18694
+   * @memberOf fabric.Path
18695
+   * @param {SVGElement} element to parse
18696
+   * @param {Function} callback Callback to invoke when an fabric.Path instance is created
18697
+   * @param {Object} [options] Options object
18698
+   * @param {Function} [callback] Options callback invoked after parsing is finished
18699
+   */
18700
+  fabric.Path.fromElement = function(element, callback, options) {
18701
+    var parsedAttributes = fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES);
18702
+    callback(new fabric.Path(parsedAttributes.d, extend(parsedAttributes, options)));
18703
+  };
18704
+  /* _FROM_SVG_END_ */
18705
+
18706
+})(typeof exports !== 'undefined' ? exports : this);
18707
+
18708
+
18709
+(function(global) {
18710
+
18711
+  'use strict';
18712
+
18713
+  var fabric = global.fabric || (global.fabric = { }),
18714
+      min = fabric.util.array.min,
18715
+      max = fabric.util.array.max;
18716
+
18717
+  if (fabric.Group) {
18718
+    return;
18719
+  }
18720
+
18721
+  /**
18722
+   * Group class
18723
+   * @class fabric.Group
18724
+   * @extends fabric.Object
18725
+   * @mixes fabric.Collection
18726
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#groups}
18727
+   * @see {@link fabric.Group#initialize} for constructor definition
18728
+   */
18729
+  fabric.Group = fabric.util.createClass(fabric.Object, fabric.Collection, /** @lends fabric.Group.prototype */ {
18730
+
18731
+    /**
18732
+     * Type of an object
18733
+     * @type String
18734
+     * @default
18735
+     */
18736
+    type: 'group',
18737
+
18738
+    /**
18739
+     * Width of stroke
18740
+     * @type Number
18741
+     * @default
18742
+     */
18743
+    strokeWidth: 0,
18744
+
18745
+    /**
18746
+     * Indicates if click events should also check for subtargets
18747
+     * @type Boolean
18748
+     * @default
18749
+     */
18750
+    subTargetCheck: false,
18751
+
18752
+    /**
18753
+     * Groups are container, do not render anything on theyr own, ence no cache properties
18754
+     * @type Array
18755
+     * @default
18756
+     */
18757
+    cacheProperties: [],
18758
+
18759
+    /**
18760
+     * setOnGroup is a method used for TextBox that is no more used since 2.0.0 The behavior is still
18761
+     * available setting this boolean to true.
18762
+     * @type Boolean
18763
+     * @since 2.0.0
18764
+     * @default
18765
+     */
18766
+    useSetOnGroup: false,
18767
+
18768
+    /**
18769
+     * Constructor
18770
+     * @param {Object} objects Group objects
18771
+     * @param {Object} [options] Options object
18772
+     * @param {Boolean} [isAlreadyGrouped] if true, objects have been grouped already.
18773
+     * @return {Object} thisArg
18774
+     */
18775
+    initialize: function(objects, options, isAlreadyGrouped) {
18776
+      options = options || {};
18777
+      this._objects = [];
18778
+      // if objects enclosed in a group have been grouped already,
18779
+      // we cannot change properties of objects.
18780
+      // Thus we need to set options to group without objects,
18781
+      isAlreadyGrouped && this.callSuper('initialize', options);
18782
+      this._objects = objects || [];
18783
+      for (var i = this._objects.length; i--; ) {
18784
+        this._objects[i].group = this;
18785
+      }
18786
+
18787
+      if (!isAlreadyGrouped) {
18788
+        var center = options && options.centerPoint;
18789
+        // we want to set origins before calculating the bounding box.
18790
+        // so that the topleft can be set with that in mind.
18791
+        // if specific top and left are passed, are overwritten later
18792
+        // with the callSuper('initialize', options)
18793
+        if (options.originX !== undefined) {
18794
+          this.originX = options.originX;
18795
+        }
18796
+        if (options.originY !== undefined) {
18797
+          this.originY = options.originY;
18798
+        }
18799
+        // if coming from svg i do not want to calc bounds.
18800
+        // i assume width and height are passed along options
18801
+        center || this._calcBounds();
18802
+        this._updateObjectsCoords(center);
18803
+        delete options.centerPoint;
18804
+        this.callSuper('initialize', options);
18805
+      }
18806
+      else {
18807
+        this._updateObjectsACoords();
18808
+      }
18809
+
18810
+      this.setCoords();
18811
+    },
18812
+
18813
+    /**
18814
+     * @private
18815
+     * @param {Boolean} [skipCoordsChange] if true, coordinates of objects enclosed in a group do not change
18816
+     */
18817
+    _updateObjectsACoords: function() {
18818
+      var ignoreZoom = true, skipAbsolute = true;
18819
+      for (var i = this._objects.length; i--; ){
18820
+        this._objects[i].setCoords(ignoreZoom, skipAbsolute);
18821
+      }
18822
+    },
18823
+
18824
+    /**
18825
+     * @private
18826
+     * @param {Boolean} [skipCoordsChange] if true, coordinates of objects enclosed in a group do not change
18827
+     */
18828
+    _updateObjectsCoords: function(center) {
18829
+      var center = center || this.getCenterPoint();
18830
+      for (var i = this._objects.length; i--; ){
18831
+        this._updateObjectCoords(this._objects[i], center);
18832
+      }
18833
+    },
18834
+
18835
+    /**
18836
+     * @private
18837
+     * @param {Object} object
18838
+     * @param {fabric.Point} center, current center of group.
18839
+     */
18840
+    _updateObjectCoords: function(object, center) {
18841
+      var objectLeft = object.left,
18842
+          objectTop = object.top,
18843
+          ignoreZoom = true, skipAbsolute = true;
18844
+
18845
+      object.set({
18846
+        left: objectLeft - center.x,
18847
+        top: objectTop - center.y
18848
+      });
18849
+      object.group = this;
18850
+      object.setCoords(ignoreZoom, skipAbsolute);
18851
+    },
18852
+
18853
+    /**
18854
+     * Returns string represenation of a group
18855
+     * @return {String}
18856
+     */
18857
+    toString: function() {
18858
+      return '#<fabric.Group: (' + this.complexity() + ')>';
18859
+    },
18860
+
18861
+    /**
18862
+     * Adds an object to a group; Then recalculates group's dimension, position.
18863
+     * @param {Object} object
18864
+     * @return {fabric.Group} thisArg
18865
+     * @chainable
18866
+     */
18867
+    addWithUpdate: function(object) {
18868
+      this._restoreObjectsState();
18869
+      fabric.util.resetObjectTransform(this);
18870
+      if (object) {
18871
+        this._objects.push(object);
18872
+        object.group = this;
18873
+        object._set('canvas', this.canvas);
18874
+      }
18875
+      this._calcBounds();
18876
+      this._updateObjectsCoords();
18877
+      this.setCoords();
18878
+      this.dirty = true;
18879
+      return this;
18880
+    },
18881
+
18882
+    /**
18883
+     * Removes an object from a group; Then recalculates group's dimension, position.
18884
+     * @param {Object} object
18885
+     * @return {fabric.Group} thisArg
18886
+     * @chainable
18887
+     */
18888
+    removeWithUpdate: function(object) {
18889
+      this._restoreObjectsState();
18890
+      fabric.util.resetObjectTransform(this);
18891
+
18892
+      this.remove(object);
18893
+      this._calcBounds();
18894
+      this._updateObjectsCoords();
18895
+      this.setCoords();
18896
+      this.dirty = true;
18897
+      return this;
18898
+    },
18899
+
18900
+    /**
18901
+     * @private
18902
+     */
18903
+    _onObjectAdded: function(object) {
18904
+      this.dirty = true;
18905
+      object.group = this;
18906
+      object._set('canvas', this.canvas);
18907
+    },
18908
+
18909
+    /**
18910
+     * @private
18911
+     */
18912
+    _onObjectRemoved: function(object) {
18913
+      this.dirty = true;
18914
+      delete object.group;
18915
+    },
18916
+
18917
+    /**
18918
+     * @private
18919
+     */
18920
+    _set: function(key, value) {
18921
+      var i = this._objects.length;
18922
+      if (this.useSetOnGroup) {
18923
+        while (i--) {
18924
+          this._objects[i].setOnGroup(key, value);
18925
+        }
18926
+      }
18927
+      if (key === 'canvas') {
18928
+        while (i--) {
18929
+          this._objects[i]._set(key, value);
18930
+        }
18931
+      }
18932
+      fabric.Object.prototype._set.call(this, key, value);
18933
+    },
18934
+
18935
+    /**
18936
+     * Returns object representation of an instance
18937
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
18938
+     * @return {Object} object representation of an instance
18939
+     */
18940
+    toObject: function(propertiesToInclude) {
18941
+      var _includeDefaultValues = this.includeDefaultValues;
18942
+      var objsToObject = this._objects.map(function(obj) {
18943
+        var originalDefaults = obj.includeDefaultValues;
18944
+        obj.includeDefaultValues = _includeDefaultValues;
18945
+        var _obj = obj.toObject(propertiesToInclude);
18946
+        obj.includeDefaultValues = originalDefaults;
18947
+        return _obj;
18948
+      });
18949
+      var obj = fabric.Object.prototype.toObject.call(this, propertiesToInclude);
18950
+      obj.objects = objsToObject;
18951
+      return obj;
18952
+    },
18953
+
18954
+    /**
18955
+     * Returns object representation of an instance, in dataless mode.
18956
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
18957
+     * @return {Object} object representation of an instance
18958
+     */
18959
+    toDatalessObject: function(propertiesToInclude) {
18960
+      var objsToObject, sourcePath = this.sourcePath;
18961
+      if (sourcePath) {
18962
+        objsToObject = sourcePath;
18963
+      }
18964
+      else {
18965
+        var _includeDefaultValues = this.includeDefaultValues;
18966
+        objsToObject = this._objects.map(function(obj) {
18967
+          var originalDefaults = obj.includeDefaultValues;
18968
+          obj.includeDefaultValues = _includeDefaultValues;
18969
+          var _obj = obj.toDatalessObject(propertiesToInclude);
18970
+          obj.includeDefaultValues = originalDefaults;
18971
+          return _obj;
18972
+        });
18973
+      }
18974
+      var obj = fabric.Object.prototype.toDatalessObject.call(this, propertiesToInclude);
18975
+      obj.objects = objsToObject;
18976
+      return obj;
18977
+    },
18978
+
18979
+    /**
18980
+     * Renders instance on a given context
18981
+     * @param {CanvasRenderingContext2D} ctx context to render instance on
18982
+     */
18983
+    render: function(ctx) {
18984
+      this._transformDone = true;
18985
+      this.callSuper('render', ctx);
18986
+      this._transformDone = false;
18987
+    },
18988
+
18989
+    /**
18990
+     * Decide if the object should cache or not. Create its own cache level
18991
+     * objectCaching is a global flag, wins over everything
18992
+     * needsItsOwnCache should be used when the object drawing method requires
18993
+     * a cache step. None of the fabric classes requires it.
18994
+     * Generally you do not cache objects in groups because the group outside is cached.
18995
+     * @return {Boolean}
18996
+     */
18997
+    shouldCache: function() {
18998
+      var ownCache = this.objectCaching && (!this.group || this.needsItsOwnCache() || !this.group.isOnACache());
18999
+      this.ownCaching = ownCache;
19000
+      if (ownCache) {
19001
+        for (var i = 0, len = this._objects.length; i < len; i++) {
19002
+          if (this._objects[i].willDrawShadow()) {
19003
+            this.ownCaching = false;
19004
+            return false;
19005
+          }
19006
+        }
19007
+      }
19008
+      return ownCache;
19009
+    },
19010
+
19011
+    /**
19012
+     * Check if this object or a child object will cast a shadow
19013
+     * @return {Boolean}
19014
+     */
19015
+    willDrawShadow: function() {
19016
+      if (this.shadow) {
19017
+        return fabric.Object.prototype.willDrawShadow.call(this);
19018
+      }
19019
+      for (var i = 0, len = this._objects.length; i < len; i++) {
19020
+        if (this._objects[i].willDrawShadow()) {
19021
+          return true;
19022
+        }
19023
+      }
19024
+      return false;
19025
+    },
19026
+
19027
+    /**
19028
+     * Check if this group or its parent group are caching, recursively up
19029
+     * @return {Boolean}
19030
+     */
19031
+    isOnACache: function() {
19032
+      return this.ownCaching || (this.group && this.group.isOnACache());
19033
+    },
19034
+
19035
+    /**
19036
+     * Execute the drawing operation for an object on a specified context
19037
+     * @param {CanvasRenderingContext2D} ctx Context to render on
19038
+     */
19039
+    drawObject: function(ctx) {
19040
+      for (var i = 0, len = this._objects.length; i < len; i++) {
19041
+        this._objects[i].render(ctx);
19042
+      }
19043
+      this._drawClipPath(ctx);
19044
+    },
19045
+
19046
+    /**
19047
+     * Check if cache is dirty
19048
+     */
19049
+    isCacheDirty: function(skipCanvas) {
19050
+      if (this.callSuper('isCacheDirty', skipCanvas)) {
19051
+        return true;
19052
+      }
19053
+      if (!this.statefullCache) {
19054
+        return false;
19055
+      }
19056
+      for (var i = 0, len = this._objects.length; i < len; i++) {
19057
+        if (this._objects[i].isCacheDirty(true)) {
19058
+          if (this._cacheCanvas) {
19059
+            // if this group has not a cache canvas there is nothing to clean
19060
+            var x = this.cacheWidth / this.zoomX, y = this.cacheHeight / this.zoomY;
19061
+            this._cacheContext.clearRect(-x / 2, -y / 2, x, y);
19062
+          }
19063
+          return true;
19064
+        }
19065
+      }
19066
+      return false;
19067
+    },
19068
+
19069
+    /**
19070
+     * Retores original state of each of group objects (original state is that which was before group was created).
19071
+     * @private
19072
+     * @return {fabric.Group} thisArg
19073
+     * @chainable
19074
+     */
19075
+    _restoreObjectsState: function() {
19076
+      this._objects.forEach(this._restoreObjectState, this);
19077
+      return this;
19078
+    },
19079
+
19080
+    /**
19081
+     * Realises the transform from this group onto the supplied object
19082
+     * i.e. it tells you what would happen if the supplied object was in
19083
+     * the group, and then the group was destroyed. It mutates the supplied
19084
+     * object.
19085
+     * @param {fabric.Object} object
19086
+     * @return {fabric.Object} transformedObject
19087
+     */
19088
+    realizeTransform: function(object) {
19089
+      var matrix = object.calcTransformMatrix(),
19090
+          options = fabric.util.qrDecompose(matrix),
19091
+          center = new fabric.Point(options.translateX, options.translateY);
19092
+      object.flipX = false;
19093
+      object.flipY = false;
19094
+      object.set('scaleX', options.scaleX);
19095
+      object.set('scaleY', options.scaleY);
19096
+      object.skewX = options.skewX;
19097
+      object.skewY = options.skewY;
19098
+      object.angle = options.angle;
19099
+      object.setPositionByOrigin(center, 'center', 'center');
19100
+      return object;
19101
+    },
19102
+
19103
+    /**
19104
+     * Restores original state of a specified object in group
19105
+     * @private
19106
+     * @param {fabric.Object} object
19107
+     * @return {fabric.Group} thisArg
19108
+     */
19109
+    _restoreObjectState: function(object) {
19110
+      this.realizeTransform(object);
19111
+      object.setCoords();
19112
+      delete object.group;
19113
+      return this;
19114
+    },
19115
+
19116
+    /**
19117
+     * Destroys a group (restoring state of its objects)
19118
+     * @return {fabric.Group} thisArg
19119
+     * @chainable
19120
+     */
19121
+    destroy: function() {
19122
+      // when group is destroyed objects needs to get a repaint to be eventually
19123
+      // displayed on canvas.
19124
+      this._objects.forEach(function(object) {
19125
+        object.set('dirty', true);
19126
+      });
19127
+      return this._restoreObjectsState();
19128
+    },
19129
+
19130
+    /**
19131
+     * make a group an active selection, remove the group from canvas
19132
+     * the group has to be on canvas for this to work.
19133
+     * @return {fabric.ActiveSelection} thisArg
19134
+     * @chainable
19135
+     */
19136
+    toActiveSelection: function() {
19137
+      if (!this.canvas) {
19138
+        return;
19139
+      }
19140
+      var objects = this._objects, canvas = this.canvas;
19141
+      this._objects = [];
19142
+      var options = this.toObject();
19143
+      delete options.objects;
19144
+      var activeSelection = new fabric.ActiveSelection([]);
19145
+      activeSelection.set(options);
19146
+      activeSelection.type = 'activeSelection';
19147
+      canvas.remove(this);
19148
+      objects.forEach(function(object) {
19149
+        object.group = activeSelection;
19150
+        object.dirty = true;
19151
+        canvas.add(object);
19152
+      });
19153
+      activeSelection.canvas = canvas;
19154
+      activeSelection._objects = objects;
19155
+      canvas._activeObject = activeSelection;
19156
+      activeSelection.setCoords();
19157
+      return activeSelection;
19158
+    },
19159
+
19160
+    /**
19161
+     * Destroys a group (restoring state of its objects)
19162
+     * @return {fabric.Group} thisArg
19163
+     * @chainable
19164
+     */
19165
+    ungroupOnCanvas: function() {
19166
+      return this._restoreObjectsState();
19167
+    },
19168
+
19169
+    /**
19170
+     * Sets coordinates of all objects inside group
19171
+     * @return {fabric.Group} thisArg
19172
+     * @chainable
19173
+     */
19174
+    setObjectsCoords: function() {
19175
+      var ignoreZoom = true, skipAbsolute = true;
19176
+      this.forEachObject(function(object) {
19177
+        object.setCoords(ignoreZoom, skipAbsolute);
19178
+      });
19179
+      return this;
19180
+    },
19181
+
19182
+    /**
19183
+     * @private
19184
+     */
19185
+    _calcBounds: function(onlyWidthHeight) {
19186
+      var aX = [],
19187
+          aY = [],
19188
+          o, prop,
19189
+          props = ['tr', 'br', 'bl', 'tl'],
19190
+          i = 0, iLen = this._objects.length,
19191
+          j, jLen = props.length,
19192
+          ignoreZoom = true;
19193
+
19194
+      for ( ; i < iLen; ++i) {
19195
+        o = this._objects[i];
19196
+        o.setCoords(ignoreZoom);
19197
+        for (j = 0; j < jLen; j++) {
19198
+          prop = props[j];
19199
+          aX.push(o.oCoords[prop].x);
19200
+          aY.push(o.oCoords[prop].y);
19201
+        }
19202
+      }
19203
+
19204
+      this._getBounds(aX, aY, onlyWidthHeight);
19205
+    },
19206
+
19207
+    /**
19208
+     * @private
19209
+     */
19210
+    _getBounds: function(aX, aY, onlyWidthHeight) {
19211
+      var minXY = new fabric.Point(min(aX), min(aY)),
19212
+          maxXY = new fabric.Point(max(aX), max(aY)),
19213
+          top = minXY.y || 0, left = minXY.x || 0,
19214
+          width = (maxXY.x - minXY.x) || 0,
19215
+          height = (maxXY.y - minXY.y) || 0;
19216
+      this.width = width;
19217
+      this.height = height;
19218
+      if (!onlyWidthHeight) {
19219
+        // the bounding box always finds the topleft most corner.
19220
+        // whatever is the group origin, we set up here the left/top position.
19221
+        this.setPositionByOrigin({ x: left, y: top }, 'left', 'top');
19222
+      }
19223
+    },
19224
+
19225
+    /* _TO_SVG_START_ */
19226
+    /**
19227
+     * Returns svg representation of an instance
19228
+     * @param {Function} [reviver] Method for further parsing of svg representation.
19229
+     * @return {String} svg representation of an instance
19230
+     */
19231
+    toSVG: function(reviver) {
19232
+      var svgString = [];
19233
+
19234
+      for (var i = 0, len = this._objects.length; i < len; i++) {
19235
+        svgString.push('\t', this._objects[i].toSVG(reviver));
19236
+      }
19237
+
19238
+      return this._createBaseSVGMarkup(
19239
+        svgString,
19240
+        { reviver: reviver, noStyle: true, withShadow: true });
19241
+    },
19242
+
19243
+    /**
19244
+     * Returns svg clipPath representation of an instance
19245
+     * @param {Function} [reviver] Method for further parsing of svg representation.
19246
+     * @return {String} svg representation of an instance
19247
+     */
19248
+    toClipPathSVG: function(reviver) {
19249
+      var svgString = [];
19250
+
19251
+      for (var i = 0, len = this._objects.length; i < len; i++) {
19252
+        svgString.push('\t', this._objects[i].toClipPathSVG(reviver));
19253
+      }
19254
+
19255
+      return this._createBaseClipPathSVGMarkup(svgString, { reviver: reviver });
19256
+    },
19257
+    /* _TO_SVG_END_ */
19258
+  });
19259
+
19260
+  /**
19261
+   * Returns {@link fabric.Group} instance from an object representation
19262
+   * @static
19263
+   * @memberOf fabric.Group
19264
+   * @param {Object} object Object to create a group from
19265
+   * @param {Function} [callback] Callback to invoke when an group instance is created
19266
+   */
19267
+  fabric.Group.fromObject = function(object, callback) {
19268
+    fabric.util.enlivenObjects(object.objects, function(enlivenedObjects) {
19269
+      fabric.util.enlivenObjects([object.clipPath], function(enlivedClipPath) {
19270
+        var options = fabric.util.object.clone(object, true);
19271
+        options.clipPath = enlivedClipPath[0];
19272
+        delete options.objects;
19273
+        callback && callback(new fabric.Group(enlivenedObjects, options, true));
19274
+      });
19275
+    });
19276
+  };
19277
+
19278
+})(typeof exports !== 'undefined' ? exports : this);
19279
+
19280
+
19281
+(function(global) {
19282
+
19283
+  'use strict';
19284
+
19285
+  var fabric = global.fabric || (global.fabric = { });
19286
+
19287
+  if (fabric.ActiveSelection) {
19288
+    return;
19289
+  }
19290
+
19291
+  /**
19292
+   * Group class
19293
+   * @class fabric.ActiveSelection
19294
+   * @extends fabric.Group
19295
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-3#groups}
19296
+   * @see {@link fabric.ActiveSelection#initialize} for constructor definition
19297
+   */
19298
+  fabric.ActiveSelection = fabric.util.createClass(fabric.Group, /** @lends fabric.ActiveSelection.prototype */ {
19299
+
19300
+    /**
19301
+     * Type of an object
19302
+     * @type String
19303
+     * @default
19304
+     */
19305
+    type: 'activeSelection',
19306
+
19307
+    /**
19308
+     * Constructor
19309
+     * @param {Object} objects ActiveSelection objects
19310
+     * @param {Object} [options] Options object
19311
+     * @return {Object} thisArg
19312
+     */
19313
+    initialize: function(objects, options) {
19314
+      options = options || {};
19315
+      this._objects = objects || [];
19316
+      for (var i = this._objects.length; i--; ) {
19317
+        this._objects[i].group = this;
19318
+      }
19319
+
19320
+      if (options.originX) {
19321
+        this.originX = options.originX;
19322
+      }
19323
+      if (options.originY) {
19324
+        this.originY = options.originY;
19325
+      }
19326
+      this._calcBounds();
19327
+      this._updateObjectsCoords();
19328
+      fabric.Object.prototype.initialize.call(this, options);
19329
+      this.setCoords();
19330
+    },
19331
+
19332
+    /**
19333
+     * Change te activeSelection to a normal group,
19334
+     * High level function that automatically adds it to canvas as
19335
+     * active object. no events fired.
19336
+     * @since 2.0.0
19337
+     * @return {fabric.Group}
19338
+     */
19339
+    toGroup: function() {
19340
+      var objects = this._objects.concat();
19341
+      this._objects = [];
19342
+      var options = fabric.Object.prototype.toObject.call(this);
19343
+      var newGroup = new fabric.Group([]);
19344
+      delete options.type;
19345
+      newGroup.set(options);
19346
+      objects.forEach(function(object) {
19347
+        object.canvas.remove(object);
19348
+        object.group = newGroup;
19349
+      });
19350
+      newGroup._objects = objects;
19351
+      if (!this.canvas) {
19352
+        return newGroup;
19353
+      }
19354
+      var canvas = this.canvas;
19355
+      canvas.add(newGroup);
19356
+      canvas._activeObject = newGroup;
19357
+      newGroup.setCoords();
19358
+      return newGroup;
19359
+    },
19360
+
19361
+    /**
19362
+     * If returns true, deselection is cancelled.
19363
+     * @since 2.0.0
19364
+     * @return {Boolean} [cancel]
19365
+     */
19366
+    onDeselect: function() {
19367
+      this.destroy();
19368
+      return false;
19369
+    },
19370
+
19371
+    /**
19372
+     * Returns string representation of a group
19373
+     * @return {String}
19374
+     */
19375
+    toString: function() {
19376
+      return '#<fabric.ActiveSelection: (' + this.complexity() + ')>';
19377
+    },
19378
+
19379
+    /**
19380
+     * Decide if the object should cache or not. Create its own cache level
19381
+     * objectCaching is a global flag, wins over everything
19382
+     * needsItsOwnCache should be used when the object drawing method requires
19383
+     * a cache step. None of the fabric classes requires it.
19384
+     * Generally you do not cache objects in groups because the group outside is cached.
19385
+     * @return {Boolean}
19386
+     */
19387
+    shouldCache: function() {
19388
+      return false;
19389
+    },
19390
+
19391
+    /**
19392
+     * Check if this group or its parent group are caching, recursively up
19393
+     * @return {Boolean}
19394
+     */
19395
+    isOnACache: function() {
19396
+      return false;
19397
+    },
19398
+
19399
+    /**
19400
+     * Renders controls and borders for the object
19401
+     * @param {CanvasRenderingContext2D} ctx Context to render on
19402
+     * @param {Object} [styleOverride] properties to override the object style
19403
+     * @param {Object} [childrenOverride] properties to override the children overrides
19404
+     */
19405
+    _renderControls: function(ctx, styleOverride, childrenOverride) {
19406
+      ctx.save();
19407
+      ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
19408
+      this.callSuper('_renderControls', ctx, styleOverride);
19409
+      childrenOverride = childrenOverride || { };
19410
+      if (typeof childrenOverride.hasControls === 'undefined') {
19411
+        childrenOverride.hasControls = false;
19412
+      }
19413
+      if (typeof childrenOverride.hasRotatingPoint === 'undefined') {
19414
+        childrenOverride.hasRotatingPoint = false;
19415
+      }
19416
+      childrenOverride.forActiveSelection = true;
19417
+      for (var i = 0, len = this._objects.length; i < len; i++) {
19418
+        this._objects[i]._renderControls(ctx, childrenOverride);
19419
+      }
19420
+      ctx.restore();
19421
+    },
19422
+  });
19423
+
19424
+  /**
19425
+   * Returns {@link fabric.ActiveSelection} instance from an object representation
19426
+   * @static
19427
+   * @memberOf fabric.ActiveSelection
19428
+   * @param {Object} object Object to create a group from
19429
+   * @param {Function} [callback] Callback to invoke when an ActiveSelection instance is created
19430
+   */
19431
+  fabric.ActiveSelection.fromObject = function(object, callback) {
19432
+    fabric.util.enlivenObjects(object.objects, function(enlivenedObjects) {
19433
+      delete object.objects;
19434
+      callback && callback(new fabric.ActiveSelection(enlivenedObjects, object, true));
19435
+    });
19436
+  };
19437
+
19438
+})(typeof exports !== 'undefined' ? exports : this);
19439
+
19440
+
19441
+(function(global) {
19442
+
19443
+  'use strict';
19444
+
19445
+  var extend = fabric.util.object.extend;
19446
+
19447
+  if (!global.fabric) {
19448
+    global.fabric = { };
19449
+  }
19450
+
19451
+  if (global.fabric.Image) {
19452
+    fabric.warn('fabric.Image is already defined.');
19453
+    return;
19454
+  }
19455
+
19456
+  /**
19457
+   * Image class
19458
+   * @class fabric.Image
19459
+   * @extends fabric.Object
19460
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-1#images}
19461
+   * @see {@link fabric.Image#initialize} for constructor definition
19462
+   */
19463
+  fabric.Image = fabric.util.createClass(fabric.Object, /** @lends fabric.Image.prototype */ {
19464
+
19465
+    /**
19466
+     * Type of an object
19467
+     * @type String
19468
+     * @default
19469
+     */
19470
+    type: 'image',
19471
+
19472
+    /**
19473
+     * crossOrigin value (one of "", "anonymous", "use-credentials")
19474
+     * @see https://developer.mozilla.org/en-US/docs/HTML/CORS_settings_attributes
19475
+     * @type String
19476
+     * @default
19477
+     */
19478
+    crossOrigin: '',
19479
+
19480
+    /**
19481
+     * Width of a stroke.
19482
+     * For image quality a stroke multiple of 2 gives better results.
19483
+     * @type Number
19484
+     * @default
19485
+     */
19486
+    strokeWidth: 0,
19487
+
19488
+    /**
19489
+     * private
19490
+     * contains last value of scaleX to detect
19491
+     * if the Image got resized after the last Render
19492
+     * @type Number
19493
+     */
19494
+    _lastScaleX: 1,
19495
+
19496
+    /**
19497
+     * private
19498
+     * contains last value of scaleY to detect
19499
+     * if the Image got resized after the last Render
19500
+     * @type Number
19501
+     */
19502
+    _lastScaleY: 1,
19503
+
19504
+    /**
19505
+     * private
19506
+     * contains last value of scaling applied by the apply filter chain
19507
+     * @type Number
19508
+     */
19509
+    _filterScalingX: 1,
19510
+
19511
+    /**
19512
+     * private
19513
+     * contains last value of scaling applied by the apply filter chain
19514
+     * @type Number
19515
+     */
19516
+    _filterScalingY: 1,
19517
+
19518
+    /**
19519
+     * minimum scale factor under which any resizeFilter is triggered to resize the image
19520
+     * 0 will disable the automatic resize. 1 will trigger automatically always.
19521
+     * number bigger than 1 are not implemented yet.
19522
+     * @type Number
19523
+     */
19524
+    minimumScaleTrigger: 0.5,
19525
+
19526
+    /**
19527
+     * List of properties to consider when checking if
19528
+     * state of an object is changed ({@link fabric.Object#hasStateChanged})
19529
+     * as well as for history (undo/redo) purposes
19530
+     * @type Array
19531
+     */
19532
+    stateProperties: fabric.Object.prototype.stateProperties.concat('cropX', 'cropY'),
19533
+
19534
+    /**
19535
+     * key used to retrieve the texture representing this image
19536
+     * since 2.0.0
19537
+     * @type String
19538
+     * @default
19539
+     */
19540
+    cacheKey: '',
19541
+
19542
+    /**
19543
+     * Image crop in pixels from original image size.
19544
+     * since 2.0.0
19545
+     * @type Number
19546
+     * @default
19547
+     */
19548
+    cropX: 0,
19549
+
19550
+    /**
19551
+     * Image crop in pixels from original image size.
19552
+     * since 2.0.0
19553
+     * @type Number
19554
+     * @default
19555
+     */
19556
+    cropY: 0,
19557
+
19558
+    /**
19559
+     * Constructor
19560
+     * @param {HTMLImageElement | String} element Image element
19561
+     * @param {Object} [options] Options object
19562
+     * @param {function} [callback] callback function to call after eventual filters applied.
19563
+     * @return {fabric.Image} thisArg
19564
+     */
19565
+    initialize: function(element, options) {
19566
+      options || (options = { });
19567
+      this.filters = [];
19568
+      this.cacheKey = 'texture' + fabric.Object.__uid++;
19569
+      this.callSuper('initialize', options);
19570
+      this._initElement(element, options);
19571
+    },
19572
+
19573
+    /**
19574
+     * Returns image element which this instance if based on
19575
+     * @return {HTMLImageElement} Image element
19576
+     */
19577
+    getElement: function() {
19578
+      return this._element || {};
19579
+    },
19580
+
19581
+    /**
19582
+     * Sets image element for this instance to a specified one.
19583
+     * If filters defined they are applied to new image.
19584
+     * You might need to call `canvas.renderAll` and `object.setCoords` after replacing, to render new image and update controls area.
19585
+     * @param {HTMLImageElement} element
19586
+     * @param {Object} [options] Options object
19587
+     * @return {fabric.Image} thisArg
19588
+     * @chainable
19589
+     */
19590
+    setElement: function(element, options) {
19591
+      this.removeTexture(this.cacheKey);
19592
+      this.removeTexture(this.cacheKey + '_filtered');
19593
+      this._element = element;
19594
+      this._originalElement = element;
19595
+      this._initConfig(options);
19596
+      if (this.filters.length !== 0) {
19597
+        this.applyFilters();
19598
+      }
19599
+      // resizeFilters work on the already filtered copy.
19600
+      // we need to apply resizeFilters AFTER normal filters.
19601
+      // applyResizeFilters is run more often than normal fiters
19602
+      // and is triggered by user interactions rather than dev code
19603
+      if (this.resizeFilter) {
19604
+        this.applyResizeFilters();
19605
+      }
19606
+      return this;
19607
+    },
19608
+
19609
+    /**
19610
+     * Delete a single texture if in webgl mode
19611
+     */
19612
+    removeTexture: function(key) {
19613
+      var backend = fabric.filterBackend;
19614
+      if (backend && backend.evictCachesForKey) {
19615
+        backend.evictCachesForKey(key);
19616
+      }
19617
+    },
19618
+
19619
+    /**
19620
+     * Delete textures, reference to elements and eventually JSDOM cleanup
19621
+     */
19622
+    dispose: function() {
19623
+      this.removeTexture(this.cacheKey);
19624
+      this.removeTexture(this.cacheKey + '_filtered');
19625
+      this._cacheContext = undefined;
19626
+      ['_originalElement', '_element', '_filteredEl', '_cacheCanvas'].forEach((function(element) {
19627
+        fabric.util.cleanUpJsdomNode(this[element]);
19628
+        this[element] = undefined;
19629
+      }).bind(this));
19630
+    },
19631
+
19632
+    /**
19633
+     * Sets crossOrigin value (on an instance and corresponding image element)
19634
+     * @return {fabric.Image} thisArg
19635
+     * @chainable
19636
+     */
19637
+    setCrossOrigin: function(value) {
19638
+      this.crossOrigin = value;
19639
+      this._element.crossOrigin = value;
19640
+
19641
+      return this;
19642
+    },
19643
+
19644
+    /**
19645
+     * Returns original size of an image
19646
+     * @return {Object} Object with "width" and "height" properties
19647
+     */
19648
+    getOriginalSize: function() {
19649
+      var element = this.getElement();
19650
+      return {
19651
+        width: element.naturalWidth || element.width,
19652
+        height: element.naturalHeight || element.height
19653
+      };
19654
+    },
19655
+
19656
+    /**
19657
+     * @private
19658
+     * @param {CanvasRenderingContext2D} ctx Context to render on
19659
+     */
19660
+    _stroke: function(ctx) {
19661
+      if (!this.stroke || this.strokeWidth === 0) {
19662
+        return;
19663
+      }
19664
+      var w = this.width / 2, h = this.height / 2;
19665
+      ctx.beginPath();
19666
+      ctx.moveTo(-w, -h);
19667
+      ctx.lineTo(w, -h);
19668
+      ctx.lineTo(w, h);
19669
+      ctx.lineTo(-w, h);
19670
+      ctx.lineTo(-w, -h);
19671
+      ctx.closePath();
19672
+    },
19673
+
19674
+    /**
19675
+     * @private
19676
+     * @param {CanvasRenderingContext2D} ctx Context to render on
19677
+     */
19678
+    _renderDashedStroke: function(ctx) {
19679
+      var x = -this.width / 2,
19680
+          y = -this.height / 2,
19681
+          w = this.width,
19682
+          h = this.height;
19683
+
19684
+      ctx.save();
19685
+      this._setStrokeStyles(ctx, this);
19686
+
19687
+      ctx.beginPath();
19688
+      fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
19689
+      fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
19690
+      fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
19691
+      fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
19692
+      ctx.closePath();
19693
+      ctx.restore();
19694
+    },
19695
+
19696
+    /**
19697
+     * Returns object representation of an instance
19698
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
19699
+     * @return {Object} Object representation of an instance
19700
+     */
19701
+    toObject: function(propertiesToInclude) {
19702
+      var filters = [];
19703
+
19704
+      this.filters.forEach(function(filterObj) {
19705
+        if (filterObj) {
19706
+          filters.push(filterObj.toObject());
19707
+        }
19708
+      });
19709
+      var object = extend(
19710
+        this.callSuper(
19711
+          'toObject',
19712
+          ['crossOrigin', 'cropX', 'cropY'].concat(propertiesToInclude)
19713
+        ), {
19714
+          src: this.getSrc(),
19715
+          filters: filters,
19716
+        });
19717
+      if (this.resizeFilter) {
19718
+        object.resizeFilter = this.resizeFilter.toObject();
19719
+      }
19720
+      return object;
19721
+    },
19722
+
19723
+    /**
19724
+     * Returns true if an image has crop applied, inspecting values of cropX,cropY,width,hight.
19725
+     * @return {Boolean}
19726
+     */
19727
+    hasCrop: function() {
19728
+      return this.cropX || this.cropY || this.width < this._element.width || this.height < this._element.height;
19729
+    },
19730
+
19731
+    /* _TO_SVG_START_ */
19732
+    /**
19733
+     * Returns svg representation of an instance
19734
+     * @return {Array} an array of strings with the specific svg representation
19735
+     * of the instance
19736
+     */
19737
+    _toSVG: function() {
19738
+      var svgString = [], imageMarkup = [], strokeSvg,
19739
+          x = -this.width / 2, y = -this.height / 2, clipPath = '';
19740
+      if (this.hasCrop()) {
19741
+        var clipPathId = fabric.Object.__uid++;
19742
+        svgString.push(
19743
+          '<clipPath id="imageCrop_' + clipPathId + '">\n',
19744
+          '\t<rect x="' + x + '" y="' + y + '" width="' + this.width + '" height="' + this.height + '" />\n',
19745
+          '</clipPath>\n'
19746
+        );
19747
+        clipPath = ' clip-path="url(#imageCrop_' + clipPathId + ')" ';
19748
+      }
19749
+      imageMarkup.push('\t<image ', 'COMMON_PARTS', 'xlink:href="', this.getSvgSrc(true),
19750
+        '" x="', x - this.cropX, '" y="', y - this.cropY,
19751
+        // we're essentially moving origin of transformation from top/left corner to the center of the shape
19752
+        // by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
19753
+        // so that object's center aligns with container's left/top
19754
+        '" width="', this._element.width || this._element.naturalWidth,
19755
+        '" height="', this._element.height || this._element.height,
19756
+        '"', clipPath,
19757
+        '></image>\n');
19758
+
19759
+      if (this.stroke || this.strokeDashArray) {
19760
+        var origFill = this.fill;
19761
+        this.fill = null;
19762
+        strokeSvg = [
19763
+          '\t<rect ',
19764
+          'x="', x, '" y="', y,
19765
+          '" width="', this.width, '" height="', this.height,
19766
+          '" style="', this.getSvgStyles(),
19767
+          '"/>\n'
19768
+        ];
19769
+        this.fill = origFill;
19770
+      }
19771
+      if (this.paintFirst !== 'fill') {
19772
+        svgString = svgString.concat(strokeSvg, imageMarkup);
19773
+      }
19774
+      else {
19775
+        svgString = svgString.concat(imageMarkup, strokeSvg);
19776
+      }
19777
+      return svgString;
19778
+    },
19779
+    /* _TO_SVG_END_ */
19780
+
19781
+    /**
19782
+     * Returns source of an image
19783
+     * @param {Boolean} filtered indicates if the src is needed for svg
19784
+     * @return {String} Source of an image
19785
+     */
19786
+    getSrc: function(filtered) {
19787
+      var element = filtered ? this._element : this._originalElement;
19788
+      if (element) {
19789
+        if (element.toDataURL) {
19790
+          return element.toDataURL();
19791
+        }
19792
+        return element.src;
19793
+      }
19794
+      else {
19795
+        return this.src || '';
19796
+      }
19797
+    },
19798
+
19799
+    /**
19800
+     * Sets source of an image
19801
+     * @param {String} src Source string (URL)
19802
+     * @param {Function} [callback] Callback is invoked when image has been loaded (and all filters have been applied)
19803
+     * @param {Object} [options] Options object
19804
+     * @return {fabric.Image} thisArg
19805
+     * @chainable
19806
+     */
19807
+    setSrc: function(src, callback, options) {
19808
+      fabric.util.loadImage(src, function(img) {
19809
+        this.setElement(img, options);
19810
+        this._setWidthHeight();
19811
+        callback(this);
19812
+      }, this, options && options.crossOrigin);
19813
+      return this;
19814
+    },
19815
+
19816
+    /**
19817
+     * Returns string representation of an instance
19818
+     * @return {String} String representation of an instance
19819
+     */
19820
+    toString: function() {
19821
+      return '#<fabric.Image: { src: "' + this.getSrc() + '" }>';
19822
+    },
19823
+
19824
+    applyResizeFilters: function() {
19825
+      var filter = this.resizeFilter,
19826
+          minimumScale = this.minimumScaleTrigger,
19827
+          objectScale = this.getTotalObjectScaling(),
19828
+          scaleX = objectScale.scaleX,
19829
+          scaleY = objectScale.scaleY,
19830
+          elementToFilter = this._filteredEl || this._originalElement;
19831
+      if (this.group) {
19832
+        this.set('dirty', true);
19833
+      }
19834
+      if (!filter || (scaleX > minimumScale && scaleY > minimumScale)) {
19835
+        this._element = elementToFilter;
19836
+        this._filterScalingX = 1;
19837
+        this._filterScalingY = 1;
19838
+        this._lastScaleX = scaleX;
19839
+        this._lastScaleY = scaleY;
19840
+        return;
19841
+      }
19842
+      if (!fabric.filterBackend) {
19843
+        fabric.filterBackend = fabric.initFilterBackend();
19844
+      }
19845
+      var canvasEl = fabric.util.createCanvasElement(),
19846
+          cacheKey = this._filteredEl ? (this.cacheKey + '_filtered') : this.cacheKey,
19847
+          sourceWidth = elementToFilter.width, sourceHeight = elementToFilter.height;
19848
+      canvasEl.width = sourceWidth;
19849
+      canvasEl.height = sourceHeight;
19850
+      this._element = canvasEl;
19851
+      this._lastScaleX = filter.scaleX = scaleX;
19852
+      this._lastScaleY = filter.scaleY = scaleY;
19853
+      fabric.filterBackend.applyFilters(
19854
+        [filter], elementToFilter, sourceWidth, sourceHeight, this._element, cacheKey);
19855
+      this._filterScalingX = canvasEl.width / this._originalElement.width;
19856
+      this._filterScalingY = canvasEl.height / this._originalElement.height;
19857
+    },
19858
+
19859
+    /**
19860
+     * Applies filters assigned to this image (from "filters" array) or from filter param
19861
+     * @method applyFilters
19862
+     * @param {Array} filters to be applied
19863
+     * @param {Boolean} forResizing specify if the filter operation is a resize operation
19864
+     * @return {thisArg} return the fabric.Image object
19865
+     * @chainable
19866
+     */
19867
+    applyFilters: function(filters) {
19868
+
19869
+      filters = filters || this.filters || [];
19870
+      filters = filters.filter(function(filter) { return filter && !filter.isNeutralState(); });
19871
+      this.set('dirty', true);
19872
+
19873
+      // needs to clear out or WEBGL will not resize correctly
19874
+      this.removeTexture(this.cacheKey + '_filtered');
19875
+
19876
+      if (filters.length === 0) {
19877
+        this._element = this._originalElement;
19878
+        this._filteredEl = null;
19879
+        this._filterScalingX = 1;
19880
+        this._filterScalingY = 1;
19881
+        return this;
19882
+      }
19883
+
19884
+      var imgElement = this._originalElement,
19885
+          sourceWidth = imgElement.naturalWidth || imgElement.width,
19886
+          sourceHeight = imgElement.naturalHeight || imgElement.height;
19887
+
19888
+      if (this._element === this._originalElement) {
19889
+        // if the element is the same we need to create a new element
19890
+        var canvasEl = fabric.util.createCanvasElement();
19891
+        canvasEl.width = sourceWidth;
19892
+        canvasEl.height = sourceHeight;
19893
+        this._element = canvasEl;
19894
+        this._filteredEl = canvasEl;
19895
+      }
19896
+      else {
19897
+        // clear the existing element to get new filter data
19898
+        // also dereference the eventual resized _element
19899
+        this._element = this._filteredEl;
19900
+        this._filteredEl.getContext('2d').clearRect(0, 0, sourceWidth, sourceHeight);
19901
+        // we also need to resize again at next renderAll, so remove saved _lastScaleX/Y
19902
+        this._lastScaleX = 1;
19903
+        this._lastScaleY = 1;
19904
+      }
19905
+      if (!fabric.filterBackend) {
19906
+        fabric.filterBackend = fabric.initFilterBackend();
19907
+      }
19908
+      fabric.filterBackend.applyFilters(
19909
+        filters, this._originalElement, sourceWidth, sourceHeight, this._element, this.cacheKey);
19910
+      if (this._originalElement.width !== this._element.width ||
19911
+        this._originalElement.height !== this._element.height) {
19912
+        this._filterScalingX = this._element.width / this._originalElement.width;
19913
+        this._filterScalingY = this._element.height / this._originalElement.height;
19914
+      }
19915
+      return this;
19916
+    },
19917
+
19918
+    /**
19919
+     * @private
19920
+     * @param {CanvasRenderingContext2D} ctx Context to render on
19921
+     */
19922
+    _render: function(ctx) {
19923
+      if (this.isMoving !== true && this.resizeFilter && this._needsResize()) {
19924
+        this.applyResizeFilters();
19925
+      }
19926
+      this._stroke(ctx);
19927
+      this._renderPaintInOrder(ctx);
19928
+    },
19929
+
19930
+    /**
19931
+     * Decide if the object should cache or not. Create its own cache level
19932
+     * objectCaching is a global flag, wins over everything
19933
+     * needsItsOwnCache should be used when the object drawing method requires
19934
+     * a cache step. None of the fabric classes requires it.
19935
+     * Generally you do not cache objects in groups because the group outside is cached.
19936
+     * This is the special image version where we would like to avoid caching where possible.
19937
+     * Essentially images do not benefit from caching. They may require caching, and in that
19938
+     * case we do it. Also caching an image usually ends in a loss of details.
19939
+     * A full performance audit should be done.
19940
+     * @return {Boolean}
19941
+     */
19942
+    shouldCache: function() {
19943
+      this.ownCaching = this.objectCaching && this.needsItsOwnCache();
19944
+      return this.ownCaching;
19945
+    },
19946
+
19947
+    _renderFill: function(ctx) {
19948
+      var w = this.width, h = this.height, sW = w * this._filterScalingX, sH = h * this._filterScalingY,
19949
+          x = -w / 2, y = -h / 2, elementToDraw = this._element;
19950
+      elementToDraw && ctx.drawImage(elementToDraw,
19951
+        this.cropX * this._filterScalingX,
19952
+        this.cropY * this._filterScalingY,
19953
+        sW,
19954
+        sH,
19955
+        x, y, w, h);
19956
+    },
19957
+
19958
+    /**
19959
+     * @private, needed to check if image needs resize
19960
+     */
19961
+    _needsResize: function() {
19962
+      var scale = this.getTotalObjectScaling();
19963
+      return (scale.scaleX !== this._lastScaleX || scale.scaleY !== this._lastScaleY);
19964
+    },
19965
+
19966
+    /**
19967
+     * @private
19968
+     */
19969
+    _resetWidthHeight: function() {
19970
+      this.set(this.getOriginalSize());
19971
+    },
19972
+
19973
+    /**
19974
+     * The Image class's initialization method. This method is automatically
19975
+     * called by the constructor.
19976
+     * @private
19977
+     * @param {HTMLImageElement|String} element The element representing the image
19978
+     * @param {Object} [options] Options object
19979
+     */
19980
+    _initElement: function(element, options) {
19981
+      this.setElement(fabric.util.getById(element), options);
19982
+      fabric.util.addClass(this.getElement(), fabric.Image.CSS_CANVAS);
19983
+    },
19984
+
19985
+    /**
19986
+     * @private
19987
+     * @param {Object} [options] Options object
19988
+     */
19989
+    _initConfig: function(options) {
19990
+      options || (options = { });
19991
+      this.setOptions(options);
19992
+      this._setWidthHeight(options);
19993
+      if (this._element && this.crossOrigin) {
19994
+        this._element.crossOrigin = this.crossOrigin;
19995
+      }
19996
+    },
19997
+
19998
+    /**
19999
+     * @private
20000
+     * @param {Array} filters to be initialized
20001
+     * @param {Function} callback Callback to invoke when all fabric.Image.filters instances are created
20002
+     */
20003
+    _initFilters: function(filters, callback) {
20004
+      if (filters && filters.length) {
20005
+        fabric.util.enlivenObjects(filters, function(enlivenedObjects) {
20006
+          callback && callback(enlivenedObjects);
20007
+        }, 'fabric.Image.filters');
20008
+      }
20009
+      else {
20010
+        callback && callback();
20011
+      }
20012
+    },
20013
+
20014
+    /**
20015
+     * @private
20016
+     * Set the width and the height of the image object, using the element or the
20017
+     * options.
20018
+     * @param {Object} [options] Object with width/height properties
20019
+     */
20020
+    _setWidthHeight: function(options) {
20021
+      options || (options = { });
20022
+      var el = this.getElement();
20023
+      this.width = options.width || el.naturalWidth || el.width || 0;
20024
+      this.height = options.height || el.naturalHeight || el.height || 0;
20025
+    },
20026
+
20027
+    /**
20028
+     * Calculate offset for center and scale factor for the image in order to respect
20029
+     * the preserveAspectRatio attribute
20030
+     * @private
20031
+     * @return {Object}
20032
+     */
20033
+    parsePreserveAspectRatioAttribute: function() {
20034
+      var pAR = fabric.util.parsePreserveAspectRatioAttribute(this.preserveAspectRatio || ''),
20035
+          rWidth = this._element.width, rHeight = this._element.height,
20036
+          scaleX = 1, scaleY = 1, offsetLeft = 0, offsetTop = 0, cropX = 0, cropY = 0,
20037
+          offset, pWidth = this.width, pHeight = this.height, parsedAttributes = { width: pWidth, height: pHeight };
20038
+      if (pAR && (pAR.alignX !== 'none' || pAR.alignY !== 'none')) {
20039
+        if (pAR.meetOrSlice === 'meet') {
20040
+          scaleX = scaleY = fabric.util.findScaleToFit(this._element, parsedAttributes);
20041
+          offset = (pWidth - rWidth * scaleX) / 2;
20042
+          if (pAR.alignX === 'Min') {
20043
+            offsetLeft = -offset;
20044
+          }
20045
+          if (pAR.alignX === 'Max') {
20046
+            offsetLeft = offset;
20047
+          }
20048
+          offset = (pHeight - rHeight * scaleY) / 2;
20049
+          if (pAR.alignY === 'Min') {
20050
+            offsetTop = -offset;
20051
+          }
20052
+          if (pAR.alignY === 'Max') {
20053
+            offsetTop = offset;
20054
+          }
20055
+        }
20056
+        if (pAR.meetOrSlice === 'slice') {
20057
+          scaleX = scaleY = fabric.util.findScaleToCover(this._element, parsedAttributes);
20058
+          offset = rWidth - pWidth / scaleX;
20059
+          if (pAR.alignX === 'Mid') {
20060
+            cropX = offset / 2;
20061
+          }
20062
+          if (pAR.alignX === 'Max') {
20063
+            cropX = offset;
20064
+          }
20065
+          offset = rHeight - pHeight / scaleY;
20066
+          if (pAR.alignY === 'Mid') {
20067
+            cropY = offset / 2;
20068
+          }
20069
+          if (pAR.alignY === 'Max') {
20070
+            cropY = offset;
20071
+          }
20072
+          rWidth = pWidth / scaleX;
20073
+          rHeight = pHeight / scaleY;
20074
+        }
20075
+      }
20076
+      else {
20077
+        scaleX = pWidth / rWidth;
20078
+        scaleY = pHeight / rHeight;
20079
+      }
20080
+      return {
20081
+        width: rWidth,
20082
+        height: rHeight,
20083
+        scaleX: scaleX,
20084
+        scaleY: scaleY,
20085
+        offsetLeft: offsetLeft,
20086
+        offsetTop: offsetTop,
20087
+        cropX: cropX,
20088
+        cropY: cropY
20089
+      };
20090
+    }
20091
+  });
20092
+
20093
+  /**
20094
+   * Default CSS class name for canvas
20095
+   * @static
20096
+   * @type String
20097
+   * @default
20098
+   */
20099
+  fabric.Image.CSS_CANVAS = 'canvas-img';
20100
+
20101
+  /**
20102
+   * Alias for getSrc
20103
+   * @static
20104
+   */
20105
+  fabric.Image.prototype.getSvgSrc = fabric.Image.prototype.getSrc;
20106
+
20107
+  /**
20108
+   * Creates an instance of fabric.Image from its object representation
20109
+   * @static
20110
+   * @param {Object} object Object to create an instance from
20111
+   * @param {Function} callback Callback to invoke when an image instance is created
20112
+   */
20113
+  fabric.Image.fromObject = function(_object, callback) {
20114
+    var object = fabric.util.object.clone(_object);
20115
+    fabric.util.loadImage(object.src, function(img, error) {
20116
+      if (error) {
20117
+        callback && callback(null, error);
20118
+        return;
20119
+      }
20120
+      fabric.Image.prototype._initFilters.call(object, object.filters, function(filters) {
20121
+        object.filters = filters || [];
20122
+        fabric.Image.prototype._initFilters.call(object, [object.resizeFilter], function(resizeFilters) {
20123
+          object.resizeFilter = resizeFilters[0];
20124
+          fabric.util.enlivenObjects([object.clipPath], function(enlivedProps) {
20125
+            object.clipPath = enlivedProps[0];
20126
+            var image = new fabric.Image(img, object);
20127
+            callback(image);
20128
+          });
20129
+        });
20130
+      });
20131
+    }, null, object.crossOrigin);
20132
+  };
20133
+
20134
+  /**
20135
+   * Creates an instance of fabric.Image from an URL string
20136
+   * @static
20137
+   * @param {String} url URL to create an image from
20138
+   * @param {Function} [callback] Callback to invoke when image is created (newly created image is passed as a first argument)
20139
+   * @param {Object} [imgOptions] Options object
20140
+   */
20141
+  fabric.Image.fromURL = function(url, callback, imgOptions) {
20142
+    fabric.util.loadImage(url, function(img) {
20143
+      callback && callback(new fabric.Image(img, imgOptions));
20144
+    }, null, imgOptions && imgOptions.crossOrigin);
20145
+  };
20146
+
20147
+  /* _FROM_SVG_START_ */
20148
+  /**
20149
+   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Image.fromElement})
20150
+   * @static
20151
+   * @see {@link http://www.w3.org/TR/SVG/struct.html#ImageElement}
20152
+   */
20153
+  fabric.Image.ATTRIBUTE_NAMES =
20154
+    fabric.SHARED_ATTRIBUTES.concat('x y width height preserveAspectRatio xlink:href crossOrigin'.split(' '));
20155
+
20156
+  /**
20157
+   * Returns {@link fabric.Image} instance from an SVG element
20158
+   * @static
20159
+   * @param {SVGElement} element Element to parse
20160
+   * @param {Object} [options] Options object
20161
+   * @param {Function} callback Callback to execute when fabric.Image object is created
20162
+   * @return {fabric.Image} Instance of fabric.Image
20163
+   */
20164
+  fabric.Image.fromElement = function(element, callback, options) {
20165
+    var parsedAttributes = fabric.parseAttributes(element, fabric.Image.ATTRIBUTE_NAMES);
20166
+    fabric.Image.fromURL(parsedAttributes['xlink:href'], callback,
20167
+      extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
20168
+  };
20169
+  /* _FROM_SVG_END_ */
20170
+
20171
+})(typeof exports !== 'undefined' ? exports : this);
20172
+
20173
+
20174
+fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
20175
+
20176
+  /**
20177
+   * @private
20178
+   * @return {Number} angle value
20179
+   */
20180
+  _getAngleValueForStraighten: function() {
20181
+    var angle = this.angle % 360;
20182
+    if (angle > 0) {
20183
+      return Math.round((angle - 1) / 90) * 90;
20184
+    }
20185
+    return Math.round(angle / 90) * 90;
20186
+  },
20187
+
20188
+  /**
20189
+   * Straightens an object (rotating it from current angle to one of 0, 90, 180, 270, etc. depending on which is closer)
20190
+   * @return {fabric.Object} thisArg
20191
+   * @chainable
20192
+   */
20193
+  straighten: function() {
20194
+    this.rotate(this._getAngleValueForStraighten());
20195
+    return this;
20196
+  },
20197
+
20198
+  /**
20199
+   * Same as {@link fabric.Object.prototype.straighten} but with animation
20200
+   * @param {Object} callbacks Object with callback functions
20201
+   * @param {Function} [callbacks.onComplete] Invoked on completion
20202
+   * @param {Function} [callbacks.onChange] Invoked on every step of animation
20203
+   * @return {fabric.Object} thisArg
20204
+   * @chainable
20205
+   */
20206
+  fxStraighten: function(callbacks) {
20207
+    callbacks = callbacks || { };
20208
+
20209
+    var empty = function() { },
20210
+        onComplete = callbacks.onComplete || empty,
20211
+        onChange = callbacks.onChange || empty,
20212
+        _this = this;
20213
+
20214
+    fabric.util.animate({
20215
+      startValue: this.get('angle'),
20216
+      endValue: this._getAngleValueForStraighten(),
20217
+      duration: this.FX_DURATION,
20218
+      onChange: function(value) {
20219
+        _this.rotate(value);
20220
+        onChange();
20221
+      },
20222
+      onComplete: function() {
20223
+        _this.setCoords();
20224
+        onComplete();
20225
+      },
20226
+    });
20227
+
20228
+    return this;
20229
+  }
20230
+});
20231
+
20232
+fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
20233
+
20234
+  /**
20235
+   * Straightens object, then rerenders canvas
20236
+   * @param {fabric.Object} object Object to straighten
20237
+   * @return {fabric.Canvas} thisArg
20238
+   * @chainable
20239
+   */
20240
+  straightenObject: function (object) {
20241
+    object.straighten();
20242
+    this.requestRenderAll();
20243
+    return this;
20244
+  },
20245
+
20246
+  /**
20247
+   * Same as {@link fabric.Canvas.prototype.straightenObject}, but animated
20248
+   * @param {fabric.Object} object Object to straighten
20249
+   * @return {fabric.Canvas} thisArg
20250
+   * @chainable
20251
+   */
20252
+  fxStraightenObject: function (object) {
20253
+    object.fxStraighten({
20254
+      onChange: this.requestRenderAllBound
20255
+    });
20256
+    return this;
20257
+  }
20258
+});
20259
+
20260
+
20261
+(function() {
20262
+
20263
+  'use strict';
20264
+
20265
+  /**
20266
+   * Tests if webgl supports certain precision
20267
+   * @param {WebGL} Canvas WebGL context to test on
20268
+   * @param {String} Precision to test can be any of following: 'lowp', 'mediump', 'highp'
20269
+   * @returns {Boolean} Whether the user's browser WebGL supports given precision.
20270
+   */
20271
+  function testPrecision(gl, precision){
20272
+    var fragmentSource = 'precision ' + precision + ' float;\nvoid main(){}';
20273
+    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
20274
+    gl.shaderSource(fragmentShader, fragmentSource);
20275
+    gl.compileShader(fragmentShader);
20276
+    if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
20277
+      return false;
20278
+    }
20279
+    return true;
20280
+  }
20281
+
20282
+  /**
20283
+   * Indicate whether this filtering backend is supported by the user's browser.
20284
+   * @param {Number} tileSize check if the tileSize is supported
20285
+   * @returns {Boolean} Whether the user's browser supports WebGL.
20286
+   */
20287
+  fabric.isWebglSupported = function(tileSize) {
20288
+    if (fabric.isLikelyNode) {
20289
+      return false;
20290
+    }
20291
+    tileSize = tileSize || fabric.WebglFilterBackend.prototype.tileSize;
20292
+    var canvas = document.createElement('canvas');
20293
+    var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
20294
+    var isSupported = false;
20295
+    // eslint-disable-next-line
20296
+    if (gl) {
20297
+      fabric.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
20298
+      isSupported = fabric.maxTextureSize >= tileSize;
20299
+      var precisions = ['highp', 'mediump', 'lowp'];
20300
+      for (var i = 0; i < 3; i++){
20301
+        if (testPrecision(gl, precisions[i])){
20302
+          fabric.webGlPrecision = precisions[i];
20303
+          break;
20304
+        };
20305
+      }
20306
+    }
20307
+    this.isSupported = isSupported;
20308
+    return isSupported;
20309
+  };
20310
+
20311
+  fabric.WebglFilterBackend = WebglFilterBackend;
20312
+
20313
+  /**
20314
+   * WebGL filter backend.
20315
+   */
20316
+  function WebglFilterBackend(options) {
20317
+    if (options && options.tileSize) {
20318
+      this.tileSize = options.tileSize;
20319
+    }
20320
+    this.setupGLContext(this.tileSize, this.tileSize);
20321
+    this.captureGPUInfo();
20322
+  };
20323
+
20324
+  WebglFilterBackend.prototype = /** @lends fabric.WebglFilterBackend.prototype */ {
20325
+
20326
+    tileSize: 2048,
20327
+
20328
+    /**
20329
+     * Experimental. This object is a sort of repository of help layers used to avoid
20330
+     * of recreating them during frequent filtering. If you are previewing a filter with
20331
+     * a slider you problably do not want to create help layers every filter step.
20332
+     * in this object there will be appended some canvases, created once, resized sometimes
20333
+     * cleared never. Clearing is left to the developer.
20334
+     **/
20335
+    resources: {
20336
+
20337
+    },
20338
+
20339
+    /**
20340
+     * Setup a WebGL context suitable for filtering, and bind any needed event handlers.
20341
+     */
20342
+    setupGLContext: function(width, height) {
20343
+      this.dispose();
20344
+      this.createWebGLCanvas(width, height);
20345
+      // eslint-disable-next-line
20346
+      this.aPosition = new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]);
20347
+      this.chooseFastestCopyGLTo2DMethod(width, height);
20348
+    },
20349
+
20350
+    /**
20351
+     * Pick a method to copy data from GL context to 2d canvas.  In some browsers using
20352
+     * putImageData is faster than drawImage for that specific operation.
20353
+     */
20354
+    chooseFastestCopyGLTo2DMethod: function(width, height) {
20355
+      var canMeasurePerf = typeof window.performance !== 'undefined';
20356
+      var canUseImageData;
20357
+      try {
20358
+        new ImageData(1, 1);
20359
+        canUseImageData = true;
20360
+      }
20361
+      catch (e) {
20362
+        canUseImageData = false;
20363
+      }
20364
+      // eslint-disable-next-line no-undef
20365
+      var canUseArrayBuffer = typeof ArrayBuffer !== 'undefined';
20366
+      // eslint-disable-next-line no-undef
20367
+      var canUseUint8Clamped = typeof Uint8ClampedArray !== 'undefined';
20368
+
20369
+      if (!(canMeasurePerf && canUseImageData && canUseArrayBuffer && canUseUint8Clamped)) {
20370
+        return;
20371
+      }
20372
+
20373
+      var targetCanvas = fabric.util.createCanvasElement();
20374
+      // eslint-disable-next-line no-undef
20375
+      var imageBuffer = new ArrayBuffer(width * height * 4);
20376
+      var testContext = {
20377
+        imageBuffer: imageBuffer,
20378
+        destinationWidth: width,
20379
+        destinationHeight: height,
20380
+        targetCanvas: targetCanvas
20381
+      };
20382
+      var startTime, drawImageTime, putImageDataTime;
20383
+      targetCanvas.width = width;
20384
+      targetCanvas.height = height;
20385
+
20386
+      startTime = window.performance.now();
20387
+      copyGLTo2DDrawImage.call(testContext, this.gl, testContext);
20388
+      drawImageTime = window.performance.now() - startTime;
20389
+
20390
+      startTime = window.performance.now();
20391
+      copyGLTo2DPutImageData.call(testContext, this.gl, testContext);
20392
+      putImageDataTime = window.performance.now() - startTime;
20393
+
20394
+      if (drawImageTime > putImageDataTime) {
20395
+        this.imageBuffer = imageBuffer;
20396
+        this.copyGLTo2D = copyGLTo2DPutImageData;
20397
+      }
20398
+      else {
20399
+        this.copyGLTo2D = copyGLTo2DDrawImage;
20400
+      }
20401
+    },
20402
+
20403
+    /**
20404
+     * Create a canvas element and associated WebGL context and attaches them as
20405
+     * class properties to the GLFilterBackend class.
20406
+     */
20407
+    createWebGLCanvas: function(width, height) {
20408
+      var canvas = fabric.util.createCanvasElement();
20409
+      canvas.width = width;
20410
+      canvas.height = height;
20411
+      var glOptions = {
20412
+            alpha: true,
20413
+            premultipliedAlpha: false,
20414
+            depth: false,
20415
+            stencil: false,
20416
+            antialias: false
20417
+          },
20418
+          gl = canvas.getContext('webgl', glOptions);
20419
+      if (!gl) {
20420
+        gl = canvas.getContext('experimental-webgl', glOptions);
20421
+      }
20422
+      if (!gl) {
20423
+        return;
20424
+      }
20425
+      gl.clearColor(0, 0, 0, 0);
20426
+      // this canvas can fire webglcontextlost and webglcontextrestored
20427
+      this.canvas = canvas;
20428
+      this.gl = gl;
20429
+    },
20430
+
20431
+    /**
20432
+     * Attempts to apply the requested filters to the source provided, drawing the filtered output
20433
+     * to the provided target canvas.
20434
+     *
20435
+     * @param {Array} filters The filters to apply.
20436
+     * @param {HTMLImageElement|HTMLCanvasElement} source The source to be filtered.
20437
+     * @param {Number} width The width of the source input.
20438
+     * @param {Number} height The height of the source input.
20439
+     * @param {HTMLCanvasElement} targetCanvas The destination for filtered output to be drawn.
20440
+     * @param {String|undefined} cacheKey A key used to cache resources related to the source. If
20441
+     * omitted, caching will be skipped.
20442
+     */
20443
+    applyFilters: function(filters, source, width, height, targetCanvas, cacheKey) {
20444
+      var gl = this.gl;
20445
+      var cachedTexture;
20446
+      if (cacheKey) {
20447
+        cachedTexture = this.getCachedTexture(cacheKey, source);
20448
+      }
20449
+      var pipelineState = {
20450
+        originalWidth: source.width || source.originalWidth,
20451
+        originalHeight: source.height || source.originalHeight,
20452
+        sourceWidth: width,
20453
+        sourceHeight: height,
20454
+        destinationWidth: width,
20455
+        destinationHeight: height,
20456
+        context: gl,
20457
+        sourceTexture: this.createTexture(gl, width, height, !cachedTexture && source),
20458
+        targetTexture: this.createTexture(gl, width, height),
20459
+        originalTexture: cachedTexture ||
20460
+          this.createTexture(gl, width, height, !cachedTexture && source),
20461
+        passes: filters.length,
20462
+        webgl: true,
20463
+        aPosition: this.aPosition,
20464
+        programCache: this.programCache,
20465
+        pass: 0,
20466
+        filterBackend: this,
20467
+        targetCanvas: targetCanvas
20468
+      };
20469
+      var tempFbo = gl.createFramebuffer();
20470
+      gl.bindFramebuffer(gl.FRAMEBUFFER, tempFbo);
20471
+      filters.forEach(function(filter) { filter && filter.applyTo(pipelineState); });
20472
+      resizeCanvasIfNeeded(pipelineState);
20473
+      this.copyGLTo2D(gl, pipelineState);
20474
+      gl.bindTexture(gl.TEXTURE_2D, null);
20475
+      gl.deleteTexture(pipelineState.sourceTexture);
20476
+      gl.deleteTexture(pipelineState.targetTexture);
20477
+      gl.deleteFramebuffer(tempFbo);
20478
+      targetCanvas.getContext('2d').setTransform(1, 0, 0, 1, 0, 0);
20479
+      return pipelineState;
20480
+    },
20481
+
20482
+    /**
20483
+     * The same as the applyFilter method but with additional logging of WebGL
20484
+     * errors.
20485
+     */
20486
+    applyFiltersDebug: function(filters, source, width, height, targetCanvas, cacheKey) {
20487
+      // The following code is useful when debugging a specific issue but adds ~10x slowdown.
20488
+      var gl = this.gl;
20489
+      var ret = this.applyFilters(filters, source, width, height, targetCanvas, cacheKey);
20490
+      var glError = gl.getError();
20491
+      if (glError !== gl.NO_ERROR) {
20492
+        var errorString = this.glErrorToString(gl, glError);
20493
+        var error = new Error('WebGL Error ' + errorString);
20494
+        error.glErrorCode = glError;
20495
+        throw error;
20496
+      }
20497
+      return ret;
20498
+    },
20499
+
20500
+    glErrorToString: function(context, errorCode) {
20501
+      if (!context) {
20502
+        return 'Context undefined for error code: ' + errorCode;
20503
+      }
20504
+      else if (typeof errorCode !== 'number') {
20505
+        return 'Error code is not a number';
20506
+      }
20507
+      switch (errorCode) {
20508
+        case context.NO_ERROR:
20509
+          return 'NO_ERROR';
20510
+        case context.INVALID_ENUM:
20511
+          return 'INVALID_ENUM';
20512
+        case context.INVALID_VALUE:
20513
+          return 'INVALID_VALUE';
20514
+        case context.INVALID_OPERATION:
20515
+          return 'INVALID_OPERATION';
20516
+        case context.INVALID_FRAMEBUFFER_OPERATION:
20517
+          return 'INVALID_FRAMEBUFFER_OPERATION';
20518
+        case context.OUT_OF_MEMORY:
20519
+          return 'OUT_OF_MEMORY';
20520
+        case context.CONTEXT_LOST_WEBGL:
20521
+          return 'CONTEXT_LOST_WEBGL';
20522
+        default:
20523
+          return 'UNKNOWN_ERROR';
20524
+      }
20525
+    },
20526
+
20527
+    /**
20528
+     * Detach event listeners, remove references, and clean up caches.
20529
+     */
20530
+    dispose: function() {
20531
+      if (this.canvas) {
20532
+        this.canvas = null;
20533
+        this.gl = null;
20534
+      }
20535
+      this.clearWebGLCaches();
20536
+    },
20537
+
20538
+    /**
20539
+     * Wipe out WebGL-related caches.
20540
+     */
20541
+    clearWebGLCaches: function() {
20542
+      this.programCache = {};
20543
+      this.textureCache = {};
20544
+    },
20545
+
20546
+    /**
20547
+     * Create a WebGL texture object.
20548
+     *
20549
+     * Accepts specific dimensions to initialize the textuer to or a source image.
20550
+     *
20551
+     * @param {WebGLRenderingContext} gl The GL context to use for creating the texture.
20552
+     * @param {Number} width The width to initialize the texture at.
20553
+     * @param {Number} height The height to initialize the texture.
20554
+     * @param {HTMLImageElement|HTMLCanvasElement} textureImageSource A source for the texture data.
20555
+     * @returns {WebGLTexture}
20556
+     */
20557
+    createTexture: function(gl, width, height, textureImageSource) {
20558
+      var texture = gl.createTexture();
20559
+      gl.bindTexture(gl.TEXTURE_2D, texture);
20560
+      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
20561
+      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
20562
+      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
20563
+      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
20564
+      if (textureImageSource) {
20565
+        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureImageSource);
20566
+      }
20567
+      else {
20568
+        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
20569
+      }
20570
+      return texture;
20571
+    },
20572
+
20573
+    /**
20574
+     * Can be optionally used to get a texture from the cache array
20575
+     *
20576
+     * If an existing texture is not found, a new texture is created and cached.
20577
+     *
20578
+     * @param {String} uniqueId A cache key to use to find an existing texture.
20579
+     * @param {HTMLImageElement|HTMLCanvasElement} textureImageSource A source to use to create the
20580
+     * texture cache entry if one does not already exist.
20581
+     */
20582
+    getCachedTexture: function(uniqueId, textureImageSource) {
20583
+      if (this.textureCache[uniqueId]) {
20584
+        return this.textureCache[uniqueId];
20585
+      }
20586
+      else {
20587
+        var texture = this.createTexture(
20588
+          this.gl, textureImageSource.width, textureImageSource.height, textureImageSource);
20589
+        this.textureCache[uniqueId] = texture;
20590
+        return texture;
20591
+      }
20592
+    },
20593
+
20594
+    /**
20595
+     * Clear out cached resources related to a source image that has been
20596
+     * filtered previously.
20597
+     *
20598
+     * @param {String} cacheKey The cache key provided when the source image was filtered.
20599
+     */
20600
+    evictCachesForKey: function(cacheKey) {
20601
+      if (this.textureCache[cacheKey]) {
20602
+        this.gl.deleteTexture(this.textureCache[cacheKey]);
20603
+        delete this.textureCache[cacheKey];
20604
+      }
20605
+    },
20606
+
20607
+    copyGLTo2D: copyGLTo2DDrawImage,
20608
+
20609
+    /**
20610
+     * Attempt to extract GPU information strings from a WebGL context.
20611
+     *
20612
+     * Useful information when debugging or blacklisting specific GPUs.
20613
+     *
20614
+     * @returns {Object} A GPU info object with renderer and vendor strings.
20615
+     */
20616
+    captureGPUInfo: function() {
20617
+      if (this.gpuInfo) {
20618
+        return this.gpuInfo;
20619
+      }
20620
+      var gl = this.gl;
20621
+      var ext = gl.getExtension('WEBGL_debug_renderer_info');
20622
+      var gpuInfo = { renderer: '', vendor: '' };
20623
+      if (ext) {
20624
+        var renderer = gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
20625
+        var vendor = gl.getParameter(ext.UNMASKED_VENDOR_WEBGL);
20626
+        if (renderer) {
20627
+          gpuInfo.renderer = renderer.toLowerCase();
20628
+        }
20629
+        if (vendor) {
20630
+          gpuInfo.vendor = vendor.toLowerCase();
20631
+        }
20632
+      }
20633
+      this.gpuInfo = gpuInfo;
20634
+      return gpuInfo;
20635
+    },
20636
+  };
20637
+})();
20638
+
20639
+function resizeCanvasIfNeeded(pipelineState) {
20640
+  var targetCanvas = pipelineState.targetCanvas,
20641
+      width = targetCanvas.width, height = targetCanvas.height,
20642
+      dWidth = pipelineState.destinationWidth,
20643
+      dHeight = pipelineState.destinationHeight;
20644
+
20645
+  if (width !== dWidth || height !== dHeight) {
20646
+    targetCanvas.width = dWidth;
20647
+    targetCanvas.height = dHeight;
20648
+  }
20649
+}
20650
+
20651
+/**
20652
+ * Copy an input WebGL canvas on to an output 2D canvas.
20653
+ *
20654
+ * The WebGL canvas is assumed to be upside down, with the top-left pixel of the
20655
+ * desired output image appearing in the bottom-left corner of the WebGL canvas.
20656
+ *
20657
+ * @param {WebGLRenderingContext} sourceContext The WebGL context to copy from.
20658
+ * @param {HTMLCanvasElement} targetCanvas The 2D target canvas to copy on to.
20659
+ * @param {Object} pipelineState The 2D target canvas to copy on to.
20660
+ */
20661
+function copyGLTo2DDrawImage(gl, pipelineState) {
20662
+  var glCanvas = gl.canvas, targetCanvas = pipelineState.targetCanvas,
20663
+      ctx = targetCanvas.getContext('2d');
20664
+  ctx.translate(0, targetCanvas.height); // move it down again
20665
+  ctx.scale(1, -1); // vertical flip
20666
+  // where is my image on the big glcanvas?
20667
+  var sourceY = glCanvas.height - targetCanvas.height;
20668
+  ctx.drawImage(glCanvas, 0, sourceY, targetCanvas.width, targetCanvas.height, 0, 0,
20669
+    targetCanvas.width, targetCanvas.height);
20670
+}
20671
+
20672
+/**
20673
+ * Copy an input WebGL canvas on to an output 2D canvas using 2d canvas' putImageData
20674
+ * API. Measurably faster than using ctx.drawImage in Firefox (version 54 on OSX Sierra).
20675
+ *
20676
+ * @param {WebGLRenderingContext} sourceContext The WebGL context to copy from.
20677
+ * @param {HTMLCanvasElement} targetCanvas The 2D target canvas to copy on to.
20678
+ * @param {Object} pipelineState The 2D target canvas to copy on to.
20679
+ */
20680
+function copyGLTo2DPutImageData(gl, pipelineState) {
20681
+  var targetCanvas = pipelineState.targetCanvas, ctx = targetCanvas.getContext('2d'),
20682
+      dWidth = pipelineState.destinationWidth,
20683
+      dHeight = pipelineState.destinationHeight,
20684
+      numBytes = dWidth * dHeight * 4;
20685
+
20686
+  // eslint-disable-next-line no-undef
20687
+  var u8 = new Uint8Array(this.imageBuffer, 0, numBytes);
20688
+  // eslint-disable-next-line no-undef
20689
+  var u8Clamped = new Uint8ClampedArray(this.imageBuffer, 0, numBytes);
20690
+
20691
+  gl.readPixels(0, 0, dWidth, dHeight, gl.RGBA, gl.UNSIGNED_BYTE, u8);
20692
+  var imgData = new ImageData(u8Clamped, dWidth, dHeight);
20693
+  ctx.putImageData(imgData, 0, 0);
20694
+}
20695
+
20696
+
20697
+(function() {
20698
+
20699
+  'use strict';
20700
+
20701
+  var noop = function() {};
20702
+
20703
+  fabric.Canvas2dFilterBackend = Canvas2dFilterBackend;
20704
+
20705
+  /**
20706
+   * Canvas 2D filter backend.
20707
+   */
20708
+  function Canvas2dFilterBackend() {};
20709
+
20710
+  Canvas2dFilterBackend.prototype = /** @lends fabric.Canvas2dFilterBackend.prototype */ {
20711
+    evictCachesForKey: noop,
20712
+    dispose: noop,
20713
+    clearWebGLCaches: noop,
20714
+
20715
+    /**
20716
+     * Experimental. This object is a sort of repository of help layers used to avoid
20717
+     * of recreating them during frequent filtering. If you are previewing a filter with
20718
+     * a slider you probably do not want to create help layers every filter step.
20719
+     * in this object there will be appended some canvases, created once, resized sometimes
20720
+     * cleared never. Clearing is left to the developer.
20721
+     **/
20722
+    resources: {
20723
+
20724
+    },
20725
+
20726
+    /**
20727
+     * Apply a set of filters against a source image and draw the filtered output
20728
+     * to the provided destination canvas.
20729
+     *
20730
+     * @param {EnhancedFilter} filters The filter to apply.
20731
+     * @param {HTMLImageElement|HTMLCanvasElement} sourceElement The source to be filtered.
20732
+     * @param {Number} sourceWidth The width of the source input.
20733
+     * @param {Number} sourceHeight The height of the source input.
20734
+     * @param {HTMLCanvasElement} targetCanvas The destination for filtered output to be drawn.
20735
+     */
20736
+    applyFilters: function(filters, sourceElement, sourceWidth, sourceHeight, targetCanvas) {
20737
+      var ctx = targetCanvas.getContext('2d');
20738
+      ctx.drawImage(sourceElement, 0, 0, sourceWidth, sourceHeight);
20739
+      var imageData = ctx.getImageData(0, 0, sourceWidth, sourceHeight);
20740
+      var originalImageData = ctx.getImageData(0, 0, sourceWidth, sourceHeight);
20741
+      var pipelineState = {
20742
+        sourceWidth: sourceWidth,
20743
+        sourceHeight: sourceHeight,
20744
+        imageData: imageData,
20745
+        originalEl: sourceElement,
20746
+        originalImageData: originalImageData,
20747
+        canvasEl: targetCanvas,
20748
+        ctx: ctx,
20749
+        filterBackend: this,
20750
+      };
20751
+      filters.forEach(function(filter) { filter.applyTo(pipelineState); });
20752
+      if (pipelineState.imageData.width !== sourceWidth || pipelineState.imageData.height !== sourceHeight) {
20753
+        targetCanvas.width = pipelineState.imageData.width;
20754
+        targetCanvas.height = pipelineState.imageData.height;
20755
+      }
20756
+      ctx.putImageData(pipelineState.imageData, 0, 0);
20757
+      return pipelineState;
20758
+    },
20759
+
20760
+  };
20761
+})();
20762
+
20763
+
20764
+/**
20765
+ * @namespace fabric.Image.filters
20766
+ * @memberOf fabric.Image
20767
+ * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#image_filters}
20768
+ * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
20769
+ */
20770
+fabric.Image = fabric.Image || { };
20771
+fabric.Image.filters = fabric.Image.filters || { };
20772
+
20773
+/**
20774
+ * Root filter class from which all filter classes inherit from
20775
+ * @class fabric.Image.filters.BaseFilter
20776
+ * @memberOf fabric.Image.filters
20777
+ */
20778
+fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Image.filters.BaseFilter.prototype */ {
20779
+
20780
+  /**
20781
+   * Filter type
20782
+   * @param {String} type
20783
+   * @default
20784
+   */
20785
+  type: 'BaseFilter',
20786
+
20787
+  /**
20788
+   * Array of attributes to send with buffers. do not modify
20789
+   * @private
20790
+   */
20791
+
20792
+  vertexSource: 'attribute vec2 aPosition;\n' +
20793
+    'varying vec2 vTexCoord;\n' +
20794
+    'void main() {\n' +
20795
+      'vTexCoord = aPosition;\n' +
20796
+      'gl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0);\n' +
20797
+    '}',
20798
+
20799
+  fragmentSource: 'precision highp float;\n' +
20800
+    'varying vec2 vTexCoord;\n' +
20801
+    'uniform sampler2D uTexture;\n' +
20802
+    'void main() {\n' +
20803
+      'gl_FragColor = texture2D(uTexture, vTexCoord);\n' +
20804
+    '}',
20805
+
20806
+  /**
20807
+   * Constructor
20808
+   * @param {Object} [options] Options object
20809
+   */
20810
+  initialize: function(options) {
20811
+    if (options) {
20812
+      this.setOptions(options);
20813
+    }
20814
+  },
20815
+
20816
+  /**
20817
+   * Sets filter's properties from options
20818
+   * @param {Object} [options] Options object
20819
+   */
20820
+  setOptions: function(options) {
20821
+    for (var prop in options) {
20822
+      this[prop] = options[prop];
20823
+    }
20824
+  },
20825
+
20826
+  /**
20827
+   * Compile this filter's shader program.
20828
+   *
20829
+   * @param {WebGLRenderingContext} gl The GL canvas context to use for shader compilation.
20830
+   * @param {String} fragmentSource fragmentShader source for compilation
20831
+   * @param {String} vertexSource vertexShader source for compilation
20832
+   */
20833
+  createProgram: function(gl, fragmentSource, vertexSource) {
20834
+    fragmentSource = fragmentSource || this.fragmentSource;
20835
+    vertexSource = vertexSource || this.vertexSource;
20836
+    if (fabric.webGlPrecision !== 'highp'){
20837
+      fragmentSource = fragmentSource.replace(
20838
+        /precision highp float/g,
20839
+        'precision ' + fabric.webGlPrecision + ' float'
20840
+      );
20841
+    }
20842
+    var vertexShader = gl.createShader(gl.VERTEX_SHADER);
20843
+    gl.shaderSource(vertexShader, vertexSource);
20844
+    gl.compileShader(vertexShader);
20845
+    if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
20846
+      throw new Error(
20847
+        // eslint-disable-next-line prefer-template
20848
+        'Vertex shader compile error for ' + this.type + ': ' +
20849
+        gl.getShaderInfoLog(vertexShader)
20850
+      );
20851
+    }
20852
+
20853
+    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
20854
+    gl.shaderSource(fragmentShader, fragmentSource);
20855
+    gl.compileShader(fragmentShader);
20856
+    if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
20857
+      throw new Error(
20858
+        // eslint-disable-next-line prefer-template
20859
+        'Fragment shader compile error for ' + this.type + ': ' +
20860
+        gl.getShaderInfoLog(fragmentShader)
20861
+      );
20862
+    }
20863
+
20864
+    var program = gl.createProgram();
20865
+    gl.attachShader(program, vertexShader);
20866
+    gl.attachShader(program, fragmentShader);
20867
+    gl.linkProgram(program);
20868
+    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
20869
+      throw new Error(
20870
+        // eslint-disable-next-line prefer-template
20871
+        'Shader link error for "${this.type}" ' +
20872
+        gl.getProgramInfoLog(program)
20873
+      );
20874
+    }
20875
+
20876
+    var attributeLocations = this.getAttributeLocations(gl, program);
20877
+    var uniformLocations = this.getUniformLocations(gl, program) || { };
20878
+    uniformLocations.uStepW = gl.getUniformLocation(program, 'uStepW');
20879
+    uniformLocations.uStepH = gl.getUniformLocation(program, 'uStepH');
20880
+    return {
20881
+      program: program,
20882
+      attributeLocations: attributeLocations,
20883
+      uniformLocations: uniformLocations
20884
+    };
20885
+  },
20886
+
20887
+  /**
20888
+   * Return a map of attribute names to WebGLAttributeLocation objects.
20889
+   *
20890
+   * @param {WebGLRenderingContext} gl The canvas context used to compile the shader program.
20891
+   * @param {WebGLShaderProgram} program The shader program from which to take attribute locations.
20892
+   * @returns {Object} A map of attribute names to attribute locations.
20893
+   */
20894
+  getAttributeLocations: function(gl, program) {
20895
+    return {
20896
+      aPosition: gl.getAttribLocation(program, 'aPosition'),
20897
+    };
20898
+  },
20899
+
20900
+  /**
20901
+   * Return a map of uniform names to WebGLUniformLocation objects.
20902
+   *
20903
+   * Intended to be overridden by subclasses.
20904
+   *
20905
+   * @param {WebGLRenderingContext} gl The canvas context used to compile the shader program.
20906
+   * @param {WebGLShaderProgram} program The shader program from which to take uniform locations.
20907
+   * @returns {Object} A map of uniform names to uniform locations.
20908
+   */
20909
+  getUniformLocations: function (/* gl, program */) {
20910
+    // in case i do not need any special uniform i need to return an empty object
20911
+    return { };
20912
+  },
20913
+
20914
+  /**
20915
+   * Send attribute data from this filter to its shader program on the GPU.
20916
+   *
20917
+   * @param {WebGLRenderingContext} gl The canvas context used to compile the shader program.
20918
+   * @param {Object} attributeLocations A map of shader attribute names to their locations.
20919
+   */
20920
+  sendAttributeData: function(gl, attributeLocations, aPositionData) {
20921
+    var attributeLocation = attributeLocations.aPosition;
20922
+    var buffer = gl.createBuffer();
20923
+    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
20924
+    gl.enableVertexAttribArray(attributeLocation);
20925
+    gl.vertexAttribPointer(attributeLocation, 2, gl.FLOAT, false, 0, 0);
20926
+    gl.bufferData(gl.ARRAY_BUFFER, aPositionData, gl.STATIC_DRAW);
20927
+  },
20928
+
20929
+  _setupFrameBuffer: function(options) {
20930
+    var gl = options.context, width, height;
20931
+    if (options.passes > 1) {
20932
+      width = options.destinationWidth;
20933
+      height = options.destinationHeight;
20934
+      if (options.sourceWidth !== width || options.sourceHeight !== height) {
20935
+        gl.deleteTexture(options.targetTexture);
20936
+        options.targetTexture = options.filterBackend.createTexture(gl, width, height);
20937
+      }
20938
+      gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D,
20939
+        options.targetTexture, 0);
20940
+    }
20941
+    else {
20942
+      // draw last filter on canvas and not to framebuffer.
20943
+      gl.bindFramebuffer(gl.FRAMEBUFFER, null);
20944
+      gl.finish();
20945
+    }
20946
+  },
20947
+
20948
+  _swapTextures: function(options) {
20949
+    options.passes--;
20950
+    options.pass++;
20951
+    var temp = options.targetTexture;
20952
+    options.targetTexture = options.sourceTexture;
20953
+    options.sourceTexture = temp;
20954
+  },
20955
+
20956
+  /**
20957
+   * Generic isNeutral implementation for one parameter based filters.
20958
+   * Used only in image applyFilters to discard filters that will not have an effect
20959
+   * on the image
20960
+   * Other filters may need their own verison ( ColorMatrix, HueRotation, gamma, ComposedFilter )
20961
+   * @param {Object} options
20962
+   **/
20963
+  isNeutralState: function(/* options */) {
20964
+    var main = this.mainParameter,
20965
+        _class = fabric.Image.filters[this.type].prototype;
20966
+    if (main) {
20967
+      if (Array.isArray(_class[main])) {
20968
+        for (var i = _class[main].length; i--;) {
20969
+          if (this[main][i] !== _class[main][i]) {
20970
+            return false;
20971
+          }
20972
+        }
20973
+        return true;
20974
+      }
20975
+      else {
20976
+        return _class[main] === this[main];
20977
+      }
20978
+    }
20979
+    else {
20980
+      return false;
20981
+    }
20982
+  },
20983
+
20984
+  /**
20985
+   * Apply this filter to the input image data provided.
20986
+   *
20987
+   * Determines whether to use WebGL or Canvas2D based on the options.webgl flag.
20988
+   *
20989
+   * @param {Object} options
20990
+   * @param {Number} options.passes The number of filters remaining to be executed
20991
+   * @param {Boolean} options.webgl Whether to use webgl to render the filter.
20992
+   * @param {WebGLTexture} options.sourceTexture The texture setup as the source to be filtered.
20993
+   * @param {WebGLTexture} options.targetTexture The texture where filtered output should be drawn.
20994
+   * @param {WebGLRenderingContext} options.context The GL context used for rendering.
20995
+   * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.
20996
+   */
20997
+  applyTo: function(options) {
20998
+    if (options.webgl) {
20999
+      this._setupFrameBuffer(options);
21000
+      this.applyToWebGL(options);
21001
+      this._swapTextures(options);
21002
+    }
21003
+    else {
21004
+      this.applyTo2d(options);
21005
+    }
21006
+  },
21007
+
21008
+  /**
21009
+   * Retrieves the cached shader.
21010
+   * @param {Object} options
21011
+   * @param {WebGLRenderingContext} options.context The GL context used for rendering.
21012
+   * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.
21013
+   */
21014
+  retrieveShader: function(options) {
21015
+    if (!options.programCache.hasOwnProperty(this.type)) {
21016
+      options.programCache[this.type] = this.createProgram(options.context);
21017
+    }
21018
+    return options.programCache[this.type];
21019
+  },
21020
+
21021
+  /**
21022
+   * Apply this filter using webgl.
21023
+   *
21024
+   * @param {Object} options
21025
+   * @param {Number} options.passes The number of filters remaining to be executed
21026
+   * @param {Boolean} options.webgl Whether to use webgl to render the filter.
21027
+   * @param {WebGLTexture} options.originalTexture The texture of the original input image.
21028
+   * @param {WebGLTexture} options.sourceTexture The texture setup as the source to be filtered.
21029
+   * @param {WebGLTexture} options.targetTexture The texture where filtered output should be drawn.
21030
+   * @param {WebGLRenderingContext} options.context The GL context used for rendering.
21031
+   * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.
21032
+   */
21033
+  applyToWebGL: function(options) {
21034
+    var gl = options.context;
21035
+    var shader = this.retrieveShader(options);
21036
+    if (options.pass === 0 && options.originalTexture) {
21037
+      gl.bindTexture(gl.TEXTURE_2D, options.originalTexture);
21038
+    }
21039
+    else {
21040
+      gl.bindTexture(gl.TEXTURE_2D, options.sourceTexture);
21041
+    }
21042
+    gl.useProgram(shader.program);
21043
+    this.sendAttributeData(gl, shader.attributeLocations, options.aPosition);
21044
+
21045
+    gl.uniform1f(shader.uniformLocations.uStepW, 1 / options.sourceWidth);
21046
+    gl.uniform1f(shader.uniformLocations.uStepH, 1 / options.sourceHeight);
21047
+
21048
+    this.sendUniformData(gl, shader.uniformLocations);
21049
+    gl.viewport(0, 0, options.destinationWidth, options.destinationHeight);
21050
+    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
21051
+  },
21052
+
21053
+  bindAdditionalTexture: function(gl, texture, textureUnit) {
21054
+    gl.activeTexture(textureUnit);
21055
+    gl.bindTexture(gl.TEXTURE_2D, texture);
21056
+    // reset active texture to 0 as usual
21057
+    gl.activeTexture(gl.TEXTURE0);
21058
+  },
21059
+
21060
+  unbindAdditionalTexture: function(gl, textureUnit) {
21061
+    gl.activeTexture(textureUnit);
21062
+    gl.bindTexture(gl.TEXTURE_2D, null);
21063
+    gl.activeTexture(gl.TEXTURE0);
21064
+  },
21065
+
21066
+  getMainParameter: function() {
21067
+    return this[this.mainParameter];
21068
+  },
21069
+
21070
+  setMainParameter: function(value) {
21071
+    this[this.mainParameter] = value;
21072
+  },
21073
+
21074
+  /**
21075
+   * Send uniform data from this filter to its shader program on the GPU.
21076
+   *
21077
+   * Intended to be overridden by subclasses.
21078
+   *
21079
+   * @param {WebGLRenderingContext} gl The canvas context used to compile the shader program.
21080
+   * @param {Object} uniformLocations A map of shader uniform names to their locations.
21081
+   */
21082
+  sendUniformData: function(/* gl, uniformLocations */) {
21083
+    // Intentionally left blank.  Override me in subclasses.
21084
+  },
21085
+
21086
+  /**
21087
+   * If needed by a 2d filter, this functions can create an helper canvas to be used
21088
+   * remember that options.targetCanvas is available for use till end of chain.
21089
+   */
21090
+  createHelpLayer: function(options) {
21091
+    if (!options.helpLayer) {
21092
+      var helpLayer = document.createElement('canvas');
21093
+      helpLayer.width = options.sourceWidth;
21094
+      helpLayer.height = options.sourceHeight;
21095
+      options.helpLayer = helpLayer;
21096
+    }
21097
+  },
21098
+
21099
+  /**
21100
+   * Returns object representation of an instance
21101
+   * @return {Object} Object representation of an instance
21102
+   */
21103
+  toObject: function() {
21104
+    var object = { type: this.type }, mainP = this.mainParameter;
21105
+    if (mainP) {
21106
+      object[mainP] = this[mainP];
21107
+    }
21108
+    return object;
21109
+  },
21110
+
21111
+  /**
21112
+   * Returns a JSON representation of an instance
21113
+   * @return {Object} JSON
21114
+   */
21115
+  toJSON: function() {
21116
+    // delegate, not alias
21117
+    return this.toObject();
21118
+  }
21119
+});
21120
+
21121
+fabric.Image.filters.BaseFilter.fromObject = function(object, callback) {
21122
+  var filter = new fabric.Image.filters[object.type](object);
21123
+  callback && callback(filter);
21124
+  return filter;
21125
+};
21126
+
21127
+
21128
+(function(global) {
21129
+
21130
+  'use strict';
21131
+
21132
+  var fabric  = global.fabric || (global.fabric = { }),
21133
+      filters = fabric.Image.filters,
21134
+      createClass = fabric.util.createClass;
21135
+
21136
+  /**
21137
+   * Color Matrix filter class
21138
+   * @class fabric.Image.filters.ColorMatrix
21139
+   * @memberOf fabric.Image.filters
21140
+   * @extends fabric.Image.filters.BaseFilter
21141
+   * @see {@link fabric.Image.filters.ColorMatrix#initialize} for constructor definition
21142
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
21143
+   * @see {@Link http://www.webwasp.co.uk/tutorials/219/Color_Matrix_Filter.php}
21144
+   * @see {@Link http://phoboslab.org/log/2013/11/fast-image-filters-with-webgl}
21145
+   * @example <caption>Kodachrome filter</caption>
21146
+   * var filter = new fabric.Image.filters.ColorMatrix({
21147
+   *  matrix: [
21148
+       1.1285582396593525, -0.3967382283601348, -0.03992559172921793, 0, 63.72958762196502,
21149
+       -0.16404339962244616, 1.0835251566291304, -0.05498805115633132, 0, 24.732407896706203,
21150
+       -0.16786010706155763, -0.5603416277695248, 1.6014850761964943, 0, 35.62982807460946,
21151
+       0, 0, 0, 1, 0
21152
+      ]
21153
+   * });
21154
+   * object.filters.push(filter);
21155
+   * object.applyFilters();
21156
+   */
21157
+  filters.ColorMatrix = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.ColorMatrix.prototype */ {
21158
+
21159
+    /**
21160
+     * Filter type
21161
+     * @param {String} type
21162
+     * @default
21163
+     */
21164
+    type: 'ColorMatrix',
21165
+
21166
+    fragmentSource: 'precision highp float;\n' +
21167
+      'uniform sampler2D uTexture;\n' +
21168
+      'varying vec2 vTexCoord;\n' +
21169
+      'uniform mat4 uColorMatrix;\n' +
21170
+      'uniform vec4 uConstants;\n' +
21171
+      'void main() {\n' +
21172
+        'vec4 color = texture2D(uTexture, vTexCoord);\n' +
21173
+        'color *= uColorMatrix;\n' +
21174
+        'color += uConstants;\n' +
21175
+        'gl_FragColor = color;\n' +
21176
+      '}',
21177
+
21178
+    /**
21179
+     * Colormatrix for pixels.
21180
+     * array of 20 floats. Numbers in positions 4, 9, 14, 19 loose meaning
21181
+     * outside the -1, 1 range.
21182
+     * 0.0039215686 is the part of 1 that get translated to 1 in 2d
21183
+     * @param {Array} matrix array of 20 numbers.
21184
+     * @default
21185
+     */
21186
+    matrix: [
21187
+      1, 0, 0, 0, 0,
21188
+      0, 1, 0, 0, 0,
21189
+      0, 0, 1, 0, 0,
21190
+      0, 0, 0, 1, 0
21191
+    ],
21192
+
21193
+    mainParameter: 'matrix',
21194
+
21195
+    /**
21196
+     * Lock the colormatrix on the color part, skipping alpha, manly for non webgl scenario
21197
+     * to save some calculation
21198
+     */
21199
+    colorsOnly: true,
21200
+
21201
+    /**
21202
+     * Constructor
21203
+     * @param {Object} [options] Options object
21204
+     */
21205
+    initialize: function(options) {
21206
+      this.callSuper('initialize', options);
21207
+      // create a new array instead mutating the prototype with push
21208
+      this.matrix = this.matrix.slice(0);
21209
+    },
21210
+
21211
+    /**
21212
+     * Apply the ColorMatrix operation to a Uint8Array representing the pixels of an image.
21213
+     *
21214
+     * @param {Object} options
21215
+     * @param {ImageData} options.imageData The Uint8Array to be filtered.
21216
+     */
21217
+    applyTo2d: function(options) {
21218
+      var imageData = options.imageData,
21219
+          data = imageData.data,
21220
+          iLen = data.length,
21221
+          m = this.matrix,
21222
+          r, g, b, a, i, colorsOnly = this.colorsOnly;
21223
+
21224
+      for (i = 0; i < iLen; i += 4) {
21225
+        r = data[i];
21226
+        g = data[i + 1];
21227
+        b = data[i + 2];
21228
+        if (colorsOnly) {
21229
+          data[i] = r * m[0] + g * m[1] + b * m[2] + m[4] * 255;
21230
+          data[i + 1] = r * m[5] + g * m[6] + b * m[7] + m[9] * 255;
21231
+          data[i + 2] = r * m[10] + g * m[11] + b * m[12] + m[14] * 255;
21232
+        }
21233
+        else {
21234
+          a = data[i + 3];
21235
+          data[i] = r * m[0] + g * m[1] + b * m[2] + a * m[3] + m[4] * 255;
21236
+          data[i + 1] = r * m[5] + g * m[6] + b * m[7] + a * m[8] + m[9] * 255;
21237
+          data[i + 2] = r * m[10] + g * m[11] + b * m[12] + a * m[13] + m[14] * 255;
21238
+          data[i + 3] = r * m[15] + g * m[16] + b * m[17] + a * m[18] + m[19] * 255;
21239
+        }
21240
+      }
21241
+    },
21242
+
21243
+    /**
21244
+     * Return WebGL uniform locations for this filter's shader.
21245
+     *
21246
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
21247
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
21248
+     */
21249
+    getUniformLocations: function(gl, program) {
21250
+      return {
21251
+        uColorMatrix: gl.getUniformLocation(program, 'uColorMatrix'),
21252
+        uConstants: gl.getUniformLocation(program, 'uConstants'),
21253
+      };
21254
+    },
21255
+
21256
+    /**
21257
+     * Send data from this filter to its shader program's uniforms.
21258
+     *
21259
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
21260
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
21261
+     */
21262
+    sendUniformData: function(gl, uniformLocations) {
21263
+      var m = this.matrix,
21264
+          matrix = [
21265
+            m[0], m[1], m[2], m[3],
21266
+            m[5], m[6], m[7], m[8],
21267
+            m[10], m[11], m[12], m[13],
21268
+            m[15], m[16], m[17], m[18]
21269
+          ],
21270
+          constants = [m[4], m[9], m[14], m[19]];
21271
+      gl.uniformMatrix4fv(uniformLocations.uColorMatrix, false, matrix);
21272
+      gl.uniform4fv(uniformLocations.uConstants, constants);
21273
+    },
21274
+  });
21275
+
21276
+  /**
21277
+   * Returns filter instance from an object representation
21278
+   * @static
21279
+   * @param {Object} object Object to create an instance from
21280
+   * @param {function} [callback] function to invoke after filter creation
21281
+   * @return {fabric.Image.filters.ColorMatrix} Instance of fabric.Image.filters.ColorMatrix
21282
+   */
21283
+  fabric.Image.filters.ColorMatrix.fromObject = fabric.Image.filters.BaseFilter.fromObject;
21284
+})(typeof exports !== 'undefined' ? exports : this);
21285
+
21286
+
21287
+(function(global) {
21288
+
21289
+  'use strict';
21290
+
21291
+  var fabric  = global.fabric || (global.fabric = { }),
21292
+      filters = fabric.Image.filters,
21293
+      createClass = fabric.util.createClass;
21294
+
21295
+  /**
21296
+   * Brightness filter class
21297
+   * @class fabric.Image.filters.Brightness
21298
+   * @memberOf fabric.Image.filters
21299
+   * @extends fabric.Image.filters.BaseFilter
21300
+   * @see {@link fabric.Image.filters.Brightness#initialize} for constructor definition
21301
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
21302
+   * @example
21303
+   * var filter = new fabric.Image.filters.Brightness({
21304
+   *   brightness: 0.05
21305
+   * });
21306
+   * object.filters.push(filter);
21307
+   * object.applyFilters();
21308
+   */
21309
+  filters.Brightness = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Brightness.prototype */ {
21310
+
21311
+    /**
21312
+     * Filter type
21313
+     * @param {String} type
21314
+     * @default
21315
+     */
21316
+    type: 'Brightness',
21317
+
21318
+    /**
21319
+     * Fragment source for the brightness program
21320
+     */
21321
+    fragmentSource: 'precision highp float;\n' +
21322
+      'uniform sampler2D uTexture;\n' +
21323
+      'uniform float uBrightness;\n' +
21324
+      'varying vec2 vTexCoord;\n' +
21325
+      'void main() {\n' +
21326
+        'vec4 color = texture2D(uTexture, vTexCoord);\n' +
21327
+        'color.rgb += uBrightness;\n' +
21328
+        'gl_FragColor = color;\n' +
21329
+      '}',
21330
+
21331
+    /**
21332
+     * Brightness value, from -1 to 1.
21333
+     * translated to -255 to 255 for 2d
21334
+     * 0.0039215686 is the part of 1 that get translated to 1 in 2d
21335
+     * @param {Number} brightness
21336
+     * @default
21337
+     */
21338
+    brightness: 0,
21339
+
21340
+    /**
21341
+     * Describe the property that is the filter parameter
21342
+     * @param {String} m
21343
+     * @default
21344
+     */
21345
+    mainParameter: 'brightness',
21346
+
21347
+    /**
21348
+    * Apply the Brightness operation to a Uint8ClampedArray representing the pixels of an image.
21349
+    *
21350
+    * @param {Object} options
21351
+    * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.
21352
+    */
21353
+    applyTo2d: function(options) {
21354
+      if (this.brightness === 0) {
21355
+        return;
21356
+      }
21357
+      var imageData = options.imageData,
21358
+          data = imageData.data, i, len = data.length,
21359
+          brightness = Math.round(this.brightness * 255);
21360
+      for (i = 0; i < len; i += 4) {
21361
+        data[i] = data[i] + brightness;
21362
+        data[i + 1] = data[i + 1] + brightness;
21363
+        data[i + 2] = data[i + 2] + brightness;
21364
+      }
21365
+    },
21366
+
21367
+    /**
21368
+     * Return WebGL uniform locations for this filter's shader.
21369
+     *
21370
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
21371
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
21372
+     */
21373
+    getUniformLocations: function(gl, program) {
21374
+      return {
21375
+        uBrightness: gl.getUniformLocation(program, 'uBrightness'),
21376
+      };
21377
+    },
21378
+
21379
+    /**
21380
+     * Send data from this filter to its shader program's uniforms.
21381
+     *
21382
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
21383
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
21384
+     */
21385
+    sendUniformData: function(gl, uniformLocations) {
21386
+      gl.uniform1f(uniformLocations.uBrightness, this.brightness);
21387
+    },
21388
+  });
21389
+
21390
+  /**
21391
+   * Returns filter instance from an object representation
21392
+   * @static
21393
+   * @param {Object} object Object to create an instance from
21394
+   * @param {function} [callback] to be invoked after filter creation
21395
+   * @return {fabric.Image.filters.Brightness} Instance of fabric.Image.filters.Brightness
21396
+   */
21397
+  fabric.Image.filters.Brightness.fromObject = fabric.Image.filters.BaseFilter.fromObject;
21398
+
21399
+})(typeof exports !== 'undefined' ? exports : this);
21400
+
21401
+
21402
+(function(global) {
21403
+
21404
+  'use strict';
21405
+
21406
+  var fabric  = global.fabric || (global.fabric = { }),
21407
+      extend = fabric.util.object.extend,
21408
+      filters = fabric.Image.filters,
21409
+      createClass = fabric.util.createClass;
21410
+
21411
+  /**
21412
+   * Adapted from <a href="http://www.html5rocks.com/en/tutorials/canvas/imagefilters/">html5rocks article</a>
21413
+   * @class fabric.Image.filters.Convolute
21414
+   * @memberOf fabric.Image.filters
21415
+   * @extends fabric.Image.filters.BaseFilter
21416
+   * @see {@link fabric.Image.filters.Convolute#initialize} for constructor definition
21417
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
21418
+   * @example <caption>Sharpen filter</caption>
21419
+   * var filter = new fabric.Image.filters.Convolute({
21420
+   *   matrix: [ 0, -1,  0,
21421
+   *            -1,  5, -1,
21422
+   *             0, -1,  0 ]
21423
+   * });
21424
+   * object.filters.push(filter);
21425
+   * object.applyFilters();
21426
+   * canvas.renderAll();
21427
+   * @example <caption>Blur filter</caption>
21428
+   * var filter = new fabric.Image.filters.Convolute({
21429
+   *   matrix: [ 1/9, 1/9, 1/9,
21430
+   *             1/9, 1/9, 1/9,
21431
+   *             1/9, 1/9, 1/9 ]
21432
+   * });
21433
+   * object.filters.push(filter);
21434
+   * object.applyFilters();
21435
+   * canvas.renderAll();
21436
+   * @example <caption>Emboss filter</caption>
21437
+   * var filter = new fabric.Image.filters.Convolute({
21438
+   *   matrix: [ 1,   1,  1,
21439
+   *             1, 0.7, -1,
21440
+   *            -1,  -1, -1 ]
21441
+   * });
21442
+   * object.filters.push(filter);
21443
+   * object.applyFilters();
21444
+   * canvas.renderAll();
21445
+   * @example <caption>Emboss filter with opaqueness</caption>
21446
+   * var filter = new fabric.Image.filters.Convolute({
21447
+   *   opaque: true,
21448
+   *   matrix: [ 1,   1,  1,
21449
+   *             1, 0.7, -1,
21450
+   *            -1,  -1, -1 ]
21451
+   * });
21452
+   * object.filters.push(filter);
21453
+   * object.applyFilters();
21454
+   * canvas.renderAll();
21455
+   */
21456
+  filters.Convolute = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Convolute.prototype */ {
21457
+
21458
+    /**
21459
+     * Filter type
21460
+     * @param {String} type
21461
+     * @default
21462
+     */
21463
+    type: 'Convolute',
21464
+
21465
+    /*
21466
+     * Opaque value (true/false)
21467
+     */
21468
+    opaque: false,
21469
+
21470
+    /*
21471
+     * matrix for the filter, max 9x9
21472
+     */
21473
+    matrix: [0, 0, 0, 0, 1, 0, 0, 0, 0],
21474
+
21475
+    /**
21476
+     * Fragment source for the brightness program
21477
+     */
21478
+    fragmentSource: {
21479
+      Convolute_3_1: 'precision highp float;\n' +
21480
+        'uniform sampler2D uTexture;\n' +
21481
+        'uniform float uMatrix[9];\n' +
21482
+        'uniform float uStepW;\n' +
21483
+        'uniform float uStepH;\n' +
21484
+        'varying vec2 vTexCoord;\n' +
21485
+        'void main() {\n' +
21486
+          'vec4 color = vec4(0, 0, 0, 0);\n' +
21487
+          'for (float h = 0.0; h < 3.0; h+=1.0) {\n' +
21488
+            'for (float w = 0.0; w < 3.0; w+=1.0) {\n' +
21489
+              'vec2 matrixPos = vec2(uStepW * (w - 1), uStepH * (h - 1));\n' +
21490
+              'color += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 3.0 + w)];\n' +
21491
+            '}\n' +
21492
+          '}\n' +
21493
+          'gl_FragColor = color;\n' +
21494
+        '}',
21495
+      Convolute_3_0: 'precision highp float;\n' +
21496
+        'uniform sampler2D uTexture;\n' +
21497
+        'uniform float uMatrix[9];\n' +
21498
+        'uniform float uStepW;\n' +
21499
+        'uniform float uStepH;\n' +
21500
+        'varying vec2 vTexCoord;\n' +
21501
+        'void main() {\n' +
21502
+          'vec4 color = vec4(0, 0, 0, 1);\n' +
21503
+          'for (float h = 0.0; h < 3.0; h+=1.0) {\n' +
21504
+            'for (float w = 0.0; w < 3.0; w+=1.0) {\n' +
21505
+              'vec2 matrixPos = vec2(uStepW * (w - 1.0), uStepH * (h - 1.0));\n' +
21506
+              'color.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 3.0 + w)];\n' +
21507
+            '}\n' +
21508
+          '}\n' +
21509
+          'float alpha = texture2D(uTexture, vTexCoord).a;\n' +
21510
+          'gl_FragColor = color;\n' +
21511
+          'gl_FragColor.a = alpha;\n' +
21512
+        '}',
21513
+      Convolute_5_1: 'precision highp float;\n' +
21514
+        'uniform sampler2D uTexture;\n' +
21515
+        'uniform float uMatrix[25];\n' +
21516
+        'uniform float uStepW;\n' +
21517
+        'uniform float uStepH;\n' +
21518
+        'varying vec2 vTexCoord;\n' +
21519
+        'void main() {\n' +
21520
+          'vec4 color = vec4(0, 0, 0, 0);\n' +
21521
+          'for (float h = 0.0; h < 5.0; h+=1.0) {\n' +
21522
+            'for (float w = 0.0; w < 5.0; w+=1.0) {\n' +
21523
+              'vec2 matrixPos = vec2(uStepW * (w - 2.0), uStepH * (h - 2.0));\n' +
21524
+              'color += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 5.0 + w)];\n' +
21525
+            '}\n' +
21526
+          '}\n' +
21527
+          'gl_FragColor = color;\n' +
21528
+        '}',
21529
+      Convolute_5_0: 'precision highp float;\n' +
21530
+        'uniform sampler2D uTexture;\n' +
21531
+        'uniform float uMatrix[25];\n' +
21532
+        'uniform float uStepW;\n' +
21533
+        'uniform float uStepH;\n' +
21534
+        'varying vec2 vTexCoord;\n' +
21535
+        'void main() {\n' +
21536
+          'vec4 color = vec4(0, 0, 0, 1);\n' +
21537
+          'for (float h = 0.0; h < 5.0; h+=1.0) {\n' +
21538
+            'for (float w = 0.0; w < 5.0; w+=1.0) {\n' +
21539
+              'vec2 matrixPos = vec2(uStepW * (w - 2.0), uStepH * (h - 2.0));\n' +
21540
+              'color.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 5.0 + w)];\n' +
21541
+            '}\n' +
21542
+          '}\n' +
21543
+          'float alpha = texture2D(uTexture, vTexCoord).a;\n' +
21544
+          'gl_FragColor = color;\n' +
21545
+          'gl_FragColor.a = alpha;\n' +
21546
+        '}',
21547
+      Convolute_7_1: 'precision highp float;\n' +
21548
+        'uniform sampler2D uTexture;\n' +
21549
+        'uniform float uMatrix[49];\n' +
21550
+        'uniform float uStepW;\n' +
21551
+        'uniform float uStepH;\n' +
21552
+        'varying vec2 vTexCoord;\n' +
21553
+        'void main() {\n' +
21554
+          'vec4 color = vec4(0, 0, 0, 0);\n' +
21555
+          'for (float h = 0.0; h < 7.0; h+=1.0) {\n' +
21556
+            'for (float w = 0.0; w < 7.0; w+=1.0) {\n' +
21557
+              'vec2 matrixPos = vec2(uStepW * (w - 3.0), uStepH * (h - 3.0));\n' +
21558
+              'color += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 7.0 + w)];\n' +
21559
+            '}\n' +
21560
+          '}\n' +
21561
+          'gl_FragColor = color;\n' +
21562
+        '}',
21563
+      Convolute_7_0: 'precision highp float;\n' +
21564
+        'uniform sampler2D uTexture;\n' +
21565
+        'uniform float uMatrix[49];\n' +
21566
+        'uniform float uStepW;\n' +
21567
+        'uniform float uStepH;\n' +
21568
+        'varying vec2 vTexCoord;\n' +
21569
+        'void main() {\n' +
21570
+          'vec4 color = vec4(0, 0, 0, 1);\n' +
21571
+          'for (float h = 0.0; h < 7.0; h+=1.0) {\n' +
21572
+            'for (float w = 0.0; w < 7.0; w+=1.0) {\n' +
21573
+              'vec2 matrixPos = vec2(uStepW * (w - 3.0), uStepH * (h - 3.0));\n' +
21574
+              'color.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 7.0 + w)];\n' +
21575
+            '}\n' +
21576
+          '}\n' +
21577
+          'float alpha = texture2D(uTexture, vTexCoord).a;\n' +
21578
+          'gl_FragColor = color;\n' +
21579
+          'gl_FragColor.a = alpha;\n' +
21580
+        '}',
21581
+      Convolute_9_1: 'precision highp float;\n' +
21582
+        'uniform sampler2D uTexture;\n' +
21583
+        'uniform float uMatrix[81];\n' +
21584
+        'uniform float uStepW;\n' +
21585
+        'uniform float uStepH;\n' +
21586
+        'varying vec2 vTexCoord;\n' +
21587
+        'void main() {\n' +
21588
+          'vec4 color = vec4(0, 0, 0, 0);\n' +
21589
+          'for (float h = 0.0; h < 9.0; h+=1.0) {\n' +
21590
+            'for (float w = 0.0; w < 9.0; w+=1.0) {\n' +
21591
+              'vec2 matrixPos = vec2(uStepW * (w - 4.0), uStepH * (h - 4.0));\n' +
21592
+              'color += texture2D(uTexture, vTexCoord + matrixPos) * uMatrix[int(h * 9.0 + w)];\n' +
21593
+            '}\n' +
21594
+          '}\n' +
21595
+          'gl_FragColor = color;\n' +
21596
+        '}',
21597
+      Convolute_9_0: 'precision highp float;\n' +
21598
+        'uniform sampler2D uTexture;\n' +
21599
+        'uniform float uMatrix[81];\n' +
21600
+        'uniform float uStepW;\n' +
21601
+        'uniform float uStepH;\n' +
21602
+        'varying vec2 vTexCoord;\n' +
21603
+        'void main() {\n' +
21604
+          'vec4 color = vec4(0, 0, 0, 1);\n' +
21605
+          'for (float h = 0.0; h < 9.0; h+=1.0) {\n' +
21606
+            'for (float w = 0.0; w < 9.0; w+=1.0) {\n' +
21607
+              'vec2 matrixPos = vec2(uStepW * (w - 4.0), uStepH * (h - 4.0));\n' +
21608
+              'color.rgb += texture2D(uTexture, vTexCoord + matrixPos).rgb * uMatrix[int(h * 9.0 + w)];\n' +
21609
+            '}\n' +
21610
+          '}\n' +
21611
+          'float alpha = texture2D(uTexture, vTexCoord).a;\n' +
21612
+          'gl_FragColor = color;\n' +
21613
+          'gl_FragColor.a = alpha;\n' +
21614
+        '}',
21615
+    },
21616
+
21617
+    /**
21618
+     * Constructor
21619
+     * @memberOf fabric.Image.filters.Convolute.prototype
21620
+     * @param {Object} [options] Options object
21621
+     * @param {Boolean} [options.opaque=false] Opaque value (true/false)
21622
+     * @param {Array} [options.matrix] Filter matrix
21623
+     */
21624
+
21625
+
21626
+    /**
21627
+    * Retrieves the cached shader.
21628
+    * @param {Object} options
21629
+    * @param {WebGLRenderingContext} options.context The GL context used for rendering.
21630
+    * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.
21631
+    */
21632
+    retrieveShader: function(options) {
21633
+      var size = Math.sqrt(this.matrix.length);
21634
+      var cacheKey = this.type + '_' + size + '_' + (this.opaque ? 1 : 0);
21635
+      var shaderSource = this.fragmentSource[cacheKey];
21636
+      if (!options.programCache.hasOwnProperty(cacheKey)) {
21637
+        options.programCache[cacheKey] = this.createProgram(options.context, shaderSource);
21638
+      }
21639
+      return options.programCache[cacheKey];
21640
+    },
21641
+
21642
+    /**
21643
+     * Apply the Brightness operation to a Uint8ClampedArray representing the pixels of an image.
21644
+     *
21645
+     * @param {Object} options
21646
+     * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.
21647
+     */
21648
+    applyTo2d: function(options) {
21649
+      var imageData = options.imageData,
21650
+          data = imageData.data,
21651
+          weights = this.matrix,
21652
+          side = Math.round(Math.sqrt(weights.length)),
21653
+          halfSide = Math.floor(side / 2),
21654
+          sw = imageData.width,
21655
+          sh = imageData.height,
21656
+          output = options.ctx.createImageData(sw, sh),
21657
+          dst = output.data,
21658
+          // go through the destination image pixels
21659
+          alphaFac = this.opaque ? 1 : 0,
21660
+          r, g, b, a, dstOff,
21661
+          scx, scy, srcOff, wt,
21662
+          x, y, cx, cy;
21663
+
21664
+      for (y = 0; y < sh; y++) {
21665
+        for (x = 0; x < sw; x++) {
21666
+          dstOff = (y * sw + x) * 4;
21667
+          // calculate the weighed sum of the source image pixels that
21668
+          // fall under the convolution matrix
21669
+          r = 0; g = 0; b = 0; a = 0;
21670
+
21671
+          for (cy = 0; cy < side; cy++) {
21672
+            for (cx = 0; cx < side; cx++) {
21673
+              scy = y + cy - halfSide;
21674
+              scx = x + cx - halfSide;
21675
+
21676
+              // eslint-disable-next-line max-depth
21677
+              if (scy < 0 || scy > sh || scx < 0 || scx > sw) {
21678
+                continue;
21679
+              }
21680
+
21681
+              srcOff = (scy * sw + scx) * 4;
21682
+              wt = weights[cy * side + cx];
21683
+
21684
+              r += data[srcOff] * wt;
21685
+              g += data[srcOff + 1] * wt;
21686
+              b += data[srcOff + 2] * wt;
21687
+              // eslint-disable-next-line max-depth
21688
+              if (!alphaFac) {
21689
+                a += data[srcOff + 3] * wt;
21690
+              }
21691
+            }
21692
+          }
21693
+          dst[dstOff] = r;
21694
+          dst[dstOff + 1] = g;
21695
+          dst[dstOff + 2] = b;
21696
+          if (!alphaFac) {
21697
+            dst[dstOff + 3] = a;
21698
+          }
21699
+          else {
21700
+            dst[dstOff + 3] = data[dstOff + 3];
21701
+          }
21702
+        }
21703
+      }
21704
+      options.imageData = output;
21705
+    },
21706
+
21707
+    /**
21708
+     * Return WebGL uniform locations for this filter's shader.
21709
+     *
21710
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
21711
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
21712
+     */
21713
+    getUniformLocations: function(gl, program) {
21714
+      return {
21715
+        uMatrix: gl.getUniformLocation(program, 'uMatrix'),
21716
+        uOpaque: gl.getUniformLocation(program, 'uOpaque'),
21717
+        uHalfSize: gl.getUniformLocation(program, 'uHalfSize'),
21718
+        uSize: gl.getUniformLocation(program, 'uSize'),
21719
+      };
21720
+    },
21721
+
21722
+    /**
21723
+     * Send data from this filter to its shader program's uniforms.
21724
+     *
21725
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
21726
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
21727
+     */
21728
+    sendUniformData: function(gl, uniformLocations) {
21729
+      gl.uniform1fv(uniformLocations.uMatrix, this.matrix);
21730
+    },
21731
+
21732
+    /**
21733
+     * Returns object representation of an instance
21734
+     * @return {Object} Object representation of an instance
21735
+     */
21736
+    toObject: function() {
21737
+      return extend(this.callSuper('toObject'), {
21738
+        opaque: this.opaque,
21739
+        matrix: this.matrix
21740
+      });
21741
+    }
21742
+  });
21743
+
21744
+  /**
21745
+   * Returns filter instance from an object representation
21746
+   * @static
21747
+   * @param {Object} object Object to create an instance from
21748
+   * @param {function} [callback] to be invoked after filter creation
21749
+   * @return {fabric.Image.filters.Convolute} Instance of fabric.Image.filters.Convolute
21750
+   */
21751
+  fabric.Image.filters.Convolute.fromObject = fabric.Image.filters.BaseFilter.fromObject;
21752
+
21753
+})(typeof exports !== 'undefined' ? exports : this);
21754
+
21755
+
21756
+(function(global) {
21757
+
21758
+  'use strict';
21759
+
21760
+  var fabric  = global.fabric || (global.fabric = { }),
21761
+      filters = fabric.Image.filters,
21762
+      createClass = fabric.util.createClass;
21763
+
21764
+  /**
21765
+   * Grayscale image filter class
21766
+   * @class fabric.Image.filters.Grayscale
21767
+   * @memberOf fabric.Image.filters
21768
+   * @extends fabric.Image.filters.BaseFilter
21769
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
21770
+   * @example
21771
+   * var filter = new fabric.Image.filters.Grayscale();
21772
+   * object.filters.push(filter);
21773
+   * object.applyFilters();
21774
+   */
21775
+  filters.Grayscale = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Grayscale.prototype */ {
21776
+
21777
+    /**
21778
+     * Filter type
21779
+     * @param {String} type
21780
+     * @default
21781
+     */
21782
+    type: 'Grayscale',
21783
+
21784
+    fragmentSource: {
21785
+      average: 'precision highp float;\n' +
21786
+        'uniform sampler2D uTexture;\n' +
21787
+        'varying vec2 vTexCoord;\n' +
21788
+        'void main() {\n' +
21789
+          'vec4 color = texture2D(uTexture, vTexCoord);\n' +
21790
+          'float average = (color.r + color.b + color.g) / 3.0;\n' +
21791
+          'gl_FragColor = vec4(average, average, average, color.a);\n' +
21792
+        '}',
21793
+      lightness: 'precision highp float;\n' +
21794
+        'uniform sampler2D uTexture;\n' +
21795
+        'uniform int uMode;\n' +
21796
+        'varying vec2 vTexCoord;\n' +
21797
+        'void main() {\n' +
21798
+          'vec4 col = texture2D(uTexture, vTexCoord);\n' +
21799
+          'float average = (max(max(col.r, col.g),col.b) + min(min(col.r, col.g),col.b)) / 2.0;\n' +
21800
+          'gl_FragColor = vec4(average, average, average, col.a);\n' +
21801
+        '}',
21802
+      luminosity: 'precision highp float;\n' +
21803
+        'uniform sampler2D uTexture;\n' +
21804
+        'uniform int uMode;\n' +
21805
+        'varying vec2 vTexCoord;\n' +
21806
+        'void main() {\n' +
21807
+          'vec4 col = texture2D(uTexture, vTexCoord);\n' +
21808
+          'float average = 0.21 * col.r + 0.72 * col.g + 0.07 * col.b;\n' +
21809
+          'gl_FragColor = vec4(average, average, average, col.a);\n' +
21810
+        '}',
21811
+    },
21812
+
21813
+
21814
+    /**
21815
+     * Grayscale mode, between 'average', 'lightness', 'luminosity'
21816
+     * @param {String} type
21817
+     * @default
21818
+     */
21819
+    mode: 'average',
21820
+
21821
+    mainParameter: 'mode',
21822
+
21823
+    /**
21824
+     * Apply the Grayscale operation to a Uint8Array representing the pixels of an image.
21825
+     *
21826
+     * @param {Object} options
21827
+     * @param {ImageData} options.imageData The Uint8Array to be filtered.
21828
+     */
21829
+    applyTo2d: function(options) {
21830
+      var imageData = options.imageData,
21831
+          data = imageData.data, i,
21832
+          len = data.length, value,
21833
+          mode = this.mode;
21834
+      for (i = 0; i < len; i += 4) {
21835
+        if (mode === 'average') {
21836
+          value = (data[i] + data[i + 1] + data[i + 2]) / 3;
21837
+        }
21838
+        else if (mode === 'lightness') {
21839
+          value = (Math.min(data[i], data[i + 1], data[i + 2]) +
21840
+            Math.max(data[i], data[i + 1], data[i + 2])) / 2;
21841
+        }
21842
+        else if (mode === 'luminosity') {
21843
+          value = 0.21 * data[i] + 0.72 * data[i + 1] + 0.07 * data[i + 2];
21844
+        }
21845
+        data[i] = value;
21846
+        data[i + 1] = value;
21847
+        data[i + 2] = value;
21848
+      }
21849
+    },
21850
+
21851
+    /**
21852
+     * Retrieves the cached shader.
21853
+     * @param {Object} options
21854
+     * @param {WebGLRenderingContext} options.context The GL context used for rendering.
21855
+     * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.
21856
+     */
21857
+    retrieveShader: function(options) {
21858
+      var cacheKey = this.type + '_' + this.mode;
21859
+      if (!options.programCache.hasOwnProperty(cacheKey)) {
21860
+        var shaderSource = this.fragmentSource[this.mode];
21861
+        options.programCache[cacheKey] = this.createProgram(options.context, shaderSource);
21862
+      }
21863
+      return options.programCache[cacheKey];
21864
+    },
21865
+
21866
+    /**
21867
+     * Return WebGL uniform locations for this filter's shader.
21868
+     *
21869
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
21870
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
21871
+     */
21872
+    getUniformLocations: function(gl, program) {
21873
+      return {
21874
+        uMode: gl.getUniformLocation(program, 'uMode'),
21875
+      };
21876
+    },
21877
+
21878
+    /**
21879
+     * Send data from this filter to its shader program's uniforms.
21880
+     *
21881
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
21882
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
21883
+     */
21884
+    sendUniformData: function(gl, uniformLocations) {
21885
+      // default average mode.
21886
+      var mode = 1;
21887
+      gl.uniform1i(uniformLocations.uMode, mode);
21888
+    },
21889
+
21890
+    /**
21891
+     * Grayscale filter isNeutralState implementation
21892
+     * The filter is never neutral
21893
+     * on the image
21894
+     **/
21895
+    isNeutralState: function() {
21896
+      return false;
21897
+    },
21898
+  });
21899
+
21900
+  /**
21901
+   * Returns filter instance from an object representation
21902
+   * @static
21903
+   * @param {Object} object Object to create an instance from
21904
+   * @param {function} [callback] to be invoked after filter creation
21905
+   * @return {fabric.Image.filters.Grayscale} Instance of fabric.Image.filters.Grayscale
21906
+   */
21907
+  fabric.Image.filters.Grayscale.fromObject = fabric.Image.filters.BaseFilter.fromObject;
21908
+
21909
+})(typeof exports !== 'undefined' ? exports : this);
21910
+
21911
+
21912
+(function(global) {
21913
+
21914
+  'use strict';
21915
+
21916
+  var fabric  = global.fabric || (global.fabric = { }),
21917
+      filters = fabric.Image.filters,
21918
+      createClass = fabric.util.createClass;
21919
+
21920
+  /**
21921
+   * Invert filter class
21922
+   * @class fabric.Image.filters.Invert
21923
+   * @memberOf fabric.Image.filters
21924
+   * @extends fabric.Image.filters.BaseFilter
21925
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
21926
+   * @example
21927
+   * var filter = new fabric.Image.filters.Invert();
21928
+   * object.filters.push(filter);
21929
+   * object.applyFilters(canvas.renderAll.bind(canvas));
21930
+   */
21931
+  filters.Invert = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Invert.prototype */ {
21932
+
21933
+    /**
21934
+     * Filter type
21935
+     * @param {String} type
21936
+     * @default
21937
+     */
21938
+    type: 'Invert',
21939
+
21940
+    fragmentSource: 'precision highp float;\n' +
21941
+      'uniform sampler2D uTexture;\n' +
21942
+      'uniform int uInvert;\n' +
21943
+      'varying vec2 vTexCoord;\n' +
21944
+      'void main() {\n' +
21945
+        'vec4 color = texture2D(uTexture, vTexCoord);\n' +
21946
+        'if (uInvert == 1) {\n' +
21947
+          'gl_FragColor = vec4(1.0 - color.r,1.0 -color.g,1.0 -color.b,color.a);\n' +
21948
+        '} else {\n' +
21949
+          'gl_FragColor = color;\n' +
21950
+        '}\n' +
21951
+      '}',
21952
+
21953
+    /**
21954
+     * Filter invert. if false, does nothing
21955
+     * @param {Boolean} invert
21956
+     * @default
21957
+     */
21958
+    invert: true,
21959
+
21960
+    mainParameter: 'invert',
21961
+
21962
+    /**
21963
+     * Apply the Invert operation to a Uint8Array representing the pixels of an image.
21964
+     *
21965
+     * @param {Object} options
21966
+     * @param {ImageData} options.imageData The Uint8Array to be filtered.
21967
+     */
21968
+    applyTo2d: function(options) {
21969
+      var imageData = options.imageData,
21970
+          data = imageData.data, i,
21971
+          len = data.length;
21972
+      for (i = 0; i < len; i += 4) {
21973
+        data[i] = 255 - data[i];
21974
+        data[i + 1] = 255 - data[i + 1];
21975
+        data[i + 2] = 255 - data[i + 2];
21976
+      }
21977
+    },
21978
+
21979
+    /**
21980
+     * Invert filter isNeutralState implementation
21981
+     * Used only in image applyFilters to discard filters that will not have an effect
21982
+     * on the image
21983
+     * @param {Object} options
21984
+     **/
21985
+    isNeutralState: function() {
21986
+      return !this.invert;
21987
+    },
21988
+
21989
+    /**
21990
+     * Return WebGL uniform locations for this filter's shader.
21991
+     *
21992
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
21993
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
21994
+     */
21995
+    getUniformLocations: function(gl, program) {
21996
+      return {
21997
+        uInvert: gl.getUniformLocation(program, 'uInvert'),
21998
+      };
21999
+    },
22000
+
22001
+    /**
22002
+     * Send data from this filter to its shader program's uniforms.
22003
+     *
22004
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
22005
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
22006
+     */
22007
+    sendUniformData: function(gl, uniformLocations) {
22008
+      gl.uniform1i(uniformLocations.uInvert, this.invert);
22009
+    },
22010
+  });
22011
+
22012
+  /**
22013
+   * Returns filter instance from an object representation
22014
+   * @static
22015
+   * @param {Object} object Object to create an instance from
22016
+   * @param {function} [callback] to be invoked after filter creation
22017
+   * @return {fabric.Image.filters.Invert} Instance of fabric.Image.filters.Invert
22018
+   */
22019
+  fabric.Image.filters.Invert.fromObject = fabric.Image.filters.BaseFilter.fromObject;
22020
+
22021
+
22022
+})(typeof exports !== 'undefined' ? exports : this);
22023
+
22024
+
22025
+(function(global) {
22026
+
22027
+  'use strict';
22028
+
22029
+  var fabric  = global.fabric || (global.fabric = { }),
22030
+      extend = fabric.util.object.extend,
22031
+      filters = fabric.Image.filters,
22032
+      createClass = fabric.util.createClass;
22033
+
22034
+  /**
22035
+   * Noise filter class
22036
+   * @class fabric.Image.filters.Noise
22037
+   * @memberOf fabric.Image.filters
22038
+   * @extends fabric.Image.filters.BaseFilter
22039
+   * @see {@link fabric.Image.filters.Noise#initialize} for constructor definition
22040
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
22041
+   * @example
22042
+   * var filter = new fabric.Image.filters.Noise({
22043
+   *   noise: 700
22044
+   * });
22045
+   * object.filters.push(filter);
22046
+   * object.applyFilters();
22047
+   * canvas.renderAll();
22048
+   */
22049
+  filters.Noise = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Noise.prototype */ {
22050
+
22051
+    /**
22052
+     * Filter type
22053
+     * @param {String} type
22054
+     * @default
22055
+     */
22056
+    type: 'Noise',
22057
+
22058
+    /**
22059
+     * Fragment source for the noise program
22060
+     */
22061
+    fragmentSource: 'precision highp float;\n' +
22062
+      'uniform sampler2D uTexture;\n' +
22063
+      'uniform float uStepH;\n' +
22064
+      'uniform float uNoise;\n' +
22065
+      'uniform float uSeed;\n' +
22066
+      'varying vec2 vTexCoord;\n' +
22067
+      'float rand(vec2 co, float seed, float vScale) {\n' +
22068
+        'return fract(sin(dot(co.xy * vScale ,vec2(12.9898 , 78.233))) * 43758.5453 * (seed + 0.01) / 2.0);\n' +
22069
+      '}\n' +
22070
+      'void main() {\n' +
22071
+        'vec4 color = texture2D(uTexture, vTexCoord);\n' +
22072
+        'color.rgb += (0.5 - rand(vTexCoord, uSeed, 0.1 / uStepH)) * uNoise;\n' +
22073
+        'gl_FragColor = color;\n' +
22074
+      '}',
22075
+
22076
+    /**
22077
+     * Describe the property that is the filter parameter
22078
+     * @param {String} m
22079
+     * @default
22080
+     */
22081
+    mainParameter: 'noise',
22082
+
22083
+    /**
22084
+     * Noise value, from
22085
+     * @param {Number} noise
22086
+     * @default
22087
+     */
22088
+    noise: 0,
22089
+
22090
+    /**
22091
+     * Apply the Brightness operation to a Uint8ClampedArray representing the pixels of an image.
22092
+     *
22093
+     * @param {Object} options
22094
+     * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.
22095
+     */
22096
+    applyTo2d: function(options) {
22097
+      if (this.noise === 0) {
22098
+        return;
22099
+      }
22100
+      var imageData = options.imageData,
22101
+          data = imageData.data, i, len = data.length,
22102
+          noise = this.noise, rand;
22103
+
22104
+      for (i = 0, len = data.length; i < len; i += 4) {
22105
+
22106
+        rand = (0.5 - Math.random()) * noise;
22107
+
22108
+        data[i] += rand;
22109
+        data[i + 1] += rand;
22110
+        data[i + 2] += rand;
22111
+      }
22112
+    },
22113
+
22114
+    /**
22115
+     * Return WebGL uniform locations for this filter's shader.
22116
+     *
22117
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
22118
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
22119
+     */
22120
+    getUniformLocations: function(gl, program) {
22121
+      return {
22122
+        uNoise: gl.getUniformLocation(program, 'uNoise'),
22123
+        uSeed: gl.getUniformLocation(program, 'uSeed'),
22124
+      };
22125
+    },
22126
+
22127
+    /**
22128
+     * Send data from this filter to its shader program's uniforms.
22129
+     *
22130
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
22131
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
22132
+     */
22133
+    sendUniformData: function(gl, uniformLocations) {
22134
+      gl.uniform1f(uniformLocations.uNoise, this.noise / 255);
22135
+      gl.uniform1f(uniformLocations.uSeed, Math.random());
22136
+    },
22137
+
22138
+    /**
22139
+     * Returns object representation of an instance
22140
+     * @return {Object} Object representation of an instance
22141
+     */
22142
+    toObject: function() {
22143
+      return extend(this.callSuper('toObject'), {
22144
+        noise: this.noise
22145
+      });
22146
+    }
22147
+  });
22148
+
22149
+  /**
22150
+   * Returns filter instance from an object representation
22151
+   * @static
22152
+   * @param {Object} object Object to create an instance from
22153
+   * @param {Function} [callback] to be invoked after filter creation
22154
+   * @return {fabric.Image.filters.Noise} Instance of fabric.Image.filters.Noise
22155
+   */
22156
+  fabric.Image.filters.Noise.fromObject = fabric.Image.filters.BaseFilter.fromObject;
22157
+
22158
+})(typeof exports !== 'undefined' ? exports : this);
22159
+
22160
+
22161
+(function(global) {
22162
+
22163
+  'use strict';
22164
+
22165
+  var fabric  = global.fabric || (global.fabric = { }),
22166
+      filters = fabric.Image.filters,
22167
+      createClass = fabric.util.createClass;
22168
+
22169
+  /**
22170
+   * Pixelate filter class
22171
+   * @class fabric.Image.filters.Pixelate
22172
+   * @memberOf fabric.Image.filters
22173
+   * @extends fabric.Image.filters.BaseFilter
22174
+   * @see {@link fabric.Image.filters.Pixelate#initialize} for constructor definition
22175
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
22176
+   * @example
22177
+   * var filter = new fabric.Image.filters.Pixelate({
22178
+   *   blocksize: 8
22179
+   * });
22180
+   * object.filters.push(filter);
22181
+   * object.applyFilters();
22182
+   */
22183
+  filters.Pixelate = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Pixelate.prototype */ {
22184
+
22185
+    /**
22186
+     * Filter type
22187
+     * @param {String} type
22188
+     * @default
22189
+     */
22190
+    type: 'Pixelate',
22191
+
22192
+    blocksize: 4,
22193
+
22194
+    mainParameter: 'blocksize',
22195
+
22196
+    /**
22197
+     * Fragment source for the Pixelate program
22198
+     */
22199
+    fragmentSource: 'precision highp float;\n' +
22200
+      'uniform sampler2D uTexture;\n' +
22201
+      'uniform float uBlocksize;\n' +
22202
+      'uniform float uStepW;\n' +
22203
+      'uniform float uStepH;\n' +
22204
+      'varying vec2 vTexCoord;\n' +
22205
+      'void main() {\n' +
22206
+        'float blockW = uBlocksize * uStepW;\n' +
22207
+        'float blockH = uBlocksize * uStepW;\n' +
22208
+        'int posX = int(vTexCoord.x / blockW);\n' +
22209
+        'int posY = int(vTexCoord.y / blockH);\n' +
22210
+        'float fposX = float(posX);\n' +
22211
+        'float fposY = float(posY);\n' +
22212
+        'vec2 squareCoords = vec2(fposX * blockW, fposY * blockH);\n' +
22213
+        'vec4 color = texture2D(uTexture, squareCoords);\n' +
22214
+        'gl_FragColor = color;\n' +
22215
+      '}',
22216
+
22217
+    /**
22218
+     * Apply the Pixelate operation to a Uint8ClampedArray representing the pixels of an image.
22219
+     *
22220
+     * @param {Object} options
22221
+     * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.
22222
+     */
22223
+    applyTo2d: function(options) {
22224
+      var imageData = options.imageData,
22225
+          data = imageData.data,
22226
+          iLen = imageData.height,
22227
+          jLen = imageData.width,
22228
+          index, i, j, r, g, b, a,
22229
+          _i, _j, _iLen, _jLen;
22230
+
22231
+      for (i = 0; i < iLen; i += this.blocksize) {
22232
+        for (j = 0; j < jLen; j += this.blocksize) {
22233
+
22234
+          index = (i * 4) * jLen + (j * 4);
22235
+
22236
+          r = data[index];
22237
+          g = data[index + 1];
22238
+          b = data[index + 2];
22239
+          a = data[index + 3];
22240
+
22241
+          _iLen = Math.min(i + this.blocksize, iLen);
22242
+          _jLen = Math.min(j + this.blocksize, jLen);
22243
+          for (_i = i; _i < _iLen; _i++) {
22244
+            for (_j = j; _j < _jLen; _j++) {
22245
+              index = (_i * 4) * jLen + (_j * 4);
22246
+              data[index] = r;
22247
+              data[index + 1] = g;
22248
+              data[index + 2] = b;
22249
+              data[index + 3] = a;
22250
+            }
22251
+          }
22252
+        }
22253
+      }
22254
+    },
22255
+
22256
+    /**
22257
+     * Indicate when the filter is not gonna apply changes to the image
22258
+     **/
22259
+    isNeutralState: function() {
22260
+      return this.blocksize === 1;
22261
+    },
22262
+
22263
+    /**
22264
+     * Return WebGL uniform locations for this filter's shader.
22265
+     *
22266
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
22267
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
22268
+     */
22269
+    getUniformLocations: function(gl, program) {
22270
+      return {
22271
+        uBlocksize: gl.getUniformLocation(program, 'uBlocksize'),
22272
+        uStepW: gl.getUniformLocation(program, 'uStepW'),
22273
+        uStepH: gl.getUniformLocation(program, 'uStepH'),
22274
+      };
22275
+    },
22276
+
22277
+    /**
22278
+     * Send data from this filter to its shader program's uniforms.
22279
+     *
22280
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
22281
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
22282
+     */
22283
+    sendUniformData: function(gl, uniformLocations) {
22284
+      gl.uniform1f(uniformLocations.uBlocksize, this.blocksize);
22285
+    },
22286
+  });
22287
+
22288
+  /**
22289
+   * Returns filter instance from an object representation
22290
+   * @static
22291
+   * @param {Object} object Object to create an instance from
22292
+   * @param {Function} [callback] to be invoked after filter creation
22293
+   * @return {fabric.Image.filters.Pixelate} Instance of fabric.Image.filters.Pixelate
22294
+   */
22295
+  fabric.Image.filters.Pixelate.fromObject = fabric.Image.filters.BaseFilter.fromObject;
22296
+
22297
+})(typeof exports !== 'undefined' ? exports : this);
22298
+
22299
+
22300
+(function(global) {
22301
+
22302
+  'use strict';
22303
+
22304
+  var fabric  = global.fabric || (global.fabric = { }),
22305
+      extend = fabric.util.object.extend,
22306
+      filters = fabric.Image.filters,
22307
+      createClass = fabric.util.createClass;
22308
+
22309
+  /**
22310
+   * Remove white filter class
22311
+   * @class fabric.Image.filters.RemoveColor
22312
+   * @memberOf fabric.Image.filters
22313
+   * @extends fabric.Image.filters.BaseFilter
22314
+   * @see {@link fabric.Image.filters.RemoveColor#initialize} for constructor definition
22315
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
22316
+   * @example
22317
+   * var filter = new fabric.Image.filters.RemoveColor({
22318
+   *   threshold: 0.2,
22319
+   * });
22320
+   * object.filters.push(filter);
22321
+   * object.applyFilters();
22322
+   * canvas.renderAll();
22323
+   */
22324
+  filters.RemoveColor = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.RemoveColor.prototype */ {
22325
+
22326
+    /**
22327
+     * Filter type
22328
+     * @param {String} type
22329
+     * @default
22330
+     */
22331
+    type: 'RemoveColor',
22332
+
22333
+    /**
22334
+     * Color to remove, in any format understood by fabric.Color.
22335
+     * @param {String} type
22336
+     * @default
22337
+     */
22338
+    color: '#FFFFFF',
22339
+
22340
+    /**
22341
+     * Fragment source for the brightness program
22342
+     */
22343
+    fragmentSource: 'precision highp float;\n' +
22344
+      'uniform sampler2D uTexture;\n' +
22345
+      'uniform vec4 uLow;\n' +
22346
+      'uniform vec4 uHigh;\n' +
22347
+      'varying vec2 vTexCoord;\n' +
22348
+      'void main() {\n' +
22349
+        'gl_FragColor = texture2D(uTexture, vTexCoord);\n' +
22350
+        'if(all(greaterThan(gl_FragColor.rgb,uLow.rgb)) && all(greaterThan(uHigh.rgb,gl_FragColor.rgb))) {\n' +
22351
+          'gl_FragColor.a = 0.0;\n' +
22352
+        '}\n' +
22353
+      '}',
22354
+
22355
+    /**
22356
+     * distance to actual color, as value up or down from each r,g,b
22357
+     * between 0 and 1
22358
+     **/
22359
+    distance: 0.02,
22360
+
22361
+    /**
22362
+     * For color to remove inside distance, use alpha channel for a smoother deletion
22363
+     * NOT IMPLEMENTED YET
22364
+     **/
22365
+    useAlpha: false,
22366
+
22367
+    /**
22368
+     * Constructor
22369
+     * @memberOf fabric.Image.filters.RemoveWhite.prototype
22370
+     * @param {Object} [options] Options object
22371
+     * @param {Number} [options.color=#RRGGBB] Threshold value
22372
+     * @param {Number} [options.distance=10] Distance value
22373
+     */
22374
+
22375
+    /**
22376
+     * Applies filter to canvas element
22377
+     * @param {Object} canvasEl Canvas element to apply filter to
22378
+     */
22379
+    applyTo2d: function(options) {
22380
+      var imageData = options.imageData,
22381
+          data = imageData.data, i,
22382
+          distance = this.distance * 255,
22383
+          r, g, b,
22384
+          source = new fabric.Color(this.color).getSource(),
22385
+          lowC = [
22386
+            source[0] - distance,
22387
+            source[1] - distance,
22388
+            source[2] - distance,
22389
+          ],
22390
+          highC = [
22391
+            source[0] + distance,
22392
+            source[1] + distance,
22393
+            source[2] + distance,
22394
+          ];
22395
+
22396
+
22397
+      for (i = 0; i < data.length; i += 4) {
22398
+        r = data[i];
22399
+        g = data[i + 1];
22400
+        b = data[i + 2];
22401
+
22402
+        if (r > lowC[0] &&
22403
+            g > lowC[1] &&
22404
+            b > lowC[2] &&
22405
+            r < highC[0] &&
22406
+            g < highC[1] &&
22407
+            b < highC[2]) {
22408
+          data[i + 3] = 0;
22409
+        }
22410
+      }
22411
+    },
22412
+
22413
+    /**
22414
+     * Return WebGL uniform locations for this filter's shader.
22415
+     *
22416
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
22417
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
22418
+     */
22419
+    getUniformLocations: function(gl, program) {
22420
+      return {
22421
+        uLow: gl.getUniformLocation(program, 'uLow'),
22422
+        uHigh: gl.getUniformLocation(program, 'uHigh'),
22423
+      };
22424
+    },
22425
+
22426
+    /**
22427
+     * Send data from this filter to its shader program's uniforms.
22428
+     *
22429
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
22430
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
22431
+     */
22432
+    sendUniformData: function(gl, uniformLocations) {
22433
+      var source = new fabric.Color(this.color).getSource(),
22434
+          distance = parseFloat(this.distance),
22435
+          lowC = [
22436
+            0 + source[0] / 255 - distance,
22437
+            0 + source[1] / 255 - distance,
22438
+            0 + source[2] / 255 - distance,
22439
+            1
22440
+          ],
22441
+          highC = [
22442
+            source[0] / 255 + distance,
22443
+            source[1] / 255 + distance,
22444
+            source[2] / 255 + distance,
22445
+            1
22446
+          ];
22447
+      gl.uniform4fv(uniformLocations.uLow, lowC);
22448
+      gl.uniform4fv(uniformLocations.uHigh, highC);
22449
+    },
22450
+
22451
+    /**
22452
+     * Returns object representation of an instance
22453
+     * @return {Object} Object representation of an instance
22454
+     */
22455
+    toObject: function() {
22456
+      return extend(this.callSuper('toObject'), {
22457
+        color: this.color,
22458
+        distance: this.distance
22459
+      });
22460
+    }
22461
+  });
22462
+
22463
+  /**
22464
+   * Returns filter instance from an object representation
22465
+   * @static
22466
+   * @param {Object} object Object to create an instance from
22467
+   * @param {Function} [callback] to be invoked after filter creation
22468
+   * @return {fabric.Image.filters.RemoveColor} Instance of fabric.Image.filters.RemoveWhite
22469
+   */
22470
+  fabric.Image.filters.RemoveColor.fromObject = fabric.Image.filters.BaseFilter.fromObject;
22471
+
22472
+})(typeof exports !== 'undefined' ? exports : this);
22473
+
22474
+
22475
+(function(global) {
22476
+
22477
+  'use strict';
22478
+
22479
+  var fabric  = global.fabric || (global.fabric = { }),
22480
+      filters = fabric.Image.filters,
22481
+      createClass = fabric.util.createClass;
22482
+
22483
+  var matrices = {
22484
+    Brownie: [
22485
+      0.59970,0.34553,-0.27082,0,0.186,
22486
+      -0.03770,0.86095,0.15059,0,-0.1449,
22487
+      0.24113,-0.07441,0.44972,0,-0.02965,
22488
+      0,0,0,1,0
22489
+    ],
22490
+    Vintage: [
22491
+      0.62793,0.32021,-0.03965,0,0.03784,
22492
+      0.02578,0.64411,0.03259,0,0.02926,
22493
+      0.04660,-0.08512,0.52416,0,0.02023,
22494
+      0,0,0,1,0
22495
+    ],
22496
+    Kodachrome: [
22497
+      1.12855,-0.39673,-0.03992,0,0.24991,
22498
+      -0.16404,1.08352,-0.05498,0,0.09698,
22499
+      -0.16786,-0.56034,1.60148,0,0.13972,
22500
+      0,0,0,1,0
22501
+    ],
22502
+    Technicolor: [
22503
+      1.91252,-0.85453,-0.09155,0,0.04624,
22504
+      -0.30878,1.76589,-0.10601,0,-0.27589,
22505
+      -0.23110,-0.75018,1.84759,0,0.12137,
22506
+      0,0,0,1,0
22507
+    ],
22508
+    Polaroid: [
22509
+      1.438,-0.062,-0.062,0,0,
22510
+      -0.122,1.378,-0.122,0,0,
22511
+      -0.016,-0.016,1.483,0,0,
22512
+      0,0,0,1,0
22513
+    ],
22514
+    Sepia: [
22515
+      0.393, 0.769, 0.189, 0, 0,
22516
+      0.349, 0.686, 0.168, 0, 0,
22517
+      0.272, 0.534, 0.131, 0, 0,
22518
+      0, 0, 0, 1, 0
22519
+    ],
22520
+    BlackWhite: [
22521
+      1.5, 1.5, 1.5, 0, -1,
22522
+      1.5, 1.5, 1.5, 0, -1,
22523
+      1.5, 1.5, 1.5, 0, -1,
22524
+      0, 0, 0, 1, 0,
22525
+    ]
22526
+  };
22527
+
22528
+  for (var key in matrices) {
22529
+    filters[key] = createClass(filters.ColorMatrix, /** @lends fabric.Image.filters.Sepia.prototype */ {
22530
+
22531
+      /**
22532
+       * Filter type
22533
+       * @param {String} type
22534
+       * @default
22535
+       */
22536
+      type: key,
22537
+
22538
+      /**
22539
+       * Colormatrix for the effect
22540
+       * array of 20 floats. Numbers in positions 4, 9, 14, 19 loose meaning
22541
+       * outside the -1, 1 range.
22542
+       * @param {Array} matrix array of 20 numbers.
22543
+       * @default
22544
+       */
22545
+      matrix: matrices[key],
22546
+
22547
+      /**
22548
+       * Lock the matrix export for this kind of static, parameter less filters.
22549
+       */
22550
+      mainParameter: false,
22551
+      /**
22552
+       * Lock the colormatrix on the color part, skipping alpha
22553
+       */
22554
+      colorsOnly: true,
22555
+
22556
+    });
22557
+    fabric.Image.filters[key].fromObject = fabric.Image.filters.BaseFilter.fromObject;
22558
+  }
22559
+})(typeof exports !== 'undefined' ? exports : this);
22560
+
22561
+
22562
+(function(global) {
22563
+  'use strict';
22564
+
22565
+  var fabric = global.fabric,
22566
+      filters = fabric.Image.filters,
22567
+      createClass = fabric.util.createClass;
22568
+
22569
+  /**
22570
+   * Color Blend filter class
22571
+   * @class fabric.Image.filter.BlendColor
22572
+   * @memberOf fabric.Image.filters
22573
+   * @extends fabric.Image.filters.BaseFilter
22574
+   * @example
22575
+   * var filter = new fabric.Image.filters.BlendColor({
22576
+   *  color: '#000',
22577
+   *  mode: 'multiply'
22578
+   * });
22579
+   *
22580
+   * var filter = new fabric.Image.filters.BlendImage({
22581
+   *  image: fabricImageObject,
22582
+   *  mode: 'multiply',
22583
+   *  alpha: 0.5
22584
+   * });
22585
+   * object.filters.push(filter);
22586
+   * object.applyFilters();
22587
+   * canvas.renderAll();
22588
+   */
22589
+
22590
+  filters.BlendColor = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Blend.prototype */ {
22591
+    type: 'BlendColor',
22592
+
22593
+    /**
22594
+     * Color to make the blend operation with. default to a reddish color since black or white
22595
+     * gives always strong result.
22596
+     **/
22597
+    color: '#F95C63',
22598
+
22599
+    /**
22600
+     * Blend mode for the filter: one of multiply, add, diff, screen, subtract,
22601
+     * darken, lighten, overlay, exclusion, tint.
22602
+     **/
22603
+    mode: 'multiply',
22604
+
22605
+    /**
22606
+     * alpha value. represent the strength of the blend color operation.
22607
+     **/
22608
+    alpha: 1,
22609
+
22610
+    /**
22611
+     * Fragment source for the Multiply program
22612
+     */
22613
+    fragmentSource: {
22614
+      multiply: 'gl_FragColor.rgb *= uColor.rgb;\n',
22615
+      screen: 'gl_FragColor.rgb = 1.0 - (1.0 - gl_FragColor.rgb) * (1.0 - uColor.rgb);\n',
22616
+      add: 'gl_FragColor.rgb += uColor.rgb;\n',
22617
+      diff: 'gl_FragColor.rgb = abs(gl_FragColor.rgb - uColor.rgb);\n',
22618
+      subtract: 'gl_FragColor.rgb -= uColor.rgb;\n',
22619
+      lighten: 'gl_FragColor.rgb = max(gl_FragColor.rgb, uColor.rgb);\n',
22620
+      darken: 'gl_FragColor.rgb = min(gl_FragColor.rgb, uColor.rgb);\n',
22621
+      exclusion: 'gl_FragColor.rgb += uColor.rgb - 2.0 * (uColor.rgb * gl_FragColor.rgb);\n',
22622
+      overlay: 'if (uColor.r < 0.5) {\n' +
22623
+          'gl_FragColor.r *= 2.0 * uColor.r;\n' +
22624
+        '} else {\n' +
22625
+          'gl_FragColor.r = 1.0 - 2.0 * (1.0 - gl_FragColor.r) * (1.0 - uColor.r);\n' +
22626
+        '}\n' +
22627
+        'if (uColor.g < 0.5) {\n' +
22628
+          'gl_FragColor.g *= 2.0 * uColor.g;\n' +
22629
+        '} else {\n' +
22630
+          'gl_FragColor.g = 1.0 - 2.0 * (1.0 - gl_FragColor.g) * (1.0 - uColor.g);\n' +
22631
+        '}\n' +
22632
+        'if (uColor.b < 0.5) {\n' +
22633
+          'gl_FragColor.b *= 2.0 * uColor.b;\n' +
22634
+        '} else {\n' +
22635
+          'gl_FragColor.b = 1.0 - 2.0 * (1.0 - gl_FragColor.b) * (1.0 - uColor.b);\n' +
22636
+        '}\n',
22637
+      tint: 'gl_FragColor.rgb *= (1.0 - uColor.a);\n' +
22638
+        'gl_FragColor.rgb += uColor.rgb;\n',
22639
+    },
22640
+
22641
+    /**
22642
+     * build the fragment source for the filters, joining the common part with
22643
+     * the specific one.
22644
+     * @param {String} mode the mode of the filter, a key of this.fragmentSource
22645
+     * @return {String} the source to be compiled
22646
+     * @private
22647
+     */
22648
+    buildSource: function(mode) {
22649
+      return 'precision highp float;\n' +
22650
+        'uniform sampler2D uTexture;\n' +
22651
+        'uniform vec4 uColor;\n' +
22652
+        'varying vec2 vTexCoord;\n' +
22653
+        'void main() {\n' +
22654
+          'vec4 color = texture2D(uTexture, vTexCoord);\n' +
22655
+          'gl_FragColor = color;\n' +
22656
+          'if (color.a > 0.0) {\n' +
22657
+            this.fragmentSource[mode] +
22658
+          '}\n' +
22659
+        '}';
22660
+    },
22661
+
22662
+    /**
22663
+     * Retrieves the cached shader.
22664
+     * @param {Object} options
22665
+     * @param {WebGLRenderingContext} options.context The GL context used for rendering.
22666
+     * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.
22667
+     */
22668
+    retrieveShader: function(options) {
22669
+      var cacheKey = this.type + '_' + this.mode, shaderSource;
22670
+      if (!options.programCache.hasOwnProperty(cacheKey)) {
22671
+        shaderSource = this.buildSource(this.mode);
22672
+        options.programCache[cacheKey] = this.createProgram(options.context, shaderSource);
22673
+      }
22674
+      return options.programCache[cacheKey];
22675
+    },
22676
+
22677
+    /**
22678
+     * Apply the Blend operation to a Uint8ClampedArray representing the pixels of an image.
22679
+     *
22680
+     * @param {Object} options
22681
+     * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.
22682
+     */
22683
+    applyTo2d: function(options) {
22684
+      var imageData = options.imageData,
22685
+          data = imageData.data, iLen = data.length,
22686
+          tr, tg, tb,
22687
+          r, g, b,
22688
+          source, alpha1 = 1 - this.alpha;
22689
+
22690
+      source = new fabric.Color(this.color).getSource();
22691
+      tr = source[0] * this.alpha;
22692
+      tg = source[1] * this.alpha;
22693
+      tb = source[2] * this.alpha;
22694
+
22695
+      for (var i = 0; i < iLen; i += 4) {
22696
+
22697
+        r = data[i];
22698
+        g = data[i + 1];
22699
+        b = data[i + 2];
22700
+
22701
+        switch (this.mode) {
22702
+          case 'multiply':
22703
+            data[i] = r * tr / 255;
22704
+            data[i + 1] = g * tg / 255;
22705
+            data[i + 2] = b * tb / 255;
22706
+            break;
22707
+          case 'screen':
22708
+            data[i] = 255 - (255 - r) * (255 - tr) / 255;
22709
+            data[i + 1] = 255 - (255 - g) * (255 - tg) / 255;
22710
+            data[i + 2] = 255 - (255 - b) * (255 - tb) / 255;
22711
+            break;
22712
+          case 'add':
22713
+            data[i] = r + tr;
22714
+            data[i + 1] = g + tg;
22715
+            data[i + 2] = b + tb;
22716
+            break;
22717
+          case 'diff':
22718
+          case 'difference':
22719
+            data[i] = Math.abs(r - tr);
22720
+            data[i + 1] = Math.abs(g - tg);
22721
+            data[i + 2] = Math.abs(b - tb);
22722
+            break;
22723
+          case 'subtract':
22724
+            data[i] = r - tr;
22725
+            data[i + 1] = g - tg;
22726
+            data[i + 2] = b - tb;
22727
+            break;
22728
+          case 'darken':
22729
+            data[i] = Math.min(r, tr);
22730
+            data[i + 1] = Math.min(g, tg);
22731
+            data[i + 2] = Math.min(b, tb);
22732
+            break;
22733
+          case 'lighten':
22734
+            data[i] = Math.max(r, tr);
22735
+            data[i + 1] = Math.max(g, tg);
22736
+            data[i + 2] = Math.max(b, tb);
22737
+            break;
22738
+          case 'overlay':
22739
+            data[i] = tr < 128 ? (2 * r * tr / 255) : (255 - 2 * (255 - r) * (255 - tr) / 255);
22740
+            data[i + 1] = tg < 128 ? (2 * g * tg / 255) : (255 - 2 * (255 - g) * (255 - tg) / 255);
22741
+            data[i + 2] = tb < 128 ? (2 * b * tb / 255) : (255 - 2 * (255 - b) * (255 - tb) / 255);
22742
+            break;
22743
+          case 'exclusion':
22744
+            data[i] = tr + r - ((2 * tr * r) / 255);
22745
+            data[i + 1] = tg + g - ((2 * tg * g) / 255);
22746
+            data[i + 2] = tb + b - ((2 * tb * b) / 255);
22747
+            break;
22748
+          case 'tint':
22749
+            data[i] = tr + r * alpha1;
22750
+            data[i + 1] = tg + g * alpha1;
22751
+            data[i + 2] = tb + b * alpha1;
22752
+        }
22753
+      }
22754
+    },
22755
+
22756
+    /**
22757
+     * Return WebGL uniform locations for this filter's shader.
22758
+     *
22759
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
22760
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
22761
+     */
22762
+    getUniformLocations: function(gl, program) {
22763
+      return {
22764
+        uColor: gl.getUniformLocation(program, 'uColor'),
22765
+      };
22766
+    },
22767
+
22768
+    /**
22769
+     * Send data from this filter to its shader program's uniforms.
22770
+     *
22771
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
22772
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
22773
+     */
22774
+    sendUniformData: function(gl, uniformLocations) {
22775
+      var source = new fabric.Color(this.color).getSource();
22776
+      source[0] = this.alpha * source[0] / 255;
22777
+      source[1] = this.alpha * source[1] / 255;
22778
+      source[2] = this.alpha * source[2] / 255;
22779
+      source[3] = this.alpha;
22780
+      gl.uniform4fv(uniformLocations.uColor, source);
22781
+    },
22782
+
22783
+    /**
22784
+     * Returns object representation of an instance
22785
+     * @return {Object} Object representation of an instance
22786
+     */
22787
+    toObject: function() {
22788
+      return {
22789
+        type: this.type,
22790
+        color: this.color,
22791
+        mode: this.mode,
22792
+        alpha: this.alpha
22793
+      };
22794
+    }
22795
+  });
22796
+
22797
+  /**
22798
+   * Returns filter instance from an object representation
22799
+   * @static
22800
+   * @param {Object} object Object to create an instance from
22801
+   * @param {function} [callback] to be invoked after filter creation
22802
+   * @return {fabric.Image.filters.BlendColor} Instance of fabric.Image.filters.BlendColor
22803
+   */
22804
+  fabric.Image.filters.BlendColor.fromObject = fabric.Image.filters.BaseFilter.fromObject;
22805
+
22806
+})(typeof exports !== 'undefined' ? exports : this);
22807
+
22808
+
22809
+(function(global) {
22810
+  'use strict';
22811
+
22812
+  var fabric = global.fabric,
22813
+      filters = fabric.Image.filters,
22814
+      createClass = fabric.util.createClass;
22815
+
22816
+  /**
22817
+   * Image Blend filter class
22818
+   * @class fabric.Image.filter.BlendImage
22819
+   * @memberOf fabric.Image.filters
22820
+   * @extends fabric.Image.filters.BaseFilter
22821
+   * @example
22822
+   * var filter = new fabric.Image.filters.BlendColor({
22823
+   *  color: '#000',
22824
+   *  mode: 'multiply'
22825
+   * });
22826
+   *
22827
+   * var filter = new fabric.Image.filters.BlendImage({
22828
+   *  image: fabricImageObject,
22829
+   *  mode: 'multiply',
22830
+   *  alpha: 0.5
22831
+   * });
22832
+   * object.filters.push(filter);
22833
+   * object.applyFilters();
22834
+   * canvas.renderAll();
22835
+   */
22836
+
22837
+  filters.BlendImage = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.BlendImage.prototype */ {
22838
+    type: 'BlendImage',
22839
+
22840
+    /**
22841
+     * Color to make the blend operation with. default to a reddish color since black or white
22842
+     * gives always strong result.
22843
+     **/
22844
+    image: null,
22845
+
22846
+    /**
22847
+     * Blend mode for the filter: one of multiply, add, diff, screen, subtract,
22848
+     * darken, lighten, overlay, exclusion, tint.
22849
+     **/
22850
+    mode: 'multiply',
22851
+
22852
+    /**
22853
+     * alpha value. represent the strength of the blend image operation.
22854
+     * not implemented.
22855
+     **/
22856
+    alpha: 1,
22857
+
22858
+    vertexSource: 'attribute vec2 aPosition;\n' +
22859
+      'varying vec2 vTexCoord;\n' +
22860
+      'varying vec2 vTexCoord2;\n' +
22861
+      'uniform mat3 uTransformMatrix;\n' +
22862
+      'void main() {\n' +
22863
+        'vTexCoord = aPosition;\n' +
22864
+        'vTexCoord2 = (uTransformMatrix * vec3(aPosition, 1.0)).xy;\n' +
22865
+        'gl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0);\n' +
22866
+      '}',
22867
+
22868
+    /**
22869
+     * Fragment source for the Multiply program
22870
+     */
22871
+    fragmentSource: {
22872
+      multiply: 'precision highp float;\n' +
22873
+        'uniform sampler2D uTexture;\n' +
22874
+        'uniform sampler2D uImage;\n' +
22875
+        'uniform vec4 uColor;\n' +
22876
+        'varying vec2 vTexCoord;\n' +
22877
+        'varying vec2 vTexCoord2;\n' +
22878
+        'void main() {\n' +
22879
+          'vec4 color = texture2D(uTexture, vTexCoord);\n' +
22880
+          'vec4 color2 = texture2D(uImage, vTexCoord2);\n' +
22881
+          'color.rgba *= color2.rgba;\n' +
22882
+          'gl_FragColor = color;\n' +
22883
+        '}',
22884
+      mask: 'precision highp float;\n' +
22885
+        'uniform sampler2D uTexture;\n' +
22886
+        'uniform sampler2D uImage;\n' +
22887
+        'uniform vec4 uColor;\n' +
22888
+        'varying vec2 vTexCoord;\n' +
22889
+        'varying vec2 vTexCoord2;\n' +
22890
+        'void main() {\n' +
22891
+          'vec4 color = texture2D(uTexture, vTexCoord);\n' +
22892
+          'vec4 color2 = texture2D(uImage, vTexCoord2);\n' +
22893
+          'color.a = color2.a;\n' +
22894
+          'gl_FragColor = color;\n' +
22895
+        '}',
22896
+    },
22897
+
22898
+    /**
22899
+     * Retrieves the cached shader.
22900
+     * @param {Object} options
22901
+     * @param {WebGLRenderingContext} options.context The GL context used for rendering.
22902
+     * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.
22903
+     */
22904
+    retrieveShader: function(options) {
22905
+      var cacheKey = this.type + '_' + this.mode;
22906
+      var shaderSource = this.fragmentSource[this.mode];
22907
+      if (!options.programCache.hasOwnProperty(cacheKey)) {
22908
+        options.programCache[cacheKey] = this.createProgram(options.context, shaderSource);
22909
+      }
22910
+      return options.programCache[cacheKey];
22911
+    },
22912
+
22913
+    applyToWebGL: function(options) {
22914
+      // load texture to blend.
22915
+      var gl = options.context,
22916
+          texture = this.createTexture(options.filterBackend, this.image);
22917
+      this.bindAdditionalTexture(gl, texture, gl.TEXTURE1);
22918
+      this.callSuper('applyToWebGL', options);
22919
+      this.unbindAdditionalTexture(gl, gl.TEXTURE1);
22920
+    },
22921
+
22922
+    createTexture: function(backend, image) {
22923
+      return backend.getCachedTexture(image.cacheKey, image._element);
22924
+    },
22925
+
22926
+    /**
22927
+     * Calculate a transformMatrix to adapt the image to blend over
22928
+     * @param {Object} options
22929
+     * @param {WebGLRenderingContext} options.context The GL context used for rendering.
22930
+     * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.
22931
+     */
22932
+    calculateMatrix: function() {
22933
+      var image = this.image,
22934
+          width = image._element.width,
22935
+          height = image._element.height;
22936
+      return [
22937
+        1 / image.scaleX, 0, 0,
22938
+        0, 1 / image.scaleY, 0,
22939
+        -image.left / width, -image.top / height, 1
22940
+      ];
22941
+    },
22942
+
22943
+    /**
22944
+     * Apply the Blend operation to a Uint8ClampedArray representing the pixels of an image.
22945
+     *
22946
+     * @param {Object} options
22947
+     * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.
22948
+     */
22949
+    applyTo2d: function(options) {
22950
+      var imageData = options.imageData,
22951
+          resources = options.filterBackend.resources,
22952
+          data = imageData.data, iLen = data.length,
22953
+          width = imageData.width,
22954
+          height = imageData.height,
22955
+          tr, tg, tb, ta,
22956
+          r, g, b, a,
22957
+          canvas1, context, image = this.image, blendData;
22958
+
22959
+      if (!resources.blendImage) {
22960
+        resources.blendImage = fabric.util.createCanvasElement();
22961
+      }
22962
+      canvas1 = resources.blendImage;
22963
+      context = canvas1.getContext('2d');
22964
+      if (canvas1.width !== width || canvas1.height !== height) {
22965
+        canvas1.width = width;
22966
+        canvas1.height = height;
22967
+      }
22968
+      else {
22969
+        context.clearRect(0, 0, width, height);
22970
+      }
22971
+      context.setTransform(image.scaleX, 0, 0, image.scaleY, image.left, image.top);
22972
+      context.drawImage(image._element, 0, 0, width, height);
22973
+      blendData = context.getImageData(0, 0, width, height).data;
22974
+      for (var i = 0; i < iLen; i += 4) {
22975
+
22976
+        r = data[i];
22977
+        g = data[i + 1];
22978
+        b = data[i + 2];
22979
+        a = data[i + 3];
22980
+
22981
+        tr = blendData[i];
22982
+        tg = blendData[i + 1];
22983
+        tb = blendData[i + 2];
22984
+        ta = blendData[i + 3];
22985
+
22986
+        switch (this.mode) {
22987
+          case 'multiply':
22988
+            data[i] = r * tr / 255;
22989
+            data[i + 1] = g * tg / 255;
22990
+            data[i + 2] = b * tb / 255;
22991
+            data[i + 3] = a * ta / 255;
22992
+            break;
22993
+          case 'mask':
22994
+            data[i + 3] = ta;
22995
+            break;
22996
+        }
22997
+      }
22998
+    },
22999
+
23000
+    /**
23001
+     * Return WebGL uniform locations for this filter's shader.
23002
+     *
23003
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
23004
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
23005
+     */
23006
+    getUniformLocations: function(gl, program) {
23007
+      return {
23008
+        uTransformMatrix: gl.getUniformLocation(program, 'uTransformMatrix'),
23009
+        uImage: gl.getUniformLocation(program, 'uImage'),
23010
+      };
23011
+    },
23012
+
23013
+    /**
23014
+     * Send data from this filter to its shader program's uniforms.
23015
+     *
23016
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
23017
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
23018
+     */
23019
+    sendUniformData: function(gl, uniformLocations) {
23020
+      var matrix = this.calculateMatrix();
23021
+      gl.uniform1i(uniformLocations.uImage, 1); // texture unit 1.
23022
+      gl.uniformMatrix3fv(uniformLocations.uTransformMatrix, false, matrix);
23023
+    },
23024
+
23025
+    /**
23026
+     * Returns object representation of an instance
23027
+     * @return {Object} Object representation of an instance
23028
+     */
23029
+    toObject: function() {
23030
+      return {
23031
+        type: this.type,
23032
+        image: this.image && this.image.toObject(),
23033
+        mode: this.mode,
23034
+        alpha: this.alpha
23035
+      };
23036
+    }
23037
+  });
23038
+
23039
+  /**
23040
+   * Returns filter instance from an object representation
23041
+   * @static
23042
+   * @param {Object} object Object to create an instance from
23043
+   * @param {function} callback to be invoked after filter creation
23044
+   * @return {fabric.Image.filters.BlendImage} Instance of fabric.Image.filters.BlendImage
23045
+   */
23046
+  fabric.Image.filters.BlendImage.fromObject = function(object, callback) {
23047
+    fabric.Image.fromObject(object.image, function(image) {
23048
+      var options = fabric.util.object.clone(object);
23049
+      options.image = image;
23050
+      callback(new fabric.Image.filters.BlendImage(options));
23051
+    });
23052
+  };
23053
+
23054
+})(typeof exports !== 'undefined' ? exports : this);
23055
+
23056
+
23057
+(function(global) {
23058
+
23059
+  'use strict';
23060
+
23061
+  var fabric  = global.fabric || (global.fabric = { }), pow = Math.pow, floor = Math.floor,
23062
+      sqrt = Math.sqrt, abs = Math.abs, round = Math.round, sin = Math.sin,
23063
+      ceil = Math.ceil,
23064
+      filters = fabric.Image.filters,
23065
+      createClass = fabric.util.createClass;
23066
+
23067
+  /**
23068
+   * Resize image filter class
23069
+   * @class fabric.Image.filters.Resize
23070
+   * @memberOf fabric.Image.filters
23071
+   * @extends fabric.Image.filters.BaseFilter
23072
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
23073
+   * @example
23074
+   * var filter = new fabric.Image.filters.Resize();
23075
+   * object.filters.push(filter);
23076
+   * object.applyFilters(canvas.renderAll.bind(canvas));
23077
+   */
23078
+  filters.Resize = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Resize.prototype */ {
23079
+
23080
+    /**
23081
+     * Filter type
23082
+     * @param {String} type
23083
+     * @default
23084
+     */
23085
+    type: 'Resize',
23086
+
23087
+    /**
23088
+     * Resize type
23089
+     * for webgl resizeType is just lanczos, for canvas2d can be:
23090
+     * bilinear, hermite, sliceHack, lanczos.
23091
+     * @param {String} resizeType
23092
+     * @default
23093
+     */
23094
+    resizeType: 'hermite',
23095
+
23096
+    /**
23097
+     * Scale factor for resizing, x axis
23098
+     * @param {Number} scaleX
23099
+     * @default
23100
+     */
23101
+    scaleX: 1,
23102
+
23103
+    /**
23104
+     * Scale factor for resizing, y axis
23105
+     * @param {Number} scaleY
23106
+     * @default
23107
+     */
23108
+    scaleY: 1,
23109
+
23110
+    /**
23111
+     * LanczosLobes parameter for lanczos filter, valid for resizeType lanczos
23112
+     * @param {Number} lanczosLobes
23113
+     * @default
23114
+     */
23115
+    lanczosLobes: 3,
23116
+
23117
+
23118
+    /**
23119
+     * Return WebGL uniform locations for this filter's shader.
23120
+     *
23121
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
23122
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
23123
+     */
23124
+    getUniformLocations: function(gl, program) {
23125
+      return {
23126
+        uDelta: gl.getUniformLocation(program, 'uDelta'),
23127
+        uTaps: gl.getUniformLocation(program, 'uTaps'),
23128
+      };
23129
+    },
23130
+
23131
+    /**
23132
+     * Send data from this filter to its shader program's uniforms.
23133
+     *
23134
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
23135
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
23136
+     */
23137
+    sendUniformData: function(gl, uniformLocations) {
23138
+      gl.uniform2fv(uniformLocations.uDelta, this.horizontal ? [1 / this.width, 0] : [0, 1 / this.height]);
23139
+      gl.uniform1fv(uniformLocations.uTaps, this.taps);
23140
+    },
23141
+
23142
+    /**
23143
+     * Retrieves the cached shader.
23144
+     * @param {Object} options
23145
+     * @param {WebGLRenderingContext} options.context The GL context used for rendering.
23146
+     * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.
23147
+     */
23148
+    retrieveShader: function(options) {
23149
+      var filterWindow = this.getFilterWindow(), cacheKey = this.type + '_' + filterWindow;
23150
+      if (!options.programCache.hasOwnProperty(cacheKey)) {
23151
+        var fragmentShader = this.generateShader(filterWindow);
23152
+        options.programCache[cacheKey] = this.createProgram(options.context, fragmentShader);
23153
+      }
23154
+      return options.programCache[cacheKey];
23155
+    },
23156
+
23157
+    getFilterWindow: function() {
23158
+      var scale = this.tempScale;
23159
+      return Math.ceil(this.lanczosLobes / scale);
23160
+    },
23161
+
23162
+    getTaps: function() {
23163
+      var lobeFunction = this.lanczosCreate(this.lanczosLobes), scale = this.tempScale,
23164
+          filterWindow = this.getFilterWindow(), taps = new Array(filterWindow);
23165
+      for (var i = 1; i <= filterWindow; i++) {
23166
+        taps[i - 1] = lobeFunction(i * scale);
23167
+      }
23168
+      return taps;
23169
+    },
23170
+
23171
+    /**
23172
+     * Generate vertex and shader sources from the necessary steps numbers
23173
+     * @param {Number} filterWindow
23174
+     */
23175
+    generateShader: function(filterWindow) {
23176
+      var offsets = new Array(filterWindow),
23177
+          fragmentShader = this.fragmentSourceTOP, filterWindow;
23178
+
23179
+      for (var i = 1; i <= filterWindow; i++) {
23180
+        offsets[i - 1] = i + '.0 * uDelta';
23181
+      }
23182
+
23183
+      fragmentShader += 'uniform float uTaps[' + filterWindow + '];\n';
23184
+      fragmentShader += 'void main() {\n';
23185
+      fragmentShader += '  vec4 color = texture2D(uTexture, vTexCoord);\n';
23186
+      fragmentShader += '  float sum = 1.0;\n';
23187
+
23188
+      offsets.forEach(function(offset, i) {
23189
+        fragmentShader += '  color += texture2D(uTexture, vTexCoord + ' + offset + ') * uTaps[' + i + '];\n';
23190
+        fragmentShader += '  color += texture2D(uTexture, vTexCoord - ' + offset + ') * uTaps[' + i + '];\n';
23191
+        fragmentShader += '  sum += 2.0 * uTaps[' + i + '];\n';
23192
+      });
23193
+      fragmentShader += '  gl_FragColor = color / sum;\n';
23194
+      fragmentShader += '}';
23195
+      return fragmentShader;
23196
+    },
23197
+
23198
+    fragmentSourceTOP: 'precision highp float;\n' +
23199
+      'uniform sampler2D uTexture;\n' +
23200
+      'uniform vec2 uDelta;\n' +
23201
+      'varying vec2 vTexCoord;\n',
23202
+
23203
+    /**
23204
+     * Apply the resize filter to the image
23205
+     * Determines whether to use WebGL or Canvas2D based on the options.webgl flag.
23206
+     *
23207
+     * @param {Object} options
23208
+     * @param {Number} options.passes The number of filters remaining to be executed
23209
+     * @param {Boolean} options.webgl Whether to use webgl to render the filter.
23210
+     * @param {WebGLTexture} options.sourceTexture The texture setup as the source to be filtered.
23211
+     * @param {WebGLTexture} options.targetTexture The texture where filtered output should be drawn.
23212
+     * @param {WebGLRenderingContext} options.context The GL context used for rendering.
23213
+     * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.
23214
+     */
23215
+    applyTo: function(options) {
23216
+      if (options.webgl) {
23217
+        options.passes++;
23218
+        this.width = options.sourceWidth;
23219
+        this.horizontal = true;
23220
+        this.dW = Math.round(this.width * this.scaleX);
23221
+        this.dH = options.sourceHeight;
23222
+        this.tempScale = this.dW / this.width;
23223
+        this.taps = this.getTaps();
23224
+        options.destinationWidth = this.dW;
23225
+        this._setupFrameBuffer(options);
23226
+        this.applyToWebGL(options);
23227
+        this._swapTextures(options);
23228
+        options.sourceWidth = options.destinationWidth;
23229
+
23230
+        this.height = options.sourceHeight;
23231
+        this.horizontal = false;
23232
+        this.dH = Math.round(this.height * this.scaleY);
23233
+        this.tempScale = this.dH / this.height;
23234
+        this.taps = this.getTaps();
23235
+        options.destinationHeight = this.dH;
23236
+        this._setupFrameBuffer(options);
23237
+        this.applyToWebGL(options);
23238
+        this._swapTextures(options);
23239
+        options.sourceHeight = options.destinationHeight;
23240
+      }
23241
+      else {
23242
+        this.applyTo2d(options);
23243
+      }
23244
+    },
23245
+
23246
+    isNeutralState: function() {
23247
+      return this.scaleX === 1 && this.scaleY === 1;
23248
+    },
23249
+
23250
+    lanczosCreate: function(lobes) {
23251
+      return function(x) {
23252
+        if (x >= lobes || x <= -lobes) {
23253
+          return 0.0;
23254
+        }
23255
+        if (x < 1.19209290E-07 && x > -1.19209290E-07) {
23256
+          return 1.0;
23257
+        }
23258
+        x *= Math.PI;
23259
+        var xx = x / lobes;
23260
+        return (sin(x) / x) * sin(xx) / xx;
23261
+      };
23262
+    },
23263
+
23264
+    /**
23265
+     * Applies filter to canvas element
23266
+     * @memberOf fabric.Image.filters.Resize.prototype
23267
+     * @param {Object} canvasEl Canvas element to apply filter to
23268
+     * @param {Number} scaleX
23269
+     * @param {Number} scaleY
23270
+     */
23271
+    applyTo2d: function(options) {
23272
+      var imageData = options.imageData,
23273
+          scaleX = this.scaleX,
23274
+          scaleY = this.scaleY;
23275
+
23276
+      this.rcpScaleX = 1 / scaleX;
23277
+      this.rcpScaleY = 1 / scaleY;
23278
+
23279
+      var oW = imageData.width, oH = imageData.height,
23280
+          dW = round(oW * scaleX), dH = round(oH * scaleY),
23281
+          newData;
23282
+
23283
+      if (this.resizeType === 'sliceHack') {
23284
+        newData = this.sliceByTwo(options, oW, oH, dW, dH);
23285
+      }
23286
+      else if (this.resizeType === 'hermite') {
23287
+        newData = this.hermiteFastResize(options, oW, oH, dW, dH);
23288
+      }
23289
+      else if (this.resizeType === 'bilinear') {
23290
+        newData = this.bilinearFiltering(options, oW, oH, dW, dH);
23291
+      }
23292
+      else if (this.resizeType === 'lanczos') {
23293
+        newData = this.lanczosResize(options, oW, oH, dW, dH);
23294
+      }
23295
+      options.imageData = newData;
23296
+    },
23297
+
23298
+    /**
23299
+     * Filter sliceByTwo
23300
+     * @param {Object} canvasEl Canvas element to apply filter to
23301
+     * @param {Number} oW Original Width
23302
+     * @param {Number} oH Original Height
23303
+     * @param {Number} dW Destination Width
23304
+     * @param {Number} dH Destination Height
23305
+     * @returns {ImageData}
23306
+     */
23307
+    sliceByTwo: function(options, oW, oH, dW, dH) {
23308
+      var imageData = options.imageData,
23309
+          mult = 0.5, doneW = false, doneH = false, stepW = oW * mult,
23310
+          stepH = oH * mult, resources = fabric.filterBackend.resources,
23311
+          tmpCanvas, ctx, sX = 0, sY = 0, dX = oW, dY = 0;
23312
+      if (!resources.sliceByTwo) {
23313
+        resources.sliceByTwo = document.createElement('canvas');
23314
+      }
23315
+      tmpCanvas = resources.sliceByTwo;
23316
+      if (tmpCanvas.width < oW * 1.5 || tmpCanvas.height < oH) {
23317
+        tmpCanvas.width = oW * 1.5;
23318
+        tmpCanvas.height = oH;
23319
+      }
23320
+      ctx = tmpCanvas.getContext('2d');
23321
+      ctx.clearRect(0, 0, oW * 1.5, oH);
23322
+      ctx.putImageData(imageData, 0, 0);
23323
+
23324
+      dW = floor(dW);
23325
+      dH = floor(dH);
23326
+
23327
+      while (!doneW || !doneH) {
23328
+        oW = stepW;
23329
+        oH = stepH;
23330
+        if (dW < floor(stepW * mult)) {
23331
+          stepW = floor(stepW * mult);
23332
+        }
23333
+        else {
23334
+          stepW = dW;
23335
+          doneW = true;
23336
+        }
23337
+        if (dH < floor(stepH * mult)) {
23338
+          stepH = floor(stepH * mult);
23339
+        }
23340
+        else {
23341
+          stepH = dH;
23342
+          doneH = true;
23343
+        }
23344
+        ctx.drawImage(tmpCanvas, sX, sY, oW, oH, dX, dY, stepW, stepH);
23345
+        sX = dX;
23346
+        sY = dY;
23347
+        dY += stepH;
23348
+      }
23349
+      return ctx.getImageData(sX, sY, dW, dH);
23350
+    },
23351
+
23352
+    /**
23353
+     * Filter lanczosResize
23354
+     * @param {Object} canvasEl Canvas element to apply filter to
23355
+     * @param {Number} oW Original Width
23356
+     * @param {Number} oH Original Height
23357
+     * @param {Number} dW Destination Width
23358
+     * @param {Number} dH Destination Height
23359
+     * @returns {ImageData}
23360
+     */
23361
+    lanczosResize: function(options, oW, oH, dW, dH) {
23362
+
23363
+      function process(u) {
23364
+        var v, i, weight, idx, a, red, green,
23365
+            blue, alpha, fX, fY;
23366
+        center.x = (u + 0.5) * ratioX;
23367
+        icenter.x = floor(center.x);
23368
+        for (v = 0; v < dH; v++) {
23369
+          center.y = (v + 0.5) * ratioY;
23370
+          icenter.y = floor(center.y);
23371
+          a = 0; red = 0; green = 0; blue = 0; alpha = 0;
23372
+          for (i = icenter.x - range2X; i <= icenter.x + range2X; i++) {
23373
+            if (i < 0 || i >= oW) {
23374
+              continue;
23375
+            }
23376
+            fX = floor(1000 * abs(i - center.x));
23377
+            if (!cacheLanc[fX]) {
23378
+              cacheLanc[fX] = { };
23379
+            }
23380
+            for (var j = icenter.y - range2Y; j <= icenter.y + range2Y; j++) {
23381
+              if (j < 0 || j >= oH) {
23382
+                continue;
23383
+              }
23384
+              fY = floor(1000 * abs(j - center.y));
23385
+              if (!cacheLanc[fX][fY]) {
23386
+                cacheLanc[fX][fY] = lanczos(sqrt(pow(fX * rcpRatioX, 2) + pow(fY * rcpRatioY, 2)) / 1000);
23387
+              }
23388
+              weight = cacheLanc[fX][fY];
23389
+              if (weight > 0) {
23390
+                idx = (j * oW + i) * 4;
23391
+                a += weight;
23392
+                red += weight * srcData[idx];
23393
+                green += weight * srcData[idx + 1];
23394
+                blue += weight * srcData[idx + 2];
23395
+                alpha += weight * srcData[idx + 3];
23396
+              }
23397
+            }
23398
+          }
23399
+          idx = (v * dW + u) * 4;
23400
+          destData[idx] = red / a;
23401
+          destData[idx + 1] = green / a;
23402
+          destData[idx + 2] = blue / a;
23403
+          destData[idx + 3] = alpha / a;
23404
+        }
23405
+
23406
+        if (++u < dW) {
23407
+          return process(u);
23408
+        }
23409
+        else {
23410
+          return destImg;
23411
+        }
23412
+      }
23413
+
23414
+      var srcData = options.imageData.data,
23415
+          destImg = options.ctx.createImageData(dW, dH),
23416
+          destData = destImg.data,
23417
+          lanczos = this.lanczosCreate(this.lanczosLobes),
23418
+          ratioX = this.rcpScaleX, ratioY = this.rcpScaleY,
23419
+          rcpRatioX = 2 / this.rcpScaleX, rcpRatioY = 2 / this.rcpScaleY,
23420
+          range2X = ceil(ratioX * this.lanczosLobes / 2),
23421
+          range2Y = ceil(ratioY * this.lanczosLobes / 2),
23422
+          cacheLanc = { }, center = { }, icenter = { };
23423
+
23424
+      return process(0);
23425
+    },
23426
+
23427
+    /**
23428
+     * bilinearFiltering
23429
+     * @param {Object} canvasEl Canvas element to apply filter to
23430
+     * @param {Number} oW Original Width
23431
+     * @param {Number} oH Original Height
23432
+     * @param {Number} dW Destination Width
23433
+     * @param {Number} dH Destination Height
23434
+     * @returns {ImageData}
23435
+     */
23436
+    bilinearFiltering: function(options, oW, oH, dW, dH) {
23437
+      var a, b, c, d, x, y, i, j, xDiff, yDiff, chnl,
23438
+          color, offset = 0, origPix, ratioX = this.rcpScaleX,
23439
+          ratioY = this.rcpScaleY,
23440
+          w4 = 4 * (oW - 1), img = options.imageData,
23441
+          pixels = img.data, destImage = options.ctx.createImageData(dW, dH),
23442
+          destPixels = destImage.data;
23443
+      for (i = 0; i < dH; i++) {
23444
+        for (j = 0; j < dW; j++) {
23445
+          x = floor(ratioX * j);
23446
+          y = floor(ratioY * i);
23447
+          xDiff = ratioX * j - x;
23448
+          yDiff = ratioY * i - y;
23449
+          origPix = 4 * (y * oW + x);
23450
+
23451
+          for (chnl = 0; chnl < 4; chnl++) {
23452
+            a = pixels[origPix + chnl];
23453
+            b = pixels[origPix + 4 + chnl];
23454
+            c = pixels[origPix + w4 + chnl];
23455
+            d = pixels[origPix + w4 + 4 + chnl];
23456
+            color = a * (1 - xDiff) * (1 - yDiff) + b * xDiff * (1 - yDiff) +
23457
+                    c * yDiff * (1 - xDiff) + d * xDiff * yDiff;
23458
+            destPixels[offset++] = color;
23459
+          }
23460
+        }
23461
+      }
23462
+      return destImage;
23463
+    },
23464
+
23465
+    /**
23466
+     * hermiteFastResize
23467
+     * @param {Object} canvasEl Canvas element to apply filter to
23468
+     * @param {Number} oW Original Width
23469
+     * @param {Number} oH Original Height
23470
+     * @param {Number} dW Destination Width
23471
+     * @param {Number} dH Destination Height
23472
+     * @returns {ImageData}
23473
+     */
23474
+    hermiteFastResize: function(options, oW, oH, dW, dH) {
23475
+      var ratioW = this.rcpScaleX, ratioH = this.rcpScaleY,
23476
+          ratioWHalf = ceil(ratioW / 2),
23477
+          ratioHHalf = ceil(ratioH / 2),
23478
+          img = options.imageData, data = img.data,
23479
+          img2 = options.ctx.createImageData(dW, dH), data2 = img2.data;
23480
+      for (var j = 0; j < dH; j++) {
23481
+        for (var i = 0; i < dW; i++) {
23482
+          var x2 = (i + j * dW) * 4, weight = 0, weights = 0, weightsAlpha = 0,
23483
+              gxR = 0, gxG = 0, gxB = 0, gxA = 0, centerY = (j + 0.5) * ratioH;
23484
+          for (var yy = floor(j * ratioH); yy < (j + 1) * ratioH; yy++) {
23485
+            var dy = abs(centerY - (yy + 0.5)) / ratioHHalf,
23486
+                centerX = (i + 0.5) * ratioW, w0 = dy * dy;
23487
+            for (var xx = floor(i * ratioW); xx < (i + 1) * ratioW; xx++) {
23488
+              var dx = abs(centerX - (xx + 0.5)) / ratioWHalf,
23489
+                  w = sqrt(w0 + dx * dx);
23490
+              /* eslint-disable max-depth */
23491
+              if (w > 1 && w < -1) {
23492
+                continue;
23493
+              }
23494
+              //hermite filter
23495
+              weight = 2 * w * w * w - 3 * w * w + 1;
23496
+              if (weight > 0) {
23497
+                dx = 4 * (xx + yy * oW);
23498
+                //alpha
23499
+                gxA += weight * data[dx + 3];
23500
+                weightsAlpha += weight;
23501
+                //colors
23502
+                if (data[dx + 3] < 255) {
23503
+                  weight = weight * data[dx + 3] / 250;
23504
+                }
23505
+                gxR += weight * data[dx];
23506
+                gxG += weight * data[dx + 1];
23507
+                gxB += weight * data[dx + 2];
23508
+                weights += weight;
23509
+              }
23510
+              /* eslint-enable max-depth */
23511
+            }
23512
+          }
23513
+          data2[x2] = gxR / weights;
23514
+          data2[x2 + 1] = gxG / weights;
23515
+          data2[x2 + 2] = gxB / weights;
23516
+          data2[x2 + 3] = gxA / weightsAlpha;
23517
+        }
23518
+      }
23519
+      return img2;
23520
+    },
23521
+
23522
+    /**
23523
+     * Returns object representation of an instance
23524
+     * @return {Object} Object representation of an instance
23525
+     */
23526
+    toObject: function() {
23527
+      return {
23528
+        type: this.type,
23529
+        scaleX: this.scaleX,
23530
+        scaleY: this.scaleY,
23531
+        resizeType: this.resizeType,
23532
+        lanczosLobes: this.lanczosLobes
23533
+      };
23534
+    }
23535
+  });
23536
+
23537
+  /**
23538
+   * Returns filter instance from an object representation
23539
+   * @static
23540
+   * @param {Object} object Object to create an instance from
23541
+   * @param {Function} [callback] to be invoked after filter creation
23542
+   * @return {fabric.Image.filters.Resize} Instance of fabric.Image.filters.Resize
23543
+   */
23544
+  fabric.Image.filters.Resize.fromObject = fabric.Image.filters.BaseFilter.fromObject;
23545
+
23546
+})(typeof exports !== 'undefined' ? exports : this);
23547
+
23548
+
23549
+(function(global) {
23550
+
23551
+  'use strict';
23552
+
23553
+  var fabric  = global.fabric || (global.fabric = { }),
23554
+      filters = fabric.Image.filters,
23555
+      createClass = fabric.util.createClass;
23556
+
23557
+  /**
23558
+   * Contrast filter class
23559
+   * @class fabric.Image.filters.Contrast
23560
+   * @memberOf fabric.Image.filters
23561
+   * @extends fabric.Image.filters.BaseFilter
23562
+   * @see {@link fabric.Image.filters.Contrast#initialize} for constructor definition
23563
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
23564
+   * @example
23565
+   * var filter = new fabric.Image.filters.Contrast({
23566
+   *   contrast: 40
23567
+   * });
23568
+   * object.filters.push(filter);
23569
+   * object.applyFilters();
23570
+   */
23571
+  filters.Contrast = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Contrast.prototype */ {
23572
+
23573
+    /**
23574
+     * Filter type
23575
+     * @param {String} type
23576
+     * @default
23577
+     */
23578
+    type: 'Contrast',
23579
+
23580
+    fragmentSource: 'precision highp float;\n' +
23581
+      'uniform sampler2D uTexture;\n' +
23582
+      'uniform float uContrast;\n' +
23583
+      'varying vec2 vTexCoord;\n' +
23584
+      'void main() {\n' +
23585
+        'vec4 color = texture2D(uTexture, vTexCoord);\n' +
23586
+        'float contrastF = 1.015 * (uContrast + 1.0) / (1.0 * (1.015 - uContrast));\n' +
23587
+        'color.rgb = contrastF * (color.rgb - 0.5) + 0.5;\n' +
23588
+        'gl_FragColor = color;\n' +
23589
+      '}',
23590
+
23591
+    contrast: 0,
23592
+
23593
+    mainParameter: 'contrast',
23594
+
23595
+    /**
23596
+     * Constructor
23597
+     * @memberOf fabric.Image.filters.Contrast.prototype
23598
+     * @param {Object} [options] Options object
23599
+     * @param {Number} [options.contrast=0] Value to contrast the image up (-1...1)
23600
+     */
23601
+
23602
+    /**
23603
+      * Apply the Contrast operation to a Uint8Array representing the pixels of an image.
23604
+      *
23605
+      * @param {Object} options
23606
+      * @param {ImageData} options.imageData The Uint8Array to be filtered.
23607
+      */
23608
+    applyTo2d: function(options) {
23609
+      if (this.contrast === 0) {
23610
+        return;
23611
+      }
23612
+      var imageData = options.imageData, i, len,
23613
+          data = imageData.data, len = data.length,
23614
+          contrast = Math.floor(this.contrast * 255),
23615
+          contrastF = 259 * (contrast + 255) / (255 * (259 - contrast));
23616
+
23617
+      for (i = 0; i < len; i += 4) {
23618
+        data[i] = contrastF * (data[i] - 128) + 128;
23619
+        data[i + 1] = contrastF * (data[i + 1] - 128) + 128;
23620
+        data[i + 2] = contrastF * (data[i + 2] - 128) + 128;
23621
+      }
23622
+    },
23623
+
23624
+    /**
23625
+     * Return WebGL uniform locations for this filter's shader.
23626
+     *
23627
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
23628
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
23629
+     */
23630
+    getUniformLocations: function(gl, program) {
23631
+      return {
23632
+        uContrast: gl.getUniformLocation(program, 'uContrast'),
23633
+      };
23634
+    },
23635
+
23636
+    /**
23637
+     * Send data from this filter to its shader program's uniforms.
23638
+     *
23639
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
23640
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
23641
+     */
23642
+    sendUniformData: function(gl, uniformLocations) {
23643
+      gl.uniform1f(uniformLocations.uContrast, this.contrast);
23644
+    },
23645
+  });
23646
+
23647
+  /**
23648
+   * Returns filter instance from an object representation
23649
+   * @static
23650
+   * @param {Object} object Object to create an instance from
23651
+   * @param {function} [callback] to be invoked after filter creation
23652
+   * @return {fabric.Image.filters.Contrast} Instance of fabric.Image.filters.Contrast
23653
+   */
23654
+  fabric.Image.filters.Contrast.fromObject = fabric.Image.filters.BaseFilter.fromObject;
23655
+
23656
+})(typeof exports !== 'undefined' ? exports : this);
23657
+
23658
+
23659
+(function(global) {
23660
+
23661
+  'use strict';
23662
+
23663
+  var fabric  = global.fabric || (global.fabric = { }),
23664
+      filters = fabric.Image.filters,
23665
+      createClass = fabric.util.createClass;
23666
+
23667
+  /**
23668
+   * Saturate filter class
23669
+   * @class fabric.Image.filters.Saturation
23670
+   * @memberOf fabric.Image.filters
23671
+   * @extends fabric.Image.filters.BaseFilter
23672
+   * @see {@link fabric.Image.filters.Saturation#initialize} for constructor definition
23673
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
23674
+   * @example
23675
+   * var filter = new fabric.Image.filters.Saturation({
23676
+   *   saturation: 100
23677
+   * });
23678
+   * object.filters.push(filter);
23679
+   * object.applyFilters();
23680
+   */
23681
+  filters.Saturation = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Saturation.prototype */ {
23682
+
23683
+    /**
23684
+     * Filter type
23685
+     * @param {String} type
23686
+     * @default
23687
+     */
23688
+    type: 'Saturation',
23689
+
23690
+    fragmentSource: 'precision highp float;\n' +
23691
+      'uniform sampler2D uTexture;\n' +
23692
+      'uniform float uSaturation;\n' +
23693
+      'varying vec2 vTexCoord;\n' +
23694
+      'void main() {\n' +
23695
+        'vec4 color = texture2D(uTexture, vTexCoord);\n' +
23696
+        'float rgMax = max(color.r, color.g);\n' +
23697
+        'float rgbMax = max(rgMax, color.b);\n' +
23698
+        'color.r += rgbMax != color.r ? (rgbMax - color.r) * uSaturation : 0.00;\n' +
23699
+        'color.g += rgbMax != color.g ? (rgbMax - color.g) * uSaturation : 0.00;\n' +
23700
+        'color.b += rgbMax != color.b ? (rgbMax - color.b) * uSaturation : 0.00;\n' +
23701
+        'gl_FragColor = color;\n' +
23702
+      '}',
23703
+
23704
+    saturation: 0,
23705
+
23706
+    mainParameter: 'saturation',
23707
+
23708
+    /**
23709
+     * Constructor
23710
+     * @memberOf fabric.Image.filters.Saturate.prototype
23711
+     * @param {Object} [options] Options object
23712
+     * @param {Number} [options.saturate=0] Value to saturate the image (-1...1)
23713
+     */
23714
+
23715
+    /**
23716
+     * Apply the Saturation operation to a Uint8ClampedArray representing the pixels of an image.
23717
+     *
23718
+     * @param {Object} options
23719
+     * @param {ImageData} options.imageData The Uint8ClampedArray to be filtered.
23720
+     */
23721
+    applyTo2d: function(options) {
23722
+      if (this.saturation === 0) {
23723
+        return;
23724
+      }
23725
+      var imageData = options.imageData,
23726
+          data = imageData.data, len = data.length,
23727
+          adjust = -this.saturation, i, max;
23728
+
23729
+      for (i = 0; i < len; i += 4) {
23730
+        max = Math.max(data[i], data[i + 1], data[i + 2]);
23731
+        data[i] += max !== data[i] ? (max - data[i]) * adjust : 0;
23732
+        data[i + 1] += max !== data[i + 1] ? (max - data[i + 1]) * adjust : 0;
23733
+        data[i + 2] += max !== data[i + 2] ? (max - data[i + 2]) * adjust : 0;
23734
+      }
23735
+    },
23736
+
23737
+    /**
23738
+     * Return WebGL uniform locations for this filter's shader.
23739
+     *
23740
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
23741
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
23742
+     */
23743
+    getUniformLocations: function(gl, program) {
23744
+      return {
23745
+        uSaturation: gl.getUniformLocation(program, 'uSaturation'),
23746
+      };
23747
+    },
23748
+
23749
+    /**
23750
+     * Send data from this filter to its shader program's uniforms.
23751
+     *
23752
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
23753
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
23754
+     */
23755
+    sendUniformData: function(gl, uniformLocations) {
23756
+      gl.uniform1f(uniformLocations.uSaturation, -this.saturation);
23757
+    },
23758
+  });
23759
+
23760
+  /**
23761
+   * Returns filter instance from an object representation
23762
+   * @static
23763
+   * @param {Object} object Object to create an instance from
23764
+   * @param {Function} [callback] to be invoked after filter creation
23765
+   * @return {fabric.Image.filters.Saturation} Instance of fabric.Image.filters.Saturate
23766
+   */
23767
+  fabric.Image.filters.Saturation.fromObject = fabric.Image.filters.BaseFilter.fromObject;
23768
+
23769
+})(typeof exports !== 'undefined' ? exports : this);
23770
+
23771
+
23772
+(function(global) {
23773
+
23774
+  'use strict';
23775
+
23776
+  var fabric  = global.fabric || (global.fabric = { }),
23777
+      filters = fabric.Image.filters,
23778
+      createClass = fabric.util.createClass;
23779
+
23780
+  /**
23781
+   * Blur filter class
23782
+   * @class fabric.Image.filters.Blur
23783
+   * @memberOf fabric.Image.filters
23784
+   * @extends fabric.Image.filters.BaseFilter
23785
+   * @see {@link fabric.Image.filters.Blur#initialize} for constructor definition
23786
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
23787
+   * @example
23788
+   * var filter = new fabric.Image.filters.Blur({
23789
+   *   blur: 0.5
23790
+   * });
23791
+   * object.filters.push(filter);
23792
+   * object.applyFilters();
23793
+   * canvas.renderAll();
23794
+   */
23795
+  filters.Blur = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Blur.prototype */ {
23796
+
23797
+    type: 'Blur',
23798
+
23799
+    /*
23800
+'gl_FragColor = vec4(0.0);',
23801
+'gl_FragColor += texture2D(texture, vTexCoord + -7 * uDelta)*0.0044299121055113265;',
23802
+'gl_FragColor += texture2D(texture, vTexCoord + -6 * uDelta)*0.00895781211794;',
23803
+'gl_FragColor += texture2D(texture, vTexCoord + -5 * uDelta)*0.0215963866053;',
23804
+'gl_FragColor += texture2D(texture, vTexCoord + -4 * uDelta)*0.0443683338718;',
23805
+'gl_FragColor += texture2D(texture, vTexCoord + -3 * uDelta)*0.0776744219933;',
23806
+'gl_FragColor += texture2D(texture, vTexCoord + -2 * uDelta)*0.115876621105;',
23807
+'gl_FragColor += texture2D(texture, vTexCoord + -1 * uDelta)*0.147308056121;',
23808
+'gl_FragColor += texture2D(texture, vTexCoord              )*0.159576912161;',
23809
+'gl_FragColor += texture2D(texture, vTexCoord + 1 * uDelta)*0.147308056121;',
23810
+'gl_FragColor += texture2D(texture, vTexCoord + 2 * uDelta)*0.115876621105;',
23811
+'gl_FragColor += texture2D(texture, vTexCoord + 3 * uDelta)*0.0776744219933;',
23812
+'gl_FragColor += texture2D(texture, vTexCoord + 4 * uDelta)*0.0443683338718;',
23813
+'gl_FragColor += texture2D(texture, vTexCoord + 5 * uDelta)*0.0215963866053;',
23814
+'gl_FragColor += texture2D(texture, vTexCoord + 6 * uDelta)*0.00895781211794;',
23815
+'gl_FragColor += texture2D(texture, vTexCoord + 7 * uDelta)*0.0044299121055113265;',
23816
+*/
23817
+
23818
+    /* eslint-disable max-len */
23819
+    fragmentSource: 'precision highp float;\n' +
23820
+      'uniform sampler2D uTexture;\n' +
23821
+      'uniform vec2 uDelta;\n' +
23822
+      'varying vec2 vTexCoord;\n' +
23823
+      'const float nSamples = 15.0;\n' +
23824
+      'vec3 v3offset = vec3(12.9898, 78.233, 151.7182);\n' +
23825
+      'float random(vec3 scale) {\n' +
23826
+        /* use the fragment position for a different seed per-pixel */
23827
+        'return fract(sin(dot(gl_FragCoord.xyz, scale)) * 43758.5453);\n' +
23828
+      '}\n' +
23829
+      'void main() {\n' +
23830
+        'vec4 color = vec4(0.0);\n' +
23831
+        'float total = 0.0;\n' +
23832
+        'float offset = random(v3offset);\n' +
23833
+        'for (float t = -nSamples; t <= nSamples; t++) {\n' +
23834
+          'float percent = (t + offset - 0.5) / nSamples;\n' +
23835
+          'float weight = 1.0 - abs(percent);\n' +
23836
+          'color += texture2D(uTexture, vTexCoord + uDelta * percent) * weight;\n' +
23837
+          'total += weight;\n' +
23838
+        '}\n' +
23839
+        'gl_FragColor = color / total;\n' +
23840
+      '}',
23841
+    /* eslint-enable max-len */
23842
+
23843
+    /**
23844
+     * blur value, in percentage of image dimensions.
23845
+     * specific to keep the image blur constant at different resolutions
23846
+     * range bewteen 0 and 1.
23847
+     */
23848
+    blur: 0,
23849
+
23850
+    mainParameter: 'blur',
23851
+
23852
+    applyTo: function(options) {
23853
+      if (options.webgl) {
23854
+        // this aspectRatio is used to give the same blur to vertical and horizontal
23855
+        this.aspectRatio = options.sourceWidth / options.sourceHeight;
23856
+        options.passes++;
23857
+        this._setupFrameBuffer(options);
23858
+        this.horizontal = true;
23859
+        this.applyToWebGL(options);
23860
+        this._swapTextures(options);
23861
+        this._setupFrameBuffer(options);
23862
+        this.horizontal = false;
23863
+        this.applyToWebGL(options);
23864
+        this._swapTextures(options);
23865
+      }
23866
+      else {
23867
+        this.applyTo2d(options);
23868
+      }
23869
+    },
23870
+
23871
+    applyTo2d: function(options) {
23872
+      // paint canvasEl with current image data.
23873
+      //options.ctx.putImageData(options.imageData, 0, 0);
23874
+      options.imageData = this.simpleBlur(options);
23875
+    },
23876
+
23877
+    simpleBlur: function(options) {
23878
+      var resources = options.filterBackend.resources, canvas1, canvas2,
23879
+          width = options.imageData.width,
23880
+          height = options.imageData.height;
23881
+
23882
+      if (!resources.blurLayer1) {
23883
+        resources.blurLayer1 = fabric.util.createCanvasElement();
23884
+        resources.blurLayer2 = fabric.util.createCanvasElement();
23885
+      }
23886
+      canvas1 = resources.blurLayer1;
23887
+      canvas2 = resources.blurLayer2;
23888
+      if (canvas1.width !== width || canvas1.height !== height) {
23889
+        canvas2.width = canvas1.width = width;
23890
+        canvas2.height = canvas1.height = height;
23891
+      }
23892
+      var ctx1 = canvas1.getContext('2d'),
23893
+          ctx2 = canvas2.getContext('2d'),
23894
+          nSamples = 15,
23895
+          random, percent, j, i,
23896
+          blur = this.blur * 0.06 * 0.5;
23897
+
23898
+      // load first canvas
23899
+      ctx1.putImageData(options.imageData, 0, 0);
23900
+      ctx2.clearRect(0, 0, width, height);
23901
+
23902
+      for (i = -nSamples; i <= nSamples; i++) {
23903
+        random = (Math.random() - 0.5) / 4;
23904
+        percent = i / nSamples;
23905
+        j = blur * percent * width + random;
23906
+        ctx2.globalAlpha = 1 - Math.abs(percent);
23907
+        ctx2.drawImage(canvas1, j, random);
23908
+        ctx1.drawImage(canvas2, 0, 0);
23909
+        ctx2.globalAlpha = 1;
23910
+        ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
23911
+      }
23912
+      for (i = -nSamples; i <= nSamples; i++) {
23913
+        random = (Math.random() - 0.5) / 4;
23914
+        percent = i / nSamples;
23915
+        j = blur * percent * height + random;
23916
+        ctx2.globalAlpha = 1 - Math.abs(percent);
23917
+        ctx2.drawImage(canvas1, random, j);
23918
+        ctx1.drawImage(canvas2, 0, 0);
23919
+        ctx2.globalAlpha = 1;
23920
+        ctx2.clearRect(0, 0, canvas2.width, canvas2.height);
23921
+      }
23922
+      options.ctx.drawImage(canvas1, 0, 0);
23923
+      var newImageData = options.ctx.getImageData(0, 0, canvas1.width, canvas1.height);
23924
+      ctx1.globalAlpha = 1;
23925
+      ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
23926
+      return newImageData;
23927
+    },
23928
+
23929
+    /**
23930
+     * Return WebGL uniform locations for this filter's shader.
23931
+     *
23932
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
23933
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
23934
+     */
23935
+    getUniformLocations: function(gl, program) {
23936
+      return {
23937
+        delta: gl.getUniformLocation(program, 'uDelta'),
23938
+      };
23939
+    },
23940
+
23941
+    /**
23942
+     * Send data from this filter to its shader program's uniforms.
23943
+     *
23944
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
23945
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
23946
+     */
23947
+    sendUniformData: function(gl, uniformLocations) {
23948
+      var delta = this.chooseRightDelta();
23949
+      gl.uniform2fv(uniformLocations.delta, delta);
23950
+    },
23951
+
23952
+    /**
23953
+     * choose right value of image percentage to blur with
23954
+     * @returns {Array} a numeric array with delta values
23955
+     */
23956
+    chooseRightDelta: function() {
23957
+      var blurScale = 1, delta = [0, 0], blur;
23958
+      if (this.horizontal) {
23959
+        if (this.aspectRatio > 1) {
23960
+          // image is wide, i want to shrink radius horizontal
23961
+          blurScale = 1 / this.aspectRatio;
23962
+        }
23963
+      }
23964
+      else {
23965
+        if (this.aspectRatio < 1) {
23966
+          // image is tall, i want to shrink radius vertical
23967
+          blurScale = this.aspectRatio;
23968
+        }
23969
+      }
23970
+      blur = blurScale * this.blur * 0.12;
23971
+      if (this.horizontal) {
23972
+        delta[0] = blur;
23973
+      }
23974
+      else {
23975
+        delta[1] = blur;
23976
+      }
23977
+      return delta;
23978
+    },
23979
+  });
23980
+
23981
+  /**
23982
+   * Deserialize a JSON definition of a BlurFilter into a concrete instance.
23983
+   */
23984
+  filters.Blur.fromObject = fabric.Image.filters.BaseFilter.fromObject;
23985
+
23986
+})(typeof exports !== 'undefined' ? exports : this);
23987
+
23988
+
23989
+(function(global) {
23990
+
23991
+  'use strict';
23992
+
23993
+  var fabric  = global.fabric || (global.fabric = { }),
23994
+      filters = fabric.Image.filters,
23995
+      createClass = fabric.util.createClass;
23996
+
23997
+  /**
23998
+   * Gamma filter class
23999
+   * @class fabric.Image.filters.Gamma
24000
+   * @memberOf fabric.Image.filters
24001
+   * @extends fabric.Image.filters.BaseFilter
24002
+   * @see {@link fabric.Image.filters.Gamma#initialize} for constructor definition
24003
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
24004
+   * @example
24005
+   * var filter = new fabric.Image.filters.Gamma({
24006
+   *   brightness: 200
24007
+   * });
24008
+   * object.filters.push(filter);
24009
+   * object.applyFilters();
24010
+   */
24011
+  filters.Gamma = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Gamma.prototype */ {
24012
+
24013
+    /**
24014
+     * Filter type
24015
+     * @param {String} type
24016
+     * @default
24017
+     */
24018
+    type: 'Gamma',
24019
+
24020
+    fragmentSource: 'precision highp float;\n' +
24021
+      'uniform sampler2D uTexture;\n' +
24022
+      'uniform vec3 uGamma;\n' +
24023
+      'varying vec2 vTexCoord;\n' +
24024
+      'void main() {\n' +
24025
+        'vec4 color = texture2D(uTexture, vTexCoord);\n' +
24026
+        'vec3 correction = (1.0 / uGamma);\n' +
24027
+        'color.r = pow(color.r, correction.r);\n' +
24028
+        'color.g = pow(color.g, correction.g);\n' +
24029
+        'color.b = pow(color.b, correction.b);\n' +
24030
+        'gl_FragColor = color;\n' +
24031
+        'gl_FragColor.rgb *= color.a;\n' +
24032
+      '}',
24033
+
24034
+    /**
24035
+     * Gamma array value, from 0.01 to 2.2.
24036
+     * @param {Array} gamma
24037
+     * @default
24038
+     */
24039
+    gamma: [1, 1, 1],
24040
+
24041
+    /**
24042
+     * Describe the property that is the filter parameter
24043
+     * @param {String} m
24044
+     * @default
24045
+     */
24046
+    mainParameter: 'gamma',
24047
+
24048
+    /**
24049
+     * Constructor
24050
+     * @param {Object} [options] Options object
24051
+     */
24052
+    initialize: function(options) {
24053
+      this.gamma = [1, 1, 1];
24054
+      filters.BaseFilter.prototype.initialize.call(this, options);
24055
+    },
24056
+
24057
+    /**
24058
+     * Apply the Gamma operation to a Uint8Array representing the pixels of an image.
24059
+     *
24060
+     * @param {Object} options
24061
+     * @param {ImageData} options.imageData The Uint8Array to be filtered.
24062
+     */
24063
+    applyTo2d: function(options) {
24064
+      var imageData = options.imageData, data = imageData.data,
24065
+          gamma = this.gamma, len = data.length,
24066
+          rInv = 1 / gamma[0], gInv = 1 / gamma[1],
24067
+          bInv = 1 / gamma[2], i;
24068
+
24069
+      if (!this.rVals) {
24070
+        // eslint-disable-next-line
24071
+        this.rVals = new Uint8Array(256);
24072
+        // eslint-disable-next-line
24073
+        this.gVals = new Uint8Array(256);
24074
+        // eslint-disable-next-line
24075
+        this.bVals = new Uint8Array(256);
24076
+      }
24077
+
24078
+      // This is an optimization - pre-compute a look-up table for each color channel
24079
+      // instead of performing these pow calls for each pixel in the image.
24080
+      for (i = 0, len = 256; i < len; i++) {
24081
+        this.rVals[i] = Math.pow(i / 255, rInv) * 255;
24082
+        this.gVals[i] = Math.pow(i / 255, gInv) * 255;
24083
+        this.bVals[i] = Math.pow(i / 255, bInv) * 255;
24084
+      }
24085
+      for (i = 0, len = data.length; i < len; i += 4) {
24086
+        data[i] = this.rVals[data[i]];
24087
+        data[i + 1] = this.gVals[data[i + 1]];
24088
+        data[i + 2] = this.bVals[data[i + 2]];
24089
+      }
24090
+    },
24091
+
24092
+    /**
24093
+     * Return WebGL uniform locations for this filter's shader.
24094
+     *
24095
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
24096
+     * @param {WebGLShaderProgram} program This filter's compiled shader program.
24097
+     */
24098
+    getUniformLocations: function(gl, program) {
24099
+      return {
24100
+        uGamma: gl.getUniformLocation(program, 'uGamma'),
24101
+      };
24102
+    },
24103
+
24104
+    /**
24105
+     * Send data from this filter to its shader program's uniforms.
24106
+     *
24107
+     * @param {WebGLRenderingContext} gl The GL canvas context used to compile this filter's shader.
24108
+     * @param {Object} uniformLocations A map of string uniform names to WebGLUniformLocation objects
24109
+     */
24110
+    sendUniformData: function(gl, uniformLocations) {
24111
+      gl.uniform3fv(uniformLocations.uGamma, this.gamma);
24112
+    },
24113
+  });
24114
+
24115
+  /**
24116
+   * Returns filter instance from an object representation
24117
+   * @static
24118
+   * @param {Object} object Object to create an instance from
24119
+   * @param {function} [callback] to be invoked after filter creation
24120
+   * @return {fabric.Image.filters.Gamma} Instance of fabric.Image.filters.Gamma
24121
+   */
24122
+  fabric.Image.filters.Gamma.fromObject = fabric.Image.filters.BaseFilter.fromObject;
24123
+
24124
+})(typeof exports !== 'undefined' ? exports : this);
24125
+
24126
+
24127
+(function(global) {
24128
+
24129
+  'use strict';
24130
+
24131
+  var fabric  = global.fabric || (global.fabric = { }),
24132
+      filters = fabric.Image.filters,
24133
+      createClass = fabric.util.createClass;
24134
+
24135
+  /**
24136
+   * A container class that knows how to apply a sequence of filters to an input image.
24137
+   */
24138
+  filters.Composed = createClass(filters.BaseFilter, /** @lends fabric.Image.filters.Composed.prototype */ {
24139
+
24140
+    type: 'Composed',
24141
+
24142
+    /**
24143
+     * A non sparse array of filters to apply
24144
+     */
24145
+    subFilters: [],
24146
+
24147
+    /**
24148
+     * Constructor
24149
+     * @param {Object} [options] Options object
24150
+     */
24151
+    initialize: function(options) {
24152
+      this.callSuper('initialize', options);
24153
+      // create a new array instead mutating the prototype with push
24154
+      this.subFilters = this.subFilters.slice(0);
24155
+    },
24156
+
24157
+    /**
24158
+     * Apply this container's filters to the input image provided.
24159
+     *
24160
+     * @param {Object} options
24161
+     * @param {Number} options.passes The number of filters remaining to be applied.
24162
+     */
24163
+    applyTo: function(options) {
24164
+      options.passes += this.subFilters.length - 1;
24165
+      this.subFilters.forEach(function(filter) {
24166
+        filter.applyTo(options);
24167
+      });
24168
+    },
24169
+
24170
+    /**
24171
+     * Serialize this filter into JSON.
24172
+     *
24173
+     * @returns {Object} A JSON representation of this filter.
24174
+     */
24175
+    toObject: function() {
24176
+      return fabric.util.object.extend(this.callSuper('toObject'), {
24177
+        subFilters: this.subFilters.map(function(filter) { return filter.toObject(); }),
24178
+      });
24179
+    },
24180
+
24181
+    isNeutralState: function() {
24182
+      return !this.subFilters.some(function(filter) { return !filter.isNeutralState(); });
24183
+    }
24184
+  });
24185
+
24186
+  /**
24187
+   * Deserialize a JSON definition of a ComposedFilter into a concrete instance.
24188
+   */
24189
+  fabric.Image.filters.Composed.fromObject = function(object, callback) {
24190
+    var filters = object.subFilters || [],
24191
+        subFilters = filters.map(function(filter) {
24192
+          return new fabric.Image.filters[filter.type](filter);
24193
+        }),
24194
+        instance = new fabric.Image.filters.Composed({ subFilters: subFilters });
24195
+    callback && callback(instance);
24196
+    return instance;
24197
+  };
24198
+})(typeof exports !== 'undefined' ? exports : this);
24199
+
24200
+
24201
+(function(global) {
24202
+
24203
+  'use strict';
24204
+
24205
+  var fabric  = global.fabric || (global.fabric = { }),
24206
+      filters = fabric.Image.filters,
24207
+      createClass = fabric.util.createClass;
24208
+
24209
+  /**
24210
+   * HueRotation filter class
24211
+   * @class fabric.Image.filters.HueRotation
24212
+   * @memberOf fabric.Image.filters
24213
+   * @extends fabric.Image.filters.BaseFilter
24214
+   * @see {@link fabric.Image.filters.HueRotation#initialize} for constructor definition
24215
+   * @see {@link http://fabricjs.com/image-filters|ImageFilters demo}
24216
+   * @example
24217
+   * var filter = new fabric.Image.filters.HueRotation({
24218
+   *   rotation: -0.5
24219
+   * });
24220
+   * object.filters.push(filter);
24221
+   * object.applyFilters();
24222
+   */
24223
+  filters.HueRotation = createClass(filters.ColorMatrix, /** @lends fabric.Image.filters.HueRotation.prototype */ {
24224
+
24225
+    /**
24226
+     * Filter type
24227
+     * @param {String} type
24228
+     * @default
24229
+     */
24230
+    type: 'HueRotation',
24231
+
24232
+    /**
24233
+     * HueRotation value, from -1 to 1.
24234
+     * the unit is radians
24235
+     * @param {Number} myParameter
24236
+     * @default
24237
+     */
24238
+    rotation: 0,
24239
+
24240
+    /**
24241
+     * Describe the property that is the filter parameter
24242
+     * @param {String} m
24243
+     * @default
24244
+     */
24245
+    mainParameter: 'rotation',
24246
+
24247
+    calculateMatrix: function() {
24248
+      var rad = this.rotation * Math.PI, cos = fabric.util.cos(rad), sin = fabric.util.sin(rad),
24249
+          aThird = 1 / 3, aThirdSqtSin = Math.sqrt(aThird) * sin, OneMinusCos = 1 - cos;
24250
+      this.matrix = [
24251
+        1, 0, 0, 0, 0,
24252
+        0, 1, 0, 0, 0,
24253
+        0, 0, 1, 0, 0,
24254
+        0, 0, 0, 1, 0
24255
+      ];
24256
+      this.matrix[0] = cos + OneMinusCos / 3;
24257
+      this.matrix[1] = aThird * OneMinusCos - aThirdSqtSin;
24258
+      this.matrix[2] = aThird * OneMinusCos + aThirdSqtSin;
24259
+      this.matrix[5] = aThird * OneMinusCos + aThirdSqtSin;
24260
+      this.matrix[6] = cos + aThird * OneMinusCos;
24261
+      this.matrix[7] = aThird * OneMinusCos - aThirdSqtSin;
24262
+      this.matrix[10] = aThird * OneMinusCos - aThirdSqtSin;
24263
+      this.matrix[11] = aThird * OneMinusCos + aThirdSqtSin;
24264
+      this.matrix[12] = cos + aThird * OneMinusCos;
24265
+    },
24266
+
24267
+    /**
24268
+     * HueRotation isNeutralState implementation
24269
+     * Used only in image applyFilters to discard filters that will not have an effect
24270
+     * on the image
24271
+     * @param {Object} options
24272
+     **/
24273
+    isNeutralState: function(options) {
24274
+      this.calculateMatrix();
24275
+      return filters.BaseFilter.prototype.isNeutralState.call(this, options);
24276
+    },
24277
+
24278
+    /**
24279
+     * Apply this filter to the input image data provided.
24280
+     *
24281
+     * Determines whether to use WebGL or Canvas2D based on the options.webgl flag.
24282
+     *
24283
+     * @param {Object} options
24284
+     * @param {Number} options.passes The number of filters remaining to be executed
24285
+     * @param {Boolean} options.webgl Whether to use webgl to render the filter.
24286
+     * @param {WebGLTexture} options.sourceTexture The texture setup as the source to be filtered.
24287
+     * @param {WebGLTexture} options.targetTexture The texture where filtered output should be drawn.
24288
+     * @param {WebGLRenderingContext} options.context The GL context used for rendering.
24289
+     * @param {Object} options.programCache A map of compiled shader programs, keyed by filter type.
24290
+     */
24291
+    applyTo: function(options) {
24292
+      this.calculateMatrix();
24293
+      filters.BaseFilter.prototype.applyTo.call(this, options);
24294
+    },
24295
+
24296
+  });
24297
+
24298
+  /**
24299
+   * Returns filter instance from an object representation
24300
+   * @static
24301
+   * @param {Object} object Object to create an instance from
24302
+   * @param {function} [callback] to be invoked after filter creation
24303
+   * @return {fabric.Image.filters.HueRotation} Instance of fabric.Image.filters.HueRotation
24304
+   */
24305
+  fabric.Image.filters.HueRotation.fromObject = fabric.Image.filters.BaseFilter.fromObject;
24306
+
24307
+})(typeof exports !== 'undefined' ? exports : this);
24308
+
24309
+
24310
+(function(global) {
24311
+
24312
+  'use strict';
24313
+
24314
+  var fabric = global.fabric || (global.fabric = { }),
24315
+      clone = fabric.util.object.clone;
24316
+
24317
+  if (fabric.Text) {
24318
+    fabric.warn('fabric.Text is already defined');
24319
+    return;
24320
+  }
24321
+
24322
+  /**
24323
+   * Text class
24324
+   * @class fabric.Text
24325
+   * @extends fabric.Object
24326
+   * @return {fabric.Text} thisArg
24327
+   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2#text}
24328
+   * @see {@link fabric.Text#initialize} for constructor definition
24329
+   */
24330
+  fabric.Text = fabric.util.createClass(fabric.Object, /** @lends fabric.Text.prototype */ {
24331
+
24332
+    /**
24333
+     * Properties which when set cause object to change dimensions
24334
+     * @type Array
24335
+     * @private
24336
+     */
24337
+    _dimensionAffectingProps: [
24338
+      'fontSize',
24339
+      'fontWeight',
24340
+      'fontFamily',
24341
+      'fontStyle',
24342
+      'lineHeight',
24343
+      'text',
24344
+      'charSpacing',
24345
+      'textAlign',
24346
+      'styles',
24347
+    ],
24348
+
24349
+    /**
24350
+     * @private
24351
+     */
24352
+    _reNewline: /\r?\n/,
24353
+
24354
+    /**
24355
+     * Use this regular expression to filter for whitespaces that is not a new line.
24356
+     * Mostly used when text is 'justify' aligned.
24357
+     * @private
24358
+     */
24359
+    _reSpacesAndTabs: /[ \t\r]/g,
24360
+
24361
+    /**
24362
+     * Use this regular expression to filter for whitespace that is not a new line.
24363
+     * Mostly used when text is 'justify' aligned.
24364
+     * @private
24365
+     */
24366
+    _reSpaceAndTab: /[ \t\r]/,
24367
+
24368
+    /**
24369
+     * Use this regular expression to filter consecutive groups of non spaces.
24370
+     * Mostly used when text is 'justify' aligned.
24371
+     * @private
24372
+     */
24373
+    _reWords: /\S+/g,
24374
+
24375
+    /**
24376
+     * Type of an object
24377
+     * @type String
24378
+     * @default
24379
+     */
24380
+    type:                 'text',
24381
+
24382
+    /**
24383
+     * Font size (in pixels)
24384
+     * @type Number
24385
+     * @default
24386
+     */
24387
+    fontSize:             40,
24388
+
24389
+    /**
24390
+     * Font weight (e.g. bold, normal, 400, 600, 800)
24391
+     * @type {(Number|String)}
24392
+     * @default
24393
+     */
24394
+    fontWeight:           'normal',
24395
+
24396
+    /**
24397
+     * Font family
24398
+     * @type String
24399
+     * @default
24400
+     */
24401
+    fontFamily:           'Times New Roman',
24402
+
24403
+    /**
24404
+     * Text decoration underline.
24405
+     * @type Boolean
24406
+     * @default
24407
+     */
24408
+    underline:       false,
24409
+
24410
+    /**
24411
+     * Text decoration overline.
24412
+     * @type Boolean
24413
+     * @default
24414
+     */
24415
+    overline:       false,
24416
+
24417
+    /**
24418
+     * Text decoration linethrough.
24419
+     * @type Boolean
24420
+     * @default
24421
+     */
24422
+    linethrough:       false,
24423
+
24424
+    /**
24425
+     * Text alignment. Possible values: "left", "center", "right", "justify",
24426
+     * "justify-left", "justify-center" or "justify-right".
24427
+     * @type String
24428
+     * @default
24429
+     */
24430
+    textAlign:            'left',
24431
+
24432
+    /**
24433
+     * Font style . Possible values: "", "normal", "italic" or "oblique".
24434
+     * @type String
24435
+     * @default
24436
+     */
24437
+    fontStyle:            'normal',
24438
+
24439
+    /**
24440
+     * Line height
24441
+     * @type Number
24442
+     * @default
24443
+     */
24444
+    lineHeight:           1.16,
24445
+
24446
+    /**
24447
+     * Superscript schema object (minimum overlap)
24448
+     * @type {Object}
24449
+     * @default
24450
+     */
24451
+    superscript: {
24452
+      size:      0.60, // fontSize factor
24453
+      baseline: -0.35  // baseline-shift factor (upwards)
24454
+    },
24455
+
24456
+    /**
24457
+     * Subscript schema object (minimum overlap)
24458
+     * @type {Object}
24459
+     * @default
24460
+     */
24461
+    subscript: {
24462
+      size:      0.60, // fontSize factor
24463
+      baseline:  0.11  // baseline-shift factor (downwards)
24464
+    },
24465
+
24466
+    /**
24467
+     * Background color of text lines
24468
+     * @type String
24469
+     * @default
24470
+     */
24471
+    textBackgroundColor:  '',
24472
+
24473
+    /**
24474
+     * List of properties to consider when checking if
24475
+     * state of an object is changed ({@link fabric.Object#hasStateChanged})
24476
+     * as well as for history (undo/redo) purposes
24477
+     * @type Array
24478
+     */
24479
+    stateProperties: fabric.Object.prototype.stateProperties.concat('fontFamily',
24480
+      'fontWeight',
24481
+      'fontSize',
24482
+      'text',
24483
+      'underline',
24484
+      'overline',
24485
+      'linethrough',
24486
+      'textAlign',
24487
+      'fontStyle',
24488
+      'lineHeight',
24489
+      'textBackgroundColor',
24490
+      'charSpacing',
24491
+      'styles'),
24492
+
24493
+    /**
24494
+     * List of properties to consider when checking if cache needs refresh
24495
+     * @type Array
24496
+     */
24497
+    cacheProperties: fabric.Object.prototype.cacheProperties.concat('fontFamily',
24498
+      'fontWeight',
24499
+      'fontSize',
24500
+      'text',
24501
+      'underline',
24502
+      'overline',
24503
+      'linethrough',
24504
+      'textAlign',
24505
+      'fontStyle',
24506
+      'lineHeight',
24507
+      'textBackgroundColor',
24508
+      'charSpacing',
24509
+      'styles'),
24510
+
24511
+    /**
24512
+     * When defined, an object is rendered via stroke and this property specifies its color.
24513
+     * <b>Backwards incompatibility note:</b> This property was named "strokeStyle" until v1.1.6
24514
+     * @type String
24515
+     * @default
24516
+     */
24517
+    stroke:               null,
24518
+
24519
+    /**
24520
+     * Shadow object representing shadow of this shape.
24521
+     * <b>Backwards incompatibility note:</b> This property was named "textShadow" (String) until v1.2.11
24522
+     * @type fabric.Shadow
24523
+     * @default
24524
+     */
24525
+    shadow:               null,
24526
+
24527
+    /**
24528
+     * @private
24529
+     */
24530
+    _fontSizeFraction: 0.222,
24531
+
24532
+    /**
24533
+     * @private
24534
+     */
24535
+    offsets: {
24536
+      underline: 0.10,
24537
+      linethrough: -0.315,
24538
+      overline: -0.88
24539
+    },
24540
+
24541
+    /**
24542
+     * Text Line proportion to font Size (in pixels)
24543
+     * @type Number
24544
+     * @default
24545
+     */
24546
+    _fontSizeMult:             1.13,
24547
+
24548
+    /**
24549
+     * additional space between characters
24550
+     * expressed in thousands of em unit
24551
+     * @type Number
24552
+     * @default
24553
+     */
24554
+    charSpacing:             0,
24555
+
24556
+    /**
24557
+     * Object containing character styles - top-level properties -> line numbers,
24558
+     * 2nd-level properties - charater numbers
24559
+     * @type Object
24560
+     * @default
24561
+     */
24562
+    styles: null,
24563
+
24564
+    /**
24565
+     * Reference to a context to measure text char or couple of chars
24566
+     * the cacheContext of the canvas will be used or a freshly created one if the object is not on canvas
24567
+     * once created it will be referenced on fabric._measuringContext to avoide creating a canvas for every
24568
+     * text object created.
24569
+     * @type {CanvasRenderingContext2D}
24570
+     * @default
24571
+     */
24572
+    _measuringContext: null,
24573
+
24574
+    /**
24575
+     * Baseline shift, stlyes only, keep at 0 for the main text object
24576
+     * @type {Number}
24577
+     * @default
24578
+     */
24579
+    deltaY: 0,
24580
+
24581
+    /**
24582
+     * Array of properties that define a style unit (of 'styles').
24583
+     * @type {Array}
24584
+     * @default
24585
+     */
24586
+    _styleProperties: [
24587
+      'stroke',
24588
+      'strokeWidth',
24589
+      'fill',
24590
+      'fontFamily',
24591
+      'fontSize',
24592
+      'fontWeight',
24593
+      'fontStyle',
24594
+      'underline',
24595
+      'overline',
24596
+      'linethrough',
24597
+      'deltaY',
24598
+      'textBackgroundColor',
24599
+    ],
24600
+
24601
+    /**
24602
+     * contains characters bounding boxes
24603
+     */
24604
+    __charBounds: [],
24605
+
24606
+    /**
24607
+     * use this size when measuring text. To avoid IE11 rounding errors
24608
+     * @type {Number}
24609
+     * @default
24610
+     * @readonly
24611
+     * @private
24612
+     */
24613
+    CACHE_FONT_SIZE: 400,
24614
+
24615
+    /**
24616
+     * contains the min text width to avoid getting 0
24617
+     * @type {Number}
24618
+     * @default
24619
+     */
24620
+    MIN_TEXT_WIDTH: 2,
24621
+
24622
+    /**
24623
+     * Constructor
24624
+     * @param {String} text Text string
24625
+     * @param {Object} [options] Options object
24626
+     * @return {fabric.Text} thisArg
24627
+     */
24628
+    initialize: function(text, options) {
24629
+      this.styles = options ? (options.styles || { }) : { };
24630
+      this.text = text;
24631
+      this.__skipDimension = true;
24632
+      this.callSuper('initialize', options);
24633
+      this.__skipDimension = false;
24634
+      this.initDimensions();
24635
+      this.setCoords();
24636
+      this.setupState({ propertySet: '_dimensionAffectingProps' });
24637
+    },
24638
+
24639
+    /**
24640
+     * Return a contex for measurement of text string.
24641
+     * if created it gets stored for reuse
24642
+     * @param {String} text Text string
24643
+     * @param {Object} [options] Options object
24644
+     * @return {fabric.Text} thisArg
24645
+     */
24646
+    getMeasuringContext: function() {
24647
+      // if we did not return we have to measure something.
24648
+      if (!fabric._measuringContext) {
24649
+        fabric._measuringContext = this.canvas && this.canvas.contextCache ||
24650
+          fabric.util.createCanvasElement().getContext('2d');
24651
+      }
24652
+      return fabric._measuringContext;
24653
+    },
24654
+
24655
+    /**
24656
+     * @private
24657
+     * Divides text into lines of text and lines of graphemes.
24658
+     */
24659
+    _splitText: function() {
24660
+      var newLines = this._splitTextIntoLines(this.text);
24661
+      this.textLines = newLines.lines;
24662
+      this._textLines = newLines.graphemeLines;
24663
+      this._unwrappedTextLines = newLines._unwrappedLines;
24664
+      this._text = newLines.graphemeText;
24665
+      return newLines;
24666
+    },
24667
+
24668
+    /**
24669
+     * Initialize or update text dimensions.
24670
+     * Updates this.width and this.height with the proper values.
24671
+     * Does not return dimensions.
24672
+     */
24673
+    initDimensions: function() {
24674
+      if (this.__skipDimension) {
24675
+        return;
24676
+      }
24677
+      this._splitText();
24678
+      this._clearCache();
24679
+      this.width = this.calcTextWidth() || this.cursorWidth || this.MIN_TEXT_WIDTH;
24680
+      if (this.textAlign.indexOf('justify') !== -1) {
24681
+        // once text is measured we need to make space fatter to make justified text.
24682
+        this.enlargeSpaces();
24683
+      }
24684
+      this.height = this.calcTextHeight();
24685
+      this.saveState({ propertySet: '_dimensionAffectingProps' });
24686
+    },
24687
+
24688
+    /**
24689
+     * Enlarge space boxes and shift the others
24690
+     */
24691
+    enlargeSpaces: function() {
24692
+      var diffSpace, currentLineWidth, numberOfSpaces, accumulatedSpace, line, charBound, spaces;
24693
+      for (var i = 0, len = this._textLines.length; i < len; i++) {
24694
+        if (this.textAlign !== 'justify' && (i === len - 1 || this.isEndOfWrapping(i))) {
24695
+          continue;
24696
+        }
24697
+        accumulatedSpace = 0;
24698
+        line = this._textLines[i];
24699
+        currentLineWidth = this.getLineWidth(i);
24700
+        if (currentLineWidth < this.width && (spaces = this.textLines[i].match(this._reSpacesAndTabs))) {
24701
+          numberOfSpaces = spaces.length;
24702
+          diffSpace = (this.width - currentLineWidth) / numberOfSpaces;
24703
+          for (var j = 0, jlen = line.length; j <= jlen; j++) {
24704
+            charBound = this.__charBounds[i][j];
24705
+            if (this._reSpaceAndTab.test(line[j])) {
24706
+              charBound.width += diffSpace;
24707
+              charBound.kernedWidth += diffSpace;
24708
+              charBound.left += accumulatedSpace;
24709
+              accumulatedSpace += diffSpace;
24710
+            }
24711
+            else {
24712
+              charBound.left += accumulatedSpace;
24713
+            }
24714
+          }
24715
+        }
24716
+      }
24717
+    },
24718
+
24719
+    /**
24720
+     * Detect if the text line is ended with an hard break
24721
+     * text and itext do not have wrapping, return false
24722
+     * @return {Boolean}
24723
+     */
24724
+    isEndOfWrapping: function(lineIndex) {
24725
+      return lineIndex === this._textLines.length - 1;
24726
+    },
24727
+
24728
+    /**
24729
+     * Returns string representation of an instance
24730
+     * @return {String} String representation of text object
24731
+     */
24732
+    toString: function() {
24733
+      return '#<fabric.Text (' + this.complexity() +
24734
+        '): { "text": "' + this.text + '", "fontFamily": "' + this.fontFamily + '" }>';
24735
+    },
24736
+
24737
+    /**
24738
+     * Return the dimension and the zoom level needed to create a cache canvas
24739
+     * big enough to host the object to be cached.
24740
+     * @private
24741
+     * @param {Object} dim.x width of object to be cached
24742
+     * @param {Object} dim.y height of object to be cached
24743
+     * @return {Object}.width width of canvas
24744
+     * @return {Object}.height height of canvas
24745
+     * @return {Object}.zoomX zoomX zoom value to unscale the canvas before drawing cache
24746
+     * @return {Object}.zoomY zoomY zoom value to unscale the canvas before drawing cache
24747
+     */
24748
+    _getCacheCanvasDimensions: function() {
24749
+      var dims = this.callSuper('_getCacheCanvasDimensions');
24750
+      var fontSize = this.fontSize;
24751
+      dims.width += fontSize * dims.zoomX;
24752
+      dims.height += fontSize * dims.zoomY;
24753
+      return dims;
24754
+    },
24755
+
24756
+    /**
24757
+     * @private
24758
+     * @param {CanvasRenderingContext2D} ctx Context to render on
24759
+     */
24760
+    _render: function(ctx) {
24761
+      this._setTextStyles(ctx);
24762
+      this._renderTextLinesBackground(ctx);
24763
+      this._renderTextDecoration(ctx, 'underline');
24764
+      this._renderText(ctx);
24765
+      this._renderTextDecoration(ctx, 'overline');
24766
+      this._renderTextDecoration(ctx, 'linethrough');
24767
+    },
24768
+
24769
+    /**
24770
+     * @private
24771
+     * @param {CanvasRenderingContext2D} ctx Context to render on
24772
+     */
24773
+    _renderText: function(ctx) {
24774
+      if (this.paintFirst === 'stroke') {
24775
+        this._renderTextStroke(ctx);
24776
+        this._renderTextFill(ctx);
24777
+      }
24778
+      else {
24779
+        this._renderTextFill(ctx);
24780
+        this._renderTextStroke(ctx);
24781
+      }
24782
+    },
24783
+
24784
+    /**
24785
+     * Set the font parameter of the context with the object properties or with charStyle
24786
+     * @private
24787
+     * @param {CanvasRenderingContext2D} ctx Context to render on
24788
+     * @param {Object} [charStyle] object with font style properties
24789
+     * @param {String} [charStyle.fontFamily] Font Family
24790
+     * @param {Number} [charStyle.fontSize] Font size in pixels. ( without px suffix )
24791
+     * @param {String} [charStyle.fontWeight] Font weight
24792
+     * @param {String} [charStyle.fontStyle] Font style (italic|normal)
24793
+     */
24794
+    _setTextStyles: function(ctx, charStyle, forMeasuring) {
24795
+      ctx.textBaseline = 'alphabetic';
24796
+      ctx.font = this._getFontDeclaration(charStyle, forMeasuring);
24797
+    },
24798
+
24799
+    /**
24800
+     * calculate and return the text Width measuring each line.
24801
+     * @private
24802
+     * @param {CanvasRenderingContext2D} ctx Context to render on
24803
+     * @return {Number} Maximum width of fabric.Text object
24804
+     */
24805
+    calcTextWidth: function() {
24806
+      var maxWidth = this.getLineWidth(0);
24807
+
24808
+      for (var i = 1, len = this._textLines.length; i < len; i++) {
24809
+        var currentLineWidth = this.getLineWidth(i);
24810
+        if (currentLineWidth > maxWidth) {
24811
+          maxWidth = currentLineWidth;
24812
+        }
24813
+      }
24814
+      return maxWidth;
24815
+    },
24816
+
24817
+    /**
24818
+     * @private
24819
+     * @param {String} method Method name ("fillText" or "strokeText")
24820
+     * @param {CanvasRenderingContext2D} ctx Context to render on
24821
+     * @param {String} line Text to render
24822
+     * @param {Number} left Left position of text
24823
+     * @param {Number} top Top position of text
24824
+     * @param {Number} lineIndex Index of a line in a text
24825
+     */
24826
+    _renderTextLine: function(method, ctx, line, left, top, lineIndex) {
24827
+      this._renderChars(method, ctx, line, left, top, lineIndex);
24828
+    },
24829
+
24830
+    /**
24831
+     * Renders the text background for lines, taking care of style
24832
+     * @private
24833
+     * @param {CanvasRenderingContext2D} ctx Context to render on
24834
+     */
24835
+    _renderTextLinesBackground: function(ctx) {
24836
+      if (!this.textBackgroundColor && !this.styleHas('textBackgroundColor')) {
24837
+        return;
24838
+      }
24839
+      var lineTopOffset = 0, heightOfLine,
24840
+          lineLeftOffset, originalFill = ctx.fillStyle,
24841
+          line, lastColor,
24842
+          leftOffset = this._getLeftOffset(),
24843
+          topOffset = this._getTopOffset(),
24844
+          boxStart = 0, boxWidth = 0, charBox, currentColor;
24845
+
24846
+      for (var i = 0, len = this._textLines.length; i < len; i++) {
24847
+        heightOfLine = this.getHeightOfLine(i);
24848
+        if (!this.textBackgroundColor && !this.styleHas('textBackgroundColor', i)) {
24849
+          lineTopOffset += heightOfLine;
24850
+          continue;
24851
+        }
24852
+        line = this._textLines[i];
24853
+        lineLeftOffset = this._getLineLeftOffset(i);
24854
+        boxWidth = 0;
24855
+        boxStart = 0;
24856
+        lastColor = this.getValueOfPropertyAt(i, 0, 'textBackgroundColor');
24857
+        for (var j = 0, jlen = line.length; j < jlen; j++) {
24858
+          charBox = this.__charBounds[i][j];
24859
+          currentColor = this.getValueOfPropertyAt(i, j, 'textBackgroundColor');
24860
+          if (currentColor !== lastColor) {
24861
+            ctx.fillStyle = lastColor;
24862
+            lastColor && ctx.fillRect(
24863
+              leftOffset + lineLeftOffset + boxStart,
24864
+              topOffset + lineTopOffset,
24865
+              boxWidth,
24866
+              heightOfLine / this.lineHeight
24867
+            );
24868
+            boxStart = charBox.left;
24869
+            boxWidth = charBox.width;
24870
+            lastColor = currentColor;
24871
+          }
24872
+          else {
24873
+            boxWidth += charBox.kernedWidth;
24874
+          }
24875
+        }
24876
+        if (currentColor) {
24877
+          ctx.fillStyle = currentColor;
24878
+          ctx.fillRect(
24879
+            leftOffset + lineLeftOffset + boxStart,
24880
+            topOffset + lineTopOffset,
24881
+            boxWidth,
24882
+            heightOfLine / this.lineHeight
24883
+          );
24884
+        }
24885
+        lineTopOffset += heightOfLine;
24886
+      }
24887
+      ctx.fillStyle = originalFill;
24888
+      // if there is text background color no
24889
+      // other shadows should be casted
24890
+      this._removeShadow(ctx);
24891
+    },
24892
+
24893
+    /**
24894
+     * @private
24895
+     * @param {Object} decl style declaration for cache
24896
+     * @param {String} decl.fontFamily fontFamily
24897
+     * @param {String} decl.fontStyle fontStyle
24898
+     * @param {String} decl.fontWeight fontWeight
24899
+     * @return {Object} reference to cache
24900
+     */
24901
+    getFontCache: function(decl) {
24902
+      var fontFamily = decl.fontFamily.toLowerCase();
24903
+      if (!fabric.charWidthsCache[fontFamily]) {
24904
+        fabric.charWidthsCache[fontFamily] = { };
24905
+      }
24906
+      var cache = fabric.charWidthsCache[fontFamily],
24907
+          cacheProp = decl.fontStyle.toLowerCase() + '_' + (decl.fontWeight + '').toLowerCase();
24908
+      if (!cache[cacheProp]) {
24909
+        cache[cacheProp] = { };
24910
+      }
24911
+      return cache[cacheProp];
24912
+    },
24913
+
24914
+    /**
24915
+     * apply all the character style to canvas for rendering
24916
+     * @private
24917
+     * @param {String} _char
24918
+     * @param {Number} lineIndex
24919
+     * @param {Number} charIndex
24920
+     * @param {Object} [decl]
24921
+     */
24922
+    _applyCharStyles: function(method, ctx, lineIndex, charIndex, styleDeclaration) {
24923
+
24924
+      this._setFillStyles(ctx, styleDeclaration);
24925
+      this._setStrokeStyles(ctx, styleDeclaration);
24926
+
24927
+      ctx.font = this._getFontDeclaration(styleDeclaration);
24928
+    },
24929
+
24930
+    /**
24931
+     * measure and return the width of a single character.
24932
+     * possibly overridden to accommodate different measure logic or
24933
+     * to hook some external lib for character measurement
24934
+     * @private
24935
+     * @param {String} char to be measured
24936
+     * @param {Object} charStyle style of char to be measured
24937
+     * @param {String} [previousChar] previous char
24938
+     * @param {Object} [prevCharStyle] style of previous char
24939
+     */
24940
+    _measureChar: function(_char, charStyle, previousChar, prevCharStyle) {
24941
+      // first i try to return from cache
24942
+      var fontCache = this.getFontCache(charStyle), fontDeclaration = this._getFontDeclaration(charStyle),
24943
+          previousFontDeclaration = this._getFontDeclaration(prevCharStyle), couple = previousChar + _char,
24944
+          stylesAreEqual = fontDeclaration === previousFontDeclaration, width, coupleWidth, previousWidth,
24945
+          fontMultiplier = charStyle.fontSize / this.CACHE_FONT_SIZE, kernedWidth;
24946
+
24947
+      if (previousChar && fontCache[previousChar] !== undefined) {
24948
+        previousWidth = fontCache[previousChar];
24949
+      }
24950
+      if (fontCache[_char] !== undefined) {
24951
+        kernedWidth = width = fontCache[_char];
24952
+      }
24953
+      if (stylesAreEqual && fontCache[couple] !== undefined) {
24954
+        coupleWidth = fontCache[couple];
24955
+        kernedWidth = coupleWidth - previousWidth;
24956
+      }
24957
+      if (width === undefined || previousWidth === undefined || coupleWidth === undefined) {
24958
+        var ctx = this.getMeasuringContext();
24959
+        // send a TRUE to specify measuring font size CACHE_FONT_SIZE
24960
+        this._setTextStyles(ctx, charStyle, true);
24961
+      }
24962
+      if (width === undefined) {
24963
+        kernedWidth = width = ctx.measureText(_char).width;
24964
+        fontCache[_char] = width;
24965
+      }
24966
+      if (previousWidth === undefined && stylesAreEqual && previousChar) {
24967
+        previousWidth = ctx.measureText(previousChar).width;
24968
+        fontCache[previousChar] = previousWidth;
24969
+      }
24970
+      if (stylesAreEqual && coupleWidth === undefined) {
24971
+        // we can measure the kerning couple and subtract the width of the previous character
24972
+        coupleWidth = ctx.measureText(couple).width;
24973
+        fontCache[couple] = coupleWidth;
24974
+        kernedWidth = coupleWidth - previousWidth;
24975
+      }
24976
+      return { width: width * fontMultiplier, kernedWidth: kernedWidth * fontMultiplier };
24977
+    },
24978
+
24979
+    /**
24980
+     * Computes height of character at given position
24981
+     * @param {Number} line the line number
24982
+     * @param {Number} char the character number
24983
+     * @return {Number} fontSize of the character
24984
+     */
24985
+    getHeightOfChar: function(line, char) {
24986
+      return this.getValueOfPropertyAt(line, char, 'fontSize');
24987
+    },
24988
+
24989
+    /**
24990
+     * measure a text line measuring all characters.
24991
+     * @param {Number} lineIndex line number
24992
+     * @return {Number} Line width
24993
+     */
24994
+    measureLine: function(lineIndex) {
24995
+      var lineInfo = this._measureLine(lineIndex);
24996
+      if (this.charSpacing !== 0) {
24997
+        lineInfo.width -= this._getWidthOfCharSpacing();
24998
+      }
24999
+      if (lineInfo.width < 0) {
25000
+        lineInfo.width = 0;
25001
+      }
25002
+      return lineInfo;
25003
+    },
25004
+
25005
+    /**
25006
+     * measure every grapheme of a line, populating __charBounds
25007
+     * @param {Number} lineIndex
25008
+     * @return {Object} object.width total width of characters
25009
+     * @return {Object} object.widthOfSpaces length of chars that match this._reSpacesAndTabs
25010
+     */
25011
+    _measureLine: function(lineIndex) {
25012
+      var width = 0, i, grapheme, line = this._textLines[lineIndex], prevGrapheme,
25013
+          graphemeInfo, numOfSpaces = 0, lineBounds = new Array(line.length);
25014
+
25015
+      this.__charBounds[lineIndex] = lineBounds;
25016
+      for (i = 0; i < line.length; i++) {
25017
+        grapheme = line[i];
25018
+        graphemeInfo = this._getGraphemeBox(grapheme, lineIndex, i, prevGrapheme);
25019
+        lineBounds[i] = graphemeInfo;
25020
+        width += graphemeInfo.kernedWidth;
25021
+        prevGrapheme = grapheme;
25022
+      }
25023
+      // this latest bound box represent the last character of the line
25024
+      // to simplify cursor handling in interactive mode.
25025
+      lineBounds[i] = {
25026
+        left: graphemeInfo ? graphemeInfo.left + graphemeInfo.width : 0,
25027
+        width: 0,
25028
+        kernedWidth: 0,
25029
+        height: this.fontSize
25030
+      };
25031
+      return { width: width, numOfSpaces: numOfSpaces };
25032
+    },
25033
+
25034
+    /**
25035
+     * Measure and return the info of a single grapheme.
25036
+     * needs the the info of previous graphemes already filled
25037
+     * @private
25038
+     * @param {String} grapheme to be measured
25039
+     * @param {Number} lineIndex index of the line where the char is
25040
+     * @param {Number} charIndex position in the line
25041
+     * @param {String} [prevGrapheme] character preceding the one to be measured
25042
+     */
25043
+    _getGraphemeBox: function(grapheme, lineIndex, charIndex, prevGrapheme, skipLeft) {
25044
+      var style = this.getCompleteStyleDeclaration(lineIndex, charIndex),
25045
+          prevStyle = prevGrapheme ? this.getCompleteStyleDeclaration(lineIndex, charIndex - 1) : { },
25046
+          info = this._measureChar(grapheme, style, prevGrapheme, prevStyle),
25047
+          kernedWidth = info.kernedWidth,
25048
+          width = info.width, charSpacing;
25049
+
25050
+      if (this.charSpacing !== 0) {
25051
+        charSpacing = this._getWidthOfCharSpacing();
25052
+        width += charSpacing;
25053
+        kernedWidth += charSpacing;
25054
+      }
25055
+
25056
+      var box = {
25057
+        width: width,
25058
+        left: 0,
25059
+        height: style.fontSize,
25060
+        kernedWidth: kernedWidth,
25061
+        deltaY: style.deltaY,
25062
+      };
25063
+      if (charIndex > 0 && !skipLeft) {
25064
+        var previousBox = this.__charBounds[lineIndex][charIndex - 1];
25065
+        box.left = previousBox.left + previousBox.width + info.kernedWidth - info.width;
25066
+      }
25067
+      return box;
25068
+    },
25069
+
25070
+    /**
25071
+     * Calculate height of line at 'lineIndex'
25072
+     * @param {Number} lineIndex index of line to calculate
25073
+     * @return {Number}
25074
+     */
25075
+    getHeightOfLine: function(lineIndex) {
25076
+      if (this.__lineHeights[lineIndex]) {
25077
+        return this.__lineHeights[lineIndex];
25078
+      }
25079
+
25080
+      var line = this._textLines[lineIndex],
25081
+          // char 0 is measured before the line cycle because it nneds to char
25082
+          // emptylines
25083
+          maxHeight = this.getHeightOfChar(lineIndex, 0);
25084
+      for (var i = 1, len = line.length; i < len; i++) {
25085
+        maxHeight = Math.max(this.getHeightOfChar(lineIndex, i), maxHeight);
25086
+      }
25087
+
25088
+      return this.__lineHeights[lineIndex] = maxHeight * this.lineHeight * this._fontSizeMult;
25089
+    },
25090
+
25091
+    /**
25092
+     * Calculate text box height
25093
+     */
25094
+    calcTextHeight: function() {
25095
+      var lineHeight, height = 0;
25096
+      for (var i = 0, len = this._textLines.length; i < len; i++) {
25097
+        lineHeight = this.getHeightOfLine(i);
25098
+        height += (i === len - 1 ? lineHeight / this.lineHeight : lineHeight);
25099
+      }
25100
+      return height;
25101
+    },
25102
+
25103
+    /**
25104
+     * @private
25105
+     * @return {Number} Left offset
25106
+     */
25107
+    _getLeftOffset: function() {
25108
+      return -this.width / 2;
25109
+    },
25110
+
25111
+    /**
25112
+     * @private
25113
+     * @return {Number} Top offset
25114
+     */
25115
+    _getTopOffset: function() {
25116
+      return -this.height / 2;
25117
+    },
25118
+
25119
+    /**
25120
+     * @private
25121
+     * @param {CanvasRenderingContext2D} ctx Context to render on
25122
+     * @param {String} method Method name ("fillText" or "strokeText")
25123
+     */
25124
+    _renderTextCommon: function(ctx, method) {
25125
+      ctx.save();
25126
+      var lineHeights = 0, left = this._getLeftOffset(), top = this._getTopOffset(),
25127
+          offsets = this._applyPatternGradientTransform(ctx, method === 'fillText' ? this.fill : this.stroke);
25128
+      for (var i = 0, len = this._textLines.length; i < len; i++) {
25129
+        var heightOfLine = this.getHeightOfLine(i),
25130
+            maxHeight = heightOfLine / this.lineHeight,
25131
+            leftOffset = this._getLineLeftOffset(i);
25132
+        this._renderTextLine(
25133
+          method,
25134
+          ctx,
25135
+          this._textLines[i],
25136
+          left + leftOffset - offsets.offsetX,
25137
+          top + lineHeights + maxHeight - offsets.offsetY,
25138
+          i
25139
+        );
25140
+        lineHeights += heightOfLine;
25141
+      }
25142
+      ctx.restore();
25143
+    },
25144
+
25145
+    /**
25146
+     * @private
25147
+     * @param {CanvasRenderingContext2D} ctx Context to render on
25148
+     */
25149
+    _renderTextFill: function(ctx) {
25150
+      if (!this.fill && !this.styleHas('fill')) {
25151
+        return;
25152
+      }
25153
+
25154
+      this._renderTextCommon(ctx, 'fillText');
25155
+    },
25156
+
25157
+    /**
25158
+     * @private
25159
+     * @param {CanvasRenderingContext2D} ctx Context to render on
25160
+     */
25161
+    _renderTextStroke: function(ctx) {
25162
+      if ((!this.stroke || this.strokeWidth === 0) && this.isEmptyStyles()) {
25163
+        return;
25164
+      }
25165
+
25166
+      if (this.shadow && !this.shadow.affectStroke) {
25167
+        this._removeShadow(ctx);
25168
+      }
25169
+
25170
+      ctx.save();
25171
+      this._setLineDash(ctx, this.strokeDashArray);
25172
+      ctx.beginPath();
25173
+      this._renderTextCommon(ctx, 'strokeText');
25174
+      ctx.closePath();
25175
+      ctx.restore();
25176
+    },
25177
+
25178
+    /**
25179
+     * @private
25180
+     * @param {String} method
25181
+     * @param {CanvasRenderingContext2D} ctx Context to render on
25182
+     * @param {String} line Content of the line
25183
+     * @param {Number} left
25184
+     * @param {Number} top
25185
+     * @param {Number} lineIndex
25186
+     * @param {Number} charOffset
25187
+     */
25188
+    _renderChars: function(method, ctx, line, left, top, lineIndex) {
25189
+      // set proper line offset
25190
+      var lineHeight = this.getHeightOfLine(lineIndex),
25191
+          isJustify = this.textAlign.indexOf('justify') !== -1,
25192
+          actualStyle,
25193
+          nextStyle,
25194
+          charsToRender = '',
25195
+          charBox,
25196
+          boxWidth = 0,
25197
+          timeToRender,
25198
+          shortCut = !isJustify && this.charSpacing === 0 && this.isEmptyStyles(lineIndex);
25199
+
25200
+      ctx.save();
25201
+      top -= lineHeight * this._fontSizeFraction / this.lineHeight;
25202
+      if (shortCut) {
25203
+        // render all the line in one pass without checking
25204
+        this._renderChar(method, ctx, lineIndex, 0, this.textLines[lineIndex], left, top, lineHeight);
25205
+        ctx.restore();
25206
+        return;
25207
+      }
25208
+      for (var i = 0, len = line.length - 1; i <= len; i++) {
25209
+        timeToRender = i === len || this.charSpacing;
25210
+        charsToRender += line[i];
25211
+        charBox = this.__charBounds[lineIndex][i];
25212
+        if (boxWidth === 0) {
25213
+          left += charBox.kernedWidth - charBox.width;
25214
+          boxWidth += charBox.width;
25215
+        }
25216
+        else {
25217
+          boxWidth += charBox.kernedWidth;
25218
+        }
25219
+        if (isJustify && !timeToRender) {
25220
+          if (this._reSpaceAndTab.test(line[i])) {
25221
+            timeToRender = true;
25222
+          }
25223
+        }
25224
+        if (!timeToRender) {
25225
+          // if we have charSpacing, we render char by char
25226
+          actualStyle = actualStyle || this.getCompleteStyleDeclaration(lineIndex, i);
25227
+          nextStyle = this.getCompleteStyleDeclaration(lineIndex, i + 1);
25228
+          timeToRender = this._hasStyleChanged(actualStyle, nextStyle);
25229
+        }
25230
+        if (timeToRender) {
25231
+          this._renderChar(method, ctx, lineIndex, i, charsToRender, left, top, lineHeight);
25232
+          charsToRender = '';
25233
+          actualStyle = nextStyle;
25234
+          left += boxWidth;
25235
+          boxWidth = 0;
25236
+        }
25237
+      }
25238
+      ctx.restore();
25239
+    },
25240
+
25241
+    /**
25242
+     * @private
25243
+     * @param {String} method
25244
+     * @param {CanvasRenderingContext2D} ctx Context to render on
25245
+     * @param {Number} lineIndex
25246
+     * @param {Number} charIndex
25247
+     * @param {String} _char
25248
+     * @param {Number} left Left coordinate
25249
+     * @param {Number} top Top coordinate
25250
+     * @param {Number} lineHeight Height of the line
25251
+     */
25252
+    _renderChar: function(method, ctx, lineIndex, charIndex, _char, left, top) {
25253
+      var decl = this._getStyleDeclaration(lineIndex, charIndex),
25254
+          fullDecl = this.getCompleteStyleDeclaration(lineIndex, charIndex),
25255
+          shouldFill = method === 'fillText' && fullDecl.fill,
25256
+          shouldStroke = method === 'strokeText' && fullDecl.stroke && fullDecl.strokeWidth;
25257
+
25258
+      if (!shouldStroke && !shouldFill) {
25259
+        return;
25260
+      }
25261
+      decl && ctx.save();
25262
+
25263
+      this._applyCharStyles(method, ctx, lineIndex, charIndex, fullDecl);
25264
+
25265
+      if (decl && decl.textBackgroundColor) {
25266
+        this._removeShadow(ctx);
25267
+      }
25268
+      if (decl && decl.deltaY) {
25269
+        top += decl.deltaY;
25270
+      }
25271
+
25272
+      shouldFill && ctx.fillText(_char, left, top);
25273
+      shouldStroke && ctx.strokeText(_char, left, top);
25274
+      decl && ctx.restore();
25275
+    },
25276
+
25277
+    /**
25278
+     * Turns the character into a 'superior figure' (i.e. 'superscript')
25279
+     * @param {Number} start selection start
25280
+     * @param {Number} end selection end
25281
+     * @returns {fabric.Text} thisArg
25282
+     * @chainable
25283
+     */
25284
+    setSuperscript: function(start, end) {
25285
+      return this._setScript(start, end, this.superscript);
25286
+    },
25287
+
25288
+    /**
25289
+     * Turns the character into an 'inferior figure' (i.e. 'subscript')
25290
+     * @param {Number} start selection start
25291
+     * @param {Number} end selection end
25292
+     * @returns {fabric.Text} thisArg
25293
+     * @chainable
25294
+     */
25295
+    setSubscript: function(start, end) {
25296
+      return this._setScript(start, end, this.subscript);
25297
+    },
25298
+
25299
+    /**
25300
+     * Applies 'schema' at given position
25301
+     * @private
25302
+     * @param {Number} start selection start
25303
+     * @param {Number} end selection end
25304
+     * @param {Number} schema
25305
+     * @returns {fabric.Text} thisArg
25306
+     * @chainable
25307
+     */
25308
+    _setScript: function(start, end, schema) {
25309
+      var loc = this.get2DCursorLocation(start, true),
25310
+          fontSize = this.getValueOfPropertyAt(loc.lineIndex, loc.charIndex, 'fontSize'),
25311
+          dy = this.getValueOfPropertyAt(loc.lineIndex, loc.charIndex, 'deltaY'),
25312
+          style = { fontSize: fontSize * schema.size, deltaY: dy + fontSize * schema.baseline };
25313
+      this.setSelectionStyles(style, start, end);
25314
+      return this;
25315
+    },
25316
+
25317
+    /**
25318
+     * @private
25319
+     * @param {Object} prevStyle
25320
+     * @param {Object} thisStyle
25321
+     */
25322
+    _hasStyleChanged: function(prevStyle, thisStyle) {
25323
+      return prevStyle.fill !== thisStyle.fill ||
25324
+              prevStyle.stroke !== thisStyle.stroke ||
25325
+              prevStyle.strokeWidth !== thisStyle.strokeWidth ||
25326
+              prevStyle.fontSize !== thisStyle.fontSize ||
25327
+              prevStyle.fontFamily !== thisStyle.fontFamily ||
25328
+              prevStyle.fontWeight !== thisStyle.fontWeight ||
25329
+              prevStyle.fontStyle !== thisStyle.fontStyle ||
25330
+              prevStyle.deltaY !== thisStyle.deltaY;
25331
+    },
25332
+
25333
+    /**
25334
+     * @private
25335
+     * @param {Object} prevStyle
25336
+     * @param {Object} thisStyle
25337
+     */
25338
+    _hasStyleChangedForSvg: function(prevStyle, thisStyle) {
25339
+      return this._hasStyleChanged(prevStyle, thisStyle) ||
25340
+        prevStyle.overline !== thisStyle.overline ||
25341
+        prevStyle.underline !== thisStyle.underline ||
25342
+        prevStyle.linethrough !== thisStyle.linethrough;
25343
+    },
25344
+
25345
+    /**
25346
+     * @private
25347
+     * @param {Number} lineIndex index text line
25348
+     * @return {Number} Line left offset
25349
+     */
25350
+    _getLineLeftOffset: function(lineIndex) {
25351
+      var lineWidth = this.getLineWidth(lineIndex);
25352
+      if (this.textAlign === 'center') {
25353
+        return (this.width - lineWidth) / 2;
25354
+      }
25355
+      if (this.textAlign === 'right') {
25356
+        return this.width - lineWidth;
25357
+      }
25358
+      if (this.textAlign === 'justify-center' && this.isEndOfWrapping(lineIndex)) {
25359
+        return (this.width - lineWidth) / 2;
25360
+      }
25361
+      if (this.textAlign === 'justify-right' && this.isEndOfWrapping(lineIndex)) {
25362
+        return this.width - lineWidth;
25363
+      }
25364
+      return 0;
25365
+    },
25366
+
25367
+    /**
25368
+     * @private
25369
+     */
25370
+    _clearCache: function() {
25371
+      this.__lineWidths = [];
25372
+      this.__lineHeights = [];
25373
+      this.__charBounds = [];
25374
+    },
25375
+
25376
+    /**
25377
+     * @private
25378
+     */
25379
+    _shouldClearDimensionCache: function() {
25380
+      var shouldClear = this._forceClearCache;
25381
+      shouldClear || (shouldClear = this.hasStateChanged('_dimensionAffectingProps'));
25382
+      if (shouldClear) {
25383
+        this.dirty = true;
25384
+        this._forceClearCache = false;
25385
+      }
25386
+      return shouldClear;
25387
+    },
25388
+
25389
+    /**
25390
+     * Measure a single line given its index. Used to calculate the initial
25391
+     * text bounding box. The values are calculated and stored in __lineWidths cache.
25392
+     * @private
25393
+     * @param {Number} lineIndex line number
25394
+     * @return {Number} Line width
25395
+     */
25396
+    getLineWidth: function(lineIndex) {
25397
+      if (this.__lineWidths[lineIndex]) {
25398
+        return this.__lineWidths[lineIndex];
25399
+      }
25400
+
25401
+      var width, line = this._textLines[lineIndex], lineInfo;
25402
+
25403
+      if (line === '') {
25404
+        width = 0;
25405
+      }
25406
+      else {
25407
+        lineInfo = this.measureLine(lineIndex);
25408
+        width = lineInfo.width;
25409
+      }
25410
+      this.__lineWidths[lineIndex] = width;
25411
+      return width;
25412
+    },
25413
+
25414
+    _getWidthOfCharSpacing: function() {
25415
+      if (this.charSpacing !== 0) {
25416
+        return this.fontSize * this.charSpacing / 1000;
25417
+      }
25418
+      return 0;
25419
+    },
25420
+
25421
+    /**
25422
+     * Retrieves the value of property at given character position
25423
+     * @param {Number} lineIndex the line number
25424
+     * @param {Number} charIndex the charater number
25425
+     * @param {String} property the property name
25426
+     * @returns the value of 'property'
25427
+     */
25428
+    getValueOfPropertyAt: function(lineIndex, charIndex, property) {
25429
+      var charStyle = this._getStyleDeclaration(lineIndex, charIndex);
25430
+      if (charStyle && typeof charStyle[property] !== 'undefined') {
25431
+        return charStyle[property];
25432
+      }
25433
+      return this[property];
25434
+    },
25435
+
25436
+    /**
25437
+     * @private
25438
+     * @param {CanvasRenderingContext2D} ctx Context to render on
25439
+     */
25440
+    _renderTextDecoration: function(ctx, type) {
25441
+      if (!this[type] && !this.styleHas(type)) {
25442
+        return;
25443
+      }
25444
+      var heightOfLine, size, _size,
25445
+          lineLeftOffset, dy, _dy,
25446
+          line, lastDecoration,
25447
+          leftOffset = this._getLeftOffset(),
25448
+          topOffset = this._getTopOffset(), top,
25449
+          boxStart, boxWidth, charBox, currentDecoration,
25450
+          maxHeight, currentFill, lastFill,
25451
+          charSpacing = this._getWidthOfCharSpacing();
25452
+
25453
+      for (var i = 0, len = this._textLines.length; i < len; i++) {
25454
+        heightOfLine = this.getHeightOfLine(i);
25455
+        if (!this[type] && !this.styleHas(type, i)) {
25456
+          topOffset += heightOfLine;
25457
+          continue;
25458
+        }
25459
+        line = this._textLines[i];
25460
+        maxHeight = heightOfLine / this.lineHeight;
25461
+        lineLeftOffset = this._getLineLeftOffset(i);
25462
+        boxStart = 0;
25463
+        boxWidth = 0;
25464
+        lastDecoration = this.getValueOfPropertyAt(i, 0, type);
25465
+        lastFill = this.getValueOfPropertyAt(i, 0, 'fill');
25466
+        top = topOffset + maxHeight * (1 - this._fontSizeFraction);
25467
+        size = this.getHeightOfChar(i, 0);
25468
+        dy = this.getValueOfPropertyAt(i, 0, 'deltaY');
25469
+        for (var j = 0, jlen = line.length; j < jlen; j++) {
25470
+          charBox = this.__charBounds[i][j];
25471
+          currentDecoration = this.getValueOfPropertyAt(i, j, type);
25472
+          currentFill = this.getValueOfPropertyAt(i, j, 'fill');
25473
+          _size = this.getHeightOfChar(i, j);
25474
+          _dy = this.getValueOfPropertyAt(i, j, 'deltaY');
25475
+          if ((currentDecoration !== lastDecoration || currentFill !== lastFill || _size !== size || _dy !== dy) &&
25476
+              boxWidth > 0) {
25477
+            ctx.fillStyle = lastFill;
25478
+            lastDecoration && lastFill && ctx.fillRect(
25479
+              leftOffset + lineLeftOffset + boxStart,
25480
+              top + this.offsets[type] * size + dy,
25481
+              boxWidth,
25482
+              this.fontSize / 15
25483
+            );
25484
+            boxStart = charBox.left;
25485
+            boxWidth = charBox.width;
25486
+            lastDecoration = currentDecoration;
25487
+            lastFill = currentFill;
25488
+            size = _size;
25489
+            dy = _dy;
25490
+          }
25491
+          else {
25492
+            boxWidth += charBox.kernedWidth;
25493
+          }
25494
+        }
25495
+        ctx.fillStyle = currentFill;
25496
+        currentDecoration && currentFill && ctx.fillRect(
25497
+          leftOffset + lineLeftOffset + boxStart,
25498
+          top + this.offsets[type] * size + dy,
25499
+          boxWidth - charSpacing,
25500
+          this.fontSize / 15
25501
+        );
25502
+        topOffset += heightOfLine;
25503
+      }
25504
+      // if there is text background color no
25505
+      // other shadows should be casted
25506
+      this._removeShadow(ctx);
25507
+    },
25508
+
25509
+    /**
25510
+     * return font declaration string for canvas context
25511
+     * @param {Object} [styleObject] object
25512
+     * @returns {String} font declaration formatted for canvas context.
25513
+     */
25514
+    _getFontDeclaration: function(styleObject, forMeasuring) {
25515
+      var style = styleObject || this, family = this.fontFamily,
25516
+          fontIsGeneric = fabric.Text.genericFonts.indexOf(family.toLowerCase()) > -1;
25517
+      var fontFamily = family === undefined ||
25518
+      family.indexOf('\'') > -1 ||
25519
+      family.indexOf('"') > -1 || fontIsGeneric
25520
+        ? style.fontFamily : '"' + style.fontFamily + '"';
25521
+      return [
25522
+        // node-canvas needs "weight style", while browsers need "style weight"
25523
+        (fabric.isLikelyNode ? style.fontWeight : style.fontStyle),
25524
+        (fabric.isLikelyNode ? style.fontStyle : style.fontWeight),
25525
+        forMeasuring ? this.CACHE_FONT_SIZE + 'px' : style.fontSize + 'px',
25526
+        fontFamily
25527
+      ].join(' ');
25528
+    },
25529
+
25530
+    /**
25531
+     * Renders text instance on a specified context
25532
+     * @param {CanvasRenderingContext2D} ctx Context to render on
25533
+     */
25534
+    render: function(ctx) {
25535
+      // do not render if object is not visible
25536
+      if (!this.visible) {
25537
+        return;
25538
+      }
25539
+      if (this.canvas && this.canvas.skipOffscreen && !this.group && !this.isOnScreen()) {
25540
+        return;
25541
+      }
25542
+      if (this._shouldClearDimensionCache()) {
25543
+        this.initDimensions();
25544
+      }
25545
+      this.callSuper('render', ctx);
25546
+    },
25547
+
25548
+    /**
25549
+     * Returns the text as an array of lines.
25550
+     * @param {String} text text to split
25551
+     * @returns {Array} Lines in the text
25552
+     */
25553
+    _splitTextIntoLines: function(text) {
25554
+      var lines = text.split(this._reNewline),
25555
+          newLines = new Array(lines.length),
25556
+          newLine = ['\n'],
25557
+          newText = [];
25558
+      for (var i = 0; i < lines.length; i++) {
25559
+        newLines[i] = fabric.util.string.graphemeSplit(lines[i]);
25560
+        newText = newText.concat(newLines[i], newLine);
25561
+      }
25562
+      newText.pop();
25563
+      return { _unwrappedLines: newLines, lines: lines, graphemeText: newText, graphemeLines: newLines };
25564
+    },
25565
+
25566
+    /**
25567
+     * Returns object representation of an instance
25568
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
25569
+     * @return {Object} Object representation of an instance
25570
+     */
25571
+    toObject: function(propertiesToInclude) {
25572
+      var additionalProperties = [
25573
+        'text',
25574
+        'fontSize',
25575
+        'fontWeight',
25576
+        'fontFamily',
25577
+        'fontStyle',
25578
+        'lineHeight',
25579
+        'underline',
25580
+        'overline',
25581
+        'linethrough',
25582
+        'textAlign',
25583
+        'textBackgroundColor',
25584
+        'charSpacing',
25585
+      ].concat(propertiesToInclude);
25586
+      var obj = this.callSuper('toObject', additionalProperties);
25587
+      obj.styles = clone(this.styles, true);
25588
+      return obj;
25589
+    },
25590
+
25591
+    /**
25592
+     * 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()`.
25593
+     * @param {String|Object} key Property name or object (if object, iterate over the object properties)
25594
+     * @param {Object|Function} value Property value (if function, the value is passed into it and its return value is used as a new one)
25595
+     * @return {fabric.Object} thisArg
25596
+     * @chainable
25597
+     */
25598
+    set: function(key, value) {
25599
+      this.callSuper('set', key, value);
25600
+      var needsDims = false;
25601
+      if (typeof key === 'object') {
25602
+        for (var _key in key) {
25603
+          needsDims = needsDims || this._dimensionAffectingProps.indexOf(_key) !== -1;
25604
+        }
25605
+      }
25606
+      else {
25607
+        needsDims = this._dimensionAffectingProps.indexOf(key) !== -1;
25608
+      }
25609
+      if (needsDims) {
25610
+        this.initDimensions();
25611
+        this.setCoords();
25612
+      }
25613
+      return this;
25614
+    },
25615
+
25616
+    /**
25617
+     * Returns complexity of an instance
25618
+     * @return {Number} complexity
25619
+     */
25620
+    complexity: function() {
25621
+      return 1;
25622
+    }
25623
+  });
25624
+
25625
+  /* _FROM_SVG_START_ */
25626
+  /**
25627
+   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Text.fromElement})
25628
+   * @static
25629
+   * @memberOf fabric.Text
25630
+   * @see: http://www.w3.org/TR/SVG/text.html#TextElement
25631
+   */
25632
+  fabric.Text.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(
25633
+    'x y dx dy font-family font-style font-weight font-size letter-spacing text-decoration text-anchor'.split(' '));
25634
+
25635
+  /**
25636
+   * Default SVG font size
25637
+   * @static
25638
+   * @memberOf fabric.Text
25639
+   */
25640
+  fabric.Text.DEFAULT_SVG_FONT_SIZE = 16;
25641
+
25642
+  /**
25643
+   * Returns fabric.Text instance from an SVG element (<b>not yet implemented</b>)
25644
+   * @static
25645
+   * @memberOf fabric.Text
25646
+   * @param {SVGElement} element Element to parse
25647
+   * @param {Function} callback callback function invoked after parsing
25648
+   * @param {Object} [options] Options object
25649
+   */
25650
+  fabric.Text.fromElement = function(element, callback, options) {
25651
+    if (!element) {
25652
+      return callback(null);
25653
+    }
25654
+
25655
+    var parsedAttributes = fabric.parseAttributes(element, fabric.Text.ATTRIBUTE_NAMES),
25656
+        parsedAnchor = parsedAttributes.textAnchor || 'left';
25657
+    options = fabric.util.object.extend((options ? clone(options) : { }), parsedAttributes);
25658
+
25659
+    options.top = options.top || 0;
25660
+    options.left = options.left || 0;
25661
+    if (parsedAttributes.textDecoration) {
25662
+      var textDecoration = parsedAttributes.textDecoration;
25663
+      if (textDecoration.indexOf('underline') !== -1) {
25664
+        options.underline = true;
25665
+      }
25666
+      if (textDecoration.indexOf('overline') !== -1) {
25667
+        options.overline = true;
25668
+      }
25669
+      if (textDecoration.indexOf('line-through') !== -1) {
25670
+        options.linethrough = true;
25671
+      }
25672
+      delete options.textDecoration;
25673
+    }
25674
+    if ('dx' in parsedAttributes) {
25675
+      options.left += parsedAttributes.dx;
25676
+    }
25677
+    if ('dy' in parsedAttributes) {
25678
+      options.top += parsedAttributes.dy;
25679
+    }
25680
+    if (!('fontSize' in options)) {
25681
+      options.fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
25682
+    }
25683
+
25684
+    var textContent = '';
25685
+
25686
+    // The XML is not properly parsed in IE9 so a workaround to get
25687
+    // textContent is through firstChild.data. Another workaround would be
25688
+    // to convert XML loaded from a file to be converted using DOMParser (same way loadSVGFromString() does)
25689
+    if (!('textContent' in element)) {
25690
+      if ('firstChild' in element && element.firstChild !== null) {
25691
+        if ('data' in element.firstChild && element.firstChild.data !== null) {
25692
+          textContent = element.firstChild.data;
25693
+        }
25694
+      }
25695
+    }
25696
+    else {
25697
+      textContent = element.textContent;
25698
+    }
25699
+
25700
+    textContent = textContent.replace(/^\s+|\s+$|\n+/g, '').replace(/\s+/g, ' ');
25701
+    var originalStrokeWidth = options.strokeWidth;
25702
+    options.strokeWidth = 0;
25703
+
25704
+    var text = new fabric.Text(textContent, options),
25705
+        textHeightScaleFactor = text.getScaledHeight() / text.height,
25706
+        lineHeightDiff = (text.height + text.strokeWidth) * text.lineHeight - text.height,
25707
+        scaledDiff = lineHeightDiff * textHeightScaleFactor,
25708
+        textHeight = text.getScaledHeight() + scaledDiff,
25709
+        offX = 0;
25710
+    /*
25711
+      Adjust positioning:
25712
+        x/y attributes in SVG correspond to the bottom-left corner of text bounding box
25713
+        fabric output by default at top, left.
25714
+    */
25715
+    if (parsedAnchor === 'center') {
25716
+      offX = text.getScaledWidth() / 2;
25717
+    }
25718
+    if (parsedAnchor === 'right') {
25719
+      offX = text.getScaledWidth();
25720
+    }
25721
+    text.set({
25722
+      left: text.left - offX,
25723
+      top: text.top - (textHeight - text.fontSize * (0.07 + text._fontSizeFraction)) / text.lineHeight,
25724
+      strokeWidth: typeof originalStrokeWidth !== 'undefined' ? originalStrokeWidth : 1,
25725
+    });
25726
+    callback(text);
25727
+  };
25728
+  /* _FROM_SVG_END_ */
25729
+
25730
+  /**
25731
+   * Returns fabric.Text instance from an object representation
25732
+   * @static
25733
+   * @memberOf fabric.Text
25734
+   * @param {Object} object Object to create an instance from
25735
+   * @param {Function} [callback] Callback to invoke when an fabric.Text instance is created
25736
+   */
25737
+  fabric.Text.fromObject = function(object, callback) {
25738
+    return fabric.Object._fromObject('Text', object, callback, 'text');
25739
+  };
25740
+
25741
+  fabric.Text.genericFonts = ['sans-serif', 'serif', 'cursive', 'fantasy', 'monospace'];
25742
+
25743
+  fabric.util.createAccessors && fabric.util.createAccessors(fabric.Text);
25744
+
25745
+})(typeof exports !== 'undefined' ? exports : this);
25746
+
25747
+
25748
+(function() {
25749
+  fabric.util.object.extend(fabric.Text.prototype, /** @lends fabric.Text.prototype */ {
25750
+    /**
25751
+     * Returns true if object has no styling or no styling in a line
25752
+     * @param {Number} lineIndex , lineIndex is on wrapped lines.
25753
+     * @return {Boolean}
25754
+     */
25755
+    isEmptyStyles: function(lineIndex) {
25756
+      if (!this.styles) {
25757
+        return true;
25758
+      }
25759
+      if (typeof lineIndex !== 'undefined' && !this.styles[lineIndex]) {
25760
+        return true;
25761
+      }
25762
+      var obj = typeof lineIndex === 'undefined' ? this.styles : { line: this.styles[lineIndex] };
25763
+      for (var p1 in obj) {
25764
+        for (var p2 in obj[p1]) {
25765
+          // eslint-disable-next-line no-unused-vars
25766
+          for (var p3 in obj[p1][p2]) {
25767
+            return false;
25768
+          }
25769
+        }
25770
+      }
25771
+      return true;
25772
+    },
25773
+
25774
+    /**
25775
+     * Returns true if object has a style property or has it ina specified line
25776
+     * @param {Number} lineIndex
25777
+     * @return {Boolean}
25778
+     */
25779
+    styleHas: function(property, lineIndex) {
25780
+      if (!this.styles || !property || property === '') {
25781
+        return false;
25782
+      }
25783
+      if (typeof lineIndex !== 'undefined' && !this.styles[lineIndex]) {
25784
+        return false;
25785
+      }
25786
+      var obj = typeof lineIndex === 'undefined' ? this.styles : { line: this.styles[lineIndex] };
25787
+      // eslint-disable-next-line
25788
+      for (var p1 in obj) {
25789
+        // eslint-disable-next-line
25790
+        for (var p2 in obj[p1]) {
25791
+          if (typeof obj[p1][p2][property] !== 'undefined') {
25792
+            return true;
25793
+          }
25794
+        }
25795
+      }
25796
+      return false;
25797
+    },
25798
+
25799
+    /**
25800
+     * Check if characters in a text have a value for a property
25801
+     * whose value matches the textbox's value for that property.  If so,
25802
+     * the character-level property is deleted.  If the character
25803
+     * has no other properties, then it is also deleted.  Finally,
25804
+     * if the line containing that character has no other characters
25805
+     * then it also is deleted.
25806
+     *
25807
+     * @param {string} property The property to compare between characters and text.
25808
+     */
25809
+    cleanStyle: function(property) {
25810
+      if (!this.styles || !property || property === '') {
25811
+        return false;
25812
+      }
25813
+      var obj = this.styles, stylesCount = 0, letterCount, stylePropertyValue,
25814
+          allStyleObjectPropertiesMatch = true, graphemeCount = 0, styleObject;
25815
+      // eslint-disable-next-line
25816
+      for (var p1 in obj) {
25817
+        letterCount = 0;
25818
+        // eslint-disable-next-line
25819
+        for (var p2 in obj[p1]) {
25820
+          var styleObject = obj[p1][p2],
25821
+              stylePropertyHasBeenSet = styleObject.hasOwnProperty(property);
25822
+
25823
+          stylesCount++;
25824
+
25825
+          if (stylePropertyHasBeenSet) {
25826
+            if (!stylePropertyValue) {
25827
+              stylePropertyValue = styleObject[property];
25828
+            }
25829
+            else if (styleObject[property] !== stylePropertyValue) {
25830
+              allStyleObjectPropertiesMatch = false;
25831
+            }
25832
+
25833
+            if (styleObject[property] === this[property]) {
25834
+              delete styleObject[property];
25835
+            }
25836
+          }
25837
+          else {
25838
+            allStyleObjectPropertiesMatch = false;
25839
+          }
25840
+
25841
+          if (Object.keys(styleObject).length !== 0) {
25842
+            letterCount++;
25843
+          }
25844
+          else {
25845
+            delete obj[p1][p2];
25846
+          }
25847
+        }
25848
+
25849
+        if (letterCount === 0) {
25850
+          delete obj[p1];
25851
+        }
25852
+      }
25853
+      // if every grapheme has the same style set then
25854
+      // delete those styles and set it on the parent
25855
+      for (var i = 0; i < this._textLines.length; i++) {
25856
+        graphemeCount += this._textLines[i].length;
25857
+      }
25858
+      if (allStyleObjectPropertiesMatch && stylesCount === graphemeCount) {
25859
+        this[property] = stylePropertyValue;
25860
+        this.removeStyle(property);
25861
+      }
25862
+    },
25863
+
25864
+    /**
25865
+     * Remove a style property or properties from all individual character styles
25866
+     * in a text object.  Deletes the character style object if it contains no other style
25867
+     * props.  Deletes a line style object if it contains no other character styles.
25868
+     *
25869
+     * @param {String} props The property to remove from character styles.
25870
+     */
25871
+    removeStyle: function(property) {
25872
+      if (!this.styles || !property || property === '') {
25873
+        return;
25874
+      }
25875
+      var obj = this.styles, line, lineNum, charNum;
25876
+      for (lineNum in obj) {
25877
+        line = obj[lineNum];
25878
+        for (charNum in line) {
25879
+          delete line[charNum][property];
25880
+          if (Object.keys(line[charNum]).length === 0) {
25881
+            delete line[charNum];
25882
+          }
25883
+        }
25884
+        if (Object.keys(line).length === 0) {
25885
+          delete obj[lineNum];
25886
+        }
25887
+      }
25888
+    },
25889
+
25890
+    /**
25891
+     * @private
25892
+     */
25893
+    _extendStyles: function(index, styles) {
25894
+      var loc = this.get2DCursorLocation(index);
25895
+
25896
+      if (!this._getLineStyle(loc.lineIndex)) {
25897
+        this._setLineStyle(loc.lineIndex, {});
25898
+      }
25899
+
25900
+      if (!this._getStyleDeclaration(loc.lineIndex, loc.charIndex)) {
25901
+        this._setStyleDeclaration(loc.lineIndex, loc.charIndex, {});
25902
+      }
25903
+
25904
+      fabric.util.object.extend(this._getStyleDeclaration(loc.lineIndex, loc.charIndex), styles);
25905
+    },
25906
+
25907
+    /**
25908
+     * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start)
25909
+     * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used.
25910
+     * @param {Boolean} [skipWrapping] consider the location for unwrapped lines. usefull to manage styles.
25911
+     */
25912
+    get2DCursorLocation: function(selectionStart, skipWrapping) {
25913
+      if (typeof selectionStart === 'undefined') {
25914
+        selectionStart = this.selectionStart;
25915
+      }
25916
+      var lines = skipWrapping ? this._unwrappedTextLines : this._textLines;
25917
+      var len = lines.length;
25918
+      for (var i = 0; i < len; i++) {
25919
+        if (selectionStart <= lines[i].length) {
25920
+          return {
25921
+            lineIndex: i,
25922
+            charIndex: selectionStart
25923
+          };
25924
+        }
25925
+        selectionStart -= lines[i].length + 1;
25926
+      }
25927
+      return {
25928
+        lineIndex: i - 1,
25929
+        charIndex: lines[i - 1].length < selectionStart ? lines[i - 1].length : selectionStart
25930
+      };
25931
+    },
25932
+
25933
+    /**
25934
+     * Gets style of a current selection/cursor (at the start position)
25935
+     * if startIndex or endIndex are not provided, slectionStart or selectionEnd will be used.
25936
+     * @param {Number} [startIndex] Start index to get styles at
25937
+     * @param {Number} [endIndex] End index to get styles at, if not specified selectionEnd or startIndex + 1
25938
+     * @param {Boolean} [complete] get full style or not
25939
+     * @return {Array} styles an array with one, zero or more Style objects
25940
+     */
25941
+    getSelectionStyles: function(startIndex, endIndex, complete) {
25942
+      if (typeof startIndex === 'undefined') {
25943
+        startIndex = this.selectionStart || 0;
25944
+      }
25945
+      if (typeof endIndex === 'undefined') {
25946
+        endIndex = this.selectionEnd || startIndex;
25947
+      }
25948
+      var styles = [];
25949
+      for (var i = startIndex; i < endIndex; i++) {
25950
+        styles.push(this.getStyleAtPosition(i, complete));
25951
+      }
25952
+      return styles;
25953
+    },
25954
+
25955
+    /**
25956
+     * Gets style of a current selection/cursor position
25957
+     * @param {Number} position  to get styles at
25958
+     * @param {Boolean} [complete] full style if true
25959
+     * @return {Object} style Style object at a specified index
25960
+     * @private
25961
+     */
25962
+    getStyleAtPosition: function(position, complete) {
25963
+      var loc = this.get2DCursorLocation(position),
25964
+          style = complete ? this.getCompleteStyleDeclaration(loc.lineIndex, loc.charIndex) :
25965
+            this._getStyleDeclaration(loc.lineIndex, loc.charIndex);
25966
+      return style || {};
25967
+    },
25968
+
25969
+    /**
25970
+     * Sets style of a current selection, if no selection exist, do not set anything.
25971
+     * @param {Object} [styles] Styles object
25972
+     * @param {Number} [startIndex] Start index to get styles at
25973
+     * @param {Number} [endIndex] End index to get styles at, if not specified selectionEnd or startIndex + 1
25974
+     * @return {fabric.IText} thisArg
25975
+     * @chainable
25976
+     */
25977
+    setSelectionStyles: function(styles, startIndex, endIndex) {
25978
+      if (typeof startIndex === 'undefined') {
25979
+        startIndex = this.selectionStart || 0;
25980
+      }
25981
+      if (typeof endIndex === 'undefined') {
25982
+        endIndex = this.selectionEnd || startIndex;
25983
+      }
25984
+      for (var i = startIndex; i < endIndex; i++) {
25985
+        this._extendStyles(i, styles);
25986
+      }
25987
+      /* not included in _extendStyles to avoid clearing cache more than once */
25988
+      this._forceClearCache = true;
25989
+      return this;
25990
+    },
25991
+
25992
+    /**
25993
+     * get the reference, not a clone, of the style object for a given character
25994
+     * @param {Number} lineIndex
25995
+     * @param {Number} charIndex
25996
+     * @return {Object} style object
25997
+     */
25998
+    _getStyleDeclaration: function(lineIndex, charIndex) {
25999
+      var lineStyle = this.styles && this.styles[lineIndex];
26000
+      if (!lineStyle) {
26001
+        return null;
26002
+      }
26003
+      return lineStyle[charIndex];
26004
+    },
26005
+
26006
+    /**
26007
+     * return a new object that contains all the style property for a character
26008
+     * the object returned is newly created
26009
+     * @param {Number} lineIndex of the line where the character is
26010
+     * @param {Number} charIndex position of the character on the line
26011
+     * @return {Object} style object
26012
+     */
26013
+    getCompleteStyleDeclaration: function(lineIndex, charIndex) {
26014
+      var style = this._getStyleDeclaration(lineIndex, charIndex) || { },
26015
+          styleObject = { }, prop;
26016
+      for (var i = 0; i < this._styleProperties.length; i++) {
26017
+        prop = this._styleProperties[i];
26018
+        styleObject[prop] = typeof style[prop] === 'undefined' ? this[prop] : style[prop];
26019
+      }
26020
+      return styleObject;
26021
+    },
26022
+
26023
+    /**
26024
+     * @param {Number} lineIndex
26025
+     * @param {Number} charIndex
26026
+     * @param {Object} style
26027
+     * @private
26028
+     */
26029
+    _setStyleDeclaration: function(lineIndex, charIndex, style) {
26030
+      this.styles[lineIndex][charIndex] = style;
26031
+    },
26032
+
26033
+    /**
26034
+     *
26035
+     * @param {Number} lineIndex
26036
+     * @param {Number} charIndex
26037
+     * @private
26038
+     */
26039
+    _deleteStyleDeclaration: function(lineIndex, charIndex) {
26040
+      delete this.styles[lineIndex][charIndex];
26041
+    },
26042
+
26043
+    /**
26044
+     * @param {Number} lineIndex
26045
+     * @private
26046
+     */
26047
+    _getLineStyle: function(lineIndex) {
26048
+      return this.styles[lineIndex];
26049
+    },
26050
+
26051
+    /**
26052
+     * @param {Number} lineIndex
26053
+     * @param {Object} style
26054
+     * @private
26055
+     */
26056
+    _setLineStyle: function(lineIndex, style) {
26057
+      this.styles[lineIndex] = style;
26058
+    },
26059
+
26060
+    /**
26061
+     * @param {Number} lineIndex
26062
+     * @private
26063
+     */
26064
+    _deleteLineStyle: function(lineIndex) {
26065
+      delete this.styles[lineIndex];
26066
+    }
26067
+  });
26068
+})();
26069
+
26070
+
26071
+(function() {
26072
+
26073
+  function parseDecoration(object) {
26074
+    if (object.textDecoration) {
26075
+      object.textDecoration.indexOf('underline') > -1 && (object.underline = true);
26076
+      object.textDecoration.indexOf('line-through') > -1 && (object.linethrough = true);
26077
+      object.textDecoration.indexOf('overline') > -1 && (object.overline = true);
26078
+      delete object.textDecoration;
26079
+    }
26080
+  }
26081
+
26082
+  /**
26083
+   * IText class (introduced in <b>v1.4</b>) Events are also fired with "text:"
26084
+   * prefix when observing canvas.
26085
+   * @class fabric.IText
26086
+   * @extends fabric.Text
26087
+   * @mixes fabric.Observable
26088
+   *
26089
+   * @fires changed
26090
+   * @fires selection:changed
26091
+   * @fires editing:entered
26092
+   * @fires editing:exited
26093
+   *
26094
+   * @return {fabric.IText} thisArg
26095
+   * @see {@link fabric.IText#initialize} for constructor definition
26096
+   *
26097
+   * <p>Supported key combinations:</p>
26098
+   * <pre>
26099
+   *   Move cursor:                    left, right, up, down
26100
+   *   Select character:               shift + left, shift + right
26101
+   *   Select text vertically:         shift + up, shift + down
26102
+   *   Move cursor by word:            alt + left, alt + right
26103
+   *   Select words:                   shift + alt + left, shift + alt + right
26104
+   *   Move cursor to line start/end:  cmd + left, cmd + right or home, end
26105
+   *   Select till start/end of line:  cmd + shift + left, cmd + shift + right or shift + home, shift + end
26106
+   *   Jump to start/end of text:      cmd + up, cmd + down
26107
+   *   Select till start/end of text:  cmd + shift + up, cmd + shift + down or shift + pgUp, shift + pgDown
26108
+   *   Delete character:               backspace
26109
+   *   Delete word:                    alt + backspace
26110
+   *   Delete line:                    cmd + backspace
26111
+   *   Forward delete:                 delete
26112
+   *   Copy text:                      ctrl/cmd + c
26113
+   *   Paste text:                     ctrl/cmd + v
26114
+   *   Cut text:                       ctrl/cmd + x
26115
+   *   Select entire text:             ctrl/cmd + a
26116
+   *   Quit editing                    tab or esc
26117
+   * </pre>
26118
+   *
26119
+   * <p>Supported mouse/touch combination</p>
26120
+   * <pre>
26121
+   *   Position cursor:                click/touch
26122
+   *   Create selection:               click/touch & drag
26123
+   *   Create selection:               click & shift + click
26124
+   *   Select word:                    double click
26125
+   *   Select line:                    triple click
26126
+   * </pre>
26127
+   */
26128
+  fabric.IText = fabric.util.createClass(fabric.Text, fabric.Observable, /** @lends fabric.IText.prototype */ {
26129
+
26130
+    /**
26131
+     * Type of an object
26132
+     * @type String
26133
+     * @default
26134
+     */
26135
+    type: 'i-text',
26136
+
26137
+    /**
26138
+     * Index where text selection starts (or where cursor is when there is no selection)
26139
+     * @type Number
26140
+     * @default
26141
+     */
26142
+    selectionStart: 0,
26143
+
26144
+    /**
26145
+     * Index where text selection ends
26146
+     * @type Number
26147
+     * @default
26148
+     */
26149
+    selectionEnd: 0,
26150
+
26151
+    /**
26152
+     * Color of text selection
26153
+     * @type String
26154
+     * @default
26155
+     */
26156
+    selectionColor: 'rgba(17,119,255,0.3)',
26157
+
26158
+    /**
26159
+     * Indicates whether text is in editing mode
26160
+     * @type Boolean
26161
+     * @default
26162
+     */
26163
+    isEditing: false,
26164
+
26165
+    /**
26166
+     * Indicates whether a text can be edited
26167
+     * @type Boolean
26168
+     * @default
26169
+     */
26170
+    editable: true,
26171
+
26172
+    /**
26173
+     * Border color of text object while it's in editing mode
26174
+     * @type String
26175
+     * @default
26176
+     */
26177
+    editingBorderColor: 'rgba(102,153,255,0.25)',
26178
+
26179
+    /**
26180
+     * Width of cursor (in px)
26181
+     * @type Number
26182
+     * @default
26183
+     */
26184
+    cursorWidth: 2,
26185
+
26186
+    /**
26187
+     * Color of default cursor (when not overwritten by character style)
26188
+     * @type String
26189
+     * @default
26190
+     */
26191
+    cursorColor: '#333',
26192
+
26193
+    /**
26194
+     * Delay between cursor blink (in ms)
26195
+     * @type Number
26196
+     * @default
26197
+     */
26198
+    cursorDelay: 1000,
26199
+
26200
+    /**
26201
+     * Duration of cursor fadein (in ms)
26202
+     * @type Number
26203
+     * @default
26204
+     */
26205
+    cursorDuration: 600,
26206
+
26207
+    /**
26208
+     * Indicates whether internal text char widths can be cached
26209
+     * @type Boolean
26210
+     * @default
26211
+     */
26212
+    caching: true,
26213
+
26214
+    /**
26215
+     * @private
26216
+     */
26217
+    _reSpace: /\s|\n/,
26218
+
26219
+    /**
26220
+     * @private
26221
+     */
26222
+    _currentCursorOpacity: 0,
26223
+
26224
+    /**
26225
+     * @private
26226
+     */
26227
+    _selectionDirection: null,
26228
+
26229
+    /**
26230
+     * @private
26231
+     */
26232
+    _abortCursorAnimation: false,
26233
+
26234
+    /**
26235
+     * @private
26236
+     */
26237
+    __widthOfSpace: [],
26238
+
26239
+    /**
26240
+     * Helps determining when the text is in composition, so that the cursor
26241
+     * rendering is altered.
26242
+     */
26243
+    inCompositionMode: false,
26244
+
26245
+    /**
26246
+     * Constructor
26247
+     * @param {String} text Text string
26248
+     * @param {Object} [options] Options object
26249
+     * @return {fabric.IText} thisArg
26250
+     */
26251
+    initialize: function(text, options) {
26252
+      this.callSuper('initialize', text, options);
26253
+      this.initBehavior();
26254
+    },
26255
+
26256
+    /**
26257
+     * Sets selection start (left boundary of a selection)
26258
+     * @param {Number} index Index to set selection start to
26259
+     */
26260
+    setSelectionStart: function(index) {
26261
+      index = Math.max(index, 0);
26262
+      this._updateAndFire('selectionStart', index);
26263
+    },
26264
+
26265
+    /**
26266
+     * Sets selection end (right boundary of a selection)
26267
+     * @param {Number} index Index to set selection end to
26268
+     */
26269
+    setSelectionEnd: function(index) {
26270
+      index = Math.min(index, this.text.length);
26271
+      this._updateAndFire('selectionEnd', index);
26272
+    },
26273
+
26274
+    /**
26275
+     * @private
26276
+     * @param {String} property 'selectionStart' or 'selectionEnd'
26277
+     * @param {Number} index new position of property
26278
+     */
26279
+    _updateAndFire: function(property, index) {
26280
+      if (this[property] !== index) {
26281
+        this._fireSelectionChanged();
26282
+        this[property] = index;
26283
+      }
26284
+      this._updateTextarea();
26285
+    },
26286
+
26287
+    /**
26288
+     * Fires the even of selection changed
26289
+     * @private
26290
+     */
26291
+    _fireSelectionChanged: function() {
26292
+      this.fire('selection:changed');
26293
+      this.canvas && this.canvas.fire('text:selection:changed', { target: this });
26294
+    },
26295
+
26296
+    /**
26297
+     * Initialize text dimensions. Render all text on given context
26298
+     * or on a offscreen canvas to get the text width with measureText.
26299
+     * Updates this.width and this.height with the proper values.
26300
+     * Does not return dimensions.
26301
+     * @private
26302
+     */
26303
+    initDimensions: function() {
26304
+      this.isEditing && this.initDelayedCursor();
26305
+      this.clearContextTop();
26306
+      this.callSuper('initDimensions');
26307
+    },
26308
+
26309
+    /**
26310
+     * @private
26311
+     * @param {CanvasRenderingContext2D} ctx Context to render on
26312
+     */
26313
+    render: function(ctx) {
26314
+      this.clearContextTop();
26315
+      this.callSuper('render', ctx);
26316
+      // clear the cursorOffsetCache, so we ensure to calculate once per renderCursor
26317
+      // the correct position but not at every cursor animation.
26318
+      this.cursorOffsetCache = { };
26319
+      this.renderCursorOrSelection();
26320
+    },
26321
+
26322
+    /**
26323
+     * @private
26324
+     * @param {CanvasRenderingContext2D} ctx Context to render on
26325
+     */
26326
+    _render: function(ctx) {
26327
+      this.callSuper('_render', ctx);
26328
+    },
26329
+
26330
+    /**
26331
+     * Prepare and clean the contextTop
26332
+     */
26333
+    clearContextTop: function(skipRestore) {
26334
+      if (!this.isEditing) {
26335
+        return;
26336
+      }
26337
+      if (this.canvas && this.canvas.contextTop) {
26338
+        var ctx = this.canvas.contextTop, v = this.canvas.viewportTransform;
26339
+        ctx.save();
26340
+        ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
26341
+        this.transform(ctx);
26342
+        this.transformMatrix && ctx.transform.apply(ctx, this.transformMatrix);
26343
+        this._clearTextArea(ctx);
26344
+        skipRestore || ctx.restore();
26345
+      }
26346
+    },
26347
+
26348
+    /**
26349
+     * Renders cursor or selection (depending on what exists)
26350
+     */
26351
+    renderCursorOrSelection: function() {
26352
+      if (!this.isEditing || !this.canvas) {
26353
+        return;
26354
+      }
26355
+      var boundaries = this._getCursorBoundaries(), ctx;
26356
+      if (this.canvas && this.canvas.contextTop) {
26357
+        ctx = this.canvas.contextTop;
26358
+        this.clearContextTop(true);
26359
+      }
26360
+      else {
26361
+        ctx = this.canvas.contextContainer;
26362
+        ctx.save();
26363
+      }
26364
+      if (this.selectionStart === this.selectionEnd) {
26365
+        this.renderCursor(boundaries, ctx);
26366
+      }
26367
+      else {
26368
+        this.renderSelection(boundaries, ctx);
26369
+      }
26370
+      ctx.restore();
26371
+    },
26372
+
26373
+    _clearTextArea: function(ctx) {
26374
+      // we add 4 pixel, to be sure to do not leave any pixel out
26375
+      var width = this.width + 4, height = this.height + 4;
26376
+      ctx.clearRect(-width / 2, -height / 2, width, height);
26377
+    },
26378
+
26379
+    /**
26380
+     * Returns cursor boundaries (left, top, leftOffset, topOffset)
26381
+     * @private
26382
+     * @param {Array} chars Array of characters
26383
+     * @param {String} typeOfBoundaries
26384
+     */
26385
+    _getCursorBoundaries: function(position) {
26386
+
26387
+      // left/top are left/top of entire text box
26388
+      // leftOffset/topOffset are offset from that left/top point of a text box
26389
+
26390
+      if (typeof position === 'undefined') {
26391
+        position = this.selectionStart;
26392
+      }
26393
+
26394
+      var left = this._getLeftOffset(),
26395
+          top = this._getTopOffset(),
26396
+          offsets = this._getCursorBoundariesOffsets(position);
26397
+
26398
+      return {
26399
+        left: left,
26400
+        top: top,
26401
+        leftOffset: offsets.left,
26402
+        topOffset: offsets.top
26403
+      };
26404
+    },
26405
+
26406
+    /**
26407
+     * @private
26408
+     */
26409
+    _getCursorBoundariesOffsets: function(position) {
26410
+      if (this.cursorOffsetCache && 'top' in this.cursorOffsetCache) {
26411
+        return this.cursorOffsetCache;
26412
+      }
26413
+      var lineLeftOffset,
26414
+          lineIndex,
26415
+          charIndex,
26416
+          topOffset = 0,
26417
+          leftOffset = 0,
26418
+          boundaries,
26419
+          cursorPosition = this.get2DCursorLocation(position);
26420
+      charIndex = cursorPosition.charIndex;
26421
+      lineIndex = cursorPosition.lineIndex;
26422
+      for (var i = 0; i < lineIndex; i++) {
26423
+        topOffset += this.getHeightOfLine(i);
26424
+      }
26425
+      lineLeftOffset = this._getLineLeftOffset(lineIndex);
26426
+      var bound = this.__charBounds[lineIndex][charIndex];
26427
+      bound && (leftOffset = bound.left);
26428
+      if (this.charSpacing !== 0 && charIndex === this._textLines[lineIndex].length) {
26429
+        leftOffset -= this._getWidthOfCharSpacing();
26430
+      }
26431
+      boundaries = {
26432
+        top: topOffset,
26433
+        left: lineLeftOffset + (leftOffset > 0 ? leftOffset : 0),
26434
+      };
26435
+      this.cursorOffsetCache = boundaries;
26436
+      return this.cursorOffsetCache;
26437
+    },
26438
+
26439
+    /**
26440
+     * Renders cursor
26441
+     * @param {Object} boundaries
26442
+     * @param {CanvasRenderingContext2D} ctx transformed context to draw on
26443
+     */
26444
+    renderCursor: function(boundaries, ctx) {
26445
+      var cursorLocation = this.get2DCursorLocation(),
26446
+          lineIndex = cursorLocation.lineIndex,
26447
+          charIndex = cursorLocation.charIndex > 0 ? cursorLocation.charIndex - 1 : 0,
26448
+          charHeight = this.getValueOfPropertyAt(lineIndex, charIndex, 'fontSize'),
26449
+          multiplier = this.scaleX * this.canvas.getZoom(),
26450
+          cursorWidth = this.cursorWidth / multiplier,
26451
+          topOffset = boundaries.topOffset,
26452
+          dy = this.getValueOfPropertyAt(lineIndex, charIndex, 'deltaY');
26453
+
26454
+      topOffset += (1 - this._fontSizeFraction) * this.getHeightOfLine(lineIndex) / this.lineHeight
26455
+        - charHeight * (1 - this._fontSizeFraction);
26456
+
26457
+      if (this.inCompositionMode) {
26458
+        this.renderSelection(boundaries, ctx);
26459
+      }
26460
+
26461
+      ctx.fillStyle = this.getValueOfPropertyAt(lineIndex, charIndex, 'fill');
26462
+      ctx.globalAlpha = this.__isMousedown ? 1 : this._currentCursorOpacity;
26463
+      ctx.fillRect(
26464
+        boundaries.left + boundaries.leftOffset - cursorWidth / 2,
26465
+        topOffset + boundaries.top + dy,
26466
+        cursorWidth,
26467
+        charHeight);
26468
+    },
26469
+
26470
+    /**
26471
+     * Renders text selection
26472
+     * @param {Object} boundaries Object with left/top/leftOffset/topOffset
26473
+     * @param {CanvasRenderingContext2D} ctx transformed context to draw on
26474
+     */
26475
+    renderSelection: function(boundaries, ctx) {
26476
+
26477
+      var selectionStart = this.inCompositionMode ? this.hiddenTextarea.selectionStart : this.selectionStart,
26478
+          selectionEnd = this.inCompositionMode ? this.hiddenTextarea.selectionEnd : this.selectionEnd,
26479
+          isJustify = this.textAlign.indexOf('justify') !== -1,
26480
+          start = this.get2DCursorLocation(selectionStart),
26481
+          end = this.get2DCursorLocation(selectionEnd),
26482
+          startLine = start.lineIndex,
26483
+          endLine = end.lineIndex,
26484
+          startChar = start.charIndex < 0 ? 0 : start.charIndex,
26485
+          endChar = end.charIndex < 0 ? 0 : end.charIndex;
26486
+
26487
+      for (var i = startLine; i <= endLine; i++) {
26488
+        var lineOffset = this._getLineLeftOffset(i) || 0,
26489
+            lineHeight = this.getHeightOfLine(i),
26490
+            realLineHeight = 0, boxStart = 0, boxEnd = 0;
26491
+
26492
+        if (i === startLine) {
26493
+          boxStart = this.__charBounds[startLine][startChar].left;
26494
+        }
26495
+        if (i >= startLine && i < endLine) {
26496
+          boxEnd = isJustify && !this.isEndOfWrapping(i) ? this.width : this.getLineWidth(i) || 5; // WTF is this 5?
26497
+        }
26498
+        else if (i === endLine) {
26499
+          if (endChar === 0) {
26500
+            boxEnd = this.__charBounds[endLine][endChar].left;
26501
+          }
26502
+          else {
26503
+            var charSpacing = this._getWidthOfCharSpacing();
26504
+            boxEnd = this.__charBounds[endLine][endChar - 1].left
26505
+              + this.__charBounds[endLine][endChar - 1].width - charSpacing;
26506
+          }
26507
+        }
26508
+        realLineHeight = lineHeight;
26509
+        if (this.lineHeight < 1 || (i === endLine && this.lineHeight > 1)) {
26510
+          lineHeight /= this.lineHeight;
26511
+        }
26512
+        if (this.inCompositionMode) {
26513
+          ctx.fillStyle = this.compositionColor || 'black';
26514
+          ctx.fillRect(
26515
+            boundaries.left + lineOffset + boxStart,
26516
+            boundaries.top + boundaries.topOffset + lineHeight,
26517
+            boxEnd - boxStart,
26518
+            1);
26519
+        }
26520
+        else {
26521
+          ctx.fillStyle = this.selectionColor;
26522
+          ctx.fillRect(
26523
+            boundaries.left + lineOffset + boxStart,
26524
+            boundaries.top + boundaries.topOffset,
26525
+            boxEnd - boxStart,
26526
+            lineHeight);
26527
+        }
26528
+
26529
+
26530
+        boundaries.topOffset += realLineHeight;
26531
+      }
26532
+    },
26533
+
26534
+    /**
26535
+     * High level function to know the height of the cursor.
26536
+     * the currentChar is the one that precedes the cursor
26537
+     * Returns fontSize of char at the current cursor
26538
+     * @return {Number} Character font size
26539
+     */
26540
+    getCurrentCharFontSize: function() {
26541
+      var cp = this._getCurrentCharIndex();
26542
+      return this.getValueOfPropertyAt(cp.l, cp.c, 'fontSize');
26543
+    },
26544
+
26545
+    /**
26546
+     * High level function to know the color of the cursor.
26547
+     * the currentChar is the one that precedes the cursor
26548
+     * Returns color (fill) of char at the current cursor
26549
+     * @return {String} Character color (fill)
26550
+     */
26551
+    getCurrentCharColor: function() {
26552
+      var cp = this._getCurrentCharIndex();
26553
+      return this.getValueOfPropertyAt(cp.l, cp.c, 'fill');
26554
+    },
26555
+
26556
+    /**
26557
+     * Returns the cursor position for the getCurrent.. functions
26558
+     * @private
26559
+     */
26560
+    _getCurrentCharIndex: function() {
26561
+      var cursorPosition = this.get2DCursorLocation(this.selectionStart, true),
26562
+          charIndex = cursorPosition.charIndex > 0 ? cursorPosition.charIndex - 1 : 0;
26563
+      return { l: cursorPosition.lineIndex, c: charIndex };
26564
+    }
26565
+  });
26566
+
26567
+  /**
26568
+   * Returns fabric.IText instance from an object representation
26569
+   * @static
26570
+   * @memberOf fabric.IText
26571
+   * @param {Object} object Object to create an instance from
26572
+   * @param {function} [callback] invoked with new instance as argument
26573
+   */
26574
+  fabric.IText.fromObject = function(object, callback) {
26575
+    parseDecoration(object);
26576
+    if (object.styles) {
26577
+      for (var i in object.styles) {
26578
+        for (var j in object.styles[i]) {
26579
+          parseDecoration(object.styles[i][j]);
26580
+        }
26581
+      }
26582
+    }
26583
+    fabric.Object._fromObject('IText', object, callback, 'text');
26584
+  };
26585
+})();
26586
+
26587
+
26588
+(function() {
26589
+
26590
+  var clone = fabric.util.object.clone;
26591
+
26592
+  fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ {
26593
+
26594
+    /**
26595
+     * Initializes all the interactive behavior of IText
26596
+     */
26597
+    initBehavior: function() {
26598
+      this.initAddedHandler();
26599
+      this.initRemovedHandler();
26600
+      this.initCursorSelectionHandlers();
26601
+      this.initDoubleClickSimulation();
26602
+      this.mouseMoveHandler = this.mouseMoveHandler.bind(this);
26603
+    },
26604
+
26605
+    onDeselect: function() {
26606
+      this.isEditing && this.exitEditing();
26607
+      this.selected = false;
26608
+    },
26609
+
26610
+    /**
26611
+     * Initializes "added" event handler
26612
+     */
26613
+    initAddedHandler: function() {
26614
+      var _this = this;
26615
+      this.on('added', function() {
26616
+        var canvas = _this.canvas;
26617
+        if (canvas) {
26618
+          if (!canvas._hasITextHandlers) {
26619
+            canvas._hasITextHandlers = true;
26620
+            _this._initCanvasHandlers(canvas);
26621
+          }
26622
+          canvas._iTextInstances = canvas._iTextInstances || [];
26623
+          canvas._iTextInstances.push(_this);
26624
+        }
26625
+      });
26626
+    },
26627
+
26628
+    initRemovedHandler: function() {
26629
+      var _this = this;
26630
+      this.on('removed', function() {
26631
+        var canvas = _this.canvas;
26632
+        if (canvas) {
26633
+          canvas._iTextInstances = canvas._iTextInstances || [];
26634
+          fabric.util.removeFromArray(canvas._iTextInstances, _this);
26635
+          if (canvas._iTextInstances.length === 0) {
26636
+            canvas._hasITextHandlers = false;
26637
+            _this._removeCanvasHandlers(canvas);
26638
+          }
26639
+        }
26640
+      });
26641
+    },
26642
+
26643
+    /**
26644
+     * register canvas event to manage exiting on other instances
26645
+     * @private
26646
+     */
26647
+    _initCanvasHandlers: function(canvas) {
26648
+      canvas._mouseUpITextHandler = function() {
26649
+        if (canvas._iTextInstances) {
26650
+          canvas._iTextInstances.forEach(function(obj) {
26651
+            obj.__isMousedown = false;
26652
+          });
26653
+        }
26654
+      };
26655
+      canvas.on('mouse:up', canvas._mouseUpITextHandler);
26656
+    },
26657
+
26658
+    /**
26659
+     * remove canvas event to manage exiting on other instances
26660
+     * @private
26661
+     */
26662
+    _removeCanvasHandlers: function(canvas) {
26663
+      canvas.off('mouse:up', canvas._mouseUpITextHandler);
26664
+    },
26665
+
26666
+    /**
26667
+     * @private
26668
+     */
26669
+    _tick: function() {
26670
+      this._currentTickState = this._animateCursor(this, 1, this.cursorDuration, '_onTickComplete');
26671
+    },
26672
+
26673
+    /**
26674
+     * @private
26675
+     */
26676
+    _animateCursor: function(obj, targetOpacity, duration, completeMethod) {
26677
+
26678
+      var tickState;
26679
+
26680
+      tickState = {
26681
+        isAborted: false,
26682
+        abort: function() {
26683
+          this.isAborted = true;
26684
+        },
26685
+      };
26686
+
26687
+      obj.animate('_currentCursorOpacity', targetOpacity, {
26688
+        duration: duration,
26689
+        onComplete: function() {
26690
+          if (!tickState.isAborted) {
26691
+            obj[completeMethod]();
26692
+          }
26693
+        },
26694
+        onChange: function() {
26695
+          // we do not want to animate a selection, only cursor
26696
+          if (obj.canvas && obj.selectionStart === obj.selectionEnd) {
26697
+            obj.renderCursorOrSelection();
26698
+          }
26699
+        },
26700
+        abort: function() {
26701
+          return tickState.isAborted;
26702
+        }
26703
+      });
26704
+      return tickState;
26705
+    },
26706
+
26707
+    /**
26708
+     * @private
26709
+     */
26710
+    _onTickComplete: function() {
26711
+
26712
+      var _this = this;
26713
+
26714
+      if (this._cursorTimeout1) {
26715
+        clearTimeout(this._cursorTimeout1);
26716
+      }
26717
+      this._cursorTimeout1 = setTimeout(function() {
26718
+        _this._currentTickCompleteState = _this._animateCursor(_this, 0, this.cursorDuration / 2, '_tick');
26719
+      }, 100);
26720
+    },
26721
+
26722
+    /**
26723
+     * Initializes delayed cursor
26724
+     */
26725
+    initDelayedCursor: function(restart) {
26726
+      var _this = this,
26727
+          delay = restart ? 0 : this.cursorDelay;
26728
+
26729
+      this.abortCursorAnimation();
26730
+      this._currentCursorOpacity = 1;
26731
+      this._cursorTimeout2 = setTimeout(function() {
26732
+        _this._tick();
26733
+      }, delay);
26734
+    },
26735
+
26736
+    /**
26737
+     * Aborts cursor animation and clears all timeouts
26738
+     */
26739
+    abortCursorAnimation: function() {
26740
+      var shouldClear = this._currentTickState || this._currentTickCompleteState,
26741
+          canvas = this.canvas;
26742
+      this._currentTickState && this._currentTickState.abort();
26743
+      this._currentTickCompleteState && this._currentTickCompleteState.abort();
26744
+
26745
+      clearTimeout(this._cursorTimeout1);
26746
+      clearTimeout(this._cursorTimeout2);
26747
+
26748
+      this._currentCursorOpacity = 0;
26749
+      // to clear just itext area we need to transform the context
26750
+      // it may not be worth it
26751
+      if (shouldClear && canvas) {
26752
+        canvas.clearContext(canvas.contextTop || canvas.contextContainer);
26753
+      }
26754
+
26755
+    },
26756
+
26757
+    /**
26758
+     * Selects entire text
26759
+     * @return {fabric.IText} thisArg
26760
+     * @chainable
26761
+     */
26762
+    selectAll: function() {
26763
+      this.selectionStart = 0;
26764
+      this.selectionEnd = this._text.length;
26765
+      this._fireSelectionChanged();
26766
+      this._updateTextarea();
26767
+      return this;
26768
+    },
26769
+
26770
+    /**
26771
+     * Returns selected text
26772
+     * @return {String}
26773
+     */
26774
+    getSelectedText: function() {
26775
+      return this._text.slice(this.selectionStart, this.selectionEnd).join('');
26776
+    },
26777
+
26778
+    /**
26779
+     * Find new selection index representing start of current word according to current selection index
26780
+     * @param {Number} startFrom Surrent selection index
26781
+     * @return {Number} New selection index
26782
+     */
26783
+    findWordBoundaryLeft: function(startFrom) {
26784
+      var offset = 0, index = startFrom - 1;
26785
+
26786
+      // remove space before cursor first
26787
+      if (this._reSpace.test(this._text[index])) {
26788
+        while (this._reSpace.test(this._text[index])) {
26789
+          offset++;
26790
+          index--;
26791
+        }
26792
+      }
26793
+      while (/\S/.test(this._text[index]) && index > -1) {
26794
+        offset++;
26795
+        index--;
26796
+      }
26797
+
26798
+      return startFrom - offset;
26799
+    },
26800
+
26801
+    /**
26802
+     * Find new selection index representing end of current word according to current selection index
26803
+     * @param {Number} startFrom Current selection index
26804
+     * @return {Number} New selection index
26805
+     */
26806
+    findWordBoundaryRight: function(startFrom) {
26807
+      var offset = 0, index = startFrom;
26808
+
26809
+      // remove space after cursor first
26810
+      if (this._reSpace.test(this._text[index])) {
26811
+        while (this._reSpace.test(this._text[index])) {
26812
+          offset++;
26813
+          index++;
26814
+        }
26815
+      }
26816
+      while (/\S/.test(this._text[index]) && index < this.text.length) {
26817
+        offset++;
26818
+        index++;
26819
+      }
26820
+
26821
+      return startFrom + offset;
26822
+    },
26823
+
26824
+    /**
26825
+     * Find new selection index representing start of current line according to current selection index
26826
+     * @param {Number} startFrom Current selection index
26827
+     * @return {Number} New selection index
26828
+     */
26829
+    findLineBoundaryLeft: function(startFrom) {
26830
+      var offset = 0, index = startFrom - 1;
26831
+
26832
+      while (!/\n/.test(this._text[index]) && index > -1) {
26833
+        offset++;
26834
+        index--;
26835
+      }
26836
+
26837
+      return startFrom - offset;
26838
+    },
26839
+
26840
+    /**
26841
+     * Find new selection index representing end of current line according to current selection index
26842
+     * @param {Number} startFrom Current selection index
26843
+     * @return {Number} New selection index
26844
+     */
26845
+    findLineBoundaryRight: function(startFrom) {
26846
+      var offset = 0, index = startFrom;
26847
+
26848
+      while (!/\n/.test(this._text[index]) && index < this.text.length) {
26849
+        offset++;
26850
+        index++;
26851
+      }
26852
+
26853
+      return startFrom + offset;
26854
+    },
26855
+
26856
+    /**
26857
+     * Finds index corresponding to beginning or end of a word
26858
+     * @param {Number} selectionStart Index of a character
26859
+     * @param {Number} direction 1 or -1
26860
+     * @return {Number} Index of the beginning or end of a word
26861
+     */
26862
+    searchWordBoundary: function(selectionStart, direction) {
26863
+      var index     = this._reSpace.test(this.text.charAt(selectionStart)) ? selectionStart - 1 : selectionStart,
26864
+          _char     = this.text.charAt(index),
26865
+          reNonWord = /[ \n\.,;!\?\-]/;
26866
+
26867
+      while (!reNonWord.test(_char) && index > 0 && index < this.text.length) {
26868
+        index += direction;
26869
+        _char = this.text.charAt(index);
26870
+      }
26871
+      if (reNonWord.test(_char) && _char !== '\n') {
26872
+        index += direction === 1 ? 0 : 1;
26873
+      }
26874
+      return index;
26875
+    },
26876
+
26877
+    /**
26878
+     * Selects a word based on the index
26879
+     * @param {Number} selectionStart Index of a character
26880
+     */
26881
+    selectWord: function(selectionStart) {
26882
+      selectionStart = selectionStart || this.selectionStart;
26883
+      var newSelectionStart = this.searchWordBoundary(selectionStart, -1), /* search backwards */
26884
+          newSelectionEnd = this.searchWordBoundary(selectionStart, 1); /* search forward */
26885
+
26886
+      this.selectionStart = newSelectionStart;
26887
+      this.selectionEnd = newSelectionEnd;
26888
+      this._fireSelectionChanged();
26889
+      this._updateTextarea();
26890
+      this.renderCursorOrSelection();
26891
+    },
26892
+
26893
+    /**
26894
+     * Selects a line based on the index
26895
+     * @param {Number} selectionStart Index of a character
26896
+     * @return {fabric.IText} thisArg
26897
+     * @chainable
26898
+     */
26899
+    selectLine: function(selectionStart) {
26900
+      selectionStart = selectionStart || this.selectionStart;
26901
+      var newSelectionStart = this.findLineBoundaryLeft(selectionStart),
26902
+          newSelectionEnd = this.findLineBoundaryRight(selectionStart);
26903
+
26904
+      this.selectionStart = newSelectionStart;
26905
+      this.selectionEnd = newSelectionEnd;
26906
+      this._fireSelectionChanged();
26907
+      this._updateTextarea();
26908
+      return this;
26909
+    },
26910
+
26911
+    /**
26912
+     * Enters editing state
26913
+     * @return {fabric.IText} thisArg
26914
+     * @chainable
26915
+     */
26916
+    enterEditing: function(e) {
26917
+      if (this.isEditing || !this.editable) {
26918
+        return;
26919
+      }
26920
+
26921
+      if (this.canvas) {
26922
+        this.canvas.calcOffset();
26923
+        this.exitEditingOnOthers(this.canvas);
26924
+      }
26925
+
26926
+      this.isEditing = true;
26927
+
26928
+      this.initHiddenTextarea(e);
26929
+      this.hiddenTextarea.focus();
26930
+      this.hiddenTextarea.value = this.text;
26931
+      this._updateTextarea();
26932
+      this._saveEditingProps();
26933
+      this._setEditingProps();
26934
+      this._textBeforeEdit = this.text;
26935
+
26936
+      this._tick();
26937
+      this.fire('editing:entered');
26938
+      this._fireSelectionChanged();
26939
+      if (!this.canvas) {
26940
+        return this;
26941
+      }
26942
+      this.canvas.fire('text:editing:entered', { target: this });
26943
+      this.initMouseMoveHandler();
26944
+      this.canvas.requestRenderAll();
26945
+      return this;
26946
+    },
26947
+
26948
+    exitEditingOnOthers: function(canvas) {
26949
+      if (canvas._iTextInstances) {
26950
+        canvas._iTextInstances.forEach(function(obj) {
26951
+          obj.selected = false;
26952
+          if (obj.isEditing) {
26953
+            obj.exitEditing();
26954
+          }
26955
+        });
26956
+      }
26957
+    },
26958
+
26959
+    /**
26960
+     * Initializes "mousemove" event handler
26961
+     */
26962
+    initMouseMoveHandler: function() {
26963
+      this.canvas.on('mouse:move', this.mouseMoveHandler);
26964
+    },
26965
+
26966
+    /**
26967
+     * @private
26968
+     */
26969
+    mouseMoveHandler: function(options) {
26970
+      if (!this.__isMousedown || !this.isEditing) {
26971
+        return;
26972
+      }
26973
+
26974
+      var newSelectionStart = this.getSelectionStartFromPointer(options.e),
26975
+          currentStart = this.selectionStart,
26976
+          currentEnd = this.selectionEnd;
26977
+      if (
26978
+        (newSelectionStart !== this.__selectionStartOnMouseDown || currentStart === currentEnd)
26979
+        &&
26980
+        (currentStart === newSelectionStart || currentEnd === newSelectionStart)
26981
+      ) {
26982
+        return;
26983
+      }
26984
+      if (newSelectionStart > this.__selectionStartOnMouseDown) {
26985
+        this.selectionStart = this.__selectionStartOnMouseDown;
26986
+        this.selectionEnd = newSelectionStart;
26987
+      }
26988
+      else {
26989
+        this.selectionStart = newSelectionStart;
26990
+        this.selectionEnd = this.__selectionStartOnMouseDown;
26991
+      }
26992
+      if (this.selectionStart !== currentStart || this.selectionEnd !== currentEnd) {
26993
+        this.restartCursorIfNeeded();
26994
+        this._fireSelectionChanged();
26995
+        this._updateTextarea();
26996
+        this.renderCursorOrSelection();
26997
+      }
26998
+    },
26999
+
27000
+    /**
27001
+     * @private
27002
+     */
27003
+    _setEditingProps: function() {
27004
+      this.hoverCursor = 'text';
27005
+
27006
+      if (this.canvas) {
27007
+        this.canvas.defaultCursor = this.canvas.moveCursor = 'text';
27008
+      }
27009
+
27010
+      this.borderColor = this.editingBorderColor;
27011
+
27012
+      this.hasControls = this.selectable = false;
27013
+      this.lockMovementX = this.lockMovementY = true;
27014
+    },
27015
+
27016
+    /**
27017
+     * convert from textarea to grapheme indexes
27018
+     */
27019
+    fromStringToGraphemeSelection: function(start, end, text) {
27020
+      var smallerTextStart = text.slice(0, start),
27021
+          graphemeStart = fabric.util.string.graphemeSplit(smallerTextStart).length;
27022
+      if (start === end) {
27023
+        return { selectionStart: graphemeStart, selectionEnd: graphemeStart };
27024
+      }
27025
+      var smallerTextEnd = text.slice(start, end),
27026
+          graphemeEnd = fabric.util.string.graphemeSplit(smallerTextEnd).length;
27027
+      return { selectionStart: graphemeStart, selectionEnd: graphemeStart + graphemeEnd };
27028
+    },
27029
+
27030
+    /**
27031
+     * convert from fabric to textarea values
27032
+     */
27033
+    fromGraphemeToStringSelection: function(start, end, _text) {
27034
+      var smallerTextStart = _text.slice(0, start),
27035
+          graphemeStart = smallerTextStart.join('').length;
27036
+      if (start === end) {
27037
+        return { selectionStart: graphemeStart, selectionEnd: graphemeStart };
27038
+      }
27039
+      var smallerTextEnd = _text.slice(start, end),
27040
+          graphemeEnd = smallerTextEnd.join('').length;
27041
+      return { selectionStart: graphemeStart, selectionEnd: graphemeStart + graphemeEnd };
27042
+    },
27043
+
27044
+    /**
27045
+     * @private
27046
+     */
27047
+    _updateTextarea: function() {
27048
+      this.cursorOffsetCache = { };
27049
+      if (!this.hiddenTextarea) {
27050
+        return;
27051
+      }
27052
+      if (!this.inCompositionMode) {
27053
+        var newSelection = this.fromGraphemeToStringSelection(this.selectionStart, this.selectionEnd, this._text);
27054
+        this.hiddenTextarea.selectionStart = newSelection.selectionStart;
27055
+        this.hiddenTextarea.selectionEnd = newSelection.selectionEnd;
27056
+      }
27057
+      this.updateTextareaPosition();
27058
+    },
27059
+
27060
+    /**
27061
+     * @private
27062
+     */
27063
+    updateFromTextArea: function() {
27064
+      if (!this.hiddenTextarea) {
27065
+        return;
27066
+      }
27067
+      this.cursorOffsetCache = { };
27068
+      this.text = this.hiddenTextarea.value;
27069
+      if (this._shouldClearDimensionCache()) {
27070
+        this.initDimensions();
27071
+        this.setCoords();
27072
+      }
27073
+      var newSelection = this.fromStringToGraphemeSelection(
27074
+        this.hiddenTextarea.selectionStart, this.hiddenTextarea.selectionEnd, this.hiddenTextarea.value);
27075
+      this.selectionEnd = this.selectionStart = newSelection.selectionEnd;
27076
+      if (!this.inCompositionMode) {
27077
+        this.selectionStart = newSelection.selectionStart;
27078
+      }
27079
+      this.updateTextareaPosition();
27080
+    },
27081
+
27082
+    /**
27083
+     * @private
27084
+     */
27085
+    updateTextareaPosition: function() {
27086
+      if (this.selectionStart === this.selectionEnd) {
27087
+        var style = this._calcTextareaPosition();
27088
+        this.hiddenTextarea.style.left = style.left;
27089
+        this.hiddenTextarea.style.top = style.top;
27090
+      }
27091
+    },
27092
+
27093
+    /**
27094
+     * @private
27095
+     * @return {Object} style contains style for hiddenTextarea
27096
+     */
27097
+    _calcTextareaPosition: function() {
27098
+      if (!this.canvas) {
27099
+        return { x: 1, y: 1 };
27100
+      }
27101
+      var desiredPostion = this.inCompositionMode ? this.compositionStart : this.selectionStart,
27102
+          boundaries = this._getCursorBoundaries(desiredPostion),
27103
+          cursorLocation = this.get2DCursorLocation(desiredPostion),
27104
+          lineIndex = cursorLocation.lineIndex,
27105
+          charIndex = cursorLocation.charIndex,
27106
+          charHeight = this.getValueOfPropertyAt(lineIndex, charIndex, 'fontSize') * this.lineHeight,
27107
+          leftOffset = boundaries.leftOffset,
27108
+          m = this.calcTransformMatrix(),
27109
+          p = {
27110
+            x: boundaries.left + leftOffset,
27111
+            y: boundaries.top + boundaries.topOffset + charHeight
27112
+          },
27113
+          upperCanvas = this.canvas.upperCanvasEl,
27114
+          upperCanvasWidth = upperCanvas.width,
27115
+          upperCanvasHeight = upperCanvas.height,
27116
+          maxWidth = upperCanvasWidth - charHeight,
27117
+          maxHeight = upperCanvasHeight - charHeight,
27118
+          scaleX = upperCanvas.clientWidth / upperCanvasWidth,
27119
+          scaleY = upperCanvas.clientHeight / upperCanvasHeight;
27120
+
27121
+      p = fabric.util.transformPoint(p, m);
27122
+      p = fabric.util.transformPoint(p, this.canvas.viewportTransform);
27123
+      p.x *= scaleX;
27124
+      p.y *= scaleY;
27125
+      if (p.x < 0) {
27126
+        p.x = 0;
27127
+      }
27128
+      if (p.x > maxWidth) {
27129
+        p.x = maxWidth;
27130
+      }
27131
+      if (p.y < 0) {
27132
+        p.y = 0;
27133
+      }
27134
+      if (p.y > maxHeight) {
27135
+        p.y = maxHeight;
27136
+      }
27137
+
27138
+      // add canvas offset on document
27139
+      p.x += this.canvas._offset.left;
27140
+      p.y += this.canvas._offset.top;
27141
+
27142
+      return { left: p.x + 'px', top: p.y + 'px', fontSize: charHeight + 'px', charHeight: charHeight };
27143
+    },
27144
+
27145
+    /**
27146
+     * @private
27147
+     */
27148
+    _saveEditingProps: function() {
27149
+      this._savedProps = {
27150
+        hasControls: this.hasControls,
27151
+        borderColor: this.borderColor,
27152
+        lockMovementX: this.lockMovementX,
27153
+        lockMovementY: this.lockMovementY,
27154
+        hoverCursor: this.hoverCursor,
27155
+        defaultCursor: this.canvas && this.canvas.defaultCursor,
27156
+        moveCursor: this.canvas && this.canvas.moveCursor
27157
+      };
27158
+    },
27159
+
27160
+    /**
27161
+     * @private
27162
+     */
27163
+    _restoreEditingProps: function() {
27164
+      if (!this._savedProps) {
27165
+        return;
27166
+      }
27167
+
27168
+      this.hoverCursor = this._savedProps.hoverCursor;
27169
+      this.hasControls = this._savedProps.hasControls;
27170
+      this.borderColor = this._savedProps.borderColor;
27171
+      this.lockMovementX = this._savedProps.lockMovementX;
27172
+      this.lockMovementY = this._savedProps.lockMovementY;
27173
+
27174
+      if (this.canvas) {
27175
+        this.canvas.defaultCursor = this._savedProps.defaultCursor;
27176
+        this.canvas.moveCursor = this._savedProps.moveCursor;
27177
+      }
27178
+    },
27179
+
27180
+    /**
27181
+     * Exits from editing state
27182
+     * @return {fabric.IText} thisArg
27183
+     * @chainable
27184
+     */
27185
+    exitEditing: function() {
27186
+      var isTextChanged = (this._textBeforeEdit !== this.text);
27187
+      this.selected = false;
27188
+      this.isEditing = false;
27189
+      this.selectable = true;
27190
+
27191
+      this.selectionEnd = this.selectionStart;
27192
+
27193
+      if (this.hiddenTextarea) {
27194
+        this.hiddenTextarea.blur && this.hiddenTextarea.blur();
27195
+        this.canvas && this.hiddenTextarea.parentNode.removeChild(this.hiddenTextarea);
27196
+        this.hiddenTextarea = null;
27197
+      }
27198
+
27199
+      this.abortCursorAnimation();
27200
+      this._restoreEditingProps();
27201
+      this._currentCursorOpacity = 0;
27202
+      if (this._shouldClearDimensionCache()) {
27203
+        this.initDimensions();
27204
+        this.setCoords();
27205
+      }
27206
+      this.fire('editing:exited');
27207
+      isTextChanged && this.fire('modified');
27208
+      if (this.canvas) {
27209
+        this.canvas.off('mouse:move', this.mouseMoveHandler);
27210
+        this.canvas.fire('text:editing:exited', { target: this });
27211
+        isTextChanged && this.canvas.fire('object:modified', { target: this });
27212
+      }
27213
+      return this;
27214
+    },
27215
+
27216
+    /**
27217
+     * @private
27218
+     */
27219
+    _removeExtraneousStyles: function() {
27220
+      for (var prop in this.styles) {
27221
+        if (!this._textLines[prop]) {
27222
+          delete this.styles[prop];
27223
+        }
27224
+      }
27225
+    },
27226
+
27227
+    /**
27228
+     * remove and reflow a style block from start to end.
27229
+     * @param {Number} start linear start position for removal (included in removal)
27230
+     * @param {Number} end linear end position for removal ( excluded from removal )
27231
+     */
27232
+    removeStyleFromTo: function(start, end) {
27233
+      var cursorStart = this.get2DCursorLocation(start, true),
27234
+          cursorEnd = this.get2DCursorLocation(end, true),
27235
+          lineStart = cursorStart.lineIndex,
27236
+          charStart = cursorStart.charIndex,
27237
+          lineEnd = cursorEnd.lineIndex,
27238
+          charEnd = cursorEnd.charIndex,
27239
+          i, styleObj;
27240
+      if (lineStart !== lineEnd) {
27241
+        // step1 remove the trailing of lineStart
27242
+        if (this.styles[lineStart]) {
27243
+          for (i = charStart; i < this._unwrappedTextLines[lineStart].length; i++) {
27244
+            delete this.styles[lineStart][i];
27245
+          }
27246
+        }
27247
+        // step2 move the trailing of lineEnd to lineStart if needed
27248
+        if (this.styles[lineEnd]) {
27249
+          for (i = charEnd; i < this._unwrappedTextLines[lineEnd].length; i++) {
27250
+            styleObj = this.styles[lineEnd][i];
27251
+            if (styleObj) {
27252
+              this.styles[lineStart] || (this.styles[lineStart] = { });
27253
+              this.styles[lineStart][charStart + i - charEnd] = styleObj;
27254
+            }
27255
+          }
27256
+        }
27257
+        // step3 detects lines will be completely removed.
27258
+        for (i = lineStart + 1; i <= lineEnd; i++) {
27259
+          delete this.styles[i];
27260
+        }
27261
+        // step4 shift remaining lines.
27262
+        this.shiftLineStyles(lineEnd, lineStart - lineEnd);
27263
+      }
27264
+      else {
27265
+        // remove and shift left on the same line
27266
+        if (this.styles[lineStart]) {
27267
+          styleObj = this.styles[lineStart];
27268
+          var diff = charEnd - charStart, numericChar, _char;
27269
+          for (i = charStart; i < charEnd; i++) {
27270
+            delete styleObj[i];
27271
+          }
27272
+          for (_char in this.styles[lineStart]) {
27273
+            numericChar = parseInt(_char, 10);
27274
+            if (numericChar >= charEnd) {
27275
+              styleObj[numericChar - diff] = styleObj[_char];
27276
+              delete styleObj[_char];
27277
+            }
27278
+          }
27279
+        }
27280
+      }
27281
+    },
27282
+
27283
+    /**
27284
+     * Shifts line styles up or down
27285
+     * @param {Number} lineIndex Index of a line
27286
+     * @param {Number} offset Can any number?
27287
+     */
27288
+    shiftLineStyles: function(lineIndex, offset) {
27289
+      // shift all line styles by offset upward or downward
27290
+      // do not clone deep. we need new array, not new style objects
27291
+      var clonedStyles = clone(this.styles);
27292
+      for (var line in this.styles) {
27293
+        var numericLine = parseInt(line, 10);
27294
+        if (numericLine > lineIndex) {
27295
+          this.styles[numericLine + offset] = clonedStyles[numericLine];
27296
+          if (!clonedStyles[numericLine - offset]) {
27297
+            delete this.styles[numericLine];
27298
+          }
27299
+        }
27300
+      }
27301
+    },
27302
+
27303
+    restartCursorIfNeeded: function() {
27304
+      if (!this._currentTickState || this._currentTickState.isAborted
27305
+        || !this._currentTickCompleteState || this._currentTickCompleteState.isAborted
27306
+      ) {
27307
+        this.initDelayedCursor();
27308
+      }
27309
+    },
27310
+
27311
+    /**
27312
+     * Inserts new style object
27313
+     * @param {Number} lineIndex Index of a line
27314
+     * @param {Number} charIndex Index of a char
27315
+     * @param {Number} qty number of lines to add
27316
+     * @param {Array} copiedStyle Array of objects styles
27317
+     */
27318
+    insertNewlineStyleObject: function(lineIndex, charIndex, qty, copiedStyle) {
27319
+      var currentCharStyle,
27320
+          newLineStyles = {},
27321
+          somethingAdded = false;
27322
+
27323
+      qty || (qty = 1);
27324
+      this.shiftLineStyles(lineIndex, qty);
27325
+      if (this.styles[lineIndex]) {
27326
+        currentCharStyle = this.styles[lineIndex][charIndex === 0 ? charIndex : charIndex - 1];
27327
+      }
27328
+
27329
+      // we clone styles of all chars
27330
+      // after cursor onto the current line
27331
+      for (var index in this.styles[lineIndex]) {
27332
+        var numIndex = parseInt(index, 10);
27333
+        if (numIndex >= charIndex) {
27334
+          somethingAdded = true;
27335
+          newLineStyles[numIndex - charIndex] = this.styles[lineIndex][index];
27336
+          // remove lines from the previous line since they're on a new line now
27337
+          delete this.styles[lineIndex][index];
27338
+        }
27339
+      }
27340
+      if (somethingAdded) {
27341
+        this.styles[lineIndex + qty] = newLineStyles;
27342
+      }
27343
+      else {
27344
+        delete this.styles[lineIndex + qty];
27345
+      }
27346
+      // for the other lines
27347
+      // we clone current char style onto the next (otherwise empty) line
27348
+      while (qty > 1) {
27349
+        qty--;
27350
+        if (copiedStyle && copiedStyle[qty]) {
27351
+          this.styles[lineIndex + qty] = { 0: clone(copiedStyle[qty]) };
27352
+        }
27353
+        else if (currentCharStyle) {
27354
+          this.styles[lineIndex + qty] = { 0: clone(currentCharStyle) };
27355
+        }
27356
+        else {
27357
+          delete this.styles[lineIndex + qty];
27358
+        }
27359
+      }
27360
+      this._forceClearCache = true;
27361
+    },
27362
+
27363
+    /**
27364
+     * Inserts style object for a given line/char index
27365
+     * @param {Number} lineIndex Index of a line
27366
+     * @param {Number} charIndex Index of a char
27367
+     * @param {Number} quantity number Style object to insert, if given
27368
+     * @param {Array} copiedStyle array of style objecs
27369
+     */
27370
+    insertCharStyleObject: function(lineIndex, charIndex, quantity, copiedStyle) {
27371
+      if (!this.styles) {
27372
+        this.styles = {};
27373
+      }
27374
+      var currentLineStyles       = this.styles[lineIndex],
27375
+          currentLineStylesCloned = currentLineStyles ? clone(currentLineStyles) : {};
27376
+
27377
+      quantity || (quantity = 1);
27378
+      // shift all char styles by quantity forward
27379
+      // 0,1,2,3 -> (charIndex=2) -> 0,1,3,4 -> (insert 2) -> 0,1,2,3,4
27380
+      for (var index in currentLineStylesCloned) {
27381
+        var numericIndex = parseInt(index, 10);
27382
+        if (numericIndex >= charIndex) {
27383
+          currentLineStyles[numericIndex + quantity] = currentLineStylesCloned[numericIndex];
27384
+          // only delete the style if there was nothing moved there
27385
+          if (!currentLineStylesCloned[numericIndex - quantity]) {
27386
+            delete currentLineStyles[numericIndex];
27387
+          }
27388
+        }
27389
+      }
27390
+      this._forceClearCache = true;
27391
+      if (copiedStyle) {
27392
+        while (quantity--) {
27393
+          if (!Object.keys(copiedStyle[quantity]).length) {
27394
+            continue;
27395
+          }
27396
+          if (!this.styles[lineIndex]) {
27397
+            this.styles[lineIndex] = {};
27398
+          }
27399
+          this.styles[lineIndex][charIndex + quantity] = clone(copiedStyle[quantity]);
27400
+        }
27401
+        return;
27402
+      }
27403
+      if (!currentLineStyles) {
27404
+        return;
27405
+      }
27406
+      var newStyle = currentLineStyles[charIndex ? charIndex - 1 : 1];
27407
+      while (newStyle && quantity--) {
27408
+        this.styles[lineIndex][charIndex + quantity] = clone(newStyle);
27409
+      }
27410
+    },
27411
+
27412
+    /**
27413
+     * Inserts style object(s)
27414
+     * @param {Array} insertedText Characters at the location where style is inserted
27415
+     * @param {Number} start cursor index for inserting style
27416
+     * @param {Array} [copiedStyle] array of style objects to insert.
27417
+     */
27418
+    insertNewStyleBlock: function(insertedText, start, copiedStyle) {
27419
+      var cursorLoc = this.get2DCursorLocation(start, true),
27420
+          addedLines = [0], linesLenght = 0;
27421
+      for (var i = 0; i < insertedText.length; i++) {
27422
+        if (insertedText[i] === '\n') {
27423
+          linesLenght++;
27424
+          addedLines[linesLenght] = 0;
27425
+        }
27426
+        else {
27427
+          addedLines[linesLenght]++;
27428
+        }
27429
+      }
27430
+      if (addedLines[0] > 0) {
27431
+        this.insertCharStyleObject(cursorLoc.lineIndex, cursorLoc.charIndex, addedLines[0], copiedStyle);
27432
+        copiedStyle = copiedStyle && copiedStyle.slice(addedLines[0] + 1);
27433
+      }
27434
+      linesLenght && this.insertNewlineStyleObject(
27435
+        cursorLoc.lineIndex, cursorLoc.charIndex + addedLines[0], linesLenght);
27436
+      for (var i = 1; i < linesLenght; i++) {
27437
+        if (addedLines[i] > 0) {
27438
+          this.insertCharStyleObject(cursorLoc.lineIndex + i, 0, addedLines[i], copiedStyle);
27439
+        }
27440
+        else if (copiedStyle) {
27441
+          this.styles[cursorLoc.lineIndex + i][0] = copiedStyle[0];
27442
+        }
27443
+        copiedStyle = copiedStyle && copiedStyle.slice(addedLines[i] + 1);
27444
+      }
27445
+      // we use i outside the loop to get it like linesLength
27446
+      if (addedLines[i] > 0) {
27447
+        this.insertCharStyleObject(cursorLoc.lineIndex + i, 0, addedLines[i], copiedStyle);
27448
+      }
27449
+    },
27450
+
27451
+    /**
27452
+     * Set the selectionStart and selectionEnd according to the ne postion of cursor
27453
+     * mimic the key - mouse navigation when shift is pressed.
27454
+     */
27455
+    setSelectionStartEndWithShift: function(start, end, newSelection) {
27456
+      if (newSelection <= start) {
27457
+        if (end === start) {
27458
+          this._selectionDirection = 'left';
27459
+        }
27460
+        else if (this._selectionDirection === 'right') {
27461
+          this._selectionDirection = 'left';
27462
+          this.selectionEnd = start;
27463
+        }
27464
+        this.selectionStart = newSelection;
27465
+      }
27466
+      else if (newSelection > start && newSelection < end) {
27467
+        if (this._selectionDirection === 'right') {
27468
+          this.selectionEnd = newSelection;
27469
+        }
27470
+        else {
27471
+          this.selectionStart = newSelection;
27472
+        }
27473
+      }
27474
+      else {
27475
+        // newSelection is > selection start and end
27476
+        if (end === start) {
27477
+          this._selectionDirection = 'right';
27478
+        }
27479
+        else if (this._selectionDirection === 'left') {
27480
+          this._selectionDirection = 'right';
27481
+          this.selectionStart = end;
27482
+        }
27483
+        this.selectionEnd = newSelection;
27484
+      }
27485
+    },
27486
+
27487
+    setSelectionInBoundaries: function() {
27488
+      var length = this.text.length;
27489
+      if (this.selectionStart > length) {
27490
+        this.selectionStart = length;
27491
+      }
27492
+      else if (this.selectionStart < 0) {
27493
+        this.selectionStart = 0;
27494
+      }
27495
+      if (this.selectionEnd > length) {
27496
+        this.selectionEnd = length;
27497
+      }
27498
+      else if (this.selectionEnd < 0) {
27499
+        this.selectionEnd = 0;
27500
+      }
27501
+    }
27502
+  });
27503
+})();
27504
+
27505
+
27506
+fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ {
27507
+  /**
27508
+   * Initializes "dbclick" event handler
27509
+   */
27510
+  initDoubleClickSimulation: function() {
27511
+
27512
+    // for double click
27513
+    this.__lastClickTime = +new Date();
27514
+
27515
+    // for triple click
27516
+    this.__lastLastClickTime = +new Date();
27517
+
27518
+    this.__lastPointer = { };
27519
+
27520
+    this.on('mousedown', this.onMouseDown);
27521
+  },
27522
+
27523
+  /**
27524
+   * Default event handler to simulate triple click
27525
+   * @private
27526
+   */
27527
+  onMouseDown: function(options) {
27528
+    if (!this.canvas) {
27529
+      return;
27530
+    }
27531
+    this.__newClickTime = +new Date();
27532
+    var newPointer = options.pointer;
27533
+    if (this.isTripleClick(newPointer)) {
27534
+      this.fire('tripleclick', options);
27535
+      this._stopEvent(options.e);
27536
+    }
27537
+    this.__lastLastClickTime = this.__lastClickTime;
27538
+    this.__lastClickTime = this.__newClickTime;
27539
+    this.__lastPointer = newPointer;
27540
+    this.__lastIsEditing = this.isEditing;
27541
+    this.__lastSelected = this.selected;
27542
+  },
27543
+
27544
+  isTripleClick: function(newPointer) {
27545
+    return this.__newClickTime - this.__lastClickTime < 500 &&
27546
+        this.__lastClickTime - this.__lastLastClickTime < 500 &&
27547
+        this.__lastPointer.x === newPointer.x &&
27548
+        this.__lastPointer.y === newPointer.y;
27549
+  },
27550
+
27551
+  /**
27552
+   * @private
27553
+   */
27554
+  _stopEvent: function(e) {
27555
+    e.preventDefault && e.preventDefault();
27556
+    e.stopPropagation && e.stopPropagation();
27557
+  },
27558
+
27559
+  /**
27560
+   * Initializes event handlers related to cursor or selection
27561
+   */
27562
+  initCursorSelectionHandlers: function() {
27563
+    this.initMousedownHandler();
27564
+    this.initMouseupHandler();
27565
+    this.initClicks();
27566
+  },
27567
+
27568
+  /**
27569
+   * Initializes double and triple click event handlers
27570
+   */
27571
+  initClicks: function() {
27572
+    this.on('mousedblclick', function(options) {
27573
+      this.selectWord(this.getSelectionStartFromPointer(options.e));
27574
+    });
27575
+    this.on('tripleclick', function(options) {
27576
+      this.selectLine(this.getSelectionStartFromPointer(options.e));
27577
+    });
27578
+  },
27579
+
27580
+  /**
27581
+   * Default event handler for the basic functionalities needed on _mouseDown
27582
+   * can be overridden to do something different.
27583
+   * Scope of this implementation is: find the click position, set selectionStart
27584
+   * find selectionEnd, initialize the drawing of either cursor or selection area
27585
+   */
27586
+  _mouseDownHandler: function(options) {
27587
+    if (!this.canvas || !this.editable || (options.e.button && options.e.button !== 1)) {
27588
+      return;
27589
+    }
27590
+
27591
+    this.__isMousedown = true;
27592
+
27593
+    if (this.selected) {
27594
+      this.setCursorByClick(options.e);
27595
+    }
27596
+
27597
+    if (this.isEditing) {
27598
+      this.__selectionStartOnMouseDown = this.selectionStart;
27599
+      if (this.selectionStart === this.selectionEnd) {
27600
+        this.abortCursorAnimation();
27601
+      }
27602
+      this.renderCursorOrSelection();
27603
+    }
27604
+  },
27605
+
27606
+  /**
27607
+   * Default event handler for the basic functionalities needed on mousedown:before
27608
+   * can be overridden to do something different.
27609
+   * Scope of this implementation is: verify the object is already selected when mousing down
27610
+   */
27611
+  _mouseDownHandlerBefore: function(options) {
27612
+    if (!this.canvas || !this.editable || (options.e.button && options.e.button !== 1)) {
27613
+      return;
27614
+    }
27615
+    if (this === this.canvas._activeObject) {
27616
+      this.selected = true;
27617
+    }
27618
+  },
27619
+
27620
+  /**
27621
+   * Initializes "mousedown" event handler
27622
+   */
27623
+  initMousedownHandler: function() {
27624
+    this.on('mousedown', this._mouseDownHandler);
27625
+    this.on('mousedown:before', this._mouseDownHandlerBefore);
27626
+  },
27627
+
27628
+  /**
27629
+   * Initializes "mouseup" event handler
27630
+   */
27631
+  initMouseupHandler: function() {
27632
+    this.on('mouseup', this.mouseUpHandler);
27633
+  },
27634
+
27635
+  /**
27636
+   * standard hander for mouse up, overridable
27637
+   * @private
27638
+   */
27639
+  mouseUpHandler: function(options) {
27640
+    this.__isMousedown = false;
27641
+    if (!this.editable || this.group ||
27642
+      (options.transform && options.transform.actionPerformed) ||
27643
+      (options.e.button && options.e.button !== 1)) {
27644
+      return;
27645
+    }
27646
+
27647
+    if (this.canvas) {
27648
+      var currentActive = this.canvas._activeObject;
27649
+      if (currentActive && currentActive !== this) {
27650
+        // avoid running this logic when there is an active object
27651
+        // this because is possible with shift click and fast clicks,
27652
+        // to rapidly deselect and reselect this object and trigger an enterEdit
27653
+        return;
27654
+      }
27655
+    }
27656
+
27657
+    if (this.__lastSelected && !this.__corner) {
27658
+      this.selected = false;
27659
+      this.__lastSelected = false;
27660
+      this.enterEditing(options.e);
27661
+      if (this.selectionStart === this.selectionEnd) {
27662
+        this.initDelayedCursor(true);
27663
+      }
27664
+      else {
27665
+        this.renderCursorOrSelection();
27666
+      }
27667
+    }
27668
+    else {
27669
+      this.selected = true;
27670
+    }
27671
+  },
27672
+
27673
+  /**
27674
+   * Changes cursor location in a text depending on passed pointer (x/y) object
27675
+   * @param {Event} e Event object
27676
+   */
27677
+  setCursorByClick: function(e) {
27678
+    var newSelection = this.getSelectionStartFromPointer(e),
27679
+        start = this.selectionStart, end = this.selectionEnd;
27680
+    if (e.shiftKey) {
27681
+      this.setSelectionStartEndWithShift(start, end, newSelection);
27682
+    }
27683
+    else {
27684
+      this.selectionStart = newSelection;
27685
+      this.selectionEnd = newSelection;
27686
+    }
27687
+    if (this.isEditing) {
27688
+      this._fireSelectionChanged();
27689
+      this._updateTextarea();
27690
+    }
27691
+  },
27692
+
27693
+  /**
27694
+   * Returns index of a character corresponding to where an object was clicked
27695
+   * @param {Event} e Event object
27696
+   * @return {Number} Index of a character
27697
+   */
27698
+  getSelectionStartFromPointer: function(e) {
27699
+    var mouseOffset = this.getLocalPointer(e),
27700
+        prevWidth = 0,
27701
+        width = 0,
27702
+        height = 0,
27703
+        charIndex = 0,
27704
+        lineIndex = 0,
27705
+        lineLeftOffset,
27706
+        line;
27707
+
27708
+    for (var i = 0, len = this._textLines.length; i < len; i++) {
27709
+      if (height <= mouseOffset.y) {
27710
+        height += this.getHeightOfLine(i) * this.scaleY;
27711
+        lineIndex = i;
27712
+        if (i > 0) {
27713
+          charIndex += this._textLines[i - 1].length + 1;
27714
+        }
27715
+      }
27716
+      else {
27717
+        break;
27718
+      }
27719
+    }
27720
+    lineLeftOffset = this._getLineLeftOffset(lineIndex);
27721
+    width = lineLeftOffset * this.scaleX;
27722
+    line = this._textLines[lineIndex];
27723
+    for (var j = 0, jlen = line.length; j < jlen; j++) {
27724
+      prevWidth = width;
27725
+      // i removed something about flipX here, check.
27726
+      width += this.__charBounds[lineIndex][j].kernedWidth * this.scaleX;
27727
+      if (width <= mouseOffset.x) {
27728
+        charIndex++;
27729
+      }
27730
+      else {
27731
+        break;
27732
+      }
27733
+    }
27734
+    return this._getNewSelectionStartFromOffset(mouseOffset, prevWidth, width, charIndex, jlen);
27735
+  },
27736
+
27737
+  /**
27738
+   * @private
27739
+   */
27740
+  _getNewSelectionStartFromOffset: function(mouseOffset, prevWidth, width, index, jlen) {
27741
+    // we need Math.abs because when width is after the last char, the offset is given as 1, while is 0
27742
+    var distanceBtwLastCharAndCursor = mouseOffset.x - prevWidth,
27743
+        distanceBtwNextCharAndCursor = width - mouseOffset.x,
27744
+        offset = distanceBtwNextCharAndCursor > distanceBtwLastCharAndCursor ||
27745
+          distanceBtwNextCharAndCursor < 0 ? 0 : 1,
27746
+        newSelectionStart = index + offset;
27747
+    // if object is horizontally flipped, mirror cursor location from the end
27748
+    if (this.flipX) {
27749
+      newSelectionStart = jlen - newSelectionStart;
27750
+    }
27751
+
27752
+    if (newSelectionStart > this._text.length) {
27753
+      newSelectionStart = this._text.length;
27754
+    }
27755
+
27756
+    return newSelectionStart;
27757
+  }
27758
+});
27759
+
27760
+
27761
+fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ {
27762
+
27763
+  /**
27764
+   * Initializes hidden textarea (needed to bring up keyboard in iOS)
27765
+   */
27766
+  initHiddenTextarea: function() {
27767
+    this.hiddenTextarea = fabric.document.createElement('textarea');
27768
+    this.hiddenTextarea.setAttribute('autocapitalize', 'off');
27769
+    this.hiddenTextarea.setAttribute('autocorrect', 'off');
27770
+    this.hiddenTextarea.setAttribute('autocomplete', 'off');
27771
+    this.hiddenTextarea.setAttribute('spellcheck', 'false');
27772
+    this.hiddenTextarea.setAttribute('data-fabric-hiddentextarea', '');
27773
+    this.hiddenTextarea.setAttribute('wrap', 'off');
27774
+    var style = this._calcTextareaPosition();
27775
+    // line-height: 1px; was removed from the style to fix this:
27776
+    // https://bugs.chromium.org/p/chromium/issues/detail?id=870966
27777
+    this.hiddenTextarea.style.cssText = 'position: absolute; top: ' + style.top +
27778
+    '; left: ' + style.left + '; z-index: -999; opacity: 0; width: 1px; height: 1px; font-size: 1px;' +
27779
+    ' paddingï½°top: ' + style.fontSize + ';';
27780
+    fabric.document.body.appendChild(this.hiddenTextarea);
27781
+
27782
+    fabric.util.addListener(this.hiddenTextarea, 'keydown', this.onKeyDown.bind(this));
27783
+    fabric.util.addListener(this.hiddenTextarea, 'keyup', this.onKeyUp.bind(this));
27784
+    fabric.util.addListener(this.hiddenTextarea, 'input', this.onInput.bind(this));
27785
+    fabric.util.addListener(this.hiddenTextarea, 'copy', this.copy.bind(this));
27786
+    fabric.util.addListener(this.hiddenTextarea, 'cut', this.copy.bind(this));
27787
+    fabric.util.addListener(this.hiddenTextarea, 'paste', this.paste.bind(this));
27788
+    fabric.util.addListener(this.hiddenTextarea, 'compositionstart', this.onCompositionStart.bind(this));
27789
+    fabric.util.addListener(this.hiddenTextarea, 'compositionupdate', this.onCompositionUpdate.bind(this));
27790
+    fabric.util.addListener(this.hiddenTextarea, 'compositionend', this.onCompositionEnd.bind(this));
27791
+
27792
+    if (!this._clickHandlerInitialized && this.canvas) {
27793
+      fabric.util.addListener(this.canvas.upperCanvasEl, 'click', this.onClick.bind(this));
27794
+      this._clickHandlerInitialized = true;
27795
+    }
27796
+  },
27797
+
27798
+  /**
27799
+   * For functionalities on keyDown
27800
+   * Map a special key to a function of the instance/prototype
27801
+   * If you need different behaviour for ESC or TAB or arrows, you have to change
27802
+   * this map setting the name of a function that you build on the fabric.Itext or
27803
+   * your prototype.
27804
+   * the map change will affect all Instances unless you need for only some text Instances
27805
+   * in that case you have to clone this object and assign your Instance.
27806
+   * this.keysMap = fabric.util.object.clone(this.keysMap);
27807
+   * The function must be in fabric.Itext.prototype.myFunction And will receive event as args[0]
27808
+   */
27809
+  keysMap: {
27810
+    9:  'exitEditing',
27811
+    27: 'exitEditing',
27812
+    33: 'moveCursorUp',
27813
+    34: 'moveCursorDown',
27814
+    35: 'moveCursorRight',
27815
+    36: 'moveCursorLeft',
27816
+    37: 'moveCursorLeft',
27817
+    38: 'moveCursorUp',
27818
+    39: 'moveCursorRight',
27819
+    40: 'moveCursorDown',
27820
+  },
27821
+
27822
+  /**
27823
+   * For functionalities on keyUp + ctrl || cmd
27824
+   */
27825
+  ctrlKeysMapUp: {
27826
+    67: 'copy',
27827
+    88: 'cut'
27828
+  },
27829
+
27830
+  /**
27831
+   * For functionalities on keyDown + ctrl || cmd
27832
+   */
27833
+  ctrlKeysMapDown: {
27834
+    65: 'selectAll'
27835
+  },
27836
+
27837
+  onClick: function() {
27838
+    // No need to trigger click event here, focus is enough to have the keyboard appear on Android
27839
+    this.hiddenTextarea && this.hiddenTextarea.focus();
27840
+  },
27841
+
27842
+  /**
27843
+   * Handles keyup event
27844
+   * @param {Event} e Event object
27845
+   */
27846
+  onKeyDown: function(e) {
27847
+    if (!this.isEditing || this.inCompositionMode) {
27848
+      return;
27849
+    }
27850
+    if (e.keyCode in this.keysMap) {
27851
+      this[this.keysMap[e.keyCode]](e);
27852
+    }
27853
+    else if ((e.keyCode in this.ctrlKeysMapDown) && (e.ctrlKey || e.metaKey)) {
27854
+      this[this.ctrlKeysMapDown[e.keyCode]](e);
27855
+    }
27856
+    else {
27857
+      return;
27858
+    }
27859
+    e.stopImmediatePropagation();
27860
+    e.preventDefault();
27861
+    if (e.keyCode >= 33 && e.keyCode <= 40) {
27862
+      // if i press an arrow key just update selection
27863
+      this.clearContextTop();
27864
+      this.renderCursorOrSelection();
27865
+    }
27866
+    else {
27867
+      this.canvas && this.canvas.requestRenderAll();
27868
+    }
27869
+  },
27870
+
27871
+  /**
27872
+   * Handles keyup event
27873
+   * We handle KeyUp because ie11 and edge have difficulties copy/pasting
27874
+   * if a copy/cut event fired, keyup is dismissed
27875
+   * @param {Event} e Event object
27876
+   */
27877
+  onKeyUp: function(e) {
27878
+    if (!this.isEditing || this._copyDone || this.inCompositionMode) {
27879
+      this._copyDone = false;
27880
+      return;
27881
+    }
27882
+    if ((e.keyCode in this.ctrlKeysMapUp) && (e.ctrlKey || e.metaKey)) {
27883
+      this[this.ctrlKeysMapUp[e.keyCode]](e);
27884
+    }
27885
+    else {
27886
+      return;
27887
+    }
27888
+    e.stopImmediatePropagation();
27889
+    e.preventDefault();
27890
+    this.canvas && this.canvas.requestRenderAll();
27891
+  },
27892
+
27893
+  /**
27894
+   * Handles onInput event
27895
+   * @param {Event} e Event object
27896
+   */
27897
+  onInput: function(e) {
27898
+    var fromPaste = this.fromPaste;
27899
+    this.fromPaste = false;
27900
+    e && e.stopPropagation();
27901
+    if (!this.isEditing) {
27902
+      return;
27903
+    }
27904
+    // decisions about style changes.
27905
+    var nextText = this._splitTextIntoLines(this.hiddenTextarea.value).graphemeText,
27906
+        charCount = this._text.length,
27907
+        nextCharCount = nextText.length,
27908
+        removedText, insertedText,
27909
+        charDiff = nextCharCount - charCount;
27910
+    if (this.hiddenTextarea.value === '') {
27911
+      this.styles = { };
27912
+      this.updateFromTextArea();
27913
+      this.fire('changed');
27914
+      if (this.canvas) {
27915
+        this.canvas.fire('text:changed', { target: this });
27916
+        this.canvas.requestRenderAll();
27917
+      }
27918
+      return;
27919
+    }
27920
+
27921
+    var textareaSelection = this.fromStringToGraphemeSelection(
27922
+      this.hiddenTextarea.selectionStart,
27923
+      this.hiddenTextarea.selectionEnd,
27924
+      this.hiddenTextarea.value
27925
+    );
27926
+    var backDelete = this.selectionStart > textareaSelection.selectionStart;
27927
+
27928
+    if (this.selectionStart !== this.selectionEnd) {
27929
+      removedText = this._text.slice(this.selectionStart, this.selectionEnd);
27930
+      charDiff += this.selectionEnd - this.selectionStart;
27931
+    }
27932
+    else if (nextCharCount < charCount) {
27933
+      if (backDelete) {
27934
+        removedText = this._text.slice(this.selectionEnd + charDiff, this.selectionEnd);
27935
+      }
27936
+      else {
27937
+        removedText = this._text.slice(this.selectionStart, this.selectionStart - charDiff);
27938
+      }
27939
+    }
27940
+    insertedText = nextText.slice(textareaSelection.selectionEnd - charDiff, textareaSelection.selectionEnd);
27941
+    if (removedText && removedText.length) {
27942
+      if (this.selectionStart !== this.selectionEnd) {
27943
+        this.removeStyleFromTo(this.selectionStart, this.selectionEnd);
27944
+      }
27945
+      else if (backDelete) {
27946
+        // detect differencies between forwardDelete and backDelete
27947
+        this.removeStyleFromTo(this.selectionEnd - removedText.length, this.selectionEnd);
27948
+      }
27949
+      else {
27950
+        this.removeStyleFromTo(this.selectionEnd, this.selectionEnd + removedText.length);
27951
+      }
27952
+    }
27953
+    if (insertedText.length) {
27954
+      if (fromPaste && insertedText.join('') === fabric.copiedText) {
27955
+        this.insertNewStyleBlock(insertedText, this.selectionStart, fabric.copiedTextStyle);
27956
+      }
27957
+      else {
27958
+        this.insertNewStyleBlock(insertedText, this.selectionStart);
27959
+      }
27960
+    }
27961
+    this.updateFromTextArea();
27962
+    this.fire('changed');
27963
+    if (this.canvas) {
27964
+      this.canvas.fire('text:changed', { target: this });
27965
+      this.canvas.requestRenderAll();
27966
+    }
27967
+  },
27968
+  /**
27969
+   * Composition start
27970
+   */
27971
+  onCompositionStart: function() {
27972
+    this.inCompositionMode = true;
27973
+  },
27974
+
27975
+  /**
27976
+   * Composition end
27977
+   */
27978
+  onCompositionEnd: function() {
27979
+    this.inCompositionMode = false;
27980
+  },
27981
+
27982
+  // /**
27983
+  //  * Composition update
27984
+  //  */
27985
+  onCompositionUpdate: function(e) {
27986
+    this.compositionStart = e.target.selectionStart;
27987
+    this.compositionEnd = e.target.selectionEnd;
27988
+    this.updateTextareaPosition();
27989
+  },
27990
+
27991
+  /**
27992
+   * Copies selected text
27993
+   * @param {Event} e Event object
27994
+   */
27995
+  copy: function() {
27996
+    if (this.selectionStart === this.selectionEnd) {
27997
+      //do not cut-copy if no selection
27998
+      return;
27999
+    }
28000
+
28001
+    fabric.copiedText = this.getSelectedText();
28002
+    fabric.copiedTextStyle = this.getSelectionStyles(this.selectionStart, this.selectionEnd, true);
28003
+    this._copyDone = true;
28004
+  },
28005
+
28006
+  /**
28007
+   * Pastes text
28008
+   * @param {Event} e Event object
28009
+   */
28010
+  paste: function() {
28011
+    this.fromPaste = true;
28012
+  },
28013
+
28014
+  /**
28015
+   * @private
28016
+   * @param {Event} e Event object
28017
+   * @return {Object} Clipboard data object
28018
+   */
28019
+  _getClipboardData: function(e) {
28020
+    return (e && e.clipboardData) || fabric.window.clipboardData;
28021
+  },
28022
+
28023
+  /**
28024
+   * Finds the width in pixels before the cursor on the same line
28025
+   * @private
28026
+   * @param {Number} lineIndex
28027
+   * @param {Number} charIndex
28028
+   * @return {Number} widthBeforeCursor width before cursor
28029
+   */
28030
+  _getWidthBeforeCursor: function(lineIndex, charIndex) {
28031
+    var widthBeforeCursor = this._getLineLeftOffset(lineIndex), bound;
28032
+
28033
+    if (charIndex > 0) {
28034
+      bound = this.__charBounds[lineIndex][charIndex - 1];
28035
+      widthBeforeCursor += bound.left + bound.width;
28036
+    }
28037
+    return widthBeforeCursor;
28038
+  },
28039
+
28040
+  /**
28041
+   * Gets start offset of a selection
28042
+   * @param {Event} e Event object
28043
+   * @param {Boolean} isRight
28044
+   * @return {Number}
28045
+   */
28046
+  getDownCursorOffset: function(e, isRight) {
28047
+    var selectionProp = this._getSelectionForOffset(e, isRight),
28048
+        cursorLocation = this.get2DCursorLocation(selectionProp),
28049
+        lineIndex = cursorLocation.lineIndex;
28050
+    // if on last line, down cursor goes to end of line
28051
+    if (lineIndex === this._textLines.length - 1 || e.metaKey || e.keyCode === 34) {
28052
+      // move to the end of a text
28053
+      return this._text.length - selectionProp;
28054
+    }
28055
+    var charIndex = cursorLocation.charIndex,
28056
+        widthBeforeCursor = this._getWidthBeforeCursor(lineIndex, charIndex),
28057
+        indexOnOtherLine = this._getIndexOnLine(lineIndex + 1, widthBeforeCursor),
28058
+        textAfterCursor = this._textLines[lineIndex].slice(charIndex);
28059
+    return textAfterCursor.length + indexOnOtherLine + 2;
28060
+  },
28061
+
28062
+  /**
28063
+   * private
28064
+   * Helps finding if the offset should be counted from Start or End
28065
+   * @param {Event} e Event object
28066
+   * @param {Boolean} isRight
28067
+   * @return {Number}
28068
+   */
28069
+  _getSelectionForOffset: function(e, isRight) {
28070
+    if (e.shiftKey && this.selectionStart !== this.selectionEnd && isRight) {
28071
+      return this.selectionEnd;
28072
+    }
28073
+    else {
28074
+      return this.selectionStart;
28075
+    }
28076
+  },
28077
+
28078
+  /**
28079
+   * @param {Event} e Event object
28080
+   * @param {Boolean} isRight
28081
+   * @return {Number}
28082
+   */
28083
+  getUpCursorOffset: function(e, isRight) {
28084
+    var selectionProp = this._getSelectionForOffset(e, isRight),
28085
+        cursorLocation = this.get2DCursorLocation(selectionProp),
28086
+        lineIndex = cursorLocation.lineIndex;
28087
+    if (lineIndex === 0 || e.metaKey || e.keyCode === 33) {
28088
+      // if on first line, up cursor goes to start of line
28089
+      return -selectionProp;
28090
+    }
28091
+    var charIndex = cursorLocation.charIndex,
28092
+        widthBeforeCursor = this._getWidthBeforeCursor(lineIndex, charIndex),
28093
+        indexOnOtherLine = this._getIndexOnLine(lineIndex - 1, widthBeforeCursor),
28094
+        textBeforeCursor = this._textLines[lineIndex].slice(0, charIndex);
28095
+    // return a negative offset
28096
+    return -this._textLines[lineIndex - 1].length + indexOnOtherLine - textBeforeCursor.length;
28097
+  },
28098
+
28099
+  /**
28100
+   * for a given width it founds the matching character.
28101
+   * @private
28102
+   */
28103
+  _getIndexOnLine: function(lineIndex, width) {
28104
+
28105
+    var line = this._textLines[lineIndex],
28106
+        lineLeftOffset = this._getLineLeftOffset(lineIndex),
28107
+        widthOfCharsOnLine = lineLeftOffset,
28108
+        indexOnLine = 0, charWidth, foundMatch;
28109
+
28110
+    for (var j = 0, jlen = line.length; j < jlen; j++) {
28111
+      charWidth = this.__charBounds[lineIndex][j].width;
28112
+      widthOfCharsOnLine += charWidth;
28113
+      if (widthOfCharsOnLine > width) {
28114
+        foundMatch = true;
28115
+        var leftEdge = widthOfCharsOnLine - charWidth,
28116
+            rightEdge = widthOfCharsOnLine,
28117
+            offsetFromLeftEdge = Math.abs(leftEdge - width),
28118
+            offsetFromRightEdge = Math.abs(rightEdge - width);
28119
+
28120
+        indexOnLine = offsetFromRightEdge < offsetFromLeftEdge ? j : (j - 1);
28121
+        break;
28122
+      }
28123
+    }
28124
+
28125
+    // reached end
28126
+    if (!foundMatch) {
28127
+      indexOnLine = line.length - 1;
28128
+    }
28129
+
28130
+    return indexOnLine;
28131
+  },
28132
+
28133
+
28134
+  /**
28135
+   * Moves cursor down
28136
+   * @param {Event} e Event object
28137
+   */
28138
+  moveCursorDown: function(e) {
28139
+    if (this.selectionStart >= this._text.length && this.selectionEnd >= this._text.length) {
28140
+      return;
28141
+    }
28142
+    this._moveCursorUpOrDown('Down', e);
28143
+  },
28144
+
28145
+  /**
28146
+   * Moves cursor up
28147
+   * @param {Event} e Event object
28148
+   */
28149
+  moveCursorUp: function(e) {
28150
+    if (this.selectionStart === 0 && this.selectionEnd === 0) {
28151
+      return;
28152
+    }
28153
+    this._moveCursorUpOrDown('Up', e);
28154
+  },
28155
+
28156
+  /**
28157
+   * Moves cursor up or down, fires the events
28158
+   * @param {String} direction 'Up' or 'Down'
28159
+   * @param {Event} e Event object
28160
+   */
28161
+  _moveCursorUpOrDown: function(direction, e) {
28162
+    // getUpCursorOffset
28163
+    // getDownCursorOffset
28164
+    var action = 'get' + direction + 'CursorOffset',
28165
+        offset = this[action](e, this._selectionDirection === 'right');
28166
+    if (e.shiftKey) {
28167
+      this.moveCursorWithShift(offset);
28168
+    }
28169
+    else {
28170
+      this.moveCursorWithoutShift(offset);
28171
+    }
28172
+    if (offset !== 0) {
28173
+      this.setSelectionInBoundaries();
28174
+      this.abortCursorAnimation();
28175
+      this._currentCursorOpacity = 1;
28176
+      this.initDelayedCursor();
28177
+      this._fireSelectionChanged();
28178
+      this._updateTextarea();
28179
+    }
28180
+  },
28181
+
28182
+  /**
28183
+   * Moves cursor with shift
28184
+   * @param {Number} offset
28185
+   */
28186
+  moveCursorWithShift: function(offset) {
28187
+    var newSelection = this._selectionDirection === 'left'
28188
+      ? this.selectionStart + offset
28189
+      : this.selectionEnd + offset;
28190
+    this.setSelectionStartEndWithShift(this.selectionStart, this.selectionEnd, newSelection);
28191
+    return offset !== 0;
28192
+  },
28193
+
28194
+  /**
28195
+   * Moves cursor up without shift
28196
+   * @param {Number} offset
28197
+   */
28198
+  moveCursorWithoutShift: function(offset) {
28199
+    if (offset < 0) {
28200
+      this.selectionStart += offset;
28201
+      this.selectionEnd = this.selectionStart;
28202
+    }
28203
+    else {
28204
+      this.selectionEnd += offset;
28205
+      this.selectionStart = this.selectionEnd;
28206
+    }
28207
+    return offset !== 0;
28208
+  },
28209
+
28210
+  /**
28211
+   * Moves cursor left
28212
+   * @param {Event} e Event object
28213
+   */
28214
+  moveCursorLeft: function(e) {
28215
+    if (this.selectionStart === 0 && this.selectionEnd === 0) {
28216
+      return;
28217
+    }
28218
+    this._moveCursorLeftOrRight('Left', e);
28219
+  },
28220
+
28221
+  /**
28222
+   * @private
28223
+   * @return {Boolean} true if a change happened
28224
+   */
28225
+  _move: function(e, prop, direction) {
28226
+    var newValue;
28227
+    if (e.altKey) {
28228
+      newValue = this['findWordBoundary' + direction](this[prop]);
28229
+    }
28230
+    else if (e.metaKey || e.keyCode === 35 ||  e.keyCode === 36 ) {
28231
+      newValue = this['findLineBoundary' + direction](this[prop]);
28232
+    }
28233
+    else {
28234
+      this[prop] += direction === 'Left' ? -1 : 1;
28235
+      return true;
28236
+    }
28237
+    if (typeof newValue !== undefined && this[prop] !== newValue) {
28238
+      this[prop] = newValue;
28239
+      return true;
28240
+    }
28241
+  },
28242
+
28243
+  /**
28244
+   * @private
28245
+   */
28246
+  _moveLeft: function(e, prop) {
28247
+    return this._move(e, prop, 'Left');
28248
+  },
28249
+
28250
+  /**
28251
+   * @private
28252
+   */
28253
+  _moveRight: function(e, prop) {
28254
+    return this._move(e, prop, 'Right');
28255
+  },
28256
+
28257
+  /**
28258
+   * Moves cursor left without keeping selection
28259
+   * @param {Event} e
28260
+   */
28261
+  moveCursorLeftWithoutShift: function(e) {
28262
+    var change = true;
28263
+    this._selectionDirection = 'left';
28264
+
28265
+    // only move cursor when there is no selection,
28266
+    // otherwise we discard it, and leave cursor on same place
28267
+    if (this.selectionEnd === this.selectionStart && this.selectionStart !== 0) {
28268
+      change = this._moveLeft(e, 'selectionStart');
28269
+
28270
+    }
28271
+    this.selectionEnd = this.selectionStart;
28272
+    return change;
28273
+  },
28274
+
28275
+  /**
28276
+   * Moves cursor left while keeping selection
28277
+   * @param {Event} e
28278
+   */
28279
+  moveCursorLeftWithShift: function(e) {
28280
+    if (this._selectionDirection === 'right' && this.selectionStart !== this.selectionEnd) {
28281
+      return this._moveLeft(e, 'selectionEnd');
28282
+    }
28283
+    else if (this.selectionStart !== 0){
28284
+      this._selectionDirection = 'left';
28285
+      return this._moveLeft(e, 'selectionStart');
28286
+    }
28287
+  },
28288
+
28289
+  /**
28290
+   * Moves cursor right
28291
+   * @param {Event} e Event object
28292
+   */
28293
+  moveCursorRight: function(e) {
28294
+    if (this.selectionStart >= this._text.length && this.selectionEnd >= this._text.length) {
28295
+      return;
28296
+    }
28297
+    this._moveCursorLeftOrRight('Right', e);
28298
+  },
28299
+
28300
+  /**
28301
+   * Moves cursor right or Left, fires event
28302
+   * @param {String} direction 'Left', 'Right'
28303
+   * @param {Event} e Event object
28304
+   */
28305
+  _moveCursorLeftOrRight: function(direction, e) {
28306
+    var actionName = 'moveCursor' + direction + 'With';
28307
+    this._currentCursorOpacity = 1;
28308
+
28309
+    if (e.shiftKey) {
28310
+      actionName += 'Shift';
28311
+    }
28312
+    else {
28313
+      actionName += 'outShift';
28314
+    }
28315
+    if (this[actionName](e)) {
28316
+      this.abortCursorAnimation();
28317
+      this.initDelayedCursor();
28318
+      this._fireSelectionChanged();
28319
+      this._updateTextarea();
28320
+    }
28321
+  },
28322
+
28323
+  /**
28324
+   * Moves cursor right while keeping selection
28325
+   * @param {Event} e
28326
+   */
28327
+  moveCursorRightWithShift: function(e) {
28328
+    if (this._selectionDirection === 'left' && this.selectionStart !== this.selectionEnd) {
28329
+      return this._moveRight(e, 'selectionStart');
28330
+    }
28331
+    else if (this.selectionEnd !== this._text.length) {
28332
+      this._selectionDirection = 'right';
28333
+      return this._moveRight(e, 'selectionEnd');
28334
+    }
28335
+  },
28336
+
28337
+  /**
28338
+   * Moves cursor right without keeping selection
28339
+   * @param {Event} e Event object
28340
+   */
28341
+  moveCursorRightWithoutShift: function(e) {
28342
+    var changed = true;
28343
+    this._selectionDirection = 'right';
28344
+
28345
+    if (this.selectionStart === this.selectionEnd) {
28346
+      changed = this._moveRight(e, 'selectionStart');
28347
+      this.selectionEnd = this.selectionStart;
28348
+    }
28349
+    else {
28350
+      this.selectionStart = this.selectionEnd;
28351
+    }
28352
+    return changed;
28353
+  },
28354
+
28355
+  /**
28356
+   * Removes characters from start/end
28357
+   * start/end ar per grapheme position in _text array.
28358
+   *
28359
+   * @param {Number} start
28360
+   * @param {Number} end default to start + 1
28361
+   */
28362
+  removeChars: function(start, end) {
28363
+    if (typeof end === 'undefined') {
28364
+      end = start + 1;
28365
+    }
28366
+    this.removeStyleFromTo(start, end);
28367
+    this._text.splice(start, end - start);
28368
+    this.text = this._text.join('');
28369
+    this.set('dirty', true);
28370
+    if (this._shouldClearDimensionCache()) {
28371
+      this.initDimensions();
28372
+      this.setCoords();
28373
+    }
28374
+    this._removeExtraneousStyles();
28375
+  },
28376
+
28377
+  /**
28378
+   * insert characters at start position, before start position.
28379
+   * start  equal 1 it means the text get inserted between actual grapheme 0 and 1
28380
+   * if style array is provided, it must be as the same length of text in graphemes
28381
+   * if end is provided and is bigger than start, old text is replaced.
28382
+   * start/end ar per grapheme position in _text array.
28383
+   *
28384
+   * @param {String} text text to insert
28385
+   * @param {Array} style array of style objects
28386
+   * @param {Number} start
28387
+   * @param {Number} end default to start + 1
28388
+   */
28389
+  insertChars: function(text, style, start, end) {
28390
+    if (typeof end === 'undefined') {
28391
+      end = start;
28392
+    }
28393
+    if (end > start) {
28394
+      this.removeStyleFromTo(start, end);
28395
+    }
28396
+    var graphemes = fabric.util.string.graphemeSplit(text);
28397
+    this.insertNewStyleBlock(graphemes, start, style);
28398
+    this._text = [].concat(this._text.slice(0, start), graphemes, this._text.slice(end));
28399
+    this.text = this._text.join('');
28400
+    this.set('dirty', true);
28401
+    if (this._shouldClearDimensionCache()) {
28402
+      this.initDimensions();
28403
+      this.setCoords();
28404
+    }
28405
+    this._removeExtraneousStyles();
28406
+  },
28407
+
28408
+});
28409
+
28410
+
28411
+/* _TO_SVG_START_ */
28412
+(function() {
28413
+  var toFixed = fabric.util.toFixed,
28414
+      multipleSpacesRegex = /  +/g;
28415
+
28416
+  fabric.util.object.extend(fabric.Text.prototype, /** @lends fabric.Text.prototype */ {
28417
+
28418
+    /**
28419
+     * Returns SVG representation of an instance
28420
+     * @param {Function} [reviver] Method for further parsing of svg representation.
28421
+     * @return {String} svg representation of an instance
28422
+     */
28423
+    toSVG: function(reviver) {
28424
+      var offsets = this._getSVGLeftTopOffsets(),
28425
+          textAndBg = this._getSVGTextAndBg(offsets.textTop, offsets.textLeft),
28426
+          internalMarkup = this._wrapSVGTextAndBg(textAndBg);
28427
+      return this._createBaseSVGMarkup(
28428
+        internalMarkup, { reviver: reviver, noStyle: true, withShadow: true });
28429
+    },
28430
+
28431
+    /**
28432
+     * @private
28433
+     */
28434
+    _getSVGLeftTopOffsets: function() {
28435
+      return {
28436
+        textLeft: -this.width / 2,
28437
+        textTop: -this.height / 2,
28438
+        lineTop: this.getHeightOfLine(0)
28439
+      };
28440
+    },
28441
+
28442
+    /**
28443
+     * @private
28444
+     */
28445
+    _wrapSVGTextAndBg: function(textAndBg) {
28446
+      var noShadow = true,
28447
+          textDecoration = this.getSvgTextDecoration(this);
28448
+      return [
28449
+        textAndBg.textBgRects.join(''),
28450
+        '\t\t<text xml:space="preserve" ',
28451
+        (this.fontFamily ? 'font-family="' + this.fontFamily.replace(/"/g, '\'') + '" ' : ''),
28452
+        (this.fontSize ? 'font-size="' + this.fontSize + '" ' : ''),
28453
+        (this.fontStyle ? 'font-style="' + this.fontStyle + '" ' : ''),
28454
+        (this.fontWeight ? 'font-weight="' + this.fontWeight + '" ' : ''),
28455
+        (textDecoration ? 'text-decoration="' + textDecoration + '" ' : ''),
28456
+        'style="', this.getSvgStyles(noShadow), '"', this.addPaintOrder(), ' >',
28457
+        textAndBg.textSpans.join(''),
28458
+        '</text>\n'
28459
+      ];
28460
+    },
28461
+
28462
+    /**
28463
+     * @private
28464
+     * @param {Number} textTopOffset Text top offset
28465
+     * @param {Number} textLeftOffset Text left offset
28466
+     * @return {Object}
28467
+     */
28468
+    _getSVGTextAndBg: function(textTopOffset, textLeftOffset) {
28469
+      var textSpans = [],
28470
+          textBgRects = [],
28471
+          height = textTopOffset, lineOffset;
28472
+      // bounding-box background
28473
+      this._setSVGBg(textBgRects);
28474
+
28475
+      // text and text-background
28476
+      for (var i = 0, len = this._textLines.length; i < len; i++) {
28477
+        lineOffset = this._getLineLeftOffset(i);
28478
+        if (this.textBackgroundColor || this.styleHas('textBackgroundColor', i)) {
28479
+          this._setSVGTextLineBg(textBgRects, i, textLeftOffset + lineOffset, height);
28480
+        }
28481
+        this._setSVGTextLineText(textSpans, i, textLeftOffset + lineOffset, height);
28482
+        height += this.getHeightOfLine(i);
28483
+      }
28484
+
28485
+      return {
28486
+        textSpans: textSpans,
28487
+        textBgRects: textBgRects
28488
+      };
28489
+    },
28490
+
28491
+    /**
28492
+     * @private
28493
+     */
28494
+    _createTextCharSpan: function(_char, styleDecl, left, top) {
28495
+      var shouldUseWhitespace = _char !== _char.trim() || _char.match(multipleSpacesRegex),
28496
+          styleProps = this.getSvgSpanStyles(styleDecl, shouldUseWhitespace),
28497
+          fillStyles = styleProps ? 'style="' + styleProps + '"' : '',
28498
+          dy = styleDecl.deltaY, dySpan = '',
28499
+          NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
28500
+      if (dy) {
28501
+        dySpan = ' dy="' + toFixed(dy, NUM_FRACTION_DIGITS) + '" ';
28502
+      }
28503
+      return [
28504
+        '<tspan x="', toFixed(left, NUM_FRACTION_DIGITS), '" y="',
28505
+        toFixed(top, NUM_FRACTION_DIGITS), '" ', dySpan,
28506
+        fillStyles, '>',
28507
+        fabric.util.string.escapeXml(_char),
28508
+        '</tspan>'
28509
+      ].join('');
28510
+    },
28511
+
28512
+    _setSVGTextLineText: function(textSpans, lineIndex, textLeftOffset, textTopOffset) {
28513
+      // set proper line offset
28514
+      var lineHeight = this.getHeightOfLine(lineIndex),
28515
+          isJustify = this.textAlign.indexOf('justify') !== -1,
28516
+          actualStyle,
28517
+          nextStyle,
28518
+          charsToRender = '',
28519
+          charBox, style,
28520
+          boxWidth = 0,
28521
+          line = this._textLines[lineIndex],
28522
+          timeToRender;
28523
+
28524
+      textTopOffset += lineHeight * (1 - this._fontSizeFraction) / this.lineHeight;
28525
+      for (var i = 0, len = line.length - 1; i <= len; i++) {
28526
+        timeToRender = i === len || this.charSpacing;
28527
+        charsToRender += line[i];
28528
+        charBox = this.__charBounds[lineIndex][i];
28529
+        if (boxWidth === 0) {
28530
+          textLeftOffset += charBox.kernedWidth - charBox.width;
28531
+          boxWidth += charBox.width;
28532
+        }
28533
+        else {
28534
+          boxWidth += charBox.kernedWidth;
28535
+        }
28536
+        if (isJustify && !timeToRender) {
28537
+          if (this._reSpaceAndTab.test(line[i])) {
28538
+            timeToRender = true;
28539
+          }
28540
+        }
28541
+        if (!timeToRender) {
28542
+          // if we have charSpacing, we render char by char
28543
+          actualStyle = actualStyle || this.getCompleteStyleDeclaration(lineIndex, i);
28544
+          nextStyle = this.getCompleteStyleDeclaration(lineIndex, i + 1);
28545
+          timeToRender = this._hasStyleChangedForSvg(actualStyle, nextStyle);
28546
+        }
28547
+        if (timeToRender) {
28548
+          style = this._getStyleDeclaration(lineIndex, i) || { };
28549
+          textSpans.push(this._createTextCharSpan(charsToRender, style, textLeftOffset, textTopOffset));
28550
+          charsToRender = '';
28551
+          actualStyle = nextStyle;
28552
+          textLeftOffset += boxWidth;
28553
+          boxWidth = 0;
28554
+        }
28555
+      }
28556
+    },
28557
+
28558
+    _pushTextBgRect: function(textBgRects, color, left, top, width, height) {
28559
+      var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS;
28560
+      textBgRects.push(
28561
+        '\t\t<rect ',
28562
+        this._getFillAttributes(color),
28563
+        ' x="',
28564
+        toFixed(left, NUM_FRACTION_DIGITS),
28565
+        '" y="',
28566
+        toFixed(top, NUM_FRACTION_DIGITS),
28567
+        '" width="',
28568
+        toFixed(width, NUM_FRACTION_DIGITS),
28569
+        '" height="',
28570
+        toFixed(height, NUM_FRACTION_DIGITS),
28571
+        '"></rect>\n');
28572
+    },
28573
+
28574
+    _setSVGTextLineBg: function(textBgRects, i, leftOffset, textTopOffset) {
28575
+      var line = this._textLines[i],
28576
+          heightOfLine = this.getHeightOfLine(i) / this.lineHeight,
28577
+          boxWidth = 0,
28578
+          boxStart = 0,
28579
+          charBox, currentColor,
28580
+          lastColor = this.getValueOfPropertyAt(i, 0, 'textBackgroundColor');
28581
+      for (var j = 0, jlen = line.length; j < jlen; j++) {
28582
+        charBox = this.__charBounds[i][j];
28583
+        currentColor = this.getValueOfPropertyAt(i, j, 'textBackgroundColor');
28584
+        if (currentColor !== lastColor) {
28585
+          lastColor && this._pushTextBgRect(textBgRects, lastColor, leftOffset + boxStart,
28586
+            textTopOffset, boxWidth, heightOfLine);
28587
+          boxStart = charBox.left;
28588
+          boxWidth = charBox.width;
28589
+          lastColor = currentColor;
28590
+        }
28591
+        else {
28592
+          boxWidth += charBox.kernedWidth;
28593
+        }
28594
+      }
28595
+      currentColor && this._pushTextBgRect(textBgRects, currentColor, leftOffset + boxStart,
28596
+        textTopOffset, boxWidth, heightOfLine);
28597
+    },
28598
+
28599
+    /**
28600
+     * Adobe Illustrator (at least CS5) is unable to render rgba()-based fill values
28601
+     * we work around it by "moving" alpha channel into opacity attribute and setting fill's alpha to 1
28602
+     *
28603
+     * @private
28604
+     * @param {*} value
28605
+     * @return {String}
28606
+     */
28607
+    _getFillAttributes: function(value) {
28608
+      var fillColor = (value && typeof value === 'string') ? new fabric.Color(value) : '';
28609
+      if (!fillColor || !fillColor.getSource() || fillColor.getAlpha() === 1) {
28610
+        return 'fill="' + value + '"';
28611
+      }
28612
+      return 'opacity="' + fillColor.getAlpha() + '" fill="' + fillColor.setAlpha(1).toRgb() + '"';
28613
+    },
28614
+
28615
+    /**
28616
+     * @private
28617
+     */
28618
+    _getSVGLineTopOffset: function(lineIndex) {
28619
+      var lineTopOffset = 0, lastHeight = 0;
28620
+      for (var j = 0; j < lineIndex; j++) {
28621
+        lineTopOffset += this.getHeightOfLine(j);
28622
+      }
28623
+      lastHeight = this.getHeightOfLine(j);
28624
+      return {
28625
+        lineTop: lineTopOffset,
28626
+        offset: (this._fontSizeMult - this._fontSizeFraction) * lastHeight / (this.lineHeight * this._fontSizeMult)
28627
+      };
28628
+    },
28629
+
28630
+    /**
28631
+     * Returns styles-string for svg-export
28632
+     * @param {Boolean} skipShadow a boolean to skip shadow filter output
28633
+     * @return {String}
28634
+     */
28635
+    getSvgStyles: function(skipShadow) {
28636
+      var svgStyle = fabric.Object.prototype.getSvgStyles.call(this, skipShadow);
28637
+      return svgStyle + ' white-space: pre;';
28638
+    },
28639
+  });
28640
+})();
28641
+/* _TO_SVG_END_ */
28642
+
28643
+
28644
+(function(global) {
28645
+
28646
+  'use strict';
28647
+
28648
+  var fabric = global.fabric || (global.fabric = {});
28649
+
28650
+  /**
28651
+   * Textbox class, based on IText, allows the user to resize the text rectangle
28652
+   * and wraps lines automatically. Textboxes have their Y scaling locked, the
28653
+   * user can only change width. Height is adjusted automatically based on the
28654
+   * wrapping of lines.
28655
+   * @class fabric.Textbox
28656
+   * @extends fabric.IText
28657
+   * @mixes fabric.Observable
28658
+   * @return {fabric.Textbox} thisArg
28659
+   * @see {@link fabric.Textbox#initialize} for constructor definition
28660
+   */
28661
+  fabric.Textbox = fabric.util.createClass(fabric.IText, fabric.Observable, {
28662
+
28663
+    /**
28664
+     * Type of an object
28665
+     * @type String
28666
+     * @default
28667
+     */
28668
+    type: 'textbox',
28669
+
28670
+    /**
28671
+     * Minimum width of textbox, in pixels.
28672
+     * @type Number
28673
+     * @default
28674
+     */
28675
+    minWidth: 20,
28676
+
28677
+    /**
28678
+     * Minimum calculated width of a textbox, in pixels.
28679
+     * fixed to 2 so that an empty textbox cannot go to 0
28680
+     * and is still selectable without text.
28681
+     * @type Number
28682
+     * @default
28683
+     */
28684
+    dynamicMinWidth: 2,
28685
+
28686
+    /**
28687
+     * Cached array of text wrapping.
28688
+     * @type Array
28689
+     */
28690
+    __cachedLines: null,
28691
+
28692
+    /**
28693
+     * Override standard Object class values
28694
+     */
28695
+    lockScalingFlip: true,
28696
+
28697
+    /**
28698
+     * Override standard Object class values
28699
+     * Textbox needs this on false
28700
+     */
28701
+    noScaleCache: false,
28702
+
28703
+    /**
28704
+     * Properties which when set cause object to change dimensions
28705
+     * @type Object
28706
+     * @private
28707
+     */
28708
+    _dimensionAffectingProps: fabric.Text.prototype._dimensionAffectingProps.concat('width'),
28709
+
28710
+    /**
28711
+     * Unlike superclass's version of this function, Textbox does not update
28712
+     * its width.
28713
+     * @private
28714
+     * @override
28715
+     */
28716
+    initDimensions: function() {
28717
+      if (this.__skipDimension) {
28718
+        return;
28719
+      }
28720
+      this.isEditing && this.initDelayedCursor();
28721
+      this.clearContextTop();
28722
+      this._clearCache();
28723
+      // clear dynamicMinWidth as it will be different after we re-wrap line
28724
+      this.dynamicMinWidth = 0;
28725
+      // wrap lines
28726
+      this._styleMap = this._generateStyleMap(this._splitText());
28727
+      // if after wrapping, the width is smaller than dynamicMinWidth, change the width and re-wrap
28728
+      if (this.dynamicMinWidth > this.width) {
28729
+        this._set('width', this.dynamicMinWidth);
28730
+      }
28731
+      if (this.textAlign.indexOf('justify') !== -1) {
28732
+        // once text is measured we need to make space fatter to make justified text.
28733
+        this.enlargeSpaces();
28734
+      }
28735
+      // clear cache and re-calculate height
28736
+      this.height = this.calcTextHeight();
28737
+      this.saveState({ propertySet: '_dimensionAffectingProps' });
28738
+    },
28739
+
28740
+    /**
28741
+     * Generate an object that translates the style object so that it is
28742
+     * broken up by visual lines (new lines and automatic wrapping).
28743
+     * The original text styles object is broken up by actual lines (new lines only),
28744
+     * which is only sufficient for Text / IText
28745
+     * @private
28746
+     */
28747
+    _generateStyleMap: function(textInfo) {
28748
+      var realLineCount     = 0,
28749
+          realLineCharCount = 0,
28750
+          charCount         = 0,
28751
+          map               = {};
28752
+
28753
+      for (var i = 0; i < textInfo.graphemeLines.length; i++) {
28754
+        if (textInfo.graphemeText[charCount] === '\n' && i > 0) {
28755
+          realLineCharCount = 0;
28756
+          charCount++;
28757
+          realLineCount++;
28758
+        }
28759
+        else if (this._reSpaceAndTab.test(textInfo.graphemeText[charCount]) && i > 0) {
28760
+          // this case deals with space's that are removed from end of lines when wrapping
28761
+          realLineCharCount++;
28762
+          charCount++;
28763
+        }
28764
+
28765
+        map[i] = { line: realLineCount, offset: realLineCharCount };
28766
+
28767
+        charCount += textInfo.graphemeLines[i].length;
28768
+        realLineCharCount += textInfo.graphemeLines[i].length;
28769
+      }
28770
+
28771
+      return map;
28772
+    },
28773
+
28774
+    /**
28775
+     * Returns true if object has a style property or has it ina specified line
28776
+     * @param {Number} lineIndex
28777
+     * @return {Boolean}
28778
+     */
28779
+    styleHas: function(property, lineIndex) {
28780
+      if (this._styleMap && !this.isWrapping) {
28781
+        var map = this._styleMap[lineIndex];
28782
+        if (map) {
28783
+          lineIndex = map.line;
28784
+        }
28785
+      }
28786
+      return fabric.Text.prototype.styleHas.call(this, property, lineIndex);
28787
+    },
28788
+
28789
+    /**
28790
+     * Returns true if object has no styling or no styling in a line
28791
+     * @param {Number} lineIndex , lineIndex is on wrapped lines.
28792
+     * @return {Boolean}
28793
+     */
28794
+    isEmptyStyles: function(lineIndex) {
28795
+      var offset = 0, nextLineIndex = lineIndex + 1, nextOffset, obj, shouldLimit = false;
28796
+      var map = this._styleMap[lineIndex];
28797
+      var mapNextLine = this._styleMap[lineIndex + 1];
28798
+      if (map) {
28799
+        lineIndex = map.line;
28800
+        offset = map.offset;
28801
+      }
28802
+      if (mapNextLine) {
28803
+        nextLineIndex = mapNextLine.line;
28804
+        shouldLimit = nextLineIndex === lineIndex;
28805
+        nextOffset = mapNextLine.offset;
28806
+      }
28807
+      obj = typeof lineIndex === 'undefined' ? this.styles : { line: this.styles[lineIndex] };
28808
+      for (var p1 in obj) {
28809
+        for (var p2 in obj[p1]) {
28810
+          if (p2 >= offset && (!shouldLimit || p2 < nextOffset)) {
28811
+            // eslint-disable-next-line no-unused-vars
28812
+            for (var p3 in obj[p1][p2]) {
28813
+              return false;
28814
+            }
28815
+          }
28816
+        }
28817
+      }
28818
+      return true;
28819
+    },
28820
+
28821
+    /**
28822
+     * @param {Number} lineIndex
28823
+     * @param {Number} charIndex
28824
+     * @private
28825
+     */
28826
+    _getStyleDeclaration: function(lineIndex, charIndex) {
28827
+      if (this._styleMap && !this.isWrapping) {
28828
+        var map = this._styleMap[lineIndex];
28829
+        if (!map) {
28830
+          return null;
28831
+        }
28832
+        lineIndex = map.line;
28833
+        charIndex = map.offset + charIndex;
28834
+      }
28835
+      return this.callSuper('_getStyleDeclaration', lineIndex, charIndex);
28836
+    },
28837
+
28838
+    /**
28839
+     * @param {Number} lineIndex
28840
+     * @param {Number} charIndex
28841
+     * @param {Object} style
28842
+     * @private
28843
+     */
28844
+    _setStyleDeclaration: function(lineIndex, charIndex, style) {
28845
+      var map = this._styleMap[lineIndex];
28846
+      lineIndex = map.line;
28847
+      charIndex = map.offset + charIndex;
28848
+
28849
+      this.styles[lineIndex][charIndex] = style;
28850
+    },
28851
+
28852
+    /**
28853
+     * @param {Number} lineIndex
28854
+     * @param {Number} charIndex
28855
+     * @private
28856
+     */
28857
+    _deleteStyleDeclaration: function(lineIndex, charIndex) {
28858
+      var map = this._styleMap[lineIndex];
28859
+      lineIndex = map.line;
28860
+      charIndex = map.offset + charIndex;
28861
+
28862
+      delete this.styles[lineIndex][charIndex];
28863
+    },
28864
+
28865
+    /**
28866
+    * probably broken need a fix
28867
+     * @param {Number} lineIndex
28868
+     * @private
28869
+     */
28870
+    _getLineStyle: function(lineIndex) {
28871
+      var map = this._styleMap[lineIndex];
28872
+      return this.styles[map.line];
28873
+    },
28874
+
28875
+    /**
28876
+     * probably broken need a fix
28877
+     * @param {Number} lineIndex
28878
+     * @param {Object} style
28879
+     * @private
28880
+     */
28881
+    _setLineStyle: function(lineIndex, style) {
28882
+      var map = this._styleMap[lineIndex];
28883
+      this.styles[map.line] = style;
28884
+    },
28885
+
28886
+    /**
28887
+     * probably broken need a fix
28888
+     * @param {Number} lineIndex
28889
+     * @private
28890
+     */
28891
+    _deleteLineStyle: function(lineIndex) {
28892
+      var map = this._styleMap[lineIndex];
28893
+      delete this.styles[map.line];
28894
+    },
28895
+
28896
+    /**
28897
+     * Wraps text using the 'width' property of Textbox. First this function
28898
+     * splits text on newlines, so we preserve newlines entered by the user.
28899
+     * Then it wraps each line using the width of the Textbox by calling
28900
+     * _wrapLine().
28901
+     * @param {Array} lines The string array of text that is split into lines
28902
+     * @param {Number} desiredWidth width you want to wrap to
28903
+     * @returns {Array} Array of lines
28904
+     */
28905
+    _wrapText: function(lines, desiredWidth) {
28906
+      var wrapped = [], i;
28907
+      this.isWrapping = true;
28908
+      for (i = 0; i < lines.length; i++) {
28909
+        wrapped = wrapped.concat(this._wrapLine(lines[i], i, desiredWidth));
28910
+      }
28911
+      this.isWrapping = false;
28912
+      return wrapped;
28913
+    },
28914
+
28915
+    /**
28916
+     * Helper function to measure a string of text, given its lineIndex and charIndex offset
28917
+     * it gets called when charBounds are not available yet.
28918
+     * @param {CanvasRenderingContext2D} ctx
28919
+     * @param {String} text
28920
+     * @param {number} lineIndex
28921
+     * @param {number} charOffset
28922
+     * @returns {number}
28923
+     * @private
28924
+     */
28925
+    _measureWord: function(word, lineIndex, charOffset) {
28926
+      var width = 0, prevGrapheme, skipLeft = true;
28927
+      charOffset = charOffset || 0;
28928
+      for (var i = 0, len = word.length; i < len; i++) {
28929
+        var box = this._getGraphemeBox(word[i], lineIndex, i + charOffset, prevGrapheme, skipLeft);
28930
+        width += box.kernedWidth;
28931
+        prevGrapheme = word[i];
28932
+      }
28933
+      return width;
28934
+    },
28935
+
28936
+    /**
28937
+     * Wraps a line of text using the width of the Textbox and a context.
28938
+     * @param {Array} line The grapheme array that represent the line
28939
+     * @param {Number} lineIndex
28940
+     * @param {Number} desiredWidth width you want to wrap the line to
28941
+     * @param {Number} reservedSpace space to remove from wrapping for custom functionalities
28942
+     * @returns {Array} Array of line(s) into which the given text is wrapped
28943
+     * to.
28944
+     */
28945
+    _wrapLine: function(_line, lineIndex, desiredWidth, reservedSpace) {
28946
+      var lineWidth        = 0,
28947
+          graphemeLines    = [],
28948
+          line             = [],
28949
+          // spaces in different languges?
28950
+          words            = _line.split(this._reSpaceAndTab),
28951
+          word             = '',
28952
+          offset           = 0,
28953
+          infix            = ' ',
28954
+          wordWidth        = 0,
28955
+          infixWidth       = 0,
28956
+          largestWordWidth = 0,
28957
+          lineJustStarted = true,
28958
+          additionalSpace = this._getWidthOfCharSpacing(),
28959
+          reservedSpace = reservedSpace || 0;
28960
+
28961
+      desiredWidth -= reservedSpace;
28962
+      for (var i = 0; i < words.length; i++) {
28963
+        // i would avoid resplitting the graphemes
28964
+        word = fabric.util.string.graphemeSplit(words[i]);
28965
+        wordWidth = this._measureWord(word, lineIndex, offset);
28966
+        offset += word.length;
28967
+
28968
+        lineWidth += infixWidth + wordWidth - additionalSpace;
28969
+
28970
+        if (lineWidth >= desiredWidth && !lineJustStarted) {
28971
+          graphemeLines.push(line);
28972
+          line = [];
28973
+          lineWidth = wordWidth;
28974
+          lineJustStarted = true;
28975
+        }
28976
+        else {
28977
+          lineWidth += additionalSpace;
28978
+        }
28979
+
28980
+        if (!lineJustStarted) {
28981
+          line.push(infix);
28982
+        }
28983
+        line = line.concat(word);
28984
+
28985
+        infixWidth = this._measureWord([infix], lineIndex, offset);
28986
+        offset++;
28987
+        lineJustStarted = false;
28988
+        // keep track of largest word
28989
+        if (wordWidth > largestWordWidth) {
28990
+          largestWordWidth = wordWidth;
28991
+        }
28992
+      }
28993
+
28994
+      i && graphemeLines.push(line);
28995
+
28996
+      if (largestWordWidth + reservedSpace > this.dynamicMinWidth) {
28997
+        this.dynamicMinWidth = largestWordWidth - additionalSpace + reservedSpace;
28998
+      }
28999
+
29000
+      return graphemeLines;
29001
+    },
29002
+
29003
+    /**
29004
+     * Detect if the text line is ended with an hard break
29005
+     * text and itext do not have wrapping, return false
29006
+     * @param {Number} lineIndex text to split
29007
+     * @return {Boolean}
29008
+     */
29009
+    isEndOfWrapping: function(lineIndex) {
29010
+      if (!this._styleMap[lineIndex + 1]) {
29011
+        // is last line, return true;
29012
+        return true;
29013
+      }
29014
+      if (this._styleMap[lineIndex + 1].line !== this._styleMap[lineIndex].line) {
29015
+        // this is last line before a line break, return true;
29016
+        return true;
29017
+      }
29018
+      return false;
29019
+    },
29020
+
29021
+    /**
29022
+    * Gets lines of text to render in the Textbox. This function calculates
29023
+    * text wrapping on the fly every time it is called.
29024
+    * @param {String} text text to split
29025
+    * @returns {Array} Array of lines in the Textbox.
29026
+    * @override
29027
+    */
29028
+    _splitTextIntoLines: function(text) {
29029
+      var newText = fabric.Text.prototype._splitTextIntoLines.call(this, text),
29030
+          graphemeLines = this._wrapText(newText.lines, this.width),
29031
+          lines = new Array(graphemeLines.length);
29032
+
29033
+      for (var i = 0; i < graphemeLines.length; i++) {
29034
+        lines[i] = graphemeLines[i].join('');
29035
+      }
29036
+      newText.lines = lines;
29037
+      newText.graphemeLines = graphemeLines;
29038
+      return newText;
29039
+    },
29040
+
29041
+    getMinWidth: function() {
29042
+      return Math.max(this.minWidth, this.dynamicMinWidth);
29043
+    },
29044
+
29045
+    /**
29046
+     * Returns object representation of an instance
29047
+     * @method toObject
29048
+     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
29049
+     * @return {Object} object representation of an instance
29050
+     */
29051
+    toObject: function(propertiesToInclude) {
29052
+      return this.callSuper('toObject', ['minWidth'].concat(propertiesToInclude));
29053
+    }
29054
+  });
29055
+
29056
+  /**
29057
+   * Returns fabric.Textbox instance from an object representation
29058
+   * @static
29059
+   * @memberOf fabric.Textbox
29060
+   * @param {Object} object Object to create an instance from
29061
+   * @param {Function} [callback] Callback to invoke when an fabric.Textbox instance is created
29062
+   */
29063
+  fabric.Textbox.fromObject = function(object, callback) {
29064
+    return fabric.Object._fromObject('Textbox', object, callback, 'text');
29065
+  };
29066
+})(typeof exports !== 'undefined' ? exports : this);
29067
+
29068
+
29069
+(function() {
29070
+
29071
+  /**
29072
+   * Override _setObjectScale and add Textbox specific resizing behavior. Resizing
29073
+   * a Textbox doesn't scale text, it only changes width and makes text wrap automatically.
29074
+   */
29075
+  var setObjectScaleOverridden = fabric.Canvas.prototype._setObjectScale;
29076
+
29077
+  fabric.Canvas.prototype._setObjectScale = function(localMouse, transform,
29078
+    lockScalingX, lockScalingY, by, lockScalingFlip, _dim) {
29079
+
29080
+    var t = transform.target;
29081
+    if (by === 'x' && t instanceof fabric.Textbox) {
29082
+      var tw = t._getTransformedDimensions().x;
29083
+      var w = t.width * (localMouse.x / tw);
29084
+      if (w >= t.getMinWidth()) {
29085
+        t.set('width', w);
29086
+        return true;
29087
+      }
29088
+    }
29089
+    else {
29090
+      return setObjectScaleOverridden.call(fabric.Canvas.prototype, localMouse, transform,
29091
+        lockScalingX, lockScalingY, by, lockScalingFlip, _dim);
29092
+    }
29093
+  };
29094
+
29095
+  fabric.util.object.extend(fabric.Textbox.prototype, /** @lends fabric.IText.prototype */ {
29096
+    /**
29097
+     * @private
29098
+     */
29099
+    _removeExtraneousStyles: function() {
29100
+      for (var prop in this._styleMap) {
29101
+        if (!this._textLines[prop]) {
29102
+          delete this.styles[this._styleMap[prop].line];
29103
+        }
29104
+      }
29105
+    },
29106
+
29107
+  });
29108
+})();
29109
+