fabric-rails 1.0.12 → 1.0.12.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ })();