Current Dev State

This commit is contained in:
Tim Lorsbach
2025-06-23 20:13:54 +02:00
parent b4f9bb277d
commit ded50edaa2
22617 changed files with 4345095 additions and 174 deletions

21
static/js/ketcher2/node_modules/svg-pathdata/LICENCE generated vendored Executable file
View File

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2013-2015 Nicolas Froidure, <http://insertafter.com/>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

231
static/js/ketcher2/node_modules/svg-pathdata/README.md generated vendored Executable file
View File

@ -0,0 +1,231 @@
# SVGPathData
> Manipulating SVG path data (path[d] attribute content) simply and efficiently.
[![NPM version](https://badge.fury.io/js/svg-pathdata.svg)](https://npmjs.org/package/svg-pathdata) [![Build status](https://secure.travis-ci.org/nfroidure/SVGPathData.svg?branch=master)](https://travis-ci.org/nfroidure/SVGPathData?branch=master) [![Dependency Status](https://david-dm.org/nfroidure/SVGPathData.svg)](https://david-dm.org/nfroidure/SVGPathData) [![devDependency Status](https://david-dm.org/nfroidure/SVGPathData/dev-status.svg)](https://david-dm.org/nfroidure/SVGPathData#info=devDependencies) [![Coverage Status](https://coveralls.io/repos/nfroidure/SVGPathData/badge.svg?branch=master)](https://coveralls.io/r/nfroidure/SVGPathData?branch=master)
## Including the library
This library is fully node based (based on current stream implementation) but
you can also use it in modern browsers with the
[browserified build](https://github.com/nfroidure/SVGPathData/blob/master/dist/SVGPathData.js)
or in your own build using Browserify.
## Reading PathData
```js
var pathData = new SVGPathData ('\
M 10 10 \
H 60 \
V 60 \
L 10 60 \
Z \
');
console.log(pathData.commands);
// {"commands":[{
// "type": SVGPathData.MOVE_TO,
// "relative": false,
// "x": 10, "y": 10
// },{
// "type": SVGPathData.HORIZ_LINE_TO,
// "relative": false,
// "x": 60
// },{
// "type": SVGPathData.VERT_LINE_TO,
// "relative":false,
// "y": 60
// },{
// "type": SVGPathData.LINE_TO,
// "relative": false,
// "x": 10,
// "y": 60
// },{
// "type": SVGPathData.CLOSE_PATH
// }
// ]}
```
## Reading streamed PathData
```js
var parser = new SVGPathData.Parser();
parser.on('data', function(cmd) {
console.log(cmd);
});
parser.write(' ');
parser.write('M 10');
parser.write(' 10');
// {
// "type": SVGPathData.MOVE_TO,
// "relative": false,
// "x": 10, "y": 10
// }
parser.write('H 60');
// {
// "type": SVGPathData.HORIZ_LINE_TO,
// "relative": false,
// "x": 60
// }
parser.write('V');
parser.write('60');
// {
// "type": SVGPathData.VERT_LINE_TO,
// "relative": false,
// "y": 60
// }
parser.write('L 10 60 \
Z');
// {
// "type": SVGPathData.LINE_TO,
// "relative": false,
// "x": 10,
// "y": 60
// }
// {
// "type": SVGPathData.CLOSE_PATH
// }
parser.end();
```
## Outputting PathData
```js
var pathData = new SVGPathData ('\
M 10 10 \
H 60 \
V 60 \
L 10 60 \
Z \
');
console.log(pathData.encode());
// "M10 10H60V60L10 60Z"
```
## Streaming PathData out
```js
var encoder = new SVGPathData.Encoder();
encoder.setEncoding('utf8');
encode.on('data', function(str) {
console.log(str);
});
encoder.write({
"type": SVGPathData.MOVE_TO,
"relative": false,
"x": 10, "y": 10
});
// "M10 10"
encoder.write({
"type": SVGPathData.HORIZ_LINE_TO,
"relative": false,
"x": 60
});
// "H60"
encoder.write({
"type": SVGPathData.VERT_LINE_TO,
"relative": false,
"y": 60
});
// "V60"
encoder.write({
"type": SVGPathData.LINE_TO,
"relative": false,
"x": 10,
"y": 60
});
// "L10 60"
encoder.write({"type": SVGPathData.CLOSE_PATH});
// "Z"
encode.end();
```
## Transforming PathData
This library was made to live decoding/transform/encoding SVG PathData. Here is
[an example of that kind of use](https://github.com/nfroidure/svgicons2svgfont/blob/aa6df0211419e9d61c417c63bcc353f0cb2ea0c8/src/index.js#L192).
### The synchronous way
```js
console.log(
new SVGPathData ('\
m 10,10 \
h 60 \
v 60 \
l 10,60 \
z'
)
.toAbs()
.encode()
);
// "M10,10 H70 V70 L80,130 Z"
```
### The streaming/asynchronous way
Here, we take SVGPathData from stdin and output it transformed to stdout.
```js
// stdin to parser
process.stdin.pipe(new SVGPathData.Parser())
// parser to transformer to absolute
.pipe(new SVGPathData.Transformer(SVGPathData.Transformer.TO_ABS))
// transformer to encoder
.pipe(new SVGPathData.Encoder())
// encoder to stdout
.pipe(process.stdout);
```
## Supported transformations
You can find every supported transformations in
[this file](https://github.com/nfroidure/SVGPathData/blob/master/src/SVGPathDataTransformer.js#L47)
of course, you can create yours by using this format:
```js
function SET_X_TO(xValue) {
xValue = xValue || 10; // Provide default values or throw errors for options
function(command) {
command.x = xValue; // transform command objects and return them
return command;
};
};
// Synchronous usage
new SVGPathData('...')
.transform(SET_X_TO, 25)
.encode();
// Streaming usage
process.stdin.pipe(new SVGPathData.Parser())
.pipe(new SVGPathData.Transformer(SET_X_TO, 25))
.pipe(new SVGPathData.Encoder())
.pipe(process.stdout);
```
## Stats
[![NPM](https://nodei.co/npm/svg-pathdata.png?downloads=true&stars=true)](https://nodei.co/npm/svg-pathdata/)
[![NPM](https://nodei.co/npm-dl/svg-pathdata.png)](https://nodei.co/npm/svg-pathdata/)
## Contributing
Clone this project, run:
```sh
npm install; npm test
```
Build:
```sh
npm build
```

View File

@ -0,0 +1,213 @@
body, html {
margin:0; padding: 0;
height: 100%;
}
body {
font-family: Helvetica Neue, Helvetica, Arial;
font-size: 14px;
color:#333;
}
.small { font-size: 12px; }
*, *:after, *:before {
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}
h1 { font-size: 20px; margin: 0;}
h2 { font-size: 14px; }
pre {
font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
margin: 0;
padding: 0;
-moz-tab-size: 2;
-o-tab-size: 2;
tab-size: 2;
}
a { color:#0074D9; text-decoration:none; }
a:hover { text-decoration:underline; }
.strong { font-weight: bold; }
.space-top1 { padding: 10px 0 0 0; }
.pad2y { padding: 20px 0; }
.pad1y { padding: 10px 0; }
.pad2x { padding: 0 20px; }
.pad2 { padding: 20px; }
.pad1 { padding: 10px; }
.space-left2 { padding-left:55px; }
.space-right2 { padding-right:20px; }
.center { text-align:center; }
.clearfix { display:block; }
.clearfix:after {
content:'';
display:block;
height:0;
clear:both;
visibility:hidden;
}
.fl { float: left; }
@media only screen and (max-width:640px) {
.col3 { width:100%; max-width:100%; }
.hide-mobile { display:none!important; }
}
.quiet {
color: #7f7f7f;
color: rgba(0,0,0,0.5);
}
.quiet a { opacity: 0.7; }
.fraction {
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
font-size: 10px;
color: #555;
background: #E8E8E8;
padding: 4px 5px;
border-radius: 3px;
vertical-align: middle;
}
div.path a:link, div.path a:visited { color: #333; }
table.coverage {
border-collapse: collapse;
margin: 10px 0 0 0;
padding: 0;
}
table.coverage td {
margin: 0;
padding: 0;
vertical-align: top;
}
table.coverage td.line-count {
text-align: right;
padding: 0 5px 0 20px;
}
table.coverage td.line-coverage {
text-align: right;
padding-right: 10px;
min-width:20px;
}
table.coverage td span.cline-any {
display: inline-block;
padding: 0 5px;
width: 100%;
}
.missing-if-branch {
display: inline-block;
margin-right: 5px;
border-radius: 3px;
position: relative;
padding: 0 4px;
background: #333;
color: yellow;
}
.skip-if-branch {
display: none;
margin-right: 10px;
position: relative;
padding: 0 4px;
background: #ccc;
color: white;
}
.missing-if-branch .typ, .skip-if-branch .typ {
color: inherit !important;
}
.coverage-summary {
border-collapse: collapse;
width: 100%;
}
.coverage-summary tr { border-bottom: 1px solid #bbb; }
.keyline-all { border: 1px solid #ddd; }
.coverage-summary td, .coverage-summary th { padding: 10px; }
.coverage-summary tbody { border: 1px solid #bbb; }
.coverage-summary td { border-right: 1px solid #bbb; }
.coverage-summary td:last-child { border-right: none; }
.coverage-summary th {
text-align: left;
font-weight: normal;
white-space: nowrap;
}
.coverage-summary th.file { border-right: none !important; }
.coverage-summary th.pct { }
.coverage-summary th.pic,
.coverage-summary th.abs,
.coverage-summary td.pct,
.coverage-summary td.abs { text-align: right; }
.coverage-summary td.file { white-space: nowrap; }
.coverage-summary td.pic { min-width: 120px !important; }
.coverage-summary tfoot td { }
.coverage-summary .sorter {
height: 10px;
width: 7px;
display: inline-block;
margin-left: 0.5em;
background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
}
.coverage-summary .sorted .sorter {
background-position: 0 -20px;
}
.coverage-summary .sorted-desc .sorter {
background-position: 0 -10px;
}
.status-line { height: 10px; }
/* dark red */
.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
.low .chart { border:1px solid #C21F39 }
/* medium red */
.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
/* light red */
.low, .cline-no { background:#FCE1E5 }
/* light green */
.high, .cline-yes { background:rgb(230,245,208) }
/* medium green */
.cstat-yes { background:rgb(161,215,106) }
/* dark green */
.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
.high .chart { border:1px solid rgb(77,146,33) }
/* dark yellow (gold) */
.medium .chart { border:1px solid #f9cd0b; }
.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
/* light yellow */
.medium { background: #fff4c2; }
/* light gray */
span.cline-neutral { background: #eaeaea; }
.cbranch-no { background: yellow !important; color: #111; }
.cstat-skip { background: #ddd; color: #111; }
.fstat-skip { background: #ddd; color: #111 !important; }
.cbranch-skip { background: #ddd !important; color: #111; }
.cover-fill, .cover-empty {
display:inline-block;
height: 12px;
}
.chart {
line-height: 0;
}
.cover-empty {
background: white;
}
.cover-full {
border-right: none !important;
}
pre.prettyprint {
border: none !important;
padding: 0 !important;
margin: 0 !important;
}
.com { color: #999 !important; }
.ignore-none { color: #999; font-weight: normal; }
.wrapper {
min-height: 100%;
height: auto !important;
height: 100%;
margin: 0 auto -48px;
}
.footer, .push {
height: 48px;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,93 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for All files</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="prettify.css" />
<link rel="stylesheet" href="base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
/
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">98.48% </span>
<span class="quiet">Statements</span>
<span class='fraction'>649/659</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">94.36% </span>
<span class="quiet">Branches</span>
<span class='fraction'>435/461</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>57/57</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">98.47% </span>
<span class="quiet">Lines</span>
<span class='fraction'>645/655</span>
</div>
</div>
</div>
<div class='status-line high'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file high" data-value="src/"><a href="src/index.html">src/</a></td>
<td data-value="98.48" class="pic high"><div class="chart"><div class="cover-fill" style="width: 98%;"></div><div class="cover-empty" style="width:2%;"></div></div></td>
<td data-value="98.48" class="pct high">98.48%</td>
<td data-value="659" class="abs high">649/659</td>
<td data-value="94.36" class="pct high">94.36%</td>
<td data-value="461" class="abs high">435/461</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="57" class="abs high">57/57</td>
<td data-value="98.47" class="pct high">98.47%</td>
<td data-value="655" class="abs high">645/655</td>
</tr>
</tbody>
</table>
</div><div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Mon Nov 07 2016 15:21:32 GMT+0100 (CET)
</div>
</div>
<script src="prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="sorter.js"></script>
</body>
</html>

View File

@ -0,0 +1 @@
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

View File

@ -0,0 +1,158 @@
var addSorting = (function () {
"use strict";
var cols,
currentSort = {
index: 0,
desc: false
};
// returns the summary table element
function getTable() { return document.querySelector('.coverage-summary'); }
// returns the thead element of the summary table
function getTableHeader() { return getTable().querySelector('thead tr'); }
// returns the tbody element of the summary table
function getTableBody() { return getTable().querySelector('tbody'); }
// returns the th element for nth column
function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; }
// loads all columns
function loadColumns() {
var colNodes = getTableHeader().querySelectorAll('th'),
colNode,
cols = [],
col,
i;
for (i = 0; i < colNodes.length; i += 1) {
colNode = colNodes[i];
col = {
key: colNode.getAttribute('data-col'),
sortable: !colNode.getAttribute('data-nosort'),
type: colNode.getAttribute('data-type') || 'string'
};
cols.push(col);
if (col.sortable) {
col.defaultDescSort = col.type === 'number';
colNode.innerHTML = colNode.innerHTML + '<span class="sorter"></span>';
}
}
return cols;
}
// attaches a data attribute to every tr element with an object
// of data values keyed by column name
function loadRowData(tableRow) {
var tableCols = tableRow.querySelectorAll('td'),
colNode,
col,
data = {},
i,
val;
for (i = 0; i < tableCols.length; i += 1) {
colNode = tableCols[i];
col = cols[i];
val = colNode.getAttribute('data-value');
if (col.type === 'number') {
val = Number(val);
}
data[col.key] = val;
}
return data;
}
// loads all row data
function loadData() {
var rows = getTableBody().querySelectorAll('tr'),
i;
for (i = 0; i < rows.length; i += 1) {
rows[i].data = loadRowData(rows[i]);
}
}
// sorts the table using the data for the ith column
function sortByIndex(index, desc) {
var key = cols[index].key,
sorter = function (a, b) {
a = a.data[key];
b = b.data[key];
return a < b ? -1 : a > b ? 1 : 0;
},
finalSorter = sorter,
tableBody = document.querySelector('.coverage-summary tbody'),
rowNodes = tableBody.querySelectorAll('tr'),
rows = [],
i;
if (desc) {
finalSorter = function (a, b) {
return -1 * sorter(a, b);
};
}
for (i = 0; i < rowNodes.length; i += 1) {
rows.push(rowNodes[i]);
tableBody.removeChild(rowNodes[i]);
}
rows.sort(finalSorter);
for (i = 0; i < rows.length; i += 1) {
tableBody.appendChild(rows[i]);
}
}
// removes sort indicators for current column being sorted
function removeSortIndicators() {
var col = getNthColumn(currentSort.index),
cls = col.className;
cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
col.className = cls;
}
// adds sort indicators for current column being sorted
function addSortIndicators() {
getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted';
}
// adds event listeners for all sorter widgets
function enableUI() {
var i,
el,
ithSorter = function ithSorter(i) {
var col = cols[i];
return function () {
var desc = col.defaultDescSort;
if (currentSort.index === i) {
desc = !currentSort.desc;
}
sortByIndex(i, desc);
removeSortIndicators();
currentSort.index = i;
currentSort.desc = desc;
addSortIndicators();
};
};
for (i =0 ; i < cols.length; i += 1) {
if (cols[i].sortable) {
// add the click event handler on the th so users
// dont have to click on those tiny arrows
el = getNthColumn(i).querySelector('.sorter').parentElement;
if (el.addEventListener) {
el.addEventListener('click', ithSorter(i));
} else {
el.attachEvent('onclick', ithSorter(i));
}
}
}
}
// adds sorting functionality to the UI
return function () {
if (!getTable()) {
return;
}
cols = loadColumns();
loadData(cols);
addSortIndicators();
enableUI();
};
})();
window.addEventListener('load', addSorting);

View File

@ -0,0 +1,518 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/SVGPathData.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../prettify.css" />
<link rel="stylesheet" href="../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../index.html">all files</a> / <a href="index.html">src/</a> SVGPathData.js
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Statements</span>
<span class='fraction'>81/81</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Branches</span>
<span class='fraction'>6/6</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>19/19</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>81/81</span>
</div>
</div>
</div>
<div class='status-line high'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152</td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">218×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">50×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">17×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">12×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">11×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">3×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">5×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">13×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">10×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">3×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">3×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">8×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">93×</span>
<span class="cline-any cline-yes">93×</span>
<span class="cline-any cline-yes">93×</span>
<span class="cline-any cline-yes">93×</span>
<span class="cline-any cline-yes">93×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">93×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">82×</span>
<span class="cline-any cline-yes">687×</span>
<span class="cline-any cline-yes">687×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">683×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">82×</span>
<span class="cline-any cline-yes">82×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">50×</span>
<span class="cline-any cline-yes">50×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">50×</span>
<span class="cline-any cline-yes">100×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">100×</span>
<span class="cline-any cline-yes">150×</span>
<span class="cline-any cline-yes">150×</span>
<span class="cline-any cline-yes">50×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">50×</span>
<span class="cline-any cline-yes">50×</span>
<span class="cline-any cline-yes">50×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">218×</span>
<span class="cline-any cline-yes">218×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">218×</span>
<span class="cline-any cline-yes">921×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">921×</span>
<span class="cline-any cline-yes">1664×</span>
<span class="cline-any cline-yes">1664×</span>
<span class="cline-any cline-yes">743×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">218×</span>
<span class="cline-any cline-yes">208×</span>
<span class="cline-any cline-yes">178×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">'use strict';
&nbsp;
function SVGPathData(content) {
this.commands = SVGPathData.parse(content);
}
&nbsp;
SVGPathData.prototype.encode = function() {
return SVGPathData.encode(this.commands);
};
&nbsp;
SVGPathData.prototype.round = function() {
return this.transform.apply(this, [SVGPathData.Transformer.ROUND].concat(
[].slice.call(arguments, 0)));
};
&nbsp;
SVGPathData.prototype.toAbs = function() {
return this.transform(SVGPathData.Transformer.TO_ABS);
};
&nbsp;
SVGPathData.prototype.toRel = function() {
return this.transform(SVGPathData.Transformer.TO_REL);
};
&nbsp;
SVGPathData.prototype.translate = function() {
return this.transform.apply(this, [SVGPathData.Transformer.TRANSLATE].concat(
[].slice.call(arguments, 0)));
};
&nbsp;
SVGPathData.prototype.scale = function() {
return this.transform.apply(this, [SVGPathData.Transformer.SCALE].concat(
[].slice.call(arguments, 0)));
};
&nbsp;
SVGPathData.prototype.rotate = function() {
return this.transform.apply(this, [SVGPathData.Transformer.ROTATE].concat(
[].slice.call(arguments, 0)));
};
&nbsp;
SVGPathData.prototype.matrix = function() {
return this.transform.apply(this, [SVGPathData.Transformer.MATRIX].concat(
[].slice.call(arguments, 0)));
};
&nbsp;
SVGPathData.prototype.skewX = function() {
return this.transform.apply(this, [SVGPathData.Transformer.SKEW_X].concat(
[].slice.call(arguments, 0)));
};
&nbsp;
SVGPathData.prototype.skewY = function() {
return this.transform.apply(this, [SVGPathData.Transformer.SKEW_Y].concat(
[].slice.call(arguments, 0)));
};
&nbsp;
SVGPathData.prototype.xSymetry = function() {
return this.transform.apply(this, [SVGPathData.Transformer.X_AXIS_SIMETRY].concat(
[].slice.call(arguments, 0)));
};
&nbsp;
SVGPathData.prototype.ySymetry = function() {
return this.transform.apply(this, [SVGPathData.Transformer.Y_AXIS_SIMETRY].concat(
[].slice.call(arguments, 0)));
};
&nbsp;
SVGPathData.prototype.aToC = function() {
return this.transform.apply(this, [SVGPathData.Transformer.A_TO_C].concat(
[].slice.call(arguments, 0)));
};
&nbsp;
SVGPathData.prototype.transform = function(transformFunction) {
var newCommands = [];
var curCommands = [];
var commands = this.commands;
var i;
var ii;
&nbsp;
transformFunction = transformFunction.apply(null, [].slice.call(arguments, 1));
&nbsp;
for(i = 0, ii = commands.length; i &lt; ii; i++) {
curCommands = transformFunction(commands[i]);
if(curCommands instanceof Array) {
newCommands = newCommands.concat(curCommands);
} else {
newCommands.push(curCommands);
}
}
this.commands = newCommands;
return this;
};
&nbsp;
// Static methods
SVGPathData.encode = function(commands) {
var content = '';
var encoder = new SVGPathData.Encoder();
&nbsp;
encoder.on('readable', function() {
var str;
&nbsp;
do {
str = encoder.read();
if(null !== str) {
content += str;
}
} while(null !== str);
});
encoder.write(commands);
encoder.end();
return content;
};
&nbsp;
SVGPathData.parse = function(content) {
var commands = [];
var parser = new SVGPathData.Parser();
&nbsp;
parser.on('readable', function() {
var command;
&nbsp;
do {
command = parser.read();
if(null !== command) {
commands.push(command);
}
} while(null !== command);
});
parser.write(content);
parser.end();
return commands;
};
&nbsp;
// Commands static vars
SVGPathData.CLOSE_PATH = 1;
SVGPathData.MOVE_TO = 2;
SVGPathData.HORIZ_LINE_TO = 4;
SVGPathData.VERT_LINE_TO = 8;
SVGPathData.LINE_TO = 16;
SVGPathData.CURVE_TO = 32;
SVGPathData.SMOOTH_CURVE_TO = 64;
SVGPathData.QUAD_TO = 128;
SVGPathData.SMOOTH_QUAD_TO = 256;
SVGPathData.ARC = 512;
SVGPathData.DRAWING_COMMANDS =
SVGPathData.HORIZ_LINE_TO | SVGPathData.VERT_LINE_TO | SVGPathData.LINE_TO |
SVGPathData.CURVE_TO | SVGPathData.SMOOTH_CURVE_TO | SVGPathData.QUAD_TO |
SVGPathData.SMOOTH_QUAD_TO | SVGPathData.ARC;
&nbsp;
// Export the main contructor first (tests are failing otherwise)
module.exports = SVGPathData;
&nbsp;
// Expose the internal constructors
SVGPathData.Parser = require('./SVGPathDataParser.js');
SVGPathData.Encoder = require('./SVGPathDataEncoder.js');
SVGPathData.Transformer = require('./SVGPathDataTransformer.js');
&nbsp;</pre></td></tr>
</table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Mon Nov 07 2016 15:21:32 GMT+0100 (CET)
</div>
</div>
<script src="../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../sorter.js"></script>
</body>
</html>

View File

@ -0,0 +1,383 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/SVGPathDataEncoder.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../prettify.css" />
<link rel="stylesheet" href="../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../index.html">all files</a> / <a href="index.html">src/</a> SVGPathDataEncoder.js
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Statements</span>
<span class='fraction'>43/43</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Branches</span>
<span class='fraction'>42/42</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>2/2</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Lines</span>
<span class='fraction'>43/43</span>
</div>
</div>
</div>
<div class='status-line high'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107</td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">53×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">52×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">52×</span>
<span class="cline-any cline-yes">52×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">51×</span>
<span class="cline-any cline-yes">51×</span>
<span class="cline-any cline-yes">51×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">51×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">51×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">409×</span>
<span class="cline-any cline-yes">43×</span>
<span class="cline-any cline-yes">43×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">366×</span>
<span class="cline-any cline-yes">24×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">342×</span>
<span class="cline-any cline-yes">6×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">336×</span>
<span class="cline-any cline-yes">46×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">290×</span>
<span class="cline-any cline-yes">136×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">154×</span>
<span class="cline-any cline-yes">121×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">33×</span>
<span class="cline-any cline-yes">9×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">24×</span>
<span class="cline-any cline-yes">6×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">18×</span>
<span class="cline-any cline-yes">7×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">11×</span>
<span class="cline-any cline-yes">10×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">50×</span>
<span class="cline-any cline-yes">50×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">'use strict';
&nbsp;
// Encode SVG PathData
// http://www.w3.org/TR/SVG/paths.html#PathDataBNF
&nbsp;
// Access to SVGPathData constructor
var SVGPathData = require('./SVGPathData.js');
&nbsp;
// TransformStream inherance required modules
var TransformStream = require('readable-stream').Transform;
var util = require('util');
&nbsp;
// Private consts : Char groups
var WSP = ' ';
&nbsp;
// Inherit of writeable stream
util.inherits(SVGPathDataEncoder, TransformStream);
&nbsp;
// Constructor
function SVGPathDataEncoder(options) {
&nbsp;
// Ensure new were used
if(!(this instanceof SVGPathDataEncoder)) {
return new SVGPathDataEncoder(options);
}
&nbsp;
// Parent constructor
TransformStream.call(this, {
objectMode: true,
});
&nbsp;
// Setting objectMode separately
this._writableState.objectMode = true;
this._readableState.objectMode = false;
&nbsp;
}
&nbsp;
&nbsp;
// Read method
SVGPathDataEncoder.prototype._transform = function(commands, encoding, done) {
var str = '';
var i;
var j;
&nbsp;
if(!(commands instanceof Array)) {
commands = [commands];
}
for(i = 0, j = commands.length; i &lt; j; i++) {
// Horizontal move to command
if(commands[i].type === SVGPathData.CLOSE_PATH) {
str += 'z';
continue;
// Horizontal move to command
} else if(commands[i].type === SVGPathData.HORIZ_LINE_TO) {
str += (commands[i].relative ? 'h' : 'H') +
commands[i].x;
// Vertical move to command
} else if(commands[i].type === SVGPathData.VERT_LINE_TO) {
str += (commands[i].relative ? 'v' : 'V') +
commands[i].y;
// Move to command
} else if(commands[i].type === SVGPathData.MOVE_TO) {
str += (commands[i].relative ? 'm' : 'M') +
commands[i].x + WSP + commands[i].y;
// Line to command
} else if(commands[i].type === SVGPathData.LINE_TO) {
str += (commands[i].relative ? 'l' : 'L') +
commands[i].x + WSP + commands[i].y;
// Curve to command
} else if(commands[i].type === SVGPathData.CURVE_TO) {
str += (commands[i].relative ? 'c' : 'C') +
commands[i].x2 + WSP + commands[i].y2 +
WSP + commands[i].x1 + WSP + commands[i].y1 +
WSP + commands[i].x + WSP + commands[i].y;
// Smooth curve to command
} else if(commands[i].type === SVGPathData.SMOOTH_CURVE_TO) {
str += (commands[i].relative ? 's' : 'S') +
commands[i].x2 + WSP + commands[i].y2 +
WSP + commands[i].x + WSP + commands[i].y;
// Quadratic bezier curve to command
} else if(commands[i].type === SVGPathData.QUAD_TO) {
str += (commands[i].relative ? 'q' : 'Q') +
commands[i].x1 + WSP + commands[i].y1 +
WSP + commands[i].x + WSP + commands[i].y;
// Smooth quadratic bezier curve to command
} else if(commands[i].type === SVGPathData.SMOOTH_QUAD_TO) {
str += (commands[i].relative ? 't' : 'T') +
commands[i].x + WSP + commands[i].y;
// Elliptic arc command
} else if(commands[i].type === SVGPathData.ARC) {
str += (commands[i].relative ? 'a' : 'A') +
commands[i].rX + WSP + commands[i].rY +
WSP + commands[i].xRot +
WSP + commands[i].lArcFlag + WSP + commands[i].sweepFlag +
WSP + commands[i].x + WSP + commands[i].y;
// Unkown command
} else {
this.emit('error', new Error('Unexpected command type "' +
commands[i].type + '" at index ' + i + '.'));
}
}
this.push(new Buffer(str, 'utf8'));
done();
};
&nbsp;
module.exports = SVGPathDataEncoder;
&nbsp;</pre></td></tr>
</table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Mon Nov 07 2016 15:21:32 GMT+0100 (CET)
</div>
</div>
<script src="../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../sorter.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,446 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/a2c.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../prettify.css" />
<link rel="stylesheet" href="../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../index.html">all files</a> / <a href="index.html">src/</a> a2c.js
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">94.37% </span>
<span class="quiet">Statements</span>
<span class='fraction'>67/71</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">85.42% </span>
<span class="quiet">Branches</span>
<span class='fraction'>41/48</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>5/5</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">94.29% </span>
<span class="quiet">Lines</span>
<span class='fraction'>66/70</span>
</div>
</div>
</div>
<div class='status-line high'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128</td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">8×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">28×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">28×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">8×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">2×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">8×</span>
<span class="cline-any cline-yes">8×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">8×</span>
<span class="cline-any cline-yes">8×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">8×</span>
<span class="cline-any cline-yes">8×</span>
<span class="cline-any cline-yes">8×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-yes">48×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">4×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">56×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">56×</span>
<span class="cline-any cline-yes">28×</span>
<span class="cline-any cline-yes">28×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">28×</span>
<span class="cline-any cline-yes">28×</span>
<span class="cline-any cline-yes">28×</span>
<span class="cline-any cline-yes">28×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">8×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1×</span>
<span class="cline-any cline-yes">154×</span>
<span class="cline-any cline-yes">28×</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">// Borrowed from https://github.com/PPvG/svg-path/blob/master/lib/Path.js#L208
// that were borrowed from https://github.com/DmitryBaranovskiy/raphael/blob/4d97d4ff5350bb949b88e6d78b877f76ea8b5e24/raphael.js#L2216-L2304
// (MIT licensed; http://raphaeljs.com/license.html).
&nbsp;
module.exports = a2c;
&nbsp;
// LOL
var PI = Math.PI;
var math = Math;
var abs = Math.abs;
var split = "split";
var concat = "concat";
var apply = "apply";
var has = "hasOwnProperty";
&nbsp;
function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
// for more information of where this math came from visit:
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
var _120 = PI * 120 / 180,
rad = PI / 180 * (+angle || 0),
res = [],
xy,
rotate = cacher(function (x, y, rad) {
var X = x * math.cos(rad) - y * math.sin(rad),
Y = x * math.sin(rad) + y * math.cos(rad);
return {x: X, y: Y};
});
if (!recursive) {
xy = rotate(x1, y1, -rad);
x1 = xy.x;
y1 = xy.y;
xy = rotate(x2, y2, -rad);
x2 = xy.x;
y2 = xy.y;
var cos = math.cos(PI / 180 * angle),
sin = math.sin(PI / 180 * angle),
x = (x1 - x2) / 2,
y = (y1 - y2) / 2;
var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
<span class="missing-if-branch" title="if path not taken" >I</span>if (h &gt; 1) {
<span class="cstat-no" title="statement not covered" > h = math.sqrt(h);</span>
<span class="cstat-no" title="statement not covered" > rx = h * rx;</span>
<span class="cstat-no" title="statement not covered" > ry = h * ry;</span>
}
var rx2 = rx * rx,
ry2 = ry * ry,
k = (large_arc_flag == sweep_flag ? -1 : 1) *
math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
cx = k * rx * y / ry + (x1 + x2) / 2,
cy = k * -ry * x / rx + (y1 + y2) / 2,
f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
f2 = math.asin(((y2 - cy) / ry).toFixed(9));
&nbsp;
f1 = x1 &lt; cx ? PI - f1 : f1;
f2 = x2 &lt; cx ? <span class="branch-0 cbranch-no" title="branch not covered" >PI - f2 </span>: f2;
f1 &lt; 0 &amp;&amp; (f1 = PI * 2 + f1);
f2 &lt; 0 &amp;&amp; (<span class="branch-1 cbranch-no" title="branch not covered" >f2 = PI * 2 + f2)</span>;
if (sweep_flag &amp;&amp; f1 &gt; f2) {
f1 = f1 - PI * 2;
}
<span class="missing-if-branch" title="if path not taken" >I</span>if (!sweep_flag &amp;&amp; f2 &gt; f1) {
<span class="cstat-no" title="statement not covered" > f2 = f2 - PI * 2;</span>
}
} else {
f1 = recursive[0];
f2 = recursive[1];
cx = recursive[2];
cy = recursive[3];
}
var df = f2 - f1;
if (abs(df) &gt; _120) {
var f2old = f2,
x2old = x2,
y2old = y2;
f2 = f1 + _120 * (sweep_flag &amp;&amp; f2 &gt; f1 ? 1 : -1);
x2 = cx + rx * math.cos(f2);
y2 = cy + ry * math.sin(f2);
res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
}
df = f2 - f1;
var c1 = math.cos(f1),
s1 = math.sin(f1),
c2 = math.cos(f2),
s2 = math.sin(f2),
t = math.tan(df / 4),
hx = 4 / 3 * rx * t,
hy = 4 / 3 * ry * t,
m1 = [x1, y1],
m2 = [x1 + hx * s1, y1 - hy * c1],
m3 = [x2 + hx * s2, y2 - hy * c2],
m4 = [x2, y2];
m2[0] = 2 * m1[0] - m2[0];
m2[1] = 2 * m1[1] - m2[1];
if (recursive) {
return [m2, m3, m4][concat](res);
} else {
res = [m2, m3, m4][concat](res).join()[split](",");
var newres = [];
for (var i = 0, ii = res.length; i &lt; ii; i++) {
newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
}
return newres;
}
}
function cacher(f, scope, postprocessor) {
function newf() {
var arg = Array.prototype.slice.call(arguments, 0),
args = arg.join("\u2400"),
cache = newf.cache = newf.cache || {},
count = newf.count = newf.count || [];
if (cache[has](args)) {
repush(count, args);
return postprocessor ? <span class="branch-0 cbranch-no" title="branch not covered" >postprocessor(cache[args]) </span>: cache[args];
}
count.length &gt;= 1e3 &amp;&amp; <span class="branch-1 cbranch-no" title="branch not covered" >delete cache[count.shift()];</span>
count.push(args);
cache[args] = f[apply](scope, arg);
return postprocessor ? <span class="branch-0 cbranch-no" title="branch not covered" >postprocessor(cache[args]) </span>: cache[args];
}
return newf;
}
&nbsp;
function repush(array, item) {
for (var i = 0, ii = array.length; i &lt; ii; i++) if (array[i] === item) {
return array.push(array.splice(i, 1)[0]);
}
}
&nbsp;</pre></td></tr>
</table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Mon Nov 07 2016 15:21:32 GMT+0100 (CET)
</div>
</div>
<script src="../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../sorter.js"></script>
</body>
</html>

View File

@ -0,0 +1,145 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../prettify.css" />
<link rel="stylesheet" href="../base.css" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1>
<a href="../index.html">all files</a> src/
</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">98.48% </span>
<span class="quiet">Statements</span>
<span class='fraction'>649/659</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">94.36% </span>
<span class="quiet">Branches</span>
<span class='fraction'>435/461</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">100% </span>
<span class="quiet">Functions</span>
<span class='fraction'>57/57</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">98.47% </span>
<span class="quiet">Lines</span>
<span class='fraction'>645/655</span>
</div>
</div>
</div>
<div class='status-line high'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file high" data-value="SVGPathData.js"><a href="SVGPathData.js.html">SVGPathData.js</a></td>
<td data-value="100" class="pic high"><div class="chart"><div class="cover-fill cover-full" style="width: 100%;"></div><div class="cover-empty" style="width:0%;"></div></div></td>
<td data-value="100" class="pct high">100%</td>
<td data-value="81" class="abs high">81/81</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="6" class="abs high">6/6</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="19" class="abs high">19/19</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="81" class="abs high">81/81</td>
</tr>
<tr>
<td class="file high" data-value="SVGPathDataEncoder.js"><a href="SVGPathDataEncoder.js.html">SVGPathDataEncoder.js</a></td>
<td data-value="100" class="pic high"><div class="chart"><div class="cover-fill cover-full" style="width: 100%;"></div><div class="cover-empty" style="width:0%;"></div></div></td>
<td data-value="100" class="pct high">100%</td>
<td data-value="43" class="abs high">43/43</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="42" class="abs high">42/42</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="2" class="abs high">2/2</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="43" class="abs high">43/43</td>
</tr>
<tr>
<td class="file high" data-value="SVGPathDataParser.js"><a href="SVGPathDataParser.js.html">SVGPathDataParser.js</a></td>
<td data-value="97.8" class="pic high"><div class="chart"><div class="cover-fill" style="width: 97%;"></div><div class="cover-empty" style="width:3%;"></div></div></td>
<td data-value="97.8" class="pct high">97.8%</td>
<td data-value="273" class="abs high">267/273</td>
<td data-value="93.85" class="pct high">93.85%</td>
<td data-value="179" class="abs high">168/179</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="4" class="abs high">4/4</td>
<td data-value="97.8" class="pct high">97.8%</td>
<td data-value="273" class="abs high">267/273</td>
</tr>
<tr>
<td class="file high" data-value="SVGPathDataTransformer.js"><a href="SVGPathDataTransformer.js.html">SVGPathDataTransformer.js</a></td>
<td data-value="100" class="pic high"><div class="chart"><div class="cover-fill cover-full" style="width: 100%;"></div><div class="cover-empty" style="width:0%;"></div></div></td>
<td data-value="100" class="pct high">100%</td>
<td data-value="191" class="abs high">191/191</td>
<td data-value="95.7" class="pct high">95.7%</td>
<td data-value="186" class="abs high">178/186</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="27" class="abs high">27/27</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="188" class="abs high">188/188</td>
</tr>
<tr>
<td class="file high" data-value="a2c.js"><a href="a2c.js.html">a2c.js</a></td>
<td data-value="94.37" class="pic high"><div class="chart"><div class="cover-fill" style="width: 94%;"></div><div class="cover-empty" style="width:6%;"></div></div></td>
<td data-value="94.37" class="pct high">94.37%</td>
<td data-value="71" class="abs high">67/71</td>
<td data-value="85.42" class="pct high">85.42%</td>
<td data-value="48" class="abs high">41/48</td>
<td data-value="100" class="pct high">100%</td>
<td data-value="5" class="abs high">5/5</td>
<td data-value="94.29" class="pct high">94.29%</td>
<td data-value="70" class="abs high">66/70</td>
</tr>
</tbody>
</table>
</div><div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage
generated by <a href="http://istanbul-js.org/" target="_blank">istanbul</a> at Mon Nov 07 2016 15:21:32 GMT+0100 (CET)
</div>
</div>
<script src="../prettify.js"></script>
<script>
window.onload = function () {
if (typeof prettyPrint === 'function') {
prettyPrint();
}
};
</script>
<script src="../sorter.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

69
static/js/ketcher2/node_modules/svg-pathdata/karma.conf.js generated vendored Executable file
View File

@ -0,0 +1,69 @@
// Karma configuration
// Generated on Tue Oct 15 2013 15:27:53 GMT+0200 (CEST)
module.exports = function(config) {
config.set({
// base path, that will be used to resolve files and exclude
basePath: '',
// frameworks to use
frameworks: ['mocha', 'chai'],
// list of files / patterns to load in the browser
files: [
'dist/SVGPathData.js',
'tests/**/*.mocha.js'
],
// list of files to exclude
exclude: [
],
// test results reporter to use
// possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers: ['Chrome', 'Firefox', 'PhantomJS'],
// If browser does not capture in given timeout [ms], kill it
captureTimeout: 60000,
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun: false
});
};

View File

@ -0,0 +1,18 @@
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

View File

@ -0,0 +1,36 @@
# readable-stream
***Node-core v5.8.0 streams for userland*** [![Build Status](https://travis-ci.org/nodejs/readable-stream.svg?branch=master)](https://travis-ci.org/nodejs/readable-stream)
[![NPM](https://nodei.co/npm/readable-stream.png?downloads=true&downloadRank=true)](https://nodei.co/npm/readable-stream/)
[![NPM](https://nodei.co/npm-dl/readable-stream.png?&months=6&height=3)](https://nodei.co/npm/readable-stream/)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/readable-stream.svg)](https://saucelabs.com/u/readable-stream)
```bash
npm install --save readable-stream
```
***Node-core streams for userland***
This package is a mirror of the Streams2 and Streams3 implementations in
Node-core, including [documentation](doc/stream.markdown).
If you want to guarantee a stable streams base, regardless of what version of
Node you, or the users of your libraries are using, use **readable-stream** *only* and avoid the *"stream"* module in Node-core, for background see [this blogpost](http://r.va.gg/2014/06/why-i-dont-use-nodes-core-stream-module.html).
As of version 2.0.0 **readable-stream** uses semantic versioning.
# Streams WG Team Members
* **Chris Dickinson** ([@chrisdickinson](https://github.com/chrisdickinson)) &lt;christopher.s.dickinson@gmail.com&gt;
- Release GPG key: 9554F04D7259F04124DE6B476D5A82AC7E37093B
* **Calvin Metcalf** ([@calvinmetcalf](https://github.com/calvinmetcalf)) &lt;calvin.metcalf@gmail.com&gt;
- Release GPG key: F3EF5F62A87FC27A22E643F714CE4FF5015AA242
* **Rod Vagg** ([@rvagg](https://github.com/rvagg)) &lt;rod@vagg.org&gt;
- Release GPG key: DD8F2338BAE7501E3DD5AC78C273792F7D83545D
* **Sam Newman** ([@sonewman](https://github.com/sonewman)) &lt;newmansam@outlook.com&gt;
* **Mathias Buus** ([@mafintosh](https://github.com/mafintosh)) &lt;mathiasbuus@gmail.com&gt;
* **Domenic Denicola** ([@domenic](https://github.com/domenic)) &lt;d@domenic.me&gt;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,60 @@
# streams WG Meeting 2015-01-30
## Links
* **Google Hangouts Video**: http://www.youtube.com/watch?v=I9nDOSGfwZg
* **GitHub Issue**: https://github.com/iojs/readable-stream/issues/106
* **Original Minutes Google Doc**: https://docs.google.com/document/d/17aTgLnjMXIrfjgNaTUnHQO7m3xgzHR2VXBTmi03Qii4/
## Agenda
Extracted from https://github.com/iojs/readable-stream/labels/wg-agenda prior to meeting.
* adopt a charter [#105](https://github.com/iojs/readable-stream/issues/105)
* release and versioning strategy [#101](https://github.com/iojs/readable-stream/issues/101)
* simpler stream creation [#102](https://github.com/iojs/readable-stream/issues/102)
* proposal: deprecate implicit flowing of streams [#99](https://github.com/iojs/readable-stream/issues/99)
## Minutes
### adopt a charter
* group: +1's all around
### What versioning scheme should be adopted?
* group: +1s 3.0.0
* domenic+group: pulling in patches from other sources where appropriate
* mikeal: version independently, suggesting versions for io.js
* mikeal+domenic: work with TC to notify in advance of changes
simpler stream creation
### streamline creation of streams
* sam: streamline creation of streams
* domenic: nice simple solution posted
but, we lose the opportunity to change the model
may not be backwards incompatible (double check keys)
**action item:** domenic will check
### remove implicit flowing of streams on(data)
* add isFlowing / isPaused
* mikeal: worrying that were documenting polyfill methods confuses users
* domenic: more reflective API is probably good, with warning labels for users
* new section for mad scientists (reflective stream access)
* calvin: name the “third state”
* mikeal: maybe borrow the name from whatwg?
* domenic: were missing the “third state”
* consensus: kind of difficult to name the third state
* mikeal: figure out differences in states / compat
* mathias: always flow on data eliminates third state
* explore what it breaks
**action items:**
* ask isaac for ability to list packages by what public io.js APIs they use (esp. Stream)
* ask rod/build for infrastructure
* **chris**: explore the “flow on data” approach
* add isPaused/isFlowing
* add new docs section
* move isPaused to that section

View File

@ -0,0 +1 @@
module.exports = require("./lib/_stream_duplex.js")

View File

@ -0,0 +1,75 @@
// a duplex stream is just a stream that is both readable and writable.
// Since JS doesn't have multiple prototypal inheritance, this class
// prototypally inherits from Readable, and then parasitically from
// Writable.
'use strict';
/*<replacement>*/
var objectKeys = Object.keys || function (obj) {
var keys = [];
for (var key in obj) {
keys.push(key);
}return keys;
};
/*</replacement>*/
module.exports = Duplex;
/*<replacement>*/
var processNextTick = require('process-nextick-args');
/*</replacement>*/
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
var Readable = require('./_stream_readable');
var Writable = require('./_stream_writable');
util.inherits(Duplex, Readable);
var keys = objectKeys(Writable.prototype);
for (var v = 0; v < keys.length; v++) {
var method = keys[v];
if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method];
}
function Duplex(options) {
if (!(this instanceof Duplex)) return new Duplex(options);
Readable.call(this, options);
Writable.call(this, options);
if (options && options.readable === false) this.readable = false;
if (options && options.writable === false) this.writable = false;
this.allowHalfOpen = true;
if (options && options.allowHalfOpen === false) this.allowHalfOpen = false;
this.once('end', onend);
}
// the no-half-open enforcer
function onend() {
// if we allow half-open state, or if the writable side ended,
// then we're ok.
if (this.allowHalfOpen || this._writableState.ended) return;
// no more data can be written.
// But allow more writes to happen in this tick.
processNextTick(onEndNT, this);
}
function onEndNT(self) {
self.end();
}
function forEach(xs, f) {
for (var i = 0, l = xs.length; i < l; i++) {
f(xs[i], i);
}
}

View File

@ -0,0 +1,26 @@
// a passthrough stream.
// basically just the most minimal sort of Transform stream.
// Every written chunk gets output as-is.
'use strict';
module.exports = PassThrough;
var Transform = require('./_stream_transform');
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
util.inherits(PassThrough, Transform);
function PassThrough(options) {
if (!(this instanceof PassThrough)) return new PassThrough(options);
Transform.call(this, options);
}
PassThrough.prototype._transform = function (chunk, encoding, cb) {
cb(null, chunk);
};

View File

@ -0,0 +1,880 @@
'use strict';
module.exports = Readable;
/*<replacement>*/
var processNextTick = require('process-nextick-args');
/*</replacement>*/
/*<replacement>*/
var isArray = require('isarray');
/*</replacement>*/
/*<replacement>*/
var Buffer = require('buffer').Buffer;
/*</replacement>*/
Readable.ReadableState = ReadableState;
var EE = require('events');
/*<replacement>*/
var EElistenerCount = function (emitter, type) {
return emitter.listeners(type).length;
};
/*</replacement>*/
/*<replacement>*/
var Stream;
(function () {
try {
Stream = require('st' + 'ream');
} catch (_) {} finally {
if (!Stream) Stream = require('events').EventEmitter;
}
})();
/*</replacement>*/
var Buffer = require('buffer').Buffer;
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
/*<replacement>*/
var debugUtil = require('util');
var debug = undefined;
if (debugUtil && debugUtil.debuglog) {
debug = debugUtil.debuglog('stream');
} else {
debug = function () {};
}
/*</replacement>*/
var StringDecoder;
util.inherits(Readable, Stream);
var Duplex;
function ReadableState(options, stream) {
Duplex = Duplex || require('./_stream_duplex');
options = options || {};
// object stream flag. Used to make read(n) ignore n and to
// make all the buffer merging and length checks go away
this.objectMode = !!options.objectMode;
if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.readableObjectMode;
// the point at which it stops calling _read() to fill the buffer
// Note: 0 is a valid value, means "don't call _read preemptively ever"
var hwm = options.highWaterMark;
var defaultHwm = this.objectMode ? 16 : 16 * 1024;
this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm;
// cast to ints.
this.highWaterMark = ~ ~this.highWaterMark;
this.buffer = [];
this.length = 0;
this.pipes = null;
this.pipesCount = 0;
this.flowing = null;
this.ended = false;
this.endEmitted = false;
this.reading = false;
// a flag to be able to tell if the onwrite cb is called immediately,
// or on a later tick. We set this to true at first, because any
// actions that shouldn't happen until "later" should generally also
// not happen before the first write call.
this.sync = true;
// whenever we return null, then we set a flag to say
// that we're awaiting a 'readable' event emission.
this.needReadable = false;
this.emittedReadable = false;
this.readableListening = false;
this.resumeScheduled = false;
// Crypto is kind of old and crusty. Historically, its default string
// encoding is 'binary' so we have to make this configurable.
// Everything else in the universe uses 'utf8', though.
this.defaultEncoding = options.defaultEncoding || 'utf8';
// when piping, we only care about 'readable' events that happen
// after read()ing all the bytes and not getting any pushback.
this.ranOut = false;
// the number of writers that are awaiting a drain event in .pipe()s
this.awaitDrain = 0;
// if true, a maybeReadMore has been scheduled
this.readingMore = false;
this.decoder = null;
this.encoding = null;
if (options.encoding) {
if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder;
this.decoder = new StringDecoder(options.encoding);
this.encoding = options.encoding;
}
}
var Duplex;
function Readable(options) {
Duplex = Duplex || require('./_stream_duplex');
if (!(this instanceof Readable)) return new Readable(options);
this._readableState = new ReadableState(options, this);
// legacy
this.readable = true;
if (options && typeof options.read === 'function') this._read = options.read;
Stream.call(this);
}
// Manually shove something into the read() buffer.
// This returns true if the highWaterMark has not been hit yet,
// similar to how Writable.write() returns true if you should
// write() some more.
Readable.prototype.push = function (chunk, encoding) {
var state = this._readableState;
if (!state.objectMode && typeof chunk === 'string') {
encoding = encoding || state.defaultEncoding;
if (encoding !== state.encoding) {
chunk = new Buffer(chunk, encoding);
encoding = '';
}
}
return readableAddChunk(this, state, chunk, encoding, false);
};
// Unshift should *always* be something directly out of read()
Readable.prototype.unshift = function (chunk) {
var state = this._readableState;
return readableAddChunk(this, state, chunk, '', true);
};
Readable.prototype.isPaused = function () {
return this._readableState.flowing === false;
};
function readableAddChunk(stream, state, chunk, encoding, addToFront) {
var er = chunkInvalid(state, chunk);
if (er) {
stream.emit('error', er);
} else if (chunk === null) {
state.reading = false;
onEofChunk(stream, state);
} else if (state.objectMode || chunk && chunk.length > 0) {
if (state.ended && !addToFront) {
var e = new Error('stream.push() after EOF');
stream.emit('error', e);
} else if (state.endEmitted && addToFront) {
var e = new Error('stream.unshift() after end event');
stream.emit('error', e);
} else {
var skipAdd;
if (state.decoder && !addToFront && !encoding) {
chunk = state.decoder.write(chunk);
skipAdd = !state.objectMode && chunk.length === 0;
}
if (!addToFront) state.reading = false;
// Don't add to the buffer if we've decoded to an empty string chunk and
// we're not in object mode
if (!skipAdd) {
// if we want the data now, just emit it.
if (state.flowing && state.length === 0 && !state.sync) {
stream.emit('data', chunk);
stream.read(0);
} else {
// update the buffer info.
state.length += state.objectMode ? 1 : chunk.length;
if (addToFront) state.buffer.unshift(chunk);else state.buffer.push(chunk);
if (state.needReadable) emitReadable(stream);
}
}
maybeReadMore(stream, state);
}
} else if (!addToFront) {
state.reading = false;
}
return needMoreData(state);
}
// if it's past the high water mark, we can push in some more.
// Also, if we have no data yet, we can stand some
// more bytes. This is to work around cases where hwm=0,
// such as the repl. Also, if the push() triggered a
// readable event, and the user called read(largeNumber) such that
// needReadable was set, then we ought to push more, so that another
// 'readable' event will be triggered.
function needMoreData(state) {
return !state.ended && (state.needReadable || state.length < state.highWaterMark || state.length === 0);
}
// backwards compatibility.
Readable.prototype.setEncoding = function (enc) {
if (!StringDecoder) StringDecoder = require('string_decoder/').StringDecoder;
this._readableState.decoder = new StringDecoder(enc);
this._readableState.encoding = enc;
return this;
};
// Don't raise the hwm > 8MB
var MAX_HWM = 0x800000;
function computeNewHighWaterMark(n) {
if (n >= MAX_HWM) {
n = MAX_HWM;
} else {
// Get the next highest power of 2
n--;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
n++;
}
return n;
}
function howMuchToRead(n, state) {
if (state.length === 0 && state.ended) return 0;
if (state.objectMode) return n === 0 ? 0 : 1;
if (n === null || isNaN(n)) {
// only flow one buffer at a time
if (state.flowing && state.buffer.length) return state.buffer[0].length;else return state.length;
}
if (n <= 0) return 0;
// If we're asking for more than the target buffer level,
// then raise the water mark. Bump up to the next highest
// power of 2, to prevent increasing it excessively in tiny
// amounts.
if (n > state.highWaterMark) state.highWaterMark = computeNewHighWaterMark(n);
// don't have that much. return null, unless we've ended.
if (n > state.length) {
if (!state.ended) {
state.needReadable = true;
return 0;
} else {
return state.length;
}
}
return n;
}
// you can override either this method, or the async _read(n) below.
Readable.prototype.read = function (n) {
debug('read', n);
var state = this._readableState;
var nOrig = n;
if (typeof n !== 'number' || n > 0) state.emittedReadable = false;
// if we're doing read(0) to trigger a readable event, but we
// already have a bunch of data in the buffer, then just trigger
// the 'readable' event and move on.
if (n === 0 && state.needReadable && (state.length >= state.highWaterMark || state.ended)) {
debug('read: emitReadable', state.length, state.ended);
if (state.length === 0 && state.ended) endReadable(this);else emitReadable(this);
return null;
}
n = howMuchToRead(n, state);
// if we've ended, and we're now clear, then finish it up.
if (n === 0 && state.ended) {
if (state.length === 0) endReadable(this);
return null;
}
// All the actual chunk generation logic needs to be
// *below* the call to _read. The reason is that in certain
// synthetic stream cases, such as passthrough streams, _read
// may be a completely synchronous operation which may change
// the state of the read buffer, providing enough data when
// before there was *not* enough.
//
// So, the steps are:
// 1. Figure out what the state of things will be after we do
// a read from the buffer.
//
// 2. If that resulting state will trigger a _read, then call _read.
// Note that this may be asynchronous, or synchronous. Yes, it is
// deeply ugly to write APIs this way, but that still doesn't mean
// that the Readable class should behave improperly, as streams are
// designed to be sync/async agnostic.
// Take note if the _read call is sync or async (ie, if the read call
// has returned yet), so that we know whether or not it's safe to emit
// 'readable' etc.
//
// 3. Actually pull the requested chunks out of the buffer and return.
// if we need a readable event, then we need to do some reading.
var doRead = state.needReadable;
debug('need readable', doRead);
// if we currently have less than the highWaterMark, then also read some
if (state.length === 0 || state.length - n < state.highWaterMark) {
doRead = true;
debug('length less than watermark', doRead);
}
// however, if we've ended, then there's no point, and if we're already
// reading, then it's unnecessary.
if (state.ended || state.reading) {
doRead = false;
debug('reading or ended', doRead);
}
if (doRead) {
debug('do read');
state.reading = true;
state.sync = true;
// if the length is currently zero, then we *need* a readable event.
if (state.length === 0) state.needReadable = true;
// call internal read method
this._read(state.highWaterMark);
state.sync = false;
}
// If _read pushed data synchronously, then `reading` will be false,
// and we need to re-evaluate how much data we can return to the user.
if (doRead && !state.reading) n = howMuchToRead(nOrig, state);
var ret;
if (n > 0) ret = fromList(n, state);else ret = null;
if (ret === null) {
state.needReadable = true;
n = 0;
}
state.length -= n;
// If we have nothing in the buffer, then we want to know
// as soon as we *do* get something into the buffer.
if (state.length === 0 && !state.ended) state.needReadable = true;
// If we tried to read() past the EOF, then emit end on the next tick.
if (nOrig !== n && state.ended && state.length === 0) endReadable(this);
if (ret !== null) this.emit('data', ret);
return ret;
};
function chunkInvalid(state, chunk) {
var er = null;
if (!Buffer.isBuffer(chunk) && typeof chunk !== 'string' && chunk !== null && chunk !== undefined && !state.objectMode) {
er = new TypeError('Invalid non-string/buffer chunk');
}
return er;
}
function onEofChunk(stream, state) {
if (state.ended) return;
if (state.decoder) {
var chunk = state.decoder.end();
if (chunk && chunk.length) {
state.buffer.push(chunk);
state.length += state.objectMode ? 1 : chunk.length;
}
}
state.ended = true;
// emit 'readable' now to make sure it gets picked up.
emitReadable(stream);
}
// Don't emit readable right away in sync mode, because this can trigger
// another read() call => stack overflow. This way, it might trigger
// a nextTick recursion warning, but that's not so bad.
function emitReadable(stream) {
var state = stream._readableState;
state.needReadable = false;
if (!state.emittedReadable) {
debug('emitReadable', state.flowing);
state.emittedReadable = true;
if (state.sync) processNextTick(emitReadable_, stream);else emitReadable_(stream);
}
}
function emitReadable_(stream) {
debug('emit readable');
stream.emit('readable');
flow(stream);
}
// at this point, the user has presumably seen the 'readable' event,
// and called read() to consume some data. that may have triggered
// in turn another _read(n) call, in which case reading = true if
// it's in progress.
// However, if we're not ended, or reading, and the length < hwm,
// then go ahead and try to read some more preemptively.
function maybeReadMore(stream, state) {
if (!state.readingMore) {
state.readingMore = true;
processNextTick(maybeReadMore_, stream, state);
}
}
function maybeReadMore_(stream, state) {
var len = state.length;
while (!state.reading && !state.flowing && !state.ended && state.length < state.highWaterMark) {
debug('maybeReadMore read 0');
stream.read(0);
if (len === state.length)
// didn't get any data, stop spinning.
break;else len = state.length;
}
state.readingMore = false;
}
// abstract method. to be overridden in specific implementation classes.
// call cb(er, data) where data is <= n in length.
// for virtual (non-string, non-buffer) streams, "length" is somewhat
// arbitrary, and perhaps not very meaningful.
Readable.prototype._read = function (n) {
this.emit('error', new Error('not implemented'));
};
Readable.prototype.pipe = function (dest, pipeOpts) {
var src = this;
var state = this._readableState;
switch (state.pipesCount) {
case 0:
state.pipes = dest;
break;
case 1:
state.pipes = [state.pipes, dest];
break;
default:
state.pipes.push(dest);
break;
}
state.pipesCount += 1;
debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts);
var doEnd = (!pipeOpts || pipeOpts.end !== false) && dest !== process.stdout && dest !== process.stderr;
var endFn = doEnd ? onend : cleanup;
if (state.endEmitted) processNextTick(endFn);else src.once('end', endFn);
dest.on('unpipe', onunpipe);
function onunpipe(readable) {
debug('onunpipe');
if (readable === src) {
cleanup();
}
}
function onend() {
debug('onend');
dest.end();
}
// when the dest drains, it reduces the awaitDrain counter
// on the source. This would be more elegant with a .once()
// handler in flow(), but adding and removing repeatedly is
// too slow.
var ondrain = pipeOnDrain(src);
dest.on('drain', ondrain);
var cleanedUp = false;
function cleanup() {
debug('cleanup');
// cleanup event handlers once the pipe is broken
dest.removeListener('close', onclose);
dest.removeListener('finish', onfinish);
dest.removeListener('drain', ondrain);
dest.removeListener('error', onerror);
dest.removeListener('unpipe', onunpipe);
src.removeListener('end', onend);
src.removeListener('end', cleanup);
src.removeListener('data', ondata);
cleanedUp = true;
// if the reader is waiting for a drain event from this
// specific writer, then it would cause it to never start
// flowing again.
// So, if this is awaiting a drain, then we just call it now.
// If we don't know, then assume that we are waiting for one.
if (state.awaitDrain && (!dest._writableState || dest._writableState.needDrain)) ondrain();
}
src.on('data', ondata);
function ondata(chunk) {
debug('ondata');
var ret = dest.write(chunk);
if (false === ret) {
// If the user unpiped during `dest.write()`, it is possible
// to get stuck in a permanently paused state if that write
// also returned false.
if (state.pipesCount === 1 && state.pipes[0] === dest && src.listenerCount('data') === 1 && !cleanedUp) {
debug('false write response, pause', src._readableState.awaitDrain);
src._readableState.awaitDrain++;
}
src.pause();
}
}
// if the dest has an error, then stop piping into it.
// however, don't suppress the throwing behavior for this.
function onerror(er) {
debug('onerror', er);
unpipe();
dest.removeListener('error', onerror);
if (EElistenerCount(dest, 'error') === 0) dest.emit('error', er);
}
// This is a brutally ugly hack to make sure that our error handler
// is attached before any userland ones. NEVER DO THIS.
if (!dest._events || !dest._events.error) dest.on('error', onerror);else if (isArray(dest._events.error)) dest._events.error.unshift(onerror);else dest._events.error = [onerror, dest._events.error];
// Both close and finish should trigger unpipe, but only once.
function onclose() {
dest.removeListener('finish', onfinish);
unpipe();
}
dest.once('close', onclose);
function onfinish() {
debug('onfinish');
dest.removeListener('close', onclose);
unpipe();
}
dest.once('finish', onfinish);
function unpipe() {
debug('unpipe');
src.unpipe(dest);
}
// tell the dest that it's being piped to
dest.emit('pipe', src);
// start the flow if it hasn't been started already.
if (!state.flowing) {
debug('pipe resume');
src.resume();
}
return dest;
};
function pipeOnDrain(src) {
return function () {
var state = src._readableState;
debug('pipeOnDrain', state.awaitDrain);
if (state.awaitDrain) state.awaitDrain--;
if (state.awaitDrain === 0 && EElistenerCount(src, 'data')) {
state.flowing = true;
flow(src);
}
};
}
Readable.prototype.unpipe = function (dest) {
var state = this._readableState;
// if we're not piping anywhere, then do nothing.
if (state.pipesCount === 0) return this;
// just one destination. most common case.
if (state.pipesCount === 1) {
// passed in one, but it's not the right one.
if (dest && dest !== state.pipes) return this;
if (!dest) dest = state.pipes;
// got a match.
state.pipes = null;
state.pipesCount = 0;
state.flowing = false;
if (dest) dest.emit('unpipe', this);
return this;
}
// slow case. multiple pipe destinations.
if (!dest) {
// remove all.
var dests = state.pipes;
var len = state.pipesCount;
state.pipes = null;
state.pipesCount = 0;
state.flowing = false;
for (var _i = 0; _i < len; _i++) {
dests[_i].emit('unpipe', this);
}return this;
}
// try to find the right one.
var i = indexOf(state.pipes, dest);
if (i === -1) return this;
state.pipes.splice(i, 1);
state.pipesCount -= 1;
if (state.pipesCount === 1) state.pipes = state.pipes[0];
dest.emit('unpipe', this);
return this;
};
// set up data events if they are asked for
// Ensure readable listeners eventually get something
Readable.prototype.on = function (ev, fn) {
var res = Stream.prototype.on.call(this, ev, fn);
// If listening to data, and it has not explicitly been paused,
// then call resume to start the flow of data on the next tick.
if (ev === 'data' && false !== this._readableState.flowing) {
this.resume();
}
if (ev === 'readable' && !this._readableState.endEmitted) {
var state = this._readableState;
if (!state.readableListening) {
state.readableListening = true;
state.emittedReadable = false;
state.needReadable = true;
if (!state.reading) {
processNextTick(nReadingNextTick, this);
} else if (state.length) {
emitReadable(this, state);
}
}
}
return res;
};
Readable.prototype.addListener = Readable.prototype.on;
function nReadingNextTick(self) {
debug('readable nexttick read 0');
self.read(0);
}
// pause() and resume() are remnants of the legacy readable stream API
// If the user uses them, then switch into old mode.
Readable.prototype.resume = function () {
var state = this._readableState;
if (!state.flowing) {
debug('resume');
state.flowing = true;
resume(this, state);
}
return this;
};
function resume(stream, state) {
if (!state.resumeScheduled) {
state.resumeScheduled = true;
processNextTick(resume_, stream, state);
}
}
function resume_(stream, state) {
if (!state.reading) {
debug('resume read 0');
stream.read(0);
}
state.resumeScheduled = false;
stream.emit('resume');
flow(stream);
if (state.flowing && !state.reading) stream.read(0);
}
Readable.prototype.pause = function () {
debug('call pause flowing=%j', this._readableState.flowing);
if (false !== this._readableState.flowing) {
debug('pause');
this._readableState.flowing = false;
this.emit('pause');
}
return this;
};
function flow(stream) {
var state = stream._readableState;
debug('flow', state.flowing);
if (state.flowing) {
do {
var chunk = stream.read();
} while (null !== chunk && state.flowing);
}
}
// wrap an old-style stream as the async data source.
// This is *not* part of the readable stream interface.
// It is an ugly unfortunate mess of history.
Readable.prototype.wrap = function (stream) {
var state = this._readableState;
var paused = false;
var self = this;
stream.on('end', function () {
debug('wrapped end');
if (state.decoder && !state.ended) {
var chunk = state.decoder.end();
if (chunk && chunk.length) self.push(chunk);
}
self.push(null);
});
stream.on('data', function (chunk) {
debug('wrapped data');
if (state.decoder) chunk = state.decoder.write(chunk);
// don't skip over falsy values in objectMode
if (state.objectMode && (chunk === null || chunk === undefined)) return;else if (!state.objectMode && (!chunk || !chunk.length)) return;
var ret = self.push(chunk);
if (!ret) {
paused = true;
stream.pause();
}
});
// proxy all the other methods.
// important when wrapping filters and duplexes.
for (var i in stream) {
if (this[i] === undefined && typeof stream[i] === 'function') {
this[i] = function (method) {
return function () {
return stream[method].apply(stream, arguments);
};
}(i);
}
}
// proxy certain important events.
var events = ['error', 'close', 'destroy', 'pause', 'resume'];
forEach(events, function (ev) {
stream.on(ev, self.emit.bind(self, ev));
});
// when we try to consume some more bytes, simply unpause the
// underlying stream.
self._read = function (n) {
debug('wrapped _read', n);
if (paused) {
paused = false;
stream.resume();
}
};
return self;
};
// exposed for testing purposes only.
Readable._fromList = fromList;
// Pluck off n bytes from an array of buffers.
// Length is the combined lengths of all the buffers in the list.
function fromList(n, state) {
var list = state.buffer;
var length = state.length;
var stringMode = !!state.decoder;
var objectMode = !!state.objectMode;
var ret;
// nothing in the list, definitely empty.
if (list.length === 0) return null;
if (length === 0) ret = null;else if (objectMode) ret = list.shift();else if (!n || n >= length) {
// read it all, truncate the array.
if (stringMode) ret = list.join('');else if (list.length === 1) ret = list[0];else ret = Buffer.concat(list, length);
list.length = 0;
} else {
// read just some of it.
if (n < list[0].length) {
// just take a part of the first list item.
// slice is the same for buffers and strings.
var buf = list[0];
ret = buf.slice(0, n);
list[0] = buf.slice(n);
} else if (n === list[0].length) {
// first list is a perfect match
ret = list.shift();
} else {
// complex case.
// we have enough to cover it, but it spans past the first buffer.
if (stringMode) ret = '';else ret = new Buffer(n);
var c = 0;
for (var i = 0, l = list.length; i < l && c < n; i++) {
var buf = list[0];
var cpy = Math.min(n - c, buf.length);
if (stringMode) ret += buf.slice(0, cpy);else buf.copy(ret, c, 0, cpy);
if (cpy < buf.length) list[0] = buf.slice(cpy);else list.shift();
c += cpy;
}
}
}
return ret;
}
function endReadable(stream) {
var state = stream._readableState;
// If we get here before consuming all the bytes, then that is a
// bug in node. Should never happen.
if (state.length > 0) throw new Error('endReadable called on non-empty stream');
if (!state.endEmitted) {
state.ended = true;
processNextTick(endReadableNT, state, stream);
}
}
function endReadableNT(state, stream) {
// Check that we didn't get one last unshift.
if (!state.endEmitted && state.length === 0) {
state.endEmitted = true;
stream.readable = false;
stream.emit('end');
}
}
function forEach(xs, f) {
for (var i = 0, l = xs.length; i < l; i++) {
f(xs[i], i);
}
}
function indexOf(xs, x) {
for (var i = 0, l = xs.length; i < l; i++) {
if (xs[i] === x) return i;
}
return -1;
}

View File

@ -0,0 +1,180 @@
// a transform stream is a readable/writable stream where you do
// something with the data. Sometimes it's called a "filter",
// but that's not a great name for it, since that implies a thing where
// some bits pass through, and others are simply ignored. (That would
// be a valid example of a transform, of course.)
//
// While the output is causally related to the input, it's not a
// necessarily symmetric or synchronous transformation. For example,
// a zlib stream might take multiple plain-text writes(), and then
// emit a single compressed chunk some time in the future.
//
// Here's how this works:
//
// The Transform stream has all the aspects of the readable and writable
// stream classes. When you write(chunk), that calls _write(chunk,cb)
// internally, and returns false if there's a lot of pending writes
// buffered up. When you call read(), that calls _read(n) until
// there's enough pending readable data buffered up.
//
// In a transform stream, the written data is placed in a buffer. When
// _read(n) is called, it transforms the queued up data, calling the
// buffered _write cb's as it consumes chunks. If consuming a single
// written chunk would result in multiple output chunks, then the first
// outputted bit calls the readcb, and subsequent chunks just go into
// the read buffer, and will cause it to emit 'readable' if necessary.
//
// This way, back-pressure is actually determined by the reading side,
// since _read has to be called to start processing a new chunk. However,
// a pathological inflate type of transform can cause excessive buffering
// here. For example, imagine a stream where every byte of input is
// interpreted as an integer from 0-255, and then results in that many
// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in
// 1kb of data being output. In this case, you could write a very small
// amount of input, and end up with a very large amount of output. In
// such a pathological inflating mechanism, there'd be no way to tell
// the system to stop doing the transform. A single 4MB write could
// cause the system to run out of memory.
//
// However, even in such a pathological case, only a single written chunk
// would be consumed, and then the rest would wait (un-transformed) until
// the results of the previous transformed chunk were consumed.
'use strict';
module.exports = Transform;
var Duplex = require('./_stream_duplex');
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
util.inherits(Transform, Duplex);
function TransformState(stream) {
this.afterTransform = function (er, data) {
return afterTransform(stream, er, data);
};
this.needTransform = false;
this.transforming = false;
this.writecb = null;
this.writechunk = null;
this.writeencoding = null;
}
function afterTransform(stream, er, data) {
var ts = stream._transformState;
ts.transforming = false;
var cb = ts.writecb;
if (!cb) return stream.emit('error', new Error('no writecb in Transform class'));
ts.writechunk = null;
ts.writecb = null;
if (data !== null && data !== undefined) stream.push(data);
cb(er);
var rs = stream._readableState;
rs.reading = false;
if (rs.needReadable || rs.length < rs.highWaterMark) {
stream._read(rs.highWaterMark);
}
}
function Transform(options) {
if (!(this instanceof Transform)) return new Transform(options);
Duplex.call(this, options);
this._transformState = new TransformState(this);
// when the writable side finishes, then flush out anything remaining.
var stream = this;
// start out asking for a readable event once data is transformed.
this._readableState.needReadable = true;
// we have implemented the _read method, and done the other things
// that Readable wants before the first _read call, so unset the
// sync guard flag.
this._readableState.sync = false;
if (options) {
if (typeof options.transform === 'function') this._transform = options.transform;
if (typeof options.flush === 'function') this._flush = options.flush;
}
this.once('prefinish', function () {
if (typeof this._flush === 'function') this._flush(function (er) {
done(stream, er);
});else done(stream);
});
}
Transform.prototype.push = function (chunk, encoding) {
this._transformState.needTransform = false;
return Duplex.prototype.push.call(this, chunk, encoding);
};
// This is the part where you do stuff!
// override this function in implementation classes.
// 'chunk' is an input chunk.
//
// Call `push(newChunk)` to pass along transformed output
// to the readable side. You may call 'push' zero or more times.
//
// Call `cb(err)` when you are done with this chunk. If you pass
// an error, then that'll put the hurt on the whole operation. If you
// never call cb(), then you'll never get another chunk.
Transform.prototype._transform = function (chunk, encoding, cb) {
throw new Error('not implemented');
};
Transform.prototype._write = function (chunk, encoding, cb) {
var ts = this._transformState;
ts.writecb = cb;
ts.writechunk = chunk;
ts.writeencoding = encoding;
if (!ts.transforming) {
var rs = this._readableState;
if (ts.needTransform || rs.needReadable || rs.length < rs.highWaterMark) this._read(rs.highWaterMark);
}
};
// Doesn't matter what the args are here.
// _transform does all the work.
// That we got here means that the readable side wants more data.
Transform.prototype._read = function (n) {
var ts = this._transformState;
if (ts.writechunk !== null && ts.writecb && !ts.transforming) {
ts.transforming = true;
this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
} else {
// mark that we need a transform, so that any data that comes in
// will get processed, now that we've asked for it.
ts.needTransform = true;
}
};
function done(stream, er) {
if (er) return stream.emit('error', er);
// if there's nothing in the write buffer, then that means
// that nothing more will ever be provided
var ws = stream._writableState;
var ts = stream._transformState;
if (ws.length) throw new Error('calling transform done when ws.length != 0');
if (ts.transforming) throw new Error('calling transform done when still transforming');
return stream.push(null);
}

View File

@ -0,0 +1,516 @@
// A bit simpler than readable streams.
// Implement an async ._write(chunk, encoding, cb), and it'll handle all
// the drain event emission and buffering.
'use strict';
module.exports = Writable;
/*<replacement>*/
var processNextTick = require('process-nextick-args');
/*</replacement>*/
/*<replacement>*/
var asyncWrite = !process.browser && ['v0.10', 'v0.9.'].indexOf(process.version.slice(0, 5)) > -1 ? setImmediate : processNextTick;
/*</replacement>*/
/*<replacement>*/
var Buffer = require('buffer').Buffer;
/*</replacement>*/
Writable.WritableState = WritableState;
/*<replacement>*/
var util = require('core-util-is');
util.inherits = require('inherits');
/*</replacement>*/
/*<replacement>*/
var internalUtil = {
deprecate: require('util-deprecate')
};
/*</replacement>*/
/*<replacement>*/
var Stream;
(function () {
try {
Stream = require('st' + 'ream');
} catch (_) {} finally {
if (!Stream) Stream = require('events').EventEmitter;
}
})();
/*</replacement>*/
var Buffer = require('buffer').Buffer;
util.inherits(Writable, Stream);
function nop() {}
function WriteReq(chunk, encoding, cb) {
this.chunk = chunk;
this.encoding = encoding;
this.callback = cb;
this.next = null;
}
var Duplex;
function WritableState(options, stream) {
Duplex = Duplex || require('./_stream_duplex');
options = options || {};
// object stream flag to indicate whether or not this stream
// contains buffers or objects.
this.objectMode = !!options.objectMode;
if (stream instanceof Duplex) this.objectMode = this.objectMode || !!options.writableObjectMode;
// the point at which write() starts returning false
// Note: 0 is a valid value, means that we always return false if
// the entire buffer is not flushed immediately on write()
var hwm = options.highWaterMark;
var defaultHwm = this.objectMode ? 16 : 16 * 1024;
this.highWaterMark = hwm || hwm === 0 ? hwm : defaultHwm;
// cast to ints.
this.highWaterMark = ~ ~this.highWaterMark;
this.needDrain = false;
// at the start of calling end()
this.ending = false;
// when end() has been called, and returned
this.ended = false;
// when 'finish' is emitted
this.finished = false;
// should we decode strings into buffers before passing to _write?
// this is here so that some node-core streams can optimize string
// handling at a lower level.
var noDecode = options.decodeStrings === false;
this.decodeStrings = !noDecode;
// Crypto is kind of old and crusty. Historically, its default string
// encoding is 'binary' so we have to make this configurable.
// Everything else in the universe uses 'utf8', though.
this.defaultEncoding = options.defaultEncoding || 'utf8';
// not an actual buffer we keep track of, but a measurement
// of how much we're waiting to get pushed to some underlying
// socket or file.
this.length = 0;
// a flag to see when we're in the middle of a write.
this.writing = false;
// when true all writes will be buffered until .uncork() call
this.corked = 0;
// a flag to be able to tell if the onwrite cb is called immediately,
// or on a later tick. We set this to true at first, because any
// actions that shouldn't happen until "later" should generally also
// not happen before the first write call.
this.sync = true;
// a flag to know if we're processing previously buffered items, which
// may call the _write() callback in the same tick, so that we don't
// end up in an overlapped onwrite situation.
this.bufferProcessing = false;
// the callback that's passed to _write(chunk,cb)
this.onwrite = function (er) {
onwrite(stream, er);
};
// the callback that the user supplies to write(chunk,encoding,cb)
this.writecb = null;
// the amount that is being written when _write is called.
this.writelen = 0;
this.bufferedRequest = null;
this.lastBufferedRequest = null;
// number of pending user-supplied write callbacks
// this must be 0 before 'finish' can be emitted
this.pendingcb = 0;
// emit prefinish if the only thing we're waiting for is _write cbs
// This is relevant for synchronous Transform streams
this.prefinished = false;
// True if the error was already emitted and should not be thrown again
this.errorEmitted = false;
// count buffered requests
this.bufferedRequestCount = 0;
// create the two objects needed to store the corked requests
// they are not a linked list, as no new elements are inserted in there
this.corkedRequestsFree = new CorkedRequest(this);
this.corkedRequestsFree.next = new CorkedRequest(this);
}
WritableState.prototype.getBuffer = function writableStateGetBuffer() {
var current = this.bufferedRequest;
var out = [];
while (current) {
out.push(current);
current = current.next;
}
return out;
};
(function () {
try {
Object.defineProperty(WritableState.prototype, 'buffer', {
get: internalUtil.deprecate(function () {
return this.getBuffer();
}, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + 'instead.')
});
} catch (_) {}
})();
var Duplex;
function Writable(options) {
Duplex = Duplex || require('./_stream_duplex');
// Writable ctor is applied to Duplexes, though they're not
// instanceof Writable, they're instanceof Readable.
if (!(this instanceof Writable) && !(this instanceof Duplex)) return new Writable(options);
this._writableState = new WritableState(options, this);
// legacy.
this.writable = true;
if (options) {
if (typeof options.write === 'function') this._write = options.write;
if (typeof options.writev === 'function') this._writev = options.writev;
}
Stream.call(this);
}
// Otherwise people can pipe Writable streams, which is just wrong.
Writable.prototype.pipe = function () {
this.emit('error', new Error('Cannot pipe. Not readable.'));
};
function writeAfterEnd(stream, cb) {
var er = new Error('write after end');
// TODO: defer error events consistently everywhere, not just the cb
stream.emit('error', er);
processNextTick(cb, er);
}
// If we get something that is not a buffer, string, null, or undefined,
// and we're not in objectMode, then that's an error.
// Otherwise stream chunks are all considered to be of length=1, and the
// watermarks determine how many objects to keep in the buffer, rather than
// how many bytes or characters.
function validChunk(stream, state, chunk, cb) {
var valid = true;
if (!Buffer.isBuffer(chunk) && typeof chunk !== 'string' && chunk !== null && chunk !== undefined && !state.objectMode) {
var er = new TypeError('Invalid non-string/buffer chunk');
stream.emit('error', er);
processNextTick(cb, er);
valid = false;
}
return valid;
}
Writable.prototype.write = function (chunk, encoding, cb) {
var state = this._writableState;
var ret = false;
if (typeof encoding === 'function') {
cb = encoding;
encoding = null;
}
if (Buffer.isBuffer(chunk)) encoding = 'buffer';else if (!encoding) encoding = state.defaultEncoding;
if (typeof cb !== 'function') cb = nop;
if (state.ended) writeAfterEnd(this, cb);else if (validChunk(this, state, chunk, cb)) {
state.pendingcb++;
ret = writeOrBuffer(this, state, chunk, encoding, cb);
}
return ret;
};
Writable.prototype.cork = function () {
var state = this._writableState;
state.corked++;
};
Writable.prototype.uncork = function () {
var state = this._writableState;
if (state.corked) {
state.corked--;
if (!state.writing && !state.corked && !state.finished && !state.bufferProcessing && state.bufferedRequest) clearBuffer(this, state);
}
};
Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) {
// node::ParseEncoding() requires lower case.
if (typeof encoding === 'string') encoding = encoding.toLowerCase();
if (!(['hex', 'utf8', 'utf-8', 'ascii', 'binary', 'base64', 'ucs2', 'ucs-2', 'utf16le', 'utf-16le', 'raw'].indexOf((encoding + '').toLowerCase()) > -1)) throw new TypeError('Unknown encoding: ' + encoding);
this._writableState.defaultEncoding = encoding;
};
function decodeChunk(state, chunk, encoding) {
if (!state.objectMode && state.decodeStrings !== false && typeof chunk === 'string') {
chunk = new Buffer(chunk, encoding);
}
return chunk;
}
// if we're already writing something, then just put this
// in the queue, and wait our turn. Otherwise, call _write
// If we return false, then we need a drain event, so set that flag.
function writeOrBuffer(stream, state, chunk, encoding, cb) {
chunk = decodeChunk(state, chunk, encoding);
if (Buffer.isBuffer(chunk)) encoding = 'buffer';
var len = state.objectMode ? 1 : chunk.length;
state.length += len;
var ret = state.length < state.highWaterMark;
// we must ensure that previous needDrain will not be reset to false.
if (!ret) state.needDrain = true;
if (state.writing || state.corked) {
var last = state.lastBufferedRequest;
state.lastBufferedRequest = new WriteReq(chunk, encoding, cb);
if (last) {
last.next = state.lastBufferedRequest;
} else {
state.bufferedRequest = state.lastBufferedRequest;
}
state.bufferedRequestCount += 1;
} else {
doWrite(stream, state, false, len, chunk, encoding, cb);
}
return ret;
}
function doWrite(stream, state, writev, len, chunk, encoding, cb) {
state.writelen = len;
state.writecb = cb;
state.writing = true;
state.sync = true;
if (writev) stream._writev(chunk, state.onwrite);else stream._write(chunk, encoding, state.onwrite);
state.sync = false;
}
function onwriteError(stream, state, sync, er, cb) {
--state.pendingcb;
if (sync) processNextTick(cb, er);else cb(er);
stream._writableState.errorEmitted = true;
stream.emit('error', er);
}
function onwriteStateUpdate(state) {
state.writing = false;
state.writecb = null;
state.length -= state.writelen;
state.writelen = 0;
}
function onwrite(stream, er) {
var state = stream._writableState;
var sync = state.sync;
var cb = state.writecb;
onwriteStateUpdate(state);
if (er) onwriteError(stream, state, sync, er, cb);else {
// Check if we're actually ready to finish, but don't emit yet
var finished = needFinish(state);
if (!finished && !state.corked && !state.bufferProcessing && state.bufferedRequest) {
clearBuffer(stream, state);
}
if (sync) {
/*<replacement>*/
asyncWrite(afterWrite, stream, state, finished, cb);
/*</replacement>*/
} else {
afterWrite(stream, state, finished, cb);
}
}
}
function afterWrite(stream, state, finished, cb) {
if (!finished) onwriteDrain(stream, state);
state.pendingcb--;
cb();
finishMaybe(stream, state);
}
// Must force callback to be called on nextTick, so that we don't
// emit 'drain' before the write() consumer gets the 'false' return
// value, and has a chance to attach a 'drain' listener.
function onwriteDrain(stream, state) {
if (state.length === 0 && state.needDrain) {
state.needDrain = false;
stream.emit('drain');
}
}
// if there's something in the buffer waiting, then process it
function clearBuffer(stream, state) {
state.bufferProcessing = true;
var entry = state.bufferedRequest;
if (stream._writev && entry && entry.next) {
// Fast case, write everything using _writev()
var l = state.bufferedRequestCount;
var buffer = new Array(l);
var holder = state.corkedRequestsFree;
holder.entry = entry;
var count = 0;
while (entry) {
buffer[count] = entry;
entry = entry.next;
count += 1;
}
doWrite(stream, state, true, state.length, buffer, '', holder.finish);
// doWrite is always async, defer these to save a bit of time
// as the hot path ends with doWrite
state.pendingcb++;
state.lastBufferedRequest = null;
state.corkedRequestsFree = holder.next;
holder.next = null;
} else {
// Slow case, write chunks one-by-one
while (entry) {
var chunk = entry.chunk;
var encoding = entry.encoding;
var cb = entry.callback;
var len = state.objectMode ? 1 : chunk.length;
doWrite(stream, state, false, len, chunk, encoding, cb);
entry = entry.next;
// if we didn't call the onwrite immediately, then
// it means that we need to wait until it does.
// also, that means that the chunk and cb are currently
// being processed, so move the buffer counter past them.
if (state.writing) {
break;
}
}
if (entry === null) state.lastBufferedRequest = null;
}
state.bufferedRequestCount = 0;
state.bufferedRequest = entry;
state.bufferProcessing = false;
}
Writable.prototype._write = function (chunk, encoding, cb) {
cb(new Error('not implemented'));
};
Writable.prototype._writev = null;
Writable.prototype.end = function (chunk, encoding, cb) {
var state = this._writableState;
if (typeof chunk === 'function') {
cb = chunk;
chunk = null;
encoding = null;
} else if (typeof encoding === 'function') {
cb = encoding;
encoding = null;
}
if (chunk !== null && chunk !== undefined) this.write(chunk, encoding);
// .end() fully uncorks
if (state.corked) {
state.corked = 1;
this.uncork();
}
// ignore unnecessary end() calls.
if (!state.ending && !state.finished) endWritable(this, state, cb);
};
function needFinish(state) {
return state.ending && state.length === 0 && state.bufferedRequest === null && !state.finished && !state.writing;
}
function prefinish(stream, state) {
if (!state.prefinished) {
state.prefinished = true;
stream.emit('prefinish');
}
}
function finishMaybe(stream, state) {
var need = needFinish(state);
if (need) {
if (state.pendingcb === 0) {
prefinish(stream, state);
state.finished = true;
stream.emit('finish');
} else {
prefinish(stream, state);
}
}
return need;
}
function endWritable(stream, state, cb) {
state.ending = true;
finishMaybe(stream, state);
if (cb) {
if (state.finished) processNextTick(cb);else stream.once('finish', cb);
}
state.ended = true;
stream.writable = false;
}
// It seems a linked list but it is not
// there will be only 2 of these for each stream
function CorkedRequest(state) {
var _this = this;
this.next = null;
this.entry = null;
this.finish = function (err) {
var entry = _this.entry;
_this.entry = null;
while (entry) {
var cb = entry.callback;
state.pendingcb--;
cb(err);
entry = entry.next;
}
if (state.corkedRequestsFree) {
state.corkedRequestsFree.next = _this;
} else {
state.corkedRequestsFree = _this;
}
};
}

View File

@ -0,0 +1,66 @@
{
"_from": "readable-stream@~2.0.4",
"_id": "readable-stream@2.0.6",
"_inBundle": false,
"_integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
"_location": "/svg-pathdata/readable-stream",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "readable-stream@~2.0.4",
"name": "readable-stream",
"escapedName": "readable-stream",
"rawSpec": "~2.0.4",
"saveSpec": null,
"fetchSpec": "~2.0.4"
},
"_requiredBy": [
"/svg-pathdata"
],
"_resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
"_shasum": "8f90341e68a53ccc928788dacfcd11b36eb9b78e",
"_spec": "readable-stream@~2.0.4",
"_where": "/home/manfred/enviPath/ketcher2/ketcher/node_modules/svg-pathdata",
"browser": {
"util": false
},
"bugs": {
"url": "https://github.com/nodejs/readable-stream/issues"
},
"bundleDependencies": false,
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "~1.0.0",
"process-nextick-args": "~1.0.6",
"string_decoder": "~0.10.x",
"util-deprecate": "~1.0.1"
},
"deprecated": false,
"description": "Streams3, a user-land copy of the stream library from Node.js",
"devDependencies": {
"tap": "~0.2.6",
"tape": "~4.5.1",
"zuul": "~3.9.0"
},
"homepage": "https://github.com/nodejs/readable-stream#readme",
"keywords": [
"readable",
"stream",
"pipe"
],
"license": "MIT",
"main": "readable.js",
"name": "readable-stream",
"repository": {
"type": "git",
"url": "git://github.com/nodejs/readable-stream.git"
},
"scripts": {
"browser": "npm run write-zuul && zuul -- test/browser.js",
"test": "tap test/parallel/*.js test/ours/*.js",
"write-zuul": "printf \"ui: tape\nbrowsers:\n - name: $BROWSER_NAME\n version: $BROWSER_VERSION\n\">.zuul.yml"
},
"version": "2.0.6"
}

View File

@ -0,0 +1 @@
module.exports = require("./lib/_stream_passthrough.js")

View File

@ -0,0 +1,12 @@
var Stream = (function (){
try {
return require('st' + 'ream'); // hack to fix a circular dependency issue when used with browserify
} catch(_){}
}());
exports = module.exports = require('./lib/_stream_readable.js');
exports.Stream = Stream || exports;
exports.Readable = exports;
exports.Writable = require('./lib/_stream_writable.js');
exports.Duplex = require('./lib/_stream_duplex.js');
exports.Transform = require('./lib/_stream_transform.js');
exports.PassThrough = require('./lib/_stream_passthrough.js');

View File

@ -0,0 +1 @@
module.exports = require("./lib/_stream_transform.js")

View File

@ -0,0 +1 @@
module.exports = require("./lib/_stream_writable.js")

View File

@ -0,0 +1,87 @@
{
"_from": "svg-pathdata@^1.0.4",
"_id": "svg-pathdata@1.0.4",
"_inBundle": false,
"_integrity": "sha1-emgTQqrH7/2NUq+6eZmRDJ2juVk=",
"_location": "/svg-pathdata",
"_phantomChildren": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"string_decoder": "0.10.31",
"util-deprecate": "1.0.2"
},
"_requested": {
"type": "range",
"registry": true,
"raw": "svg-pathdata@^1.0.4",
"name": "svg-pathdata",
"escapedName": "svg-pathdata",
"rawSpec": "^1.0.4",
"saveSpec": null,
"fetchSpec": "^1.0.4"
},
"_requiredBy": [
"/svgicons2svgfont"
],
"_resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-1.0.4.tgz",
"_shasum": "7a681342aac7effd8d52afba7999910c9da3b959",
"_spec": "svg-pathdata@^1.0.4",
"_where": "/home/manfred/enviPath/ketcher2/ketcher/node_modules/svgicons2svgfont",
"author": {
"name": "Nicolas Froidure"
},
"bugs": {
"url": "https://github.com/nfroidure/SVGPathData/issues"
},
"bundleDependencies": false,
"dependencies": {
"readable-stream": "~2.0.4"
},
"deprecated": false,
"description": "Parse, transform and encode SVG Path Data.",
"devDependencies": {
"browserify": "^12.0.1",
"chai": "^3.4.1",
"coveralls": "^2.11.4",
"eslint": "^1.10.0",
"eslint-config-simplifield": "^1.1.0",
"istanbul": "^0.4.0",
"mocha": "^2.3.4",
"mocha-lcov-reporter": "^1.0.0",
"rimraf": "^2.4.4"
},
"engines": {
"node": ">= 0.10.0"
},
"homepage": "https://github.com/nfroidure/SVGPathData#readme",
"keywords": [
"svg",
"path",
"data",
"parser",
"encoder",
"transformer",
"reader",
"writer",
"stream"
],
"license": "MIT",
"main": "src/SVGPathData.js",
"name": "svg-pathdata",
"repository": {
"type": "git",
"url": "git+https://github.com/nfroidure/SVGPathData.git"
},
"scripts": {
"build": "browserify src/SVGPathData.js -o dist/SVGPathData.js -s SVGPathData",
"cli": "env NPM_RUN_CLI=1",
"cover": "istanbul cover --report html _mocha -- tests/*.mocha.js -R spec -t 5000",
"coveralls": "istanbul cover _mocha --report lcovonly -- tests/*.mocha.js -R spec -t 5000 && coveralls < ./coverage/lcov.info && rimraf ./coverage",
"lint": "eslint src/*.js tests/*.js",
"preversion": "npm test",
"test": "mocha tests/*.mocha.js"
},
"version": "1.0.4"
}

View File

@ -0,0 +1,151 @@
'use strict';
function SVGPathData(content) {
this.commands = SVGPathData.parse(content);
}
SVGPathData.prototype.encode = function() {
return SVGPathData.encode(this.commands);
};
SVGPathData.prototype.round = function() {
return this.transform.apply(this, [SVGPathData.Transformer.ROUND].concat(
[].slice.call(arguments, 0)));
};
SVGPathData.prototype.toAbs = function() {
return this.transform(SVGPathData.Transformer.TO_ABS);
};
SVGPathData.prototype.toRel = function() {
return this.transform(SVGPathData.Transformer.TO_REL);
};
SVGPathData.prototype.translate = function() {
return this.transform.apply(this, [SVGPathData.Transformer.TRANSLATE].concat(
[].slice.call(arguments, 0)));
};
SVGPathData.prototype.scale = function() {
return this.transform.apply(this, [SVGPathData.Transformer.SCALE].concat(
[].slice.call(arguments, 0)));
};
SVGPathData.prototype.rotate = function() {
return this.transform.apply(this, [SVGPathData.Transformer.ROTATE].concat(
[].slice.call(arguments, 0)));
};
SVGPathData.prototype.matrix = function() {
return this.transform.apply(this, [SVGPathData.Transformer.MATRIX].concat(
[].slice.call(arguments, 0)));
};
SVGPathData.prototype.skewX = function() {
return this.transform.apply(this, [SVGPathData.Transformer.SKEW_X].concat(
[].slice.call(arguments, 0)));
};
SVGPathData.prototype.skewY = function() {
return this.transform.apply(this, [SVGPathData.Transformer.SKEW_Y].concat(
[].slice.call(arguments, 0)));
};
SVGPathData.prototype.xSymetry = function() {
return this.transform.apply(this, [SVGPathData.Transformer.X_AXIS_SIMETRY].concat(
[].slice.call(arguments, 0)));
};
SVGPathData.prototype.ySymetry = function() {
return this.transform.apply(this, [SVGPathData.Transformer.Y_AXIS_SIMETRY].concat(
[].slice.call(arguments, 0)));
};
SVGPathData.prototype.aToC = function() {
return this.transform.apply(this, [SVGPathData.Transformer.A_TO_C].concat(
[].slice.call(arguments, 0)));
};
SVGPathData.prototype.transform = function(transformFunction) {
var newCommands = [];
var curCommands = [];
var commands = this.commands;
var i;
var ii;
transformFunction = transformFunction.apply(null, [].slice.call(arguments, 1));
for(i = 0, ii = commands.length; i < ii; i++) {
curCommands = transformFunction(commands[i]);
if(curCommands instanceof Array) {
newCommands = newCommands.concat(curCommands);
} else {
newCommands.push(curCommands);
}
}
this.commands = newCommands;
return this;
};
// Static methods
SVGPathData.encode = function(commands) {
var content = '';
var encoder = new SVGPathData.Encoder();
encoder.on('readable', function() {
var str;
do {
str = encoder.read();
if(null !== str) {
content += str;
}
} while(null !== str);
});
encoder.write(commands);
encoder.end();
return content;
};
SVGPathData.parse = function(content) {
var commands = [];
var parser = new SVGPathData.Parser();
parser.on('readable', function() {
var command;
do {
command = parser.read();
if(null !== command) {
commands.push(command);
}
} while(null !== command);
});
parser.write(content);
parser.end();
return commands;
};
// Commands static vars
SVGPathData.CLOSE_PATH = 1;
SVGPathData.MOVE_TO = 2;
SVGPathData.HORIZ_LINE_TO = 4;
SVGPathData.VERT_LINE_TO = 8;
SVGPathData.LINE_TO = 16;
SVGPathData.CURVE_TO = 32;
SVGPathData.SMOOTH_CURVE_TO = 64;
SVGPathData.QUAD_TO = 128;
SVGPathData.SMOOTH_QUAD_TO = 256;
SVGPathData.ARC = 512;
SVGPathData.DRAWING_COMMANDS =
SVGPathData.HORIZ_LINE_TO | SVGPathData.VERT_LINE_TO | SVGPathData.LINE_TO |
SVGPathData.CURVE_TO | SVGPathData.SMOOTH_CURVE_TO | SVGPathData.QUAD_TO |
SVGPathData.SMOOTH_QUAD_TO | SVGPathData.ARC;
// Export the main contructor first (tests are failing otherwise)
module.exports = SVGPathData;
// Expose the internal constructors
SVGPathData.Parser = require('./SVGPathDataParser.js');
SVGPathData.Encoder = require('./SVGPathDataEncoder.js');
SVGPathData.Transformer = require('./SVGPathDataTransformer.js');

View File

@ -0,0 +1,106 @@
'use strict';
// Encode SVG PathData
// http://www.w3.org/TR/SVG/paths.html#PathDataBNF
// Access to SVGPathData constructor
var SVGPathData = require('./SVGPathData.js');
// TransformStream inherance required modules
var TransformStream = require('readable-stream').Transform;
var util = require('util');
// Private consts : Char groups
var WSP = ' ';
// Inherit of writeable stream
util.inherits(SVGPathDataEncoder, TransformStream);
// Constructor
function SVGPathDataEncoder(options) {
// Ensure new were used
if(!(this instanceof SVGPathDataEncoder)) {
return new SVGPathDataEncoder(options);
}
// Parent constructor
TransformStream.call(this, {
objectMode: true,
});
// Setting objectMode separately
this._writableState.objectMode = true;
this._readableState.objectMode = false;
}
// Read method
SVGPathDataEncoder.prototype._transform = function(commands, encoding, done) {
var str = '';
var i;
var j;
if(!(commands instanceof Array)) {
commands = [commands];
}
for(i = 0, j = commands.length; i < j; i++) {
// Horizontal move to command
if(commands[i].type === SVGPathData.CLOSE_PATH) {
str += 'z';
continue;
// Horizontal move to command
} else if(commands[i].type === SVGPathData.HORIZ_LINE_TO) {
str += (commands[i].relative ? 'h' : 'H') +
commands[i].x;
// Vertical move to command
} else if(commands[i].type === SVGPathData.VERT_LINE_TO) {
str += (commands[i].relative ? 'v' : 'V') +
commands[i].y;
// Move to command
} else if(commands[i].type === SVGPathData.MOVE_TO) {
str += (commands[i].relative ? 'm' : 'M') +
commands[i].x + WSP + commands[i].y;
// Line to command
} else if(commands[i].type === SVGPathData.LINE_TO) {
str += (commands[i].relative ? 'l' : 'L') +
commands[i].x + WSP + commands[i].y;
// Curve to command
} else if(commands[i].type === SVGPathData.CURVE_TO) {
str += (commands[i].relative ? 'c' : 'C') +
commands[i].x2 + WSP + commands[i].y2 +
WSP + commands[i].x1 + WSP + commands[i].y1 +
WSP + commands[i].x + WSP + commands[i].y;
// Smooth curve to command
} else if(commands[i].type === SVGPathData.SMOOTH_CURVE_TO) {
str += (commands[i].relative ? 's' : 'S') +
commands[i].x2 + WSP + commands[i].y2 +
WSP + commands[i].x + WSP + commands[i].y;
// Quadratic bezier curve to command
} else if(commands[i].type === SVGPathData.QUAD_TO) {
str += (commands[i].relative ? 'q' : 'Q') +
commands[i].x1 + WSP + commands[i].y1 +
WSP + commands[i].x + WSP + commands[i].y;
// Smooth quadratic bezier curve to command
} else if(commands[i].type === SVGPathData.SMOOTH_QUAD_TO) {
str += (commands[i].relative ? 't' : 'T') +
commands[i].x + WSP + commands[i].y;
// Elliptic arc command
} else if(commands[i].type === SVGPathData.ARC) {
str += (commands[i].relative ? 'a' : 'A') +
commands[i].rX + WSP + commands[i].rY +
WSP + commands[i].xRot +
WSP + commands[i].lArcFlag + WSP + commands[i].sweepFlag +
WSP + commands[i].x + WSP + commands[i].y;
// Unkown command
} else {
this.emit('error', new Error('Unexpected command type "' +
commands[i].type + '" at index ' + i + '.'));
}
}
this.push(new Buffer(str, 'utf8'));
done();
};
module.exports = SVGPathDataEncoder;

View File

@ -0,0 +1,508 @@
'use strict';
// Parse SVG PathData
// http://www.w3.org/TR/SVG/paths.html#PathDataBNF
// Access to SVGPathData constructor
var SVGPathData = require('./SVGPathData.js');
// TransformStream inherance required modules
var TransformStream = require('readable-stream').Transform;
var util = require('util');
// Private consts : Char groups
var WSP = [' ', '\t', '\r', '\n'];
var DIGITS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
var SIGNS = ['-', '+'];
var EXPONENTS = ['e', 'E'];
var DECPOINT = ['.'];
var FLAGS = ['0', '1'];
var COMMA = [','];
var COMMANDS = [
'm', 'M', 'z', 'Z', 'l', 'L', 'h', 'H', 'v', 'V', 'c', 'C',
's', 'S', 'q', 'Q', 't', 'T', 'a', 'A',
];
// Inherit of transform stream
util.inherits(SVGPathDataParser, TransformStream);
// Constructor
function SVGPathDataParser(options) {
// Ensure new were used
if(!(this instanceof SVGPathDataParser)) {
return new SVGPathDataParser(options);
}
// Parent constructor
TransformStream.call(this, {
objectMode: true,
});
// Setting objectMode separately
this._writableState.objectMode = false;
this._readableState.objectMode = true;
// Parsing vars
this.state = SVGPathDataParser.STATE_COMMAS_WSPS;
this.curNumber = '';
this.curCommand = null;
this._flush = function(callback) {
this._transform(new Buffer(' '), 'utf-8', function() {});
// Adding residual command
if(null !== this.curCommand) {
if(this.curCommand.invalid) {
this.emit('error',
new SyntaxError('Unterminated command at the path end.'));
}
this.push(this.curCommand);
this.curCommand = null;
this.state ^= this.state & SVGPathDataParser.STATE_COMMANDS_MASK;
}
callback();
};
this._transform = function(chunk, encoding, callback) {
var str = chunk.toString('buffer' !== encoding ? encoding : 'utf8');
var i;
var j;
for(i = 0, j = str.length; i < j; i++) {
// White spaces parsing
if(this.state & SVGPathDataParser.STATE_WSP ||
this.state & SVGPathDataParser.STATE_WSPS) {
if(-1 !== WSP.indexOf(str[i])) {
this.state ^= this.state & SVGPathDataParser.STATE_WSP;
// any space stops current number parsing
if('' !== this.curNumber) {
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
} else {
continue;
}
}
}
// Commas parsing
if(this.state & SVGPathDataParser.STATE_COMMA ||
this.state & SVGPathDataParser.STATE_COMMAS) {
if(-1 !== COMMA.indexOf(str[i])) {
this.state ^= this.state & SVGPathDataParser.STATE_COMMA;
// any comma stops current number parsing
if('' !== this.curNumber) {
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
} else {
continue;
}
}
}
// Numbers parsing : -125.25e-125
if(this.state & SVGPathDataParser.STATE_NUMBER) {
// Reading the sign
if((this.state & SVGPathDataParser.STATE_NUMBER_MASK) ===
SVGPathDataParser.STATE_NUMBER) {
this.state |= SVGPathDataParser.STATE_NUMBER_INT |
SVGPathDataParser.STATE_NUMBER_DIGITS;
if(-1 !== SIGNS.indexOf(str[i])) {
this.curNumber += str[i];
continue;
}
}
// Reading the exponent sign
if(this.state & SVGPathDataParser.STATE_NUMBER_EXPSIGN) {
this.state ^= SVGPathDataParser.STATE_NUMBER_EXPSIGN;
this.state |= SVGPathDataParser.STATE_NUMBER_DIGITS;
if(-1 !== SIGNS.indexOf(str[i])) {
this.curNumber += str[i];
continue;
}
}
// Reading digits
if(this.state & SVGPathDataParser.STATE_NUMBER_DIGITS) {
if(-1 !== DIGITS.indexOf(str[i])) {
this.curNumber += str[i];
continue;
}
this.state ^= SVGPathDataParser.STATE_NUMBER_DIGITS;
}
// Ended reading left side digits
if(this.state & SVGPathDataParser.STATE_NUMBER_INT) {
this.state ^= SVGPathDataParser.STATE_NUMBER_INT;
// if got a point, reading right side digits
if(-1 !== DECPOINT.indexOf(str[i])) {
this.curNumber += str[i];
this.state |= SVGPathDataParser.STATE_NUMBER_FLOAT |
SVGPathDataParser.STATE_NUMBER_DIGITS;
continue;
// if got e/E, reading the exponent
} else if(-1 !== EXPONENTS.indexOf(str[i])) {
this.curNumber += str[i];
this.state |= SVGPathDataParser.STATE_NUMBER_EXP |
SVGPathDataParser.STATE_NUMBER_EXPSIGN;
continue;
}
// else we're done with that number
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
}
// Ended reading decimal digits
if(this.state & SVGPathDataParser.STATE_NUMBER_FLOAT) {
this.state ^= SVGPathDataParser.STATE_NUMBER_FLOAT;
// if got e/E, reading the exponent
if(-1 !== EXPONENTS.indexOf(str[i])) {
this.curNumber += str[i];
this.state |= SVGPathDataParser.STATE_NUMBER_EXP |
SVGPathDataParser.STATE_NUMBER_EXPSIGN;
continue;
}
// else we're done with that number
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
}
// Ended reading exponent digits
if(this.state & SVGPathDataParser.STATE_NUMBER_EXP) {
// we're done with that number
this.state ^= this.state & SVGPathDataParser.STATE_NUMBER_MASK;
}
}
// New number
if(this.curNumber) {
// Horizontal move to command (x)
if(this.state & SVGPathDataParser.STATE_HORIZ_LINE_TO) {
if(null === this.curCommand) {
this.push({
type: SVGPathData.HORIZ_LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
x: Number(this.curNumber),
});
} else {
this.curCommand.x = Number(this.curNumber);
delete this.curCommand.invalid;
this.push(this.curCommand);
this.curCommand = null;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Vertical move to command (y)
} else if(this.state & SVGPathDataParser.STATE_VERT_LINE_TO) {
if(null === this.curCommand) {
this.push({
type: SVGPathData.VERT_LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
y: Number(this.curNumber),
});
} else {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
this.push(this.curCommand);
this.curCommand = null;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Move to / line to / smooth quadratic curve to commands (x, y)
} else if(this.state & SVGPathDataParser.STATE_MOVE_TO ||
this.state & SVGPathDataParser.STATE_LINE_TO ||
this.state & SVGPathDataParser.STATE_SMOOTH_QUAD_TO) {
if(null === this.curCommand) {
this.curCommand = {
type: (this.state & SVGPathDataParser.STATE_MOVE_TO ?
SVGPathData.MOVE_TO :
(this.state & SVGPathDataParser.STATE_LINE_TO ?
SVGPathData.LINE_TO : SVGPathData.SMOOTH_QUAD_TO
)
),
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
x: Number(this.curNumber),
};
} else if('undefined' === typeof this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
} else {
delete this.curCommand.invalid;
this.curCommand.y = Number(this.curNumber);
this.push(this.curCommand);
this.curCommand = null;
// Switch to line to state
if(this.state & SVGPathDataParser.STATE_MOVE_TO) {
this.state ^= SVGPathDataParser.STATE_MOVE_TO;
this.state |= SVGPathDataParser.STATE_LINE_TO;
}
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Curve to commands (x1, y1, x2, y2, x, y)
} else if(this.state & SVGPathDataParser.STATE_CURVE_TO) {
if(null === this.curCommand) {
this.curCommand = {
type: SVGPathData.CURVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
x2: Number(this.curNumber),
};
} else if('undefined' === typeof this.curCommand.x2) {
this.curCommand.x2 = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.y2) {
this.curCommand.y2 = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.x1) {
this.curCommand.x1 = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.y1) {
this.curCommand.y1 = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.y) {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
this.push(this.curCommand);
this.curCommand = null;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Smooth curve to commands (x1, y1, x, y)
} else if(this.state & SVGPathDataParser.STATE_SMOOTH_CURVE_TO) {
if(null === this.curCommand) {
this.curCommand = {
type: SVGPathData.SMOOTH_CURVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
x2: Number(this.curNumber),
};
} else if('undefined' === typeof this.curCommand.x2) {
this.curCommand.x2 = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.y2) {
this.curCommand.y2 = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.y) {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
this.push(this.curCommand);
this.curCommand = null;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Quadratic bezier curve to commands (x1, y1, x, y)
} else if(this.state & SVGPathDataParser.STATE_QUAD_TO) {
if(null === this.curCommand) {
this.curCommand = {
type: SVGPathData.QUAD_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
x1: Number(this.curNumber),
};
} else if('undefined' === typeof this.curCommand.x1) {
this.curCommand.x1 = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.y1) {
this.curCommand.y1 = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.y) {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
this.push(this.curCommand);
this.curCommand = null;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
// Elliptic arc commands (rX, rY, xRot, lArcFlag, sweepFlag, x, y)
} else if(this.state & SVGPathDataParser.STATE_ARC) {
if(null === this.curCommand) {
this.curCommand = {
type: SVGPathData.ARC,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
rX: Number(this.curNumber),
};
} else if('undefined' === typeof this.curCommand.rX) {
if(0 > Number(this.curNumber)) {
this.emit('error', new SyntaxError('Expected positive number,' +
' got "' + this.curNumber + '" at index "' + i + '"'));
}
this.curCommand.rX = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.rY) {
if(0 > Number(this.curNumber)) {
this.emit('error', new SyntaxError('Expected positive number,' +
' got "' + this.curNumber + '" at index "' + i + '"'));
}
this.curCommand.rY = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.xRot) {
this.curCommand.xRot = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.lArcFlag) {
if(-1 === FLAGS.indexOf(this.curNumber)) {
this.emit('error', new SyntaxError('Expected a flag, got "' +
this.curNumber + '" at index "' + i + '"'));
}
this.curCommand.lArcFlag = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.sweepFlag) {
if('0' !== this.curNumber && '1' !== this.curNumber) {
this.emit('error', new SyntaxError('Expected a flag, got "' +
this.curNumber + '" at index "' + i + '"'));
}
this.curCommand.sweepFlag = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.x) {
this.curCommand.x = Number(this.curNumber);
} else if('undefined' === typeof this.curCommand.y) {
this.curCommand.y = Number(this.curNumber);
delete this.curCommand.invalid;
this.push(this.curCommand);
this.curCommand = null;
}
this.state |= SVGPathDataParser.STATE_NUMBER;
}
this.curNumber = '';
// Continue if a white space or a comma was detected
if(-1 !== WSP.indexOf(str[i]) || -1 !== COMMA.indexOf(str[i])) {
continue;
}
// if a sign is detected, then parse the new number
if(-1 !== SIGNS.indexOf(str[i])) {
this.curNumber = str[i];
this.state |= SVGPathDataParser.STATE_NUMBER_INT |
SVGPathDataParser.STATE_NUMBER_DIGITS;
continue;
}
// if the decpoint is detected, then parse the new number
if(-1 !== DECPOINT.indexOf(str[i])) {
this.curNumber = str[i];
this.state |= SVGPathDataParser.STATE_NUMBER_FLOAT |
SVGPathDataParser.STATE_NUMBER_DIGITS;
continue;
}
}
// End of a command
if(-1 !== COMMANDS.indexOf(str[i])) {
// Adding residual command
if(null !== this.curCommand) {
if(this.curCommand.invalid) {
this.emit('error',
new SyntaxError('Unterminated command at index ' + i + '.'));
}
this.push(this.curCommand);
this.curCommand = null;
this.state ^= this.state & SVGPathDataParser.STATE_COMMANDS_MASK;
}
}
// Detecting the next command
this.state ^= this.state & SVGPathDataParser.STATE_COMMANDS_MASK;
// Is the command relative
if(str[i] === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_RELATIVE;
} else {
this.state ^= this.state & SVGPathDataParser.STATE_RELATIVE;
}
// Horizontal move to command
if('z' === str[i].toLowerCase()) {
this.push({
type: SVGPathData.CLOSE_PATH,
});
this.state = SVGPathDataParser.STATE_COMMAS_WSPS;
continue;
// Horizontal move to command
} else if('h' === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_HORIZ_LINE_TO;
this.curCommand = {
type: SVGPathData.HORIZ_LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
// Vertical move to command
} else if('v' === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_VERT_LINE_TO;
this.curCommand = {
type: SVGPathData.VERT_LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
// Move to command
} else if('m' === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_MOVE_TO;
this.curCommand = {
type: SVGPathData.MOVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
// Line to command
} else if('l' === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_LINE_TO;
this.curCommand = {
type: SVGPathData.LINE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
// Curve to command
} else if('c' === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_CURVE_TO;
this.curCommand = {
type: SVGPathData.CURVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
// Smooth curve to command
} else if('s' === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_SMOOTH_CURVE_TO;
this.curCommand = {
type: SVGPathData.SMOOTH_CURVE_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
// Quadratic bezier curve to command
} else if('q' === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_QUAD_TO;
this.curCommand = {
type: SVGPathData.QUAD_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
// Smooth quadratic bezier curve to command
} else if('t' === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_SMOOTH_QUAD_TO;
this.curCommand = {
type: SVGPathData.SMOOTH_QUAD_TO,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
// Elliptic arc command
} else if('a' === str[i].toLowerCase()) {
this.state |= SVGPathDataParser.STATE_ARC;
this.curCommand = {
type: SVGPathData.ARC,
relative: !!(this.state & SVGPathDataParser.STATE_RELATIVE),
invalid: true,
};
// Unkown command
} else {
this.emit('error', new SyntaxError('Unexpected character "' + str[i] +
'" at index ' + i + '.'));
}
// White spaces can follow a command
this.state |= SVGPathDataParser.STATE_COMMAS_WSPS |
SVGPathDataParser.STATE_NUMBER;
}
callback();
};
}
// Static consts
// Parsing states
SVGPathDataParser.STATE_WSP = 1;
SVGPathDataParser.STATE_WSPS = 2;
SVGPathDataParser.STATE_COMMA = 4;
SVGPathDataParser.STATE_COMMAS = 8;
SVGPathDataParser.STATE_COMMAS_WSPS =
SVGPathDataParser.STATE_WSP | SVGPathDataParser.STATE_WSPS |
SVGPathDataParser.STATE_COMMA | SVGPathDataParser.STATE_COMMAS;
SVGPathDataParser.STATE_NUMBER = 16;
SVGPathDataParser.STATE_NUMBER_DIGITS = 32;
SVGPathDataParser.STATE_NUMBER_INT = 64;
SVGPathDataParser.STATE_NUMBER_FLOAT = 128;
SVGPathDataParser.STATE_NUMBER_EXP = 256;
SVGPathDataParser.STATE_NUMBER_EXPSIGN = 512;
SVGPathDataParser.STATE_NUMBER_MASK = SVGPathDataParser.STATE_NUMBER |
SVGPathDataParser.STATE_NUMBER_DIGITS | SVGPathDataParser.STATE_NUMBER_INT |
SVGPathDataParser.STATE_NUMBER_EXP | SVGPathDataParser.STATE_NUMBER_FLOAT;
SVGPathDataParser.STATE_RELATIVE = 1024;
SVGPathDataParser.STATE_CLOSE_PATH = 2048; // Close path command (z/Z)
SVGPathDataParser.STATE_MOVE_TO = 4096; // Move to command (m/M)
SVGPathDataParser.STATE_LINE_TO = 8192; // Line to command (l/L=)
SVGPathDataParser.STATE_HORIZ_LINE_TO = 16384; // Horizontal line to command (h/H)
SVGPathDataParser.STATE_VERT_LINE_TO = 32768; // Vertical line to command (v/V)
SVGPathDataParser.STATE_CURVE_TO = 65536; // Curve to command (c/C)
SVGPathDataParser.STATE_SMOOTH_CURVE_TO = 131072; // Smooth curve to command (s/S)
SVGPathDataParser.STATE_QUAD_TO = 262144; // Quadratic bezier curve to command (q/Q)
SVGPathDataParser.STATE_SMOOTH_QUAD_TO = 524288; // Smooth quadratic bezier curve to command (t/T)
SVGPathDataParser.STATE_ARC = 1048576; // Elliptic arc command (a/A)
SVGPathDataParser.STATE_COMMANDS_MASK =
SVGPathDataParser.STATE_CLOSE_PATH | SVGPathDataParser.STATE_MOVE_TO |
SVGPathDataParser.STATE_LINE_TO | SVGPathDataParser.STATE_HORIZ_LINE_TO |
SVGPathDataParser.STATE_VERT_LINE_TO | SVGPathDataParser.STATE_CURVE_TO |
SVGPathDataParser.STATE_SMOOTH_CURVE_TO | SVGPathDataParser.STATE_QUAD_TO |
SVGPathDataParser.STATE_SMOOTH_QUAD_TO | SVGPathDataParser.STATE_ARC;
module.exports = SVGPathDataParser;

View File

@ -0,0 +1,416 @@
'use strict';
// Transform SVG PathData
// http://www.w3.org/TR/SVG/paths.html#PathDataBNF
// a2c utility
var a2c = require('./a2c.js');
// Access to SVGPathData constructor
var SVGPathData = require('./SVGPathData.js');
// TransformStream inherance required modules
var TransformStream = require('readable-stream').Transform;
var util = require('util');
// Inherit of transform stream
util.inherits(SVGPathDataTransformer, TransformStream);
function SVGPathDataTransformer(transformFunction) {
// Ensure new were used
if(!(this instanceof SVGPathDataTransformer)) {
return new (SVGPathDataTransformer.bind.apply(SVGPathDataTransformer,
[SVGPathDataTransformer].concat([].slice.call(arguments, 0))));
}
// Transform function needed
if('function' !== typeof transformFunction) {
throw new Error('Please provide a transform callback to receive commands.');
}
this._transformer = transformFunction.apply(null, [].slice.call(arguments, 1));
if('function' !== typeof this._transformer) {
throw new Error('Please provide a valid transform (returning a function).');
}
// Parent constructor
TransformStream.call(this, {
objectMode: true,
});
}
SVGPathDataTransformer.prototype._transform = function(commands, encoding, done) {
var i;
var j;
if(!(commands instanceof Array)) {
commands = [commands];
}
for(i = 0, j = commands.length; i < j; i++) {
this.push(this._transformer(commands[i]));
}
done();
};
// Predefined transforming functions
// Rounds commands values
SVGPathDataTransformer.ROUND = function roundGenerator(roundVal) {
roundVal = roundVal || 10e12;
return function round(command) {
// x1/y1 values
if('undefined' !== typeof command.x1) {
command.x1 = Math.round(command.x1 * roundVal) / roundVal;
}
if('undefined' !== typeof command.y1) {
command.y1 = Math.round(command.y1 * roundVal) / roundVal;
}
// x2/y2 values
if('undefined' !== typeof command.x2) {
command.x2 = Math.round(command.x2 * roundVal) / roundVal;
}
if('undefined' !== typeof command.y2) {
command.y2 = Math.round(command.y2 * roundVal) / roundVal;
}
// Finally x/y values
if('undefined' !== typeof command.x) {
command.x = Math.round(command.x * roundVal, 12) / roundVal;
}
if('undefined' !== typeof command.y) {
command.y = Math.round(command.y * roundVal, 12) / roundVal;
}
return command;
};
};
// Relative to absolute commands
SVGPathDataTransformer.TO_ABS = function toAbsGenerator() {
var prevX = 0;
var prevY = 0;
var pathStartX = NaN;
var pathStartY = NaN;
return function toAbs(command) {
if(isNaN(pathStartX) && (command.type & SVGPathData.DRAWING_COMMANDS)) {
pathStartX = prevX;
pathStartY = prevY;
}
if((command.type & SVGPathData.CLOSE_PATH) && !isNaN(pathStartX)) {
prevX = isNaN(pathStartX) ? 0 : pathStartX;
prevY = isNaN(pathStartY) ? 0 : pathStartY;
pathStartX = NaN;
pathStartY = NaN;
}
if(command.relative) {
// x1/y1 values
if('undefined' !== typeof command.x1) {
command.x1 = prevX + command.x1;
}
if('undefined' !== typeof command.y1) {
command.y1 = prevY + command.y1;
}
// x2/y2 values
if('undefined' !== typeof command.x2) {
command.x2 = prevX + command.x2;
}
if('undefined' !== typeof command.y2) {
command.y2 = prevY + command.y2;
}
// Finally x/y values
if('undefined' !== typeof command.x) {
command.x = prevX + command.x;
}
if('undefined' !== typeof command.y) {
command.y = prevY + command.y;
}
command.relative = false;
}
prevX = ('undefined' !== typeof command.x ? command.x : prevX);
prevY = ('undefined' !== typeof command.y ? command.y : prevY);
if(command.type & SVGPathData.MOVE_TO) {
pathStartX = prevX;
pathStartY = prevY;
}
return command;
};
};
// Absolute to relative commands
SVGPathDataTransformer.TO_REL = function toRelGenerator() {
var prevX = 0;
var prevY = 0;
return function toRel(command) {
if(!command.relative) {
// x1/y1 values
if('undefined' !== typeof command.x1) {
command.x1 -= prevX;
}
if('undefined' !== typeof command.y1) {
command.y1 -= prevY;
}
// x2/y2 values
if('undefined' !== typeof command.x2) {
command.x2 -= prevX;
}
if('undefined' !== typeof command.y2) {
command.y2 -= prevY;
}
// Finally x/y values
if('undefined' !== typeof command.x) {
command.x -= prevX;
}
if('undefined' !== typeof command.y) {
command.y -= prevY;
}
command.relative = true;
}
prevX = ('undefined' !== typeof command.x ? prevX + command.x : prevX);
prevY = ('undefined' !== typeof command.y ? prevY + command.y : prevY);
return command;
};
};
// SVG Transforms : http://www.w3.org/TR/SVGTiny12/coords.html#TransformList
// Matrix : http://apike.ca/prog_svg_transform.html
SVGPathDataTransformer.MATRIX = function matrixGenerator(a, b, c, d, e, f) {
var prevX;
var prevY;
if('number' !== typeof a || 'number' !== typeof b ||
'number' !== typeof c || 'number' !== typeof d ||
'number' !== typeof e || 'number' !== typeof f) {
throw new Error('A matrix transformation requires parameters' +
' [a,b,c,d,e,f] to be set and to be numbers.');
}
return function matrix(command) {
var origX = command.x;
var origX1 = command.x1;
var origX2 = command.x2;
if('undefined' !== typeof command.x) {
command.x = (command.x * a) +
('undefined' !== typeof command.y ?
command.y : (command.relative ? 0 : prevY || 0)
) * c +
(command.relative && 'undefined' !== typeof prevX ? 0 : e);
}
if('undefined' !== typeof command.y) {
command.y = ('undefined' !== typeof origX ?
origX : (command.relative ? 0 : prevX || 0)
) * b +
command.y * d +
(command.relative && 'undefined' !== typeof prevY ? 0 : f);
}
if('undefined' !== typeof command.x1) {
command.x1 = command.x1 * a + command.y1 * c +
(command.relative && 'undefined' !== typeof prevX ? 0 : e);
}
if('undefined' !== typeof command.y1) {
command.y1 = origX1 * b + command.y1 * d +
(command.relative && 'undefined' !== typeof prevY ? 0 : f);
}
if('undefined' !== typeof command.x2) {
command.x2 = command.x2 * a + command.y2 * c +
(command.relative && 'undefined' !== typeof prevX ? 0 : e);
}
if('undefined' !== typeof command.y2) {
command.y2 = origX2 * b + command.y2 * d +
(command.relative && 'undefined' !== typeof prevY ? 0 : f);
}
function sq(x) { return x*x; }
var det = a*d - b*c;
if('undefined' !== typeof command.xRot) {
// Skip if this is a pure translation
if(a !== 1 || b !== 0 || c !== 0 || d !== 1) {
// Special case for singular matrix
if(det === 0) {
// In the singular case, the arc is compressed to a line. The actual geometric image of the original
// curve under this transform possibly extends beyond the starting and/or ending points of the segment, but
// for simplicity we ignore this detail and just replace this command with a single line segment.
delete command.rX;
delete command.rY;
delete command.xRot;
delete command.lArcFlag;
delete command.sweepFlag;
command.type = SVGPathData.LINE_TO;
} else {
// Convert to radians
var xRot = command.xRot*Math.PI/180;
// Convert rotated ellipse to general conic form
// x0^2/rX^2 + y0^2/rY^2 - 1 = 0
// x0 = x*cos(xRot) + y*sin(xRot)
// y0 = -x*sin(xRot) + y*cos(xRot)
// --> A*x^2 + B*x*y + C*y^2 - 1 = 0, where
var sinRot = Math.sin(xRot), cosRot = Math.cos(xRot),
xCurve = 1/sq(command.rX), yCurve = 1/sq(command.rY);
var A = sq(cosRot)*xCurve + sq(sinRot)*yCurve,
B = 2*sinRot*cosRot*(xCurve - yCurve),
C = sq(sinRot)*xCurve + sq(cosRot)*yCurve;
// Apply matrix to A*x^2 + B*x*y + C*y^2 - 1 = 0
// x1 = a*x + c*y
// y1 = b*x + d*y
// (we can ignore e and f, since pure translations don't affect the shape of the ellipse)
// --> A1*x1^2 + B1*x1*y1 + C1*y1^2 - det^2 = 0, where
var A1 = A*d*d - B*b*d + C*b*b,
B1 = B*(a*d + b*c) - 2*(A*c*d + C*a*b),
C1 = A*c*c - B*a*c + C*a*a;
// Unapply newXRot to get back to axis-aligned ellipse equation
// x1 = x2*cos(newXRot) - y2*sin(newXRot)
// y1 = x2*sin(newXRot) + y2*cos(newXRot)
// A1*x1^2 + B1*x1*y1 + C1*y1^2 - det^2 =
// x2^2*(A1*cos(newXRot)^2 + B1*sin(newXRot)*cos(newXRot) + C1*sin(newXRot)^2)
// + x2*y2*(2*(C1 - A1)*sin(newXRot)*cos(newXRot) + B1*(cos(newXRot)^2 - sin(newXRot)^2))
// + y2^2*(A1*sin(newXRot)^2 - B1*sin(newXRot)*cos(newXRot) + C1*cos(newXRot)^2)
// (which must have the same zeroes as)
// x2^2/newRX^2 + y2^2/newRY^2 - 1
// (so we have)
// 2*(C1 - A1)*sin(newXRot)*cos(newXRot) + B1*(cos(newXRot)^2 - sin(newXRot)^2) = 0
// (A1 - C1)*sin(2*newXRot) = B1*cos(2*newXRot)
// 2*newXRot = atan2(B1, A1 - C1)
var newXRot = ((Math.atan2(B1, A1 - C1) + Math.PI) % Math.PI)/2;
// For any integer n, (atan2(B1, A1 - C1) + n*pi)/2 is a solution to the above; incrementing n just swaps the
// x and y radii computed below (since that's what rotating an ellipse by pi/2 does). Choosing the rotation
// between 0 and pi/2 eliminates the ambiguity and leads to more predictable output.
// Finally, we get newRX and newRY from the same-zeroes relationship that gave us newXRot
var newSinRot = Math.sin(newXRot), newCosRot = Math.cos(newXRot);
command.rX = Math.abs(det)/Math.sqrt(A1*sq(newCosRot) + B1*newSinRot*newCosRot + C1*sq(newSinRot));
command.rY = Math.abs(det)/Math.sqrt(A1*sq(newSinRot) - B1*newSinRot*newCosRot + C1*sq(newCosRot));
command.xRot = newXRot*180/Math.PI;
}
}
}
// sweepFlag needs to be inverted when mirroring shapes
// see http://www.itk.ilstu.edu/faculty/javila/SVG/SVG_drawing1/elliptical_curve.htm
// m 65,10 a 50,25 0 1 0 50,25
// M 65,60 A 50,25 0 1 1 115,35
if('undefined' !== typeof command.sweepFlag) {
command.sweepFlag = (command.sweepFlag + (0 <= det ? 0 : 1)) % 2;
}
prevX = ('undefined' !== typeof command.x ?
(command.relative ? (prevX || 0) + command.x : command.x) :
prevX || 0);
prevY = ('undefined' !== typeof command.y ?
(command.relative ? (prevY || 0) + command.y : command.y) :
prevY || 0);
return command;
};
};
// Rotation
SVGPathDataTransformer.ROTATE = function rotateGenerator(a, x, y) {
if('number' !== typeof a) {
throw new Error('A rotate transformation requires the parameter a' +
' to be set and to be a number.');
}
return (function(toOrigin, doRotate, fromOrigin) {
return function rotate(command) {
return fromOrigin(doRotate(toOrigin(command)));
};
})(SVGPathDataTransformer.TRANSLATE(-(x || 0), -(y || 0)),
SVGPathDataTransformer.MATRIX(Math.cos(a), Math.sin(a),
-Math.sin(a), Math.cos(a), 0, 0),
SVGPathDataTransformer.TRANSLATE(x || 0, y || 0)
);
};
// Translation
SVGPathDataTransformer.TRANSLATE = function translateGenerator(dX, dY) {
if('number' !== typeof dX) {
throw new Error('A translate transformation requires the parameter dX' +
' to be set and to be a number.');
}
return SVGPathDataTransformer.MATRIX(1, 0, 0, 1, dX, dY || 0);
};
// Scaling
SVGPathDataTransformer.SCALE = function scaleGenerator(dX, dY) {
if('number' !== typeof dX) {
throw new Error('A scale transformation requires the parameter dX' +
' to be set and to be a number.');
}
return SVGPathDataTransformer.MATRIX(dX, 0, 0, dY || dX, 0, 0);
};
// Skew
SVGPathDataTransformer.SKEW_X = function skewXGenerator(a) {
if('number' !== typeof a) {
throw new Error('A skewX transformation requires the parameter x' +
' to be set and to be a number.');
}
return SVGPathDataTransformer.MATRIX(1, 0, Math.atan(a), 1, 0, 0);
};
SVGPathDataTransformer.SKEW_Y = function skewYGenerator(a) {
if('number' !== typeof a) {
throw new Error('A skewY transformation requires the parameter y' +
' to be set and to be a number.');
}
return SVGPathDataTransformer.MATRIX(1, Math.atan(a), 0, 1, 0, 0);
};
// Symetry througth the X axis
SVGPathDataTransformer.X_AXIS_SIMETRY = function xSymetryGenerator(xDecal) {
return (function(toAbs, scale, translate) {
return function xSymetry(command) {
return translate(scale(toAbs(command)));
};
})(SVGPathDataTransformer.TO_ABS(),
SVGPathDataTransformer.SCALE(-1, 1),
SVGPathDataTransformer.TRANSLATE(xDecal || 0, 0)
);
};
// Symetry througth the Y axis
SVGPathDataTransformer.Y_AXIS_SIMETRY = function ySymetryGenerator(yDecal) {
return (function(toAbs, scale, translate) {
return function ySymetry(command) {
return translate(scale(toAbs(command)));
};
})(SVGPathDataTransformer.TO_ABS(),
SVGPathDataTransformer.SCALE(1, -1),
SVGPathDataTransformer.TRANSLATE(0, yDecal || 0)
);
};
// Convert arc commands to curve commands
SVGPathDataTransformer.A_TO_C = function a2CGenerator() {
var prevX = 0;
var prevY = 0;
var args;
return (function(toAbs) {
return function a2C(command) {
var commands = [];
var i;
var ii;
command = toAbs(command);
if(command.type === SVGPathData.ARC) {
args = a2c(prevX, prevY, command.rX, command.rX, command.xRot,
command.lArcFlag, command.sweepFlag, command.x, command.y);
prevX = command.x; prevY = command.y;
for(i = 0, ii = args.length; i < ii; i += 6) {
commands.push({
type: SVGPathData.CURVE_TO,
relative: false,
x2: args[i],
y2: args[i + 1],
x1: args[i + 2],
y1: args[i + 3],
x: args[i + 4],
y: args[i + 5],
});
}
return commands;
}
prevX = command.x; prevY = command.y;
return command;
};
})(SVGPathDataTransformer.TO_ABS());
};
module.exports = SVGPathDataTransformer;

127
static/js/ketcher2/node_modules/svg-pathdata/src/a2c.js generated vendored Normal file
View File

@ -0,0 +1,127 @@
// Borrowed from https://github.com/PPvG/svg-path/blob/master/lib/Path.js#L208
// that were borrowed from https://github.com/DmitryBaranovskiy/raphael/blob/4d97d4ff5350bb949b88e6d78b877f76ea8b5e24/raphael.js#L2216-L2304
// (MIT licensed; http://raphaeljs.com/license.html).
module.exports = a2c;
// LOL
var PI = Math.PI;
var math = Math;
var abs = Math.abs;
var split = "split";
var concat = "concat";
var apply = "apply";
var has = "hasOwnProperty";
function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
// for more information of where this math came from visit:
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
var _120 = PI * 120 / 180,
rad = PI / 180 * (+angle || 0),
res = [],
xy,
rotate = cacher(function (x, y, rad) {
var X = x * math.cos(rad) - y * math.sin(rad),
Y = x * math.sin(rad) + y * math.cos(rad);
return {x: X, y: Y};
});
if (!recursive) {
xy = rotate(x1, y1, -rad);
x1 = xy.x;
y1 = xy.y;
xy = rotate(x2, y2, -rad);
x2 = xy.x;
y2 = xy.y;
var cos = math.cos(PI / 180 * angle),
sin = math.sin(PI / 180 * angle),
x = (x1 - x2) / 2,
y = (y1 - y2) / 2;
var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
if (h > 1) {
h = math.sqrt(h);
rx = h * rx;
ry = h * ry;
}
var rx2 = rx * rx,
ry2 = ry * ry,
k = (large_arc_flag == sweep_flag ? -1 : 1) *
math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
cx = k * rx * y / ry + (x1 + x2) / 2,
cy = k * -ry * x / rx + (y1 + y2) / 2,
f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
f2 = math.asin(((y2 - cy) / ry).toFixed(9));
f1 = x1 < cx ? PI - f1 : f1;
f2 = x2 < cx ? PI - f2 : f2;
f1 < 0 && (f1 = PI * 2 + f1);
f2 < 0 && (f2 = PI * 2 + f2);
if (sweep_flag && f1 > f2) {
f1 = f1 - PI * 2;
}
if (!sweep_flag && f2 > f1) {
f2 = f2 - PI * 2;
}
} else {
f1 = recursive[0];
f2 = recursive[1];
cx = recursive[2];
cy = recursive[3];
}
var df = f2 - f1;
if (abs(df) > _120) {
var f2old = f2,
x2old = x2,
y2old = y2;
f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
x2 = cx + rx * math.cos(f2);
y2 = cy + ry * math.sin(f2);
res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
}
df = f2 - f1;
var c1 = math.cos(f1),
s1 = math.sin(f1),
c2 = math.cos(f2),
s2 = math.sin(f2),
t = math.tan(df / 4),
hx = 4 / 3 * rx * t,
hy = 4 / 3 * ry * t,
m1 = [x1, y1],
m2 = [x1 + hx * s1, y1 - hy * c1],
m3 = [x2 + hx * s2, y2 - hy * c2],
m4 = [x2, y2];
m2[0] = 2 * m1[0] - m2[0];
m2[1] = 2 * m1[1] - m2[1];
if (recursive) {
return [m2, m3, m4][concat](res);
} else {
res = [m2, m3, m4][concat](res).join()[split](",");
var newres = [];
for (var i = 0, ii = res.length; i < ii; i++) {
newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
}
return newres;
}
}
function cacher(f, scope, postprocessor) {
function newf() {
var arg = Array.prototype.slice.call(arguments, 0),
args = arg.join("\u2400"),
cache = newf.cache = newf.cache || {},
count = newf.count = newf.count || [];
if (cache[has](args)) {
repush(count, args);
return postprocessor ? postprocessor(cache[args]) : cache[args];
}
count.length >= 1e3 && delete cache[count.shift()];
count.push(args);
cache[args] = f[apply](scope, arg);
return postprocessor ? postprocessor(cache[args]) : cache[args];
}
return newf;
}
function repush(array, item) {
for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
return array.push(array.splice(i, 1)[0]);
}
}

View File

@ -0,0 +1,267 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Parsing eliptical arc commands", function() {
beforeEach(function() {
});
afterEach(function() {
});
it("should not work when badly declarated", function() {
assert.throw(function() {
new SVGPathData('A');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('A 30');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('A 30 50');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('A 30 50 0');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('A 30 50 0 0');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('A 30 50 0 0 1');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('A 30 50 0 0 1 162.55');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('A 30 50 0 0 1 A 30 50 0 0 1 162.55 162.45');
}, SyntaxError, 'Unterminated command at index 14.');
});
it("should not work with bad rX value", function() {
assert.throw(function() {
new SVGPathData('A,-30,50,0,0,1,162.55,162.45');
}, SyntaxError, 'Expected positive number, got "-30" at index "5"');
});
it("should not work with bad rY value", function() {
assert.throw(function() {
new SVGPathData('A,30,-50,0,0,1,162.55,162.45');
}, SyntaxError, 'Expected positive number, got "-50" at index "8"');
});
it("should not work with bad lArcFlag value", function() {
assert.throw(function() {
new SVGPathData('A,30,50,0,15,1,162.55,162.45');
}, SyntaxError, 'Expected a flag, got "15" at index "12"');
});
it("should not work with bad sweepFlag value", function() {
assert.throw(function() {
new SVGPathData('A,30,50,0,0,15,162.55,162.45');
}, SyntaxError, 'Expected a flag, got "15" at index "14"');
});
it("should work with comma separated coordinates", function() {
var commands = new SVGPathData('A,30,50,0,0,1,162.55,162.45').commands;
assert.equal(commands[0].type, SVGPathData.ARC);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].rX, '30');
assert.equal(commands[0].rY, '50');
assert.equal(commands[0].xRot, '0');
assert.equal(commands[0].lArcFlag, '0');
assert.equal(commands[0].sweepFlag, '1');
assert.equal(commands[0].x, '162.55');
assert.equal(commands[0].y, '162.45');
});
it("should work with space separated coordinates", function() {
var commands = new SVGPathData('A 30 50 0 0 1 162.55 162.45').commands;
assert.equal(commands[0].type, SVGPathData.ARC);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].rX, '30');
assert.equal(commands[0].rY, '50');
assert.equal(commands[0].xRot, '0');
assert.equal(commands[0].lArcFlag, '0');
assert.equal(commands[0].sweepFlag, '1');
assert.equal(commands[0].x, '162.55');
assert.equal(commands[0].y, '162.45');
});
it("should work with nested separated complexer coordinate pairs", function() {
var commands = new SVGPathData('A 30,50 0 0 1 162.55,162.45').commands;
assert.equal(commands[0].type, SVGPathData.ARC);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].rX, '30');
assert.equal(commands[0].rY, '50');
assert.equal(commands[0].xRot, '0');
assert.equal(commands[0].lArcFlag, '0');
assert.equal(commands[0].sweepFlag, '1');
assert.equal(commands[0].x, '162.55');
assert.equal(commands[0].y, '162.45');
});
it("should work with multiple pairs of coordinates", function() {
var commands = new SVGPathData(
'A 10.0032e-5,20.0032e-5 0 0 1 -30.0032e-5,-40.0032e-5\
50.0032e-5,60.0032e-5 0 1 0 -70.0032e-5,-80.0032e-5\
90.0032e-5,90.0032e-5 0 0 1 -80.0032e-5,-70.0032e-5').commands;
assert.equal(commands[0].type, SVGPathData.ARC);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].rX, '10.0032e-5');
assert.equal(commands[0].rY, '20.0032e-5');
assert.equal(commands[0].xRot, '0');
assert.equal(commands[0].lArcFlag, '0');
assert.equal(commands[0].sweepFlag, '1');
assert.equal(commands[0].x, '-30.0032e-5');
assert.equal(commands[0].y, '-40.0032e-5');
assert.equal(commands[1].type, SVGPathData.ARC);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].rX, '50.0032e-5');
assert.equal(commands[1].rY, '60.0032e-5');
assert.equal(commands[1].xRot, '0');
assert.equal(commands[1].lArcFlag, '1');
assert.equal(commands[1].sweepFlag, '0');
assert.equal(commands[1].x, '-70.0032e-5');
assert.equal(commands[1].y, '-80.0032e-5');
assert.equal(commands[2].type, SVGPathData.ARC);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].rX, '90.0032e-5');
assert.equal(commands[2].rY, '90.0032e-5');
assert.equal(commands[2].xRot, '0');
assert.equal(commands[2].lArcFlag, '0');
assert.equal(commands[2].sweepFlag, '1');
assert.equal(commands[2].x, '-80.0032e-5');
assert.equal(commands[2].y, '-70.0032e-5');
});
it("should work with multiple declarated pairs of coordinates", function() {
var commands = new SVGPathData(
'A 10.0032e-5,20.0032e-5 0 0 1 -30.0032e-5,-40.0032e-5\
a50.0032e-5,60.0032e-5 0 1 0 -70.0032e-5,-80.0032e-5\
A90.0032e-5,90.0032e-5 0 0 1 -80.0032e-5,-70.0032e-5').commands;
assert.equal(commands[0].type, SVGPathData.ARC);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].rX, '10.0032e-5');
assert.equal(commands[0].rY, '20.0032e-5');
assert.equal(commands[0].xRot, '0');
assert.equal(commands[0].lArcFlag, '0');
assert.equal(commands[0].sweepFlag, '1');
assert.equal(commands[0].x, '-30.0032e-5');
assert.equal(commands[0].y, '-40.0032e-5');
assert.equal(commands[1].type, SVGPathData.ARC);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].rX, '50.0032e-5');
assert.equal(commands[1].rY, '60.0032e-5');
assert.equal(commands[1].xRot, '0');
assert.equal(commands[1].lArcFlag, '1');
assert.equal(commands[1].sweepFlag, '0');
assert.equal(commands[1].x, '-70.0032e-5');
assert.equal(commands[1].y, '-80.0032e-5');
assert.equal(commands[2].type, SVGPathData.ARC);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].rX, '90.0032e-5');
assert.equal(commands[2].rY, '90.0032e-5');
assert.equal(commands[2].xRot, '0');
assert.equal(commands[2].lArcFlag, '0');
assert.equal(commands[2].sweepFlag, '1');
assert.equal(commands[2].x, '-80.0032e-5');
assert.equal(commands[2].y, '-70.0032e-5');
});
});
describe("Encoding eliptical arc commands", function() {
it("should work with one command", function() {
assert.equal(
new SVGPathData('A30 50 0 0 1 162.55 162.45').encode(),
'A30 50 0 0 1 162.55 162.45'
);
});
it("should work with several commands", function() {
assert.equal(
new SVGPathData('A30 50 0 0 1 162.55 162.45A30 50 0 0 1 162.55 162.45A30 50 0 0 1 162.55 162.45').encode(),
'A30 50 0 0 1 162.55 162.45A30 50 0 0 1 162.55 162.45A30 50 0 0 1 162.55 162.45'
);
});
});
describe("Transforming elliptical arc commands", function() {
function assertDeepCloseTo(x, y, delta) {
if(typeof x === 'number' && typeof y === 'number') {
assert.closeTo(x, y, delta);
} else if(typeof x === 'object' && typeof y === 'object') {
var keys = Object.getOwnPropertyNames(x);
assert.sameMembers(keys, Object.getOwnPropertyNames(y));
for(var i = 0; i < keys.length; i++) {
assertDeepCloseTo(x[keys[i]], y[keys[i]], delta);
}
} else if(typeof x === 'array' && typeof y === 'array') {
assert.equal(x.length, y.length, 'arrays have different lengths');
for(var i = 0; i < x.length; i++) {
assertDeepCloseTo(x[i], y[i], delta);
}
} else {
assert.equal(x, y);
}
}
it("should rotate an axis-aligned arc", function() {
assertDeepCloseTo(
new SVGPathData('M 0,0 A 100,50 0 0 1 100,50z').rotate(Math.PI/6).commands,
new SVGPathData('M 0,0 A 100,50 30 0 1 61.6,93.3z').commands,
0.1
);
});
it("should rotate an arbitrary arc", function() {
assertDeepCloseTo(
new SVGPathData('M 0,0 A 100,50 -15 0 1 100,0z').rotate(Math.PI/4).commands,
new SVGPathData('M 0,0 A 100,50 30 0 1 70.7,70.7z').commands,
0.1
);
});
it("should skew", function() {
assertDeepCloseTo(
new SVGPathData('M 0,0 A 50,100 0 0 1 50,100z').skewX(Math.tan(-1)).commands,
new SVGPathData('M 0,0 A 34.2,146.0 48.6 0 1 -50,100 Z').commands,
0.1
);
});
it("should tolerate singular matrices", function() {
assertDeepCloseTo(
new SVGPathData('M 0,0 A 80,80 0 0 1 50,100z').matrix(0.8,2,0.5,1.25,0,0).commands,
new SVGPathData('M 0,0 L 90,225 Z').commands,
0.1
);
});
it("should match what Inkscape does on this random case", function() {
assertDeepCloseTo(
new SVGPathData('M 170.19275,911.55263 A 61.42857,154.28572 21.033507 0 1 57.481868,1033.5109 61.42857,154.28572 21.033507 0 1 55.521508,867.4575 61.42857,154.28572 21.033507 0 1 168.2324,745.4993 A 61.42857,154.28572 21.033507 0 1 170.19275,911.55263 z').matrix(-0.10825745,-0.37157241,0.77029181,0.3345653,-560.10375,633.84215).commands,
new SVGPathData('M 123.63314,875.5771 A 135.65735,17.465974 30.334289 0 1 229.77839,958.26036 135.65735,17.465974 30.334289 0 1 102.08104,903.43307 135.65735,17.465974 30.334289 0 1 -4.0641555,820.74983 135.65735,17.465974 30.334289 0 1 123.63314,875.5771 z').commands,
0.0001
);
});
it("should reflect the sweep flag any time the determinant is negative", function() {
assertDeepCloseTo(
new SVGPathData('M 0,0 A 50,100 -30 1 1 80,80 Z').matrix(-1,0,0,1,0,0).commands,
new SVGPathData('M 0,0 A 50,100 30 1 0 -80,80 Z').commands,
0.1
);
});
});

View File

@ -0,0 +1,53 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
// Sample pathes from MDN
// https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
// Here we have to round output before testing since there is some lil
// differences across browsers.
describe("Converting eliptical arc commands to curves", function() {
it("should work sweepFlag on 0 and largeArcFlag on 0", function() {
assert.equal(
new SVGPathData('M80 80 A 45 45, 0, 0, 0, 125 125 L 125 80 Z').aToC()
.round().encode(),
'M80 80C80 104.8528137423857 100.1471862576143 125 125 125L125 80z'
);
});
it("should work sweepFlag on 1 and largeArcFlag on 0", function() {
assert.equal(
new SVGPathData('M230 80 A 45 45, 0, 1, 0, 275 125 L 275 80 Z').aToC()
.round().encode(),
'M230 80C195.3589838486225 80 173.7083487540115 117.5 191.0288568297003 147.5C208.349364905389 177.5 251.650635094611 177.5 268.9711431702998 147.5C272.9207180979216 140.6591355570587 275 132.8991498552438 275 125L275 80z'
);
});
it("should work sweepFlag on 0 and largeArcFlag on 1", function() {
assert.equal(
new SVGPathData('M80 230 A 45 45, 0, 0, 1, 125 275 L 125 230 Z').aToC()
.round().encode(),
'M80 230C104.8528137423857 230 125 250.1471862576143 125 275L125 230z'
);
});
it("should work sweepFlag on 1 and largeArcFlag on 1", function() {
assert.equal(
new SVGPathData('M230 230 A 45 45, 0, 1, 1, 275 275 L 275 230 Z').aToC()
.round().encode(),
'M230 230C230 195.3589838486225 267.5 173.7083487540115 297.5 191.0288568297003C327.5 208.349364905389 327.5 251.650635094611 297.5 268.9711431702998C290.6591355570588 272.9207180979216 282.8991498552438 275 275 275L275 230z'
);
});
});

View File

@ -0,0 +1,63 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Parsing close path commands", function() {
it("should work", function() {
var commands = new SVGPathData('Z').commands;
assert.equal(commands[0].type, SVGPathData.CLOSE_PATH);
});
it("should work with spaces before", function() {
var commands = new SVGPathData(' Z').commands;
assert.equal(commands[0].type, SVGPathData.CLOSE_PATH);
});
it("should work with spaces after", function() {
var commands = new SVGPathData('Z ').commands;
assert.equal(commands[0].type, SVGPathData.CLOSE_PATH);
});
it("should work before a command sequence", function() {
var commands = new SVGPathData(' Z M10,10 L10,10, H10, V10').commands;
assert.equal(commands[0].type, SVGPathData.CLOSE_PATH);
});
it("should work after a command sequence", function() {
var commands = new SVGPathData('M10,10 L10,10, H10, V10 Z').commands;
assert.equal(commands[4].type, SVGPathData.CLOSE_PATH);
});
it("should work in a command sequence", function() {
var commands = new SVGPathData('M10,10 L10,10, H10, V10 Z M10,10 L10,10, H10, V10').commands;
assert.equal(commands[4].type, SVGPathData.CLOSE_PATH);
});
});
describe("Encoding close path commands", function() {
it("should work with one command", function() {
assert.equal(
new SVGPathData('z').encode(),
'z'
);
});
it("should work with several commands", function() {
assert.equal(
new SVGPathData('zzzz').encode(),
'zzzz'
);
});
});

View File

@ -0,0 +1,168 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Parsing curve to commands", function() {
beforeEach(function() {
});
afterEach(function() {
});
it("should not work when badly declarated", function() {
assert.throw(function() {
new SVGPathData('C');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('C10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('C10 10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('C10 10 10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('C10 10 10 10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('C10 10 10 10 10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('C10 10 10 10 10 10 10 10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('C10 10 10C10 10 10 10 10 10');
}, SyntaxError, 'Unterminated command at index 9.');
});
it("should work with comma separated coordinates", function() {
var commands = new SVGPathData('C123,456 789,987 654,321').commands;
assert.equal(commands[0].type, SVGPathData.CURVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x2, '123');
assert.equal(commands[0].y2, '456');
assert.equal(commands[0].x1, '789');
assert.equal(commands[0].y1, '987');
assert.equal(commands[0].x, '654');
assert.equal(commands[0].y, '321');
});
it("should work with space separated coordinates", function() {
var commands = new SVGPathData('C123 456 789 987 654 321').commands;
assert.equal(commands[0].type, SVGPathData.CURVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x2, '123');
assert.equal(commands[0].y2, '456');
assert.equal(commands[0].x1, '789');
assert.equal(commands[0].y1, '987');
assert.equal(commands[0].x, '654');
assert.equal(commands[0].y, '321');
});
it("should work with nested separated complexer coordinate pairs", function() {
var commands = new SVGPathData(
'C-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5'
).commands;
assert.equal(commands[0].type, SVGPathData.CURVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x2, '-10.0032e-5');
assert.equal(commands[0].y2, '-20.0032e-5');
assert.equal(commands[0].x1, '-30.0032e-5');
assert.equal(commands[0].y1, '-40.0032e-5');
assert.equal(commands[0].x, '-50.0032e-5');
assert.equal(commands[0].y, '-60.0032e-5');
});
it("should work with multiple pairs of coordinates", function() {
var commands = new SVGPathData('\
C-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5\
-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5\
-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5'
).commands;
assert.equal(commands[0].type, SVGPathData.CURVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x2, '-10.0032e-5');
assert.equal(commands[0].y2, '-20.0032e-5');
assert.equal(commands[0].x1, '-30.0032e-5');
assert.equal(commands[0].y1, '-40.0032e-5');
assert.equal(commands[0].x, '-50.0032e-5');
assert.equal(commands[0].y, '-60.0032e-5');
assert.equal(commands[1].type, SVGPathData.CURVE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x2, '-10.0032e-5');
assert.equal(commands[1].y2, '-20.0032e-5');
assert.equal(commands[1].x1, '-30.0032e-5');
assert.equal(commands[1].y1, '-40.0032e-5');
assert.equal(commands[1].x, '-50.0032e-5');
assert.equal(commands[1].y, '-60.0032e-5');
assert.equal(commands[2].type, SVGPathData.CURVE_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].x2, '-10.0032e-5');
assert.equal(commands[2].y2, '-20.0032e-5');
assert.equal(commands[2].x1, '-30.0032e-5');
assert.equal(commands[2].y1, '-40.0032e-5');
assert.equal(commands[2].x, '-50.0032e-5');
assert.equal(commands[2].y, '-60.0032e-5');
});
it("should work with multiple declarated pairs of coordinates", function() {
var commands = new SVGPathData('\
C-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5\
c-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5\
C-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -50.0032e-5,-60.0032e-5'
).commands;
assert.equal(commands[0].type, SVGPathData.CURVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x2, '-10.0032e-5');
assert.equal(commands[0].y2, '-20.0032e-5');
assert.equal(commands[0].x1, '-30.0032e-5');
assert.equal(commands[0].y1, '-40.0032e-5');
assert.equal(commands[0].x, '-50.0032e-5');
assert.equal(commands[0].y, '-60.0032e-5');
assert.equal(commands[1].type, SVGPathData.CURVE_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x2, '-10.0032e-5');
assert.equal(commands[1].y2, '-20.0032e-5');
assert.equal(commands[1].x1, '-30.0032e-5');
assert.equal(commands[1].y1, '-40.0032e-5');
assert.equal(commands[1].x, '-50.0032e-5');
assert.equal(commands[1].y, '-60.0032e-5');
assert.equal(commands[2].type, SVGPathData.CURVE_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].x2, '-10.0032e-5');
assert.equal(commands[2].y2, '-20.0032e-5');
assert.equal(commands[2].x1, '-30.0032e-5');
assert.equal(commands[2].y1, '-40.0032e-5');
assert.equal(commands[2].x, '-50.0032e-5');
assert.equal(commands[2].y, '-60.0032e-5');
});
});
describe("Encoding curve to commands", function() {
it("should work with one command", function() {
assert.equal(
new SVGPathData('C-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5 -50.0032e-5 -60.0032e-5').encode(),
'C-0.000100032 -0.000200032 -0.000300032 -0.000400032 -0.000500032 -0.000600032'
);
});
it("should work with several commands", function() {
assert.equal(
new SVGPathData('C-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5 -50.0032e-5 -60.0032e-5C-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5 -50.0032e-5 -60.0032e-5C-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5 -50.0032e-5 -60.0032e-5').encode(),
'C-0.000100032 -0.000200032 -0.000300032 -0.000400032 -0.000500032 -0.000600032C-0.000100032 -0.000200032 -0.000300032 -0.000400032 -0.000500032 -0.000600032C-0.000100032 -0.000200032 -0.000300032 -0.000400032 -0.000500032 -0.000600032'
);
});
});

View File

@ -0,0 +1,33 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("SVGPathDataEncoder", function() {
it("should still work when the new operator is forgotten", function() {
assert.doesNotThrow(function() {
SVGPathData.Encoder();
});
});
it("should fail when a bad command is given", function() {
assert.throws(function() {
var encoder = new SVGPathData.Encoder();
encoder.write({
type: 'plop',
x: 0,
y: 0
});
}, 'Unexpected command type "plop" at index 0.');
});
});

View File

@ -0,0 +1,258 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Parsing horizontal commands", function() {
beforeEach(function() {
});
afterEach(function() {
});
it("should work with single coordinate", function() {
var commands = new SVGPathData('H100').commands;
assert.equal(commands[0].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '100');
});
it("should work with single complexer coordinate", function() {
var commands = new SVGPathData('H-10e-5').commands;
assert.equal(commands[0].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '-10e-5');
});
it("should work with single even more complexer coordinate", function() {
var commands = new SVGPathData('H-10.0032e-5').commands;
assert.equal(commands[0].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '-10.0032e-5');
});
it("should work with single relative coordinate", function() {
var commands = new SVGPathData('h100').commands;
assert.equal(commands[0].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '100');
});
it("should work with comma separated coordinates", function() {
var commands = new SVGPathData('H123,456,7890,9876').commands;
assert.equal(commands[0].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[0].x, '123');
assert.equal(commands[1].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[1].x, '456');
assert.equal(commands[2].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[2].x, '7890');
assert.equal(commands[3].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[3].x, '9876');
});
it("should work with space separated coordinates", function() {
var commands = new SVGPathData('H123 456 7890 9876').commands;
assert.equal(commands[0].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[0].x, '123');
assert.equal(commands[1].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[1].x, '456');
assert.equal(commands[2].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[2].x, '7890');
assert.equal(commands[3].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[3].x, '9876');
});
it("should work with nested separated coordinates", function() {
var commands = new SVGPathData('H123 , 456 \t,\n7890 \r\n 9876').commands;
assert.equal(commands[0].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[0].x, '123');
assert.equal(commands[1].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[1].x, '456');
assert.equal(commands[2].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[2].x, '7890');
assert.equal(commands[3].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[3].x, '9876');
});
it("should work with multiple command declarations", function() {
var commands = new SVGPathData('H123 , 456 \t,\n7890 \r\n 9876H123 , \
456 \t,\n7890 \r\n 9876').commands;
assert.equal(commands[0].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[0].x, '123');
assert.equal(commands[1].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[1].x, '456');
assert.equal(commands[2].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[2].x, '7890');
assert.equal(commands[3].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[3].x, '9876');
assert.equal(commands[4].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[4].x, '123');
assert.equal(commands[5].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[5].x, '456');
assert.equal(commands[6].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[6].x, '7890');
assert.equal(commands[7].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[7].x, '9876');
});
});
describe("Parsing vertical commands", function() {
beforeEach(function() {
});
afterEach(function() {
});
it("should work with single coordinate", function() {
var commands = new SVGPathData('V100').commands;
assert.equal(commands[0].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].y, '100');
});
it("should work with single complexer coordinate", function() {
var commands = new SVGPathData('V-10e-5').commands;
assert.equal(commands[0].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].y, '-10e-5');
});
it("should work with single even more complexer coordinate", function() {
var commands = new SVGPathData('V-10.0032e-5').commands;
assert.equal(commands[0].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].y, '-10.0032e-5');
});
it("should work with single relative coordinate", function() {
var commands = new SVGPathData('v100').commands;
assert.equal(commands[0].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].y, '100');
});
it("should work with comma separated coordinates", function() {
var commands = new SVGPathData('V123,456,7890,9876').commands;
assert.equal(commands[0].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[0].y, '123');
assert.equal(commands[1].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[1].y, '456');
assert.equal(commands[2].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[2].y, '7890');
assert.equal(commands[3].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[3].y, '9876');
});
it("should work with space separated coordinates", function() {
var commands = new SVGPathData('V123 456 7890 9876').commands;
assert.equal(commands[0].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[0].y, '123');
assert.equal(commands[1].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[1].y, '456');
assert.equal(commands[2].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[2].y, '7890');
assert.equal(commands[3].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[3].y, '9876');
});
it("should work with nested separated coordinates", function() {
var commands = new SVGPathData('V123 , 456 \t,\n7890 \r\n 9876').commands;
assert.equal(commands[0].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[0].y, '123');
assert.equal(commands[1].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[1].y, '456');
assert.equal(commands[2].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[2].y, '7890');
assert.equal(commands[3].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[3].y, '9876');
});
it("should work with multiple command declarations", function() {
var commands = new SVGPathData('V123 , 456 \t,\n7890 \r\n\
9876V123 , 456 \t,\n7890 \r\n 9876').commands;
assert.equal(commands[0].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[0].y, '123');
assert.equal(commands[1].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[1].y, '456');
assert.equal(commands[2].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[2].y, '7890');
assert.equal(commands[3].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[3].y, '9876');
assert.equal(commands[4].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[4].y, '123');
assert.equal(commands[5].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[5].y, '456');
assert.equal(commands[6].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[6].y, '7890');
assert.equal(commands[7].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[7].y, '9876');
});
});
describe("Parsing nested vertical/horizontal commands", function() {
beforeEach(function() {
});
afterEach(function() {
});
it("should work", function() {
var commands = new SVGPathData(
'V100H100v0.12h0.12,V100,h100v-10e-5 H-10e-5').commands;
assert.equal(commands[0].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].y, '100');
assert.equal(commands[1].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '100');
assert.equal(commands[2].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].y, '0.12');
assert.equal(commands[3].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, '0.12');
assert.equal(commands[4].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[4].relative, false);
assert.equal(commands[4].y, '100');
assert.equal(commands[5].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[5].relative, true);
assert.equal(commands[5].x, '100');
assert.equal(commands[6].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[6].relative, true);
assert.equal(commands[6].y, '-10e-5');
assert.equal(commands[7].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[7].relative, false);
assert.equal(commands[7].x, '-10e-5');
});
});
describe("Encoding nested vertical/horizontal commands", function() {
beforeEach(function() {
});
afterEach(function() {
});
it("should work", function() {
assert.equal(
new SVGPathData('V100H100v0.12h0.12V100h100v-10e-5H-10e-5').encode(),
'V100H100v0.12h0.12V100h100v-0.0001H-0.0001'
);
});
});

View File

@ -0,0 +1,146 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Parsing line to commands", function() {
beforeEach(function() {
});
afterEach(function() {
});
it("should not work with single coordinate", function() {
assert.throw(function() {
new SVGPathData('L100');
}, SyntaxError, 'Unterminated command at the path end.');
});
it("should not work with single complexer coordinate", function() {
assert.throw(function() {
new SVGPathData('l-10e-5');
}, SyntaxError, 'Unterminated command at the path end.');
});
it("should work with single coordinate followed by another", function() {
assert.throw(function() {
new SVGPathData('l-10l10 10');
}, SyntaxError, 'Unterminated command at index 4.');
});
it("should work with comma separated coordinates", function() {
var commands = new SVGPathData('L100,100').commands;
assert.equal(commands[0].type, SVGPathData.LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '100');
assert.equal(commands[0].y, '100');
});
it("should work with space separated coordinates", function() {
var commands = new SVGPathData('l100 \t 100').commands;
assert.equal(commands[0].type, SVGPathData.LINE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '100');
assert.equal(commands[0].y, '100');
});
it("should work with complexer coordinates", function() {
var commands = new SVGPathData('l-10e-5 -10e-5').commands;
assert.equal(commands[0].type, SVGPathData.LINE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '-10e-5');
assert.equal(commands[0].y, '-10e-5');
});
it("should work with single even more complexer coordinates", function() {
var commands = new SVGPathData('L-10.0032e-5 -10.0032e-5').commands;
assert.equal(commands[0].type, SVGPathData.LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '-10.0032e-5');
assert.equal(commands[0].y, '-10.0032e-5');
});
it("should work with comma separated coordinate pairs", function() {
var commands = new SVGPathData('L123,456 7890,9876').commands;
assert.equal(commands[0].type, SVGPathData.LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathData.LINE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
});
it("should work with space separated coordinate pairs", function() {
var commands = new SVGPathData('l123 \t 456 \n 7890 \r 9876').commands;
assert.equal(commands[0].type, SVGPathData.LINE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathData.LINE_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
});
it("should work with nested separated coordinates", function() {
var commands = new SVGPathData('L123 , 456 \t,\n7890 \r\n 9876').commands;
assert.equal(commands[0].type, SVGPathData.LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathData.LINE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
});
it("should work with multiple command declarations", function() {
var commands = new SVGPathData('L123 , 456 \t,\n7890 \r\n 9876l123 , \
456 \t,\n7890 \r\n 9876').commands;
assert.equal(commands[0].type, SVGPathData.LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathData.LINE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
assert.equal(commands[2].type, SVGPathData.LINE_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].x, '123');
assert.equal(commands[2].y, '456');
assert.equal(commands[3].type, SVGPathData.LINE_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, '7890');
assert.equal(commands[3].y, '9876');
});
});
describe("Encoding line to commands", function() {
it("should work with one command", function() {
assert.equal(
new SVGPathData('L-0.000500032 -0.000600032').encode(),
'L-0.000500032 -0.000600032'
);
});
it("should work with several commands", function() {
assert.equal(
new SVGPathData('L-50.0032e-5 -60.0032e-5L-50.0032e-5 -60.0032e-5L-50.0032e-5 -60.0032e-5').encode(),
'L-0.000500032 -0.000600032L-0.000500032 -0.000600032L-0.000500032 -0.000600032'
);
});
});

View File

@ -0,0 +1,62 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Matrix transformation should be the same than it's equivalent transformation", function() {
it("should fail with bad args", function() {
assert.throws(function() {
new SVGPathData(
'm20,30l10,10z'
).matrix().encode();
}, 'A matrix transformation requires parameters [a,b,c,d,e,f]'
+' to be set and to be numbers.');
assert.throws(function() {
new SVGPathData(
'm20,30l10,10z'
).matrix(1).encode();
}, 'A matrix transformation requires parameters [a,b,c,d,e,f]'
+' to be set and to be numbers.');
assert.throws(function() {
new SVGPathData(
'm20,30l10,10z'
).matrix(1, 1).encode();
}, 'A matrix transformation requires parameters [a,b,c,d,e,f]'
+' to be set and to be numbers.');
assert.throws(function() {
new SVGPathData(
'm20,30l10,10z'
).matrix(1, 1, 1).encode();
}, 'A matrix transformation requires parameters [a,b,c,d,e,f]'
+' to be set and to be numbers.');
assert.throws(function() {
new SVGPathData(
'm20,30l10,10z'
).matrix(1, 1, 1, 1).encode();
}, 'A matrix transformation requires parameters [a,b,c,d,e,f]'
+' to be set and to be numbers.');
assert.throws(function() {
new SVGPathData(
'm20,30l10,10z'
).matrix(1, 1, 1, 1, 1).encode();
}, 'A matrix transformation requires parameters [a,b,c,d,e,f]'
+' to be set and to be numbers.');
});
it("for scale", function() {
assert.equal(
new SVGPathData('m20 30c0 0 10 20 15 30z').scale(10, 10).encode(),
new SVGPathData('m20 30c0 0 10 20 15 30z').matrix(10, 0, 0, 10, 0, 0).encode()
);
});
});

View File

@ -0,0 +1,146 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Parsing move to commands", function() {
beforeEach(function() {
});
afterEach(function() {
});
it("should not work with single coordinate", function() {
assert.throw(function() {
new SVGPathData('M100');
}, SyntaxError, 'Unterminated command at the path end.');
});
it("should work with single complexer coordinate", function() {
assert.throw(function() {
new SVGPathData('m-10e-5');
}, SyntaxError, 'Unterminated command at the path end.');
});
it("should work with single coordinate followed by another", function() {
assert.throw(function() {
new SVGPathData('m-10m10 10');
}, SyntaxError, 'Unterminated command at index 4.');
});
it("should work with comma separated coordinates", function() {
var commands = new SVGPathData('M100,100').commands;
assert.equal(commands[0].type, SVGPathData.MOVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '100');
assert.equal(commands[0].y, '100');
});
it("should work with space separated coordinates", function() {
var commands = new SVGPathData('m100 \t 100').commands;
assert.equal(commands[0].type, SVGPathData.MOVE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '100');
assert.equal(commands[0].y, '100');
});
it("should work with complexer coordinates", function() {
var commands = new SVGPathData('m-10e-5 -10e-5').commands;
assert.equal(commands[0].type, SVGPathData.MOVE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '-10e-5');
assert.equal(commands[0].y, '-10e-5');
});
it("should work with even more complexer coordinates", function() {
var commands = new SVGPathData('M-10.0032e-5 -10.0032e-5').commands;
assert.equal(commands[0].type, SVGPathData.MOVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '-10.0032e-5');
assert.equal(commands[0].y, '-10.0032e-5');
});
it("should work with comma separated coordinate pairs", function() {
var commands = new SVGPathData('M123,456 7890,9876').commands;
assert.equal(commands[0].type, SVGPathData.MOVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathData.LINE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
});
it("should work with space separated coordinate pairs", function() {
var commands = new SVGPathData('m123 \t 456 \n 7890 \r 9876').commands;
assert.equal(commands[0].type, SVGPathData.MOVE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathData.LINE_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
});
it("should work with nested separated coordinates", function() {
var commands = new SVGPathData('M123 , 456 \t,\n7890 \r\n 9876').commands;
assert.equal(commands[0].type, SVGPathData.MOVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathData.LINE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
});
it("should work with multiple command declarations", function() {
var commands = new SVGPathData('M123 , 456 \t,\n7890 \r\n 9876m123 \
, 456 \t,\n7890 \r\n 9876').commands;
assert.equal(commands[0].type, SVGPathData.MOVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathData.LINE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
assert.equal(commands[2].type, SVGPathData.MOVE_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].x, '123');
assert.equal(commands[2].y, '456');
assert.equal(commands[3].type, SVGPathData.LINE_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, '7890');
assert.equal(commands[3].y, '9876');
});
});
describe("Encoding move to commands", function() {
it("should work with one command", function() {
assert.equal(
new SVGPathData('M-50.0032e-5 -60.0032e-5').encode(),
'M-0.000500032 -0.000600032'
);
});
it("should work with several commands", function() {
assert.equal(
new SVGPathData('M-50.0032e-5 -60.0032e-5M-50.0032e-5 -60.0032e-5M-50.0032e-5 -60.0032e-5').encode(),
'M-0.000500032 -0.000600032M-0.000500032 -0.000600032M-0.000500032 -0.000600032'
);
});
});

View File

@ -0,0 +1,96 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Parsing commands with different numbers", function() {
it("should work with a 1 char integer", function() {
assert.equal(new SVGPathData('H0').commands[0].x, 0);
});
it("should work with a big integer", function() {
assert.equal(new SVGPathData('H1234567890').commands[0].x, 1234567890);
});
it("should work with a explicitly positive integer", function() {
assert.equal(new SVGPathData('H+1254664').commands[0].x, +1254664);
});
it("should work with a negative integer", function() {
assert.equal(new SVGPathData('H-1254664').commands[0].x, -1254664);
});
it("should work with a float with left side digits", function() {
assert.equal(new SVGPathData('H123.456').commands[0].x, 123.456);
});
it("should work with a float without left side digits", function() {
assert.equal(new SVGPathData('H.456').commands[0].x, .456);
});
it("should work with a float without right side digits", function() {
assert.equal(new SVGPathData('H123.').commands[0].x, 123.);
});
it("should work with a number with a positive exponent", function() {
assert.equal(new SVGPathData('H123.456e125').commands[0].x, 123.456e125);
});
it("should work with a number with an explicitly positive exponent", function() {
assert.equal(new SVGPathData('H123.456e+125').commands[0].x, 123.456e+125);
});
it("should work with a number with a negative exponent", function() {
assert.equal(new SVGPathData('H123.456e-125').commands[0].x, 123.456e-125);
});
it("should work with a negative number with a positive exponent", function() {
assert.equal(new SVGPathData('H-123.456e125').commands[0].x, -123.456e125);
});
it("should work with a negative number with an explicitly positive exponent", function() {
assert.equal(new SVGPathData('H-123.456e+125').commands[0].x, -123.456e+125);
});
it("should work with a negative number with a negative exponent", function() {
assert.equal(new SVGPathData('H-123.456e-125').commands[0].x, -123.456e-125);
});
it("should work with sign separated numbers", function() {
var commands = new SVGPathData('M-123.456e-125-1234.456e-125').commands;
assert.equal(commands[0].x, -123.456e-125);
assert.equal(commands[0].y, -1234.456e-125);
});
it("should work with sign separated numbers", function() {
var commands = new SVGPathData('M-1.456e-125-12.456e-125-123.456e-125-1234.456e-125').commands;
assert.equal(commands[0].x, -1.456e-125);
assert.equal(commands[0].y, -12.456e-125);
assert.equal(commands[1].x, -123.456e-125);
assert.equal(commands[1].y, -1234.456e-125);
});
it("should work with decpoint separated numbers", function() {
var commands = new SVGPathData('M-123.123e-123.456e-456').commands;
assert.equal(commands[0].x, -123.123e-123);
assert.equal(commands[0].y, .456e-456);
});
it("should work with decpoint separated numbers", function() {
var commands = new SVGPathData('M-123.123e-123.456e-456.789e-789.123e-123').commands;
assert.equal(commands[0].x, -123.123e-123);
assert.equal(commands[0].y, .456e-456);
assert.equal(commands[1].x, .789e-789);
assert.equal(commands[1].y, .123e-123);
});
});

View File

@ -0,0 +1,30 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("SVGPathDataParser", function() {
it("should still work when the new operator is forgotten", function() {
assert.doesNotThrow(function() {
SVGPathData.Parser();
});
});
it("should fail when a bad command is given", function() {
assert.throws(function() {
var parser = new SVGPathData.Parser();
parser.write('b80,20');
parser.end();
}, 'Unexpected character "b" at index 0.');
});
});

View File

@ -0,0 +1,138 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Parsing quadratic bezier curve to commands", function() {
beforeEach(function() {
});
afterEach(function() {
});
it("should not work when badly declarated", function() {
assert.throw(function() {
new SVGPathData('Q');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('Q10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('Q10 10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('Q10 10 10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('Q10 10 10 10 10 10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('Q10 10 10Q10 10 10 10');
}, SyntaxError, 'Unterminated command at index 9.');
});
it("should work with comma separated coordinates", function() {
var commands = new SVGPathData('Q123,456 789,987').commands;
assert.equal(commands[0].type, SVGPathData.QUAD_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x1, '123');
assert.equal(commands[0].y1, '456');
assert.equal(commands[0].x, '789');
assert.equal(commands[0].y, '987');
});
it("should work with space separated coordinates", function() {
var commands = new SVGPathData('Q123 456 789 987').commands;
assert.equal(commands[0].type, SVGPathData.QUAD_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x1, '123');
assert.equal(commands[0].y1, '456');
assert.equal(commands[0].x, '789');
assert.equal(commands[0].y, '987');
});
it("should work with nested separated complexer coordinate pairs", function() {
var commands = new SVGPathData('Q-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5').commands;
assert.equal(commands[0].type, SVGPathData.QUAD_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x1, '-10.0032e-5');
assert.equal(commands[0].y1, '-20.0032e-5');
assert.equal(commands[0].x, '-30.0032e-5');
assert.equal(commands[0].y, '-40.0032e-5');
});
it("should work with multiple pairs of coordinates", function() {
var commands = new SVGPathData('Q-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5\
-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5\
-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5').commands;
assert.equal(commands[0].type, SVGPathData.QUAD_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x1, '-10.0032e-5');
assert.equal(commands[0].y1, '-20.0032e-5');
assert.equal(commands[0].x, '-30.0032e-5');
assert.equal(commands[0].y, '-40.0032e-5');
assert.equal(commands[1].type, SVGPathData.QUAD_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x1, '-10.0032e-5');
assert.equal(commands[1].y1, '-20.0032e-5');
assert.equal(commands[1].x, '-30.0032e-5');
assert.equal(commands[1].y, '-40.0032e-5');
assert.equal(commands[2].type, SVGPathData.QUAD_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].x1, '-10.0032e-5');
assert.equal(commands[2].y1, '-20.0032e-5');
assert.equal(commands[2].x, '-30.0032e-5');
assert.equal(commands[2].y, '-40.0032e-5');
});
it("should work with multiple declarated pairs of coordinates", function() {
var commands = new SVGPathData('Q-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5\
q-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5\
Q-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5').commands;
assert.equal(commands[0].type, SVGPathData.QUAD_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x1, '-10.0032e-5');
assert.equal(commands[0].y1, '-20.0032e-5');
assert.equal(commands[0].x, '-30.0032e-5');
assert.equal(commands[0].y, '-40.0032e-5');
assert.equal(commands[1].type, SVGPathData.QUAD_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x1, '-10.0032e-5');
assert.equal(commands[1].y1, '-20.0032e-5');
assert.equal(commands[1].x, '-30.0032e-5');
assert.equal(commands[1].y, '-40.0032e-5');
assert.equal(commands[2].type, SVGPathData.QUAD_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].x1, '-10.0032e-5');
assert.equal(commands[2].y1, '-20.0032e-5');
assert.equal(commands[2].x, '-30.0032e-5');
assert.equal(commands[2].y, '-40.0032e-5');
});
});
describe("Encoding line to commands", function() {
it("should work with one command", function() {
assert.equal(
new SVGPathData('Q-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5').encode(),
'Q-0.000100032 -0.000200032 -0.000300032 -0.000400032'
);
});
it("should work with several commands", function() {
assert.equal(
new SVGPathData('Q-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5q-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5Q-10.0032e-5 -20.0032e-5 -30.0032e-5 -40.0032e-5').encode(),
'Q-0.000100032 -0.000200032 -0.000300032 -0.000400032q-0.000100032 -0.000200032 -0.000300032 -0.000400032Q-0.000100032 -0.000200032 -0.000300032 -0.000400032'
);
});
});

View File

@ -0,0 +1,40 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Dealing with real world commands", function() {
it("and Y axis symetry with y coords equal to 0", function() {
assert.equal(new SVGPathData('M250,381 C199.119048,381 151.285714,361.164188 115.333333,325.159688 L165.857143,274.653375 C188.333333,297.156188 218.238095,309.5625 250,309.5625 C298.214286,309.5625 341.333333,280.797 359.904762,236.291438 L369.071429,214.3125 L500,214.3125 L500,285.75 L415,285.75 C381.285714,344.304937 318.880952,381 250,381 L250,381 L250,381 L250,381 Z M130.952381,166.6875 L0,166.6875 L0,95.25 L85.047619,95.25 C118.738095,36.6950625 181.142857,0 250,0 C300.880952,0 348.714286,19.8358125 384.690476,55.8403125 L334.166667,106.346625 C311.690476,83.8438125 281.809524,71.4375 250,71.4375 C201.833333,71.4375 158.666667,100.203 140.119048,144.708563 L130.952381,166.6875 L130.952381,166.6875 L130.952381,166.6875 L130.952381,166.6875 Z M130.952381,166.6875').ySymetry(381).ySymetry(381).encode()
, 'M250 381C199.119048 381 151.285714 361.164188 115.333333 325.159688L165.857143 274.653375C188.333333 297.156188 218.238095 309.5625 250 309.5625C298.214286 309.5625 341.333333 280.797 359.904762 236.291438L369.071429 214.3125L500 214.3125L500 285.75L415 285.75C381.285714 344.304937 318.880952 381 250 381L250 381L250 381L250 381zM130.952381 166.6875L0 166.6875L0 95.25L85.047619 95.25C118.738095 36.695062500000006 181.142857 0 250 0C300.880952 0 348.714286 19.835812499999975 384.690476 55.84031249999998L334.166667 106.34662500000002C311.690476 83.84381250000001 281.809524 71.4375 250 71.4375C201.833333 71.4375 158.666667 100.20299999999997 140.119048 144.708563L130.952381 166.6875L130.952381 166.6875L130.952381 166.6875L130.952381 166.6875zM130.952381 166.6875');
});
it("of sapegin", function() {
assert.equal(new SVGPathData('M77.225 66.837l-18.895-18.895c2.85-4.681 4.49-10.177 4.49-16.058 0-17.081-14.802-31.884-31.888-31.884-17.082 0-30.932 13.85-30.932 30.934s14.803 31.885 31.885 31.885c5.68 0 11-1.538 15.575-4.21l18.996 18.997c1.859 1.859 4.873 1.859 6.73 0l4.713-4.711c1.859-1.86 1.185-4.2-.674-6.058m-67.705-35.903c0-11.828 9.588-21.416 21.412-21.416 11.83 0 22.369 10.539 22.369 22.367s-9.588 21.416-21.417 21.416c-11.825 0-22.364-10.539-22.364-22.367').ySymetry(79).encode(), 'M77.225 12.162999999999997L58.33 31.057999999999993C61.18 35.73899999999999 62.82 41.23499999999999 62.82 47.11599999999999C62.82 64.19699999999999 48.018 79 30.932 79C13.849999999999998 79 0 65.14999999999999 0 48.06599999999999S14.803 16.18099999999999 31.885 16.18099999999999C37.565 16.18099999999999 42.885000000000005 17.718999999999987 47.46 20.39099999999999L66.456 1.3939999999999912C68.315 -0.4650000000000034 71.32900000000001 -0.4650000000000034 73.186 1.3939999999999912L77.899 6.10499999999999C79.758 7.964999999999989 79.084 10.304999999999993 77.225 12.162999999999982M9.519999999999996 48.06599999999998C9.519999999999996 59.89399999999998 19.107999999999997 69.48199999999999 30.931999999999995 69.48199999999999C42.76199999999999 69.48199999999999 53.300999999999995 58.942999999999984 53.300999999999995 47.11499999999998S43.712999999999994 25.698999999999984 31.883999999999993 25.698999999999984C20.058999999999994 25.698999999999984 9.519999999999992 36.237999999999985 9.519999999999992 48.06599999999999');
});
it("of hannesjohansson", function() {
assert.equal(new SVGPathData('M2.25 12.751C2.25 18.265 6.736 22.751 12.25 22.751C14.361 22.751 16.318 22.09 17.933 20.969L25.482 28.518C25.97 29.006 26.61 29.25 27.25 29.25S28.53 29.006 29.018 28.518C29.995 27.542 29.995 25.96 29.018 24.983L21.207 17.172C21.869 15.837 22.251 14.339 22.251 12.751C22.251 7.237 17.765 2.751 12.251 2.751S2.251 7.236 2.25 12.751zM6.251 12.751C6.251 9.442 8.942 6.751 12.251 6.751S18.251 9.442 18.251 12.751S15.56 18.751 12.251 18.751S6.251 16.06 6.251 12.751z').ySymetry(32).ySymetry(32).round(10e10).encode(), 'M2.25 12.751C2.25 18.265 6.736 22.751 12.25 22.751C14.361 22.751 16.318 22.09 17.933 20.969L25.482 28.518C25.97 29.006 26.61 29.25 27.25 29.25S28.53 29.006 29.018 28.518C29.995 27.542 29.995 25.96 29.018 24.983L21.207 17.172C21.869 15.837 22.251 14.339 22.251 12.751C22.251 7.237 17.765 2.751 12.251 2.751S2.251 7.236 2.25 12.751zM6.251 12.751C6.251 9.442 8.942 6.751 12.251 6.751S18.251 9.442 18.251 12.751S15.56 18.751 12.251 18.751S6.251 16.06 6.251 12.751z');
});
it("of my blog", function() {
assert.equal(new SVGPathData('m 0,100 0,10 5,0 0,-5 15,0 0,15 -20,0 0,30 25,0 0,-50 z m 5,25 15,0 0,20 -15,0 z').toAbs().encode(), 'M0 100L0 110L5 110L5 105L20 105L20 120L0 120L0 150L25 150L25 100zM5 125L20 125L20 145L5 145z');
});
it("of tremby bug report", function() {
assert.equal(new SVGPathData('M0,250 l20,0 a40,20 0 0,0 40,20 l80,-20 a40,20 0 0,1 40,20 l80,-20 a40,20 0 1,0 40,20 l80,-20 a40,20 0 1,1 40,20 l80,-20 l0,-120 H0 Z').scale(1, -1).encode(), 'M0 -250l20 0a40 20 0 0 1 40 -20l80 20a40 20 0 0 0 40 -20l80 20a40 20 0 1 1 40 -20l80 20a40 20 0 1 0 40 -20l80 20l0 120H0z');
});
it("of fh1ch bug report", function() {
assert.equal(new SVGPathData('M382.658 327.99c16.71-17.204 26.987-40.676 26.987-66.542 0-52.782-42.792-95.575-95.574-95.575-29.894 0-56.583 13.74-74.104 35.24-17.47-7.164-37.11-9.877-57.725-7.596-44.774 4.964-82.87 38.712-94.42 84.393-2.14 8.447-5.14 13.34-14.276 16.473-26.103 8.952-42.988 35.322-41.446 61.6 1.696 28.703 21.253 52.36 48.917 59.185 1.942.48 3.813.668 5.61 1.048.063 0 .114-.216.166-.216h224.753c.154 0 .31.235.463.216 39.072-1.706 70.56-33.144 71.865-71.815.197-5.66-.225-11.13-1.21-16.472m-63.554 62.75c-2.312.503-4.697.847-7.1 1.166-6.095.83-3.763.156-18.232.156H103.716c-3.113 0-6.207.11-9.29-.044-21.283-1.038-36.646-16.796-37.243-37.185-.617-20.696 13.596-37.283 34.52-39.833 5.365-.646 10.873-.082 16.217-.082 6.186-58.885 31.18-90.46 76.418-96.802 19.834-2.785 38.66.794 56.06 10.825 25.434 14.654 38.69 37.81 44.127 66.47 4.748-1.108 8.355-1.973 11.962-2.796 27.85-6.33 54.868 10.033 61.034 36.958 6.516 28.426-9.844 55.01-38.414 61.168zm8.86-121.502c-4.225-1.07-8.613-1.778-13.125-2.097-1.756-.124-3.35-.34-4.788-.668-6.207-1.4-9.794-4.8-13.124-11.49-.185-.37-.37-.73-.555-1.1-5.333-10.44-11.92-19.68-19.537-27.604l-17.82-14.973-.42-.35c13.616-13.822 34.58-24.47 55.473-24.47 41.363 0 75.02 33.657 75.02 75.022 0 16.452-5.334 31.683-14.357 44.056-9.68-17.788-26.348-31.2-46.768-36.327z').toAbs().round(1000).encode(), 'M382.658 327.99C399.368 310.786 409.645 287.314 409.645 261.448C409.645 208.666 366.853 165.873 314.071 165.873C284.177 165.873 257.488 179.613 239.967 201.113C222.497 193.949 202.857 191.236 182.242 193.517C137.468 198.481 99.372 232.229 87.822 277.91C85.682 286.357 82.682 291.25 73.546 294.383C47.443 303.335 30.558 329.705 32.1 355.983C33.796 384.686 53.353 408.343 81.017 415.168C82.959 415.648 84.83 415.836 86.627 416.216C86.69 416.216 86.741 416 86.793 416H311.546C311.7 416 311.856 416.235 312.009 416.216C351.081 414.51 382.569 383.072 383.874 344.401C384.071 338.741 383.649 333.271 382.664 327.929M319.11 390.679C316.798 391.182 314.413 391.526 312.01 391.845C305.915 392.675 308.247 392.001 293.778 392.001H103.716C100.603 392.001 97.509 392.111 94.426 391.957C73.143 390.919 57.78 375.161 57.183 354.772C56.566 334.076 70.779 317.489 91.703 314.939C97.068 314.293 102.576 314.857 107.92 314.857C114.106 255.972 139.1 224.397 184.338 218.055C204.172 215.27 222.998 218.849 240.398 228.88C265.832 243.534 279.088 266.69 284.525 295.35C289.273 294.242 292.88 293.377 296.487 292.554C324.337 286.224 351.355 302.587 357.521 329.512C364.037 357.938 347.677 384.522 319.107 390.68zM327.97 269.177C323.745 268.107 319.357 267.399 314.845 267.08C313.089 266.956 311.495 266.74 310.057 266.412C303.85 265.012 300.263 261.612 296.933 254.922C296.748 254.552 296.563 254.192 296.378 253.822C291.045 243.382 284.458 234.142 276.841 226.218L259.021 211.245L258.601 210.895C272.217 197.073 293.181 186.425 314.074 186.425C355.437 186.425 389.094 220.082 389.094 261.447C389.094 277.899 383.76 293.13 374.737 305.503C365.057 287.715 348.389 274.303 327.969 269.176z');
});
});

View File

@ -0,0 +1,565 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Converting relative commands to absolute ones", function() {
it("should work with m commands", function() {
var commands = new SVGPathData('m-100,100M10,10m10,10m-1,-1').toAbs().commands;
assert.equal(commands[0].type, SVGPathData.MOVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, -100);
assert.equal(commands[0].y, 100);
assert.equal(commands[1].type, SVGPathData.MOVE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, 10);
assert.equal(commands[1].y, 10);
assert.equal(commands[2].type, SVGPathData.MOVE_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].x, 20);
assert.equal(commands[2].y, 20);
assert.equal(commands[3].type, SVGPathData.MOVE_TO);
assert.equal(commands[3].relative, false);
assert.equal(commands[3].x, 19);
assert.equal(commands[3].y, 19);
});
it("should work with h commands", function() {
var commands = new SVGPathData('h100H10h10h-5').toAbs().commands;
assert.equal(commands[0].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, 100);
assert.equal(commands[1].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, 10);
assert.equal(commands[2].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].x, 20);
assert.equal(commands[3].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[3].relative, false);
assert.equal(commands[3].x, 15);
});
it("should work with v commands", function() {
var commands = new SVGPathData('v100V10v5v5').toAbs().commands;
assert.equal(commands[0].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].y, 100);
assert.equal(commands[1].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].y, 10);
assert.equal(commands[2].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].y, 15);
assert.equal(commands[3].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[3].relative, false);
assert.equal(commands[3].y, 20);
});
it("should work with l commands", function() {
var commands = new SVGPathData('l100,-100L1,0l2,2l-1,-1').toAbs().commands;
assert.equal(commands[0].type, SVGPathData.LINE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, 100);
assert.equal(commands[0].y, -100);
assert.equal(commands[1].type, SVGPathData.LINE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, 1);
assert.equal(commands[1].y, 0);
assert.equal(commands[2].type, SVGPathData.LINE_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].x, 3);
assert.equal(commands[2].y, 2);
assert.equal(commands[3].type, SVGPathData.LINE_TO);
assert.equal(commands[3].relative, false);
assert.equal(commands[3].x, 2);
assert.equal(commands[3].y, 1);
});
it("should work with c commands", function() {
var commands = new SVGPathData('c100,100 100,100 100,100\
c100,100 100,100 100,100\
c100,100 100,100 100,100\
c100,100 100,100 100,100').toAbs().commands;
assert.equal(commands[0].type, SVGPathData.CURVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, 100);
assert.equal(commands[0].y, 100);
assert.equal(commands[0].x1, 100);
assert.equal(commands[0].y1, 100);
assert.equal(commands[0].x2, 100);
assert.equal(commands[0].y2, 100);
assert.equal(commands[1].type, SVGPathData.CURVE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, 200);
assert.equal(commands[1].y, 200);
assert.equal(commands[1].x1, 200);
assert.equal(commands[1].y1, 200);
assert.equal(commands[1].x2, 200);
assert.equal(commands[1].y2, 200);
assert.equal(commands[2].type, SVGPathData.CURVE_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].x, 300);
assert.equal(commands[2].y, 300);
assert.equal(commands[2].x1, 300);
assert.equal(commands[2].y1, 300);
assert.equal(commands[2].x2, 300);
assert.equal(commands[2].y2, 300);
assert.equal(commands[3].type, SVGPathData.CURVE_TO);
assert.equal(commands[3].relative, false);
assert.equal(commands[3].x, 400);
assert.equal(commands[3].y, 400);
assert.equal(commands[3].x1, 400);
assert.equal(commands[3].y1, 400);
assert.equal(commands[3].x2, 400);
assert.equal(commands[3].y2, 400);
});
it("should work with s commands", function() {
var commands = new SVGPathData('s100,100 100,100\
s100,100 100,100s100,100 100,100s100,100 100,100').toAbs().commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, 100);
assert.equal(commands[0].y, 100);
assert.equal(commands[0].x2, 100);
assert.equal(commands[0].y2, 100);
assert.equal(commands[1].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, 200);
assert.equal(commands[1].y, 200);
assert.equal(commands[1].x2, 200);
assert.equal(commands[1].y2, 200);
assert.equal(commands[2].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].x, 300);
assert.equal(commands[2].y, 300);
assert.equal(commands[2].x2, 300);
assert.equal(commands[2].y2, 300);
assert.equal(commands[3].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[3].relative, false);
assert.equal(commands[3].x, 400);
assert.equal(commands[3].y, 400);
assert.equal(commands[3].x2, 400);
assert.equal(commands[3].y2, 400);
});
it("should work with q commands", function() {
var commands = new SVGPathData('q-100,100 -100,100q-100,100 -100,100\
q-100,100 -100,100q-100,100 -100,100').toAbs().commands;
assert.equal(commands[0].type, SVGPathData.QUAD_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, -100);
assert.equal(commands[0].y, 100);
assert.equal(commands[0].x1, -100);
assert.equal(commands[0].y1, 100);
assert.equal(commands[1].type, SVGPathData.QUAD_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, -200);
assert.equal(commands[1].y, 200);
assert.equal(commands[1].x1, -200);
assert.equal(commands[1].y1, 200);
assert.equal(commands[2].type, SVGPathData.QUAD_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].x, -300);
assert.equal(commands[2].y, 300);
assert.equal(commands[2].x1, -300);
assert.equal(commands[2].y1, 300);
assert.equal(commands[3].type, SVGPathData.QUAD_TO);
assert.equal(commands[3].relative, false);
assert.equal(commands[3].x, -400);
assert.equal(commands[3].y, 400);
assert.equal(commands[3].x1, -400);
assert.equal(commands[3].y1, 400);
});
it("should work with t commands", function() {
var commands = new SVGPathData('t-100,100t-100,100t10,10t10,10').toAbs().commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, -100);
assert.equal(commands[0].y, 100);
assert.equal(commands[1].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, -200);
assert.equal(commands[1].y, 200);
assert.equal(commands[2].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].x, -190);
assert.equal(commands[2].y, 210);
assert.equal(commands[3].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[3].relative, false);
assert.equal(commands[3].x, -180);
assert.equal(commands[3].y, 220);
});
it("should work with a commands", function() {
var commands = new SVGPathData('a20,20 180 1 0 -100,100\
a20,20 180 1 0 -100,100a20,20 180 1 0 -100,100\
a20,20 180 1 0 -100,100').toAbs().commands;
assert.equal(commands[0].type, SVGPathData.ARC);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].rX, 20);
assert.equal(commands[0].rY, 20);
assert.equal(commands[0].xRot, 180);
assert.equal(commands[0].lArcFlag, 1);
assert.equal(commands[0].sweepFlag, 0);
assert.equal(commands[0].x, -100);
assert.equal(commands[0].y, 100);
assert.equal(commands[1].type, SVGPathData.ARC);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].rX, 20);
assert.equal(commands[1].rY, 20);
assert.equal(commands[1].xRot, 180);
assert.equal(commands[1].lArcFlag, 1);
assert.equal(commands[1].sweepFlag, 0);
assert.equal(commands[1].x, -200);
assert.equal(commands[1].y, 200);
assert.equal(commands[2].type, SVGPathData.ARC);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].rX, 20);
assert.equal(commands[2].rY, 20);
assert.equal(commands[2].xRot, 180);
assert.equal(commands[2].lArcFlag, 1);
assert.equal(commands[2].sweepFlag, 0);
assert.equal(commands[2].x, -300);
assert.equal(commands[2].y, 300);
assert.equal(commands[3].type, SVGPathData.ARC);
assert.equal(commands[3].relative, false);
assert.equal(commands[3].rX, 20);
assert.equal(commands[3].rY, 20);
assert.equal(commands[3].xRot, 180);
assert.equal(commands[3].lArcFlag, 1);
assert.equal(commands[3].sweepFlag, 0);
assert.equal(commands[3].x, -400);
assert.equal(commands[3].y, 400);
});
it("should work with nested commands", function() {
var commands = new SVGPathData('a20,20 180 1 0 -100,100h10v10l10,10\
c10,10 20,20 100,100').toAbs().commands;
assert.equal(commands[0].type, SVGPathData.ARC);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].rX, 20);
assert.equal(commands[0].rY, 20);
assert.equal(commands[0].xRot, 180);
assert.equal(commands[0].lArcFlag, 1);
assert.equal(commands[0].sweepFlag, 0);
assert.equal(commands[0].x, -100);
assert.equal(commands[0].y, 100);
assert.equal(commands[1].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, -90);
assert.equal(commands[2].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].y, 110);
assert.equal(commands[3].type, SVGPathData.LINE_TO);
assert.equal(commands[3].relative, false);
assert.equal(commands[3].x, -80);
assert.equal(commands[3].y, 120);
assert.equal(commands[4].type, SVGPathData.CURVE_TO);
assert.equal(commands[4].relative, false);
assert.equal(commands[4].x, 20);
assert.equal(commands[4].y, 220);
assert.equal(commands[4].x1, -60);
assert.equal(commands[4].y1, 140);
assert.equal(commands[4].x2, -70);
assert.equal(commands[4].y2, 130);
});
});
describe("Converting absolute commands to relative ones", function() {
it("should work with M commands", function() {
var commands = new SVGPathData('M100,100M110,90M120,80M130,70').toRel().commands;
assert.equal(commands[0].type, SVGPathData.MOVE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, 100);
assert.equal(commands[0].y, 100);
assert.equal(commands[1].type, SVGPathData.MOVE_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x, 10);
assert.equal(commands[1].y, -10);
assert.equal(commands[2].type, SVGPathData.MOVE_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].x, 10);
assert.equal(commands[2].y, -10);
assert.equal(commands[3].type, SVGPathData.MOVE_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, 10);
assert.equal(commands[3].y, -10);
});
it("should work with M commands", function() {
var commands = new SVGPathData('M-100,100m90,-90M20,20M19,19').toRel().commands;
assert.equal(commands[0].type, SVGPathData.MOVE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, -100);
assert.equal(commands[0].y, 100);
assert.equal(commands[1].type, SVGPathData.MOVE_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x, 90);
assert.equal(commands[1].y, -90);
assert.equal(commands[2].type, SVGPathData.MOVE_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].x, 30);
assert.equal(commands[2].y, 10);
assert.equal(commands[3].type, SVGPathData.MOVE_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, -1);
assert.equal(commands[3].y, -1);
});
it("should work with H commands", function() {
var commands = new SVGPathData('H100H10H20H15').toRel().commands;
assert.equal(commands[0].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, 100);
assert.equal(commands[1].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x, -90);
assert.equal(commands[2].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].x, 10);
assert.equal(commands[3].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, -5);
});
it("should work with V commands", function() {
var commands = new SVGPathData('V100V10V15V20').toRel().commands;
assert.equal(commands[0].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].y, 100);
assert.equal(commands[1].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].y, -90);
assert.equal(commands[2].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].y, 5);
assert.equal(commands[3].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].y, 5);
});
it("should work with L commands", function() {
var commands = new SVGPathData('L100,-100L1,0L3,2L2,1').toRel().commands;
assert.equal(commands[0].type, SVGPathData.LINE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, 100);
assert.equal(commands[0].y, -100);
assert.equal(commands[1].type, SVGPathData.LINE_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x, -99);
assert.equal(commands[1].y, 100);
assert.equal(commands[2].type, SVGPathData.LINE_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].x, 2);
assert.equal(commands[2].y, 2);
assert.equal(commands[3].type, SVGPathData.LINE_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, -1);
assert.equal(commands[3].y, -1);
});
it("should work with C commands", function() {
var commands = new SVGPathData('C100,100 100,100 100,100\
C200,200 200,200 200,200\
C300,300 300,300 300,300\
C400,400 400,400 400,400').toRel().commands;
assert.equal(commands[0].type, SVGPathData.CURVE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, 100);
assert.equal(commands[0].y, 100);
assert.equal(commands[0].x1, 100);
assert.equal(commands[0].y1, 100);
assert.equal(commands[0].x2, 100);
assert.equal(commands[0].y2, 100);
assert.equal(commands[1].type, SVGPathData.CURVE_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x, 100);
assert.equal(commands[1].y, 100);
assert.equal(commands[1].x1, 100);
assert.equal(commands[1].y1, 100);
assert.equal(commands[1].x2, 100);
assert.equal(commands[1].y2, 100);
assert.equal(commands[2].type, SVGPathData.CURVE_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].x, 100);
assert.equal(commands[2].y, 100);
assert.equal(commands[2].x1, 100);
assert.equal(commands[2].y1, 100);
assert.equal(commands[2].x2, 100);
assert.equal(commands[2].y2, 100);
assert.equal(commands[3].type, SVGPathData.CURVE_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, 100);
assert.equal(commands[3].y, 100);
assert.equal(commands[3].x1, 100);
assert.equal(commands[3].y1, 100);
assert.equal(commands[3].x2, 100);
assert.equal(commands[3].y2, 100);
});
it("should work with S commands", function() {
var commands = new SVGPathData('S100,100 100,100\
S200,200 200,200S300,300 300,300S400,400 400,400').toRel().commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, 100);
assert.equal(commands[0].y, 100);
assert.equal(commands[0].x2, 100);
assert.equal(commands[0].y2, 100);
assert.equal(commands[1].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x, 100);
assert.equal(commands[1].y, 100);
assert.equal(commands[1].x2, 100);
assert.equal(commands[1].y2, 100);
assert.equal(commands[2].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].x, 100);
assert.equal(commands[2].y, 100);
assert.equal(commands[2].x2, 100);
assert.equal(commands[2].y2, 100);
assert.equal(commands[3].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, 100);
assert.equal(commands[3].y, 100);
assert.equal(commands[3].x2, 100);
assert.equal(commands[3].y2, 100);
});
it("should work with Q commands", function() {
var commands = new SVGPathData('Q-100,100 -100,100Q-200,200 -200,200\
Q-300,300 -300,300Q-400,400 -400,400').toRel().commands;
assert.equal(commands[0].type, SVGPathData.QUAD_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, -100);
assert.equal(commands[0].y, 100);
assert.equal(commands[0].x1, -100);
assert.equal(commands[0].y1, 100);
assert.equal(commands[1].type, SVGPathData.QUAD_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x, -100);
assert.equal(commands[1].y, 100);
assert.equal(commands[1].x1, -100);
assert.equal(commands[1].y1, 100);
assert.equal(commands[2].type, SVGPathData.QUAD_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].x, -100);
assert.equal(commands[2].y, 100);
assert.equal(commands[2].x1, -100);
assert.equal(commands[2].y1, 100);
assert.equal(commands[3].type, SVGPathData.QUAD_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, -100);
assert.equal(commands[3].y, 100);
assert.equal(commands[3].x1, -100);
assert.equal(commands[3].y1, 100);
});
it("should work with T commands", function() {
var commands = new SVGPathData('T-100,100T-200,200T-190,210T-180,220').toRel().commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, -100);
assert.equal(commands[0].y, 100);
assert.equal(commands[1].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x, -100);
assert.equal(commands[1].y, 100);
assert.equal(commands[2].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].x, 10);
assert.equal(commands[2].y, 10);
assert.equal(commands[3].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, 10);
assert.equal(commands[3].y, 10);
});
it("should work with A commands", function() {
var commands = new SVGPathData('A20,20 180 1 0 -100,100\
A20,20 180 1 0 -200,200A20,20 180 1 0 -300,300\
A20,20 180 1 0 -400,400').toRel().commands;
assert.equal(commands[0].type, SVGPathData.ARC);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].rX, 20);
assert.equal(commands[0].rY, 20);
assert.equal(commands[0].xRot, 180);
assert.equal(commands[0].lArcFlag, 1);
assert.equal(commands[0].sweepFlag, 0);
assert.equal(commands[0].x, -100);
assert.equal(commands[0].y, 100);
assert.equal(commands[1].type, SVGPathData.ARC);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].rX, 20);
assert.equal(commands[1].rY, 20);
assert.equal(commands[1].xRot, 180);
assert.equal(commands[1].lArcFlag, 1);
assert.equal(commands[1].sweepFlag, 0);
assert.equal(commands[1].x, -100);
assert.equal(commands[1].y, 100);
assert.equal(commands[2].type, SVGPathData.ARC);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].rX, 20);
assert.equal(commands[2].rY, 20);
assert.equal(commands[2].xRot, 180);
assert.equal(commands[2].lArcFlag, 1);
assert.equal(commands[2].sweepFlag, 0);
assert.equal(commands[2].x, -100);
assert.equal(commands[2].y, 100);
assert.equal(commands[3].type, SVGPathData.ARC);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].rX, 20);
assert.equal(commands[3].rY, 20);
assert.equal(commands[3].xRot, 180);
assert.equal(commands[3].lArcFlag, 1);
assert.equal(commands[3].sweepFlag, 0);
assert.equal(commands[3].x, -100);
assert.equal(commands[3].y, 100);
});
it("should work with nested commands", function() {
var commands = new SVGPathData('A20,20 180 1 0 -100,100H-90V110L20,220\
C10,10 20,20 20,220').toRel().commands;
assert.equal(commands[0].type, SVGPathData.ARC);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].rX, 20);
assert.equal(commands[0].rY, 20);
assert.equal(commands[0].xRot, 180);
assert.equal(commands[0].lArcFlag, 1);
assert.equal(commands[0].sweepFlag, 0);
assert.equal(commands[0].x, -100);
assert.equal(commands[0].y, 100);
assert.equal(commands[1].type, SVGPathData.HORIZ_LINE_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x, 10);
assert.equal(commands[2].type, SVGPathData.VERT_LINE_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].y, 10);
assert.equal(commands[3].type, SVGPathData.LINE_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, 110);
assert.equal(commands[3].y, 110);
assert.equal(commands[4].type, SVGPathData.CURVE_TO);
assert.equal(commands[4].relative, true);
assert.equal(commands[4].x, 0);
assert.equal(commands[4].y, 0);
assert.equal(commands[4].x1, 0);
assert.equal(commands[4].y1, -200);
assert.equal(commands[4].x2, -10);
assert.equal(commands[4].y2, -210);
});
});

View File

@ -0,0 +1,102 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Positive rotate from the origin", function() {
it("should fail with no args", function() {
assert.throws(function() {
new SVGPathData(
'm20,30l10,10z'
).rotate().encode();
}, 'A rotate transformation requires the parameter a'
+' to be set and to be a number.');
});
it("should work with relative horinzontal path", function() {
assert.equal(new SVGPathData(
'm10 0l60 0z'
).rotate(Math.PI).round(6).encode(),
'm-10 0l-60 0z');
});
it("should work with relative vertical path", function() {
assert.equal(new SVGPathData(
'm0 10l0 60z'
).rotate(Math.PI).round(6).encode(),
'm0 -10l0 -60z');
});
it("should work with relative path", function() {
assert.equal(new SVGPathData(
'm75 100l0 -50z'
).rotate(Math.PI).round(6).encode(),
'm-75 -100l0 50z');
});
it("should work with absolute path", function() {
assert.equal(new SVGPathData(
'M75,100L75,50z'
).rotate(Math.PI).round(6).encode(),
'M-75 -100L-75 -50z');
});
});
describe("Positive rotate", function() {
it("should work with relative path (Math.PI)", function() {
assert.equal(new SVGPathData(
'm100 100l100 100z'
).rotate(Math.PI, 150, 150).round(6).encode(),
'm200 200l-100 -100z');
});
it("should work with relative path (Math.PI/2)", function() {
assert.equal(new SVGPathData(
'm100 100l100 100z'
).rotate(Math.PI/2, 150, 150).round(6).encode(),
'm200 100l-100 100z');
});
it("should work with relative path", function() {
assert.equal(new SVGPathData(
'm75 100l0 -50z'
).rotate(Math.PI, 75, 75).round(6).encode(),
'm75 50l0 50z');
});
it("should work with absolute path", function() {
assert.equal(new SVGPathData(
'M75,100L75,50z'
).rotate(Math.PI, 75, 75).round(6).encode(),
'M75 50L75 100z');
});
});
describe("360° Positive rotate", function() {
it("should work with relative path", function() {
assert.equal(new SVGPathData(
'm100 75l-50 -45l0 90z'
).rotate(2*Math.PI, 75, 75).round(6).encode(),
'm100 75l-50 -45l0 90z');
});
it("should work with absolute path", function() {
assert.equal(new SVGPathData(
'M 100,75L50,30L50,120 z'
).rotate(2*Math.PI, 75, 75).round(6).encode(),
'M100 75L50 30L50 120z');
});
});

View File

@ -0,0 +1,23 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Path rounding", function() {
it("should work", function() {
assert.equal(new SVGPathData(
'M137.5 140C137.5 140 129.86983 107.04527999999999 120.28202 94.914299C120.28202 94.914299 111.67409 87.871064 107.37013999999999 87.284128C103.06618 86.69719099999999 76.261082 86.11025599999999 76.261082 86.11025599999999L68.239383 76.289234H64.522357L63.348485 60.47904L66.870103 56.762015999999996L68.434791 46.979513L67.847854 42.479433H66.087046C66.087046 42.479433 64.948119 29.761523 65.109294 28.391533000000003C65.269757 27.022253000000003 60.197715 12.206213000000002 47.48052000000001 10.215053000000001C34.76333000000001 8.223173000000001 12.446920000000006 20.723563 12.446920000000006 20.723563L13.652170000000012 42.282593L15.999920000000003 48.935013L14.63064 49.326543C14.63064 49.326543 14.826040000000006 53.043563 16.391449999999992 55.586716C16.391449999999992 55.586716 18.543779999999998 66.34769 24.021619999999984 66.543098L28.326289999999986 79.064881L32.04331999999998 83.174148L34.97799999999998 91.78207L20.30458999999999 109.587L19.130719999999997 119.95644C19.130719999999997 119.95644 10.603379999999987 127.56165 12.812780000000004 139.99858H137.5z'
).round(10e12).encode(),
'M137.5 140C137.5 140 129.86983 107.04528 120.28202 94.914299C120.28202 94.914299 111.67409 87.871064 107.37014 87.284128C103.06618 86.697191 76.261082 86.110256 76.261082 86.110256L68.239383 76.289234H64.522357L63.348485 60.47904L66.870103 56.762016L68.434791 46.979513L67.847854 42.479433H66.087046C66.087046 42.479433 64.948119 29.761523 65.109294 28.391533C65.269757 27.022253 60.197715 12.206213 47.48052 10.215053C34.76333 8.223173 12.44692 20.723563 12.44692 20.723563L13.65217 42.282593L15.99992 48.935013L14.63064 49.326543C14.63064 49.326543 14.82604 53.043563 16.39145 55.586716C16.39145 55.586716 18.54378 66.34769 24.02162 66.543098L28.32629 79.064881L32.04332 83.174148L34.978 91.78207L20.30459 109.587L19.13072 119.95644C19.13072 119.95644 10.60338 127.56165 12.81278 139.99858H137.5z');
});
});

View File

@ -0,0 +1,38 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Positive scale", function() {
it("should fail with no args", function() {
assert.throws(function() {
new SVGPathData(
'm20,30l10,10z'
).scale().encode();
}, 'A scale transformation requires the parameter dX'
+' to be set and to be a number.');
});
it("should work with relative path", function() {
assert.equal(new SVGPathData(
'm20 30c0 0 10 20 15 30z'
).scale(10, 10).encode(),
'm200 300c0 0 100 200 150 300z');
});
it("should work with absolute path", function() {
assert.equal(new SVGPathData(
'M20 30C0 0 10 20 15 30z'
).scale(10, 10).encode(),
'M200 300C0 0 100 200 150 300z');
});
});

View File

@ -0,0 +1,63 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("X axis skew", function() {
it("should fail with bad args", function() {
assert.throws(function() {
new SVGPathData(
'm20,30l10,10z'
).skewX().encode();
}, 'A skewX transformation requires the parameter x'
+' to be set and to be a number.');
});
it("should work with relative path", function() {
assert.equal(new SVGPathData(
'm100 75l-50 -45l0 90z'
).skewX(Math.PI/2).encode(),
'm175.29136163904155 75l-95.17481698342493 -45l90.34963396684985 90z');
});
it("should work with absolute path", function() {
assert.equal(new SVGPathData(
'M 100,75 50,30 50,120 z'
).skewX(Math.PI/2).encode(),
'M175.29136163904155 75L80.11654465561662 30L170.46617862246646 120z');
});
});
describe("Y axis skew", function() {
it("should fail with bad args", function() {
assert.throws(function() {
new SVGPathData(
'm20,30l10,10z'
).skewY().encode();
}, 'A skewY transformation requires the parameter y'
+' to be set and to be a number.');
});
it("should work with relative path", function() {
assert.equal(new SVGPathData(
'm100 75l-50 -45l0 90z'
).skewY(Math.PI/2).encode(),
'm100 175.3884821853887l-50 -95.19424109269436l0 90z');
});
it("should work with absolute path", function() {
assert.equal(new SVGPathData(
'M 100,75 50,30 50,120 z'
).skewY(Math.PI/2).encode(),
'M100 175.3884821853887L50 80.19424109269436L50 170.19424109269437z');
});
});

View File

@ -0,0 +1,116 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Parsing smooth curve to commands", function() {
beforeEach(function() {
});
afterEach(function() {
});
it("should not work when badly declarated", function() {
assert.throw(function() {
new SVGPathData('S');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('S10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('S10 10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('S10 10 10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('S10 10 10 10 10 10');
}, SyntaxError, 'Unterminated command at the path end.');
assert.throw(function() {
new SVGPathData('S10 10 10S10 10 10 10');
}, SyntaxError, 'Unterminated command at index 9.');
});
it("should work with comma separated coordinates", function() {
var commands = new SVGPathData('S123,456 789,987').commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x2, '123');
assert.equal(commands[0].y2, '456');
assert.equal(commands[0].x, '789');
assert.equal(commands[0].y, '987');
});
it("should work with space separated coordinates", function() {
var commands = new SVGPathData('S123 456 789 987').commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x2, '123');
assert.equal(commands[0].y2, '456');
assert.equal(commands[0].x, '789');
assert.equal(commands[0].y, '987');
});
it("should work with nested separated complexer coordinate pairs", function() {
var commands = new SVGPathData('S-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5').commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x2, '-10.0032e-5');
assert.equal(commands[0].y2, '-20.0032e-5');
assert.equal(commands[0].x, '-30.0032e-5');
assert.equal(commands[0].y, '-40.0032e-5');
});
it("should work with multiple pairs of coordinates", function() {
var commands = new SVGPathData('S-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 -10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5').commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x2, '-10.0032e-5');
assert.equal(commands[0].y2, '-20.0032e-5');
assert.equal(commands[0].x, '-30.0032e-5');
assert.equal(commands[0].y, '-40.0032e-5');
assert.equal(commands[1].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x2, '-10.0032e-5');
assert.equal(commands[1].y2, '-20.0032e-5');
assert.equal(commands[1].x, '-30.0032e-5');
assert.equal(commands[1].y, '-40.0032e-5');
assert.equal(commands[2].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].x2, '-10.0032e-5');
assert.equal(commands[2].y2, '-20.0032e-5');
assert.equal(commands[2].x, '-30.0032e-5');
assert.equal(commands[2].y, '-40.0032e-5');
});
it("should work with multiple declarated pairs of coordinates", function() {
var commands = new SVGPathData('S-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 s-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5 S-10.0032e-5,-20.0032e-5 -30.0032e-5,-40.0032e-5').commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x2, '-10.0032e-5');
assert.equal(commands[0].y2, '-20.0032e-5');
assert.equal(commands[0].x, '-30.0032e-5');
assert.equal(commands[0].y, '-40.0032e-5');
assert.equal(commands[1].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x2, '-10.0032e-5');
assert.equal(commands[1].y2, '-20.0032e-5');
assert.equal(commands[1].x, '-30.0032e-5');
assert.equal(commands[1].y, '-40.0032e-5');
assert.equal(commands[2].type, SVGPathData.SMOOTH_CURVE_TO);
assert.equal(commands[2].relative, false);
assert.equal(commands[2].x2, '-10.0032e-5');
assert.equal(commands[2].y2, '-20.0032e-5');
assert.equal(commands[2].x, '-30.0032e-5');
assert.equal(commands[2].y, '-40.0032e-5');
});
});

View File

@ -0,0 +1,140 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Parsing smooth quadratic curve to commands", function() {
beforeEach(function() {
});
afterEach(function() {
});
it("should fail with a with single coordinate", function() {
assert.throw(function() {
new SVGPathData('T100');
}, SyntaxError, 'Unterminated command at the path end.');
});
it("should fail with a single complexer coordinate", function() {
assert.throw(function() {
new SVGPathData('t-10e-5');
}, SyntaxError, 'Unterminated command at the path end.');
});
it("should work with comma separated coordinates", function() {
var commands = new SVGPathData('T100,100').commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '100');
assert.equal(commands[0].y, '100');
});
it("should work with space separated coordinates", function() {
var commands = new SVGPathData('t100 \t 100').commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '100');
assert.equal(commands[0].y, '100');
});
it("should work with complexer coordinates", function() {
var commands = new SVGPathData('t-10e-5 -10e-5').commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '-10e-5');
assert.equal(commands[0].y, '-10e-5');
});
it("should work with even more complexer coordinates", function() {
var commands = new SVGPathData('T-10.0032e-5 -10.0032e-5').commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '-10.0032e-5');
assert.equal(commands[0].y, '-10.0032e-5');
});
it("should work with comma separated coordinate pairs", function() {
var commands = new SVGPathData('T123,456 7890,9876').commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
});
it("should work with space separated coordinate pairs", function() {
var commands = new SVGPathData('t123 \t 456 \n 7890 \r 9876').commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[0].relative, true);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[1].relative, true);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
});
it("should work with nested separated coordinates", function() {
var commands = new SVGPathData('T123 , 456 \t,\n7890 \r\n 9876').commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
});
it("should work with multiple command declarations", function() {
var commands = new SVGPathData('T123 , 456 \t,\n7890 \r\n\
9876t123 , 456 \t,\n7890 \r\n 9876').commands;
assert.equal(commands[0].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[0].relative, false);
assert.equal(commands[0].x, '123');
assert.equal(commands[0].y, '456');
assert.equal(commands[1].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[1].relative, false);
assert.equal(commands[1].x, '7890');
assert.equal(commands[1].y, '9876');
assert.equal(commands[2].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[2].relative, true);
assert.equal(commands[2].x, '123');
assert.equal(commands[2].y, '456');
assert.equal(commands[3].type, SVGPathData.SMOOTH_QUAD_TO);
assert.equal(commands[3].relative, true);
assert.equal(commands[3].x, '7890');
assert.equal(commands[3].y, '9876');
});
});
describe("Encoding smooth quadratic bezier curve to commands", function() {
it("should work with one command", function() {
assert.equal(
new SVGPathData('T-50.0032e-5 -60.0032e-5').encode(),
'T-0.000500032 -0.000600032'
);
});
it("should work with several commands", function() {
assert.equal(
new SVGPathData('T-50.0032e-5 -60.0032e-5t-50.0032e-5 -60.0032e-5T-50.0032e-5 -60.0032e-5 -50.0032e-5 -60.0032e-5').encode(),
'T-0.000500032 -0.000600032t-0.000500032 -0.000600032T-0.000500032 -0.000600032T-0.000500032 -0.000600032'
);
});
});

View File

@ -0,0 +1,48 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("X axis symetry", function() {
it("should work with an arbitrary path", function() {
assert.equal(new SVGPathData(
'm12.5 140c0 0 7.63017 -32.95472 17.21798 -45.085701c0 0 8.60793 -7.043235 12.91188 -7.630171c4.30396 -0.586937 31.109058 -1.173872 31.109058 -1.173872l8.021699 -9.821022h3.717026l1.173872 -15.810194l-3.521618 -3.717024l-1.564688 -9.782503l0.586937 -4.50008h1.760808c0 0 1.138927 -12.71791 0.977752 -14.0879c-0.160463 -1.36928 4.911579 -16.18532 17.628774 -18.17648c12.71719 -1.99188 35.0336 10.50851 35.0336 10.50851l-1.20525 21.55903l-2.34775 6.65242l1.36928 0.39153c0 0 -0.1954 3.71702 -1.76081 6.260173c0 0 -2.15233 10.760974 -7.63017 10.956382l-4.30467 12.521783l-3.71703 4.109267l-2.93468 8.607922l14.67341 17.80493l1.17387 10.36944c0 0 8.52734 7.60521 6.31794 20.04214h-124.68722z'
).xSymetry(150).encode(),
'M137.5 140C137.5 140 129.86983 107.04527999999999 120.28202 94.914299C120.28202 94.914299 111.67409 87.871064 107.37013999999999 87.284128C103.06618 86.69719099999999 76.261082 86.11025599999999 76.261082 86.11025599999999L68.239383 76.289234H64.522357L63.348485 60.47904L66.870103 56.762015999999996L68.434791 46.979513L67.847854 42.479433H66.087046C66.087046 42.479433 64.948119 29.761523 65.109294 28.391533000000003C65.269757 27.022253000000003 60.197715 12.206213000000002 47.48052000000001 10.215053000000001C34.76333000000001 8.223173000000001 12.446920000000006 20.723563 12.446920000000006 20.723563L13.652170000000012 42.282593L15.999920000000003 48.935013L14.63064 49.326543C14.63064 49.326543 14.826040000000006 53.043563 16.391449999999992 55.586716C16.391449999999992 55.586716 18.543779999999998 66.34769 24.021619999999984 66.543098L28.326289999999986 79.064881L32.04331999999998 83.174148L34.97799999999998 91.78207L20.30458999999999 109.587L19.130719999999997 119.95644C19.130719999999997 119.95644 10.603379999999987 127.56165 12.812780000000004 139.99858H137.5z');
});
it("should work when reversed", function() {
assert.equal(new SVGPathData(
'm12.5 140c0 0 7.63017 -32.95472 17.21798 -45.085701c0 0 8.60793 -7.043235 12.91188 -7.630171c4.30396 -0.586937 31.109058 -1.173872 31.109058 -1.173872l8.021699 -9.821022h3.717026l1.173872 -15.810194l-3.521618 -3.717024l-1.564688 -9.782503l0.586937 -4.50008h1.760808c0 0 1.138927 -12.71791 0.977752 -14.0879c-0.160463 -1.36928 4.911579 -16.18532 17.628774 -18.17648c12.71719 -1.99188 35.0336 10.50851 35.0336 10.50851l-1.20525 21.55903l-2.34775 6.65242l1.36928 0.39153c0 0 -0.1954 3.71702 -1.76081 6.260173c0 0 -2.15233 10.760974 -7.63017 10.956382l-4.30467 12.521783l-3.71703 4.109267l-2.93468 8.607922l14.67341 17.80493l1.17387 10.36944c0 0 8.52734 7.60521 6.31794 20.04214h-124.68722z'
).xSymetry(150).xSymetry(150).encode(),
'M12.5 140C12.5 140 20.130169999999993 107.04527999999999 29.717979999999997 94.914299C29.717979999999997 94.914299 38.32590999999999 87.871064 42.62986000000001 87.284128C46.93382 86.69719099999999 73.738918 86.11025599999999 73.738918 86.11025599999999L81.760617 76.289234H85.477643L86.651515 60.47904L83.129897 56.762015999999996L81.565209 46.979513L82.152146 42.479433H83.912954C83.912954 42.479433 85.051881 29.761523 84.890706 28.391533000000003C84.730243 27.022253000000003 89.802285 12.206213000000002 102.51947999999999 10.215053000000001C115.23666999999999 8.223173000000001 137.55308 20.723563 137.55308 20.723563L136.34783 42.282593L134.00008 48.935013L135.36936 49.326543C135.36936 49.326543 135.17396 53.043563 133.60855 55.586716C133.60855 55.586716 131.45622 66.34769 125.97838000000002 66.543098L121.67371000000001 79.064881L117.95668000000002 83.174148L115.02200000000002 91.78207L129.69541 109.587L130.86928 119.95644C130.86928 119.95644 139.39662 127.56165 137.18722 139.99858H12.5z');
});
});
describe("Y axis symetry", function() {
it("should work with an arbitrary path", function() {
assert.equal(new SVGPathData(
'm12.5 140c0 0 7.63017 -32.95472 17.21798 -45.085701c0 0 8.60793 -7.043235 12.91188 -7.630171c4.30396 -0.586937 31.109058 -1.173872 31.109058 -1.173872l8.021699 -9.821022h3.717026l1.173872 -15.810194l-3.521618 -3.717024l-1.564688 -9.782503l0.586937 -4.50008h1.760808c0 0 1.138927 -12.71791 0.977752 -14.0879c-0.160463 -1.36928 4.911579 -16.18532 17.628774 -18.17648c12.71719 -1.99188 35.0336 10.50851 35.0336 10.50851l-1.20525 21.55903l-2.34775 6.65242l1.36928 0.39153c0 0 -0.1954 3.71702 -1.76081 6.260173c0 0 -2.15233 10.760974 -7.63017 10.956382l-4.30467 12.521783l-3.71703 4.109267l-2.93468 8.607922l14.67341 17.80493l1.17387 10.36944c0 0 8.52734 7.60521 6.31794 20.04214h-124.68722z'
).ySymetry(150).encode(),
'M12.5 10C12.5 10 20.13017 42.95472000000001 29.71798 55.085701C29.71798 55.085701 38.32591 62.128935999999996 42.62986 62.715872000000005C46.93382 63.30280900000001 73.738918 63.88974400000001 73.738918 63.88974400000001L81.760617 73.710766H85.477643L86.651515 89.52096L83.129897 93.23798400000001L81.565209 103.020487L82.152146 107.520567H83.912954C83.912954 107.520567 85.051881 120.238477 84.890706 121.60846699999999C84.730243 122.977747 89.802285 137.793787 102.51947999999999 139.784947C115.23666999999999 141.776827 137.55308 129.276437 137.55308 129.276437L136.34783 107.71740700000001L134.00008 101.064987L135.36936 100.673457C135.36936 100.673457 135.17396 96.956437 133.60855 94.413284C133.60855 94.413284 131.45622 83.65231 125.97838000000002 83.456902L121.67371000000001 70.935119L117.95668000000002 66.825852L115.02200000000002 58.217929999999996L129.69541 40.413L130.86928 30.04356C130.86928 30.04356 139.39662 22.43835 137.18722 10.001419999999996H12.5z');
});
it("should work when reversed", function() {
assert.equal(new SVGPathData(
'm12.5 140c0 0 7.63017 -32.95472 17.21798 -45.085701c0 0 8.60793 -7.043235 12.91188 -7.630171c4.30396 -0.586937 31.109058 -1.173872 31.109058 -1.173872l8.021699 -9.821022h3.717026l1.173872 -15.810194l-3.521618 -3.717024l-1.564688 -9.782503l0.586937 -4.50008h1.760808c0 0 1.138927 -12.71791 0.977752 -14.0879c-0.160463 -1.36928 4.911579 -16.18532 17.628774 -18.17648c12.71719 -1.99188 35.0336 10.50851 35.0336 10.50851l-1.20525 21.55903l-2.34775 6.65242l1.36928 0.39153c0 0 -0.1954 3.71702 -1.76081 6.260173c0 0 -2.15233 10.760974 -7.63017 10.956382l-4.30467 12.521783l-3.71703 4.109267l-2.93468 8.607922l14.67341 17.80493l1.17387 10.36944c0 0 8.52734 7.60521 6.31794 20.04214h-124.68722z'
).ySymetry(150).ySymetry(150).encode(),
'M12.5 140C12.5 140 20.13017 107.04527999999999 29.71798 94.914299C29.71798 94.914299 38.32591 87.871064 42.62986 87.284128C46.93382 86.69719099999999 73.738918 86.11025599999999 73.738918 86.11025599999999L81.760617 76.289234H85.477643L86.651515 60.47904L83.129897 56.76201599999999L81.565209 46.979513L82.152146 42.479433H83.912954C83.912954 42.479433 85.051881 29.761522999999997 84.890706 28.39153300000001C84.730243 27.022253000000006 89.802285 12.206212999999991 102.51947999999999 10.215053000000012C115.23666999999999 8.223173000000003 137.55308 20.723563000000013 137.55308 20.723563000000013L136.34783 42.28259299999999L134.00008 48.935013L135.36936 49.326543C135.36936 49.326543 135.17396 53.043563000000006 133.60855 55.586715999999996C133.60855 55.586715999999996 131.45622 66.34769 125.97838000000002 66.543098L121.67371000000001 79.064881L117.95668000000002 83.174148L115.02200000000002 91.78207L129.69541 109.587L130.86928 119.95644C130.86928 119.95644 139.39662 127.56165 137.18722 139.99858H12.5z');
});
});

View File

@ -0,0 +1,44 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("SVGPathDataTransformer", function() {
it("should fail with bad args", function() {
assert.throws(function() {
new SVGPathData.Transformer();
}, 'Please provide a transform callback to receive commands.');
});
it("should fail with bad transform function", function() {
assert.throws(function() {
new SVGPathData.Transformer(function() {});
}, 'Please provide a valid transform (returning a function).');
});
it("should still work when the new operator is forgotten", function() {
assert.doesNotThrow(function() {
SVGPathData.Transformer(SVGPathData.Transformer.SCALE, 1, 1);
});
});
it("should work in streaming mode", function() {
var encoder = new SVGPathData.Transformer(SVGPathData.Transformer.SCALE, 1, 1);
encoder.write({
type: SVGPathData.Parser.LINE_TO,
relative: true,
x: 10,
y: 10
});
encoder.end();
});
});

View File

@ -0,0 +1,38 @@
var assert = (
global && global.chai
? global.chai.assert
: require('chai').assert
)
, SVGPathData = (
global && global.SVGPathData
? global.SVGPathData
: require(__dirname + '/../src/SVGPathData.js')
)
;
describe("Possitive translation", function() {
it("should fail with no args", function() {
assert.throws(function() {
new SVGPathData(
'm20,30l10,10z'
).translate().encode();
}, 'A translate transformation requires the parameter dX'
+' to be set and to be a number.');
});
it("should work with relative path", function() {
assert.equal(new SVGPathData(
'm20,30c0 0 10 20 15 30s10 20 15 30q10 20 15 30t10 10l10 10h10v10a10 10 5 1 0 10 10z'
).translate(10, 10).encode(),
'm30 40c0 0 10 20 15 30s10 20 15 30q10 20 15 30t10 10l10 10h10v10a10 10 5 1 0 10 10z');
});
it("should work with absolute path", function() {
assert.equal(new SVGPathData(
'M20,30C0 0 10 20 15 30S10 20 15 30Q10 20 15 30T10 10L10 10H10V10A10 10 5 1 0 10 10z'
).translate(10, 10).encode(),
'M30 40C10 10 20 30 25 40S20 30 25 40Q20 30 25 40T20 20L20 20H20V20A10 10 5 1 0 20 20z');
});
});