/*! * Copyright (c) 2009 Simo Kinnunen. * Licensed under the MIT license. */ var Cufon = (function() { var api = function() { return api.replace.apply(null, arguments); }; var DOM = api.DOM = { ready: (function() { var complete = false, readyStatus = { loaded: 1, complete: 1 }; var queue = [], perform = function() { if (complete) return; complete = true; for (var fn; fn = queue.shift(); fn()); }; // Gecko, Opera, WebKit r26101+ if (document.addEventListener) { document.addEventListener('DOMContentLoaded', perform, false); window.addEventListener('pageshow', perform, false); // For cached Gecko pages } // Old WebKit, Internet Explorer if (!window.opera && document.readyState) (function() { readyStatus[document.readyState] ? perform() : setTimeout(arguments.callee, 10); })(); // Internet Explorer if (document.readyState && document.createStyleSheet) (function() { try { document.body.doScroll('left'); perform(); } catch (e) { setTimeout(arguments.callee, 1); } })(); addEvent(window, 'load', perform); // Fallback return function(listener) { if (!arguments.length) perform(); else complete ? listener() : queue.push(listener); }; })() }; var CSS = api.CSS = { Size: function(value, base) { this.value = parseFloat(value); this.unit = String(value).match(/[a-z%]*$/)[0] || 'px'; this.convert = function(value) { return value / base * this.value; }; this.convertFrom = function(value) { return value / this.value * base; }; this.toString = function() { return this.value + this.unit; }; }, getStyle: function(el) { var view = document.defaultView; if (view && view.getComputedStyle) return new Style(view.getComputedStyle(el, null)); if (el.currentStyle) return new Style(el.currentStyle); return new Style(el.style); }, ready: (function() { var complete = false; var queue = [], perform = function() { complete = true; for (var fn; fn = queue.shift(); fn()); }; // Safari 2 does not include '); } var typeIndex = 0; // this is used to reference VML ShapeTypes function getFontSizeInPixels(el, value) { return getSizeInPixels(el, /(?:em|ex|%)$/i.test(value) ? '1em' : value); } // Original by Dead Edwards. // Combined with getFontSizeInPixels it also works with relative units. function getSizeInPixels(el, value) { if (/px$/i.test(value)) return parseFloat(value); var style = el.style.left, runtimeStyle = el.runtimeStyle.left; el.runtimeStyle.left = el.currentStyle.left; el.style.left = value; var result = el.style.pixelLeft; el.style.left = style; el.runtimeStyle.left = runtimeStyle; return result; } function createType(glyph, viewBox) { var shapeType = document.createElement('cvml:shapetype'); shapeType.id = 'cufon-glyph-' + typeIndex++; glyph.typeRef = '#' + shapeType.id; shapeType.stroked = 'f'; shapeType.coordsize = viewBox.width + ',' + viewBox.height; shapeType.coordorigin = viewBox.minX + ',' + viewBox.minY; var ensureSize = 'm' + viewBox.minX + ',' + viewBox.minY + ' r' + viewBox.width + ',' + viewBox.height; shapeType.path = (glyph.d ? 'm' + glyph.d + 'x' : '') + ensureSize; document.body.insertBefore(shapeType, document.body.firstChild); } return function(font, text, style, options, node, el, hasNext) { var redraw = (text === null); if (redraw) text = node.alt; // @todo word-spacing, text-decoration var viewBox = font.viewBox; var size = style.computedFontSize || (style.computedFontSize = new Cufon.CSS.Size(getFontSizeInPixels(el, style.get('fontSize')) + 'px', font.baseSize)); var letterSpacing = style.computedLSpacing; if (letterSpacing == undefined) { letterSpacing = style.get('letterSpacing'); style.computedLSpacing = letterSpacing = (letterSpacing == 'normal') ? 0 : size.convertFrom(getSizeInPixels(el, letterSpacing)); } var wrapper, canvas; if (redraw) { wrapper = node; canvas = node.firstChild; } else { wrapper = document.createElement('span'); wrapper.className = 'cufon cufon-vml'; wrapper.alt = text; canvas = document.createElement('cvml:group'); wrapper.appendChild(canvas); if (options.printable) { var print = document.createElement('span'); print.className = 'cufon-alt'; print.innerText = text; wrapper.appendChild(print); } // ie6, for some reason, has trouble rendering the last VML element in the document. // we can work around this by injecting a dummy element where needed. // @todo find a better solution if (!hasNext) wrapper.appendChild(document.createElement('cvml:group')); } var wStyle = wrapper.style; var cStyle = canvas.style; var height = size.convert(viewBox.height); cStyle.height = Math.ceil(height); cStyle.top = Math.round(size.convert(viewBox.minY - font.ascent)); cStyle.left = Math.round(size.convert(viewBox.minX)); var roundingFactor = parseInt(cStyle.height, 10) / height; wStyle.height = size.convert(-font.ascent + font.descent) + 'px'; var textDecoration = options.enableTextDecoration ? Cufon.CSS.textDecoration(el, style) : {}; var color = style.get('color'); var chars = Cufon.CSS.textTransform(text, style).split(''); var width = 0, offsetX = 0, advance = null; var shadows = options.textShadow; for (var i = 0, k = -1, l = chars.length; i < l; ++i) { var glyph = font.glyphs[chars[i]] || font.missingGlyph, shape; if (!glyph) continue; if (!glyph.typeRef) createType(glyph, viewBox); if (redraw) { // some glyphs may be missing so we can't use i shape = canvas.childNodes[++k]; } else { shape = document.createElement('cvml:shape'); canvas.appendChild(shape); } shape.type = glyph.typeRef; var sStyle = shape.style; sStyle.width = viewBox.width; sStyle.height = viewBox.height; sStyle.top = 0; sStyle.left = offsetX; sStyle.zIndex = 1; shape.fillcolor = color; if (shadows) { // the VML shadow element is not used because it can only support // up to 2 shadows. and it breaks text selection. for (var z = 0, p = shadows.length; z < p; ++z) { var shadow = shadows[z]; var shadowColor = Cufon.CSS.color(shadow.color); var shadowNode = shape.cloneNode(false), zStyle = shadowNode.runtimeStyle; zStyle.top = size.convertFrom(parseFloat(shadow.offY)); zStyle.left = offsetX + size.convertFrom(parseFloat(shadow.offX)); zStyle.zIndex = 0; shadowNode.fillcolor = shadowColor.color; if (shadowColor.opacity) { var shadowFill = document.createElement('cvml:fill'); shadowFill.opacity = shadowColor.opacity; shadowNode.appendChild(shadowFill); } canvas.appendChild(shadowNode); } ++k; } advance = Number(glyph.w || font.w) + letterSpacing; width += advance; offsetX += advance; } if (advance === null) return null; var fullWidth = -viewBox.minX + width + (viewBox.width - advance); canvas.coordsize = fullWidth + ',' + viewBox.height; cStyle.width = size.convert(fullWidth * roundingFactor); wStyle.width = Math.max(Math.ceil(size.convert(width * roundingFactor)), 0); return wrapper; }; })());