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,934 @@
1
+ (function(global) {
2
+
3
+ "use strict";
4
+
5
+ var fabric = global.fabric || (global.fabric = { }),
6
+ extend = fabric.util.object.extend,
7
+ clone = fabric.util.object.clone,
8
+ toFixed = fabric.util.toFixed;
9
+
10
+ if (fabric.Text) {
11
+ fabric.warn('fabric.Text is already defined');
12
+ return;
13
+ }
14
+
15
+ var dimensionAffectingProps = {
16
+ fontSize: true,
17
+ fontWeight: true,
18
+ fontFamily: true,
19
+ textDecoration: true,
20
+ fontStyle: true,
21
+ lineHeight: true,
22
+ strokeStyle: true,
23
+ strokeWidth: true,
24
+ text: true
25
+ };
26
+
27
+ var stateProperties = fabric.Object.prototype.stateProperties.concat();
28
+ stateProperties.push(
29
+ 'fontFamily',
30
+ 'fontWeight',
31
+ 'fontSize',
32
+ 'path',
33
+ 'text',
34
+ 'textDecoration',
35
+ 'textShadow',
36
+ 'textAlign',
37
+ 'fontStyle',
38
+ 'lineHeight',
39
+ 'strokeStyle',
40
+ 'strokeWidth',
41
+ 'backgroundColor',
42
+ 'textBackgroundColor',
43
+ 'useNative'
44
+ );
45
+
46
+ /**
47
+ * Text class
48
+ * @class Text
49
+ * @extends fabric.Object
50
+ */
51
+ fabric.Text = fabric.util.createClass(fabric.Object, /** @scope fabric.Text.prototype */ {
52
+
53
+ /**
54
+ * Font size (in pixels)
55
+ * @property
56
+ * @type Number
57
+ */
58
+ fontSize: 40,
59
+
60
+ /**
61
+ * Font weight (e.g. bold, normal, 400, 600, 800)
62
+ * @property
63
+ * @type Number
64
+ */
65
+ fontWeight: 'normal',
66
+
67
+ /**
68
+ * Font family
69
+ * @property
70
+ * @type String
71
+ */
72
+ fontFamily: 'Times New Roman',
73
+
74
+ /**
75
+ * Text decoration (e.g. underline, overline)
76
+ * @property
77
+ * @type String
78
+ */
79
+ textDecoration: '',
80
+
81
+ /**
82
+ * Text shadow
83
+ * @property
84
+ * @type String | null
85
+ */
86
+ textShadow: '',
87
+
88
+ /**
89
+ * Text alignment. Possible values: "left", "center", or "right".
90
+ * @property
91
+ * @type String
92
+ */
93
+ textAlign: 'left',
94
+
95
+ /**
96
+ * Font style (e.g. italic)
97
+ * @property
98
+ * @type String
99
+ */
100
+ fontStyle: '',
101
+
102
+ /**
103
+ * Line height
104
+ * @property
105
+ * @type Number
106
+ */
107
+ lineHeight: 1.3,
108
+
109
+ /**
110
+ * Stroke style. When specified, text is rendered with stroke
111
+ * @property
112
+ * @type String
113
+ */
114
+ strokeStyle: '',
115
+
116
+ /**
117
+ * Stroke width
118
+ * @property
119
+ * @type Number
120
+ */
121
+ strokeWidth: 1,
122
+
123
+ /**
124
+ * Background color of an entire text box
125
+ * @property
126
+ * @type String
127
+ */
128
+ backgroundColor: '',
129
+
130
+ /**
131
+ * Background color of text lines
132
+ * @property
133
+ * @type String
134
+ */
135
+ textBackgroundColor: '',
136
+
137
+ /**
138
+ * URL of a font file, when using Cufon
139
+ * @property
140
+ * @type String | null
141
+ */
142
+ path: null,
143
+
144
+ /**
145
+ * Type of an object
146
+ * @property
147
+ * @type String
148
+ */
149
+ type: 'text',
150
+
151
+ /**
152
+ * Indicates whether canvas native text methods should be used to render text (otherwise, Cufon is used)
153
+ * @property
154
+ * @type Boolean
155
+ */
156
+ useNative: true,
157
+
158
+ /**
159
+ * List of properties to consider when checking if state of an object is changed (fabric.Object#hasStateChanged)
160
+ * as well as for history (undo/redo) purposes
161
+ * @property
162
+ * @type Array
163
+ */
164
+ stateProperties: stateProperties,
165
+
166
+ /**
167
+ * Constructor
168
+ * @method initialize
169
+ * @param {String} text
170
+ * @param {Object} [options]
171
+ * @return {fabric.Text} thisArg
172
+ */
173
+ initialize: function(text, options) {
174
+ options = options || { };
175
+
176
+ this.text = text;
177
+ this.setOptions(options);
178
+ this._initDimensions();
179
+ this.setCoords();
180
+ },
181
+
182
+ /**
183
+ * Renders text object on offscreen canvas, so that it would get dimensions
184
+ * @private
185
+ * @method _initDimensions
186
+ */
187
+ _initDimensions: function() {
188
+ var canvasEl = fabric.util.createCanvasElement();
189
+ this._render(canvasEl.getContext('2d'));
190
+ },
191
+
192
+ /**
193
+ * Returns string representation of an instance
194
+ * @method toString
195
+ * @return {String} String representation of text object
196
+ */
197
+ toString: function() {
198
+ return '#<fabric.Text (' + this.complexity() +
199
+ '): { "text": "' + this.text + '", "fontFamily": "' + this.fontFamily + '" }>';
200
+ },
201
+
202
+ /**
203
+ * @private
204
+ * @method _render
205
+ * @param {CanvasRenderingContext2D} ctx Context to render on
206
+ */
207
+ _render: function(ctx) {
208
+ if (typeof Cufon === 'undefined' || this.useNative === true) {
209
+ this._renderViaNative(ctx);
210
+ }
211
+ else {
212
+ this._renderViaCufon(ctx);
213
+ }
214
+ },
215
+
216
+ /**
217
+ * @private
218
+ * @method _renderViaCufon
219
+ */
220
+ _renderViaCufon: function(ctx) {
221
+ var o = Cufon.textOptions || (Cufon.textOptions = { });
222
+
223
+ // export options to be used by cufon.js
224
+ o.left = this.left;
225
+ o.top = this.top;
226
+ o.context = ctx;
227
+ o.color = this.fill;
228
+
229
+ var el = this._initDummyElementForCufon();
230
+
231
+ // set "cursor" to top/left corner
232
+ this.transform(ctx);
233
+
234
+ // draw text
235
+ Cufon.replaceElement(el, {
236
+ engine: 'canvas',
237
+ separate: 'none',
238
+ fontFamily: this.fontFamily,
239
+ fontWeight: this.fontWeight,
240
+ textDecoration: this.textDecoration,
241
+ textShadow: this.textShadow,
242
+ textAlign: this.textAlign,
243
+ fontStyle: this.fontStyle,
244
+ lineHeight: this.lineHeight,
245
+ strokeStyle: this.strokeStyle,
246
+ strokeWidth: this.strokeWidth,
247
+ backgroundColor: this.backgroundColor,
248
+ textBackgroundColor: this.textBackgroundColor
249
+ });
250
+
251
+ // update width, height
252
+ this.width = o.width;
253
+ this.height = o.height;
254
+
255
+ this._totalLineHeight = o.totalLineHeight;
256
+ this._fontAscent = o.fontAscent;
257
+ this._boundaries = o.boundaries;
258
+ this._shadowOffsets = o.shadowOffsets;
259
+ this._shadows = o.shadows || [ ];
260
+
261
+ el = null;
262
+
263
+ // need to set coords _after_ the width/height was retreived from Cufon
264
+ this.setCoords();
265
+ },
266
+
267
+ /**
268
+ * @private
269
+ * @method _render_native
270
+ * @param {CanvasRenderingContext2D} ctx Context to render on
271
+ */
272
+ _renderViaNative: function(ctx) {
273
+
274
+ this.transform(ctx);
275
+ this._setTextStyles(ctx);
276
+
277
+ var textLines = this.text.split(/\r?\n/);
278
+
279
+ this.width = this._getTextWidth(ctx, textLines);
280
+ this.height = this._getTextHeight(ctx, textLines);
281
+
282
+ this._renderTextBackground(ctx, textLines);
283
+
284
+ if (this.textAlign !== 'left' && this.textAlign !== 'justify') {
285
+ ctx.save();
286
+ ctx.translate(this.textAlign === 'center' ? (this.width / 2) : this.width, 0);
287
+ }
288
+
289
+ this._setTextShadow(ctx);
290
+ this._renderTextFill(ctx, textLines);
291
+ this.textShadow && ctx.restore();
292
+
293
+ this._renderTextStroke(ctx, textLines);
294
+ if (this.textAlign !== 'left' && this.textAlign !== 'justify') {
295
+ ctx.restore();
296
+ }
297
+
298
+ this._renderTextDecoration(ctx, textLines);
299
+ this._setBoundaries(ctx, textLines);
300
+ this._totalLineHeight = 0;
301
+
302
+ this.setCoords();
303
+ },
304
+
305
+ /**
306
+ * @private
307
+ * @method _setBoundaries
308
+ */
309
+ _setBoundaries: function(ctx, textLines) {
310
+ this._boundaries = [ ];
311
+
312
+ for (var i = 0, len = textLines.length; i < len; i++) {
313
+
314
+ var lineWidth = this._getLineWidth(ctx, textLines[i]);
315
+ var lineLeftOffset = this._getLineLeftOffset(lineWidth);
316
+
317
+ this._boundaries.push({
318
+ height: this.fontSize * this.lineHeight,
319
+ width: lineWidth,
320
+ left: lineLeftOffset
321
+ });
322
+ }
323
+ },
324
+
325
+ /**
326
+ * @private
327
+ * @method _setTextStyles
328
+ */
329
+ _setTextStyles: function(ctx) {
330
+ ctx.fillStyle = this.fill.toLive
331
+ ? this.fill.toLive(ctx)
332
+ : this.fill;
333
+ ctx.strokeStyle = this.strokeStyle;
334
+ ctx.lineWidth = this.strokeWidth;
335
+ ctx.textBaseline = 'alphabetic';
336
+ ctx.textAlign = this.textAlign;
337
+ ctx.font = this._getFontDeclaration();
338
+ },
339
+
340
+ /**
341
+ * @private
342
+ * @method _getTextHeight
343
+ */
344
+ _getTextHeight: function(ctx, textLines) {
345
+ return this.fontSize * textLines.length * this.lineHeight;
346
+ },
347
+
348
+ /**
349
+ * @private
350
+ * @method _getTextWidth
351
+ */
352
+ _getTextWidth: function(ctx, textLines) {
353
+ var maxWidth = ctx.measureText(textLines[0]).width;
354
+
355
+ for (var i = 1, len = textLines.length; i < len; i++) {
356
+ var currentLineWidth = ctx.measureText(textLines[i]).width;
357
+ if (currentLineWidth > maxWidth) {
358
+ maxWidth = currentLineWidth;
359
+ }
360
+ }
361
+ return maxWidth;
362
+ },
363
+
364
+ /**
365
+ * @private
366
+ * @method _setTextShadow
367
+ */
368
+ _setTextShadow: function(ctx) {
369
+ if (this.textShadow) {
370
+
371
+ // "rgba(0,0,0,0.2) 2px 2px 10px"
372
+ // "rgb(0, 100, 0) 0 0 5px"
373
+ // "red 2px 2px 1px"
374
+ // "#f55 123 345 567"
375
+ var reOffsetsAndBlur = /\s+(-?\d+)(?:px)?\s+(-?\d+)(?:px)?\s+(\d+)(?:px)?\s*/;
376
+
377
+ var shadowDeclaration = this.textShadow;
378
+ var offsetsAndBlur = reOffsetsAndBlur.exec(this.textShadow);
379
+ var shadowColor = shadowDeclaration.replace(reOffsetsAndBlur, '');
380
+
381
+ ctx.save();
382
+ ctx.shadowColor = shadowColor;
383
+ ctx.shadowOffsetX = parseInt(offsetsAndBlur[1], 10);
384
+ ctx.shadowOffsetY = parseInt(offsetsAndBlur[2], 10);
385
+ ctx.shadowBlur = parseInt(offsetsAndBlur[3], 10);
386
+
387
+ this._shadows = [{
388
+ blur: ctx.shadowBlur,
389
+ color: ctx.shadowColor,
390
+ offX: ctx.shadowOffsetX,
391
+ offY: ctx.shadowOffsetY
392
+ }];
393
+
394
+ this._shadowOffsets = [[
395
+ parseInt(ctx.shadowOffsetX, 10), parseInt(ctx.shadowOffsetY, 10)
396
+ ]];
397
+ }
398
+ },
399
+
400
+ /**
401
+ * @private
402
+ * @method _drawTextLine
403
+ * @param method
404
+ * @param ctx
405
+ * @param line
406
+ * @param left
407
+ * param top
408
+ */
409
+ _drawTextLine: function(method, ctx, line, left, top) {
410
+
411
+ // short-circuit
412
+ if (this.textAlign !== 'justify') {
413
+ ctx[method](line, left, top);
414
+ return;
415
+ }
416
+
417
+ var lineWidth = ctx.measureText(line).width;
418
+ var totalWidth = this.width;
419
+
420
+ if (totalWidth > lineWidth) {
421
+ // stretch the line
422
+
423
+ var words = line.split(/\s+/);
424
+ var wordsWidth = ctx.measureText(line.replace(/\s+/g, '')).width;
425
+ var widthDiff = totalWidth - wordsWidth;
426
+ var numSpaces = words.length - 1;
427
+ var spaceWidth = widthDiff / numSpaces;
428
+
429
+ var leftOffset = 0;
430
+ for (var i = 0, len = words.length; i < len; i++) {
431
+ ctx[method](words[i], left + leftOffset, top);
432
+ leftOffset += ctx.measureText(words[i]).width + spaceWidth;
433
+ }
434
+ }
435
+ else {
436
+ ctx[method](line, left, top);
437
+ }
438
+ },
439
+
440
+ /**
441
+ * @private
442
+ * @method _renderTextFill
443
+ */
444
+ _renderTextFill: function(ctx, textLines) {
445
+ this._boundaries = [ ];
446
+ for (var i = 0, len = textLines.length; i < len; i++) {
447
+ this._drawTextLine(
448
+ 'fillText',
449
+ ctx,
450
+ textLines[i],
451
+ -this.width / 2,
452
+ (-this.height / 2) + (i * this.fontSize * this.lineHeight) + this.fontSize
453
+ );
454
+ }
455
+ },
456
+
457
+ /**
458
+ * @private
459
+ * @method _renderTextStroke
460
+ */
461
+ _renderTextStroke: function(ctx, textLines) {
462
+ if (this.strokeStyle) {
463
+ ctx.beginPath();
464
+ for (var i = 0, len = textLines.length; i < len; i++) {
465
+ this._drawTextLine(
466
+ 'strokeText',
467
+ ctx,
468
+ textLines[i],
469
+ -this.width / 2,
470
+ (-this.height / 2) + (i * this.fontSize * this.lineHeight) + this.fontSize
471
+ );
472
+ }
473
+ ctx.closePath();
474
+ }
475
+ },
476
+
477
+ /**
478
+ * @private
479
+ * @method _renderTextBackground
480
+ */
481
+ _renderTextBackground: function(ctx, textLines) {
482
+ this._renderTextBoxBackground(ctx);
483
+ this._renderTextLinesBackground(ctx, textLines);
484
+ },
485
+
486
+ /**
487
+ * @private
488
+ * @method _renderTextBoxBackground
489
+ */
490
+ _renderTextBoxBackground: function(ctx) {
491
+ if (this.backgroundColor) {
492
+ ctx.save();
493
+ ctx.fillStyle = this.backgroundColor;
494
+
495
+ ctx.fillRect(
496
+ (-this.width / 2),
497
+ (-this.height / 2),
498
+ this.width,
499
+ this.height
500
+ );
501
+
502
+ ctx.restore();
503
+ }
504
+ },
505
+
506
+ /**
507
+ * @private
508
+ * @method _renderTextLinesBackground
509
+ */
510
+ _renderTextLinesBackground: function(ctx, textLines) {
511
+ if (this.textBackgroundColor) {
512
+ ctx.save();
513
+ ctx.fillStyle = this.textBackgroundColor;
514
+
515
+ for (var i = 0, len = textLines.length; i < len; i++) {
516
+
517
+ if (textLines[i] !== '') {
518
+
519
+ var lineWidth = this._getLineWidth(ctx, textLines[i]);
520
+ var lineLeftOffset = this._getLineLeftOffset(lineWidth);
521
+
522
+ ctx.fillRect(
523
+ (-this.width / 2) + lineLeftOffset,
524
+ (-this.height / 2) + (i * this.fontSize * this.lineHeight),
525
+ lineWidth,
526
+ this.fontSize * this.lineHeight
527
+ );
528
+ }
529
+ }
530
+ ctx.restore();
531
+ }
532
+ },
533
+
534
+ /**
535
+ * @private
536
+ * @method _getLineLeftOffset
537
+ */
538
+ _getLineLeftOffset: function(lineWidth) {
539
+ if (this.textAlign === 'center') {
540
+ return (this.width - lineWidth) / 2;
541
+ }
542
+ if (this.textAlign === 'right') {
543
+ return this.width - lineWidth;
544
+ }
545
+ return 0;
546
+ },
547
+
548
+ /**
549
+ * @private
550
+ * @method _getLineWidth
551
+ * @param ctx
552
+ * @param line
553
+ */
554
+ _getLineWidth: function(ctx, line) {
555
+ return this.textAlign === 'justify'
556
+ ? this.width
557
+ : ctx.measureText(line).width;
558
+ },
559
+
560
+ /**
561
+ * @private
562
+ * @method _renderTextDecoration
563
+ */
564
+ _renderTextDecoration: function(ctx, textLines) {
565
+
566
+ var halfOfVerticalBox = this._getTextHeight(ctx, textLines) / 2;
567
+ var _this = this;
568
+
569
+ /** @ignore */
570
+ function renderLinesAtOffset(offset) {
571
+ for (var i = 0, len = textLines.length; i < len; i++) {
572
+
573
+ var lineWidth = _this._getLineWidth(ctx, textLines[i]);
574
+ var lineLeftOffset = _this._getLineLeftOffset(lineWidth);
575
+
576
+ ctx.fillRect(
577
+ (-_this.width / 2) + lineLeftOffset,
578
+ (offset + (i * _this.fontSize * _this.lineHeight)) - halfOfVerticalBox,
579
+ lineWidth,
580
+ 1);
581
+ }
582
+ }
583
+
584
+ if (this.textDecoration.indexOf('underline') > -1) {
585
+ renderLinesAtOffset(this.fontSize);
586
+ }
587
+ if (this.textDecoration.indexOf('line-through') > -1) {
588
+ renderLinesAtOffset(this.fontSize / 2);
589
+ }
590
+ if (this.textDecoration.indexOf('overline') > -1) {
591
+ renderLinesAtOffset(0);
592
+ }
593
+ },
594
+
595
+ /**
596
+ * @private
597
+ * @method _getFontDeclaration
598
+ */
599
+ _getFontDeclaration: function() {
600
+ return [
601
+ // node-canvas needs "weight style", while browsers need "style weight"
602
+ (fabric.isLikelyNode ? this.fontWeight : this.fontStyle),
603
+ (fabric.isLikelyNode ? this.fontStyle : this.fontWeight),
604
+ this.fontSize + 'px',
605
+ (fabric.isLikelyNode ? ('"' + this.fontFamily + '"') : this.fontFamily)
606
+ ].join(' ');
607
+ },
608
+
609
+ /**
610
+ * @private
611
+ * @method _initDummyElement
612
+ */
613
+ _initDummyElementForCufon: function() {
614
+ var el = fabric.document.createElement('pre'),
615
+ container = fabric.document.createElement('div');
616
+
617
+ // Cufon doesn't play nice with textDecoration=underline if element doesn't have a parent
618
+ container.appendChild(el);
619
+
620
+ if (typeof G_vmlCanvasManager === 'undefined') {
621
+ el.innerHTML = this.text;
622
+ }
623
+ else {
624
+ // IE 7 & 8 drop newlines and white space on text nodes
625
+ // see: http://web.student.tuwien.ac.at/~e0226430/innerHtmlQuirk.html
626
+ // see: http://www.w3schools.com/dom/dom_mozilla_vs_ie.asp
627
+ el.innerText = this.text.replace(/\r?\n/gi, '\r');
628
+ }
629
+
630
+ el.style.fontSize = this.fontSize + 'px';
631
+ el.style.letterSpacing = 'normal';
632
+
633
+ return el;
634
+ },
635
+
636
+ /**
637
+ * Renders text instance on a specified context
638
+ * @method render
639
+ * @param ctx {CanvasRenderingContext2D} context to render on
640
+ */
641
+ render: function(ctx, noTransform) {
642
+ ctx.save();
643
+ this._render(ctx);
644
+ if (!noTransform && this.active) {
645
+ this.drawBorders(ctx);
646
+ this.drawControls(ctx);
647
+ }
648
+ ctx.restore();
649
+ },
650
+
651
+ /**
652
+ * Returns object representation of an instance
653
+ * @method toObject
654
+ * @param {Array} propertiesToInclude
655
+ * @return {Object} object representation of an instance
656
+ */
657
+ toObject: function(propertiesToInclude) {
658
+ return extend(this.callSuper('toObject', propertiesToInclude), {
659
+ text: this.text,
660
+ fontSize: this.fontSize,
661
+ fontWeight: this.fontWeight,
662
+ fontFamily: this.fontFamily,
663
+ fontStyle: this.fontStyle,
664
+ lineHeight: this.lineHeight,
665
+ textDecoration: this.textDecoration,
666
+ textShadow: this.textShadow,
667
+ textAlign: this.textAlign,
668
+ path: this.path,
669
+ strokeStyle: this.strokeStyle,
670
+ strokeWidth: this.strokeWidth,
671
+ backgroundColor: this.backgroundColor,
672
+ textBackgroundColor: this.textBackgroundColor,
673
+ useNative: this.useNative
674
+ });
675
+ },
676
+
677
+ /**
678
+ * Returns SVG representation of an instance
679
+ * @method toSVG
680
+ * @return {String} svg representation of an instance
681
+ */
682
+ toSVG: function() {
683
+
684
+ var textLines = this.text.split(/\r?\n/),
685
+ lineTopOffset = this.useNative
686
+ ? this.fontSize * this.lineHeight
687
+ : (-this._fontAscent - ((this._fontAscent / 5) * this.lineHeight)),
688
+
689
+ textLeftOffset = -(this.width/2),
690
+ textTopOffset = this.useNative
691
+ ? this.fontSize - 1
692
+ : (this.height/2) - (textLines.length * this.fontSize) - this._totalLineHeight,
693
+
694
+ textAndBg = this._getSVGTextAndBg(lineTopOffset, textLeftOffset, textLines),
695
+ shadowSpans = this._getSVGShadows(lineTopOffset, textLines);
696
+
697
+ // move top offset by an ascent
698
+ textTopOffset += (this._fontAscent ? ((this._fontAscent / 5) * this.lineHeight) : 0);
699
+
700
+ return [
701
+ '<g transform="', this.getSvgTransform(), '">',
702
+ textAndBg.textBgRects.join(''),
703
+ '<text ',
704
+ (this.fontFamily ? 'font-family="\'' + this.fontFamily + '\'" ': ''),
705
+ (this.fontSize ? 'font-size="' + this.fontSize + '" ': ''),
706
+ (this.fontStyle ? 'font-style="' + this.fontStyle + '" ': ''),
707
+ (this.fontWeight ? 'font-weight="' + this.fontWeight + '" ': ''),
708
+ (this.textDecoration ? 'text-decoration="' + this.textDecoration + '" ': ''),
709
+ 'style="', this.getSvgStyles(), '" ',
710
+ /* svg starts from left/bottom corner so we normalize height */
711
+ 'transform="translate(', toFixed(textLeftOffset, 2), ' ', toFixed(textTopOffset, 2), ')">',
712
+ shadowSpans.join(''),
713
+ textAndBg.textSpans.join(''),
714
+ '</text>',
715
+ '</g>'
716
+ ].join('');
717
+ },
718
+
719
+ /**
720
+ * @private
721
+ * @method _getSVGShadows
722
+ */
723
+ _getSVGShadows: function(lineTopOffset, textLines) {
724
+ var shadowSpans = [], j, i, jlen, ilen, lineTopOffsetMultiplier = 1;
725
+
726
+ if (!this._shadows || !this._boundaries) {
727
+ return shadowSpans;
728
+ }
729
+
730
+ for (j = 0, jlen = this._shadows.length; j < jlen; j++) {
731
+ for (i = 0, ilen = textLines.length; i < ilen; i++) {
732
+ if (textLines[i] !== '') {
733
+ var lineLeftOffset = (this._boundaries && this._boundaries[i]) ? this._boundaries[i].left : 0;
734
+ shadowSpans.push(
735
+ '<tspan x="',
736
+ toFixed((lineLeftOffset + lineTopOffsetMultiplier) + this._shadowOffsets[j][0], 2),
737
+ ((i === 0 || this.useNative) ? '" y' : '" dy'), '="',
738
+ toFixed(this.useNative
739
+ ? ((lineTopOffset * i) - this.height / 2 + this._shadowOffsets[j][1])
740
+ : (lineTopOffset + (i === 0 ? this._shadowOffsets[j][1] : 0)), 2),
741
+ '" ',
742
+ this._getFillAttributes(this._shadows[j].color), '>',
743
+ fabric.util.string.escapeXml(textLines[i]),
744
+ '</tspan>');
745
+ lineTopOffsetMultiplier = 1;
746
+ } else {
747
+ // in some environments (e.g. IE 7 & 8) empty tspans are completely ignored, using a lineTopOffsetMultiplier
748
+ // prevents empty tspans
749
+ lineTopOffsetMultiplier++;
750
+ }
751
+ }
752
+ }
753
+ return shadowSpans;
754
+ },
755
+
756
+ /**
757
+ * @private
758
+ * @method _getSVGTextAndBg
759
+ */
760
+ _getSVGTextAndBg: function(lineTopOffset, textLeftOffset, textLines) {
761
+ var textSpans = [ ], textBgRects = [ ], i, lineLeftOffset, len, lineTopOffsetMultiplier = 1;
762
+
763
+ // bounding-box background
764
+ if (this.backgroundColor && this._boundaries) {
765
+ textBgRects.push(
766
+ '<rect ',
767
+ this._getFillAttributes(this.backgroundColor),
768
+ ' x="',
769
+ toFixed(-this.width / 2, 2),
770
+ '" y="',
771
+ toFixed(-this.height / 2, 2),
772
+ '" width="',
773
+ toFixed(this.width, 2),
774
+ '" height="',
775
+ toFixed(this.height, 2),
776
+ '"></rect>');
777
+ }
778
+
779
+ // text and text-background
780
+ for (i = 0, len = textLines.length; i < len; i++) {
781
+ if (textLines[i] !== '') {
782
+ lineLeftOffset = (this._boundaries && this._boundaries[i]) ? toFixed(this._boundaries[i].left, 2) : 0;
783
+ textSpans.push(
784
+ '<tspan x="',
785
+ lineLeftOffset, '" ',
786
+ (i === 0 || this.useNative ? 'y' : 'dy'), '="',
787
+ toFixed(this.useNative ? ((lineTopOffset * i) - this.height / 2) : (lineTopOffset * lineTopOffsetMultiplier), 2) , '" ',
788
+ // doing this on <tspan> elements since setting opacity on containing <text> one doesn't work in Illustrator
789
+ this._getFillAttributes(this.fill), '>',
790
+ fabric.util.string.escapeXml(textLines[i]),
791
+ '</tspan>'
792
+ );
793
+ lineTopOffsetMultiplier = 1;
794
+ }
795
+ else {
796
+ // in some environments (e.g. IE 7 & 8) empty tspans are completely ignored, using a lineTopOffsetMultiplier
797
+ // prevents empty tspans
798
+ lineTopOffsetMultiplier++;
799
+ }
800
+
801
+ if (!this.textBackgroundColor || !this._boundaries) continue;
802
+
803
+ textBgRects.push(
804
+ '<rect ',
805
+ this._getFillAttributes(this.textBackgroundColor),
806
+ ' x="',
807
+ toFixed(textLeftOffset + this._boundaries[i].left, 2),
808
+ '" y="',
809
+ /* an offset that seems to straighten things out */
810
+ toFixed((lineTopOffset * i) - this.height / 2, 2),
811
+ '" width="',
812
+ toFixed(this._boundaries[i].width, 2),
813
+ '" height="',
814
+ toFixed(this._boundaries[i].height, 2),
815
+ '"></rect>');
816
+ }
817
+ return {
818
+ textSpans: textSpans,
819
+ textBgRects: textBgRects
820
+ };
821
+ },
822
+
823
+ /**
824
+ * Adobe Illustrator (at least CS5) is unable to render rgba()-based fill values
825
+ * we work around it by "moving" alpha channel into opacity attribute and setting fill's alpha to 1
826
+ *
827
+ * @private
828
+ * @method _getFillAttributes
829
+ */
830
+ _getFillAttributes: function(value) {
831
+ var fillColor = value ? new fabric.Color(value) : '';
832
+ if (!fillColor || !fillColor.getSource() || fillColor.getAlpha() === 1) {
833
+ return 'fill="' + value + '"';
834
+ }
835
+ return 'opacity="' + fillColor.getAlpha() + '" fill="' + fillColor.setAlpha(1).toRgb() + '"';
836
+ },
837
+
838
+ /**
839
+ * Sets "color" of an instance (alias of `set('fill', &hellip;)`)
840
+ * @method setColor
841
+ * @param {String} value
842
+ * @return {fabric.Text} thisArg
843
+ * @chainable
844
+ */
845
+ setColor: function(value) {
846
+ this.set('fill', value);
847
+ return this;
848
+ },
849
+
850
+ /**
851
+ * Returns actual text value of an instance
852
+ * @method getText
853
+ * @return {String}
854
+ */
855
+ getText: function() {
856
+ return this.text;
857
+ },
858
+
859
+ /**
860
+ * Sets specified property to a specified value
861
+ * @method set
862
+ * @param {String} name
863
+ * @param {Any} value
864
+ * @return {fabric.Text} thisArg
865
+ * @chainable
866
+ */
867
+ _set: function(name, value) {
868
+ if (name === 'fontFamily' && this.path) {
869
+ this.path = this.path.replace(/(.*?)([^\/]*)(\.font\.js)/, '$1' + value + '$3');
870
+ }
871
+ this.callSuper('_set', name, value);
872
+
873
+ if (name in dimensionAffectingProps) {
874
+ this._initDimensions();
875
+ this.setCoords();
876
+ }
877
+ }
878
+ });
879
+
880
+ /**
881
+ * List of attribute names to account for when parsing SVG element (used by {@link fabric.Text.fromElement})
882
+ * @static
883
+ */
884
+ fabric.Text.ATTRIBUTE_NAMES =
885
+ ('x y fill fill-opacity opacity stroke stroke-width transform ' +
886
+ 'font-family font-style font-weight font-size text-decoration').split(' ');
887
+
888
+ /**
889
+ * Returns fabric.Text instance from an object representation
890
+ * @static
891
+ * @method fromObject
892
+ * @param {Object} object to create an instance from
893
+ * @return {fabric.Text} an instance
894
+ */
895
+ fabric.Text.fromObject = function(object) {
896
+ return new fabric.Text(object.text, clone(object));
897
+ };
898
+
899
+ /**
900
+ * Returns fabric.Text instance from an SVG element (<b>not yet implemented</b>)
901
+ * @static
902
+ * @method fabric.Text.fromElement
903
+ * @param element
904
+ * @param options
905
+ * @return {fabric.Text} an instance
906
+ */
907
+ fabric.Text.fromElement = function(element, options) {
908
+
909
+ if (!element) {
910
+ return null;
911
+ }
912
+
913
+ var parsedAttributes = fabric.parseAttributes(element, fabric.Text.ATTRIBUTE_NAMES);
914
+ options = fabric.util.object.extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes);
915
+
916
+ var text = new fabric.Text(element.textContent, options);
917
+
918
+ /*
919
+ Adjust positioning:
920
+ x/y attributes in SVG correspond to the bottom-left corner of text bounding box
921
+ top/left properties in Fabric correspond to center point of text bounding box
922
+ */
923
+
924
+ text.set({
925
+ left: text.getLeft() + text.getWidth() / 2,
926
+ top: text.getTop() - text.getHeight() / 2
927
+ });
928
+
929
+ return text;
930
+ };
931
+
932
+ fabric.util.createAccessors(fabric.Text);
933
+
934
+ })(typeof exports !== 'undefined' ? exports : this);