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,308 @@
1
+ (function() {
2
+
3
+ var degreesToRadians = fabric.util.degreesToRadians;
4
+
5
+ fabric.util.object.extend(fabric.Object.prototype, /** @scope fabric.Object.prototype */ {
6
+
7
+ /**
8
+ * Returns true if object intersects with an area formed by 2 points
9
+ * @method intersectsWithRect
10
+ * @param {Object} selectionTL
11
+ * @param {Object} selectionBR
12
+ * @return {Boolean}
13
+ */
14
+ intersectsWithRect: function(selectionTL, selectionBR) {
15
+ var oCoords = this.oCoords,
16
+ tl = new fabric.Point(oCoords.tl.x, oCoords.tl.y),
17
+ tr = new fabric.Point(oCoords.tr.x, oCoords.tr.y),
18
+ bl = new fabric.Point(oCoords.bl.x, oCoords.bl.y),
19
+ br = new fabric.Point(oCoords.br.x, oCoords.br.y);
20
+
21
+ var intersection = fabric.Intersection.intersectPolygonRectangle(
22
+ [tl, tr, br, bl],
23
+ selectionTL,
24
+ selectionBR
25
+ );
26
+ return intersection.status === 'Intersection';
27
+ },
28
+
29
+ /**
30
+ * Returns true if object intersects with another object
31
+ * @method intersectsWithObject
32
+ * @param {Object} other Object to test
33
+ * @return {Boolean}
34
+ */
35
+ intersectsWithObject: function(other) {
36
+ // extracts coords
37
+ function getCoords(oCoords) {
38
+ return {
39
+ tl: new fabric.Point(oCoords.tl.x, oCoords.tl.y),
40
+ tr: new fabric.Point(oCoords.tr.x, oCoords.tr.y),
41
+ bl: new fabric.Point(oCoords.bl.x, oCoords.bl.y),
42
+ br: new fabric.Point(oCoords.br.x, oCoords.br.y)
43
+ };
44
+ }
45
+ var thisCoords = getCoords(this.oCoords),
46
+ otherCoords = getCoords(other.oCoords);
47
+
48
+ var intersection = fabric.Intersection.intersectPolygonPolygon(
49
+ [thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl],
50
+ [otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl]
51
+ );
52
+
53
+ return intersection.status === 'Intersection';
54
+ },
55
+
56
+ /**
57
+ * Returns true if object is fully contained within area of another object
58
+ * @method isContainedWithinObject
59
+ * @param {Object} other Object to test
60
+ * @return {Boolean}
61
+ */
62
+ isContainedWithinObject: function(other) {
63
+ return this.isContainedWithinRect(other.oCoords.tl, other.oCoords.br);
64
+ },
65
+
66
+ /**
67
+ * Returns true if object is fully contained within area formed by 2 points
68
+ * @method isContainedWithinRect
69
+ * @param {Object} selectionTL
70
+ * @param {Object} selectionBR
71
+ * @return {Boolean}
72
+ */
73
+ isContainedWithinRect: function(selectionTL, selectionBR) {
74
+ var oCoords = this.oCoords,
75
+ tl = new fabric.Point(oCoords.tl.x, oCoords.tl.y),
76
+ tr = new fabric.Point(oCoords.tr.x, oCoords.tr.y),
77
+ bl = new fabric.Point(oCoords.bl.x, oCoords.bl.y);
78
+
79
+ return tl.x > selectionTL.x
80
+ && tr.x < selectionBR.x
81
+ && tl.y > selectionTL.y
82
+ && bl.y < selectionBR.y;
83
+ },
84
+
85
+ /**
86
+ * Returns width of an object's bounding rectangle
87
+ * @deprecated since 1.0.4
88
+ * @method getBoundingRectWidth
89
+ * @return {Number} width value
90
+ */
91
+ getBoundingRectWidth: function() {
92
+ return this.getBoundingRect().width;
93
+ },
94
+
95
+ /**
96
+ * Returns height of an object's bounding rectangle
97
+ * @deprecated since 1.0.4
98
+ * @method getBoundingRectHeight
99
+ * @return {Number} height value
100
+ */
101
+ getBoundingRectHeight: function() {
102
+ return this.getBoundingRect().height;
103
+ },
104
+
105
+ /**
106
+ * Returns coordinates of object's bounding rectangle (left, top, width, height)
107
+ * @method getBoundingRect
108
+ * @return {Object} Object with left, top, width, height properties
109
+ */
110
+ getBoundingRect: function() {
111
+ this.oCoords || this.setCoords();
112
+
113
+ var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x];
114
+ var minX = fabric.util.array.min(xCoords);
115
+ var maxX = fabric.util.array.max(xCoords);
116
+ var width = Math.abs(minX - maxX);
117
+
118
+ var yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y];
119
+ var minY = fabric.util.array.min(yCoords);
120
+ var maxY = fabric.util.array.max(yCoords);
121
+ var height = Math.abs(minY - maxY);
122
+
123
+ return {
124
+ left: minX,
125
+ top: minY,
126
+ width: width,
127
+ height: height
128
+ };
129
+ },
130
+
131
+ /**
132
+ * Returns width of an object
133
+ * @method getWidth
134
+ * @return {Number} width value
135
+ */
136
+ getWidth: function() {
137
+ return this.width * this.scaleX;
138
+ },
139
+
140
+ /**
141
+ * Returns height of an object
142
+ * @method getHeight
143
+ * @return {Number} height value
144
+ */
145
+ getHeight: function() {
146
+ return this.height * this.scaleY;
147
+ },
148
+
149
+ /**
150
+ * Makes sure the scale is valid and modifies it if necessary
151
+ * @private
152
+ * @method _constrainScale
153
+ * @param {Number} value
154
+ * @return {Number}
155
+ */
156
+ _constrainScale: function(value) {
157
+ if (Math.abs(value) < this.minScaleLimit) {
158
+ if (value < 0)
159
+ return -this.minScaleLimit;
160
+ else
161
+ return this.minScaleLimit;
162
+ }
163
+
164
+ return value;
165
+ },
166
+
167
+ /**
168
+ * Scales an object (equally by x and y)
169
+ * @method scale
170
+ * @param value {Number} scale factor
171
+ * @return {fabric.Object} thisArg
172
+ * @chainable
173
+ */
174
+ scale: function(value) {
175
+ value = this._constrainScale(value);
176
+
177
+ if (value < 0) {
178
+ this.flipX = !this.flipX;
179
+ this.flipY = !this.flipY;
180
+ value *= -1;
181
+ }
182
+
183
+ this.scaleX = value;
184
+ this.scaleY = value;
185
+ this.setCoords();
186
+ return this;
187
+ },
188
+
189
+ /**
190
+ * Scales an object to a given width, with respect to bounding box (scaling by x/y equally)
191
+ * @method scaleToWidth
192
+ * @param value {Number} new width value
193
+ * @return {fabric.Object} thisArg
194
+ * @chainable
195
+ */
196
+ scaleToWidth: function(value) {
197
+ // adjust to bounding rect factor so that rotated shapes would fit as well
198
+ var boundingRectFactor = this.getBoundingRectWidth() / this.getWidth();
199
+ return this.scale(value / this.width / boundingRectFactor);
200
+ },
201
+
202
+ /**
203
+ * Scales an object to a given height, with respect to bounding box (scaling by x/y equally)
204
+ * @method scaleToHeight
205
+ * @param value {Number} new height value
206
+ * @return {fabric.Object} thisArg
207
+ * @chainable
208
+ */
209
+ scaleToHeight: function(value) {
210
+ // adjust to bounding rect factor so that rotated shapes would fit as well
211
+ var boundingRectFactor = this.getBoundingRectHeight() / this.getHeight();
212
+ return this.scale(value / this.height / boundingRectFactor);
213
+ },
214
+
215
+ /**
216
+ * Sets corner position coordinates based on current angle, width and height
217
+ * @method setCoords
218
+ * @return {fabric.Object} thisArg
219
+ * @chainable
220
+ */
221
+ setCoords: function() {
222
+
223
+ var strokeWidth = this.strokeWidth > 1 ? this.strokeWidth : 0,
224
+ padding = this.padding,
225
+ theta = degreesToRadians(this.angle);
226
+
227
+ this.currentWidth = (this.width + strokeWidth) * this.scaleX + padding * 2;
228
+ this.currentHeight = (this.height + strokeWidth) * this.scaleY + padding * 2;
229
+
230
+ // If width is negative, make postive. Fixes path selection issue
231
+ if (this.currentWidth < 0) {
232
+ this.currentWidth = Math.abs(this.currentWidth);
233
+ }
234
+
235
+ var _hypotenuse = Math.sqrt(
236
+ Math.pow(this.currentWidth / 2, 2) +
237
+ Math.pow(this.currentHeight / 2, 2));
238
+
239
+ var _angle = Math.atan(this.currentHeight / this.currentWidth);
240
+
241
+ // offset added for rotate and scale actions
242
+ var offsetX = Math.cos(_angle + theta) * _hypotenuse,
243
+ offsetY = Math.sin(_angle + theta) * _hypotenuse,
244
+ sinTh = Math.sin(theta),
245
+ cosTh = Math.cos(theta);
246
+
247
+ var coords = this.getCenterPoint();
248
+ var tl = {
249
+ x: coords.x - offsetX,
250
+ y: coords.y - offsetY
251
+ };
252
+ var tr = {
253
+ x: tl.x + (this.currentWidth * cosTh),
254
+ y: tl.y + (this.currentWidth * sinTh)
255
+ };
256
+ var br = {
257
+ x: tr.x - (this.currentHeight * sinTh),
258
+ y: tr.y + (this.currentHeight * cosTh)
259
+ };
260
+ var bl = {
261
+ x: tl.x - (this.currentHeight * sinTh),
262
+ y: tl.y + (this.currentHeight * cosTh)
263
+ };
264
+ var ml = {
265
+ x: tl.x - (this.currentHeight/2 * sinTh),
266
+ y: tl.y + (this.currentHeight/2 * cosTh)
267
+ };
268
+ var mt = {
269
+ x: tl.x + (this.currentWidth/2 * cosTh),
270
+ y: tl.y + (this.currentWidth/2 * sinTh)
271
+ };
272
+ var mr = {
273
+ x: tr.x - (this.currentHeight/2 * sinTh),
274
+ y: tr.y + (this.currentHeight/2 * cosTh)
275
+ };
276
+ var mb = {
277
+ x: bl.x + (this.currentWidth/2 * cosTh),
278
+ y: bl.y + (this.currentWidth/2 * sinTh)
279
+ };
280
+ var mtr = {
281
+ x: tl.x + (this.currentWidth/2 * cosTh),
282
+ y: tl.y + (this.currentWidth/2 * sinTh)
283
+ };
284
+
285
+ // debugging
286
+
287
+ // setTimeout(function() {
288
+ // canvas.contextTop.fillStyle = 'green';
289
+ // canvas.contextTop.fillRect(mb.x, mb.y, 3, 3);
290
+ // canvas.contextTop.fillRect(bl.x, bl.y, 3, 3);
291
+ // canvas.contextTop.fillRect(br.x, br.y, 3, 3);
292
+ // canvas.contextTop.fillRect(tl.x, tl.y, 3, 3);
293
+ // canvas.contextTop.fillRect(tr.x, tr.y, 3, 3);
294
+ // canvas.contextTop.fillRect(ml.x, ml.y, 3, 3);
295
+ // canvas.contextTop.fillRect(mr.x, mr.y, 3, 3);
296
+ // canvas.contextTop.fillRect(mt.x, mt.y, 3, 3);
297
+ // }, 50);
298
+
299
+ // clockwise
300
+ this.oCoords = { tl: tl, tr: tr, br: br, bl: bl, ml: ml, mt: mt, mr: mr, mb: mb, mtr: mtr };
301
+
302
+ // set coordinates of the draggable boxes in the corners used to scale/rotate the image
303
+ this._setCornerCoords();
304
+
305
+ return this;
306
+ }
307
+ });
308
+ })();
@@ -0,0 +1,496 @@
1
+ (function(){
2
+
3
+ var getPointer = fabric.util.getPointer,
4
+ degreesToRadians = fabric.util.degreesToRadians;
5
+
6
+ fabric.util.object.extend(fabric.Object.prototype, /** @scope fabric.Object.prototype */ {
7
+
8
+ /**
9
+ * Determines which one of the four corners has been clicked
10
+ * @method _findTargetCorner
11
+ * @private
12
+ * @param e {Event} event object
13
+ * @param offset {Object} canvas offset
14
+ * @return {String|Boolean} corner code (tl, tr, bl, br, etc.), or false if nothing is found
15
+ */
16
+ _findTargetCorner: function(e, offset) {
17
+ if (!this.hasControls || !this.active) return false;
18
+
19
+ var pointer = getPointer(e, this.canvas.upperCanvasEl),
20
+ ex = pointer.x - offset.left,
21
+ ey = pointer.y - offset.top,
22
+ xpoints,
23
+ lines;
24
+
25
+ for (var i in this.oCoords) {
26
+
27
+ if (i === 'mtr' && !this.hasRotatingPoint) {
28
+ continue;
29
+ }
30
+
31
+ if (this.get('lockUniScaling') && (i === 'mt' || i === 'mr' || i === 'mb' || i === 'ml')) {
32
+ continue;
33
+ }
34
+
35
+ lines = this._getImageLines(this.oCoords[i].corner, i);
36
+
37
+ // debugging
38
+
39
+ // canvas.contextTop.fillRect(lines.bottomline.d.x, lines.bottomline.d.y, 2, 2);
40
+ // canvas.contextTop.fillRect(lines.bottomline.o.x, lines.bottomline.o.y, 2, 2);
41
+
42
+ // canvas.contextTop.fillRect(lines.leftline.d.x, lines.leftline.d.y, 2, 2);
43
+ // canvas.contextTop.fillRect(lines.leftline.o.x, lines.leftline.o.y, 2, 2);
44
+
45
+ // canvas.contextTop.fillRect(lines.topline.d.x, lines.topline.d.y, 2, 2);
46
+ // canvas.contextTop.fillRect(lines.topline.o.x, lines.topline.o.y, 2, 2);
47
+
48
+ // canvas.contextTop.fillRect(lines.rightline.d.x, lines.rightline.d.y, 2, 2);
49
+ // canvas.contextTop.fillRect(lines.rightline.o.x, lines.rightline.o.y, 2, 2);
50
+
51
+ xpoints = this._findCrossPoints(ex, ey, lines);
52
+ if (xpoints % 2 === 1 && xpoints !== 0) {
53
+ this.__corner = i;
54
+ return i;
55
+ }
56
+ }
57
+ return false;
58
+ },
59
+
60
+ /**
61
+ * Helper method to determine how many cross points are between the 4 image edges
62
+ * and the horizontal line determined by the position of our mouse when clicked on canvas
63
+ * @method _findCrossPoints
64
+ * @private
65
+ * @param ex {Number} x coordinate of the mouse
66
+ * @param ey {Number} y coordinate of the mouse
67
+ * @param oCoords {Object} Coordinates of the image being evaluated
68
+ */
69
+ _findCrossPoints: function(ex, ey, oCoords) {
70
+ var b1, b2, a1, a2, xi, yi,
71
+ xcount = 0,
72
+ iLine;
73
+
74
+ for (var lineKey in oCoords) {
75
+ iLine = oCoords[lineKey];
76
+ // optimisation 1: line below dot. no cross
77
+ if ((iLine.o.y < ey) && (iLine.d.y < ey)) {
78
+ continue;
79
+ }
80
+ // optimisation 2: line above dot. no cross
81
+ if ((iLine.o.y >= ey) && (iLine.d.y >= ey)) {
82
+ continue;
83
+ }
84
+ // optimisation 3: vertical line case
85
+ if ((iLine.o.x === iLine.d.x) && (iLine.o.x >= ex)) {
86
+ xi = iLine.o.x;
87
+ yi = ey;
88
+ }
89
+ // calculate the intersection point
90
+ else {
91
+ b1 = 0;
92
+ b2 = (iLine.d.y-iLine.o.y)/(iLine.d.x-iLine.o.x);
93
+ a1 = ey-b1*ex;
94
+ a2 = iLine.o.y-b2*iLine.o.x;
95
+
96
+ xi = - (a1-a2)/(b1-b2);
97
+ yi = a1+b1*xi;
98
+ }
99
+ // dont count xi < ex cases
100
+ if (xi >= ex) {
101
+ xcount += 1;
102
+ }
103
+ // optimisation 4: specific for square images
104
+ if (xcount === 2) {
105
+ break;
106
+ }
107
+ }
108
+ return xcount;
109
+ },
110
+
111
+ /**
112
+ * Method that returns an object with the image lines in it given the coordinates of the corners
113
+ * @method _getImageLines
114
+ * @private
115
+ * @param oCoords {Object} coordinates of the image corners
116
+ */
117
+ _getImageLines: function(oCoords) {
118
+ return {
119
+ topline: {
120
+ o: oCoords.tl,
121
+ d: oCoords.tr
122
+ },
123
+ rightline: {
124
+ o: oCoords.tr,
125
+ d: oCoords.br
126
+ },
127
+ bottomline: {
128
+ o: oCoords.br,
129
+ d: oCoords.bl
130
+ },
131
+ leftline: {
132
+ o: oCoords.bl,
133
+ d: oCoords.tl
134
+ }
135
+ };
136
+ },
137
+
138
+ /**
139
+ * Sets the coordinates of the draggable boxes in the corners of
140
+ * the image used to scale/rotate it.
141
+ * @method _setCornerCoords
142
+ * @private
143
+ */
144
+ _setCornerCoords: function() {
145
+ var coords = this.oCoords,
146
+ theta = degreesToRadians(this.angle),
147
+ newTheta = degreesToRadians(45 - this.angle),
148
+ cornerHypotenuse = Math.sqrt(2 * Math.pow(this.cornerSize, 2)) / 2,
149
+ cosHalfOffset = cornerHypotenuse * Math.cos(newTheta),
150
+ sinHalfOffset = cornerHypotenuse * Math.sin(newTheta),
151
+ sinTh = Math.sin(theta),
152
+ cosTh = Math.cos(theta);
153
+
154
+ coords.tl.corner = {
155
+ tl: {
156
+ x: coords.tl.x - sinHalfOffset,
157
+ y: coords.tl.y - cosHalfOffset
158
+ },
159
+ tr: {
160
+ x: coords.tl.x + cosHalfOffset,
161
+ y: coords.tl.y - sinHalfOffset
162
+ },
163
+ bl: {
164
+ x: coords.tl.x - cosHalfOffset,
165
+ y: coords.tl.y + sinHalfOffset
166
+ },
167
+ br: {
168
+ x: coords.tl.x + sinHalfOffset,
169
+ y: coords.tl.y + cosHalfOffset
170
+ }
171
+ };
172
+
173
+ coords.tr.corner = {
174
+ tl: {
175
+ x: coords.tr.x - sinHalfOffset,
176
+ y: coords.tr.y - cosHalfOffset
177
+ },
178
+ tr: {
179
+ x: coords.tr.x + cosHalfOffset,
180
+ y: coords.tr.y - sinHalfOffset
181
+ },
182
+ br: {
183
+ x: coords.tr.x + sinHalfOffset,
184
+ y: coords.tr.y + cosHalfOffset
185
+ },
186
+ bl: {
187
+ x: coords.tr.x - cosHalfOffset,
188
+ y: coords.tr.y + sinHalfOffset
189
+ }
190
+ };
191
+
192
+ coords.bl.corner = {
193
+ tl: {
194
+ x: coords.bl.x - sinHalfOffset,
195
+ y: coords.bl.y - cosHalfOffset
196
+ },
197
+ bl: {
198
+ x: coords.bl.x - cosHalfOffset,
199
+ y: coords.bl.y + sinHalfOffset
200
+ },
201
+ br: {
202
+ x: coords.bl.x + sinHalfOffset,
203
+ y: coords.bl.y + cosHalfOffset
204
+ },
205
+ tr: {
206
+ x: coords.bl.x + cosHalfOffset,
207
+ y: coords.bl.y - sinHalfOffset
208
+ }
209
+ };
210
+
211
+ coords.br.corner = {
212
+ tr: {
213
+ x: coords.br.x + cosHalfOffset,
214
+ y: coords.br.y - sinHalfOffset
215
+ },
216
+ bl: {
217
+ x: coords.br.x - cosHalfOffset,
218
+ y: coords.br.y + sinHalfOffset
219
+ },
220
+ br: {
221
+ x: coords.br.x + sinHalfOffset,
222
+ y: coords.br.y + cosHalfOffset
223
+ },
224
+ tl: {
225
+ x: coords.br.x - sinHalfOffset,
226
+ y: coords.br.y - cosHalfOffset
227
+ }
228
+ };
229
+
230
+ coords.ml.corner = {
231
+ tl: {
232
+ x: coords.ml.x - sinHalfOffset,
233
+ y: coords.ml.y - cosHalfOffset
234
+ },
235
+ tr: {
236
+ x: coords.ml.x + cosHalfOffset,
237
+ y: coords.ml.y - sinHalfOffset
238
+ },
239
+ bl: {
240
+ x: coords.ml.x - cosHalfOffset,
241
+ y: coords.ml.y + sinHalfOffset
242
+ },
243
+ br: {
244
+ x: coords.ml.x + sinHalfOffset,
245
+ y: coords.ml.y + cosHalfOffset
246
+ }
247
+ };
248
+
249
+ coords.mt.corner = {
250
+ tl: {
251
+ x: coords.mt.x - sinHalfOffset,
252
+ y: coords.mt.y - cosHalfOffset
253
+ },
254
+ tr: {
255
+ x: coords.mt.x + cosHalfOffset,
256
+ y: coords.mt.y - sinHalfOffset
257
+ },
258
+ bl: {
259
+ x: coords.mt.x - cosHalfOffset,
260
+ y: coords.mt.y + sinHalfOffset
261
+ },
262
+ br: {
263
+ x: coords.mt.x + sinHalfOffset,
264
+ y: coords.mt.y + cosHalfOffset
265
+ }
266
+ };
267
+
268
+ coords.mr.corner = {
269
+ tl: {
270
+ x: coords.mr.x - sinHalfOffset,
271
+ y: coords.mr.y - cosHalfOffset
272
+ },
273
+ tr: {
274
+ x: coords.mr.x + cosHalfOffset,
275
+ y: coords.mr.y - sinHalfOffset
276
+ },
277
+ bl: {
278
+ x: coords.mr.x - cosHalfOffset,
279
+ y: coords.mr.y + sinHalfOffset
280
+ },
281
+ br: {
282
+ x: coords.mr.x + sinHalfOffset,
283
+ y: coords.mr.y + cosHalfOffset
284
+ }
285
+ };
286
+
287
+ coords.mb.corner = {
288
+ tl: {
289
+ x: coords.mb.x - sinHalfOffset,
290
+ y: coords.mb.y - cosHalfOffset
291
+ },
292
+ tr: {
293
+ x: coords.mb.x + cosHalfOffset,
294
+ y: coords.mb.y - sinHalfOffset
295
+ },
296
+ bl: {
297
+ x: coords.mb.x - cosHalfOffset,
298
+ y: coords.mb.y + sinHalfOffset
299
+ },
300
+ br: {
301
+ x: coords.mb.x + sinHalfOffset,
302
+ y: coords.mb.y + cosHalfOffset
303
+ }
304
+ };
305
+
306
+ coords.mtr.corner = {
307
+ tl: {
308
+ x: coords.mtr.x - sinHalfOffset + (sinTh * this.rotatingPointOffset),
309
+ y: coords.mtr.y - cosHalfOffset - (cosTh * this.rotatingPointOffset)
310
+ },
311
+ tr: {
312
+ x: coords.mtr.x + cosHalfOffset + (sinTh * this.rotatingPointOffset),
313
+ y: coords.mtr.y - sinHalfOffset - (cosTh * this.rotatingPointOffset)
314
+ },
315
+ bl: {
316
+ x: coords.mtr.x - cosHalfOffset + (sinTh * this.rotatingPointOffset),
317
+ y: coords.mtr.y + sinHalfOffset - (cosTh * this.rotatingPointOffset)
318
+ },
319
+ br: {
320
+ x: coords.mtr.x + sinHalfOffset + (sinTh * this.rotatingPointOffset),
321
+ y: coords.mtr.y + cosHalfOffset - (cosTh * this.rotatingPointOffset)
322
+ }
323
+ };
324
+ },
325
+ /**
326
+ * Draws borders of an object's bounding box.
327
+ * Requires public properties: width, height
328
+ * Requires public options: padding, borderColor
329
+ * @method drawBorders
330
+ * @param {CanvasRenderingContext2D} ctx Context to draw on
331
+ * @return {fabric.Object} thisArg
332
+ * @chainable
333
+ */
334
+ drawBorders: function(ctx) {
335
+ if (!this.hasBorders) return;
336
+
337
+ var padding = this.padding,
338
+ padding2 = padding * 2,
339
+ strokeWidth = this.strokeWidth > 1 ? this.strokeWidth : 0;
340
+
341
+ ctx.save();
342
+
343
+ ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
344
+ ctx.strokeStyle = this.borderColor;
345
+
346
+ var scaleX = 1 / this._constrainScale(this.scaleX),
347
+ scaleY = 1 / this._constrainScale(this.scaleY);
348
+
349
+ ctx.lineWidth = 1 / this.borderScaleFactor;
350
+
351
+ ctx.scale(scaleX, scaleY);
352
+
353
+ var w = this.getWidth(),
354
+ h = this.getHeight();
355
+
356
+ ctx.strokeRect(
357
+ ~~(-(w / 2) - padding - strokeWidth / 2 * this.scaleX) + 0.5, // offset needed to make lines look sharper
358
+ ~~(-(h / 2) - padding - strokeWidth / 2 * this.scaleY) + 0.5,
359
+ ~~(w + padding2 + strokeWidth * this.scaleX),
360
+ ~~(h + padding2 + strokeWidth * this.scaleY)
361
+ );
362
+
363
+ if (this.hasRotatingPoint && !this.get('lockRotation') && this.hasControls) {
364
+
365
+ var rotateHeight = (
366
+ this.flipY
367
+ ? h + (strokeWidth * this.scaleY) + (padding * 2)
368
+ : -h - (strokeWidth * this.scaleY) - (padding * 2)
369
+ ) / 2;
370
+
371
+ ctx.beginPath();
372
+ ctx.moveTo(0, rotateHeight);
373
+ ctx.lineTo(0, rotateHeight + (this.flipY ? this.rotatingPointOffset : -this.rotatingPointOffset));
374
+ ctx.closePath();
375
+ ctx.stroke();
376
+ }
377
+
378
+ ctx.restore();
379
+ return this;
380
+ },
381
+
382
+ /**
383
+ * Draws corners of an object's bounding box.
384
+ * Requires public properties: width, height, scaleX, scaleY
385
+ * Requires public options: cornerSize, padding
386
+ * @method drawControls
387
+ * @param {CanvasRenderingContext2D} ctx Context to draw on
388
+ * @return {fabric.Object} thisArg
389
+ * @chainable
390
+ */
391
+ drawControls: function(ctx) {
392
+ if (!this.hasControls) return;
393
+
394
+ var size = this.cornerSize,
395
+ size2 = size / 2,
396
+ strokeWidth2 = this.strokeWidth / 2,
397
+ left = -(this.width / 2),
398
+ top = -(this.height / 2),
399
+ _left,
400
+ _top,
401
+ sizeX = size / this.scaleX,
402
+ sizeY = size / this.scaleY,
403
+ paddingX = this.padding / this.scaleX,
404
+ paddingY = this.padding / this.scaleY,
405
+ scaleOffsetY = size2 / this.scaleY,
406
+ scaleOffsetX = size2 / this.scaleX,
407
+ scaleOffsetSizeX = (size2 - size) / this.scaleX,
408
+ scaleOffsetSizeY = (size2 - size) / this.scaleY,
409
+ height = this.height,
410
+ width = this.width,
411
+ methodName = this.transparentCorners ? 'strokeRect' : 'fillRect',
412
+ isVML = typeof G_vmlCanvasManager !== 'undefined';
413
+
414
+ ctx.save();
415
+
416
+ ctx.lineWidth = 1 / Math.max(this.scaleX, this.scaleY);
417
+
418
+ ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
419
+ ctx.strokeStyle = ctx.fillStyle = this.cornerColor;
420
+
421
+ // top-left
422
+ _left = left - scaleOffsetX - strokeWidth2 - paddingX;
423
+ _top = top - scaleOffsetY - strokeWidth2 - paddingY;
424
+
425
+ isVML || ctx.clearRect(_left, _top, sizeX, sizeY);
426
+ ctx[methodName](_left, _top, sizeX, sizeY);
427
+
428
+ // top-right
429
+ _left = left + width - scaleOffsetX + strokeWidth2 + paddingX;
430
+ _top = top - scaleOffsetY - strokeWidth2 - paddingY;
431
+
432
+ isVML || ctx.clearRect(_left, _top, sizeX, sizeY);
433
+ ctx[methodName](_left, _top, sizeX, sizeY);
434
+
435
+ // bottom-left
436
+ _left = left - scaleOffsetX - strokeWidth2 - paddingX;
437
+ _top = top + height + scaleOffsetSizeY + strokeWidth2 + paddingY;
438
+
439
+ isVML || ctx.clearRect(_left, _top, sizeX, sizeY);
440
+ ctx[methodName](_left, _top, sizeX, sizeY);
441
+
442
+ // bottom-right
443
+ _left = left + width + scaleOffsetSizeX + strokeWidth2 + paddingX;
444
+ _top = top + height + scaleOffsetSizeY + strokeWidth2 + paddingY;
445
+
446
+ isVML || ctx.clearRect(_left, _top, sizeX, sizeY);
447
+ ctx[methodName](_left, _top, sizeX, sizeY);
448
+
449
+ if (!this.get('lockUniScaling')) {
450
+ // middle-top
451
+ _left = left + width/2 - scaleOffsetX;
452
+ _top = top - scaleOffsetY - strokeWidth2 - paddingY;
453
+
454
+ isVML || ctx.clearRect(_left, _top, sizeX, sizeY);
455
+ ctx[methodName](_left, _top, sizeX, sizeY);
456
+
457
+ // middle-bottom
458
+ _left = left + width/2 - scaleOffsetX;
459
+ _top = top + height + scaleOffsetSizeY + strokeWidth2 + paddingY;
460
+
461
+ isVML || ctx.clearRect(_left, _top, sizeX, sizeY);
462
+ ctx[methodName](_left, _top, sizeX, sizeY);
463
+
464
+ // middle-right
465
+ _left = left + width + scaleOffsetSizeX + strokeWidth2 + paddingX;
466
+ _top = top + height/2 - scaleOffsetY;
467
+
468
+ isVML || ctx.clearRect(_left, _top, sizeX, sizeY);
469
+ ctx[methodName](_left, _top, sizeX, sizeY);
470
+
471
+ // middle-left
472
+ _left = left - scaleOffsetX - strokeWidth2 - paddingX;
473
+ _top = top + height/2 - scaleOffsetY;
474
+
475
+ isVML || ctx.clearRect(_left, _top, sizeX, sizeY);
476
+ ctx[methodName](_left, _top, sizeX, sizeY);
477
+ }
478
+
479
+ // middle-top-rotate
480
+ if (this.hasRotatingPoint) {
481
+
482
+ _left = left + width/2 - scaleOffsetX;
483
+ _top = this.flipY ?
484
+ (top + height + (this.rotatingPointOffset / this.scaleY) - sizeY/2 + strokeWidth2 + paddingY)
485
+ : (top - (this.rotatingPointOffset / this.scaleY) - sizeY/2 - strokeWidth2 - paddingY);
486
+
487
+ isVML || ctx.clearRect(_left, _top, sizeX, sizeY);
488
+ ctx[methodName](_left, _top, sizeX, sizeY);
489
+ }
490
+
491
+ ctx.restore();
492
+
493
+ return this;
494
+ }
495
+ });
496
+ })();