diff options
author | Ulrich-Matthias Schäfer <ulima.ums@googlemail.com> | 2020-04-03 18:42:19 +1000 |
---|---|---|
committer | Ulrich-Matthias Schäfer <ulima.ums@googlemail.com> | 2020-04-03 18:42:19 +1000 |
commit | 7547a6a45818bcfefd545da4697196b81bd8da5b (patch) | |
tree | 0f62eb9f98b81c6fe20536d5437c9acf39e08b01 | |
parent | 28e74482b8cc7b7f2e4aca099ceea9f7d7a888d5 (diff) | |
download | svg.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.md | 7 | ||||
-rw-r--r-- | spec/spec/elements/Dom.js | 55 | ||||
-rw-r--r-- | spec/spec/elements/G.js | 32 | ||||
-rw-r--r-- | spec/spec/elements/Svg.js | 156 | ||||
-rw-r--r-- | src/elements/Dom.js | 6 | ||||
-rw-r--r-- | src/elements/Element.js | 7 | ||||
-rw-r--r-- | src/elements/Svg.js | 55 | ||||
-rw-r--r-- | svg.js.d.ts | 58 |
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 {
|