aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlrich-Matthias Schäfer <ulima.ums@googlemail.com>2020-04-03 18:42:19 +1000
committerUlrich-Matthias Schäfer <ulima.ums@googlemail.com>2020-04-03 18:42:19 +1000
commit7547a6a45818bcfefd545da4697196b81bd8da5b (patch)
tree0f62eb9f98b81c6fe20536d5437c9acf39e08b01
parent28e74482b8cc7b7f2e4aca099ceea9f7d7a888d5 (diff)
downloadsvg.js-7547a6a45818bcfefd545da4697196b81bd8da5b.tar.gz
svg.js-7547a6a45818bcfefd545da4697196b81bd8da5b.zip
- fixed `put()` which correctly creates an svgjs object from the passed element now before returning
- fixed `parent()` which correctly returns null if direct parent is the document or a document-fragment - fixed `add()` which correctly removes namespaces of non-root svg elements now when added to another svg element (#1086) - fixed `isRoot()` which correctly returns false, if the element is in a document-fragment
-rw-r--r--CHANGELOG.md7
-rw-r--r--spec/spec/elements/Dom.js55
-rw-r--r--spec/spec/elements/G.js32
-rw-r--r--spec/spec/elements/Svg.js156
-rw-r--r--src/elements/Dom.js6
-rw-r--r--src/elements/Element.js7
-rw-r--r--src/elements/Svg.js55
-rw-r--r--svg.js.d.ts58
8 files changed, 273 insertions, 103 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 320fd0a..e39de5e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,7 @@ The document follows the conventions described in [“Keep a CHANGELOG”](http:
====
-## [3.0.17]
+## [3.1.0]
### Fixed
- fixed `zoom()` method of runner which was passed a wrong parameter
@@ -16,9 +16,12 @@ The document follows the conventions described in [“Keep a CHANGELOG”](http:
- fixed a case in `rbox()` where not always all values of the box were updated
- fixed `getOrigin()` function used by `transform()` so that all origin (#1085) popssibilities specified in the docs are working
- fixed positioning of text by its baseline when using `amove()`
- - fixed tons of typings in the svg.d.ts file
+ - fixed tons of typings in the svg.d.ts file and relaxed type requirements for `put()` and `parent()`
- fixed adopter when adopting an svg/html string. It had still its wrapper as parentNode attached
- fixed `put()` which correctly creates an svgjs object from the passed element now before returning
+ - fixed `parent()` which correctly returns null if direct parent is the document or a document-fragment
+ - fixed `add()` which correctly removes namespaces of non-root svg elements now when added to another svg element
+ - fixed `isRoot()` which correctly returns false, if the element is in a document-fragment (#1081)
### Added
- added second Parameter to `SVG(el, isHTML)` which allows to explicitely create elements in the HTML namespace (#1058)
diff --git a/spec/spec/elements/Dom.js b/spec/spec/elements/Dom.js
index fcdba4c..8004317 100644
--- a/spec/spec/elements/Dom.js
+++ b/spec/spec/elements/Dom.js
@@ -1,15 +1,64 @@
-/* globals describe, expect, it, beforeEach, jasmine */
+/* globals describe, expect, it, beforeEach, jasmine, container */
-import { SVG, G, Rect } from '../../../src/main.js'
+import { SVG, G, Rect, Svg } from '../../../src/main.js'
+import { getWindow } from '../../../src/utils/window.js'
const { any } = jasmine
describe('Dom.js', function () {
+ describe('parent()', () => {
+ var canvas, rect, group1, group2
+
+ beforeEach(function () {
+ canvas = SVG().addTo(container)
+ group1 = canvas.group().addClass('test')
+ group2 = group1.group()
+ rect = group2.rect(100, 100)
+ })
+
+ it('returns the svg parent with no argument given', () => {
+ expect(rect.parent()).toBe(group2)
+ })
+
+ it('returns the closest parent with the correct type', () => {
+ expect(rect.parent(Svg)).toBe(canvas)
+ })
+
+ it('returns the closest parent matching the selector', () => {
+ expect(rect.parent('.test')).toBe(group1)
+ })
+
+ it('returns null if it cannot find a parent matching the argument', () => {
+ expect(rect.parent('.not-there')).toBe(null)
+ })
+
+ it('returns null if it cannot find a parent matching the argument in a #document-fragment', () => {
+ const fragment = getWindow().document.createDocumentFragment()
+ const svg = new Svg().addTo(fragment)
+ const rect = svg.rect(100, 100)
+ expect(rect.parent('.not-there')).toBe(null)
+ })
+
+ it('returns null if parent is #document', () => {
+ // cant test that here
+ })
+
+ it('returns null if parent is #document-fragment', () => {
+ const fragment = getWindow().document.createDocumentFragment()
+ const svg = new Svg().addTo(fragment)
+ expect(svg.parent()).toBe(null)
+ })
+
+ it('returns html parents, too', () => {
+ expect(canvas.parent().node).toBe(container)
+ })
+ })
+
describe('wrap()', function () {
var canvas
var rect
beforeEach(function () {
- canvas = new SVG()
+ canvas = SVG()
rect = canvas.rect(100, 100)
})
diff --git a/spec/spec/elements/G.js b/spec/spec/elements/G.js
index 7e2fab1..d926256 100644
--- a/spec/spec/elements/G.js
+++ b/spec/spec/elements/G.js
@@ -1,6 +1,6 @@
-/* globals describe, expect, it, jasmine, spyOn */
+/* globals describe, expect, it, jasmine, spyOn, container */
-import { Box, G, Rect, makeInstance } from '../../../src/main.js'
+import { Box, G, Rect, SVG } from '../../../src/main.js'
const { any, objectContaining } = jasmine
@@ -19,7 +19,7 @@ describe('G.js', () => {
describe('Container', () => {
describe('group()', () => {
it('creates a group in the container', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = canvas.group()
expect(g).toEqual(any(G))
expect(g.parent()).toBe(canvas)
@@ -29,7 +29,7 @@ describe('G.js', () => {
describe('dmove()', () => {
it('moves the bbox of the group by a certain amount (1)', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = canvas.group()
g.add(new Rect({ width: 100, height: 120, x: 10, y: 20 }))
@@ -44,7 +44,7 @@ describe('G.js', () => {
})
it('moves the bbox of the group by a certain amount (2)', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = canvas.group()
g.rect(400, 200).move(123, 312).rotate(34).skew(12)
@@ -67,7 +67,7 @@ describe('G.js', () => {
describe('move()', () => {
it('calls dmove() with the correct difference', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = canvas.group()
g.rect(100, 200).move(111, 223)
@@ -80,7 +80,7 @@ describe('G.js', () => {
describe('x()', () => {
it('gets the x value of the bbox', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = new G()
g.add(new Rect({ width: 100, height: 120, x: 10, y: 20 }))
@@ -92,7 +92,7 @@ describe('G.js', () => {
expect(g.x()).toBe(10)
})
it('calls move with the paramater as x', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = canvas.group()
g.rect(100, 200).move(111, 223)
@@ -105,7 +105,7 @@ describe('G.js', () => {
describe('y()', () => {
it('gets the y value of the bbox', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = new G()
g.add(new Rect({ width: 100, height: 120, x: 10, y: 20 }))
@@ -117,7 +117,7 @@ describe('G.js', () => {
expect(g.y()).toBe(20)
})
it('calls move with the paramater as y', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = canvas.group()
g.rect(100, 200).move(111, 223)
@@ -130,7 +130,7 @@ describe('G.js', () => {
describe('size()', () => {
it('changes the dimensions of the bbox (1)', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = new G()
g.add(new Rect({ width: 100, height: 120, x: 10, y: 20 }))
@@ -164,7 +164,7 @@ describe('G.js', () => {
})
it('changes the dimensions of the bbox (2)', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = canvas.group()
g.rect(400, 200).move(123, 312).rotate(34).skew(12)
@@ -188,7 +188,7 @@ describe('G.js', () => {
describe('width()', () => {
it('gets the width value of the bbox', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = new G()
g.add(new Rect({ width: 100, height: 120, x: 10, y: 20 }))
@@ -200,7 +200,7 @@ describe('G.js', () => {
expect(g.width()).toBe(110)
})
it('sets the width value of the bbox by moving all children', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = new G()
g.add(new Rect({ width: 100, height: 120, x: 10, y: 20 }))
@@ -224,7 +224,7 @@ describe('G.js', () => {
describe('height()', () => {
it('gets the height value of the bbox', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = new G()
g.add(new Rect({ width: 100, height: 120, x: 10, y: 20 }))
@@ -236,7 +236,7 @@ describe('G.js', () => {
expect(g.height()).toBe(140)
})
it('sets the height value of the bbox by moving all children', () => {
- const canvas = makeInstance().addTo('#canvas')
+ const canvas = SVG().addTo(container)
const g = new G()
g.add(new Rect({ width: 100, height: 120, x: 10, y: 20 }))
diff --git a/spec/spec/elements/Svg.js b/spec/spec/elements/Svg.js
new file mode 100644
index 0000000..804bdfd
--- /dev/null
+++ b/spec/spec/elements/Svg.js
@@ -0,0 +1,156 @@
+/* globals describe, expect, it, jasmine, container */
+
+import { Svg, SVG, Defs } from '../../../src/main.js'
+import { ns, xlink, svgjs } from '../../../src/modules/core/namespaces.js'
+import { getWindow } from '../../../src/utils/window.js'
+
+const { any } = jasmine
+
+describe('Svg.js', () => {
+
+ describe('()', () => {
+ it('creates a new object of type Svg', () => {
+ expect(new Svg()).toEqual(any(Svg))
+ })
+
+ it('sets passed attributes on the element', () => {
+ expect(new Svg({ id: 'foo' }).id()).toBe('foo')
+ })
+
+ it('creates namespaces on creation', () => {
+ const svg = new Svg()
+
+ expect(svg.attr('xmlns')).toBe(ns)
+ expect(svg.attr('version')).toBe(1.1)
+ expect(svg.attr('xmlns:xlink')).toBe(xlink)
+ expect(svg.attr('xmlns:svgjs')).toBe(svgjs)
+ })
+ })
+
+ describe('defs()', () => {
+ it('returns the defs if its the root svg', () => {
+ const svg = new Svg()
+ const defs = new Defs().addTo(svg)
+ expect(svg.defs()).toBe(defs)
+ })
+
+ it('returns the defs if its not the root svg', () => {
+ const svg = new Svg()
+ const defs = new Defs().addTo(svg)
+ const nested = new Svg().addTo(svg)
+ expect(nested.defs()).toBe(defs)
+ })
+
+ it('creates the defs if not found', () => {
+ const svg = new SVG()
+
+ expect(svg.findOne('defs')).toBe(null)
+
+ const defs = svg.defs()
+
+ expect(svg.findOne('defs')).toBe(defs)
+ })
+ })
+
+ describe('namespace()', () => {
+ it('returns itself', () => {
+ const svg = SVG('<svg>')
+ expect(svg.namespace()).toBe(svg)
+ })
+
+ it('creates the namespace attributes on the svg', () => {
+ const svg = SVG('<svg>')
+
+ expect(svg.attr('xmlns')).toBe(undefined)
+
+ svg.namespace()
+
+ expect(svg.attr('xmlns')).toBe(ns)
+ expect(svg.attr('version')).toBe(1.1)
+ expect(svg.attr('xmlns:xlink')).toBe(xlink)
+ expect(svg.attr('xmlns:svgjs')).toBe(svgjs)
+ })
+ })
+
+ describe('isRoot()', () => {
+ it('returns true if svg is the root svg', () => {
+ const canvas = SVG().addTo(container)
+ expect(canvas.isRoot()).toBe(true)
+ })
+
+ it('returns true if its detached from the dom', () => {
+ const svg = new Svg()
+ expect(svg.isRoot()).toBe(true)
+ })
+
+ it('returns true if its the root child of the document', () => {
+ // cannot be tested here
+ })
+
+ it('returns false if its the child of a document-fragment', () => {
+ const fragment = getWindow().document.createDocumentFragment()
+ const svg = new Svg().addTo(fragment)
+ expect(svg.isRoot()).toBe(false)
+ })
+
+ it('returns false if its a child of another svg element', () => {
+ const svg = new Svg()
+ const nested = new Svg().addTo(svg)
+ expect(nested.isRoot()).toBe(false)
+ })
+ })
+
+ describe('removeNamespaces()', () => {
+ it('returns itself', () => {
+ const svg = new Svg()
+ expect(svg.removeNamespaces()).toBe(svg)
+ })
+
+ it('removes the namespace attributes from the svg element', () => {
+ const svg = new Svg()
+
+ expect(svg.attr('xmlns')).toBe(ns)
+
+ svg.removeNamespaces()
+
+ expect(svg.attr('xmlns')).toBe(undefined)
+ expect(svg.attr('version')).toBe(undefined)
+ expect(svg.attr('xmlns:xlink')).toBe(undefined)
+ expect(svg.attr('xmlns:svgjs')).toBe(undefined)
+ })
+ })
+
+ describe('root()', () => {
+ it('returns itself if its the root svg', () => {
+ const svg = new Svg()
+ expect(svg.root()).toBe(svg)
+ })
+
+ it('returns the actual root if its not the root svg', () => {
+ const svg = new Svg()
+ const nested = new Svg().addTo(svg)
+ expect(nested.root()).toBe(svg)
+ })
+ })
+
+ describe('Container', () => {
+ describe('nested()', () => {
+ it('creates an svg element in the container', () => {
+ const svg = new Svg()
+ const nested = svg.nested()
+ expect(nested).toEqual(any(Svg))
+ expect(nested.parent()).toBe(svg)
+ })
+
+ it('has no namespaces set', () => {
+ const svg = new Svg()
+ const nested = svg.nested()
+
+ expect(nested.attr('xmlns')).toBe(undefined)
+ expect(nested.attr('version')).toBe(undefined)
+ expect(nested.attr('xmlns:xlink')).toBe(undefined)
+ expect(nested.attr('xmlns:svgjs')).toBe(undefined)
+ })
+ })
+ })
+})
diff --git a/src/elements/Dom.js b/src/elements/Dom.js
index 2e081a8..ef51ad6 100644
--- a/src/elements/Dom.js
+++ b/src/elements/Dom.js
@@ -30,6 +30,11 @@ export default class Dom extends EventTarget {
add (element, i) {
element = makeInstance(element)
+ // If non-root svg nodes are added we have to remove their namespaces
+ if (element.removeNamespaces && this.node instanceof globals.window.SVGElement) {
+ element.removeNamespaces()
+ }
+
if (i == null) {
this.node.appendChild(element.node)
} else if (element.node !== this.node.childNodes[i]) {
@@ -146,6 +151,7 @@ export default class Dom extends EventTarget {
// check for parent
if (!parent.node.parentNode) return null
+ if (parent.node.parentNode.nodeName === '#document' || parent.node.parentNode.nodeName === '#document-fragment') return null
// get parent element
parent = adopt(parent.node.parentNode)
diff --git a/src/elements/Element.js b/src/elements/Element.js
index 56dbcf3..d75db17 100644
--- a/src/elements/Element.js
+++ b/src/elements/Element.js
@@ -106,8 +106,13 @@ export default class Element extends Dom {
const parents = new List()
let parent = this
- while ((parent = parent.parent()) && parent.node !== globals.document) {
+ while (
+ (parent = parent.parent())
+ && parent.node !== globals.document
+ && parent.nodeName !== '#document-fragment') {
+
parents.push(parent)
+
if (parent.node === until.node) {
break
}
diff --git a/src/elements/Svg.js b/src/elements/Svg.js
index 7cec826..6672919 100644
--- a/src/elements/Svg.js
+++ b/src/elements/Svg.js
@@ -16,17 +16,19 @@ export default class Svg extends Container {
this.namespace()
}
- isRoot () {
- return !this.node.parentNode
- || !(this.node.parentNode instanceof globals.window.SVGElement)
- || this.node.parentNode.nodeName === '#document'
+ // Creates and returns defs element
+ defs () {
+ if (!this.isRoot()) return this.root().defs()
+
+ return adopt(this.node.querySelector('defs'))
+ || this.put(new Defs())
}
- // Check if this is a root svg
- // If not, call docs from this element
- root () {
- if (this.isRoot()) return this
- return super.root()
+ isRoot () {
+
+ return !this.node.parentNode
+ || (!(this.node.parentNode instanceof globals.window.SVGElement) && this.node.parentNode.nodeName !== '#document-fragment')
+ // || this.node.parentNode.nodeName === '#document'
}
// Add namespaces
@@ -38,36 +40,19 @@ export default class Svg extends Container {
.attr('xmlns:svgjs', svgjs, xmlns)
}
- // Creates and returns defs element
- defs () {
- if (!this.isRoot()) return this.root().defs()
-
- return adopt(this.node.querySelector('defs'))
- || this.put(new Defs())
+ removeNamespaces () {
+ return this.attr({ xmlns: null, version: null })
+ .attr('xmlns:xlink', null, xmlns)
+ .attr('xmlns:svgjs', null, xmlns)
}
- // custom parent method
- parent (type) {
- if (this.isRoot()) {
- return this.node.parentNode.nodeName === '#document'
- ? null
- : adopt(this.node.parentNode)
- }
-
- return super.parent(type)
+ // Check if this is a root svg
+ // If not, call root() from this element
+ root () {
+ if (this.isRoot()) return this
+ return super.root()
}
- clear () {
- // remove children
- while (this.node.hasChildNodes()) {
- this.node.removeChild(this.node.lastChild)
- }
-
- // remove defs reference
- delete this._defs
-
- return this
- }
}
registerMethods({
diff --git a/svg.js.d.ts b/svg.js.d.ts
index 24cdc0f..72c6d7d 100644
--- a/svg.js.d.ts
+++ b/svg.js.d.ts
@@ -309,6 +309,10 @@ declare module "@svgdotjs/svg.js" {
type ElementAlias = Dom | Svg | Rect | Line | Polygon | Polyline | Ellipse | ClipPath | Use |
Text | Path | TextPath | Circle | G | Gradient | Image | Element
+ type ElementTypeAlias = typeof Dom | typeof Svg | typeof Rect | typeof Line | typeof Polygon |
+ typeof Polyline | typeof Ellipse | typeof ClipPath | typeof Use | typeof Text | typeof Path |
+ typeof TextPath | typeof Circle | typeof G | typeof Gradient | typeof Image | typeof Element
+
type AttributeReference = "href" | "marker-start" | "marker-mid" | "marker-end" | "mask" |
"clip-path" | "filter" | "fill"
@@ -896,41 +900,11 @@ declare module "@svgdotjs/svg.js" {
cancelImmediate(o: object): void
}
- // use with parent query
- type ParentTypeAlias = typeof Svg | typeof G | typeof A;
-
- // use with putIn
- type ParentClassAlias = Svg | G | A;
-
/**
* Just fancy type alias to refer to css query selector.
*/
type QuerySelector = string
- // cannot really avoid using anonymous any string as typescript does not provide
- // runtime type check for example, QuerySelector should contain . or # at least
- // Important: this type alias is provided an overview of how value look as a string
- type ParentQueryAlias = ParentElement | keyof HTMLElementTagNameMap | ParentTypeAlias | QuerySelector
-
- type ParentQueryMapping<T> =
- T extends Tspan | TextPath ? ParentQueryAlias | TextType | ClipPathType | Text | ClipPath | Dom :
- T extends Shape ? ParentQueryAlias | ClipPathType | ClipPath | Dom :
- T extends Element ? ParentQueryAlias | Dom : keyof HTMLElementTagNameMap | Dom
-
- type ParentQueryResultMapping<T> =
- T extends Tspan | TextPath ? ParentClassAlias | Text | ClipPath | Dom :
- T extends Circle ? ParentClassAlias | Text | ClipPath | Mask | Dom :
- T extends Shape ? ParentClassAlias | ClipPath | Dom : ParentClassAlias | Dom
-
- type PutInMapping<T> =
- T extends Svg ? ParentClassAlias | Dom | HTMLElement | string :
- T extends Shape ? ParentClassAlias | ClipPath | string :
- T extends Element ? ParentClassAlias | string : HTMLElement | string
-
- type PutInResultMapping<T> =
- T extends Svg ? ParentTypeAlias | Dom :
- T extends Element ? ParentTypeAlias : Dom
-
class Dom extends EventTarget {
node: HTMLElement | SVGElement;
type: string;
@@ -955,26 +929,16 @@ declare module "@svgdotjs/svg.js" {
last(): Element;
matches(selector: string): boolean;
/**
- * Get the parent of current element. The input query can be given with string, object type or none (undefined).
- * The input is vary based on the implement in hierarchy of SVG.JS element or dom.
- * 1. If The input is a string, the string value must be a valid HTML element tag name or svg tag name. e.g "svg" or "g" or "div"
- * 2. If the given input is an object type then only SVG.JS object type is accept. e.g Dom, Svg or G
- * 3. if the given input query is undefined then the element will return the closest parent in Dom hierarchy
- *
- * For more information see ParentQueryMapping.
- * @param type can be either string, object type or undefined.
+ * Finds the closest ancestor which matches the string or is of passed type. If nothing is passed, the parent is returned
+ * @param type can be either string, svg.js object or undefined.
*/
- parent<T extends this>(type?: ParentQueryMapping<T>): ParentQueryResultMapping<T>;
+ parent(type?: ElementTypeAlias | QuerySelector): Dom | null;
put(element: Element, i?: number): Element;
/**
- * Put the element into the given parent element. The input parent element can be vary base on the class implementation.
- * 1. If the current class is a Dom then parent input is only accept a valid HTML element or a valid string id of HTML element
- * 2. If the current class is an Svg then parent input can only Dom, Svg, G, A, a valid HTML element and a valid string id of HTML element
- *
- * For more information see PutInMapping.
- * @param parent an object of SVG.JS Dom or implement container or a string id or a valid HTML element object.
+ * Put the element into the given parent element and returns the parent element
+ * @param parent The parent in which the current element is inserted
*/
- putIn<T extends this>(parent: PutInMapping<T>): PutInResultMapping<T>;
+ putIn(parent: ElementAlias | Node | QuerySelector): Dom;
remove(): this;
removeElement(element: Element): this;
@@ -1457,6 +1421,8 @@ declare module "@svgdotjs/svg.js" {
ref(x: string | number, y: string | number): this;
update(block: (marker: Marker) => void): this;
toString(): string;
+ orient(orientation: 'auto' | 'auto-start-reverse' | number | Number): this;
+ orient(): string;
}
// mask.js
class Mask extends Container {