fabric-rails 1.0.12 → 1.0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. data/CHANGELOG.md +16 -0
  2. data/README.md +1 -1
  3. data/lib/fabric/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/event.js +1909 -0
  5. data/vendor/assets/javascripts/fabric.js +64 -16464
  6. data/vendor/assets/javascripts/fabric/HEADER.js +31 -0
  7. data/vendor/assets/javascripts/fabric/canvas.class.js +1007 -0
  8. data/vendor/assets/javascripts/fabric/canvas_animation.mixin.js +113 -0
  9. data/vendor/assets/javascripts/fabric/canvas_events.mixin.js +482 -0
  10. data/vendor/assets/javascripts/fabric/canvas_gestures.mixin.js +79 -0
  11. data/vendor/assets/javascripts/fabric/canvas_serialization.mixin.js +311 -0
  12. data/vendor/assets/javascripts/fabric/circle.class.js +182 -0
  13. data/vendor/assets/javascripts/fabric/color.class.js +284 -0
  14. data/vendor/assets/javascripts/fabric/ellipse.class.js +169 -0
  15. data/vendor/assets/javascripts/fabric/freedrawing.class.js +256 -0
  16. data/vendor/assets/javascripts/fabric/gradient.class.js +211 -0
  17. data/vendor/assets/javascripts/fabric/group.class.js +556 -0
  18. data/vendor/assets/javascripts/fabric/image.class.js +418 -0
  19. data/vendor/assets/javascripts/fabric/image_filters.js +809 -0
  20. data/vendor/assets/javascripts/fabric/intersection.class.js +178 -0
  21. data/vendor/assets/javascripts/fabric/line.class.js +188 -0
  22. data/vendor/assets/javascripts/fabric/log.js +26 -0
  23. data/vendor/assets/javascripts/fabric/node.js +149 -0
  24. data/vendor/assets/javascripts/fabric/object.class.js +1068 -0
  25. data/vendor/assets/javascripts/fabric/object_geometry.mixin.js +308 -0
  26. data/vendor/assets/javascripts/fabric/object_interactivity.mixin.js +496 -0
  27. data/vendor/assets/javascripts/fabric/object_origin.mixin.js +207 -0
  28. data/vendor/assets/javascripts/fabric/object_straightening.mixin.js +94 -0
  29. data/vendor/assets/javascripts/fabric/observable.mixin.js +91 -0
  30. data/vendor/assets/javascripts/fabric/parser.js +750 -0
  31. data/vendor/assets/javascripts/fabric/path.class.js +794 -0
  32. data/vendor/assets/javascripts/fabric/path_group.class.js +240 -0
  33. data/vendor/assets/javascripts/fabric/pattern.class.js +69 -0
  34. data/vendor/assets/javascripts/fabric/point.class.js +327 -0
  35. data/vendor/assets/javascripts/fabric/polygon.class.js +184 -0
  36. data/vendor/assets/javascripts/fabric/polyline.class.js +157 -0
  37. data/vendor/assets/javascripts/fabric/rect.class.js +298 -0
  38. data/vendor/assets/javascripts/fabric/scout.js +45 -0
  39. data/vendor/assets/javascripts/fabric/shadow.class.js +70 -0
  40. data/vendor/assets/javascripts/fabric/stateful.js +88 -0
  41. data/vendor/assets/javascripts/fabric/static_canvas.class.js +1298 -0
  42. data/vendor/assets/javascripts/fabric/text.class.js +934 -0
  43. data/vendor/assets/javascripts/fabric/triangle.class.js +108 -0
  44. data/vendor/assets/javascripts/fabric/util/anim_ease.js +360 -0
  45. data/vendor/assets/javascripts/fabric/util/dom_event.js +237 -0
  46. data/vendor/assets/javascripts/fabric/util/dom_misc.js +245 -0
  47. data/vendor/assets/javascripts/fabric/util/dom_request.js +72 -0
  48. data/vendor/assets/javascripts/fabric/util/dom_style.js +71 -0
  49. data/vendor/assets/javascripts/fabric/util/lang_array.js +250 -0
  50. data/vendor/assets/javascripts/fabric/util/lang_class.js +97 -0
  51. data/vendor/assets/javascripts/fabric/util/lang_function.js +35 -0
  52. data/vendor/assets/javascripts/fabric/util/lang_object.js +34 -0
  53. data/vendor/assets/javascripts/fabric/util/lang_string.js +60 -0
  54. data/vendor/assets/javascripts/fabric/util/misc.js +406 -0
  55. data/vendor/assets/javascripts/json2.js +491 -0
  56. metadata +53 -2
@@ -0,0 +1,31 @@
1
+ /*! Fabric.js Copyright 2008-2013, Printio (Juriy Zaytsev, Maxim Chernyak) */
2
+
3
+ var fabric = fabric || { version: "1.0.12" };
4
+
5
+ if (typeof exports !== 'undefined') {
6
+ exports.fabric = fabric;
7
+ }
8
+
9
+ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
10
+ fabric.document = document;
11
+ fabric.window = window;
12
+ }
13
+ else {
14
+ // assume we're running under node.js when document/window are not present
15
+ fabric.document = require("jsdom").jsdom("<!DOCTYPE html><html><head></head><body></body></html>");
16
+ fabric.window = fabric.document.createWindow();
17
+ }
18
+
19
+ /**
20
+ * True when in environment that supports touch events
21
+ * @property isTouchSupported
22
+ * @type boolean
23
+ */
24
+ fabric.isTouchSupported = "ontouchstart" in fabric.document.documentElement;
25
+
26
+ /**
27
+ * True when in environment that's probably Node.js
28
+ * @property isLikelyNode
29
+ * @type boolean
30
+ */
31
+ fabric.isLikelyNode = typeof Buffer !== 'undefined' && typeof window === 'undefined';
@@ -0,0 +1,1007 @@
1
+ (function() {
2
+
3
+ var extend = fabric.util.object.extend,
4
+ getPointer = fabric.util.getPointer,
5
+ degreesToRadians = fabric.util.degreesToRadians,
6
+ radiansToDegrees = fabric.util.radiansToDegrees,
7
+ atan2 = Math.atan2,
8
+ abs = Math.abs,
9
+ min = Math.min,
10
+ max = Math.max,
11
+
12
+ STROKE_OFFSET = 0.5;
13
+
14
+ /**
15
+ * Canvas class
16
+ * @class fabric.Canvas
17
+ * @constructor
18
+ * @extends fabric.StaticCanvas
19
+ * @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
20
+ * @param {Object} [options] Options object
21
+ */
22
+ fabric.Canvas = function(el, options) {
23
+ options || (options = { });
24
+
25
+ this._initStatic(el, options);
26
+ this._initInteractive();
27
+ this._createCacheCanvas();
28
+
29
+ fabric.Canvas.activeInstance = this;
30
+ };
31
+
32
+ function ProtoProxy(){ }
33
+ ProtoProxy.prototype = fabric.StaticCanvas.prototype;
34
+ fabric.Canvas.prototype = new ProtoProxy();
35
+
36
+ var InteractiveMethods = /** @scope fabric.Canvas.prototype */ {
37
+
38
+ /**
39
+ * When true, objects can be transformed by one side (unproportionally)
40
+ * @property
41
+ * @type Boolean
42
+ */
43
+ uniScaleTransform: false,
44
+
45
+ /**
46
+ * When true, objects use center point as the origin of transformation
47
+ * @property
48
+ * @type Boolean
49
+ */
50
+ centerTransform: false,
51
+
52
+ /**
53
+ * Indicates that canvas is interactive. This property should not be changed.
54
+ * @property
55
+ * @type Boolean
56
+ */
57
+ interactive: true,
58
+
59
+ /**
60
+ * Indicates whether group selection should be enabled
61
+ * @property
62
+ * @type Boolean
63
+ */
64
+ selection: true,
65
+
66
+ /**
67
+ * Color of selection
68
+ * @property
69
+ * @type String
70
+ */
71
+ selectionColor: 'rgba(100, 100, 255, 0.3)', // blue
72
+
73
+ /**
74
+ * Default dash array pattern
75
+ * If not empty the selection border is dashed
76
+ * @property
77
+ * @type Array
78
+ */
79
+ selectionDashArray: [ ],
80
+
81
+ /**
82
+ * Color of the border of selection (usually slightly darker than color of selection itself)
83
+ * @property
84
+ * @type String
85
+ */
86
+ selectionBorderColor: 'rgba(255, 255, 255, 0.3)',
87
+
88
+ /**
89
+ * Width of a line used in object/group selection
90
+ * @property
91
+ * @type Number
92
+ */
93
+ selectionLineWidth: 1,
94
+
95
+ /**
96
+ * Color of the line used in free drawing mode
97
+ * @property
98
+ * @type String
99
+ */
100
+ freeDrawingColor: 'rgb(0, 0, 0)',
101
+
102
+ /**
103
+ * Width of a line used in free drawing mode
104
+ * @property
105
+ * @type Number
106
+ */
107
+ freeDrawingLineWidth: 1,
108
+
109
+ /**
110
+ * Default cursor value used when hovering over an object on canvas
111
+ * @property
112
+ * @type String
113
+ */
114
+ hoverCursor: 'move',
115
+
116
+ /**
117
+ * Default cursor value used when moving an object on canvas
118
+ * @property
119
+ * @type String
120
+ */
121
+ moveCursor: 'move',
122
+
123
+ /**
124
+ * Default cursor value used for the entire canvas
125
+ * @property
126
+ * @type String
127
+ */
128
+ defaultCursor: 'default',
129
+
130
+ /**
131
+ * Cursor value used during free drawing
132
+ * @property
133
+ * @type String
134
+ */
135
+ freeDrawingCursor: 'crosshair',
136
+
137
+ /**
138
+ * Cursor value used for rotation point
139
+ * @property
140
+ * @type String
141
+ */
142
+ rotationCursor: 'crosshair',
143
+
144
+ /**
145
+ * Default element class that's given to wrapper (div) element of canvas
146
+ * @property
147
+ * @type String
148
+ */
149
+ containerClass: 'canvas-container',
150
+
151
+ /**
152
+ * When true, object detection happens on per-pixel basis rather than on per-bounding-box
153
+ * @property
154
+ * @type Boolean
155
+ */
156
+ perPixelTargetFind: false,
157
+
158
+ /**
159
+ * Number of pixels around target pixel to tolerate (consider active) during object detection
160
+ * @property
161
+ * @type Number
162
+ */
163
+ targetFindTolerance: 0,
164
+
165
+ /**
166
+ * @method _initInteractive
167
+ * @private
168
+ */
169
+ _initInteractive: function() {
170
+ this._currentTransform = null;
171
+ this._groupSelector = null;
172
+ this.freeDrawing = fabric.FreeDrawing && new fabric.FreeDrawing(this);
173
+ this._initWrapperElement();
174
+ this._createUpperCanvas();
175
+ this._initEvents();
176
+ this.calcOffset();
177
+ },
178
+
179
+ /**
180
+ * Resets the current transform to its original values and chooses the type of resizing based on the event
181
+ * @method _resetCurrentTransform
182
+ * @param e {Event} Event object fired on mousemove
183
+ */
184
+ _resetCurrentTransform: function(e) {
185
+ var t = this._currentTransform;
186
+
187
+ t.target.set('scaleX', t.original.scaleX);
188
+ t.target.set('scaleY', t.original.scaleY);
189
+ t.target.set('left', t.original.left);
190
+ t.target.set('top', t.original.top);
191
+
192
+ if (e.altKey || this.centerTransform) {
193
+ if (t.originX !== 'center') {
194
+ if (t.originX === 'right') {
195
+ t.mouseXSign = -1;
196
+ }
197
+ else {
198
+ t.mouseXSign = 1;
199
+ }
200
+ }
201
+ if (t.originY !== 'center') {
202
+ if (t.originY === 'bottom') {
203
+ t.mouseYSign = -1;
204
+ }
205
+ else {
206
+ t.mouseYSign = 1;
207
+ }
208
+ }
209
+
210
+ t.originX = 'center';
211
+ t.originY = 'center';
212
+ }
213
+ else {
214
+ t.originX = t.original.originX;
215
+ t.originY = t.original.originY;
216
+ }
217
+ },
218
+
219
+ /**
220
+ * Applies one implementation of 'point inside polygon' algorithm
221
+ * @method containsPoint
222
+ * @param e { Event } event object
223
+ * @param target { fabric.Object } object to test against
224
+ * @return {Boolean} true if point contains within area of given object
225
+ */
226
+ containsPoint: function (e, target) {
227
+ var pointer = this.getPointer(e),
228
+ xy = this._normalizePointer(target, pointer),
229
+ x = xy.x,
230
+ y = xy.y;
231
+
232
+ // http://www.geog.ubc.ca/courses/klink/gis.notes/ncgia/u32.html
233
+ // http://idav.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html
234
+
235
+ // we iterate through each object. If target found, return it.
236
+ var iLines = target._getImageLines(target.oCoords),
237
+ xpoints = target._findCrossPoints(x, y, iLines);
238
+
239
+ // if xcount is odd then we clicked inside the object
240
+ // For the specific case of square images xcount === 1 in all true cases
241
+ if ((xpoints && xpoints % 2 === 1) || target._findTargetCorner(e, this._offset)) {
242
+ return true;
243
+ }
244
+ return false;
245
+ },
246
+
247
+ /**
248
+ * @private
249
+ * @method _normalizePointer
250
+ */
251
+ _normalizePointer: function (object, pointer) {
252
+
253
+ var activeGroup = this.getActiveGroup(),
254
+ x = pointer.x,
255
+ y = pointer.y;
256
+
257
+ var isObjectInGroup = (
258
+ activeGroup &&
259
+ object.type !== 'group' &&
260
+ activeGroup.contains(object)
261
+ );
262
+
263
+ if (isObjectInGroup) {
264
+ x -= activeGroup.left;
265
+ y -= activeGroup.top;
266
+ }
267
+ return { x: x, y: y };
268
+ },
269
+
270
+ /**
271
+ * @private
272
+ * @method _isTargetTransparent
273
+ */
274
+ _isTargetTransparent: function (target, x, y) {
275
+ var cacheContext = this.contextCache;
276
+
277
+ var hasBorders = target.hasBorders, transparentCorners = target.transparentCorners;
278
+ target.hasBorders = target.transparentCorners = false;
279
+
280
+ this._draw(cacheContext, target);
281
+
282
+ target.hasBorders = hasBorders;
283
+ target.transparentCorners = transparentCorners;
284
+
285
+ // If tolerance is > 0 adjust start coords to take into account. If moves off Canvas fix to 0
286
+ if (this.targetFindTolerance > 0) {
287
+ if (x > this.targetFindTolerance) {
288
+ x -= this.targetFindTolerance;
289
+ }
290
+ else {
291
+ x = 0;
292
+ }
293
+ if (y > this.targetFindTolerance) {
294
+ y -= this.targetFindTolerance;
295
+ }
296
+ else {
297
+ y = 0;
298
+ }
299
+ }
300
+
301
+ var isTransparent = true;
302
+ var imageData = cacheContext.getImageData(
303
+ x, y, (this.targetFindTolerance * 2) || 1, (this.targetFindTolerance * 2) || 1);
304
+
305
+ // Split image data - for tolerance > 1, pixelDataSize = 4;
306
+ for (var i = 3; i < imageData.data.length; i += 4) {
307
+ var temp = imageData.data[i];
308
+ isTransparent = temp <= 0;
309
+ if (isTransparent === false) break; //Stop if colour found
310
+ }
311
+
312
+ imageData = null;
313
+ this.clearContext(cacheContext);
314
+ return isTransparent;
315
+ },
316
+
317
+ /**
318
+ * @private
319
+ * @method _shouldClearSelection
320
+ */
321
+ _shouldClearSelection: function (e) {
322
+ var target = this.findTarget(e),
323
+ activeGroup = this.getActiveGroup();
324
+ return (
325
+ !target || (
326
+ target &&
327
+ activeGroup &&
328
+ !activeGroup.contains(target) &&
329
+ activeGroup !== target &&
330
+ !e.shiftKey
331
+ )
332
+ );
333
+ },
334
+
335
+ /**
336
+ * @private
337
+ * @method _setupCurrentTransform
338
+ */
339
+ _setupCurrentTransform: function (e, target) {
340
+ var action = 'drag',
341
+ corner,
342
+ pointer = getPointer(e, target.canvas.upperCanvasEl);
343
+
344
+ corner = target._findTargetCorner(e, this._offset);
345
+ if (corner) {
346
+ action = (corner === 'ml' || corner === 'mr')
347
+ ? 'scaleX'
348
+ : (corner === 'mt' || corner === 'mb')
349
+ ? 'scaleY'
350
+ : corner === 'mtr'
351
+ ? 'rotate'
352
+ : 'scale';
353
+ }
354
+
355
+ var originX = "center", originY = "center";
356
+
357
+ if (corner === 'ml' || corner === 'tl' || corner === 'bl') {
358
+ originX = "right";
359
+ }
360
+ else if (corner === 'mr' || corner === 'tr' || corner === 'br') {
361
+ originX = "left";
362
+ }
363
+
364
+ if (corner === 'tl' || corner === 'mt' || corner === 'tr') {
365
+ originY = "bottom";
366
+ }
367
+ else if (corner === 'bl' || corner === 'mb' || corner === 'br') {
368
+ originY = "top";
369
+ }
370
+
371
+ if (corner === 'mtr') {
372
+ originX = 'center';
373
+ originY = 'center';
374
+ }
375
+
376
+ // var center = target.getCenterPoint();
377
+ this._currentTransform = {
378
+ target: target,
379
+ action: action,
380
+ scaleX: target.scaleX,
381
+ scaleY: target.scaleY,
382
+ offsetX: pointer.x - target.left,
383
+ offsetY: pointer.y - target.top,
384
+ originX: originX,
385
+ originY: originY,
386
+ ex: pointer.x,
387
+ ey: pointer.y,
388
+ left: target.left,
389
+ top: target.top,
390
+ theta: degreesToRadians(target.angle),
391
+ width: target.width * target.scaleX,
392
+ mouseXSign: 1,
393
+ mouseYSign: 1
394
+ };
395
+
396
+ this._currentTransform.original = {
397
+ left: target.left,
398
+ top: target.top,
399
+ scaleX: target.scaleX,
400
+ scaleY: target.scaleY,
401
+ originX: originX,
402
+ originY: originY
403
+ };
404
+
405
+ this._resetCurrentTransform(e);
406
+ },
407
+
408
+ /**
409
+ * @method _shouldHandleGroupLogic
410
+ * @param e {Event}
411
+ * @param target {fabric.Object}
412
+ * @return {Boolean}
413
+ */
414
+ _shouldHandleGroupLogic: function(e, target) {
415
+ var activeObject = this.getActiveObject();
416
+ return e.shiftKey &&
417
+ (this.getActiveGroup() || (activeObject && activeObject !== target))
418
+ && this.selection;
419
+ },
420
+
421
+ /**
422
+ * @private
423
+ * @method _handleGroupLogic
424
+ */
425
+ _handleGroupLogic: function (e, target) {
426
+ if (target === this.getActiveGroup()) {
427
+ // if it's a group, find target again, this time skipping group
428
+ target = this.findTarget(e, true);
429
+ // if even object is not found, bail out
430
+ if (!target || target.isType('group')) {
431
+ return;
432
+ }
433
+ }
434
+ var activeGroup = this.getActiveGroup();
435
+ if (activeGroup) {
436
+ if (activeGroup.contains(target)) {
437
+ activeGroup.removeWithUpdate(target);
438
+ this._resetObjectTransform(activeGroup);
439
+ target.setActive(false);
440
+ if (activeGroup.size() === 1) {
441
+ // remove group alltogether if after removal it only contains 1 object
442
+ this.discardActiveGroup();
443
+ }
444
+ }
445
+ else {
446
+ activeGroup.addWithUpdate(target);
447
+ this._resetObjectTransform(activeGroup);
448
+ }
449
+ this.fire('selection:created', { target: activeGroup, e: e });
450
+ activeGroup.setActive(true);
451
+ }
452
+ else {
453
+ // group does not exist
454
+ if (this._activeObject) {
455
+ // only if there's an active object
456
+ if (target !== this._activeObject) {
457
+ // and that object is not the actual target
458
+ var group = new fabric.Group([ this._activeObject, target ]);
459
+ this.setActiveGroup(group);
460
+ activeGroup = this.getActiveGroup();
461
+ }
462
+ }
463
+ // activate target object in any case
464
+ target.setActive(true);
465
+ }
466
+
467
+ if (activeGroup) {
468
+ activeGroup.saveCoords();
469
+ }
470
+ },
471
+
472
+ /**
473
+ * Translates object by "setting" its left/top
474
+ * @method _translateObject
475
+ * @param x {Number} pointer's x coordinate
476
+ * @param y {Number} pointer's y coordinate
477
+ */
478
+ _translateObject: function (x, y) {
479
+ var target = this._currentTransform.target;
480
+
481
+ if (!target.get('lockMovementX')) {
482
+ target.set('left', x - this._currentTransform.offsetX);
483
+ }
484
+ if (!target.get('lockMovementY')) {
485
+ target.set('top', y - this._currentTransform.offsetY);
486
+ }
487
+ },
488
+
489
+ /**
490
+ * Scales object by invoking its scaleX/scaleY methods
491
+ * @method _scaleObject
492
+ * @param x {Number} pointer's x coordinate
493
+ * @param y {Number} pointer's y coordinate
494
+ * @param by {String} Either 'x' or 'y' - specifies dimension constraint by which to scale an object.
495
+ * When not provided, an object is scaled by both dimensions equally
496
+ */
497
+ _scaleObject: function (x, y, by) {
498
+ var t = this._currentTransform,
499
+ offset = this._offset,
500
+ target = t.target;
501
+
502
+ var lockScalingX = target.get('lockScalingX'),
503
+ lockScalingY = target.get('lockScalingY');
504
+
505
+ if (lockScalingX && lockScalingY) return;
506
+
507
+ // Get the constraint point
508
+ var constraintPosition = target.translateToOriginPoint(target.getCenterPoint(), t.originX, t.originY);
509
+ var localMouse = target.toLocalPoint(new fabric.Point(x - offset.left, y - offset.top), t.originX, t.originY);
510
+
511
+ if (t.originX === 'right') {
512
+ localMouse.x *= -1;
513
+ }
514
+ else if (t.originX === 'center') {
515
+ localMouse.x *= t.mouseXSign * 2;
516
+
517
+ if (localMouse.x < 0) {
518
+ t.mouseXSign = -t.mouseXSign;
519
+ }
520
+ }
521
+
522
+ if (t.originY === 'bottom') {
523
+ localMouse.y *= -1;
524
+ }
525
+ else if (t.originY === 'center') {
526
+ localMouse.y *= t.mouseYSign * 2;
527
+
528
+ if (localMouse.y < 0) {
529
+ t.mouseYSign = -t.mouseYSign;
530
+ }
531
+ }
532
+
533
+ // Actually scale the object
534
+ var newScaleX = target.scaleX, newScaleY = target.scaleY;
535
+ if (by === 'equally' && !lockScalingX && !lockScalingY) {
536
+ var dist = localMouse.y + localMouse.x;
537
+ var lastDist = (target.height) * t.original.scaleY +
538
+ (target.width) * t.original.scaleX +
539
+ (target.padding * 2) -
540
+ (target.strokeWidth * 2) + 1 /* additional offset needed probably due to subpixel rendering, and avoids jerk when scaling an object */;
541
+
542
+ // We use t.scaleX/Y instead of target.scaleX/Y because the object may have a min scale and we'll loose the proportions
543
+ newScaleX = t.original.scaleX * dist/lastDist;
544
+ newScaleY = t.original.scaleY * dist/lastDist;
545
+
546
+ target.set('scaleX', newScaleX);
547
+ target.set('scaleY', newScaleY);
548
+ }
549
+ else if (!by) {
550
+ newScaleX = localMouse.x/(target.width+target.padding);
551
+ newScaleY = localMouse.y/(target.height+target.padding);
552
+
553
+ lockScalingX || target.set('scaleX', newScaleX);
554
+ lockScalingY || target.set('scaleY', newScaleY);
555
+ }
556
+ else if (by === 'x' && !target.get('lockUniScaling')) {
557
+ newScaleX = localMouse.x/(target.width+target.padding);
558
+ lockScalingX || target.set('scaleX', newScaleX);
559
+ }
560
+ else if (by === 'y' && !target.get('lockUniScaling')) {
561
+ newScaleY = localMouse.y/(target.height+target.padding);
562
+ lockScalingY || target.set('scaleY', newScaleY);
563
+ }
564
+
565
+ // Check if we flipped
566
+ if (newScaleX < 0)
567
+ {
568
+ if (t.originX === 'left')
569
+ t.originX = 'right';
570
+ else if (t.originX === 'right')
571
+ t.originX = 'left';
572
+ }
573
+
574
+ if (newScaleY < 0)
575
+ {
576
+ if (t.originY === 'top')
577
+ t.originY = 'bottom';
578
+ else if (t.originY === 'bottom')
579
+ t.originY = 'top';
580
+ }
581
+
582
+ // Make sure the constraints apply
583
+ target.setPositionByOrigin(constraintPosition, t.originX, t.originY);
584
+ },
585
+
586
+ /**
587
+ * Rotates object by invoking its rotate method
588
+ * @method _rotateObject
589
+ * @param x {Number} pointer's x coordinate
590
+ * @param y {Number} pointer's y coordinate
591
+ */
592
+ _rotateObject: function (x, y) {
593
+
594
+ var t = this._currentTransform,
595
+ o = this._offset;
596
+
597
+ if (t.target.get('lockRotation')) return;
598
+
599
+ var lastAngle = atan2(t.ey - t.top - o.top, t.ex - t.left - o.left),
600
+ curAngle = atan2(y - t.top - o.top, x - t.left - o.left);
601
+
602
+ t.target.angle = radiansToDegrees(curAngle - lastAngle + t.theta);
603
+ },
604
+
605
+ /**
606
+ * @method _setCursor
607
+ */
608
+ _setCursor: function (value) {
609
+ this.upperCanvasEl.style.cursor = value;
610
+ },
611
+
612
+ /**
613
+ * @private
614
+ * @method _resetObjectTransform:
615
+ */
616
+ _resetObjectTransform: function (target) {
617
+ target.scaleX = 1;
618
+ target.scaleY = 1;
619
+ target.setAngle(0);
620
+ },
621
+
622
+ /**
623
+ * @method _drawSelection
624
+ * @private
625
+ */
626
+ _drawSelection: function () {
627
+ var ctx = this.contextTop,
628
+ groupSelector = this._groupSelector,
629
+ left = groupSelector.left,
630
+ top = groupSelector.top,
631
+ aleft = abs(left),
632
+ atop = abs(top);
633
+
634
+ ctx.fillStyle = this.selectionColor;
635
+
636
+ ctx.fillRect(
637
+ groupSelector.ex - ((left > 0) ? 0 : -left),
638
+ groupSelector.ey - ((top > 0) ? 0 : -top),
639
+ aleft,
640
+ atop
641
+ );
642
+
643
+ ctx.lineWidth = this.selectionLineWidth;
644
+ ctx.strokeStyle = this.selectionBorderColor;
645
+
646
+ // selection border
647
+ if (this.selectionDashArray.length > 1) {
648
+
649
+ var px = groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0: aleft);
650
+ var py = groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0: atop);
651
+
652
+ ctx.beginPath();
653
+
654
+ fabric.util.drawDashedLine(ctx, px, py, px+aleft, py, this.selectionDashArray);
655
+ fabric.util.drawDashedLine(ctx, px, py+atop-1, px+aleft, py+atop-1, this.selectionDashArray);
656
+ fabric.util.drawDashedLine(ctx, px, py, px, py+atop, this.selectionDashArray);
657
+ fabric.util.drawDashedLine(ctx, px+aleft-1, py, px+aleft-1, py+atop, this.selectionDashArray);
658
+
659
+ ctx.closePath();
660
+ ctx.stroke();
661
+ }
662
+ else {
663
+ ctx.strokeRect(
664
+ groupSelector.ex + STROKE_OFFSET - ((left > 0) ? 0 : aleft),
665
+ groupSelector.ey + STROKE_OFFSET - ((top > 0) ? 0 : atop),
666
+ aleft,
667
+ atop
668
+ );
669
+ }
670
+ },
671
+
672
+ /**
673
+ * @private
674
+ * @method _findSelectedObjects
675
+ */
676
+ _findSelectedObjects: function (e) {
677
+ var group = [ ],
678
+ x1 = this._groupSelector.ex,
679
+ y1 = this._groupSelector.ey,
680
+ x2 = x1 + this._groupSelector.left,
681
+ y2 = y1 + this._groupSelector.top,
682
+ currentObject,
683
+ selectionX1Y1 = new fabric.Point(min(x1, x2), min(y1, y2)),
684
+ selectionX2Y2 = new fabric.Point(max(x1, x2), max(y1, y2));
685
+
686
+ for (var i = 0, len = this._objects.length; i < len; ++i) {
687
+ currentObject = this._objects[i];
688
+
689
+ if (!currentObject) continue;
690
+
691
+ if (currentObject.intersectsWithRect(selectionX1Y1, selectionX2Y2) ||
692
+ currentObject.isContainedWithinRect(selectionX1Y1, selectionX2Y2)) {
693
+
694
+ if (this.selection && currentObject.selectable) {
695
+ currentObject.setActive(true);
696
+ group.push(currentObject);
697
+ }
698
+ }
699
+ }
700
+
701
+ // do not create group for 1 element only
702
+ if (group.length === 1) {
703
+ this.setActiveObject(group[0], e);
704
+ }
705
+ else if (group.length > 1) {
706
+ group = new fabric.Group(group);
707
+ this.setActiveGroup(group);
708
+ group.saveCoords();
709
+ this.fire('selection:created', { target: group });
710
+ }
711
+
712
+ this.renderAll();
713
+ },
714
+
715
+ /**
716
+ * Method that determines what object we are clicking on
717
+ * @method findTarget
718
+ * @param {Event} e mouse event
719
+ * @param {Boolean} skipGroup when true, group is skipped and only objects are traversed through
720
+ */
721
+ findTarget: function (e, skipGroup) {
722
+
723
+ var target,
724
+ pointer = this.getPointer(e);
725
+
726
+ if (this.controlsAboveOverlay &&
727
+ this.lastRenderedObjectWithControlsAboveOverlay &&
728
+ this.containsPoint(e, this.lastRenderedObjectWithControlsAboveOverlay) &&
729
+ this.lastRenderedObjectWithControlsAboveOverlay._findTargetCorner(e, this._offset)) {
730
+ target = this.lastRenderedObjectWithControlsAboveOverlay;
731
+ return target;
732
+ }
733
+
734
+ // first check current group (if one exists)
735
+ var activeGroup = this.getActiveGroup();
736
+ if (activeGroup && !skipGroup && this.containsPoint(e, activeGroup)) {
737
+ target = activeGroup;
738
+ return target;
739
+ }
740
+
741
+ // then check all of the objects on canvas
742
+ // Cache all targets where their bounding box contains point.
743
+ var possibleTargets = [];
744
+ for (var i = this._objects.length; i--; ) {
745
+ if (this._objects[i] && this.containsPoint(e, this._objects[i])) {
746
+ if (this.perPixelTargetFind || this._objects[i].perPixelTargetFind) {
747
+ possibleTargets[possibleTargets.length] = this._objects[i];
748
+ }
749
+ else {
750
+ target = this._objects[i];
751
+ this.relatedTarget = target;
752
+ break;
753
+ }
754
+ }
755
+ }
756
+ for (var j = 0, len = possibleTargets.length; j < len; j++) {
757
+ pointer = this.getPointer(e);
758
+ var isTransparent = this._isTargetTransparent(possibleTargets[j], pointer.x, pointer.y);
759
+ if (!isTransparent) {
760
+ target = possibleTargets[j];
761
+ this.relatedTarget = target;
762
+ break;
763
+ }
764
+ }
765
+ if (target && target.selectable) {
766
+ return target;
767
+ }
768
+ },
769
+
770
+ /**
771
+ * Returns pointer coordinates relative to canvas.
772
+ * @method getPointer
773
+ * @param {Event} e
774
+ * @return {Object} object with "x" and "y" number values
775
+ */
776
+ getPointer: function (e) {
777
+ var pointer = getPointer(e, this.upperCanvasEl);
778
+ return {
779
+ x: pointer.x - this._offset.left,
780
+ y: pointer.y - this._offset.top
781
+ };
782
+ },
783
+
784
+ /**
785
+ * @private
786
+ * @method _createUpperCanvas
787
+ * @param {HTMLElement|String} canvasEl Canvas element
788
+ * @throws {CANVAS_INIT_ERROR} If canvas can not be initialized
789
+ */
790
+ _createUpperCanvas: function () {
791
+ this.upperCanvasEl = this._createCanvasElement();
792
+ this.upperCanvasEl.className = 'upper-canvas';
793
+
794
+ this.wrapperEl.appendChild(this.upperCanvasEl);
795
+
796
+ this._applyCanvasStyle(this.upperCanvasEl);
797
+ this.contextTop = this.upperCanvasEl.getContext('2d');
798
+ },
799
+
800
+ /**
801
+ * @private
802
+ * @method _createCacheCanvas
803
+ */
804
+ _createCacheCanvas: function () {
805
+ this.cacheCanvasEl = this._createCanvasElement();
806
+ this.cacheCanvasEl.setAttribute('width', this.width);
807
+ this.cacheCanvasEl.setAttribute('height', this.height);
808
+ this.contextCache = this.cacheCanvasEl.getContext('2d');
809
+ },
810
+
811
+ /**
812
+ * @private
813
+ * @method _initWrapperElement
814
+ * @param {Number} width
815
+ * @param {Number} height
816
+ */
817
+ _initWrapperElement: function () {
818
+ this.wrapperEl = fabric.util.wrapElement(this.lowerCanvasEl, 'div', {
819
+ 'class': this.containerClass
820
+ });
821
+ fabric.util.setStyle(this.wrapperEl, {
822
+ width: this.getWidth() + 'px',
823
+ height: this.getHeight() + 'px',
824
+ position: 'relative'
825
+ });
826
+ fabric.util.makeElementUnselectable(this.wrapperEl);
827
+ },
828
+
829
+ /**
830
+ * @private
831
+ * @method _applyCanvasStyle
832
+ * @param {Element} element
833
+ */
834
+ _applyCanvasStyle: function (element) {
835
+ var width = this.getWidth() || element.width,
836
+ height = this.getHeight() || element.height;
837
+
838
+ fabric.util.setStyle(element, {
839
+ position: 'absolute',
840
+ width: width + 'px',
841
+ height: height + 'px',
842
+ left: 0,
843
+ top: 0
844
+ });
845
+ element.width = width;
846
+ element.height = height;
847
+ fabric.util.makeElementUnselectable(element);
848
+ },
849
+
850
+ /**
851
+ * Returns context of canvas where object selection is drawn
852
+ * @method getSelectionContext
853
+ * @return {CanvasRenderingContext2D}
854
+ */
855
+ getSelectionContext: function() {
856
+ return this.contextTop;
857
+ },
858
+
859
+ /**
860
+ * Returns &lt;canvas> element on which object selection is drawn
861
+ * @method getSelectionElement
862
+ * @return {HTMLCanvasElement}
863
+ */
864
+ getSelectionElement: function () {
865
+ return this.upperCanvasEl;
866
+ },
867
+
868
+ /**
869
+ * Sets given object as active
870
+ * @method setActiveObject
871
+ * @param object {fabric.Object} Object to set as an active one
872
+ * @return {fabric.Canvas} thisArg
873
+ * @chainable
874
+ */
875
+ setActiveObject: function (object, e) {
876
+ if (this._activeObject) {
877
+ this._activeObject.setActive(false);
878
+ }
879
+ this._activeObject = object;
880
+ object.setActive(true);
881
+
882
+ this.renderAll();
883
+
884
+ this.fire('object:selected', { target: object, e: e });
885
+ object.fire('selected', { e: e });
886
+ return this;
887
+ },
888
+
889
+ /**
890
+ * Returns currently active object
891
+ * @method getActiveObject
892
+ * @return {fabric.Object} active object
893
+ */
894
+ getActiveObject: function () {
895
+ return this._activeObject;
896
+ },
897
+
898
+ /**
899
+ * Discards currently active object
900
+ * @method discardActiveObject
901
+ * @return {fabric.Canvas} thisArg
902
+ * @chainable
903
+ */
904
+ discardActiveObject: function () {
905
+ if (this._activeObject) {
906
+ this._activeObject.setActive(false);
907
+ }
908
+ this._activeObject = null;
909
+ return this;
910
+ },
911
+
912
+ /**
913
+ * Sets active group to a speicified one
914
+ * @method setActiveGroup
915
+ * @param {fabric.Group} group Group to set as a current one
916
+ * @return {fabric.Canvas} thisArg
917
+ * @chainable
918
+ */
919
+ setActiveGroup: function (group) {
920
+ this._activeGroup = group;
921
+ if (group) {
922
+ group.canvas = this;
923
+ group.setActive(true);
924
+ }
925
+ return this;
926
+ },
927
+
928
+ /**
929
+ * Returns currently active group
930
+ * @method getActiveGroup
931
+ * @return {fabric.Group} Current group
932
+ */
933
+ getActiveGroup: function () {
934
+ return this._activeGroup;
935
+ },
936
+
937
+ /**
938
+ * Removes currently active group
939
+ * @method discardActiveGroup
940
+ * @return {fabric.Canvas} thisArg
941
+ */
942
+ discardActiveGroup: function () {
943
+ var g = this.getActiveGroup();
944
+ if (g) {
945
+ g.destroy();
946
+ }
947
+ return this.setActiveGroup(null);
948
+ },
949
+
950
+ /**
951
+ * Deactivates all objects by calling their setActive(false)
952
+ * @method deactivateAll
953
+ * @return {fabric.Canvas} thisArg
954
+ */
955
+ deactivateAll: function () {
956
+ var allObjects = this.getObjects(),
957
+ i = 0,
958
+ len = allObjects.length;
959
+ for ( ; i < len; i++) {
960
+ allObjects[i].setActive(false);
961
+ }
962
+ this.discardActiveGroup();
963
+ this.discardActiveObject();
964
+ return this;
965
+ },
966
+
967
+ /**
968
+ * Deactivates all objects and dispatches appropriate events
969
+ * @method deactivateAllWithDispatch
970
+ * @return {fabric.Canvas} thisArg
971
+ */
972
+ deactivateAllWithDispatch: function () {
973
+ var activeObject = this.getActiveGroup() || this.getActiveObject();
974
+ if (activeObject) {
975
+ this.fire('before:selection:cleared', { target: activeObject });
976
+ }
977
+ this.deactivateAll();
978
+ if (activeObject) {
979
+ this.fire('selection:cleared');
980
+ }
981
+ return this;
982
+ }
983
+ };
984
+
985
+ fabric.Canvas.prototype.toString = fabric.StaticCanvas.prototype.toString;
986
+ extend(fabric.Canvas.prototype, InteractiveMethods);
987
+
988
+ // iterating manually to workaround Opera's bug
989
+ // where "prototype" property is enumerable and overrides existing prototype
990
+ for (var prop in fabric.StaticCanvas) {
991
+ if (prop !== 'prototype') {
992
+ fabric.Canvas[prop] = fabric.StaticCanvas[prop];
993
+ }
994
+ }
995
+
996
+ if (fabric.isTouchSupported) {
997
+ fabric.Canvas.prototype._setCursorFromEvent = function() { };
998
+ }
999
+
1000
+ /**
1001
+ * @class fabric.Element
1002
+ * @alias fabric.Canvas
1003
+ * @deprecated Use {@link fabric.Canvas} instead.
1004
+ * @constructor
1005
+ */
1006
+ fabric.Element = fabric.Canvas;
1007
+ })();