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

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]);
}
}