From 8f9ccb16300142307b8ed6aed46c2a4984545600 Mon Sep 17 00:00:00 2001 From: wout Date: Fri, 14 Feb 2014 22:25:12 +0100 Subject: Completely reworked `SVG.Text`, `SVG.TSpan` and `SVG.PathArray` and bumped to 1.0.0-rc.5 --- CHANGELOG.md | 15 + Gemfile | 0 Gemfile.lock | 0 MIT-LICENSE | 0 README.md | 364 ++++++-- Rakefile | 2 +- bower.json | 2 +- component.json | 2 +- dist/svg.js | 1556 +++++++++++++++----------------- dist/svg.min.js | 4 +- package.json | 2 +- spec/index.html | 0 spec/lib/jasmine-1.3.1/MIT.LICENSE | 0 spec/lib/jasmine-1.3.1/jasmine-html.js | 0 spec/lib/jasmine-1.3.1/jasmine.css | 0 spec/lib/jasmine-1.3.1/jasmine.js | 0 spec/spec/arrange.js | 0 spec/spec/array.js | 42 + spec/spec/bbox.js | 0 spec/spec/clip.js | 0 spec/spec/color.js | 0 spec/spec/container.js | 30 +- spec/spec/doc.js | 0 spec/spec/element.js | 33 - spec/spec/ellipse.js | 25 + spec/spec/gradient.js | 0 spec/spec/group.js | 24 + spec/spec/helper.js | 0 spec/spec/hyperlink.js | 0 spec/spec/image.js | 25 + spec/spec/line.js | 36 + spec/spec/mask.js | 0 spec/spec/memory.js | 0 spec/spec/number.js | 0 spec/spec/path.js | 38 +- spec/spec/pattern.js | 0 spec/spec/polygon.js | 28 + spec/spec/polyline.js | 28 + spec/spec/rect.js | 25 + spec/spec/set.js | 0 spec/spec/svg.js | 0 spec/spec/text.js | 162 +++- spec/spec/textpath.js | 0 spec/spec/use.js | 0 src/data.js | 0 src/default.js | 4 + src/doc.js | 65 +- src/element.js | 61 +- src/fx.js | 774 ++++++++-------- src/hyperlink.js | 0 src/inventor.js | 0 src/path.js | 2 +- src/patharray.js | 337 +++---- src/pattern.js | 0 src/pointarray.js | 2 - src/regex.js | 3 - src/relative.js | 34 +- src/sugar.js | 47 +- src/svg.js | 8 +- src/text.js | 220 ++--- 60 files changed, 2203 insertions(+), 1797 deletions(-) mode change 100644 => 100755 CHANGELOG.md mode change 100644 => 100755 Gemfile mode change 100644 => 100755 Gemfile.lock mode change 100644 => 100755 MIT-LICENSE mode change 100644 => 100755 README.md mode change 100644 => 100755 Rakefile mode change 100644 => 100755 bower.json mode change 100644 => 100755 component.json mode change 100644 => 100755 dist/svg.js mode change 100644 => 100755 dist/svg.min.js mode change 100644 => 100755 package.json mode change 100644 => 100755 spec/index.html mode change 100644 => 100755 spec/lib/jasmine-1.3.1/MIT.LICENSE mode change 100644 => 100755 spec/lib/jasmine-1.3.1/jasmine-html.js mode change 100644 => 100755 spec/lib/jasmine-1.3.1/jasmine.css mode change 100644 => 100755 spec/lib/jasmine-1.3.1/jasmine.js mode change 100644 => 100755 spec/spec/arrange.js mode change 100644 => 100755 spec/spec/array.js mode change 100644 => 100755 spec/spec/bbox.js mode change 100644 => 100755 spec/spec/clip.js mode change 100644 => 100755 spec/spec/color.js mode change 100644 => 100755 spec/spec/container.js mode change 100644 => 100755 spec/spec/doc.js mode change 100644 => 100755 spec/spec/element.js mode change 100644 => 100755 spec/spec/ellipse.js mode change 100644 => 100755 spec/spec/gradient.js mode change 100644 => 100755 spec/spec/group.js mode change 100644 => 100755 spec/spec/helper.js mode change 100644 => 100755 spec/spec/hyperlink.js mode change 100644 => 100755 spec/spec/image.js mode change 100644 => 100755 spec/spec/line.js mode change 100644 => 100755 spec/spec/mask.js mode change 100644 => 100755 spec/spec/memory.js mode change 100644 => 100755 spec/spec/number.js mode change 100644 => 100755 spec/spec/path.js mode change 100644 => 100755 spec/spec/pattern.js mode change 100644 => 100755 spec/spec/polygon.js mode change 100644 => 100755 spec/spec/polyline.js mode change 100644 => 100755 spec/spec/rect.js mode change 100644 => 100755 spec/spec/set.js mode change 100644 => 100755 spec/spec/svg.js mode change 100644 => 100755 spec/spec/text.js mode change 100644 => 100755 spec/spec/textpath.js mode change 100644 => 100755 spec/spec/use.js mode change 100644 => 100755 src/data.js mode change 100644 => 100755 src/hyperlink.js mode change 100644 => 100755 src/inventor.js mode change 100644 => 100755 src/patharray.js mode change 100644 => 100755 src/pattern.js mode change 100644 => 100755 src/pointarray.js mode change 100644 => 100755 src/relative.js diff --git a/CHANGELOG.md b/CHANGELOG.md old mode 100644 new mode 100755 index d4f6cbf..f0307b7 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +# 1.0.0-rc.5 (14/02/2014) + +- added `plain()` method to `SVG.Text` element to add plain text content, without tspans +- added `plain()` method to parent elements to create a text element without tspans +- updated `SVG.TSpan` to accept nested tspan elements, not unlike the `text()` method in `SVG.Text` +- removed the `relative()` method in favour of `dx()`, `dy()` and `dmove()` +- switched form objects to arrays in `SVG.PathArray` for compatibility with other libraries and better performance on parsing and rendering (up-to 48% faster than 1.0.0-rc.4) +- refined docs on element-specific methods and `SVG.PathArray` structure +- added `build()` to enable/disable build mode +- removed verbose style application to tspans +- reworked `leading()` implementation to be more font-size "aware" +- refactored the `attr` method on `SVG.Element` +- applied Helvetica as default font +- building `SVG.FX` class with `SVG.invent()` function + # 1.0.0-rc.4 (04/02/2014) - switched to `MAJOR`.`MINOR`.`PATCH` versioning format to play nice with package managers diff --git a/Gemfile b/Gemfile old mode 100644 new mode 100755 diff --git a/Gemfile.lock b/Gemfile.lock old mode 100644 new mode 100755 diff --git a/MIT-LICENSE b/MIT-LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index e2302ea..92aa8e0 --- a/README.md +++ b/README.md @@ -181,16 +181,18 @@ __`returns`: `SVG.Defs`__ _Javascript inheritance stack: `SVG.Defs` < `SVG.Container` < `SVG.Parent`_ - -## Elements - -### Rect +## Rect Rects have two arguments, their `width` and `height`: ```javascript var rect = draw.rect(100, 100) ``` +__`returns`: `SVG.Rect`__ + +_Javascript inheritance stack: `SVG.Rect` < `SVG.Shape` < `SVG.Element`_ + +### radius() Rects can also have rounded corners: ```javascript @@ -203,12 +205,10 @@ This will set the `rx` and `ry` attributes to `10`. To set `rx` and `ry` individ rect.radius(10, 20) ``` -__`returns`: `SVG.Rect`__ - -_Javascript inheritance stack: `SVG.Rect` < `SVG.Shape` < `SVG.Element`_ +__`returns`: `itself`__ -### Ellipse +## Ellipse Ellipses, like rects, have two arguments, their `width` and `height`: ```javascript @@ -219,7 +219,16 @@ __`returns`: `SVG.Ellipse`__ _Javascript inheritance stack: `SVG.Ellipse` < `SVG.Shape` < `SVG.Element`_ -### Circle +### radius() +Ellipses can also be redefined by their radii: + +```javascript +rect.radius(75, 50) +``` + +__`returns`: `itself`__ + +## Circle The only argument necessary for a circle is the diameter: ```javascript @@ -232,24 +241,36 @@ _Javascript inheritance stack: `SVG.Ellipse` < `SVG.Shape` < `SVG.Element`_ _Note that this generates an `` element instead of a ``. This choice has been made to keep the size of the library down._ -### Line +### radius() +Circles can also be redefined by their radius: + +```javascript +rect.radius(75) +``` + +__`returns`: `itself`__ + +## Line The line element always takes four arguments, `x1`, `y1`, `x2` and `y2`: ```javascript var line = draw.line(0, 0, 100, 150).stroke({ width: 1 }) ``` +__`returns`: `SVG.Line`__ + +_Javascript inheritance stack: `SVG.Line` < `SVG.Shape` < `SVG.Element`_ + +### plot() Updating a line is done with the `plot()` method: ```javascript line.plot(50, 30, 100, 150) ``` -__`returns`: `SVG.Line`__ - -_Javascript inheritance stack: `SVG.Line` < `SVG.Shape` < `SVG.Element`_ +__`returns`: `itself`__ -### Polyline +## Polyline The polyline element defines a set of connected straight line segments. Typically, polyline elements define open shapes: ```javascript @@ -266,6 +287,11 @@ As an alternative an array of points will work as well: var polyline = draw.polyline([[0,0], [100,50], [50,100]]).fill('none').stroke({ width: 1 }) ``` +__`returns`: `SVG.Polyline`__ + +_Javascript inheritance stack: `SVG.Polyline` < `SVG.Shape` < `SVG.Element`_ + +### plot() Polylines can be updated using the `plot()` method: ```javascript @@ -278,11 +304,9 @@ The `plot()` method can also be animated: polyline.animate(3000).plot([[0,0], [100,50], [50,100], [150,50], [200,50], [250,100], [300,50], [350,50]]) ``` -__`returns`: `SVG.Polyline`__ - -_Javascript inheritance stack: `SVG.Polyline` < `SVG.Shape` < `SVG.Element`_ +__`returns`: `itself`__ -### Polygon +## Polygon The polygon element, unlike the polyline element, defines a closed shape consisting of a set of connected straight line segments: ```javascript @@ -296,17 +320,26 @@ __`returns`: `SVG.Polygon`__ _Javascript inheritance stack: `SVG.Polygon` < `SVG.Shape` < `SVG.Element`_ -### Path -The path string is similar to the polygon string but much more complex in order to support curves: +### plot() +Like polylines, polygons can be updated using the `plot()` method: ```javascript -var path = draw.path('M10,20L30,40') +polygon.plot([[0,0], [100,50], [50,100], [150,50], [200,50]]) ``` -Paths can be updated using the `plot()` method: +The `plot()` method can also be animated: ```javascript -path.plot('M100,200L300,400') +polygon.animate(3000).plot([[0,0], [100,50], [50,100], [150,50], [200,50], [250,100], [300,50], [350,50]]) +``` + +__`returns`: `itself`__ + +## Path +The path string is similar to the polygon string but much more complex in order to support curves: + +```javascript +var path = draw.path('M10,20L30,40') ``` __`returns`: `SVG.Path`__ @@ -316,8 +349,16 @@ _Javascript inheritance stack: `SVG.Path` < `SVG.Shape` < `SVG.Element`_ For more details on path data strings, please refer to the SVG documentation: http://www.w3.org/TR/SVG/paths.html#PathData +### plot() +Paths can be updated using the `plot()` method: -### Image +```javascript +path.plot('M100,200L300,400') +``` + +__`returns`: `itself`__ + +## Image Creating images is as you might expect: ```javascript @@ -330,6 +371,20 @@ If you know the size of the image, those parameters can be passed as the second var image = draw.image('/path/to/image.jpg', 200, 300) ``` +__`returns`: `SVG.Image`__ + +_Javascript inheritance stack: `SVG.Image` < `SVG.Shape` < `SVG.Element`_ + +### load() +Loading another image can be done with the `load()` method: + +```javascript +draw.image('/path/to/another/image.jpg') +``` + +__`returns`: `itself`__ + +### loaded() If you don't know the size of the image, obviously you will have to wait for the image to be `loaded`: ```javascript @@ -344,13 +399,10 @@ The returned `loader` object as first the argument of the loaded method contains - `ratio` (width / height) - `url` - -__`returns`: `SVG.Image`__ - -_Javascript inheritance stack: `SVG.Image` < `SVG.Shape` < `SVG.Element`_ +__`returns`: `itself`__ -### Text +## Text Unlike html, text in svg is much harder to tame. There is no way to create flowing text, so newlines should be entered manually. In svg.js there are two ways to create text elements. The first and easiest method is to provide a string of text, split by newlines: @@ -374,18 +426,54 @@ var text = draw.text(function(add) { }) ``` +If you want to go the other way and don't want to add tspans at all, just one line of text, you can use the `plain()` method instead: + +```javascript +var text = draw.plain('Lorem ipsum dolor sit amet consectetur.') +``` + +This is a shortcut to the `plain` method on the `SVG.Text` instance which doesn't render newlines at all. + +_Javascript inheritance stack: `SVG.Text` < `SVG.Shape` < `SVG.Element`_ + +__`returns`: `SVG.Text`__ + +### text() Changing text afterwards is also possible with the `text()` method: ```javascript text.text('Brilliant!') ``` +__`returns`: `itself`__ + To get the raw text content: ```javascript -text.content +text.text() +``` + +__`returns`: `string`__ + +### tspan() +Just adding one tspan is also possible: + +```javascript +text.tspan(' on a train...').fill('#f06') ``` +__`returns`: `SVG.TSpan`__ + +### plain() +If the content of the element doesn't need any stying or multiple lines, it might be sufficient to just add some plain text: + +```javascript +text.plain('I do not have any expectations.') +``` + +__`returns`: `itself`__ + +### font() The sugar.js module provides some syntax sugar specifically for this element type: ```javascript @@ -397,11 +485,122 @@ text.font({ }) ``` -__`returns`: `SVG.Text`__ +__`returns`: `itself`__ -_Javascript inheritance stack: `SVG.Text` < `SVG.Shape` < `SVG.Element`_ +### leading() +As opposed to html, where leading is defined by `line-height`, svg does not have a natural leading equivalent. In svg, lines are not defined naturally. They are defined by `` nodes with a `dy` attribute defining the line height and a `x` value resetting the line to the `x` position of the parent text element. But you can also have many nodes in one line defining a different `y`, `dy`, `x` or even `dx` value. This gives us a lot of freedom, but also a lot more responsibility. We have to decide when a new line is defined, where it starts, what its offset is and what it's height is. The `leading()` method in svg.js tries to ease the pain by giving you behaviour that is much closer to html. In combination with newline separated text, it works just like html: + +```javascript +var text = draw.text("Lorem ipsum dolor sit amet consectetur.\nCras sodales imperdiet auctor.") +text.leading(1.3) +``` + +This will render a text element with a tspan element for each line, with a `dy` value of `130%` of the font size. + +Note that the `leading()` method assumes that every first level tspan in a text node represents a new line. Using `leading()` on text elements containing multiple tspans in one line (e.g. without a wrapping tspan defining a new line) will render scrambeled. So it is advisable to use this method with care, preferably only when throwing newline separated text at the text element or calling the `newLine()` method on every first level tspan added in the block passed as argument to the text element. + +__`returns`: `itself`__ + +### clear() +Clear all the contents of the called text element: + +```javascript +text.clear() +``` + +__`returns`: `itself`__ + +## TSpan +The tspan elements are only available inside text elements or inside other tspan elements. In svg.js they have a class of their own: + +_Javascript inheritance stack: `SVG.TSpan` < `SVG.Shape` < `SVG.Element`_ + +### text() +Update the content of the tspan. This can be done by either passing a string: + + +```javascript +tspan.text('Just a string.') +``` + +Which will basicly call the `plain()` method. + +Or by passing a block to add more specific content inside the called tspan: + +```javascript +tspan.text(function(add) { + add.plain('Just plain text.') + add.tspan('Fancy text wrapped in a tspan.').fill('#f06') + add.tspan(function(addMore) { + addMore.tspan('And you can doo deeper and deeper...') + }) +}) +``` + +__`returns`: `itself`__ + +### tspan() +Add a nested tspan: + +```javascript +tspan.tspan('I am a child of my parent').fill('#f06') +``` + +__`returns`: `SVG.TSpan`__ -### TextPath +### plain() +Just adds some plain text: + +```javascript +tspan.plain('I do not have any expectations.') +``` + +__`returns`: `itself`__ + +### dx() +Define the dynamic `x` value of the element, much like a html element with `position:relative` and `left` defined: + +```javascript +tspan.dx(30) +``` + +__`returns`: `itself`__ + +### dy() +Define the dynamic `y` value of the element, much like a html element with `position:relative` and `top` defined: + +```javascript +tspan.dy(30) +``` + +__`returns`: `itself`__ + +### newLine() +The `newLine()` is a convenience method for adding a new line with a `dy` attribute using the current "leading": + +```javascript +var text = draw.text(function(add) { + add.tspan('Lorem ipsum dolor sit amet ').newLine() + add.tspan('consectetur').fill('#f06') + add.tspan('.') + add.tspan('Cras sodales imperdiet auctor.').newLine().dx(20) + add.tspan('Nunc ultrices lectus at erat').newLine() + add.tspan('dictum pharetra elementum ante').newLine() +}) +``` + +__`returns`: `itself`__ + +### clear() +Clear all the contents of the called tspan element: + +```javascript +tspan.clear() +``` + +__`returns`: `itself`__ + +## TextPath A nice feature in svg is the ability to run text along a path: ```javascript @@ -433,18 +632,19 @@ And they can be animated as well of course: text.textPath.animate(3000).attr('startOffset', 0.8) ``` +__`returns`: `SVG.TextPath`__ + +_Javascript inheritance stack: `SVG.TextPath` < `SVG.Element`_ + +### track Referencing the linked path element directly: ```javascript var path = text.track ``` -__`returns`: `SVG.TextPath`__ -_Javascript inheritance stack: `SVG.TextPath` < `SVG.Element`_ - - -### Use +## Use The use element simply emulates another existing element. Any changes on the master element will be reflected on all the `use` instances. The usage of `use()` is very straightforward: ```javascript @@ -465,6 +665,7 @@ __`returns`: `SVG.Use`__ _Javascript inheritance stack: `SVG.Use` < `SVG.Shape` < `SVG.Element`_ + ## Referencing elements ### By id @@ -796,15 +997,6 @@ Move the element to a given `x` and `y` position by its upper left corner: rect.move(200, 350) ``` -The `text` element has one optional argument: - -```javascript -// move(x, y, anchor) -rect.move(200, 350, true) -``` - -The third argument can be used to move the text element by its anchor point rather than the calculated left top position. - Note that you can also use the following code to move some elements (like images and rects) around: ```javascript @@ -849,36 +1041,40 @@ rect.y() //-> returns 350 `setter`__`returns`: `itself`__ -### relative() -The `move()`, `x()` and `y()` methods move an element to an absolute position. With the `relative()` method, elements can be moved relatively to their current position. This can be done by calling the `relative()` method before the `move()`, `x()` or `y()` methods in the method chain: +### dmove() +Move the element to a given `x` and `y` position relative to its current position: ```javascript -rect.move(100, 100).relative().move(20, -50) +rect.dmove(10, 30) ``` -This will set the `x` position of the element to `120` and the `y` position to `50`. +__`returns`: `itself`__ -It works the same way for `relative().x()` and `relative().y()`. +### dx() +Move element only along x-axis relative to its current position: -__`returns`: `object`__ +```javascript +rect.dx(200) +``` +__`returns`: `itself`__ -### center() -This is an extra method to move an element by its center: +### dx() +Move element only along y-axis relative to its current position: ```javascript -rect.center(150, 150) +rect.dy(200) ``` -The `text` element has one optional argument: +__`returns`: `itself`__ + +### center() +This is an extra method to move an element by its center: ```javascript -// center(x, y, anchor) -rect.center(150, 150, true) +rect.center(150, 150) ``` -The third argument can be used to center the text element by its anchor point rather than the calculated center position. - __`returns`: `itself`__ ### cx() @@ -936,6 +1132,8 @@ rect.size(null, 200) Same as with `move()` the size of an element could be set by using `attr()`. But because every type of element is handles its size differently the `size()` method is much more convenient. +There is one exceptions though, the `SVG.Text` only takes one argument and applies the given value to the `font-size` attribute. + __`returns`: `itself`__ ### width() @@ -1849,7 +2047,18 @@ stop.at({ offset: 0, color: '#333', opacity: 1 }) __`returns`: `itself`__ -### from() and to() +### from() +To define the direction you can set from `x`, `y` and to `x`, `y`: + +```javascript +gradient.from(0, 0).to(0, 1) +``` + +The from and to values are also expressed in percent. + +__`returns`: `itself`__ + +### to() To define the direction you can set from `x`, `y` and to `x`, `y`: ```javascript @@ -2319,7 +2528,10 @@ Is a bit more complex and is used for polyline and polygon elements. This is a p The dynamic representation: ```javascript -new SVG.PointArray([[0, 0], [100, 100]]) +[ + [0, 0] +, [100, 100] +] ``` Note that every instance of `SVG.Polyline` and `SVG.Polygon` carries a reference to the `SVG.PointArray` instance: @@ -2331,20 +2543,20 @@ polygon.array //-> returns the SVG.PointArray instance _Javascript inheritance stack: `SVG.PointArray` < `SVG.Array`_ ### SVG.PathArray -Path arrays carry objects representing every point in a path string: +Path arrays carry arrays representing every segment in a path string: ```javascript -'M 0 0 L 100 100 z' +'M0 0L100 100z' ``` The dynamic representation: ```javascript -new SVG.PathArray([ - { type: 'M', x: 0, y: 0 } -, { type: 'L', x: 100, y: 100 } -, { type: 'z' } -]) +[ + ['M', 0, 0] +, ['L', 100, 100] +, ['z'] +] ``` Note that every instance of `SVG.Path` carries a reference to the `SVG.PathArray` instance: @@ -2355,7 +2567,6 @@ path.array //-> returns the SVG.PathArray instance _Javascript inheritance stack: `SVG.PathArray` < `SVG.Array`_ - ### morph() In order to animate array values the `morph()` method lets you pass a destination value. This can be either the string value, a plain array or an instance of the same type of svg.js array: @@ -2433,7 +2644,7 @@ __`returns`: `object`__ ## Extending functionality ### SVG.invent() -Creating your own custom elements with svg.js is piece of cake thanks to the `SVG.invent` function. For the sake of this example, lets "invent" a shape. We want a `rect` with rounded corners that are always proportional to the size. The new shape lives in the `SVG` namespace and is called `Rounded`. Here is how we achieve that. +Creating your own custom elements with svg.js is piece of cake thanks to the `SVG.invent` function. For the sake of this example, lets "invent" a shape. We want a `rect` with rounded corners that are always proportional to the height of the element. The new shape lives in the `SVG` namespace and is called `Rounded`. Here is how we achieve that. ```javascript SVG.Rounded = SVG.invent({ @@ -2450,7 +2661,7 @@ SVG.Rounded = SVG.invent({ return this.attr({ width: width , height: height - , rx: width / 5 + , rx: height / 5 , ry: height / 5 }) } @@ -2479,8 +2690,8 @@ That's it, the invention is now ready to be used! The `SVG.invent()` function always expectes an object. The object can have the following configuration values: - `create`: can be either a string with the node name (e.g. `rect`, `ellipse`, ...) or a custom initializer function; `[required]` -- `inherit`: the desired svg.js class to inherit from (e.g. `SVG.Shape`, `SVG.Element`, `SVG.Container`, `SVG.Rect`, ...); `[optional]` -- `extend`: an object with the methods that should eb applied to the element's prototype; `[optional]` +- `inherit`: the desired svg.js class to inherit from (e.g. `SVG.Shape`, `SVG.Element`, `SVG.Container`, `SVG.Rect`, ...); `[optional but recommended]` +- `extend`: an object with the methods that should be applied to the element's prototype; `[optional]` - `construct`: an objects with the methods to create the element on the parent element; `[optional]` - `parent`: an svg.js parent class on which the methods in the passed `construct` object should be available; `[optional]` @@ -2510,7 +2721,7 @@ SVG.extend(SVG.Ellipse, { ``` The complete inheritance stack for `SVG.Ellipse` is: -_SVG.Ellipse < SVG.Shape < SVG.Element_ +_`SVG.Ellipse` < `SVG.Shape` < `SVG.Element`_ The SVG document can be extended by using: @@ -2531,7 +2742,6 @@ SVG.extend(SVG.Ellipse, SVG.Path, SVG.Polygon, { return this.fill('orangered') } }) - ``` diff --git a/Rakefile b/Rakefile old mode 100644 new mode 100755 index c21e183..932c317 --- a/Rakefile +++ b/Rakefile @@ -1,4 +1,4 @@ -SVGJS_VERSION = '1.0.0-rc.4' +SVGJS_VERSION = '1.0.0-rc.5' # all available modules in the correct loading order MODULES = %w[ svg inventor regex default color array pointarray patharray number viewbox bbox rbox element parent container fx relative event defs group arrange mask clip gradient pattern doc shape use rect ellipse line poly path image text textpath nested hyperlink sugar set data memory loader ] diff --git a/bower.json b/bower.json old mode 100644 new mode 100755 index df989c9..7be85f2 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "svg.js", - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "homepage": "http://svgjs.com/", "authors": [ "Wout Fierens " diff --git a/component.json b/component.json old mode 100644 new mode 100755 index e4b6357..5bc99c1 --- a/component.json +++ b/component.json @@ -2,7 +2,7 @@ "name": "svg.js", "repo": "wout/svg.js", "description": "A lightweight library for manipulating and animating SVG", - "version": "1.0.0-rc.4", + "version": "1.0.0-rc.5", "keywords": ["svg"], "author": "Wout Fierens ", "main": "dist/svg.js", diff --git a/dist/svg.js b/dist/svg.js old mode 100644 new mode 100755 index 7cc2ee6..bee59e9 --- a/dist/svg.js +++ b/dist/svg.js @@ -1,4 +1,4 @@ -/* svg.js 1.0.0-rc.4-9-g6841c32 - svg inventor regex default color array pointarray patharray number viewbox bbox rbox element parent container fx relative event defs group arrange mask clip gradient pattern doc shape use rect ellipse line poly path image text textpath nested hyperlink sugar set data memory loader - svgjs.com/license */ +/* svg.js 1.0.0-rc.5 - svg inventor regex default color array pointarray patharray number viewbox bbox rbox element parent container fx relative event defs group arrange mask clip gradient pattern doc shape use rect ellipse line poly path image text textpath nested hyperlink sugar set data memory loader - svgjs.com/license */ ;(function() { this.SVG = function(element) { @@ -67,13 +67,17 @@ /* 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 } } @@ -132,9 +136,6 @@ /* test css declaration */ , isCss: /[^:]+:[^;]+;?/ - /* test css property */ - , isStyle: /^font|text|leading|cursor/ - /* test for blank string */ , isBlank: /^(\s+)?$/ @@ -180,6 +181,10 @@ , offset: 0 , 'stop-opacity': 1 , 'stop-color': '#000000' + /* text */ + , 'font-size': 16 + , 'font-family': 'Helvetica, Arial, sans-serif' + , 'text-anchor': 'start' } // Default transformation values @@ -480,8 +485,6 @@ } // 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() @@ -499,48 +502,7 @@ 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) { @@ -553,45 +515,35 @@ 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 - break - case 'A': - /* only move position values */ - this.value[i].x += x - this.value[i].y += y - break + } 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 + } + + } else if (l == 'A') { + this.value[i][6] += x + this.value[i][7] += y } + } } @@ -600,61 +552,51 @@ // 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 @@ -662,7 +604,7 @@ , 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 @@ -671,7 +613,8 @@ 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 @@ -683,44 +626,32 @@ 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 } @@ -728,48 +659,30 @@ /* 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 @@ -782,6 +695,43 @@ } }) + + // 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 + ' ' + } SVG.Number = function(value) { @@ -1227,33 +1177,18 @@ 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) @@ -1272,7 +1207,7 @@ } /* 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 */ @@ -1283,23 +1218,21 @@ 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 @@ -1468,10 +1401,6 @@ 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) { @@ -1647,434 +1576,442 @@ }) - 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 - } - - /* 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) - } + // 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 } - /* collect transformation keys */ - if (tkeys == null) { - tkeys = [] - for (key in fx.trans) - tkeys.push(key) - } + /* 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 style keys */ - if (skeys == null) { - skeys = [] - for (key in fx.styles) - skeys.push(key) - } + /* collect transformation keys */ + if (tkeys == null) { + tkeys = [] + for (key in fx.trans) + tkeys.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)) + /* collect style keys */ + if (skeys == null) { + skeys = [] + for (key in fx.styles) + skeys.push(key) + } - } 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)) - } + /* 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)) - /* 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 + } 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)) } - /* 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) + /* 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(){ - /* 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()) + /* start animation */ + fx.render() + + }, new SVG.Number(delay).valueOf()) + } + + 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) + // 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 } + 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 - } - // Add animatable transformations - , transform: function(o, v) { - if (arguments.length == 1) { - /* parse matrix string */ - o = this.target._parseMatrix(o) + // 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) + } - /* dlete matrixstring from object */ - delete o.matrix + return this + } + // Add animatable styles + , style: function(s, v) { + if (typeof s == 'object') + for (var key in s) + this.style(key, s[key]) - /* store matrix values */ - for (v in o) - this.trans[v] = { from: this.target.trans[v], to: o[v] } + else + this.styles[s] = { from: this.target.style(s), to: v } - } else { - /* apply transformations as object if key value arguments are given*/ - var transform = {} - transform[o] = v + return this + } + // Animatable x-axis + , x: function(x) { + this._x = { from: this.target.x(), to: x } - 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 y-axis + , y: function(y) { + this._y = { from: this.target.y(), to: y } - } else { - /* animate bbox based size for all other elements */ - var box = this.target.bbox() + 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) + + } 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 } + // Callback after animation + , after: function(after) { + this._after = after + + return this + } + // Make loopable + , loop: function(times) { + this._loop = times || true - 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 - } - // 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() + } - return this - } - // Pause running animation - , pause: function() { - if (this.situation.play === true) { - this.situation.play = false - this.situation.pause = new Date().getTime() + 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 + } - return this + return this + } + } - // Play running animation - , play: function() { - if (this.situation.play === false) { - var pause = new Date().getTime() - this.situation.pause + + // Define parent class + , parent: SVG.Element + + // 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() - this.situation.finish += pause - this.situation.start += pause - this.situation.play = true + return this } + // Pause current animation + , pause: function() { + if (this.fx) + this.fx.pause() - return this - } - - }) + return this + } + // Play paused current animation + , play: function() { + if (this.fx) + this.fx.play() - 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() - - return this } - }) // Calculate position according to from and to @@ -2100,29 +2037,17 @@ })() 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) } }) @@ -2583,11 +2508,11 @@ 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() } @@ -2596,50 +2521,20 @@ // 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') - - /* set temporary wrapper to position relative */ - wrapper.style.cssText = 'position:relative;height:100%;' - - /* 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) - } - } + var element = this + + /* insert element */ + this.parent.appendChild(this.node) - 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 } @@ -2651,8 +2546,8 @@ // 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) @@ -2664,8 +2559,9 @@ return this } + // Enable sub-pixel offset , fixSubPixelOffset: function() { - this.doSubPixelOffsetFix = true + this.doSpof = true return this } @@ -2951,7 +2847,7 @@ , 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) { @@ -3047,22 +2943,17 @@ } }) - 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 @@ -3071,96 +2962,68 @@ // 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) { @@ -3169,47 +3032,34 @@ 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 } } @@ -3220,7 +3070,12 @@ 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({ @@ -3234,8 +3089,8 @@ , 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 @@ -3248,15 +3103,69 @@ } // 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 + } + }) + SVG.TextPath = SVG.invent({ @@ -3369,30 +3278,27 @@ }) - 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 } @@ -3442,7 +3348,7 @@ }) - 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 }) @@ -3465,12 +3371,14 @@ 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 } diff --git a/dist/svg.min.js b/dist/svg.min.js old mode 100644 new mode 100755 index e491292..de8cc65 --- a/dist/svg.min.js +++ b/dist/svg.min.js @@ -1,2 +1,2 @@ -(function(){function t(t,e){return"number"==typeof t.from?t.from+(t.to-t.from)*e:t instanceof SVG.Color||t instanceof SVG.Number?t.at(e):1>e?t.from:t.to}if(this.SVG=function(t){return SVG.supported?(t=new SVG.Doc(t),SVG.parser||SVG.prepare(t),t):void 0},SVG.ns="http://www.w3.org/2000/svg",SVG.xmlns="http://www.w3.org/2000/xmlns/",SVG.xlink="http://www.w3.org/1999/xlink",SVG.did=1e3,SVG.eid=function(t){return"Svgjs"+t.charAt(0).toUpperCase()+t.slice(1)+SVG.did++},SVG.create=function(t){var e=document.createElementNS(this.ns,t);return e.setAttribute("id",this.eid(t)),e},SVG.extend=function(){var t,e,i,n;for(t=[].slice.call(arguments),e=t.pop(),n=t.length-1;n>=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i];SVG.Set&&SVG.Set.inherit&&SVG.Set.inherit()},SVG.get=function(t){var e=document.getElementById(t);return e?e.instance:void 0},SVG.prepare=function(t){var e=document.getElementsByTagName("body")[0],i=(e?new SVG.Doc(e):t.nested()).size(2,2);SVG.parser={body:e||t.parent,draw:i.style("opacity:0;position:fixed;left:100%;top:100%;overflow:hidden"),poly:i.polygon().node,path:i.path().node}},SVG.supported=function(){return!!document.createElementNS&&!!document.createElementNS(SVG.ns,"svg").createSVGRect}(),!SVG.supported)return!1;SVG.invent=function(t){var e="function"==typeof t.create?t.create:function(){this.constructor.call(this,SVG.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&SVG.extend(e,t.extend),t.construct&&SVG.extend(t.parent||SVG.Container,t.construct),e},SVG.regex={test:function(t,e){return this[e].test(t)},unit:/^(-?[\d\.]+)([a-z%]{0,2})$/,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isStyle:/^font|text|leading|cursor/,isBlank:/^(\s+)?$/,isNumber:/^-?[\d\.]+$/,isPercent:/^-?[\d\.]+%$/,isImage:/\.(jpg|jpeg|png|gif)(\?[^=]+.*)?/i},SVG.defaults={matrix:"1 0 0 1 0 0",attrs:{"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,"stroke-linejoin":"miter","stroke-linecap":"butt",fill:"#000000",stroke:"#000000",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,"stop-opacity":1,"stop-color":"#000000"},trans:function(){return{x:0,y:0,scaleX:1,scaleY:1,rotation:0,skewX:0,skewY:0,matrix:this.matrix,a:1,b:0,c:0,d:1,e:0,f:0}}},SVG.Color=function(t){var e;this.r=0,this.g=0,this.b=0,"string"==typeof t?SVG.regex.isRgb.test(t)?(e=SVG.regex.rgb.exec(t.replace(/\s/g,"")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):SVG.regex.isHex.test(t)&&(e=SVG.regex.hex.exec(this._fullHex(t)),this.r=parseInt(e[1],16),this.g=parseInt(e[2],16),this.b=parseInt(e[3],16)):"object"==typeof t&&(this.r=t.r,this.g=t.g,this.b=t.b)},SVG.extend(SVG.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+this._compToHex(this.r)+this._compToHex(this.g)+this._compToHex(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return.3*(this.r/255)+.59*(this.g/255)+.11*(this.b/255)},morph:function(t){return this.destination=new SVG.Color(t),this},at:function(t){return this.destination?(t=0>t?0:t>1?1:t,new SVG.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this},_fullHex:function(t){return 4==t.length?["#",t.substring(1,2),t.substring(1,2),t.substring(2,3),t.substring(2,3),t.substring(3,4),t.substring(3,4)].join(""):t},_compToHex:function(t){var e=t.toString(16);return 1==e.length?"0"+e:e}}),SVG.Color.test=function(t){return t+="",SVG.regex.isHex.test(t)||SVG.regex.isRgb.test(t)},SVG.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},SVG.Color.isColor=function(t){return SVG.Color.isRgb(t)||SVG.Color.test(t)},SVG.Array=function(t,e){t=(t||[]).valueOf(),0==t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},SVG.extend(SVG.Array,{morph:function(t){if(this.destination=this.parse(t),this.value.length!=this.destination.length){for(var e=this.value[this.value.length-1],i=this.destination[this.destination.length-1];this.value.length>this.destination.length;)this.destination.push(i);for(;this.value.lengtht;t++)-1==i.indexOf(this.value[t])&&i.push(this.value[t]);return this.value=i},at:function(t){if(!this.destination)return this;for(var e=0,i=this.value.length,n=[];i>e;e++)n.push(this.value[e]+(this.destination[e]-this.value[e])*t);return new SVG.Array(n)},toString:function(){return this.value.join(" ")},valueOf:function(){return this.value},parse:function(t){return t=t.valueOf(),Array.isArray(t)?t:this.split(t)},split:function(t){return t.replace(/\s+/g," ").replace(/^\s+|\s+$/g,"").split(" ")}}),SVG.PointArray=function(){this.constructor.apply(this,arguments)},SVG.PointArray.prototype=new SVG.Array,SVG.extend(SVG.PointArray,{toString:function(){for(var t=0,e=this.value.length,i=[];e>t;t++)i.push(this.value[t].join(","));return i.join(" ")},at:function(t){if(!this.destination)return this;for(var e=0,i=this.value.length,n=[];i>e;e++)n.push([this.value[e][0]+(this.destination[e][0]-this.value[e][0])*t,this.value[e][1]+(this.destination[e][1]-this.value[e][1])*t]);return new SVG.PointArray(n)},parse:function(t){if(t=t.valueOf(),Array.isArray(t))return t;t=this.split(t);for(var e,i=0,n=t.length,r=[];n>i;i++)e=t[i].split(","),r.push([parseFloat(e[0]),parseFloat(e[1])]);return r},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n=this.value.length-1;n>=0;n--)this.value[n]=[this.value[n][0]+t,this.value[n][1]+e];return this},size:function(t,e){var i,n=this.bbox();for(i=this.value.length-1;i>=0;i--)this.value[i][0]=(this.value[i][0]-n.x)*t/n.width+n.x,this.value[i][1]=(this.value[i][1]-n.y)*e/n.height+n.x;return this},bbox:function(){return this._cachedBBox?this._cachedBBox:(SVG.parser.poly.setAttribute("points",this.toString()),SVG.parser.poly.getBBox())}}),SVG.PathArray=function(t,e){this.constructor.call(this,t,e)},SVG.PathArray.prototype=new SVG.Array,SVG.extend(SVG.PathArray,{toString:function(){for(var t,e=0,i=this.value.length,n=[];i>e;e++){switch(t=[this.value[e].type],this.value[e].type){case"H":t.push(this.value[e].x);break;case"V":t.push(this.value[e].y);break;case"M":case"L":case"T":case"S":case"Q":case"C":/[QC]/.test(this.value[e].type)&&t.push(this.value[e].x1,this.value[e].y1),/[CS]/.test(this.value[e].type)&&t.push(this.value[e].x2,this.value[e].y2),t.push(this.value[e].x,this.value[e].y);break;case"A":t.push(this.value[e].r1,this.value[e].r2,this.value[e].a,this.value[e].l,this.value[e].s,this.value[e].x,this.value[e].y)}n.push(t.join(" "))}return n.join(" ")},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n=this.value.length-1;n>=0;n--)switch(this.value[n].type){case"H":this.value[n].x+=t;break;case"V":this.value[n].y+=e;break;case"M":case"L":case"T":case"S":case"Q":case"C":this.value[n].x+=t,this.value[n].y+=e,/[CQ]/.test(this.value[n].type)&&(this.value[n].x1+=t,this.value[n].y1+=e),/[CS]/.test(this.value[n].type)&&(this.value[n].x2+=t,this.value[n].y2+=e);break;case"A":this.value[n].x+=t,this.value[n].y+=e}return this},size:function(t,e){for(var i=this.bbox(),n=this.value.length-1;n>=0;n--)switch(this.value[n].type){case"H":this.value[n].x=(this.value[n].x-i.x)*t/i.width+i.x;break;case"V":this.value[n].y=(this.value[n].y-i.y)*e/i.height+i.y;break;case"M":case"L":case"T":case"S":case"Q":case"C":this.value[n].x=(this.value[n].x-i.x)*t/i.width+i.x,this.value[n].y=(this.value[n].y-i.y)*e/i.height+i.y,/[CQ]/.test(this.value[n].type)&&(this.value[n].x1=(this.value[n].x1-i.x)*t/i.width+i.x,this.value[n].y1=(this.value[n].y1-i.y)*e/i.height+i.y),/[CS]/.test(this.value[n].type)&&(this.value[n].x2=(this.value[n].x2-i.x)*t/i.width+i.x,this.value[n].y2=(this.value[n].y2-i.y)*e/i.height+i.y);break;case"A":this.value[n].values.r1=this.value[n].values.r1*t/i.width,this.value[n].values.r2=this.value[n].values.r2*e/i.height,this.value[n].values.x=(this.value[n].values.x-i.x)*t/i.width+i.x,this.value[n].values.y=(this.value[n].values.y-i.y)*e/i.height+i.y}return this},parse:function(t){if(t=t.valueOf(),Array.isArray(t))return t;var e,i,n,r,s,h,o,a,u,l,c,f=0,p=0;for(SVG.parser.path.setAttribute("d",t),c=SVG.parser.path.pathSegList,e=0,i=c.numberOfItems;i>e;++e){if(l=c.getItem(e),u=l.pathSegTypeAsLetter,/[MLHVCSQTA]/.test(u))"x"in l&&(f=l.x),"y"in l&&(p=l.y);else switch("x1"in l&&(s=f+l.x1),"x2"in l&&(o=f+l.x2),"y1"in l&&(h=p+l.y1),"y2"in l&&(a=p+l.y2),"x"in l&&(f+=l.x),"y"in l&&(p+=l.y),u){case"m":c.replaceItem(SVG.parser.path.createSVGPathSegMovetoAbs(f,p),e);break;case"l":c.replaceItem(SVG.parser.path.createSVGPathSegLinetoAbs(f,p),e);break;case"h":c.replaceItem(SVG.parser.path.createSVGPathSegLinetoHorizontalAbs(f),e);break;case"v":c.replaceItem(SVG.parser.path.createSVGPathSegLinetoVerticalAbs(p),e);break;case"c":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicAbs(f,p,s,h,o,a),e);break;case"s":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(f,p,o,a),e);break;case"q":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticAbs(f,p,s,h),e);break;case"t":c.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(f,p),e);break;case"a":c.replaceItem(SVG.parser.path.createSVGPathSegArcAbs(f,p,l.r1,l.r2,l.angle,l.largeArcFlag,l.sweepFlag),e);break;case"z":case"Z":f=n,p=r}/[Mm]/.test(u)&&(n=f,r=p)}for(t=[],c=SVG.parser.path.pathSegList,e=0,i=c.numberOfItems;i>e;++e){switch(l=c.getItem(e),u={},l.pathSegTypeAsLetter){case"M":case"L":case"T":case"S":case"Q":case"C":/[QC]/.test(l.pathSegTypeAsLetter)&&(u.x1=l.x1,u.y1=l.y1),/[SC]/.test(l.pathSegTypeAsLetter)&&(u.x2=l.x2,u.y2=l.y2);break;case"A":u={r1:l.r1,r2:l.r2,a:l.angle,l:0|l.largeArcFlag,s:0|l.sweepFlag}}u.type=l.pathSegTypeAsLetter,u.x=l.x,u.y=l.y,t.push(u)}return t},bbox:function(){return SVG.parser.path.setAttribute("d",this.toString()),SVG.parser.path.getBBox()}}),SVG.Number=function(t){switch(this.value=0,this.unit="",typeof t){case"number":this.value=isNaN(t)?0:isFinite(t)?t:0>t?-3.4e38:3.4e38;break;case"string":var e=t.match(SVG.regex.unit);e&&(this.value=parseFloat(e[1]),"%"==e[2]?this.value/=100:"s"==e[2]&&(this.value*=1e3),this.unit=e[2]);break;default:t instanceof SVG.Number&&(this.value=t.value,this.unit=t.unit)}},SVG.extend(SVG.Number,{toString:function(){return("%"==this.unit?~~(1e8*this.value)/1e6:"s"==this.unit?this.value/1e3:this.value)+this.unit},valueOf:function(){return this.value},plus:function(t){return this.value=this+new SVG.Number(t),this},minus:function(t){return this.plus(-new SVG.Number(t))},times:function(t){return this.value=this*new SVG.Number(t),this},divide:function(t){return this.value=this/new SVG.Number(t),this},to:function(t){return"string"==typeof t&&(this.unit=t),this},morph:function(t){return this.destination=new SVG.Number(t),this},at:function(t){return this.destination?new SVG.Number(this.destination).minus(this).times(t).plus(this):this}}),SVG.ViewBox=function(t){var e,i,n,r,s=1,h=1,o=t.bbox(),a=(t.attr("viewBox")||"").match(/-?[\d\.]+/g);for(n=new SVG.Number(t.width()),r=new SVG.Number(t.height());"%"==n.unit;)s*=n.value,n=new SVG.Number(t instanceof SVG.Doc?t.parent.offsetWidth:t.width());for(;"%"==r.unit;)h*=r.value,r=new SVG.Number(t instanceof SVG.Doc?t.parent.offsetHeight:t.height());this.x=o.x,this.y=o.y,this.width=n*s,this.height=r*h,this.zoom=1,a&&(e=parseFloat(a[0]),i=parseFloat(a[1]),n=parseFloat(a[2]),r=parseFloat(a[3]),this.zoom=this.width/this.height>n/r?this.height/r:this.width/n,this.x=e,this.y=i,this.width=n,this.height=r)},SVG.extend(SVG.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),SVG.BBox=function(t){var e;if(this.x=0,this.y=0,this.width=0,this.height=0,t){try{e=t.node.getBBox()}catch(i){e={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}this.x=e.x+t.trans.x,this.y=e.y+t.trans.y,this.width=e.width*t.trans.scaleX,this.height=e.height*t.trans.scaleY}this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.BBox,{merge:function(t){var e=new SVG.BBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.RBox=function(t){var e,i,n={};if(this.x=0,this.y=0,this.width=0,this.height=0,t){for(e=t.doc().parent,i=t.doc().viewbox().zoom,n=t.node.getBoundingClientRect(),this.x=n.left,this.y=n.top,this.x-=e.offsetLeft,this.y-=e.offsetTop;e=e.offsetParent;)this.x-=e.offsetLeft,this.y-=e.offsetTop;for(e=t;e=e.parent;)"svg"==e.type&&e.viewbox&&(i*=e.viewbox().zoom,this.x-=e.x()||0,this.y-=e.y()||0)}this.x/=i,this.y/=i,this.width=n.width/=i,this.height=n.height/=i,this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.RBox,{merge:function(t){var e=new SVG.RBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.Element=SVG.invent({create:function(t){this._stroke=SVG.defaults.attrs.stroke,this.styles={},this.trans=SVG.defaults.trans(),(this.node=t)&&(this.type=t.nodeName,this.node.instance=this)},extend:{x:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleX),this.attr("x",t)},y:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleY),this.attr("y",t)},cx:function(t){return null==t?this.x()+this.width()/2:this.x(t-this.width()/2)},cy:function(t){return null==t?this.y()+this.height()/2:this.y(t-this.height()/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr("width",t)},height:function(t){return this.attr("height",t)},size:function(t,e){var i=this._proportionalSize(t,e);return this.attr({width:new SVG.Number(i.width),height:new SVG.Number(i.height)})},clone:function(){var t,e,i=this.type;return t="rect"==i||"ellipse"==i?this.parent[i](0,0):"line"==i?this.parent[i](0,0,0,0):"image"==i?this.parent[i](this.src):"text"==i?this.parent[i](this.content):"path"==i?this.parent[i](this.attr("d")):"polyline"==i||"polygon"==i?this.parent[i](this.attr("points")):"g"==i?this.parent.group():this.parent[i](),e=this.attr(),delete e.id,t.attr(e),t.trans=this.trans,t.transform({})},remove:function(){return this.parent&&this.parent.removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return t.put(this)},putIn:function(t){return t.add(this)},doc:function(t){return this._parent(t||SVG.Doc)},attr:function(t,e,i){if(null==t){for(t={},e=this.node.attributes,i=e.length-1;i>=0;i--)t[e[i].nodeName]=SVG.regex.test(e[i].nodeValue,"isNumber")?parseFloat(e[i].nodeValue):e[i].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return this._isStyle(t)?"text"==t?this.content:"leading"==t&&this.leading?this.leading():this.style(t):(e=this.node.getAttribute(t),null==e?SVG.defaults.attrs[t]:SVG.regex.test(e,"isNumber")?parseFloat(e):e);if("style"==t)return this.style(e);if("x"==t&&Array.isArray(this.lines))for(i=this.lines.length-1;i>=0;i--)this.lines[i].attr(t,e);"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),("fill"==t||"stroke"==t)&&(SVG.regex.isImage.test(e)&&(e=this.doc().defs().image(e,0,0)),e instanceof SVG.Image&&(e=this.doc().defs().pattern(0,0,function(){this.add(e)}))),SVG.Color.test(e)||SVG.Color.isRgb(e)?e=new SVG.Color(e):"number"==typeof e?e=new SVG.Number(e):Array.isArray(e)&&(e=new SVG.Array(e)),null!=i?this.node.setAttributeNS(i,t,e.toString()):this.node.setAttribute(t,e.toString()),this._isStyle(t)&&("text"==t?this.text(e):"leading"==t&&this.leading?this.leading(e):this.style(t,e),this.rebuild&&this.rebuild(t,e))}return this},transform:function(t,e){if(0==arguments.length)return this.trans;if("string"==typeof t){if(2>arguments.length)return this.trans[t];var i={};return i[t]=e,this.transform(i)}var i=[];t=this._parseMatrix(t);for(e in t)null!=t[e]&&(this.trans[e]=t[e]);return this.trans.matrix=this.trans.a+" "+this.trans.b+" "+this.trans.c+" "+this.trans.d+" "+this.trans.e+" "+this.trans.f,t=this.trans,t.matrix!=SVG.defaults.matrix&&i.push("matrix("+t.matrix+")"),0!=t.rotation&&i.push("rotate("+t.rotation+" "+(null==t.cx?this.bbox().cx:t.cx)+" "+(null==t.cy?this.bbox().cy:t.cy)+")"),(1!=t.scaleX||1!=t.scaleY)&&i.push("scale("+t.scaleX+" "+t.scaleY+")"),0!=t.skewX&&i.push("skewX("+t.skewX+")"),0!=t.skewY&&i.push("skewY("+t.skewY+")"),(0!=t.x||0!=t.y)&&i.push("translate("+new SVG.Number(t.x/t.scaleX)+" "+new SVG.Number(t.y/t.scaleY)+")"),0==i.length?this.node.removeAttribute("transform"):this.node.setAttribute("transform",i.join(" ")),this},style:function(t,e){if(0==arguments.length)return this.attr("style")||"";if(2>arguments.length)if("object"==typeof t)for(e in t)this.style(e,t[e]);else{if(!SVG.regex.isCss.test(t))return this.styles[t];t=t.split(";");for(var i=0;t.length>i;i++)e=t[i].split(":"),2==e.length&&this.style(e[0].replace(/\s+/g,""),e[1].replace(/^\s+/,"").replace(/\s+$/,""))}else null===e||SVG.regex.test(e,"isBlank")?delete this.styles[t]:this.styles[t]=e;t="";for(e in this.styles)t+=e+":"+this.styles[e]+";";return""==t?this.node.removeAttribute("style"):this.node.setAttribute("style",t),this},bbox:function(){return new SVG.BBox(this)},rbox:function(){return new SVG.RBox(this)},inside:function(t,e){var i=this.bbox();return t>i.x&&e>i.y&&i.x+i.width>t&&i.y+i.height>e},show:function(){return this.style("display","")},hide:function(){return this.style("display","none")},visible:function(){return"none"!=this.style("display")},toString:function(){return this.attr("id")},_parent:function(t){for(var e=this;null!=e&&!(e instanceof t);)e=e.parent;return e},_isStyle:function(t){return"string"==typeof t?SVG.regex.test(t,"isStyle"):!1},_parseMatrix:function(t){if(t.matrix){var e=t.matrix.replace(/\s/g,"").split(",");6==e.length&&(t.a=parseFloat(e[0]),t.b=parseFloat(e[1]),t.c=parseFloat(e[2]),t.d=parseFloat(e[3]),t.e=parseFloat(e[4]),t.f=parseFloat(e[5]))}return t},_proportionalSize:function(t,e){if(null==t||null==e){var i=this.bbox();null==e?e=i.height/i.width*t:null==t&&(t=i.width/i.height*e)}return{width:t,height:e}}}}),SVG.Parent=SVG.invent({create:function(t){this.constructor.call(this,t)},inherit:SVG.Element,extend:{children:function(){return this._children||(this._children=[])},add:function(t,e){return this.has(t)||(e=null==e?this.children().length:e,t.parent&&t.parent.children().splice(t.parent.index(t),1),this.children().splice(e,0,t),this.node.insertBefore(t.node,this.node.childNodes[e]||null),t.parent=this),this._defs&&(this.node.removeChild(this._defs.node),this.node.appendChild(this._defs.node)),this},put:function(t,e){return this.add(t,e),t},has:function(t){return this.index(t)>=0},index:function(t){return this.children().indexOf(t)},get:function(t){return this.children()[t]},first:function(){return this.children()[0]},last:function(){return this.children()[this.children().length-1]},each:function(t,e){var i,n,r=this.children();for(i=0,n=r.length;n>i;i++)r[i]instanceof SVG.Element&&t.apply(r[i],[i,r]),e&&r[i]instanceof SVG.Container&&r[i].each(t,e);return this},removeElement:function(t){return this.children().splice(this.index(t),1),this.node.removeChild(t.node),t.parent=null,this},clear:function(){for(var t=this.children().length-1;t>=0;t--)this.removeElement(this.children()[t]);return this._defs&&this._defs.clear(),this},defs:function(){return this.doc().defs()}}}),SVG.Container=SVG.invent({create:function(t){this.constructor.call(this,t)},inherit:SVG.Parent,extend:{viewbox:function(t){return 0==arguments.length?new SVG.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t))}}}),SVG.FX=function(t){this.target=t},SVG.extend(SVG.FX,{animate:function(e,i,n){var r,s,h,o,a=this.target,u=this;return"object"==typeof e&&(n=e.delay,i=e.ease,e=e.duration),e="="==e?e:null==e?1e3:new SVG.Number(e).valueOf(),i=i||"<>",u.to=function(e){var n;if(e=0>e?0:e>1?1:e,null==r){r=[];for(o in u.attrs)r.push(o);if(a.morphArray&&(u._plot||r.indexOf("points")>-1)){var l,c=new a.morphArray(u._plot||u.attrs.points||a.array);u._size&&c.size(u._size.width.to,u._size.height.to),l=c.bbox(),u._x?c.move(u._x.to,l.y):u._cx&&c.move(u._cx.to-l.width/2,l.y),l=c.bbox(),u._y?c.move(l.x,u._y.to):u._cy&&c.move(l.x,u._cy.to-l.height/2),delete u._x,delete u._y,delete u._cx,delete u._cy,delete u._size,u._plot=a.array.morph(c)}}if(null==s){s=[];for(o in u.trans)s.push(o)}if(null==h){h=[];for(o in u.styles)h.push(o)}for(e="<>"==i?-Math.cos(e*Math.PI)/2+.5:">"==i?Math.sin(e*Math.PI/2):"<"==i?-Math.cos(e*Math.PI/2)+1:"-"==i?e:"function"==typeof i?i(e):e,u._plot?a.plot(u._plot.at(e)):(u._x?a.x(t(u._x,e)):u._cx&&a.cx(t(u._cx,e)),u._y?a.y(t(u._y,e)):u._cy&&a.cy(t(u._cy,e)),u._size&&a.size(t(u._size.width,e),t(u._size.height,e))),u._viewbox&&a.viewbox(t(u._viewbox.x,e),t(u._viewbox.y,e),t(u._viewbox.width,e),t(u._viewbox.height,e)),n=r.length-1;n>=0;n--)a.attr(r[n],t(u.attrs[r[n]],e));for(n=s.length-1;n>=0;n--)a.transform(s[n],t(u.trans[s[n]],e));for(n=h.length-1;n>=0;n--)a.style(h[n],t(u.styles[h[n]],e));u._during&&u._during.call(a,e,function(i,n){return t({from:i,to:n},e)})},"number"==typeof e&&(this.timeout=setTimeout(function(){var t=(new Date).getTime();u.situation={interval:1e3/60,start:t,play:!0,finish:t+e,duration:e},u.render=function(){if(u.situation.play===!0){var t=(new Date).getTime(),r=t>u.situation.finish?1:(t-u.situation.start)/e;u.to(r),t>u.situation.finish?(u._plot&&a.plot(new SVG.PointArray(u._plot.destination).settle()),u._loop===!0||"number"==typeof u._loop&&u._loop>1?("number"==typeof u._loop&&--u._loop,u.animate(e,i,n)):u._after?u._after.apply(a,[u]):u.stop()):requestAnimFrame(u.render)}else requestAnimFrame(u.render)},u.render()},new SVG.Number(n).valueOf())),this},bbox:function(){return this.target.bbox()},attr:function(t,e){if("object"==typeof t)for(var i in t)this.attr(i,t[i]);else{var n=this.target.attr(t);this.attrs[t]=SVG.Color.isColor(n)?new SVG.Color(n).morph(e):SVG.regex.unit.test(n)?new SVG.Number(n).morph(e):{from:n,to:e}}return this},transform:function(t,e){if(1==arguments.length){t=this.target._parseMatrix(t),delete t.matrix;for(e in t)this.trans[e]={from:this.target.trans[e],to:t[e]}}else{var i={};i[t]=e,this.transform(i)}return this},style:function(t,e){if("object"==typeof t)for(var i in t)this.style(i,t[i]);else this.styles[t]={from:this.target.style(t),to:e};return this},x:function(t){return this._x={from:this.target.x(),to:t},this},y:function(t){return this._y={from:this.target.y(),to:t},this},cx:function(t){return this._cx={from:this.target.cx(),to:t},this},cy:function(t){return this._cy={from:this.target.cy(),to:t},this},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},size:function(t,e){if(this.target instanceof SVG.Text)this.attr("font-size",t);else{var i=this.target.bbox();this._size={width:{from:i.width,to:t},height:{from:i.height,to:e}}}return this},plot:function(t){return this._plot=t,this},viewbox:function(t,e,i,n){if(this.target instanceof SVG.Container){var r=this.target.viewbox();this._viewbox={x:{from:r.x,to:t},y:{from:r.y,to:e},width:{from:r.width,to:i},height:{from:r.height,to:n}}}return this},update:function(t){return this.target instanceof SVG.Stop&&(null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset))),this},during:function(t){return this._during=t,this},after:function(t){return this._after=t,this},loop:function(t){return this._loop=t||!0,this},stop:function(){return clearTimeout(this.timeout),clearInterval(this.interval),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,this},pause:function(){return this.situation.play===!0&&(this.situation.play=!1,this.situation.pause=(new Date).getTime()),this},play:function(){if(this.situation.play===!1){var t=(new Date).getTime()-this.situation.pause;this.situation.finish+=t,this.situation.start+=t,this.situation.play=!0}return this}}),SVG.extend(SVG.Element,{animate:function(t,e,i){return(this.fx||(this.fx=new SVG.FX(this))).stop().animate(t,e,i)},stop:function(){return this.fx&&this.fx.stop(),this},pause:function(){return this.fx&&this.fx.pause(),this},play:function(){return this.fx&&this.fx.play(),this}}),window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||function(t){window.setTimeout(t,1e3/60)}}(),SVG.extend(SVG.Element,SVG.FX,{relative:function(){var t,e=this;return{x:function(i){return t=e.bbox(),e.x(t.x+(i||0))},y:function(i){return t=e.bbox(),e.y(t.y+(i||0))},move:function(t,e){return this.x(t),this.y(e)}}}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave"].forEach(function(t){SVG.Element.prototype[t]=function(e){var i=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(i,arguments)}:null,this}}),SVG.on=function(t,e,i){t.addEventListener?t.addEventListener(e,i,!1):t.attachEvent("on"+e,i)},SVG.off=function(t,e,i){t.removeEventListener?t.removeEventListener(e,i,!1):t.detachEvent("on"+e,i)},SVG.extend(SVG.Element,{on:function(t,e){return SVG.on(this.node,t,e),this},off:function(t,e){return SVG.off(this.node,t,e),this}}),SVG.Defs=SVG.invent({create:"defs",inherit:SVG.Container}),SVG.G=SVG.invent({create:"g",inherit:SVG.Container,extend:{x:function(t){return null==t?this.trans.x:this.transform("x",t)},y:function(t){return null==t?this.trans.y:this.transform("y",t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)}},construct:{group:function(){return this.put(new SVG.G)}}}),SVG.extend(SVG.Element,{siblings:function(){return this.parent.children()},position:function(){return this.parent.index(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position();return this.parent.removeElement(this).put(this,t+1)},backward:function(){var t=this.position();return t>0&&this.parent.removeElement(this).add(this,t-1),this},front:function(){return this.parent.removeElement(this).put(this)},back:function(){return this.position()>0&&this.parent.removeElement(this).add(this,0),this},before:function(t){t.remove();var e=this.position();return this.parent.add(t,e),this},after:function(t){t.remove();var e=this.position();return this.parent.add(t,e+1),this}}),SVG.Mask=SVG.invent({create:function(){this.constructor.call(this,SVG.create("mask")),this.targets=[]},inherit:SVG.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unmask();return delete this.targets,this.parent.removeElement(this),this}},construct:{mask:function(){return this.defs().put(new SVG.Mask)}}}),SVG.extend(SVG.Element,{maskWith:function(t){return this.masker=t instanceof SVG.Mask?t:this.parent.mask().add(t),this.masker.targets.push(this),this.attr("mask",'url("#'+this.masker.attr("id")+'")')},unmask:function(){return delete this.masker,this.attr("mask",null)}}),SVG.Clip=SVG.invent({create:function(){this.constructor.call(this,SVG.create("clipPath")),this.targets=[]},inherit:SVG.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unclip();return delete this.targets,this.parent.removeElement(this),this}},construct:{clip:function(){return this.defs().put(new SVG.Clip)}}}),SVG.extend(SVG.Element,{clipWith:function(t){return this.clipper=t instanceof SVG.Clip?t:this.parent.clip().add(t),this.clipper.targets.push(this),this.attr("clip-path",'url("#'+this.clipper.attr("id")+'")')},unclip:function(){return delete this.clipper,this.attr("clip-path",null)}}),SVG.Gradient=SVG.invent({create:function(t){this.constructor.call(this,SVG.create(t+"Gradient")),this.type=t},inherit:SVG.Container,extend:{from:function(t,e){return"radial"==this.type?this.attr({fx:new SVG.Number(t),fy:new SVG.Number(e)}):this.attr({x1:new SVG.Number(t),y1:new SVG.Number(e)})},to:function(t,e){return"radial"==this.type?this.attr({cx:new SVG.Number(t),cy:new SVG.Number(e)}):this.attr({x2:new SVG.Number(t),y2:new SVG.Number(e)})},radius:function(t){return"radial"==this.type?this.attr({r:new SVG.Number(t)}):this},at:function(t){return this.put(new SVG.Stop).update(t)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},fill:function(){return"url(#"+this.attr("id")+")"},toString:function(){return this.fill()}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),SVG.extend(SVG.Defs,{gradient:function(t,e){return this.put(new SVG.Gradient(t)).update(e)}}),SVG.Stop=SVG.invent({create:"stop",inherit:SVG.Element,extend:{update:function(t){return null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset)),this}}}),SVG.Pattern=SVG.invent({create:"pattern",inherit:SVG.Container,extend:{fill:function(){return"url(#"+this.attr("id")+")"},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return this.fill()}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),SVG.extend(SVG.Defs,{pattern:function(t,e,i){return this.put(new SVG.Pattern).update(i).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),SVG.Doc=SVG.invent({create:function(t){this.parent="string"==typeof t?document.getElementById(t):t,this.constructor.call(this,"svg"==this.parent.nodeName?this.parent:SVG.create("svg")),this.attr({xmlns:SVG.ns,version:"1.1",width:"100%",height:"100%"}).attr("xmlns:xlink",SVG.xlink,SVG.xmlns),this._defs=new SVG.Defs,this._defs.parent=this,this.node.appendChild(this._defs.node),this.doSubPixelOffsetFix=!1,"svg"!=this.parent.nodeName&&this.stage()},inherit:SVG.Container,extend:{stage:function(){var t,e=this,i=document.createElement("div");return i.style.cssText="position:relative;height:100%;",e.parent.appendChild(i),i.appendChild(e.node),t=function(){"complete"===document.readyState?(e.style("position:absolute;"),setTimeout(function(){e.style("position:relative;overflow:hidden;"),e.parent.removeChild(e.node.parentNode),e.node.parentNode.removeChild(e.node),e.parent.appendChild(e.node),e.subPixelOffsetFix(),SVG.on(window,"resize",function(){e.subPixelOffsetFix()})},5)):setTimeout(t,10)},t(),this},defs:function(){return this._defs},subPixelOffsetFix:function(){if(this.doSubPixelOffsetFix){var t=this.node.getScreenCTM();t&&this.style("left",-t.e%1+"px").style("top",-t.f%1+"px")}return this},fixSubPixelOffset:function(){return this.doSubPixelOffsetFix=!0,this}}}),SVG.Shape=SVG.invent({create:function(t){this.constructor.call(this,t)},inherit:SVG.Element}),SVG.Use=SVG.invent({create:"use",inherit:SVG.Shape,extend:{element:function(t){return this.target=t,this.attr("href","#"+t,SVG.xlink)}},construct:{use:function(t){return this.put(new SVG.Use).element(t)}}}),SVG.Rect=SVG.invent({create:"rect",inherit:SVG.Shape,construct:{rect:function(t,e){return this.put((new SVG.Rect).size(t,e))}}}),SVG.Ellipse=SVG.invent({create:"ellipse",inherit:SVG.Shape,extend:{x:function(t){return null==t?this.cx()-this.attr("rx"):this.cx(t+this.attr("rx"))},y:function(t){return null==t?this.cy()-this.attr("ry"):this.cy(t+this.attr("ry"))},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",new SVG.Number(t).divide(this.trans.scaleX))},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",new SVG.Number(t).divide(this.trans.scaleY))},width:function(t){return null==t?2*this.attr("rx"):this.attr("rx",new SVG.Number(t).divide(2))},height:function(t){return null==t?2*this.attr("ry"):this.attr("ry",new SVG.Number(t).divide(2))},size:function(t,e){var i=this._proportionalSize(t,e);return this.attr({rx:new SVG.Number(i.width).divide(2),ry:new SVG.Number(i.height).divide(2)})}},construct:{circle:function(t){return this.ellipse(t,t)},ellipse:function(t,e){return this.put(new SVG.Ellipse).size(t,e).move(0,0)}}}),SVG.Line=SVG.invent({create:"line",inherit:SVG.Shape,extend:{x:function(t){var e=this.bbox(); -return null==t?e.x:this.attr({x1:this.attr("x1")-e.x+t,x2:this.attr("x2")-e.x+t})},y:function(t){var e=this.bbox();return null==t?e.y:this.attr({y1:this.attr("y1")-e.y+t,y2:this.attr("y2")-e.y+t})},cx:function(t){var e=this.bbox().width/2;return null==t?this.x()+e:this.x(t-e)},cy:function(t){var e=this.bbox().height/2;return null==t?this.y()+e:this.y(t-e)},width:function(t){var e=this.bbox();return null==t?e.width:this.attr(this.attr("x1")e;e++)this.tspan(n[e]).newLine();this.rebuild()}return this},tspan:function(t){var e=this.textPath?this.textPath.node:this.node,i=(new SVG.TSpan).text(t),n=this.style();return e.appendChild(i.node),this.lines.add(i),SVG.regex.isBlank.test(n)||i.style(n),this.content+=t,i.parent=this,i},size:function(t){return this.attr("font-size",t)},leading:function(t){return null==t?this._leading:(t=new SVG.Number(t),this._leading=t,this.lines.each(function(){this.newLined&&this.attr("dy",t)}),this)},rebuild:function(){return this._rebuild&&this.lines.attr({x:this.attr("x"),dy:this._leading,style:this.style()}),this},clear:function(){for(var t=this.textPath?this.textPath.node:this.node;t.hasChildNodes();)t.removeChild(t.lastChild);return delete this.lines,this.lines=new SVG.Set,this.content="",this}},construct:{text:function(t){return this.put(new SVG.Text).text(t)}}}),SVG.TSpan=SVG.invent({create:"tspan",inherit:SVG.Shape,extend:{text:function(t){return this.node.appendChild(document.createTextNode(t)),this},dx:function(t){return this.attr("dx",t)},dy:function(t){return this.attr("dy",t)},newLine:function(){return this.newLined=!0,this.parent.content+="\n",this.dy(this.parent._leading),this.attr("x",this.parent.x())}}}),SVG.TextPath=SVG.invent({create:"textPath",inherit:SVG.Element,parent:SVG.Text,construct:{path:function(t){for(this.textPath=new SVG.TextPath;this.node.hasChildNodes();)this.textPath.node.appendChild(this.node.firstChild);return this.node.appendChild(this.textPath.node),this.track=this.doc().defs().path(t),this.textPath.parent=this,this.textPath.attr("href","#"+this.track,SVG.xlink),this},plot:function(t){return this.track&&this.track.plot(t),this}}}),SVG.Nested=SVG.invent({create:function(){this.constructor.call(this,SVG.create("svg")),this.style("overflow","visible")},inherit:SVG.Container,construct:{nested:function(){return this.put(new SVG.Nested)}}}),SVG.A=SVG.invent({create:"a",inherit:SVG.Container,extend:{to:function(t){return this.attr("href",t,SVG.xlink)},show:function(t){return this.attr("show",t,SVG.xlink)},target:function(t){return this.attr("target",t)}},construct:{link:function(t){return this.put(new SVG.A).to(t)}}}),SVG.extend(SVG.Element,{linkTo:function(t){var e=new SVG.A;return"function"==typeof t?t.call(e,e):e.to(t),this.parent.put(e).put(this)}}),SVG._stroke=["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],SVG._fill=["color","opacity","rule"];var i=function(t,e){return"color"==e?t:t+"-"+e};["fill","stroke"].forEach(function(t){var e={};e[t]=function(e){if("string"==typeof e||SVG.Color.isRgb(e)||e&&"function"==typeof e.fill)this.attr(t,e);else for(index=SVG["_"+t].length-1;index>=0;index--)null!=e[SVG["_"+t][index]]&&this.attr(i(t,SVG["_"+t][index]),e[SVG["_"+t][index]]);return this},SVG.extend(SVG.Element,SVG.FX,e)}),SVG.extend(SVG.Element,SVG.FX,{rotate:function(t,e,i){return this.transform({rotation:t||0,cx:e,cy:i})},skew:function(t,e){return this.transform({skewX:t||0,skewY:e||0})},scale:function(t,e){return this.transform({scaleX:t,scaleY:null==e?t:e})},translate:function(t,e){return this.transform({x:t,y:e})},matrix:function(t){return this.transform({matrix:t})},opacity:function(t){return this.attr("opacity",t)}}),SVG.extend(SVG.Rect,SVG.Ellipse,{radius:function(t,e){return this.attr({rx:t,ry:e||t})}}),SVG.extend(SVG.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),SVG.extend(SVG.Text,SVG.FX,{font:function(t){for(var i in t)"anchor"==i?this.attr("text-anchor",t[i]):e.indexOf(i)>-1?this.attr("font-"+i,t[i]):this.attr(i,t[i]);return this}}),SVG.Set=SVG.invent({create:function(){this.clear()},extend:{add:function(){var t,e,i=[].slice.call(arguments);for(t=0,e=i.length;e>t;t++)this.members.push(i[t]);return this},remove:function(t){var e=this.index(t);return e>-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;i>e;e++)t.apply(this.members[e],[e,this.members]);return this},clear:function(){return this.members=[],this},has:function(t){return this.index(t)>=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},valueOf:function(){return this.members},bbox:function(){var t=new SVG.BBox;if(0==this.members.length)return t;var e=this.members[0].rbox();return t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height,this.each(function(){t=t.merge(this.rbox())}),t}},construct:{set:function(){return new SVG.Set}}}),SVG.SetFX=SVG.invent({create:function(t){this.set=t}}),SVG.Set.inherit=function(){var t,e=[];for(var t in SVG.Shape.prototype)"function"==typeof SVG.Shape.prototype[t]&&"function"!=typeof SVG.Set.prototype[t]&&e.push(t);e.forEach(function(t){SVG.Set.prototype[t]=function(){for(var e=0,i=this.members.length;i>e;e++)this.members[e]&&"function"==typeof this.members[e][t]&&this.members[e][t].apply(this.members[e],arguments);return"animate"==t?this.fx||(this.fx=new SVG.SetFX(this)):this}}),e=[];for(var t in SVG.FX.prototype)"function"==typeof SVG.FX.prototype[t]&&"function"!=typeof SVG.SetFX.prototype[t]&&e.push(t);e.forEach(function(t){SVG.SetFX.prototype[t]=function(){for(var e=0,i=this.set.members.length;i>e;e++)this.set.members[e].fx[t].apply(this.set.members[e].fx,arguments);return this}})},SVG.extend(SVG.Element,{data:function(t,e,i){if("object"==typeof t)for(e in t)this.data(e,t[e]);else if(2>arguments.length)try{return JSON.parse(this.attr("data-"+t))}catch(n){return this.attr("data-"+t)}else this.attr("data-"+t,null===e?null:i===!0||"string"==typeof e||"number"==typeof e?e:JSON.stringify(e));return this}}),SVG.extend(SVG.Element,{remember:function(t,e){if("object"==typeof arguments[0])for(var e in t)this.remember(e,t[e]);else{if(1==arguments.length)return this.memory()[t];this.memory()[t]=e}return this},forget:function(){if(0==arguments.length)this._memory={};else for(var t=arguments.length-1;t>=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),"function"==typeof define&&define.amd?define(function(){return SVG}):"undefined"!=typeof exports&&(exports.SVG=SVG)}).call(this); \ No newline at end of file +(function(){function t(t){for(var e=0,i=t.length,n="";i>e;e++)n+=t[e][0],null!=t[e][1]&&(n+=t[e][1],null!=t[e][2]&&(n+=" ",n+=t[e][2],null!=t[e][3]&&(n+=" ",n+=t[e][3],n+=" ",n+=t[e][4],null!=t[e][5]&&(n+=" ",n+=t[e][5],n+=" ",n+=t[e][6],null!=t[e][7]&&(n+=" ",n+=t[e][7])))));return n+" "}function e(t,e){return"number"==typeof t.from?t.from+(t.to-t.from)*e:t instanceof SVG.Color||t instanceof SVG.Number?t.at(e):1>e?t.from:t.to}if(this.SVG=function(t){return SVG.supported?(t=new SVG.Doc(t),SVG.parser||SVG.prepare(t),t):void 0},SVG.ns="http://www.w3.org/2000/svg",SVG.xmlns="http://www.w3.org/2000/xmlns/",SVG.xlink="http://www.w3.org/1999/xlink",SVG.did=1e3,SVG.eid=function(t){return"Svgjs"+t.charAt(0).toUpperCase()+t.slice(1)+SVG.did++},SVG.create=function(t){var e=document.createElementNS(this.ns,t);return e.setAttribute("id",this.eid(t)),e},SVG.extend=function(){var t,e,i,n;for(t=[].slice.call(arguments),e=t.pop(),n=t.length-1;n>=0;n--)if(t[n])for(i in e)t[n].prototype[i]=e[i];SVG.Set&&SVG.Set.inherit&&SVG.Set.inherit()},SVG.get=function(t){var e=document.getElementById(t);return e?e.instance:void 0},SVG.prepare=function(t){var e=document.getElementsByTagName("body")[0],i=(e?new SVG.Doc(e):t.nested()).size(2,2),n=SVG.create("path");i.node.appendChild(n),SVG.parser={body:e||t.parent,draw:i.style("opacity:0;position:fixed;left:100%;top:100%;overflow:hidden"),poly:i.polyline().node,path:n}},SVG.supported=function(){return!!document.createElementNS&&!!document.createElementNS(SVG.ns,"svg").createSVGRect}(),!SVG.supported)return!1;SVG.invent=function(t){var e="function"==typeof t.create?t.create:function(){this.constructor.call(this,SVG.create(t.create))};return t.inherit&&(e.prototype=new t.inherit),t.extend&&SVG.extend(e,t.extend),t.construct&&SVG.extend(t.parent||SVG.Container,t.construct),e},SVG.regex={test:function(t,e){return this[e].test(t)},unit:/^(-?[\d\.]+)([a-z%]{0,2})$/,hex:/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,rgb:/rgb\((\d+),(\d+),(\d+)\)/,isHex:/^#[a-f0-9]{3,6}$/i,isRgb:/^rgb\(/,isCss:/[^:]+:[^;]+;?/,isBlank:/^(\s+)?$/,isNumber:/^-?[\d\.]+$/,isPercent:/^-?[\d\.]+%$/,isImage:/\.(jpg|jpeg|png|gif)(\?[^=]+.*)?/i},SVG.defaults={matrix:"1 0 0 1 0 0",attrs:{"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,"stroke-linejoin":"miter","stroke-linecap":"butt",fill:"#000000",stroke:"#000000",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,"stop-opacity":1,"stop-color":"#000000","font-size":16,"font-family":"Helvetica, Arial, sans-serif","text-anchor":"start"},trans:function(){return{x:0,y:0,scaleX:1,scaleY:1,rotation:0,skewX:0,skewY:0,matrix:this.matrix,a:1,b:0,c:0,d:1,e:0,f:0}}},SVG.Color=function(t){var e;this.r=0,this.g=0,this.b=0,"string"==typeof t?SVG.regex.isRgb.test(t)?(e=SVG.regex.rgb.exec(t.replace(/\s/g,"")),this.r=parseInt(e[1]),this.g=parseInt(e[2]),this.b=parseInt(e[3])):SVG.regex.isHex.test(t)&&(e=SVG.regex.hex.exec(this._fullHex(t)),this.r=parseInt(e[1],16),this.g=parseInt(e[2],16),this.b=parseInt(e[3],16)):"object"==typeof t&&(this.r=t.r,this.g=t.g,this.b=t.b)},SVG.extend(SVG.Color,{toString:function(){return this.toHex()},toHex:function(){return"#"+this._compToHex(this.r)+this._compToHex(this.g)+this._compToHex(this.b)},toRgb:function(){return"rgb("+[this.r,this.g,this.b].join()+")"},brightness:function(){return.3*(this.r/255)+.59*(this.g/255)+.11*(this.b/255)},morph:function(t){return this.destination=new SVG.Color(t),this},at:function(t){return this.destination?(t=0>t?0:t>1?1:t,new SVG.Color({r:~~(this.r+(this.destination.r-this.r)*t),g:~~(this.g+(this.destination.g-this.g)*t),b:~~(this.b+(this.destination.b-this.b)*t)})):this},_fullHex:function(t){return 4==t.length?["#",t.substring(1,2),t.substring(1,2),t.substring(2,3),t.substring(2,3),t.substring(3,4),t.substring(3,4)].join(""):t},_compToHex:function(t){var e=t.toString(16);return 1==e.length?"0"+e:e}}),SVG.Color.test=function(t){return t+="",SVG.regex.isHex.test(t)||SVG.regex.isRgb.test(t)},SVG.Color.isRgb=function(t){return t&&"number"==typeof t.r&&"number"==typeof t.g&&"number"==typeof t.b},SVG.Color.isColor=function(t){return SVG.Color.isRgb(t)||SVG.Color.test(t)},SVG.Array=function(t,e){t=(t||[]).valueOf(),0==t.length&&e&&(t=e.valueOf()),this.value=this.parse(t)},SVG.extend(SVG.Array,{morph:function(t){if(this.destination=this.parse(t),this.value.length!=this.destination.length){for(var e=this.value[this.value.length-1],i=this.destination[this.destination.length-1];this.value.length>this.destination.length;)this.destination.push(i);for(;this.value.lengtht;t++)-1==i.indexOf(this.value[t])&&i.push(this.value[t]);return this.value=i},at:function(t){if(!this.destination)return this;for(var e=0,i=this.value.length,n=[];i>e;e++)n.push(this.value[e]+(this.destination[e]-this.value[e])*t);return new SVG.Array(n)},toString:function(){return this.value.join(" ")},valueOf:function(){return this.value},parse:function(t){return t=t.valueOf(),Array.isArray(t)?t:this.split(t)},split:function(t){return t.replace(/\s+/g," ").replace(/^\s+|\s+$/g,"").split(" ")}}),SVG.PointArray=function(){this.constructor.apply(this,arguments)},SVG.PointArray.prototype=new SVG.Array,SVG.extend(SVG.PointArray,{toString:function(){for(var t=0,e=this.value.length,i=[];e>t;t++)i.push(this.value[t].join(","));return i.join(" ")},at:function(t){if(!this.destination)return this;for(var e=0,i=this.value.length,n=[];i>e;e++)n.push([this.value[e][0]+(this.destination[e][0]-this.value[e][0])*t,this.value[e][1]+(this.destination[e][1]-this.value[e][1])*t]);return new SVG.PointArray(n)},parse:function(t){if(t=t.valueOf(),Array.isArray(t))return t;t=this.split(t);for(var e,i=0,n=t.length,r=[];n>i;i++)e=t[i].split(","),r.push([parseFloat(e[0]),parseFloat(e[1])]);return r},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n=this.value.length-1;n>=0;n--)this.value[n]=[this.value[n][0]+t,this.value[n][1]+e];return this},size:function(t,e){var i,n=this.bbox();for(i=this.value.length-1;i>=0;i--)this.value[i][0]=(this.value[i][0]-n.x)*t/n.width+n.x,this.value[i][1]=(this.value[i][1]-n.y)*e/n.height+n.x;return this},bbox:function(){return SVG.parser.poly.setAttribute("points",this.toString()),SVG.parser.poly.getBBox()}}),SVG.PathArray=function(t,e){this.constructor.call(this,t,e)},SVG.PathArray.prototype=new SVG.Array,SVG.extend(SVG.PathArray,{toString:function(){return t(this.value)},move:function(t,e){var i=this.bbox();if(t-=i.x,e-=i.y,!isNaN(t)&&!isNaN(e))for(var n,r=this.value.length-1;r>=0;r--)n=this.value[r][0],"M"==n||"L"==n||"T"==n?(this.value[r][1]+=t,this.value[r][2]+=e):"H"==n?this.value[r][1]+=t:"V"==n?this.value[r][1]+=e:"C"==n||"S"==n||"Q"==n?(this.value[r][1]+=t,this.value[r][2]+=e,this.value[r][3]+=t,this.value[r][4]+=e,"C"==n&&(this.value[r][5]+=t,this.value[r][6]+=e)):"A"==n&&(this.value[r][6]+=t,this.value[r][7]+=e);return this},size:function(t,e){var i,n,r=this.bbox();for(i=this.value.length-1;i>=0;i--)n=this.value[i][0],"M"==n||"L"==n||"T"==n?(this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x,this.value[i][2]=(this.value[i][2]-r.y)*e/r.height+r.y):"H"==n?this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x:"V"==n?this.value[i][1]=(this.value[i][1]-r.y)*e/r.height+r.y:"C"==n||"S"==n||"Q"==n?(this.value[i][1]=(this.value[i][1]-r.x)*t/r.width+r.x,this.value[i][2]=(this.value[i][2]-r.y)*e/r.height+r.y,this.value[i][3]=(this.value[i][3]-r.x)*t/r.width+r.x,this.value[i][4]=(this.value[i][4]-r.y)*e/r.height+r.y,"C"==n&&(this.value[i][5]=(this.value[i][5]-r.x)*t/r.width+r.x,this.value[i][6]=(this.value[i][6]-r.y)*e/r.height+r.y)):"A"==n&&(this.value[i][1]=this.value[i][1]*t/r.width,this.value[i][2]=this.value[i][2]*e/r.height,this.value[i][6]=(this.value[i][6]-r.x)*t/r.width+r.x,this.value[i][7]=(this.value[i][7]-r.y)*e/r.height+r.y);return this},parse:function(e){if(e instanceof SVG.PathArray)return e.valueOf();var i,n,r,s,h,o,a,u,l,c,f,p=0,d=0;for(SVG.parser.path.setAttribute("d","string"==typeof e?e:t(e)),f=SVG.parser.path.pathSegList,i=0,n=f.numberOfItems;n>i;++i)c=f.getItem(i),l=c.pathSegTypeAsLetter,"M"==l||"L"==l||"H"==l||"V"==l||"C"==l||"S"==l||"Q"==l||"T"==l||"A"==l?("x"in c&&(p=c.x),"y"in c&&(d=c.y)):("x1"in c&&(h=p+c.x1),"x2"in c&&(a=p+c.x2),"y1"in c&&(o=d+c.y1),"y2"in c&&(u=d+c.y2),"x"in c&&(p+=c.x),"y"in c&&(d+=c.y),"m"==l?f.replaceItem(SVG.parser.path.createSVGPathSegMovetoAbs(p,d),i):"l"==l?f.replaceItem(SVG.parser.path.createSVGPathSegLinetoAbs(p,d),i):"h"==l?f.replaceItem(SVG.parser.path.createSVGPathSegLinetoHorizontalAbs(p),i):"v"==l?f.replaceItem(SVG.parser.path.createSVGPathSegLinetoVerticalAbs(d),i):"c"==l?f.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicAbs(p,d,h,o,a,u),i):"s"==l?f.replaceItem(SVG.parser.path.createSVGPathSegCurvetoCubicSmoothAbs(p,d,a,u),i):"q"==l?f.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticAbs(p,d,h,o),i):"t"==l?f.replaceItem(SVG.parser.path.createSVGPathSegCurvetoQuadraticSmoothAbs(p,d),i):"a"==l?f.replaceItem(SVG.parser.path.createSVGPathSegArcAbs(p,d,c.r1,c.r2,c.angle,c.largeArcFlag,c.sweepFlag),i):("z"==l||"Z"==l)&&(p=r,d=s)),("M"==l||"m"==l)&&(r=p,s=d);for(e=[],f=SVG.parser.path.pathSegList,i=0,n=f.numberOfItems;n>i;++i)c=f.getItem(i),l=c.pathSegTypeAsLetter,p=[l],"M"==l||"L"==l||"T"==l?p.push(c.x,c.y):"H"==l?p.push(c.x):"V"==l?p.push(c.y):"C"==l?p.push(c.x1,c.y1,c.x2,c.y2,c.x,c.y):"S"==l?p.push(c.x2,c.y2,c.x,c.y):"Q"==l?p.push(c.x1,c.y1,c.x,c.y):"A"==l&&p.push(c.r1,c.r2,c.angle,0|c.largeArcFlag,0|c.sweepFlag,c.x,c.y),e.push(p);return e},bbox:function(){return SVG.parser.path.setAttribute("d",this.toString()),SVG.parser.path.getBBox()}}),SVG.Number=function(t){switch(this.value=0,this.unit="",typeof t){case"number":this.value=isNaN(t)?0:isFinite(t)?t:0>t?-3.4e38:3.4e38;break;case"string":var e=t.match(SVG.regex.unit);e&&(this.value=parseFloat(e[1]),"%"==e[2]?this.value/=100:"s"==e[2]&&(this.value*=1e3),this.unit=e[2]);break;default:t instanceof SVG.Number&&(this.value=t.value,this.unit=t.unit)}},SVG.extend(SVG.Number,{toString:function(){return("%"==this.unit?~~(1e8*this.value)/1e6:"s"==this.unit?this.value/1e3:this.value)+this.unit},valueOf:function(){return this.value},plus:function(t){return this.value=this+new SVG.Number(t),this},minus:function(t){return this.plus(-new SVG.Number(t))},times:function(t){return this.value=this*new SVG.Number(t),this},divide:function(t){return this.value=this/new SVG.Number(t),this},to:function(t){return"string"==typeof t&&(this.unit=t),this},morph:function(t){return this.destination=new SVG.Number(t),this},at:function(t){return this.destination?new SVG.Number(this.destination).minus(this).times(t).plus(this):this}}),SVG.ViewBox=function(t){var e,i,n,r,s=1,h=1,o=t.bbox(),a=(t.attr("viewBox")||"").match(/-?[\d\.]+/g);for(n=new SVG.Number(t.width()),r=new SVG.Number(t.height());"%"==n.unit;)s*=n.value,n=new SVG.Number(t instanceof SVG.Doc?t.parent.offsetWidth:t.width());for(;"%"==r.unit;)h*=r.value,r=new SVG.Number(t instanceof SVG.Doc?t.parent.offsetHeight:t.height());this.x=o.x,this.y=o.y,this.width=n*s,this.height=r*h,this.zoom=1,a&&(e=parseFloat(a[0]),i=parseFloat(a[1]),n=parseFloat(a[2]),r=parseFloat(a[3]),this.zoom=this.width/this.height>n/r?this.height/r:this.width/n,this.x=e,this.y=i,this.width=n,this.height=r)},SVG.extend(SVG.ViewBox,{toString:function(){return this.x+" "+this.y+" "+this.width+" "+this.height}}),SVG.BBox=function(t){var e;if(this.x=0,this.y=0,this.width=0,this.height=0,t){try{e=t.node.getBBox()}catch(i){e={x:t.node.clientLeft,y:t.node.clientTop,width:t.node.clientWidth,height:t.node.clientHeight}}this.x=e.x+t.trans.x,this.y=e.y+t.trans.y,this.width=e.width*t.trans.scaleX,this.height=e.height*t.trans.scaleY}this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.BBox,{merge:function(t){var e=new SVG.BBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.RBox=function(t){var e,i,n={};if(this.x=0,this.y=0,this.width=0,this.height=0,t){for(e=t.doc().parent,i=t.doc().viewbox().zoom,n=t.node.getBoundingClientRect(),this.x=n.left,this.y=n.top,this.x-=e.offsetLeft,this.y-=e.offsetTop;e=e.offsetParent;)this.x-=e.offsetLeft,this.y-=e.offsetTop;for(e=t;e=e.parent;)"svg"==e.type&&e.viewbox&&(i*=e.viewbox().zoom,this.x-=e.x()||0,this.y-=e.y()||0)}this.x/=i,this.y/=i,this.width=n.width/=i,this.height=n.height/=i,this.cx=this.x+this.width/2,this.cy=this.y+this.height/2},SVG.extend(SVG.RBox,{merge:function(t){var e=new SVG.RBox;return e.x=Math.min(this.x,t.x),e.y=Math.min(this.y,t.y),e.width=Math.max(this.x+this.width,t.x+t.width)-e.x,e.height=Math.max(this.y+this.height,t.y+t.height)-e.y,e.cx=e.x+e.width/2,e.cy=e.y+e.height/2,e}}),SVG.Element=SVG.invent({create:function(t){this._stroke=SVG.defaults.attrs.stroke,this.styles={},this.trans=SVG.defaults.trans(),(this.node=t)&&(this.type=t.nodeName,this.node.instance=this)},extend:{x:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleX),this.attr("x",t)},y:function(t){return t&&(t=new SVG.Number(t),t.value/=this.trans.scaleY),this.attr("y",t)},cx:function(t){return null==t?this.x()+this.width()/2:this.x(t-this.width()/2)},cy:function(t){return null==t?this.y()+this.height()/2:this.y(t-this.height()/2)},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},width:function(t){return this.attr("width",t)},height:function(t){return this.attr("height",t)},size:function(t,e){var i=this._proportionalSize(t,e);return this.attr({width:new SVG.Number(i.width),height:new SVG.Number(i.height)})},clone:function(){var t,e,i=this.type;return t="rect"==i||"ellipse"==i?this.parent[i](0,0):"line"==i?this.parent[i](0,0,0,0):"image"==i?this.parent[i](this.src):"text"==i?this.parent[i](this.content):"path"==i?this.parent[i](this.attr("d")):"polyline"==i||"polygon"==i?this.parent[i](this.attr("points")):"g"==i?this.parent.group():this.parent[i](),e=this.attr(),delete e.id,t.attr(e),t.trans=this.trans,t.transform({})},remove:function(){return this.parent&&this.parent.removeElement(this),this},replace:function(t){return this.after(t).remove(),t},addTo:function(t){return t.put(this)},putIn:function(t){return t.add(this)},doc:function(t){return this._parent(t||SVG.Doc)},attr:function(t,e,i){if(null==t){for(t={},e=this.node.attributes,i=e.length-1;i>=0;i--)t[e[i].nodeName]=SVG.regex.test(e[i].nodeValue,"isNumber")?parseFloat(e[i].nodeValue):e[i].nodeValue;return t}if("object"==typeof t)for(e in t)this.attr(e,t[e]);else if(null===e)this.node.removeAttribute(t);else{if(null==e)return e=this.node.getAttribute(t),null==e?SVG.defaults.attrs[t]:SVG.regex.test(e,"isNumber")?parseFloat(e):e;if("style"==t)return this.style(e);"stroke-width"==t?this.attr("stroke",parseFloat(e)>0?this._stroke:null):"stroke"==t&&(this._stroke=e),("fill"==t||"stroke"==t)&&(SVG.regex.isImage.test(e)&&(e=this.doc().defs().image(e,0,0)),e instanceof SVG.Image&&(e=this.doc().defs().pattern(0,0,function(){this.add(e)}))),SVG.Color.isColor(e)?e=new SVG.Color(e):"number"==typeof e?e=new SVG.Number(e):Array.isArray(e)&&(e=new SVG.Array(e)),"leading"==t?this.leading&&this.leading(e):null!=i?this.node.setAttributeNS(i,t,e.toString()):this.node.setAttribute(t,e.toString()),!this.rebuild||"font-size"!=t&&"x"!=t||this.rebuild(t,e)}return this},transform:function(t,e){if(0==arguments.length)return this.trans;if("string"==typeof t){if(2>arguments.length)return this.trans[t];var i={};return i[t]=e,this.transform(i)}var i=[];t=this._parseMatrix(t);for(e in t)null!=t[e]&&(this.trans[e]=t[e]);return this.trans.matrix=this.trans.a+" "+this.trans.b+" "+this.trans.c+" "+this.trans.d+" "+this.trans.e+" "+this.trans.f,t=this.trans,t.matrix!=SVG.defaults.matrix&&i.push("matrix("+t.matrix+")"),0!=t.rotation&&i.push("rotate("+t.rotation+" "+(null==t.cx?this.bbox().cx:t.cx)+" "+(null==t.cy?this.bbox().cy:t.cy)+")"),(1!=t.scaleX||1!=t.scaleY)&&i.push("scale("+t.scaleX+" "+t.scaleY+")"),0!=t.skewX&&i.push("skewX("+t.skewX+")"),0!=t.skewY&&i.push("skewY("+t.skewY+")"),(0!=t.x||0!=t.y)&&i.push("translate("+new SVG.Number(t.x/t.scaleX)+" "+new SVG.Number(t.y/t.scaleY)+")"),0==i.length?this.node.removeAttribute("transform"):this.node.setAttribute("transform",i.join(" ")),this},style:function(t,e){if(0==arguments.length)return this.attr("style")||"";if(2>arguments.length)if("object"==typeof t)for(e in t)this.style(e,t[e]);else{if(!SVG.regex.isCss.test(t))return this.styles[t];t=t.split(";");for(var i=0;t.length>i;i++)e=t[i].split(":"),2==e.length&&this.style(e[0].replace(/\s+/g,""),e[1].replace(/^\s+/,"").replace(/\s+$/,""))}else null===e||SVG.regex.test(e,"isBlank")?delete this.styles[t]:this.styles[t]=e;t="";for(e in this.styles)t+=e+":"+this.styles[e]+";";return""==t?this.node.removeAttribute("style"):this.node.setAttribute("style",t),this},bbox:function(){return new SVG.BBox(this)},rbox:function(){return new SVG.RBox(this)},inside:function(t,e){var i=this.bbox();return t>i.x&&e>i.y&&i.x+i.width>t&&i.y+i.height>e},show:function(){return this.style("display","")},hide:function(){return this.style("display","none")},visible:function(){return"none"!=this.style("display")},toString:function(){return this.attr("id")},_parent:function(t){for(var e=this;null!=e&&!(e instanceof t);)e=e.parent;return e},_parseMatrix:function(t){if(t.matrix){var e=t.matrix.replace(/\s/g,"").split(",");6==e.length&&(t.a=parseFloat(e[0]),t.b=parseFloat(e[1]),t.c=parseFloat(e[2]),t.d=parseFloat(e[3]),t.e=parseFloat(e[4]),t.f=parseFloat(e[5]))}return t},_proportionalSize:function(t,e){if(null==t||null==e){var i=this.bbox();null==e?e=i.height/i.width*t:null==t&&(t=i.width/i.height*e)}return{width:t,height:e}}}}),SVG.Parent=SVG.invent({create:function(t){this.constructor.call(this,t)},inherit:SVG.Element,extend:{children:function(){return this._children||(this._children=[])},add:function(t,e){return this.has(t)||(e=null==e?this.children().length:e,t.parent&&t.parent.children().splice(t.parent.index(t),1),this.children().splice(e,0,t),this.node.insertBefore(t.node,this.node.childNodes[e]||null),t.parent=this),this._defs&&(this.node.removeChild(this._defs.node),this.node.appendChild(this._defs.node)),this},put:function(t,e){return this.add(t,e),t},has:function(t){return this.index(t)>=0},index:function(t){return this.children().indexOf(t)},get:function(t){return this.children()[t]},first:function(){return this.children()[0]},last:function(){return this.children()[this.children().length-1]},each:function(t,e){var i,n,r=this.children();for(i=0,n=r.length;n>i;i++)r[i]instanceof SVG.Element&&t.apply(r[i],[i,r]),e&&r[i]instanceof SVG.Container&&r[i].each(t,e);return this},removeElement:function(t){return this.children().splice(this.index(t),1),this.node.removeChild(t.node),t.parent=null,this},clear:function(){for(var t=this.children().length-1;t>=0;t--)this.removeElement(this.children()[t]);return this._defs&&this._defs.clear(),this},defs:function(){return this.doc().defs()}}}),SVG.Container=SVG.invent({create:function(t){this.constructor.call(this,t)},inherit:SVG.Parent,extend:{viewbox:function(t){return 0==arguments.length?new SVG.ViewBox(this):(t=1==arguments.length?[t.x,t.y,t.width,t.height]:[].slice.call(arguments),this.attr("viewBox",t))}}}),SVG.FX=SVG.invent({create:function(t){this.target=t},extend:{animate:function(t,i,n){var r,s,h,o,a=this.target,u=this;return"object"==typeof t&&(n=t.delay,i=t.ease,t=t.duration),t="="==t?t:null==t?1e3:new SVG.Number(t).valueOf(),i=i||"<>",u.to=function(t){var n;if(t=0>t?0:t>1?1:t,null==r){r=[];for(o in u.attrs)r.push(o);if(a.morphArray&&(u._plot||r.indexOf("points")>-1)){var l,c=new a.morphArray(u._plot||u.attrs.points||a.array);u._size&&c.size(u._size.width.to,u._size.height.to),l=c.bbox(),u._x?c.move(u._x.to,l.y):u._cx&&c.move(u._cx.to-l.width/2,l.y),l=c.bbox(),u._y?c.move(l.x,u._y.to):u._cy&&c.move(l.x,u._cy.to-l.height/2),delete u._x,delete u._y,delete u._cx,delete u._cy,delete u._size,u._plot=a.array.morph(c)}}if(null==s){s=[];for(o in u.trans)s.push(o)}if(null==h){h=[];for(o in u.styles)h.push(o)}for(t="<>"==i?-Math.cos(t*Math.PI)/2+.5:">"==i?Math.sin(t*Math.PI/2):"<"==i?-Math.cos(t*Math.PI/2)+1:"-"==i?t:"function"==typeof i?i(t):t,u._plot?a.plot(u._plot.at(t)):(u._x?a.x(e(u._x,t)):u._cx&&a.cx(e(u._cx,t)),u._y?a.y(e(u._y,t)):u._cy&&a.cy(e(u._cy,t)),u._size&&a.size(e(u._size.width,t),e(u._size.height,t))),u._viewbox&&a.viewbox(e(u._viewbox.x,t),e(u._viewbox.y,t),e(u._viewbox.width,t),e(u._viewbox.height,t)),n=r.length-1;n>=0;n--)a.attr(r[n],e(u.attrs[r[n]],t));for(n=s.length-1;n>=0;n--)a.transform(s[n],e(u.trans[s[n]],t));for(n=h.length-1;n>=0;n--)a.style(h[n],e(u.styles[h[n]],t));u._during&&u._during.call(a,t,function(i,n){return e({from:i,to:n},t)})},"number"==typeof t&&(this.timeout=setTimeout(function(){var e=(new Date).getTime();u.situation={interval:1e3/60,start:e,play:!0,finish:e+t,duration:t},u.render=function(){if(u.situation.play===!0){var e=(new Date).getTime(),r=e>u.situation.finish?1:(e-u.situation.start)/t;u.to(r),e>u.situation.finish?(u._plot&&a.plot(new SVG.PointArray(u._plot.destination).settle()),u._loop===!0||"number"==typeof u._loop&&u._loop>1?("number"==typeof u._loop&&--u._loop,u.animate(t,i,n)):u._after?u._after.apply(a,[u]):u.stop()):requestAnimFrame(u.render)}else requestAnimFrame(u.render)},u.render()},new SVG.Number(n).valueOf())),this},bbox:function(){return this.target.bbox()},attr:function(t,e){if("object"==typeof t)for(var i in t)this.attr(i,t[i]);else{var n=this.target.attr(t);this.attrs[t]=SVG.Color.isColor(n)?new SVG.Color(n).morph(e):SVG.regex.unit.test(n)?new SVG.Number(n).morph(e):{from:n,to:e}}return this},transform:function(t,e){if(1==arguments.length){t=this.target._parseMatrix(t),delete t.matrix;for(e in t)this.trans[e]={from:this.target.trans[e],to:t[e]}}else{var i={};i[t]=e,this.transform(i)}return this},style:function(t,e){if("object"==typeof t)for(var i in t)this.style(i,t[i]);else this.styles[t]={from:this.target.style(t),to:e};return this},x:function(t){return this._x={from:this.target.x(),to:t},this},y:function(t){return this._y={from:this.target.y(),to:t},this},cx:function(t){return this._cx={from:this.target.cx(),to:t},this},cy:function(t){return this._cy={from:this.target.cy(),to:t},this},move:function(t,e){return this.x(t).y(e)},center:function(t,e){return this.cx(t).cy(e)},size:function(t,e){if(this.target instanceof SVG.Text)this.attr("font-size",t);else{var i=this.target.bbox();this._size={width:{from:i.width,to:t},height:{from:i.height,to:e}}}return this},plot:function(t){return this._plot=t,this},viewbox:function(t,e,i,n){if(this.target instanceof SVG.Container){var r=this.target.viewbox();this._viewbox={x:{from:r.x,to:t},y:{from:r.y,to:e},width:{from:r.width,to:i},height:{from:r.height,to:n}}}return this},update:function(t){return this.target instanceof SVG.Stop&&(null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset))),this},during:function(t){return this._during=t,this},after:function(t){return this._after=t,this},loop:function(t){return this._loop=t||!0,this},stop:function(){return clearTimeout(this.timeout),clearInterval(this.interval),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,this},pause:function(){return this.situation.play===!0&&(this.situation.play=!1,this.situation.pause=(new Date).getTime()),this},play:function(){if(this.situation.play===!1){var t=(new Date).getTime()-this.situation.pause;this.situation.finish+=t,this.situation.start+=t,this.situation.play=!0}return this}},parent:SVG.Element,construct:{animate:function(t,e,i){return(this.fx||(this.fx=new SVG.FX(this))).stop().animate(t,e,i)},stop:function(){return this.fx&&this.fx.stop(),this},pause:function(){return this.fx&&this.fx.pause(),this},play:function(){return this.fx&&this.fx.play(),this}}}),window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||function(t){window.setTimeout(t,1e3/60)}}(),SVG.extend(SVG.Element,SVG.FX,{dx:function(t){return this.x(this.x()+t)},dy:function(t){return this.y(this.y()+t)},dmove:function(t,e){return this.dx(t).dy(e)}}),["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave"].forEach(function(t){SVG.Element.prototype[t]=function(e){var i=this;return this.node["on"+t]="function"==typeof e?function(){return e.apply(i,arguments)}:null,this}}),SVG.on=function(t,e,i){t.addEventListener?t.addEventListener(e,i,!1):t.attachEvent("on"+e,i)},SVG.off=function(t,e,i){t.removeEventListener?t.removeEventListener(e,i,!1):t.detachEvent("on"+e,i)},SVG.extend(SVG.Element,{on:function(t,e){return SVG.on(this.node,t,e),this},off:function(t,e){return SVG.off(this.node,t,e),this}}),SVG.Defs=SVG.invent({create:"defs",inherit:SVG.Container}),SVG.G=SVG.invent({create:"g",inherit:SVG.Container,extend:{x:function(t){return null==t?this.trans.x:this.transform("x",t)},y:function(t){return null==t?this.trans.y:this.transform("y",t)},cx:function(t){return null==t?this.bbox().cx:this.x(t-this.bbox().width/2)},cy:function(t){return null==t?this.bbox().cy:this.y(t-this.bbox().height/2)}},construct:{group:function(){return this.put(new SVG.G)}}}),SVG.extend(SVG.Element,{siblings:function(){return this.parent.children()},position:function(){return this.parent.index(this)},next:function(){return this.siblings()[this.position()+1]},previous:function(){return this.siblings()[this.position()-1]},forward:function(){var t=this.position();return this.parent.removeElement(this).put(this,t+1)},backward:function(){var t=this.position();return t>0&&this.parent.removeElement(this).add(this,t-1),this},front:function(){return this.parent.removeElement(this).put(this)},back:function(){return this.position()>0&&this.parent.removeElement(this).add(this,0),this},before:function(t){t.remove();var e=this.position();return this.parent.add(t,e),this},after:function(t){t.remove();var e=this.position();return this.parent.add(t,e+1),this}}),SVG.Mask=SVG.invent({create:function(){this.constructor.call(this,SVG.create("mask")),this.targets=[]},inherit:SVG.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unmask();return delete this.targets,this.parent.removeElement(this),this}},construct:{mask:function(){return this.defs().put(new SVG.Mask)}}}),SVG.extend(SVG.Element,{maskWith:function(t){return this.masker=t instanceof SVG.Mask?t:this.parent.mask().add(t),this.masker.targets.push(this),this.attr("mask",'url("#'+this.masker.attr("id")+'")')},unmask:function(){return delete this.masker,this.attr("mask",null)}}),SVG.Clip=SVG.invent({create:function(){this.constructor.call(this,SVG.create("clipPath")),this.targets=[]},inherit:SVG.Container,extend:{remove:function(){for(var t=this.targets.length-1;t>=0;t--)this.targets[t]&&this.targets[t].unclip();return delete this.targets,this.parent.removeElement(this),this}},construct:{clip:function(){return this.defs().put(new SVG.Clip)}}}),SVG.extend(SVG.Element,{clipWith:function(t){return this.clipper=t instanceof SVG.Clip?t:this.parent.clip().add(t),this.clipper.targets.push(this),this.attr("clip-path",'url("#'+this.clipper.attr("id")+'")')},unclip:function(){return delete this.clipper,this.attr("clip-path",null)}}),SVG.Gradient=SVG.invent({create:function(t){this.constructor.call(this,SVG.create(t+"Gradient")),this.type=t},inherit:SVG.Container,extend:{from:function(t,e){return"radial"==this.type?this.attr({fx:new SVG.Number(t),fy:new SVG.Number(e)}):this.attr({x1:new SVG.Number(t),y1:new SVG.Number(e)})},to:function(t,e){return"radial"==this.type?this.attr({cx:new SVG.Number(t),cy:new SVG.Number(e)}):this.attr({x2:new SVG.Number(t),y2:new SVG.Number(e)})},radius:function(t){return"radial"==this.type?this.attr({r:new SVG.Number(t)}):this},at:function(t){return this.put(new SVG.Stop).update(t)},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},fill:function(){return"url(#"+this.attr("id")+")"},toString:function(){return this.fill()}},construct:{gradient:function(t,e){return this.defs().gradient(t,e)}}}),SVG.extend(SVG.Defs,{gradient:function(t,e){return this.put(new SVG.Gradient(t)).update(e)}}),SVG.Stop=SVG.invent({create:"stop",inherit:SVG.Element,extend:{update:function(t){return null!=t.opacity&&this.attr("stop-opacity",t.opacity),null!=t.color&&this.attr("stop-color",t.color),null!=t.offset&&this.attr("offset",new SVG.Number(t.offset)),this}}}),SVG.Pattern=SVG.invent({create:"pattern",inherit:SVG.Container,extend:{fill:function(){return"url(#"+this.attr("id")+")"},update:function(t){return this.clear(),"function"==typeof t&&t.call(this,this),this},toString:function(){return this.fill()}},construct:{pattern:function(t,e,i){return this.defs().pattern(t,e,i)}}}),SVG.extend(SVG.Defs,{pattern:function(t,e,i){return this.put(new SVG.Pattern).update(i).attr({x:0,y:0,width:t,height:e,patternUnits:"userSpaceOnUse"})}}),SVG.Doc=SVG.invent({create:function(t){this.parent="string"==typeof t?document.getElementById(t):t,this.constructor.call(this,"svg"==this.parent.nodeName?this.parent:SVG.create("svg")),this.attr({xmlns:SVG.ns,version:"1.1",width:"100%",height:"100%"}).attr("xmlns:xlink",SVG.xlink,SVG.xmlns),this._defs=new SVG.Defs,this._defs.parent=this,this.node.appendChild(this._defs.node),this.doSpof=!1,this.parent!=this.node&&this.stage()},inherit:SVG.Container,extend:{stage:function(){var t=this;return this.parent.appendChild(this.node),t.spof(),SVG.on(window,"resize",function(){t.spof()}),this},defs:function(){return this._defs},spof:function(){if(this.doSpof){var t=this.node.getScreenCTM();t&&this.style("left",-t.e%1+"px").style("top",-t.f%1+"px")}return this},fixSubPixelOffset:function(){return this.doSpof=!0,this}}}),SVG.Shape=SVG.invent({create:function(t){this.constructor.call(this,t)},inherit:SVG.Element}),SVG.Use=SVG.invent({create:"use",inherit:SVG.Shape,extend:{element:function(t){return this.target=t,this.attr("href","#"+t,SVG.xlink)}},construct:{use:function(t){return this.put(new SVG.Use).element(t)}}}),SVG.Rect=SVG.invent({create:"rect",inherit:SVG.Shape,construct:{rect:function(t,e){return this.put((new SVG.Rect).size(t,e))}}}),SVG.Ellipse=SVG.invent({create:"ellipse",inherit:SVG.Shape,extend:{x:function(t){return null==t?this.cx()-this.attr("rx"):this.cx(t+this.attr("rx"))},y:function(t){return null==t?this.cy()-this.attr("ry"):this.cy(t+this.attr("ry"))},cx:function(t){return null==t?this.attr("cx"):this.attr("cx",new SVG.Number(t).divide(this.trans.scaleX))},cy:function(t){return null==t?this.attr("cy"):this.attr("cy",new SVG.Number(t).divide(this.trans.scaleY))},width:function(t){return null==t?2*this.attr("rx"):this.attr("rx",new SVG.Number(t).divide(2))},height:function(t){return null==t?2*this.attr("ry"):this.attr("ry",new SVG.Number(t).divide(2))},size:function(t,e){var i=this._proportionalSize(t,e);return this.attr({rx:new SVG.Number(i.width).divide(2),ry:new SVG.Number(i.height).divide(2)})}},construct:{circle:function(t){return this.ellipse(t,t)},ellipse:function(t,e){return this.put(new SVG.Ellipse).size(t,e).move(0,0)}}}),SVG.Line=SVG.invent({create:"line",inherit:SVG.Shape,extend:{x:function(t){var e=this.bbox();return null==t?e.x:this.attr({x1:this.attr("x1")-e.x+t,x2:this.attr("x2")-e.x+t})},y:function(t){var e=this.bbox();return null==t?e.y:this.attr({y1:this.attr("y1")-e.y+t,y2:this.attr("y2")-e.y+t})},cx:function(t){var e=this.bbox().width/2;return null==t?this.x()+e:this.x(t-e)},cy:function(t){var e=this.bbox().height/2;return null==t?this.y()+e:this.y(t-e)},width:function(t){var e=this.bbox();return null==t?e.width:this.attr(this.attr("x1")e;e++)this.tspan(t[e]).newLine()}return this.build(!1).rebuild()},size:function(t){return this.attr("font-size",t).rebuild()},leading:function(t){return null==t?this._leading:(this._leading=new SVG.Number(t),this.rebuild())},rebuild:function(t){var e=this;return"boolean"==typeof t&&(this._rebuild=t),this._rebuild&&this.lines.each(function(){this.newLined&&(this.textPath||this.attr("x",e.attr("x")),this.attr("dy",e._leading*new SVG.Number(e.attr("font-size"))))}),this},build:function(t){return this._build=!!t,this}},construct:{text:function(t){return this.put(new SVG.Text).text(t)},plain:function(t){return this.put(new SVG.Text).plain(t)}}}),SVG.TSpan=SVG.invent({create:"tspan",inherit:SVG.Shape,extend:{text:function(t){return"function"==typeof t?t.call(this,this):this.plain(t),this},dx:function(t){return this.attr("dx",t)},dy:function(t){return this.attr("dy",t)},newLine:function(){var t=this.doc(SVG.Text);return this.newLined=!0,this.dy(t._leading*t.attr("font-size")).attr("x",t.x())}}}),SVG.extend(SVG.Text,SVG.TSpan,{plain:function(t){return this._build===!1&&this.clear(),this.node.appendChild(document.createTextNode(this.content=t)),this},tspan:function(t){var e=(this.textPath||this).node,i=new SVG.TSpan;return this._build===!1&&this.clear(),e.appendChild(i.node),i.parent=this,this instanceof SVG.Text&&this.lines.add(i),i.text(t)},clear:function(){for(var t=(this.textPath||this).node;t.hasChildNodes();)t.removeChild(t.lastChild);return this instanceof SVG.Text&&(delete this.lines,this.lines=new SVG.Set,this.content=""),this}}),SVG.TextPath=SVG.invent({create:"textPath",inherit:SVG.Element,parent:SVG.Text,construct:{path:function(t){for(this.textPath=new SVG.TextPath;this.node.hasChildNodes();)this.textPath.node.appendChild(this.node.firstChild);return this.node.appendChild(this.textPath.node),this.track=this.doc().defs().path(t),this.textPath.parent=this,this.textPath.attr("href","#"+this.track,SVG.xlink),this},plot:function(t){return this.track&&this.track.plot(t),this}}}),SVG.Nested=SVG.invent({create:function(){this.constructor.call(this,SVG.create("svg")),this.style("overflow","visible")},inherit:SVG.Container,construct:{nested:function(){return this.put(new SVG.Nested)}}}),SVG.A=SVG.invent({create:"a",inherit:SVG.Container,extend:{to:function(t){return this.attr("href",t,SVG.xlink)},show:function(t){return this.attr("show",t,SVG.xlink)},target:function(t){return this.attr("target",t)}},construct:{link:function(t){return this.put(new SVG.A).to(t)}}}),SVG.extend(SVG.Element,{linkTo:function(t){var e=new SVG.A;return"function"==typeof t?t.call(e,e):e.to(t),this.parent.put(e).put(this)}});var i={stroke:["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],fill:["color","opacity","rule"],prefix:function(t,e){return"color"==e?t:t+"-"+e}};["fill","stroke"].forEach(function(t){var e,n={};n[t]=function(n){if("string"==typeof n||SVG.Color.isRgb(n)||n&&"function"==typeof n.fill)this.attr(t,n);else for(e=i[t].length-1;e>=0;e--)null!=n[i[t][e]]&&this.attr(i.prefix(t,i[t][e]),n[i[t][e]]);return this},SVG.extend(SVG.Element,SVG.FX,n)}),SVG.extend(SVG.Element,SVG.FX,{rotate:function(t,e,i){return this.transform({rotation:t||0,cx:e,cy:i})},skew:function(t,e){return this.transform({skewX:t||0,skewY:e||0})},scale:function(t,e){return this.transform({scaleX:t,scaleY:null==e?t:e})},translate:function(t,e){return this.transform({x:t,y:e})},matrix:function(t){return this.transform({matrix:t})},opacity:function(t){return this.attr("opacity",t)}}),SVG.extend(SVG.Rect,SVG.Ellipse,SVG.FX,{radius:function(t,e){return this.attr({rx:t,ry:e||t})}}),SVG.extend(SVG.Path,{length:function(){return this.node.getTotalLength()},pointAt:function(t){return this.node.getPointAtLength(t)}}),SVG.extend(SVG.Text,SVG.FX,{font:function(t){for(var e in t)"leading"==e?this.leading(t[e]):"anchor"==e?this.attr("text-anchor",t[e]):"size"==e||"family"==e||"weight"==e||"stretch"==e||"variant"==e||"style"==e?this.attr("font-"+e,t[e]):this.attr(e,t[e]);return this}}),SVG.Set=SVG.invent({create:function(){this.clear()},extend:{add:function(){var t,e,i=[].slice.call(arguments);for(t=0,e=i.length;e>t;t++)this.members.push(i[t]);return this},remove:function(t){var e=this.index(t);return e>-1&&this.members.splice(e,1),this},each:function(t){for(var e=0,i=this.members.length;i>e;e++)t.apply(this.members[e],[e,this.members]);return this},clear:function(){return this.members=[],this},has:function(t){return this.index(t)>=0},index:function(t){return this.members.indexOf(t)},get:function(t){return this.members[t]},valueOf:function(){return this.members},bbox:function(){var t=new SVG.BBox;if(0==this.members.length)return t;var e=this.members[0].rbox();return t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height,this.each(function(){t=t.merge(this.rbox())}),t}},construct:{set:function(){return new SVG.Set}}}),SVG.SetFX=SVG.invent({create:function(t){this.set=t}}),SVG.Set.inherit=function(){var t,e=[];for(var t in SVG.Shape.prototype)"function"==typeof SVG.Shape.prototype[t]&&"function"!=typeof SVG.Set.prototype[t]&&e.push(t);e.forEach(function(t){SVG.Set.prototype[t]=function(){for(var e=0,i=this.members.length;i>e;e++)this.members[e]&&"function"==typeof this.members[e][t]&&this.members[e][t].apply(this.members[e],arguments);return"animate"==t?this.fx||(this.fx=new SVG.SetFX(this)):this}}),e=[];for(var t in SVG.FX.prototype)"function"==typeof SVG.FX.prototype[t]&&"function"!=typeof SVG.SetFX.prototype[t]&&e.push(t);e.forEach(function(t){SVG.SetFX.prototype[t]=function(){for(var e=0,i=this.set.members.length;i>e;e++)this.set.members[e].fx[t].apply(this.set.members[e].fx,arguments);return this}})},SVG.extend(SVG.Element,{data:function(t,e,i){if("object"==typeof t)for(e in t)this.data(e,t[e]);else if(2>arguments.length)try{return JSON.parse(this.attr("data-"+t))}catch(n){return this.attr("data-"+t)}else this.attr("data-"+t,null===e?null:i===!0||"string"==typeof e||"number"==typeof e?e:JSON.stringify(e));return this}}),SVG.extend(SVG.Element,{remember:function(t,e){if("object"==typeof arguments[0])for(var e in t)this.remember(e,t[e]);else{if(1==arguments.length)return this.memory()[t];this.memory()[t]=e}return this},forget:function(){if(0==arguments.length)this._memory={};else for(var t=arguments.length-1;t>=0;t--)delete this.memory()[arguments[t]];return this},memory:function(){return this._memory||(this._memory={})}}),"function"==typeof define&&define.amd?define(function(){return SVG}):"undefined"!=typeof exports&&(exports.SVG=SVG)}).call(this); \ No newline at end of file diff --git a/package.json b/package.json old mode 100644 new mode 100755 index e7b2b86..551b737 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ , "keywords": ["svg", "vector", "graphics", "animation"] , "author": "Wout Fierens " , "main": "dist/svg.js" -, "version": "1.0.0-rc.4" +, "version": "1.0.0-rc.5" , "jam": { "include": [ "dist/svg.js" diff --git a/spec/index.html b/spec/index.html old mode 100644 new mode 100755 diff --git a/spec/lib/jasmine-1.3.1/MIT.LICENSE b/spec/lib/jasmine-1.3.1/MIT.LICENSE old mode 100644 new mode 100755 diff --git a/spec/lib/jasmine-1.3.1/jasmine-html.js b/spec/lib/jasmine-1.3.1/jasmine-html.js old mode 100644 new mode 100755 diff --git a/spec/lib/jasmine-1.3.1/jasmine.css b/spec/lib/jasmine-1.3.1/jasmine.css old mode 100644 new mode 100755 diff --git a/spec/lib/jasmine-1.3.1/jasmine.js b/spec/lib/jasmine-1.3.1/jasmine.js old mode 100644 new mode 100755 diff --git a/spec/spec/arrange.js b/spec/spec/arrange.js old mode 100644 new mode 100755 diff --git a/spec/spec/array.js b/spec/spec/array.js old mode 100644 new mode 100755 index 375326c..c051976 --- a/spec/spec/array.js +++ b/spec/spec/array.js @@ -26,4 +26,46 @@ describe('PointArray', function () { expect(array + '').toBe('0,0.15 -100,-3.141592654 50,100') }) +}) + +/* toUpperCase() for Firefox */ +describe('PathArray', function () { + var p1, p2 + + beforeEach(function() { + p1 = new SVG.PathArray('m10 10 h 80 v 80 h -80 l 300 400 z') + p2 = new SVG.PathArray('m10 80 c 40 10 65 10 95 80 s 150 150 180 80 t 300 300 q 52 10 95 80 z') + p3 = new SVG.PathArray('m80 80 A 45 45, 0, 0, 0, 125 125 L 125 80 z') + }) + + it('converts to absolute values', function() { + expect(p1.toString()).toBe('M10 10H90V90H10L310 490Z ') + expect(p2.toString()).toBe('M10 80C50 90 75 90 105 160S255 310 285 240T585 540Q637 550 680 620Z ') + expect(p3.toString()).toBe('M80 80A45 45 0 0 0 125 125L125 80Z ') + }) + + describe('move()', function() { + it('moves all points in a straight path', function() { + expect(p1.move(100,200).toString()).toBe('M100 200H180V280H100L400 680Z ') + }) + it('moves all points in a curved path', function() { + expect(p2.move(100,200).toString()).toBe('M100 200C140 210 165 210 195 280S345 430 375 360T675 660Q727 670 770 740Z ') + }) + it('moves all points in a arc path', function() { + expect(p3.move(100,200).toString()).toBe('M100 200A45 45 0 0 0 145 245L145 200Z ') + }) + }) + + describe('size()', function() { + it('resizes all points in a straight path', function() { + expect(p1.size(600,200).toString()).toBe('M10 10H170V43.333333333333336H10L610 210Z ') + }) + it('resizes all points in a curved path', function() { + expect(p2.size(600,200).toString()).toBe('M10 80C45.82089552238806 83.70370370370371 68.2089552238806 83.70370370370371 95.07462686567165 109.62962962962963S229.40298507462686 165.1851851851852 256.2686567164179 139.25925925925927T524.9253731343283 250.37037037037038Q571.4925373134329 254.07407407407408 610 280Z ') + }) + it('resizes all points in a arc path', function() { + expect(p3.size(600,200).toString()).toBe('M80 80A599.9998982747568 199.9999660915856 0 0 0 679.9998982747568 279.99996609158563L679.9998982747568 80Z ') + }) + }) + }) \ No newline at end of file diff --git a/spec/spec/bbox.js b/spec/spec/bbox.js old mode 100644 new mode 100755 diff --git a/spec/spec/clip.js b/spec/spec/clip.js old mode 100644 new mode 100755 diff --git a/spec/spec/color.js b/spec/spec/color.js old mode 100644 new mode 100755 diff --git a/spec/spec/container.js b/spec/spec/container.js old mode 100644 new mode 100755 index 02bc4e9..f72ec17 --- a/spec/spec/container.js +++ b/spec/spec/container.js @@ -165,24 +165,44 @@ describe('Container', function() { }) describe('text()', function() { - it('should increase children by 1', function() { + it('increases children by 1', function() { var initial = draw.children().length draw.text(loremIpsum) expect(draw.children().length).toBe(initial + 1) }) - it('should create a rect', function() { + it('creates a text element', function() { expect(draw.text(loremIpsum).type).toBe('text') }) - it('should create an instance of SVG.Rect', function() { + it('creates an instance of SVG.Rect', function() { expect(draw.text(loremIpsum) instanceof SVG.Text).toBe(true) }) - it('should be an instance of SVG.Shape', function() { + it('is an instance of SVG.Shape', function() { expect(draw.text(loremIpsum) instanceof SVG.Shape).toBe(true) }) - it('should be an instance of SVG.Element', function() { + it('is an instance of SVG.Element', function() { expect(draw.text(loremIpsum) instanceof SVG.Element).toBe(true) }) }) + + describe('plain()', function() { + it('increases children by 1', function() { + var initial = draw.children().length + draw.plain(loremIpsum) + expect(draw.children().length).toBe(initial + 1) + }) + it('creates a plain element', function() { + expect(draw.plain(loremIpsum).type).toBe('text') + }) + it('creates an instance of SVG.Rect', function() { + expect(draw.plain(loremIpsum) instanceof SVG.Text).toBe(true) + }) + it('is an instance of SVG.Shape', function() { + expect(draw.plain(loremIpsum) instanceof SVG.Shape).toBe(true) + }) + it('is an instance of SVG.Element', function() { + expect(draw.plain(loremIpsum) instanceof SVG.Element).toBe(true) + }) + }) describe('clear()', function() { it('removes all children', function() { diff --git a/spec/spec/doc.js b/spec/spec/doc.js old mode 100644 new mode 100755 diff --git a/spec/spec/element.js b/spec/spec/element.js old mode 100644 new mode 100755 index 67d12f0..22b09bf --- a/spec/spec/element.js +++ b/spec/spec/element.js @@ -314,39 +314,6 @@ describe('Element', function() { expect(element).toBe(circle) }) }) - - describe('relative()', function() { - var rect - - beforeEach(function() { - rect = draw.rect(100,100).move(50,60) - }) - - afterEach(function() { - draw.clear() - }) - - describe('x()', function() { - it('moves the x positon of the element', function() { - rect.relative().x(100) - expect(rect.node.getAttribute('x')).toBe('150') - }) - }) - describe('y()', function() { - it('moves the y positon of the element', function() { - rect.relative().x(120) - expect(rect.node.getAttribute('x')).toBe('170') - }) - }) - describe('move()', function() { - it('moves the x and y positon of the element', function() { - rect.relative().move(80, 25) - expect(rect.node.getAttribute('x')).toBe('130') - expect(rect.node.getAttribute('y')).toBe('85') - }) - }) - - }) }) diff --git a/spec/spec/ellipse.js b/spec/spec/ellipse.js old mode 100644 new mode 100755 index 7d7101a..f9e6387 --- a/spec/spec/ellipse.js +++ b/spec/spec/ellipse.js @@ -74,6 +74,31 @@ describe('Ellipse', function() { expect(box.y).toBe(456) }) }) + + describe('dx()', function() { + it('moves the x positon of the element relative to the current position', function() { + ellipse.move(50,60) + ellipse.dx(100) + expect(ellipse.node.getAttribute('cx')).toBe('270') + }) + }) + + describe('dy()', function() { + it('moves the y positon of the element relative to the current position', function() { + ellipse.move(50,60) + ellipse.dy(120) + expect(ellipse.node.getAttribute('cy')).toBe('225') + }) + }) + + describe('dmove()', function() { + it('moves the x and y positon of the element relative to the current position', function() { + ellipse.move(50,60) + ellipse.dmove(80, 25) + expect(ellipse.node.getAttribute('cx')).toBe('250') + expect(ellipse.node.getAttribute('cy')).toBe('130') + }) + }) describe('center()', function() { it('sets the cx and cy position', function() { diff --git a/spec/spec/gradient.js b/spec/spec/gradient.js old mode 100644 new mode 100755 diff --git a/spec/spec/group.js b/spec/spec/group.js old mode 100644 new mode 100755 index 64d0463..dd56ed5 --- a/spec/spec/group.js +++ b/spec/spec/group.js @@ -69,6 +69,30 @@ describe('Group', function() { expect(box.cy).toBe(567) }) }) + + describe('dx()', function() { + it('moves the x positon of the element relative to the current position', function() { + group.move(50,60) + group.dx(100) + expect(group.node.getAttribute('transform')).toBe('translate(150 60)') + }) + }) + + describe('dy()', function() { + it('moves the y positon of the element relative to the current position', function() { + group.move(50,60) + group.dy(120) + expect(group.node.getAttribute('transform')).toBe('translate(50 180)') + }) + }) + + describe('dmove()', function() { + it('moves the x and y positon of the element relative to the current position', function() { + group.move(50, 60) + group.dmove(80, 25) + expect(group.node.getAttribute('transform')).toBe('translate(130 85)') + }) + }) }) \ No newline at end of file diff --git a/spec/spec/helper.js b/spec/spec/helper.js old mode 100644 new mode 100755 diff --git a/spec/spec/hyperlink.js b/spec/spec/hyperlink.js old mode 100644 new mode 100755 diff --git a/spec/spec/image.js b/spec/spec/image.js old mode 100644 new mode 100755 index b24835b..91cfff6 --- a/spec/spec/image.js +++ b/spec/spec/image.js @@ -60,6 +60,31 @@ describe('Image', function() { expect(image.node.getAttribute('y')).toBe('456') }) }) + + describe('dx()', function() { + it('moves the x positon of the element relative to the current position', function() { + image.move(50,60) + image.dx(100) + expect(image.node.getAttribute('x')).toBe('150') + }) + }) + + describe('dy()', function() { + it('moves the y positon of the element relative to the current position', function() { + image.move(50,60) + image.dy(120) + expect(image.node.getAttribute('y')).toBe('180') + }) + }) + + describe('dmove()', function() { + it('moves the x and y positon of the element relative to the current position', function() { + image.move(50,60) + image.dmove(80, 25) + expect(image.node.getAttribute('x')).toBe('130') + expect(image.node.getAttribute('y')).toBe('85') + }) + }) describe('center()', function() { it('should set the cx and cy position', function() { diff --git a/spec/spec/line.js b/spec/spec/line.js old mode 100644 new mode 100755 index cb9d6f6..97490ad --- a/spec/spec/line.js +++ b/spec/spec/line.js @@ -63,6 +63,42 @@ describe('Line', function() { expect(box.y).toBe(456) }) }) + + describe('dx()', function() { + it('moves the x positon of the element relative to the current position', function() { + line.move(50,60) + line.dx(100) + var box = line.bbox() + expect(box.x).toBe(150) + expect(box.y + box.height).toBe(160) + expect(box.x + box.width).toBe(250) + expect(box.y).toBe(60) + }) + }) + + describe('dy()', function() { + it('moves the y positon of the element relative to the current position', function() { + line.move(50, 60) + line.dy(120) + var box = line.bbox() + expect(box.x).toBe(50) + expect(box.y + box.height).toBe(280) + expect(box.x + box.width).toBe(150) + expect(box.y).toBe(180) + }) + }) + + describe('dmove()', function() { + it('moves the x and y positon of the element relative to the current position', function() { + line.move(50,60) + line.dmove(80, 25) + var box = line.bbox() + expect(box.x).toBe(130) + expect(box.y + box.height).toBe(185) + expect(box.x + box.width).toBe(230) + expect(box.y).toBe(85) + }) + }) describe('center()', function() { it('should set the cx and cy position', function() { diff --git a/spec/spec/mask.js b/spec/spec/mask.js old mode 100644 new mode 100755 diff --git a/spec/spec/memory.js b/spec/spec/memory.js old mode 100644 new mode 100755 diff --git a/spec/spec/number.js b/spec/spec/number.js old mode 100644 new mode 100755 diff --git a/spec/spec/path.js b/spec/spec/path.js old mode 100644 new mode 100755 index 2862a92..6ca9cfe --- a/spec/spec/path.js +++ b/spec/spec/path.js @@ -67,6 +67,34 @@ describe('Path', function() { expect(box.y).toBe(456) }) }) + + describe('dx()', function() { + it('moves the x positon of the element relative to the current position', function() { + path.move(50,60) + path.dx(100) + var box = path.bbox() + expect(box.x).toBe(150) + }) + }) + + describe('dy()', function() { + it('moves the y positon of the element relative to the current position', function() { + path.move(50, 60) + path.dy(120) + var box = path.bbox() + expect(box.y).toBe(180) + }) + }) + + describe('dmove()', function() { + it('moves the x and y positon of the element relative to the current position', function() { + path.move(50,60) + path.dmove(80, 25) + var box = path.bbox() + expect(box.x).toBe(130) + expect(box.y).toBe(85) + }) + }) describe('center()', function() { it('should set the cx and cy position', function() { @@ -149,18 +177,18 @@ describe('Path', function() { describe('plot()', function() { it('falls back to a single point without an argument', function() { path = draw.path() - expect(path.node.getAttribute('d')).toBe('M 0 0') + expect(path.node.getAttribute('d')).toBe('M0 0 ') }) }) describe('toString()', function() { it('renders path array correctly to string', function() { - path = path.plot('M 50 60 A 60 60 0 0 0 50 -60 H 100 V 100 L 20 30 C 10 20 30 40 50 60') - expect(path.node.getAttribute('d')).toBe('M 50 60 A 60 60 0 0 0 50 -60 H 100 V 100 L 20 30 C 10 20 30 40 50 60') + path = path.plot('M 50 60 A 60 60 0 0 0 50 -60 H 100 V 100 L 20 30 C 10 20 30 40 50 60 ') + expect(path.node.getAttribute('d')).toBe('M50 60A60 60 0 0 0 50 -60H100V100L20 30C10 20 30 40 50 60 ') }) it('renders path array correctly to string', function() { - path = path.plot('M 50 60 A 60 60 1 1 0 50 -60 H 100 V 100 L 20 30 C 10 20 30 40 50 60') - expect(path.node.getAttribute('d')).toBe('M 50 60 A 60 60 1 1 0 50 -60 H 100 V 100 L 20 30 C 10 20 30 40 50 60') + path = path.plot('M 50 60 A 60 60 1 1 0 50 -60 H 100 V 100 L 20 30 C 10 20 30 40 50 60 ') + expect(path.node.getAttribute('d')).toBe('M50 60A60 60 1 1 0 50 -60H100V100L20 30C10 20 30 40 50 60 ') }) }) diff --git a/spec/spec/pattern.js b/spec/spec/pattern.js old mode 100644 new mode 100755 diff --git a/spec/spec/polygon.js b/spec/spec/polygon.js old mode 100644 new mode 100755 index e94facd..88899de --- a/spec/spec/polygon.js +++ b/spec/spec/polygon.js @@ -61,6 +61,34 @@ describe('Polygon', function() { expect(box.y).toBe(456) }) }) + + describe('dx()', function() { + it('moves the x positon of the element relative to the current position', function() { + polygon.move(50,60) + polygon.dx(100) + var box = polygon.bbox() + expect(box.x).toBe(150) + }) + }) + + describe('dy()', function() { + it('moves the y positon of the element relative to the current position', function() { + polygon.move(50, 60) + polygon.dy(120) + var box = polygon.bbox() + expect(box.y).toBe(180) + }) + }) + + describe('dmove()', function() { + it('moves the x and y positon of the element relative to the current position', function() { + polygon.move(50,60) + polygon.dmove(80, 25) + var box = polygon.bbox() + expect(box.x).toBe(130) + expect(box.y).toBe(85) + }) + }) describe('center()', function() { it('should set the cx and cy position', function() { diff --git a/spec/spec/polyline.js b/spec/spec/polyline.js old mode 100644 new mode 100755 index 204cfa7..99dce1a --- a/spec/spec/polyline.js +++ b/spec/spec/polyline.js @@ -61,6 +61,34 @@ describe('Polyline', function() { expect(box.y).toBe(456) }) }) + + describe('dx()', function() { + it('moves the x positon of the element relative to the current position', function() { + polyline.move(50,60) + polyline.dx(100) + var box = polyline.bbox() + expect(box.x).toBe(150) + }) + }) + + describe('dy()', function() { + it('moves the y positon of the element relative to the current position', function() { + polyline.move(50, 60) + polyline.dy(120) + var box = polyline.bbox() + expect(box.y).toBe(180) + }) + }) + + describe('dmove()', function() { + it('moves the x and y positon of the element relative to the current position', function() { + polyline.move(50,60) + polyline.dmove(80, 25) + var box = polyline.bbox() + expect(box.x).toBe(130) + expect(box.y).toBe(85) + }) + }) describe('center()', function() { it('should set the cx and cy position', function() { diff --git a/spec/spec/rect.js b/spec/spec/rect.js old mode 100644 new mode 100755 index 36d4925..938bc46 --- a/spec/spec/rect.js +++ b/spec/spec/rect.js @@ -73,6 +73,31 @@ describe('Rect', function() { expect(rect.node.getAttribute('y')).toBe('456') }) }) + + describe('dx()', function() { + it('moves the x positon of the element relative to the current position', function() { + rect.move(50,60) + rect.dx(100) + expect(rect.node.getAttribute('x')).toBe('150') + }) + }) + + describe('dy()', function() { + it('moves the y positon of the element relative to the current position', function() { + rect.move(50,60) + rect.dy(120) + expect(rect.node.getAttribute('y')).toBe('180') + }) + }) + + describe('dmove()', function() { + it('moves the x and y positon of the element relative to the current position', function() { + rect.move(50,60) + rect.dmove(80, 25) + expect(rect.node.getAttribute('x')).toBe('130') + expect(rect.node.getAttribute('y')).toBe('85') + }) + }) describe('center()', function() { it('should set the cx and cy position', function() { diff --git a/spec/spec/set.js b/spec/spec/set.js old mode 100644 new mode 100755 diff --git a/spec/spec/svg.js b/spec/spec/svg.js old mode 100644 new mode 100755 diff --git a/spec/spec/text.js b/spec/spec/text.js old mode 100644 new mode 100755 index f23f4f5..f9019a2 --- a/spec/spec/text.js +++ b/spec/spec/text.js @@ -14,15 +14,15 @@ describe('Text', function() { }) describe('x()', function() { - it('should return the value of x without an argument', function() { + it('returns the value of x without an argument', function() { expect(approximately(text.x())).toBe(approximately(0)) }) - it('should set the value of x with the first argument', function() { + it('sets the value of x with the first argument', function() { text.x(123) var box = text.bbox() expect(approximately(box.x, 5)).toBe(approximately(123, 5)) }) - it('should set the value of x based on the anchor with the first argument', function() { + it('sets the value of x based on the anchor with the first argument', function() { text.x(123, true) var box = text.bbox() expect(approximately(box.x, 5)).toBe(approximately(123, 5)) @@ -30,15 +30,15 @@ describe('Text', function() { }) describe('y()', function() { - it('should return the value of y without an argument', function() { + it('returns the value of y without an argument', function() { expect(text.y()).toBe(0) }) - it('should set the value of y with the first argument', function() { + it('sets the value of y with the first argument', function() { text.y(345) var box = text.bbox() expect(approximately(box.y, 5)).toBe(approximately(345, 5)) }) - it('should set the value of y based on the anchor with the first argument', function() { + it('sets the value of y based on the anchor with the first argument', function() { text.y(345, true) var box = text.bbox() expect(approximately(box.y, 5)).toBe(approximately(345, 5)) @@ -46,16 +46,16 @@ describe('Text', function() { }) describe('cx()', function() { - it('should return the value of cx without an argument', function() { + it('returns the value of cx without an argument', function() { var box = text.bbox() expect(approximately(text.cx())).toBe(approximately(box.width / 2)) }) - it('should set the value of cx with the first argument', function() { + it('sets the value of cx with the first argument', function() { text.cx(123) var box = text.bbox() expect(approximately(box.cx, 5)).toBe(approximately(123, 5)) }) - it('should set the value of cx based on the anchor with the first argument', function() { + it('sets the value of cx based on the anchor with the first argument', function() { text.cx(123, true) var box = text.bbox() expect(approximately(box.cx, 5)).toBe(approximately(123, 5)) @@ -63,32 +63,28 @@ describe('Text', function() { }) describe('cy()', function() { - it('should return the value of cy without an argument', function() { + it('returns the value of cy without an argument', function() { var box = text.bbox() expect(text.cy()).toBe(box.cy) }) - it('should set the value of cy with the first argument', function() { + it('sets the value of cy with the first argument', function() { text.cy(345) var box = text.bbox() expect(approximately(box.cy, 5)).toBe(approximately(345, 5)) }) - it('should set the value of cy based on the anchor with the first argument', function() { - text.cy(345, true) - var box = text.bbox() - expect(approximately(box.cy, 5)).toBe(approximately(345 + box.height / 2, 5)) - }) }) describe('move()', function() { - it('should set the x and y position', function() { + it('sets the x and y position', function() { text.move(123,456) - expect(text.node.getAttribute('x')).toBe('123') - expect(text.node.getAttribute('y')).toBe('456') + var box = text.bbox() + expect(box.x).toBe(123) + expect(box.y).toBe(456) }) }) describe('center()', function() { - it('should set the cx and cy position', function() { + it('sets the cx and cy position', function() { text.center(321,567) var box = text.bbox() expect(approximately(box.cx, 5)).toBe(approximately(321, 5)) @@ -99,16 +95,138 @@ describe('Text', function() { describe('size()', function() { it('should define the width and height of the element', function() { text.size(50) - expect(text.style('font-size').valueOf()).toBe(50) + expect(text.attr('font-size').valueOf()).toBe(50) }) }) describe('translate()', function() { - it('should set the translation of an element', function() { + it('sets the translation of an element', function() { text.transform({ x: 12, y: 12 }) expect(text.node.getAttribute('transform')).toBe('translate(12 12)') }) }) + + describe('text()', function() { + it('adds content in a nested tspan', function() { + text.text('It is a bear!') + expect(text.node.childNodes[0].nodeType).toBe(1) + expect(text.node.childNodes[0].childNodes[0].nodeValue).toBe('It is a bear!') + }) + it('creates multiple lines with a newline separated string', function() { + text.text('It is\nJUST\na bear!') + expect(text.node.childNodes.length).toBe(3) + }) + it('stores a reference to the tspan nodes in a set', function() { + text.text('It is\nJUST\na bear!') + expect(text.lines instanceof SVG.Set).toBe(true) + expect(text.lines.members.length).toBe(3) + }) + it('stores the text value in the content reference', function() { + text.text('It is\nJUST\na bear!') + expect(text.content).toBe('It is\nJUST\na bear!') + }) + it('gets the given content of a text element without an argument', function() { + text.text('It is another bear!') + expect(text.node.childNodes[0].nodeType).toBe(1) + expect(text.text()).toMatch('It is another bear!') + }) + it('accepts a block as first arguments', function() { + text.text(function(add) { + add.tspan('mastaba') + add.plain('hut') + }) + expect(text.node.childNodes[0].nodeType).toBe(1) + expect(text.node.childNodes[0].childNodes[0].nodeValue).toBe('mastaba') + expect(text.node.childNodes[1].nodeType).toBe(3) + expect(text.node.childNodes[1].nodeValue).toBe('hut') + }) + }) + + describe('plain()', function() { + it('adds content without a tspan', function() { + text.plain('It is a bear!') + expect(text.node.childNodes[0].nodeType).toBe(3) + expect(text.node.childNodes[0].nodeValue).toBe('It is a bear!') + }) + it('clears content before adding new content', function() { + text.plain('It is not a bear!') + expect(text.node.childNodes.length).toBe(1) + expect(text.node.childNodes[0].nodeValue).toBe('It is not a bear!') + }) + it('stores the text value in the content reference', function() { + text.plain('Just plain text!') + expect(text.content).toBe('Just plain text!') + }) + }) + + describe('tspan()', function() { + it('adds content in a tspan', function() { + text.tspan('It is a bear!') + expect(text.node.childNodes[0].nodeType).toBe(1) + expect(text.node.childNodes[0].childNodes[0].nodeValue).toBe('It is a bear!') + }) + it('clears content before adding new content', function() { + text.tspan('It is not a bear!') + expect(text.node.childNodes.length).toBe(1) + expect(text.node.childNodes[0].childNodes[0].nodeValue).toBe('It is not a bear!') + }) + }) + + describe('clear()', function() { + it('removes all content', function() { + text.text(function(add) { + add.tspan('The first.') + add.tspan('The second.') + add.tspan('The third.') + }) + expect(text.node.childNodes.length).toBe(3) + text.clear() + expect(text.node.childNodes.length).toBe(0) + }) + it('initializes a new set for the lines reference', function() { + var lines = text.lines + text.clear() + expect(text.lines instanceof SVG.Set).toBe(true) + expect(text.lines).not.toBe(lines) + }) + it('clears the stored content value', function() { + text.text('Stored locally.') + expect(text.content).toBe('Stored locally.') + text.clear() + expect(text.content).toBe('') + }) + }) + + describe('build()', function() { + it('enables adding multiple plain text nodes when given true', function() { + text.clear().build(true) + text.plain('A great piece!') + text.plain('Another great piece!') + expect(text.node.childNodes[0].nodeValue).toBe('A great piece!') + expect(text.node.childNodes[1].nodeValue).toBe('Another great piece!') + }) + it('enables adding multiple tspan nodes when given true', function() { + text.clear().build(true) + text.tspan('A great piece!') + text.tspan('Another great piece!') + expect(text.node.childNodes[0].childNodes[0].nodeValue).toBe('A great piece!') + expect(text.node.childNodes[1].childNodes[0].nodeValue).toBe('Another great piece!') + }) + it('disables adding multiple plain text nodes when given false', function() { + text.clear().build(true) + text.plain('A great piece!') + text.build(false).plain('Another great piece!') + expect(text.node.childNodes[0].nodeValue).toBe('Another great piece!') + expect(text.node.childNodes[1]).toBe(undefined) + }) + it('disables adding multiple tspan nodes when given false', function() { + text.clear().build(true) + text.tspan('A great piece!') + text.build(false).tspan('Another great piece!') + expect(text.node.childNodes[0].childNodes[0].nodeValue).toBe('Another great piece!') + expect(text.node.childNodes[1]).toBe(undefined) + }) + }) }) diff --git a/spec/spec/textpath.js b/spec/spec/textpath.js old mode 100644 new mode 100755 diff --git a/spec/spec/use.js b/spec/spec/use.js old mode 100644 new mode 100755 diff --git a/src/data.js b/src/data.js old mode 100644 new mode 100755 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 diff --git a/src/doc.js b/src/doc.js index 5129dd9..a10bffe 100755 --- a/src/doc.js +++ b/src/doc.js @@ -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) { diff --git a/src/fx.js b/src/fx.js index 41629ed..e64f2ad 100755 --- a/src/fx.js +++ b/src/fx.js @@ -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 old mode 100644 new mode 100755 diff --git a/src/inventor.js b/src/inventor.js old mode 100644 new mode 100755 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 old mode 100644 new mode 100755 index 314f53e..2ccb7d5 --- 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 old mode 100644 new mode 100755 diff --git a/src/pointarray.js b/src/pointarray.js old mode 100644 new mode 100755 index 28dbf3e..16f3283 --- 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 old mode 100644 new mode 100755 index 2d02c08..7759bb9 --- 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 } diff --git a/src/svg.js b/src/svg.js index 364a786..07afa7a 100755 --- a/src/svg.js +++ b/src/svg.js @@ -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 + } +}) + -- cgit v1.2.3