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,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);