Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'dev'

  • Loading branch information...
commit af60c03c46d98f36de5d3096d42d19c140db6e41 2 parents c4616ca + db48d1e
@deepsweet deepsweet authored
View
8 CHANGELOG.md
@@ -1,3 +1,11 @@
+### [ [>](https://github.com/svg/svgo/tree/v0.1.4) ] 0.1.4 / 05.12.2012
+* plugins/_collections: more defaults
+* `README.ru.md`
+* `docs/how-it-works/ru.md`
+* mocha + mocha-as-promised + chai + chai-as-promised + should + istanbul = <3
+* update dependencies semvers in `package.json`
+* `v0.1.x` and `v0.2.x` milestones
+
### [ [>](https://github.com/svg/svgo/tree/v0.1.3) ] 0.1.3 / 30.11.2012
* new plugin [plugins/cleanupNumericValues](https://github.com/svg/svgo/blob/master/plugins/cleanupNumericValues.js) (close [#8](https://github.com/svg/svgo/issues/8))
* plugins/removeDefaultPx functionality now included in plugins/removeUnknownsAndDefaults
View
19 README.md
@@ -1,8 +1,11 @@
+**english** | [русский](https://github.com/svg/svgo/blob/master/README.ru.md)
+- - -
+
<img src="http://soulshine.in/svgo/logo.svg?v3" width="200" height="200" alt="logo"/>
-## SVGO v0.1.3 [![Build Status](https://secure.travis-ci.org/svg/svgo.png)](http://travis-ci.org/svg/svgo)
+## SVGO v0.1.4 [![Build Status](https://secure.travis-ci.org/svg/svgo.png)](http://travis-ci.org/svg/svgo)
-**SVG** **O**ptimizer is a Nodejs-based tool for optimizing SVG vector graphics files.
+**SVG** **O**ptimizer – это инструмент для оптимизации векторной графики в формате SVG, написанный на Node.js.
![](//mc.yandex.ru/watch/18431326)
## Why?
@@ -19,16 +22,16 @@ Today we have:
* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js) ] remove doctype declaration
* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeXMLProcInst.js) ] remove XML processing instructions
* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeComments.js) ] remove comments
-* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeMetadata.js) ] remove metadata
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeMetadata.js) ] remove `<metadata>`
* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeEditorsNSData.js) ] remove editors namespaces, elements and attributes
* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeEmptyAttrs.js) ] remove empty attributes
* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeHiddenElems.js) ] remove hidden elements
* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeEmptyText.js) ] remove empty Text elements
* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeEmptyContainers.js) ] remove empty Container elements
-* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeViewBox.js) ] remove viewBox attribute when possible
-* [ [>](https://github.com/svg/svgo/blob/master/plugins/cleanupEnableBackground.js) ] remove or cleanup enable-background attribute when possible
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeViewBox.js) ] remove `viewBox` attribute when possible
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/cleanupEnableBackground.js) ] remove or cleanup `enable-background` attribute when possible
* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertStyleToAttrs.js) ] convert styles into attributes
-* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertColors.js) ] convert colors (from rgb() to #rrggbb, from #rrggbb to #rgb)
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertColors.js) ] convert colors (from `rgb()` to `#rrggbb`, from `#rrggbb` to `#rgb`)
* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertPathData.js) ] convert Path data to relative, convert one segment to another, trim useless delimiters and much more
* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertTransform.js) ] collapse multiple transforms into one, convert matrices to the short aliases and much more
* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js) ] remove unknown elements content and attributes, remove attrs with default values
@@ -97,8 +100,8 @@ Arguments:
## TODO
-* [v0.1.4](https://github.com/svg/svgo/issues?milestone=6&state=open)
-* [v0.1.5](https://github.com/svg/svgo/issues?milestone=7&state=open)
+* [v0.1.x](https://github.com/svg/svgo/issues?milestone=6&state=open)
+* [v0.2.x](https://github.com/svg/svgo/issues?milestone=7&state=open)
## License and copyrights
View
111 README.ru.md
@@ -0,0 +1,111 @@
+[english](https://github.com/svg/svgo/blob/master/README.md) | **русский**
+- - -
+
+<img src="http://soulshine.in/svgo/logo.svg?v3" width="200" height="200" alt="logo"/>
+
+## SVGO v0.1.4 [![Build Status](https://secure.travis-ci.org/svg/svgo.png)](http://travis-ci.org/svg/svgo)
+
+**SVG** **O**ptimizer – это инструмент для оптимизации векторной графики в формате SVG, написанный на Node.js.
+![](//mc.yandex.ru/watch/18431326)
+
+## Зачем?
+
+SVG файлы, особенно экспортированные из различных редакторов, содержат много избыточной и бесполезной информации, комментариев, скрытых элементов, неоптимальные или дефолтные значения и другой мусор, удаление которого безопасно и не влияет на конечный результат рендеринга.
+
+## Возможности
+
+SVGO имеет плагинную архитектуру, в которой практически каждая оптимизация является отдельным плагином.
+
+Сегодня у нас есть:
+
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/cleanupAttrs.js) ] удаление переносов строк и лишних пробелов
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeDoctype.js) ] удаление doctype
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeXMLProcInst.js) ] удаление XML-инструкций
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeComments.js) ] удаление комментариев
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeMetadata.js) ] удаление `<metadata>`
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeEditorsNSData.js) ] удаление пространств имён различных редакторов, их элементов и атрибутов
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeEmptyAttrs.js) ] удаление пустых атрибутов
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeHiddenElems.js) ] удаление скрытых элементов
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeEmptyText.js) ] удаление пустых текстовых элементов
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeEmptyContainers.js) ] удаление пустых элементов-контейнеров
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeViewBox.js) ] удаление атрибута `viewBox` когда это возможно
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/cleanupEnableBackground.js) ] удаление или оптимизация атрибута `enable-background` когда это возможно
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertStyleToAttrs.js) ] конвертирование стилей в атрибуте `style` в отдельные svg-атрибуты
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertColors.js) ] конвертирование цветовых значений (из `rgb()` в `#rrggbb`, из `#rrggbb` в `#rgb`)
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertPathData.js) ] конвертирование данных в Path в относительные координаты, конвертирование одних типов сегментов в другие, удаление ненужных разделителей и многое другое
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertTransform.js) ] схлопывание нескольких трансформаций в одну, конвертирование матриц в короткие алиасы и многое другое
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js) ] удаление неизвестных элементов, контента и атрибутов
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeUnusedNS.js) ] удаление деклараций неиспользуемых пространств имён
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/cleanupNumericValues.js) ] округление дробных чисел до заданной точности, удаление `px` как единицы измерения по умолчанию
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/moveElemsAttrsToGroup.js) ] перемещение совпадающих атрибутов у всех элементов внутри группы `<g>`
+* [ [>](https://github.com/svg/svgo/blob/master/plugins/collapseGroups.js) ] схлопывание бесполезных групп `<g>`
+
+Хотите узнать, как это работает и как написать свой плагин? [Конечно же да](https://github.com/svg/svgo/blob/master/docs/how-it-works/ru.md).
+
+
+## Как использовать
+
+```sh
+$ [sudo] npm install -g svgo
+```
+
+```
+Usage:
+ svgo [OPTIONS] [ARGS]
+
+Options:
+ -h, --help : Help
+ -v, --version : Version
+ -i INPUT, --input=INPUT : Input file, "-" for STDIN
+ -s STRING, --string=STRING : Input SVG data string
+ -f FOLDER, --folder=FOLDER : Input folder, optimize and rewrite all *.svg files
+ -o OUTPUT, --output=OUTPUT : Output file (by default the same as the input), "-" for STDOUT
+ -c CONFIG, --config=CONFIG : Local config file to extend default
+ --disable=DISABLE : Disable plugin by name
+ --enable=ENABLE : Enable plugin by name
+ --datauri : Output as Data URI base64 string
+ --pretty : Make SVG pretty printed
+
+Arguments:
+ INPUT : Alias to --input
+ OUTPUT : Alias to --output
+```
+
+* с файлами:
+
+ $ svgo test.svg
+
+ или:
+
+ $ svgo test.svg test.min.svg
+
+* с STDIN / STDOUT:
+
+ $ cat test.svg | svgo -i - -o - > test.min.svg
+
+* с папками
+
+ $ svgo -f ../path/to/folder/with/svg/files
+
+* со строками:
+
+ $ svgo -s '<svg version="1.1">test</svg>' -o test.min.svg
+
+ или даже с Data URI base64:
+
+ $ svgo -s 'data:image/svg+xml;base64,…' -o test.min.svg
+
+* с помощью [GUI](https://github.com/svg/svgo-gui)
+* как модуль Node.js – [examples](https://github.com/svg/svgo/tree/master/examples)
+
+## TODO
+
+* [v0.1.x](https://github.com/svg/svgo/issues?milestone=6&state=open)
+* [v0.2.x](https://github.com/svg/svgo/issues?milestone=7&state=open)
+
+
+## Лицензия и копирайты
+
+Данное программное обеспечение выпускается под [лицензией MIT](https://github.com/svg/svgo/blob/master/LICENSE).
+
+Логотип – [Егор Большаков](http://xizzzy.ru/).
View
16 docs/how-it-works/en.md
@@ -51,7 +51,7 @@ It's important to note that every plugin:
- - -
### 2. svg2js
-SVGO converts SVG-as-XML data into SVG-as-JS representation. Something like this:
+SVGO converts SVG-as-XML data into SVG-as-JS AST representation. Something like this:
```xml
<?xml version="1.0" standalone="no"?>
@@ -118,15 +118,15 @@ It's important to note that:
- - -
### 3. plugins
-SVGO applies all plugins from the config to SVG-as-JS data. See a lot of examples in the [plugins directory](https://github.com/svg/svgo/tree/master/plugins) above.
+SVGO applies all plugins from the config to AST data. See a lot of examples in the [plugins directory](https://github.com/svg/svgo/tree/master/plugins) above.
#### 3.1 types
-In the simplest case plugins process can be represented as "each plugin runs over all SVG-as-JS data items and perform some actions". But 90% of typical optimizations requires some actions only on one (current) item from the data, so there is no sense to copypaste a recursive per-item loop every time on every plugin. And that's why we have a three types of plugins:
+In the simplest case plugins applying process can be represented as "each plugin runs over all AST data items and perform some actions". But 90% of typical optimizations requires some actions only on one (current) item from the data, so there is no sense to copypaste a recursive per-item loop every time on every plugin. And that's why we have a three types of plugins:
* `perItem` - plugin works only with one current item, inside a "from the outside into the depths" recursive loop (default)
* `perItemReverse` - plugin works only with one current item, inside a "from the depths to the outside" recursive loop (useful when you need to collapse elements one after other)
-* `full` - plugin works with the full SVG-as-JS data and must returns the same
+* `full` - plugin works with the full AST and must returns the same
`perItem` and `perItemReverse` plugins runs inside the recursive `Array.prototype.filter` loop, so if a plugin wants to remove current item then it should just `return false`.
@@ -182,9 +182,10 @@ And of course, writing plugins would not have been so cool without some sugar AP
* @param {Object} attr attribute object
* @return {Object} created attribute
-##### eachAttr(callback)
+##### eachAttr(callback, [context])
* Iterates over all attributes.
* @param {Function} callback
+ * @param {Object} [context] callback context
* @return {Boolean} false if there are no any attributes
#### 3.3 tests
@@ -200,9 +201,8 @@ You can see a lot of examples in the [test/plugins directory](https://github.com
- - -
### 4. js2svg
-SVGO converts SVG-as-JS representation into SVG-as-XML data string.
+SVGO converts AST back into SVG-as-XML data string.
## What's next
1. Write your own plugin :) or
-2. Give me an idea of new plugin or API method
-3. Pull request my crooked english
+2. Give me an idea of new optimization or API method
View
208 docs/how-it-works/ru.md
@@ -0,0 +1,208 @@
+## TOC
+* [Введение](#%D0%92%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5)
+* [Как это работает](#%D0%9A%D0%B0%D0%BA-%D1%8D%D1%82%D0%BE-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0%D0%B5%D1%82)
+ * [конфиг](#1-%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B3)
+ * [svg2js](#2-svg2js)
+ * [плагины](#3-%D0%BF%D0%BB%D0%B0%D0%B3%D0%B8%D0%BD%D1%8B)
+ * [типы](#31-%D1%82%D0%B8%D0%BF%D1%8B)
+ * [API](#32-api)
+ * [тесты](#33-%D1%82%D0%B5%D1%81%D1%82%D1%8B)
+ * [js2svg](#4-js2svg)
+* [Что дальше](#%D0%A7%D1%82%D0%BE-%D0%B4%D0%B0%D0%BB%D1%8C%D1%88%D0%B5)
+
+
+## Введение
+Итак, как уже было [сказано ранеее](https://github.com/svg/svgo#what-it-can-do), SVGO SVGO имеет плагинную архитектуру, в которой практически каждая оптимизация является отдельным плагином.
+
+Плагины могут удалять и изменять SVG элементы, схлопывать контент, перемещать атрибуты и выполнять любые другие действия, которые вы захотите.
+
+## Как это работает
+### 1. конфиг
+SVGO читает, парсит и/или расширяет [конфиг по умолчанию](https://github.com/svg/svgo/blob/master/.svgo.yml), который содержит все настройки, включая плагины. Что-то вроде этого:
+
+```yaml
+plugins:
+
+ - name: myTestPlugin
+ active: true
+ type: perItem
+ params:
+ testParam: true
+ testParam2: 3
+
+ - name: myTestPlugin2
+ active: false
+ type: perItemReverse
+
+ - name: myTestPlugin3
+ active: true
+ type: full
+
+
+```
+
+Важно отметить, что каждый плагин:
+
+ * находится в определённой позиции в массиве плагинов
+ * может быть включён через `"active": true` и выключен через `"active": false`
+ * может иметь свои собственные параметры `params`, которые будут доступны позже в коде плагина
+ * должен быть одного из трёх типов: `perItem`, `perItemReverse` и `full` (мы вернёмся к этому позже)
+
+- - -
+
+### 2. svg2js
+SVGO конвертирует SVG-как-XML данные в SVG-как-JS AST-представление. Что-то вроде этого:
+
+```xml
+<?xml version="1.0" standalone="no"?>
+<!-- Generator: Adobe Illustrator 16.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+ <text>test</text>
+ <script><![CDATA[ alert('hello'); ]]></script>
+</svg>
+```
+
+```js
+{
+ content: [
+ {
+ processinginstruction: { name: 'xml', body: 'version="1.0" standalone="no"' }
+ },{
+ doctype: ' svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"'
+ },{
+ comment: 'Generator: Adobe Illustrator 16.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)'
+ },{
+ elem: 'svg',
+ prefix: '',
+ local: 'svg',
+ attrs: {
+ version: {
+ name: 'version',
+ value: '1.1',
+ prefix: '',
+ local: 'version'
+ },
+ xmlns: {
+ name: 'xmlns',
+ value: 'http://www.w3.org/2000/svg',
+ prefix: 'xmlns',
+ local: ''
+ }
+ },
+ content: [
+ {
+ elem: 'text',
+ prefix: '',
+ local: 'text',
+ content: [ { text: 'test' } ]
+ },{
+ elem: 'script',
+ prefix: '',
+ local: 'script',
+ content: [ { cdata: ' alert(\'hello\'); ' } ]
+ }
+ ]
+
+ }
+ ]
+}
+```
+
+Важно отметить, что:
+
+ * для представления различных узлов SVG в объектах есть специальные поля: `elem`, `processinginstruction`, `doctype`, `comment`, `cdata` и `text`
+ * `content` – это всегда массив
+ * в поле `attrs` имя атрибута всегда полное, вместе с пространством имён, если оно есть, а все детали разбиты на части в специальных полях `prefix` и `local`
+
+- - -
+
+### 3. плагины
+SVGO применяет все плагины из конфига на данные из AST. Можно посмотреть на множество примеров в [директории плагинов](https://github.com/svg/svgo/tree/master/plugins) выше.
+
+
+#### 3.1 типы
+В самом простом случае процесс применения плагинов можно представить как «каждый плагин пробегается по дереву AST и выполняет какие-то действия». Но 90% обычных оптимизаций требуют каких-либо действий только на одном (текущем) элементе из дерева данных, и нет смысла копипастить рекурсивный цикл по всем элементам в каждом плагине. Это объясняет, почему у нас есть три типа плагинов:
+
+* `perItem` - плагин работает только с текущим элементом внутри рекурсивного цикла «снаружи внутрь» (по умолчанию)
+* `perItemReverse` - плагин работает только с текущим элементом внутри рекурсивного цикла «изнутри наружу» (полезно, например, в случае необходимости схлопывать вложенные элементы один за другим)
+* `full` - плагин работает с полным деревом AST и должен вернуть его же
+
+`perItem` и `perItemReverse` плагины выполняются внутри цикла `Array.prototype.filter`, поэтому можно удалить текущий элемент просто вернув `false`.
+
+Но это ещё не всё ;) Мы избавились от копипаста рекурсивного цикла, но каждый плагин по-прежнему пробегается по всему дереву AST, что не слишком оптимально. На самом деле, на первом шаге, где мы получаем итоговый конфиг, происходит группировка идущих подряд плагинов одного типа и один цикл обрабатывает целую такую группу:
+
+```yaml
+plugins
+
+ - [perItem group],
+ - [perItemReverse group],
+ -
+ - [perItem group],
+ -
+ - [full group]
+ -
+
+
+```
+
+#### 3.2 API
+
+И конечно же, написание плагинов не было бы таким клёвым без удобного API:
+
+##### isElem([param])
+ * 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}
+
+##### isEmpty()
+ * Determine if element is empty.
+ * @return {Boolean}
+
+##### hasAttr([attr], [val])
+ * 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}
+
+##### attr(name, [val])
+ * 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}
+
+##### removeAttr(name, [val])
+ * Remove a specific attribute (by name or name + val).
+ * @param {String} name attribute name
+ * @param {String} [val] attribute value
+ * @return {Boolean}
+
+##### addAttr(attr)
+ * Add an attribute.
+ * @param {Object} attr attribute object
+ * @return {Object} created attribute
+
+##### eachAttr(callback, [context])
+ * Iterates over all attributes.
+ * @param {Function} callback
+ * @param {Object} [context] callback context
+ * @return {Boolean} false if there are no any attributes
+
+#### 3.3 тесты
+
+Нет ничего проще, чем протестировать ваш плагин:
+
+1. создайте `myPlugin.01.orig.svg` и `myPlugin.01.should.svg` в `test/plugins`
+2. запустите `npm test`
+3. PROFIT!
+
+Можно увидеть множество примеров в [test/plugins directory](https://github.com/svg/svgo/tree/master/test/plugins).
+
+- - -
+
+### 4. js2svg
+SVGO конвертирует обратно AST в SVG-как-XML строку.
+
+## Что дальше
+1. Напишите свой собственный плагин :) или
+2. Или подскажите идею новой оптимизации или метода API
View
6 lib/svgo/config.js
@@ -92,11 +92,7 @@ function _getConfig(params) {
return readConfig(defaultConfigPath)
.then(function(defaultConfig) {
- if (params) {
- return extend(true, defaultConfig, params);
- }
-
- return defaultConfig;
+ return extend(true, defaultConfig, params);
});
View
54 lib/svgo/jsAPI.js
@@ -51,24 +51,6 @@ module.exports = INHERIT(/** @lends Nodes.prototype */{
},
/**
- * Iterates over all attributes.
- *
- * @param {Function} callback callback
- * @param {Object} [context] callback context
- *
- * @return {Boolean} false if there are no any attributes
- */
- eachAttr: function(callback, context) {
-
- if (!this.hasAttr()) return false;
-
- for (var name in this.attrs) {
- callback.call(context, this.attrs[name]);
- }
-
- },
-
- /**
* Determine if element has an attribute
* (any, or by name or by name + value).
*
@@ -102,7 +84,7 @@ module.exports = INHERIT(/** @lends Nodes.prototype */{
if (!this.hasAttr() || !arguments.length) return undefined;
- if (val !== undefined) return this.hasAttr(name, val) && this.attrs[name];
+ if (val !== undefined) return this.hasAttr(name, val) ? this.attrs[name] : undefined;
return this.attrs[name];
@@ -118,12 +100,9 @@ module.exports = INHERIT(/** @lends Nodes.prototype */{
*/
removeAttr: function(name, val) {
- if (!this.hasAttr(name, val)) return false;
+ if (!arguments.length) return false;
- if (!arguments.length) {
- delete this.attrs;
- return true;
- }
+ if (!this.hasAttr(name)) return false;
if (val && this.attrs[name].value !== val) return false;
@@ -144,10 +123,37 @@ module.exports = INHERIT(/** @lends Nodes.prototype */{
*/
addAttr: function(attr) {
+ if (!attr ||
+ (attr && attr.name === undefined) ||
+ (attr && attr.value === undefined) ||
+ (attr && attr.prefix === undefined) ||
+ (attr && attr.local === undefined)
+ ) return false;
+
this.attrs = this.attrs || {};
return (this.attrs[attr.name] = attr);
+ },
+
+ /**
+ * Iterates over all attributes.
+ *
+ * @param {Function} callback callback
+ * @param {Object} [context] callback context
+ *
+ * @return {Boolean} false if there are no any attributes
+ */
+ eachAttr: function(callback, context) {
+
+ if (!this.hasAttr()) return false;
+
+ for (var name in this.attrs) {
+ callback.call(context, this.attrs[name]);
+ }
+
+ return true;
+
}
});
View
7 lib/svgo/svg2js.js
@@ -109,7 +109,12 @@ module.exports = function(svg, config) {
sax.onerror = function(e) {
- throw new Error('svg2js: ' + e.message);
+ deferred.reject(new Error('svg2js: ' + e.message));
+
+ // https://github.com/isaacs/sax-js#events
+ // "The error will be hanging out on parser.error,
+ // and must be deleted before parsing can continue"
+ this.error = null;
};
View
18 package.json
@@ -1,6 +1,6 @@
{
"name": "svgo",
- "version": "0.1.3",
+ "version": "0.1.4",
"description": "Nodejs-based tool for optimizing SVG vector graphics files",
"keywords": [ "svgo", "svg", "optimize", "minify" ],
"homepage": "http://svg.github.com/svgo/",
@@ -32,23 +32,25 @@
"example": "./examples"
},
"scripts": {
- "test": "./node_modules/.bin/mocha --reporter spec --require should --recursive",
- "cover": "./node_modules/.bin/istanbul instrument --output lib-cov --no-compact --variable global.__coverage__ lib && ./node_modules/.bin/mocha --reporter mocha-istanbul --require should test/config test/svg2js",
+ "test": "./node_modules/.bin/mocha --reporter spec --recursive",
+ "cover": "./node_modules/.bin/istanbul instrument --output lib-cov --no-compact --variable global.__coverage__ lib && ./node_modules/.bin/mocha --reporter mocha-istanbul --recursive",
"jshint": "jshint --show-non-errors ."
},
"dependencies": {
- "sax": "~0.4",
+ "sax": "~0.4.2",
"q": "~0.8.10",
- "q-fs": "~0.1",
+ "q-fs": "~0.1.0",
"coa": "~0.3.7",
"inherit": "",
"node.extend": "",
"yamljs": "~0.1.3"
},
"devDependencies": {
- "mocha": "~1.6",
- "should": "~1",
- "istanbul": "~0.1",
+ "mocha": "~1.7.0",
+ "mocha-as-promised": "~1.1.0",
+ "chai": "~1.1.0",
+ "chai-as-promised": "~3.2.3",
+ "istanbul": "~0.1.0",
"mocha-istanbul": ""
},
"engines": {
View
56 plugins/_collections.js
@@ -9,7 +9,8 @@ exports.elemsGroups = {
gradient: ['linearGradient', 'radialGradient'],
container: ['a', 'defs', 'glyph', 'g', 'marker', 'mask', 'missing-glyph', 'pattern', 'svg', 'switch', 'symbol'],
textContentChild: ['altGlyph', 'textPath', 'tref', 'tspan'],
- lightSource: ['feDiffuseLighting', 'feSpecularLighting', 'feDistantLight', 'fePointLight', 'feSpotLight']
+ lightSource: ['feDiffuseLighting', 'feSpecularLighting', 'feDistantLight', 'fePointLight', 'feSpotLight'],
+ filterPrimitive: ['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feFlood', 'feGaussianBlur', 'feImage', 'feMerge', 'feMorphology', 'feOffset', 'feSpecularLighting', 'feTile', 'feTurbulence']
};
// var defaults = exports.defaults = {
@@ -37,7 +38,56 @@ exports.attrsGroups = {
exports.attrsGroupsDefaults = {
core: {'xml:space': 'preserve'},
filterPrimitive: {x: '0', y: '0', width: '100%', height: '100%'},
- presentation: {'stop-color': '#000', 'stop-opacity': '1'},
+ presentation: {
+ clip: 'auto',
+ 'clip-path': 'none',
+ 'clip-rule': 'nonzero',
+ mask: 'none',
+ opacity: '1',
+ 'stop-color': '#000',
+ 'stop-opacity': '1',
+ 'fill-opacity': '1',
+ 'fill-rule': 'nonzero',
+ fill: '#000',
+ stroke: 'none',
+ 'stroke-width': '1',
+ 'stroke-linecap': 'butt',
+ 'stroke-linejoin': 'miter',
+ 'stroke-miterlimit': '4',
+ 'stroke-dasharray': 'none',
+ 'stroke-dashoffset': '0',
+ 'stroke-opacity': '1',
+ display: 'inline',
+ visibility: 'visible',
+ 'marker-start': 'none',
+ 'marker-mid': 'none',
+ 'marker-end': 'none',
+ 'color-interpolation': 'sRGB',
+ 'color-interpolation-filters': 'linearRGB',
+ 'color-rendering': 'auto',
+ 'shape-rendering': 'auto',
+ 'text-rendering': 'auto',
+ 'image-rendering': 'auto',
+ 'font-style': 'normal',
+ 'font-variant': 'normal',
+ 'font-weight': 'normal',
+ 'font-stretch': 'normal',
+ 'font-size': 'medium',
+ 'font-size-adjust': 'none',
+ kerning: 'auto',
+ 'letter-spacing': 'normal',
+ 'word-spacing': 'normal',
+ 'text-decoration': 'none',
+ 'text-anchor': 'start',
+ 'writing-mode': 'lr-tb',
+ 'glyph-orientation-vertical': 'auto',
+ 'glyph-orientation-horizontal': '0deg',
+ direction: 'ltr',
+ 'unicode-bidi': 'normal',
+ 'dominant-baseline': 'auto',
+ 'alignment-baseline': 'baseline',
+ 'baseline-shift': 'baseline'
+ },
transferFunction: {slope: '1', intercept: '0', amplitude: '1', exponent: '1', offset: '0'}
};
@@ -889,7 +939,7 @@ exports.elems = {
},
contentGroups: [
'descriptive',
- 'filter'
+ 'filterPrimitive'
],
content: [
'animate',
View
155 test/config/_index.js
@@ -1,8 +1,13 @@
'use strict';
-var cover = process.argv[3] === 'mocha-istanbul',
+var CHAI = require('chai'),
+ cover = process.argv[3] === 'mocha-istanbul',
config = require(cover ? '../../lib-cov/svgo/config' : '../../lib/svgo/config');
+require('mocha-as-promised')();
+CHAI.use(require('chai-as-promised'));
+CHAI.should();
+
function getPlugin(name, config) {
var found;
@@ -23,81 +28,141 @@ describe('config', function() {
describe('default config', function() {
- var result;
-
- before(function(done) {
- config().then(function(data) {
- result = data;
- done();
- })
- .done();
- });
+ var defaultConfig = config();
- it('result should exists', function() {
- result.should.exist;
+ it('should fulfilled', function() {
+ return defaultConfig.should.fulfilled;
});
- it('result should be an instance of Object', function() {
- result.should.be.an.instanceOf(Object);
+ it('should eventually be an Object', function() {
+ return defaultConfig.should.eventually.be.an('object');
});
- it('result should have property "svg2js" with instance of Object', function() {
- result.should.have.property('svg2js').with.instanceOf(Object);
+ it('should eventually have property "svg2js"', function() {
+ return defaultConfig.should.eventually.have.property('svg2js');
});
- it('result should have property "js2svg" with instance of Object', function() {
- result.should.have.property('js2svg').with.instanceOf(Object);
+ it('should eventually have property "js2svg"', function() {
+ return defaultConfig.should.eventually.have.property('js2svg');
});
- it('result should have property "plugins" with instance of Array', function() {
- result.should.have.property('plugins').with.instanceOf(Array);
+ it('should eventually have property "plugins"', function() {
+ return defaultConfig.should.eventually.have.property('plugins');
});
});
- describe('extend default config with object', function() {
+ describe('extend with object', function() {
- var result,
- myConfig = {
+ var myConfig = {
plugins: [{
name: 'removeDoctype',
active: false
}]
- };
-
- before(function(done) {
- config(myConfig).then(function(data) {
- result = data;
- done();
- })
- .done();
- });
+ },
+ extendedConfig = config(myConfig);
- it('result should exists', function() {
- getPlugin('removeDoctype', result).active.should.be.false;
+ it('removeDoctype plugin should be disabled', function() {
+ return extendedConfig.then(function(data) {
+ return getPlugin('removeDoctype', data).active.should.be.false;
+ });
});
});
- describe('extend default config with file', function() {
+ describe('extend with file', function() {
var myConfig = {
coa: {
config: './test/config/config.yml'
}
},
- result;
-
- before(function(done) {
- config(myConfig).then(function(data) {
- result = data;
- done();
- })
- .done();
+ extendedConfig = config(myConfig);
+
+ it('removeDoctype plugin should be disabled', function() {
+ return extendedConfig.then(function(data) {
+ return getPlugin('removeDoctype', data).active.should.be.false;
+ });
});
- it('result should exists', function() {
- getPlugin('removeDoctype', result).active.should.be.false;
+ });
+
+ describe('extend with file that does not exist', function() {
+
+ var myConfig = {
+ coa: {
+ config: './test/config/unknown.yml'
+ }
+ },
+ extendedConfig = config(myConfig);
+
+ it('removeDoctype plugin should be enabled', function() {
+ return extendedConfig.then(function(data) {
+ return getPlugin('removeDoctype', data).active.should.be.true;
+ });
+ });
+
+ });
+
+ describe('change plugins states with --disable', function() {
+
+ var myConfig = {
+ coa: {
+ disable: ['removeDoctype', 'cleanupAttrs', 'unknownPlugin']
+ }
+ },
+ extendedConfig = config(myConfig);
+
+ it('removeDoctype plugin should be disabled', function() {
+ return extendedConfig.then(function(data) {
+ return getPlugin('removeDoctype', data).active.should.be.false;
+ });
+ });
+
+ it('cleanupAttrs plugin should be disabled', function() {
+ return extendedConfig.then(function(data) {
+ return getPlugin('cleanupAttrs', data).active.should.be.false;
+ });
+ });
+
+ });
+
+ describe('change plugins states with --enable', function() {
+
+ var myConfig = {
+ coa: {
+ enable: ['removeDoctype', 'cleanupAttrs', 'unknownPlugin']
+ }
+ },
+ extendedConfig = config(myConfig);
+
+ it('removeDoctype plugin should be disabled', function() {
+ return extendedConfig.then(function(data) {
+ return getPlugin('removeDoctype', data).active.should.be.true;
+ });
+ });
+
+ it('cleanupAttrs plugin should be disabled', function() {
+ return extendedConfig.then(function(data) {
+ return getPlugin('cleanupAttrs', data).active.should.be.true;
+ });
+ });
+
+ });
+
+ describe('change config.js2svg.pretty with --pretty', function() {
+
+ var myConfig = {
+ coa: {
+ pretty: true
+ }
+ },
+ extendedConfig = config(myConfig);
+
+ it('config.js2svg.pretty should be true', function() {
+ return extendedConfig.then(function(data) {
+ return data.js2svg.pretty.should.be.true;
+ });
});
});
View
23 test/plugins/_index.js
@@ -1,11 +1,16 @@
'use strict';
-var INHERIT = require('inherit'),
+var CHAI = require('chai'),
+ INHERIT = require('inherit'),
QFS = require('q-fs'),
FS = require('fs'),
PATH = require('path'),
regFilename = /^(.*)\.(\d+)\.orig\.svg$/;
+require('mocha-as-promised')();
+CHAI.use(require('chai-as-promised'));
+CHAI.should();
+
var MySVGO = INHERIT(require('../../lib/svgo'), {
enableOnlyOne: function(name) {
@@ -38,24 +43,16 @@ describe('plugins tests', function() {
var match = file.match(regFilename),
index,
- name,
- result;
+ name;
if (match) {
name = match[1];
index = match[2];
- before(function(done) {
- getResult(name, index).then(function(data) {
- result = data;
-
- done();
- })
- .done();
- });
-
it(name + '.' + index, function() {
- result[0].should.be.equal(result[1]);
+ return getResult(name, index).then(function(data) {
+ return data[0].should.be.equal(data[1]);
+ });
});
}
View
553 test/svg2js/_index.js
@@ -1,189 +1,512 @@
'use strict';
-var cover = process.argv[3] === 'mocha-istanbul',
- svg2js = require(cover ? '../../lib-cov/svgo/svg2js' : '../../lib/svgo/svg2js');
+var CHAI = require('chai'),
+ QFS = require('q-fs'),
+ PATH = require('path'),
+ assert = CHAI.assert,
+ cover = process.argv[3] === 'mocha-istanbul',
+ svg2js = require(cover ? '../../lib-cov/svgo/svg2js' : '../../lib/svgo/svg2js'),
+ config = {
+ strict: true,
+ trim: true,
+ normalize: true,
+ lowercase: true,
+ xmlns: true,
+ position: false
+ };
+
+require('mocha-as-promised')();
+CHAI.use(require('chai-as-promised'));
+CHAI.should();
describe('svg2js', function() {
- var svg = '<svg xmlns="http://www.w3.org/2000/svg"><g/></svg>',
- config = {
- strict: true,
- trim: true,
- normalize: true,
- lowercase: true,
- xmlns: true,
- position: false
- },
- result;
-
- before(function(done) {
- svg2js(svg, config).then(function(data) {
- result = data;
- done();
- });
- });
+ describe('working svg', function() {
- describe('parser', function() {
+ var path = PATH.resolve(__dirname, './test.svg'),
+ root = QFS.read(path).then(function(data) {
+ return svg2js(data.toString(), config);
+ });
- it('result should exist', function() {
- result.should.exist;
- });
+ describe('nodes', function() {
- it('result should be an instance of Object', function() {
- result.content.should.be.an.instanceOf(Object);
- });
+ describe('root', function() {
- it('result should have property "content" with instance of Array', function() {
- result.should.have.property('content').with.instanceOf(Array);
- });
+ it('should be fulfiled', function() {
+ return root.should.be.fulfiled;
+ });
- it('content should have length 1', function() {
- result.content.should.have.length(1);
- });
+ it('should eventually exist', function() {
+ return root.should.eventually.exist;
+ });
- it('content[0] should have property "elem" with value "svg"', function() {
- result.content[0].should.have.property('elem', 'svg');
- });
+ it('should eventually be an Object', function() {
+ return root.should.eventually.be.an('object');
+ });
- it('svg should have properties "prefix" and "local', function() {
- result.content[0].should.have.property('prefix');
- result.content[0].should.have.property('local');
- });
+ it('should eventually have property "content"', function() {
+ return root.should.eventually.have.property('content');
+ });
- });
+ });
- describe('attributes', function() {
+ describe('root.content', function() {
- it('svg should have property "attrs" with instance of Object', function() {
- result.content[0].should.have.property('attrs').with.instanceOf(Object);
- });
+ var content = root.then(function(data) {
+ return data.content;
+ });
- it('svg.attrs should have property "xmlns" with instance of Object', function() {
- result.content[0].attrs.should.have.property('xmlns').with.instanceOf(Object);
- });
+ it('should eventually exist', function() {
+ return content.should.eventually.exist;
+ });
- it('svg.attrs.xmlns should have properties "name", "value", "prefix", "local" and "uri"', function() {
- result.content[0].attrs.xmlns.should.have.property('name');
- result.content[0].attrs.xmlns.should.have.property('prefix');
- result.content[0].attrs.xmlns.should.have.property('local');
- });
+ it('should eventually be an Array', function() {
+ return content.should.eventually.be.an('array');
+ });
- it('svg.attrs.xmlns.name shoud be equal "xmlns"', function() {
- result.content[0].attrs.xmlns.name.should.equal('xmlns');
- });
+ it('should eventually have length 4', function() {
+ return content.should.eventually.have.length(4);
+ });
- it('svg.attrs.xmlns.value shoud be equal "http://www.w3.org/2000/svg"', function() {
- result.content[0].attrs.xmlns.value.should.equal('http://www.w3.org/2000/svg');
- });
+ });
- it('svg.attrs.xmlns.prefix shoud be equal "xmlns"', function() {
- result.content[0].attrs.xmlns.prefix.should.equal('xmlns');
- });
+ describe('root.content[0].processinginstruction', function() {
- it('svg.attrs.xmlns.local shoud be empty ""', function() {
- result.content[0].attrs.xmlns.local.should.be.empty;
- });
+ var processinginstruction = root.then(function(data) {
+ return data.content[0].processinginstruction;
+ });
- });
+ it('should eventually exist', function() {
+ return processinginstruction.should.eventually.exist;
+ });
- describe('content', function() {
+ it('should eventually be an Object', function() {
+ return processinginstruction.should.eventually.be.an('object');
+ });
- it('svg should have property "content" with instance of Array', function() {
- result.content[0].should.have.property('content').with.instanceOf(Array);
- });
+ it('should eventually have property "name" with value "xml"', function() {
+ return processinginstruction.should.eventually.have.property('name', 'xml');
+ });
- it('svg.content should have length 1', function() {
- result.content[0].content.should.have.length(1);
- });
+ it('should eventually have property "body" with value "version=\"1.0\" encoding=\"utf-8\""', function() {
+ return processinginstruction.should.eventually.have.property('body', 'version=\"1.0\" encoding=\"utf-8\"');
+ });
- });
+ });
+
+ describe('root.content[1].comment', function() {
- describe('API', function() {
+ var comment = root.then(function(data) {
+ return data.content[1].comment;
+ });
- describe('isElem()', function() {
+ it('should eventually exist', function() {
+ return comment.should.eventually.exist;
+ });
+
+ it('should eventually equal "Generator: Adobe Illustrator…"', function() {
+ return comment.should.eventually.equal('Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)');
+ });
- it('svg should have property "isElem" with instance of Function', function() {
- result.content[0].should.have.property('isElem').with.instanceOf(Function);
});
- it('svg.sNode() should be true', function() {
- result.content[0].isElem().should.be.true;
+ describe('root.content[2].doctype', function() {
+
+ var doctype = root.then(function(data) {
+ return data.content[2].doctype;
+ });
+
+ it('should eventually exist', function() {
+ return doctype.should.eventually.exist;
+ });
+
+ it('should eventually equal " svg PUBLIC…"', function() {
+ return doctype.should.eventually.equal(' svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"');
+ });
+
});
- it('svg.isElem("svg") should be true', function() {
- result.content[0].isElem('svg').should.be.true;
+ describe('root.content[3]', function() {
+
+ var elem = root.then(function(data) {
+ return data.content[3];
+ });
+
+ it('should eventually have property elem: "svg"', function() {
+ return elem.should.eventually.have.property('elem', 'svg');
+ });
+
+ it('should eventually have property prefix: ""', function() {
+ return elem.should.eventually.have.property('prefix', '');
+ });
+
+ it('should eventually have property local: "svg"', function() {
+ return elem.should.eventually.have.property('local', 'svg');
+ });
+
});
- it('svg.isElem("trololo") should be false', function() {
- result.content[0].isElem('123').should.be.false;
+ });
+
+ describe('attributes', function() {
+
+ describe('root.content[3].attrs', function() {
+
+ var attrs = root.then(function(data) {
+ return data.content[3].attrs;
+ });
+
+ it('should eventually exist', function() {
+ return attrs.should.eventually.exist;
+ });
+
+ it('should eventually be an Object', function() {
+ return attrs.should.be.an('object');
+ });
+
});
- it('svg.isElem(["svg", "trololo"]) should be true', function() {
- result.content[0].isElem(['svg', 'trololo']).should.be.true;
+ describe('root.content[3].attrs.version', function() {
+
+ var version = root.then(function(data) {
+ return data.content[3].attrs.version;
+ });
+
+ it('should eventually exist', function() {
+ return version.should.eventually.exist;
+ });
+
+ it('should eventually be an Object', function() {
+ return version.should.eventually.be.an('object');
+ });
+
+ it('should eventually have property name: "version"', function() {
+ return version.should.eventually.have.property('name', 'version');
+ });
+
+ it('should eventually have property value: "1.1"', function() {
+ return version.should.eventually.have.property('value', '1.1');
+ });
+
+ it('should eventually have property prefix: ""', function() {
+ return version.should.eventually.have.property('prefix', '');
+ });
+
+ it('should eventually have property local: "version"', function() {
+ return version.should.eventually.have.property('local', 'version');
+ });
+
});
});
- describe('hasAttr()', function() {
+ describe('content', function() {
- it('svg should have property "hasAttr" with instance of Function', function() {
- result.content[0].should.have.property('hasAttr').with.instanceOf(Function);
+ var content = root.then(function(data) {
+ return data.content[3].content;
});
- it('svg.hasAttr() should be true', function() {
- result.content[0].hasAttr().should.be.true;
+ it('should eventually exist', function() {
+ return content.should.eventually.exist;
});
- it('svg.hasAttr("xmlns") should be true', function() {
- result.content[0].hasAttr('xmlns').should.be.true;
+ it('should eventually be an Array', function() {
+ return content.should.eventually.be.an('array');
});
- it('svg.hasAttr("xmlns", "http://www.w3.org/2000/svg") should be true', function() {
- result.content[0].hasAttr('xmlns', 'http://www.w3.org/2000/svg').should.be.true;
+ it('should eventually have length 3', function() {
+ return content.should.eventually.have.length(3);
});
- it('svg.hasAttr("xmlns", "trololo") should be false', function() {
- result.content[0].hasAttr('xmlns', 'trololo').should.be.false;
- });
+ });
+
+ describe('API', function() {
- it('svg.hasAttr("trololo") should be false', function() {
- result.content[0].hasAttr('trololo').should.be.false;
+ var svg = root.then(function(data) {
+ return data.content[3];
});
- it('svg.hasAttr("trololo", "ololo") should be false', function() {
- result.content[0].hasAttr('trololo', 'ololo').should.be.false;
+ describe('isElem()', function() {
+
+ it('svg should eventually have property "isElem"', function() {
+ return svg.should.eventually.have.property('isElem');
+ });
+
+ it('svg.isElem() should be true', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.isElem().should.be.true;
+ });
+ });
+
+ it('svg.isElem("svg") should be true', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.isElem('svg').should.be.true;
+ });
+ });
+
+ it('svg.isElem("trololo") should be false', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.isElem('trololo').should.be.false;
+ });
+ });
+
+ it('svg.isElem(["svg", "trololo"]) should be true', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.isElem(['svg', 'trololo']).should.be.true;
+ });
+ });
+
});
- it('svg.g.hasAttr() should be false', function() {
- result.content[0].content[0].hasAttr().should.be.false;
+ describe('isEmpty()', function() {
+
+ it('svg should eventually have property "isEmpty"', function() {
+ return svg.should.eventually.have.property('isEmpty');
+ });
+
+ it('svg.isEmpty() should be false', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.isEmpty().should.be.false;
+ });
+ });
+
+ it('svg.content[0].content[0].isEmpty() should be true', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.content[0].content[0].isEmpty().should.be.true;
+ });
+ });
+
});
- it('svg.g.hasAttr("trololo") should be false', function() {
- result.content[0].content[0].hasAttr('trololo').should.be.false;
+ describe('hasAttr()', function() {
+
+ it('svg should eventually have property "hasAttr"', function() {
+ return svg.should.eventually.have.property('hasAttr');
+ });
+
+ it('svg.hasAttr() should be true', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.hasAttr().should.be.true;
+ });
+ });
+
+ it('svg.hasAttr("xmlns") should be true', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.hasAttr('xmlns').should.be.true;
+ });
+ });
+
+ it('svg.hasAttr("xmlns", "http://www.w3.org/2000/svg") should be true', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.hasAttr('xmlns', 'http://www.w3.org/2000/svg').should.be.true;
+ });
+ });
+
+ it('svg.hasAttr("xmlns", "trololo") should be false', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.hasAttr('xmlns', 'trololo').should.be.false;
+ });
+ });
+
+ it('svg.hasAttr("trololo") should be false', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.hasAttr('trololo').should.be.false;
+ });
+ });
+
+ it('svg.content[1].hasAttr() should be false', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.content[1].hasAttr().should.be.false;
+ });
+ });
+
});
- it('svg.g.hasAttr("trololo", "ololo") should be false', function() {
- result.content[0].content[0].hasAttr('trololo', 'ololo').should.be.false;
+ describe('attr()', function() {
+
+ it('svg should eventually have property "attr"', function() {
+ return svg.should.eventually.have.property('attr');
+ });
+
+ it('svg.attr("xmlns") should be an Object', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.attr('xmlns').should.be.an('object');
+ });
+ });
+
+ it('svg.attr("xmlns", "http://www.w3.org/2000/svg") should be an Object', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.attr('xmlns', 'http://www.w3.org/2000/svg').should.be.an('object');
+ });
+ });
+
+ it('svg.attr("xmlns", "trololo") should be an undefined', function() {
+ return svg.then(function(svgElem) {
+ return assert.strictEqual(svgElem.attr('xmlns', 'trololo'), undefined);
+ });
+ });
+
+ it('svg.attr("trololo") should be an undefined', function() {
+ return svg.then(function(svgElem) {
+ return assert.strictEqual(svgElem.attr('trololo'), undefined);
+ });
+ });
+
+ it('svg.attr() should be undefined', function() {
+ return svg.then(function(svgElem) {
+ return assert.strictEqual(svgElem.attr(), undefined);
+ });
+ });
+
});
- });
+ describe('removeAttr()', function() {
- describe('isEmpty()', function() {
+ it('svg should eventually have property "removeAttr"', function() {
+ return svg.should.eventually.have.property('removeAttr');
+ });
+
+ it('svg.removeAttr("width") should be true', function() {
+ return svg.then(function(svgElem) {
+ svgElem.removeAttr('width').should.be.true;
+
+ return svgElem.hasAttr('width').should.be.false;
+ });
+ });
+
+ it('svg.removeAttr("height", "120px") should be true', function() {
+ return svg.then(function(svgElem) {
+ svgElem.removeAttr('height', '120px').should.be.true;
+
+ return svgElem.hasAttr('height').should.be.false;
+ });
+ });
+
+ it('svg.removeAttr("x", "1px") should be false', function() {
+ return svg.then(function(svgElem) {
+ svgElem.removeAttr('x', '1px').should.be.false;
+
+ return svgElem.hasAttr('x').should.be.true;
+ });
+ });
+
+ it('svg.removeAttr("z") should be false', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.removeAttr('z').should.be.false;
+ });
+ });
+
+ it('svg.removeAttr() should be false', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.removeAttr().should.be.false;
+ });
+ });
+
+ it('svg.content[2].removeAttr("style") should be true', function() {
+ return svg.then(function(svgElem) {
+ svgElem.content[2].removeAttr('style').should.be.true;
+
+ return svgElem.content[2].hasAttr().should.be.false;
+ });
+ });
- it('svg should have property "isEmpty" with instance of Function', function() {
- result.content[0].should.have.property('isEmpty').with.instanceOf(Function);
});
- it('svg.isEmpty() should be false', function() {
- result.content[0].isEmpty().should.be.false;
+ describe('addAttr()', function() {
+
+ var attr = {
+ name: 'test',
+ value: 3,
+ prefix: '',
+ local: 'test'
+ };
+
+ it('svg should eventually have property "addAttr"', function() {
+ return svg.should.eventually.have.property('addAttr');
+ });
+
+ it('svg.addAttr(attr) should be an Object', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.addAttr(attr).should.be.an('object');
+ });
+ });
+
+ it('svg.content[1].content[0].addAttr(attr) should be an Object', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.content[1].content[0].addAttr(attr).should.be.an('object');
+ });
+ });
+
+ it('svg.addAttr({ name: "trololo" }) should be false', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.addAttr({ name: 'trololo' }).should.be.false;
+ });
+ });
+
+ it('svg.addAttr({ name: "trololo", value: 3 }) should be false', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.addAttr({ name: 'trololo', value: 3 }).should.be.false;
+ });
+ });
+
+ it('svg.addAttr({ name: "trololo", value: 3, prefix: "" }) should be false', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.addAttr({ name: 'trololo', value: 3, prefix: '' }).should.be.false;
+ });
+ });
+
+ it('svg.addAttr({ name: "trololo", value: 3, local: "trololo" }) should be false', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.addAttr({ name: 'trololo', value: 3, local: 'trololo' }).should.be.false;
+ });
+ });
+
+ it('svg.addAttr() should be false', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.addAttr().should.be.false;
+ });
+ });
+
});
- it('svg.g.isEmpty() should be true', function() {
- result.content[0].content[0].isEmpty().should.be.true;
+ describe('eachAttr()', function() {
+
+ it('svg should eventually have property "eachAttr"', function() {
+ return svg.should.eventually.have.property('eachAttr');
+ });
+
+ it('svg.content[0].eachAttr(function() {}) should be true', function() {
+ return svg.then(function(svgElem) {
+ svgElem.content[0].eachAttr(function(attr) {
+ attr.test = 1;
+ }).should.be.true;
+
+ return svgElem.content[0].attr('type').test.should.equal(1);
+ });
+ });
+
+ it('svg.content[1].eachAttr(function() {}) should be false', function() {
+ return svg.then(function(svgElem) {
+ return svgElem.content[1].eachAttr(function() {}).should.be.false;
+ });
+ });
+
});
});
+ });
+
+ describe('bad svg', function() {
+
+ var path = PATH.resolve(__dirname, './test.bad.svg'),
+ promise = QFS.read(path).then(function(data) {
+ return svg2js(data.toString(), config);
+ });
+
+ it('should be rejected with "svg2js: Unexpected close tag"', function() {
+ return promise.should.be.rejected.with('svg2js: Unexpected close tag');
+ });
});
View
18 test/svg2js/test.bad.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="120px" height="120px" viewBox="0 0 120 120"
+ enable-background="new 0 0 120 120" xml:space="preserve"
+>
+ style type="text/css"><![CDATA[
+ svg { fill: red; }
+ ]]></style>
+ <g>
+ <g>
+ <circle fill="#ff0000" cx="60px" cy="60px" r="50px"/>
+ <text>test</text>
+ </g>
+ </g>
+</svg>
View
19 test/svg2js/test.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="120px" height="120px" viewBox="0 0 120 120"
+ enable-background="new 0 0 120 120" xml:space="preserve"
+>
+ <style type="text/css"><![CDATA[
+ svg { fill: red; }
+ ]]></style>
+ <g>
+ <g>
+ <circle fill="#ff0000" cx="60px" cy="60px" r="50px"/>
+ <text>test</text>
+ </g>
+ </g>
+ <g style="color: black"></g>
+</svg>
Please sign in to comment.
Something went wrong with that request. Please try again.