202 lines
5.4 KiB
JavaScript
202 lines
5.4 KiB
JavaScript
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);
|
|
};
|
|
|
|
});*/
|