/*!
* svg.js - A lightweight library for manipulating and animating SVG.
* @version 2.6.6
* https://svgdotjs.github.io/
*/;
(function (root, factory) {
/* istanbul ignore next */
if (typeof define === 'function' && define.amd) {
define(function () {
return factory(root, root.document)
})
/* below check fixes #412 */
} else if (typeof exports === 'object' && typeof module !== 'undefined') {
module.exports = root.document ? factory(root, root.document) : function (w) { return factory(w, w.document) }
} else {
root.SVG = factory(root, root.document)
}
}(typeof window !== 'undefined' ? window : this, function (window, document) {
// Find global reference - uses 'this' by default when available,
// falls back to 'window' otherwise (for bundlers like Webpack)
var globalRef = (typeof this !== 'undefined') ? this : window
// The main wrapping element
var SVG = globalRef.SVG = function (element) {
if (SVG.supported) {
element = new SVG.Doc(element)
if (!SVG.parser.draw) { SVG.prepare() }
return element
}
}
// Default namespaces
SVG.ns = 'http://www.w3.org/2000/svg'
SVG.xmlns = 'http://www.w3.org/2000/xmlns/'
SVG.xlink = 'http://www.w3.org/1999/xlink'
SVG.svgjs = 'http://svgjs.dev'
// Svg support test
SVG.supported = (function () {
return true
// !!document.createElementNS &&
// !! document.createElementNS(SVG.ns,'svg').createSVGRect
})()
// Don't bother to continue if SVG is not supported
if (!SVG.supported) return false
// Element id sequence
SVG.did = 1000
// Get next named element id
SVG.eid = function (name) {
return 'Svgjs' + capitalize(name) + (SVG.did++)
}
// Method for element creation
SVG.create = function (name) {
// create element
var element = document.createElementNS(this.ns, name)
// apply unique id
element.setAttribute('id', this.eid(name))
return element
}
// Method for extending objects
SVG.extend = function () {
var modules, methods
// Get list of modules
modules = [].slice.call(arguments)
// Get object with extensions
methods = modules.pop()
for (var i = modules.length - 1; i >= 0; i--) {
if (modules[i]) {
for (var key in methods) { modules[i].prototype[key] = methods[key] }
}
}
// Make sure SVG.Set inherits any newly added methods
if (SVG.Set && SVG.Set.inherit) { SVG.Set.inherit() }
}
// Invent new element
SVG.invent = function (config) {
// Create element initializer
var initializer = typeof config.create === 'function'
? config.create
: function () {
this.constructor.call(this, SVG.create(config.create))
}
// Inherit prototype
if (config.inherit) { initializer.prototype = new config.inherit() }
// Extend with methods
if (config.extend) { SVG.extend(initializer, config.extend) }
// Attach construct method to parent
if (config.construct) { SVG.extend(config.parent || SVG.Container, config.construct) }
return initializer
}
// Adopt existing svg elements
SVG.adopt = function (node) {
// check for presence of node
if (!node) return null
// make sure a node isn't already adopted
if (node.instance) return node.instance
// initialize variables
var element
// adopt with element-specific settings
if (node.nodeName == 'svg') { element = node.parentNode instanceof window.SVGElement ? new SVG.Nested() : new SVG.Doc() } else if (node.nodeName == 'linearGradient') { element = new SVG.Gradient('linear') } else if (node.nodeName == 'radialGradient') { element = new SVG.Gradient('radial') } else if (SVG[capitalize(node.nodeName)]) { element = new SVG[capitalize(node.nodeName)]() } else { element = new SVG.Element(node) }
// ensure references
element.type = node.nodeName
element.node = node
node.instance = element
// SVG.Class specific preparations
if (element instanceof SVG.Doc) { element.namespace().defs() }
// pull svgjs data from the dom (getAttributeNS doesn't work in html5)
element.setData(JSON.parse(node.getAttribute('svgjs:data')) || {})
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.style('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 = {
// Parse unit value
numberAndUnit: /^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i,
// Parse hex value
hex: /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,
// Parse rgb value
rgb: /rgb\((\d+),(\d+),(\d+)\)/,
// Parse reference id
reference: /#([a-z0-9\-_]+)/i,
// splits a transformation chain
transforms: /\)\s*,?\s*/,
// Whitespace
whitespace: /\s/g,
// Test hex value
isHex: /^#[a-f0-9]{3,6}$/i,
// Test rgb value
isRgb: /^rgb\(/,
// Test css declaration
isCss: /[^:]+:[^;]+;?/,
// Test for blank string
isBlank: /^(\s+)?$/,
// Test for numeric string
isNumber: /^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,
// Test for percent value
isPercent: /^-?[\d\.]+%$/,
// Test for image url
isImage: /\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i,
// split at whitespace and comma
delimiter: /[\s,]+/,
// The following regex are used to parse the d attribute of a path
// Matches all hyphens which are not after an exponent
hyphen: /([^e])\-/gi,
// Replaces and tests for all path letters
pathLetters: /[MLHVCSQTAZ]/gi,
// yes we need this one, too
isPathLetter: /[MLHVCSQTAZ]/i,
// matches 0.154.23.45
numbersWithDots: /((\d?\.\d+(?:e[+-]?\d+)?)((?:\.\d+(?:e[+-]?\d+)?)+))+/gi,
// matches .
dots: /\./g
}
SVG.utils = {
// Map function
map: function (array, block) {
var il = array.length,
result = []
for (var i = 0; i < il; i++) { result.push(block(array[i])) }
return result
},
// Filter function
filter: function (array, block) {
var il = array.length,
result = []
for (var i = 0; i < il; i++) {
if (block(array[i])) { result.push(array[i]) }
}
return result
},
filterSVGElements: function (nodes) {
return this.filter(nodes, function (el) { return el instanceof window.SVGElement })
}
}
SVG.defaults = {
// Default attribute values
attrs: {
// fill and stroke
'fill-opacity': 1,
'stroke-opacity': 1,
'stroke-width': 0,
'stroke-linejoin': 'miter',
'stroke-linecap': 'butt',
fill: '#000000',
stroke: '#000000',
opacity: 1,
// position
x: 0,
y: 0,
cx: 0,
cy: 0,
// size
width: 0,
height: 0,
// radius
r: 0,
rx: 0,
ry: 0,
// gradient
offset: 0,
'stop-opacity': 1,
'stop-color': '#000000',
// text
'font-size': 16,
'font-family': 'Helvetica, Arial, sans-serif',
'text-anchor': 'start'
}
}
// Module for color convertions
SVG.Color = function (color) {
var match
// initialize defaults
this.r = 0
this.g = 0
this.b = 0
if (!color) return
// parse color
if (typeof color === 'string') {
if (SVG.regex.isRgb.test(color)) {
// get rgb values
match = SVG.regex.rgb.exec(color.replace(SVG.regex.whitespace, ''))
// parse numeric values
this.r = parseInt(match[1])
this.g = parseInt(match[2])
this.b = parseInt(match[3])
} else if (SVG.regex.isHex.test(color)) {
// get hex values
match = SVG.regex.hex.exec(fullHex(color))
// parse numeric values
this.r = parseInt(match[1], 16)
this.g = parseInt(match[2], 16)
this.b = parseInt(match[3], 16)
}
} else if (typeof color === 'object') {
this.r = color.r
this.g = color.g
this.b = color.b
}
}
SVG.extend(SVG.Color, {
// Default to hex conversion
toString: function () {
return this.toHex()
},
// Build hex value
toHex: function () {
return '#' +
compToHex(this.r) +
compToHex(this.g) +
compToHex(this.b)
},
// Build rgb value
toRgb: function () {
return 'rgb(' + [this.r, this.g, this.b].join() + ')'
},
// Calculate true brightness
brightness: function () {
return (this.r / 255 * 0.30) +
(this.g / 255 * 0.59) +
(this.b / 255 * 0.11)
},
// Make color morphable
morph: function (color) {
this.destination = new SVG.Color(color)
return this
},
// Get morphed color at given position
at: function (pos) {
// make sure a destination is defined
if (!this.destination) return this
// normalise pos
pos = pos < 0 ? 0 : pos > 1 ? 1 : pos
// generate morphed color
return new SVG.Color({
r: ~~(this.r + (this.destination.r - this.r) * pos),
g: ~~(this.g + (this.destination.g - this.g) * pos),
b: ~~(this.b + (this.destination.b - this.b) * pos)
})
}
})
// Testers
// Test if given value is a color string
SVG.Color.test = function (color) {
color += ''
return SVG.regex.isHex.test(color) ||
SVG.regex.isRgb.test(color)
}
// Test if given value is a rgb object
SVG.Color.isRgb = function (color) {
return color && typeof color.r === 'number' &&
typeof color.g === 'number' &&
typeof color.b === 'number'
}
// Test if given value is a color
SVG.Color.isColor = function (color) {
return SVG.Color.isRgb(color) || SVG.Color.test(color)
}
// Module for array conversion
SVG.Array = function (array, fallback) {
array = (array || []).valueOf()
// if array is empty and fallback is provided, use fallback
if (array.length == 0 && fallback) { array = fallback.valueOf() }
// parse array
this.value = this.parse(array)
}
SVG.extend(SVG.Array, {
// Convert array to string
toString: function () {
return this.value.join(' ')
},
// Real value
valueOf: function () {
return this.value
},
// Parse whitespace separated string
parse: function (array) {
array = array.valueOf()
// if already is an array, no need to parse it
if (Array.isArray(array)) return array
return this.split(array)
},
})
// Poly points array
SVG.PointArray = function (array, fallback) {
SVG.Array.call(this, array, fallback || [[0, 0]])
}
// Inherit from SVG.Array
SVG.PointArray.prototype = new SVG.Array()
SVG.PointArray.prototype.constructor = SVG.PointArray
var pathHandlers = {
M: function (c, p, p0) {
p.x = p0.x = c[0]
p.y = p0.y = c[1]
return ['M', p.x, p.y]
},
L: function (c, p) {
p.x = c[0]
p.y = c[1]
return ['L', c[0], c[1]]
},
H: function (c, p) {
p.x = c[0]
return ['H', c[0]]
},
V: function (c, p) {
p.y = c[0]
return ['V', c[0]]
},
C: function (c, p) {
p.x = c[4]
p.y = c[5]
return ['C', c[0], c[1], c[2], c[3], c[4], c[5]]
},
Q: function (c, p) {
p.x = c[2]
p.y = c[3]
return ['Q', c[0], c[1], c[2], c[3]]
},
Z: function (c, p, p0) {
p.x = p0.x
p.y = p0.y
return ['Z']
},
}
var mlhvqtcsa = 'mlhvqtcsaz'.split('')
for (var i = 0, il = mlhvqtcsa.length; i < il; ++i) {
pathHandlers[mlhvqtcsa[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
else if (i == 'A') {
c[5] = c[5] + p.x,
c[6] = c[6] + p.y
} else {
for (var j = 0, jl = c.length; j < jl; ++j) {
c[j] = c[j] + (j % 2 ? p.y : p.x)
}
}
if(pathHandlers && typeof pathHandlers[i] === 'function') {
// this check fixes jest unit tests
return pathHandlers[i](c, p, p0)
}
}
})(mlhvqtcsa[i].toUpperCase())
}
// Path points array
SVG.PathArray = function (array, fallback) {
SVG.Array.call(this, array, fallback || [['M', 0, 0]])
}
// Inherit from SVG.Array
SVG.PathArray.prototype = new SVG.Array()
SVG.PathArray.prototype.constructor = SVG.PathArray
SVG.extend(SVG.PathArray, {
// Convert array to string
toString: function () {
return arrayToString(this.value)
},
// Move path string
move: function (x, y) {
// get bounding box of current situation
var box = this.bbox()
// get relative offset
x -= box.x
y -= box.y
return this
},
// Get morphed path array at given position
at: function (pos) {
// make sure a destination is defined
if (!this.destination) return this
var sourceArray = this.value,
destinationArray = this.destination.value,
array = [], pathArray = new SVG.PathArray(),
il, jl
// Animate has specified in the SVG spec
// See: https://www.w3.org/TR/SVG11/paths.html#PathElement
for (var i = 0, il = sourceArray.length; i < il; i++) {
array[i] = [sourceArray[i][0]]
for (var j = 1, jl = sourceArray[i].length; j < jl; j++) {
array[i][j] = sourceArray[i][j] + (destinationArray[i][j] - sourceArray[i][j]) * pos
}
// For the two flags of the elliptical arc command, the SVG spec say:
// Flags and booleans are interpolated as fractions between zero and one, with any non-zero value considered to be a value of one/true
// Elliptical arc command as an array followed by corresponding indexes:
// ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
// 0 1 2 3 4 5 6 7
if (array[i][0] === 'A') {
array[i][4] = +(array[i][4] != 0)
array[i][5] = +(array[i][5] != 0)
}
}
// Directly modify the value of a path array, this is done this way for performance
pathArray.value = array
return pathArray
},
// Absolutize and parse path to array
parse: function (array) {
// if it's already a patharray, no need to parse it
if (array instanceof SVG.PathArray) return array.valueOf()
// prepare for parsing
var i, x0, y0, s, seg, arr,
x = 0,
y = 0,
paramCnt = { 'M': 2, 'L': 2, 'H': 1, 'V': 1, 'C': 6, 'S': 4, 'Q': 4, 'T': 2, 'A': 7, 'Z': 0 }
if (typeof array === 'string') {
array = array
.replace(SVG.regex.numbersWithDots, pathRegReplace) // convert 45.123.123 to 45.123 .123
.replace(SVG.regex.pathLetters, ' $& ') // put some room between letters and numbers
.replace(SVG.regex.hyphen, '$1 -') // add space before hyphen
.trim() // trim
.split(SVG.regex.delimiter) // split into array
} else {
array = array.reduce(function (prev, curr) {
return [].concat.call(prev, curr)
}, [])
}
// array now is an array containing all parts of a path e.g. ['M', '0', '0', 'L', '30', '30' ...]
var arr = [],
p = new SVG.Point(),
p0 = new SVG.Point(),
index = 0,
len = array.length
do {
// Test if we have a path letter
if (SVG.regex.isPathLetter.test(array[index])) {
s = array[index]
++index
// If last letter was a move command and we got no new, it defaults to [L]ine
} else if (s == 'M') {
s = 'L'
} else if (s == 'm') {
s = 'l'
}
arr.push(pathHandlers[s].call(null,
array.slice(index, (index = index + paramCnt[s.toUpperCase()])).map(parseFloat),
p, p0
)
)
} while (len > index)
return arr
},
// Get bounding box of path
bbox: function () {
if (!SVG.parser.draw) { SVG.prepare() }
SVG.parser.path.setAttribute('d', this.toString())
return SVG.parser.path.getBBox()
}
})
// Module for unit convertions
SVG.Number = SVG.invent({
// Initialize
create: function (value, unit) {
// initialize defaults
this.value = 0
this.unit = unit || ''
// parse value
if (typeof value === 'number') {
// ensure a valid numeric value
this.value = isNaN(value) ? 0 : !isFinite(value) ? (value < 0 ? -3.4e+38 : +3.4e+38) : value
} else if (typeof value === 'string') {
unit = value.match(SVG.regex.numberAndUnit)
if (unit) {
// make value numeric
this.value = parseFloat(unit[1])
// normalize
if (unit[5] == '%') { this.value /= 100 } else if (unit[5] == 's') { this.value *= 1000 }
// store unit
this.unit = unit[5]
}
} else {
if (value instanceof SVG.Number) {
this.value = value.valueOf()
this.unit = value.unit
}
}
},
// Add methods
extend: {
// Stringalize
toString: function () {
return (
this.unit == '%'
? ~~(this.value * 1e8) / 1e6
: this.unit == 's'
? this.value / 1e3
: this.value
) + this.unit
},
toJSON: function () {
return this.toString()
}, // Convert to primitive
valueOf: function () {
return this.value
},
// Add number
plus: function (number) {
number = new SVG.Number(number)
return new SVG.Number(this + number, this.unit || number.unit)
},
// Subtract number
minus: function (number) {
number = new SVG.Number(number)
return new SVG.Number(this - number, this.unit || number.unit)
},
// Multiply number
times: function (number) {
number = new SVG.Number(number)
return new SVG.Number(this * number, this.unit || number.unit)
},
// Divide number
divide: function (number) {
number = new SVG.Number(number)
return new SVG.Number(this / number, this.unit || number.unit)
},
// Convert to different unit
to: function (unit) {
var number = new SVG.Number(this)
if (typeof unit === 'string') { number.unit = unit }
return number
},
// Make number morphable
morph: function (number) {
this.destination = new SVG.Number(number)
if (number.relative) {
this.destination.value += this.value
}
return this
},
// Get morphed number at given position
at: function (pos) {
// Make sure a destination is defined
if (!this.destination) return this
// Generate new morphed number
return new SVG.Number(this.destination)
.minus(this)
.times(pos)
.plus(this)
}
}
})
SVG.Element = SVG.invent({
// Initialize node
create: function (node) {
// make stroke value accessible dynamically
this._stroke = SVG.defaults.attrs.stroke
this._event = null
// initialize data object
this.dom = {}
// create circular reference
if (this.node = node) {
this.type = node.nodeName
this.node.instance = this
// store current attribute value
this._stroke = node.getAttribute('stroke') || this._stroke
}
},
// Add class methods
extend: {
// Move over x-axis
x: function (x) {
return this.attr('x', x)
},
// Move over y-axis
y: function (y) {
return this.attr('y', y)
},
// Move by center over x-axis
cx: function (x) {
return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2)
},
// Move by center over y-axis
cy: function (y) {
return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2)
},
// Move element to given x and y values
move: function (x, y) {
return this.x(x).y(y)
},
// Move element by its center
center: function (x, y) {
return this.cx(x).cy(y)
},
// Set width of element
width: function (width) {
return this.attr('width', width)
},
// Set height of element
height: function (height) {
return this.attr('height', height)
},
// Set element size to given width and height
size: function (width, height) {
var p = proportionalSize(this, width, height)
return this
.width(new SVG.Number(p.width))
.height(new SVG.Number(p.height))
},
// Clone element
clone: function (parent) {
// write dom data to the dom so the clone can pickup the data
this.writeDataToDom()
// clone element and assign new id
var clone = assignNewId(this.node.cloneNode(true))
// insert the clone in the given parent or after myself
if (parent) parent.add(clone)
else this.after(clone)
return clone
},
// Remove element
remove: function () {
if (this.parent()) { this.parent().removeElement(this) }
return this
},
// Replace element
replace: function (element) {
this.after(element).remove()
return element
},
// Add element to given container and return self
addTo: function (parent) {
return parent.put(this)
},
// Add element to given container and return container
putIn: function (parent) {
return parent.add(this)
},
// Get / set id
id: function (id) {
return this.attr('id', id)
},
// Show element
show: function () {
return this.style('display', '')
},
// Hide element
hide: function () {
return this.style('display', 'none')
},
// Is element visible?
visible: function () {
return this.style('display') != 'none'
},
// Return id on string conversion
toString: function () {
return this.attr('id')
},
// Return array of classes on the node
classes: function () {
var attr = this.attr('class')
return attr == null ? [] : attr.trim().split(SVG.regex.delimiter)
},
// Return true if class exists on the node, false otherwise
hasClass: function (name) {
return this.classes().indexOf(name) != -1
},
// Add class to the node
addClass: function (name) {
if (!this.hasClass(name)) {
var array = this.classes()
array.push(name)
this.attr('class', array.join(' '))
}
return this
},
// Remove class from the node
removeClass: function (name) {
if (this.hasClass(name)) {
this.attr('class', this.classes().filter(function (c) {
return c != name
}).join(' '))
}
return this
},
// Toggle the presence of a class on the node
toggleClass: function (name) {
return this.hasClass(name) ? this.removeClass(name) : this.addClass(name)
},
// Get referenced element form attribute value
reference: function (attr) {
return SVG.get(this.attr(attr))
},
// Returns the parent element instance
parent: function (type) {
var parent = this
// check for parent
if (!parent.node.parentNode) return null
// get parent element
parent = SVG.adopt(parent.node.parentNode)
if (!type) return parent
// loop trough ancestors if type is given
while (parent && parent.node instanceof window.SVGElement) {
if (typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent
if (!parent.node.parentNode || parent.node.parentNode.nodeName == '#document') return null // #759, #720
parent = SVG.adopt(parent.node.parentNode)
}
},
// Get parent document
doc: function () {
return this instanceof SVG.Doc ? this : this.parent(SVG.Doc)
},
// return array of all ancestors of given type up to the root svg
parents: function (type) {
var parents = [], parent = this
do {
parent = parent.parent(type)
if (!parent || !parent.node) break
parents.push(parent)
} while (parent.parent)
return parents
},
// matches the element vs a css selector
matches: function (selector) {
return matches(this.node, selector)
},
// Returns the svg node to call native svg methods on it
native: function () {
return this.node
},
// Import raw svg
svg: function (svg) {
// create temporary holder
var well = document.createElement('svg')
// act as a setter if svg is given
if (svg && this instanceof SVG.Parent) {
// dump raw svg
well.innerHTML = ''
// transplant nodes
for (var i = 0, il = well.firstChild.childNodes.length; i < il; i++) { this.node.appendChild(well.firstChild.firstChild) }
// otherwise act as a getter
} else {
// create a wrapping svg element in case of partial content
well.appendChild(svg = document.createElement('svg'))
// write svgjs data to the dom
this.writeDataToDom()
// insert a copy of this node
svg.appendChild(this.node.cloneNode(true))
// return target element
return well.innerHTML.replace(/^