jointjs
Version:
JavaScript diagramming library
1,262 lines (992 loc) • 56 kB
JavaScript
'use strict';
QUnit.module('vectorizer', function(hooks) {
var fixture = document.createElement('div');
fixture.id = 'qunit-fixture';
var svgContainer;
var svgPath;
var svgGroup;
var svgCircle;
var svgEllipse;
var svgPolygon;
var svgText;
var svgRectangle;
var svgGroup1;
var svgGroup2;
var svgGroup3;
var svgPath2;
var svgPath3;
var svgLinearGradient;
var childrenTagNames = function(vel) {
var tagNames = [];
Array.prototype.slice.call(vel.node.childNodes).forEach(function(childNode) {
tagNames.push(childNode.tagName.toLowerCase());
});
return tagNames;
};
hooks.beforeEach(function() {
var svgContent = '<path id="svg-path" d="M10 10"/>' +
'<!-- comment -->' +
'<g id="svg-group">' +
'<ellipse id="svg-ellipse" x="10" y="10" rx="30" ry="30"/>' +
'<circle id="svg-circle" cx="10" cy="10" r="2" fill="red"/>' +
'</g>' +
'<polygon id="svg-polygon" points="200,10 250,190 160,210"/>' +
'<text id="svg-text" x="0" y="15" fill="red">Test</text>' +
'<rect id="svg-rectangle" x="100" y="100" width="50" height="100"/>' +
'<g id="svg-group-1" class="group-1">' +
'<g id="svg-group-2" class="group-2">' +
'<g id="svg-group-3" class="group3">' +
'<path id="svg-path-2" d="M 100 100 C 100 100 0 150 100 200 Z"/>' +
'</g>' +
'</g>' +
'</g>' +
'<path id="svg-path-3"/>' +
'<linearGradient id= "svg-linear-gradient"><stop/></linearGradient>';
document.body.appendChild(fixture);
fixture.appendChild(V('svg', { id: 'svg-container' }, V(svgContent)).node);
svgContainer = document.getElementById('svg-container');
svgPath = document.getElementById('svg-path');
svgGroup = document.getElementById('svg-group');
svgCircle = document.getElementById('svg-circle');
svgEllipse = document.getElementById('svg-ellipse');
svgPolygon = document.getElementById('svg-polygon');
svgText = document.getElementById('svg-text');
svgRectangle = document.getElementById('svg-rectangle');
svgGroup1 = document.getElementById('svg-group-1');
svgGroup2 = document.getElementById('svg-group-2');
svgGroup3 = document.getElementById('svg-group-3');
svgPath2 = document.getElementById('svg-path-2');
svgPath3 = document.getElementById('svg-path-3');
svgLinearGradient = document.getElementById('svg-linear-gradient');
});
function serializeNode(node) {
return (new XMLSerializer()).serializeToString(node);
}
QUnit.test('constuctor', function(assert) {
var vRect = V('rect');
assert.ok(V.isVElement(vRect), 'Constructor produces a vectorizer element, when a string was provided.');
assert.ok(vRect.node instanceof SVGElement, 'The vectorizer element has the attribute "node" that references to an SVGElement.');
assert.ok(V.isVElement(V(vRect)), 'Constructor produces a vectorizer element, when a vectorizer element was provided.');
assert.ok(V(vRect).node instanceof SVGElement, 'The vectorizer element has again the attribute "node" that references to an SVGElement.');
});
QUnit.test('id', function(assert) {
var vRect = V('rect');
assert.ok(vRect.id);
assert.equal(vRect.id, vRect.node.id);
vRect.id = 'newid';
assert.equal(vRect.node.id, 'newid');
});
QUnit.test('V(\'<invalid markup>\')', function(assert) {
var error;
try {
V('<invalid markup>');
} catch (e) {
error = e;
}
assert.ok(typeof error !== 'undefined', 'Should throw an error when given invalid markup.');
});
QUnit.test('V(\'<valid markup>\')', function(assert) {
var error;
try {
V('<rect width="100%" height="100%" fill="red" />');
} catch (e) {
error = e;
}
assert.ok(typeof error === 'undefined', 'Should not throw an error when given valid markup.');
});
QUnit.test('V.ensureId()', function(assert) {
var node = document.createElementNS('http://www.w3.org/2000/svg', 'g');
assert.notOk(node.id);
var id = V.ensureId(node);
assert.ok(id);
assert.equal(id, node.id);
assert.equal(id, V.ensureId(node));
assert.equal(id, node.id);
});
QUnit.test('V.isSVGGraphicsElement()', function(assert) {
assert.ok(V.isSVGGraphicsElement(svgCircle));
assert.ok(V.isSVGGraphicsElement(V('circle', { class: 'not-in-dom' })));
assert.ok(V.isSVGGraphicsElement(svgGroup));
assert.notOk(V.isSVGGraphicsElement());
assert.notOk(V.isSVGGraphicsElement(svgLinearGradient));
});
QUnit.test('index()', function(assert) {
// svg container
assert.equal(V(svgContainer).index(), 0, 'SVG container contains 5 various nodes and 1 comment. Container itself has index 0.');
// nodes in an svg container
assert.equal(V(svgPath).index(), 0, 'The first node has index 0.');
assert.equal(V(svgGroup).index(), 1, 'The second node has index 1.');
assert.equal(V(svgPolygon).index(), 2, 'The third node has index 2.');
assert.equal(V(svgText).index(), 3, 'The fourth node has index 3.');
assert.equal(V(svgRectangle).index(), 4, 'The fifth node has index 4.');
// nodes in a group
assert.equal(V(svgEllipse).index(), 0, 'The first node in the group has index 0.');
assert.equal(V(svgCircle).index(), 1, 'The second node in the group has index 1.');
});
QUnit.module('tagName()', function() {
QUnit.test('sanity', function(assert) {
assert.equal(typeof V(svgContainer).tagName(), 'string');
assert.equal(typeof V(svgPath).tagName(), 'string');
assert.equal(typeof V(svgGroup).tagName(), 'string');
assert.equal(typeof V(svgCircle).tagName(), 'string');
assert.equal(typeof V(svgEllipse).tagName(), 'string');
assert.equal(typeof V(svgPolygon).tagName(), 'string');
assert.equal(typeof V(svgText).tagName(), 'string');
assert.equal(typeof V(svgRectangle).tagName(), 'string');
assert.equal(typeof V(svgGroup1).tagName(), 'string');
assert.equal(typeof V(svgGroup2).tagName(), 'string');
assert.equal(typeof V(svgGroup3).tagName(), 'string');
assert.equal(typeof V(svgPath2).tagName(), 'string');
assert.equal(typeof V(svgPath3).tagName(), 'string');
});
QUnit.test('correctness', function(assert) {
assert.equal(V(svgContainer).tagName(), 'SVG');
assert.equal(V(svgPath).tagName(), 'PATH');
assert.equal(V(svgGroup).tagName(), 'G');
assert.equal(V(svgCircle).tagName(), 'CIRCLE');
assert.equal(V(svgEllipse).tagName(), 'ELLIPSE');
assert.equal(V(svgPolygon).tagName(), 'POLYGON');
assert.equal(V(svgText).tagName(), 'TEXT');
assert.equal(V(svgRectangle).tagName(), 'RECT');
assert.equal(V(svgGroup1).tagName(), 'G');
assert.equal(V(svgGroup2).tagName(), 'G');
assert.equal(V(svgGroup3).tagName(), 'G');
assert.equal(V(svgPath2).tagName(), 'PATH');
assert.equal(V(svgPath3).tagName(), 'PATH');
});
});
QUnit.module('text', function() {
var getSvg = function() {
var svg = V('svg');
svg.attr('width', 600);
svg.attr('height', 800);
fixture.appendChild(svg.node);
return svg;
};
QUnit.test('single line, styles, position', function(assert) {
var svg = getSvg();
var t = V('text', { x: 250, dy: 100, fill: 'black' });
t.text('abc');
assert.equal(t.node.childNodes.length, 1, 'There is only one child node which is a v-line node.');
assert.equal(t.node.childNodes[0].childNodes.length, 1, 'There is only one child of that v-line node which is a text node.');
assert.equal(serializeNode(t.node.childNodes[0].childNodes[0]), 'abc', 'Generated text is ok for a single line and no annotations.');
assert.equal(t.attr('fill'), 'black', 'fill attribute set');
assert.equal(t.attr('x'), '250', 'x attribute set');
assert.equal(t.attr('dy'), '100', 'dy attribute set');
svg.remove();
});
QUnit.test('multi-line and annotations', function(assert) {
var svg = getSvg();
var t = V('text', { x: 250, dy: 100, fill: 'black' });
t.text('abc\ndef');
assert.equal(t.node.childNodes.length, 2, 'There are two child nodes one for each line.');
t.text('abcdefgh', {
annotations: [
{ start: 1, end: 3, attrs: { fill: 'red', stroke: 'orange' }},
{ start: 2, end: 5, attrs: { fill: 'blue' }}
]
});
assert.equal(t.find('.v-line').length, 1, 'One .v-line element rendered');
assert.equal(t.find('tspan').length, 4, '4 tspans rendered in total');
t.text('abcd\nefgh', {
annotations: [
{ start: 1, end: 3, attrs: { fill: 'red', stroke: 'orange' }},
{ start: 2, end: 5, attrs: { fill: 'blue' }}
]
});
assert.equal(t.find('.v-line').length, 2, 'Two .v-line elements rendered');
assert.equal(t.find('tspan').length, 5, '5 tspans rendered in total');
svg.remove();
});
QUnit.test('line height', function(assert) {
var t = V('text', { 'font-size': 20 });
var linesDy;
var text = 'abcd\nefgh';
var annotations = [
{ start: 0, end: 4, attrs: { fill: 'red' }},
{ start: 5, end: 9, attrs: { fill: 'blue' }}
];
t.text(text, {
lineHeight: '2.1em',
annotations: annotations
});
linesDy = t.children().map(function(vTSpan) {
return vTSpan.attr('dy');
});
assert.deepEqual(linesDy, ['0', '2.1em']); // hard-coded line-height
t.text(text, {
lineHeight: 'auto',
annotations: annotations
});
linesDy = t.children().map(function(vTSpan) {
return vTSpan.attr('dy');
});
assert.deepEqual(linesDy, ['0', '24']); // base font-size * 1.2
t.text(text, {
lineHeight: 'auto',
annotations: [
{ start: 0, end: 4, attrs: { fill: 'red' }},
{ start: 5, end: 9, attrs: { fill: 'blue', 'font-size': 30 }}
]
});
linesDy = t.children().map(function(vTSpan) {
return vTSpan.attr('dy');
});
assert.deepEqual(linesDy, ['0', '36']); // max font-size * 1.2
});
QUnit.test('custom EOL', function(assert) {
var svg = getSvg();
var t = V('text', { x: 250, dy: 100, fill: 'black' });
t.text('abc\ndef', { eol: 'X' });
assert.equal(t.node.childNodes[0].textContent, 'abcX');
assert.equal(t.node.childNodes[1].textContent, 'def');
t.text('abc\ndef\n', { eol: 'X' });
assert.equal(t.node.childNodes[0].textContent, 'abcX');
assert.equal(t.node.childNodes[1].textContent, 'defX');
svg.remove();
});
QUnit.test('includeAnnotationIndices', function(assert) {
var svg = getSvg();
var t = V('text', { x: 250, dy: 100, fill: 'black' });
t.text('abcdefgh', {
includeAnnotationIndices: true, annotations: [
{ start: 1, end: 3, attrs: { fill: 'red', stroke: 'orange' }},
{ start: 2, end: 5, attrs: { fill: 'blue' }}
]
});
assert.equal(V(t.find('tspan')[1]).attr('annotations'), '0', 'annotation indices added as an attribute');
assert.equal(V(t.find('tspan')[2]).attr('annotations'), '0,1', 'annotation indices added as an attribute');
assert.equal(V(t.find('tspan')[3]).attr('annotations'), '1', 'annotation indices added as an attribute');
t.text('');
assert.equal(t.attr('display'), 'none');
t.text('text');
assert.equal(t.attr('display'), null);
svg.remove();
});
QUnit.test('visibility', function(assert) {
var svg = getSvg();
var t = V('text', { x: 250, dy: 100, fill: 'black' });
t.text('');
assert.equal(t.attr('display'), 'none');
t.text('text');
assert.equal(t.attr('display'), null);
svg.remove();
});
QUnit.test('textVerticalAnchor', function(assert) {
var texts = ['one', 'one\ntwo', 'one\ntwo\nthree'];
var n = texts.length;
var fontSize = 30;
assert.expect(3 * n);
var svg = getSvg();
var t = V('text', { 'font-size': fontSize }).appendTo(svg);
for (var i = 0; i < n; i++) {
var text = texts[i];
var bbox;
// 'bottom'
t.text(text, { textVerticalAnchor: 'bottom' });
bbox = t.getBBox();
assert.ok(Math.abs(bbox.corner().y) < (fontSize * 0.2), 'bottom anchor: ' + text);
// 'top'
t.text(text, { textVerticalAnchor: 'top' });
bbox = t.getBBox();
assert.ok(Math.abs(bbox.origin().y) < (fontSize * 0.2), 'top anchor: ' + text);
// 'middle'
t.text(text, { textVerticalAnchor: 'middle' });
bbox = t.getBBox();
assert.ok(Math.abs(bbox.center().y) < (fontSize * 0.2), 'middle anchor: ' + text);
}
svg.remove();
});
});
QUnit.test('annotateString', function(assert) {
var annotations = V.annotateString('This is a text that goes on multiple lines.', [
{ start: 2, end: 5, attrs: { fill: 'red' }},
{ start: 4, end: 8, attrs: { fill: 'blue' }}
]);
assert.deepEqual(
annotations,
[
'Th',
{ t: 'is', attrs: { fill: 'red' }},
{ t: ' is ', attrs: { fill: 'blue' }},
'a text that goes on multiple lines.'
],
'String cut into pieces and attributed according to the spans.'
);
annotations = V.annotateString('abcdefgh', [
{ start: 1, end: 3, attrs: { 'class': 'one' }},
{ start: 2, end: 5, attrs: { 'class': 'two', fill: 'blue' }}
]);
assert.deepEqual(
annotations,
[
'a',
{ t: 'b', attrs: { 'class': 'one' }},
{ t: 'c', attrs: { 'class': 'one two', fill: 'blue' }},
{ t: 'de', attrs: { 'class': 'two', fill: 'blue' }},
'fgh'
],
'String cut into pieces and attributed according to the annotations including concatenated classes.'
);
annotations = V.annotateString('abcdefgh', [
{ start: 1, end: 3, attrs: { 'class': 'one' }},
{ start: 2, end: 5, attrs: { 'class': 'two', fill: 'blue' }}
], { includeAnnotationIndices: true });
assert.deepEqual(
annotations,
[
'a',
{ t: 'b', attrs: { 'class': 'one' }, annotations: [0] },
{ t: 'c', attrs: { 'class': 'one two', fill: 'blue' }, annotations: [0, 1] },
{ t: 'de', attrs: { 'class': 'two', fill: 'blue' }, annotations: [1] },
'fgh'
],
'annotation indices included'
);
});
QUnit.test('styleToObject', function(assert) {
assert.deepEqual(V.styleToObject('fill=red; stroke=blue'), { fill: 'red', stroke: 'blue' }, 'style string parsed properly');
});
QUnit.test('mergeAttrs', function(assert) {
assert.deepEqual(
V.mergeAttrs({ x: 5, y: 10, style: 'fill=red; stroke=blue' }, { y: 20, style: { stroke: 'orange' }}),
{ x: 5, y: 20, style: { fill: 'red', stroke: 'orange' }},
'style string parsed properly'
);
});
QUnit.test('find()', function(assert) {
var found = V(svgContainer).find('circle');
assert.ok(Array.isArray(found), 'The result should be an array.');
assert.ok(found.length > 0, 'The array should not be empty.');
assert.ok(found.reduce(function(memo, vel) { return memo && V.isVElement(vel); }, true), 'Items in the array should be wrapped in Vectorizer.');
});
QUnit.test('children()', function(assert) {
var checkChildren = svgGroup.childNodes;
assert.ok(checkChildren.length > 0, 'The checkChildren collection should not be empty.');
assert.ok(checkChildren.length === 2, 'The checkChildren collection should have two elements.');
var children = V(svgGroup).children();
assert.ok(Array.isArray(children), 'The result should be an array.');
assert.ok(children.length > 0, 'The array should not be empty.');
assert.ok(children.length === 2, 'The array should have two elements.');
assert.ok(children.reduce(function(memo, vel) { return memo && V.isVElement(vel); }, true), 'Items in the array should be wrapped in Vectorizer.');
var textNode = document.createTextNode('Text node');
svgGroup.appendChild(textNode);
var comment = document.createComment('Comment');
svgGroup.appendChild(comment);
var attribute = document.createAttribute('Attribute');
attribute.value = 'Hello World';
svgGroup.setAttributeNode(attribute);
var checkChildren2 = svgGroup.childNodes;
assert.ok(checkChildren2.length > 0, 'The checkChildren2 collection should not be empty.');
assert.ok(checkChildren2.length === 4, 'The checkChildren2 collection should have four child nodes.');
var numElements = 0;
for (var i = 0; i < checkChildren2.length; i++) {
var currentChild = checkChildren2[i];
if (currentChild.nodeType === 1) {
numElements += 1;
}
}
assert.ok(numElements === 2, 'The checkChildren2 collection should have two child elements.');
var children2 = V(svgGroup).children();
assert.ok(Array.isArray(children2), 'The result should be an array.');
assert.ok(children2.length > 0, 'The array should not be empty.');
assert.ok(children2.length === 2, 'The array should have two child elements.');
assert.ok(children2.reduce(function(memo, vel) { return memo && V.isVElement(vel); }, true), 'Items in the array should be wrapped in Vectorizer.');
var emptyChildren = V(svgCircle).children();
assert.ok(Array.isArray(emptyChildren), 'The result should be an array.');
assert.ok(emptyChildren.length === 0, 'The array should be empty.');
});
QUnit.test('V.transformPoint', function(assert) {
var p = { x: 1, y: 2 };
var t;
var group = V('<g/>');
V(svgContainer).append(group);
t = V.transformPoint(p, group.node.getCTM());
assert.deepEqual({ x: t.x, y: t.y }, { x: 1, y: 2 }, 'transform without transformation returns the point unchanged.');
group.scale(2, 3);
t = V.transformPoint(p, group.node.getCTM());
assert.deepEqual({ x: t.x, y: t.y }, { x: 2, y: 6 }, 'transform with scale transformation returns correct point.');
group.attr('transform', 'rotate(90)');
t = V.transformPoint(p, group.node.getCTM());
assert.deepEqual({ x: t.x, y: t.y }, { x: -2, y: 1 }, 'transform with rotate transformation returns correct point.');
});
QUnit.test('native getTransformToElement vs VElement getTransformToElement - translate', function(assert) {
var container = V(svgContainer);
var group = V('<g/>');
var rect = V('<rect/>');
var transformNativeResult = {
a: 1,
b: 0,
c: 0,
d: 1,
e: -10,
f: -10
};
container.append(group);
container.append(rect);
rect.translate(10, 10);
var transformPoly = group.getTransformToElement(rect.node);
var matrix = {
a: transformPoly.a,
b: transformPoly.b,
c: transformPoly.c,
d: transformPoly.d,
e: transformPoly.e,
f: transformPoly.f
};
assert.deepEqual(matrix, transformNativeResult);
group.remove();
rect.remove();
});
QUnit.test('native getTransformToElement vs VElement getTransformToElement - rotate', function(assert) {
var container = V(svgContainer);
var normalizeFloat = function(value) {
var temp = value * 100;
return temp > 0 ? Math.floor(temp) : Math.ceil(temp);
};
var group = V('<g/>');
var rect = V('<rect/>');
var transformNativeResult = {
a: normalizeFloat(0.7071067811865476),
b: normalizeFloat(-0.7071067811865475),
c: normalizeFloat(0.7071067811865475),
d: normalizeFloat(0.7071067811865476),
e: normalizeFloat(-0),
f: normalizeFloat(0)
};
container.append(group);
container.append(rect);
rect.rotate(45);
var transformPoly = group.getTransformToElement(rect.node);
var matrix = {
a: normalizeFloat(transformPoly.a),
b: normalizeFloat(transformPoly.b),
c: normalizeFloat(transformPoly.c),
d: normalizeFloat(transformPoly.d),
e: normalizeFloat(transformPoly.e),
f: normalizeFloat(transformPoly.f)
};
assert.deepEqual(matrix, transformNativeResult);
group.remove();
rect.remove();
});
QUnit.test('findParentByClass', function(assert) {
assert.equal(
V(svgGroup3).findParentByClass('group-1').node,
svgGroup1,
'parent exists'
);
assert.notOk(
V(svgGroup3).findParentByClass('not-a-parent'),
'parent does not exist'
);
assert.notOk(
V(svgGroup3).findParentByClass('group-1', svgGroup2),
'parent exists, terminator on the way down'
);
assert.equal(
V(svgGroup3).findParentByClass('group-1', svgCircle).node,
svgGroup1,
'parent exists, terminator not on the way down'
);
assert.notOk(
V(svgGroup3).findParentByClass('not-a-parent', svgCircle),
'parent does not exist, terminator not on the way down'
);
});
QUnit.test('contains()', function(assert) {
assert.ok(V(svgContainer).contains(svgGroup1));
assert.ok(V(svgGroup1).contains(svgGroup3));
assert.ok(V(svgGroup1).contains(svgGroup2));
assert.notOk(V(svgGroup3).contains(svgGroup1));
assert.notOk(V(svgGroup2).contains(svgGroup1));
assert.notOk(V(svgGroup1).contains(svgGroup1));
assert.notOk(V(svgGroup1).contains(document));
});
QUnit.module('transform()', function(hooks) {
var vel;
hooks.beforeEach(function() {
vel = V('rect').appendTo(svgContainer);
});
hooks.afterEach(function() {
vel.remove();
});
QUnit.test('as a getter', function(assert) {
assert.deepEqual(vel.transform(), V.createSVGMatrix({
a: 1,
b: 0,
c: 0,
d: 1,
e: 0,
f: 0
}));
});
QUnit.test('single transformation', function(assert) {
vel.transform({ a: 2, b: 0, c: 0, d: 2, e: 0, f: 0 });
assert.deepEqual(vel.transform(), V.createSVGMatrix({
a: 2,
b: 0,
c: 0,
d: 2,
e: 0,
f: 0
}));
});
QUnit.test('multiple transformations', function(assert) {
vel.transform({ a: 2, b: 0, c: 0, d: 2, e: 0, f: 0 });
vel.transform({ a: 1, b: 0, c: 0, d: 1, e: 10, f: 10 });
assert.deepEqual(vel.transform(), V.createSVGMatrix({
a: 2,
b: 0,
c: 0,
d: 2,
e: 20,
f: 20
}));
});
QUnit.test('as a getter (element not in the DOM)', function(assert) {
vel.transform({ a: 2, b: 0, c: 0, d: 2, e: 0, f: 0 });
vel.transform({ a: 1, b: 0, c: 0, d: 1, e: 10, f: 10 });
vel.remove();
assert.deepEqual(vel.transform(), V.createSVGMatrix({
a: 2,
b: 0,
c: 0,
d: 2,
e: 20,
f: 20
}));
});
QUnit.test('opt to clear transformation list', function(assert) {
vel.transform({ a: 2, b: 0, c: 0, d: 2, e: 0, f: 0 });
vel.transform({ a: 1, b: 1, c: 1, d: 1, e: 1, f: 1 }, { absolute: true });
vel.remove();
assert.deepEqual(vel.transform(), V.createSVGMatrix({
a: 1,
b: 1,
c: 1,
d: 1,
e: 1,
f: 1
}), 'should clean transformation list before applying 2nd transformation');
});
});
QUnit.module('empty()', function(hooks) {
var vel;
hooks.beforeEach(function() {
vel = V('g');
V(svgContainer).append(vel);
});
hooks.afterEach(function() {
vel.remove();
});
QUnit.test('should remove all child nodes', function(assert) {
vel.append([
V('rect'),
V('polygon'),
V('circle')
]);
assert.equal(vel.node.childNodes.length, 3);
vel.empty();
assert.equal(vel.node.childNodes.length, 0);
});
});
QUnit.module('attribute', function(hooks) {
var svgToString = function(svg) {
return new XMLSerializer().serializeToString(svg.node);
};
hooks.beforeEach(function() {
this.svg = V('svg');
});
QUnit.module('set', function(hooks) {
QUnit.test('no namespace', function(assert) {
var element = V('a').attr('href', 'www.seznam.cz');
this.svg.append(element);
var text = svgToString(element);
assert.equal(text.indexOf(':href'), -1, 'should find attr without namespace');
assert.ok(text.indexOf('href') > 0, 'attr has been set');
assert.ok(text.indexOf('href') > 0, 'attr values has been set');
});
QUnit.test('with namespace', function(assert) {
var element = V('a').attr('xlink:href', 'www.seznam.cz');
this.svg.append(element);
var text = svgToString(this.svg);
assert.ok(text.indexOf('xlink:href') > 0, 'message');
});
QUnit.test('value "null" removes attr', function(assert) {
var element = V('a').attr('xlink:href', 'www.seznam.cz');
this.svg.append(element);
element.attr('xlink:href', null);
var text = svgToString(this.svg);
assert.ok(text.indexOf('xlink:href') === -1, 'attribute should be removed');
});
QUnit.test('special attr', function(assert) {
var element = V('a').attr('id', 'x');
this.svg.append(element);
var text = svgToString(element);
assert.ok(text.indexOf('id') > 0, 'id has been set');
});
});
QUnit.test('remove simple', function(assert) {
var a = V('a').attr('href', 'www.seznam.cz');
this.svg.append(a);
a.removeAttr('href');
var text = svgToString(this.svg);
assert.equal(text.indexOf('href'), -1, 'should be deleted');
});
QUnit.test('try to remove non existing', function(assert) {
var a = V('a').attr('href', 'www.seznam.cz');
this.svg.append(a);
a.removeAttr('blah');
var text = svgToString(this.svg);
assert.ok(text.indexOf('href') > 0, 'should not throw');
});
QUnit.test('remove with namespace', function(assert) {
var a = V('a').attr('xlink:href', 'www.seznam.cz');
this.svg.append(a);
a.removeAttr('xlink:href');
var text = svgToString(this.svg);
assert.equal(text.indexOf('href'), -1, 'message');
assert.equal(text.indexOf('seznam'), -1, 'message');
});
QUnit.test('remove with not known namespace', function(assert) {
var a = V('a').attr('xxx:href', 'www.seznam.cz');
this.svg.append(a);
a.removeAttr('xxx:href');
var text = svgToString(this.svg);
assert.equal(text.indexOf('href'), -1, 'message');
assert.equal(text.indexOf('seznam'), -1, 'message');
});
QUnit.test('apply remove attr', function(assert) {
var element = V('a');
this.svg.append(element);
element.text();
element.text('text');
var text = svgToString(this.svg);
assert.ok(text.indexOf('display="null"') === -1, 'attr display should be removed');
});
});
QUnit.module('append()', function(hooks) {
var groupElement;
hooks.beforeEach(function() {
groupElement = V(svgGroup).clone().empty();
});
QUnit.test('single element', function(assert) {
groupElement.append(V('<rect/>'));
assert.equal(groupElement.node.childNodes.length, 1);
assert.deepEqual(childrenTagNames(groupElement), ['rect']);
groupElement.append(V('<circle/>'));
assert.equal(groupElement.node.childNodes.length, 2);
assert.deepEqual(childrenTagNames(groupElement), ['rect', 'circle']);
});
QUnit.test('multiple elements', function(assert) {
groupElement.append(V('<rect/><circle/>'));
assert.equal(groupElement.node.childNodes.length, 2);
assert.deepEqual(childrenTagNames(groupElement), ['rect', 'circle']);
groupElement.append(V('<line/><polygon/>'));
assert.equal(groupElement.node.childNodes.length, 4);
assert.deepEqual(childrenTagNames(groupElement), ['rect', 'circle', 'line', 'polygon']);
});
});
QUnit.module('appendTo()', function(hooks) {
var groupNode;
hooks.beforeEach(function() {
groupNode = V(svgGroup).clone().empty().node;
});
QUnit.test('append vnode', function(assert) {
var rect = V('<rect/>').appendTo(V(groupNode));
assert.ok(V.isV(rect));
assert.equal(rect.node.parentNode, groupNode);
assert.equal(rect.node, groupNode.lastChild);
});
QUnit.test('append node', function(assert) {
var rect = V('<rect/>').appendTo(groupNode);
assert.ok(V.isV(rect));
assert.equal(rect.node.parentNode, groupNode);
assert.equal(rect.node, groupNode.lastChild);
});
});
QUnit.module('prepend()', function(hooks) {
var groupElement;
hooks.beforeEach(function() {
groupElement = V(svgGroup).clone().empty();
});
QUnit.test('single element', function(assert) {
groupElement.prepend(V('<rect/>'));
assert.equal(groupElement.node.childNodes.length, 1);
assert.deepEqual(childrenTagNames(groupElement), ['rect']);
groupElement.prepend(V('<circle/>'));
assert.equal(groupElement.node.childNodes.length, 2);
assert.deepEqual(childrenTagNames(groupElement), ['circle', 'rect']);
});
QUnit.test('multiple elements', function(assert) {
groupElement.prepend(V('<rect/><circle/>'));
assert.equal(groupElement.node.childNodes.length, 2);
assert.deepEqual(childrenTagNames(groupElement), ['rect', 'circle']);
groupElement.prepend(V('<line/><polygon/>'));
assert.equal(groupElement.node.childNodes.length, 4);
assert.deepEqual(childrenTagNames(groupElement), ['line', 'polygon', 'rect', 'circle']);
});
});
QUnit.module('before()', function(hooks) {
var groupElement, rectElement;
hooks.beforeEach(function() {
groupElement = V(svgGroup).clone().empty();
rectElement = V(svgRectangle).clone().empty();
groupElement.append(rectElement);
});
QUnit.test('single element', function(assert) {
rectElement.before(V('<circle/>'));
assert.equal(groupElement.node.childNodes.length, 2);
assert.deepEqual(childrenTagNames(groupElement), ['circle', 'rect']);
rectElement.before(V('<line/>'));
assert.equal(groupElement.node.childNodes.length, 3);
assert.deepEqual(childrenTagNames(groupElement), ['circle', 'line', 'rect']);
});
QUnit.test('multiple elements', function(assert) {
rectElement.before(V('<ellipse/><circle/>'));
assert.equal(groupElement.node.childNodes.length, 3);
assert.deepEqual(childrenTagNames(groupElement), ['ellipse', 'circle', 'rect']);
rectElement.before(V('<line/><polygon/>'));
assert.equal(groupElement.node.childNodes.length, 5);
assert.deepEqual(childrenTagNames(groupElement), ['ellipse', 'circle', 'line', 'polygon', 'rect']);
});
});
QUnit.module('convertToPathData()', function(hooks) {
// round all numbers in a path data string
function roundPathData(pathData) {
return pathData.split(' ').map(function(command) {
var number = parseInt(command, 10);
if (isNaN(number)) return command;
return number.toFixed(0);
}).join(' ');
}
QUnit.test('invalid', function(assert) {
assert.throws(function() {
var group = V('<group/>');
V(group).convertToPathData();
}, 'Exception thrown');
});
QUnit.test('<path>', function(assert) {
var path = V('<path/>', { d: 'M 100 50 L 200 150' });
assert.equal(path.convertToPathData(), 'M 100 50 L 200 150');
});
QUnit.test('<line>', function(assert) {
var line = V('<line/>', { x1: 100, y1: 50, x2: 200, y2: 150 });
assert.equal(line.convertToPathData(), 'M 100 50 L 200 150');
});
QUnit.test('<rect>', function(assert) {
var rect = V('<rect/>', { x: 100, y: 50, width: 200, height: 150 });
assert.equal(rect.convertToPathData(), 'M 100 50 H 300 V 200 H 100 V 50 Z');
});
QUnit.test('<rect rx ry/>', function(assert) {
var rect = V('<rect/>', { x: 100, y: 50, width: 200, height: 150, rx: 200, ry: 200 });
assert.equal(rect.convertToPathData(), 'M 100 125 v 0 a 100 75 0 0 0 100 75 h 0 a 100 75 0 0 0 100 -75 v 0 a 100 75 0 0 0 -100 -75 h 0 a 100 75 0 0 0 -100 75 Z');
});
QUnit.test('<circle>', function(assert) {
var circle = V('<circle/>', { cx: 100, cy: 50, r: 50 });
assert.equal(roundPathData(circle.convertToPathData()), 'M 100 0 C 127 0 150 22 150 50 C 150 77 127 100 100 100 C 72 100 50 77 50 50 C 50 22 72 0 100 0 Z');
});
QUnit.test('<ellipse>', function(assert) {
var ellipse = V('<ellipse/>', { cx: 100, cy: 50, rx: 100, ry: 50 });
assert.equal(roundPathData(ellipse.convertToPathData()), 'M 100 0 C 155 0 200 22 200 50 C 200 77 155 100 100 100 C 44 100 0 77 0 50 C 0 22 44 0 100 0 Z');
});
QUnit.test('<polygon>', function(assert) {
var polygon = V('<polygon/>', { points: '200,10 250,190 160,210' });
assert.equal(polygon.convertToPathData(), 'M 200 10 L250 190 L160 210 Z');
});
QUnit.test('<polyline>', function(assert) {
var polyline = V('<polyline/>', { points: '100,10 200,10 150,110' });
assert.equal(polyline.convertToPathData(), 'M 100 10 L200 10 L150 110');
});
});
QUnit.module('transformStringToMatrix()', function(hooks) {
var svgTestGroup;
hooks.beforeEach(function() {
svgTestGroup = V('g');
V(svgContainer).append(svgTestGroup);
});
hooks.afterEach(function() {
svgTestGroup.remove();
});
[
'',
'scale(2)',
'scale(2,3)',
'scale(2.5,3.1)',
'translate(10, 10)',
'translate(10,10)',
'translate(10.2,11.6)',
'rotate(10)',
'rotate(10,100,100)',
'skewX(40)',
'skewY(60)',
'scale(2,2) matrix(1 0 0 1 10 10)',
'matrix(1 0 0 1 10 10) scale(2,2)',
'rotate(10,100,100) matrix(1 0 0 1 10 10) scale(2,2) translate(10,20)'
].forEach(function(transformString) {
QUnit.test(transformString, function(assert) {
svgTestGroup.attr('transform', transformString);
assert.deepEqual(V.transformStringToMatrix(transformString), svgTestGroup.node.getCTM());
});
});
});
QUnit.module('matrixToTransformString()', function() {
QUnit.test('return correct transformation string', function(assert) {
assert.equal(V.matrixToTransformString(), 'matrix(1,0,0,1,0,0)');
assert.equal(V.matrixToTransformString({ a: 2, d: 2 }), 'matrix(2,0,0,2,0,0)');
assert.equal(V.matrixToTransformString({ a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 }), 'matrix(1,2,3,4,5,6)');
assert.equal(V.matrixToTransformString(V.createSVGMatrix({ a: 1, b: 2, c: 3, d: 4, e: 5, f: 6 })), 'matrix(1,2,3,4,5,6)');
assert.equal(V.matrixToTransformString({ a: 0, b: 1, c: 1, d: 0, e: 0, f: 0 }), 'matrix(0,1,1,0,0,0)');
});
});
QUnit.module('matrixTo[Transformation]()', function() {
function roundObject(obj) {
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
obj[i] = Math.round(obj[i]);
}
}
return obj;
}
QUnit.test('Rotate', function(assert) {
var angle;
angle = V.matrixToRotate(V.createSVGMatrix().rotate(45));
assert.deepEqual(roundObject(angle), { angle: 45 });
angle = V.matrixToRotate(V.createSVGMatrix().translate(50,50).rotate(15));
assert.deepEqual(roundObject(angle), { angle: 15 });
angle = V.matrixToRotate(V.createSVGMatrix().translate(50,50).rotate(60).scale(2));
assert.deepEqual(roundObject(angle), { angle: 60 });
angle = V.matrixToRotate(V.createSVGMatrix().rotate(60).rotate(60));
assert.deepEqual(roundObject(angle), { angle: 120 });
});
QUnit.test('Translate', function(assert) {
var translate;
translate = V.matrixToTranslate(V.createSVGMatrix().translate(10,20));
assert.deepEqual(roundObject(translate), { tx: 10, ty: 20 });
translate = V.matrixToTranslate(V.createSVGMatrix().translate(10,20).rotate(10,20).scale(2));
assert.deepEqual(roundObject(translate), { tx: 10, ty: 20 });
translate = V.matrixToTranslate(V.createSVGMatrix().translate(10,20).translate(30,40));
assert.deepEqual(roundObject(translate), { tx: 40, ty: 60 });
});
QUnit.test('Scale', function(assert) {
var scale;
scale = V.matrixToScale(V.createSVGMatrix().scale(2));
assert.deepEqual(roundObject(scale), { sx: 2, sy: 2 });
scale = V.matrixToScale(V.createSVGMatrix().translate(15,15).scaleNonUniform(2,3).rotate(10,20));
assert.deepEqual(roundObject(scale), { sx: 2, sy: 3 });
scale = V.matrixToScale(V.createSVGMatrix().scale(2,2).scale(3,3));
assert.deepEqual(roundObject(scale), { sx: 6, sy: 6 });
});
});
QUnit.module('bbox()', function() {
QUnit.test('sanity', function(assert) {
assert.ok(V(svgCircle).bbox() instanceof g.Rect);
assert.ok(V(svgCircle).bbox(true) instanceof g.Rect);
assert.ok(V(svgCircle).bbox(false, svgGroup) instanceof g.Rect);
assert.ok(V('circle', { class: 'not-in-dom' }).bbox() instanceof g.Rect);
});
});
QUnit.module('getBBox()', function() {
QUnit.test('sanity', function(assert) {
assert.ok(V(svgCircle).getBBox() instanceof g.Rect);
assert.ok(V(svgCircle).getBBox({}) instanceof g.Rect);
assert.ok(V(svgCircle).getBBox({ recursive: true }) instanceof g.Rect);
assert.ok(V(svgCircle).getBBox({ target: svgCircle }) instanceof g.Rect);
assert.ok(V(svgCircle).getBBox({ target: svgCircle, recursive: true }) instanceof g.Rect);
assert.ok(V(svgCircle).getBBox({ target: svgContainer }) instanceof g.Rect);
assert.ok(V(svgCircle).getBBox({ target: svgContainer, recursive: true }) instanceof g.Rect);
assert.ok(V('circle', { class: 'not-in-dom' }).getBBox() instanceof g.Rect);
assert.ok(V('circle', { class: 'not-in-dom' }).getBBox({}) instanceof g.Rect);
assert.ok(V('circle', { class: 'not-in-dom' }).getBBox({ recursive: true }) instanceof g.Rect);
assert.ok(V('circle', { class: 'not-in-dom' }).getBBox({ target: svgCircle }) instanceof g.Rect);
assert.ok(V('circle', { class: 'not-in-dom' }).getBBox({ target: svgCircle, recursive: true }) instanceof g.Rect);
assert.ok(V('circle', { class: 'not-in-dom' }).getBBox({ target: svgContainer }) instanceof g.Rect);
assert.ok(V('circle', { class: 'not-in-dom' }).getBBox({ target: svgContainer, recursive: true }) instanceof g.Rect);
// Not an SVGGraphicsElement
assert.ok(V(svgLinearGradient).getBBox({}) instanceof g.Rect);
assert.ok(V(svgLinearGradient).getBBox({ target: svgContainer }) instanceof g.Rect);
assert.ok(V(svgLinearGradient).getBBox({ target: svgCircle }) instanceof g.Rect);
});
QUnit.test('recursive', function(assert) {
assert.equal(V(svgGroup3).getBBox({ recursive: true }).toString(), V(svgPath2).getBBox().toString());
assert.equal(V(svgGroup3).getBBox({ recursive: true }).toString(), V(svgPath2).getBBox({ recursive: true }).toString());
assert.equal(V(svgGroup3).getBBox({ target: svgGroup1, recursive: true }).toString(), V(svgPath2).getBBox({ target: svgGroup1 }).toString());
assert.equal(V(svgGroup3).getBBox({ target: svgGroup1, recursive: true }).toString(), V(svgPath2).getBBox({ target: svgGroup1, recursive: true }).toString());
});
});
QUnit.module('normalizePath()', function() {
QUnit.test('sanity', function(assert) {
assert.ok(V(svgPath).normalizePath() instanceof V);
assert.ok(V(svgPath2).normalizePath() instanceof V);
assert.ok(V(svgPath3).normalizePath() instanceof V);
assert.ok(V(svgContainer).normalizePath() instanceof V);
assert.ok(V(svgGroup).normalizePath() instanceof V);
assert.ok(V(svgCircle).normalizePath() instanceof V);
assert.ok(V(svgEllipse).normalizePath() instanceof V);
assert.ok(V(svgPolygon).normalizePath() instanceof V);
assert.ok(V(svgText).normalizePath() instanceof V);
assert.ok(V(svgRectangle).normalizePath() instanceof V);
assert.ok(V(svgGroup1).normalizePath() instanceof V);
assert.ok(V(svgGroup2).normalizePath() instanceof V);
assert.ok(V(svgGroup3).normalizePath() instanceof V);
});
QUnit.test('normalizations', function(assert) {
assert.equal(V(svgPath).normalizePath().node.hasAttribute('d'), true);
assert.equal(V(svgPath2).normalizePath().node.hasAttribute('d'), true);
assert.equal(V(svgPath3).normalizePath().node.hasAttribute('d'), true);
assert.equal(V(svgPath).normalizePath().attr('d'), 'M 10 10');
assert.equal(V(svgPath2).normalizePath().attr('d'), 'M 100 100 C 100 100 0 150 100 200 Z');
assert.equal(V(svgPath3).normalizePath().attr('d'), 'M 0 0');
});
QUnit.test('silent failures', function(assert) {
assert.equal(V(svgContainer).normalizePath().node.hasAttribute('d'), false);
assert.equal(V(svgGroup).normalizePath().node.hasAttribute('d'), false);
assert.equal(V(svgCircle).normalizePath().node.hasAttribute('d'), false);
assert.equal(V(svgEllipse).normalizePath().node.hasAttribute('d'), false);
assert.equal(V(svgPolygon).normalizePath().node.hasAttribute('d'), false);
assert.equal(V(svgText).normalizePath().node.hasAttribute('d'), false);
assert.equal(V(svgRectangle).normalizePath().node.hasAttribute('d'), false);
assert.equal(V(svgGroup1).normalizePath().node.hasAttribute('d'), false);
assert.equal(V(svgGroup2).normalizePath().node.hasAttribute('d'), false);
assert.equal(V(svgGroup3).normalizePath().node.hasAttribute('d'), false);
});
});
QUnit.module('normalizePathData()', function() {
QUnit.test('sanity', function(assert) {
// normalizations
assert.equal(typeof V.normalizePathData('M 10 10 H 20'), 'string');
assert.equal(typeof V.normalizePathData('M 10 10 V 20'), 'string');
assert.equal(typeof V.normalizePathData('M 10 20 C 10 10 25 10 25 20 S 40 30 40 20'), 'string');
assert.equal(typeof V.normalizePathData('M 20 20 Q 40 0 60 20'), 'string');
assert.equal(typeof V.normalizePathData('M 20 20 Q 40 0 60 20 T 100 20'), 'string');
assert.equal(typeof V.normalizePathData('M 30 15 A 15 15 0 0 0 15 30'), 'string');
assert.equal(typeof V.normalizePathData('m 10 10'), 'string');
assert.equal(typeof V.normalizePathData('M 10 10 m 10 10'), 'string');
assert.equal(typeof V.normalizePathData('M 10 10 l 10 10'), 'string');
assert.equal(typeof V.normalizePathData('M 10 10 c 0 10 10 10 10 0'), 'string');
assert.equal(typeof V.normalizePathData('M 10 10 z'), 'string');
assert.equal(typeof V.normalizePathData('M 10 10 20 20'), 'string');
assert.equal(typeof V.normalizePathData('M 10 10 L 20 20 30 30'), 'string');
assert.equal(typeof V.normalizePathData('M 10 10 C 10 20 20 20 20 10 20 0 30 0 30 10'), 'string');
// edge cases
assert.equal(typeof V.normalizePathData('L 10 10'), 'string');
assert.equal(typeof V.normalizePathData('C 0 10 10 10 10 0'), 'string');
assert.equal(typeof V.normalizePathData('Z'), 'string');
assert.equal(typeof V.normalizePathData('M 10 10 Z L 20 20'), 'string');
assert.equal(typeof V.normalizePathData('M 10 10 Z C 10 20 20 20 20 10'), 'string');
assert.equal(typeof V.normalizePathData('M 10 10 Z Z'), 'string');
assert.equal(typeof V.normalizePathData(''), 'string');
assert.equal(typeof V.normalizePathData('X'), 'string');
assert.equal(typeof V.normalizePathData('M'), 'string');
assert.equal(typeof V.normalizePathData('M 10'), 'string');
assert.equal(typeof V.normalizePathData('M 10 10 20'), 'string');
assert.equal(typeof V.normalizePathData('X M 10 10'), 'string');
assert.equal(typeof V.normalizePathData('X M 10 10 X L 20 20'), 'string');
});
QUnit.test('normalizations', function(assert) {
assert.equal(V.normalizePathData('M 10 10 H 20'), 'M 10 10 L 20 10');
assert.equal(V.normalizePathData('M 10 10 V 20'), 'M 10 10 L 10 20');
assert.equal(V.normalizePathData('M 10 20 C 10 10 25 10 25 20 S 40 30 40 20'), 'M 10 20 C 10 10 25 10 25 20 C 25 30 40 30 40 20');
assert.equal(V.normalizePathData('M 20 20 Q 40 0 60 20'), 'M 20 20 C 33.33333333333333 6.666666666666666 46.666666666666664 6.666666666666666 60 20');
assert.equal(V.normalizePathData('M 20 20 Q 40 0 60 20 T 100 20'), 'M 20 20 C 33.33333333333333 6.666666666666666 46.666666666666664 6.666