Lots of breaking changes. Read below! (#646, #716)

- added `SVG.HTMLNode` which is the object wrapped around html nodes to put something in them
- moved `defs()` method from `SVG.Parent` to `SVG.Element`
- `SVG()` can be called with css selector, node or svg string, now. Without an argument it creates a new `SVG.Doc()` (#646)
- `add()`, `put()`, `addTo()`, `putIn()` now excepts all arguments accepted by `SVG()`
- `SVG.Nested` is not `overflow:visible` by default
- all `SVG.*` objects now can have a node as parameter when constructing
- `SVG()` does not set a default size anymore
This commit is contained in:
Ulrich-Matthias Schäfer 2017-07-25 09:14:48 +02:00
parent bc9bfb6025
commit d6d3891334
27 changed files with 475 additions and 267 deletions

View File

@ -13,6 +13,7 @@ The document follows the conventions described in [“Keep a CHANGELOG”](http:
### Added
- added `SVG.$()` and `SVG.$$()` which will query for one/multiple elements
- added `text()` method to `SVG.Path` to create a textPath from this path (#705)
- added `SVG.HTMLNode` which is the object wrapped around html nodes to put something in them
- added `random` option and `randomize()` method to `SVG.Color` -> __TODO!__
- added `precision()` method to round numeric element attributes -> __TODO!__
@ -48,6 +49,12 @@ The document follows the conventions described in [“Keep a CHANGELOG”](http:
- renamed `fill()` method on `SVG.Gradient` and `SVG.Pattern` to `url()` (#708)
- renamed `previous()` method to `prev()`
- changed `childNodes` to `children` (same for `firstChild`, `lastChild`, ...) (#710)
- moved `defs()` method from `SVG.Parent` to `SVG.Element`
- `SVG()` can be called with css selector, node or svg string, now. Without an argument it creates a new `SVG.Doc()` (#646)
- `add()`, `put()`, `addTo()`, `putIn()` now excepts all arguments accepted by `SVG()`
- `SVG.Nested` is not `overflow:visible` by default
- all `SVG.*` objects now can have a node as parameter when constructing
- `SVG()` does not set a default size anymore
### Fixed
- fixed a bug in clipping and masking where empty nodes persists after removal -> __TODO!__

View File

@ -40,11 +40,12 @@
<script src="../dist/svg.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js"></script>
<script src="svg.bench.js"></script>
<!-- <script src="tests/10000-each.js"></script> -->
<!-- <script src="tests/10000-each.js"></script>
<script src="tests/10000-rects.js"></script>
<script src="tests/10000-circles.js"></script>
<script src="tests/10000-paths.js"></script>
<script src="tests/10000-boxes.js"></script>
<script src="tests/10000-boxes.js"></script>-->
<script src="tests/10000-pointArray-bbox.js"></script>
<script>
SVG.bench.run()
</script>

View File

@ -5,7 +5,7 @@
_chain: []
, _before: function() {}
, _after: function() {}
, draw: SVG('draw')
, draw: SVG().addTo('#draw')
, snap: Snap(100, 100)
, raw: document.getElementById('native')

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,29 @@
SVG.bench.describe('Generate 10000 pathArrays bbox', function(bench) {
var data = '209,153 389,107 547,188 482,289 374,287 91,254 407,243 391,185 166,226 71,177 65,52 234,50 107,136 163,199 158,131 323,45 218,145 305,190 374,143 174,216 296,241'
var dataArr = [[209,153],[389,107],[547,188],[482,289],[374,287],[91,254],[407,243],[391,185],[166,226],[71,177],[65,52],[234,50],[107,136],[163,199],[158,131],[323,45],[218,145],[305,190],[374,143],[174,216],[296,241]]
bench.test('using SVG.js v3.0.0', function() {
for (var i = 0; i < 10000; i++) {
SVG.parser.poly.setAttribute('points', data)
SVG.parser.poly.getBBox()
}
})
bench.test('using SVG.js v3.0.0 without parser', function() {
for (var i = 0; i < 10000; i++) {
var maxX = -Infinity, maxY = -Infinity, minX = Infinity, minY = Infinity
dataArr.forEach(function(el) {
maxX = Math.max(el[0], maxX)
maxY = Math.max(el[1], maxY)
minX = Math.min(el[0], minX)
minY = Math.min(el[1], minY)
})
var a = {x: minX, y: minY, width: maxX-minX, height: maxY-minY}
}
//new SVG.Path().attr('d', data).addTo(draw).bbox()
})
})

233
dist/svg.js vendored
View File

@ -6,7 +6,7 @@
* @copyright Wout Fierens <wout@mick-wout.com>
* @license MIT
*
* BUILT: Mon Jul 10 2017 13:46:12 GMT+0200 (Mitteleuropäische Sommerzeit)
* BUILT: Tue Jul 25 2017 09:09:06 GMT+0200 (Mitteleuropäische Sommerzeit)
*/;
(function(root, factory) {
/* istanbul ignore next */
@ -24,10 +24,7 @@
// The main wrapping element
var SVG = this.SVG = function(element) {
if (SVG.supported) {
element = new SVG.Doc(element)
if(!SVG.parser.draw)
SVG.prepare()
element = createElement(element)
return element
}
@ -106,6 +103,9 @@ SVG.adopt = function(node) {
// make sure a node isn't already adopted
if (node.instance) return node.instance
if(!(node instanceof window.SVGElement))
return new SVG.HtmlNode(node)
// initialize variables
var element
@ -119,7 +119,7 @@ SVG.adopt = function(node) {
else if (SVG[capitalize(node.nodeName)])
element = new SVG[capitalize(node.nodeName)]
else
element = new SVG.Element(node)
element = new SVG.Parent(node)
// ensure references
element.type = node.nodeName
@ -135,37 +135,6 @@ SVG.adopt = function(node) {
return element
}
// Initialize parsing element
SVG.prepare = function() {
// Select document body and create invisible svg element
var body = document.getElementsByTagName('body')[0]
, draw = (body ? new SVG.Doc(body) : SVG.adopt(document.documentElement).nested()).size(2, 0)
// Create parser object
SVG.parser = {
body: body || document.documentElement
, draw: draw.css({
opacity:0,
position:'absolute',
left:'-100%',
top:'-100%',
overflow:'hidden'
}).node
, poly: draw.polyline().node
, path: draw.path().node
, native: SVG.create('svg')
}
}
SVG.parser = {
native: SVG.create('svg')
}
document.addEventListener('DOMContentLoaded', function() {
if(!SVG.parser.draw)
SVG.prepare()
}, false)
// Storage for regular expressions
SVG.regex = {
@ -591,9 +560,14 @@ SVG.extend(SVG.PointArray, {
}
// Get bounding box of points
, bbox: function() {
SVG.parser.poly.setAttribute('points', this.toString())
return SVG.parser.poly.getBBox()
var maxX = -Infinity, maxY = -Infinity, minX = Infinity, minY = Infinity
this.value.forEach(function(el) {
maxX = Math.max(el[0], maxX)
maxY = Math.max(el[1], maxY)
minX = Math.min(el[0], minX)
minY = Math.min(el[1], minY)
})
return {x: minX, y: minY, width: maxX-minX, height: maxY-minY}
}
})
@ -649,10 +623,10 @@ var pathHandlers = {
}
}
var mlhvqtcsa = 'mlhvqtcsaz'.split('')
var mlhvqtcsaz = 'mlhvqtcsaz'.split('')
for(var i = 0, il = mlhvqtcsa.length; i < il; ++i){
pathHandlers[mlhvqtcsa[i]] = (function(i){
for(var i = 0, il = mlhvqtcsaz.length; i < il; ++i){
pathHandlers[mlhvqtcsaz[i]] = (function(i){
return function(c, p, p0) {
if(i == 'H') c[0] = c[0] + p.x
else if(i == 'V') c[0] = c[0] + p.y
@ -667,7 +641,7 @@ for(var i = 0, il = mlhvqtcsa.length; i < il; ++i){
return pathHandlers[i](c, p, p0)
}
})(mlhvqtcsa[i].toUpperCase())
})(mlhvqtcsaz[i].toUpperCase())
}
// Path points array
@ -888,9 +862,8 @@ SVG.extend(SVG.PathArray, {
}
// Get bounding box of path
, bbox: function() {
SVG.parser.path.setAttribute('d', this.toString())
return SVG.parser.path.getBBox()
SVG.parser().path.setAttribute('d', this.toString())
return SVG.parser.nodes.path.getBBox()
}
})
@ -1006,6 +979,34 @@ SVG.Number = SVG.invent({
}
})
SVG.HtmlNode = SVG.invent({
create: function(element) {
this.node = element
}
, extend: {
add: function(element, i) {
element = createElement(element)
if(element instanceof SVG.Nested) {
element = new SVG.Doc(element.node)
element.setData(JSON.parse(element.node.getAttribute('svgjs:data')) || {})
}
if (i == null)
this.node.appendChild(element.node)
else if (element.node != this.node.children[i])
this.node.insertBefore(element.node, this.node.children[i])
return this
}
, put: function(element, i) {
this.add(element, i)
return element
}
}
})
SVG.Element = SVG.invent({
// Initialize node
@ -1094,11 +1095,11 @@ SVG.Element = SVG.invent({
}
// Add element to given container and return self
, addTo: function(parent) {
return parent.put(this)
return createElement(parent).put(this)
}
// Add element to given container and return container
, putIn: function(parent) {
return parent.add(this)
return createElement(parent).add(this)
}
// Get / set id
, id: function(id) {
@ -1195,6 +1196,10 @@ SVG.Element = SVG.invent({
, doc: function() {
return this instanceof SVG.Doc ? this : this.parent(SVG.Doc)
}
, // Get defs
defs: function() {
return this.doc().defs()
}
// return array of all ancestors of given type up to the root svg
, parents: function(type) {
var parents = [], parent = this
@ -1235,6 +1240,9 @@ SVG.Element = SVG.invent({
// otherwise act as a getter
} else {
// write svgjs data to the dom
this.writeDataToDom()
return this.node.outerHTML
}
@ -2343,7 +2351,7 @@ SVG.Matrix = SVG.invent({
// Convert to native SVGMatrix
, native: function() {
// create new matrix
var matrix = SVG.parser.native.createSVGMatrix()
var matrix = SVG.parser.nodes.svg.node.createSVGMatrix()
// update with current values
for (var i = abcdef.length - 1; i >= 0; i--)
@ -2432,7 +2440,7 @@ SVG.Point = SVG.invent({
// Convert to native SVGPoint
, native: function() {
// create new point
var point = SVG.parser.native.createSVGPoint()
var point = SVG.parser.nodes.svg.node.createSVGPoint()
// update with current values
point.x = this.x
@ -2967,6 +2975,8 @@ SVG.Parent = SVG.invent({
}
// Add given element at a position
, add: function(element, i) {
element = createElement(element)
if (i == null)
this.node.appendChild(element.node)
else if (element.node != this.node.children[i])
@ -2977,7 +2987,7 @@ SVG.Parent = SVG.invent({
// Basically does the same as `add()` but returns the added element instead
, put: function(element, i) {
this.add(element, i)
return element
return element.instance || element
}
// Checks if the given element is a child
, has: function(element) {
@ -3031,10 +3041,6 @@ SVG.Parent = SVG.invent({
return this
}
, // Get defs
defs: function() {
return this.doc().defs()
}
}
})
@ -3630,25 +3636,11 @@ SVG.extend(SVG.Defs, {
SVG.Doc = SVG.invent({
// Initialize node
create: function(element) {
if (element) {
// ensure the presence of a dom element
element = typeof element == 'string' ?
document.getElementById(element) :
element
element = element || SVG.create('svg')
this.constructor.call(this, element)
// If the target is an svg element, use that element as the main wrapper.
// This allows svg.js to work with svg documents as well.
if (element.nodeName == 'svg') {
this.constructor.call(this, element)
} else {
this.constructor.call(this, SVG.create('svg'))
element.appendChild(this.node)
this.size('100%', '100%')
}
// set svg element attributes and ensure defs node
this.namespace().defs()
}
// set svg element attributes and ensure defs node
this.namespace().defs()
}
// Inherit from
@ -3665,20 +3657,7 @@ SVG.Doc = SVG.invent({
}
// Creates and returns defs element
, defs: function() {
if (!this._defs) {
var defs
// Find or create a defs element in this instance
if (defs = this.node.getElementsByTagName('defs')[0])
this._defs = SVG.adopt(defs)
else
this._defs = new SVG.Defs
// Make sure the defs node is at the end of the stack
this.node.appendChild(this._defs.node)
}
return this._defs
return this.put(this.node.getElementsByTagName('defs')[0] || new SVG.Defs)
}
// custom parent method
, parent: function() {
@ -3696,16 +3675,15 @@ SVG.Doc = SVG.invent({
// remove children
while(this.node.hasChildNodes())
this.node.removeChild(this.node.lastChild)
// remove defs reference
delete this._defs
// add back parser
if(!SVG.parser.draw.parentNode)
this.node.appendChild(SVG.parser.draw)
return this
}
, toNested: function() {
var el = SVG.create('svg')
this.node.instance = null
el.appendChild(this.node)
return SVG.adopt(this.node)
}
}
})
@ -4476,11 +4454,7 @@ SVG.extend([SVG.Path], {
SVG.Nested = SVG.invent({
// Initialize node
create: function() {
this.constructor.call(this, SVG.create('svg'))
this.css('overflow', 'visible')
}
create: 'svg'
// Inherit from
, inherit: SVG.Container
@ -4492,7 +4466,8 @@ SVG.Nested = SVG.invent({
return this.put(new SVG.Nested)
}
}
})
})
SVG.A = SVG.invent({
// Initialize node
create: 'a'
@ -4832,6 +4807,35 @@ SVG.extend(SVG.Parent, {
}
})
function createElement(element, makeNested) {
if(element instanceof SVG.Element) return element
if(typeof element == 'object') {
return SVG.adopt(element)
}
if(element == null) {
return new SVG.Doc()
}
if(typeof element == 'string' && element.charAt(0) != '<') {
return SVG.adopt(document.querySelector(element))
}
var node = SVG.create('svg')
node.innerHTML = element
element = SVG.adopt(node.firstElementChild)
//if(element instanceof SVG.Nested) {
// // We cant use the adopter for this because it will create an SVG.Nested
// element = new SVG.Doc(element.node)
// element.setData(JSON.parse(element.node.getAttribute('svgjs:data')) || {})
//}
return element
}
function isNulledBox(box) {
return !box.w && !box.h && !box.x && !box.y
}
@ -5125,7 +5129,7 @@ SVG.Box = SVG.invent({
}
} catch(e) {
try {
var clone = this.clone(SVG.parser.draw.instance).show()
var clone = this.clone(SVG.parser().svg).show()
box = clone.node.getBBox()
clone.remove()
} catch(e) {
@ -5158,6 +5162,29 @@ SVG.extend([SVG.Doc, SVG.Nested, SVG.Symbol, SVG.Image, SVG.Pattern, SVG.Marker,
return this.attr('viewBox', new SVG.Box(x, y, width, height))
}
})
SVG.parser = function() {
var b
if(!SVG.parser.nodes.svg.node.parentNode) {
b = document.body || document.documentElement
SVG.parser.nodes.svg.addTo(b)
}
return SVG.parser.nodes
}
SVG.parser.nodes = {
svg: new SVG.Nested().size(2, 0).css({
opacity:0,
position:'absolute',
left:'-100%',
top:'-100%',
overflow:'hidden'
})
}
SVG.parser.nodes.path = SVG.parser.nodes.svg.path().node
return SVG

4
dist/svg.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -38,6 +38,7 @@ var parts = [
, 'src/pointarray.js'
, 'src/patharray.js'
, 'src/number.js'
, 'src/HtmlNode.js'
, 'src/element.js'
, 'src/fx.js'
, 'src/matrix.js'
@ -81,6 +82,7 @@ var parts = [
, 'src/helpers.js'
, 'src/polyfill.js'
, 'src/boxes.js'
, 'src/parser.js'
]
gulp.task('clean', function() {

View File

@ -23,7 +23,7 @@ describe('Adopter', function() {
expect(path.parent().node.getAttribute('xmlns:xlink')).toBe(SVG.xlink)
})
it('initializes a defs node', function() {
expect(path.parent()._defs).toBe(path.parent().defs())
expect(path.defs() instanceof SVG.Defs).toBe(true)
})
})

View File

@ -340,11 +340,37 @@ describe('Container', function() {
})
})
describe('defs()', function() {
it('returns the defs from the svg', function() {
var g = draw.group()
expect(g.defs()).toBe(draw.doc().defs())
expect(g.defs() instanceof SVG.Defs).toBeTruthy()
describe('add()', function() {
it('adds element at the end of the parent element when no position given', function() {
var rect = draw.rect(100,100)
var line = draw.line(100,100, 50, 50)
var group = draw.group()
group.circle(10,10)
expect(group.add(rect)).toBe(group)
expect(rect.position()).toBe(1)
})
it('adds element at the given position', function() {
var rect = draw.rect(100,100)
var line = draw.line(100,100, 50, 50)
var group = draw.group()
group.circle(10,10)
expect(group.add(rect)).toBe(group)
expect(group.add(line, 1)).toBe(group)
expect(line.position()).toBe(1)
})
})
describe('put()', function() {
it('calls add() but returns added element', function() {
var rect = draw.rect(100,100)
var group = draw.group()
spyOn(group, 'add')
expect(group.put(rect, 0)).toBe(rect)
expect(group.add).toHaveBeenCalledWith(rect, 0)
})
})

View File

@ -2,7 +2,7 @@ describe('Doc', function() {
describe('create()', function(){
it('doenst alter size when adopting width SVG()', function() {
var svg = SVG('inlineSVG')
var svg = SVG('#inlineSVG')
expect(svg.width()).toBe(0)
expect(svg.height()).toBe(0)
})
@ -26,7 +26,7 @@ describe('Doc', function() {
describe('defs()', function() {
it('returns defs element', function(){
expect(draw.defs()).toBe(draw._defs)
expect(draw.defs()).toBe(draw.node.getElementsByTagName('defs')[0].instance)
})
it('references parent node', function(){
expect(draw.defs().parent()).toBe(draw)
@ -43,7 +43,7 @@ describe('Doc', function() {
expect(window.document.querySelectorAll('svg').length).toBe(cnt-1)
}
draw = SVG(drawing).size(100,100);
draw = SVG().addTo(drawing).size(100,100);
expect(window.document.querySelectorAll('svg').length).toBe(cnt)
})
})

View File

@ -829,7 +829,7 @@ describe('Element', function() {
toBeTested === '<svg xmlns:svgjs="http://svgjs.com/svgjs" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" xmlns="http://www.w3.org/2000/svg" height="100" width="100"><rect height="100" width="100"></rect><circle fill="#ff0066" cy="50" cx="50" r="50"></circle></svg>'
// Firefox
|| toBeTested === '<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs"><rect width="100" height="100"></rect><circle r="50" cx="50" cy="50" fill="#ff0066"></circle></svg>'
|| toBeTested === '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="100" height="100"><rect width="100" height="100"></rect><circle r="50" cx="50" cy="50" fill="#ff0066"></circle></svg>'
// svgdom
|| toBeTested === '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="100" height="100"><svg id="SvgjsSvg1002" width="2" height="0" style="overflow: hidden; top: -100%; left: -100%; position: absolute; opacity: 0"><polyline id="SvgjsPolyline1003" points="10,10 20,10 30,10"></polyline><path id="SvgjsPath1004" d="M80 80A45 45 0 0 0 125 125L125 80Z "></path></svg><rect width="100" height="100"></rect><circle r="50" cx="50" cy="50" fill="#ff0066"></circle></svg>'
@ -977,4 +977,11 @@ describe('Element', function() {
expect(rect.is(SVG.Parent)).toBeFalsy()
})
})
describe('defs()', function() {
it('returns the defs from the svg', function() {
var g = draw.group()
expect(g.defs()).toBe(draw.doc().defs())
expect(g.defs() instanceof SVG.Defs).toBeTruthy()
})
})
})

View File

@ -133,7 +133,7 @@ if(typeof exports === 'object'){
parserInDoc |= 0
drawing.id = 'drawing'
draw = SVG(drawing).size(100,100)
draw = SVG().addTo(drawing).size(100,100)
parser = parserInDoc ? [SVG.parser.draw.instance] : []

View File

@ -1,36 +1,83 @@
describe('SVG', function() {
describe('()', function() {
var drawing, wrapper
var drawing, wrapper, wrapperHTML, rect
beforeEach(function() {
wrapper = document.createElement('svg')
wrapper = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
wrapper.id = 'testSvg'
wrapperHTML = document.createElement('div')
wrapperHTML.id = 'testDiv'
rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
document.documentElement.appendChild(wrapper)
drawing = SVG(wrapper)
document.documentElement.appendChild(wrapperHTML)
})
afterEach(function() {
wrapper.parentNode.removeChild(wrapper)
wrapperHTML.parentNode.removeChild(wrapperHTML)
})
it('creates a new svg drawing', function() {
expect(drawing.type).toBe('svg')
it('creates an instanceof SVG.Doc without any argument', function() {
expect(SVG() instanceof SVG.Doc).toBe(true)
expect(SVG().node.nodeName).toBe('svg')
})
it('creates an instance of SVG.Doc', function() {
expect(drawing instanceof SVG.Doc).toBe(true)
it('creates an instanceof SVG.HtmlNode with html node', function() {
var el = SVG(wrapperHTML)
expect(el instanceof SVG.HtmlNode).toBe(true)
expect(el.node).toBe(wrapperHTML)
})
if(parserInDoc){
it('sets no default size in svg documents', function() {
expect(drawing.width()).toBe(0)
expect(drawing.height()).toBe(0)
})
}else{
it('sets size to 100% in html documents', function() {
expect(drawing.width()).toBe('100%')
expect(drawing.height()).toBe('100%')
})
}
it('creates new SVG.HtmlNode when called with css selector pointing to html node', function() {
var el = SVG('#testDiv')
expect(el instanceof SVG.HtmlNode).toBe(true)
expect(el.node).toBe(wrapperHTML)
})
it('creates an instanceof SVG.Doc with svg node', function() {
var doc = SVG(wrapper)
expect(doc instanceof SVG.Doc).toBe(true)
expect(doc.node).toBe(wrapper)
})
it('creates new SVG.Doc when called with css selector pointing to svg node', function() {
var doc = SVG('#testSvg')
expect(doc instanceof SVG.Doc).toBe(true)
expect(doc.node).toBe(wrapper)
})
it('adopts any SVGElement', function() {
expect(SVG(rect) instanceof SVG.Rect).toBe(true)
expect(SVG(rect).node).toBe(rect)
})
it('creates an instanceof SVG.Nested when importing a whole svg', function() {
var doc = SVG('<svg width="200"><rect></rect></svg>')
expect(doc instanceof SVG.Nested).toBe(true)
expect(doc.node.nodeName).toBe('svg')
expect(doc.width()).toBe(200)
expect(doc.get(0).node.nodeName).toBe('rect')
})
it('creates SVG.Shape from any shape string', function() {
var rect = SVG('<rect width="200" height="100">')
, circle = SVG('<circle r="200">')
expect(rect instanceof SVG.Rect).toBe(true)
expect(rect.node.nodeName).toBe('rect')
expect(rect.width()).toBe(200)
expect(circle instanceof SVG.Circle).toBe(true)
expect(circle.node.nodeName).toBe('circle')
expect(circle.attr('r')).toBe(200)
})
it('returns the argument when called with any SVG.Element', function() {
drawing = SVG(wrapper)
expect(SVG(drawing)).toBe(drawing)
})
})
describe('create()', function() {
@ -78,25 +125,4 @@ describe('SVG', function() {
expect(typeof SVG.Bogus).toBe('undefined')
})
})
describe('prepare()', function() {
var drawing, wrapper, parser
beforeEach(function() {
wrapper = document.createElement('svg')
document.documentElement.appendChild(wrapper)
drawing = SVG(wrapper)
})
it('creates a parser element when calling SVG()', function() {
expect(SVG.parser.draw.nodeName).toBe('svg')
})
it('hides the parser', function() {
expect(window.stripped(SVG.parser.draw.getAttribute('style'))).toBe('opacity:0;position:absolute;left:-100%;top:-100%;overflow:hidden')
})
it('holds polyline and path', function() {
expect(SVG.select('polyline', SVG.parser.draw.node)[0].type).toBe('polyline')
expect(SVG.select('path', SVG.parser.draw.node)[0].type).toBe('path')
})
})
})

27
src/HtmlNode.js Normal file
View File

@ -0,0 +1,27 @@
SVG.HtmlNode = SVG.invent({
create: function(element) {
this.node = element
}
, extend: {
add: function(element, i) {
element = createElement(element)
if(element instanceof SVG.Nested) {
element = new SVG.Doc(element.node)
element.setData(JSON.parse(element.node.getAttribute('svgjs:data')) || {})
}
if (i == null)
this.node.appendChild(element.node)
else if (element.node != this.node.children[i])
this.node.insertBefore(element.node, this.node.children[i])
return this
}
, put: function(element, i) {
this.add(element, i)
return element
}
}
})

View File

@ -102,7 +102,7 @@ SVG.Box = SVG.invent({
}
} catch(e) {
try {
var clone = this.clone(SVG.parser.draw.instance).show()
var clone = this.clone(SVG.parser().svg).show()
box = clone.node.getBBox()
clone.remove()
} catch(e) {

View File

@ -1,25 +1,11 @@
SVG.Doc = SVG.invent({
// Initialize node
create: function(element) {
if (element) {
// ensure the presence of a dom element
element = typeof element == 'string' ?
document.getElementById(element) :
element
create: function(element) {
element = element || SVG.create('svg')
this.constructor.call(this, element)
// If the target is an svg element, use that element as the main wrapper.
// This allows svg.js to work with svg documents as well.
if (element.nodeName == 'svg') {
this.constructor.call(this, element)
} else {
this.constructor.call(this, SVG.create('svg'))
element.appendChild(this.node)
this.size('100%', '100%')
}
// set svg element attributes and ensure defs node
this.namespace().defs()
}
// set svg element attributes and ensure defs node
this.namespace().defs()
}
// Inherit from
@ -36,20 +22,7 @@ SVG.Doc = SVG.invent({
}
// Creates and returns defs element
, defs: function() {
if (!this._defs) {
var defs
// Find or create a defs element in this instance
if (defs = this.node.getElementsByTagName('defs')[0])
this._defs = SVG.adopt(defs)
else
this._defs = new SVG.Defs
// Make sure the defs node is at the end of the stack
this.node.appendChild(this._defs.node)
}
return this._defs
return this.put(this.node.getElementsByTagName('defs')[0] || new SVG.Defs)
}
// custom parent method
, parent: function() {
@ -67,16 +40,15 @@ SVG.Doc = SVG.invent({
// remove children
while(this.node.hasChildNodes())
this.node.removeChild(this.node.lastChild)
// remove defs reference
delete this._defs
// add back parser
if(!SVG.parser.draw.parentNode)
this.node.appendChild(SVG.parser.draw)
return this
}
, toNested: function() {
var el = SVG.create('svg')
this.node.instance = null
el.appendChild(this.node)
return SVG.adopt(this.node)
}
}
})

View File

@ -86,11 +86,11 @@ SVG.Element = SVG.invent({
}
// Add element to given container and return self
, addTo: function(parent) {
return parent.put(this)
return createElement(parent).put(this)
}
// Add element to given container and return container
, putIn: function(parent) {
return parent.add(this)
return createElement(parent).add(this)
}
// Get / set id
, id: function(id) {
@ -187,6 +187,10 @@ SVG.Element = SVG.invent({
, doc: function() {
return this instanceof SVG.Doc ? this : this.parent(SVG.Doc)
}
, // Get defs
defs: function() {
return this.doc().defs()
}
// return array of all ancestors of given type up to the root svg
, parents: function(type) {
var parents = [], parent = this
@ -227,6 +231,9 @@ SVG.Element = SVG.invent({
// otherwise act as a getter
} else {
// write svgjs data to the dom
this.writeDataToDom()
return this.node.outerHTML
}

View File

@ -1,3 +1,32 @@
function createElement(element, makeNested) {
if(element instanceof SVG.Element) return element
if(typeof element == 'object') {
return SVG.adopt(element)
}
if(element == null) {
return new SVG.Doc()
}
if(typeof element == 'string' && element.charAt(0) != '<') {
return SVG.adopt(document.querySelector(element))
}
var node = SVG.create('svg')
node.innerHTML = element
element = SVG.adopt(node.firstElementChild)
//if(element instanceof SVG.Nested) {
// // We cant use the adopter for this because it will create an SVG.Nested
// element = new SVG.Doc(element.node)
// element.setData(JSON.parse(element.node.getAttribute('svgjs:data')) || {})
//}
return element
}
function isNulledBox(box) {
return !box.w && !box.h && !box.x && !box.y
}

View File

@ -156,7 +156,7 @@ SVG.Matrix = SVG.invent({
// Convert to native SVGMatrix
, native: function() {
// create new matrix
var matrix = SVG.parser.native.createSVGMatrix()
var matrix = SVG.parser.nodes.svg.node.createSVGMatrix()
// update with current values
for (var i = abcdef.length - 1; i >= 0; i--)

View File

@ -1,10 +1,6 @@
SVG.Nested = SVG.invent({
// Initialize node
create: function() {
this.constructor.call(this, SVG.create('svg'))
this.css('overflow', 'visible')
}
create: 'svg'
// Inherit from
, inherit: SVG.Container
@ -16,4 +12,4 @@ SVG.Nested = SVG.invent({
return this.put(new SVG.Nested)
}
}
})
})

View File

@ -17,6 +17,8 @@ SVG.Parent = SVG.invent({
}
// Add given element at a position
, add: function(element, i) {
element = createElement(element)
if (i == null)
this.node.appendChild(element.node)
else if (element.node != this.node.children[i])
@ -27,7 +29,7 @@ SVG.Parent = SVG.invent({
// Basically does the same as `add()` but returns the added element instead
, put: function(element, i) {
this.add(element, i)
return element
return element.instance || element
}
// Checks if the given element is a child
, has: function(element) {
@ -53,7 +55,7 @@ SVG.Parent = SVG.invent({
, each: function(block, deep) {
var i, il
, children = this.children()
for (i = 0, il = children.length; i < il; i++) {
if (children[i] instanceof SVG.Element)
block.apply(children[i], [i, children])
@ -61,13 +63,13 @@ SVG.Parent = SVG.invent({
if (deep && (children[i] instanceof SVG.Parent))
children[i].each(block, deep)
}
return this
}
// Remove a given child
, removeElement: function(element) {
this.node.removeChild(element.node)
return this
}
// Remove all elements in this container
@ -75,16 +77,12 @@ SVG.Parent = SVG.invent({
// remove children
while(this.node.hasChildNodes())
this.node.removeChild(this.node.lastChild)
// remove defs reference
delete this._defs
return this
}
, // Get defs
defs: function() {
return this.doc().defs()
}
}
})

22
src/parser.js Normal file
View File

@ -0,0 +1,22 @@
SVG.parser = function() {
var b
if(!SVG.parser.nodes.svg.node.parentNode) {
b = document.body || document.documentElement
SVG.parser.nodes.svg.addTo(b)
}
return SVG.parser.nodes
}
SVG.parser.nodes = {
svg: new SVG.Nested().size(2, 0).css({
opacity:0,
position:'absolute',
left:'-100%',
top:'-100%',
overflow:'hidden'
})
}
SVG.parser.nodes.path = SVG.parser.nodes.svg.path().node

View File

@ -50,10 +50,10 @@ var pathHandlers = {
}
}
var mlhvqtcsa = 'mlhvqtcsaz'.split('')
var mlhvqtcsaz = 'mlhvqtcsaz'.split('')
for(var i = 0, il = mlhvqtcsa.length; i < il; ++i){
pathHandlers[mlhvqtcsa[i]] = (function(i){
for(var i = 0, il = mlhvqtcsaz.length; i < il; ++i){
pathHandlers[mlhvqtcsaz[i]] = (function(i){
return function(c, p, p0) {
if(i == 'H') c[0] = c[0] + p.x
else if(i == 'V') c[0] = c[0] + p.y
@ -68,7 +68,7 @@ for(var i = 0, il = mlhvqtcsa.length; i < il; ++i){
return pathHandlers[i](c, p, p0)
}
})(mlhvqtcsa[i].toUpperCase())
})(mlhvqtcsaz[i].toUpperCase())
}
// Path points array
@ -289,9 +289,8 @@ SVG.extend(SVG.PathArray, {
}
// Get bounding box of path
, bbox: function() {
SVG.parser.path.setAttribute('d', this.toString())
return SVG.parser.path.getBBox()
SVG.parser().path.setAttribute('d', this.toString())
return SVG.parser.nodes.path.getBBox()
}
})

View File

@ -45,7 +45,7 @@ SVG.Point = SVG.invent({
// Convert to native SVGPoint
, native: function() {
// create new point
var point = SVG.parser.native.createSVGPoint()
var point = SVG.parser.nodes.svg.node.createSVGPoint()
// update with current values
point.x = this.x

View File

@ -95,8 +95,13 @@ SVG.extend(SVG.PointArray, {
}
// Get bounding box of points
, bbox: function() {
SVG.parser.poly.setAttribute('points', this.toString())
return SVG.parser.poly.getBBox()
var maxX = -Infinity, maxY = -Infinity, minX = Infinity, minY = Infinity
this.value.forEach(function(el) {
maxX = Math.max(el[0], maxX)
maxY = Math.max(el[1], maxY)
minX = Math.min(el[0], minX)
minY = Math.min(el[1], minY)
})
return {x: minX, y: minY, width: maxX-minX, height: maxY-minY}
}
})

View File

@ -1,11 +1,8 @@
// The main wrapping element
var SVG = this.SVG = function(element) {
if (SVG.supported) {
element = new SVG.Doc(element)
if(!SVG.parser.draw)
SVG.prepare()
element = createElement(element)
return element
}
}
@ -83,6 +80,9 @@ SVG.adopt = function(node) {
// make sure a node isn't already adopted
if (node.instance) return node.instance
if(!(node instanceof window.SVGElement))
return new SVG.HtmlNode(node)
// initialize variables
var element
@ -96,7 +96,7 @@ SVG.adopt = function(node) {
else if (SVG[capitalize(node.nodeName)])
element = new SVG[capitalize(node.nodeName)]
else
element = new SVG.Element(node)
element = new SVG.Parent(node)
// ensure references
element.type = node.nodeName
@ -112,34 +112,3 @@ SVG.adopt = function(node) {
return element
}
// Initialize parsing element
SVG.prepare = function() {
// Select document body and create invisible svg element
var body = document.getElementsByTagName('body')[0]
, draw = (body ? new SVG.Doc(body) : SVG.adopt(document.documentElement).nested()).size(2, 0)
// Create parser object
SVG.parser = {
body: body || document.documentElement
, draw: draw.css({
opacity:0,
position:'absolute',
left:'-100%',
top:'-100%',
overflow:'hidden'
}).node
, poly: draw.polyline().node
, path: draw.path().node
, native: SVG.create('svg')
}
}
SVG.parser = {
native: SVG.create('svg')
}
document.addEventListener('DOMContentLoaded', function() {
if(!SVG.parser.draw)
SVG.prepare()
}, false)