aboutsummaryrefslogtreecommitdiff
path: root/node_modules/svgo/plugins
diff options
context:
space:
mode:
authorruki <waruqi@gmail.com>2018-11-08 00:38:48 +0800
committerruki <waruqi@gmail.com>2018-11-07 21:53:09 +0800
commit26105034da4fcce7ac883c899d781f016559310d (patch)
treec459a5dc4e3aa0972d9919033ece511ce76dd129 /node_modules/svgo/plugins
parent2c77f00f1a7ecb6c8192f9c16d3b2001b254a107 (diff)
downloadxmake-docs-26105034da4fcce7ac883c899d781f016559310d.tar.gz
xmake-docs-26105034da4fcce7ac883c899d781f016559310d.zip
switch to vuepress
Diffstat (limited to 'node_modules/svgo/plugins')
-rw-r--r--node_modules/svgo/plugins/_collections.js2529
-rw-r--r--node_modules/svgo/plugins/_path.js962
-rw-r--r--node_modules/svgo/plugins/_transforms.js308
-rw-r--r--node_modules/svgo/plugins/addAttributesToSVGElement.js57
-rw-r--r--node_modules/svgo/plugins/addClassesToSVGElement.js64
-rw-r--r--node_modules/svgo/plugins/cleanupAttrs.js56
-rw-r--r--node_modules/svgo/plugins/cleanupEnableBackground.js84
-rw-r--r--node_modules/svgo/plugins/cleanupIDs.js208
-rw-r--r--node_modules/svgo/plugins/cleanupListOfValues.js141
-rw-r--r--node_modules/svgo/plugins/cleanupNumericValues.js76
-rw-r--r--node_modules/svgo/plugins/collapseGroups.js91
-rw-r--r--node_modules/svgo/plugins/convertColors.js127
-rw-r--r--node_modules/svgo/plugins/convertPathData.js957
-rw-r--r--node_modules/svgo/plugins/convertShapeToPath.js103
-rw-r--r--node_modules/svgo/plugins/convertStyleToAttrs.js117
-rw-r--r--node_modules/svgo/plugins/convertTransform.js364
-rw-r--r--node_modules/svgo/plugins/mergePaths.js71
-rw-r--r--node_modules/svgo/plugins/minifyStyles.js45
-rw-r--r--node_modules/svgo/plugins/moveElemsAttrsToGroup.js126
-rw-r--r--node_modules/svgo/plugins/moveGroupAttrsToElems.js63
-rw-r--r--node_modules/svgo/plugins/removeAttrs.js119
-rw-r--r--node_modules/svgo/plugins/removeComments.js27
-rw-r--r--node_modules/svgo/plugins/removeDesc.js32
-rw-r--r--node_modules/svgo/plugins/removeDimensions.js32
-rw-r--r--node_modules/svgo/plugins/removeDoctype.js40
-rw-r--r--node_modules/svgo/plugins/removeEditorsNSData.js65
-rw-r--r--node_modules/svgo/plugins/removeElementsByAttr.js80
-rw-r--r--node_modules/svgo/plugins/removeEmptyAttrs.js29
-rw-r--r--node_modules/svgo/plugins/removeEmptyContainers.js32
-rw-r--r--node_modules/svgo/plugins/removeEmptyText.js59
-rw-r--r--node_modules/svgo/plugins/removeHiddenElems.js218
-rw-r--r--node_modules/svgo/plugins/removeMetadata.js23
-rw-r--r--node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js40
-rw-r--r--node_modules/svgo/plugins/removeRasterImages.js28
-rw-r--r--node_modules/svgo/plugins/removeStyleElement.js23
-rw-r--r--node_modules/svgo/plugins/removeTitle.js24
-rw-r--r--node_modules/svgo/plugins/removeUnknownsAndDefaults.js143
-rw-r--r--node_modules/svgo/plugins/removeUnusedNS.js107
-rw-r--r--node_modules/svgo/plugins/removeUselessDefs.js51
-rw-r--r--node_modules/svgo/plugins/removeUselessStrokeAndFill.js92
-rw-r--r--node_modules/svgo/plugins/removeViewBox.js49
-rw-r--r--node_modules/svgo/plugins/removeXMLNS.js28
-rw-r--r--node_modules/svgo/plugins/removeXMLProcInst.js24
-rw-r--r--node_modules/svgo/plugins/sortAttrs.js81
-rw-r--r--node_modules/svgo/plugins/transformsWithOnePath.js327
45 files changed, 8322 insertions, 0 deletions
diff --git a/node_modules/svgo/plugins/_collections.js b/node_modules/svgo/plugins/_collections.js
new file mode 100644
index 00000000..8ff3d1fb
--- /dev/null
+++ b/node_modules/svgo/plugins/_collections.js
@@ -0,0 +1,2529 @@
+'use strict';
+
+// http://www.w3.org/TR/SVG/intro.html#Definitions
+exports.elemsGroups = {
+ animation: ['animate', 'animateColor', 'animateMotion', 'animateTransform', 'set'],
+ descriptive: ['desc', 'metadata', 'title'],
+ shape: ['circle', 'ellipse', 'line', 'path', 'polygon', 'polyline', 'rect'],
+ structural: ['defs', 'g', 'svg', 'symbol', 'use'],
+ paintServer: ['solidColor', 'linearGradient', 'radialGradient', 'meshGradient', 'pattern', 'hatch'],
+ nonRendering: ['linearGradient', 'radialGradient', 'pattern', 'clipPath', 'mask', 'marker', 'symbol', 'filter', 'solidColor'],
+ container: ['a', 'defs', 'g', 'marker', 'mask', 'missing-glyph', 'pattern', 'svg', 'switch', 'symbol', 'foreignObject'],
+ textContent: ['altGlyph', 'altGlyphDef', 'altGlyphItem', 'glyph', 'glyphRef', 'textPath', 'text', 'tref', 'tspan'],
+ textContentChild: ['altGlyph', 'textPath', 'tref', 'tspan'],
+ lightSource: ['feDiffuseLighting', 'feSpecularLighting', 'feDistantLight', 'fePointLight', 'feSpotLight'],
+ filterPrimitive: ['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feFlood', 'feGaussianBlur', 'feImage', 'feMerge', 'feMorphology', 'feOffset', 'feSpecularLighting', 'feTile', 'feTurbulence']
+};
+
+exports.pathElems = ['path', 'glyph', 'missing-glyph'];
+
+// http://www.w3.org/TR/SVG/intro.html#Definitions
+exports.attrsGroups = {
+ animationAddition: ['additive', 'accumulate'],
+ animationAttributeTarget: ['attributeType', 'attributeName'],
+ animationEvent: ['onbegin', 'onend', 'onrepeat', 'onload'],
+ animationTiming: ['begin', 'dur', 'end', 'min', 'max', 'restart', 'repeatCount', 'repeatDur', 'fill'],
+ animationValue: ['calcMode', 'values', 'keyTimes', 'keySplines', 'from', 'to', 'by'],
+ conditionalProcessing: ['requiredFeatures', 'requiredExtensions', 'systemLanguage'],
+ core: ['id', 'tabindex', 'xml:base', 'xml:lang', 'xml:space'],
+ graphicalEvent: ['onfocusin', 'onfocusout', 'onactivate', 'onclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmousemove', 'onmouseout', 'onload'],
+ presentation: [
+ 'alignment-baseline',
+ 'baseline-shift',
+ 'buffered-rendering',
+ 'clip',
+ 'clip-path',
+ 'clip-rule',
+ 'color',
+ 'color-interpolation',
+ 'color-interpolation-filters',
+ 'color-profile',
+ 'color-rendering',
+ 'cursor',
+ 'direction',
+ 'display',
+ 'dominant-baseline',
+ 'enable-background',
+ 'fill',
+ 'fill-opacity',
+ 'fill-rule',
+ 'filter',
+ 'flood-color',
+ 'flood-opacity',
+ 'font-family',
+ 'font-size',
+ 'font-size-adjust',
+ 'font-stretch',
+ 'font-style',
+ 'font-variant',
+ 'font-weight',
+ 'glyph-orientation-horizontal',
+ 'glyph-orientation-vertical',
+ 'image-rendering',
+ 'kerning',
+ 'letter-spacing',
+ 'lighting-color',
+ 'marker-end',
+ 'marker-mid',
+ 'marker-start',
+ 'mask',
+ 'opacity',
+ 'overflow',
+ 'pointer-events',
+ 'shape-rendering',
+ 'solid-color',
+ 'solid-opacity',
+ 'stop-color',
+ 'stop-opacity',
+ 'stroke',
+ 'stroke-dasharray',
+ 'stroke-dashoffset',
+ 'stroke-linecap',
+ 'stroke-linejoin',
+ 'stroke-miterlimit',
+ 'stroke-opacity',
+ 'stroke-width',
+ 'paint-order',
+ 'text-anchor',
+ 'text-decoration',
+ 'text-overflow',
+ 'white-space',
+ 'text-rendering',
+ 'unicode-bidi',
+ 'vector-effect',
+ 'viewport-fill',
+ 'viewport-fill-opacity',
+ 'visibility',
+ 'white-space',
+ 'word-spacing',
+ 'writing-mode'
+ ],
+ xlink: ['xlink:href', 'xlink:show', 'xlink:actuate', 'xlink:type', 'xlink:role', 'xlink:arcrole', 'xlink:title'],
+ documentEvent: ['onunload', 'onabort', 'onerror', 'onresize', 'onscroll', 'onzoom'],
+ filterPrimitive: ['x', 'y', 'width', 'height', 'result'],
+ transferFunction: ['type', 'tableValues', 'slope', 'intercept', 'amplitude', 'exponent', 'offset']
+};
+
+exports.attrsGroupsDefaults = {
+ core: {'xml:space': 'preserve'},
+ filterPrimitive: {x: '0', y: '0', width: '100%', height: '100%'},
+ presentation: {
+ clip: 'auto',
+ 'clip-path': 'none',
+ 'clip-rule': 'nonzero',
+ mask: 'none',
+ opacity: '1',
+ 'solid-color': '#000',
+ 'solid-opacity': '1',
+ 'stop-color': '#000',
+ 'stop-opacity': '1',
+ 'fill-opacity': '1',
+ 'fill-rule': 'nonzero',
+ fill: '#000',
+ stroke: 'none',
+ 'stroke-width': '1',
+ 'stroke-linecap': 'butt',
+ 'stroke-linejoin': 'miter',
+ 'stroke-miterlimit': '4',
+ 'stroke-dasharray': 'none',
+ 'stroke-dashoffset': '0',
+ 'stroke-opacity': '1',
+ 'paint-order': 'normal',
+ 'vector-effect': 'none',
+ 'viewport-fill': 'none',
+ 'viewport-fill-opacity': '1',
+ display: 'inline',
+ visibility: 'visible',
+ 'marker-start': 'none',
+ 'marker-mid': 'none',
+ 'marker-end': 'none',
+ 'color-interpolation': 'sRGB',
+ 'color-interpolation-filters': 'linearRGB',
+ 'color-rendering': 'auto',
+ 'shape-rendering': 'auto',
+ 'text-rendering': 'auto',
+ 'image-rendering': 'auto',
+ 'buffered-rendering': 'auto',
+ 'font-style': 'normal',
+ 'font-variant': 'normal',
+ 'font-weight': 'normal',
+ 'font-stretch': 'normal',
+ 'font-size': 'medium',
+ 'font-size-adjust': 'none',
+ kerning: 'auto',
+ 'letter-spacing': 'normal',
+ 'word-spacing': 'normal',
+ 'text-decoration': 'none',
+ 'text-anchor': 'start',
+ 'text-overflow': 'clip',
+ 'writing-mode': 'lr-tb',
+ 'glyph-orientation-vertical': 'auto',
+ 'glyph-orientation-horizontal': '0deg',
+ direction: 'ltr',
+ 'unicode-bidi': 'normal',
+ 'dominant-baseline': 'auto',
+ 'alignment-baseline': 'baseline',
+ 'baseline-shift': 'baseline'
+ },
+ transferFunction: {slope: '1', intercept: '0', amplitude: '1', exponent: '1', offset: '0'}
+};
+
+// http://www.w3.org/TR/SVG/eltindex.html
+exports.elems = {
+ a: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ 'xlink'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'target'
+ ],
+ defaults: {
+ target: '_self'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer'
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view'
+ ]
+ },
+ altGlyph: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ 'xlink'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'x',
+ 'y',
+ 'dx',
+ 'dy',
+ 'glyphRef',
+ 'format',
+ 'rotate'
+ ]
+ },
+ altGlyphDef: {
+ attrsGroups: [
+ 'core'
+ ],
+ content: [
+ 'glyphRef'
+ ]
+ },
+ altGlyphItem: {
+ attrsGroups: [
+ 'core'
+ ],
+ content: [
+ 'glyphRef',
+ 'altGlyphItem'
+ ]
+ },
+ animate: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'animationAddition',
+ 'animationAttributeTarget',
+ 'animationEvent',
+ 'animationTiming',
+ 'animationValue',
+ 'presentation',
+ 'xlink'
+ ],
+ attrs: [
+ 'externalResourcesRequired'
+ ],
+ contentGroups: [
+ 'descriptive'
+ ]
+ },
+ animateColor: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'animationEvent',
+ 'xlink',
+ 'animationAttributeTarget',
+ 'animationTiming',
+ 'animationValue',
+ 'animationAddition',
+ 'presentation'
+ ],
+ attrs: [
+ 'externalResourcesRequired'
+ ],
+ contentGroups: [
+ 'descriptive'
+ ]
+ },
+ animateMotion: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'animationEvent',
+ 'xlink',
+ 'animationTiming',
+ 'animationValue',
+ 'animationAddition'
+ ],
+ attrs: [
+ 'externalResourcesRequired',
+ 'path',
+ 'keyPoints',
+ 'rotate',
+ 'origin'
+ ],
+ defaults: {
+ 'rotate': '0'
+ },
+ contentGroups: [
+ 'descriptive'
+ ],
+ content: [
+ 'mpath'
+ ]
+ },
+ animateTransform: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'animationEvent',
+ 'xlink',
+ 'animationAttributeTarget',
+ 'animationTiming',
+ 'animationValue',
+ 'animationAddition'
+ ],
+ attrs: [
+ 'externalResourcesRequired',
+ 'type'
+ ],
+ contentGroups: [
+ 'descriptive'
+ ]
+ },
+ circle: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'cx',
+ 'cy',
+ 'r'
+ ],
+ defaults: {
+ cx: '0',
+ cy: '0'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive'
+ ]
+ },
+ clipPath: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'clipPathUnits'
+ ],
+ defaults: {
+ clipPathUnits: 'userSpaceOnUse'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape'
+ ],
+ content: [
+ 'text',
+ 'use'
+ ]
+ },
+ 'color-profile': {
+ attrsGroups: [
+ 'core',
+ 'xlink'
+ ],
+ attrs: [
+ 'local',
+ 'name',
+ 'rendering-intent'
+ ],
+ defaults: {
+ name: 'sRGB',
+ 'rendering-intent': 'auto'
+ },
+ contentGroups: [
+ 'descriptive'
+ ]
+ },
+ cursor: {
+ attrsGroups: [
+ 'core',
+ 'conditionalProcessing',
+ 'xlink'
+ ],
+ attrs: [
+ 'externalResourcesRequired',
+ 'x',
+ 'y'
+ ],
+ defaults: {
+ x: '0',
+ y: '0'
+ },
+ contentGroups: [
+ 'descriptive'
+ ]
+ },
+ defs: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform'
+ ],
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer'
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view'
+ ]
+ },
+ desc: {
+ attrsGroups: [
+ 'core'
+ ],
+ attrs: [
+ 'class',
+ 'style'
+ ]
+ },
+ ellipse: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'cx',
+ 'cy',
+ 'rx',
+ 'ry'
+ ],
+ defaults: {
+ cx: '0',
+ cy: '0'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive'
+ ]
+ },
+ feBlend: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ // TODO: in - 'If no value is provided and this is the first filter primitive,
+ // then this filter primitive will use SourceGraphic as its input'
+ 'in',
+ 'in2',
+ 'mode'
+ ],
+ defaults: {
+ mode: 'normal'
+ },
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ feColorMatrix: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'in',
+ 'type',
+ 'values'
+ ],
+ defaults: {
+ type: 'matrix'
+ },
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ feComponentTransfer: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'in'
+ ],
+ content: [
+ 'feFuncA',
+ 'feFuncB',
+ 'feFuncG',
+ 'feFuncR'
+ ]
+ },
+ feComposite: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'in',
+ 'in2',
+ 'operator',
+ 'k1',
+ 'k2',
+ 'k3',
+ 'k4'
+ ],
+ defaults: {
+ operator: 'over',
+ k1: '0',
+ k2: '0',
+ k3: '0',
+ k4: '0'
+ },
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ feConvolveMatrix: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'in',
+ 'order',
+ 'kernelMatrix',
+ // TODO: divisor - 'The default value is the sum of all values in kernelMatrix,
+ // with the exception that if the sum is zero, then the divisor is set to 1'
+ 'divisor',
+ 'bias',
+ // TODO: targetX - 'By default, the convolution matrix is centered in X over each
+ // pixel of the input image (i.e., targetX = floor ( orderX / 2 ))'
+ 'targetX',
+ 'targetY',
+ 'edgeMode',
+ // TODO: kernelUnitLength - 'The first number is the <dx> value. The second number
+ // is the <dy> value. If the <dy> value is not specified, it defaults to the same value as <dx>'
+ 'kernelUnitLength',
+ 'preserveAlpha'
+ ],
+ defaults: {
+ order: '3',
+ bias: '0',
+ edgeMode: 'duplicate',
+ preserveAlpha: 'false'
+ },
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ feDiffuseLighting: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'in',
+ 'surfaceScale',
+ 'diffuseConstant',
+ 'kernelUnitLength'
+ ],
+ defaults: {
+ surfaceScale: '1',
+ diffuseConstant: '1'
+ },
+ contentGroups: [
+ 'descriptive'
+ ],
+ content: [
+ // TODO: 'exactly one light source element, in any order'
+ 'feDistantLight',
+ 'fePointLight',
+ 'feSpotLight'
+ ]
+ },
+ feDisplacementMap: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'in',
+ 'in2',
+ 'scale',
+ 'xChannelSelector',
+ 'yChannelSelector'
+ ],
+ defaults: {
+ scale: '0',
+ xChannelSelector: 'A',
+ yChannelSelector: 'A'
+ },
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ feDistantLight: {
+ attrsGroups: [
+ 'core'
+ ],
+ attrs: [
+ 'azimuth',
+ 'elevation'
+ ],
+ defaults: {
+ azimuth: '0',
+ elevation: '0'
+ },
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ feFlood: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style'
+ ],
+ content: [
+ 'animate',
+ 'animateColor',
+ 'set'
+ ]
+ },
+ feFuncA: {
+ attrsGroups: [
+ 'core',
+ 'transferFunction'
+ ],
+ content: [
+ 'set',
+ 'animate'
+ ]
+ },
+ feFuncB: {
+ attrsGroups: [
+ 'core',
+ 'transferFunction'
+ ],
+ content: [
+ 'set',
+ 'animate'
+ ]
+ },
+ feFuncG: {
+ attrsGroups: [
+ 'core',
+ 'transferFunction'
+ ],
+ content: [
+ 'set',
+ 'animate'
+ ]
+ },
+ feFuncR: {
+ attrsGroups: [
+ 'core',
+ 'transferFunction'
+ ],
+ content: [
+ 'set',
+ 'animate'
+ ]
+ },
+ feGaussianBlur: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'in',
+ 'stdDeviation'
+ ],
+ defaults: {
+ stdDeviation: '0'
+ },
+ content: [
+ 'set',
+ 'animate'
+ ]
+ },
+ feImage: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive',
+ 'xlink'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'preserveAspectRatio',
+ 'xlink:href'
+ ],
+ defaults: {
+ preserveAspectRatio: 'xMidYMid meet'
+ },
+ content: [
+ 'animate',
+ 'animateTransform',
+ 'set'
+ ]
+ },
+ feMerge: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style'
+ ],
+ content: [
+ 'feMergeNode'
+ ]
+ },
+ feMergeNode: {
+ attrsGroups: [
+ 'core'
+ ],
+ attrs: [
+ 'in'
+ ],
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ feMorphology: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'in',
+ 'operator',
+ 'radius'
+ ],
+ defaults: {
+ operator: 'erode',
+ radius: '0'
+ },
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ feOffset: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'in',
+ 'dx',
+ 'dy'
+ ],
+ defaults: {
+ dx: '0',
+ dy: '0'
+ },
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ fePointLight: {
+ attrsGroups: [
+ 'core'
+ ],
+ attrs: [
+ 'x',
+ 'y',
+ 'z'
+ ],
+ defaults: {
+ x: '0',
+ y: '0',
+ z: '0'
+ },
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ feSpecularLighting: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'in',
+ 'surfaceScale',
+ 'specularConstant',
+ 'specularExponent',
+ 'kernelUnitLength'
+ ],
+ defaults: {
+ surfaceScale: '1',
+ specularConstant: '1',
+ specularExponent: '1'
+ },
+ contentGroups: [
+ 'descriptive',
+ // TODO: exactly one 'light source element'
+ 'lightSource'
+ ]
+ },
+ feSpotLight: {
+ attrsGroups: [
+ 'core'
+ ],
+ attrs: [
+ 'x',
+ 'y',
+ 'z',
+ 'pointsAtX',
+ 'pointsAtY',
+ 'pointsAtZ',
+ 'specularExponent',
+ 'limitingConeAngle'
+ ],
+ defaults: {
+ x: '0',
+ y: '0',
+ z: '0',
+ pointsAtX: '0',
+ pointsAtY: '0',
+ pointsAtZ: '0',
+ specularExponent: '1'
+ },
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ feTile: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'in'
+ ],
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ feTurbulence: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'filterPrimitive'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'baseFrequency',
+ 'numOctaves',
+ 'seed',
+ 'stitchTiles',
+ 'type'
+ ],
+ defaults: {
+ baseFrequency: '0',
+ numOctaves: '1',
+ seed: '0',
+ stitchTiles: 'noStitch',
+ type: 'turbulence'
+ },
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ filter: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'xlink'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'filterRes',
+ 'filterUnits',
+ 'primitiveUnits',
+ 'xlink:href'
+ ],
+ defaults: {
+ primitiveUnits: 'userSpaceOnUse',
+ x: '-10%',
+ y: '-10%',
+ width: '120%',
+ height: '120%'
+ },
+ contentGroups: [
+ 'descriptive',
+ 'filterPrimitive'
+ ],
+ content: [
+ 'animate',
+ 'set'
+ ]
+ },
+ font: {
+ attrsGroups: [
+ 'core',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'horiz-origin-x',
+ 'horiz-origin-y',
+ 'horiz-adv-x',
+ 'vert-origin-x',
+ 'vert-origin-y',
+ 'vert-adv-y'
+ ],
+ defaults: {
+ 'horiz-origin-x': '0',
+ 'horiz-origin-y': '0'
+ },
+ contentGroups: [
+ 'descriptive'
+ ],
+ content: [
+ 'font-face',
+ 'glyph',
+ 'hkern',
+ 'missing-glyph',
+ 'vkern'
+ ]
+ },
+ 'font-face': {
+ attrsGroups: [
+ 'core'
+ ],
+ attrs: [
+ 'font-family',
+ 'font-style',
+ 'font-variant',
+ 'font-weight',
+ 'font-stretch',
+ 'font-size',
+ 'unicode-range',
+ 'units-per-em',
+ 'panose-1',
+ 'stemv',
+ 'stemh',
+ 'slope',
+ 'cap-height',
+ 'x-height',
+ 'accent-height',
+ 'ascent',
+ 'descent',
+ 'widths',
+ 'bbox',
+ 'ideographic',
+ 'alphabetic',
+ 'mathematical',
+ 'hanging',
+ 'v-ideographic',
+ 'v-alphabetic',
+ 'v-mathematical',
+ 'v-hanging',
+ 'underline-position',
+ 'underline-thickness',
+ 'strikethrough-position',
+ 'strikethrough-thickness',
+ 'overline-position',
+ 'overline-thickness'
+ ],
+ defaults: {
+ 'font-style': 'all',
+ 'font-variant': 'normal',
+ 'font-weight': 'all',
+ 'font-stretch': 'normal',
+ 'unicode-range': 'U+0-10FFFF',
+ 'units-per-em': '1000',
+ 'panose-1': '0 0 0 0 0 0 0 0 0 0',
+ 'slope': '0'
+ },
+ contentGroups: [
+ 'descriptive'
+ ],
+ content: [
+ // TODO: "at most one 'font-face-src' element"
+ 'font-face-src'
+ ]
+ },
+ // TODO: empty content
+ 'font-face-format': {
+ attrsGroups: [
+ 'core'
+ ],
+ attrs: [
+ 'string'
+ ]
+ },
+ 'font-face-name': {
+ attrsGroups: [
+ 'core'
+ ],
+ attrs: [
+ 'name'
+ ]
+ },
+ 'font-face-src': {
+ attrsGroups: [
+ 'core'
+ ],
+ content: [
+ 'font-face-name',
+ 'font-face-uri'
+ ]
+ },
+ 'font-face-uri': {
+ attrsGroups: [
+ 'core',
+ 'xlink'
+ ],
+ attrs: [
+ 'xlink:href'
+ ],
+ content: [
+ 'font-face-format'
+ ]
+ },
+ foreignObject: {
+ attrsGroups: [
+ 'core',
+ 'conditionalProcessing',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'x',
+ 'y',
+ 'width',
+ 'height'
+ ],
+ defaults: {
+ x: 0,
+ y: 0
+ }
+ },
+ g: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform'
+ ],
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer'
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view'
+ ]
+ },
+ glyph: {
+ attrsGroups: [
+ 'core',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'd',
+ 'horiz-adv-x',
+ 'vert-origin-x',
+ 'vert-origin-y',
+ 'vert-adv-y',
+ 'unicode',
+ 'glyph-name',
+ 'orientation',
+ 'arabic-form',
+ 'lang'
+ ],
+ defaults: {
+ 'arabic-form': 'initial'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer'
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view'
+ ],
+ },
+ glyphRef: {
+ attrsGroups: [
+ 'core',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'd',
+ 'horiz-adv-x',
+ 'vert-origin-x',
+ 'vert-origin-y',
+ 'vert-adv-y'
+ ],
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer'
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view'
+ ]
+ },
+ hatch: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'xlink'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'x',
+ 'y',
+ 'pitch',
+ 'rotate',
+ 'hatchUnits',
+ 'hatchContentUnits',
+ 'transform'
+ ],
+ defaults: {
+ hatchUnits: 'objectBoundingBox',
+ hatchContentUnits: 'userSpaceOnUse',
+ x: '0',
+ y: '0',
+ pitch: '0',
+ rotate: '0'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive'
+ ],
+ content: [
+ 'hatchPath'
+ ]
+ },
+ hatchPath: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'xlink'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'd',
+ 'offset'
+ ],
+ defaults: {
+ offset: '0'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive'
+ ]
+ },
+ hkern: {
+ attrsGroups: [
+ 'core'
+ ],
+ attrs: [
+ 'u1',
+ 'g1',
+ 'u2',
+ 'g2',
+ 'k'
+ ]
+ },
+ image: {
+ attrsGroups: [
+ 'core',
+ 'conditionalProcessing',
+ 'graphicalEvent',
+ 'xlink',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'preserveAspectRatio',
+ 'transform',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'xlink:href'
+ ],
+ defaults: {
+ x: '0',
+ y: '0',
+ preserveAspectRatio: 'xMidYMid meet'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive'
+ ]
+ },
+ line: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'x1',
+ 'y1',
+ 'x2',
+ 'y2'
+ ],
+ defaults: {
+ x1: '0',
+ y1: '0',
+ x2: '0',
+ y2: '0'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive'
+ ]
+ },
+ linearGradient: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'xlink'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'x1',
+ 'y1',
+ 'x2',
+ 'y2',
+ 'gradientUnits',
+ 'gradientTransform',
+ 'spreadMethod',
+ 'xlink:href'
+ ],
+ defaults: {
+ x1: '0',
+ y1: '0',
+ x2: '100%',
+ y2: '0',
+ spreadMethod: 'pad'
+ },
+ contentGroups: [
+ 'descriptive'
+ ],
+ content: [
+ 'animate',
+ 'animateTransform',
+ 'set',
+ 'stop'
+ ]
+ },
+ marker: {
+ attrsGroups: [
+ 'core',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'viewBox',
+ 'preserveAspectRatio',
+ 'refX',
+ 'refY',
+ 'markerUnits',
+ 'markerWidth',
+ 'markerHeight',
+ 'orient'
+ ],
+ defaults: {
+ markerUnits: 'strokeWidth',
+ refX: '0',
+ refY: '0',
+ markerWidth: '3',
+ markerHeight: '3'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer'
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view'
+ ]
+ },
+ mask: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'maskUnits',
+ 'maskContentUnits'
+ ],
+ defaults: {
+ maskUnits: 'objectBoundingBox',
+ maskContentUnits: 'userSpaceOnUse',
+ x: '-10%',
+ y: '-10%',
+ width: '120%',
+ height: '120%'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer'
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view'
+ ]
+ },
+ metadata: {
+ attrsGroups: [
+ 'core'
+ ]
+ },
+ 'missing-glyph': {
+ attrsGroups: [
+ 'core',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'd',
+ 'horiz-adv-x',
+ 'vert-origin-x',
+ 'vert-origin-y',
+ 'vert-adv-y'
+ ],
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer'
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view'
+ ]
+ },
+ mpath: {
+ attrsGroups: [
+ 'core',
+ 'xlink'
+ ],
+ attrs: [
+ 'externalResourcesRequired',
+ 'xlink:href'
+ ],
+ contentGroups: [
+ 'descriptive'
+ ]
+ },
+ path: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'd',
+ 'pathLength'
+ ],
+ contentGroups: [
+ 'animation',
+ 'descriptive'
+ ]
+ },
+ pattern: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'presentation',
+ 'xlink'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'viewBox',
+ 'preserveAspectRatio',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'patternUnits',
+ 'patternContentUnits',
+ 'patternTransform',
+ 'xlink:href'
+ ],
+ defaults: {
+ patternUnits: 'objectBoundingBox',
+ patternContentUnits: 'userSpaceOnUse',
+ x: '0',
+ y: '0',
+ width: '0',
+ height: '0',
+ preserveAspectRatio: 'xMidYMid meet'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'paintServer',
+ 'shape',
+ 'structural'
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view'
+ ]
+ },
+ polygon: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'points'
+ ],
+ contentGroups: [
+ 'animation',
+ 'descriptive'
+ ]
+ },
+ polyline: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'points'
+ ],
+ contentGroups: [
+ 'animation',
+ 'descriptive'
+ ]
+ },
+ radialGradient: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'xlink'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'cx',
+ 'cy',
+ 'r',
+ 'fx',
+ 'fy',
+ 'fr',
+ 'gradientUnits',
+ 'gradientTransform',
+ 'spreadMethod',
+ 'xlink:href'
+ ],
+ defaults: {
+ gradientUnits: 'objectBoundingBox',
+ cx: '50%',
+ cy: '50%',
+ r: '50%'
+ },
+ contentGroups: [
+ 'descriptive'
+ ],
+ content: [
+ 'animate',
+ 'animateTransform',
+ 'set',
+ 'stop'
+ ]
+ },
+ meshGradient: {
+ attrsGroups: [
+ 'core',
+ 'presentation',
+ 'xlink'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'x',
+ 'y',
+ 'gradientUnits',
+ 'transform'
+ ],
+ contentGroups: [
+ 'descriptive',
+ 'paintServer',
+ 'animation',
+ ],
+ content: [
+ 'meshRow'
+ ]
+ },
+ meshRow: {
+ attrsGroups: [
+ 'core',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style'
+ ],
+ contentGroups: [
+ 'descriptive'
+ ],
+ content: [
+ 'meshPatch'
+ ]
+ },
+ meshPatch: {
+ attrsGroups: [
+ 'core',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style'
+ ],
+ contentGroups: [
+ 'descriptive'
+ ],
+ content: [
+ 'stop'
+ ]
+ },
+ rect: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'rx',
+ 'ry'
+ ],
+ defaults: {
+ x: '0',
+ y: '0'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive'
+ ]
+ },
+ script: {
+ attrsGroups: [
+ 'core',
+ 'xlink'
+ ],
+ attrs: [
+ 'externalResourcesRequired',
+ 'type',
+ 'xlink:href'
+ ]
+ },
+ set: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'animation',
+ 'xlink',
+ 'animationAttributeTarget',
+ 'animationTiming',
+ ],
+ attrs: [
+ 'externalResourcesRequired',
+ 'to'
+ ],
+ contentGroups: [
+ 'descriptive'
+ ]
+ },
+ solidColor: {
+ attrsGroups: [
+ 'core',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style'
+ ],
+ contentGroups: [
+ 'paintServer'
+ ]
+ },
+ stop: {
+ attrsGroups: [
+ 'core',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'offset',
+ 'path'
+ ],
+ content: [
+ 'animate',
+ 'animateColor',
+ 'set'
+ ]
+ },
+ style: {
+ attrsGroups: [
+ 'core'
+ ],
+ attrs: [
+ 'type',
+ 'media',
+ 'title'
+ ],
+ defaults: {
+ type: 'text/css'
+ }
+ },
+ svg: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'documentEvent',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'viewBox',
+ 'preserveAspectRatio',
+ 'zoomAndPan',
+ 'version',
+ 'baseProfile',
+ 'contentScriptType',
+ 'contentStyleType'
+ ],
+ defaults: {
+ x: '0',
+ y: '0',
+ width: '100%',
+ height: '100%',
+ preserveAspectRatio: 'xMidYMid meet',
+ zoomAndPan: 'magnify',
+ version: '1.1',
+ baseProfile: 'none',
+ contentScriptType: 'application/ecmascript',
+ contentStyleType: 'text/css'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer'
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view'
+ ]
+ },
+ switch: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform'
+ ],
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape'
+ ],
+ content: [
+ 'a',
+ 'foreignObject',
+ 'g',
+ 'image',
+ 'svg',
+ 'switch',
+ 'text',
+ 'use'
+ ]
+ },
+ symbol: {
+ attrsGroups: [
+ 'core',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'preserveAspectRatio',
+ 'viewBox',
+ 'refX',
+ 'refY'
+ ],
+ defaults: {
+ refX: 0,
+ refY: 0
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'shape',
+ 'structural',
+ 'paintServer'
+ ],
+ content: [
+ 'a',
+ 'altGlyphDef',
+ 'clipPath',
+ 'color-profile',
+ 'cursor',
+ 'filter',
+ 'font',
+ 'font-face',
+ 'foreignObject',
+ 'image',
+ 'marker',
+ 'mask',
+ 'pattern',
+ 'script',
+ 'style',
+ 'switch',
+ 'text',
+ 'view'
+ ]
+ },
+ text: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'lengthAdjust',
+ 'x',
+ 'y',
+ 'dx',
+ 'dy',
+ 'rotate',
+ 'textLength'
+ ],
+ defaults: {
+ x: '0',
+ y: '0',
+ lengthAdjust: 'spacing'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive',
+ 'textContentChild'
+ ],
+ content: [
+ 'a'
+ ]
+ },
+ textPath: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ 'xlink'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'xlink:href',
+ 'startOffset',
+ 'method',
+ 'spacing',
+ 'd'
+ ],
+ defaults: {
+ startOffset: '0',
+ method: 'align',
+ spacing: 'exact'
+ },
+ contentGroups: [
+ 'descriptive'
+ ],
+ content: [
+ 'a',
+ 'altGlyph',
+ 'animate',
+ 'animateColor',
+ 'set',
+ 'tref',
+ 'tspan'
+ ]
+ },
+ title: {
+ attrsGroups: [
+ 'core'
+ ],
+ attrs: [
+ 'class',
+ 'style'
+ ]
+ },
+ tref: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation',
+ 'xlink'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'xlink:href'
+ ],
+ contentGroups: [
+ 'descriptive'
+ ],
+ content: [
+ 'animate',
+ 'animateColor',
+ 'set'
+ ]
+ },
+ tspan: {
+ attrsGroups: [
+ 'conditionalProcessing',
+ 'core',
+ 'graphicalEvent',
+ 'presentation'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'x',
+ 'y',
+ 'dx',
+ 'dy',
+ 'rotate',
+ 'textLength',
+ 'lengthAdjust'
+ ],
+ contentGroups: [
+ 'descriptive'
+ ],
+ content: [
+ 'a',
+ 'altGlyph',
+ 'animate',
+ 'animateColor',
+ 'set',
+ 'tref',
+ 'tspan'
+ ]
+ },
+ use: {
+ attrsGroups: [
+ 'core',
+ 'conditionalProcessing',
+ 'graphicalEvent',
+ 'presentation',
+ 'xlink'
+ ],
+ attrs: [
+ 'class',
+ 'style',
+ 'externalResourcesRequired',
+ 'transform',
+ 'x',
+ 'y',
+ 'width',
+ 'height',
+ 'xlink:href'
+ ],
+ defaults: {
+ x: '0',
+ y: '0'
+ },
+ contentGroups: [
+ 'animation',
+ 'descriptive'
+ ]
+ },
+ view: {
+ attrsGroups: [
+ 'core'
+ ],
+ attrs: [
+ 'externalResourcesRequired',
+ 'viewBox',
+ 'preserveAspectRatio',
+ 'zoomAndPan',
+ 'viewTarget'
+ ],
+ contentGroups: [
+ 'descriptive'
+ ]
+ },
+ vkern: {
+ attrsGroups: [
+ 'core'
+ ],
+ attrs: [
+ 'u1',
+ 'g1',
+ 'u2',
+ 'g2',
+ 'k'
+ ]
+ }
+};
+
+// http://wiki.inkscape.org/wiki/index.php/Inkscape-specific_XML_attributes
+exports.editorNamespaces = [
+ 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd',
+ 'http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd',
+ 'http://www.inkscape.org/namespaces/inkscape',
+ 'http://www.bohemiancoding.com/sketch/ns',
+ 'http://ns.adobe.com/AdobeIllustrator/10.0/',
+ 'http://ns.adobe.com/Graphs/1.0/',
+ 'http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/',
+ 'http://ns.adobe.com/Variables/1.0/',
+ 'http://ns.adobe.com/SaveForWeb/1.0/',
+ 'http://ns.adobe.com/Extensibility/1.0/',
+ 'http://ns.adobe.com/Flows/1.0/',
+ 'http://ns.adobe.com/ImageReplacement/1.0/',
+ 'http://ns.adobe.com/GenericCustomNamespace/1.0/',
+ 'http://ns.adobe.com/XPath/1.0/'
+];
+
+// http://www.w3.org/TR/SVG/linking.html#processingIRI
+exports.referencesProps = [
+ 'clip-path',
+ 'color-profile',
+ 'fill',
+ 'filter',
+ 'marker-start',
+ 'marker-mid',
+ 'marker-end',
+ 'mask',
+ 'stroke',
+ 'style'
+];
+
+// http://www.w3.org/TR/SVG/propidx.html
+exports.inheritableAttrs = [
+ 'clip-rule',
+ 'color',
+ 'color-interpolation',
+ 'color-interpolation-filters',
+ 'color-profile',
+ 'color-rendering',
+ 'cursor',
+ 'direction',
+ 'fill',
+ 'fill-opacity',
+ 'fill-rule',
+ 'font',
+ 'font-family',
+ 'font-size',
+ 'font-size-adjust',
+ 'font-stretch',
+ 'font-style',
+ 'font-variant',
+ 'font-weight',
+ 'glyph-orientation-horizontal',
+ 'glyph-orientation-vertical',
+ 'image-rendering',
+ 'kerning',
+ 'letter-spacing',
+ 'marker',
+ 'marker-end',
+ 'marker-mid',
+ 'marker-start',
+ 'pointer-events',
+ 'shape-rendering',
+ 'stroke',
+ 'stroke-dasharray',
+ 'stroke-dashoffset',
+ 'stroke-linecap',
+ 'stroke-linejoin',
+ 'stroke-miterlimit',
+ 'stroke-opacity',
+ 'stroke-width',
+ 'text-anchor',
+ 'text-rendering',
+ 'transform',
+ 'visibility',
+ 'white-space',
+ 'word-spacing',
+ 'writing-mode'
+];
+
+// http://www.w3.org/TR/SVG/single-page.html#types-ColorKeywords
+exports.colorsNames = {
+ 'aliceblue': '#f0f8ff',
+ 'antiquewhite': '#faebd7',
+ 'aqua': '#0ff',
+ 'aquamarine': '#7fffd4',
+ 'azure': '#f0ffff',
+ 'beige': '#f5f5dc',
+ 'bisque': '#ffe4c4',
+ 'black': '#000',
+ 'blanchedalmond': '#ffebcd',
+ 'blue': '#00f',
+ 'blueviolet': '#8a2be2',
+ 'brown': '#a52a2a',
+ 'burlywood': '#deb887',
+ 'cadetblue': '#5f9ea0',
+ 'chartreuse': '#7fff00',
+ 'chocolate': '#d2691e',
+ 'coral': '#ff7f50',
+ 'cornflowerblue': '#6495ed',
+ 'cornsilk': '#fff8dc',
+ 'crimson': '#dc143c',
+ 'cyan': '#0ff',
+ 'darkblue': '#00008b',
+ 'darkcyan': '#008b8b',
+ 'darkgoldenrod': '#b8860b',
+ 'darkgray': '#a9a9a9',
+ 'darkgreen': '#006400',
+ 'darkkhaki': '#bdb76b',
+ 'darkmagenta': '#8b008b',
+ 'darkolivegreen': '#556b2f',
+ 'darkorange': '#ff8c00',
+ 'darkorchid': '#9932cc',
+ 'darkred': '#8b0000',
+ 'darksalmon': '#e9967a',
+ 'darkseagreen': '#8fbc8f',
+ 'darkslateblue': '#483d8b',
+ 'darkslategray': '#2f4f4f',
+ 'darkturquoise': '#00ced1',
+ 'darkviolet': '#9400d3',
+ 'deeppink': '#ff1493',
+ 'deepskyblue': '#00bfff',
+ 'dimgray': '#696969',
+ 'dodgerblue': '#1e90ff',
+ 'firebrick': '#b22222',
+ 'floralwhite': '#fffaf0',
+ 'forestgreen': '#228b22',
+ 'fuchsia': '#f0f',
+ 'gainsboro': '#dcdcdc',
+ 'ghostwhite': '#f8f8ff',
+ 'gold': '#ffd700',
+ 'goldenrod': '#daa520',
+ 'gray': '#808080',
+ 'green': '#008000',
+ 'greenyellow': '#adff2f',
+ 'honeydew': '#f0fff0',
+ 'hotpink': '#ff69b4',
+ 'indianred': '#cd5c5c',
+ 'indigo': '#4b0082',
+ 'ivory': '#fffff0',
+ 'khaki': '#f0e68c',
+ 'lavender': '#e6e6fa',
+ 'lavenderblush': '#fff0f5',
+ 'lawngreen': '#7cfc00',
+ 'lemonchiffon': '#fffacd',
+ 'lightblue': '#add8e6',
+ 'lightcoral': '#f08080',
+ 'lightcyan': '#e0ffff',
+ 'lightgoldenrodyellow': '#fafad2',
+ 'lightgreen': '#90ee90',
+ 'lightgrey': '#d3d3d3',
+ 'lightpink': '#ffb6c1',
+ 'lightsalmon': '#ffa07a',
+ 'lightseagreen': '#20b2aa',
+ 'lightskyblue': '#87cefa',
+ 'lightslategray': '#789',
+ 'lightsteelblue': '#b0c4de',
+ 'lightyellow': '#ffffe0',
+ 'lime': '#0f0',
+ 'limegreen': '#32cd32',
+ 'linen': '#faf0e6',
+ 'magenta': '#f0f',
+ 'maroon': '#800000',
+ 'mediumaquamarine': '#66cdaa',
+ 'mediumblue': '#0000cd',
+ 'mediumorchid': '#ba55d3',
+ 'mediumpurple': '#9370db',
+ 'mediumseagreen': '#3cb371',
+ 'mediumslateblue': '#7b68ee',
+ 'mediumspringgreen': '#00fa9a',
+ 'mediumturquoise': '#48d1cc',
+ 'mediumvioletred': '#c71585',
+ 'midnightblue': '#191970',
+ 'mintcream': '#f5fffa',
+ 'mistyrose': '#ffe4e1',
+ 'moccasin': '#ffe4b5',
+ 'navajowhite': '#ffdead',
+ 'navy': '#000080',
+ 'oldlace': '#fdf5e6',
+ 'olive': '#808000',
+ 'olivedrab': '#6b8e23',
+ 'orange': '#ffa500',
+ 'orangered': '#ff4500',
+ 'orchid': '#da70d6',
+ 'palegoldenrod': '#eee8aa',
+ 'palegreen': '#98fb98',
+ 'paleturquoise': '#afeeee',
+ 'palevioletred': '#db7093',
+ 'papayawhip': '#ffefd5',
+ 'peachpuff': '#ffdab9',
+ 'peru': '#cd853f',
+ 'pink': '#ffc0cb',
+ 'plum': '#dda0dd',
+ 'powderblue': '#b0e0e6',
+ 'purple': '#800080',
+ 'red': '#f00',
+ 'rosybrown': '#bc8f8f',
+ 'royalblue': '#4169e1',
+ 'saddlebrown': '#8b4513',
+ 'salmon': '#fa8072',
+ 'sandybrown': '#f4a460',
+ 'seagreen': '#2e8b57',
+ 'seashell': '#fff5ee',
+ 'sienna': '#a0522d',
+ 'silver': '#c0c0c0',
+ 'skyblue': '#87ceeb',
+ 'slateblue': '#6a5acd',
+ 'slategray': '#708090',
+ 'snow': '#fffafa',
+ 'springgreen': '#00ff7f',
+ 'steelblue': '#4682b4',
+ 'tan': '#d2b48c',
+ 'teal': '#008080',
+ 'thistle': '#d8bfd8',
+ 'tomato': '#ff6347',
+ 'turquoise': '#40e0d0',
+ 'violet': '#ee82ee',
+ 'wheat': '#f5deb3',
+ 'white': '#fff',
+ 'whitesmoke': '#f5f5f5',
+ 'yellow': '#ff0',
+ 'yellowgreen': '#9acd32'
+};
+
+exports.colorsShortNames = {
+ '#f0ffff': 'azure',
+ '#f5f5dc': 'beige',
+ '#ffe4c4': 'bisque',
+ '#a52a2a': 'brown',
+ '#ff7f50': 'coral',
+ '#ffd700': 'gold',
+ '#808080': 'gray',
+ '#008000': 'green',
+ '#4b0082': 'indigo',
+ '#fffff0': 'ivory',
+ '#f0e68c': 'khaki',
+ '#faf0e6': 'linen',
+ '#800000': 'maroon',
+ '#000080': 'navy',
+ '#808000': 'olive',
+ '#ffa500': 'orange',
+ '#da70d6': 'orchid',
+ '#cd853f': 'peru',
+ '#ffc0cb': 'pink',
+ '#dda0dd': 'plum',
+ '#800080': 'purple',
+ '#f00': 'red',
+ '#fa8072': 'salmon',
+ '#a0522d': 'sienna',
+ '#c0c0c0': 'silver',
+ '#fffafa': 'snow',
+ '#d2b48c': 'tan',
+ '#008080': 'teal',
+ '#ff6347': 'tomato',
+ '#ee82ee': 'violet',
+ '#f5deb3': 'wheat'
+};
+
+// http://www.w3.org/TR/SVG/single-page.html#types-DataTypeColor
+exports.colorsProps = [
+ 'color', 'fill', 'stroke', 'stop-color', 'flood-color', 'lighting-color'
+];
diff --git a/node_modules/svgo/plugins/_path.js b/node_modules/svgo/plugins/_path.js
new file mode 100644
index 00000000..2aa567a0
--- /dev/null
+++ b/node_modules/svgo/plugins/_path.js
@@ -0,0 +1,962 @@
+/* global a2c */
+'use strict';
+
+var regPathInstructions = /([MmLlHhVvCcSsQqTtAaZz])\s*/,
+ regPathData = /[-+]?(?:\d*\.\d+|\d+\.?)([eE][-+]?\d+)?/g,
+ regNumericValues = /[-+]?(\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/,
+ transform2js = require('./_transforms').transform2js,
+ transformsMultiply = require('./_transforms').transformsMultiply,
+ transformArc = require('./_transforms').transformArc,
+ collections = require('./_collections.js'),
+ referencesProps = collections.referencesProps,
+ defaultStrokeWidth = collections.attrsGroupsDefaults.presentation['stroke-width'],
+ cleanupOutData = require('../lib/svgo/tools').cleanupOutData,
+ removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero,
+ prevCtrlPoint;
+
+/**
+ * Convert path string to JS representation.
+ *
+ * @param {String} pathString input string
+ * @param {Object} params plugin params
+ * @return {Array} output array
+ */
+exports.path2js = function(path) {
+ if (path.pathJS) return path.pathJS;
+
+ var paramsLength = { // Number of parameters of every path command
+ H: 1, V: 1, M: 2, L: 2, T: 2, Q: 4, S: 4, C: 6, A: 7,
+ h: 1, v: 1, m: 2, l: 2, t: 2, q: 4, s: 4, c: 6, a: 7
+ },
+ pathData = [], // JS representation of the path data
+ instruction, // current instruction context
+ startMoveto = false;
+
+ // splitting path string into array like ['M', '10 50', 'L', '20 30']
+ path.attr('d').value.split(regPathInstructions).forEach(function(data) {
+ if (!data) return;
+ if (!startMoveto) {
+ if (data == 'M' || data == 'm') {
+ startMoveto = true;
+ } else return;
+ }
+
+ // instruction item
+ if (regPathInstructions.test(data)) {
+ instruction = data;
+
+ // z - instruction w/o data
+ if (instruction == 'Z' || instruction == 'z') {
+ pathData.push({
+ instruction: 'z'
+ });
+ }
+ // data item
+ } else {
+ data = data.match(regPathData);
+ if (!data) return;
+
+ data = data.map(Number);
+
+ // Subsequent moveto pairs of coordinates are threated as implicit lineto commands
+ // http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands
+ if (instruction == 'M' || instruction == 'm') {
+ pathData.push({
+ instruction: pathData.length == 0 ? 'M' : instruction,
+ data: data.splice(0, 2)
+ });
+ instruction = instruction == 'M' ? 'L' : 'l';
+ }
+
+ for (var pair = paramsLength[instruction]; data.length;) {
+ pathData.push({
+ instruction: instruction,
+ data: data.splice(0, pair)
+ });
+ }
+ }
+ });
+
+ // First moveto is actually absolute. Subsequent coordinates were separated above.
+ if (pathData.length && pathData[0].instruction == 'm') {
+ pathData[0].instruction = 'M';
+ }
+ path.pathJS = pathData;
+
+ return pathData;
+};
+
+/**
+ * Convert relative Path data to absolute.
+ *
+ * @param {Array} data input data
+ * @return {Array} output data
+ */
+var relative2absolute = exports.relative2absolute = function(data) {
+ var currentPoint = [0, 0],
+ subpathPoint = [0, 0],
+ i;
+
+ data = data.map(function(item) {
+
+ var instruction = item.instruction,
+ itemData = item.data && item.data.slice();
+
+ if (instruction == 'M') {
+
+ set(currentPoint, itemData);
+ set(subpathPoint, itemData);
+
+ } else if ('mlcsqt'.indexOf(instruction) > -1) {
+
+ for (i = 0; i < itemData.length; i++) {
+ itemData[i] += currentPoint[i % 2];
+ }
+ set(currentPoint, itemData);
+
+ if (instruction == 'm') {
+ set(subpathPoint, itemData);
+ }
+
+ } else if (instruction == 'a') {
+
+ itemData[5] += currentPoint[0];
+ itemData[6] += currentPoint[1];
+ set(currentPoint, itemData);
+
+ } else if (instruction == 'h') {
+
+ itemData[0] += currentPoint[0];
+ currentPoint[0] = itemData[0];
+
+ } else if (instruction == 'v') {
+
+ itemData[0] += currentPoint[1];
+ currentPoint[1] = itemData[0];
+
+ } else if ('MZLCSQTA'.indexOf(instruction) > -1) {
+
+ set(currentPoint, itemData);
+
+ } else if (instruction == 'H') {
+
+ currentPoint[0] = itemData[0];
+
+ } else if (instruction == 'V') {
+
+ currentPoint[1] = itemData[0];
+
+ } else if (instruction == 'z') {
+
+ set(currentPoint, subpathPoint);
+
+ }
+
+ return instruction == 'z' ?
+ { instruction: 'z' } :
+ {
+ instruction: instruction.toUpperCase(),
+ data: itemData
+ };
+
+ });
+
+ return data;
+};
+
+/**
+ * Apply transformation(s) to the Path data.
+ *
+ * @param {Object} elem current element
+ * @param {Array} path input path data
+ * @param {Object} params whether to apply transforms to stroked lines and transform precision (used for stroke width)
+ * @return {Array} output path data
+ */
+exports.applyTransforms = function(elem, path, params) {
+ // if there are no 'stroke' attr and references to other objects such as
+ // gradiends or clip-path which are also subjects to transform.
+ if (!elem.hasAttr('transform') || !elem.attr('transform').value ||
+ elem.someAttr(function(attr) {
+ return ~referencesProps.indexOf(attr.name) && ~attr.value.indexOf('url(');
+ }))
+ return path;
+
+ var matrix = transformsMultiply(transform2js(elem.attr('transform').value)),
+ stroke = elem.computedAttr('stroke'),
+ id = elem.computedAttr('id'),
+ transformPrecision = params.transformPrecision,
+ newPoint, scale;
+
+ if (stroke && stroke != 'none') {
+ if (!params.applyTransformsStroked ||
+ (matrix.data[0] != matrix.data[3] || matrix.data[1] != -matrix.data[2]) &&
+ (matrix.data[0] != -matrix.data[3] || matrix.data[1] != matrix.data[2]))
+ return path;
+
+ // "stroke-width" should be inside the part with ID, otherwise it can be overrided in <use>
+ if (id) {
+ var idElem = elem,
+ hasStrokeWidth = false;
+
+ do {
+ if (idElem.hasAttr('stroke-width')) hasStrokeWidth = true;
+ } while (!idElem.hasAttr('id', id) && !hasStrokeWidth && (idElem = idElem.parentNode));
+
+ if (!hasStrokeWidth) return path;
+ }
+
+ scale = +Math.sqrt(matrix.data[0] * matrix.data[0] + matrix.data[1] * matrix.data[1]).toFixed(transformPrecision);
+
+ if (scale !== 1) {
+ var strokeWidth = elem.computedAttr('stroke-width') || defaultStrokeWidth;
+
+ if (elem.hasAttr('stroke-width')) {
+ elem.attrs['stroke-width'].value = elem.attrs['stroke-width'].value.trim()
+ .replace(regNumericValues, function(num) { return removeLeadingZero(num * scale) });
+ } else {
+ elem.addAttr({
+ name: 'stroke-width',
+ prefix: '',
+ local: 'stroke-width',
+ value: strokeWidth.replace(regNumericValues, function(num) { return removeLeadingZero(num * scale) })
+ });
+ }
+ }
+ } else if (id) { // Stroke and stroke-width can be redefined with <use>
+ return path;
+ }
+
+ path.forEach(function(pathItem) {
+
+ if (pathItem.data) {
+
+ // h -> l
+ if (pathItem.instruction === 'h') {
+
+ pathItem.instruction = 'l';
+ pathItem.data[1] = 0;
+
+ // v -> l
+ } else if (pathItem.instruction === 'v') {
+
+ pathItem.instruction = 'l';
+ pathItem.data[1] = pathItem.data[0];
+ pathItem.data[0] = 0;
+
+ }
+
+ // if there is a translate() transform
+ if (pathItem.instruction === 'M' &&
+ (matrix.data[4] !== 0 ||
+ matrix.data[5] !== 0)
+ ) {
+
+ // then apply it only to the first absoluted M
+ newPoint = transformPoint(matrix.data, pathItem.data[0], pathItem.data[1]);
+ set(pathItem.data, newPoint);
+ set(pathItem.coords, newPoint);
+
+ // clear translate() data from transform matrix
+ matrix.data[4] = 0;
+ matrix.data[5] = 0;
+
+ } else {
+
+ if (pathItem.instruction == 'a') {
+
+ transformArc(pathItem.data, matrix.data);
+
+ // reduce number of digits in rotation angle
+ if (Math.abs(pathItem.data[2]) > 80) {
+ var a = pathItem.data[0],
+ rotation = pathItem.data[2];
+ pathItem.data[0] = pathItem.data[1];
+ pathItem.data[1] = a;
+ pathItem.data[2] = rotation + (rotation > 0 ? -90 : 90);
+ }
+
+ newPoint = transformPoint(matrix.data, pathItem.data[5], pathItem.data[6]);
+ pathItem.data[5] = newPoint[0];
+ pathItem.data[6] = newPoint[1];
+
+ } else {
+
+ for (var i = 0; i < pathItem.data.length; i += 2) {
+ newPoint = transformPoint(matrix.data, pathItem.data[i], pathItem.data[i + 1]);
+ pathItem.data[i] = newPoint[0];
+ pathItem.data[i + 1] = newPoint[1];
+ }
+ }
+
+ pathItem.coords[0] = pathItem.base[0] + pathItem.data[pathItem.data.length - 2];
+ pathItem.coords[1] = pathItem.base[1] + pathItem.data[pathItem.data.length - 1];
+
+ }
+
+ }
+
+ });
+
+ // remove transform attr
+ elem.removeAttr('transform');
+
+ return path;
+};
+
+/**
+ * Apply transform 3x3 matrix to x-y point.
+ *
+ * @param {Array} matrix transform 3x3 matrix
+ * @param {Array} point x-y point
+ * @return {Array} point with new coordinates
+ */
+function transformPoint(matrix, x, y) {
+
+ return [
+ matrix[0] * x + matrix[2] * y + matrix[4],
+ matrix[1] * x + matrix[3] * y + matrix[5]
+ ];
+
+}
+
+/**
+ * Compute Cubic Bézie bounding box.
+ *
+ * @see http://processingjs.nihongoresources.com/bezierinfo/
+ *
+ * @param {Float} xa
+ * @param {Float} ya
+ * @param {Float} xb
+ * @param {Float} yb
+ * @param {Float} xc
+ * @param {Float} yc
+ * @param {Float} xd
+ * @param {Float} yd
+ *
+ * @return {Object}
+ */
+exports.computeCubicBoundingBox = function(xa, ya, xb, yb, xc, yc, xd, yd) {
+
+ var minx = Number.POSITIVE_INFINITY,
+ miny = Number.POSITIVE_INFINITY,
+ maxx = Number.NEGATIVE_INFINITY,
+ maxy = Number.NEGATIVE_INFINITY,
+ ts,
+ t,
+ x,
+ y,
+ i;
+
+ // X
+ if (xa < minx) { minx = xa; }
+ if (xa > maxx) { maxx = xa; }
+ if (xd < minx) { minx= xd; }
+ if (xd > maxx) { maxx = xd; }
+
+ ts = computeCubicFirstDerivativeRoots(xa, xb, xc, xd);
+
+ for (i = 0; i < ts.length; i++) {
+
+ t = ts[i];
+
+ if (t >= 0 && t <= 1) {
+ x = computeCubicBaseValue(t, xa, xb, xc, xd);
+ // y = computeCubicBaseValue(t, ya, yb, yc, yd);
+
+ if (x < minx) { minx = x; }
+ if (x > maxx) { maxx = x; }
+ }
+
+ }
+
+ // Y
+ if (ya < miny) { miny = ya; }
+ if (ya > maxy) { maxy = ya; }
+ if (yd < miny) { miny = yd; }
+ if (yd > maxy) { maxy = yd; }
+
+ ts = computeCubicFirstDerivativeRoots(ya, yb, yc, yd);
+
+ for (i = 0; i < ts.length; i++) {
+
+ t = ts[i];
+
+ if (t >= 0 && t <= 1) {
+ // x = computeCubicBaseValue(t, xa, xb, xc, xd);
+ y = computeCubicBaseValue(t, ya, yb, yc, yd);
+
+ if (y < miny) { miny = y; }
+ if (y > maxy) { maxy = y; }
+ }
+
+ }
+
+ return {
+ minx: minx,
+ miny: miny,
+ maxx: maxx,
+ maxy: maxy
+ };
+
+};
+
+// compute the value for the cubic bezier function at time=t
+function computeCubicBaseValue(t, a, b, c, d) {
+
+ var mt = 1 - t;
+
+ return mt * mt * mt * a + 3 * mt * mt * t * b + 3 * mt * t * t * c + t * t * t * d;
+
+}
+
+// compute the value for the first derivative of the cubic bezier function at time=t
+function computeCubicFirstDerivativeRoots(a, b, c, d) {
+
+ var result = [-1, -1],
+ tl = -a + 2 * b - c,
+ tr = -Math.sqrt(-a * (c - d) + b * b - b * (c + d) + c * c),
+ dn = -a + 3 * b - 3 * c + d;
+
+ if (dn !== 0) {
+ result[0] = (tl + tr) / dn;
+ result[1] = (tl - tr) / dn;
+ }
+
+ return result;
+
+}
+
+/**
+ * Compute Quadratic Bézier bounding box.
+ *
+ * @see http://processingjs.nihongoresources.com/bezierinfo/
+ *
+ * @param {Float} xa
+ * @param {Float} ya
+ * @param {Float} xb
+ * @param {Float} yb
+ * @param {Float} xc
+ * @param {Float} yc
+ *
+ * @return {Object}
+ */
+exports.computeQuadraticBoundingBox = function(xa, ya, xb, yb, xc, yc) {
+
+ var minx = Number.POSITIVE_INFINITY,
+ miny = Number.POSITIVE_INFINITY,
+ maxx = Number.NEGATIVE_INFINITY,
+ maxy = Number.NEGATIVE_INFINITY,
+ t,
+ x,
+ y;
+
+ // X
+ if (xa < minx) { minx = xa; }
+ if (xa > maxx) { maxx = xa; }
+ if (xc < minx) { minx = xc; }
+ if (xc > maxx) { maxx = xc; }
+
+ t = computeQuadraticFirstDerivativeRoot(xa, xb, xc);
+
+ if (t >= 0 && t <= 1) {
+ x = computeQuadraticBaseValue(t, xa, xb, xc);
+ // y = computeQuadraticBaseValue(t, ya, yb, yc);
+
+ if (x < minx) { minx = x; }
+ if (x > maxx) { maxx = x; }
+ }
+
+ // Y
+ if (ya < miny) { miny = ya; }
+ if (ya > maxy) { maxy = ya; }
+ if (yc < miny) { miny = yc; }
+ if (yc > maxy) { maxy = yc; }
+
+ t = computeQuadraticFirstDerivativeRoot(ya, yb, yc);
+
+ if (t >= 0 && t <=1 ) {
+ // x = computeQuadraticBaseValue(t, xa, xb, xc);
+ y = computeQuadraticBaseValue(t, ya, yb, yc);
+
+ if (y < miny) { miny = y; }
+ if (y > maxy) { maxy = y ; }
+
+ }
+
+ return {
+ minx: minx,
+ miny: miny,
+ maxx: maxx,
+ maxy: maxy
+ };
+
+};
+
+// compute the value for the quadratic bezier function at time=t
+function computeQuadraticBaseValue(t, a, b, c) {
+
+ var mt = 1 - t;
+
+ return mt * mt * a + 2 * mt * t * b + t * t * c;
+
+}
+
+// compute the value for the first derivative of the quadratic bezier function at time=t
+function computeQuadraticFirstDerivativeRoot(a, b, c) {
+
+ var t = -1,
+ denominator = a - 2 * b + c;
+
+ if (denominator !== 0) {
+ t = (a - b) / denominator;
+ }
+
+ return t;
+
+}
+
+/**
+ * Convert path array to string.
+ *
+ * @param {Array} path input path data
+ * @param {Object} params plugin params
+ * @return {String} output path string
+ */
+exports.js2path = function(path, data, params) {
+
+ path.pathJS = data;
+
+ if (params.collapseRepeated) {
+ data = collapseRepeated(data);
+ }
+
+ path.attr('d').value = data.reduce(function(pathString, item) {
+ return pathString += item.instruction + (item.data ? cleanupOutData(item.data, params) : '');
+ }, '');
+
+};
+
+/**
+ * Collapse repeated instructions data
+ *
+ * @param {Array} path input path data
+ * @return {Array} output path data
+ */
+function collapseRepeated(data) {
+
+ var prev,
+ prevIndex;
+
+ // copy an array and modifieds item to keep original data untouched
+ data = data.reduce(function(newPath, item) {
+ if (
+ prev && item.data &&
+ item.instruction == prev.instruction
+ ) {
+ // concat previous data with current
+ if (item.instruction != 'M') {
+ prev = newPath[prevIndex] = {
+ instruction: prev.instruction,
+ data: prev.data.concat(item.data),
+ coords: item.coords,
+ base: prev.base
+ };
+ } else {
+ prev.data = item.data;
+ prev.coords = item.coords;
+ }
+ } else {
+ newPath.push(item);
+ prev = item;
+ prevIndex = newPath.length - 1;
+ }
+
+ return newPath;
+ }, []);
+
+ return data;
+
+}
+
+function set(dest, source) {
+ dest[0] = source[source.length - 2];
+ dest[1] = source[source.length - 1];
+ return dest;
+}
+
+/**
+ * Checks if two paths have an intersection by checking convex hulls
+ * collision using Gilbert-Johnson-Keerthi distance algorithm
+ * http://entropyinteractive.com/2011/04/gjk-algorithm/
+ *
+ * @param {Array} path1 JS path representation
+ * @param {Array} path2 JS path representation
+ * @return {Boolean}
+ */
+exports.intersects = function(path1, path2) {
+ if (path1.length < 3 || path2.length < 3) return false; // nothing to fill
+
+ // Collect points of every subpath.
+ var points1 = relative2absolute(path1).reduce(gatherPoints, []),
+ points2 = relative2absolute(path2).reduce(gatherPoints, []);
+
+ // Axis-aligned bounding box check.
+ if (points1.maxX <= points2.minX || points2.maxX <= points1.minX ||
+ points1.maxY <= points2.minY || points2.maxY <= points1.minY ||
+ points1.every(function (set1) {
+ return points2.every(function (set2) {
+ return set1[set1.maxX][0] <= set2[set2.minX][0] ||
+ set2[set2.maxX][0] <= set1[set1.minX][0] ||
+ set1[set1.maxY][1] <= set2[set2.minY][1] ||
+ set2[set2.maxY][1] <= set1[set1.minY][1];
+ });
+ })
+ ) return false;
+
+ // Get a convex hull from points of each subpath. Has the most complexity O(n·log n).
+ var hullNest1 = points1.map(convexHull),
+ hullNest2 = points2.map(convexHull);
+
+ // Check intersection of every subpath of the first path with every subpath of the second.
+ return hullNest1.some(function(hull1) {
+ if (hull1.length < 3) return false;
+
+ return hullNest2.some(function(hull2) {
+ if (hull2.length < 3) return false;
+
+ var simplex = [getSupport(hull1, hull2, [1, 0])], // create the initial simplex
+ direction = minus(simplex[0]); // set the direction to point towards the origin
+
+ var iterations = 1e4; // infinite loop protection, 10 000 iterations is more than enough
+ while (true) {
+ if (iterations-- == 0) {
+ console.error('Error: infinite loop while processing mergePaths plugin.');
+ return true; // true is the safe value that means “do nothing with paths”
+ }
+ // add a new point
+ simplex.push(getSupport(hull1, hull2, direction));
+ // see if the new point was on the correct side of the origin
+ if (dot(direction, simplex[simplex.length - 1]) <= 0) return false;
+ // process the simplex
+ if (processSimplex(simplex, direction)) return true;
+ }
+ });
+ });
+
+ function getSupport(a, b, direction) {
+ return sub(supportPoint(a, direction), supportPoint(b, minus(direction)));
+ }
+
+ // Computes farthest polygon point in particular direction.
+ // Thanks to knowledge of min/max x and y coordinates we can choose a quadrant to search in.
+ // Since we're working on convex hull, the dot product is increasing until we find the farthest point.
+ function supportPoint(polygon, direction) {
+ var index = direction[1] >= 0 ?
+ direction[0] < 0 ? polygon.maxY : polygon.maxX :
+ direction[0] < 0 ? polygon.minX : polygon.minY,
+ max = -Infinity,
+ value;
+ while ((value = dot(polygon[index], direction)) > max) {
+ max = value;
+ index = ++index % polygon.length;
+ }
+ return polygon[(index || polygon.length) - 1];
+ }
+};
+
+function processSimplex(simplex, direction) {
+ /* jshint -W004 */
+
+ // we only need to handle to 1-simplex and 2-simplex
+ if (simplex.length == 2) { // 1-simplex
+ var a = simplex[1],
+ b = simplex[0],
+ AO = minus(simplex[1]),
+ AB = sub(b, a);
+ // AO is in the same direction as AB
+ if (dot(AO, AB) > 0) {
+ // get the vector perpendicular to AB facing O
+ set(direction, orth(AB, a));
+ } else {
+ set(direction, AO);
+ // only A remains in the simplex
+ simplex.shift();
+ }
+ } else { // 2-simplex
+ var a = simplex[2], // [a, b, c] = simplex
+ b = simplex[1],
+ c = simplex[0],
+ AB = sub(b, a),
+ AC = sub(c, a),
+ AO = minus(a),
+ ACB = orth(AB, AC), // the vector perpendicular to AB facing away from C
+ ABC = orth(AC, AB); // the vector perpendicular to AC facing away from B
+
+ if (dot(ACB, AO) > 0) {
+ if (dot(AB, AO) > 0) { // region 4
+ set(direction, ACB);
+ simplex.shift(); // simplex = [b, a]
+ } else { // region 5
+ set(direction, AO);
+ simplex.splice(0, 2); // simplex = [a]
+ }
+ } else if (dot(ABC, AO) > 0) {
+ if (dot(AC, AO) > 0) { // region 6
+ set(direction, ABC);
+ simplex.splice(1, 1); // simplex = [c, a]
+ } else { // region 5 (again)
+ set(direction, AO);
+ simplex.splice(0, 2); // simplex = [a]
+ }
+ } else // region 7
+ return true;
+ }
+ return false;
+}
+
+function minus(v) {
+ return [-v[0], -v[1]];
+}
+
+function sub(v1, v2) {
+ return [v1[0] - v2[0], v1[1] - v2[1]];
+}
+
+function dot(v1, v2) {
+ return v1[0] * v2[0] + v1[1] * v2[1];
+}
+
+function orth(v, from) {
+ var o = [-v[1], v[0]];
+ return dot(o, minus(from)) < 0 ? minus(o) : o;
+}
+
+function gatherPoints(points, item, index, path) {
+
+ var subPath = points.length && points[points.length - 1],
+ prev = index && path[index - 1],
+ basePoint = subPath.length && subPath[subPath.length - 1],
+ data = item.data,
+ ctrlPoint = basePoint;
+
+ switch (item.instruction) {
+ case 'M':
+ points.push(subPath = []);
+ break;
+ case 'H':
+ addPoint(subPath, [data[0], basePoint[1]]);
+ break;
+ case 'V':
+ addPoint(subPath, [basePoint[0], data[0]]);
+ break;
+ case 'Q':
+ addPoint(subPath, data.slice(0, 2));
+ prevCtrlPoint = [data[2] - data[0], data[3] - data[1]]; // Save control point for shorthand
+ break;
+ case 'T':
+ if (prev.instruction == 'Q' && prev.instruction == 'T') {
+ ctrlPoint = [basePoint[0] + prevCtrlPoint[0], basePoint[1] + prevCtrlPoint[1]];
+ addPoint(subPath, ctrlPoint);
+ prevCtrlPoint = [data[0] - ctrlPoint[0], data[1] - ctrlPoint[1]];
+ }
+ break;
+ case 'C':
+ // Approximate quibic Bezier curve with middle points between control points
+ addPoint(subPath, [.5 * (basePoint[0] + data[0]), .5 * (basePoint[1] + data[1])]);
+ addPoint(subPath, [.5 * (data[0] + data[2]), .5 * (data[1] + data[3])]);
+ addPoint(subPath, [.5 * (data[2] + data[4]), .5 * (data[3] + data[5])]);
+ prevCtrlPoint = [data[4] - data[2], data[5] - data[3]]; // Save control point for shorthand
+ break;
+ case 'S':
+ if (prev.instruction == 'C' && prev.instruction == 'S') {
+ addPoint(subPath, [basePoint[0] + .5 * prevCtrlPoint[0], basePoint[1] + .5 * prevCtrlPoint[1]]);
+ ctrlPoint = [basePoint[0] + prevCtrlPoint[0], basePoint[1] + prevCtrlPoint[1]];
+ }
+ addPoint(subPath, [.5 * (ctrlPoint[0] + data[0]), .5 * (ctrlPoint[1]+ data[1])]);
+ addPoint(subPath, [.5 * (data[0] + data[2]), .5 * (data[1] + data[3])]);
+ prevCtrlPoint = [data[2] - data[0], data[3] - data[1]];
+ break;
+ case 'A':
+ // Convert the arc to bezier curves and use the same approximation
+ var curves = a2c.apply(0, basePoint.concat(data));
+ for (var cData; (cData = curves.splice(0,6).map(toAbsolute)).length;) {
+ addPoint(subPath, [.5 * (basePoint[0] + cData[0]), .5 * (basePoint[1] + cData[1])]);
+ addPoint(subPath, [.5 * (cData[0] + cData[2]), .5 * (cData[1] + cData[3])]);
+ addPoint(subPath, [.5 * (cData[2] + cData[4]), .5 * (cData[3] + cData[5])]);
+ if (curves.length) addPoint(subPath, basePoint = cData.slice(-2));
+ }
+ break;
+ }
+ // Save final command coordinates
+ if (data && data.length >= 2) addPoint(subPath, data.slice(-2));
+ return points;
+
+ function toAbsolute(n, i) { return n + basePoint[i % 2] }
+
+ // Writes data about the extreme points on each axle
+ function addPoint(path, point) {
+ if (!path.length || point[1] > path[path.maxY][1]) {
+ path.maxY = path.length;
+ points.maxY = points.length ? Math.max(point[1], points.maxY) : point[1];
+ }
+ if (!path.length || point[0] > path[path.maxX][0]) {
+ path.maxX = path.length;
+ points.maxX = points.length ? Math.max(point[0], points.maxX) : point[0];
+ }
+ if (!path.length || point[1] < path[path.minY][1]) {
+ path.minY = path.length;
+ points.minY = points.length ? Math.min(point[1], points.minY) : point[1];
+ }
+ if (!path.length || point[0] < path[path.minX][0]) {
+ path.minX = path.length;
+ points.minX = points.length ? Math.min(point[0], points.minX) : point[0];
+ }
+ path.push(point);
+ }
+}
+
+/**
+ * Forms a convex hull from set of points of every subpath using monotone chain convex hull algorithm.
+ * http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
+ *
+ * @param points An array of [X, Y] coordinates
+ */
+function convexHull(points) {
+ /* jshint -W004 */
+
+ points.sort(function(a, b) {
+ return a[0] == b[0] ? a[1] - b[1] : a[0] - b[0];
+ });
+
+ var lower = [],
+ minY = 0,
+ bottom = 0;
+ for (var i = 0; i < points.length; i++) {
+ while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], points[i]) <= 0) {
+ lower.pop();
+ }
+ if (points[i][1] < points[minY][1]) {
+ minY = i;
+ bottom = lower.length;
+ }
+ lower.push(points[i]);
+ }
+
+ var upper = [],
+ maxY = points.length - 1,
+ top = 0;
+ for (var i = points.length; i--;) {
+ while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], points[i]) <= 0) {
+ upper.pop();
+ }
+ if (points[i][1] > points[maxY][1]) {
+ maxY = i;
+ top = upper.length;
+ }
+ upper.push(points[i]);
+ }
+
+ // last points are equal to starting points of the other part
+ upper.pop();
+ lower.pop();
+
+ var hull = lower.concat(upper);
+
+ hull.minX = 0; // by sorting
+ hull.maxX = lower.length;
+ hull.minY = bottom;
+ hull.maxY = (lower.length + top) % hull.length;
+
+ return hull;
+}
+
+function cross(o, a, b) {
+ return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
+}
+
+/* Based on code from Snap.svg (Apache 2 license). http://snapsvg.io/
+ * Thanks to Dmitry Baranovskiy for his great work!
+ */
+
+// jshint ignore: start
+function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
+ // for more information of where this Math came from visit:
+ // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
+ var _120 = Math.PI * 120 / 180,
+ rad = Math.PI / 180 * (+angle || 0),
+ res = [],
+ rotateX = function(x, y, rad) { return x * Math.cos(rad) - y * Math.sin(rad) },
+ rotateY = function(x, y, rad) { return x * Math.sin(rad) + y * Math.cos(rad) };
+ if (!recursive) {
+ x1 = rotateX(x1, y1, -rad);
+ y1 = rotateY(x1, y1, -rad);
+ x2 = rotateX(x2, y2, -rad);
+ y2 = rotateY(x2, y2, -rad);
+ var x = (x1 - x2) / 2,
+ y = (y1 - y2) / 2;
+ var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
+ if (h > 1) {
+ h = Math.sqrt(h);
+ rx = h * rx;
+ ry = h * ry;
+ }
+ var rx2 = rx * rx,
+ ry2 = ry * ry,
+ k = (large_arc_flag == sweep_flag ? -1 : 1) *
+ Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
+ cx = k * rx * y / ry + (x1 + x2) / 2,
+ cy = k * -ry * x / rx + (y1 + y2) / 2,
+ f1 = Math.asin(((y1 - cy) / ry).toFixed(9)),
+ f2 = Math.asin(((y2 - cy) / ry).toFixed(9));
+
+ f1 = x1 < cx ? Math.PI - f1 : f1;
+ f2 = x2 < cx ? Math.PI - f2 : f2;
+ f1 < 0 && (f1 = Math.PI * 2 + f1);
+ f2 < 0 && (f2 = Math.PI * 2 + f2);
+ if (sweep_flag && f1 > f2) {
+ f1 = f1 - Math.PI * 2;
+ }
+ if (!sweep_flag && f2 > f1) {
+ f2 = f2 - Math.PI * 2;
+ }
+ } else {
+ f1 = recursive[0];
+ f2 = recursive[1];
+ cx = recursive[2];
+ cy = recursive[3];
+ }
+ var df = f2 - f1;
+ if (Math.abs(df) > _120) {
+ var f2old = f2,
+ x2old = x2,
+ y2old = y2;
+ f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
+ x2 = cx + rx * Math.cos(f2);
+ y2 = cy + ry * Math.sin(f2);
+ res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
+ }
+ df = f2 - f1;
+ var c1 = Math.cos(f1),
+ s1 = Math.sin(f1),
+ c2 = Math.cos(f2),
+ s2 = Math.sin(f2),
+ t = Math.tan(df / 4),
+ hx = 4 / 3 * rx * t,
+ hy = 4 / 3 * ry * t,
+ m = [
+ - hx * s1, hy * c1,
+ x2 + hx * s2 - x1, y2 - hy * c2 - y1,
+ x2 - x1, y2 - y1
+ ];
+ if (recursive) {
+ return m.concat(res);
+ } else {
+ res = m.concat(res);
+ var newres = [];
+ for (var i = 0, n = res.length; i < n; i++) {
+ newres[i] = i % 2 ? rotateY(res[i - 1], res[i], rad) : rotateX(res[i], res[i + 1], rad);
+ }
+ return newres;
+ }
+}
+// jshint ignore: end
diff --git a/node_modules/svgo/plugins/_transforms.js b/node_modules/svgo/plugins/_transforms.js
new file mode 100644
index 00000000..796ca259
--- /dev/null
+++ b/node_modules/svgo/plugins/_transforms.js
@@ -0,0 +1,308 @@
+'use strict';
+
+var regTransformTypes = /matrix|translate|scale|rotate|skewX|skewY/,
+ regTransformSplit = /\s*(matrix|translate|scale|rotate|skewX|skewY)\s*\(\s*(.+?)\s*\)[\s,]*/,
+ regNumericValues = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
+
+/**
+ * Convert transform string to JS representation.
+ *
+ * @param {String} transformString input string
+ * @param {Object} params plugin params
+ * @return {Array} output array
+ */
+exports.transform2js = function(transformString) {
+
+ // JS representation of the transform data
+ var transforms = [],
+ // current transform context
+ current;
+
+ // split value into ['', 'translate', '10 50', '', 'scale', '2', '', 'rotate', '-45', '']
+ transformString.split(regTransformSplit).forEach(function(item) {
+ /*jshint -W084 */
+ var num;
+
+ if (item) {
+ // if item is a translate function
+ if (regTransformTypes.test(item)) {
+ // then collect it and change current context
+ transforms.push(current = { name: item });
+ // else if item is data
+ } else {
+ // then split it into [10, 50] and collect as context.data
+ while (num = regNumericValues.exec(item)) {
+ num = Number(num);
+ if (current.data)
+ current.data.push(num);
+ else
+ current.data = [num];
+ }
+ }
+ }
+ });
+
+ return transforms;
+
+};
+
+/**
+ * Multiply transforms into one.
+ *
+ * @param {Array} input transforms array
+ * @return {Array} output matrix array
+ */
+exports.transformsMultiply = function(transforms) {
+
+ // convert transforms objects to the matrices
+ transforms = transforms.map(function(transform) {
+ if (transform.name === 'matrix') {
+ return transform.data;
+ }
+ return transformToMatrix(transform);
+ });
+
+ // multiply all matrices into one
+ transforms = {
+ name: 'matrix',
+ data: transforms.reduce(function(a, b) {
+ return multiplyTransformMatrices(a, b);
+ })
+ };
+
+ return transforms;
+
+};
+
+/**
+ * Do math like a schoolgirl.
+ *
+ * @type {Object}
+ */
+var mth = exports.mth = {
+
+ rad: function(deg) {
+ return deg * Math.PI / 180;
+ },
+
+ deg: function(rad) {
+ return rad * 180 / Math.PI;
+ },
+
+ cos: function(deg) {
+ return Math.cos(this.rad(deg));
+ },
+
+ acos: function(val, floatPrecision) {
+ return +(this.deg(Math.acos(val)).toFixed(floatPrecision));
+ },
+
+ sin: function(deg) {
+ return Math.sin(this.rad(deg));
+ },
+
+ asin: function(val, floatPrecision) {
+ return +(this.deg(Math.asin(val)).toFixed(floatPrecision));
+ },
+
+ tan: function(deg) {
+ return Math.tan(this.rad(deg));
+ },
+
+ atan: function(val, floatPrecision) {
+ return +(this.deg(Math.atan(val)).toFixed(floatPrecision));
+ }
+
+};
+
+/**
+ * Decompose matrix into simple transforms. See
+ * http://www.maths-informatique-jeux.com/blog/frederic/?post/2013/12/01/Decomposition-of-2D-transform-matrices
+ *
+ * @param {Object} data matrix transform object
+ * @return {Object|Array} transforms array or original transform object
+ */
+exports.matrixToTransform = function(transform, params) {
+ var floatPrecision = params.floatPrecision,
+ data = transform.data,
+ transforms = [],
+ sx = +Math.sqrt(data[0] * data[0] + data[1] * data[1]).toFixed(params.transformPrecision),
+ sy = +((data[0] * data[3] - data[1] * data[2]) / sx).toFixed(params.transformPrecision),
+ colsSum = data[0] * data[2] + data[1] * data[3],
+ rowsSum = data[0] * data[1] + data[2] * data[3],
+ scaleBefore = rowsSum || +(sx == sy);
+
+ // [..., ..., ..., ..., tx, ty] → translate(tx, ty)
+ if (data[4] || data[5]) {
+ transforms.push({ name: 'translate', data: data.slice(4, data[5] ? 6 : 5) });
+ }
+
+ // [sx, 0, tan(a)·sy, sy, 0, 0] → skewX(a)·scale(sx, sy)
+ if (!data[1] && data[2]) {
+ transforms.push({ name: 'skewX', data: [mth.atan(data[2] / sy, floatPrecision)] });
+
+ // [sx, sx·tan(a), 0, sy, 0, 0] → skewY(a)·scale(sx, sy)
+ } else if (data[1] && !data[2]) {
+ transforms.push({ name: 'skewY', data: [mth.atan(data[1] / data[0], floatPrecision)] });
+ sx = data[0];
+ sy = data[3];
+
+ // [sx·cos(a), sx·sin(a), sy·-sin(a), sy·cos(a), x, y] → rotate(a[, cx, cy])·(scale or skewX) or
+ // [sx·cos(a), sy·sin(a), sx·-sin(a), sy·cos(a), x, y] → scale(sx, sy)·rotate(a[, cx, cy]) (if !scaleBefore)
+ } else if (!colsSum || (sx == 1 && sy == 1) || !scaleBefore) {
+ if (!scaleBefore) {
+ sx = (data[0] < 0 ? -1 : 1) * Math.sqrt(data[0] * data[0] + data[2] * data[2]);
+ sy = (data[3] < 0 ? -1 : 1) * Math.sqrt(data[1] * data[1] + data[3] * data[3]);
+ transforms.push({ name: 'scale', data: [sx, sy] });
+ }
+ var rotate = [mth.acos(data[0] / sx, floatPrecision) * (data[1] * sy < 0 ? -1 : 1)];
+
+ if (rotate[0]) transforms.push({ name: 'rotate', data: rotate });
+
+ if (rowsSum && colsSum) transforms.push({
+ name: 'skewX',
+ data: [mth.atan(colsSum / (sx * sx), floatPrecision)]
+ });
+
+ // rotate(a, cx, cy) can consume translate() within optional arguments cx, cy (rotation point)
+ if (rotate[0] && (data[4] || data[5])) {
+ transforms.shift();
+ var cos = data[0] / sx,
+ sin = data[1] / (scaleBefore ? sx : sy),
+ x = data[4] * (scaleBefore || sy),
+ y = data[5] * (scaleBefore || sx),
+ denom = (Math.pow(1 - cos, 2) + Math.pow(sin, 2)) * (scaleBefore || sx * sy);
+ rotate.push(((1 - cos) * x - sin * y) / denom);
+ rotate.push(((1 - cos) * y + sin * x) / denom);
+ }
+
+ // Too many transformations, return original matrix if it isn't just a scale/translate
+ } else if (data[1] || data[2]) {
+ return transform;
+ }
+
+ if (scaleBefore && (sx != 1 || sy != 1) || !transforms.length) transforms.push({
+ name: 'scale',
+ data: sx == sy ? [sx] : [sx, sy]
+ });
+
+ return transforms;
+};
+
+/**
+ * Convert transform to the matrix data.
+ *
+ * @param {Object} transform transform object
+ * @return {Array} matrix data
+ */
+function transformToMatrix(transform) {
+
+ if (transform.name === 'matrix') return transform.data;
+
+ var matrix;
+
+ switch (transform.name) {
+ case 'translate':
+ // [1, 0, 0, 1, tx, ty]
+ matrix = [1, 0, 0, 1, transform.data[0], transform.data[1] || 0];
+ break;
+ case 'scale':
+ // [sx, 0, 0, sy, 0, 0]
+ matrix = [transform.data[0], 0, 0, transform.data[1] || transform.data[0], 0, 0];
+ break;
+ case 'rotate':
+ // [cos(a), sin(a), -sin(a), cos(a), x, y]
+ var cos = mth.cos(transform.data[0]),
+ sin = mth.sin(transform.data[0]),
+ cx = transform.data[1] || 0,
+ cy = transform.data[2] || 0;
+
+ matrix = [cos, sin, -sin, cos, (1 - cos) * cx + sin * cy, (1 - cos) * cy - sin * cx];
+ break;
+ case 'skewX':
+ // [1, 0, tan(a), 1, 0, 0]
+ matrix = [1, 0, mth.tan(transform.data[0]), 1, 0, 0];
+ break;
+ case 'skewY':
+ // [1, tan(a), 0, 1, 0, 0]
+ matrix = [1, mth.tan(transform.data[0]), 0, 1, 0, 0];
+ break;
+ }
+
+ return matrix;
+
+}
+
+/**
+ * Applies transformation to an arc. To do so, we represent ellipse as a matrix, multiply it
+ * by the transformation matrix and use a singular value decomposition to represent in a form
+ * rotate(θ)·scale(a b)·rotate(φ). This gives us new ellipse params a, b and θ.
+ * SVD is being done with the formulae provided by Wolffram|Alpha (svd {{m0, m2}, {m1, m3}})
+ *
+ * @param {Array} arc [a, b, rotation in deg]
+ * @param {Array} transform transformation matrix
+ * @return {Array} arc transformed input arc
+ */
+exports.transformArc = function(arc, transform) {
+
+ var a = arc[0],
+ b = arc[1],
+ rot = arc[2] * Math.PI / 180,
+ cos = Math.cos(rot),
+ sin = Math.sin(rot),
+ h = Math.pow(arc[5] * cos + arc[6] * sin, 2) / (4 * a * a) +
+ Math.pow(arc[6] * cos - arc[5] * sin, 2) / (4 * b * b);
+ if (h > 1) {
+ h = Math.sqrt(h);
+ a *= h;
+ b *= h;
+ }
+ var ellipse = [a * cos, a * sin, -b * sin, b * cos, 0, 0],
+ m = multiplyTransformMatrices(transform, ellipse),
+ // Decompose the new ellipse matrix
+ lastCol = m[2] * m[2] + m[3] * m[3],
+ squareSum = m[0] * m[0] + m[1] * m[1] + lastCol,
+ root = Math.sqrt(
+ (Math.pow(m[0] - m[3], 2) + Math.pow(m[1] + m[2], 2)) *
+ (Math.pow(m[0] + m[3], 2) + Math.pow(m[1] - m[2], 2))
+ );
+
+ if (!root) { // circle
+ arc[0] = arc[1] = Math.sqrt(squareSum / 2);
+ arc[2] = 0;
+ } else {
+ var majorAxisSqr = (squareSum + root) / 2,
+ minorAxisSqr = (squareSum - root) / 2,
+ major = Math.abs(majorAxisSqr - lastCol) > 1e-6,
+ sub = (major ? majorAxisSqr : minorAxisSqr) - lastCol,
+ rowsSum = m[0] * m[2] + m[1] * m[3],
+ term1 = m[0] * sub + m[2] * rowsSum,
+ term2 = m[1] * sub + m[3] * rowsSum;
+ arc[0] = Math.sqrt(majorAxisSqr);
+ arc[1] = Math.sqrt(minorAxisSqr);
+ arc[2] = ((major ? term2 < 0 : term1 > 0) ? -1 : 1) *
+ Math.acos((major ? term1 : term2) / Math.sqrt(term1 * term1 + term2 * term2)) * 180 / Math.PI;
+ }
+ return arc;
+
+};
+
+/**
+ * Multiply transformation matrices.
+ *
+ * @param {Array} a matrix A data
+ * @param {Array} b matrix B data
+ * @return {Array} result
+ */
+function multiplyTransformMatrices(a, b) {
+
+ return [
+ a[0] * b[0] + a[2] * b[1],
+ a[1] * b[0] + a[3] * b[1],
+ a[0] * b[2] + a[2] * b[3],
+ a[1] * b[2] + a[3] * b[3],
+ a[0] * b[4] + a[2] * b[5] + a[4],
+ a[1] * b[4] + a[3] * b[5] + a[5]
+ ];
+
+}
diff --git a/node_modules/svgo/plugins/addAttributesToSVGElement.js b/node_modules/svgo/plugins/addAttributesToSVGElement.js
new file mode 100644
index 00000000..9b97c95c
--- /dev/null
+++ b/node_modules/svgo/plugins/addAttributesToSVGElement.js
@@ -0,0 +1,57 @@
+'use strict';
+
+exports.type = 'full';
+
+exports.active = false;
+
+exports.description = 'adds attributes to an outer <svg> element';
+
+var ENOCLS = 'Error in plugin "addAttributesToSVGElement": absent parameters.\n\
+It should have a list of classes in "attributes" or one "attribute".\n\
+Config example:\n\n\
+\
+plugins:\n\
+- addAttributesToSVGElement:\n\
+ attribute: "mySvg"\n\n\
+\
+plugins:\n\
+- addAttributesToSVGElement:\n\
+ attributes: ["mySvg", "size-big"]\n';
+
+/**
+ * Add attributes to an outer <svg> element. Example config:
+ *
+ * plugins:
+ * - addAttributesToSVGElement:
+ * attribute: 'data-icon'
+ *
+ * plugins:
+ * - addAttributesToSVGElement:
+ * attributes: ['data-icon', 'data-disabled']
+ *
+ * @author April Arcus
+ */
+exports.fn = function(data, params) {
+ if (!params || !(Array.isArray(params.attributes) && params.attributes.some(String) || params.attribute)) {
+ console.error(ENOCLS);
+ return data;
+ }
+
+ var attributes = params.attributes || [ params.attribute ],
+ svg = data.content[0];
+
+ if (svg.isElem('svg')) {
+ attributes.forEach(function (attribute) {
+ if (!svg.hasAttr(attribute)) {
+ svg.addAttr({
+ name: attribute,
+ prefix: '',
+ local: attribute
+ });
+ }
+ });
+ }
+
+ return data;
+
+};
diff --git a/node_modules/svgo/plugins/addClassesToSVGElement.js b/node_modules/svgo/plugins/addClassesToSVGElement.js
new file mode 100644
index 00000000..3f9596aa
--- /dev/null
+++ b/node_modules/svgo/plugins/addClassesToSVGElement.js
@@ -0,0 +1,64 @@
+'use strict';
+
+exports.type = 'full';
+
+exports.active = false;
+
+exports.description = 'adds classnames to an outer <svg> element';
+
+var ENOCLS = 'Error in plugin "addClassesToSVGElement": absent parameters.\n\
+It should have a list of classes in "classNames" or one "className".\n\
+Config example:\n\n\
+\
+plugins:\n\
+- addClassesToSVGElement:\n\
+ className: "mySvg"\n\n\
+\
+plugins:\n\
+- addClassesToSVGElement:\n\
+ classNames: ["mySvg", "size-big"]\n';
+
+/**
+ * Add classnames to an outer <svg> element. Example config:
+ *
+ * plugins:
+ * - addClassesToSVGElement:
+ * className: 'mySvg'
+ *
+ * plugins:
+ * - addClassesToSVGElement:
+ * classNames: ['mySvg', 'size-big']
+ *
+ * @author April Arcus
+ */
+exports.fn = function(data, params) {
+ if (!params || !(Array.isArray(params.classNames) && params.classNames.some(String) || params.className)) {
+ console.error(ENOCLS);
+ return data;
+ }
+
+ var classNames = params.classNames || [ params.className ],
+ svg = data.content[0];
+
+ if (svg.isElem('svg')) {
+ if (svg.hasAttr('class')) {
+ var classes = svg.attr('class').value.split(' ');
+ classNames.forEach(function(className){
+ if (classes.indexOf(className) < 0) {
+ classes.push(className);
+ }
+ });
+ svg.attr('class').value = classes.join(' ');
+ } else {
+ svg.addAttr({
+ name: 'class',
+ value: classNames.join(' '),
+ prefix: '',
+ local: 'class'
+ });
+ }
+ }
+
+ return data;
+
+};
diff --git a/node_modules/svgo/plugins/cleanupAttrs.js b/node_modules/svgo/plugins/cleanupAttrs.js
new file mode 100644
index 00000000..47e77ac7
--- /dev/null
+++ b/node_modules/svgo/plugins/cleanupAttrs.js
@@ -0,0 +1,56 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'cleanups attributes from newlines, trailing and repeating spaces';
+
+exports.params = {
+ newlines: true,
+ trim: true,
+ spaces: true
+};
+
+var regNewlinesNeedSpace = /(\S)\r?\n(\S)/g,
+ regNewlines = /\r?\n/g,
+ regSpaces = /\s{2,}/g;
+
+/**
+ * Cleanup attributes values from newlines, trailing and repeating spaces.
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item, params) {
+
+ if (item.isElem()) {
+
+ item.eachAttr(function(attr) {
+
+ if (params.newlines) {
+ // new line which requires a space instead of themselve
+ attr.value = attr.value.replace(regNewlinesNeedSpace, function(match, p1, p2) {
+ return p1 + ' ' + p2;
+ });
+
+ // simple new line
+ attr.value = attr.value.replace(regNewlines, '');
+ }
+
+ if (params.trim) {
+ attr.value = attr.value.trim();
+ }
+
+ if (params.spaces) {
+ attr.value = attr.value.replace(regSpaces, ' ');
+ }
+
+ });
+
+ }
+
+};
diff --git a/node_modules/svgo/plugins/cleanupEnableBackground.js b/node_modules/svgo/plugins/cleanupEnableBackground.js
new file mode 100644
index 00000000..e6384ab5
--- /dev/null
+++ b/node_modules/svgo/plugins/cleanupEnableBackground.js
@@ -0,0 +1,84 @@
+'use strict';
+
+exports.type = 'full';
+
+exports.active = true;
+
+exports.description = 'remove or cleanup enable-background attribute when possible';
+
+/**
+ * Remove or cleanup enable-background attr which coincides with a width/height box.
+ *
+ * @see http://www.w3.org/TR/SVG/filters.html#EnableBackgroundProperty
+ *
+ * @example
+ * <svg width="100" height="50" enable-background="new 0 0 100 50">
+ * ⬇
+ * <svg width="100" height="50">
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(data) {
+
+ var regEnableBackground = /^new\s0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)$/,
+ hasFilter = false,
+ elems = ['svg', 'mask', 'pattern'];
+
+ function checkEnableBackground(item) {
+ if (
+ item.isElem(elems) &&
+ item.hasAttr('enable-background') &&
+ item.hasAttr('width') &&
+ item.hasAttr('height')
+ ) {
+
+ var match = item.attr('enable-background').value.match(regEnableBackground);
+
+ if (match) {
+ if (
+ item.attr('width').value === match[1] &&
+ item.attr('height').value === match[3]
+ ) {
+ if (item.isElem('svg')) {
+ item.removeAttr('enable-background');
+ } else {
+ item.attr('enable-background').value = 'new';
+ }
+ }
+ }
+
+ }
+ }
+
+ function checkForFilter(item) {
+ if (item.isElem('filter')) {
+ hasFilter = true;
+ }
+ }
+
+ function monkeys(items, fn) {
+ items.content.forEach(function(item) {
+ fn(item);
+
+ if (item.content) {
+ monkeys(item, fn);
+ }
+ });
+ return items;
+ }
+
+ var firstStep = monkeys(data, function(item) {
+ checkEnableBackground(item);
+ if (!hasFilter) {
+ checkForFilter(item);
+ }
+ });
+
+ return hasFilter ? firstStep : monkeys(firstStep, function(item) {
+ //we don't need 'enable-background' if we have no filters
+ item.removeAttr('enable-background');
+ });
+};
diff --git a/node_modules/svgo/plugins/cleanupIDs.js b/node_modules/svgo/plugins/cleanupIDs.js
new file mode 100644
index 00000000..38b5cfb4
--- /dev/null
+++ b/node_modules/svgo/plugins/cleanupIDs.js
@@ -0,0 +1,208 @@
+'use strict';
+
+exports.type = 'full';
+
+exports.active = true;
+
+exports.description = 'removes unused IDs and minifies used';
+
+exports.params = {
+ remove: true,
+ minify: true,
+ prefix: ''
+};
+
+var referencesProps = require('./_collections').referencesProps,
+ regReferencesUrl = /\burl\(("|')?#(.+?)\1\)/,
+ regReferencesHref = /^#(.+?)$/,
+ regReferencesBegin = /^(\w+?)\./,
+ styleOrScript = ['style', 'script'],
+ generateIDchars = [
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
+ ],
+ maxIDindex = generateIDchars.length - 1;
+
+/**
+ * Remove unused and minify used IDs
+ * (only if there are no any <style> or <script>).
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(data, params) {
+
+ var currentID,
+ currentIDstring,
+ IDs = Object.create(null),
+ referencesIDs = Object.create(null),
+ idPrefix = 'id-', // prefix IDs so that values like '__proto__' don't break the work
+ hasStyleOrScript = false;
+
+ /**
+ * Bananas!
+ *
+ * @param {Array} items input items
+ * @return {Array} output items
+ */
+ function monkeys(items) {
+
+ for (var i = 0; i < items.content.length && !hasStyleOrScript; i++) {
+
+ var item = items.content[i],
+ match;
+
+ // check if <style> of <script> presents
+ if (item.isElem(styleOrScript)) {
+ hasStyleOrScript = true;
+ continue;
+ }
+
+ // …and don't remove any ID if yes
+ if (item.isElem()) {
+
+ item.eachAttr(function(attr) {
+ var key;
+ // save IDs
+ if (attr.name === 'id') {
+ key = idPrefix + attr.value;
+ if (key in IDs) {
+ item.removeAttr('id');
+ } else {
+ IDs[key] = item;
+ }
+ }
+
+ // save IDs url() references
+ else if (referencesProps.indexOf(attr.name) > -1) {
+ match = attr.value.match(regReferencesUrl);
+
+ if (match) {
+ key = idPrefix + match[2];
+ if (referencesIDs[key]) {
+ referencesIDs[key].push(attr);
+ } else {
+ referencesIDs[key] = [attr];
+ }
+ }
+ }
+
+ // save IDs href references
+ else if (
+ attr.local === 'href' && (match = attr.value.match(regReferencesHref)) ||
+ attr.name === 'begin' && (match = attr.value.match(regReferencesBegin))
+ ) {
+ key = idPrefix + match[1];
+ if (referencesIDs[key]) {
+ referencesIDs[key].push(attr);
+ } else {
+ referencesIDs[key] = [attr];
+ }
+ }
+ });
+
+ }
+
+ // go deeper
+ if (item.content) {
+ monkeys(item);
+ }
+ }
+
+ return items;
+
+ }
+
+ data = monkeys(data);
+
+ if (hasStyleOrScript) {
+ return data;
+ }
+
+ var idKey;
+ for (var k in referencesIDs) {
+ if (IDs[k]) {
+ idKey = k;
+ k = k.replace(idPrefix, '');
+ // replace referenced IDs with the minified ones
+ if (params.minify) {
+ currentIDstring = getIDstring(currentID = generateID(currentID), params);
+ IDs[idKey].attr('id').value = currentIDstring;
+
+ referencesIDs[idKey].forEach(function(attr) {
+ attr.value = attr.value
+ .replace('#' + k, '#' + currentIDstring)
+ .replace(k + '.', currentIDstring + '.');
+ });
+
+ idKey = idPrefix + k;
+ }
+
+ // don't remove referenced IDs
+ delete IDs[idKey];
+ }
+ }
+
+ // remove non-referenced IDs attributes from elements
+ if (params.remove) {
+
+ for(var ID in IDs) {
+ IDs[ID].removeAttr('id');
+ }
+
+ }
+
+ return data;
+
+};
+
+/**
+ * Generate unique minimal ID.
+ *
+ * @param {Array} [currentID] current ID
+ * @return {Array} generated ID array
+ */
+function generateID(currentID) {
+
+ if (!currentID) return [0];
+
+ currentID[currentID.length - 1]++;
+
+ for(var i = currentID.length - 1; i > 0; i--) {
+ if (currentID[i] > maxIDindex) {
+ currentID[i] = 0;
+
+ if (currentID[i - 1] !== undefined) {
+ currentID[i - 1]++;
+ }
+ }
+ }
+
+ if (currentID[0] > maxIDindex) {
+ currentID[0] = 0;
+ currentID.unshift(0);
+ }
+
+ return currentID;
+
+}
+
+/**
+ * Get string from generated ID array.
+ *
+ * @param {Array} arr input ID array
+ * @return {String} output ID string
+ */
+function getIDstring(arr, params) {
+
+ var str = params.prefix;
+
+ arr.forEach(function(i) {
+ str += generateIDchars[i];
+ });
+
+ return str;
+
+}
diff --git a/node_modules/svgo/plugins/cleanupListOfValues.js b/node_modules/svgo/plugins/cleanupListOfValues.js
new file mode 100644
index 00000000..e75944da
--- /dev/null
+++ b/node_modules/svgo/plugins/cleanupListOfValues.js
@@ -0,0 +1,141 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = false;
+
+exports.description = 'rounds list of values to the fixed precision';
+
+exports.params = {
+ floatPrecision: 3,
+ leadingZero: true,
+ defaultPx: true,
+ convertToPx: true
+};
+
+var regNumericValues = /^([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/,
+ regSeparator = /\s+,?\s*|,\s*/,
+ removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero,
+ absoluteLengths = { // relative to px
+ cm: 96/2.54,
+ mm: 96/25.4,
+ in: 96,
+ pt: 4/3,
+ pc: 16
+ };
+
+/**
+ * Round list of values to the fixed precision.
+ *
+ * @example
+ * <svg viewBox="0 0 200.28423 200.28423" enable-background="new 0 0 200.28423 200.28423">
+ * ⬇
+ * <svg viewBox="0 0 200.284 200.284" enable-background="new 0 0 200.284 200.284">
+ *
+ *
+ * <polygon points="208.250977 77.1308594 223.069336 ... "/>
+ * ⬇
+ * <polygon points="208.251 77.131 223.069 ... "/>
+ *
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author kiyopikko
+ */
+exports.fn = function(item, params) {
+
+
+ if ( item.hasAttr('points') ) {
+ roundValues(item.attrs.points);
+ }
+
+ if ( item.hasAttr('enable-background') ) {
+ roundValues(item.attrs['enable-background']);
+ }
+
+ if ( item.hasAttr('viewBox') ) {
+ roundValues(item.attrs.viewBox);
+ }
+
+ if ( item.hasAttr('stroke-dasharray') ) {
+ roundValues(item.attrs['stroke-dasharray']);
+ }
+
+ if ( item.hasAttr('dx') ) {
+ roundValues(item.attrs.dx);
+ }
+
+ if ( item.hasAttr('dy') ) {
+ roundValues(item.attrs.dy);
+ }
+
+ if ( item.hasAttr('x') ) {
+ roundValues(item.attrs.x);
+ }
+
+ if ( item.hasAttr('y') ) {
+ roundValues(item.attrs.y);
+ }
+
+
+ function roundValues($prop){
+
+ var num, units,
+ match,
+ matchNew,
+ lists = $prop.value,
+ listsArr = lists.split(regSeparator),
+ roundedListArr = [],
+ roundedList;
+
+ listsArr.forEach(function(elem){
+
+ match = elem.match(regNumericValues);
+ matchNew = elem.match(/new/);
+
+ // if attribute value matches regNumericValues
+ if(match){
+
+ // round it to the fixed precision
+ num = +(+match[1]).toFixed(params.floatPrecision),
+ units = match[3] || '';
+
+ // convert absolute values to pixels
+ if (params.convertToPx && units && (units in absoluteLengths)) {
+ var pxNum = +(absoluteLengths[units] * match[1]).toFixed(params.floatPrecision);
+
+ if (String(pxNum).length < match[0].length)
+ num = pxNum,
+ units = 'px';
+ }
+
+ // and remove leading zero
+ if (params.leadingZero) {
+ num = removeLeadingZero(num);
+ }
+
+ // remove default 'px' units
+ if (params.defaultPx && units === 'px') {
+ units = '';
+ }
+
+ roundedListArr.push(num+units);
+
+ }
+ // if attribute value is "new"(only enable-background).
+ else if(matchNew){
+
+ roundedListArr.push('new');
+
+ }
+
+ });
+
+ roundedList = roundedListArr.join(' ');
+ $prop.value = roundedList;
+
+ }
+
+};
diff --git a/node_modules/svgo/plugins/cleanupNumericValues.js b/node_modules/svgo/plugins/cleanupNumericValues.js
new file mode 100644
index 00000000..487443a5
--- /dev/null
+++ b/node_modules/svgo/plugins/cleanupNumericValues.js
@@ -0,0 +1,76 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'rounds numeric values to the fixed precision, removes default ‘px’ units';
+
+exports.params = {
+ floatPrecision: 3,
+ leadingZero: true,
+ defaultPx: true,
+ convertToPx: true
+};
+
+var regNumericValues = /^([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)(px|pt|pc|mm|cm|m|in|ft|em|ex|%)?$/,
+ removeLeadingZero = require('../lib/svgo/tools').removeLeadingZero,
+ absoluteLengths = { // relative to px
+ cm: 96/2.54,
+ mm: 96/25.4,
+ in: 96,
+ pt: 4/3,
+ pc: 16
+ };
+
+/**
+ * Round numeric values to the fixed precision,
+ * remove default 'px' units.
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item, params) {
+
+ if (item.isElem()) {
+
+ var match;
+
+ item.eachAttr(function(attr) {
+ match = attr.value.match(regNumericValues);
+
+ // if attribute value matches regNumericValues
+ if (match) {
+ // round it to the fixed precision
+ var num = +(+match[1]).toFixed(params.floatPrecision),
+ units = match[3] || '';
+
+ // convert absolute values to pixels
+ if (params.convertToPx && units && (units in absoluteLengths)) {
+ var pxNum = +(absoluteLengths[units] * match[1]).toFixed(params.floatPrecision);
+
+ if (String(pxNum).length < match[0].length)
+ num = pxNum,
+ units = 'px';
+ }
+
+ // and remove leading zero
+ if (params.leadingZero) {
+ num = removeLeadingZero(num);
+ }
+
+ // remove default 'px' units
+ if (params.defaultPx && units === 'px') {
+ units = '';
+ }
+
+ attr.value = num + units;
+ }
+ });
+
+ }
+
+};
diff --git a/node_modules/svgo/plugins/collapseGroups.js b/node_modules/svgo/plugins/collapseGroups.js
new file mode 100644
index 00000000..adc97638
--- /dev/null
+++ b/node_modules/svgo/plugins/collapseGroups.js
@@ -0,0 +1,91 @@
+'use strict';
+
+exports.type = 'perItemReverse';
+
+exports.active = true;
+
+exports.description = 'collapses useless groups';
+
+var collections = require('./_collections'),
+ attrsInheritable = collections.inheritableAttrs,
+ animationElems = collections.elemsGroups.animation;
+
+function hasAnimatedAttr(item) {
+ /* jshint validthis:true */
+ return item.isElem(animationElems) && item.hasAttr('attributeName', this) ||
+ !item.isEmpty() && item.content.some(hasAnimatedAttr, this);
+}
+
+/*
+ * Collapse useless groups.
+ *
+ * @example
+ * <g>
+ * <g attr1="val1">
+ * <path d="..."/>
+ * </g>
+ * </g>
+ * ⬇
+ * <g>
+ * <g>
+ * <path attr1="val1" d="..."/>
+ * </g>
+ * </g>
+ * ⬇
+ * <path attr1="val1" d="..."/>
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item) {
+
+ // non-empty elements
+ if (item.isElem() && !item.isElem('switch') && !item.isEmpty()) {
+
+ item.content.forEach(function(g, i) {
+
+ // non-empty groups
+ if (g.isElem('g') && !g.isEmpty()) {
+
+ // move group attibutes to the single content element
+ if (g.hasAttr() && g.content.length === 1) {
+ var inner = g.content[0];
+
+ if (inner.isElem() && !inner.hasAttr('id') &&
+ !(g.hasAttr('class') && inner.hasAttr('class')) && (
+ !g.hasAttr('clip-path') && !g.hasAttr('mask') ||
+ inner.isElem('g') && !g.hasAttr('transform') && !inner.hasAttr('transform')
+ )
+ ) {
+ g.eachAttr(function(attr) {
+ if (g.content.some(hasAnimatedAttr, attr.name)) return;
+
+ if (!inner.hasAttr(attr.name)) {
+ inner.addAttr(attr);
+ } else if (attr.name == 'transform') {
+ inner.attr(attr.name).value = attr.value + ' ' + inner.attr(attr.name).value;
+ } else if (
+ attrsInheritable.indexOf(attr.name) < 0 &&
+ !inner.hasAttr(attr.name, attr.value)
+ ) {
+ return;
+ }
+
+ g.removeAttr(attr.name);
+ });
+ }
+ }
+
+ // collapse groups without attributes
+ if (!g.hasAttr() && !g.content.some(function(item) { return item.isElem(animationElems) })) {
+ item.spliceContent(i, 1, g.content);
+ }
+ }
+
+ });
+
+ }
+
+};
diff --git a/node_modules/svgo/plugins/convertColors.js b/node_modules/svgo/plugins/convertColors.js
new file mode 100644
index 00000000..b856e130
--- /dev/null
+++ b/node_modules/svgo/plugins/convertColors.js
@@ -0,0 +1,127 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'converts colors: rgb() to #rrggbb and #rrggbb to #rgb';
+
+exports.params = {
+ currentColor: false,
+ names2hex: true,
+ rgb2hex: true,
+ shorthex: true,
+ shortname: true
+};
+
+var collections = require('./_collections'),
+ rNumber = '([+-]?(?:\\d*\\.\\d+|\\d+\\.?)%?)',
+ rComma = '\\s*,\\s*',
+ regRGB = new RegExp('^rgb\\(\\s*' + rNumber + rComma + rNumber + rComma + rNumber + '\\s*\\)$'),
+ regHEX = /^\#(([a-fA-F0-9])\2){3}$/,
+ none = /\bnone\b/i;
+
+/**
+ * Convert different colors formats in element attributes to hex.
+ *
+ * @see http://www.w3.org/TR/SVG/types.html#DataTypeColor
+ * @see http://www.w3.org/TR/SVG/single-page.html#types-ColorKeywords
+ *
+ * @example
+ * Convert color name keyword to long hex:
+ * fuchsia ➡ #ff00ff
+ *
+ * Convert rgb() to long hex:
+ * rgb(255, 0, 255) ➡ #ff00ff
+ * rgb(50%, 100, 100%) ➡ #7f64ff
+ *
+ * Convert long hex to short hex:
+ * #aabbcc ➡ #abc
+ *
+ * Convert hex to short name
+ * #000080 ➡ navy
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item, params) {
+
+ if (item.elem) {
+
+ item.eachAttr(function(attr) {
+
+ if (collections.colorsProps.indexOf(attr.name) > -1) {
+
+ var val = attr.value,
+ match;
+
+ // Convert colors to currentColor
+ if (params.currentColor) {
+ if (typeof params.currentColor === 'string') {
+ match = val === params.currentColor;
+ } else if (params.currentColor.exec) {
+ match = params.currentColor.exec(val);
+ } else {
+ match = !val.match(none);
+ }
+ if (match) {
+ val = 'currentColor';
+ }
+ }
+
+ // Convert color name keyword to long hex
+ if (params.names2hex && val.toLowerCase() in collections.colorsNames) {
+ val = collections.colorsNames[val.toLowerCase()];
+ }
+
+ // Convert rgb() to long hex
+ if (params.rgb2hex && (match = val.match(regRGB))) {
+ match = match.slice(1, 4).map(function(m) {
+ if (m.indexOf('%') > -1)
+ m = Math.round(parseFloat(m) * 2.55);
+
+ return Math.max(0, Math.min(m, 255));
+ });
+
+ val = rgb2hex(match);
+ }
+
+ // Convert long hex to short hex
+ if (params.shorthex && (match = val.match(regHEX))) {
+ val = '#' + match[0][1] + match[0][3] + match[0][5];
+ }
+
+ // Convert hex to short name
+ if (params.shortname && val in collections.colorsShortNames) {
+ val = collections.colorsShortNames[val];
+ }
+
+ attr.value = val;
+
+ }
+
+ });
+
+ }
+
+};
+
+/**
+ * Convert [r, g, b] to #rrggbb.
+ *
+ * @see https://gist.github.com/983535
+ *
+ * @example
+ * rgb2hex([255, 255, 255]) // '#ffffff'
+ *
+ * @param {Array} rgb [r, g, b]
+ * @return {String} #rrggbb
+ *
+ * @author Jed Schmidt
+ */
+function rgb2hex(rgb) {
+ return '#' + ('00000' + (rgb[0] << 16 | rgb[1] << 8 | rgb[2]).toString(16)).slice(-6).toUpperCase();
+}
diff --git a/node_modules/svgo/plugins/convertPathData.js b/node_modules/svgo/plugins/convertPathData.js
new file mode 100644
index 00000000..c76a52c2
--- /dev/null
+++ b/node_modules/svgo/plugins/convertPathData.js
@@ -0,0 +1,957 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'optimizes path data: writes in shorter form, applies transformations';
+
+exports.params = {
+ applyTransforms: true,
+ applyTransformsStroked: true,
+ makeArcs: {
+ threshold: 2.5, // coefficient of rounding error
+ tolerance: 0.5 // percentage of radius
+ },
+ straightCurves: true,
+ lineShorthands: true,
+ curveSmoothShorthands: true,
+ floatPrecision: 3,
+ transformPrecision: 5,
+ removeUseless: true,
+ collapseRepeated: true,
+ utilizeAbsolute: true,
+ leadingZero: true,
+ negativeExtraSpace: true
+};
+
+var pathElems = require('./_collections.js').pathElems,
+ path2js = require('./_path.js').path2js,
+ js2path = require('./_path.js').js2path,
+ applyTransforms = require('./_path.js').applyTransforms,
+ cleanupOutData = require('../lib/svgo/tools').cleanupOutData,
+ roundData,
+ precision,
+ error,
+ arcThreshold,
+ arcTolerance,
+ hasMarkerMid;
+
+/**
+ * Convert absolute Path to relative,
+ * collapse repeated instructions,
+ * detect and convert Lineto shorthands,
+ * remove useless instructions like "l0,0",
+ * trim useless delimiters and leading zeros,
+ * decrease accuracy of floating-point numbers.
+ *
+ * @see http://www.w3.org/TR/SVG/paths.html#PathData
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item, params) {
+
+ if (item.isElem(pathElems) && item.hasAttr('d')) {
+
+ precision = params.floatPrecision;
+ error = precision !== false ? +Math.pow(.1, precision).toFixed(precision) : 1e-2;
+ roundData = precision > 0 && precision < 20 ? strongRound : round;
+ if (params.makeArcs) {
+ arcThreshold = params.makeArcs.threshold;
+ arcTolerance = params.makeArcs.tolerance;
+ }
+ hasMarkerMid = item.hasAttr('marker-mid');
+
+ var data = path2js(item);
+
+ // TODO: get rid of functions returns
+ if (data.length) {
+ convertToRelative(data);
+
+ if (params.applyTransforms) {
+ data = applyTransforms(item, data, params);
+ }
+
+ data = filters(data, params);
+
+ if (params.utilizeAbsolute) {
+ data = convertToMixed(data, params);
+ }
+
+ js2path(item, data, params);
+ }
+
+ }
+
+};
+
+/**
+ * Convert absolute path data coordinates to relative.
+ *
+ * @param {Array} path input path data
+ * @param {Object} params plugin params
+ * @return {Array} output path data
+ */
+function convertToRelative(path) {
+
+ var point = [0, 0],
+ subpathPoint = [0, 0],
+ baseItem;
+
+ path.forEach(function(item, index) {
+
+ var instruction = item.instruction,
+ data = item.data;
+
+ // data !== !z
+ if (data) {
+
+ // already relative
+ // recalculate current point
+ if ('mcslqta'.indexOf(instruction) > -1) {
+
+ point[0] += data[data.length - 2];
+ point[1] += data[data.length - 1];
+
+ if (instruction === 'm') {
+ subpathPoint[0] = point[0];
+ subpathPoint[1] = point[1];
+ baseItem = item;
+ }
+
+ } else if (instruction === 'h') {
+
+ point[0] += data[0];
+
+ } else if (instruction === 'v') {
+
+ point[1] += data[0];
+
+ }
+
+ // convert absolute path data coordinates to relative
+ // if "M" was not transformed from "m"
+ // M → m
+ if (instruction === 'M') {
+
+ if (index > 0) instruction = 'm';
+
+ data[0] -= point[0];
+ data[1] -= point[1];
+
+ subpathPoint[0] = point[0] += data[0];
+ subpathPoint[1] = point[1] += data[1];
+
+ baseItem = item;
+
+ }
+
+ // L → l
+ // T → t
+ else if ('LT'.indexOf(instruction) > -1) {
+
+ instruction = instruction.toLowerCase();
+
+ // x y
+ // 0 1
+ data[0] -= point[0];
+ data[1] -= point[1];
+
+ point[0] += data[0];
+ point[1] += data[1];
+
+ // C → c
+ } else if (instruction === 'C') {
+
+ instruction = 'c';
+
+ // x1 y1 x2 y2 x y
+ // 0 1 2 3 4 5
+ data[0] -= point[0];
+ data[1] -= point[1];
+ data[2] -= point[0];
+ data[3] -= point[1];
+ data[4] -= point[0];
+ data[5] -= point[1];
+
+ point[0] += data[4];
+ point[1] += data[5];
+
+ // S → s
+ // Q → q
+ } else if ('SQ'.indexOf(instruction) > -1) {
+
+ instruction = instruction.toLowerCase();
+
+ // x1 y1 x y
+ // 0 1 2 3
+ data[0] -= point[0];
+ data[1] -= point[1];
+ data[2] -= point[0];
+ data[3] -= point[1];
+
+ point[0] += data[2];
+ point[1] += data[3];
+
+ // A → a
+ } else if (instruction === 'A') {
+
+ instruction = 'a';
+
+ // rx ry x-axis-rotation large-arc-flag sweep-flag x y
+ // 0 1 2 3 4 5 6
+ data[5] -= point[0];
+ data[6] -= point[1];
+
+ point[0] += data[5];
+ point[1] += data[6];
+
+ // H → h
+ } else if (instruction === 'H') {
+
+ instruction = 'h';
+
+ data[0] -= point[0];
+
+ point[0] += data[0];
+
+ // V → v
+ } else if (instruction === 'V') {
+
+ instruction = 'v';
+
+ data[0] -= point[1];
+
+ point[1] += data[0];
+
+ }
+
+ item.instruction = instruction;
+ item.data = data;
+
+ // store absolute coordinates for later use
+ item.coords = point.slice(-2);
+
+ }
+
+ // !data === z, reset current point
+ else if (instruction == 'z') {
+ if (baseItem) {
+ item.coords = baseItem.coords;
+ }
+ point[0] = subpathPoint[0];
+ point[1] = subpathPoint[1];
+ }
+
+ item.base = index > 0 ? path[index - 1].coords : [0, 0];
+
+ });
+
+ return path;
+
+}
+
+/**
+ * Main filters loop.
+ *
+ * @param {Array} path input path data
+ * @param {Object} params plugin params
+ * @return {Array} output path data
+ */
+function filters(path, params) {
+
+ var stringify = data2Path.bind(null, params),
+ relSubpoint = [0, 0],
+ pathBase = [0, 0],
+ prev = {};
+
+ path = path.filter(function(item, index, path) {
+
+ var instruction = item.instruction,
+ data = item.data,
+ next = path[index + 1];
+
+ if (data) {
+
+ var sdata = data,
+ circle;
+
+ if (instruction === 's') {
+ sdata = [0, 0].concat(data);
+
+ if ('cs'.indexOf(prev.instruction) > -1) {
+ var pdata = prev.data,
+ n = pdata.length;
+
+ // (-x, -y) of the prev tangent point relative to the current point
+ sdata[0] = pdata[n - 2] - pdata[n - 4];
+ sdata[1] = pdata[n - 1] - pdata[n - 3];
+ }
+
+ }
+
+ // convert curves to arcs if possible
+ if (
+ params.makeArcs &&
+ (instruction == 'c' || instruction == 's') &&
+ isConvex(sdata) &&
+ (circle = findCircle(sdata))
+ ) {
+ var r = roundData([circle.radius])[0],
+ angle = findArcAngle(sdata, circle),
+ sweep = sdata[5] * sdata[0] - sdata[4] * sdata[1] > 0 ? 1 : 0,
+ arc = {
+ instruction: 'a',
+ data: [r, r, 0, 0, sweep, sdata[4], sdata[5]],
+ coords: item.coords.slice(),
+ base: item.base
+ },
+ output = [arc],
+ // relative coordinates to adjust the found circle
+ relCenter = [circle.center[0] - sdata[4], circle.center[1] - sdata[5]],
+ relCircle = { center: relCenter, radius: circle.radius },
+ arcCurves = [item],
+ hasPrev = 0,
+ suffix = '',
+ nextLonghand;
+
+ if (
+ prev.instruction == 'c' && isConvex(prev.data) && isArcPrev(prev.data, circle) ||
+ prev.instruction == 'a' && prev.sdata && isArcPrev(prev.sdata, circle)
+ ) {
+ arcCurves.unshift(prev);
+ arc.base = prev.base;
+ arc.data[5] = arc.coords[0] - arc.base[0];
+ arc.data[6] = arc.coords[1] - arc.base[1];
+ var prevData = prev.instruction == 'a' ? prev.sdata : prev.data;
+ angle += findArcAngle(prevData,
+ {
+ center: [prevData[4] + relCenter[0], prevData[5] + relCenter[1]],
+ radius: circle.radius
+ }
+ );
+ if (angle > Math.PI) arc.data[3] = 1;
+ hasPrev = 1;
+ }
+
+ // check if next curves are fitting the arc
+ for (var j = index; (next = path[++j]) && ~'cs'.indexOf(next.instruction);) {
+ var nextData = next.data;
+ if (next.instruction == 's') {
+ nextLonghand = makeLonghand({instruction: 's', data: next.data.slice() },
+ path[j - 1].data);
+ nextData = nextLonghand.data;
+ nextLonghand.data = nextData.slice(0, 2);
+ suffix = stringify([nextLonghand]);
+ }
+ if (isConvex(nextData) && isArc(nextData, relCircle)) {
+ angle += findArcAngle(nextData, relCircle);
+ if (angle - 2 * Math.PI > 1e-3) break; // more than 360°
+ if (angle > Math.PI) arc.data[3] = 1;
+ arcCurves.push(next);
+ if (2 * Math.PI - angle > 1e-3) { // less than 360°
+ arc.coords = next.coords;
+ arc.data[5] = arc.coords[0] - arc.base[0];
+ arc.data[6] = arc.coords[1] - arc.base[1];
+ } else {
+ // full circle, make a half-circle arc and add a second one
+ arc.data[5] = 2 * (relCircle.center[0] - nextData[4]);
+ arc.data[6] = 2 * (relCircle.center[1] - nextData[5]);
+ arc.coords = [arc.base[0] + arc.data[5], arc.base[1] + arc.data[6]];
+ arc = {
+ instruction: 'a',
+ data: [r, r, 0, 0, sweep,
+ next.coords[0] - arc.coords[0], next.coords[1] - arc.coords[1]],
+ coords: next.coords,
+ base: arc.coords
+ };
+ output.push(arc);
+ j++;
+ break;
+ }
+ relCenter[0] -= nextData[4];
+ relCenter[1] -= nextData[5];
+ } else break;
+ }
+
+ if ((stringify(output) + suffix).length < stringify(arcCurves).length) {
+ if (path[j] && path[j].instruction == 's') {
+ makeLonghand(path[j], path[j - 1].data);
+ }
+ if (hasPrev) {
+ var prevArc = output.shift();
+ roundData(prevArc.data);
+ relSubpoint[0] += prevArc.data[5] - prev.data[prev.data.length - 2];
+ relSubpoint[1] += prevArc.data[6] - prev.data[prev.data.length - 1];
+ prev.instruction = 'a';
+ prev.data = prevArc.data;
+ item.base = prev.coords = prevArc.coords;
+ }
+ arc = output.shift();
+ if (arcCurves.length == 1) {
+ item.sdata = sdata.slice(); // preserve curve data for future checks
+ } else if (arcCurves.length - 1 - hasPrev > 0) {
+ // filter out consumed next items
+ path.splice.apply(path, [index + 1, arcCurves.length - 1 - hasPrev].concat(output));
+ }
+ if (!arc) return false;
+ instruction = 'a';
+ data = arc.data;
+ item.coords = arc.coords;
+ }
+ }
+
+ // Rounding relative coordinates, taking in account accummulating error
+ // to get closer to absolute coordinates. Sum of rounded value remains same:
+ // l .25 3 .25 2 .25 3 .25 2 -> l .3 3 .2 2 .3 3 .2 2
+ if (precision !== false) {
+ if ('mltqsc'.indexOf(instruction) > -1) {
+ for (var i = data.length; i--;) {
+ data[i] += item.base[i % 2] - relSubpoint[i % 2];
+ }
+ } else if (instruction == 'h') {
+ data[0] += item.base[0] - relSubpoint[0];
+ } else if (instruction == 'v') {
+ data[0] += item.base[1] - relSubpoint[1];
+ } else if (instruction == 'a') {
+ data[5] += item.base[0] - relSubpoint[0];
+ data[6] += item.base[1] - relSubpoint[1];
+ }
+ roundData(data);
+
+ if (instruction == 'h') relSubpoint[0] += data[0];
+ else if (instruction == 'v') relSubpoint[1] += data[0];
+ else {
+ relSubpoint[0] += data[data.length - 2];
+ relSubpoint[1] += data[data.length - 1];
+ }
+ roundData(relSubpoint);
+
+ if (instruction.toLowerCase() == 'm') {
+ pathBase[0] = relSubpoint[0];
+ pathBase[1] = relSubpoint[1];
+ }
+ }
+
+ // convert straight curves into lines segments
+ if (params.straightCurves) {
+
+ if (
+ instruction === 'c' &&
+ isCurveStraightLine(data) ||
+ instruction === 's' &&
+ isCurveStraightLine(sdata)
+ ) {
+ if (next && next.instruction == 's')
+ makeLonghand(next, data); // fix up next curve
+ instruction = 'l';
+ data = data.slice(-2);
+ }
+
+ else if (
+ instruction === 'q' &&
+ isCurveStraightLine(data)
+ ) {
+ if (next && next.instruction == 't')
+ makeLonghand(next, data); // fix up next curve
+ instruction = 'l';
+ data = data.slice(-2);
+ }
+
+ else if (
+ instruction === 't' &&
+ prev.instruction !== 'q' &&
+ prev.instruction !== 't'
+ ) {
+ instruction = 'l';
+ data = data.slice(-2);
+ }
+
+ else if (
+ instruction === 'a' &&
+ (data[0] === 0 || data[1] === 0)
+ ) {
+ instruction = 'l';
+ data = data.slice(-2);
+ }
+ }
+
+ // horizontal and vertical line shorthands
+ // l 50 0 → h 50
+ // l 0 50 → v 50
+ if (
+ params.lineShorthands &&
+ instruction === 'l'
+ ) {
+ if (data[1] === 0) {
+ instruction = 'h';
+ data.pop();
+ } else if (data[0] === 0) {
+ instruction = 'v';
+ data.shift();
+ }
+ }
+
+ // collapse repeated commands
+ // h 20 h 30 -> h 50
+ if (
+ params.collapseRepeated &&
+ !hasMarkerMid &&
+ ('mhv'.indexOf(instruction) > -1) &&
+ prev.instruction &&
+ instruction == prev.instruction.toLowerCase() &&
+ (
+ (instruction != 'h' && instruction != 'v') ||
+ (prev.data[0] >= 0) == (item.data[0] >= 0)
+ )) {
+ prev.data[0] += data[0];
+ if (instruction != 'h' && instruction != 'v') {
+ prev.data[1] += data[1];
+ }
+ prev.coords = item.coords;
+ path[index] = prev;
+ return false;
+ }
+
+ // convert curves into smooth shorthands
+ if (params.curveSmoothShorthands && prev.instruction) {
+
+ // curveto
+ if (instruction === 'c') {
+
+ // c + c → c + s
+ if (
+ prev.instruction === 'c' &&
+ data[0] === -(prev.data[2] - prev.data[4]) &&
+ data[1] === -(prev.data[3] - prev.data[5])
+ ) {
+ instruction = 's';
+ data = data.slice(2);
+ }
+
+ // s + c → s + s
+ else if (
+ prev.instruction === 's' &&
+ data[0] === -(prev.data[0] - prev.data[2]) &&
+ data[1] === -(prev.data[1] - prev.data[3])
+ ) {
+ instruction = 's';
+ data = data.slice(2);
+ }
+
+ // [^cs] + c → [^cs] + s
+ else if (
+ 'cs'.indexOf(prev.instruction) === -1 &&
+ data[0] === 0 &&
+ data[1] === 0
+ ) {
+ instruction = 's';
+ data = data.slice(2);
+ }
+
+ }
+
+ // quadratic Bézier curveto
+ else if (instruction === 'q') {
+
+ // q + q → q + t
+ if (
+ prev.instruction === 'q' &&
+ data[0] === (prev.data[2] - prev.data[0]) &&
+ data[1] === (prev.data[3] - prev.data[1])
+ ) {
+ instruction = 't';
+ data = data.slice(2);
+ }
+
+ // t + q → t + t
+ else if (
+ prev.instruction === 't' &&
+ data[2] === prev.data[0] &&
+ data[3] === prev.data[1]
+ ) {
+ instruction = 't';
+ data = data.slice(2);
+ }
+
+ }
+
+ }
+
+ // remove useless non-first path segments
+ if (params.removeUseless) {
+
+ // l 0,0 / h 0 / v 0 / q 0,0 0,0 / t 0,0 / c 0,0 0,0 0,0 / s 0,0 0,0
+ if (
+ (
+ 'lhvqtcs'.indexOf(instruction) > -1
+ ) &&
+ data.every(function(i) { return i === 0; })
+ ) {
+ path[index] = prev;
+ return false;
+ }
+
+ // a 25,25 -30 0,1 0,0
+ if (
+ instruction === 'a' &&
+ data[5] === 0 &&
+ data[6] === 0
+ ) {
+ path[index] = prev;
+ return false;
+ }
+
+ }
+
+ item.instruction = instruction;
+ item.data = data;
+
+ prev = item;
+
+ } else {
+
+ // z resets coordinates
+ relSubpoint[0] = pathBase[0];
+ relSubpoint[1] = pathBase[1];
+ if (prev.instruction == 'z') return false;
+ prev = item;
+
+ }
+
+ return true;
+
+ });
+
+ return path;
+
+}
+
+/**
+ * Writes data in shortest form using absolute or relative coordinates.
+ *
+ * @param {Array} data input path data
+ * @return {Boolean} output
+ */
+function convertToMixed(path, params) {
+
+ var prev = path[0];
+
+ path = path.filter(function(item, index) {
+
+ if (index == 0) return true;
+ if (!item.data) {
+ prev = item;
+ return true;
+ }
+
+ var instruction = item.instruction,
+ data = item.data,
+ adata = data && data.slice(0);
+
+ if ('mltqsc'.indexOf(instruction) > -1) {
+ for (var i = adata.length; i--;) {
+ adata[i] += item.base[i % 2];
+ }
+ } else if (instruction == 'h') {
+ adata[0] += item.base[0];
+ } else if (instruction == 'v') {
+ adata[0] += item.base[1];
+ } else if (instruction == 'a') {
+ adata[5] += item.base[0];
+ adata[6] += item.base[1];
+ }
+
+ roundData(adata);
+
+ var absoluteDataStr = cleanupOutData(adata, params),
+ relativeDataStr = cleanupOutData(data, params);
+
+ // Convert to absolute coordinates if it's shorter.
+ // v-20 -> V0
+ // Don't convert if it fits following previous instruction.
+ // l20 30-10-50 instead of l20 30L20 30
+ if (
+ absoluteDataStr.length < relativeDataStr.length &&
+ !(
+ params.negativeExtraSpace &&
+ instruction == prev.instruction &&
+ prev.instruction.charCodeAt(0) > 96 &&
+ absoluteDataStr.length == relativeDataStr.length - 1 &&
+ (data[0] < 0 || /^0\./.test(data[0]) && prev.data[prev.data.length - 1] % 1)
+ )
+ ) {
+ item.instruction = instruction.toUpperCase();
+ item.data = adata;
+ }
+
+ prev = item;
+
+ return true;
+
+ });
+
+ return path;
+
+}
+
+/**
+ * Checks if curve is convex. Control points of such a curve must form
+ * a convex quadrilateral with diagonals crosspoint inside of it.
+ *
+ * @param {Array} data input path data
+ * @return {Boolean} output
+ */
+function isConvex(data) {
+
+ var center = getIntersection([0, 0, data[2], data[3], data[0], data[1], data[4], data[5]]);
+
+ return center &&
+ (data[2] < center[0] == center[0] < 0) &&
+ (data[3] < center[1] == center[1] < 0) &&
+ (data[4] < center[0] == center[0] < data[0]) &&
+ (data[5] < center[1] == center[1] < data[1]);
+
+}
+
+/**
+ * Computes lines equations by two points and returns their intersection point.
+ *
+ * @param {Array} coords 8 numbers for 4 pairs of coordinates (x,y)
+ * @return {Array|undefined} output coordinate of lines' crosspoint
+ */
+function getIntersection(coords) {
+
+ // Prev line equation parameters.
+ var a1 = coords[1] - coords[3], // y1 - y2
+ b1 = coords[2] - coords[0], // x2 - x1
+ c1 = coords[0] * coords[3] - coords[2] * coords[1], // x1 * y2 - x2 * y1
+
+ // Next line equation parameters
+ a2 = coords[5] - coords[7], // y1 - y2
+ b2 = coords[6] - coords[4], // x2 - x1
+ c2 = coords[4] * coords[7] - coords[5] * coords[6], // x1 * y2 - x2 * y1
+ denom = (a1 * b2 - a2 * b1);
+
+ if (!denom) return; // parallel lines havn't an intersection
+
+ var cross = [
+ (b1 * c2 - b2 * c1) / denom,
+ (a1 * c2 - a2 * c1) / -denom
+ ];
+ if (
+ !isNaN(cross[0]) && !isNaN(cross[1]) &&
+ isFinite(cross[0]) && isFinite(cross[1])
+ ) {
+ return cross;
+ }
+
+}
+
+/**
+ * Decrease accuracy of floating-point numbers
+ * in path data keeping a specified number of decimals.
+ * Smart rounds values like 2.3491 to 2.35 instead of 2.349.
+ * Doesn't apply "smartness" if the number precision fits already.
+ *
+ * @param {Array} data input data array
+ * @return {Array} output data array
+ */
+function strongRound(data) {
+ for (var i = data.length; i-- > 0;) {
+ if (data[i].toFixed(precision) != data[i]) {
+ var rounded = +data[i].toFixed(precision - 1);
+ data[i] = +Math.abs(rounded - data[i]).toFixed(precision + 1) >= error ?
+ +data[i].toFixed(precision) :
+ rounded;
+ }
+ }
+ return data;
+}
+
+/**
+ * Simple rounding function if precision is 0.
+ *
+ * @param {Array} data input data array
+ * @return {Array} output data array
+ */
+function round(data) {
+ for (var i = data.length; i-- > 0;) {
+ data[i] = Math.round(data[i]);
+ }
+ return data;
+}
+
+/**
+ * Checks if a curve is a straight line by measuring distance
+ * from middle points to the line formed by end points.
+ *
+ * @param {Array} xs array of curve points x-coordinates
+ * @param {Array} ys array of curve points y-coordinates
+ * @return {Boolean}
+ */
+
+function isCurveStraightLine(data) {
+
+ // Get line equation a·x + b·y + c = 0 coefficients a, b (c = 0) by start and end points.
+ var i = data.length - 2,
+ a = -data[i + 1], // y1 − y2 (y1 = 0)
+ b = data[i], // x2 − x1 (x1 = 0)
+ d = 1 / (a * a + b * b); // same part for all points
+
+ if (i <= 1 || !isFinite(d)) return false; // curve that ends at start point isn't the case
+
+ // Distance from point (x0, y0) to the line is sqrt((c − a·x0 − b·y0)² / (a² + b²))
+ while ((i -= 2) >= 0) {
+ if (Math.sqrt(Math.pow(a * data[i] + b * data[i + 1], 2) * d) > error)
+ return false;
+ }
+
+ return true;
+
+}
+
+/**
+ * Converts next curve from shorthand to full form using the current curve data.
+ *
+ * @param {Object} item curve to convert
+ * @param {Array} data current curve data
+ */
+
+function makeLonghand(item, data) {
+ switch (item.instruction) {
+ case 's': item.instruction = 'c'; break;
+ case 't': item.instruction = 'q'; break;
+ }
+ item.data.unshift(data[data.length - 2] - data[data.length - 4], data[data.length - 1] - data[data.length - 3]);
+ return item;
+}
+
+/**
+ * Returns distance between two points
+ *
+ * @param {Array} point1 first point coordinates
+ * @param {Array} point2 second point coordinates
+ * @return {Number} distance
+ */
+
+function getDistance(point1, point2) {
+ return Math.sqrt(Math.pow(point1[0] - point2[0], 2) + Math.pow(point1[1] - point2[1], 2));
+}
+
+/**
+ * Returns coordinates of the curve point corresponding to the certain t
+ * a·(1 - t)³·p1 + b·(1 - t)²·t·p2 + c·(1 - t)·t²·p3 + d·t³·p4,
+ * where pN are control points and p1 is zero due to relative coordinates.
+ *
+ * @param {Array} curve array of curve points coordinates
+ * @param {Number} t parametric position from 0 to 1
+ * @return {Array} Point coordinates
+ */
+
+function getCubicBezierPoint(curve, t) {
+ var sqrT = t * t,
+ cubT = sqrT * t,
+ mt = 1 - t,
+ sqrMt = mt * mt;
+
+ return [
+ 3 * sqrMt * t * curve[0] + 3 * mt * sqrT * curve[2] + cubT * curve[4],
+ 3 * sqrMt * t * curve[1] + 3 * mt * sqrT * curve[3] + cubT * curve[5]
+ ];
+}
+
+/**
+ * Finds circle by 3 points of the curve and checks if the curve fits the found circle.
+ *
+ * @param {Array} curve
+ * @return {Object|undefined} circle
+ */
+
+function findCircle(curve) {
+ var midPoint = getCubicBezierPoint(curve, 1/2),
+ m1 = [midPoint[0] / 2, midPoint[1] / 2],
+ m2 = [(midPoint[0] + curve[4]) / 2, (midPoint[1] + curve[5]) / 2],
+ center = getIntersection([
+ m1[0], m1[1],
+ m1[0] + m1[1], m1[1] - m1[0],
+ m2[0], m2[1],
+ m2[0] + (m2[1] - midPoint[1]), m2[1] - (m2[0] - midPoint[0])
+ ]),
+ radius = center && getDistance([0, 0], center),
+ tolerance = Math.min(arcThreshold * error, arcTolerance * radius / 100);
+
+ if (center && [1/4, 3/4].every(function(point) {
+ return Math.abs(getDistance(getCubicBezierPoint(curve, point), center) - radius) <= tolerance;
+ }))
+ return { center: center, radius: radius};
+}
+
+/**
+ * Checks if a curve fits the given circe.
+ *
+ * @param {Object} circle
+ * @param {Array} curve
+ * @return {Boolean}
+ */
+
+function isArc(curve, circle) {
+ var tolerance = Math.min(arcThreshold * error, arcTolerance * circle.radius / 100);
+
+ return [0, 1/4, 1/2, 3/4, 1].every(function(point) {
+ return Math.abs(getDistance(getCubicBezierPoint(curve, point), circle.center) - circle.radius) <= tolerance;
+ });
+}
+
+/**
+ * Checks if a previos curve fits the given circe.
+ *
+ * @param {Object} circle
+ * @param {Array} curve
+ * @return {Boolean}
+ */
+
+function isArcPrev(curve, circle) {
+ return isArc(curve, {
+ center: [circle.center[0] + curve[4], circle.center[1] + curve[5]],
+ radius: circle.radius
+ });
+}
+
+/**
+ * Finds angle of a curve fitting the given arc.
+
+ * @param {Array} curve
+ * @param {Object} relCircle
+ * @return {Number} angle
+ */
+
+function findArcAngle(curve, relCircle) {
+ var x1 = -relCircle.center[0],
+ y1 = -relCircle.center[1],
+ x2 = curve[4] - relCircle.center[0],
+ y2 = curve[5] - relCircle.center[1];
+
+ return Math.acos(
+ (x1 * x2 + y1 * y2) /
+ Math.sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2))
+ );
+}
+
+/**
+ * Converts given path data to string.
+ *
+ * @param {Object} params
+ * @param {Array} pathData
+ * @return {String}
+ */
+
+function data2Path(params, pathData) {
+ return pathData.reduce(function(pathString, item) {
+ return pathString += item.instruction + (item.data ? cleanupOutData(roundData(item.data.slice()), params) : '');
+ }, '');
+}
diff --git a/node_modules/svgo/plugins/convertShapeToPath.js b/node_modules/svgo/plugins/convertShapeToPath.js
new file mode 100644
index 00000000..6d328049
--- /dev/null
+++ b/node_modules/svgo/plugins/convertShapeToPath.js
@@ -0,0 +1,103 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'converts basic shapes to more compact path form';
+
+var none = { value: 0 },
+ regNumber = /[-+]?(?:\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/g;
+
+/**
+ * Converts basic shape to more compact path.
+ * It also allows further optimizations like
+ * combining paths with similar attributes.
+ *
+ * @see http://www.w3.org/TR/SVG/shapes.html
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Lev Solntsev
+ */
+exports.fn = function(item) {
+
+ if (
+ item.isElem('rect') &&
+ item.hasAttr('width') &&
+ item.hasAttr('height') &&
+ !item.hasAttr('rx') &&
+ !item.hasAttr('ry')
+ ) {
+
+ var x = +(item.attr('x') || none).value,
+ y = +(item.attr('y') || none).value,
+ width = +item.attr('width').value,
+ height = +item.attr('height').value;
+
+ // Values like '100%' compute to NaN, thus running after
+ // cleanupNumericValues when 'px' units has already been removed.
+ // TODO: Calculate sizes from % and non-px units if possible.
+ if (isNaN(x - y + width - height)) return;
+
+ var pathData =
+ 'M' + x + ' ' + y +
+ 'H' + (x + width) +
+ 'V' + (y + height) +
+ 'H' + x +
+ 'z';
+
+ item.addAttr({
+ name: 'd',
+ value: pathData,
+ prefix: '',
+ local: 'd'
+ });
+
+ item.renameElem('path')
+ .removeAttr(['x', 'y', 'width', 'height']);
+
+ } else if (item.isElem('line')) {
+
+ var x1 = +(item.attr('x1') || none).value,
+ y1 = +(item.attr('y1') || none).value,
+ x2 = +(item.attr('x2') || none).value,
+ y2 = +(item.attr('y2') || none).value;
+ if (isNaN(x1 - y1 + x2 - y2)) return;
+
+ item.addAttr({
+ name: 'd',
+ value: 'M' + x1 + ' ' + y1 + 'L' + x2 + ' ' + y2,
+ prefix: '',
+ local: 'd'
+ });
+
+ item.renameElem('path')
+ .removeAttr(['x1', 'y1', 'x2', 'y2']);
+
+ } else if ((
+ item.isElem('polyline') ||
+ item.isElem('polygon')
+ ) &&
+ item.hasAttr('points')
+ ) {
+
+ var coords = (item.attr('points').value.match(regNumber) || []).map(Number);
+ if (coords.length < 4) return false;
+
+ item.addAttr({
+ name: 'd',
+ value: 'M' + coords.slice(0,2).join(' ') +
+ 'L' + coords.slice(2).join(' ') +
+ (item.isElem('polygon') ? 'z' : ''),
+ prefix: '',
+ local: 'd'
+ });
+
+ item.renameElem('path')
+ .removeAttr('points');
+ }
+
+};
diff --git a/node_modules/svgo/plugins/convertStyleToAttrs.js b/node_modules/svgo/plugins/convertStyleToAttrs.js
new file mode 100644
index 00000000..02bbc550
--- /dev/null
+++ b/node_modules/svgo/plugins/convertStyleToAttrs.js
@@ -0,0 +1,117 @@
+/* jshint quotmark: false */
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'converts style to attributes';
+
+var EXTEND = require('whet.extend'),
+ stylingProps = require('./_collections').attrsGroups.presentation,
+ rEscape = '\\\\(?:[0-9a-f]{1,6}\\s?|\\r\\n|.)', // Like \" or \2051. Code points consume one space.
+ rAttr = '\\s*(' + g('[^:;\\\\]', rEscape) + '*?)\\s*', // attribute name like ‘fill’
+ rSingleQuotes = "'(?:[^'\\n\\r\\\\]|" + rEscape + ")*?(?:'|$)", // string in single quotes: 'smth'
+ rQuotes = '"(?:[^"\\n\\r\\\\]|' + rEscape + ')*?(?:"|$)', // string in double quotes: "smth"
+ rQuotedString = new RegExp('^' + g(rSingleQuotes, rQuotes) + '$'),
+
+ // Parentheses, E.g.: url(data:image/png;base64,iVBO...).
+ // ':' and ';' inside of it should be threated as is. (Just like in strings.)
+ rParenthesis = '\\(' + g('[^\'"()\\\\]+', rEscape, rSingleQuotes, rQuotes) + '*?' + '\\)',
+
+ // The value. It can have strings and parentheses (see above). Fallbacks to anything in case of unexpected input.
+ rValue = '\\s*(' + g('[^\'"();\\\\]+?', rEscape, rSingleQuotes, rQuotes, rParenthesis, '[^;]*?') + '*?' + ')',
+
+ // End of declaration. Spaces outside of capturing groups help to do natural trimming.
+ rDeclEnd = '\\s*(?:;\\s*|$)',
+
+ // Final RegExp to parse CSS declarations.
+ regDeclarationBlock = new RegExp(rAttr + ':' + rValue + rDeclEnd, 'ig'),
+
+ // Comments expression. Honors escape sequences and strings.
+ regStripComments = new RegExp(g(rEscape, rSingleQuotes, rQuotes, '/\\*[^]*?\\*/'), 'ig');
+
+/**
+ * Convert style in attributes. Cleanups comments and illegal declarations (without colon) as a side effect.
+ *
+ * @example
+ * <g style="fill:#000; color: #fff;">
+ * ⬇
+ * <g fill="#000" color="#fff">
+ *
+ * @example
+ * <g style="fill:#000; color: #fff; -webkit-blah: blah">
+ * ⬇
+ * <g fill="#000" color="#fff" style="-webkit-blah: blah">
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item) {
+ /* jshint boss: true */
+
+ if (item.elem && item.hasAttr('style')) {
+ // ['opacity: 1', 'color: #000']
+ var styleValue = item.attr('style').value,
+ styles = [],
+ attrs = {};
+
+ // Strip CSS comments preserving escape sequences and strings.
+ styleValue = styleValue.replace(regStripComments, function(match) {
+ return match[0] == '/' ? '' :
+ match[0] == '\\' && /[-g-z]/i.test(match[1]) ? match[1] : match;
+ });
+
+ regDeclarationBlock.lastIndex = 0;
+ for (var rule; rule = regDeclarationBlock.exec(styleValue);) {
+ styles.push([rule[1], rule[2]]);
+ }
+
+ if (styles.length) {
+
+ styles = styles.filter(function(style) {
+ if (style[0]) {
+ var prop = style[0].toLowerCase(),
+ val = style[1];
+
+ if (rQuotedString.test(val)) {
+ val = val.slice(1, -1);
+ }
+
+ if (stylingProps.indexOf(prop) > -1) {
+
+ attrs[prop] = {
+ name: prop,
+ value: val,
+ local: prop,
+ prefix: ''
+ };
+
+ return false;
+ }
+ }
+
+ return true;
+ });
+
+ EXTEND(item.attrs, attrs);
+
+ if (styles.length) {
+ item.attr('style').value = styles
+ .map(function(declaration) { return declaration.join(':') })
+ .join(';');
+ } else {
+ item.removeAttr('style');
+ }
+
+ }
+
+ }
+
+};
+
+function g() {
+ return '(?:' + Array.prototype.join.call(arguments, '|') + ')';
+}
diff --git a/node_modules/svgo/plugins/convertTransform.js b/node_modules/svgo/plugins/convertTransform.js
new file mode 100644
index 00000000..c2085892
--- /dev/null
+++ b/node_modules/svgo/plugins/convertTransform.js
@@ -0,0 +1,364 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'collapses multiple transformations and optimizes it';
+
+exports.params = {
+ convertToShorts: true,
+ // degPrecision: 3, // transformPrecision (or matrix precision) - 2 by default
+ floatPrecision: 3,
+ transformPrecision: 5,
+ matrixToTransform: true,
+ shortTranslate: true,
+ shortScale: true,
+ shortRotate: true,
+ removeUseless: true,
+ collapseIntoOne: true,
+ leadingZero: true,
+ negativeExtraSpace: false
+};
+
+var cleanupOutData = require('../lib/svgo/tools').cleanupOutData,
+ EXTEND = require('whet.extend'),
+ transform2js = require('./_transforms.js').transform2js,
+ transformsMultiply = require('./_transforms.js').transformsMultiply,
+ matrixToTransform = require('./_transforms.js').matrixToTransform,
+ degRound,
+ floatRound,
+ transformRound;
+
+/**
+ * Convert matrices to the short aliases,
+ * convert long translate, scale or rotate transform notations to the shorts ones,
+ * convert transforms to the matrices and multiply them all into one,
+ * remove useless transforms.
+ *
+ * @see http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item, params) {
+
+ if (item.elem) {
+
+ // transform
+ if (item.hasAttr('transform')) {
+ convertTransform(item, 'transform', params);
+ }
+
+ // gradientTransform
+ if (item.hasAttr('gradientTransform')) {
+ convertTransform(item, 'gradientTransform', params);
+ }
+
+ // patternTransform
+ if (item.hasAttr('patternTransform')) {
+ convertTransform(item, 'patternTransform', params);
+ }
+
+ }
+
+};
+
+/**
+ * Main function.
+ *
+ * @param {Object} item input item
+ * @param {String} attrName attribute name
+ * @param {Object} params plugin params
+ */
+function convertTransform(item, attrName, params) {
+ var data = transform2js(item.attr(attrName).value);
+ params = definePrecision(data, params);
+
+ if (params.collapseIntoOne && data.length > 1) {
+ data = [transformsMultiply(data)];
+ }
+
+ if (params.convertToShorts) {
+ data = convertToShorts(data, params);
+ } else {
+ data.forEach(roundTransform);
+ }
+
+ if (params.removeUseless) {
+ data = removeUseless(data);
+ }
+
+ if (data.length) {
+ item.attr(attrName).value = js2transform(data, params);
+ } else {
+ item.removeAttr(attrName);
+ }
+}
+
+/**
+ * Defines precision to work with certain parts.
+ * transformPrecision - for scale and four first matrix parameters (needs a better precision due to multiplying),
+ * floatPrecision - for translate including two last matrix and rotate parameters,
+ * degPrecision - for rotate and skew. By default it's equal to (rougly)
+ * transformPrecision - 2 or floatPrecision whichever is lower. Can be set in params.
+ *
+ * @param {Array} transforms input array
+ * @param {Object} params plugin params
+ * @return {Array} output array
+ */
+function definePrecision(data, params) {
+ /* jshint validthis: true */
+ var matrixData = data.reduce(getMatrixData, []),
+ significantDigits = params.transformPrecision;
+
+ // Clone params so it don't affect other elements transformations.
+ params = EXTEND({}, params);
+
+ // Limit transform precision with matrix one. Calculating with larger precision doesn't add any value.
+ if (matrixData.length) {
+ params.transformPrecision = Math.min(params.transformPrecision,
+ Math.max.apply(Math, matrixData.map(floatDigits)) || params.transformPrecision);
+
+ significantDigits = Math.max.apply(Math, matrixData.map(function(n) {
+ return String(n).replace(/\D+/g, '').length; // Number of digits in a number. 123.45 → 5
+ }));
+ }
+ // No sense in angle precision more then number of significant digits in matrix.
+ if (!('degPrecision' in params)) {
+ params.degPrecision = Math.max(0, Math.min(params.floatPrecision, significantDigits - 2));
+ }
+
+ floatRound = params.floatPrecision >= 1 && params.floatPrecision < 20 ?
+ smartRound.bind(this, params.floatPrecision) :
+ round;
+ degRound = params.degPrecision >= 1 && params.floatPrecision < 20 ?
+ smartRound.bind(this, params.degPrecision) :
+ round;
+ transformRound = params.transformPrecision >= 1 && params.floatPrecision < 20 ?
+ smartRound.bind(this, params.transformPrecision) :
+ round;
+
+ return params;
+}
+
+/**
+ * Gathers four first matrix parameters.
+ *
+ * @param {Array} a array of data
+ * @param {Object} transform
+ * @return {Array} output array
+ */
+function getMatrixData(a, b) {
+ return b.name == 'matrix' ? a.concat(b.data.slice(0, 4)) : a;
+}
+
+/**
+ * Returns number of digits after the point. 0.125 → 3
+ */
+function floatDigits(n) {
+ return (n = String(n)).slice(n.indexOf('.')).length - 1;
+}
+
+/**
+ * Convert transforms to the shorthand alternatives.
+ *
+ * @param {Array} transforms input array
+ * @param {Object} params plugin params
+ * @return {Array} output array
+ */
+function convertToShorts(transforms, params) {
+
+ for(var i = 0; i < transforms.length; i++) {
+
+ var transform = transforms[i];
+
+ // convert matrix to the short aliases
+ if (
+ params.matrixToTransform &&
+ transform.name === 'matrix'
+ ) {
+ var decomposed = matrixToTransform(transform, params);
+ if (decomposed != transform &&
+ js2transform(decomposed, params).length <= js2transform([transform], params).length) {
+
+ transforms.splice.apply(transforms, [i, 1].concat(decomposed));
+ }
+ transform = transforms[i];
+ }
+
+ // fixed-point numbers
+ // 12.754997 → 12.755
+ roundTransform(transform);
+
+ // convert long translate transform notation to the shorts one
+ // translate(10 0) → translate(10)
+ if (
+ params.shortTranslate &&
+ transform.name === 'translate' &&
+ transform.data.length === 2 &&
+ !transform.data[1]
+ ) {
+ transform.data.pop();
+ }
+
+ // convert long scale transform notation to the shorts one
+ // scale(2 2) → scale(2)
+ if (
+ params.shortScale &&
+ transform.name === 'scale' &&
+ transform.data.length === 2 &&
+ transform.data[0] === transform.data[1]
+ ) {
+ transform.data.pop();
+ }
+
+ // convert long rotate transform notation to the short one
+ // translate(cx cy) rotate(a) translate(-cx -cy) → rotate(a cx cy)
+ if (
+ params.shortRotate &&
+ transforms[i - 2] &&
+ transforms[i - 2].name === 'translate' &&
+ transforms[i - 1].name === 'rotate' &&
+ transforms[i].name === 'translate' &&
+ transforms[i - 2].data[0] === -transforms[i].data[0] &&
+ transforms[i - 2].data[1] === -transforms[i].data[1]
+ ) {
+ transforms.splice(i - 2, 3, {
+ name: 'rotate',
+ data: [
+ transforms[i - 1].data[0],
+ transforms[i - 2].data[0],
+ transforms[i - 2].data[1]
+ ]
+ });
+
+ // splice compensation
+ i -= 2;
+
+ transform = transforms[i];
+ }
+
+ }
+
+ return transforms;
+
+}
+
+/**
+ * Remove useless transforms.
+ *
+ * @param {Array} transforms input array
+ * @return {Array} output array
+ */
+function removeUseless(transforms) {
+
+ return transforms.filter(function(transform) {
+
+ // translate(0), rotate(0[, cx, cy]), skewX(0), skewY(0)
+ if (
+ ['translate', 'rotate', 'skewX', 'skewY'].indexOf(transform.name) > -1 &&
+ (transform.data.length == 1 || transform.name == 'rotate') &&
+ !transform.data[0] ||
+
+ // translate(0, 0)
+ transform.name == 'translate' &&
+ !transform.data[0] &&
+ !transform.data[1] ||
+
+ // scale(1)
+ transform.name == 'scale' &&
+ transform.data[0] == 1 &&
+ (transform.data.length < 2 || transform.data[1] == 1) ||
+
+ // matrix(1 0 0 1 0 0)
+ transform.name == 'matrix' &&
+ transform.data[0] == 1 &&
+ transform.data[3] == 1 &&
+ !(transform.data[1] || transform.data[2] || transform.data[4] || transform.data[5])
+ ) {
+ return false;
+ }
+
+ return true;
+
+ });
+
+}
+
+/**
+ * Convert transforms JS representation to string.
+ *
+ * @param {Array} transformJS JS representation array
+ * @param {Object} params plugin params
+ * @return {String} output string
+ */
+function js2transform(transformJS, params) {
+
+ var transformString = '';
+
+ // collect output value string
+ transformJS.forEach(function(transform) {
+ roundTransform(transform);
+ transformString += (transformString && ' ') + transform.name + '(' + cleanupOutData(transform.data, params) + ')';
+ });
+
+ return transformString;
+
+}
+
+function roundTransform(transform) {
+ switch (transform.name) {
+ case 'translate':
+ transform.data = floatRound(transform.data);
+ break;
+ case 'rotate':
+ transform.data = degRound(transform.data.slice(0, 1)).concat(floatRound(transform.data.slice(1)));
+ break;
+ case 'skewX':
+ case 'skewY':
+ transform.data = degRound(transform.data);
+ break;
+ case 'scale':
+ transform.data = transformRound(transform.data);
+ break;
+ case 'matrix':
+ transform.data = transformRound(transform.data.slice(0, 4)).concat(floatRound(transform.data.slice(4)));
+ break;
+ }
+ return transform;
+}
+
+/**
+ * Rounds numbers in array.
+ *
+ * @param {Array} data input data array
+ * @return {Array} output data array
+ */
+function round(data) {
+ return data.map(Math.round);
+}
+
+/**
+ * Decrease accuracy of floating-point numbers
+ * in transforms keeping a specified number of decimals.
+ * Smart rounds values like 2.349 to 2.35.
+ *
+ * @param {Number} fixed number of decimals
+ * @param {Array} data input data array
+ * @return {Array} output data array
+ */
+function smartRound(precision, data) {
+ for (var i = data.length, tolerance = +Math.pow(.1, precision).toFixed(precision); i--;) {
+ if (data[i].toFixed(precision) != data[i]) {
+ var rounded = +data[i].toFixed(precision - 1);
+ data[i] = +Math.abs(rounded - data[i]).toFixed(precision + 1) >= tolerance ?
+ +data[i].toFixed(precision) :
+ rounded;
+ }
+ }
+ return data;
+}
diff --git a/node_modules/svgo/plugins/mergePaths.js b/node_modules/svgo/plugins/mergePaths.js
new file mode 100644
index 00000000..63cf56e8
--- /dev/null
+++ b/node_modules/svgo/plugins/mergePaths.js
@@ -0,0 +1,71 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'merges multiple paths in one if possible';
+
+exports.params = {
+ collapseRepeated: true,
+ leadingZero: true,
+ negativeExtraSpace: true
+};
+
+var path2js = require('./_path.js').path2js,
+ js2path = require('./_path.js').js2path,
+ intersects = require('./_path.js').intersects;
+
+/**
+ * Merge multiple Paths into one.
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich, Lev Solntsev
+ */
+exports.fn = function(item, params) {
+
+ if (!item.isElem() || item.isEmpty()) return;
+
+ var prevContentItem = null,
+ prevContentItemKeys = null;
+
+ item.content = item.content.filter(function(contentItem) {
+
+ if (prevContentItem &&
+ prevContentItem.isElem('path') &&
+ prevContentItem.isEmpty() &&
+ prevContentItem.hasAttr('d') &&
+ contentItem.isElem('path') &&
+ contentItem.isEmpty() &&
+ contentItem.hasAttr('d')
+ ) {
+
+ if (!prevContentItemKeys) {
+ prevContentItemKeys = Object.keys(prevContentItem.attrs);
+ }
+
+ var contentItemAttrs = Object.keys(contentItem.attrs),
+ equalData = prevContentItemKeys.length == contentItemAttrs.length &&
+ contentItemAttrs.every(function(key) {
+ return key == 'd' ||
+ prevContentItem.hasAttr(key) &&
+ prevContentItem.attr(key).value == contentItem.attr(key).value;
+ }),
+ prevPathJS = path2js(prevContentItem),
+ curPathJS = path2js(contentItem);
+
+ if (equalData && !intersects(prevPathJS, curPathJS)) {
+ js2path(prevContentItem, prevPathJS.concat(curPathJS), params);
+ return false;
+ }
+ }
+
+ prevContentItem = contentItem;
+ prevContentItemKeys = null;
+ return true;
+
+ });
+
+};
diff --git a/node_modules/svgo/plugins/minifyStyles.js b/node_modules/svgo/plugins/minifyStyles.js
new file mode 100644
index 00000000..3977b367
--- /dev/null
+++ b/node_modules/svgo/plugins/minifyStyles.js
@@ -0,0 +1,45 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.params = {
+ svgo: {}
+};
+
+exports.description = 'minifies existing styles in svg';
+
+var csso = require('csso');
+
+/**
+ * Minifies styles (<style> element + style attribute) using svgo
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author strarsis <strarsis@gmail.com>
+ */
+exports.fn = function(item, svgoOptions) {
+
+ if(item.elem) {
+ if(item.isElem('style') && !item.isEmpty()) {
+ var styleCss = item.content[0].text || item.content[0].cdata || [],
+ DATA = styleCss.indexOf('>') >= 0 || styleCss.indexOf('<') >= 0 ? 'cdata' : 'text';
+ if(styleCss.length > 0) {
+ var styleCssMinified = csso.minify(styleCss, svgoOptions);
+ item.content[0][DATA] = styleCssMinified.css;
+ }
+ }
+
+ if(item.hasAttr('style')) {
+ var itemCss = item.attr('style').value;
+ if(itemCss.length > 0) {
+ var itemCssMinified = csso.minifyBlock(itemCss, svgoOptions);
+ item.attr('style').value = itemCssMinified.css;
+ }
+ }
+ }
+
+ return item;
+};
diff --git a/node_modules/svgo/plugins/moveElemsAttrsToGroup.js b/node_modules/svgo/plugins/moveElemsAttrsToGroup.js
new file mode 100644
index 00000000..0f3b65c7
--- /dev/null
+++ b/node_modules/svgo/plugins/moveElemsAttrsToGroup.js
@@ -0,0 +1,126 @@
+'use strict';
+
+exports.type = 'perItemReverse';
+
+exports.active = true;
+
+exports.description = 'moves elements attributes to the existing group wrapper';
+
+var inheritableAttrs = require('./_collections').inheritableAttrs,
+ pathElems = require('./_collections.js').pathElems;
+
+/**
+ * Collapse content's intersected and inheritable
+ * attributes to the existing group wrapper.
+ *
+ * @example
+ * <g attr1="val1">
+ * <g attr2="val2">
+ * text
+ * </g>
+ * <circle attr2="val2" attr3="val3"/>
+ * </g>
+ * ⬇
+ * <g attr1="val1" attr2="val2">
+ * <g>
+ * text
+ * </g>
+ * <circle attr3="val3"/>
+ * </g>
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item) {
+
+ if (item.isElem('g') && !item.isEmpty() && item.content.length > 1) {
+
+ var intersection = {},
+ hasTransform = false,
+ hasClip = item.hasAttr('clip-path') || item.hasAttr('mask'),
+ intersected = item.content.every(function(inner) {
+ if (inner.isElem() && inner.hasAttr()) {
+ // don't mess with possible styles (hack until CSS parsing is implemented)
+ if (inner.hasAttr('class')) return false;
+ if (!Object.keys(intersection).length) {
+ intersection = inner.attrs;
+ } else {
+ intersection = intersectInheritableAttrs(intersection, inner.attrs);
+
+ if (!intersection) return false;
+ }
+
+ return true;
+ }
+ }),
+ allPath = item.content.every(function(inner) {
+ return inner.isElem(pathElems);
+ });
+
+ if (intersected) {
+
+ item.content.forEach(function(g) {
+
+ for (var name in intersection) {
+
+ if (!allPath && !hasClip || name !== 'transform') {
+
+ g.removeAttr(name);
+
+ if (name === 'transform') {
+ if (!hasTransform) {
+ if (item.hasAttr('transform')) {
+ item.attr('transform').value += ' ' + intersection[name].value;
+ } else {
+ item.addAttr(intersection[name]);
+ }
+
+ hasTransform = true;
+ }
+ } else {
+ item.addAttr(intersection[name]);
+ }
+
+ }
+ }
+
+ });
+
+ }
+
+ }
+
+};
+
+/**
+ * Intersect inheritable attributes.
+ *
+ * @param {Object} a first attrs object
+ * @param {Object} b second attrs object
+ *
+ * @return {Object} intersected attrs object
+ */
+function intersectInheritableAttrs(a, b) {
+
+ var c = {};
+
+ for (var n in a) {
+ if (
+ b.hasOwnProperty(n) &&
+ inheritableAttrs.indexOf(n) > -1 &&
+ a[n].name === b[n].name &&
+ a[n].value === b[n].value &&
+ a[n].prefix === b[n].prefix &&
+ a[n].local === b[n].local
+ ) {
+ c[n] = a[n];
+ }
+ }
+
+ if (!Object.keys(c).length) return false;
+
+ return c;
+
+}
diff --git a/node_modules/svgo/plugins/moveGroupAttrsToElems.js b/node_modules/svgo/plugins/moveGroupAttrsToElems.js
new file mode 100644
index 00000000..a966f882
--- /dev/null
+++ b/node_modules/svgo/plugins/moveGroupAttrsToElems.js
@@ -0,0 +1,63 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'moves some group attributes to the content elements';
+
+var collections = require('./_collections.js'),
+ pathElems = collections.pathElems.concat(['g', 'text']),
+ referencesProps = collections.referencesProps;
+
+/**
+ * Move group attrs to the content elements.
+ *
+ * @example
+ * <g transform="scale(2)">
+ * <path transform="rotate(45)" d="M0,0 L10,20"/>
+ * <path transform="translate(10, 20)" d="M0,10 L20,30"/>
+ * </g>
+ * ⬇
+ * <g>
+ * <path transform="scale(2) rotate(45)" d="M0,0 L10,20"/>
+ * <path transform="scale(2) translate(10, 20)" d="M0,10 L20,30"/>
+ * </g>
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item) {
+
+ // move group transform attr to content's pathElems
+ if (
+ item.isElem('g') &&
+ item.hasAttr('transform') &&
+ !item.isEmpty() &&
+ !item.someAttr(function(attr) {
+ return ~referencesProps.indexOf(attr.name) && ~attr.value.indexOf('url(');
+ }) &&
+ item.content.every(function(inner) {
+ return inner.isElem(pathElems) && !inner.hasAttr('id');
+ })
+ ) {
+ item.content.forEach(function(inner) {
+ var attr = item.attr('transform');
+ if (inner.hasAttr('transform')) {
+ inner.attr('transform').value = attr.value + ' ' + inner.attr('transform').value;
+ } else {
+ inner.addAttr({
+ 'name': attr.name,
+ 'local': attr.local,
+ 'prefix': attr.prefix,
+ 'value': attr.value
+ });
+ }
+ });
+
+ item.removeAttr('transform');
+ }
+
+};
diff --git a/node_modules/svgo/plugins/removeAttrs.js b/node_modules/svgo/plugins/removeAttrs.js
new file mode 100644
index 00000000..bd2fca62
--- /dev/null
+++ b/node_modules/svgo/plugins/removeAttrs.js
@@ -0,0 +1,119 @@
+'use strict';
+
+var ELEM_SEP = ':';
+
+exports.type = 'perItem';
+
+exports.active = false;
+
+exports.description = 'removes specified attributes';
+
+exports.params = {
+ attrs: []
+};
+
+/**
+ * Remove attributes
+ *
+ * @param attrs:
+ *
+ * format: [ element* : attribute* ]
+ *
+ * element : regexp (wrapped into ^...$), single * or omitted > all elements
+ * attribute : regexp (wrapped into ^...$)
+ *
+ * examples:
+ *
+ * > basic: remove fill attribute
+ * ---
+ * removeAttrs:
+ * attrs: 'fill'
+ *
+ * > remove fill attribute on path element
+ * ---
+ * attrs: 'path:fill'
+ *
+ *
+ * > remove all fill and stroke attribute
+ * ---
+ * attrs:
+ * - 'fill'
+ * - 'stroke'
+ *
+ * [is same as]
+ *
+ * attrs: '(fill|stroke)'
+ *
+ * [is same as]
+ *
+ * attrs: '*:(fill|stroke)'
+ *
+ * [is same as]
+ *
+ * attrs: '.*:(fill|stroke)'
+ *
+ *
+ * > remove all stroke related attributes
+ * ----
+ * attrs: 'stroke.*'
+ *
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Benny Schudel
+ */
+exports.fn = function(item, params) {
+
+ // wrap into an array if params is not
+ if (!Array.isArray(params.attrs)) {
+ params.attrs = [params.attrs];
+ }
+
+ if (item.isElem()) {
+
+ // prepare patterns
+ var patterns = params.attrs.map(function(pattern) {
+
+ // apply to all elements if specifc element is omitted
+ if (pattern.indexOf(ELEM_SEP) === -1) {
+ pattern = ['.*', ELEM_SEP, pattern].join('');
+ }
+
+ // create regexps for element and attribute name
+ return pattern.split(ELEM_SEP)
+ .map(function(value) {
+
+ // adjust single * to match anything
+ if (value === '*') { value = '.*'; }
+
+ return new RegExp(['^', value, '$'].join(''), 'i');
+ });
+
+ });
+
+ // loop patterns
+ patterns.forEach(function(pattern) {
+
+ // matches element
+ if (pattern[0].test(item.elem)) {
+
+ // loop attributes
+ item.eachAttr(function(attr) {
+ var name = attr.name;
+
+ // matches attribute name
+ if (pattern[1].test(name)) {
+ item.removeAttr(name);
+ }
+
+ });
+
+ }
+
+ });
+
+ }
+
+};
diff --git a/node_modules/svgo/plugins/removeComments.js b/node_modules/svgo/plugins/removeComments.js
new file mode 100644
index 00000000..8bfd21b0
--- /dev/null
+++ b/node_modules/svgo/plugins/removeComments.js
@@ -0,0 +1,27 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'removes comments';
+
+/**
+ * Remove comments.
+ *
+ * @example
+ * <!-- Generator: Adobe Illustrator 15.0.0, SVG Export
+ * Plug-In . SVG Version: 6.00 Build 0) -->
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item) {
+
+ if (item.comment && item.comment.charAt(0) !== '!') {
+ return false;
+ }
+
+};
diff --git a/node_modules/svgo/plugins/removeDesc.js b/node_modules/svgo/plugins/removeDesc.js
new file mode 100644
index 00000000..e2848c6c
--- /dev/null
+++ b/node_modules/svgo/plugins/removeDesc.js
@@ -0,0 +1,32 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.params = {
+ removeAny: false
+};
+
+exports.description = 'removes <desc> (only non-meaningful by default)';
+
+var standardDescs = /^Created with/;
+
+/**
+ * Removes <desc>.
+ * Removes only standard editors content or empty elements 'cause it can be used for accessibility.
+ * Enable parameter 'removeAny' to remove any description.
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/SVG/Element/desc
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Daniel Wabyick
+ */
+exports.fn = function(item, params) {
+
+ return !item.isElem('desc') || !(params.removeAny || item.isEmpty() ||
+ standardDescs.test(item.content[0].text));
+
+};
diff --git a/node_modules/svgo/plugins/removeDimensions.js b/node_modules/svgo/plugins/removeDimensions.js
new file mode 100644
index 00000000..b25ff8f7
--- /dev/null
+++ b/node_modules/svgo/plugins/removeDimensions.js
@@ -0,0 +1,32 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = false;
+
+exports.description = 'removes width and height in presence of viewBox';
+
+/**
+ * Remove width/height attributes when a viewBox attribute is present.
+ *
+ * @example
+ * <svg width="100" height="50" viewBox="0 0 100 50">
+ * ↓
+ * <svg viewBox="0 0 100 50">
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if true, with and height will be filtered out
+ *
+ * @author Benny Schudel
+ */
+exports.fn = function(item) {
+
+ if (
+ item.isElem('svg') &&
+ item.hasAttr('viewBox')
+ ) {
+ item.removeAttr('width');
+ item.removeAttr('height');
+ }
+
+};
diff --git a/node_modules/svgo/plugins/removeDoctype.js b/node_modules/svgo/plugins/removeDoctype.js
new file mode 100644
index 00000000..32ee4f46
--- /dev/null
+++ b/node_modules/svgo/plugins/removeDoctype.js
@@ -0,0 +1,40 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'removes doctype declaration';
+
+/**
+ * Remove DOCTYPE declaration.
+ *
+ * "Unfortunately the SVG DTDs are a source of so many
+ * issues that the SVG WG has decided not to write one
+ * for the upcoming SVG 1.2 standard. In fact SVG WG
+ * members are even telling people not to use a DOCTYPE
+ * declaration in SVG 1.0 and 1.1 documents"
+ * https://jwatt.org/svg/authoring/#doctype-declaration
+ *
+ * @example
+ * <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ * q"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+ *
+ * @example
+ * <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ * "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" [
+ * <!-- an internal subset can be embedded here -->
+ * ]>
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item) {
+
+ if (item.doctype) {
+ return false;
+ }
+
+};
diff --git a/node_modules/svgo/plugins/removeEditorsNSData.js b/node_modules/svgo/plugins/removeEditorsNSData.js
new file mode 100644
index 00000000..9e5c8cd8
--- /dev/null
+++ b/node_modules/svgo/plugins/removeEditorsNSData.js
@@ -0,0 +1,65 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'removes editors namespaces, elements and attributes';
+
+var editorNamespaces = require('./_collections').editorNamespaces,
+ prefixes = [];
+
+exports.params = {
+ additionalNamespaces: []
+};
+
+/**
+ * Remove editors namespaces, elements and attributes.
+ *
+ * @example
+ * <svg xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd">
+ * <sodipodi:namedview/>
+ * <path sodipodi:nodetypes="cccc"/>
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item, params) {
+
+ if (Array.isArray(params.additionalNamespaces)) {
+ editorNamespaces = editorNamespaces.concat(params.additionalNamespaces);
+ }
+
+ if (item.elem) {
+
+ if (item.isElem('svg')) {
+
+ item.eachAttr(function(attr) {
+ if (attr.prefix === 'xmlns' && editorNamespaces.indexOf(attr.value) > -1) {
+ prefixes.push(attr.local);
+
+ // <svg xmlns:sodipodi="">
+ item.removeAttr(attr.name);
+ }
+ });
+
+ }
+
+ // <* sodipodi:*="">
+ item.eachAttr(function(attr) {
+ if (prefixes.indexOf(attr.prefix) > -1) {
+ item.removeAttr(attr.name);
+ }
+ });
+
+ // <sodipodi:*>
+ if (prefixes.indexOf(item.prefix) > -1) {
+ return false;
+ }
+
+ }
+
+};
diff --git a/node_modules/svgo/plugins/removeElementsByAttr.js b/node_modules/svgo/plugins/removeElementsByAttr.js
new file mode 100644
index 00000000..c726f708
--- /dev/null
+++ b/node_modules/svgo/plugins/removeElementsByAttr.js
@@ -0,0 +1,80 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = false;
+
+exports.description = 'removes arbitrary elements by ID or className (disabled by default)';
+
+exports.params = {
+ id: [],
+ class: []
+};
+
+/**
+ * Remove arbitrary SVG elements by ID or className.
+ *
+ * @param id
+ * examples:
+ *
+ * > single: remove element with ID of `elementID`
+ * ---
+ * removeElementsByAttr:
+ * id: 'elementID'
+ *
+ * > list: remove multiple elements by ID
+ * ---
+ * removeElementsByAttr:
+ * id:
+ * - 'elementID'
+ * - 'anotherID'
+ *
+ * @param class
+ * examples:
+ *
+ * > single: remove all elements with class of `elementClass`
+ * ---
+ * removeElementsByAttr:
+ * class: 'elementClass'
+ *
+ * > list: remove all elements with class of `elementClass` or `anotherClass`
+ * ---
+ * removeElementsByAttr:
+ * class:
+ * - 'elementClass'
+ * - 'anotherClass'
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Eli Dupuis (@elidupuis)
+ */
+exports.fn = function(item, params) {
+ var elemId, elemClass;
+
+ // wrap params in an array if not already
+ ['id', 'class'].forEach(function(key) {
+ if (!Array.isArray(params[key])) {
+ params[key] = [ params[key] ];
+ }
+ });
+
+ // abort if current item is no an element
+ if (!item.isElem()) {
+ return;
+ }
+
+ // remove element if it's `id` matches configured `id` params
+ elemId = item.attr('id');
+ if (elemId) {
+ return params.id.indexOf(elemId.value) === -1;
+ }
+
+ // remove element if it's `class` contains any of the configured `class` params
+ elemClass = item.attr('class');
+ if (elemClass) {
+ var hasClassRegex = new RegExp(params.class.join('|'));
+ return !hasClassRegex.test(elemClass.value);
+ }
+};
diff --git a/node_modules/svgo/plugins/removeEmptyAttrs.js b/node_modules/svgo/plugins/removeEmptyAttrs.js
new file mode 100644
index 00000000..ae73e6c9
--- /dev/null
+++ b/node_modules/svgo/plugins/removeEmptyAttrs.js
@@ -0,0 +1,29 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'removes empty attributes';
+
+/**
+ * Remove attributes with empty values.
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item) {
+
+ if (item.elem) {
+
+ item.eachAttr(function(attr) {
+ if (attr.value === '') {
+ item.removeAttr(attr.name);
+ }
+ });
+
+ }
+
+};
diff --git a/node_modules/svgo/plugins/removeEmptyContainers.js b/node_modules/svgo/plugins/removeEmptyContainers.js
new file mode 100644
index 00000000..356a5835
--- /dev/null
+++ b/node_modules/svgo/plugins/removeEmptyContainers.js
@@ -0,0 +1,32 @@
+'use strict';
+
+exports.type = 'perItemReverse';
+
+exports.active = true;
+
+exports.description = 'removes empty container elements';
+
+var container = require('./_collections').elemsGroups.container;
+
+/**
+ * Remove empty containers.
+ *
+ * @see http://www.w3.org/TR/SVG/intro.html#TermContainerElement
+ *
+ * @example
+ * <defs/>
+ *
+ * @example
+ * <g><marker><a/></marker></g>
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item) {
+
+ return !(item.isElem(container) && !item.isElem('svg') && item.isEmpty() &&
+ (!item.isElem('pattern') || !item.hasAttrLocal('href')));
+
+};
diff --git a/node_modules/svgo/plugins/removeEmptyText.js b/node_modules/svgo/plugins/removeEmptyText.js
new file mode 100644
index 00000000..049b1819
--- /dev/null
+++ b/node_modules/svgo/plugins/removeEmptyText.js
@@ -0,0 +1,59 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'removes empty <text> elements';
+
+exports.params = {
+ text: true,
+ tspan: true,
+ tref: true
+};
+
+/**
+ * Remove empty Text elements.
+ *
+ * @see http://www.w3.org/TR/SVG/text.html
+ *
+ * @example
+ * Remove empty text element:
+ * <text/>
+ *
+ * Remove empty tspan element:
+ * <tspan/>
+ *
+ * Remove tref with empty xlink:href attribute:
+ * <tref xlink:href=""/>
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item, params) {
+
+ // Remove empty text element
+ if (
+ params.text &&
+ item.isElem('text') &&
+ item.isEmpty()
+ ) return false;
+
+ // Remove empty tspan element
+ if (
+ params.tspan &&
+ item.isElem('tspan') &&
+ item.isEmpty()
+ ) return false;
+
+ // Remove tref with empty xlink:href attribute
+ if (
+ params.tref &&
+ item.isElem('tref') &&
+ !item.hasAttrLocal('href')
+ ) return false;
+
+};
diff --git a/node_modules/svgo/plugins/removeHiddenElems.js b/node_modules/svgo/plugins/removeHiddenElems.js
new file mode 100644
index 00000000..231a2bdf
--- /dev/null
+++ b/node_modules/svgo/plugins/removeHiddenElems.js
@@ -0,0 +1,218 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'removes hidden elements (zero sized, with absent attributes)';
+
+exports.params = {
+ displayNone: true,
+ opacity0: true,
+ circleR0: true,
+ ellipseRX0: true,
+ ellipseRY0: true,
+ rectWidth0: true,
+ rectHeight0: true,
+ patternWidth0: true,
+ patternHeight0: true,
+ imageWidth0: true,
+ imageHeight0: true,
+ pathEmptyD: true,
+ polylineEmptyPoints: true,
+ polygonEmptyPoints: true
+};
+
+var regValidPath = /M\s*(?:[-+]?(?:\d*\.\d+|\d+(?:\.|(?!\.)))([eE][-+]?\d+)?(?!\d)\s*,?\s*){2}\D*\d/i;
+
+/**
+ * Remove hidden elements with disabled rendering:
+ * - display="none"
+ * - opacity="0"
+ * - circle with zero radius
+ * - ellipse with zero x-axis or y-axis radius
+ * - rectangle with zero width or height
+ * - pattern with zero width or height
+ * - image with zero width or height
+ * - path with empty data
+ * - polyline with empty points
+ * - polygon with empty points
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item, params) {
+
+ if (item.elem) {
+
+ // display="none"
+ //
+ // http://www.w3.org/TR/SVG/painting.html#DisplayProperty
+ // "A value of display: none indicates that the given element
+ // and its children shall not be rendered directly"
+ if (
+ params.displayNone &&
+ item.hasAttr('display', 'none')
+ ) return false;
+
+ // opacity="0"
+ //
+ // http://www.w3.org/TR/SVG/masking.html#ObjectAndGroupOpacityProperties
+ if (
+ params.opacity0 &&
+ item.hasAttr('opacity', '0')
+ ) return false;
+
+ // Circles with zero radius
+ //
+ // http://www.w3.org/TR/SVG/shapes.html#CircleElementRAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <circle r="0">
+ if (
+ params.circleR0 &&
+ item.isElem('circle') &&
+ item.isEmpty() &&
+ item.hasAttr('r', '0')
+ ) return false;
+
+ // Ellipse with zero x-axis radius
+ //
+ // http://www.w3.org/TR/SVG/shapes.html#EllipseElementRXAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <ellipse rx="0">
+ if (
+ params.ellipseRX0 &&
+ item.isElem('ellipse') &&
+ item.isEmpty() &&
+ item.hasAttr('rx', '0')
+ ) return false;
+
+ // Ellipse with zero y-axis radius
+ //
+ // http://www.w3.org/TR/SVG/shapes.html#EllipseElementRYAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <ellipse ry="0">
+ if (
+ params.ellipseRY0 &&
+ item.isElem('ellipse') &&
+ item.isEmpty() &&
+ item.hasAttr('ry', '0')
+ ) return false;
+
+ // Rectangle with zero width
+ //
+ // http://www.w3.org/TR/SVG/shapes.html#RectElementWidthAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <rect width="0">
+ if (
+ params.rectWidth0 &&
+ item.isElem('rect') &&
+ item.isEmpty() &&
+ item.hasAttr('width', '0')
+ ) return false;
+
+ // Rectangle with zero height
+ //
+ // http://www.w3.org/TR/SVG/shapes.html#RectElementHeightAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <rect height="0">
+ if (
+ params.rectHeight0 &&
+ params.rectWidth0 &&
+ item.isElem('rect') &&
+ item.isEmpty() &&
+ item.hasAttr('height', '0')
+ ) return false;
+
+ // Pattern with zero width
+ //
+ // http://www.w3.org/TR/SVG/pservers.html#PatternElementWidthAttribute
+ // "A value of zero disables rendering of the element (i.e., no paint is applied)"
+ //
+ // <pattern width="0">
+ if (
+ params.patternWidth0 &&
+ item.isElem('pattern') &&
+ item.hasAttr('width', '0')
+ ) return false;
+
+ // Pattern with zero height
+ //
+ // http://www.w3.org/TR/SVG/pservers.html#PatternElementHeightAttribute
+ // "A value of zero disables rendering of the element (i.e., no paint is applied)"
+ //
+ // <pattern height="0">
+ if (
+ params.patternHeight0 &&
+ item.isElem('pattern') &&
+ item.hasAttr('height', '0')
+ ) return false;
+
+ // Image with zero width
+ //
+ // http://www.w3.org/TR/SVG/struct.html#ImageElementWidthAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <image width="0">
+ if (
+ params.imageWidth0 &&
+ item.isElem('image') &&
+ item.hasAttr('width', '0')
+ ) return false;
+
+ // Image with zero height
+ //
+ // http://www.w3.org/TR/SVG/struct.html#ImageElementHeightAttribute
+ // "A value of zero disables rendering of the element"
+ //
+ // <image height="0">
+ if (
+ params.imageHeight0 &&
+ item.isElem('image') &&
+ item.hasAttr('height', '0')
+ ) return false;
+
+ // Path with empty data
+ //
+ // http://www.w3.org/TR/SVG/paths.html#DAttribute
+ //
+ // <path d=""/>
+ if (
+ params.pathEmptyD &&
+ item.isElem('path') &&
+ (!item.hasAttr('d') || !regValidPath.test(item.attr('d').value))
+ ) return false;
+
+ // Polyline with empty points
+ //
+ // http://www.w3.org/TR/SVG/shapes.html#PolylineElementPointsAttribute
+ //
+ // <polyline points="">
+ if (
+ params.polylineEmptyPoints &&
+ item.isElem('polyline') &&
+ !item.hasAttr('points')
+ ) return false;
+
+ // Polygon with empty points
+ //
+ // http://www.w3.org/TR/SVG/shapes.html#PolygonElementPointsAttribute
+ //
+ // <polygon points="">
+ if (
+ params.polygonEmptyPoints &&
+ item.isElem('polygon') &&
+ !item.hasAttr('points')
+ ) return false;
+
+ }
+
+};
diff --git a/node_modules/svgo/plugins/removeMetadata.js b/node_modules/svgo/plugins/removeMetadata.js
new file mode 100644
index 00000000..fa7a5795
--- /dev/null
+++ b/node_modules/svgo/plugins/removeMetadata.js
@@ -0,0 +1,23 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'removes <metadata>';
+
+/**
+ * Remove <metadata>.
+ *
+ * http://www.w3.org/TR/SVG/metadata.html
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item) {
+
+ return !item.isElem('metadata');
+
+};
diff --git a/node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js b/node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js
new file mode 100644
index 00000000..f999d568
--- /dev/null
+++ b/node_modules/svgo/plugins/removeNonInheritableGroupAttrs.js
@@ -0,0 +1,40 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'removes non-inheritable group’s presentational attributes';
+
+var inheritableAttrs = require('./_collections').inheritableAttrs,
+ attrsGroups = require('./_collections').attrsGroups,
+ excludedAttrs = ['display', 'opacity'];
+
+/**
+ * Remove non-inheritable group's "presentation" attributes.
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item) {
+
+ if (item.isElem('g')) {
+
+ item.eachAttr(function(attr) {
+ if (
+ ~attrsGroups.presentation.indexOf(attr.name) &&
+ ~attrsGroups.graphicalEvent.indexOf(attr.name) &&
+ ~attrsGroups.core.indexOf(attr.name) &&
+ ~attrsGroups.conditionalProcessing.indexOf(attr.name) &&
+ !~excludedAttrs.indexOf(attr.name) &&
+ !~inheritableAttrs.indexOf(attr.name)
+ ) {
+ item.removeAttr(attr.name);
+ }
+ });
+
+ }
+
+};
diff --git a/node_modules/svgo/plugins/removeRasterImages.js b/node_modules/svgo/plugins/removeRasterImages.js
new file mode 100644
index 00000000..cbddebe3
--- /dev/null
+++ b/node_modules/svgo/plugins/removeRasterImages.js
@@ -0,0 +1,28 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = false;
+
+exports.description = 'removes raster images (disabled by default)';
+
+/**
+ * Remove raster images references in <image>.
+ *
+ * @see https://bugs.webkit.org/show_bug.cgi?id=63548
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item) {
+
+ if (
+ item.isElem('image') &&
+ item.hasAttrLocal('href', /(\.|image\/)(jpg|png|gif)/)
+ ) {
+ return false;
+ }
+
+};
diff --git a/node_modules/svgo/plugins/removeStyleElement.js b/node_modules/svgo/plugins/removeStyleElement.js
new file mode 100644
index 00000000..964ce45c
--- /dev/null
+++ b/node_modules/svgo/plugins/removeStyleElement.js
@@ -0,0 +1,23 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = false;
+
+exports.description = 'removes <style> element (disabled by default)';
+
+/**
+ * Remove <style>.
+ *
+ * http://www.w3.org/TR/SVG/styling.html#StyleElement
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Betsy Dupuis
+ */
+exports.fn = function(item) {
+
+ return !item.isElem('style');
+
+};
diff --git a/node_modules/svgo/plugins/removeTitle.js b/node_modules/svgo/plugins/removeTitle.js
new file mode 100644
index 00000000..3d077e45
--- /dev/null
+++ b/node_modules/svgo/plugins/removeTitle.js
@@ -0,0 +1,24 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = false;
+
+exports.description = 'removes <title> (disabled by default)';
+
+/**
+ * Remove <title>.
+ * Disabled by default cause it may be used for accessibility.
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Igor Kalashnikov
+ */
+exports.fn = function(item) {
+
+ return !item.isElem('title');
+
+};
diff --git a/node_modules/svgo/plugins/removeUnknownsAndDefaults.js b/node_modules/svgo/plugins/removeUnknownsAndDefaults.js
new file mode 100644
index 00000000..341b1fd7
--- /dev/null
+++ b/node_modules/svgo/plugins/removeUnknownsAndDefaults.js
@@ -0,0 +1,143 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'removes unknown elements content and attributes, removes attrs with default values';
+
+exports.params = {
+ unknownContent: true,
+ unknownAttrs: true,
+ defaultAttrs: true,
+ uselessOverrides: true,
+ keepDataAttrs: true
+};
+
+var collections = require('./_collections'),
+ elems = collections.elems,
+ attrsGroups = collections.attrsGroups,
+ elemsGroups = collections.elemsGroups,
+ attrsGroupsDefaults = collections.attrsGroupsDefaults,
+ attrsInheritable = collections.inheritableAttrs;
+
+// collect and extend all references
+for (var elem in elems) {
+ elem = elems[elem];
+
+ if (elem.attrsGroups) {
+ elem.attrs = elem.attrs || [];
+
+ elem.attrsGroups.forEach(function(attrsGroupName) {
+ elem.attrs = elem.attrs.concat(attrsGroups[attrsGroupName]);
+
+ var groupDefaults = attrsGroupsDefaults[attrsGroupName];
+
+ if (groupDefaults) {
+ elem.defaults = elem.defaults || {};
+
+ for (var attrName in groupDefaults) {
+ elem.defaults[attrName] = groupDefaults[attrName];
+ }
+ }
+ });
+
+ }
+
+ if (elem.contentGroups) {
+ elem.content = elem.content || [];
+
+ elem.contentGroups.forEach(function(contentGroupName) {
+ elem.content = elem.content.concat(elemsGroups[contentGroupName]);
+ });
+ }
+}
+
+/**
+ * Remove unknown elements content and attributes,
+ * remove attributes with default values.
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item, params) {
+
+ // elems w/o namespace prefix
+ if (item.isElem() && !item.prefix) {
+
+ var elem = item.elem;
+
+ // remove unknown element's content
+ if (
+ params.unknownContent &&
+ !item.isEmpty() &&
+ elems[elem] && // make sure we know of this element before checking its children
+ elem !== 'foreignObject' // Don't check foreignObject
+ ) {
+ item.content.forEach(function(content, i) {
+ if (
+ content.isElem() &&
+ !content.prefix &&
+ (
+ (
+ elems[elem].content && // Do we have a record of its permitted content?
+ elems[elem].content.indexOf(content.elem) === -1
+ ) ||
+ (
+ !elems[elem].content && // we dont know about its permitted content
+ !elems[content.elem] // check that we know about the element at all
+ )
+ )
+ ) {
+ item.content.splice(i, 1);
+ }
+ });
+ }
+
+ // remove element's unknown attrs and attrs with default values
+ if (elems[elem] && elems[elem].attrs) {
+
+ item.eachAttr(function(attr) {
+
+ if (
+ attr.name !== 'xmlns' &&
+ (attr.prefix === 'xml' || !attr.prefix) &&
+ (!params.keepDataAttrs || attr.name.indexOf('data-') != 0)
+ ) {
+ if (
+ // unknown attrs
+ (
+ params.unknownAttrs &&
+ elems[elem].attrs.indexOf(attr.name) === -1
+ ) ||
+ // attrs with default values
+ (
+ params.defaultAttrs &&
+ elems[elem].defaults &&
+ elems[elem].defaults[attr.name] === attr.value && (
+ attrsInheritable.indexOf(attr.name) < 0 ||
+ !item.parentNode.computedAttr(attr.name)
+ )
+ ) ||
+ // useless overrides
+ (
+ params.uselessOverrides &&
+ attr.name !== 'transform' &&
+ attrsInheritable.indexOf(attr.name) > -1 &&
+ item.parentNode.computedAttr(attr.name, attr.value)
+ )
+ ) {
+ item.removeAttr(attr.name);
+ }
+ }
+
+ });
+
+ }
+
+ }
+
+};
diff --git a/node_modules/svgo/plugins/removeUnusedNS.js b/node_modules/svgo/plugins/removeUnusedNS.js
new file mode 100644
index 00000000..c4787915
--- /dev/null
+++ b/node_modules/svgo/plugins/removeUnusedNS.js
@@ -0,0 +1,107 @@
+'use strict';
+
+exports.type = 'full';
+
+exports.active = true;
+
+exports.description = 'removes unused namespaces declaration';
+
+/**
+ * Remove unused namespaces declaration.
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(data) {
+
+ var svgElem,
+ xmlnsCollection = [];
+
+ /**
+ * Remove namespace from collection.
+ *
+ * @param {String} ns namescape name
+ */
+ function removeNSfromCollection(ns) {
+
+ var pos = xmlnsCollection.indexOf(ns);
+
+ // if found - remove ns from the namespaces collection
+ if (pos > -1) {
+ xmlnsCollection.splice(pos, 1);
+ }
+
+ }
+
+ /**
+ * Bananas!
+ *
+ * @param {Array} items input items
+ *
+ * @return {Array} output items
+ */
+ function monkeys(items) {
+
+ var i = 0,
+ length = items.content.length;
+
+ while(i < length) {
+
+ var item = items.content[i];
+
+ if (item.isElem('svg')) {
+
+ item.eachAttr(function(attr) {
+ // collect namespaces
+ if (attr.prefix === 'xmlns' && attr.local) {
+ xmlnsCollection.push(attr.local);
+ }
+ });
+
+ // if svg element has ns-attr
+ if (xmlnsCollection.length) {
+ // save svg element
+ svgElem = item;
+ }
+
+ } else if (xmlnsCollection.length) {
+
+ // check item for the ns-attrs
+ if (item.prefix) {
+ removeNSfromCollection(item.prefix);
+ }
+
+ // check each attr for the ns-attrs
+ item.eachAttr(function(attr) {
+ removeNSfromCollection(attr.prefix);
+ });
+
+ }
+
+ // if nothing is found - go deeper
+ if (xmlnsCollection.length && item.content) {
+ monkeys(item);
+ }
+
+ i++;
+
+ }
+
+ return items;
+
+ }
+
+ data = monkeys(data);
+
+ // remove svg element ns-attributes if they are not used even once
+ if (xmlnsCollection.length) {
+ xmlnsCollection.forEach(function(name) {
+ svgElem.removeAttr('xmlns:' + name);
+ });
+ }
+
+ return data;
+
+};
diff --git a/node_modules/svgo/plugins/removeUselessDefs.js b/node_modules/svgo/plugins/removeUselessDefs.js
new file mode 100644
index 00000000..b8c97a52
--- /dev/null
+++ b/node_modules/svgo/plugins/removeUselessDefs.js
@@ -0,0 +1,51 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'removes elements in <defs> without id';
+
+var nonRendering = require('./_collections').elemsGroups.nonRendering,
+ defs;
+
+/**
+ * Removes content of defs and properties that aren't rendered directly without ids.
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Lev Solntsev
+ */
+exports.fn = function(item) {
+
+ if (item.isElem('defs')) {
+
+ defs = item;
+ item.content = (item.content || []).reduce(getUsefulItems, []);
+
+ if (item.isEmpty()) return false;
+
+ } else if (item.isElem(nonRendering) && !item.hasAttr('id')) {
+
+ return false;
+
+ }
+
+};
+
+function getUsefulItems(usefulItems, item) {
+
+ if (item.hasAttr('id') || item.isElem('style')) {
+
+ usefulItems.push(item);
+ item.parentNode = defs;
+
+ } else if (!item.isEmpty()) {
+
+ item.content.reduce(getUsefulItems, usefulItems);
+
+ }
+
+ return usefulItems;
+}
diff --git a/node_modules/svgo/plugins/removeUselessStrokeAndFill.js b/node_modules/svgo/plugins/removeUselessStrokeAndFill.js
new file mode 100644
index 00000000..47090eb8
--- /dev/null
+++ b/node_modules/svgo/plugins/removeUselessStrokeAndFill.js
@@ -0,0 +1,92 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'removes useless stroke and fill attributes';
+
+exports.params = {
+ stroke: true,
+ fill: true
+};
+
+var shape = require('./_collections').elemsGroups.shape,
+ regStrokeProps = /^stroke/,
+ regFillProps = /^fill-/,
+ styleOrScript = ['style', 'script'],
+ hasStyleOrScript = false;
+
+/**
+ * Remove useless stroke and fill attrs.
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item, params) {
+
+ if (item.isElem(styleOrScript)) {
+ hasStyleOrScript = true;
+ }
+
+ if (!hasStyleOrScript && item.isElem(shape) && !item.computedAttr('id')) {
+
+ var stroke = params.stroke && item.computedAttr('stroke'),
+ fill = params.fill && !item.computedAttr('fill', 'none');
+
+ // remove stroke*
+ if (
+ params.stroke &&
+ (!stroke ||
+ stroke == 'none' ||
+ item.computedAttr('stroke-opacity', '0') ||
+ item.computedAttr('stroke-width', '0')
+ )
+ ) {
+ var parentStroke = item.parentNode.computedAttr('stroke'),
+ declineStroke = parentStroke && parentStroke != 'none';
+
+ item.eachAttr(function(attr) {
+ if (regStrokeProps.test(attr.name)) {
+ item.removeAttr(attr.name);
+ }
+ });
+
+ if (declineStroke) item.addAttr({
+ name: 'stroke',
+ value: 'none',
+ prefix: '',
+ local: 'stroke'
+ });
+ }
+
+ // remove fill*
+ if (
+ params.fill &&
+ (!fill || item.computedAttr('fill-opacity', '0'))
+ ) {
+ item.eachAttr(function(attr) {
+ if (regFillProps.test(attr.name)) {
+ item.removeAttr(attr.name);
+ }
+ });
+
+ if (fill) {
+ if (item.hasAttr('fill'))
+ item.attr('fill').value = 'none';
+ else
+ item.addAttr({
+ name: 'fill',
+ value: 'none',
+ prefix: '',
+ local: 'fill'
+ });
+ }
+ }
+
+ }
+
+};
diff --git a/node_modules/svgo/plugins/removeViewBox.js b/node_modules/svgo/plugins/removeViewBox.js
new file mode 100644
index 00000000..33595656
--- /dev/null
+++ b/node_modules/svgo/plugins/removeViewBox.js
@@ -0,0 +1,49 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = false;
+
+exports.description = 'removes viewBox attribute when possible (disabled by default)';
+
+var regViewBox = /^0\s0\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)\s([\-+]?\d*\.?\d+([eE][\-+]?\d+)?)$/,
+ viewBoxElems = ['svg', 'pattern'];
+
+/**
+ * Remove viewBox attr which coincides with a width/height box.
+ *
+ * @see http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
+ *
+ * @example
+ * <svg width="100" height="50" viewBox="0 0 100 50">
+ * ⬇
+ * <svg width="100" height="50">
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item) {
+
+ if (
+ item.isElem(viewBoxElems) &&
+ item.hasAttr('viewBox') &&
+ item.hasAttr('width') &&
+ item.hasAttr('height')
+ ) {
+
+ var match = item.attr('viewBox').value.match(regViewBox);
+
+ if (match) {
+ if (
+ item.attr('width').value === match[1] &&
+ item.attr('height').value === match[3]
+ ) {
+ item.removeAttr('viewBox');
+ }
+ }
+
+ }
+
+};
diff --git a/node_modules/svgo/plugins/removeXMLNS.js b/node_modules/svgo/plugins/removeXMLNS.js
new file mode 100644
index 00000000..afda99d3
--- /dev/null
+++ b/node_modules/svgo/plugins/removeXMLNS.js
@@ -0,0 +1,28 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = false;
+
+exports.description = 'removes xmlns attribute (for inline svg, disabled by default)';
+
+/**
+ * Remove the xmlns attribute when present.
+ *
+ * @example
+ * <svg viewBox="0 0 100 50" xmlns="http://www.w3.org/2000/svg">
+ * ↓
+ * <svg viewBox="0 0 100 50">
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if true, xmlns will be filtered out
+ *
+ * @author Ricardo Tomasi
+ */
+exports.fn = function(item) {
+
+ if (item.isElem('svg') && item.hasAttr('xmlns')) {
+ item.removeAttr('xmlns');
+ }
+
+}; \ No newline at end of file
diff --git a/node_modules/svgo/plugins/removeXMLProcInst.js b/node_modules/svgo/plugins/removeXMLProcInst.js
new file mode 100644
index 00000000..afd425d5
--- /dev/null
+++ b/node_modules/svgo/plugins/removeXMLProcInst.js
@@ -0,0 +1,24 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = true;
+
+exports.description = 'removes XML processing instructions';
+
+/**
+ * Remove XML Processing Instruction.
+ *
+ * @example
+ * <?xml version="1.0" encoding="utf-8"?>
+ *
+ * @param {Object} item current iteration item
+ * @return {Boolean} if false, item will be filtered out
+ *
+ * @author Kir Belevich
+ */
+exports.fn = function(item) {
+
+ return !(item.processinginstruction && item.processinginstruction.name === 'xml');
+
+};
diff --git a/node_modules/svgo/plugins/sortAttrs.js b/node_modules/svgo/plugins/sortAttrs.js
new file mode 100644
index 00000000..221a4bf5
--- /dev/null
+++ b/node_modules/svgo/plugins/sortAttrs.js
@@ -0,0 +1,81 @@
+'use strict';
+
+exports.type = 'perItem';
+
+exports.active = false;
+
+exports.description = 'sorts element attributes (disabled by default)';
+
+exports.params = {
+ order: [
+ 'id',
+ 'width', 'height',
+ 'x', 'x1', 'x2',
+ 'y', 'y1', 'y2',
+ 'cx', 'cy', 'r',
+ 'fill', 'stroke', 'marker',
+ 'd', 'points'
+ ]
+};
+
+/**
+ * Sort element attributes for epic readability.
+ *
+ * @param {Object} item current iteration item
+ * @param {Object} params plugin params
+ *
+ * @author Nikolay Frantsev
+ */
+exports.fn = function(item, params) {
+
+ var attrs = [],
+ sorted = {},
+ orderlen = params.order.length + 1;
+
+ if (item.elem) {
+
+ item.eachAttr(function(attr) {
+ attrs.push(attr);
+ });
+
+ attrs.sort(function(a, b) {
+ if (a.prefix != b.prefix) {
+ // xmlns attributes implicitly have the prefix xmlns
+ if (a.prefix == 'xmlns')
+ return -1;
+ if (b.prefix == 'xmlns')
+ return 1;
+ return a.prefix < b.prefix ? -1 : 1;
+ }
+
+ var aindex = orderlen;
+ var bindex = orderlen;
+
+ for (var i = 0; i < params.order.length; i++) {
+ if (a.name == params.order[i]) {
+ aindex = i;
+ } else if (a.name.indexOf(params.order[i] + '-') === 0) {
+ aindex = i + .5;
+ }
+ if (b.name == params.order[i]) {
+ bindex = i;
+ } else if (b.name.indexOf(params.order[i] + '-') === 0) {
+ bindex = i + .5;
+ }
+ }
+
+ if (aindex != bindex) {
+ return aindex - bindex;
+ }
+ return a.name < b.name ? -1 : 1;
+ });
+
+ attrs.forEach(function (attr) {
+ sorted[attr.name] = attr;
+ });
+
+ item.attrs = sorted;
+
+ }
+
+};
diff --git a/node_modules/svgo/plugins/transformsWithOnePath.js b/node_modules/svgo/plugins/transformsWithOnePath.js
new file mode 100644
index 00000000..f4dec7e6
--- /dev/null
+++ b/node_modules/svgo/plugins/transformsWithOnePath.js
@@ -0,0 +1,327 @@
+'use strict';
+
+/*
+ * Thanks to http://fontello.com project for sponsoring this plugin
+ */
+
+exports.type = 'full';
+
+exports.active = false;
+
+exports.description = 'performs a set of operations on SVG with one path inside (disabled by default)';
+
+exports.params = {
+ // width and height to resize SVG and rescale inner Path
+ width: false,
+ height: false,
+
+ // scale inner Path without resizing SVG
+ scale: false,
+
+ // shiftX/Y inner Path
+ shiftX: false,
+ shiftY: false,
+
+ // crop SVG width along the real width of inner Path
+ hcrop: false,
+
+ // vertical center inner Path inside SVG height
+ vcenter: false,
+
+ // stringify params
+ floatPrecision: 3,
+ leadingZero: true,
+ negativeExtraSpace: true
+};
+
+var _path = require('./_path.js'),
+ relative2absolute = _path.relative2absolute,
+ computeCubicBoundingBox = _path.computeCubicBoundingBox,
+ computeQuadraticBoundingBox = _path.computeQuadraticBoundingBox,
+ applyTransforms = _path.applyTransforms,
+ js2path = _path.js2path,
+ path2js = _path.path2js,
+ EXTEND = require('whet.extend');
+
+exports.fn = function(data, params) {
+
+ data.content.forEach(function(item) {
+
+ // only for SVG with one Path inside
+ if (item.isElem('svg') &&
+ item.content.length === 1 &&
+ item.content[0].isElem('path')
+ ) {
+
+ var svgElem = item,
+ pathElem = svgElem.content[0],
+ // get absoluted Path data
+ path = relative2absolute(EXTEND(true, [], path2js(pathElem))),
+ xs = [],
+ ys = [],
+ cubicСontrolPoint = [0, 0],
+ quadraticСontrolPoint = [0, 0],
+ lastPoint = [0, 0],
+ cubicBoundingBox,
+ quadraticBoundingBox,
+ i,
+ segment;
+
+ path.forEach(function(pathItem) {
+
+ // ML
+ if ('ML'.indexOf(pathItem.instruction) > -1) {
+
+ for (i = 0; i < pathItem.data.length; i++) {
+ if (i % 2 === 0) {
+ xs.push(pathItem.data[i]);
+ } else {
+ ys.push(pathItem.data[i]);
+ }
+ }
+
+ lastPoint = cubicСontrolPoint = quadraticСontrolPoint = pathItem.data.slice(-2);
+
+ // H
+ } else if (pathItem.instruction === 'H') {
+
+ pathItem.data.forEach(function(d) {
+ xs.push(d);
+ });
+
+ lastPoint[0] = cubicСontrolPoint[0] = quadraticСontrolPoint[0] = pathItem.data[pathItem.data.length - 2];
+
+ // V
+ } else if (pathItem.instruction === 'V') {
+
+ pathItem.data.forEach(function(d) {
+ ys.push(d);
+ });
+
+ lastPoint[1] = cubicСontrolPoint[1] = quadraticСontrolPoint[1] = pathItem.data[pathItem.data.length - 1];
+
+ // C
+ } else if (pathItem.instruction === 'C') {
+
+ for (i = 0; i < pathItem.data.length; i += 6) {
+
+ segment = pathItem.data.slice(i, i + 6);
+
+ cubicBoundingBox = computeCubicBoundingBox.apply(this, lastPoint.concat(segment));
+
+ xs.push(cubicBoundingBox.minx);
+ xs.push(cubicBoundingBox.maxx);
+
+ ys.push(cubicBoundingBox.miny);
+ ys.push(cubicBoundingBox.maxy);
+
+ // reflected control point for the next possible S
+ cubicСontrolPoint = [
+ 2 * segment[4] - segment[2],
+ 2 * segment[5] - segment[3]
+ ];
+
+ lastPoint = segment.slice(-2);
+
+ }
+
+ // S
+ } else if (pathItem.instruction === 'S') {
+
+ for (i = 0; i < pathItem.data.length; i += 4) {
+
+ segment = pathItem.data.slice(i, i + 4);
+
+ cubicBoundingBox = computeCubicBoundingBox.apply(this, lastPoint.concat(cubicСontrolPoint).concat(segment));
+
+ xs.push(cubicBoundingBox.minx);
+ xs.push(cubicBoundingBox.maxx);
+
+ ys.push(cubicBoundingBox.miny);
+ ys.push(cubicBoundingBox.maxy);
+
+ // reflected control point for the next possible S
+ cubicСontrolPoint = [
+ 2 * segment[2] - cubicСontrolPoint[0],
+ 2 * segment[3] - cubicСontrolPoint[1],
+ ];
+
+ lastPoint = segment.slice(-2);
+
+ }
+
+ // Q
+ } else if (pathItem.instruction === 'Q') {
+
+ for (i = 0; i < pathItem.data.length; i += 4) {
+
+ segment = pathItem.data.slice(i, i + 4);
+
+ quadraticBoundingBox = computeQuadraticBoundingBox.apply(this, lastPoint.concat(segment));
+
+ xs.push(quadraticBoundingBox.minx);
+ xs.push(quadraticBoundingBox.maxx);
+
+ ys.push(quadraticBoundingBox.miny);
+ ys.push(quadraticBoundingBox.maxy);
+
+ // reflected control point for the next possible T
+ quadraticСontrolPoint = [
+ 2 * segment[2] - segment[0],
+ 2 * segment[3] - segment[1]
+ ];
+
+ lastPoint = segment.slice(-2);
+
+ }
+
+ // S
+ } else if (pathItem.instruction === 'T') {
+
+ for (i = 0; i < pathItem.data.length; i += 2) {
+
+ segment = pathItem.data.slice(i, i + 2);
+
+ quadraticBoundingBox = computeQuadraticBoundingBox.apply(this, lastPoint.concat(quadraticСontrolPoint).concat(segment));
+
+ xs.push(quadraticBoundingBox.minx);
+ xs.push(quadraticBoundingBox.maxx);
+
+ ys.push(quadraticBoundingBox.miny);
+ ys.push(quadraticBoundingBox.maxy);
+
+ // reflected control point for the next possible T
+ quadraticСontrolPoint = [
+ 2 * segment[0] - quadraticСontrolPoint[0],
+ 2 * segment[1] - quadraticСontrolPoint[1]
+ ];
+
+ lastPoint = segment.slice(-2);
+
+ }
+
+ }
+
+ });
+
+ var xmin = Math.min.apply(this, xs).toFixed(params.floatPrecision),
+ xmax = Math.max.apply(this, xs).toFixed(params.floatPrecision),
+ ymin = Math.min.apply(this, ys).toFixed(params.floatPrecision),
+ ymax = Math.max.apply(this, ys).toFixed(params.floatPrecision),
+ svgWidth = +svgElem.attr('width').value,
+ svgHeight = +svgElem.attr('height').value,
+ realWidth = Math.round(xmax - xmin),
+ realHeight = Math.round(ymax - ymin),
+ transform = '',
+ scale;
+
+ // width & height
+ if (params.width && params.height) {
+
+ scale = Math.min(params.width / svgWidth, params.height / svgHeight);
+
+ realWidth = realWidth * scale;
+ realHeight = realHeight * scale;
+
+ svgWidth = svgElem.attr('width').value = params.width;
+ svgHeight = svgElem.attr('height').value = params.height;
+
+ transform += ' scale(' + scale + ')';
+
+ // width
+ } else if (params.width && !params.height) {
+
+ scale = params.width / svgWidth;
+
+ realWidth = realWidth * scale;
+ realHeight = realHeight * scale;
+
+ svgWidth = svgElem.attr('width').value = params.width;
+ svgHeight = svgElem.attr('height').value = svgHeight * scale;
+
+ transform += ' scale(' + scale + ')';
+
+ // height
+ } else if (params.height && !params.width) {
+
+ scale = params.height / svgHeight;
+
+ realWidth = realWidth * scale;
+ realHeight = realHeight * scale;
+
+ svgWidth = svgElem.attr('width').value = svgWidth * scale;
+ svgHeight = svgElem.attr('height').value = params.height;
+
+ transform += ' scale(' + scale + ')';
+
+ }
+
+ // shiftX
+ if (params.shiftX) {
+ transform += ' translate(' + realWidth * params.shiftX + ', 0)';
+ }
+
+ // shiftY
+ if (params.shiftY) {
+ transform += ' translate(0, ' + realHeight * params.shiftY + ')';
+ }
+
+ // scale
+ if (params.scale) {
+ scale = params.scale;
+
+ var shiftX = svgWidth / 2,
+ shiftY = svgHeight / 2;
+
+ realWidth = realWidth * scale;
+ realHeight = realHeight * scale;
+
+ if (params.shiftX || params.shiftY) {
+ transform += ' scale(' + scale + ')';
+ } else {
+ transform += ' translate(' + shiftX + ' ' + shiftY + ') scale(' + scale + ') translate(-' + shiftX + ' -' + shiftY + ')';
+ }
+ }
+
+ // hcrop
+ if (params.hcrop) {
+ transform += ' translate(' + (-xmin) + ' 0)';
+
+ svgElem.attr('width').value = realWidth;
+ }
+
+ // vcenter
+ if (params.vcenter) {
+ transform += ' translate(0 ' + (((svgHeight - realHeight) / 2) - ymin) + ')';
+ }
+
+ if (transform) {
+ pathElem.addAttr({
+ name: 'transform',
+ prefix: '',
+ local: 'transform',
+ value: transform
+ });
+
+ path = applyTransforms(pathElem, pathElem.pathJS, true, params.floatPrecision);
+
+ // transformed data rounding
+ path.forEach(function(pathItem) {
+ if (pathItem.data) {
+ pathItem.data = pathItem.data.map(function(num) {
+ return +num.toFixed(params.floatPrecision);
+ });
+ }
+ });
+
+ // save new
+ js2path(pathElem, path, params);
+ }
+
+ }
+
+ });
+
+ return data;
+
+};