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,794 @@
1
+ (function(global) {
2
+
3
+ var commandLengths = {
4
+ m: 2,
5
+ l: 2,
6
+ h: 1,
7
+ v: 1,
8
+ c: 6,
9
+ s: 4,
10
+ q: 4,
11
+ t: 2,
12
+ a: 7
13
+ };
14
+
15
+ function drawArc(ctx, x, y, coords) {
16
+ var rx = coords[0];
17
+ var ry = coords[1];
18
+ var rot = coords[2];
19
+ var large = coords[3];
20
+ var sweep = coords[4];
21
+ var ex = coords[5];
22
+ var ey = coords[6];
23
+ var segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, x, y);
24
+ for (var i=0; i<segs.length; i++) {
25
+ var bez = segmentToBezier.apply(this, segs[i]);
26
+ ctx.bezierCurveTo.apply(ctx, bez);
27
+ }
28
+ }
29
+
30
+ var arcToSegmentsCache = { },
31
+ segmentToBezierCache = { },
32
+ _join = Array.prototype.join,
33
+ argsString;
34
+
35
+ // Generous contribution by Raph Levien, from libsvg-0.1.0.tar.gz
36
+ function arcToSegments(x, y, rx, ry, large, sweep, rotateX, ox, oy) {
37
+ argsString = _join.call(arguments);
38
+ if (arcToSegmentsCache[argsString]) {
39
+ return arcToSegmentsCache[argsString];
40
+ }
41
+
42
+ var th = rotateX * (Math.PI/180);
43
+ var sin_th = Math.sin(th);
44
+ var cos_th = Math.cos(th);
45
+ rx = Math.abs(rx);
46
+ ry = Math.abs(ry);
47
+ var px = cos_th * (ox - x) * 0.5 + sin_th * (oy - y) * 0.5;
48
+ var py = cos_th * (oy - y) * 0.5 - sin_th * (ox - x) * 0.5;
49
+ var pl = (px*px) / (rx*rx) + (py*py) / (ry*ry);
50
+ if (pl > 1) {
51
+ pl = Math.sqrt(pl);
52
+ rx *= pl;
53
+ ry *= pl;
54
+ }
55
+
56
+ var a00 = cos_th / rx;
57
+ var a01 = sin_th / rx;
58
+ var a10 = (-sin_th) / ry;
59
+ var a11 = (cos_th) / ry;
60
+ var x0 = a00 * ox + a01 * oy;
61
+ var y0 = a10 * ox + a11 * oy;
62
+ var x1 = a00 * x + a01 * y;
63
+ var y1 = a10 * x + a11 * y;
64
+
65
+ var d = (x1-x0) * (x1-x0) + (y1-y0) * (y1-y0);
66
+ var sfactor_sq = 1 / d - 0.25;
67
+ if (sfactor_sq < 0) sfactor_sq = 0;
68
+ var sfactor = Math.sqrt(sfactor_sq);
69
+ if (sweep === large) sfactor = -sfactor;
70
+ var xc = 0.5 * (x0 + x1) - sfactor * (y1-y0);
71
+ var yc = 0.5 * (y0 + y1) + sfactor * (x1-x0);
72
+
73
+ var th0 = Math.atan2(y0-yc, x0-xc);
74
+ var th1 = Math.atan2(y1-yc, x1-xc);
75
+
76
+ var th_arc = th1-th0;
77
+ if (th_arc < 0 && sweep === 1){
78
+ th_arc += 2*Math.PI;
79
+ } else if (th_arc > 0 && sweep === 0) {
80
+ th_arc -= 2 * Math.PI;
81
+ }
82
+
83
+ var segments = Math.ceil(Math.abs(th_arc / (Math.PI * 0.5 + 0.001)));
84
+ var result = [];
85
+ for (var i=0; i<segments; i++) {
86
+ var th2 = th0 + i * th_arc / segments;
87
+ var th3 = th0 + (i+1) * th_arc / segments;
88
+ result[i] = [xc, yc, th2, th3, rx, ry, sin_th, cos_th];
89
+ }
90
+
91
+ return (arcToSegmentsCache[argsString] = result);
92
+ }
93
+
94
+ function segmentToBezier(cx, cy, th0, th1, rx, ry, sin_th, cos_th) {
95
+ argsString = _join.call(arguments);
96
+ if (segmentToBezierCache[argsString]) {
97
+ return segmentToBezierCache[argsString];
98
+ }
99
+
100
+ var a00 = cos_th * rx;
101
+ var a01 = -sin_th * ry;
102
+ var a10 = sin_th * rx;
103
+ var a11 = cos_th * ry;
104
+
105
+ var th_half = 0.5 * (th1 - th0);
106
+ var t = (8/3) * Math.sin(th_half * 0.5) * Math.sin(th_half * 0.5) / Math.sin(th_half);
107
+ var x1 = cx + Math.cos(th0) - t * Math.sin(th0);
108
+ var y1 = cy + Math.sin(th0) + t * Math.cos(th0);
109
+ var x3 = cx + Math.cos(th1);
110
+ var y3 = cy + Math.sin(th1);
111
+ var x2 = x3 + t * Math.sin(th1);
112
+ var y2 = y3 - t * Math.cos(th1);
113
+
114
+ return (segmentToBezierCache[argsString] = [
115
+ a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
116
+ a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
117
+ a00 * x3 + a01 * y3, a10 * x3 + a11 * y3
118
+ ]);
119
+ }
120
+
121
+ "use strict";
122
+
123
+ var fabric = global.fabric || (global.fabric = { }),
124
+ min = fabric.util.array.min,
125
+ max = fabric.util.array.max,
126
+ extend = fabric.util.object.extend,
127
+ _toString = Object.prototype.toString;
128
+
129
+ if (fabric.Path) {
130
+ fabric.warn('fabric.Path is already defined');
131
+ return;
132
+ }
133
+
134
+ /**
135
+ * @private
136
+ */
137
+ function getX(item) {
138
+ if (item[0] === 'H') {
139
+ return item[1];
140
+ }
141
+ return item[item.length - 2];
142
+ }
143
+
144
+ /**
145
+ * @private
146
+ */
147
+ function getY(item) {
148
+ if (item[0] === 'V') {
149
+ return item[1];
150
+ }
151
+ return item[item.length - 1];
152
+ }
153
+
154
+ /**
155
+ * Path class
156
+ * @class Path
157
+ * @extends fabric.Object
158
+ */
159
+ fabric.Path = fabric.util.createClass(fabric.Object, /** @scope fabric.Path.prototype */ {
160
+
161
+ /**
162
+ * Type of an object
163
+ * @property
164
+ * @type String
165
+ */
166
+ type: 'path',
167
+
168
+ /**
169
+ * Constructor
170
+ * @method initialize
171
+ * @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
172
+ * @param {Object} [options] Options object
173
+ */
174
+ initialize: function(path, options) {
175
+ options = options || { };
176
+
177
+ this.setOptions(options);
178
+
179
+ if (!path) {
180
+ throw new Error('`path` argument is required');
181
+ }
182
+
183
+ var fromArray = _toString.call(path) === '[object Array]';
184
+
185
+ this.path = fromArray
186
+ ? path
187
+ // one of commands (m,M,l,L,q,Q,c,C,etc.) followed by non-command characters (i.e. command values)
188
+ : path.match && path.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi);
189
+
190
+ if (!this.path) return;
191
+
192
+ if (!fromArray) {
193
+ this.path = this._parsePath();
194
+ }
195
+ this._initializePath(options);
196
+
197
+ if (options.sourcePath) {
198
+ this.setSourcePath(options.sourcePath);
199
+ }
200
+ },
201
+
202
+ /**
203
+ * @private
204
+ * @method _initializePath
205
+ */
206
+ _initializePath: function (options) {
207
+ var isWidthSet = 'width' in options,
208
+ isHeightSet = 'height' in options,
209
+ isLeftSet = 'left' in options,
210
+ isTopSet = 'top' in options;
211
+
212
+ if (!isWidthSet || !isHeightSet) {
213
+ extend(this, this._parseDimensions());
214
+ if (isWidthSet) {
215
+ this.width = options.width;
216
+ }
217
+ if (isHeightSet) {
218
+ this.height = options.height;
219
+ }
220
+ }
221
+ else { //Set center location relative to given height/width if not specified
222
+ if (!isTopSet) {
223
+ this.top = this.height / 2;
224
+ }
225
+ if (!isLeftSet) {
226
+ this.left = this.width / 2;
227
+ }
228
+ }
229
+ this.pathOffset = this._calculatePathOffset(isTopSet || isLeftSet); //Save top-left coords as offset
230
+ },
231
+
232
+ /**
233
+ * @private
234
+ * @method _calculatePathOffset
235
+ */
236
+ _calculatePathOffset: function (positionSet) {
237
+ return {
238
+ x: positionSet ? 0 : this.left - (this.width / 2),
239
+ y: positionSet ? 0 : this.top - (this.height / 2)
240
+ };
241
+ },
242
+
243
+ /**
244
+ * @private
245
+ * @method _render
246
+ */
247
+ _render: function(ctx) {
248
+ var current, // current instruction
249
+ previous = null,
250
+ x = 0, // current x
251
+ y = 0, // current y
252
+ controlX = 0, // current control point x
253
+ controlY = 0, // current control point y
254
+ tempX,
255
+ tempY,
256
+ tempControlX,
257
+ tempControlY,
258
+ l = -((this.width / 2) + this.pathOffset.x),
259
+ t = -((this.height / 2) + this.pathOffset.y);
260
+
261
+ for (var i = 0, len = this.path.length; i < len; ++i) {
262
+
263
+ current = this.path[i];
264
+
265
+ switch (current[0]) { // first letter
266
+
267
+ case 'l': // lineto, relative
268
+ x += current[1];
269
+ y += current[2];
270
+ ctx.lineTo(x + l, y + t);
271
+ break;
272
+
273
+ case 'L': // lineto, absolute
274
+ x = current[1];
275
+ y = current[2];
276
+ ctx.lineTo(x + l, y + t);
277
+ break;
278
+
279
+ case 'h': // horizontal lineto, relative
280
+ x += current[1];
281
+ ctx.lineTo(x + l, y + t);
282
+ break;
283
+
284
+ case 'H': // horizontal lineto, absolute
285
+ x = current[1];
286
+ ctx.lineTo(x + l, y + t);
287
+ break;
288
+
289
+ case 'v': // vertical lineto, relative
290
+ y += current[1];
291
+ ctx.lineTo(x + l, y + t);
292
+ break;
293
+
294
+ case 'V': // verical lineto, absolute
295
+ y = current[1];
296
+ ctx.lineTo(x + l, y + t);
297
+ break;
298
+
299
+ case 'm': // moveTo, relative
300
+ x += current[1];
301
+ y += current[2];
302
+ // draw a line if previous command was moveTo as well (otherwise, it will have no effect)
303
+ ctx[(previous && (previous[0] === 'm' || previous[0] === 'M')) ? 'lineTo' : 'moveTo'](x + l, y + t);
304
+ break;
305
+
306
+ case 'M': // moveTo, absolute
307
+ x = current[1];
308
+ y = current[2];
309
+ // draw a line if previous command was moveTo as well (otherwise, it will have no effect)
310
+ ctx[(previous && (previous[0] === 'm' || previous[0] === 'M')) ? 'lineTo' : 'moveTo'](x + l, y + t);
311
+ break;
312
+
313
+ case 'c': // bezierCurveTo, relative
314
+ tempX = x + current[5];
315
+ tempY = y + current[6];
316
+ controlX = x + current[3];
317
+ controlY = y + current[4];
318
+ ctx.bezierCurveTo(
319
+ x + current[1] + l, // x1
320
+ y + current[2] + t, // y1
321
+ controlX + l, // x2
322
+ controlY + t, // y2
323
+ tempX + l,
324
+ tempY + t
325
+ );
326
+ x = tempX;
327
+ y = tempY;
328
+ break;
329
+
330
+ case 'C': // bezierCurveTo, absolute
331
+ x = current[5];
332
+ y = current[6];
333
+ controlX = current[3];
334
+ controlY = current[4];
335
+ ctx.bezierCurveTo(
336
+ current[1] + l,
337
+ current[2] + t,
338
+ controlX + l,
339
+ controlY + t,
340
+ x + l,
341
+ y + t
342
+ );
343
+ break;
344
+
345
+ case 's': // shorthand cubic bezierCurveTo, relative
346
+
347
+ // transform to absolute x,y
348
+ tempX = x + current[3];
349
+ tempY = y + current[4];
350
+
351
+ // calculate reflection of previous control points
352
+ controlX = controlX ? (2 * x - controlX) : x;
353
+ controlY = controlY ? (2 * y - controlY) : y;
354
+
355
+ ctx.bezierCurveTo(
356
+ controlX + l,
357
+ controlY + t,
358
+ x + current[1] + l,
359
+ y + current[2] + t,
360
+ tempX + l,
361
+ tempY + t
362
+ );
363
+ // set control point to 2nd one of this command
364
+ // "... the first control point is assumed to be the reflection of the second control point on the previous command relative to the current point."
365
+ controlX = x + current[1];
366
+ controlY = y + current[2];
367
+
368
+ x = tempX;
369
+ y = tempY;
370
+ break;
371
+
372
+ case 'S': // shorthand cubic bezierCurveTo, absolute
373
+ tempX = current[3];
374
+ tempY = current[4];
375
+ // calculate reflection of previous control points
376
+ controlX = 2*x - controlX;
377
+ controlY = 2*y - controlY;
378
+ ctx.bezierCurveTo(
379
+ controlX + l,
380
+ controlY + t,
381
+ current[1] + l,
382
+ current[2] + t,
383
+ tempX + l,
384
+ tempY + t
385
+ );
386
+ x = tempX;
387
+ y = tempY;
388
+
389
+ // set control point to 2nd one of this command
390
+ // "... the first control point is assumed to be the reflection of the second control point on the previous command relative to the current point."
391
+ controlX = current[1];
392
+ controlY = current[2];
393
+
394
+ break;
395
+
396
+ case 'q': // quadraticCurveTo, relative
397
+ // transform to absolute x,y
398
+ tempX = x + current[3];
399
+ tempY = y + current[4];
400
+
401
+ controlX = x + current[1];
402
+ controlY = y + current[2];
403
+
404
+ ctx.quadraticCurveTo(
405
+ controlX + l,
406
+ controlY + t,
407
+ tempX + l,
408
+ tempY + t
409
+ );
410
+ x = tempX;
411
+ y = tempY;
412
+ break;
413
+
414
+ case 'Q': // quadraticCurveTo, absolute
415
+ tempX = current[3];
416
+ tempY = current[4];
417
+
418
+ ctx.quadraticCurveTo(
419
+ current[1] + l,
420
+ current[2] + t,
421
+ tempX + l,
422
+ tempY + t
423
+ );
424
+ x = tempX;
425
+ y = tempY;
426
+ controlX = current[1];
427
+ controlY = current[2];
428
+ break;
429
+
430
+ case 't': // shorthand quadraticCurveTo, relative
431
+
432
+ // transform to absolute x,y
433
+ tempX = x + current[1];
434
+ tempY = y + current[2];
435
+
436
+
437
+ if (previous[0].match(/[QqTt]/) === null) {
438
+ // If there is no previous command or if the previous command was not a Q, q, T or t,
439
+ // assume the control point is coincident with the current point
440
+ controlX = x;
441
+ controlY = y;
442
+ }
443
+ else if (previous[0] === 't') {
444
+ // calculate reflection of previous control points for t
445
+ controlX = 2 * x - tempControlX;
446
+ controlY = 2 * y - tempControlY;
447
+ }
448
+ else if (previous[0] === 'q') {
449
+ // calculate reflection of previous control points for q
450
+ controlX = 2 * x - controlX;
451
+ controlY = 2 * y - controlY;
452
+ }
453
+
454
+ tempControlX = controlX;
455
+ tempControlY = controlY;
456
+
457
+ ctx.quadraticCurveTo(
458
+ controlX + l,
459
+ controlY + t,
460
+ tempX + l,
461
+ tempY + t
462
+ );
463
+ x = tempX;
464
+ y = tempY;
465
+ controlX = x + current[1];
466
+ controlY = y + current[2];
467
+ break;
468
+
469
+ case 'T':
470
+ tempX = current[1];
471
+ tempY = current[2];
472
+
473
+ // calculate reflection of previous control points
474
+ controlX = 2 * x - controlX;
475
+ controlY = 2 * y - controlY;
476
+ ctx.quadraticCurveTo(
477
+ controlX + l,
478
+ controlY + t,
479
+ tempX + l,
480
+ tempY + t
481
+ );
482
+ x = tempX;
483
+ y = tempY;
484
+ break;
485
+
486
+ case 'a':
487
+ // TODO: optimize this
488
+ drawArc(ctx, x + l, y + t, [
489
+ current[1],
490
+ current[2],
491
+ current[3],
492
+ current[4],
493
+ current[5],
494
+ current[6] + x + l,
495
+ current[7] + y + t
496
+ ]);
497
+ x += current[6];
498
+ y += current[7];
499
+ break;
500
+
501
+ case 'A':
502
+ // TODO: optimize this
503
+ drawArc(ctx, x + l, y + t, [
504
+ current[1],
505
+ current[2],
506
+ current[3],
507
+ current[4],
508
+ current[5],
509
+ current[6] + l,
510
+ current[7] + t
511
+ ]);
512
+ x = current[6];
513
+ y = current[7];
514
+ break;
515
+
516
+ case 'z':
517
+ case 'Z':
518
+ ctx.closePath();
519
+ break;
520
+ }
521
+ previous = current;
522
+ }
523
+ },
524
+
525
+ /**
526
+ * Renders path on a specified context
527
+ * @method render
528
+ * @param {CanvasRenderingContext2D} ctx context to render path on
529
+ * @param {Boolean} noTransform When true, context is not transformed
530
+ */
531
+ render: function(ctx, noTransform) {
532
+ ctx.save();
533
+ var m = this.transformMatrix;
534
+ if (m) {
535
+ ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
536
+ }
537
+ if (!noTransform) {
538
+ this.transform(ctx);
539
+ }
540
+ // ctx.globalCompositeOperation = this.fillRule;
541
+
542
+ if (this.overlayFill) {
543
+ ctx.fillStyle = this.overlayFill;
544
+ }
545
+ else if (this.fill) {
546
+ ctx.fillStyle = this.fill.toLive
547
+ ? this.fill.toLive(ctx)
548
+ : this.fill;
549
+ }
550
+
551
+ if (this.stroke) {
552
+ ctx.strokeStyle = this.stroke.toLive
553
+ ? this.stroke.toLive(ctx)
554
+ : this.stroke;
555
+ }
556
+ ctx.beginPath();
557
+
558
+ this._setShadow(ctx);
559
+ this._render(ctx);
560
+
561
+ if (this.fill) {
562
+ ctx.fill();
563
+ }
564
+ this._removeShadow(ctx);
565
+
566
+ if (this.stroke) {
567
+ ctx.strokeStyle = this.stroke;
568
+ ctx.lineWidth = this.strokeWidth;
569
+ ctx.lineCap = ctx.lineJoin = 'round';
570
+ ctx.stroke();
571
+ }
572
+ if (!noTransform && this.active) {
573
+ this.drawBorders(ctx);
574
+ this.drawControls(ctx);
575
+ }
576
+ ctx.restore();
577
+ },
578
+
579
+ /**
580
+ * Returns string representation of an instance
581
+ * @method toString
582
+ * @return {String} string representation of an instance
583
+ */
584
+ toString: function() {
585
+ return '#<fabric.Path (' + this.complexity() +
586
+ '): { "top": ' + this.top + ', "left": ' + this.left + ' }>';
587
+ },
588
+
589
+ /**
590
+ * Returns object representation of an instance
591
+ * @method toObject
592
+ * @param {Array} propertiesToInclude
593
+ * @return {Object} object representation of an instance
594
+ */
595
+ toObject: function(propertiesToInclude) {
596
+ var o = extend(this.callSuper('toObject', propertiesToInclude), {
597
+ path: this.path
598
+ });
599
+ if (this.sourcePath) {
600
+ o.sourcePath = this.sourcePath;
601
+ }
602
+ if (this.transformMatrix) {
603
+ o.transformMatrix = this.transformMatrix;
604
+ }
605
+ return o;
606
+ },
607
+
608
+ /**
609
+ * Returns dataless object representation of an instance
610
+ * @method toDatalessObject
611
+ * @param {Array} propertiesToInclude
612
+ * @return {Object} object representation of an instance
613
+ */
614
+ toDatalessObject: function(propertiesToInclude) {
615
+ var o = this.toObject(propertiesToInclude);
616
+ if (this.sourcePath) {
617
+ o.path = this.sourcePath;
618
+ }
619
+ delete o.sourcePath;
620
+ return o;
621
+ },
622
+
623
+ /**
624
+ * Returns svg representation of an instance
625
+ * @method toSVG
626
+ * @return {String} svg representation of an instance
627
+ */
628
+ toSVG: function() {
629
+ var chunks = [];
630
+ for (var i = 0, len = this.path.length; i < len; i++) {
631
+ chunks.push(this.path[i].join(' '));
632
+ }
633
+ var path = chunks.join(' ');
634
+
635
+ return [
636
+ '<g transform="', (this.group ? '' : this.getSvgTransform()), '">',
637
+ '<path ',
638
+ 'd="', path, '" ',
639
+ 'style="', this.getSvgStyles(), '" ',
640
+ 'transform="translate(', (-this.width / 2), ' ', (-this.height/2), ')" />',
641
+ '</g>'
642
+ ].join('');
643
+ },
644
+
645
+ /**
646
+ * Returns number representation of an instance complexity
647
+ * @method complexity
648
+ * @return {Number} complexity
649
+ */
650
+ complexity: function() {
651
+ return this.path.length;
652
+ },
653
+
654
+ /**
655
+ * @private
656
+ * @method _parsePath
657
+ */
658
+ _parsePath: function() {
659
+ var result = [ ],
660
+ currentPath,
661
+ chunks,
662
+ parsed;
663
+
664
+ for (var i = 0, chunksParsed, len = this.path.length; i < len; i++) {
665
+ currentPath = this.path[i];
666
+ chunks = currentPath.slice(1).trim().replace(/(\d)-/g, '$1###-').split(/\s|,|###/);
667
+ chunksParsed = [ currentPath.charAt(0) ];
668
+
669
+ for (var j = 0, jlen = chunks.length; j < jlen; j++) {
670
+ parsed = parseFloat(chunks[j]);
671
+ if (!isNaN(parsed)) {
672
+ chunksParsed.push(parsed);
673
+ }
674
+ }
675
+
676
+ var command = chunksParsed[0].toLowerCase(),
677
+ commandLength = commandLengths[command];
678
+
679
+ if (chunksParsed.length - 1 > commandLength) {
680
+ for (var k = 1, klen = chunksParsed.length; k < klen; k += commandLength) {
681
+ result.push([ chunksParsed[0] ].concat(chunksParsed.slice(k, k + commandLength)));
682
+ }
683
+ }
684
+ else {
685
+ result.push(chunksParsed);
686
+ }
687
+ }
688
+
689
+ return result;
690
+ },
691
+
692
+ /**
693
+ * @method _parseDimensions
694
+ */
695
+ _parseDimensions: function() {
696
+ var aX = [],
697
+ aY = [],
698
+ previousX,
699
+ previousY,
700
+ isLowerCase = false,
701
+ x,
702
+ y;
703
+
704
+ this.path.forEach(function(item, i) {
705
+ if (item[0] !== 'H') {
706
+ previousX = (i === 0) ? getX(item) : getX(this.path[i-1]);
707
+ }
708
+ if (item[0] !== 'V') {
709
+ previousY = (i === 0) ? getY(item) : getY(this.path[i-1]);
710
+ }
711
+
712
+ // lowercased letter denotes relative position;
713
+ // transform to absolute
714
+ if (item[0] === item[0].toLowerCase()) {
715
+ isLowerCase = true;
716
+ }
717
+
718
+ // last 2 items in an array of coordinates are the actualy x/y (except H/V);
719
+ // collect them
720
+
721
+ // TODO (kangax): support relative h/v commands
722
+
723
+ x = isLowerCase
724
+ ? previousX + getX(item)
725
+ : item[0] === 'V'
726
+ ? previousX
727
+ : getX(item);
728
+
729
+ y = isLowerCase
730
+ ? previousY + getY(item)
731
+ : item[0] === 'H'
732
+ ? previousY
733
+ : getY(item);
734
+
735
+ var val = parseInt(x, 10);
736
+ if (!isNaN(val)) aX.push(val);
737
+
738
+ val = parseInt(y, 10);
739
+ if (!isNaN(val)) aY.push(val);
740
+
741
+ }, this);
742
+
743
+ var minX = min(aX),
744
+ minY = min(aY),
745
+ maxX = max(aX),
746
+ maxY = max(aY),
747
+ deltaX = maxX - minX,
748
+ deltaY = maxY - minY;
749
+
750
+ var o = {
751
+ top: minY + deltaY / 2,
752
+ left: minX + deltaX / 2,
753
+ bottom: max(aY) - deltaY,
754
+ right: max(aX) - deltaX
755
+ };
756
+
757
+ o.width = deltaX;
758
+ o.height = deltaY;
759
+
760
+ return o;
761
+ }
762
+ });
763
+
764
+ /**
765
+ * Creates an instance of fabric.Path from an object
766
+ * @static
767
+ * @method fabric.Path.fromObject
768
+ * @return {fabric.Path} Instance of fabric.Path
769
+ */
770
+ fabric.Path.fromObject = function(object) {
771
+ return new fabric.Path(object.path, object);
772
+ };
773
+
774
+ /**
775
+ * List of attribute names to account for when parsing SVG element (used by `fabric.Path.fromElement`)
776
+ * @static
777
+ * @see http://www.w3.org/TR/SVG/paths.html#PathElement
778
+ */
779
+ fabric.Path.ATTRIBUTE_NAMES = 'd fill fill-opacity opacity fill-rule stroke stroke-width transform'.split(' ');
780
+
781
+ /**
782
+ * Creates an instance of fabric.Path from an SVG <path> element
783
+ * @static
784
+ * @method fabric.Path.fromElement
785
+ * @param {SVGElement} element to parse
786
+ * @param {Object} [options] Options object
787
+ * @return {fabric.Path} Instance of fabric.Path
788
+ */
789
+ fabric.Path.fromElement = function(element, options) {
790
+ var parsedAttributes = fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES);
791
+ return new fabric.Path(parsedAttributes.d, extend(parsedAttributes, options));
792
+ };
793
+
794
+ })(typeof exports !== 'undefined' ? exports : this);