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,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
+ })();