PageSourceSearch

https://foodtechconnect.com/wp-content/themes/bangkokpress/javascript/cufon.js?ver=1.0

js foodtechconnect.com collected 2026-10-01 10:42:29 UTC 41,428 bytes, 1,446 lines download raw bytes

1/*!
2 * Copyright (c) 2011 Simo Kinnunen.
3 * Licensed under the MIT license.
4 *
5 * @version ${Version}
6 */
7
8var Cufon = (function() {
9
10	var api = function() {
11		return api.replace.apply(null, arguments);
12	};
13
14	var DOM = api.DOM = {
15
16		ready: (function() {
17
18			var complete = false, readyStatus = { loaded: 1, complete: 1 };
19
20			var queue = [], 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 (document.addEventListener) {
29				document.addEventListener('DOMContentLoaded', perform, false);
30				window.addEventListener('pageshow', perform, false); // For cached Gecko pages
31			}
32
33			// Old WebKit, Internet Explorer
34
35			if (!window.opera && document.readyState) (function() {
36				readyStatus[document.readyState] ? perform() : setTimeout(arguments.callee, 10);
37			})();
38
39			// Internet Explorer
40
41			if (document.readyState && document.createStyleSheet) (function() {
42				try {
43					document.body.doScroll('left');
44					perform();
45				}
46				catch (e) {
47					setTimeout(arguments.callee, 1);
48				}
49			})();
50
51			addEvent(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		root: function() {
61			return document.documentElement || document.body;
62		},
63
64		strict: (function() {
65			var doctype;
66			// no doctype (doesn't always catch it though.. IE I'm looking at you)
67			if (document.compatMode == 'BackCompat') return false;
68			// WebKit, Gecko, Opera, IE9+
69			doctype = document.doctype;
70			if (doctype) {
71				return !/frameset|transitional/i.test(doctype.publicId);
72			}
73			// IE<9, firstChild is the doctype even if there's an XML declaration
74			doctype = document.firstChild;
75			if (doctype.nodeType != 8 || /^DOCTYPE.+(transitional|frameset)/i.test(doctype.data)) {
76				return false;
77			}
78			return true;
79		})()
80
81	};
82
83	var CSS = api.CSS = {
84
85		Size: function(value, base) {
86
87			this.value = parseFloat(value);
88			this.unit = String(value).match(/[a-z%]*$/)[0] || 'px';
89
90			this.convert = function(value) {
91				return value / base * this.value;
92			};
93
94			this.convertFrom = function(value) {
95				return value / this.value * base;
96			};
97
98			this.toString = function() {
99				return this.value + this.unit;
100			};
101
102		},
103
104		addClass: function(el, className) {
105			var current = el.className;
106			el.className = current + (current && ' ') + className;
107			return el;
108		},
109
110		color: cached(function(value) {
111			var parsed = {};
112			parsed.color = value.replace(/^rgba\((.*?),\s*([\d.]+)\)/, function($0, $1, $2) {
113				parsed.opacity = parseFloat($2);
114				return 'rgb(' + $1 + ')';
115			});
116			return parsed;
117		}),
118
119		// has no direct CSS equivalent.
120		// @see http://msdn.microsoft.com/en-us/library/system.windows.fontstretches.aspx
121		fontStretch: cached(function(value) {
122			if (typeof value == 'number') return value;
123			if (/%$/.test(value)) return parseFloat(value) / 100;
124			return {
125				'ultra-condensed': 0.5,
126				'extra-condensed': 0.625,
127				condensed: 0.75,
128				'semi-condensed': 0.875,
129				'semi-expanded': 1.125,
130				expanded: 1.25,
131				'extra-expanded': 1.5,
132				'ultra-expanded': 2
133			}[value] || 1;
134		}),
135
136		getStyle: function(el) {
137			var view = document.defaultView;
138			if (view && view.getComputedStyle) return new Style(view.getComputedStyle(el, null));
139			if (el.currentStyle) return new Style(el.currentStyle);
140			return new Style(el.style);
141		},
142
143		gradient: cached(function(value) {
144			var gradient = {
145				id: value,
146				type: value.match(/^-([a-z]+)-gradient\(/)[1],
147				stops: []
148			}, colors = value.substr(value.indexOf('(')).match(/([\d.]+=)?(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)/ig);
149			for (var i = 0, l = colors.length, stop; i < l; ++i) {
150				stop = colors[i].split('=', 2).reverse();
151				gradient.stops.push([ stop[1] || i / (l - 1), stop[0] ]);
152			}
153			return gradient;
154		}),
155
156		quotedList: cached(function(value) {
157			// doesn't work properly with empty quoted strings (""), but
158			// it's not worth the extra code.
159			var list = [], re = /\s*((["'])([\s\S]*?[^\\])\2|[^,]+)\s*/g, match;
160			while (match = re.exec(value)) list.push(match[3] || match[1]);
161			return list;
162		}),
163
164		recognizesMedia: cached(function(media) {
165			var el = document.createElement('style'), sheet, container, supported;
166			el.type = 'text/css';
167			el.media = media;
168			try { // this is cached anyway
169				el.appendChild(document.createTextNode('/**/'));
170			} catch (e) {}
171			container = elementsByTagName('head')[0];
172			container.insertBefore(el, container.firstChild);
173			sheet = (el.sheet || el.styleSheet);
174			supported = sheet && !sheet.disabled;
175			container.removeChild(el);
176			return supported;
177		}),
178
179		removeClass: function(el, className) {
180			var re = RegExp('(?:^|\\s+)' + className +  '(?=\\s|$)', 'g');
181			el.className = el.className.replace(re, '');
182			return el;
183		},
184
185		supports: function(property, value) {
186			var checker = document.createElement('span').style;
187			if (checker[property] === undefined) return false;
188			checker[property] = value;
189			return checker[property] === value;
190		},
191
192		textAlign: function(word, style, position, wordCount) {
193			if (style.get('textAlign') == 'right') {
194				if (position > 0) word = ' ' + word;
195			}
196			else if (position < wordCount - 1) word += ' ';
197			return word;
198		},
199
200		textShadow: cached(function(value) {
201			if (value == 'none') return null;
202			var shadows = [], currentShadow = {}, result, offCount = 0;
203			var re = /(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)|(-?[\d.]+[a-z%]*)|,/ig;
204			while (result = re.exec(value)) {
205				if (result[0] == ',') {
206					shadows.push(currentShadow);
207					currentShadow = {};
208					offCount = 0;
209				}
210				else if (result[1]) {
211					currentShadow.color = result[1];
212				}
213				else {
214					currentShadow[[ 'offX', 'offY', 'blur' ][offCount++]] = result[2];
215				}
216			}
217			shadows.push(currentShadow);
218			return shadows;
219		}),
220
221		textTransform: (function() {
222			var map = {
223				uppercase: function(s) {
224					return s.toUpperCase();
225				},
226				lowercase: function(s) {
227					return s.toLowerCase();
228				},
229				capitalize: function(s) {
230					return s.replace(/(?:^|\s)./g, function($0) {
231						return $0.toUpperCase();
232					});
233				}
234			};
235			return function(text, style) {
236				var transform = map[style.get('textTransform')];
237				return transform ? transform(text) : text;
238			};
239		})(),
240
241		whiteSpace: (function() {
242			var ignore = {
243				inline: 1,
244				'inline-block': 1,
245				'run-in': 1
246			};
247			var wsStart = /^\s+/, wsEnd = /\s+$/;
248			return function(text, style, node, previousElement, simple) {
249				if (simple) return text.replace(wsStart, '').replace(wsEnd, ''); // @fixme too simple
250				if (previousElement) {
251					if (previousElement.nodeName.toLowerCase() == 'br') {
252						text = text.replace(wsStart, '');
253					}
254				}
255				if (ignore[style.get('display')]) return text;
256				if (!node.previousSibling) text = text.replace(wsStart, '');
257				if (!node.nextSibling) text = text.replace(wsEnd, '');
258				return text;
259			};
260		})()
261
262	};
263
264	CSS.ready = (function() {
265
266		// don't do anything in Safari 2 (it doesn't recognize any media type)
267		var complete = !CSS.recognizesMedia('all'), hasLayout = false;
268
269		var queue = [], perform = function() {
270			complete = true;
271			for (var fn; fn = queue.shift(); fn());
272		};
273
274		var links = elementsByTagName('link'), styles = elementsByTagName('style');
275
276		var checkTypes = {
277			'': 1,
278			'text/css': 1
279		};
280
281		function isContainerReady(el) {
282			if (!checkTypes[el.type.toLowerCase()]) return true;
283			return el.disabled || isSheetReady(el.sheet, el.media || 'screen');
284		}
285
286		function isSheetReady(sheet, media) {
287			// in Opera sheet.disabled is true when it's still loading,
288			// even though link.disabled is false. they stay in sync if
289			// set manually.
290			if (!CSS.recognizesMedia(media || 'all')) return true;
291			if (!sheet || sheet.disabled) return false;
292			try {
293				var rules = sheet.cssRules, rule;
294				if (rules) {
295					// needed for Safari 3 and Chrome 1.0.
296					// in standards-conforming browsers cssRules contains @-rules.
297					// Chrome 1.0 weirdness: rules[<number larger than .length - 1>]
298					// returns the last rule, so a for loop is the only option.
299					search: for (var i = 0, l = rules.length; rule = rules[i], i < l; ++i) {
300						switch (rule.type) {
301							case 2: // @charset
302								break;
303							case 3: // @import
304								if (!isSheetReady(rule.styleSheet, rule.media.mediaText)) return false;
305								break;
306							default:
307								// only @charset can precede @import
308								break search;
309						}
310					}
311				}
312			}
313			catch (e) {} // probably a style sheet from another domain
314			return true;
315		}
316
317		function allStylesLoaded() {
318			// Internet Explorer's style sheet model, there's no need to do anything
319			if (document.createStyleSheet) return true;
320			// standards-compliant browsers
321			var el, i;
322			for (i = 0; el = links[i]; ++i) {
323				if (el.rel.toLowerCase() == 'stylesheet' && !isContainerReady(el)) return false;
324			}
325			for (i = 0; el = styles[i]; ++i) {
326				if (!isContainerReady(el)) return false;
327			}
328			return true;
329		}
330
331		DOM.ready(function() {
332			// getComputedStyle returns null in Gecko if used in an iframe with display: none
333			if (!hasLayout) hasLayout = CSS.getStyle(document.body).isUsable();
334			if (complete || (hasLayout && allStylesLoaded())) perform();
335			else setTimeout(arguments.callee, 10);
336		});
337
338		return function(listener) {
339			if (complete) listener();
340			else queue.push(listener);
341		};
342
343	})();
344
345	function Font(data) {
346
347		var face = this.face = data.face, wordSeparators = {
348			'\u0020': 1,
349			'\u00a0': 1,
350			'\u3000': 1
351		};
352
353		this.glyphs = (function(glyphs) {
354			var key, fallbacks = {
355				'\u2011': '\u002d',
356				'\u00ad': '\u2011'
357			};
358			for (key in fallbacks) {
359				if (!hasOwnProperty(fallbacks, key)) continue;
360				if (!glyphs[key]) glyphs[key] = glyphs[fallbacks[key]];
361			}
362			return glyphs;
363		})(data.glyphs);
364
365		this.w = data.w;
366		this.baseSize = parseInt(face['units-per-em'], 10);
367
368		this.family = face['font-family'].toLowerCase();
369		this.weight = face['font-weight'];
370		this.style = face['font-style'] || 'normal';
371
372		this.viewBox = (function () {
373			var parts = face.bbox.split(/\s+/);
374			var box = {
375				minX: parseInt(parts[0], 10),
376				minY: parseInt(parts[1], 10),
377				maxX: parseInt(parts[2], 10),
378				maxY: parseInt(parts[3], 10)
379			};
380			box.width = box.maxX - box.minX;
381			box.height = box.maxY - box.minY;
382			box.toString = function() {
383				return [ this.minX, this.minY, this.width, this.height ].join(' ');
384			};
385			return box;
386		})();
387
388		this.ascent = -parseInt(face.ascent, 10);
389		this.descent = -parseInt(face.descent, 10);
390
391		this.height = -this.ascent + this.descent;
392
393		this.spacing = function(chars, letterSpacing, wordSpacing) {
394			var glyphs = this.glyphs, glyph,
395				kerning, k,
396				jumps = [],
397				width = 0, w,
398				i = -1, j = -1, chr;
399			while (chr = chars[++i]) {
400				glyph = glyphs[chr] || this.missingGlyph;
401				if (!glyph) continue;
402				if (kerning) {
403					width -= k = kerning[chr] || 0;
404					jumps[j] -= k;
405				}
406				w = glyph.w;
407				if (isNaN(w)) w = +this.w; // may have been a String in old fonts
408				if (w > 0) {
409					w += letterSpacing;
410					if (wordSeparators[chr]) w += wordSpacing;
411				}
412				width += jumps[++j] = ~~w; // get rid of decimals
413				kerning = glyph.k;
414			}
415			jumps.total = width;
416			return jumps;
417		};
418
419	}
420
421	function FontFamily() {
422
423		var styles = {}, mapping = {
424			oblique: 'italic',
425			italic: 'oblique'
426		};
427
428		this.add = function(font) {
429			(styles[font.style] || (styles[font.style] = {}))[font.weight] = font;
430		};
431
432		this.get = function(style, weight) {
433			var weights = styles[style] || styles[mapping[style]]
434				|| styles.normal || styles.italic || styles.oblique;
435			if (!weights) return null;
436			// we don't have to worry about "bolder" and "lighter"
437			// because IE's currentStyle returns a numeric value for it,
438			// and other browsers use the computed value anyway
439			weight = {
440				normal: 400,
441				bold: 700
442			}[weight] || parseInt(weight, 10);
443			if (weights[weight]) return weights[weight];
444			// http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight
445			// Gecko uses x99/x01 for lighter/bolder
446			var up = {
447				1: 1,
448				99: 0
449			}[weight % 100], alts = [], min, max;
450			if (up === undefined) up = weight > 400;
451			if (weight == 500) weight = 400;
452			for (var alt in weights) {
453				if (!hasOwnProperty(weights, alt)) continue;
454				alt = parseInt(alt, 10);
455				if (!min || alt < min) min = alt;
456				if (!max || alt > max) max = alt;
457				alts.push(alt);
458			}
459			if (weight < min) weight = min;
460			if (weight > max) weight = max;
461			alts.sort(function(a, b) {
462				return (up
463					? (a >= weight && b >= weight) ? a < b : a > b
464					: (a <= weight && b <= weight) ? a > b : a < b) ? -1 : 1;
465			});
466			return weights[alts[0]];
467		};
468
469	}
470
471	function HoverHandler() {
472
473		function contains(node, anotherNode) {
474			try {
475				if (node.contains) return node.contains(anotherNode);
476				return node.compareDocumentPosition(anotherNode) & 16;
477			}
478			catch(e) {} // probably a XUL element such as a scrollbar
479			return false;
480		}
481
482		// mouseover/mouseout (standards) mode
483		function onOverOut(e) {
484			var related = e.relatedTarget;
485			// there might be no relatedTarget if the element is right next
486			// to the window frame
487			if (related && contains(this, related)) return;
488			trigger(this, e.type == 'mouseover');
489		}
490
491		// mouseenter/mouseleave (probably ie) mode
492		function onEnterLeave(e) {
493			if (!e) e = window.event;
494			// ie model, we don't have access to "this", but
495			// mouseenter/leave doesn't bubble so it's fine.
496			trigger(e.target || e.srcElement, e.type == 'mouseenter');
497		}
498
499		function trigger(el, hoverState) {
500			// A timeout is needed so that the event can actually "happen"
501			// before replace is triggered. This ensures that styles are up
502			// to date.
503			setTimeout(function() {
504				var options = sharedStorage.get(el).options;
505				if (hoverState) {
506					options = merge(options, options.hover);
507					options._mediatorMode = 1;
508				}
509				api.replace(el, options, true);
510			}, 10);
511		}
512
513		this.attach = function(el) {
514			if (el.onmouseenter === undefined) {
515				addEvent(el, 'mouseover', onOverOut);
516				addEvent(el, 'mouseout', onOverOut);
517			}
518			else {
519				addEvent(el, 'mouseenter', onEnterLeave);
520				addEvent(el, 'mouseleave', onEnterLeave);
521			}
522		};
523
524		this.detach = function(el) {
525			if (el.onmouseenter === undefined) {
526				removeEvent(el, 'mouseover', onOverOut);
527				removeEvent(el, 'mouseout', onOverOut);
528			}
529			else {
530				removeEvent(el, 'mouseenter', onEnterLeave);
531				removeEvent(el, 'mouseleave', onEnterLeave);
532			}
533		};
534
535	}
536
537	function ReplaceHistory() {
538
539		var list = [], map = {};
540
541		function filter(keys) {
542			var values = [], key;
543			for (var i = 0; key = keys[i]; ++i) values[i] = list[map[key]];
544			return values;
545		}
546
547		this.add = function(key, args) {
548			map[key] = list.push(args) - 1;
549		};
550
551		this.repeat = function() {
552			var snapshot = arguments.length ? filter(arguments) : list, args;
553			for (var i = 0; args = snapshot[i++];) api.replace(args[0], args[1], true);
554		};
555
556	}
557
558	function Storage() {
559
560		var map = {}, at = 0;
561
562		function identify(el) {
563			return el.cufid || (el.cufid = ++at);
564		}
565
566		this.get = function(el) {
567			var id = identify(el);
568			return map[id] || (map[id] = {});
569		};
570
571	}
572
573	function Style(style) {
574
575		var custom = {}, sizes = {};
576
577		this.extend = function(styles) {
578			for (var property in styles) {
579				if (hasOwnProperty(styles, property)) custom[property] = styles[property];
580			}
581			return this;
582		};
583
584		this.get = function(property) {
585			return custom[property] != undefined ? custom[property] : style[property];
586		};
587
588		this.getSize = function(property, base) {
589			return sizes[property] || (sizes[property] = new CSS.Size(this.get(property), base));
590		};
591
592		this.isUsable = function() {
593			return !!style;
594		};
595
596	}
597
598	function addEvent(el, type, listener) {
599		if (el.addEventListener) {
600			el.addEventListener(type, listener, false);
601		}
602		else if (el.attachEvent) {
603			// we don't really need "this" right now, saves code
604			el.attachEvent('on' + type, listener);
605		}
606	}
607
608	function attach(el, options) {
609		if (options._mediatorMode) return el;
610		var storage = sharedStorage.get(el);
611		var oldOptions = storage.options;
612		if (oldOptions) {
613			if (oldOptions === options) return el;
614			if (oldOptions.hover) hoverHandler.detach(el);
615		}
616		if (options.hover && options.hoverables[el.nodeName.toLowerCase()]) {
617			hoverHandler.attach(el);
618		}
619		storage.options = options;
620		return el;
621	}
622
623	function cached(fun) {
624		var cache = {};
625		return function(key) {
626			if (!hasOwnProperty(cache, key)) cache[key] = fun.apply(null, arguments);
627			return cache[key];
628		};
629	}
630
631	function getFont(el, style) {
632		var families = CSS.quotedList(style.get('fontFamily').toLowerCase()), family;
633		for (var i = 0; family = families[i]; ++i) {
634			if (fonts[family]) return fonts[family].get(style.get('fontStyle'), style.get('fontWeight'));
635		}
636		return null;
637	}
638
639	function elementsByTagName(query) {
640		return document.getElementsByTagName(query);
641	}
642
643	function hasOwnProperty(obj, property) {
644		return obj.hasOwnProperty(property);
645	}
646
647	function merge() {
648		var merged = {}, arg, key;
649		for (var i = 0, l = arguments.length; arg = arguments[i], i < l; ++i) {
650			for (key in arg) {
651				if (hasOwnProperty(arg, key)) merged[key] = arg[key];
652			}
653		}
654		return merged;
655	}
656
657	function process(font, text, style, options, node, el) {
658		var fragment = document.createDocumentFragment(), processed;
659		if (text === '') return fragment;
660		var separate = options.separate;
661		var parts = text.split(separators[separate]), needsAligning = (separate == 'words');
662		if (needsAligning && HAS_BROKEN_REGEXP) {
663			// @todo figure out a better way to do this
664			if (/^\s/.test(text)) parts.unshift('');
665			if (/\s$/.test(text)) parts.push('');
666		}
667		for (var i = 0, l = parts.length; i < l; ++i) {
668			processed = engines[options.engine](font,
669				needsAligning ? CSS.textAlign(parts[i], style, i, l) : parts[i],
670				style, options, node, el, i < l - 1);
671			if (processed) fragment.appendChild(processed);
672		}
673		return fragment;
674	}
675
676	function removeEvent(el, type, listener) {
677		if (el.removeEventListener) {
678			el.removeEventListener(type, listener, false);
679		}
680		else if (el.detachEvent) {
681			el.detachEvent('on' + type, listener);
682		}
683	}
684
685	function replaceElement(el, options) {
686		var name = el.nodeName.toLowerCase();
687		if (options.ignore[name]) return;
688		if (options.ignoreClass && options.ignoreClass.test(el.className)) return;
689		if (options.onBeforeReplace) options.onBeforeReplace(el, options);
690		var replace = !options.textless[name], simple = (options.trim === 'simple');
691		var style = CSS.getStyle(attach(el, options)).extend(options);
692		// may cause issues if the element contains other elements
693		// with larger fontSize, however such cases are rare and can
694		// be fixed by using a more specific selector
695		if (parseFloat(style.get('fontSize')) === 0) return;
696		var font = getFont(el, style), node, type, next, anchor, text, lastElement;
697		var isShy = options.softHyphens, anyShy = false, pos, shy, reShy = /\u00ad/g;
698		var modifyText = options.modifyText;
699		if (!font) return;
700		for (node = el.firstChild; node; node = next) {
701			type = node.nodeType;
702			next = node.nextSibling;
703			if (replace && type == 3) {
704				if (isShy && el.nodeName.toLowerCase() != TAG_SHY) {
705					pos = node.data.indexOf('\u00ad');
706					if (pos >= 0) {
707						node.splitText(pos);
708						next = node.nextSibling;
709						next.deleteData(0, 1);
710						shy = document.createElement(TAG_SHY);
711						shy.appendChild(document.createTextNode('\u00ad'));
712						el.insertBefore(shy, next);
713						next = shy;
714						anyShy = true;
715					}
716				}
717				// Node.normalize() is broken in IE 6, 7, 8
718				if (anchor) {
719					anchor.appendData(node.data);
720					el.removeChild(node);
721				}
722				else anchor = node;
723				if (next) continue;
724			}
725			if (anchor) {
726				text = anchor.data;
727				if (!isShy) text = text.replace(reShy, '');
728				text = CSS.whiteSpace(text, style, anchor, lastElement, simple);
729				// modify text only on the first replace
730				if (modifyText) text = modifyText(text, anchor, el, options);
731				el.replaceChild(process(font, text, style, options, node, el), anchor);
732				anchor = null;
733			}
734			if (type == 1) {
735				if (node.firstChild) {
736					if (node.nodeName.toLowerCase() == 'cufon') {
737						engines[options.engine](font, null, style, options, node, el);
738					}
739					else arguments.callee(node, options);
740				}
741				lastElement = node;
742			}
743		}
744		if (isShy && anyShy) {
745			updateShy(el);
746			if (!trackingShy) addEvent(window, 'resize', updateShyOnResize);
747			trackingShy = true;
748		}
749		if (options.onAfterReplace) options.onAfterReplace(el, options);
750	}
751
752	function updateShy(context) {
753		var shys, shy, parent, glue, newGlue, next, prev, i;
754		shys = context.getElementsByTagName(TAG_SHY);
755		// unfortunately there doesn't seem to be any easy
756		// way to avoid having to loop through the shys twice.
757		for (i = 0; shy = shys[i]; ++i) {
758			shy.className = C_SHY_DISABLED;
759			glue = parent = shy.parentNode;
760			if (glue.nodeName.toLowerCase() != TAG_GLUE) {
761				newGlue = document.createElement(TAG_GLUE);
762				newGlue.appendChild(shy.previousSibling);
763				parent.insertBefore(newGlue, shy);
764				newGlue.appendChild(shy);
765			}
766			else {
767				// get rid of double glue (edge case fix)
768				glue = glue.parentNode;
769				if (glue.nodeName.toLowerCase() == TAG_GLUE) {
770					parent = glue.parentNode;
771					while (glue.firstChild) {
772						parent.insertBefore(glue.firstChild, glue);
773					}
774					parent.removeChild(glue);
775				}
776			}
777		}
778		for (i = 0; shy = shys[i]; ++i) {
779			shy.className = '';
780			glue = shy.parentNode;
781			parent = glue.parentNode;
782			next = glue.nextSibling || parent.nextSibling;
783			// make sure we're comparing same types
784			prev = (next.nodeName.toLowerCase() == TAG_GLUE) ? glue : shy.previousSibling;
785			if (prev.offsetTop >= next.offsetTop) {
786				shy.className = C_SHY_DISABLED;
787				if (prev.offsetTop < next.offsetTop) {
788					// we have an annoying edge case, double the glue
789					newGlue = document.createElement(TAG_GLUE);
790					parent.insertBefore(newGlue, glue);
791					newGlue.appendChild(glue);
792					newGlue.appendChild(next);
793				}
794			}
795		}
796	}
797
798	function updateShyOnResize() {
799		if (ignoreResize) return; // needed for IE
800		CSS.addClass(DOM.root(), C_VIEWPORT_RESIZING);
801		clearTimeout(shyTimer);
802		shyTimer = setTimeout(function() {
803			ignoreResize = true;
804			CSS.removeClass(DOM.root(), C_VIEWPORT_RESIZING);
805			updateShy(document);
806			ignoreResize = false;
807		}, 100);
808	}
809
810	var HAS_BROKEN_REGEXP = ' '.split(/\s+/).length == 0;
811	var TAG_GLUE = 'cufonglue';
812	var TAG_SHY = 'cufonshy';
813	var C_SHY_DISABLED = 'cufon-shy-disabled';
814	var C_VIEWPORT_RESIZING = 'cufon-viewport-resizing';
815
816	var sharedStorage = new Storage();
817	var hoverHandler = new HoverHandler();
818	var replaceHistory = new ReplaceHistory();
819	var initialized = false;
820	var trackingShy = false;
821	var shyTimer;
822	var ignoreResize = false;
823
824	var engines = {}, fonts = {}, defaultOptions = {
825		autoDetect: false,
826		engine: null,
827		forceHitArea: false,
828		hover: false,
829		hoverables: {
830			a: true
831		},
832		ignore: {
833			applet: 1,
834			canvas: 1,
835			col: 1,
836			colgroup: 1,
837			head: 1,
838			iframe: 1,
839			map: 1,
840			noscript: 1,
841			optgroup: 1,
842			option: 1,
843			script: 1,
844			select: 1,
845			style: 1,
846			textarea: 1,
847			title: 1,
848			pre: 1
849		},
850		ignoreClass: null,
851		modifyText: null,
852		onAfterReplace: null,
853		onBeforeReplace: null,
854		printable: true,
855		selector: (
856				window.Sizzle
857			||	(window.jQuery && function(query) { return jQuery(query); }) // avoid noConflict issues
858			||	(window.dojo && dojo.query)
859			||	(window.glow && glow.dom && glow.dom.get)
860			||	(window.Ext && Ext.query)
861			||	(window.YAHOO && YAHOO.util && YAHOO.util.Selector && YAHOO.util.Selector.query)
862			||	(window.$$ && function(query) { return $$(query); })
863			||	(window.$ && function(query) { return $(query); })
864			||	(document.querySelectorAll && function(query) { return document.querySelectorAll(query); })
865			||	elementsByTagName
866		),
867		separate: 'words', // 'none' and 'characters' are also accepted
868		softHyphens: true,
869		textless: {
870			dl: 1,
871			html: 1,
872			ol: 1,
873			table: 1,
874			tbody: 1,
875			thead: 1,
876			tfoot: 1,
877			tr: 1,
878			ul: 1
879		},
880		textShadow: 'none',
881		trim: 'advanced'
882	};
883
884	var separators = {
885		// The first pattern may cause unicode characters above
886		// code point 255 to be removed in Safari 3.0. Luckily enough
887		// Safari 3.0 does not include non-breaking spaces in \s, so
888		// we can just use a simple alternative pattern.
889		words: /\s/.test('\u00a0') ? /[^\S\u00a0]+/ : /\s+/,
890		characters: '',
891		none: /^/
892	};
893
894	api.now = function() {
895		DOM.ready();
896		return api;
897	};
898
899	api.refresh = function() {
900		replaceHistory.repeat.apply(replaceHistory, arguments);
901		return api;
902	};
903
904	api.registerEngine = function(id, engine) {
905		if (!engine) return api;
906		engines[id] = engine;
907		return api.set('engine', id);
908	};
909
910	api.registerFont = function(data) {
911		if (!data) return api;
912		var font = new Font(data), family = font.family;
913		if (!fonts[family]) fonts[family] = new FontFamily();
914		fonts[family].add(font);
915		return api.set('fontFamily', '"' + family + '"');
916	};
917	
918	api.hasFont = function(family){
919		if(fonts[family.toLowerCase()]){ 
920			return true; 
921		} else {
922			return false;
923		}
924	};
925
926	api.replace = function(elements, options, ignoreHistory) {
927		options = merge(defaultOptions, options);
928		if (!options.engine) return api; // there's no browser support so we'll just stop here
929		if (!initialized) {
930			CSS.addClass(DOM.root(), 'cufon-active cufon-loading');
931			CSS.ready(function() {
932				// fires before any replace() calls, but it doesn't really matter
933				CSS.addClass(CSS.removeClass(DOM.root(), 'cufon-loading'), 'cufon-ready');
934			});
935			initialized = true;
936		}
937		if (options.hover) options.forceHitArea = true;
938		if (options.autoDetect) delete options.fontFamily;
939		if (typeof options.ignoreClass == 'string') {
940			options.ignoreClass = new RegExp('(?:^|\\s)(?:' + options.ignoreClass.replace(/\s+/g, '|') + ')(?:\\s|$)');
941		}
942		if (typeof options.textShadow == 'string') {
943			options.textShadow = CSS.textShadow(options.textShadow);
944		}
945		if (typeof options.color == 'string' && /^-/.test(options.color)) {
946			options.textGradient = CSS.gradient(options.color);
947		}
948		else delete options.textGradient;
949		if (typeof elements == 'string') {
950			if (!ignoreHistory) replaceHistory.add(elements, arguments);
951			elements = [ elements ];
952		}
953		else if (elements.nodeType) elements = [ elements ];
954		CSS.ready(function() {
955			for (var i = 0, l = elements.length; i < l; ++i) {
956				var el = elements[i];
957				if (typeof el == 'string') api.replace(options.selector(el), options, true);
958				else replaceElement(el, options);
959			}
960		});
961		return api;
962	};
963
964	api.set = function(option, value) {
965		defaultOptions[option] = value;
966		return api;
967	};
968
969	return api;
970
971})();
972
973Cufon.registerEngine('vml', (function() {
974
975	var ns = document.namespaces;
976	if (!ns) return;
977	ns.add('cvml', 'urn:schemas-microsoft-com:vml');
978	ns = null;
979
980	var check = document.createElement('cvml:shape');
981	check.style.behavior = 'url(#default#VML)';
982	if (!check.coordsize) return; // VML isn't supported
983	check = null;
984
985	var HAS_BROKEN_LINEHEIGHT = (document.documentMode || 0) < 8;
986
987	document.write(('<style type="text/css">' +
988		'cufoncanvas{text-indent:0;}' +
989		'@media screen{' +
990			'cvml\\:shape,cvml\\:rect,cvml\\:fill,cvml\\:shadow{behavior:url(#default#VML);display:block;antialias:true;position:absolute;}' +
991			'cufoncanvas{position:absolute;text-align:left;}' +
992			'cufon{display:inline-block;position:relative;vertical-align:' +
993			(HAS_BROKEN_LINEHEIGHT
994				? 'middle'
995				: 'text-bottom') +
996			';}' +
997			'cufon cufontext{position:absolute;left:-10000in;font-size:1px;text-align:left;}' +
998			'cufonshy.cufon-shy-disabled,.cufon-viewport-resizing cufonshy{display:none;}' +
999			'cufonglue{white-space:nowrap;display:inline-block;}' +
1000			'.cufon-viewport-resizing cufonglue{white-space:normal;}' +
1001			'a cufon{cursor:pointer}' + // ignore !important here
1002		'}' +
1003		'@media print{' +
1004			'cufon cufoncanvas{display:none;}' +
1005		'}' +
1006	'</style>').replace(/;/g, '!important;'));
1007
1008	function getFontSizeInPixels(el, value) {
1009		return getSizeInPixels(el, /(?:em|ex|%)$|^[a-z-]+$/i.test(value) ? '1em' : value);
1010	}
1011
1012	// Original by Dead Edwards.
1013	// Combined with getFontSizeInPixels it also works with relative units.
1014	function getSizeInPixels(el, value) {
1015		if (!isNaN(value) || /px$/i.test(value)) return parseFloat(value);
1016		var style = el.style.left, runtimeStyle = el.runtimeStyle.left;
1017		el.runtimeStyle.left = el.currentStyle.left;
1018		el.style.left = value.replace('%', 'em');
1019		var result = el.style.pixelLeft;
1020		el.style.left = style;
1021		el.runtimeStyle.left = runtimeStyle;
1022		return result;
1023	}
1024
1025	function getSpacingValue(el, style, size, property) {
1026		var key = 'computed' + property, value = style[key];
1027		if (isNaN(value)) {
1028			value = style.get(property);
1029			style[key] = value = (value == 'normal') ? 0 : ~~size.convertFrom(getSizeInPixels(el, value));
1030		}
1031		return value;
1032	}
1033
1034	var fills = {};
1035
1036	function gradientFill(gradient) {
1037		var id = gradient.id;
1038		if (!fills[id]) {
1039			var stops = gradient.stops, fill = document.createElement('cvml:fill'), colors = [];
1040			fill.type = 'gradient';
1041			fill.angle = 180;
1042			fill.focus = '0';
1043			fill.method = 'none';
1044			fill.color = stops[0][1];
1045			for (var j = 1, k = stops.length - 1; j < k; ++j) {
1046				colors.push(stops[j][0] * 100 + '% ' + stops[j][1]);
1047			}
1048			fill.colors = colors.join(',');
1049			fill.color2 = stops[k][1];
1050			fills[id] = fill;
1051		}
1052		return fills[id];
1053	}
1054
1055	return function(font, text, style, options, node, el, hasNext) {
1056
1057		var redraw = (text === null);
1058
1059		if (redraw) text = node.alt;
1060
1061		var viewBox = font.viewBox;
1062
1063		var size = style.computedFontSize || (style.computedFontSize = new Cufon.CSS.Size(getFontSizeInPixels(el, style.get('fontSize')) + 'px', font.baseSize));
1064
1065		var wrapper, canvas;
1066
1067		if (redraw) {
1068			wrapper = node;
1069			canvas = node.firstChild;
1070		}
1071		else {
1072			wrapper = document.createElement('cufon');
1073			wrapper.className = 'cufon cufon-vml';
1074			wrapper.alt = text;
1075
1076			canvas = document.createElement('cufoncanvas');
1077			wrapper.appendChild(canvas);
1078
1079			if (options.printable) {
1080				var print = document.createElement('cufontext');
1081				print.appendChild(document.createTextNode(text));
1082				wrapper.appendChild(print);
1083			}
1084
1085			// ie6, for some reason, has trouble rendering the last VML element in the document.
1086			// we can work around this by injecting a dummy element where needed.
1087			// @todo find a better solution
1088			if (!hasNext) wrapper.appendChild(document.createElement('cvml:shape'));
1089		}
1090
1091		var wStyle = wrapper.style;
1092		var cStyle = canvas.style;
1093
1094		var height = size.convert(viewBox.height), roundedHeight = Math.ceil(height);
1095		var roundingFactor = roundedHeight / height;
1096		var stretchFactor = roundingFactor * Cufon.CSS.fontStretch(style.get('fontStretch'));
1097		var minX = viewBox.minX, minY = viewBox.minY;
1098
1099		cStyle.height = roundedHeight;
1100		cStyle.top = Math.round(size.convert(minY - font.ascent));
1101		cStyle.left = Math.round(size.convert(minX));
1102
1103		wStyle.height = size.convert(font.height) + 'px';
1104
1105		var color = style.get('color');
1106		var chars = Cufon.CSS.textTransform(text, style).split('');
1107
1108		var jumps = font.spacing(chars,
1109			getSpacingValue(el, style, size, 'letterSpacing'),
1110			getSpacingValue(el, style, size, 'wordSpacing')
1111		);
1112
1113		if (!jumps.length) return null;
1114
1115		var width = jumps.total;
1116		var fullWidth = -minX + width + (viewBox.width - jumps[jumps.length - 1]);
1117
1118		var shapeWidth = size.convert(fullWidth * stretchFactor), roundedShapeWidth = Math.round(shapeWidth);
1119
1120		var coordSize = fullWidth + ',' + viewBox.height, coordOrigin;
1121		var stretch = 'r' + coordSize + 'ns';
1122
1123		var fill = options.textGradient && gradientFill(options.textGradient);
1124
1125		var glyphs = font.glyphs, offsetX = 0;
1126		var shadows = options.textShadow;
1127		var i = -1, j = 0, chr;
1128
1129		while (chr = chars[++i]) {
1130
1131			var glyph = glyphs[chars[i]] || font.missingGlyph, shape;
1132			if (!glyph) continue;
1133
1134			if (redraw) {
1135				// some glyphs may be missing so we can't use i
1136				shape = canvas.childNodes[j];
1137				while (shape.firstChild) shape.removeChild(shape.firstChild); // shadow, fill
1138			}
1139			else {
1140				shape = document.createElement('cvml:shape');
1141				canvas.appendChild(shape);
1142			}
1143
1144			shape.stroked = 'f';
1145			shape.coordsize = coordSize;
1146			shape.coordorigin = coordOrigin = (minX - offsetX) + ',' + minY;
1147			shape.path = (glyph.d ? 'm' + glyph.d + 'xe' : '') + 'm' + coordOrigin + stretch;
1148			shape.fillcolor = color;
1149
1150			if (fill) shape.appendChild(fill.cloneNode(false));
1151
1152			// it's important to not set top/left or IE8 will grind to a halt
1153			var sStyle = shape.style;
1154			sStyle.width = roundedShapeWidth;
1155			sStyle.height = roundedHeight;
1156
1157			if (shadows) {
1158				// due to the limitations of the VML shadow element there
1159				// can only be two visible shadows. opacity is shared
1160				// for all shadows.
1161				var shadow1 = shadows[0], shadow2 = shadows[1];
1162				var color1 = Cufon.CSS.color(shadow1.color), color2;
1163				var shadow = document.createElement('cvml:shadow');
1164				shadow.on = 't';
1165				shadow.color = color1.color;
1166				shadow.offset = shadow1.offX + ',' + shadow1.offY;
1167				if (shadow2) {
1168					color2 = Cufon.CSS.color(shadow2.color);
1169					shadow.type = 'double';
1170					shadow.color2 = color2.color;
1171					shadow.offset2 = shadow2.offX + ',' + shadow2.offY;
1172				}
1173				shadow.opacity = color1.opacity || (color2 && color2.opacity) || 1;
1174				shape.appendChild(shadow);
1175			}
1176
1177			offsetX += jumps[j++];
1178		}
1179
1180		// addresses flickering issues on :hover
1181
1182		var cover = shape.nextSibling, coverFill, vStyle;
1183
1184		if (options.forceHitArea) {
1185
1186			if (!cover) {
1187				cover = document.createElement('cvml:rect');
1188				cover.stroked = 'f';
1189				cover.className = 'cufon-vml-cover';
1190				coverFill = document.createElement('cvml:fill');
1191				coverFill.opacity = 0;
1192				cover.appendChild(coverFill);
1193				canvas.appendChild(cover);
1194			}
1195
1196			vStyle = cover.style;
1197
1198			vStyle.width = roundedShapeWidth;
1199			vStyle.height = roundedHeight;
1200
1201		}
1202		else if (cover) canvas.removeChild(cover);
1203
1204		wStyle.width = Math.max(Math.ceil(size.convert(width * stretchFactor)), 0);
1205
1206		if (HAS_BROKEN_LINEHEIGHT) {
1207
1208			var yAdjust = style.computedYAdjust;
1209
1210			if (yAdjust === undefined) {
1211				var lineHeight = style.get('lineHeight');
1212				if (lineHeight == 'normal') lineHeight = '1em';
1213				else if (!isNaN(lineHeight)) lineHeight += 'em'; // no unit
1214				style.computedYAdjust = yAdjust = 0.5 * (getSizeInPixels(el, lineHeight) - parseFloat(wStyle.height));
1215			}
1216
1217			if (yAdjust) {
1218				wStyle.marginTop = Math.ceil(yAdjust) + 'px';
1219				wStyle.marginBottom = yAdjust + 'px';
1220			}
1221
1222		}
1223
1224		return wrapper;
1225
1226	};
1227
1228})());
1229
1230Cufon.registerEngine('canvas', (function() {
1231
1232	// Safari 2 doesn't support .apply() on native methods
1233
1234	var check = document.createElement('canvas');
1235	if (!check || !check.getContext || !check.getContext.apply) return;
1236	check = null;
1237
1238	var HAS_INLINE_BLOCK = Cufon.CSS.supports('display', 'inline-block');
1239
1240	// Firefox 2 w/ non-strict doctype (almost standards mode)
1241	var HAS_BROKEN_LINEHEIGHT = !HAS_INLINE_BLOCK && (document.compatMode == 'BackCompat' || /frameset|transitional/i.test(document.doctype.publicId));
1242
1243	var styleSheet = document.createElement('style');
1244	styleSheet.type = 'text/css';
1245	styleSheet.appendChild(document.createTextNode((
1246		'cufon{text-indent:0;}' +
1247		'@media screen,projection{' +
1248			'cufon{display:inline;display:inline-block;position:relative;vertical-align:middle;' +
1249			(HAS_BROKEN_LINEHEIGHT
1250				? ''
1251				: 'font-size:1px;line-height:1px;') +
1252			'}cufon cufontext{display:-moz-inline-box;display:inline-block;width:0;height:0;text-align:left;text-indent:-10000in;}' +
1253			(HAS_INLINE_BLOCK
1254				? 'cufon canvas{position:relative;}'
1255				: 'cufon canvas{position:absolute;}') +
1256			'cufonshy.cufon-shy-disabled,.cufon-viewport-resizing cufonshy{display:none;}' +
1257			'cufonglue{white-space:nowrap;display:inline-block;}' +
1258			'.cufon-viewport-resizing cufonglue{white-space:normal;}' +
1259		'}' +
1260		'@media print{' +
1261			'cufon{padding:0;}' + // Firefox 2
1262			'cufon canvas{display:none;}' +
1263		'}'
1264	).replace(/;/g, '!important;')));
1265	document.getElementsByTagName('head')[0].appendChild(styleSheet);
1266
1267	function generateFromVML(path, context) {
1268		var atX = 0, atY = 0;
1269		var code = [], re = /([mrvxe])([^a-z]*)/g, match;
1270		generate: for (var i = 0; match = re.exec(path); ++i) {
1271			var c = match[2].split(',');
1272			switch (match[1]) {
1273				case 'v':
1274					code[i] = { m: 'bezierCurveTo', a: [ atX + ~~c[0], atY + ~~c[1], atX + ~~c[2], atY + ~~c[3], atX += ~~c[4], atY += ~~c[5] ] };
1275					break;
1276				case 'r':
1277					code[i] = { m: 'lineTo', a: [ atX += ~~c[0], atY += ~~c[1] ] };
1278					break;
1279				case 'm':
1280					code[i] = { m: 'moveTo', a: [ atX = ~~c[0], atY = ~~c[1] ] };
1281					break;
1282				case 'x':
1283					code[i] = { m: 'closePath' };
1284					break;
1285				case 'e':
1286					break generate;
1287			}
1288			context[code[i].m].apply(context, code[i].a);
1289		}
1290		return code;
1291	}
1292
1293	function interpret(code, context) {
1294		for (var i = 0, l = code.length; i < l; ++i) {
1295			var line = code[i];
1296			context[line.m].apply(context, line.a);
1297		}
1298	}
1299
1300	return function(font, text, style, options, node, el) {
1301
1302		var redraw = (text === null);
1303
1304		if (redraw) text = node.getAttribute('alt');
1305
1306		var viewBox = font.viewBox;
1307
1308		var size = style.getSize('fontSize', font.baseSize);
1309
1310		var expandTop = 0, expandRight = 0, expandBottom = 0, expandLeft = 0;
1311		var shadows = options.textShadow, shadowOffsets = [];
1312		if (shadows) {
1313			for (var i = shadows.length; i--;) {
1314				var shadow = shadows[i];
1315				var x = size.convertFrom(parseFloat(shadow.offX));
1316				var y = size.convertFrom(parseFloat(shadow.offY));
1317				shadowOffsets[i] = [ x, y ];
1318				if (y < expandTop) expandTop = y;
1319				if (x > expandRight) expandRight = x;
1320				if (y > expandBottom) expandBottom = y;
1321				if (x < expandLeft) expandLeft = x;
1322			}
1323		}
1324
1325		var chars = Cufon.CSS.textTransform(text, style).split('');
1326
1327		var jumps = font.spacing(chars,
1328			~~size.convertFrom(parseFloat(style.get('letterSpacing')) || 0),
1329			~~size.convertFrom(parseFloat(style.get('wordSpacing')) || 0)
1330		);
1331
1332		if (!jumps.length) return null; // there's nothing to render
1333
1334		var width = jumps.total;
1335
1336		expandRight += viewBox.width - jumps[jumps.length - 1];
1337		expandLeft += viewBox.minX;
1338
1339		var wrapper, canvas;
1340
1341		if (redraw) {
1342			wrapper = node;
1343			canvas = node.firstChild;
1344		}
1345		else {
1346			wrapper = document.createElement('cufon');
1347			wrapper.className = 'cufon cufon-canvas';
1348			wrapper.setAttribute('alt', text);
1349
1350			canvas = document.createElement('canvas');
1351			wrapper.appendChild(canvas);
1352
1353			if (options.printable) {
1354				var print = document.createElement('cufontext');
1355				print.appendChild(document.createTextNode(text));
1356				wrapper.appendChild(print);
1357			}
1358		}
1359
1360		var wStyle = wrapper.style;
1361		var cStyle = canvas.style;
1362
1363		var height = size.convert(viewBox.height);
1364		var roundedHeight = Math.ceil(height);
1365		var roundingFactor = roundedHeight / height;
1366		var stretchFactor = roundingFactor * Cufon.CSS.fontStretch(style.get('fontStretch'));
1367		var stretchedWidth = width * stretchFactor;
1368
1369		var canvasWidth = Math.ceil(size.convert(stretchedWidth + expandRight - expandLeft));
1370		var canvasHeight = Math.ceil(size.convert(viewBox.height - expandTop + expandBottom));
1371
1372		canvas.width = canvasWidth;
1373		canvas.height = canvasHeight;
1374
1375		// needed for WebKit and full page zoom
1376		cStyle.width = canvasWidth + 'px';
1377		cStyle.height = canvasHeight + 'px';
1378
1379		// minY has no part in canvas.height
1380		expandTop += viewBox.minY;
1381
1382		cStyle.top = Math.round(size.convert(expandTop - font.ascent)) + 'px';
1383		cStyle.left = Math.round(size.convert(expandLeft)) + 'px';
1384
1385		var wrapperWidth = Math.max(Math.ceil(size.convert(stretchedWidth)), 0) + 'px';
1386
1387		if (HAS_INLINE_BLOCK) {
1388			wStyle.width = wrapperWidth;
1389			wStyle.height = size.convert(font.height) + 'px';
1390		}
1391		else {
1392			wStyle.paddingLeft = wrapperWidth;
1393			wStyle.paddingBottom = (size.convert(font.height) - 1) + 'px';
1394		}
1395
1396		var g = canvas.getContext('2d'), scale = height / viewBox.height;
1397
1398		// proper horizontal scaling is performed later
1399		g.scale(scale, scale * roundingFactor);
1400		g.translate(-expandLeft, -expandTop);
1401		g.save();
1402
1403		function renderText() {
1404			var glyphs = font.glyphs, glyph, i = -1, j = -1, chr;
1405			g.scale(stretchFactor, 1);
1406			while (chr = chars[++i]) {
1407				var glyph = glyphs[chars[i]] || font.missingGlyph;
1408				if (!glyph) continue;
1409				if (glyph.d) {
1410					g.beginPath();
1411					if (glyph.code) interpret(glyph.code, g);
1412					else glyph.code = generateFromVML('m' + glyph.d, g);
1413					g.fill();
1414				}
1415				g.translate(jumps[++j], 0);
1416			}
1417			g.restore();
1418		}
1419
1420		if (shadows) {
1421			for (var i = shadows.length; i--;) {
1422				var shadow = shadows[i];
1423				g.save();
1424				g.fillStyle = shadow.color;
1425				g.translate.apply(g, shadowOffsets[i]);
1426				renderText();
1427			}
1428		}
1429
1430		var gradient = options.textGradient;
1431		if (gradient) {
1432			var stops = gradient.stops, fill = g.createLinearGradient(0, viewBox.minY, 0, viewBox.maxY);
1433			for (var i = 0, l = stops.length; i < l; ++i) {
1434				fill.addColorStop.apply(fill, stops[i]);
1435			}
1436			g.fillStyle = fill;
1437		}
1438		else g.fillStyle = style.get('color');
1439
1440		renderText();
1441
1442		return wrapper;
1443
1444	};
1445
1446})());

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.