Demo for query storing

Change-Id: I947bcac841992c3f6cfd01ab337c265b0d01cb70
diff --git a/node_modules/svgo/lib/css-tools.js b/node_modules/svgo/lib/css-tools.js
new file mode 100644
index 0000000..6517892
--- /dev/null
+++ b/node_modules/svgo/lib/css-tools.js
@@ -0,0 +1,222 @@
+'use strict';
+
+var csstree     = require('css-tree'),
+    List        = csstree.List,
+    stable      = require('stable'),
+    specificity = require('csso/lib/restructure/prepare/specificity');
+
+
+/**
+ * Flatten a CSS AST to a selectors list.
+ *
+ * @param {Object} cssAst css-tree AST to flatten
+ * @return {Array} selectors
+ */
+function flattenToSelectors(cssAst) {
+    var selectors = [];
+
+    csstree.walk(cssAst, {visit: 'Rule', enter: function(node) {
+        if (node.type !== 'Rule') {
+            return;
+        }
+
+        var atrule = this.atrule;
+        var rule = node;
+
+        node.prelude.children.each(function(selectorNode, selectorItem) {
+            var selector = {
+                item: selectorItem,
+                atrule: atrule,
+                rule: rule,
+                pseudos: []
+            };
+
+            selectorNode.children.each(function(selectorChildNode, selectorChildItem, selectorChildList) {
+                if (selectorChildNode.type === 'PseudoClassSelector' ||
+                    selectorChildNode.type === 'PseudoElementSelector') {
+                    selector.pseudos.push({
+                        item: selectorChildItem,
+                        list: selectorChildList
+                    });
+                }
+            });
+
+            selectors.push(selector);
+        });
+    }});
+
+    return selectors;
+}
+
+/**
+ * Filter selectors by Media Query.
+ *
+ * @param {Array} selectors to filter
+ * @param {Array} useMqs Array with strings of media queries that should pass (<name> <expression>)
+ * @return {Array} Filtered selectors that match the passed media queries
+ */
+function filterByMqs(selectors, useMqs) {
+    return selectors.filter(function(selector) {
+        if (selector.atrule === null) {
+            return ~useMqs.indexOf('');
+        }
+
+        var mqName = selector.atrule.name;
+        var mqStr = mqName;
+        if (selector.atrule.expression &&
+            selector.atrule.expression.children.first().type === 'MediaQueryList') {
+            var mqExpr = csstree.generate(selector.atrule.expression);
+            mqStr = [mqName, mqExpr].join(' ');
+        }
+
+        return ~useMqs.indexOf(mqStr);
+    });
+}
+
+/**
+ * Filter selectors by the pseudo-elements and/or -classes they contain.
+ *
+ * @param {Array} selectors to filter
+ * @param {Array} usePseudos Array with strings of single or sequence of pseudo-elements and/or -classes that should pass
+ * @return {Array} Filtered selectors that match the passed pseudo-elements and/or -classes
+ */
+function filterByPseudos(selectors, usePseudos) {
+    return selectors.filter(function(selector) {
+        var pseudoSelectorsStr = csstree.generate({
+            type: 'Selector',
+            children: new List().fromArray(selector.pseudos.map(function(pseudo) {
+                return pseudo.item.data;
+            }))
+        });
+        return ~usePseudos.indexOf(pseudoSelectorsStr);
+    });
+}
+
+/**
+ * Remove pseudo-elements and/or -classes from the selectors for proper matching.
+ *
+ * @param {Array} selectors to clean
+ * @return {Array} Selectors without pseudo-elements and/or -classes
+ */
+function cleanPseudos(selectors) {
+    selectors.forEach(function(selector) {
+        selector.pseudos.forEach(function(pseudo) {
+            pseudo.list.remove(pseudo.item);
+        });
+    });
+}
+
+
+/**
+ * Compares two selector specificities.
+ * extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
+ *
+ * @param {Array} aSpecificity Specificity of selector A
+ * @param {Array} bSpecificity Specificity of selector B
+ * @return {Number} Score of selector specificity A compared to selector specificity B
+ */
+function compareSpecificity(aSpecificity, bSpecificity) {
+    for (var i = 0; i < 4; i += 1) {
+        if (aSpecificity[i] < bSpecificity[i]) {
+            return -1;
+        } else if (aSpecificity[i] > bSpecificity[i]) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+
+/**
+ * Compare two simple selectors.
+ *
+ * @param {Object} aSimpleSelectorNode Simple selector A
+ * @param {Object} bSimpleSelectorNode Simple selector B
+ * @return {Number} Score of selector A compared to selector B
+ */
+function compareSimpleSelectorNode(aSimpleSelectorNode, bSimpleSelectorNode) {
+    var aSpecificity = specificity(aSimpleSelectorNode),
+        bSpecificity = specificity(bSimpleSelectorNode);
+    return compareSpecificity(aSpecificity, bSpecificity);
+}
+
+function _bySelectorSpecificity(selectorA, selectorB) {
+    return compareSimpleSelectorNode(selectorA.item.data, selectorB.item.data);
+}
+
+
+/**
+ * Sort selectors stably by their specificity.
+ *
+ * @param {Array} selectors to be sorted
+ * @return {Array} Stable sorted selectors
+ */
+function sortSelectors(selectors) {
+    return stable(selectors, _bySelectorSpecificity);
+}
+
+
+/**
+ * Convert a css-tree AST style declaration to CSSStyleDeclaration property.
+ *
+ * @param {Object} declaration css-tree style declaration
+ * @return {Object} CSSStyleDeclaration property
+ */
+function csstreeToStyleDeclaration(declaration) {
+    var propertyName = declaration.property,
+        propertyValue = csstree.generate(declaration.value),
+        propertyPriority = (declaration.important ? 'important' : '');
+    return {
+        name: propertyName,
+        value: propertyValue,
+        priority: propertyPriority
+    };
+}
+
+
+/**
+ * Gets the CSS string of a style element
+ *
+ * @param {Object} element style element
+ * @return {String|Array} CSS string or empty array if no styles are set
+ */
+function getCssStr(elem) {
+    return elem.content[0].text || elem.content[0].cdata || [];
+}
+
+/**
+ * Sets the CSS string of a style element
+ *
+ * @param {Object} element style element
+ * @param {String} CSS string to be set
+ * @return {Object} reference to field with CSS
+ */
+function setCssStr(elem, css) {
+    // in case of cdata field
+    if(elem.content[0].cdata) {
+        elem.content[0].cdata = css;
+        return elem.content[0].cdata;
+    }
+
+    // in case of text field + if nothing was set yet
+    elem.content[0].text  = css;
+    return elem.content[0].text;
+}
+
+
+module.exports.flattenToSelectors = flattenToSelectors;
+
+module.exports.filterByMqs = filterByMqs;
+module.exports.filterByPseudos = filterByPseudos;
+module.exports.cleanPseudos = cleanPseudos;
+
+module.exports.compareSpecificity = compareSpecificity;
+module.exports.compareSimpleSelectorNode = compareSimpleSelectorNode;
+
+module.exports.sortSelectors = sortSelectors;
+
+module.exports.csstreeToStyleDeclaration = csstreeToStyleDeclaration;
+
+module.exports.getCssStr = getCssStr;
+module.exports.setCssStr = setCssStr;
diff --git a/node_modules/svgo/lib/svgo.js b/node_modules/svgo/lib/svgo.js
new file mode 100644
index 0000000..cd1569f
--- /dev/null
+++ b/node_modules/svgo/lib/svgo.js
@@ -0,0 +1,90 @@
+'use strict';
+
+/**
+ * SVGO is a Nodejs-based tool for optimizing SVG vector graphics files.
+ *
+ * @see https://github.com/svg/svgo
+ *
+ * @author Kir Belevich <kir@soulshine.in> (https://github.com/deepsweet)
+ * @copyright © 2012 Kir Belevich
+ * @license MIT https://raw.githubusercontent.com/svg/svgo/master/LICENSE
+ */
+
+var CONFIG = require('./svgo/config.js'),
+    SVG2JS = require('./svgo/svg2js.js'),
+    PLUGINS = require('./svgo/plugins.js'),
+    JSAPI = require('./svgo/jsAPI.js'),
+    encodeSVGDatauri = require('./svgo/tools.js').encodeSVGDatauri,
+    JS2SVG = require('./svgo/js2svg.js');
+
+var SVGO = function(config) {
+    this.config = CONFIG(config);
+};
+
+SVGO.prototype.optimize = function(svgstr, info) {
+    info = info || {};
+    return new Promise((resolve, reject) => {
+        if (this.config.error) {
+            reject(this.config.error);
+            return;
+        }
+
+        var config = this.config,
+            maxPassCount = config.multipass ? 10 : 1,
+            counter = 0,
+            prevResultSize = Number.POSITIVE_INFINITY,
+            optimizeOnceCallback = (svgjs) => {
+                if (svgjs.error) {
+                    reject(svgjs.error);
+                    return;
+                }
+
+                info.multipassCount = counter;
+                if (++counter < maxPassCount && svgjs.data.length < prevResultSize) {
+                    prevResultSize = svgjs.data.length;
+                    this._optimizeOnce(svgjs.data, info, optimizeOnceCallback);
+                } else {
+                    if (config.datauri) {
+                        svgjs.data = encodeSVGDatauri(svgjs.data, config.datauri);
+                    }
+                    if (info && info.path) {
+                        svgjs.path = info.path;
+                    }
+                    resolve(svgjs);
+                }
+            };
+
+        this._optimizeOnce(svgstr, info, optimizeOnceCallback);
+    });
+};
+
+SVGO.prototype._optimizeOnce = function(svgstr, info, callback) {
+    var config = this.config;
+
+    SVG2JS(svgstr, function(svgjs) {
+        if (svgjs.error) {
+            callback(svgjs);
+            return;
+        }
+
+        svgjs = PLUGINS(svgjs, info, config.plugins);
+
+        callback(JS2SVG(svgjs, config.js2svg));
+    });
+};
+
+/**
+ * The factory that creates a content item with the helper methods.
+ *
+ * @param {Object} data which passed to jsAPI constructor
+ * @returns {JSAPI} content item
+ */
+SVGO.prototype.createContentItem = function(data) {
+    return new JSAPI(data);
+};
+
+SVGO.Config = CONFIG;
+
+module.exports = SVGO;
+// Offer ES module interop compatibility.
+module.exports.default = SVGO;
diff --git a/node_modules/svgo/lib/svgo/coa.js b/node_modules/svgo/lib/svgo/coa.js
new file mode 100644
index 0000000..1ac62ad
--- /dev/null
+++ b/node_modules/svgo/lib/svgo/coa.js
@@ -0,0 +1,579 @@
+/* jshint quotmark: false */
+'use strict';
+
+var FS = require('fs'),
+    PATH = require('path'),
+    chalk = require('chalk'),
+    mkdirp = require('mkdirp'),
+    promisify = require('util.promisify'),
+    readdir = promisify(FS.readdir),
+    readFile = promisify(FS.readFile),
+    writeFile = promisify(FS.writeFile),
+    SVGO = require('../svgo.js'),
+    YAML = require('js-yaml'),
+    PKG = require('../../package.json'),
+    encodeSVGDatauri = require('./tools.js').encodeSVGDatauri,
+    decodeSVGDatauri = require('./tools.js').decodeSVGDatauri,
+    checkIsDir = require('./tools.js').checkIsDir,
+    regSVGFile = /\.svg$/,
+    noop = () => {},
+    svgo;
+
+/**
+ * Command-Option-Argument.
+ *
+ * @see https://github.com/veged/coa
+ */
+module.exports = require('coa').Cmd()
+    .helpful()
+    .name(PKG.name)
+    .title(PKG.description)
+    .opt()
+        .name('version').title('Version')
+        .short('v').long('version')
+        .only()
+        .flag()
+        .act(function() {
+            // output the version to stdout instead of stderr if returned
+            process.stdout.write(PKG.version + '\n');
+            // coa will run `.toString` on the returned value and send it to stderr
+            return '';
+        })
+        .end()
+    .opt()
+        .name('input').title('Input file, "-" for STDIN')
+        .short('i').long('input')
+        .arr()
+        .val(function(val) {
+            return val || this.reject("Option '--input' must have a value.");
+        })
+        .end()
+    .opt()
+        .name('string').title('Input SVG data string')
+        .short('s').long('string')
+        .end()
+    .opt()
+        .name('folder').title('Input folder, optimize and rewrite all *.svg files')
+        .short('f').long('folder')
+        .val(function(val) {
+            return val || this.reject("Option '--folder' must have a value.");
+        })
+        .end()
+    .opt()
+        .name('output').title('Output file or folder (by default the same as the input), "-" for STDOUT')
+        .short('o').long('output')
+        .arr()
+        .val(function(val) {
+            return val || this.reject("Option '--output' must have a value.");
+        })
+        .end()
+    .opt()
+        .name('precision').title('Set number of digits in the fractional part, overrides plugins params')
+        .short('p').long('precision')
+        .val(function(val) {
+            return !isNaN(val) ? val : this.reject("Option '--precision' must be an integer number");
+        })
+        .end()
+    .opt()
+        .name('config').title('Config file or JSON string to extend or replace default')
+        .long('config')
+        .val(function(val) {
+            return val || this.reject("Option '--config' must have a value.");
+        })
+        .end()
+    .opt()
+        .name('disable').title('Disable plugin by name, "--disable={PLUGIN1,PLUGIN2}" for multiple plugins (*nix)')
+        .long('disable')
+        .arr()
+        .val(function(val) {
+            return val || this.reject("Option '--disable' must have a value.");
+        })
+        .end()
+    .opt()
+        .name('enable').title('Enable plugin by name, "--enable={PLUGIN3,PLUGIN4}" for multiple plugins (*nix)')
+        .long('enable')
+        .arr()
+        .val(function(val) {
+            return val || this.reject("Option '--enable' must have a value.");
+        })
+        .end()
+    .opt()
+        .name('datauri').title('Output as Data URI string (base64, URI encoded or unencoded)')
+        .long('datauri')
+        .val(function(val) {
+            return val || this.reject("Option '--datauri' must have one of the following values: 'base64', 'enc' or 'unenc'");
+        })
+        .end()
+    .opt()
+        .name('multipass').title('Pass over SVGs multiple times to ensure all optimizations are applied')
+        .long('multipass')
+        .flag()
+        .end()
+    .opt()
+        .name('pretty').title('Make SVG pretty printed')
+        .long('pretty')
+        .flag()
+        .end()
+    .opt()
+        .name('indent').title('Indent number when pretty printing SVGs')
+        .long('indent')
+        .val(function(val) {
+            return !isNaN(val) ? val : this.reject("Option '--indent' must be an integer number");
+        })
+        .end()
+    .opt()
+        .name('recursive').title('Use with \'-f\'. Optimizes *.svg files in folders recursively.')
+        .short('r').long('recursive')
+        .flag()
+        .end()
+    .opt()
+        .name('quiet').title('Only output error messages, not regular status messages')
+        .short('q').long('quiet')
+        .flag()
+        .end()
+    .opt()
+        .name('show-plugins').title('Show available plugins and exit')
+        .long('show-plugins')
+        .flag()
+        .end()
+    .arg()
+        .name('input').title('Alias to --input')
+        .arr()
+        .end()
+    .act(function(opts, args) {
+        var input = opts.input || args.input,
+            output = opts.output,
+            config = {};
+
+        // --show-plugins
+        if (opts['show-plugins']) {
+            showAvailablePlugins();
+            return;
+        }
+
+        // w/o anything
+        if (
+            (!input || input[0] === '-') &&
+            !opts.string &&
+            !opts.stdin &&
+            !opts.folder &&
+            process.stdin.isTTY === true
+        ) return this.usage();
+
+        if (typeof process == 'object' && process.versions && process.versions.node && PKG && PKG.engines.node) {
+            var nodeVersion = String(PKG.engines.node).match(/\d*(\.\d+)*/)[0];
+            if (parseFloat(process.versions.node) < parseFloat(nodeVersion)) {
+                return printErrorAndExit(`Error: ${PKG.name} requires Node.js version ${nodeVersion} or higher.`);
+            }
+        }
+
+        // --config
+        if (opts.config) {
+            // string
+            if (opts.config.charAt(0) === '{') {
+                try {
+                    config = JSON.parse(opts.config);
+                } catch (e) {
+                    return printErrorAndExit(`Error: Couldn't parse config JSON.\n${String(e)}`);
+                }
+            // external file
+            } else {
+                var configPath = PATH.resolve(opts.config),
+                    configData;
+                try {
+                    // require() adds some weird output on YML files
+                    configData = FS.readFileSync(configPath, 'utf8');
+                    config = JSON.parse(configData);
+                } catch (err) {
+                    if (err.code === 'ENOENT') {
+                        return printErrorAndExit(`Error: couldn't find config file '${opts.config}'.`);
+                    } else if (err.code === 'EISDIR') {
+                        return printErrorAndExit(`Error: directory '${opts.config}' is not a config file.`);
+                    }
+                    config = YAML.safeLoad(configData);
+                    config.__DIR = PATH.dirname(configPath); // will use it to resolve custom plugins defined via path
+
+                    if (!config || Array.isArray(config)) {
+                        return printErrorAndExit(`Error: invalid config file '${opts.config}'.`);
+                    }
+                }
+            }
+        }
+
+        // --quiet
+        if (opts.quiet) {
+            config.quiet = opts.quiet;
+        }
+
+        // --recursive
+        if (opts.recursive) {
+            config.recursive = opts.recursive;
+        }
+
+        // --precision
+        if (opts.precision) {
+            var precision = Math.min(Math.max(0, parseInt(opts.precision)), 20);
+            if (!isNaN(precision)) {
+                config.floatPrecision = precision;
+            }
+        }
+
+        // --disable
+        if (opts.disable) {
+            changePluginsState(opts.disable, false, config);
+        }
+
+        // --enable
+        if (opts.enable) {
+            changePluginsState(opts.enable, true, config);
+        }
+
+        // --multipass
+        if (opts.multipass) {
+            config.multipass = true;
+        }
+
+        // --pretty
+        if (opts.pretty) {
+            config.js2svg = config.js2svg || {};
+            config.js2svg.pretty = true;
+            var indent;
+            if (opts.indent && !isNaN(indent = parseInt(opts.indent))) {
+                config.js2svg.indent = indent;
+            }
+        }
+
+        svgo = new SVGO(config);
+
+        // --output
+        if (output) {
+            if (input && input[0] != '-') {
+                if (output.length == 1 && checkIsDir(output[0])) {
+                    var dir = output[0];
+                    for (var i = 0; i < input.length; i++) {
+                        output[i] = checkIsDir(input[i]) ? input[i] : PATH.resolve(dir, PATH.basename(input[i]));
+                    }
+                } else if (output.length < input.length) {
+                    output = output.concat(input.slice(output.length));
+                }
+            }
+        } else if (input) {
+            output = input;
+        } else if (opts.string) {
+            output = '-';
+        }
+
+        if (opts.datauri) {
+            config.datauri = opts.datauri;
+        }
+
+        // --folder
+        if (opts.folder) {
+            var ouputFolder = output && output[0] || opts.folder;
+            return optimizeFolder(config, opts.folder, ouputFolder).then(noop, printErrorAndExit);
+        }
+
+        // --input
+        if (input) {
+            // STDIN
+            if (input[0] === '-') {
+                return new Promise((resolve, reject) => {
+                    var data = '',
+                        file = output[0];
+
+                    process.stdin
+                        .on('data', chunk => data += chunk)
+                        .once('end', () => processSVGData(config, {input: 'string'}, data, file).then(resolve, reject));
+                });
+            // file
+            } else {
+                return Promise.all(input.map((file, n) => optimizeFile(config, file, output[n])))
+                    .then(noop, printErrorAndExit);
+            }
+
+        // --string
+        } else if (opts.string) {
+            var data = decodeSVGDatauri(opts.string);
+
+            return processSVGData(config, {input: 'string'}, data, output[0]);
+        }
+    });
+
+/**
+ * Change plugins state by names array.
+ *
+ * @param {Array} names plugins names
+ * @param {Boolean} state active state
+ * @param {Object} config original config
+ * @return {Object} changed config
+ */
+function changePluginsState(names, state, config) {
+    names.forEach(flattenPluginsCbk);
+
+    // extend config
+    if (config.plugins) {
+        for (var name of names) {
+            var matched = false,
+                key;
+
+            for (var plugin of config.plugins) {
+                // get plugin name
+                if (typeof plugin === 'object') {
+                    key = Object.keys(plugin)[0];
+                } else {
+                    key = plugin;
+                }
+
+                // if there is such a plugin name
+                if (key === name) {
+                    // don't replace plugin's params with true
+                    if (typeof plugin[key] !== 'object' || !state) {
+                        plugin[key] = state;
+                    }
+                    // mark it as matched
+                    matched = true;
+                }
+            }
+
+            // if not matched and current config is not full
+            if (!matched && !config.full) {
+                // push new plugin Object
+                config.plugins.push({ [name]: state });
+                matched = true;
+            }
+        }
+    // just push
+    } else {
+        config.plugins = names.map(name => ({ [name]: state }));
+    }
+    return config;
+}
+
+/**
+ * Flatten an array of plugins by invoking this callback on each element
+ * whose value may be a comma separated list of plugins.
+ *
+ * @param {String} name Plugin name
+ * @param {Number} index Plugin index
+ * @param {Array} names Plugins being traversed
+ */
+function flattenPluginsCbk(name, index, names)
+{
+    var split = name.split(',');
+
+    if(split.length > 1) {
+        names[index] = split.shift();
+        names.push.apply(names, split);
+    }
+
+}
+
+/**
+ * Optimize SVG files in a directory.
+ * @param {Object} config options
+ * @param {string} dir input directory
+ * @param {string} output output directory
+ * @return {Promise}
+ */
+function optimizeFolder(config, dir, output) {
+    if (!config.quiet) {
+        console.log(`Processing directory '${dir}':\n`);
+    }
+    return readdir(dir).then(files => processDirectory(config, dir, files, output));
+}
+
+/**
+ * Process given files, take only SVG.
+ * @param {Object} config options
+ * @param {string} dir input directory
+ * @param {Array} files list of file names in the directory
+ * @param {string} output output directory
+ * @return {Promise}
+ */
+function processDirectory(config, dir, files, output) {
+    // take only *.svg files, recursively if necessary
+    var svgFilesDescriptions = getFilesDescriptions(config, dir, files, output);
+
+    return svgFilesDescriptions.length ?
+        Promise.all(svgFilesDescriptions.map(fileDescription => optimizeFile(config, fileDescription.inputPath, fileDescription.outputPath))) :
+        Promise.reject(new Error(`No SVG files have been found in '${dir}' directory.`));
+}
+
+/**
+ * Get svg files descriptions
+ * @param {Object} config options
+ * @param {string} dir input directory
+ * @param {Array} files list of file names in the directory
+ * @param {string} output output directory
+ * @return {Array}
+ */
+function getFilesDescriptions(config, dir, files, output) {
+    const filesInThisFolder = files
+        .filter(name => regSVGFile.test(name))
+        .map(name => ({
+            inputPath: PATH.resolve(dir, name),
+            outputPath: PATH.resolve(output, name),
+        }));
+
+    return config.recursive ?
+        [].concat(
+            filesInThisFolder,
+            files
+                .filter(name => checkIsDir(PATH.resolve(dir, name)))
+                .map(subFolderName => {
+                    const subFolderPath = PATH.resolve(dir, subFolderName);
+                    const subFolderFiles = FS.readdirSync(subFolderPath);
+                    const subFolderOutput = PATH.resolve(output, subFolderName);
+                    return getFilesDescriptions(config, subFolderPath, subFolderFiles, subFolderOutput);
+                })
+                .reduce((a, b) => [].concat(a, b), [])
+        ) :
+        filesInThisFolder;
+}
+
+/**
+ * Read SVG file and pass to processing.
+ * @param {Object} config options
+ * @param {string} file
+ * @param {string} output
+ * @return {Promise}
+ */
+function optimizeFile(config, file, output) {
+    return readFile(file, 'utf8').then(
+        data => processSVGData(config, {input: 'file', path: file}, data, output, file),
+        error => checkOptimizeFileError(config, file, output, error)
+    );
+}
+
+/**
+ * Optimize SVG data.
+ * @param {Object} config options
+ * @param {string} data SVG content to optimize
+ * @param {string} output where to write optimized file
+ * @param {string} [input] input file name (being used if output is a directory)
+ * @return {Promise}
+ */
+function processSVGData(config, info, data, output, input) {
+    var startTime = Date.now(),
+        prevFileSize = Buffer.byteLength(data, 'utf8');
+
+    return svgo.optimize(data, info).then(function(result) {
+        if (config.datauri) {
+            result.data = encodeSVGDatauri(result.data, config.datauri);
+        }
+        var resultFileSize = Buffer.byteLength(result.data, 'utf8'),
+            processingTime = Date.now() - startTime;
+
+        return writeOutput(input, output, result.data).then(function() {
+            if (!config.quiet && output != '-') {
+                if (input) {
+                    console.log(`\n${PATH.basename(input)}:`);
+                }
+                printTimeInfo(processingTime);
+                printProfitInfo(prevFileSize, resultFileSize);
+            }
+        },
+        error => Promise.reject(new Error(error.code === 'ENOTDIR' ? `Error: output '${output}' is not a directory.` : error)));
+    });
+}
+
+/**
+ * Write result of an optimization.
+ * @param {string} input
+ * @param {string} output output file name. '-' for stdout
+ * @param {string} data data to write
+ * @return {Promise}
+ */
+function writeOutput(input, output, data) {
+    if (output == '-') {
+        console.log(data);
+        return Promise.resolve();
+    }
+
+    mkdirp.sync(PATH.dirname(output));
+
+    return writeFile(output, data, 'utf8').catch(error => checkWriteFileError(input, output, data, error));
+}
+
+
+/**
+ * Write a time taken by optimization.
+ * @param {number} time time in milliseconds.
+ */
+function printTimeInfo(time) {
+    console.log(`Done in ${time} ms!`);
+}
+
+/**
+ * Write optimizing information in human readable format.
+ * @param {number} inBytes size before optimization.
+ * @param {number} outBytes size after optimization.
+ */
+function printProfitInfo(inBytes, outBytes) {
+    var profitPercents = 100 - outBytes * 100 / inBytes;
+
+    console.log(
+        (Math.round((inBytes / 1024) * 1000) / 1000) + ' KiB' +
+        (profitPercents < 0 ? ' + ' : ' - ') +
+        chalk.green(Math.abs((Math.round(profitPercents * 10) / 10)) + '%') + ' = ' +
+        (Math.round((outBytes / 1024) * 1000) / 1000) + ' KiB'
+    );
+}
+
+/**
+ * Check for errors, if it's a dir optimize the dir.
+ * @param {Object} config
+ * @param {string} input
+ * @param {string} output
+ * @param {Error} error
+ * @return {Promise}
+ */
+function checkOptimizeFileError(config, input, output, error) {
+    if (error.code == 'EISDIR') {
+        return optimizeFolder(config, input, output);
+    } else if (error.code == 'ENOENT') {
+        return Promise.reject(new Error(`Error: no such file or directory '${error.path}'.`));
+    }
+    return Promise.reject(error);
+}
+
+/**
+ * Check for saving file error. If the output is a dir, then write file there.
+ * @param {string} input
+ * @param {string} output
+ * @param {string} data
+ * @param {Error} error
+ * @return {Promise}
+ */
+function checkWriteFileError(input, output, data, error) {
+    if (error.code == 'EISDIR' && input) {
+        return writeFile(PATH.resolve(output, PATH.basename(input)), data, 'utf8');
+    } else {
+        return Promise.reject(error);
+    }
+}
+
+/**
+ * Show list of available plugins with short description.
+ */
+function showAvailablePlugins() {
+    console.log('Currently available plugins:');
+
+    // Flatten an array of plugins grouped per type, sort and write output
+    var list = [].concat.apply([], new SVGO().config.plugins)
+        .sort((a, b) => a.name.localeCompare(b.name))
+        .map(plugin => ` [ ${chalk.green(plugin.name)} ] ${plugin.description}`)
+        .join('\n');
+    console.log(list);
+}
+
+/**
+ * Write an error and exit.
+ * @param {Error} error
+ * @return {Promise} a promise for running tests
+ */
+function printErrorAndExit(error) {
+    console.error(chalk.red(error));
+    process.exit(1);
+    return Promise.reject(error); // for tests
+}
diff --git a/node_modules/svgo/lib/svgo/config.js b/node_modules/svgo/lib/svgo/config.js
new file mode 100644
index 0000000..a3d7b49
--- /dev/null
+++ b/node_modules/svgo/lib/svgo/config.js
@@ -0,0 +1,250 @@
+'use strict';
+
+var FS = require('fs');
+var PATH = require('path');
+var yaml = require('js-yaml');
+
+/**
+ * Read and/or extend/replace default config file,
+ * prepare and optimize plugins array.
+ *
+ * @param {Object} [config] input config
+ * @return {Object} output config
+ */
+module.exports = function(config) {
+
+    var defaults;
+    config = typeof config == 'object' && config || {};
+
+    if (config.plugins && !Array.isArray(config.plugins)) {
+        return { error: 'Error: Invalid plugins list. Provided \'plugins\' in config should be an array.' };
+    }
+
+    if (config.full) {
+        defaults = config;
+
+        if (Array.isArray(defaults.plugins)) {
+            defaults.plugins = preparePluginsArray(config, defaults.plugins);
+        }
+    } else {
+        defaults = Object.assign({}, yaml.safeLoad(FS.readFileSync(__dirname + '/../../.svgo.yml', 'utf8')));
+        defaults.plugins = preparePluginsArray(config, defaults.plugins || []);
+        defaults = extendConfig(defaults, config);
+    }
+
+    if ('floatPrecision' in config && Array.isArray(defaults.plugins)) {
+        defaults.plugins.forEach(function(plugin) {
+            if (plugin.params && ('floatPrecision' in plugin.params)) {
+                // Don't touch default plugin params
+                plugin.params = Object.assign({}, plugin.params, { floatPrecision: config.floatPrecision });
+            }
+        });
+    }
+
+    if ('datauri' in config) {
+        defaults.datauri = config.datauri;
+    }
+
+    if (Array.isArray(defaults.plugins)) {
+        defaults.plugins = optimizePluginsArray(defaults.plugins);
+    }
+
+    return defaults;
+
+};
+
+/**
+ * Require() all plugins in array.
+ *
+ * @param {Object} config
+ * @param {Array} plugins input plugins array
+ * @return {Array} input plugins array of arrays
+ */
+function preparePluginsArray(config, plugins) {
+
+    var plugin,
+        key;
+
+    return plugins.map(function(item) {
+
+        // {}
+        if (typeof item === 'object') {
+
+            key = Object.keys(item)[0];
+
+            // custom
+            if (typeof item[key] === 'object' && item[key].fn && typeof item[key].fn === 'function') {
+                plugin = setupCustomPlugin(key, item[key]);
+
+            } else {
+
+                plugin = setPluginActiveState(
+                    loadPlugin(config, key, item[key].path),
+                    item,
+                    key
+                );
+                plugin.name = key;
+            }
+
+        // name
+        } else {
+
+            plugin = loadPlugin(config, item);
+            plugin.name = item;
+            if (typeof plugin.params === 'object') {
+                plugin.params = Object.assign({}, plugin.params);
+            }
+
+        }
+
+        return plugin;
+
+    });
+
+}
+
+/**
+ * Extend plugins with the custom config object.
+ *
+ * @param {Array} plugins input plugins
+ * @param {Object} config config
+ * @return {Array} output plugins
+ */
+function extendConfig(defaults, config) {
+
+    var key;
+
+    // plugins
+    if (config.plugins) {
+
+        config.plugins.forEach(function(item) {
+
+            // {}
+            if (typeof item === 'object') {
+
+                key = Object.keys(item)[0];
+
+                if (item[key] == null) {
+                    console.error(`Error: '${key}' plugin is misconfigured! Have you padded its content in YML properly?\n`);
+                }
+
+                // custom
+                if (typeof item[key] === 'object' && item[key].fn && typeof item[key].fn === 'function') {
+                    defaults.plugins.push(setupCustomPlugin(key, item[key]));
+
+                // plugin defined via path
+                } else if (typeof item[key] === 'object' && item[key].path) {
+                    defaults.plugins.push(setPluginActiveState(loadPlugin(config, undefined, item[key].path), item, key));
+
+                } else {
+                    defaults.plugins.forEach(function(plugin) {
+
+                        if (plugin.name === key) {
+                            plugin = setPluginActiveState(plugin, item, key);
+                        }
+                    });
+                }
+
+            }
+
+        });
+
+    }
+
+    defaults.multipass = config.multipass;
+
+    // svg2js
+    if (config.svg2js) {
+        defaults.svg2js = config.svg2js;
+    }
+
+    // js2svg
+    if (config.js2svg) {
+        defaults.js2svg = config.js2svg;
+    }
+
+    return defaults;
+
+}
+
+/**
+ * Setup and enable a custom plugin
+ *
+ * @param {String} plugin name
+ * @param {Object} custom plugin
+ * @return {Array} enabled plugin
+ */
+function setupCustomPlugin(name, plugin) {
+    plugin.active = true;
+    plugin.params = Object.assign({}, plugin.params || {});
+    plugin.name = name;
+
+    return plugin;
+}
+
+/**
+ * Try to group sequential elements of plugins array.
+ *
+ * @param {Object} plugins input plugins
+ * @return {Array} output plugins
+ */
+function optimizePluginsArray(plugins) {
+
+    var prev;
+
+    return plugins.reduce(function(plugins, item) {
+        if (prev && item.type == prev[0].type) {
+            prev.push(item);
+        } else {
+            plugins.push(prev = [item]);
+        }
+        return plugins;
+    }, []);
+
+}
+
+/**
+ * Sets plugin to active or inactive state.
+ *
+ * @param {Object} plugin
+ * @param {Object} item
+ * @param {Object} key
+ * @return {Object} plugin
+ */
+function setPluginActiveState(plugin, item, key) {
+    // name: {}
+    if (typeof item[key] === 'object') {
+        plugin.params = Object.assign({}, plugin.params || {}, item[key]);
+        plugin.active = true;
+
+    // name: false
+    } else if (item[key] === false) {
+        plugin.active = false;
+
+    // name: true
+    } else if (item[key] === true) {
+        plugin.active = true;
+    }
+
+    return plugin;
+}
+
+/**
+ * Loads default plugin using name or custom plugin defined via path in config.
+ *
+ * @param {Object} config
+ * @param {Object} name
+ * @param {Object} path
+ * @return {Object} plugin
+ */
+function loadPlugin(config, name, path) {
+    var plugin;
+
+    if (!path) {
+        plugin = require('../../plugins/' + name);
+    } else {
+        plugin = require(PATH.resolve(config.__DIR, path));
+    }
+
+    return Object.assign({}, plugin);
+}
diff --git a/node_modules/svgo/lib/svgo/css-class-list.js b/node_modules/svgo/lib/svgo/css-class-list.js
new file mode 100644
index 0000000..8401a9f
--- /dev/null
+++ b/node_modules/svgo/lib/svgo/css-class-list.js
@@ -0,0 +1,138 @@
+'use strict';
+
+var values = require('object.values');
+if (!Object.values) {
+    values.shim();
+}
+
+
+var CSSClassList = function(node) {
+    this.parentNode = node;
+    this.classNames = new Set();
+    this.classAttr = null;
+    //this.classValue = null;
+};
+
+/**
+ * Performs a deep clone of this object.
+ *
+ * @param parentNode the parentNode to assign to the cloned result
+ */
+CSSClassList.prototype.clone = function(parentNode) {
+    var node = this;
+    var nodeData = {};
+
+    Object.keys(node).forEach(function(key) {
+        if (key !== 'parentNode') {
+            nodeData[key] = node[key];
+        }
+    });
+
+    // Deep-clone node data.
+    nodeData = JSON.parse(JSON.stringify(nodeData));
+
+    var clone = new CSSClassList(parentNode);
+   Object.assign(clone, nodeData);
+    return clone;
+};
+
+CSSClassList.prototype.hasClass = function() {
+    this.classAttr = { // empty class attr
+        'name': 'class',
+        'value': null
+    };
+
+    this.addClassHandler();
+};
+
+
+// attr.class
+
+CSSClassList.prototype.addClassHandler = function() {
+
+    Object.defineProperty(this.parentNode.attrs, 'class', {
+        get: this.getClassAttr.bind(this),
+        set: this.setClassAttr.bind(this),
+        enumerable: true,
+        configurable: true
+    });
+
+    this.addClassValueHandler();
+};
+
+// attr.class.value
+
+CSSClassList.prototype.addClassValueHandler = function() {
+
+    Object.defineProperty(this.classAttr, 'value', {
+        get: this.getClassValue.bind(this),
+        set: this.setClassValue.bind(this),
+        enumerable: true,
+        configurable: true
+    });
+};
+
+CSSClassList.prototype.getClassAttr = function() {
+    return this.classAttr;
+};
+
+CSSClassList.prototype.setClassAttr = function(newClassAttr) {
+    this.setClassValue(newClassAttr.value); // must before applying value handler!
+
+    this.classAttr = newClassAttr;
+    this.addClassValueHandler();
+};
+
+CSSClassList.prototype.getClassValue = function() {
+    var arrClassNames = Array.from(this.classNames);
+    return arrClassNames.join(' ');
+};
+
+CSSClassList.prototype.setClassValue = function(newValue) {
+    if(typeof newValue === 'undefined') {
+      this.classNames.clear();
+      return;
+    }
+    var arrClassNames = newValue.split(' ');
+    this.classNames = new Set(arrClassNames);
+};
+
+
+CSSClassList.prototype.add = function(/* variadic */) {
+    this.hasClass();
+    Object.values(arguments).forEach(this._addSingle.bind(this));
+};
+
+CSSClassList.prototype._addSingle = function(className) {
+    this.classNames.add(className);
+};
+
+
+CSSClassList.prototype.remove = function(/* variadic */) {
+    this.hasClass();
+    Object.values(arguments).forEach(this._removeSingle.bind(this));
+};
+
+CSSClassList.prototype._removeSingle = function(className) {
+    this.classNames.delete(className);
+};
+
+
+CSSClassList.prototype.item = function(index) {
+    var arrClassNames = Array.from(this.classNames);
+    return arrClassNames[index];
+};
+
+CSSClassList.prototype.toggle = function(className, force) {
+    if(this.contains(className) || force === false) {
+        this.classNames.delete(className);
+    }
+    this.classNames.add(className);
+};
+
+CSSClassList.prototype.contains = function(className) {
+    return this.classNames.has(className);
+};
+
+
+module.exports = CSSClassList;
\ No newline at end of file
diff --git a/node_modules/svgo/lib/svgo/css-select-adapter.js b/node_modules/svgo/lib/svgo/css-select-adapter.js
new file mode 100644
index 0000000..c37678c
--- /dev/null
+++ b/node_modules/svgo/lib/svgo/css-select-adapter.js
@@ -0,0 +1,53 @@
+'use strict';
+
+var baseCssAdapter = require('css-select-base-adapter');
+
+/**
+ * DOMUtils API for SVGO AST (used by css-select)
+ */
+var svgoCssSelectAdapterMin = {
+
+    // is the node a tag?
+    // isTag: ( node:Node ) => isTag:Boolean
+    isTag: function(node) {
+        return node.isElem();
+    },
+
+    // get the parent of the node
+    // getParent: ( node:Node ) => parentNode:Node
+    // returns null when no parent exists
+    getParent: function(node) {
+        return node.parentNode || null;
+    },
+
+    // get the node's children
+    // getChildren: ( node:Node ) => children:[Node]
+    getChildren: function(node) {
+        return node.content || [];
+    },
+
+    // get the name of the tag
+    // getName: ( elem:ElementNode ) => tagName:String
+    getName: function(elemAst) {
+        return elemAst.elem;
+    },
+
+    // get the text content of the node, and its children if it has any
+    // getText: ( node:Node ) => text:String
+    // returns empty string when there is no text
+    getText: function(node) {
+        return node.content[0].text || node.content[0].cdata || '';
+    },
+
+    // get the attribute value
+    // getAttributeValue: ( elem:ElementNode, name:String ) => value:String
+    // returns null when attribute doesn't exist
+    getAttributeValue: function(elem, name) {
+        return elem.hasAttr(name) ? elem.attr(name).value : null;
+    }
+};
+
+// use base adapter for default implementation
+var svgoCssSelectAdapter = baseCssAdapter(svgoCssSelectAdapterMin);
+
+module.exports = svgoCssSelectAdapter;
diff --git a/node_modules/svgo/lib/svgo/css-style-declaration.js b/node_modules/svgo/lib/svgo/css-style-declaration.js
new file mode 100644
index 0000000..fb6d781
--- /dev/null
+++ b/node_modules/svgo/lib/svgo/css-style-declaration.js
@@ -0,0 +1,285 @@
+'use strict';
+
+var csstree = require('css-tree'),
+    csstools = require('../css-tools');
+
+
+var CSSStyleDeclaration = function(node) {
+    this.parentNode = node;
+
+    this.properties = new Map();
+    this.hasSynced = false;
+
+    this.styleAttr = null;
+    this.styleValue = null;
+
+    this.parseError = false;
+};
+
+/**
+ * Performs a deep clone of this object.
+ *
+ * @param parentNode the parentNode to assign to the cloned result
+ */
+CSSStyleDeclaration.prototype.clone = function(parentNode) {
+    var node = this;
+    var nodeData = {};
+
+    Object.keys(node).forEach(function(key) {
+        if (key !== 'parentNode') {
+            nodeData[key] = node[key];
+        }
+    });
+
+    // Deep-clone node data.
+    nodeData = JSON.parse(JSON.stringify(nodeData));
+
+    var clone = new CSSStyleDeclaration(parentNode);
+    Object.assign(clone, nodeData);
+    return clone;
+};
+
+CSSStyleDeclaration.prototype.hasStyle = function() {
+    this.addStyleHandler();
+};
+
+
+
+
+// attr.style
+
+CSSStyleDeclaration.prototype.addStyleHandler = function() {
+
+    this.styleAttr = { // empty style attr
+        'name': 'style',
+        'value': null
+    };
+
+    Object.defineProperty(this.parentNode.attrs, 'style', {
+        get: this.getStyleAttr.bind(this),
+        set: this.setStyleAttr.bind(this),
+        enumerable: true,
+        configurable: true
+    });
+
+    this.addStyleValueHandler();
+};
+
+// attr.style.value
+
+CSSStyleDeclaration.prototype.addStyleValueHandler = function() {
+
+    Object.defineProperty(this.styleAttr, 'value', {
+        get: this.getStyleValue.bind(this),
+        set: this.setStyleValue.bind(this),
+        enumerable: true,
+        configurable: true
+    });
+};
+
+CSSStyleDeclaration.prototype.getStyleAttr = function() {
+    return this.styleAttr;
+};
+
+CSSStyleDeclaration.prototype.setStyleAttr = function(newStyleAttr) {
+    this.setStyleValue(newStyleAttr.value); // must before applying value handler!
+
+    this.styleAttr = newStyleAttr;
+    this.addStyleValueHandler();
+    this.hasSynced = false; // raw css changed
+};
+
+CSSStyleDeclaration.prototype.getStyleValue = function() {
+    return this.getCssText();
+};
+
+CSSStyleDeclaration.prototype.setStyleValue = function(newValue) {
+    this.properties.clear(); // reset all existing properties
+    this.styleValue = newValue;
+    this.hasSynced = false; // raw css changed
+};
+
+
+
+
+CSSStyleDeclaration.prototype._loadCssText = function() {
+    if (this.hasSynced) {
+        return;
+    }
+    this.hasSynced = true; // must be set here to prevent loop in setProperty(...)
+
+    if (!this.styleValue || this.styleValue.length === 0) {
+        return;
+    }
+    var inlineCssStr = this.styleValue;
+
+    var declarations = {};
+    try {
+        declarations = csstree.parse(inlineCssStr, {
+            context: 'declarationList',
+            parseValue: false
+        });
+    } catch (parseError) {
+        this.parseError = parseError;
+        return;
+    }
+    this.parseError = false;
+
+    var self = this;
+    declarations.children.each(function(declaration) {
+        try {
+          var styleDeclaration = csstools.csstreeToStyleDeclaration(declaration);
+          self.setProperty(styleDeclaration.name, styleDeclaration.value, styleDeclaration.priority);
+        } catch(styleError) {
+            if(styleError.message !== 'Unknown node type: undefined') {
+                self.parseError = styleError;
+            }
+        }
+    });
+};
+
+
+// only reads from properties
+
+/**
+ * Get the textual representation of the declaration block (equivalent to .cssText attribute).
+ *
+ * @return {String} Textual representation of the declaration block (empty string for no properties)
+ */
+CSSStyleDeclaration.prototype.getCssText = function() {
+    var properties = this.getProperties();
+
+    if (this.parseError) {
+        // in case of a parse error, pass through original styles
+        return this.styleValue;
+    }
+
+    var cssText = [];
+    properties.forEach(function(property, propertyName) {
+        var strImportant = property.priority === 'important' ? '!important' : '';
+        cssText.push(propertyName.trim() + ':' + property.value.trim() + strImportant);
+    });
+    return cssText.join(';');
+};
+
+CSSStyleDeclaration.prototype._handleParseError = function() {
+    if (this.parseError) {
+        console.warn('Warning: Parse error when parsing inline styles, style properties of this element cannot be used. The raw styles can still be get/set using .attr(\'style\').value. Error details: ' + this.parseError);
+    }
+};
+
+
+CSSStyleDeclaration.prototype._getProperty = function(propertyName) {
+    if(typeof propertyName === 'undefined') {
+        throw Error('1 argument required, but only 0 present.');
+    }
+
+    var properties = this.getProperties();
+    this._handleParseError();
+
+    var property = properties.get(propertyName.trim());
+    return property;
+};
+
+/**
+ * Return the optional priority, "important".
+ *
+ * @param {String} propertyName representing the property name to be checked.
+ * @return {String} priority that represents the priority (e.g. "important") if one exists. If none exists, returns the empty string.
+ */
+CSSStyleDeclaration.prototype.getPropertyPriority = function(propertyName) {
+    var property = this._getProperty(propertyName);
+    return property ? property.priority : '';
+};
+
+/**
+ * Return the property value given a property name.
+ *
+ * @param {String} propertyName representing the property name to be checked.
+ * @return {String} value containing the value of the property. If not set, returns the empty string.
+ */
+CSSStyleDeclaration.prototype.getPropertyValue = function(propertyName) {
+    var property = this._getProperty(propertyName);
+    return property ? property.value : null;
+};
+
+/**
+ * Return a property name.
+ *
+ * @param {Number} index of the node to be fetched. The index is zero-based.
+ * @return {String} propertyName that is the name of the CSS property at the specified index.
+ */
+CSSStyleDeclaration.prototype.item = function(index) {
+    if(typeof index === 'undefined') {
+        throw Error('1 argument required, but only 0 present.');
+    }
+
+    var properties = this.getProperties();
+    this._handleParseError();
+
+    return Array.from(properties.keys())[index];
+};
+
+/**
+ * Return all properties of the node.
+ *
+ * @return {Map} properties that is a Map with propertyName as key and property (propertyValue + propertyPriority) as value.
+ */
+CSSStyleDeclaration.prototype.getProperties = function() {
+    this._loadCssText();
+    return this.properties;
+};
+
+
+// writes to properties
+
+/**
+ * Remove a property from the CSS declaration block.
+ *
+ * @param {String} propertyName representing the property name to be removed.
+ * @return {String} oldValue equal to the value of the CSS property before it was removed.
+ */
+CSSStyleDeclaration.prototype.removeProperty = function(propertyName) {
+    if(typeof propertyName === 'undefined') {
+        throw Error('1 argument required, but only 0 present.');
+    }
+
+    this.hasStyle();
+
+    var properties = this.getProperties();
+    this._handleParseError();
+
+    var oldValue = this.getPropertyValue(propertyName);
+    properties.delete(propertyName.trim());
+    return oldValue;
+};
+
+/**
+ * Modify an existing CSS property or creates a new CSS property in the declaration block.
+ *
+ * @param {String} propertyName representing the CSS property name to be modified.
+ * @param {String} [value] containing the new property value. If not specified, treated as the empty string. value must not contain "!important" -- that should be set using the priority parameter.
+ * @param {String} [priority] allowing the "important" CSS priority to be set. If not specified, treated as the empty string.
+ * @return {undefined}
+ */
+CSSStyleDeclaration.prototype.setProperty = function(propertyName, value, priority) {
+    if(typeof propertyName === 'undefined') {
+        throw Error('propertyName argument required, but only not present.');
+    }
+
+    this.hasStyle();
+
+    var properties = this.getProperties();
+    this._handleParseError();
+
+    var property = {
+        value: value.trim(),
+        priority: priority.trim()
+    };
+    properties.set(propertyName.trim(), property);
+
+    return property;
+};
+
+
+module.exports = CSSStyleDeclaration;
diff --git a/node_modules/svgo/lib/svgo/js2svg.js b/node_modules/svgo/lib/svgo/js2svg.js
new file mode 100644
index 0000000..0a52c6b
--- /dev/null
+++ b/node_modules/svgo/lib/svgo/js2svg.js
@@ -0,0 +1,348 @@
+'use strict';
+
+var EOL = require('os').EOL,
+    textElem = require('../../plugins/_collections.js').elemsGroups.textContent.concat('title');
+
+var defaults = {
+    doctypeStart: '<!DOCTYPE',
+    doctypeEnd: '>',
+    procInstStart: '<?',
+    procInstEnd: '?>',
+    tagOpenStart: '<',
+    tagOpenEnd: '>',
+    tagCloseStart: '</',
+    tagCloseEnd: '>',
+    tagShortStart: '<',
+    tagShortEnd: '/>',
+    attrStart: '="',
+    attrEnd: '"',
+    commentStart: '<!--',
+    commentEnd: '-->',
+    cdataStart: '<![CDATA[',
+    cdataEnd: ']]>',
+    textStart: '',
+    textEnd: '',
+    indent: 4,
+    regEntities: /[&'"<>]/g,
+    regValEntities: /[&"<>]/g,
+    encodeEntity: encodeEntity,
+    pretty: false,
+    useShortTags: true
+};
+
+var entities = {
+      '&': '&amp;',
+      '\'': '&apos;',
+      '"': '&quot;',
+      '>': '&gt;',
+      '<': '&lt;',
+    };
+
+/**
+ * Convert SVG-as-JS object to SVG (XML) string.
+ *
+ * @param {Object} data input data
+ * @param {Object} config config
+ *
+ * @return {Object} output data
+ */
+module.exports = function(data, config) {
+
+    return new JS2SVG(config).convert(data);
+
+};
+
+function JS2SVG(config) {
+
+    if (config) {
+        this.config = Object.assign({}, defaults, config);
+    } else {
+        this.config = Object.assign({}, defaults);
+    }
+
+    var indent = this.config.indent;
+    if (typeof indent == 'number' && !isNaN(indent)) {
+        this.config.indent = (indent < 0) ? '\t' : ' '.repeat(indent);
+    } else if (typeof indent != 'string') {
+        this.config.indent = '    ';
+    }
+
+    if (this.config.pretty) {
+        this.config.doctypeEnd += EOL;
+        this.config.procInstEnd += EOL;
+        this.config.commentEnd += EOL;
+        this.config.cdataEnd += EOL;
+        this.config.tagShortEnd += EOL;
+        this.config.tagOpenEnd += EOL;
+        this.config.tagCloseEnd += EOL;
+        this.config.textEnd += EOL;
+    }
+
+    this.indentLevel = 0;
+    this.textContext = null;
+
+}
+
+function encodeEntity(char) {
+    return entities[char];
+}
+
+/**
+ * Start conversion.
+ *
+ * @param {Object} data input data
+ *
+ * @return {String}
+ */
+JS2SVG.prototype.convert = function(data) {
+
+    var svg = '';
+
+    if (data.content) {
+
+        this.indentLevel++;
+
+        data.content.forEach(function(item) {
+
+            if (item.elem) {
+               svg += this.createElem(item);
+            } else if (item.text) {
+               svg += this.createText(item.text);
+            } else if (item.doctype) {
+                svg += this.createDoctype(item.doctype);
+            } else if (item.processinginstruction) {
+                svg += this.createProcInst(item.processinginstruction);
+            } else if (item.comment) {
+                svg += this.createComment(item.comment);
+            } else if (item.cdata) {
+                svg += this.createCDATA(item.cdata);
+            }
+
+        }, this);
+
+    }
+
+    this.indentLevel--;
+
+    return {
+        data: svg,
+        info: {
+            width: this.width,
+            height: this.height
+        }
+    };
+
+};
+
+/**
+ * Create indent string in accordance with the current node level.
+ *
+ * @return {String}
+ */
+JS2SVG.prototype.createIndent = function() {
+
+    var indent = '';
+
+    if (this.config.pretty && !this.textContext) {
+        indent = this.config.indent.repeat(this.indentLevel - 1);
+    }
+
+    return indent;
+
+};
+
+/**
+ * Create doctype tag.
+ *
+ * @param {String} doctype doctype body string
+ *
+ * @return {String}
+ */
+JS2SVG.prototype.createDoctype = function(doctype) {
+
+    return  this.config.doctypeStart +
+            doctype +
+            this.config.doctypeEnd;
+
+};
+
+/**
+ * Create XML Processing Instruction tag.
+ *
+ * @param {Object} instruction instruction object
+ *
+ * @return {String}
+ */
+JS2SVG.prototype.createProcInst = function(instruction) {
+
+    return  this.config.procInstStart +
+            instruction.name +
+            ' ' +
+            instruction.body +
+            this.config.procInstEnd;
+
+};
+
+/**
+ * Create comment tag.
+ *
+ * @param {String} comment comment body
+ *
+ * @return {String}
+ */
+JS2SVG.prototype.createComment = function(comment) {
+
+    return  this.config.commentStart +
+            comment +
+            this.config.commentEnd;
+
+};
+
+/**
+ * Create CDATA section.
+ *
+ * @param {String} cdata CDATA body
+ *
+ * @return {String}
+ */
+JS2SVG.prototype.createCDATA = function(cdata) {
+
+    return  this.createIndent() +
+            this.config.cdataStart +
+            cdata +
+            this.config.cdataEnd;
+
+};
+
+/**
+ * Create element tag.
+ *
+ * @param {Object} data element object
+ *
+ * @return {String}
+ */
+JS2SVG.prototype.createElem = function(data) {
+
+    // beautiful injection for obtaining SVG information :)
+    if (
+        data.isElem('svg') &&
+        data.hasAttr('width') &&
+        data.hasAttr('height')
+    ) {
+        this.width = data.attr('width').value;
+        this.height = data.attr('height').value;
+    }
+
+    // empty element and short tag
+    if (data.isEmpty()) {
+        if (this.config.useShortTags) {
+            return this.createIndent() +
+                   this.config.tagShortStart +
+                   data.elem +
+                   this.createAttrs(data) +
+                   this.config.tagShortEnd;
+        } else {
+            return this.createIndent() +
+                   this.config.tagShortStart +
+                   data.elem +
+                   this.createAttrs(data) +
+                   this.config.tagOpenEnd +
+                   this.config.tagCloseStart +
+                   data.elem +
+                   this.config.tagCloseEnd;
+        }
+    // non-empty element
+    } else {
+        var tagOpenStart = this.config.tagOpenStart,
+            tagOpenEnd = this.config.tagOpenEnd,
+            tagCloseStart = this.config.tagCloseStart,
+            tagCloseEnd = this.config.tagCloseEnd,
+            openIndent = this.createIndent(),
+            textIndent = '',
+            processedData = '',
+            dataEnd = '';
+
+        if (this.textContext) {
+            tagOpenStart = defaults.tagOpenStart;
+            tagOpenEnd = defaults.tagOpenEnd;
+            tagCloseStart = defaults.tagCloseStart;
+            tagCloseEnd = defaults.tagCloseEnd;
+            openIndent = '';
+        } else if (data.isElem(textElem)) {
+            if (this.config.pretty) {
+                textIndent += openIndent + this.config.indent;
+            }
+            this.textContext = data;
+        }
+
+        processedData += this.convert(data).data;
+
+        if (this.textContext == data) {
+            this.textContext = null;
+            if (this.config.pretty) dataEnd = EOL;
+        }
+
+        return  openIndent +
+                tagOpenStart +
+                data.elem +
+                this.createAttrs(data) +
+                tagOpenEnd +
+                textIndent +
+                processedData +
+                dataEnd +
+                this.createIndent() +
+                tagCloseStart +
+                data.elem +
+                tagCloseEnd;
+
+    }
+
+};
+
+/**
+ * Create element attributes.
+ *
+ * @param {Object} elem attributes object
+ *
+ * @return {String}
+ */
+JS2SVG.prototype.createAttrs = function(elem) {
+
+    var attrs = '';
+
+    elem.eachAttr(function(attr) {
+
+        if (attr.value !== undefined) {
+            attrs +=    ' ' +
+                        attr.name +
+                        this.config.attrStart +
+                        String(attr.value).replace(this.config.regValEntities, this.config.encodeEntity) +
+                        this.config.attrEnd;
+        }
+        else {
+            attrs +=    ' ' +
+                        attr.name;
+        }
+
+
+    }, this);
+
+    return attrs;
+
+};
+
+/**
+ * Create text node.
+ *
+ * @param {String} text text
+ *
+ * @return {String}
+ */
+JS2SVG.prototype.createText = function(text) {
+
+    return  this.createIndent() +
+            this.config.textStart +
+            text.replace(this.config.regEntities, this.config.encodeEntity) +
+            (this.textContext ? '' : this.config.textEnd);
+
+};
diff --git a/node_modules/svgo/lib/svgo/jsAPI.js b/node_modules/svgo/lib/svgo/jsAPI.js
new file mode 100644
index 0000000..4cf958c
--- /dev/null
+++ b/node_modules/svgo/lib/svgo/jsAPI.js
@@ -0,0 +1,372 @@
+'use strict';
+
+var cssSelect = require('css-select');
+
+var svgoCssSelectAdapter = require('./css-select-adapter');
+var cssSelectOpts = {
+  xmlMode: true,
+  adapter: svgoCssSelectAdapter
+};
+
+var JSAPI = module.exports = function(data, parentNode) {
+    Object.assign(this, data);
+    if (parentNode) {
+        Object.defineProperty(this, 'parentNode', {
+            writable: true,
+            value: parentNode
+        });
+    }
+};
+
+/**
+ * Perform a deep clone of this node.
+ *
+ * @return {Object} element
+ */
+JSAPI.prototype.clone = function() {
+    var node = this;
+    var nodeData = {};
+
+    Object.keys(node).forEach(function(key) {
+        if (key !== 'class' && key !== 'style' && key !== 'content') {
+            nodeData[key] = node[key];
+        }
+    });
+
+    // Deep-clone node data.
+    nodeData = JSON.parse(JSON.stringify(nodeData));
+
+    // parentNode gets set to a proper object by the parent clone,
+    // but it needs to be true/false now to do the right thing
+    // in the constructor.
+    var clonedNode = new JSAPI(nodeData, !!node.parentNode);
+
+    if (node.class) {
+        clonedNode.class = node.class.clone(clonedNode);
+    }
+    if (node.style) {
+        clonedNode.style = node.style.clone(clonedNode);
+    }
+    if (node.content) {
+        clonedNode.content = node.content.map(function(childNode) {
+            var clonedChild = childNode.clone();
+            clonedChild.parentNode = clonedNode;
+            return clonedChild;
+        });
+    }
+
+    return clonedNode;
+};
+
+/**
+ * Determine if item is an element
+ * (any, with a specific name or in a names array).
+ *
+ * @param {String|Array} [param] element name or names arrays
+ * @return {Boolean}
+ */
+JSAPI.prototype.isElem = function(param) {
+
+    if (!param) return !!this.elem;
+
+    if (Array.isArray(param)) return !!this.elem && (param.indexOf(this.elem) > -1);
+
+    return !!this.elem && this.elem === param;
+
+};
+
+/**
+ * Renames an element
+ *
+ * @param {String} name new element name
+ * @return {Object} element
+ */
+JSAPI.prototype.renameElem = function(name) {
+
+    if (name && typeof name === 'string')
+        this.elem = this.local = name;
+
+    return this;
+
+};
+
+/**
+ * Determine if element is empty.
+ *
+ * @return {Boolean}
+ */
+ JSAPI.prototype.isEmpty = function() {
+
+    return !this.content || !this.content.length;
+
+};
+
+/**
+ * Find the closest ancestor of the current element.
+ * @param elemName
+ *
+ * @return {?Object}
+ */
+ JSAPI.prototype.closestElem = function(elemName) {
+    var elem = this;
+
+    while ((elem = elem.parentNode) && !elem.isElem(elemName));
+
+    return elem;
+};
+
+/**
+ * Changes content by removing elements and/or adding new elements.
+ *
+ * @param {Number} start Index at which to start changing the content.
+ * @param {Number} n Number of elements to remove.
+ * @param {Array|Object} [insertion] Elements to add to the content.
+ * @return {Array} Removed elements.
+ */
+ JSAPI.prototype.spliceContent = function(start, n, insertion) {
+
+    if (arguments.length < 2) return [];
+
+    if (!Array.isArray(insertion))
+        insertion = Array.apply(null, arguments).slice(2);
+
+    insertion.forEach(function(inner) { inner.parentNode = this }, this);
+
+    return this.content.splice.apply(this.content, [start, n].concat(insertion));
+
+
+};
+
+/**
+ * Determine if element has an attribute
+ * (any, or by name or by name + value).
+ *
+ * @param {String} [name] attribute name
+ * @param {String} [val] attribute value (will be toString()'ed)
+ * @return {Boolean}
+ */
+ JSAPI.prototype.hasAttr = function(name, val) {
+
+    if (!this.attrs || !Object.keys(this.attrs).length) return false;
+
+    if (!arguments.length) return !!this.attrs;
+
+    if (val !== undefined) return !!this.attrs[name] && this.attrs[name].value === val.toString();
+
+    return !!this.attrs[name];
+
+};
+
+/**
+ * Determine if element has an attribute by local name
+ * (any, or by name or by name + value).
+ *
+ * @param {String} [localName] local attribute name
+ * @param {Number|String|RegExp|Function} [val] attribute value (will be toString()'ed or executed, otherwise ignored)
+ * @return {Boolean}
+ */
+ JSAPI.prototype.hasAttrLocal = function(localName, val) {
+
+    if (!this.attrs || !Object.keys(this.attrs).length) return false;
+
+    if (!arguments.length) return !!this.attrs;
+
+    var callback;
+
+    switch (val != null && val.constructor && val.constructor.name) {
+        case 'Number':   // same as String
+        case 'String':   callback = stringValueTest; break;
+        case 'RegExp':   callback = regexpValueTest; break;
+        case 'Function': callback = funcValueTest; break;
+        default:         callback = nameTest;
+    }
+    return this.someAttr(callback);
+
+    function nameTest(attr) {
+        return attr.local === localName;
+    }
+
+    function stringValueTest(attr) {
+        return attr.local === localName && val == attr.value;
+    }
+
+    function regexpValueTest(attr) {
+        return attr.local === localName && val.test(attr.value);
+    }
+
+    function funcValueTest(attr) {
+        return attr.local === localName && val(attr.value);
+    }
+
+};
+
+/**
+ * Get a specific attribute from an element
+ * (by name or name + value).
+ *
+ * @param {String} name attribute name
+ * @param {String} [val] attribute value (will be toString()'ed)
+ * @return {Object|Undefined}
+ */
+ JSAPI.prototype.attr = function(name, val) {
+
+    if (!this.hasAttr() || !arguments.length) return undefined;
+
+    if (val !== undefined) return this.hasAttr(name, val) ? this.attrs[name] : undefined;
+
+    return this.attrs[name];
+
+};
+
+/**
+ * Get computed attribute value from an element
+ *
+ * @param {String} name attribute name
+ * @return {Object|Undefined}
+ */
+ JSAPI.prototype.computedAttr = function(name, val) {
+    /* jshint eqnull: true */
+    if (!arguments.length) return;
+
+    for (var elem = this; elem && (!elem.hasAttr(name) || !elem.attr(name).value); elem = elem.parentNode);
+
+    if (val != null) {
+        return elem ? elem.hasAttr(name, val) : false;
+    } else if (elem && elem.hasAttr(name)) {
+        return elem.attrs[name].value;
+    }
+
+};
+
+/**
+ * Remove a specific attribute.
+ *
+ * @param {String|Array} name attribute name
+ * @param {String} [val] attribute value
+ * @return {Boolean}
+ */
+ JSAPI.prototype.removeAttr = function(name, val, recursive) {
+
+    if (!arguments.length) return false;
+
+    if (Array.isArray(name)) {
+        name.forEach(this.removeAttr, this);
+        return false;
+    }
+
+    if (!this.hasAttr(name)) return false;
+
+    if (!recursive && val && this.attrs[name].value !== val) return false;
+
+    delete this.attrs[name];
+
+    if (!Object.keys(this.attrs).length) delete this.attrs;
+
+    return true;
+
+};
+
+/**
+ * Add attribute.
+ *
+ * @param {Object} [attr={}] attribute object
+ * @return {Object|Boolean} created attribute or false if no attr was passed in
+ */
+ JSAPI.prototype.addAttr = function(attr) {
+    attr = attr || {};
+
+    if (attr.name === undefined ||
+        attr.prefix === undefined ||
+        attr.local === undefined
+    ) return false;
+
+    this.attrs = this.attrs || {};
+    this.attrs[attr.name] = attr;
+
+    if(attr.name === 'class') { // newly added class attribute
+        this.class.hasClass();
+    }
+
+    if(attr.name === 'style') { // newly added style attribute
+        this.style.hasStyle();
+    }
+
+    return this.attrs[attr.name];
+
+};
+
+/**
+ * Iterates over all attributes.
+ *
+ * @param {Function} callback callback
+ * @param {Object} [context] callback context
+ * @return {Boolean} false if there are no any attributes
+ */
+ JSAPI.prototype.eachAttr = function(callback, context) {
+
+    if (!this.hasAttr()) return false;
+
+    for (var name in this.attrs) {
+        callback.call(context, this.attrs[name]);
+    }
+
+    return true;
+
+};
+
+/**
+ * Tests whether some attribute passes the test.
+ *
+ * @param {Function} callback callback
+ * @param {Object} [context] callback context
+ * @return {Boolean} false if there are no any attributes
+ */
+ JSAPI.prototype.someAttr = function(callback, context) {
+
+    if (!this.hasAttr()) return false;
+
+    for (var name in this.attrs) {
+        if (callback.call(context, this.attrs[name])) return true;
+    }
+
+    return false;
+
+};
+
+/**
+ * Evaluate a string of CSS selectors against the element and returns matched elements.
+ *
+ * @param {String} selectors CSS selector(s) string
+ * @return {Array} null if no elements matched
+ */
+ JSAPI.prototype.querySelectorAll = function(selectors) {
+
+   var matchedEls = cssSelect(selectors, this, cssSelectOpts);
+
+   return matchedEls.length > 0 ? matchedEls : null;
+
+};
+
+/**
+ * Evaluate a string of CSS selectors against the element and returns only the first matched element.
+ *
+ * @param {String} selectors CSS selector(s) string
+ * @return {Array} null if no element matched
+ */
+ JSAPI.prototype.querySelector = function(selectors) {
+
+   return cssSelect.selectOne(selectors, this, cssSelectOpts);
+
+};
+
+/**
+ * Test if a selector matches a given element.
+ *
+ * @param {String} selector CSS selector string
+ * @return {Boolean} true if element would be selected by selector string, false if it does not
+ */
+ JSAPI.prototype.matches = function(selector) {
+
+   return cssSelect.is(this, selector, cssSelectOpts);
+
+};
diff --git a/node_modules/svgo/lib/svgo/plugins.js b/node_modules/svgo/lib/svgo/plugins.js
new file mode 100644
index 0000000..a317f5b
--- /dev/null
+++ b/node_modules/svgo/lib/svgo/plugins.js
@@ -0,0 +1,101 @@
+'use strict';
+
+/**
+ * Plugins engine.
+ *
+ * @module plugins
+ *
+ * @param {Object} data input data
+ * @param {Object} info extra information
+ * @param {Object} plugins plugins object from config
+ * @return {Object} output data
+ */
+module.exports = function(data, info, plugins) {
+
+    plugins.forEach(function(group) {
+
+        switch(group[0].type) {
+            case 'perItem':
+                data = perItem(data, info, group);
+                break;
+            case 'perItemReverse':
+                data = perItem(data, info, group, true);
+                break;
+            case 'full':
+                data = full(data, info, group);
+                break;
+        }
+
+    });
+
+    return data;
+
+};
+
+/**
+ * Direct or reverse per-item loop.
+ *
+ * @param {Object} data input data
+ * @param {Object} info extra information
+ * @param {Array} plugins plugins list to process
+ * @param {Boolean} [reverse] reverse pass?
+ * @return {Object} output data
+ */
+function perItem(data, info, plugins, reverse) {
+
+    function monkeys(items) {
+
+        items.content = items.content.filter(function(item) {
+
+            // reverse pass
+            if (reverse && item.content) {
+                monkeys(item);
+            }
+
+            // main filter
+            var filter = true;
+
+            for (var i = 0; filter && i < plugins.length; i++) {
+                var plugin = plugins[i];
+
+                if (plugin.active && plugin.fn(item, plugin.params, info) === false) {
+                    filter = false;
+                }
+            }
+
+            // direct pass
+            if (!reverse && item.content) {
+                monkeys(item);
+            }
+
+            return filter;
+
+        });
+
+        return items;
+
+    }
+
+    return monkeys(data);
+
+}
+
+/**
+ * "Full" plugins.
+ *
+ * @param {Object} data input data
+ * @param {Object} info extra information
+ * @param {Array} plugins plugins list to process
+ * @return {Object} output data
+ */
+function full(data, info, plugins) {
+
+    plugins.forEach(function(plugin) {
+        if (plugin.active) {
+            data = plugin.fn(data, plugin.params, info);
+        }
+    });
+
+    return data;
+
+}
diff --git a/node_modules/svgo/lib/svgo/svg2js.js b/node_modules/svgo/lib/svgo/svg2js.js
new file mode 100644
index 0000000..6bba13e
--- /dev/null
+++ b/node_modules/svgo/lib/svgo/svg2js.js
@@ -0,0 +1,200 @@
+'use strict';
+
+var SAX = require('sax'),
+    JSAPI = require('./jsAPI.js'),
+    CSSClassList = require('./css-class-list'),
+    CSSStyleDeclaration = require('./css-style-declaration'),
+    entityDeclaration = /<!ENTITY\s+(\S+)\s+(?:'([^\']+)'|"([^\"]+)")\s*>/g;
+
+var config = {
+    strict: true,
+    trim: false,
+    normalize: true,
+    lowercase: true,
+    xmlns: true,
+    position: true
+};
+
+/**
+ * Convert SVG (XML) string to SVG-as-JS object.
+ *
+ * @param {String} data input data
+ * @param {Function} callback
+ */
+module.exports = function(data, callback) {
+
+    var sax = SAX.parser(config.strict, config),
+        root = new JSAPI({ elem: '#document', content: [] }),
+        current = root,
+        stack = [root],
+        textContext = null,
+        parsingError = false;
+
+    function pushToContent(content) {
+
+        content = new JSAPI(content, current);
+
+        (current.content = current.content || []).push(content);
+
+        return content;
+
+    }
+
+    sax.ondoctype = function(doctype) {
+
+        pushToContent({
+            doctype: doctype
+        });
+
+        var subsetStart = doctype.indexOf('['),
+            entityMatch;
+
+        if (subsetStart >= 0) {
+            entityDeclaration.lastIndex = subsetStart;
+
+            while ((entityMatch = entityDeclaration.exec(data)) != null) {
+                sax.ENTITIES[entityMatch[1]] = entityMatch[2] || entityMatch[3];
+            }
+        }
+    };
+
+    sax.onprocessinginstruction = function(data) {
+
+        pushToContent({
+            processinginstruction: data
+        });
+
+    };
+
+    sax.oncomment = function(comment) {
+
+        pushToContent({
+            comment: comment.trim()
+        });
+
+    };
+
+    sax.oncdata = function(cdata) {
+
+        pushToContent({
+            cdata: cdata
+        });
+
+    };
+
+    sax.onopentag = function(data) {
+
+        var elem = {
+            elem: data.name,
+            prefix: data.prefix,
+            local: data.local,
+            attrs: {}
+        };
+
+        elem.class = new CSSClassList(elem);
+        elem.style = new CSSStyleDeclaration(elem);
+
+        if (Object.keys(data.attributes).length) {
+            for (var name in data.attributes) {
+
+                if (name === 'class') { // has class attribute
+                    elem.class.hasClass();
+                }
+
+                if (name === 'style') { // has style attribute
+                    elem.style.hasStyle();
+                }
+
+                elem.attrs[name] = {
+                    name: name,
+                    value: data.attributes[name].value,
+                    prefix: data.attributes[name].prefix,
+                    local: data.attributes[name].local
+                };
+            }
+        }
+
+        elem = pushToContent(elem);
+        current = elem;
+
+        // Save info about <text> tag to prevent trimming of meaningful whitespace
+        if (data.name == 'text' && !data.prefix) {
+            textContext = current;
+        }
+
+        stack.push(elem);
+
+    };
+
+    sax.ontext = function(text) {
+
+        if (/\S/.test(text) || textContext) {
+
+            if (!textContext)
+                text = text.trim();
+
+            pushToContent({
+                text: text
+            });
+
+        }
+
+    };
+
+    sax.onclosetag = function() {
+
+        var last = stack.pop();
+
+        // Trim text inside <text> tag.
+        if (last == textContext) {
+            trim(textContext);
+            textContext = null;
+        }
+        current = stack[stack.length - 1];
+
+    };
+
+    sax.onerror = function(e) {
+
+        e.message = 'Error in parsing SVG: ' + e.message;
+        if (e.message.indexOf('Unexpected end') < 0) {
+            throw e;
+        }
+
+    };
+
+    sax.onend = function() {
+
+        if (!this.error) {
+            callback(root);
+        } else {
+            callback({ error: this.error.message });
+        }
+
+    };
+
+    try {
+        sax.write(data);
+    } catch (e) {
+        callback({ error: e.message });
+        parsingError = true;
+    }
+    if (!parsingError) sax.close();
+
+    function trim(elem) {
+        if (!elem.content) return elem;
+
+        var start = elem.content[0],
+            end = elem.content[elem.content.length - 1];
+
+        while (start && start.content && !start.text) start = start.content[0];
+        if (start && start.text) start.text = start.text.replace(/^\s+/, '');
+
+        while (end && end.content && !end.text) end = end.content[end.content.length - 1];
+        if (end && end.text) end.text = end.text.replace(/\s+$/, '');
+
+        return elem;
+
+    }
+
+};
diff --git a/node_modules/svgo/lib/svgo/tools.js b/node_modules/svgo/lib/svgo/tools.js
new file mode 100644
index 0000000..ed426d4
--- /dev/null
+++ b/node_modules/svgo/lib/svgo/tools.js
@@ -0,0 +1,155 @@
+'use strict';
+
+var FS = require('fs');
+
+/**
+ * Encode plain SVG data string into Data URI string.
+ *
+ * @param {String} str input string
+ * @param {String} type Data URI type
+ * @return {String} output string
+ */
+exports.encodeSVGDatauri = function(str, type) {
+    var prefix = 'data:image/svg+xml';
+    if (!type || type === 'base64') {
+        // base64
+        prefix += ';base64,';
+        if (Buffer.from) {
+            str = prefix + Buffer.from(str).toString('base64');
+        } else {
+            str = prefix + new Buffer(str).toString('base64');
+        }
+    } else if (type === 'enc') {
+        // URI encoded
+        str = prefix + ',' + encodeURIComponent(str);
+    } else if (type === 'unenc') {
+        // unencoded
+        str = prefix + ',' + str;
+    }
+    return str;
+};
+
+/**
+ * Decode SVG Data URI string into plain SVG string.
+ *
+ * @param {string} str input string
+ * @return {String} output string
+ */
+exports.decodeSVGDatauri = function(str) {
+    var regexp = /data:image\/svg\+xml(;charset=[^;,]*)?(;base64)?,(.*)/;
+    var match = regexp.exec(str);
+
+    // plain string
+    if (!match) return str;
+
+    var data = match[3];
+
+    if (match[2]) {
+        // base64
+        str = new Buffer(data, 'base64').toString('utf8');
+    } else if (data.charAt(0) === '%') {
+        // URI encoded
+        str = decodeURIComponent(data);
+    } else if (data.charAt(0) === '<') {
+        // unencoded
+        str = data;
+    }
+    return str;
+};
+
+exports.intersectArrays = function(a, b) {
+    return a.filter(function(n) {
+        return b.indexOf(n) > -1;
+    });
+};
+
+/**
+ * Convert a row of numbers to an optimized string view.
+ *
+ * @example
+ * [0, -1, .5, .5] → "0-1 .5.5"
+ *
+ * @param {number[]} data
+ * @param {Object} params
+ * @param {string?} command path data instruction
+ * @return {string}
+ */
+exports.cleanupOutData = function(data, params, command) {
+    var str = '',
+        delimiter,
+        prev;
+
+    data.forEach(function(item, i) {
+        // space delimiter by default
+        delimiter = ' ';
+
+        // no extra space in front of first number
+        if (i == 0) delimiter = '';
+
+        // no extra space after 'arcto' command flags
+        if (params.noSpaceAfterFlags && (command == 'A' || command == 'a')) {
+            var pos = i % 7;
+            if (pos == 4 || pos == 5) delimiter = '';
+        }
+
+        // remove floating-point numbers leading zeros
+        // 0.5 → .5
+        // -0.5 → -.5
+        if (params.leadingZero) {
+            item = removeLeadingZero(item);
+        }
+
+        // no extra space in front of negative number or
+        // in front of a floating number if a previous number is floating too
+        if (
+            params.negativeExtraSpace &&
+            delimiter != '' &&
+            (item < 0 ||
+                (String(item).charCodeAt(0) == 46 && prev % 1 !== 0)
+            )
+        ) {
+            delimiter = '';
+        }
+        // save prev item value
+        prev = item;
+        str += delimiter + item;
+    });
+    return str;
+};
+
+/**
+ * Remove floating-point numbers leading zero.
+ *
+ * @example
+ * 0.5 → .5
+ *
+ * @example
+ * -0.5 → -.5
+ *
+ * @param {Float} num input number
+ *
+ * @return {String} output number as string
+ */
+var removeLeadingZero = exports.removeLeadingZero = function(num) {
+    var strNum = num.toString();
+
+    if (0 < num && num < 1 && strNum.charCodeAt(0) == 48) {
+        strNum = strNum.slice(1);
+    } else if (-1 < num && num < 0 && strNum.charCodeAt(1) == 48) {
+        strNum = strNum.charAt(0) + strNum.slice(2);
+    }
+    return strNum;
+};
+
+
+/**
+ * Synchronously check if path is a directory. Tolerant to errors like ENOENT.
+ * @param {string} path
+ */
+exports.checkIsDir = function(path) {
+    try {
+        return FS.lstatSync(path).isDirectory();
+    } catch(e) {
+        return false;
+    }
+};