]> source.dussan.org Git - svg.js.git/commitdiff
- fixed `put()` which correctly creates an svgjs object from the passed element...
authorUlrich-Matthias Schäfer <ulima.ums@googlemail.com>
Fri, 3 Apr 2020 08:42:19 +0000 (18:42 +1000)
committerUlrich-Matthias Schäfer <ulima.ums@googlemail.com>
Fri, 3 Apr 2020 08:42:19 +0000 (18:42 +1000)
 - 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

CHANGELOG.md
spec/spec/elements/Dom.js
spec/spec/elements/G.js
spec/spec/elements/Svg.js [new file with mode: 0644]
src/elements/Dom.js
src/elements/Element.js
src/elements/Svg.js
svg.js.d.ts

index 320fd0ae1b4887d23c9dbccf4533c1f7752faa88..e39de5ed0f8fb0d4206576516bdbec575874754b 100644 (file)
@@ -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)
index fcdba4ca26cd9e08306db99dbf0522f8b830c22f..8004317597b39ab490647de135b831d647c81955 100644 (file)
@@ -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)
     })
 
index 7e2fab199d87918fba3d53087c386bfce3de15dd..d926256ce8f8e13db9289d284577385d6891b6fc 100644 (file)
@@ -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 (file)
index 0000000..804bdfd
--- /dev/null
@@ -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)
+      })
+    })
+  })
+})
index 2e081a8c59506fca52e545892566ac87e1701a99..ef51ad628dfc5e6d08c5927b3d5000d5f815c318 100644 (file)
@@ -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)
index 56dbcf3b33dbcb9ded942edc05c14a6735f0a8f9..d75db17036d9e25d70668bae67ae5e24848949b0 100644 (file)
@@ -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
       }
index 7cec826188e60dce88c4981cf1b24cfed7602b39..667291978f84544324e293d26096476cd8e4217f 100644 (file)
@@ -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({
index 24cdc0f1b028b5e9bcc27f6ae4cd2129900ba997..72c6d7d56601cf5c44e9af2feab423c3c186c85c 100644 (file)
@@ -309,6 +309,10 @@ declare module "@svgdotjs/svg.js" {
     type ElementAlias = Dom | Svg | Rect | Line | Polygon | Polyline | Ellipse | ClipPath | Use |\r
         Text | Path | TextPath | Circle | G | Gradient | Image | Element\r
 \r
+    type ElementTypeAlias = typeof Dom | typeof Svg | typeof Rect | typeof Line | typeof Polygon |\r
+        typeof Polyline | typeof Ellipse | typeof ClipPath | typeof Use | typeof Text | typeof Path |\r
+        typeof TextPath | typeof Circle | typeof G | typeof Gradient | typeof Image | typeof Element\r
+\r
     type AttributeReference = "href" | "marker-start" | "marker-mid" | "marker-end" | "mask" |\r
         "clip-path" | "filter" | "fill"\r
 \r
@@ -896,41 +900,11 @@ declare module "@svgdotjs/svg.js" {
         cancelImmediate(o: object): void\r
     }\r
 \r
-    // use with parent query\r
-    type ParentTypeAlias = typeof Svg | typeof G | typeof A;\r
-\r
-    // use with putIn\r
-    type ParentClassAlias = Svg | G | A;\r
-\r
     /**\r
      * Just fancy type alias to refer to css query selector.\r
      */\r
     type QuerySelector = string\r
 \r
-    // cannot really avoid using anonymous any string as typescript does not provide\r
-    // runtime type check for example, QuerySelector should contain . or # at least\r
-    // Important: this type alias is provided an overview of how value look as a string\r
-    type ParentQueryAlias = ParentElement | keyof HTMLElementTagNameMap | ParentTypeAlias | QuerySelector\r
-\r
-    type ParentQueryMapping<T> =\r
-        T extends Tspan | TextPath ? ParentQueryAlias | TextType | ClipPathType | Text | ClipPath | Dom :\r
-        T extends Shape ? ParentQueryAlias | ClipPathType | ClipPath | Dom :\r
-        T extends Element ? ParentQueryAlias | Dom : keyof HTMLElementTagNameMap | Dom\r
-\r
-    type ParentQueryResultMapping<T> =\r
-        T extends Tspan | TextPath ? ParentClassAlias | Text | ClipPath | Dom :\r
-        T extends Circle ? ParentClassAlias | Text | ClipPath | Mask | Dom :\r
-        T extends Shape ? ParentClassAlias | ClipPath | Dom : ParentClassAlias | Dom\r
-\r
-    type PutInMapping<T> =\r
-        T extends Svg ? ParentClassAlias | Dom | HTMLElement | string :\r
-        T extends Shape ? ParentClassAlias | ClipPath | string :\r
-        T extends Element ? ParentClassAlias | string : HTMLElement | string\r
-\r
-    type PutInResultMapping<T> =\r
-        T extends Svg ? ParentTypeAlias | Dom :\r
-        T extends Element ? ParentTypeAlias : Dom\r
-\r
     class Dom extends EventTarget {\r
         node: HTMLElement | SVGElement;\r
         type: string;\r
@@ -955,26 +929,16 @@ declare module "@svgdotjs/svg.js" {
         last(): Element;\r
         matches(selector: string): boolean;\r
         /**\r
-         * Get the parent of current element. The input query can be given with string, object type or none (undefined).\r
-         * The input is vary based on the implement in hierarchy of SVG.JS element or dom.\r
-         * 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"\r
-         * 2. If the given input is an object type then only SVG.JS object type is accept. e.g Dom, Svg or G\r
-         * 3. if the given input query is undefined then the element will return the closest parent in Dom hierarchy\r
-         *\r
-         * For more information see ParentQueryMapping.\r
-         * @param type can be either string, object type or undefined.\r
+         * Finds the closest ancestor which matches the string or is of passed type. If nothing is passed, the parent is returned\r
+         * @param type can be either string, svg.js object or undefined.\r
          */\r
-        parent<T extends this>(type?: ParentQueryMapping<T>): ParentQueryResultMapping<T>;\r
+        parent(type?: ElementTypeAlias | QuerySelector): Dom | null;\r
         put(element: Element, i?: number): Element;\r
         /**\r
-         * Put the element into the given parent element. The input parent element can be vary base on the class implementation.\r
-         * 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\r
-         * 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\r
-         *\r
-         * For more information see PutInMapping.\r
-         * @param parent an object of SVG.JS Dom or implement container or a string id or a valid HTML element object.\r
+         * Put the element into the given parent element and returns the parent element\r
+         * @param parent The parent in which the current element is inserted\r
          */\r
-        putIn<T extends this>(parent: PutInMapping<T>): PutInResultMapping<T>;\r
+        putIn(parent: ElementAlias | Node | QuerySelector): Dom;\r
 \r
         remove(): this;\r
         removeElement(element: Element): this;\r
@@ -1457,6 +1421,8 @@ declare module "@svgdotjs/svg.js" {
         ref(x: string | number, y: string | number): this;\r
         update(block: (marker: Marker) => void): this;\r
         toString(): string;\r
+        orient(orientation: 'auto' | 'auto-start-reverse' | number | Number): this;\r
+        orient(): string;\r
     }\r
     // mask.js\r
     class Mask extends Container {\r