SVG.Text = SVG.invent({ // Initialize node create: function() { this.constructor.call(this, SVG.create('text')) this._leading = new SVG.Number(1.3) // store leading value for rebuilding this._rebuild = true // enable automatic updating of dy values this._build = false // disable build mode for adding multiple lines // set default font this.attr('font-family', SVG.defaults.attrs['font-family']) } // Inherit from , inherit: SVG.Shape // Add class methods , extend: { // Move over x-axis x: function(x) { // act as getter if (x == null) return this.attr('x') // move lines as well if no textPath is present if (!this.textPath) this.lines().each(function() { if (this.newLined) this.x(x) }) return this.attr('x', x) } // Move over y-axis , y: function(y) { var oy = this.attr('y') , o = typeof oy === 'number' ? oy - this.bbox().y : 0 // act as getter if (y == null) return typeof oy === 'number' ? oy - o : oy return this.attr('y', typeof y === 'number' ? y + o : y) } // Move center over x-axis , cx: function(x) { return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2) } // Move center over y-axis , cy: function(y) { return y == null ? this.bbox().cy : this.y(y - this.bbox().height / 2) } // Set the text content , text: function(text) { // act as getter if (typeof text === 'undefined') return this.content // remove existing content this.clear().build(true) if (typeof text === 'function') { // call block text.call(this, this) } else { // store text and make sure text is not blank text = (this.content = text).split('\n') // build new lines for (var i = 0, il = text.length; i < il; i++) this.tspan(text[i]).newLine() } // disable build mode and rebuild lines return this.build(false).rebuild() } // Set font size , size: function(size) { return this.attr('font-size', size).rebuild() } // Set / get leading , leading: function(value) { // act as getter if (value == null) return this._leading // act as setter this._leading = new SVG.Number(value) return this.rebuild() } // Get all the first level lines , lines: function() { // filter tspans and map them to SVG.js instances for (var i = 0, il = this.node.childNodes.length, lines = []; i < il; i++) if (this.node.childNodes[i] instanceof SVGElement) lines.push(SVG.adopt(this.node.childNodes[i])) // return an instance of SVG.set return new SVG.Set(lines) } // Rebuild appearance type , rebuild: function(rebuild) { // store new rebuild flag if given if (typeof rebuild == 'boolean') this._rebuild = rebuild // define position of all lines if (this._rebuild) { var self = this this.lines().each(function() { if (this.newLined) { if (!this.textPath) this.attr('x', self.attr('x')) this.attr('dy', self._leading * new SVG.Number(self.attr('font-size'))) } }) this.fire('rebuild') } return this } // Enable / disable build mode , build: function(build) { this._build = !!build return this } } // Add parent method , construct: { // Create text element text: function(text) { return this.put(new SVG.Text).text(text) } // Create plain text element , plain: function(text) { return this.put(new SVG.Text).plain(text) } } }) SVG.Tspan = SVG.invent({ // Initialize node create: 'tspan' // Inherit from , inherit: SVG.Shape // Add class methods , extend: { // Set text content text: function(text) { typeof text === 'function' ? text.call(this, this) : this.plain(text) return this } // Shortcut dx , dx: function(dx) { return this.attr('dx', dx) } // Shortcut dy , dy: function(dy) { return this.attr('dy', dy) } // Create new line , newLine: function() { // fetch text parent var t = this.doc(SVG.Text) // mark new line this.newLined = true // apply new hy¡n return this.dy(t._leading * t.attr('font-size')).attr('x', t.x()) } } }) SVG.extend(SVG.Text, SVG.Tspan, { // Create plain text node plain: function(text) { // clear if build mode is disabled if (this._build === false) this.clear() // create text node this.node.appendChild(document.createTextNode((this.content = text))) return this } // Create a tspan , tspan: function(text) { var node = (this.textPath() || this).node , tspan = new SVG.Tspan // clear if build mode is disabled if (this._build === false) this.clear() // add new tspan node.appendChild(tspan.node) // only first level tspans are considered to be "lines" // that doenst make sence. A line is added to a SVG.Set which is never used or returned. // So why bother adding it? // Also: lines() reads all children so it already has this tspan in it because we added it before if (this instanceof SVG.Text) this.lines().add(tspan) return tspan.text(text) } // Clear all lines , clear: function() { var node = (this.textPath() || this).node // remove existing child nodes while (node.hasChildNodes()) node.removeChild(node.lastChild) // reset content references if (this instanceof SVG.Text) this.content = '' return this } // Get length of text element , length: function() { return this.node.getComputedTextLength() } })