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.
- data/CHANGELOG.md +4 -0
- data/README.md +1 -1
- data/lib/fabric/rails/version.rb +2 -2
- data/vendor/assets/javascripts/cufon.js +1226 -0
- data/vendor/assets/javascripts/event.js +20 -20
- data/vendor/assets/javascripts/excanvas.js +1464 -0
- data/vendor/assets/javascripts/fabric.js +56 -33
- data/vendor/assets/javascripts/fabric/HEADER.js +2 -4
- data/vendor/assets/javascripts/fabric/brushes/base_brush.class.js +96 -0
- data/vendor/assets/javascripts/fabric/brushes/circle_brush.class.js +99 -0
- data/vendor/assets/javascripts/fabric/brushes/pattern_brush.class.js +55 -0
- data/vendor/assets/javascripts/fabric/{freedrawing.class.js → brushes/pencil_brush.class.js} +73 -65
- data/vendor/assets/javascripts/fabric/brushes/spray_brush.class.js +157 -0
- data/vendor/assets/javascripts/fabric/canvas.class.js +154 -135
- data/vendor/assets/javascripts/fabric/color.class.js +195 -29
- data/vendor/assets/javascripts/fabric/filters/brightness_filter.class.js +70 -0
- data/vendor/assets/javascripts/fabric/filters/convolute_filter.class.js +122 -0
- data/vendor/assets/javascripts/fabric/filters/gradienttransparency_filter.class.js +69 -0
- data/vendor/assets/javascripts/fabric/filters/grayscale_filter.class.js +61 -0
- data/vendor/assets/javascripts/fabric/filters/invert_filter.class.js +57 -0
- data/vendor/assets/javascripts/fabric/filters/noise_filter.class.js +73 -0
- data/vendor/assets/javascripts/fabric/filters/pixelate_filter.class.js +98 -0
- data/vendor/assets/javascripts/fabric/filters/removewhite_filter.class.js +86 -0
- data/vendor/assets/javascripts/fabric/filters/sepia2_filter.class.js +61 -0
- data/vendor/assets/javascripts/fabric/filters/sepia_filter.class.js +58 -0
- data/vendor/assets/javascripts/fabric/filters/tint_filter.class.js +80 -0
- data/vendor/assets/javascripts/fabric/gradient.class.js +232 -80
- data/vendor/assets/javascripts/fabric/intersection.class.js +10 -28
- data/vendor/assets/javascripts/fabric/log.js +0 -2
- data/vendor/assets/javascripts/fabric/{canvas_animation.mixin.js → mixins/canvas_animation.mixin.js} +3 -6
- data/vendor/assets/javascripts/fabric/mixins/canvas_dataurl_exporter.mixin.js +137 -0
- data/vendor/assets/javascripts/fabric/{canvas_events.mixin.js → mixins/canvas_events.mixin.js} +97 -144
- data/vendor/assets/javascripts/fabric/{canvas_gestures.mixin.js → mixins/canvas_gestures.mixin.js} +4 -8
- data/vendor/assets/javascripts/fabric/{canvas_serialization.mixin.js → mixins/canvas_serialization.mixin.js} +19 -14
- data/vendor/assets/javascripts/fabric/mixins/collection.mixin.js +137 -0
- data/vendor/assets/javascripts/fabric/{object_geometry.mixin.js → mixins/object_geometry.mixin.js} +130 -47
- data/vendor/assets/javascripts/fabric/{object_interactivity.mixin.js → mixins/object_interactivity.mixin.js} +21 -102
- data/vendor/assets/javascripts/fabric/{object_origin.mixin.js → mixins/object_origin.mixin.js} +36 -26
- data/vendor/assets/javascripts/fabric/{object_straightening.mixin.js → mixins/object_straightening.mixin.js} +4 -9
- data/vendor/assets/javascripts/fabric/{observable.mixin.js → mixins/observable.mixin.js} +27 -35
- data/vendor/assets/javascripts/fabric/mixins/stateful.mixin.js +45 -0
- data/vendor/assets/javascripts/fabric/node.js +62 -26
- data/vendor/assets/javascripts/fabric/parser.js +181 -58
- data/vendor/assets/javascripts/fabric/pattern.class.js +43 -14
- data/vendor/assets/javascripts/fabric/point.class.js +4 -43
- data/vendor/assets/javascripts/fabric/shadow.class.js +19 -19
- data/vendor/assets/javascripts/fabric/{circle.class.js → shapes/circle.class.js} +32 -29
- data/vendor/assets/javascripts/fabric/{ellipse.class.js → shapes/ellipse.class.js} +45 -27
- data/vendor/assets/javascripts/fabric/{group.class.js → shapes/group.class.js} +67 -169
- data/vendor/assets/javascripts/fabric/{image.class.js → shapes/image.class.js} +134 -72
- data/vendor/assets/javascripts/fabric/{line.class.js → shapes/line.class.js} +67 -36
- data/vendor/assets/javascripts/fabric/{object.class.js → shapes/object.class.js} +394 -252
- data/vendor/assets/javascripts/fabric/{path.class.js → shapes/path.class.js} +89 -174
- data/vendor/assets/javascripts/fabric/{path_group.class.js → shapes/path_group.class.js} +12 -18
- data/vendor/assets/javascripts/fabric/{polygon.class.js → shapes/polygon.class.js} +64 -38
- data/vendor/assets/javascripts/fabric/{polyline.class.js → shapes/polyline.class.js} +64 -39
- data/vendor/assets/javascripts/fabric/{rect.class.js → shapes/rect.class.js} +78 -112
- data/vendor/assets/javascripts/fabric/{text.class.js → shapes/text.class.js} +264 -270
- data/vendor/assets/javascripts/fabric/shapes/text.cufon.js +79 -0
- data/vendor/assets/javascripts/fabric/{triangle.class.js → shapes/triangle.class.js} +46 -26
- data/vendor/assets/javascripts/fabric/static_canvas.class.js +134 -358
- data/vendor/assets/javascripts/fabric/util/anim_ease.js +2 -31
- data/vendor/assets/javascripts/fabric/util/dom_event.js +21 -7
- data/vendor/assets/javascripts/fabric/util/dom_misc.js +49 -39
- data/vendor/assets/javascripts/fabric/util/dom_request.js +1 -2
- data/vendor/assets/javascripts/fabric/util/dom_style.js +1 -2
- data/vendor/assets/javascripts/fabric/util/lang_array.js +19 -13
- data/vendor/assets/javascripts/fabric/util/lang_class.js +1 -2
- data/vendor/assets/javascripts/fabric/util/lang_function.js +3 -1
- data/vendor/assets/javascripts/fabric/util/lang_object.js +5 -5
- data/vendor/assets/javascripts/fabric/util/lang_string.js +7 -5
- data/vendor/assets/javascripts/fabric/util/misc.js +207 -42
- metadata +47 -29
- data/vendor/assets/javascripts/fabric/image_filters.js +0 -809
- data/vendor/assets/javascripts/fabric/scout.js +0 -45
- data/vendor/assets/javascripts/fabric/stateful.js +0 -88
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
data/lib/fabric/rails/version.rb
CHANGED
@@ -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
|
+
}
|