2013-01-28 21:03:27 -05:00
|
|
|
/*
|
|
|
|
* Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
|
|
|
|
* http://paperjs.org/
|
|
|
|
*
|
2014-01-03 19:47:16 -05:00
|
|
|
* Copyright (c) 2011 - 2014, Juerg Lehni & Jonathan Puckey
|
|
|
|
* http://scratchdisk.com/ & http://jonathanpuckey.com/
|
2013-01-28 21:03:27 -05:00
|
|
|
*
|
|
|
|
* Distributed under the MIT license. See LICENSE file for details.
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2013-06-11 16:43:29 -04:00
|
|
|
// Register a jsDump parser for Base.
|
2013-06-11 16:38:08 -04:00
|
|
|
QUnit.jsDump.setParser('Base', function (obj, stack) {
|
2014-08-16 13:24:54 -04:00
|
|
|
// Just compare the string representation of classes inheriting from Base,
|
|
|
|
// since they hide the internal values.
|
|
|
|
return obj.toString();
|
2013-06-11 16:38:08 -04:00
|
|
|
});
|
|
|
|
|
2013-06-11 16:43:29 -04:00
|
|
|
// Override the default object parser to handle Base objects.
|
|
|
|
// We need to keep a reference to the previous implementation.
|
|
|
|
var objectParser = QUnit.jsDump.parsers.object;
|
|
|
|
|
2013-06-11 16:38:08 -04:00
|
|
|
QUnit.jsDump.setParser('object', function (obj, stack) {
|
2014-08-16 13:24:54 -04:00
|
|
|
return (obj instanceof Base
|
|
|
|
? QUnit.jsDump.parsers.Base
|
|
|
|
: objectParser).call(this, obj, stack);
|
2013-06-11 16:38:08 -04:00
|
|
|
});
|
|
|
|
|
2014-12-28 12:10:53 -05:00
|
|
|
function compareProperties(actual, expected, properties, message, options) {
|
2014-12-28 12:23:04 -05:00
|
|
|
for (var i = 0, l = properties.length; i < l; i++) {
|
|
|
|
var key = properties[i];
|
2014-12-28 12:10:53 -05:00
|
|
|
equals(actual[key], expected[key], message + '.' + key, options);
|
2014-12-28 12:23:04 -05:00
|
|
|
}
|
2014-12-28 12:10:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function compareItem(actual, expected, message, options, properties) {
|
|
|
|
if (options && options.cloned)
|
|
|
|
QUnit.notStrictEqual(actual.id, 'not ' + expected.id, message + '.id');
|
|
|
|
QUnit.strictEqual(actual.constructor, expected.constructor,
|
|
|
|
message + '.constructor');
|
|
|
|
// When item was cloned and had a name, the name will be versioned
|
|
|
|
equals(options && options.cloned && actual.name ? actual.name + ' 1'
|
|
|
|
: actual.name, expected.name,
|
|
|
|
message + '.name');
|
|
|
|
compareProperties(actual, expected, ['children', 'bounds', 'position',
|
|
|
|
'matrix', 'data', 'opacity', 'locked', 'visible', 'blendMode',
|
|
|
|
'selected', 'fullySelected', 'clipMask', 'guide'],
|
|
|
|
message, options);
|
|
|
|
if (properties)
|
|
|
|
compareProperties(actual, expected, properties, message, options);
|
|
|
|
// Style
|
|
|
|
compareProperties(actual.style, expected.style, ['fillColor', 'strokeColor',
|
|
|
|
'strokeCap', 'strokeJoin', 'dashArray', 'dashOffset', 'miterLimit',
|
|
|
|
'fontSize', 'font', 'leading', 'justification'],
|
|
|
|
message + '.style', options);
|
|
|
|
}
|
|
|
|
|
2014-12-28 12:23:04 -05:00
|
|
|
// A list of comparator functions, based on `expected` type. See equals() for
|
|
|
|
// an explanation of how the type is determined.
|
2014-12-28 08:33:22 -05:00
|
|
|
var comparators = {
|
2014-12-28 09:21:38 -05:00
|
|
|
Null: QUnit.strictEqual,
|
|
|
|
Undefined: QUnit.strictEqual,
|
2014-12-28 10:41:23 -05:00
|
|
|
Boolean: QUnit.strictEqual,
|
|
|
|
|
|
|
|
Object: function(actual, expected, message, options) {
|
|
|
|
QUnit.push(Base.equals(actual, expected), actual, expected, message);
|
|
|
|
},
|
|
|
|
|
|
|
|
Base: function(actual, expected, message, options) {
|
|
|
|
comparators.Object(actual, expected, message, options);
|
|
|
|
},
|
2014-12-28 09:21:38 -05:00
|
|
|
|
2014-12-28 08:33:22 -05:00
|
|
|
Number: function(actual, expected, message, options) {
|
|
|
|
// Compare with a default tolerance of Numerical.TOLERANCE:
|
|
|
|
var ok = Math.abs(actual - expected)
|
|
|
|
<= Base.pick(options && options.tolerance, Numerical.TOLERANCE);
|
|
|
|
QUnit.push(ok, ok ? expected : actual, expected, message);
|
|
|
|
},
|
|
|
|
|
|
|
|
Array: function(actual, expected, message, options) {
|
2014-12-28 12:10:53 -05:00
|
|
|
QUnit.strictEqual(actual.length, expected.length, message + '.length');
|
2014-12-28 08:33:22 -05:00
|
|
|
for (var i = 0, l = actual.length; i < l; i++) {
|
2014-12-28 12:10:53 -05:00
|
|
|
equals(actual[i], expected[i], (message || '') + '[' + i + ']',
|
2014-12-28 08:33:22 -05:00
|
|
|
options);
|
|
|
|
}
|
2014-12-28 08:59:48 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
Point: function(actual, expected, message, options) {
|
2014-12-28 12:10:53 -05:00
|
|
|
comparators.Number(actual.x, expected.x, message + '.x', options);
|
|
|
|
comparators.Number(actual.y, expected.y, message + '.y', options);
|
2014-12-28 08:59:48 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
Size: function(actual, expected, message, options) {
|
2014-12-28 12:10:53 -05:00
|
|
|
comparators.Number(actual.width, expected.width, message + '.width',
|
|
|
|
options);
|
|
|
|
comparators.Number(actual.height, expected.height, message + '.height',
|
|
|
|
options);
|
2014-12-28 08:59:48 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
Rectangle: function(actual, expected, message, options) {
|
|
|
|
comparators.Point(actual, expected, message, options);
|
|
|
|
comparators.Size(actual, expected, message, options);
|
2014-12-28 09:21:38 -05:00
|
|
|
},
|
|
|
|
|
2014-12-28 10:41:23 -05:00
|
|
|
Matrix: function(actual, expected, message, options) {
|
|
|
|
comparators.Array(actual.values, expected.values, message, options);
|
|
|
|
},
|
|
|
|
|
2014-12-28 09:21:38 -05:00
|
|
|
Color: function(actual, expected, message, options) {
|
|
|
|
if (actual && expected) {
|
2014-12-28 12:10:53 -05:00
|
|
|
equals(actual.type, expected.type, message + '.type', options);
|
2014-12-28 10:41:23 -05:00
|
|
|
// NOTE: This also compares gradients, with identity checks and all.
|
2014-12-28 09:21:38 -05:00
|
|
|
equals(actual.components, expected.components,
|
2014-12-28 12:10:53 -05:00
|
|
|
message + '.components', options);
|
2014-12-28 09:21:38 -05:00
|
|
|
} else {
|
2014-12-28 12:10:53 -05:00
|
|
|
QUnit.strictEqual(actual, expected, message);
|
2014-12-28 09:21:38 -05:00
|
|
|
}
|
2014-12-28 10:41:23 -05:00
|
|
|
},
|
|
|
|
|
2014-12-28 12:10:53 -05:00
|
|
|
Symbol: function(actual, expected, message, options) {
|
|
|
|
equals(actual.definition, expected.definition, message + '.definition',
|
|
|
|
options);
|
|
|
|
},
|
|
|
|
|
2014-12-28 10:41:23 -05:00
|
|
|
Segment: function(actual, expected, message, options) {
|
2014-12-28 12:23:04 -05:00
|
|
|
compareProperties(actual, expected, ['handleIn', 'handleOut', 'point',
|
|
|
|
'selected'], message, options);
|
2014-12-28 10:41:23 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
SegmentPoint: function(actual, expected, message, options) {
|
|
|
|
comparators.Point(actual, expected, message, options);
|
|
|
|
comparators.Boolean(actual.selected, expected.selected,
|
2014-12-28 12:10:53 -05:00
|
|
|
message + '.selected', options);
|
|
|
|
},
|
|
|
|
|
|
|
|
Item: compareItem,
|
|
|
|
|
|
|
|
Group: function(actual, expected, message, options) {
|
2014-12-28 12:23:04 -05:00
|
|
|
compareItem(actual, expected, message, options, ['clipped']);
|
2014-12-28 12:10:53 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
Layer: function(actual, expected, message, options) {
|
|
|
|
compareItem(actual, expected, message, options);
|
2014-12-28 12:23:04 -05:00
|
|
|
var sameProject = actual.project === expected.project;
|
|
|
|
var sharedProject = !(options && options.dontShareProject);
|
|
|
|
QUnit.push(sharedProject ? sameProject : !sameProject,
|
|
|
|
actual.project,
|
|
|
|
sharedProject ? expected.project : 'not ' + expected.project,
|
|
|
|
message + '.project');
|
2014-12-28 12:10:53 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
Path: function(actual, expected, message, options) {
|
|
|
|
compareItem(actual, expected, message, options,
|
|
|
|
['segments', 'closed', 'clockwise', 'length']);
|
|
|
|
},
|
|
|
|
|
|
|
|
CompoundPath: function(actual, expected, message, options) {
|
|
|
|
compareItem(actual, expected, message, options);
|
|
|
|
},
|
|
|
|
|
|
|
|
Raster: function(actual, expected, message, options) {
|
|
|
|
compareItem(actual, expected, message, options,
|
|
|
|
['size', 'width', 'height', 'ppi', 'source', 'image']);
|
|
|
|
equals(actual.toDataURL(), expected.toDataURL(),
|
|
|
|
message + '.toDataUrl()');
|
|
|
|
},
|
|
|
|
|
|
|
|
Shape: function(actual, expected, message, options) {
|
|
|
|
compareItem(actual, expected, message, options,
|
|
|
|
['shape', 'size', 'radius']);
|
|
|
|
},
|
|
|
|
|
|
|
|
PlacedSymbol: function(actual, expected, message, options) {
|
|
|
|
compareItem(actual, expected, message,
|
|
|
|
// Cloning PlacedSymbols does not result in cloned Symbols
|
|
|
|
options && options.cloned
|
|
|
|
? new Base(options, { cloned: false })
|
|
|
|
: options,
|
|
|
|
['symbol']);
|
|
|
|
},
|
|
|
|
|
|
|
|
PointText: function(actual, expected, message, options) {
|
|
|
|
compareItem(actual, expected, message, options,
|
|
|
|
['content', 'point']);
|
|
|
|
},
|
|
|
|
|
|
|
|
Project: function(actual, expected, message, options) {
|
|
|
|
compareProperties(actual, expected, ['symbols', 'layers'],
|
|
|
|
message, options);
|
2014-12-28 08:33:22 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-12-28 12:23:04 -05:00
|
|
|
// A list of classes that should be identical after their owners were cloned.
|
|
|
|
var identicalAfterCloning = {
|
2014-12-28 10:41:23 -05:00
|
|
|
Gradient: true,
|
|
|
|
Symbol: true
|
|
|
|
};
|
|
|
|
|
2014-12-28 08:33:22 -05:00
|
|
|
function getFunctionMessage(func) {
|
|
|
|
var message = func.toString().match(
|
2014-08-16 13:24:54 -04:00
|
|
|
/^\s*function[^\{]*\{([\s\S]*)\}\s*$/)[1]
|
|
|
|
.replace(/ /g, '')
|
|
|
|
.replace(/^\s+|\s+$/g, '');
|
2014-12-28 08:33:22 -05:00
|
|
|
if (/^return /.test(message)) {
|
|
|
|
message = message
|
|
|
|
.replace(/^return /, '')
|
|
|
|
.replace(/;$/, '');
|
|
|
|
}
|
|
|
|
return message;
|
2013-07-21 19:11:09 -04:00
|
|
|
}
|
|
|
|
|
2011-05-14 13:59:04 -04:00
|
|
|
// Override equals to convert functions to message and execute them as tests()
|
2014-12-28 08:33:22 -05:00
|
|
|
function equals(actual, expected, message, options) {
|
2014-11-30 14:27:14 -05:00
|
|
|
// Allow the use of functions for actual, which will get called and their
|
|
|
|
// source content extracted for readable reports.
|
2014-08-16 13:24:54 -04:00
|
|
|
if (typeof actual === 'function') {
|
2014-12-28 08:33:22 -05:00
|
|
|
if (!message)
|
|
|
|
message = getFunctionMessage(actual);
|
2014-08-16 13:24:54 -04:00
|
|
|
actual = actual();
|
|
|
|
}
|
2014-12-28 09:21:38 -05:00
|
|
|
// Get the comparator based on the expected value's type only and ignore the
|
|
|
|
// actual value's type.
|
2014-12-28 10:41:23 -05:00
|
|
|
var type = typeof expected,
|
|
|
|
cls;
|
|
|
|
type = expected === null && 'Null'
|
|
|
|
|| type === 'number' && 'Number'
|
|
|
|
|| type === 'boolean' && 'Boolean'
|
2014-12-28 09:21:38 -05:00
|
|
|
|| type === 'undefined' && 'Undefined'
|
|
|
|
|| Array.isArray(expected) && 'Array'
|
2014-12-28 10:41:23 -05:00
|
|
|
|| (cls = expected && expected._class)
|
|
|
|
|| type === 'object' && 'Object';
|
|
|
|
var comparator = type && comparators[type];
|
2014-12-28 12:10:53 -05:00
|
|
|
if (!message) {
|
|
|
|
message = type
|
|
|
|
? type.charAt(0).toLowerCase() + type.substring(1)
|
|
|
|
: 'value';
|
|
|
|
}
|
2014-12-28 09:21:38 -05:00
|
|
|
if (comparator) {
|
|
|
|
comparator(actual, expected, message, options);
|
|
|
|
} else if (expected && expected.equals) {
|
|
|
|
// Fall back to equals
|
|
|
|
QUnit.push(expected.equals(actual), actual, expected, message);
|
|
|
|
} else {
|
|
|
|
// Finally perform a strict compare
|
|
|
|
QUnit.push(actual === expected, actual, expected, message);
|
2014-12-28 08:33:22 -05:00
|
|
|
}
|
2014-12-28 10:41:23 -05:00
|
|
|
if (options && options.cloned && cls) {
|
2014-12-28 12:23:04 -05:00
|
|
|
var identical = identicalAfterCloning[cls];
|
2014-12-28 10:41:23 -05:00
|
|
|
QUnit.push(identical ? actual === expected : actual !== expected,
|
|
|
|
actual, identical ? expected : 'not ' + expected,
|
2014-12-28 12:10:53 -05:00
|
|
|
message + ': identical after cloning');
|
2014-12-28 10:41:23 -05:00
|
|
|
}
|
2011-05-07 12:25:34 -04:00
|
|
|
}
|
2011-05-05 08:38:20 -04:00
|
|
|
|
2011-05-14 13:59:04 -04:00
|
|
|
function test(testName, expected) {
|
2014-08-16 13:24:54 -04:00
|
|
|
return QUnit.test(testName, function() {
|
|
|
|
var project = new Project();
|
|
|
|
expected();
|
|
|
|
project.remove();
|
|
|
|
});
|
2011-05-14 13:59:04 -04:00
|
|
|
}
|
|
|
|
|
2013-03-17 13:13:55 -04:00
|
|
|
function asyncTest(testName, expected) {
|
2014-08-16 13:24:54 -04:00
|
|
|
return QUnit.asyncTest(testName, function() {
|
|
|
|
var project = new Project();
|
|
|
|
expected(function() {
|
|
|
|
project.remove();
|
|
|
|
start();
|
|
|
|
});
|
|
|
|
});
|
2013-03-17 13:13:55 -04:00
|
|
|
}
|
|
|
|
|
2012-12-01 14:05:48 -05:00
|
|
|
// SVG
|
|
|
|
|
2013-04-23 10:19:08 -04:00
|
|
|
function createSVG(xml) {
|
2014-08-16 13:24:54 -04:00
|
|
|
return new DOMParser().parseFromString(
|
|
|
|
'<svg xmlns="http://www.w3.org/2000/svg">' + xml + '</svg>',
|
|
|
|
'text/xml');
|
2014-04-06 07:48:03 -04:00
|
|
|
}
|