Repository: svg/svgo
Pull request #854 (2017-11-24 20:29:12)

fix broken jsapi clone method
Since the introduction of the inlineStyles plugin, the jsAPI clone method no longer works (`JSON.parse(JSON.stringify(...)` throws an error due to the circular references).

This pull request fixes this by introducing two new `clone()` methods for the `CSSStyleDeclaration` and `CSSClassList` classes.


Patch:
diff --git a/.gitignore b/.gitignore
index b7c204f..7ab3c06 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ bin/svgo-profiling
 *.sublime-*
 *.log
 .DS_Store
+.idea
diff --git a/.npmignore b/.npmignore
index a99468b..fa34277 100644
--- a/.npmignore
+++ b/.npmignore
@@ -3,6 +3,7 @@ examples
 test
 lib-cov
 .editorconfig
+.gitignore
 .jshintignore
 .jshintrc
 .travis.yml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 90f2410..2ddc48d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+### [ [>](https://github.com/svg/svgo/tree/v1.0.5) ] 1.0.5 / 26.02.2018
+* Fixed issue with prefixIDs plugin not replacing url() values correctly (by @harrisjose).
+
+### [ [>](https://github.com/svg/svgo/tree/v1.0.4) ] 1.0.4 / 30.01.2018
+* Fixed bug with removing groups that are direct child of "<switch>".
+* Fixed bug with shorthand path points counting (thanks @alexjlockwood for noticing).
+* Fixed crash on parsing invalid transform, e.g. without close parenthesis.
+
 ### [ [>](https://github.com/svg/svgo/tree/v1.0.3) ] 1.0.3 / 08.11.2017
 * Fixed `removeViewBox` plugin to check for zero start coordinates.
 * Removed extra info from STDOUT when it set to output.
@@ -27,7 +35,7 @@
 * New option `keepAriaAttrs` in `removeUnknownsAndDefaults` (by @davidtheclark).
 * Corrected parsing in `cleanupIDs` to account animation syntax (by @caub).
 * `#ff0000` now converts to `red` as well as `#f00` (by @davidleston).
-* Added “gray” variation to colors list per CSS Color Module Level 4 (by @JoshyPHP).
+* Added “gray” variation to colors list per CSS Color Module Level 4 (by @ydaniv).
 * Fixed error on empty files.
 * A separator character in `removeAttrs` now can be changed per `elemSeparator` option (by @mikestreety).
 * `addAttributesToSVGElement` now can add values to attributes.
@@ -36,7 +44,7 @@
 * Extended `currentColor` match conditions (string, rx, bool) (by @AlimovSV)
 * Fixed removing `<animate>` in `<stop>`.
 * Fixed removing same transform in inner element in `removeUnknownsAndDefaults`.
-* Fixed collapsing groups with same non-inheritable attribue.
+* Fixed collapsing groups with same non-inheritable attribute.
 * Corrected removing of leading zero in case of exponential notation.
 
 ### [ [>](https://github.com/svg/svgo/tree/v0.7.1) ] 0.7.1 / 27.09.2016
@@ -60,7 +68,7 @@
 
 ### [ [>](https://github.com/svg/svgo/tree/v0.6.5) ] 0.6.5 / 25.04.2016
 * Extra content inserted by editors are now being removed within `<foreignObject>` as well thus fixing bug “Namespace prefix … is not defined“ after applying SVGO.
-* Doctype with entities declartion is now also being removed since svgo correctly parses them starting from the version [0.6.2](https://github.com/svg/svgo/tree/v0.6.2).
+* Doctype with entities declaration is now also being removed since svgo correctly parses them starting from the version [0.6.2](https://github.com/svg/svgo/tree/v0.6.2).
 * Corrected `moveGroupAttrsToElems` not to move attributes to `g` content if it's referenced (has an `id`).
 * `collapseGroups` now don't collapse a group if it has an animated attribute (SMIL).
 
@@ -82,10 +90,10 @@
 * Fixed error on converting curves to arcs.
 * Corrected rounding in subsequent passes with `--multipass` option.
 * Data URI option now handles charset (by @holymonson)
-* Tranformations are no longer moved to group if there is a mask (`plugins/moveElemsAttrsToGroup.js`).
+* Transformations are no longer moved to group if there is a mask (`plugins/moveElemsAttrsToGroup.js`).
 * Fixed matrix decomposition losing sign in case like `[1, 0, 0, -1, 0, 0]` (`scale(1 -1)`).
 * Fixed crash on uppercased color name.
-* Paths with `id` and without `stroke-width` aren't being trasformed now since `stroke-width` may be applied later.
+* Paths with `id` and without `stroke-width` aren't being transformed now since `stroke-width` may be applied later.
 
 ### [ [>](https://github.com/svg/svgo/tree/v0.6.1) ] 0.6.1 / 21.11.2015
 * Added option `--quiet` to suppress output (by @phihag).
@@ -97,9 +105,9 @@
 * New optimization: circular curves now being converted to arcs. A notable improvement for circles within paths.
 * New plugin “[minifyStyles](https://github.com/svg/svgo/blob/master/plugins/minifyStyles.js)” which minifies `<style>` elments content with CSSO by @strarsis (svgo still doesn't understand its content)
 * New plugin “[removeStyleElement](https://github.com/svg/svgo/blob/master/plugins/removeStyleElement.js)” (disabled by default) by @betsydupuis.
-* Fixed issues wuth parsing numbers with exponent fraction (could happen with high precision >= 7).
+* Fixed issues with parsing numbers with exponent fraction (could happen with high precision >= 7).
 * Fixed rounding error due to incorrect preserving of precision in transformations.
-* Fixed shortand curve distortion due to converted previous curve to not a curve.
+* Fixed shorthand curve distortion due to converted previous curve to not a curve.
 * Fixed interoperability issue with `precision` cli-option and `full` config.
 * Fixed an error produced by “[removeUnknownsAndDefaults](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js)” by @thiakil
 * Another Inkscape prefix namespace is being removed.
diff --git a/README.md b/README.md
index ef4ff67..b7a6a9b 100644
--- a/README.md
+++ b/README.md
@@ -3,14 +3,14 @@
 
 <img src="https://svg.github.io/svgo-logo.svg" width="200" height="200" alt="logo"/>
 
-## SVGO [![NPM version](https://badge.fury.io/js/svgo.svg)](https://npmjs.org/package/svgo) [![Dependency Status](https://gemnasium.com/svg/svgo.svg)](https://gemnasium.com/svg/svgo) [![Build Status](https://secure.travis-ci.org/svg/svgo.svg)](https://travis-ci.org/svg/svgo) [![Coverage Status](https://img.shields.io/coveralls/svg/svgo.svg)](https://coveralls.io/r/svg/svgo?branch=master)
+## SVGO [![NPM version](https://badge.fury.io/js/svgo.svg)](https://npmjs.org/package/svgo) [![Build Status](https://secure.travis-ci.org/svg/svgo.svg)](https://travis-ci.org/svg/svgo) [![Coverage Status](https://img.shields.io/coveralls/svg/svgo.svg)](https://coveralls.io/r/svg/svgo?branch=master)
 
 **SVG O**ptimizer is a Nodejs-based tool for optimizing SVG vector graphics files.
 ![](https://mc.yandex.ru/watch/18431326)
 
 ## Why?
 
-SVG files, especially exported from various editors, usually contain a lot of redundant and useless information such as editor metadata, comments, hidden elements, default or non-optimal values and other stuff that can be safely removed or converted without affecting SVG rendering result.
+SVG files, especially those exported from various editors, usually contain a lot of redundant and useless information. This can include editor metadata, comments, hidden elements, default or non-optimal values and other stuff that can be safely removed or converted without affecting the SVG rendering result.
 
 ## What it can do
 
@@ -21,6 +21,7 @@ Today we have:
 | Plugin | Description |
 | ------ | ----------- |
 | [cleanupAttrs](https://github.com/svg/svgo/blob/master/plugins/cleanupAttrs.js) | cleanup attributes from newlines, trailing, and repeating spaces |
+| [inlineStyles](https://github.com/svg/svgo/blob/master/plugins/inlineStyles.js) | move and merge styles from `<style>` elements to element `style` attributes |
 | [removeDoctype](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js) | remove doctype declaration |
 | [removeXMLProcInst](https://github.com/svg/svgo/blob/master/plugins/removeXMLProcInst.js) | remove XML processing instructions |
 | [removeComments](https://github.com/svg/svgo/blob/master/plugins/removeComments.js) | remove comments |
@@ -47,7 +48,7 @@ Today we have:
 | [removeUnusedNS](https://github.com/svg/svgo/blob/master/plugins/removeUnusedNS.js) | remove unused namespaces declaration |
 | [cleanupIDs](https://github.com/svg/svgo/blob/master/plugins/cleanupIDs.js) | remove unused and minify used IDs |
 | [cleanupNumericValues](https://github.com/svg/svgo/blob/master/plugins/cleanupNumericValues.js) | round numeric values to the fixed precision, remove default `px` units |
-| [cleanupListOfValues](https://github.com/svg/svgo/blob/master/plugins/cleanupListOfValues.js) | round numeric values in attributes that take a list of numbers (like `viewBox` or `enableBackground`) |
+| [cleanupListOfValues](https://github.com/svg/svgo/blob/master/plugins/cleanupListOfValues.js) | round numeric values in attributes that take a list of numbers (like `viewBox` or `enable-background`) |
 | [moveElemsAttrsToGroup](https://github.com/svg/svgo/blob/master/plugins/moveElemsAttrsToGroup.js) | move elements' attributes to their enclosing group |
 | [moveGroupAttrsToElems](https://github.com/svg/svgo/blob/master/plugins/moveGroupAttrsToElems.js) | move some group attributes to the contained elements |
 | [collapseGroups](https://github.com/svg/svgo/blob/master/plugins/collapseGroups.js) | collapse useless groups |
@@ -89,8 +90,8 @@ Options:
   -o OUTPUT, --output=OUTPUT : Output file or folder (by default the same as the input), "-" for STDOUT
   -p PRECISION, --precision=PRECISION : Set number of digits in the fractional part, overrides plugins params
   --config=CONFIG : Config file or JSON string to extend or replace default
-  --disable=DISABLE : Disable plugin by name
-  --enable=ENABLE : Enable plugin by name
+  --disable=PLUGIN : Disable plugin by name, "--disable={PLUGIN1,PLUGIN2}" for multiple plugins
+  --enable=PLUGIN : Enable plugin by name, "--enable={PLUGIN3,PLUGIN4}" for multiple plugins
   --datauri=DATAURI : Output as Data URI string (base64, URI encoded or unencoded)
   --multipass : Enable multipass
   --pretty : Make SVG pretty printed
@@ -176,7 +177,7 @@ Arguments:
 
 ### Other Ways to Use SVGO
 
-* as a web app - [SVGOMG](https://jakearchibald.github.io/svgomg/)
+* as a web app – [SVGOMG](https://jakearchibald.github.io/svgomg/)
 * as a Nodejs module – [examples](https://github.com/svg/svgo/tree/master/examples)
 * as a Grunt task – [grunt-svgmin](https://github.com/sindresorhus/grunt-svgmin)
 * as a Gulp task – [gulp-svgmin](https://github.com/ben-eb/gulp-svgmin)
@@ -184,7 +185,10 @@ Arguments:
 * as an OSX Folder Action – [svgo-osx-folder-action](https://github.com/svg/svgo-osx-folder-action)
 * as a webpack loader – [image-webpack-loader](https://github.com/tcoopman/image-webpack-loader)
 * as a Telegram Bot – [svgo_bot](https://github.com/maksugr/svgo_bot)
-* as a PostCSS plugin - [postcss-svgo](https://github.com/ben-eb/postcss-svgo)
+* as a PostCSS plugin – [postcss-svgo](https://github.com/ben-eb/postcss-svgo)
+* as an Inkscape plugin – [inkscape-svgo](https://github.com/konsumer/inkscape-svgo)
+* as a Sketch plugin - [svgo-compressor](https://github.com/BohemianCoding/svgo-compressor)
+* as macOS app - [Image Shrinker](https://image-shrinker.com)
 
 ## License and Copyright
 
diff --git a/README.ru.md b/README.ru.md
index 26f1499..8b939d0 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -3,24 +3,25 @@
 
 <img src="https://svg.github.io/svgo-logo.svg" width="200" height="200" alt="logo"/>
 
-## SVGO [![NPM version](https://badge.fury.io/js/svgo.svg)](https://npmjs.org/package/svgo) [![Dependency Status](https://gemnasium.com/svg/svgo.svg)](https://gemnasium.com/svg/svgo) [![Build Status](https://secure.travis-ci.org/svg/svgo.svg)](https://travis-ci.org/svg/svgo) [![Coverage Status](https://img.shields.io/coveralls/svg/svgo.svg)](https://coveralls.io/r/svg/svgo?branch=master)
+## SVGO [![NPM version](https://badge.fury.io/js/svgo.svg)](https://npmjs.org/package/svgo) [![Build Status](https://secure.travis-ci.org/svg/svgo.svg)](https://travis-ci.org/svg/svgo) [![Coverage Status](https://img.shields.io/coveralls/svg/svgo.svg)](https://coveralls.io/r/svg/svgo?branch=master)
 
 **SVG** **O**ptimizer – это инструмент для оптимизации векторной графики в формате SVG, написанный на Node.js.
 ![](https://mc.yandex.ru/watch/18431326)
 
 ## Зачем?
 
-SVG-файлы, особенно – экспортированные из различных редакторов, содержат много избыточной и бесполезной информации, комментариев, скрытых элементов, неоптимальные или стандартные значения и другой мусор, удаление которого безопасно и не влияет на конечный результат отрисовки.
+SVG-файлы, особенно экспортированные из редакторов, содержат много избыточной и бесполезной информации, комментариев, скрытых элементов, неоптимальные или стандартные значения и другой мусор, удаление которого безопасно и не влияет на конечный вид изображения.
 
 ## Возможности
 
 SVGO имеет расширяемую архитектуру, в которой почти каждая оптимизация является отдельным расширением.
 
-Сегодня у нас есть:
+Что у нас есть:
 
 | Plugin | Description |
 | ------ | ----------- |
 | [cleanupAttrs](https://github.com/svg/svgo/blob/master/plugins/cleanupAttrs.js) | удаление переносов строк и лишних пробелов |
+| [inlineStyles](https://github.com/svg/svgo/blob/master/plugins/inlineStyles.js) | перенос стилей из элементов `<style>` в атрибуты `style` |
 | [removeDoctype](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js) | удаление doctype |
 | [removeXMLProcInst](https://github.com/svg/svgo/blob/master/plugins/removeXMLProcInst.js) | удаление XML-инструкций |
 | [removeComments](https://github.com/svg/svgo/blob/master/plugins/removeComments.js) | удаление комментариев |
@@ -63,7 +64,7 @@ SVGO имеет расширяемую архитектуру, в которой
 | [removeStyleElement](https://github.com/svg/svgo/blob/master/plugins/removeStyleElement.js) | удаляет элементы `<style>` (выключено по умолчанию) |
 | [removeScriptElement](https://github.com/svg/svgo/blob/master/plugins/removeScriptElement.js) | удаляет элементы `<script>` (выключено по умолчанию) |
 
-Хотите узнать, как это работает и как написать свой плагин? [Конечно же, да!](https://github.com/svg/svgo/blob/master/docs/how-it-works/ru.md).
+Хотите узнать принципы работы и как написать свой плагин? [Конечно же, да!](https://github.com/svg/svgo/blob/master/docs/how-it-works/ru.md)
 
 
 ## Как использовать
@@ -185,6 +186,9 @@ $ [sudo] npm install -g svgo
 * через загрузчик webpack – [image-webpack-loader](https://github.com/tcoopman/image-webpack-loader)
 * с помощью бота в Telegram – [svgo_bot](https://github.com/maksugr/svgo_bot)
 * как плагин PostCSS - [postcss-svgo](https://github.com/ben-eb/postcss-svgo)
+* как плагин для Inkscape – [inkscape-svgo](https://github.com/konsumer/inkscape-svgo)
+* как плагин для Sketch - [svgo-compressor](https://github.com/BohemianCoding/svgo-compressor)
+* в виде приложения macOS - [Image Shrinker](https://image-shrinker.com)
 
 ## Лицензия и копирайты
 
diff --git a/docs/how-it-works/en.md b/docs/how-it-works/en.md
index 47b19d9..3b794b3 100644
--- a/docs/how-it-works/en.md
+++ b/docs/how-it-works/en.md
@@ -22,7 +22,7 @@ SVGO reads, parses and/or extends the [default config](https://github.com/svg/sv
 
 ```yaml
 plugins:
-  - myTestPlugi
+  - myTestPlugin
   - myTestPlugin2: false
   - myTestPlugin3:
       param1: 1
diff --git a/docs/how-it-works/ko.md b/docs/how-it-works/ko.md
index 0033a10..57de896 100644
--- a/docs/how-it-works/ko.md
+++ b/docs/how-it-works/ko.md
@@ -201,11 +201,130 @@ plugins
   * @param {Object} [context] callback context
   * @return {Boolean} false if there are no any attributes
 
+##### querySelectorAll(selectors)
+  * 요소에 대해 CSS selector들의 문자열을 평가하고 일치하는 요소를 리턴한다.
+  * @param {String} selectors CSS selector(s) string
+  * @return {Array} null if no elements matched
+
+##### querySelector(selectors)
+  * 요소에 대해 CSS selector들의 문자열을 평가하고 첫 번째로 일치하는 element만 리턴한다.
+  * @param {String} selectors CSS selector(s) string
+  * @return {Array} null if no element matched
+
+##### matches(selector)
+  * selector가 주어진 요소와 일치하는지 테스트한다.
+  * @param {String} selector CSS selector string
+  * @return {Boolean} true if element would be selected by selector string, false if it does not
+
+
+##### style.getCssText()
+  * 선언 블록의 텍스트 표현을 가져온다. (cssText 특성과 동일)
+  * @return {String} Textual representation of the declaration block (empty string for no properties)
+
+##### style.getPropertyPriority(propertyName)
+  * "중요한"으로 속성값이 부여된 선택적 요소를 리턴한다.
+  * @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.
+
+##### style.getPropertyValue(propertyName)
+  * 프로퍼티 이름으로 주어진 프로퍼티 값을 리턴한다.
+  * @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.
+
+##### style.item(index)
+  * 프로퍼티 이름을 리턴한다.
+  * @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.
+
+##### style.getProperties()
+  * 노드의 모든 프로퍼티를 리턴한다.
+  * @return {Map} properties that is a Map with propertyName as key and property (propertyValue + propertyPriority) as value.
+
+##### style.removeProperty(propertyName)
+  * CSS 선언 블록에서 속성을 제거한다.
+  * @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.
+
+##### style.setProperty(propertyName, value, priority)
+  * 기존 CSS 속성을 수정하거나 선언 블록에 새 CSS 속성을 만든다.
+  * @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}
+
+##### css-tools.flattenToSelectors(cssAst)
+  * CSS AST를 Select List로 만든다.
+  * @param {Object} CSS AST to flatten
+  * @return {Array} selectors
+
+##### css-tools.filterByMqs(selectors, useMqs)
+  * Filter selectors by Media Query.
+  * @param {Array} Selectors to filter
+  * @param {Array} Strings of media queries that should pass (<name> <expression>)
+  * @return {Array} Filtered selectors that match the passed media queries
+
+##### css-tools.filterByPseudos(selectors, useMqs)
+  * Filter selectors by the pseudo-elements and/or -classes they contain.
+  * @param {Array} Selectors to filter
+  * @param {Array} 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
+
+##### css-tools.cleanPseudos(selectors)
+  * 정당히 매칭된 selector로 부터 pseudo element와 class들을 제거한다.
+  * @param {Array} Selectors to clean
+  * @return {Array} Selectors without pseudo-elements and/or -classes
+
+##### css-tools.compareSpecificity(aSpecificity, bSpecificity)
+  * 두 개의 Selector 특성을 비교한다.
+  * @param {Array} Specificity of selector A
+  * @param {Array} Specificity of selector B
+  * @return {Number} Score of selector specificity A compared to selector specificity B
+
+##### css-tools.compareSimpleSelectorNode(aSimpleSelectorNode, bSimpleSelectorNode)
+  * 두 개의 간단한 Selector를 비교한다.
+  * @param {Object} Simple selector A
+  * @param {Object} Simple selector B
+  * @return {Number} Score of selector A compared to selector B
+
+##### css-tools.sortSelectors(selectors)
+  * Selector들을 안정적으로 정렬한다.
+  * @param {Array} Selectors to be sorted
+  * @return {Array} Stable sorted selectors
+
+##### css-tools.csstreeToStyleDeclaration(declaration)
+  * AST style의 css-tree 선언을 CSSStyleDeclaration property로 바꾼다.
+  * @param {Object} css-tree style declaration
+  * @return {Object} CSSStyleDeclaration property
+
+##### css-tools.getCssStr(elem)
+  * style element로 부터 CSS string을 가져온다.
+  * @param {Object} element style element
+  * @return {String|Array} CSS string or empty array if no styles are set
+
+##### css-tools.csstreeToStyleDeclaration(elem, css)
+  * @param {Object} element style element
+  * @param {String} CSS string to be set
+  * @return {Object} reference to field with CSS
+
+
 #### 3.3 tests
 
 당신의 플러그인을 테스팅 하는 것보다 쉬운 일은 없다.
 
 1. `test/plugins` 안에 `myPlugin.01.orig.svg` 와 `myPlugin.01.should.svg` 를 넣는다
+
+```
+[original svg]
+
+@@@
+
+[how svg should look afterwards]
+
+@@@
+
+attributes if your plugin needs them
+```
+
 2. `npm test` 를 실행한다
 3. PROFIT!
 
diff --git a/lib/svgo/config.js b/lib/svgo/config.js
index 786c37a..2f4abdc 100644
--- a/lib/svgo/config.js
+++ b/lib/svgo/config.js
@@ -27,7 +27,7 @@ module.exports = function(config) {
         }
     } else {
         defaults = Object.assign({}, yaml.safeLoad(FS.readFileSync(__dirname + '/../../.svgo.yml', 'utf8')));
-        defaults.plugins = preparePluginsArray(defaults.plugins);
+        defaults.plugins = preparePluginsArray(defaults.plugins || []);
         defaults = extendConfig(defaults, config);
     }
 
diff --git a/lib/svgo/css-class-list.js b/lib/svgo/css-class-list.js
index 28e5816..8401a9f 100644
--- a/lib/svgo/css-class-list.js
+++ b/lib/svgo/css-class-list.js
@@ -13,6 +13,28 @@ var CSSClassList = function(node) {
     //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
diff --git a/lib/svgo/css-style-declaration.js b/lib/svgo/css-style-declaration.js
index 5b2300b..4bb987c 100644
--- a/lib/svgo/css-style-declaration.js
+++ b/lib/svgo/css-style-declaration.js
@@ -16,6 +16,28 @@ var CSSStyleDeclaration = function(node) {
     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();
diff --git a/lib/svgo/jsAPI.js b/lib/svgo/jsAPI.js
index b4d4d1b..a67cc06 100644
--- a/lib/svgo/jsAPI.js
+++ b/lib/svgo/jsAPI.js
@@ -28,12 +28,12 @@ JSAPI.prototype.clone = function() {
     var nodeData = {};
 
     Object.keys(node).forEach(function(key) {
-        if (key !== 'content') {
+        if (key !== 'class' && key !== 'style' && key !== 'content') {
             nodeData[key] = node[key];
         }
     });
 
-    // Deep-clone node data
+    // Deep-clone node data.
     nodeData = JSON.parse(JSON.stringify(nodeData));
 
     // parentNode gets set to a proper object by the parent clone,
@@ -41,6 +41,12 @@ JSAPI.prototype.clone = function() {
     // 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();
diff --git a/lib/svgo/tools.js b/lib/svgo/tools.js
index 7dd33c0..fb52c9d 100644
--- a/lib/svgo/tools.js
+++ b/lib/svgo/tools.js
@@ -15,9 +15,12 @@ exports.encodeSVGDatauri = function(str, type) {
     if (!type || type === 'base64') {
 
         prefix += ';base64,';
-
-        str = prefix + new Buffer(str).toString('base64');
-
+        if (Buffer.from) {
+            str = prefix + Buffer.from(str).toString('base64');
+        } else {
+            str = prefix + new Buffer(str).toString('base64');
+        }
+        
     // URI encoded
     } else if (type === 'enc') {
 
diff --git a/package.json b/package.json
index 8739fe6..8b689f6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "svgo",
-  "version": "1.0.3",
+  "version": "1.0.5",
   "description": "Nodejs-based tool for optimizing SVG vector graphics files",
   "keywords": [
     "svgo",
@@ -49,24 +49,24 @@
     "jshint": "npm run lint"
   },
   "dependencies": {
-    "coa": "~2.0.0",
+    "coa": "~2.0.1",
     "colors": "~1.1.2",
-    "css-url-regex": "^1.1.0",
-    "unquote": "^1.1.0",
-    "mkdirp": "~0.5.1",
     "css-select": "~1.3.0-rc0",
     "css-select-base-adapter": "~0.1.0",
     "css-tree": "1.0.0-alpha25",
-    "csso": "^3.3.1",
+    "css-url-regex": "^1.1.0",
+    "csso": "^3.5.0",
     "js-yaml": "~3.10.0",
+    "mkdirp": "~0.5.1",
     "object.values": "^1.0.4",
     "sax": "~1.2.4",
     "stable": "~0.1.6",
+    "unquote": "~1.1.1",
     "util.promisify": "~1.0.0"
   },
   "devDependencies": {
     "coveralls": "~3.0.0",
-    "fs-extra": "~4.0.2",
+    "fs-extra": "~4.0.3",
     "istanbul": "~0.4.5",
     "mocha": "~4.0.1",
     "mocha-istanbul": "~0.3.0",
diff --git a/plugins/_collections.js b/plugins/_collections.js
index 3348dcb..fab9287 100644
--- a/plugins/_collections.js
+++ b/plugins/_collections.js
@@ -2291,7 +2291,8 @@ exports.editorNamespaces = [
     '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://ns.adobe.com/XPath/1.0/',
+    'http://schemas.microsoft.com/visio/2003/SVGExtensions/'
 ];
 
 // http://www.w3.org/TR/SVG/linking.html#processingIRI
diff --git a/plugins/cleanupNumericValues.js b/plugins/cleanupNumericValues.js
index e91fdf2..f7c8a1e 100644
--- a/plugins/cleanupNumericValues.js
+++ b/plugins/cleanupNumericValues.js
@@ -48,6 +48,9 @@ exports.fn = function(item, params) {
         }
 
         item.eachAttr(function(attr) {
+            // The `version` attribute is a text string and cannot be rounded
+            if (attr.name === 'version') { return }
+            
             var match = attr.value.match(regNumericValues);
 
             // if attribute value matches regNumericValues
diff --git a/plugins/collapseGroups.js b/plugins/collapseGroups.js
index f0a1343..6e38651 100644
--- a/plugins/collapseGroups.js
+++ b/plugins/collapseGroups.js
@@ -63,6 +63,8 @@ exports.fn = function(item) {
                                 inner.addAttr(attr);
                             } else if (attr.name == 'transform') {
                                 inner.attr(attr.name).value = attr.value + ' ' + inner.attr(attr.name).value;
+                            } else if (inner.hasAttr(attr.name, 'inherit')) {
+                                inner.attr(attr.name).value = attr.value;
                             } else if (
                                 attrsInheritable.indexOf(attr.name) < 0 &&
                                 !inner.hasAttr(attr.name, attr.value)
diff --git a/plugins/prefixIds.js b/plugins/prefixIds.js
index ddda078..7d2b8c8 100644
--- a/plugins/prefixIds.js
+++ b/plugins/prefixIds.js
@@ -13,7 +13,7 @@ exports.description = 'prefix IDs';
 
 var path = require('path'),
     csstree = require('css-tree'),
-    cssRx = require('css-url-regex')(),
+    cssRx = require('css-url-regex'),
     unquote = require('unquote'),
     collections = require('./_collections.js'),
     referencesProps = collections.referencesProps,
@@ -37,7 +37,7 @@ var matchId = function(urlVal) {
 
 // Matches an url(...) value, captures the URL
 var matchUrl = function(val) {
-    var urlMatches = cssRx.exec(val);
+    var urlMatches = cssRx().exec(val);
     if (urlMatches === null) {
         return false;
     }
@@ -61,13 +61,13 @@ var prefixId = function(val) {
 
 // attr.value helper methods
 
-// prefixes a normal attribute value
-var addPrefixToAttr = function(attr) {
+// prefixes a class attribute value
+var addPrefixToClassAttr = function(attr) {
     if (!attrNotEmpty(attr)) {
         return;
     }
 
-    attr.value = addPrefix(attr.value);
+    attr.value = attr.value.split(/\s+/).map(addPrefix).join(' ');
 };
 
 // prefixes an ID attribute value
@@ -76,6 +76,15 @@ var addPrefixToIdAttr = function(attr) {
         return;
     }
 
+    attr.value = addPrefix(attr.value);
+};
+
+// prefixes a href attribute value
+var addPrefixToHrefAttr = function(attr) {
+    if (!attrNotEmpty(attr)) {
+        return;
+    }
+
     var idPrefixed = prefixId(attr.value);
     if (!idPrefixed) {
         return;
@@ -123,6 +132,8 @@ exports.fn = function(node, opts, extra) {
         } else {
             prefix = opts.prefix;
         }
+    } else if (opts.prefix === false) {
+        prefix = false;
     } else if (extra && extra.path && extra.path.length > 0) {
         var filename = path.basename(extra.path);
         prefix = filename;
@@ -131,6 +142,9 @@ exports.fn = function(node, opts, extra) {
 
     // prefixes a normal value
     addPrefix = function(name) {
+        if(prefix === false){
+            return escapeIdentifierName(name);
+        }
         return escapeIdentifierName(prefix + opts.delim + name);
     };
 
@@ -192,14 +206,16 @@ exports.fn = function(node, opts, extra) {
     }
 
     // ID
-    addPrefixToAttr(node.attrs.id);
+    addPrefixToIdAttr(node.attrs.id);
+
     // Class
-    addPrefixToAttr(node.attrs.class);
+    addPrefixToClassAttr(node.attrs.class);
 
     // href
-    addPrefixToIdAttr(node.attrs.href);
+    addPrefixToHrefAttr(node.attrs.href);
+
     // (xlink:)href (deprecated, must be still supported)
-    addPrefixToIdAttr(node.attrs['xlink:href']);
+    addPrefixToHrefAttr(node.attrs['xlink:href']);
 
     // referenceable properties
     for (var referencesProp of referencesProps) {
diff --git a/plugins/removeHiddenElems.js b/plugins/removeHiddenElems.js
index 231a2bd..7379125 100644
--- a/plugins/removeHiddenElems.js
+++ b/plugins/removeHiddenElems.js
@@ -7,6 +7,7 @@ exports.active = true;
 exports.description = 'removes hidden elements (zero sized, with absent attributes)';
 
 exports.params = {
+    isHidden: true,
     displayNone: true,
     opacity0: true,
     circleR0: true,
@@ -44,9 +45,15 @@ var regValidPath = /M\s*(?:[-+]?(?:\d*\.\d+|\d+(?:\.|(?!\.)))([eE][-+]?\d+)?(?!\
  *
  * @author Kir Belevich
  */
-exports.fn = function(item, params) {
+exports.fn = function (item, params) {
 
     if (item.elem) {
+        // Removes hidden elements
+        // https://www.w3schools.com/cssref/pr_class_visibility.asp
+        if (
+            params.isHidden &&
+            item.hasAttr('visibility', 'hidden')
+        ) return false;
 
         // display="none"
         //
diff --git a/plugins/sortAttrs.js b/plugins/sortAttrs.js
index 221a4bf..cba5c1d 100644
--- a/plugins/sortAttrs.js
+++ b/plugins/sortAttrs.js
@@ -30,7 +30,8 @@ exports.fn = function(item, params) {
 
 	var attrs = [],
 		sorted = {},
-		orderlen = params.order.length + 1;
+		orderlen = params.order.length + 1,
+		xmlnsOrder = params.xmlnsOrder || 'front';
 
 	if (item.elem) {
 
@@ -41,10 +42,12 @@ exports.fn = function(item, params) {
 		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;
+				if (xmlnsOrder == 'front') {
+                    if (a.prefix == 'xmlns')
+                        return -1;
+                    if (b.prefix == 'xmlns')
+                        return 1;
+                }
 				return a.prefix < b.prefix ? -1 : 1;
 			}
 


Test patch:
diff --git a/examples/test.js b/examples/test.js
index fda337b..e4f4fa4 100644
--- a/examples/test.js
+++ b/examples/test.js
@@ -4,7 +4,77 @@ var FS = require('fs'),
     PATH = require('path'),
     SVGO = require('../lib/svgo'),
     filepath = PATH.resolve(__dirname, 'test.svg'),
-    svgo = new SVGO(/*{ custom config object }*/);
+    svgo = new SVGO({
+        plugins: [{
+          cleanupAttrs: true,
+        }, {
+          removeDoctype: true,
+        },{
+          removeXMLProcInst: true,
+        },{
+          removeComments: true,
+        },{
+          removeMetadata: true,
+        },{
+          removeTitle: true,
+        },{
+          removeDesc: true,
+        },{
+          removeUselessDefs: true,
+        },{
+          removeEditorsNSData: true,
+        },{
+          removeEmptyAttrs: true,
+        },{
+          removeHiddenElems: true,
+        },{
+          removeEmptyText: true,
+        },{
+          removeEmptyContainers: true,
+        },{
+          removeViewBox: false,
+        },{
+          cleanupEnableBackground: true,
+        },{
+          convertStyleToAttrs: true,
+        },{
+          convertColors: true,
+        },{
+          convertPathData: true,
+        },{
+          convertTransform: true,
+        },{
+          removeUnknownsAndDefaults: true,
+        },{
+          removeNonInheritableGroupAttrs: true,
+        },{
+          removeUselessStrokeAndFill: true,
+        },{
+          removeUnusedNS: true,
+        },{
+          cleanupIDs: true,
+        },{
+          cleanupNumericValues: true,
+        },{
+          moveElemsAttrsToGroup: true,
+        },{
+          moveGroupAttrsToElems: true,
+        },{
+          collapseGroups: true,
+        },{
+          removeRasterImages: false,
+        },{
+          mergePaths: true,
+        },{
+          convertShapeToPath: true,
+        },{
+          sortAttrs: true,
+        },{
+          removeDimensions: true,
+        },{
+          removeAttrs: {attrs: '(stroke|fill)'},
+        }]
+      });
 
 FS.readFile(filepath, 'utf8', function(err, data) {
 
diff --git a/test/plugins/collapseGroups.15.svg b/test/plugins/collapseGroups.15.svg
new file mode 100644
index 0000000..411020c
--- /dev/null
+++ b/test/plugins/collapseGroups.15.svg
@@ -0,0 +1,17 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+	<g color="red">
+		<g color="inherit" fill="none" stroke="none">
+			<circle cx="130" cy="80" r="60" fill="currentColor"/>
+			<circle cx="350" cy="80" r="60" stroke="currentColor" stroke-width="4"/>
+		</g>
+	</g>
+</svg>
+
+@@@
+
+<svg xmlns="http://www.w3.org/2000/svg">
+    <g color="red" fill="none" stroke="none">
+        <circle cx="130" cy="80" r="60" fill="currentColor"/>
+        <circle cx="350" cy="80" r="60" stroke="currentColor" stroke-width="4"/>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/test/plugins/prefixIds.01.svg b/test/plugins/prefixIds.01.svg
index ea23b2f..e99c2ea 100644
--- a/test/plugins/prefixIds.01.svg
+++ b/test/plugins/prefixIds.01.svg
@@ -1,6 +1,6 @@
 <svg width="120" height="120" xmlns="http://www.w3.org/2000/svg">
     <style>
-        .test2 {
+        .test {
             color: blue;
         }
         #test {
@@ -16,7 +16,7 @@
 
 <svg width="120" height="120" xmlns="http://www.w3.org/2000/svg">
     <style>
-        .prefixIds_01_svg__test2{color:blue}#prefixIds_01_svg__test{color:red}
+        .prefixIds_01_svg__test{color:blue}#prefixIds_01_svg__test{color:red}
     </style>
     <rect class="prefixIds_01_svg__test" x="10" y="10" width="100" height="100"/>
     <rect class="" id="prefixIds_01_svg__test" x="10" y="10" width="100" height="100"/>
diff --git a/test/plugins/prefixIds.05.svg b/test/plugins/prefixIds.05.svg
new file mode 100644
index 0000000..d7805dd
--- /dev/null
+++ b/test/plugins/prefixIds.05.svg
@@ -0,0 +1,41 @@
+<svg width="340" height="120" xmlns="http://www.w3.org/2000/svg">
+    <defs>
+        <linearGradient id="gradient_1">
+            <stop offset="5%" stop-color="green"/>
+            <stop offset="95%" stop-color="gold"/>
+        </linearGradient>
+        <linearGradient id="gradient_2">
+            <stop offset="5%" stop-color="red"/>
+            <stop offset="95%" stop-color="black"/>
+        </linearGradient>
+        <linearGradient id="gradient_3">
+            <stop offset="5%" stop-color="blue"/>
+            <stop offset="95%" stop-color="orange"/>
+        </linearGradient>
+    </defs>
+    <rect fill="url(#gradient_1)" x="10" y="10" width="100" height="100"/>
+    <rect fill="url(#gradient_2)" x="120" y="10" width="100" height="100"/>
+    <rect fill="url(#gradient_3)" x="230" y="10" width="100" height="100"/>
+</svg>
+
+@@@
+
+<svg width="340" height="120" xmlns="http://www.w3.org/2000/svg">
+    <defs>
+        <linearGradient id="prefixIds_05_svg__gradient_1">
+            <stop offset="5%" stop-color="green"/>
+            <stop offset="95%" stop-color="gold"/>
+        </linearGradient>
+        <linearGradient id="prefixIds_05_svg__gradient_2">
+            <stop offset="5%" stop-color="red"/>
+            <stop offset="95%" stop-color="black"/>
+        </linearGradient>
+        <linearGradient id="prefixIds_05_svg__gradient_3">
+            <stop offset="5%" stop-color="blue"/>
+            <stop offset="95%" stop-color="orange"/>
+        </linearGradient>
+    </defs>
+    <rect fill="url(#prefixIds_05_svg__gradient_1)" x="10" y="10" width="100" height="100"/>
+    <rect fill="url(#prefixIds_05_svg__gradient_2)" x="120" y="10" width="100" height="100"/>
+    <rect fill="url(#prefixIds_05_svg__gradient_3)" x="230" y="10" width="100" height="100"/>
+</svg>
diff --git a/test/plugins/prefixIds.06.svg b/test/plugins/prefixIds.06.svg
new file mode 100644
index 0000000..9bd4f76
--- /dev/null
+++ b/test/plugins/prefixIds.06.svg
@@ -0,0 +1,38 @@
+<svg width="120" height="120" xmlns="http://www.w3.org/2000/svg">
+    <style>
+        .test {
+            color: blue;
+        }
+        .test2 {
+            color: green;
+        }
+        #test {
+            color: red;
+        }
+        .test3 .test4 {
+            color: black;
+        }
+        .test5.test6 {
+            color: brown;
+        }
+        .test5.test6 #test7 {
+            color: yellow;
+        }
+    </style>
+    <rect class="test" x="10" y="10" width="100" height="100"/>
+    <rect class="test test2" x="10" y="10" width="100" height="100"/>
+    <rect class="test  test2" x="10" y="10" width="100" height="100"/>
+    <rect class="" id="test" x="10" y="10" width="100" height="100"/>
+</svg>
+
+@@@
+
+<svg width="120" height="120" xmlns="http://www.w3.org/2000/svg">
+    <style>
+        .prefixIds_06_svg__test{color:blue}.prefixIds_06_svg__test2{color:green}#prefixIds_06_svg__test{color:red}.prefixIds_06_svg__test3 .prefixIds_06_svg__test4{color:black}.prefixIds_06_svg__test5.prefixIds_06_svg__test6{color:brown}.prefixIds_06_svg__test5.prefixIds_06_svg__test6 #prefixIds_06_svg__test7{color:yellow}
+    </style>
+    <rect class="prefixIds_06_svg__test" x="10" y="10" width="100" height="100"/>
+    <rect class="prefixIds_06_svg__test prefixIds_06_svg__test2" x="10" y="10" width="100" height="100"/>
+    <rect class="prefixIds_06_svg__test prefixIds_06_svg__test2" x="10" y="10" width="100" height="100"/>
+    <rect class="" id="prefixIds_06_svg__test" x="10" y="10" width="100" height="100"/>
+</svg>
diff --git a/test/plugins/sortAttrs.02.svg b/test/plugins/sortAttrs.02.svg
new file mode 100644
index 0000000..3237551
--- /dev/null
+++ b/test/plugins/sortAttrs.02.svg
@@ -0,0 +1,15 @@
+<svg foo="bar" xmlns="http://www.w3.org/2000/svg" height="10" baz="quux" width="10" hello="world">
+    <rect x="0" y="0" width="100" height="100" stroke-width="1" stroke-linejoin="round" fill="red" stroke="orange" xmlns="http://www.w3.org/2000/svg"/>
+    test
+</svg>
+
+@@@
+
+<svg width="10" height="10" baz="quux" foo="bar" hello="world" xmlns="http://www.w3.org/2000/svg">
+    <rect width="100" height="100" x="0" y="0" fill="red" stroke="orange" stroke-linejoin="round" stroke-width="1" xmlns="http://www.w3.org/2000/svg"/>
+    test
+</svg>
+
+@@@
+
+{ "xmlnsOrder": "alphabetical" }
diff --git a/test/svg2js/_index.js b/test/svg2js/_index.js
index 4a791bc..7528d34 100644
--- a/test/svg2js/_index.js
+++ b/test/svg2js/_index.js
@@ -3,6 +3,15 @@
 var SHOULD = require('should'),
     FS = require('fs'),
     PATH = require('path'),
+    JSAPI = require(process.env.COVERAGE ?
+                     '../../lib-cov/svgo/jsAPI' :
+                     '../../lib/svgo/jsAPI'),
+    CSSClassList = require(process.env.COVERAGE ?
+                     '../../lib-cov/svgo/css-class-list' :
+                     '../../lib/svgo/css-class-list'),
+    CSSStyleDeclaration = require(process.env.COVERAGE ?
+                     '../../lib-cov/svgo/css-style-declaration' :
+                     '../../lib/svgo/css-style-declaration'),
     SVG2JS = require(process.env.COVERAGE ?
                      '../../lib-cov/svgo/svg2js' :
                      '../../lib/svgo/svg2js');
@@ -179,6 +188,26 @@ describe('svg2js', function() {
 
         describe('API', function() {
 
+            describe('clone()', function() {
+
+                it('svg should have property "clone"', function() {
+                    return root.content[3].should.have.property('clone');
+                });
+
+                it('svg.clone() should be an instance of JSAPI', function() {
+                    return root.content[3].clone().should.be.instanceOf(JSAPI);
+                });
+
+                it('root.content[3].content[0].clone() has a valid style property', function() {
+                    return root.content[3].content[0].clone().style.should.be.instanceof(CSSStyleDeclaration);
+                });
+
+                it('root.content[3].content[2].clone() has a valid class property', function() {
+                    return root.content[3].content[2].clone().class.should.be.instanceof(CSSClassList);
+                });
+
+            });
+
             describe('isElem()', function() {
 
                 it('svg should have property "isElem"', function() {
diff --git a/test/svg2js/test.svg b/test/svg2js/test.svg
index 4069162..f8e741d 100644
--- a/test/svg2js/test.svg
+++ b/test/svg2js/test.svg
@@ -15,5 +15,5 @@
             <text>test</text>
         </g>
     </g>
-    <g style="color: black"></g>
+    <g style="color: black" class="unknown-class"></g>
 </svg>