diff options
Diffstat (limited to 'src')
-rwxr-xr-x[-rw-r--r--] | src/data.js | 0 | ||||
-rwxr-xr-x | src/default.js | 4 | ||||
-rwxr-xr-x | src/doc.js | 65 | ||||
-rwxr-xr-x | src/element.js | 61 | ||||
-rwxr-xr-x | src/fx.js | 774 | ||||
-rwxr-xr-x[-rw-r--r--] | src/hyperlink.js | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | src/inventor.js | 0 | ||||
-rwxr-xr-x | src/path.js | 2 | ||||
-rwxr-xr-x[-rw-r--r--] | src/patharray.js | 337 | ||||
-rwxr-xr-x[-rw-r--r--] | src/pattern.js | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | src/pointarray.js | 2 | ||||
-rwxr-xr-x | src/regex.js | 3 | ||||
-rwxr-xr-x[-rw-r--r--] | src/relative.js | 34 | ||||
-rwxr-xr-x | src/sugar.js | 47 | ||||
-rwxr-xr-x | src/svg.js | 8 | ||||
-rwxr-xr-x | src/text.js | 220 |
16 files changed, 732 insertions, 825 deletions
diff --git a/src/data.js b/src/data.js index 1adbd52..1adbd52 100644..100755 --- a/src/data.js +++ b/src/data.js diff --git a/src/default.js b/src/default.js index e8eeecd..b9819a7 100755 --- a/src/default.js +++ b/src/default.js @@ -30,6 +30,10 @@ SVG.defaults = { , offset: 0 , 'stop-opacity': 1 , 'stop-color': '#000000' + /* text */ + , 'font-size': 16 + , 'font-family': 'Helvetica, Arial, sans-serif' + , 'text-anchor': 'start' } // Default transformation values @@ -21,11 +21,11 @@ SVG.Doc = SVG.invent({ this._defs.parent = this this.node.appendChild(this._defs.node) - /* turno of sub pixel offset by default */ - this.doSubPixelOffsetFix = false + /* turn off sub pixel offset by default */ + this.doSpof = false /* ensure correct rendering */ - if (this.parent.nodeName != 'svg') + if (this.parent != this.node) this.stage() } @@ -34,50 +34,20 @@ SVG.Doc = SVG.invent({ // Add class methods , extend: { - // Hack for safari preventing text to be rendered in one line. - // Basically it sets the position of the svg node to absolute - // when the dom is loaded, and resets it to relative a few milliseconds later. - // It also handles sub-pixel offset rendering properly. + /* enable drawing */ stage: function() { - var check - , element = this - , wrapper = document.createElement('div') + var element = this - /* set temporary wrapper to position relative */ - wrapper.style.cssText = 'position:relative;height:100%;' + /* insert element */ + this.parent.appendChild(this.node) - /* put element into wrapper */ - element.parent.appendChild(wrapper) - wrapper.appendChild(element.node) - - /* check for dom:ready */ - check = function() { - if (document.readyState === 'complete') { - element.style('position:absolute;') - setTimeout(function() { - /* set position back to relative */ - element.style('position:relative;overflow:hidden;') - - /* remove temporary wrapper */ - element.parent.removeChild(element.node.parentNode) - element.node.parentNode.removeChild(element.node) - element.parent.appendChild(element.node) - - /* after wrapping is done, fix sub-pixel offset */ - element.subPixelOffsetFix() - - /* make sure sub-pixel offset is fixed every time the window is resized */ - SVG.on(window, 'resize', function() { - element.subPixelOffsetFix() - }) - - }, 5) - } else { - setTimeout(check, 10) - } - } - - check() + /* fix sub-pixel offset */ + element.spof() + + /* make sure sub-pixel offset is fixed every time the window is resized */ + SVG.on(window, 'resize', function() { + element.spof() + }) return this } @@ -89,8 +59,8 @@ SVG.Doc = SVG.invent({ // Fix for possible sub-pixel offset. See: // https://bugzilla.mozilla.org/show_bug.cgi?id=608812 - , subPixelOffsetFix: function() { - if (this.doSubPixelOffsetFix) { + , spof: function() { + if (this.doSpof) { var pos = this.node.getScreenCTM() if (pos) @@ -102,8 +72,9 @@ SVG.Doc = SVG.invent({ return this } + // Enable sub-pixel offset , fixSubPixelOffset: function() { - this.doSubPixelOffsetFix = true + this.doSpof = true return this } diff --git a/src/element.js b/src/element.js index 9ae2cd3..116e8d1 100755 --- a/src/element.js +++ b/src/element.js @@ -147,33 +147,18 @@ SVG.Element = SVG.invent({ this.node.removeAttribute(a) } else if (v == null) { - /* act as a getter for style attributes */ - if (this._isStyle(a)) { - return a == 'text' ? - this.content : - a == 'leading' && this.leading ? - this.leading() : - this.style(a) - /* act as a getter if the first and only argument is not an object */ - } else { - v = this.node.getAttribute(a) - return v == null ? - SVG.defaults.attrs[a] : - SVG.regex.test(v, 'isNumber') ? - parseFloat(v) : v - } + v = this.node.getAttribute(a) + return v == null ? + SVG.defaults.attrs[a] : + SVG.regex.test(v, 'isNumber') ? + parseFloat(v) : v } else if (a == 'style') { /* redirect to the style method */ return this.style(v) } else { - /* treat x differently on text elements */ - if (a == 'x' && Array.isArray(this.lines)) - for (n = this.lines.length - 1; n >= 0; n--) - this.lines[n].attr(a, v) - /* BUG FIX: some browsers will render a stroke if a color is given even though stroke width is 0 */ if (a == 'stroke-width') this.attr('stroke', parseFloat(v) > 0 ? this._stroke : null) @@ -192,7 +177,7 @@ SVG.Element = SVG.invent({ } /* ensure full hex color */ - if (SVG.Color.test(v) || SVG.Color.isRgb(v)) + if (SVG.Color.isColor(v)) v = new SVG.Color(v) /* ensure correct numeric values */ @@ -203,23 +188,21 @@ SVG.Element = SVG.invent({ else if (Array.isArray(v)) v = new SVG.Array(v) - /* set give attribute on node */ - n != null ? - this.node.setAttributeNS(n, a, v.toString()) : - this.node.setAttribute(a, v.toString()) - - /* if the passed argument belongs in the style as well, add it there */ - if (this._isStyle(a)) { - a == 'text' ? - this.text(v) : - a == 'leading' && this.leading ? - this.leading(v) : - this.style(a, v) - - /* rebuild if required */ - if (this.rebuild) - this.rebuild(a, v) + /* if the passed attribute is leading... */ + if (a == 'leading') { + /* ... call the leading method instead */ + if (this.leading) + this.leading(v) + } else { + /* set give attribute on node */ + n != null ? + this.node.setAttributeNS(n, a, v.toString()) : + this.node.setAttribute(a, v.toString()) } + + /* rebuild if required */ + if (this.rebuild && (a == 'font-size' || a == 'x')) + this.rebuild(a, v) } return this @@ -388,10 +371,6 @@ SVG.Element = SVG.invent({ return element } - // Private: tester method for style detection - , _isStyle: function(a) { - return typeof a == 'string' ? SVG.regex.test(a, 'isStyle') : false - } // Private: parse a matrix string , _parseMatrix: function(o) { if (o.matrix) { @@ -1,431 +1,439 @@ -SVG.FX = function(element) { - /* store target element */ - this.target = element -} +SVG.FX = SVG.invent({ + // Initialize FX object + create: function(element) { + /* store target element */ + this.target = element + } -SVG.extend(SVG.FX, { - // Add animation parameters and start animation - animate: function(d, ease, delay) { - var akeys, tkeys, skeys, key - , element = this.target - , fx = this - - /* dissect object if one is passed */ - if (typeof d == 'object') { - delay = d.delay - ease = d.ease - d = d.duration - } + // Add class methods +, extend: { + // Add animation parameters and start animation + animate: function(d, ease, delay) { + var akeys, tkeys, skeys, key + , element = this.target + , fx = this + + /* dissect object if one is passed */ + if (typeof d == 'object') { + delay = d.delay + ease = d.ease + d = d.duration + } - /* ensure default duration and easing */ - d = d == '=' ? d : d == null ? 1000 : new SVG.Number(d).valueOf() - ease = ease || '<>' - - /* process values */ - fx.to = function(pos) { - var i - - /* normalise pos */ - pos = pos < 0 ? 0 : pos > 1 ? 1 : pos - - /* collect attribute keys */ - if (akeys == null) { - akeys = [] - for (key in fx.attrs) - akeys.push(key) - - /* make sure morphable elements are scaled, translated and morphed all together */ - if (element.morphArray && (fx._plot || akeys.indexOf('points') > -1)) { - /* get destination */ - var box - , p = new element.morphArray(fx._plot || fx.attrs.points || element.array) - - /* add size */ - if (fx._size) p.size(fx._size.width.to, fx._size.height.to) - - /* add movement */ - box = p.bbox() - if (fx._x) p.move(fx._x.to, box.y) - else if (fx._cx) p.move(fx._cx.to - box.width / 2, box.y) - - box = p.bbox() - if (fx._y) p.move(box.x, fx._y.to) - else if (fx._cy) p.move(box.x, fx._cy.to - box.height / 2) - - /* delete element oriented changes */ - delete fx._x - delete fx._y - delete fx._cx - delete fx._cy - delete fx._size - - fx._plot = element.array.morph(p) + /* ensure default duration and easing */ + d = d == '=' ? d : d == null ? 1000 : new SVG.Number(d).valueOf() + ease = ease || '<>' + + /* process values */ + fx.to = function(pos) { + var i + + /* normalise pos */ + pos = pos < 0 ? 0 : pos > 1 ? 1 : pos + + /* collect attribute keys */ + if (akeys == null) { + akeys = [] + for (key in fx.attrs) + akeys.push(key) + + /* make sure morphable elements are scaled, translated and morphed all together */ + if (element.morphArray && (fx._plot || akeys.indexOf('points') > -1)) { + /* get destination */ + var box + , p = new element.morphArray(fx._plot || fx.attrs.points || element.array) + + /* add size */ + if (fx._size) p.size(fx._size.width.to, fx._size.height.to) + + /* add movement */ + box = p.bbox() + if (fx._x) p.move(fx._x.to, box.y) + else if (fx._cx) p.move(fx._cx.to - box.width / 2, box.y) + + box = p.bbox() + if (fx._y) p.move(box.x, fx._y.to) + else if (fx._cy) p.move(box.x, fx._cy.to - box.height / 2) + + /* delete element oriented changes */ + delete fx._x + delete fx._y + delete fx._cx + delete fx._cy + delete fx._size + + fx._plot = element.array.morph(p) + } } - } - /* collect transformation keys */ - if (tkeys == null) { - tkeys = [] - for (key in fx.trans) - tkeys.push(key) - } + /* collect transformation keys */ + if (tkeys == null) { + tkeys = [] + for (key in fx.trans) + tkeys.push(key) + } - /* collect style keys */ - if (skeys == null) { - skeys = [] - for (key in fx.styles) - skeys.push(key) - } + /* collect style keys */ + if (skeys == null) { + skeys = [] + for (key in fx.styles) + skeys.push(key) + } - /* apply easing */ - pos = ease == '<>' ? - (-Math.cos(pos * Math.PI) / 2) + 0.5 : - ease == '>' ? - Math.sin(pos * Math.PI / 2) : - ease == '<' ? - -Math.cos(pos * Math.PI / 2) + 1 : - ease == '-' ? - pos : - typeof ease == 'function' ? - ease(pos) : - pos - - /* run plot function */ - if (fx._plot) { - element.plot(fx._plot.at(pos)) + /* apply easing */ + pos = ease == '<>' ? + (-Math.cos(pos * Math.PI) / 2) + 0.5 : + ease == '>' ? + Math.sin(pos * Math.PI / 2) : + ease == '<' ? + -Math.cos(pos * Math.PI / 2) + 1 : + ease == '-' ? + pos : + typeof ease == 'function' ? + ease(pos) : + pos + + /* run plot function */ + if (fx._plot) { + element.plot(fx._plot.at(pos)) + + } else { + /* run all x-position properties */ + if (fx._x) + element.x(at(fx._x, pos)) + else if (fx._cx) + element.cx(at(fx._cx, pos)) + + /* run all y-position properties */ + if (fx._y) + element.y(at(fx._y, pos)) + else if (fx._cy) + element.cy(at(fx._cy, pos)) + + /* run all size properties */ + if (fx._size) + element.size(at(fx._size.width, pos), at(fx._size.height, pos)) + } - } else { - /* run all x-position properties */ - if (fx._x) - element.x(at(fx._x, pos)) - else if (fx._cx) - element.cx(at(fx._cx, pos)) - - /* run all y-position properties */ - if (fx._y) - element.y(at(fx._y, pos)) - else if (fx._cy) - element.cy(at(fx._cy, pos)) - - /* run all size properties */ - if (fx._size) - element.size(at(fx._size.width, pos), at(fx._size.height, pos)) + /* run all viewbox properties */ + if (fx._viewbox) + element.viewbox( + at(fx._viewbox.x, pos) + , at(fx._viewbox.y, pos) + , at(fx._viewbox.width, pos) + , at(fx._viewbox.height, pos) + ) + + /* animate attributes */ + for (i = akeys.length - 1; i >= 0; i--) + element.attr(akeys[i], at(fx.attrs[akeys[i]], pos)) + + /* animate transformations */ + for (i = tkeys.length - 1; i >= 0; i--) + element.transform(tkeys[i], at(fx.trans[tkeys[i]], pos)) + + /* animate styles */ + for (i = skeys.length - 1; i >= 0; i--) + element.style(skeys[i], at(fx.styles[skeys[i]], pos)) + + /* callback for each keyframe */ + if (fx._during) + fx._during.call(element, pos, function(from, to) { + return at({ from: from, to: to }, pos) + }) } + + if (typeof d === 'number') { + /* delay animation */ + this.timeout = setTimeout(function() { + var start = new Date().getTime() + + /* initialize situation object */ + fx.situation = { + interval: 1000 / 60 + , start: start + , play: true + , finish: start + d + , duration: d + } - /* run all viewbox properties */ - if (fx._viewbox) - element.viewbox( - at(fx._viewbox.x, pos) - , at(fx._viewbox.y, pos) - , at(fx._viewbox.width, pos) - , at(fx._viewbox.height, pos) - ) - - /* animate attributes */ - for (i = akeys.length - 1; i >= 0; i--) - element.attr(akeys[i], at(fx.attrs[akeys[i]], pos)) - - /* animate transformations */ - for (i = tkeys.length - 1; i >= 0; i--) - element.transform(tkeys[i], at(fx.trans[tkeys[i]], pos)) - - /* animate styles */ - for (i = skeys.length - 1; i >= 0; i--) - element.style(skeys[i], at(fx.styles[skeys[i]], pos)) - - /* callback for each keyframe */ - if (fx._during) - fx._during.call(element, pos, function(from, to) { - return at({ from: from, to: to }, pos) - }) - } - - if (typeof d === 'number') { - /* delay animation */ - this.timeout = setTimeout(function() { - var start = new Date().getTime() - - /* initialize situation object */ - fx.situation = { - interval: 1000 / 60 - , start: start - , play: true - , finish: start + d - , duration: d - } - - /* render function */ - fx.render = function(){ - - if (fx.situation.play === true) { - // This code was borrowed from the emile.js micro framework by Thomas Fuchs, aka MadRobby. - var time = new Date().getTime() - , pos = time > fx.situation.finish ? 1 : (time - fx.situation.start) / d - - /* process values */ - fx.to(pos) + /* render function */ + fx.render = function(){ - /* finish off animation */ - if (time > fx.situation.finish) { - if (fx._plot) - element.plot(new SVG.PointArray(fx._plot.destination).settle()) - - if (fx._loop === true || (typeof fx._loop == 'number' && fx._loop > 1)) { - if (typeof fx._loop == 'number') - --fx._loop - fx.animate(d, ease, delay) + if (fx.situation.play === true) { + // This code was borrowed from the emile.js micro framework by Thomas Fuchs, aka MadRobby. + var time = new Date().getTime() + , pos = time > fx.situation.finish ? 1 : (time - fx.situation.start) / d + + /* process values */ + fx.to(pos) + + /* finish off animation */ + if (time > fx.situation.finish) { + if (fx._plot) + element.plot(new SVG.PointArray(fx._plot.destination).settle()) + + if (fx._loop === true || (typeof fx._loop == 'number' && fx._loop > 1)) { + if (typeof fx._loop == 'number') + --fx._loop + fx.animate(d, ease, delay) + } else { + fx._after ? fx._after.apply(element, [fx]) : fx.stop() + } + } else { - fx._after ? fx._after.apply(element, [fx]) : fx.stop() + requestAnimFrame(fx.render) } - } else { requestAnimFrame(fx.render) } - } else { - requestAnimFrame(fx.render) + } + + /* start animation */ + fx.render() - } + }, new SVG.Number(delay).valueOf()) + } + + return this + } + // Get bounding box of target element + , bbox: function() { + return this.target.bbox() + } + // Add animatable attributes + , attr: function(a, v) { + if (typeof a == 'object') { + for (var key in a) + this.attr(key, a[key]) + + } else { + var from = this.target.attr(a) - /* start animation */ - fx.render() - - }, new SVG.Number(delay).valueOf()) + this.attrs[a] = SVG.Color.isColor(from) ? + new SVG.Color(from).morph(v) : + SVG.regex.unit.test(from) ? + new SVG.Number(from).morph(v) : + { from: from, to: v } + } + + return this } - - return this - } - // Get bounding box of target element -, bbox: function() { - return this.target.bbox() - } - // Add animatable attributes -, attr: function(a, v) { - if (typeof a == 'object') { - for (var key in a) - this.attr(key, a[key]) - - } else { - var from = this.target.attr(a) - - this.attrs[a] = SVG.Color.isColor(from) ? - new SVG.Color(from).morph(v) : - SVG.regex.unit.test(from) ? - new SVG.Number(from).morph(v) : - { from: from, to: v } + // Add animatable transformations + , transform: function(o, v) { + if (arguments.length == 1) { + /* parse matrix string */ + o = this.target._parseMatrix(o) + + /* dlete matrixstring from object */ + delete o.matrix + + /* store matrix values */ + for (v in o) + this.trans[v] = { from: this.target.trans[v], to: o[v] } + + } else { + /* apply transformations as object if key value arguments are given*/ + var transform = {} + transform[o] = v + + this.transform(transform) + } + + return this } - - return this - } - // Add animatable transformations -, transform: function(o, v) { - if (arguments.length == 1) { - /* parse matrix string */ - o = this.target._parseMatrix(o) + // Add animatable styles + , style: function(s, v) { + if (typeof s == 'object') + for (var key in s) + this.style(key, s[key]) - /* dlete matrixstring from object */ - delete o.matrix + else + this.styles[s] = { from: this.target.style(s), to: v } - /* store matrix values */ - for (v in o) - this.trans[v] = { from: this.target.trans[v], to: o[v] } + return this + } + // Animatable x-axis + , x: function(x) { + this._x = { from: this.target.x(), to: x } - } else { - /* apply transformations as object if key value arguments are given*/ - var transform = {} - transform[o] = v + return this + } + // Animatable y-axis + , y: function(y) { + this._y = { from: this.target.y(), to: y } - this.transform(transform) + return this } - - return this - } - // Add animatable styles -, style: function(s, v) { - if (typeof s == 'object') - for (var key in s) - this.style(key, s[key]) - - else - this.styles[s] = { from: this.target.style(s), to: v } - - return this - } - // Animatable x-axis -, x: function(x) { - this._x = { from: this.target.x(), to: x } - - return this - } - // Animatable y-axis -, y: function(y) { - this._y = { from: this.target.y(), to: y } - - return this - } - // Animatable center x-axis -, cx: function(x) { - this._cx = { from: this.target.cx(), to: x } - - return this - } - // Animatable center y-axis -, cy: function(y) { - this._cy = { from: this.target.cy(), to: y } - - return this - } - // Add animatable move -, move: function(x, y) { - return this.x(x).y(y) - } - // Add animatable center -, center: function(x, y) { - return this.cx(x).cy(y) - } - // Add animatable size -, size: function(width, height) { - if (this.target instanceof SVG.Text) { - /* animate font size for Text elements */ - this.attr('font-size', width) + // Animatable center x-axis + , cx: function(x) { + this._cx = { from: this.target.cx(), to: x } - } else { - /* animate bbox based size for all other elements */ - var box = this.target.bbox() + return this + } + // Animatable center y-axis + , cy: function(y) { + this._cy = { from: this.target.cy(), to: y } + + return this + } + // Add animatable move + , move: function(x, y) { + return this.x(x).y(y) + } + // Add animatable center + , center: function(x, y) { + return this.cx(x).cy(y) + } + // Add animatable size + , size: function(width, height) { + if (this.target instanceof SVG.Text) { + /* animate font size for Text elements */ + this.attr('font-size', width) + + } else { + /* animate bbox based size for all other elements */ + var box = this.target.bbox() - this._size = { - width: { from: box.width, to: width } - , height: { from: box.height, to: height } + this._size = { + width: { from: box.width, to: width } + , height: { from: box.height, to: height } + } } + + return this } - - return this - } - // Add animatable plot -, plot: function(p) { - this._plot = p + // Add animatable plot + , plot: function(p) { + this._plot = p - return this - } - // Add animatable viewbox -, viewbox: function(x, y, width, height) { - if (this.target instanceof SVG.Container) { - var box = this.target.viewbox() + return this + } + // Add animatable viewbox + , viewbox: function(x, y, width, height) { + if (this.target instanceof SVG.Container) { + var box = this.target.viewbox() + + this._viewbox = { + x: { from: box.x, to: x } + , y: { from: box.y, to: y } + , width: { from: box.width, to: width } + , height: { from: box.height, to: height } + } + } - this._viewbox = { - x: { from: box.x, to: x } - , y: { from: box.y, to: y } - , width: { from: box.width, to: width } - , height: { from: box.height, to: height } + return this + } + // Add animateable gradient update + , update: function(o) { + if (this.target instanceof SVG.Stop) { + if (o.opacity != null) this.attr('stop-opacity', o.opacity) + if (o.color != null) this.attr('stop-color', o.color) + if (o.offset != null) this.attr('offset', new SVG.Number(o.offset)) } + + return this } - - return this - } - // Add animateable gradient update -, update: function(o) { - if (this.target instanceof SVG.Stop) { - if (o.opacity != null) this.attr('stop-opacity', o.opacity) - if (o.color != null) this.attr('stop-color', o.color) - if (o.offset != null) this.attr('offset', new SVG.Number(o.offset)) + // Add callback for each keyframe + , during: function(during) { + this._during = during + + return this } - - return this - } - // Add callback for each keyframe -, during: function(during) { - this._during = during - - return this - } - // Callback after animation -, after: function(after) { - this._after = after - - return this - } - // Make loopable -, loop: function(times) { - this._loop = times || true - - return this - } - // Stop running animation -, stop: function() { - /* stop current animation */ - clearTimeout(this.timeout) - clearInterval(this.interval) - - /* reset storage for properties that need animation */ - this.attrs = {} - this.trans = {} - this.styles = {} - this.situation = {} - - delete this._x - delete this._y - delete this._cx - delete this._cy - delete this._size - delete this._plot - delete this._loop - delete this._after - delete this._during - delete this._viewbox - - return this - } - // Pause running animation -, pause: function() { - if (this.situation.play === true) { - this.situation.play = false - this.situation.pause = new Date().getTime() + // Callback after animation + , after: function(after) { + this._after = after + + return this } + // Make loopable + , loop: function(times) { + this._loop = times || true - return this - } - // Play running animation -, play: function() { - if (this.situation.play === false) { - var pause = new Date().getTime() - this.situation.pause + return this + } + // Stop running animation + , stop: function() { + /* stop current animation */ + clearTimeout(this.timeout) + clearInterval(this.interval) - this.situation.finish += pause - this.situation.start += pause - this.situation.play = true + /* reset storage for properties that need animation */ + this.attrs = {} + this.trans = {} + this.styles = {} + this.situation = {} + + delete this._x + delete this._y + delete this._cx + delete this._cy + delete this._size + delete this._plot + delete this._loop + delete this._after + delete this._during + delete this._viewbox + + return this } + // Pause running animation + , pause: function() { + if (this.situation.play === true) { + this.situation.play = false + this.situation.pause = new Date().getTime() + } - return this - } - -}) + return this + } + // Play running animation + , play: function() { + if (this.situation.play === false) { + var pause = new Date().getTime() - this.situation.pause + + this.situation.finish += pause + this.situation.start += pause + this.situation.play = true + } -SVG.extend(SVG.Element, { - // Get fx module or create a new one, then animate with given duration and ease - animate: function(d, ease, delay) { - return (this.fx || (this.fx = new SVG.FX(this))).stop().animate(d, ease, delay) - } - // Stop current animation; this is an alias to the fx instance -, stop: function() { - if (this.fx) - this.fx.stop() + return this + } - return this } - // Pause current animation -, pause: function() { - if (this.fx) - this.fx.pause() - return this - } - // Play paused current animation -, play: function() { - if (this.fx) - this.fx.play() + // Define parent class +, parent: SVG.Element - return this + // Add method to parent elements +, construct: { + // Get fx module or create a new one, then animate with given duration and ease + animate: function(d, ease, delay) { + return (this.fx || (this.fx = new SVG.FX(this))).stop().animate(d, ease, delay) + } + // Stop current animation; this is an alias to the fx instance + , stop: function() { + if (this.fx) + this.fx.stop() + + return this + } + // Pause current animation + , pause: function() { + if (this.fx) + this.fx.pause() + + return this + } + // Play paused current animation + , play: function() { + if (this.fx) + this.fx.play() + + return this + } + } - }) // Calculate position according to from and to diff --git a/src/hyperlink.js b/src/hyperlink.js index 1981d52..1981d52 100644..100755 --- a/src/hyperlink.js +++ b/src/hyperlink.js diff --git a/src/inventor.js b/src/inventor.js index f8244f5..f8244f5 100644..100755 --- a/src/inventor.js +++ b/src/inventor.js diff --git a/src/path.js b/src/path.js index 0345487..ad231dc 100755 --- a/src/path.js +++ b/src/path.js @@ -9,7 +9,7 @@ SVG.Path = SVG.invent({ , extend: { // Plot new poly points plot: function(p) { - return this.attr('d', (this.array = new SVG.PathArray(p, [{ type:'M',x:0,y:0 }]))) + return this.attr('d', (this.array = new SVG.PathArray(p, [['M', 0, 0]]))) } // Move by left top corner , move: function(x, y) { diff --git a/src/patharray.js b/src/patharray.js index 314f53e..2ccb7d5 100644..100755 --- a/src/patharray.js +++ b/src/patharray.js @@ -9,48 +9,7 @@ SVG.PathArray.prototype = new SVG.Array SVG.extend(SVG.PathArray, { // Convert array to string toString: function() { - for (var s, i = 0, il = this.value.length, array = []; i < il; i++) { - s = [this.value[i].type] - - switch(this.value[i].type) { - case 'H': - s.push(this.value[i].x) - break - case 'V': - s.push(this.value[i].y) - break - case 'M': - case 'L': - case 'T': - case 'S': - case 'Q': - case 'C': - if (/[QC]/.test(this.value[i].type)) - s.push(this.value[i].x1, this.value[i].y1) - if (/[CS]/.test(this.value[i].type)) - s.push(this.value[i].x2, this.value[i].y2) - - s.push(this.value[i].x, this.value[i].y) - - break - case 'A': - s.push( - this.value[i].r1 - , this.value[i].r2 - , this.value[i].a - , this.value[i].l - , this.value[i].s - , this.value[i].x - , this.value[i].y - ) - break - } - - /* add to array */ - array.push(s.join(' ')) - } - - return array.join(' ') + return arrayToString(this.value) } // Move path string , move: function(x, y) { @@ -63,45 +22,35 @@ SVG.extend(SVG.PathArray, { if (!isNaN(x) && !isNaN(y)) { /* move every point */ - for (var i = this.value.length - 1; i >= 0; i--) { - switch (this.value[i].type) { - case 'H': - /* move along x axis only */ - this.value[i].x += x - break - case 'V': - /* move along y axis only */ - this.value[i].y += y - break - case 'M': - case 'L': - case 'T': - case 'S': - case 'Q': - case 'C': - /* move first point along x and y axes */ - this.value[i].x += x - this.value[i].y += y - - /* move third points along x and y axes */ - if (/[CQ]/.test(this.value[i].type)) { - this.value[i].x1 += x - this.value[i].y1 += y - } + for (var l, i = this.value.length - 1; i >= 0; i--) { + l = this.value[i][0] - /* move second points along x and y axes */ - if (/[CS]/.test(this.value[i].type)) { - this.value[i].x2 += x - this.value[i].y2 += y - } + if (l == 'M' || l == 'L' || l == 'T') { + this.value[i][1] += x + this.value[i][2] += y + + } else if (l == 'H') { + this.value[i][1] += x + + } else if (l == 'V') { + this.value[i][1] += y + + } else if (l == 'C' || l == 'S' || l == 'Q') { + this.value[i][1] += x + this.value[i][2] += y + this.value[i][3] += x + this.value[i][4] += y + + if (l == 'C') { + this.value[i][5] += x + this.value[i][6] += y + } - break - case 'A': - /* only move position values */ - this.value[i].x += x - this.value[i].y += y - break + } else if (l == 'A') { + this.value[i][6] += x + this.value[i][7] += y } + } } @@ -110,61 +59,51 @@ SVG.extend(SVG.PathArray, { // Resize path string , size: function(width, height) { /* get bounding box of current situation */ - var box = this.bbox() + var i, l, box = this.bbox() /* recalculate position of all points according to new size */ - for (var i = this.value.length - 1; i >= 0; i--) { - switch (this.value[i].type) { - case 'H': - /* move along x axis only */ - this.value[i].x = ((this.value[i].x - box.x) * width) / box.width + box.x - break - case 'V': - /* move along y axis only */ - this.value[i].y = ((this.value[i].y - box.y) * height) / box.height + box.y - break - case 'M': - case 'L': - case 'T': - case 'S': - case 'Q': - case 'C': - this.value[i].x = ((this.value[i].x - box.x) * width) / box.width + box.x - this.value[i].y = ((this.value[i].y - box.y) * height) / box.height + box.y - - /* move third points along x and y axes */ - if (/[CQ]/.test(this.value[i].type)) { - this.value[i].x1 = ((this.value[i].x1 - box.x) * width) / box.width + box.x - this.value[i].y1 = ((this.value[i].y1 - box.y) * height) / box.height + box.y - } + for (i = this.value.length - 1; i >= 0; i--) { + l = this.value[i][0] - /* move second points along x and y axes */ - if (/[CS]/.test(this.value[i].type)) { - this.value[i].x2 = ((this.value[i].x2 - box.x) * width) / box.width + box.x - this.value[i].y2 = ((this.value[i].y2 - box.y) * height) / box.height + box.y - } + if (l == 'M' || l == 'L' || l == 'T') { + this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x + this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y + + } else if (l == 'H') { + this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x + + } else if (l == 'V') { + this.value[i][1] = ((this.value[i][1] - box.y) * height) / box.height + box.y - break - case 'A': - /* resize radii */ - this.value[i].values.r1 = (this.value[i].values.r1 * width) / box.width - this.value[i].values.r2 = (this.value[i].values.r2 * height) / box.height + } else if (l == 'C' || l == 'S' || l == 'Q') { + this.value[i][1] = ((this.value[i][1] - box.x) * width) / box.width + box.x + this.value[i][2] = ((this.value[i][2] - box.y) * height) / box.height + box.y + this.value[i][3] = ((this.value[i][3] - box.x) * width) / box.width + box.x + this.value[i][4] = ((this.value[i][4] - box.y) * height) / box.height + box.y - /* move position values */ - this.value[i].values.x = ((this.value[i].values.x - box.x) * width) / box.width + box.x - this.value[i].values.y = ((this.value[i].values.y - box.y) * height) / box.height + box.y - break + if (l == 'C') { + this.value[i][5] = ((this.value[i][5] - box.x) * width) / box.width + box.x + this.value[i][6] = ((this.value[i][6] - box.y) * height) / box.height + box.y + } + + } else if (l == 'A') { + /* resize radii */ + this.value[i][1] = (this.value[i][1] * width) / box.width + this.value[i][2] = (this.value[i][2] * height) / box.height + + /* move position values */ + this.value[i][6] = ((this.value[i][6] - box.x) * width) / box.width + box.x + this.value[i][7] = ((this.value[i][7] - box.y) * height) / box.height + box.y } + } return this } // Absolutize and parse path to array , parse: function(array) { - array = array.valueOf() - - /* if already is an array, no need to parse it */ - if (Array.isArray(array)) return array + /* if it's already is a patharray, no need to parse it */ + if (array instanceof SVG.PathArray) return array.valueOf() /* prepare for parsing */ var i, il, x0, y0, x1, y1, x2, y2, s, seg, segs @@ -172,7 +111,7 @@ SVG.extend(SVG.PathArray, { , y = 0 /* populate working path */ - SVG.parser.path.setAttribute('d', array) + SVG.parser.path.setAttribute('d', typeof array === 'string' ? array : arrayToString(array)) /* get segments */ segs = SVG.parser.path.pathSegList @@ -181,7 +120,8 @@ SVG.extend(SVG.PathArray, { seg = segs.getItem(i) s = seg.pathSegTypeAsLetter - if (/[MLHVCSQTA]/.test(s)) { + /* yes, this IS quite verbose but also about 30 times faster than .test() with a precompiled regex */ + if (s == 'M' || s == 'L' || s == 'H' || s == 'V' || s == 'C' || s == 'S' || s == 'Q' || s == 'T' || s == 'A') { if ('x' in seg) x = seg.x if ('y' in seg) y = seg.y @@ -193,44 +133,32 @@ SVG.extend(SVG.PathArray, { if ('x' in seg) x += seg.x if ('y' in seg) y += seg.y - switch(s){ - case 'm': - segs.replaceItem(SVG.parser.path.createSVGPathSegMovetoAbs(x, y), i) - break - case 'l': - segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoAbs(x, y), i) - break - case 'h': - segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoHorizontalAbs(x), i) - break - case 'v': - segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoVerticalAbs(y), i) - break - case 'c': - segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2), i) - break - case 's': - segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(x, y, x2, y2), i) - break - case 'q': - segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticAbs(x, y, x1, y1), i) - break - case 't': - segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(x, y), i) - break - case 'a': - segs.replaceItem(SVG.parser.path.createSVGPathSegArcAbs(x, y, seg.r1, seg.r2, seg.angle, seg.largeArcFlag, seg.sweepFlag), i) - break - case 'z': - case 'Z': - x = x0 - y = y0 - break + if (s == 'm') + segs.replaceItem(SVG.parser.path.createSVGPathSegMovetoAbs(x, y), i) + else if (s == 'l') + segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoAbs(x, y), i) + else if (s == 'h') + segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoHorizontalAbs(x), i) + else if (s == 'v') + segs.replaceItem(SVG.parser.path.createSVGPathSegLinetoVerticalAbs(y), i) + else if (s == 'c') + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2), i) + else if (s == 's') + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(x, y, x2, y2), i) + else if (s == 'q') + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticAbs(x, y, x1, y1), i) + else if (s == 't') + segs.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(x, y), i) + else if (s == 'a') + segs.replaceItem(SVG.parser.path.createSVGPathSegArcAbs(x, y, seg.r1, seg.r2, seg.angle, seg.largeArcFlag, seg.sweepFlag), i) + else if (s == 'z' || s == 'Z') { + x = x0 + y = y0 } } /* record the start of a subpath */ - if (/[Mm]/.test(s)) { + if (s == 'M' || s == 'm') { x0 = x y0 = y } @@ -238,48 +166,30 @@ SVG.extend(SVG.PathArray, { /* build internal representation */ array = [] - segs = SVG.parser.path.pathSegList + segs = SVG.parser.path.pathSegList for (i = 0, il = segs.numberOfItems; i < il; ++i) { seg = segs.getItem(i) - s = {} - - switch (seg.pathSegTypeAsLetter) { - case 'M': - case 'L': - case 'T': - case 'S': - case 'Q': - case 'C': - if (/[QC]/.test(seg.pathSegTypeAsLetter)) { - s.x1 = seg.x1 - s.y1 = seg.y1 - } - - if (/[SC]/.test(seg.pathSegTypeAsLetter)) { - s.x2 = seg.x2 - s.y2 = seg.y2 - } - - break - case 'A': - s = { - r1: seg.r1 - , r2: seg.r2 - , a: seg.angle - , l: seg.largeArcFlag|0 - , s: seg.sweepFlag|0 - } - break - } - - /* make the letter, x and y values accessible as key/values */ - s.type = seg.pathSegTypeAsLetter - s.x = seg.x - s.y = seg.y + s = seg.pathSegTypeAsLetter + x = [s] + + if (s == 'M' || s == 'L' || s == 'T') + x.push(seg.x, seg.y) + else if (s == 'H') + x.push(seg.x) + else if (s == 'V') + x.push(seg.y) + else if (s == 'C') + x.push(seg.x1, seg.y1, seg.x2, seg.y2, seg.x, seg.y) + else if (s == 'S') + x.push(seg.x2, seg.y2, seg.x, seg.y) + else if (s == 'Q') + x.push(seg.x1, seg.y1, seg.x, seg.y) + else if (s == 'A') + x.push(seg.r1, seg.r2, seg.angle, seg.largeArcFlag|0, seg.sweepFlag|0, seg.x, seg.y) /* store segment */ - array.push(s) + array.push(x) } return array @@ -291,4 +201,41 @@ SVG.extend(SVG.PathArray, { return SVG.parser.path.getBBox() } -})
\ No newline at end of file +}) + +// PathArray Helpers +function arrayToString(a) { + for (var i = 0, il = a.length, s = ''; i < il; i++) { + s += a[i][0] + + if (a[i][1] != null) { + s += a[i][1] + + if (a[i][2] != null) { + s += ' ' + s += a[i][2] + + if (a[i][3] != null) { + s += ' ' + s += a[i][3] + s += ' ' + s += a[i][4] + + if (a[i][5] != null) { + s += ' ' + s += a[i][5] + s += ' ' + s += a[i][6] + + if (a[i][7] != null) { + s += ' ' + s += a[i][7] + } + } + } + } + } + } + + return s + ' ' +}
\ No newline at end of file diff --git a/src/pattern.js b/src/pattern.js index e5398d2..e5398d2 100644..100755 --- a/src/pattern.js +++ b/src/pattern.js diff --git a/src/pointarray.js b/src/pointarray.js index 28dbf3e..16f3283 100644..100755 --- a/src/pointarray.js +++ b/src/pointarray.js @@ -76,8 +76,6 @@ SVG.extend(SVG.PointArray, { } // Get bounding box of points , bbox: function() { - if (this._cachedBBox) return this._cachedBBox - SVG.parser.poly.setAttribute('points', this.toString()) return SVG.parser.poly.getBBox() diff --git a/src/regex.js b/src/regex.js index 3a28e64..c75b46f 100755 --- a/src/regex.js +++ b/src/regex.js @@ -23,9 +23,6 @@ SVG.regex = { /* test css declaration */ , isCss: /[^:]+:[^;]+;?/ - /* test css property */ -, isStyle: /^font|text|leading|cursor/ - /* test for blank string */ , isBlank: /^(\s+)?$/ diff --git a/src/relative.js b/src/relative.js index 2d02c08..7759bb9 100644..100755 --- a/src/relative.js +++ b/src/relative.js @@ -1,28 +1,16 @@ // SVG.extend(SVG.Element, SVG.FX, { - // Relative methods - relative: function() { - var b, e = this - - return { - // Move over x axis - x: function(x) { - b = e.bbox() - - return e.x(b.x + (x || 0)) - } - // Move over y axis - , y: function(y) { - b = e.bbox() - - return e.y(b.y + (y || 0)) - } - // Move over x and y axes - , move: function(x, y) { - this.x(x) - return this.y(y) - } - } + // Relative move over x axis + dx: function(x) { + return this.x(this.x() + x) + } + // Relative move over y axis +, dy: function(y) { + return this.y(this.y() + y) + } + // Relative move over x and y axes +, dmove: function(x, y) { + return this.dx(x).dy(y) } })
\ No newline at end of file diff --git a/src/sugar.js b/src/sugar.js index 5c71afc..1a96c23 100755 --- a/src/sugar.js +++ b/src/sugar.js @@ -1,28 +1,25 @@ // Define list of available attributes for stroke and fill -SVG._stroke = ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'] -SVG._fill = ['color', 'opacity', 'rule'] - - -// Prepend correct color prefix -var _colorPrefix = function(type, attr) { - return attr == 'color' ? type : type + '-' + attr +var sugar = { + stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'] +, fill: ['color', 'opacity', 'rule'] +, prefix: function(t, a) { + return a == 'color' ? t : t + '-' + a + } } /* Add sugar for fill and stroke */ -;['fill', 'stroke'].forEach(function(method) { - var extension = {} +;['fill', 'stroke'].forEach(function(m) { + var i, extension = {} - extension[method] = function(o) { - var indexOf - + extension[m] = function(o) { if (typeof o == 'string' || SVG.Color.isRgb(o) || (o && typeof o.fill === 'function')) - this.attr(method, o) + this.attr(m, o) else - /* set all attributes from _fillAttr and _strokeAttr list */ - for (index = SVG['_' + method].length - 1; index >= 0; index--) - if (o[SVG['_' + method][index]] != null) - this.attr(_colorPrefix(method, SVG['_' + method][index]), o[SVG['_' + method][index]]) + /* set all attributes from sugar.fill and sugar.stroke list */ + for (i = sugar[m].length - 1; i >= 0; i--) + if (o[sugar[m][i]] != null) + this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]]) return this } @@ -72,7 +69,7 @@ SVG.extend(SVG.Element, SVG.FX, { }) -SVG.extend(SVG.Rect, SVG.Ellipse, { +SVG.extend(SVG.Rect, SVG.Ellipse, SVG.FX, { // Add x and y radius radius: function(x, y) { return this.attr({ rx: x, ry: y || x }) @@ -95,12 +92,14 @@ SVG.extend(SVG.Path, { SVG.extend(SVG.Text, SVG.FX, { // Set font font: function(o) { - for (var key in o) - key == 'anchor' ? - this.attr('text-anchor', o[key]) : - _styleAttr.indexOf(key) > -1 ? - this.attr('font-'+ key, o[key]) : - this.attr(key, o[key]) + for (var k in o) + k == 'leading' ? + this.leading(o[k]) : + k == 'anchor' ? + this.attr('text-anchor', o[k]) : + k == 'size' || k == 'family' || k == 'weight' || k == 'stretch' || k == 'variant' || k == 'style' ? + this.attr('font-'+ k, o[k]) : + this.attr(k, o[k]) return this } @@ -66,13 +66,17 @@ SVG.prepare = function(element) { /* select document body and create invisible svg element */ var body = document.getElementsByTagName('body')[0] , draw = (body ? new SVG.Doc(body) : element.nested()).size(2, 2) + , path = SVG.create('path') + + /* insert parsers */ + draw.node.appendChild(path) /* create parser object */ SVG.parser = { body: body || element.parent , draw: draw.style('opacity:0;position:fixed;left:100%;top:100%;overflow:hidden') - , poly: draw.polygon().node - , path: draw.path().node + , poly: draw.polyline().node + , path: path } } diff --git a/src/text.js b/src/text.js index 73ef290..9d95b55 100755 --- a/src/text.js +++ b/src/text.js @@ -1,21 +1,15 @@ -// List font style attributes as they should be applied to style -var _styleAttr = ('size family weight stretch variant style').split(' ') - SVG.Text = SVG.invent({ // Initialize node create: function() { this.constructor.call(this, SVG.create('text')) - /* define default style */ - this.styles = { - 'font-size': 16 - , 'font-family': 'Helvetica, Arial, sans-serif' - , 'text-anchor': 'start' - } - - this._leading = new SVG.Number('1.2em') - this._rebuild = true + 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 @@ -24,96 +18,68 @@ SVG.Text = SVG.invent({ // Add class methods , extend: { // Move over x-axis - x: function(x, a) { + x: function(x) { /* act as getter */ if (x == null) - return a ? this.attr('x') : this.bbox().x + return this.attr('x') - /* set x taking anchor in mind */ - if (!a) { - a = this.style('text-anchor') - x = a == 'start' ? x : a == 'end' ? x + this.bbox().width : x + this.bbox().width / 2 - } - - /* move lines as well if no textPath si present */ + /* 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) { + /* act as getter */ + if (y == null) + return this.attr('y') + + return this.attr('y', y + this.attr('y') - this.bbox().y) + } // Move center over x-axis - , cx: function(x, a) { + , cx: function(x) { return x == null ? this.bbox().cx : this.x(x - this.bbox().width / 2) } // Move center over y-axis - , cy: function(y, a) { - return y == null ? this.bbox().cy : this.y(a ? y : y - this.bbox().height / 2) + , cy: function(y) { + return y == null ? this.bbox().cy : this.y(y - this.bbox().height / 2) } // Move element to given x and y values - , move: function(x, y, a) { - return this.x(x, a).y(y) + , move: function(x, y) { + return this.x(x).y(y) } // Move element by its center - , center: function(x, y, a) { - return this.cx(x, a).cy(y, a) + , center: function(x, y) { + return this.cx(x).cy(y) } // Set the text content , text: function(text) { /* act as getter */ - if (text == null) - return this.content + if (!text) return this.content - /* remove existing lines */ - this.clear() + /* remove existing content */ + this.clear().build(true) if (typeof text === 'function') { - this._rebuild = false - + /* call block */ text.call(this, this) } else { - this._rebuild = true - - /* make sure text is not blank */ - text = SVG.regex.isBlank.test(text) ? 'text' : text - - var i, il - , lines = text.split('\n') + /* store text and make sure text is not blank */ + text = (this.content = (SVG.regex.isBlank.test(text) ? 'text' : text)).split('\n') /* build new lines */ - for (i = 0, il = lines.length; i < il; i++) - this.tspan(lines[i]).newLine() - - this.rebuild() + for (var i = 0, il = text.length; i < il; i++) + this.tspan(text[i]).newLine() } - return this - } - // Create a tspan - , tspan: function(text) { - var node = this.textPath ? this.textPath.node : this.node - , tspan = new SVG.TSpan().text(text) - , style = this.style() - - /* add new tspan */ - node.appendChild(tspan.node) - this.lines.add(tspan) - - /* add style if any */ - if (!SVG.regex.isBlank.test(style)) - tspan.style(style) - - /* store content */ - this.content += text - - /* store text parent */ - tspan.parent = this - - return tspan + /* disable build mode and rebuild lines */ + return this.build(false).rebuild() } // Set font size , size: function(size) { - return this.attr('font-size', size) + return this.attr('font-size', size).rebuild() } // Set / get leading , leading: function(value) { @@ -122,47 +88,34 @@ SVG.Text = SVG.invent({ return this._leading /* act as setter */ - value = new SVG.Number(value) - this._leading = value + this._leading = new SVG.Number(value) - /* apply leading */ - this.lines.each(function() { - if (this.newLined) - this.attr('dy', value) - }) - - return this + return this.rebuild() } - // rebuild appearance type - , rebuild: function() { + // Rebuild appearance type + , rebuild: function(rebuild) { var self = this + /* store new rebuild flag if given */ + if (typeof rebuild == 'boolean') + this._rebuild = rebuild + /* define position of all lines */ if (this._rebuild) { - this.lines.attr({ - x: this.attr('x') - , dy: this._leading - , style: this.style() + 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'))) + } }) } return this } - // Clear all lines - , clear: function() { - var node = this.textPath ? this.textPath.node : this.node - - /* remove existing child nodes */ - while (node.hasChildNodes()) - node.removeChild(node.lastChild) - - /* refresh lines */ - delete this.lines - this.lines = new SVG.Set - - /* initialize content */ - this.content = '' - + // Enable / disable build mode + , build: function(build) { + this._build = !!build return this } } @@ -173,7 +126,12 @@ SVG.Text = SVG.invent({ 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({ @@ -187,8 +145,8 @@ SVG.TSpan = SVG.invent({ , extend: { // Set text content text: function(text) { - this.node.appendChild(document.createTextNode(text)) - + typeof text === 'function' ? text.call(this, this) : this.plain(text) + return this } // Shortcut dx @@ -201,12 +159,66 @@ SVG.TSpan = SVG.invent({ } // Create new line , newLine: function() { + /* fetch text parent */ + var t = this.doc(SVG.Text) + + /* mark new line */ this.newLined = true - this.parent.content += '\n' - this.dy(this.parent._leading) - return this.attr('x', this.parent.x()) + + /* 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 and reference */ + node.appendChild(tspan.node) + tspan.parent = this + + /* only first level tspans are considered to be "lines" */ + 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) { + delete this.lines + this.lines = new SVG.Set + this.content = '' + } + + return this + } +}) + |