fabric-rails 1.0.12.1 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }