var Class = require('../../core/class'); var Path = require('./path'); var Base = require('./base'); var Surface = require('./surface'); var DOM = require('./dom'); var createElement = DOM.createElement; var ua = typeof navigator !== 'undefined' && navigator && navigator.userAgent, hasBaseline = !(/opera|safari|ie/i).test(ua) || (/chrome/i).test(ua); var fontAnchors = { left: 'start', center: 'middle', right: 'end' }, fontAnchorOffsets = { middle: '50%', end: '100%' }; module.exports = Class(Base, { base_initialize: Base.prototype.initialize, initialize: function(text, font, alignment, path){ this.base_initialize('text'); this.draw.apply(this, arguments); }, draw: function(text, font, alignment, path){ var element = this.element; if (font){ if (typeof font == 'string'){ element.style.font = font; } else { for (var key in font){ var ckey = key.camelCase ? key.camelCase() : key; // NOT UNIVERSALLY SUPPORTED OPTIONS // if (ckey == 'kerning') element.setAttribute('kerning', font[key] ? 'auto' : '0'); // else if (ckey == 'letterSpacing') element.setAttribute('letter-spacing', Number(font[key]) + 'ex'); // else if (ckey == 'rotateGlyphs') element.setAttribute('glyph-orientation-horizontal', font[key] ? '270deg' : ''); // else element.style[ckey] = font[key]; } element.style.lineHeight = '0.5em'; } } if (alignment) element.setAttribute('text-anchor', this.textAnchor = (fontAnchors[alignment] || alignment)); if (path && typeof path != 'number'){ this._createPaths(new Path(path)); } else if (path === false){ this._ejectPaths(); this.pathElements = null; } var paths = this.pathElements, child; while ((child = element.firstChild)){ element.removeChild(child); } // Note: Gecko will (incorrectly) align gradients for each row, while others applies one for the entire element var lines = String(text).split(/\r?\n/), l = lines.length, baseline = 'central'; if (paths && l > paths.length) l = paths.length; if (hasBaseline) element.setAttribute('dominant-baseline', baseline); DOM.preserveSpace(element); for (var i = 0; i < l; i++){ var line = lines[i], row, content; if (paths){ row = createElement('textPath'); DOM.link(row, '#' + paths[i].getAttribute('id')); row.setAttribute('startOffset', fontAnchorOffsets[this.textAnchor] || 0); } else { row = createElement('tspan'); row.setAttribute('x', 0); row.setAttribute('y', (i * 1.1 + 0.5) + 'em'); } if (hasBaseline){ row.setAttribute('dominant-baseline', baseline); content = row; } else if (paths){ content = createElement('tspan'); content.setAttribute('dy', '0.35em'); row.appendChild(content); } else { content = row; row.setAttribute('y', (i * 1.1 + 0.85) + 'em'); } DOM.preserveSpace(content); content.appendChild(DOM.createTextNode(line)); element.appendChild(row); } // Measure // TODO: Move to lazy ES5 left/top/width/height/bottom/right property getters var bb; try { bb = element.getBBox(); } catch (x){ } if (!bb || !bb.width) bb = this._whileInDocument(element.getBBox, element); this.left = bb.x; this.top = bb.y; this.width = bb.width; this.height = bb.height; this.right = bb.x + bb.width; this.bottom = bb.y + bb.height; return this; }, // TODO: Unify path injection with gradients and imagefills base_place: Base.prototype._place, _place: function(){ if (this.parentNode){ this._injectPaths(); } else { this._ejectPaths(); } return this.base_place(); }, _injectPaths: function(){ var paths = this.pathElements; if (!this.parentNode || !paths) return; var defs = this.parentNode.defs; for (var i = 0, l = paths.length; i < l; i++) defs.appendChild(paths[i]); }, _ejectPaths: function(){ var paths = this.pathElements; if (!paths) return; for (var i = 0, l = paths; i < l; i++){ var path = paths[i]; if (path.parentNode) path.parentNode.removeChild(paths[i]); } }, _createPaths: function(path){ this._ejectPaths(); var id = 'p' + DOM.uniqueID() + '-'; //splitPaths = []; splitPath = ['M', 0, 0]; //path.visit(splitLine, splitCurve, null, splitMove); //splitPaths.push(splitPath); var splitPaths = [path.path]; var result = []; for (var i = 0, l = splitPaths.length; i < l; i++){ var p = createElement('path'); p.setAttribute('d', splitPaths[i].join(' ')); p.setAttribute('id', id + i); result.push(p); } this.pathElements = result; this._injectPaths(); }, _whileInDocument: function(fn, bind){ // Temporarily inject into the document var element = this.element, container = this.parentNode, parent = element.parentNode, sibling = element.nextSibling, body = element.ownerDocument.body, canvas = new Surface(1, 1).inject(body); this.inject(canvas); var result = fn.call(bind); canvas.eject(); if (container) this.inject(container); if (parent) parent.insertBefore(element, sibling); return result; } }); /* split each continuous line into individual paths */ /* var pathSplitter = new CorePath(); pathSplitter.splitPaths = []; var PathPerRow = Class(CorePath, { function splitMove(sx, sy, x, y){ if (splitPath.length > 3) splitPaths.push(splitPath); splitPath = ['M', x, y]; }; function splitLine(sx, sy, x, y){ splitPath.push('L', x, y); }; function splitCurve(sx, sy, p1x, p1y, p2x, p2y, x, y){ splitPath.push('C', p1x, p1y, p2x, p2y, x, y); }; });*/