fabric-rails 1.0.12.1 → 1.2.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 (76) hide show
  1. data/CHANGELOG.md +4 -0
  2. data/README.md +1 -1
  3. data/lib/fabric/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/cufon.js +1226 -0
  5. data/vendor/assets/javascripts/event.js +20 -20
  6. data/vendor/assets/javascripts/excanvas.js +1464 -0
  7. data/vendor/assets/javascripts/fabric.js +56 -33
  8. data/vendor/assets/javascripts/fabric/HEADER.js +2 -4
  9. data/vendor/assets/javascripts/fabric/brushes/base_brush.class.js +96 -0
  10. data/vendor/assets/javascripts/fabric/brushes/circle_brush.class.js +99 -0
  11. data/vendor/assets/javascripts/fabric/brushes/pattern_brush.class.js +55 -0
  12. data/vendor/assets/javascripts/fabric/{freedrawing.class.js → brushes/pencil_brush.class.js} +73 -65
  13. data/vendor/assets/javascripts/fabric/brushes/spray_brush.class.js +157 -0
  14. data/vendor/assets/javascripts/fabric/canvas.class.js +154 -135
  15. data/vendor/assets/javascripts/fabric/color.class.js +195 -29
  16. data/vendor/assets/javascripts/fabric/filters/brightness_filter.class.js +70 -0
  17. data/vendor/assets/javascripts/fabric/filters/convolute_filter.class.js +122 -0
  18. data/vendor/assets/javascripts/fabric/filters/gradienttransparency_filter.class.js +69 -0
  19. data/vendor/assets/javascripts/fabric/filters/grayscale_filter.class.js +61 -0
  20. data/vendor/assets/javascripts/fabric/filters/invert_filter.class.js +57 -0
  21. data/vendor/assets/javascripts/fabric/filters/noise_filter.class.js +73 -0
  22. data/vendor/assets/javascripts/fabric/filters/pixelate_filter.class.js +98 -0
  23. data/vendor/assets/javascripts/fabric/filters/removewhite_filter.class.js +86 -0
  24. data/vendor/assets/javascripts/fabric/filters/sepia2_filter.class.js +61 -0
  25. data/vendor/assets/javascripts/fabric/filters/sepia_filter.class.js +58 -0
  26. data/vendor/assets/javascripts/fabric/filters/tint_filter.class.js +80 -0
  27. data/vendor/assets/javascripts/fabric/gradient.class.js +232 -80
  28. data/vendor/assets/javascripts/fabric/intersection.class.js +10 -28
  29. data/vendor/assets/javascripts/fabric/log.js +0 -2
  30. data/vendor/assets/javascripts/fabric/{canvas_animation.mixin.js → mixins/canvas_animation.mixin.js} +3 -6
  31. data/vendor/assets/javascripts/fabric/mixins/canvas_dataurl_exporter.mixin.js +137 -0
  32. data/vendor/assets/javascripts/fabric/{canvas_events.mixin.js → mixins/canvas_events.mixin.js} +97 -144
  33. data/vendor/assets/javascripts/fabric/{canvas_gestures.mixin.js → mixins/canvas_gestures.mixin.js} +4 -8
  34. data/vendor/assets/javascripts/fabric/{canvas_serialization.mixin.js → mixins/canvas_serialization.mixin.js} +19 -14
  35. data/vendor/assets/javascripts/fabric/mixins/collection.mixin.js +137 -0
  36. data/vendor/assets/javascripts/fabric/{object_geometry.mixin.js → mixins/object_geometry.mixin.js} +130 -47
  37. data/vendor/assets/javascripts/fabric/{object_interactivity.mixin.js → mixins/object_interactivity.mixin.js} +21 -102
  38. data/vendor/assets/javascripts/fabric/{object_origin.mixin.js → mixins/object_origin.mixin.js} +36 -26
  39. data/vendor/assets/javascripts/fabric/{object_straightening.mixin.js → mixins/object_straightening.mixin.js} +4 -9
  40. data/vendor/assets/javascripts/fabric/{observable.mixin.js → mixins/observable.mixin.js} +27 -35
  41. data/vendor/assets/javascripts/fabric/mixins/stateful.mixin.js +45 -0
  42. data/vendor/assets/javascripts/fabric/node.js +62 -26
  43. data/vendor/assets/javascripts/fabric/parser.js +181 -58
  44. data/vendor/assets/javascripts/fabric/pattern.class.js +43 -14
  45. data/vendor/assets/javascripts/fabric/point.class.js +4 -43
  46. data/vendor/assets/javascripts/fabric/shadow.class.js +19 -19
  47. data/vendor/assets/javascripts/fabric/{circle.class.js → shapes/circle.class.js} +32 -29
  48. data/vendor/assets/javascripts/fabric/{ellipse.class.js → shapes/ellipse.class.js} +45 -27
  49. data/vendor/assets/javascripts/fabric/{group.class.js → shapes/group.class.js} +67 -169
  50. data/vendor/assets/javascripts/fabric/{image.class.js → shapes/image.class.js} +134 -72
  51. data/vendor/assets/javascripts/fabric/{line.class.js → shapes/line.class.js} +67 -36
  52. data/vendor/assets/javascripts/fabric/{object.class.js → shapes/object.class.js} +394 -252
  53. data/vendor/assets/javascripts/fabric/{path.class.js → shapes/path.class.js} +89 -174
  54. data/vendor/assets/javascripts/fabric/{path_group.class.js → shapes/path_group.class.js} +12 -18
  55. data/vendor/assets/javascripts/fabric/{polygon.class.js → shapes/polygon.class.js} +64 -38
  56. data/vendor/assets/javascripts/fabric/{polyline.class.js → shapes/polyline.class.js} +64 -39
  57. data/vendor/assets/javascripts/fabric/{rect.class.js → shapes/rect.class.js} +78 -112
  58. data/vendor/assets/javascripts/fabric/{text.class.js → shapes/text.class.js} +264 -270
  59. data/vendor/assets/javascripts/fabric/shapes/text.cufon.js +79 -0
  60. data/vendor/assets/javascripts/fabric/{triangle.class.js → shapes/triangle.class.js} +46 -26
  61. data/vendor/assets/javascripts/fabric/static_canvas.class.js +134 -358
  62. data/vendor/assets/javascripts/fabric/util/anim_ease.js +2 -31
  63. data/vendor/assets/javascripts/fabric/util/dom_event.js +21 -7
  64. data/vendor/assets/javascripts/fabric/util/dom_misc.js +49 -39
  65. data/vendor/assets/javascripts/fabric/util/dom_request.js +1 -2
  66. data/vendor/assets/javascripts/fabric/util/dom_style.js +1 -2
  67. data/vendor/assets/javascripts/fabric/util/lang_array.js +19 -13
  68. data/vendor/assets/javascripts/fabric/util/lang_class.js +1 -2
  69. data/vendor/assets/javascripts/fabric/util/lang_function.js +3 -1
  70. data/vendor/assets/javascripts/fabric/util/lang_object.js +5 -5
  71. data/vendor/assets/javascripts/fabric/util/lang_string.js +7 -5
  72. data/vendor/assets/javascripts/fabric/util/misc.js +207 -42
  73. metadata +47 -29
  74. data/vendor/assets/javascripts/fabric/image_filters.js +0 -809
  75. data/vendor/assets/javascripts/fabric/scout.js +0 -45
  76. data/vendor/assets/javascripts/fabric/stateful.js +0 -88
@@ -1,3 +1,7 @@
1
+ ## 1.2.1 (10 July 2013)
2
+
3
+ - fabric.js version 1.2.1
4
+
1
5
  ## 1.0.12.1 (28 February 2013)
2
6
 
3
7
  - now use fabric.js source files and compile as needed via sprockets
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  This gem provides:
6
6
 
7
- * fabric.js 1.0.12
7
+ * fabric.js 1.2.1
8
8
 
9
9
  ## Installation
10
10
 
@@ -1,6 +1,6 @@
1
1
  module Fabric
2
2
  module Rails
3
- VERSION = "1.0.12.1"
4
- FABRIC_VERSION = "1.0.12.1"
3
+ VERSION = "1.2.1"
4
+ FABRIC_VERSION = "1.2.1"
5
5
  end
6
6
  end
@@ -0,0 +1,1226 @@
1
+ /*!
2
+ * Copyright (c) 2009 Simo Kinnunen.
3
+ * Licensed under the MIT license.
4
+ */
5
+
6
+ var Cufon = (function() {
7
+
8
+ /** @ignore */
9
+ var api = function() {
10
+ return api.replace.apply(null, arguments);
11
+ };
12
+
13
+ /** @ignore */
14
+ var DOM = api.DOM = {
15
+
16
+ ready: (function() {
17
+
18
+ var complete = false, readyStatus = { loaded: 1, complete: 1 };
19
+
20
+ var queue = [], /** @ignore */ perform = function() {
21
+ if (complete) return;
22
+ complete = true;
23
+ for (var fn; fn = queue.shift(); fn());
24
+ };
25
+
26
+ // Gecko, Opera, WebKit r26101+
27
+
28
+ if (fabric.document.addEventListener) {
29
+ fabric.document.addEventListener('DOMContentLoaded', perform, false);
30
+ fabric.window.addEventListener('pageshow', perform, false); // For cached Gecko pages
31
+ }
32
+
33
+ // Old WebKit, Internet Explorer
34
+
35
+ if (!fabric.window.opera && fabric.document.readyState) (function() {
36
+ readyStatus[fabric.document.readyState] ? perform() : setTimeout(arguments.callee, 10);
37
+ })();
38
+
39
+ // Internet Explorer
40
+
41
+ if (fabric.document.readyState && fabric.document.createStyleSheet) (function() {
42
+ try {
43
+ fabric.document.body.doScroll('left');
44
+ perform();
45
+ }
46
+ catch (e) {
47
+ setTimeout(arguments.callee, 1);
48
+ }
49
+ })();
50
+
51
+ addEvent(fabric.window, 'load', perform); // Fallback
52
+
53
+ return function(listener) {
54
+ if (!arguments.length) perform();
55
+ else complete ? listener() : queue.push(listener);
56
+ };
57
+
58
+ })()
59
+
60
+ };
61
+
62
+ /** @ignore */
63
+ var CSS = api.CSS = /** @ignore */ {
64
+
65
+ /** @ignore */
66
+ Size: function(value, base) {
67
+
68
+ this.value = parseFloat(value);
69
+ this.unit = String(value).match(/[a-z%]*$/)[0] || 'px';
70
+
71
+ /** @ignore */
72
+ this.convert = function(value) {
73
+ return value / base * this.value;
74
+ };
75
+
76
+ /** @ignore */
77
+ this.convertFrom = function(value) {
78
+ return value / this.value * base;
79
+ };
80
+
81
+ /** @ignore */
82
+ this.toString = function() {
83
+ return this.value + this.unit;
84
+ };
85
+
86
+ },
87
+
88
+ /** @ignore */
89
+ getStyle: function(el) {
90
+ return new Style(el.style);
91
+ /*
92
+ var view = document.defaultView;
93
+ if (view && view.getComputedStyle) return new Style(view.getComputedStyle(el, null));
94
+ if (el.currentStyle) return new Style(el.currentStyle);
95
+ return new Style(el.style);
96
+ */
97
+ },
98
+
99
+ quotedList: cached(function(value) {
100
+ // doesn't work properly with empty quoted strings (""), but
101
+ // it's not worth the extra code.
102
+ var list = [], re = /\s*((["'])([\s\S]*?[^\\])\2|[^,]+)\s*/g, match;
103
+ while (match = re.exec(value)) list.push(match[3] || match[1]);
104
+ return list;
105
+ }),
106
+
107
+ ready: (function() {
108
+
109
+ var complete = false;
110
+
111
+ var queue = [], perform = function() {
112
+ complete = true;
113
+ for (var fn; fn = queue.shift(); fn());
114
+ };
115
+
116
+ // Safari 2 does not include <style> elements in document.styleSheets.
117
+ // Safari 2 also does not support Object.prototype.propertyIsEnumerable.
118
+
119
+ var styleElements = Object.prototype.propertyIsEnumerable ? elementsByTagName('style') : { length: 0 };
120
+ var linkElements = elementsByTagName('link');
121
+
122
+ DOM.ready(function() {
123
+ // These checks are actually only needed for WebKit-based browsers, but don't really hurt other browsers.
124
+ var linkStyles = 0, link;
125
+ for (var i = 0, l = linkElements.length; link = linkElements[i], i < l; ++i) {
126
+ // WebKit does not load alternate stylesheets.
127
+ if (!link.disabled && link.rel.toLowerCase() == 'stylesheet') ++linkStyles;
128
+ }
129
+ if (fabric.document.styleSheets.length >= styleElements.length + linkStyles) perform();
130
+ else setTimeout(arguments.callee, 10);
131
+ });
132
+
133
+ return function(listener) {
134
+ if (complete) listener();
135
+ else queue.push(listener);
136
+ };
137
+
138
+ })(),
139
+
140
+ /** @ignore */
141
+ supports: function(property, value) {
142
+ var checker = fabric.document.createElement('span').style;
143
+ if (checker[property] === undefined) return false;
144
+ checker[property] = value;
145
+ return checker[property] === value;
146
+ },
147
+
148
+ /** @ignore */
149
+ textAlign: function(word, style, position, wordCount) {
150
+ if (style.get('textAlign') == 'right') {
151
+ if (position > 0) word = ' ' + word;
152
+ }
153
+ else if (position < wordCount - 1) word += ' ';
154
+ return word;
155
+ },
156
+
157
+ /** @ignore */
158
+ textDecoration: function(el, style) {
159
+ if (!style) style = this.getStyle(el);
160
+ var types = {
161
+ underline: null,
162
+ overline: null,
163
+ 'line-through': null
164
+ };
165
+ for (var search = el; search.parentNode && search.parentNode.nodeType == 1; ) {
166
+ var foundAll = true;
167
+ for (var type in types) {
168
+ if (types[type]) continue;
169
+ if (style.get('textDecoration').indexOf(type) != -1) types[type] = style.get('color');
170
+ foundAll = false;
171
+ }
172
+ if (foundAll) break; // this is rather unlikely to happen
173
+ style = this.getStyle(search = search.parentNode);
174
+ }
175
+ return types;
176
+ },
177
+
178
+ textShadow: cached(function(value) {
179
+ if (value == 'none') return null;
180
+ var shadows = [], currentShadow = {}, result, offCount = 0;
181
+ var re = /(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)|(-?[\d.]+[a-z%]*)|,/ig;
182
+ while (result = re.exec(value)) {
183
+ if (result[0] == ',') {
184
+ shadows.push(currentShadow);
185
+ currentShadow = {}, offCount = 0;
186
+ }
187
+ else if (result[1]) {
188
+ currentShadow.color = result[1];
189
+ }
190
+ else {
191
+ currentShadow[[ 'offX', 'offY', 'blur' ][offCount++]] = result[2];
192
+ }
193
+ }
194
+ shadows.push(currentShadow);
195
+ return shadows;
196
+ }),
197
+
198
+ color: cached(function(value) {
199
+ var parsed = {};
200
+ parsed.color = value.replace(/^rgba\((.*?),\s*([\d.]+)\)/, function($0, $1, $2) {
201
+ parsed.opacity = parseFloat($2);
202
+ return 'rgb(' + $1 + ')';
203
+ });
204
+ return parsed;
205
+ }),
206
+
207
+ /** @ignore */
208
+ textTransform: function(text, style) {
209
+ return text[{
210
+ uppercase: 'toUpperCase',
211
+ lowercase: 'toLowerCase'
212
+ }[style.get('textTransform')] || 'toString']();
213
+ }
214
+
215
+ };
216
+
217
+ function Font(data) {
218
+
219
+ var face = this.face = data.face;
220
+ this.glyphs = data.glyphs;
221
+ this.w = data.w;
222
+ this.baseSize = parseInt(face['units-per-em'], 10);
223
+
224
+ this.family = face['font-family'].toLowerCase();
225
+ this.weight = face['font-weight'];
226
+ this.style = face['font-style'] || 'normal';
227
+
228
+ this.viewBox = (function () {
229
+ var parts = face.bbox.split(/\s+/);
230
+ var box = {
231
+ minX: parseInt(parts[0], 10),
232
+ minY: parseInt(parts[1], 10),
233
+ maxX: parseInt(parts[2], 10),
234
+ maxY: parseInt(parts[3], 10)
235
+ };
236
+ box.width = box.maxX - box.minX,
237
+ box.height = box.maxY - box.minY;
238
+ /** @ignore */
239
+ box.toString = function() {
240
+ return [ this.minX, this.minY, this.width, this.height ].join(' ');
241
+ };
242
+ return box;
243
+ })();
244
+
245
+ this.ascent = -parseInt(face.ascent, 10);
246
+ this.descent = -parseInt(face.descent, 10);
247
+
248
+ this.height = -this.ascent + this.descent;
249
+
250
+ }
251
+
252
+ function FontFamily() {
253
+
254
+ var styles = {}, mapping = {
255
+ oblique: 'italic',
256
+ italic: 'oblique'
257
+ };
258
+
259
+ this.add = function(font) {
260
+ (styles[font.style] || (styles[font.style] = {}))[font.weight] = font;
261
+ };
262
+
263
+ /** @ignore */
264
+ this.get = function(style, weight) {
265
+ var weights = styles[style] || styles[mapping[style]]
266
+ || styles.normal || styles.italic || styles.oblique;
267
+ if (!weights) return null;
268
+ // we don't have to worry about "bolder" and "lighter"
269
+ // because IE's currentStyle returns a numeric value for it,
270
+ // and other browsers use the computed value anyway
271
+ weight = {
272
+ normal: 400,
273
+ bold: 700
274
+ }[weight] || parseInt(weight, 10);
275
+ if (weights[weight]) return weights[weight];
276
+ // http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight
277
+ // Gecko uses x99/x01 for lighter/bolder
278
+ var up = {
279
+ 1: 1,
280
+ 99: 0
281
+ }[weight % 100], alts = [], min, max;
282
+ if (up === undefined) up = weight > 400;
283
+ if (weight == 500) weight = 400;
284
+ for (var alt in weights) {
285
+ alt = parseInt(alt, 10);
286
+ if (!min || alt < min) min = alt;
287
+ if (!max || alt > max) max = alt;
288
+ alts.push(alt);
289
+ }
290
+ if (weight < min) weight = min;
291
+ if (weight > max) weight = max;
292
+ alts.sort(function(a, b) {
293
+ return (up
294
+ ? (a > weight && b > weight) ? a < b : a > b
295
+ : (a < weight && b < weight) ? a > b : a < b) ? -1 : 1;
296
+ });
297
+ return weights[alts[0]];
298
+ };
299
+
300
+ }
301
+
302
+ function HoverHandler() {
303
+
304
+ function contains(node, anotherNode) {
305
+ if (node.contains) return node.contains(anotherNode);
306
+ return node.compareDocumentPosition(anotherNode) & 16;
307
+ }
308
+
309
+ function onOverOut(e) {
310
+ var related = e.relatedTarget;
311
+ if (!related || contains(this, related)) return;
312
+ trigger(this);
313
+ }
314
+
315
+ function onEnterLeave(e) {
316
+ trigger(this);
317
+ }
318
+
319
+ function trigger(el) {
320
+ // A timeout is needed so that the event can actually "happen"
321
+ // before replace is triggered. This ensures that styles are up
322
+ // to date.
323
+ setTimeout(function() {
324
+ api.replace(el, sharedStorage.get(el).options, true);
325
+ }, 10);
326
+ }
327
+
328
+ this.attach = function(el) {
329
+ if (el.onmouseenter === undefined) {
330
+ addEvent(el, 'mouseover', onOverOut);
331
+ addEvent(el, 'mouseout', onOverOut);
332
+ }
333
+ else {
334
+ addEvent(el, 'mouseenter', onEnterLeave);
335
+ addEvent(el, 'mouseleave', onEnterLeave);
336
+ }
337
+ };
338
+
339
+ }
340
+
341
+ function Storage() {
342
+
343
+ var map = {}, at = 0;
344
+
345
+ function identify(el) {
346
+ return el.cufid || (el.cufid = ++at);
347
+ }
348
+
349
+ /** @ignore */
350
+ this.get = function(el) {
351
+ var id = identify(el);
352
+ return map[id] || (map[id] = {});
353
+ };
354
+
355
+ }
356
+
357
+ function Style(style) {
358
+
359
+ var custom = {}, sizes = {};
360
+
361
+ this.get = function(property) {
362
+ return custom[property] != undefined ? custom[property] : style[property];
363
+ };
364
+
365
+ this.getSize = function(property, base) {
366
+ return sizes[property] || (sizes[property] = new CSS.Size(this.get(property), base));
367
+ };
368
+
369
+ this.extend = function(styles) {
370
+ for (var property in styles) custom[property] = styles[property];
371
+ return this;
372
+ };
373
+
374
+ }
375
+
376
+ function addEvent(el, type, listener) {
377
+ if (el.addEventListener) {
378
+ el.addEventListener(type, listener, false);
379
+ }
380
+ else if (el.attachEvent) {
381
+ el.attachEvent('on' + type, function() {
382
+ return listener.call(el, fabric.window.event);
383
+ });
384
+ }
385
+ }
386
+
387
+ function attach(el, options) {
388
+ var storage = sharedStorage.get(el);
389
+ if (storage.options) return el;
390
+ if (options.hover && options.hoverables[el.nodeName.toLowerCase()]) {
391
+ hoverHandler.attach(el);
392
+ }
393
+ storage.options = options;
394
+ return el;
395
+ }
396
+
397
+ function cached(fun) {
398
+ var cache = {};
399
+ return function(key) {
400
+ if (!cache.hasOwnProperty(key)) cache[key] = fun.apply(null, arguments);
401
+ return cache[key];
402
+ };
403
+ }
404
+
405
+ function getFont(el, style) {
406
+ if (!style) style = CSS.getStyle(el);
407
+ var families = CSS.quotedList(style.get('fontFamily').toLowerCase()), family;
408
+ for (var i = 0, l = families.length; i < l; ++i) {
409
+ family = families[i];
410
+ if (fonts[family]) return fonts[family].get(style.get('fontStyle'), style.get('fontWeight'));
411
+ }
412
+ return null;
413
+ }
414
+
415
+ function elementsByTagName(query) {
416
+ return fabric.document.getElementsByTagName(query);
417
+ }
418
+
419
+ function merge() {
420
+ var merged = {}, key;
421
+ for (var i = 0, l = arguments.length; i < l; ++i) {
422
+ for (key in arguments[i]) merged[key] = arguments[i][key];
423
+ }
424
+ return merged;
425
+ }
426
+
427
+ function process(font, text, style, options, node, el) {
428
+
429
+ var separate = options.separate;
430
+ if (separate == 'none') return engines[options.engine].apply(null, arguments);
431
+ var fragment = fabric.document.createDocumentFragment(), processed;
432
+ var parts = text.split(separators[separate]), needsAligning = (separate == 'words');
433
+ if (needsAligning && HAS_BROKEN_REGEXP) {
434
+ // @todo figure out a better way to do this
435
+ if (/^\s/.test(text)) parts.unshift('');
436
+ if (/\s$/.test(text)) parts.push('');
437
+ }
438
+ for (var i = 0, l = parts.length; i < l; ++i) {
439
+ processed = engines[options.engine](font,
440
+ needsAligning ? CSS.textAlign(parts[i], style, i, l) : parts[i],
441
+ style, options, node, el, i < l - 1);
442
+ if (processed) fragment.appendChild(processed);
443
+ }
444
+ return fragment;
445
+ }
446
+
447
+ /** @ignore */
448
+ function replaceElement(el, options) {
449
+ var font, style, nextNode, redraw;
450
+ for (var node = attach(el, options).firstChild; node; node = nextNode) {
451
+ nextNode = node.nextSibling;
452
+ redraw = false;
453
+ if (node.nodeType == 1) {
454
+ if (!node.firstChild) continue;
455
+ if (!/cufon/.test(node.className)) {
456
+ arguments.callee(node, options);
457
+ continue;
458
+ }
459
+ else redraw = true;
460
+ }
461
+ if (!style) style = CSS.getStyle(el).extend(options);
462
+ if (!font) font = getFont(el, style);
463
+
464
+ if (!font) continue;
465
+ if (redraw) {
466
+ engines[options.engine](font, null, style, options, node, el);
467
+ continue;
468
+ }
469
+ var text = node.data;
470
+ //for some reason, the carriage return is not stripped by IE but "\n" is, so let's keep \r as a new line marker...
471
+ if (typeof G_vmlCanvasManager != 'undefined') {
472
+ text = text.replace(/\r/g, "\n");
473
+ }
474
+ if (text === '') continue;
475
+ var processed = process(font, text, style, options, node, el);
476
+ if (processed) node.parentNode.replaceChild(processed, node);
477
+ else node.parentNode.removeChild(node);
478
+ }
479
+ }
480
+
481
+ var HAS_BROKEN_REGEXP = ' '.split(/\s+/).length == 0;
482
+
483
+ var sharedStorage = new Storage();
484
+ var hoverHandler = new HoverHandler();
485
+ var replaceHistory = [];
486
+
487
+ var engines = {}, fonts = {}, defaultOptions = {
488
+ engine: null,
489
+ //fontScale: 1,
490
+ //fontScaling: false,
491
+ hover: false,
492
+ hoverables: {
493
+ a: true
494
+ },
495
+ printable: true,
496
+ //rotation: 0,
497
+ //selectable: false,
498
+ selector: (
499
+ fabric.window.Sizzle
500
+ || (fabric.window.jQuery && function(query) { return jQuery(query); }) // avoid noConflict issues
501
+ || (fabric.window.dojo && dojo.query)
502
+ || (fabric.window.$$ && function(query) { return $$(query); })
503
+ || (fabric.window.$ && function(query) { return $(query); })
504
+ || (fabric.document.querySelectorAll && function(query) { return fabric.document.querySelectorAll(query); })
505
+ || elementsByTagName
506
+ ),
507
+ separate: 'words', // 'none' and 'characters' are also accepted
508
+ textShadow: 'none'
509
+ };
510
+
511
+ var separators = {
512
+ words: /\s+/,
513
+ characters: ''
514
+ };
515
+
516
+ /** @ignore */
517
+ api.now = function() {
518
+ DOM.ready();
519
+ return api;
520
+ };
521
+
522
+ /** @ignore */
523
+ api.refresh = function() {
524
+ var currentHistory = replaceHistory.splice(0, replaceHistory.length);
525
+ for (var i = 0, l = currentHistory.length; i < l; ++i) {
526
+ api.replace.apply(null, currentHistory[i]);
527
+ }
528
+ return api;
529
+ };
530
+
531
+ /** @ignore */
532
+ api.registerEngine = function(id, engine) {
533
+ if (!engine) return api;
534
+ engines[id] = engine;
535
+ return api.set('engine', id);
536
+ };
537
+
538
+ /** @ignore */
539
+ api.registerFont = function(data) {
540
+ var font = new Font(data), family = font.family;
541
+ if (!fonts[family]) fonts[family] = new FontFamily();
542
+ fonts[family].add(font);
543
+ return api.set('fontFamily', '"' + family + '"');
544
+ };
545
+
546
+ /** @ignore */
547
+ api.replace = function(elements, options, ignoreHistory) {
548
+ options = merge(defaultOptions, options);
549
+ if (!options.engine) return api; // there's no browser support so we'll just stop here
550
+ if (typeof options.textShadow == 'string' && options.textShadow)
551
+ options.textShadow = CSS.textShadow(options.textShadow);
552
+ if (!ignoreHistory) replaceHistory.push(arguments);
553
+ if (elements.nodeType || typeof elements == 'string') elements = [ elements ];
554
+ CSS.ready(function() {
555
+ for (var i = 0, l = elements.length; i < l; ++i) {
556
+ var el = elements[i];
557
+ if (typeof el == 'string') api.replace(options.selector(el), options, true);
558
+ else replaceElement(el, options);
559
+ }
560
+ });
561
+ return api;
562
+ };
563
+
564
+ /** @ignore */
565
+ api.replaceElement = function(el, options) {
566
+ options = merge(defaultOptions, options);
567
+ if (typeof options.textShadow == 'string' && options.textShadow)
568
+ options.textShadow = CSS.textShadow(options.textShadow);
569
+ return replaceElement(el, options);
570
+ };
571
+
572
+ api.engines = engines;
573
+ api.fonts = fonts;
574
+ /** @ignore */
575
+ api.getOptions = function() {
576
+ return merge(defaultOptions);
577
+ };
578
+
579
+ /** @ignore */
580
+ api.set = function(option, value) {
581
+ defaultOptions[option] = value;
582
+ return api;
583
+ };
584
+
585
+ return api;
586
+
587
+ })();
588
+
589
+ Cufon.registerEngine('canvas', (function() {
590
+
591
+ // Safari 2 doesn't support .apply() on native methods
592
+ var HAS_INLINE_BLOCK = Cufon.CSS.supports('display', 'inline-block');
593
+
594
+ // Firefox 2 w/ non-strict doctype (almost standards mode)
595
+ var HAS_BROKEN_LINEHEIGHT = !HAS_INLINE_BLOCK && (fabric.document.compatMode == 'BackCompat' || /frameset|transitional/i.test(fabric.document.doctype.publicId));
596
+
597
+ var styleSheet = fabric.document.createElement('style');
598
+ styleSheet.type = 'text/css';
599
+
600
+ var textNode = fabric.document.createTextNode(
601
+ '.cufon-canvas{text-indent:0}' +
602
+ '@media screen,projection{' +
603
+ '.cufon-canvas{display:inline;display:inline-block;position:relative;vertical-align:middle' +
604
+ (HAS_BROKEN_LINEHEIGHT
605
+ ? ''
606
+ : ';font-size:1px;line-height:1px') +
607
+ '}.cufon-canvas .cufon-alt{display:-moz-inline-box;display:inline-block;width:0;height:0;overflow:hidden}' +
608
+ (HAS_INLINE_BLOCK
609
+ ? '.cufon-canvas canvas{position:relative}'
610
+ : '.cufon-canvas canvas{position:absolute}') +
611
+ '}' +
612
+ '@media print{' +
613
+ '.cufon-canvas{padding:0 !important}' +
614
+ '.cufon-canvas canvas{display:none}' +
615
+ '.cufon-canvas .cufon-alt{display:inline}' +
616
+ '}'
617
+ )
618
+
619
+ try {
620
+ styleSheet.appendChild(textNode);
621
+ } catch(e) {
622
+ //IE8- can't do this...
623
+ styleSheet.setAttribute("type", "text/css");
624
+ styleSheet.styleSheet.cssText = textNode.data;
625
+ }
626
+ fabric.document.getElementsByTagName('head')[0].appendChild(styleSheet);
627
+
628
+ function generateFromVML(path, context) {
629
+ var atX = 0, atY = 0;
630
+ var code = [], re = /([mrvxe])([^a-z]*)/g, match;
631
+ generate: for (var i = 0; match = re.exec(path); ++i) {
632
+ var c = match[2].split(',');
633
+ switch (match[1]) {
634
+ case 'v':
635
+ code[i] = { m: 'bezierCurveTo', a: [ atX + ~~c[0], atY + ~~c[1], atX + ~~c[2], atY + ~~c[3], atX += ~~c[4], atY += ~~c[5] ] };
636
+ break;
637
+ case 'r':
638
+ code[i] = { m: 'lineTo', a: [ atX += ~~c[0], atY += ~~c[1] ] };
639
+ break;
640
+ case 'm':
641
+ code[i] = { m: 'moveTo', a: [ atX = ~~c[0], atY = ~~c[1] ] };
642
+ break;
643
+ case 'x':
644
+ code[i] = { m: 'closePath', a: [] };
645
+ break;
646
+ case 'e':
647
+ break generate;
648
+ }
649
+ context[code[i].m].apply(context, code[i].a);
650
+ }
651
+ return code;
652
+ }
653
+
654
+ function interpret(code, context) {
655
+ for (var i = 0, l = code.length; i < l; ++i) {
656
+ var line = code[i];
657
+ context[line.m].apply(context, line.a);
658
+ }
659
+ }
660
+
661
+ return function(font, text, style, options, node, el) {
662
+
663
+ var redraw = (text === null);
664
+
665
+ var viewBox = font.viewBox;
666
+
667
+ var size = style.getSize('fontSize', font.baseSize);
668
+
669
+ var letterSpacing = style.get('letterSpacing');
670
+ letterSpacing = (letterSpacing == 'normal') ? 0 : size.convertFrom(parseInt(letterSpacing, 10));
671
+
672
+ var expandTop = 0, expandRight = 0, expandBottom = 0, expandLeft = 0;
673
+ var shadows = options.textShadow, shadowOffsets = [];
674
+
675
+ Cufon.textOptions.shadowOffsets = [ ];
676
+ Cufon.textOptions.shadows = null;
677
+
678
+ if (shadows) {
679
+ Cufon.textOptions.shadows = shadows;
680
+ for (var i = 0, l = shadows.length; i < l; ++i) {
681
+ var shadow = shadows[i];
682
+ var x = size.convertFrom(parseFloat(shadow.offX));
683
+ var y = size.convertFrom(parseFloat(shadow.offY));
684
+ shadowOffsets[i] = [ x, y ];
685
+ //if (y < expandTop) expandTop = y;
686
+ //if (x > expandRight) expandRight = x;
687
+ //if (y > expandBottom) expandBottom = y;
688
+ //if (x < expandLeft) expandLeft = x;
689
+ }
690
+ }
691
+
692
+ var chars = Cufon.CSS.textTransform(redraw ? node.alt : text, style).split('');
693
+
694
+ var width = 0, lastWidth = null;
695
+
696
+ var maxWidth = 0, lines = 1, lineWidths = [ ];
697
+ for (var i = 0, l = chars.length; i < l; ++i) {
698
+ if (chars[i] === '\n') {
699
+ lines++;
700
+ if (width > maxWidth) {
701
+ maxWidth = width;
702
+ }
703
+ lineWidths.push(width);
704
+ width = 0;
705
+ continue;
706
+ }
707
+ var glyph = font.glyphs[chars[i]] || font.missingGlyph;
708
+ if (!glyph) continue;
709
+ width += lastWidth = Number(glyph.w || font.w) + letterSpacing;
710
+ }
711
+ lineWidths.push(width);
712
+
713
+ width = Math.max(maxWidth, width);
714
+
715
+ var lineOffsets = [ ];
716
+ for (var i = lineWidths.length; i--; ) {
717
+ lineOffsets[i] = width - lineWidths[i];
718
+ }
719
+
720
+ if (lastWidth === null) return null; // there's nothing to render
721
+
722
+ expandRight += (viewBox.width - lastWidth);
723
+ expandLeft += viewBox.minX;
724
+
725
+ var wrapper, canvas;
726
+
727
+ if (redraw) {
728
+ wrapper = node;
729
+ canvas = node.firstChild;
730
+ }
731
+ else {
732
+ wrapper = fabric.document.createElement('span');
733
+ wrapper.className = 'cufon cufon-canvas';
734
+ wrapper.alt = text;
735
+
736
+ canvas = fabric.document.createElement('canvas');
737
+ wrapper.appendChild(canvas);
738
+
739
+ if (options.printable) {
740
+ var print = fabric.document.createElement('span');
741
+ print.className = 'cufon-alt';
742
+ print.appendChild(fabric.document.createTextNode(text));
743
+ wrapper.appendChild(print);
744
+ }
745
+ }
746
+
747
+ var wStyle = wrapper.style;
748
+ var cStyle = canvas.style || { };
749
+
750
+ var height = size.convert(viewBox.height - expandTop + expandBottom);
751
+ var roundedHeight = Math.ceil(height);
752
+ var roundingFactor = roundedHeight / height;
753
+
754
+ canvas.width = Math.ceil(size.convert(width + expandRight - expandLeft) * roundingFactor);
755
+ canvas.height = roundedHeight;
756
+
757
+ expandTop += viewBox.minY;
758
+
759
+ cStyle.top = Math.round(size.convert(expandTop - font.ascent)) + 'px';
760
+ cStyle.left = Math.round(size.convert(expandLeft)) + 'px';
761
+
762
+ var _width = Math.ceil(size.convert(width * roundingFactor));
763
+ var wrapperWidth = _width + 'px';
764
+ var _height = size.convert(font.height);
765
+ var totalLineHeight = (options.lineHeight - 1) * size.convert(-font.ascent / 5) * (lines - 1);
766
+
767
+ Cufon.textOptions.width = _width;
768
+ Cufon.textOptions.height = (_height * lines) + totalLineHeight;
769
+ Cufon.textOptions.lines = lines;
770
+ Cufon.textOptions.totalLineHeight = totalLineHeight;
771
+
772
+ if (HAS_INLINE_BLOCK) {
773
+ wStyle.width = wrapperWidth;
774
+ wStyle.height = _height + 'px';
775
+ }
776
+ else {
777
+ wStyle.paddingLeft = wrapperWidth;
778
+ wStyle.paddingBottom = (_height - 1) + 'px';
779
+ }
780
+
781
+ var g = Cufon.textOptions.context || canvas.getContext('2d'),
782
+ scale = roundedHeight / viewBox.height;
783
+
784
+ Cufon.textOptions.fontAscent = font.ascent * scale;
785
+ Cufon.textOptions.boundaries = null;
786
+
787
+ for (var offsets = Cufon.textOptions.shadowOffsets, i = shadowOffsets.length; i--; ) {
788
+ offsets[i] = [ shadowOffsets[i][0] * scale, shadowOffsets[i][1] * scale ];
789
+ }
790
+
791
+ g.save();
792
+ g.scale(scale, scale);
793
+
794
+ g.translate(
795
+ // we're at the center of an object and need to jump to the top left corner
796
+ // where first character is to be drawn
797
+ -expandLeft - ((1/scale * canvas.width) / 2) + (Cufon.fonts[font.family].offsetLeft || 0),
798
+ -expandTop - ((Cufon.textOptions.height / scale) / 2) + (Cufon.fonts[font.family].offsetTop || 0)
799
+ );
800
+
801
+ g.lineWidth = font.face['underline-thickness'];
802
+
803
+ g.save();
804
+
805
+ function line(y, color) {
806
+ g.strokeStyle = color;
807
+
808
+ g.beginPath();
809
+
810
+ g.moveTo(0, y);
811
+ g.lineTo(width, y);
812
+
813
+ g.stroke();
814
+ }
815
+
816
+ var textDecoration = Cufon.getTextDecoration(options),
817
+ isItalic = options.fontStyle === 'italic';
818
+
819
+ function renderBackground() {
820
+ g.save();
821
+
822
+ var left = 0, lineNum = 0, boundaries = [{ left: 0 }];
823
+
824
+ if (options.backgroundColor) {
825
+ g.save();
826
+ g.fillStyle = options.backgroundColor;
827
+ g.translate(0, font.ascent);
828
+ g.fillRect(0, 0, width + 10, (-font.ascent + font.descent) * lines);
829
+ g.restore();
830
+ }
831
+
832
+ if (options.textAlign === 'right') {
833
+ g.translate(lineOffsets[lineNum], 0);
834
+ boundaries[0].left = lineOffsets[lineNum] * scale;
835
+ }
836
+ else if (options.textAlign === 'center') {
837
+ g.translate(lineOffsets[lineNum] / 2, 0);
838
+ boundaries[0].left = lineOffsets[lineNum] / 2 * scale;
839
+ }
840
+
841
+ for (var i = 0, l = chars.length; i < l; ++i) {
842
+ if (chars[i] === '\n') {
843
+
844
+ lineNum++;
845
+
846
+ var topOffset = -font.ascent - ((font.ascent / 5) * options.lineHeight);
847
+ var boundary = boundaries[boundaries.length - 1];
848
+ var nextBoundary = { left: 0 };
849
+
850
+ boundary.width = left * scale;
851
+ boundary.height = (-font.ascent + font.descent) * scale;
852
+
853
+ if (options.textAlign === 'right') {
854
+ g.translate(-width, topOffset);
855
+ g.translate(lineOffsets[lineNum], 0);
856
+ nextBoundary.left = lineOffsets[lineNum] * scale;
857
+ }
858
+ else if (options.textAlign === 'center') {
859
+ // offset to the start of text in previous line AND half of its offset
860
+ // (essentially moving caret to the left edge of bounding box)
861
+ g.translate(-left - (lineOffsets[lineNum - 1] / 2), topOffset);
862
+ g.translate(lineOffsets[lineNum] / 2, 0);
863
+ nextBoundary.left = lineOffsets[lineNum] / 2 * scale;
864
+ }
865
+ else {
866
+ g.translate(-left, topOffset);
867
+ }
868
+
869
+ /* push next boundary (for the next line) */
870
+ boundaries.push(nextBoundary);
871
+
872
+ left = 0;
873
+
874
+ continue;
875
+ }
876
+ var glyph = font.glyphs[chars[i]] || font.missingGlyph;
877
+ if (!glyph) continue;
878
+
879
+ var charWidth = Number(glyph.w || font.w) + letterSpacing;
880
+
881
+ // only draw text-background when there's some kind of value
882
+ if (options.textBackgroundColor) {
883
+ g.save();
884
+ g.fillStyle = options.textBackgroundColor;
885
+ g.translate(0, font.ascent);
886
+ g.fillRect(0, 0, charWidth + 10, -font.ascent + font.descent);
887
+ g.restore();
888
+ }
889
+
890
+ g.translate(charWidth, 0);
891
+ left += charWidth;
892
+
893
+ if (i == l-1) {
894
+ boundaries[boundaries.length - 1].width = left * scale;
895
+ boundaries[boundaries.length - 1].height = (-font.ascent + font.descent) * scale;
896
+ }
897
+ }
898
+ g.restore();
899
+
900
+ Cufon.textOptions.boundaries = boundaries;
901
+ }
902
+
903
+ function renderText(color) {
904
+ g.fillStyle = color || Cufon.textOptions.color || style.get('color');
905
+
906
+ var left = 0, lineNum = 0;
907
+
908
+ if (options.textAlign === 'right') {
909
+ g.translate(lineOffsets[lineNum], 0);
910
+ }
911
+ else if (options.textAlign === 'center') {
912
+ g.translate(lineOffsets[lineNum] / 2, 0);
913
+ }
914
+
915
+ for (var i = 0, l = chars.length; i < l; ++i) {
916
+ if (chars[i] === '\n') {
917
+
918
+ lineNum++;
919
+
920
+ var topOffset = -font.ascent - ((font.ascent / 5) * options.lineHeight);
921
+
922
+ if (options.textAlign === 'right') {
923
+ g.translate(-width, topOffset);
924
+ g.translate(lineOffsets[lineNum], 0);
925
+ }
926
+ else if (options.textAlign === 'center') {
927
+ // offset to the start of text in previous line AND half of its offset
928
+ // (essentially moving caret to the left edge of bounding box)
929
+ g.translate(-left - (lineOffsets[lineNum - 1] / 2), topOffset);
930
+ g.translate(lineOffsets[lineNum] / 2, 0);
931
+ }
932
+ else {
933
+ g.translate(-left, topOffset);
934
+ }
935
+
936
+ left = 0;
937
+
938
+ continue;
939
+ }
940
+ var glyph = font.glyphs[chars[i]] || font.missingGlyph;
941
+ if (!glyph) continue;
942
+
943
+ var charWidth = Number(glyph.w || font.w) + letterSpacing;
944
+
945
+ if (textDecoration) {
946
+ g.save();
947
+ g.strokeStyle = g.fillStyle;
948
+
949
+ // add 2x more thickness — closer to SVG rendering
950
+ g.lineWidth += g.lineWidth;
951
+
952
+ g.beginPath();
953
+ if (textDecoration.underline) {
954
+ g.moveTo(0, -font.face['underline-position'] + 0.5);
955
+ g.lineTo(charWidth, -font.face['underline-position'] + 0.5);
956
+ }
957
+ if (textDecoration.overline) {
958
+ g.moveTo(0, font.ascent + 0.5);
959
+ g.lineTo(charWidth, font.ascent + 0.5);
960
+ }
961
+ if (textDecoration['line-through']) {
962
+ g.moveTo(0, -font.descent + 0.5);
963
+ g.lineTo(charWidth, -font.descent + 0.5);
964
+ }
965
+ g.stroke();
966
+ g.restore();
967
+ }
968
+
969
+ if (isItalic) {
970
+ g.save();
971
+ g.transform(1, 0, -0.25, 1, 0, 0);
972
+ }
973
+
974
+ g.beginPath();
975
+ if (glyph.d) {
976
+ if (glyph.code) interpret(glyph.code, g);
977
+ else glyph.code = generateFromVML('m' + glyph.d, g);
978
+ }
979
+
980
+ g.fill();
981
+
982
+ if (options.strokeStyle) {
983
+ g.closePath();
984
+ g.save();
985
+ g.lineWidth = options.strokeWidth;
986
+ g.strokeStyle = options.strokeStyle;
987
+ g.stroke();
988
+ g.restore();
989
+ }
990
+
991
+ if (isItalic) {
992
+ g.restore();
993
+ }
994
+
995
+ g.translate(charWidth, 0);
996
+ left += charWidth;
997
+ }
998
+ }
999
+
1000
+ g.save();
1001
+ renderBackground();
1002
+ if (shadows) {
1003
+ for (var i = 0, l = shadows.length; i < l; ++i) {
1004
+ var shadow = shadows[i];
1005
+ g.save();
1006
+ g.translate.apply(g, shadowOffsets[i]);
1007
+ renderText(shadow.color);
1008
+ g.restore();
1009
+ }
1010
+ }
1011
+ renderText();
1012
+ g.restore();
1013
+ g.restore();
1014
+ g.restore();
1015
+
1016
+ return wrapper;
1017
+
1018
+ };
1019
+
1020
+ })());
1021
+
1022
+ Cufon.registerEngine('vml', (function() {
1023
+
1024
+ if (!fabric.document.namespaces) return;
1025
+
1026
+ var canvasEl = fabric.document.createElement('canvas');
1027
+ if (canvasEl && canvasEl.getContext && canvasEl.getContext.apply) return;
1028
+
1029
+ if (fabric.document.namespaces.cvml == null) {
1030
+ fabric.document.namespaces.add('cvml', 'urn:schemas-microsoft-com:vml');
1031
+ }
1032
+
1033
+ var check = fabric.document.createElement('cvml:shape');
1034
+ check.style.behavior = 'url(#default#VML)';
1035
+ if (!check.coordsize) return; // VML isn't supported
1036
+ check = null;
1037
+
1038
+ fabric.document.write('<style type="text/css">' +
1039
+ '.cufon-vml-canvas{text-indent:0}' +
1040
+ '@media screen{' +
1041
+ 'cvml\\:shape,cvml\\:shadow{behavior:url(#default#VML);display:block;antialias:true;position:absolute}' +
1042
+ '.cufon-vml-canvas{position:absolute;text-align:left}' +
1043
+ '.cufon-vml{display:inline-block;position:relative;vertical-align:middle}' +
1044
+ '.cufon-vml .cufon-alt{position:absolute;left:-10000in;font-size:1px}' +
1045
+ 'a .cufon-vml{cursor:pointer}' +
1046
+ '}' +
1047
+ '@media print{' +
1048
+ '.cufon-vml *{display:none}' +
1049
+ '.cufon-vml .cufon-alt{display:inline}' +
1050
+ '}' +
1051
+ '</style>');
1052
+
1053
+ function getFontSizeInPixels(el, value) {
1054
+ return getSizeInPixels(el, /(?:em|ex|%)$/i.test(value) ? '1em' : value);
1055
+ }
1056
+
1057
+ // Original by Dead Edwards.
1058
+ // Combined with getFontSizeInPixels it also works with relative units.
1059
+ function getSizeInPixels(el, value) {
1060
+ if (/px$/i.test(value)) return parseFloat(value);
1061
+ var style = el.style.left, runtimeStyle = el.runtimeStyle.left;
1062
+ el.runtimeStyle.left = el.currentStyle.left;
1063
+ el.style.left = value;
1064
+ var result = el.style.pixelLeft;
1065
+ el.style.left = style;
1066
+ el.runtimeStyle.left = runtimeStyle;
1067
+ return result;
1068
+ }
1069
+
1070
+ return function(font, text, style, options, node, el, hasNext) {
1071
+ var redraw = (text === null);
1072
+
1073
+ if (redraw) text = node.alt;
1074
+
1075
+ // @todo word-spacing, text-decoration
1076
+
1077
+ var viewBox = font.viewBox;
1078
+
1079
+ var size = style.computedFontSize ||
1080
+ (style.computedFontSize = new Cufon.CSS.Size(getFontSizeInPixels(el, style.get('fontSize')) + 'px', font.baseSize));
1081
+
1082
+ var letterSpacing = style.computedLSpacing;
1083
+
1084
+ if (letterSpacing == undefined) {
1085
+ letterSpacing = style.get('letterSpacing');
1086
+ style.computedLSpacing = letterSpacing =
1087
+ (letterSpacing == 'normal') ? 0 : ~~size.convertFrom(getSizeInPixels(el, letterSpacing));
1088
+ }
1089
+
1090
+ var wrapper, canvas;
1091
+
1092
+ if (redraw) {
1093
+ wrapper = node;
1094
+ canvas = node.firstChild;
1095
+ }
1096
+ else {
1097
+ wrapper = fabric.document.createElement('span');
1098
+ wrapper.className = 'cufon cufon-vml';
1099
+ wrapper.alt = text;
1100
+
1101
+ canvas = fabric.document.createElement('span');
1102
+ canvas.className = 'cufon-vml-canvas';
1103
+ wrapper.appendChild(canvas);
1104
+
1105
+ if (options.printable) {
1106
+ var print = fabric.document.createElement('span');
1107
+ print.className = 'cufon-alt';
1108
+ print.appendChild(fabric.document.createTextNode(text));
1109
+ wrapper.appendChild(print);
1110
+ }
1111
+
1112
+ // ie6, for some reason, has trouble rendering the last VML element in the document.
1113
+ // we can work around this by injecting a dummy element where needed.
1114
+ // @todo find a better solution
1115
+ if (!hasNext) wrapper.appendChild(fabric.document.createElement('cvml:shape'));
1116
+ }
1117
+
1118
+ var wStyle = wrapper.style;
1119
+ var cStyle = canvas.style;
1120
+
1121
+ var height = size.convert(viewBox.height), roundedHeight = Math.ceil(height);
1122
+ var roundingFactor = roundedHeight / height;
1123
+ var minX = viewBox.minX, minY = viewBox.minY;
1124
+
1125
+ cStyle.height = roundedHeight;
1126
+ cStyle.top = Math.round(size.convert(minY - font.ascent));
1127
+ cStyle.left = Math.round(size.convert(minX));
1128
+
1129
+ wStyle.height = size.convert(font.height) + 'px';
1130
+
1131
+ var textDecoration = Cufon.getTextDecoration(options);
1132
+
1133
+ var color = style.get('color');
1134
+
1135
+ var chars = Cufon.CSS.textTransform(text, style).split('');
1136
+
1137
+ var width = 0, offsetX = 0, advance = null;
1138
+
1139
+ var glyph, shape, shadows = options.textShadow;
1140
+
1141
+ // pre-calculate width
1142
+ for (var i = 0, k = 0, l = chars.length; i < l; ++i) {
1143
+ glyph = font.glyphs[chars[i]] || font.missingGlyph;
1144
+ if (glyph) width += advance = ~~(glyph.w || font.w) + letterSpacing;
1145
+ }
1146
+
1147
+ if (advance === null) return null;
1148
+
1149
+ var fullWidth = -minX + width + (viewBox.width - advance);
1150
+
1151
+ var shapeWidth = size.convert(fullWidth * roundingFactor), roundedShapeWidth = Math.round(shapeWidth);
1152
+
1153
+ var coordSize = fullWidth + ',' + viewBox.height, coordOrigin;
1154
+ var stretch = 'r' + coordSize + 'nsnf';
1155
+
1156
+ for (i = 0; i < l; ++i) {
1157
+
1158
+ glyph = font.glyphs[chars[i]] || font.missingGlyph;
1159
+ if (!glyph) continue;
1160
+
1161
+ if (redraw) {
1162
+ // some glyphs may be missing so we can't use i
1163
+ shape = canvas.childNodes[k];
1164
+ if (shape.firstChild) shape.removeChild(shape.firstChild); // shadow
1165
+ }
1166
+ else {
1167
+ shape = fabric.document.createElement('cvml:shape');
1168
+ canvas.appendChild(shape);
1169
+ }
1170
+
1171
+ shape.stroked = 'f';
1172
+ shape.coordsize = coordSize;
1173
+ shape.coordorigin = coordOrigin = (minX - offsetX) + ',' + minY;
1174
+ shape.path = (glyph.d ? 'm' + glyph.d + 'xe' : '') + 'm' + coordOrigin + stretch;
1175
+ shape.fillcolor = color;
1176
+
1177
+ // it's important to not set top/left or IE8 will grind to a halt
1178
+ var sStyle = shape.style;
1179
+ sStyle.width = roundedShapeWidth;
1180
+ sStyle.height = roundedHeight;
1181
+
1182
+ if (shadows) {
1183
+ // due to the limitations of the VML shadow element there
1184
+ // can only be two visible shadows. opacity is shared
1185
+ // for all shadows.
1186
+ var shadow1 = shadows[0], shadow2 = shadows[1];
1187
+ var color1 = Cufon.CSS.color(shadow1.color), color2;
1188
+ var shadow = fabric.document.createElement('cvml:shadow');
1189
+ shadow.on = 't';
1190
+ shadow.color = color1.color;
1191
+ shadow.offset = shadow1.offX + ',' + shadow1.offY;
1192
+ if (shadow2) {
1193
+ color2 = Cufon.CSS.color(shadow2.color);
1194
+ shadow.type = 'double';
1195
+ shadow.color2 = color2.color;
1196
+ shadow.offset2 = shadow2.offX + ',' + shadow2.offY;
1197
+ }
1198
+ shadow.opacity = color1.opacity || (color2 && color2.opacity) || 1;
1199
+ shape.appendChild(shadow);
1200
+ }
1201
+
1202
+ offsetX += ~~(glyph.w || font.w) + letterSpacing;
1203
+
1204
+ ++k;
1205
+
1206
+ }
1207
+
1208
+ wStyle.width = Math.max(Math.ceil(size.convert(width * roundingFactor)), 0);
1209
+
1210
+ return wrapper;
1211
+
1212
+ };
1213
+
1214
+ })());
1215
+
1216
+ Cufon.getTextDecoration = function(options) {
1217
+ return {
1218
+ underline: options.textDecoration === 'underline',
1219
+ overline: options.textDecoration === 'overline',
1220
+ 'line-through': options.textDecoration === 'line-through'
1221
+ };
1222
+ };
1223
+
1224
+ if (typeof exports != 'undefined') {
1225
+ exports.Cufon = Cufon;
1226
+ }