2014-03-30 17:12:38 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function setupIframe(callback) {
|
|
|
|
var iframe = document.createElement('iframe');
|
|
|
|
document.body.appendChild(iframe);
|
|
|
|
var doc = iframe.contentDocument;
|
|
|
|
|
|
|
|
var link = document.createElement('link');
|
|
|
|
link.setAttribute('rel', 'stylesheet');
|
|
|
|
link.setAttribute('type', 'text/css');
|
|
|
|
link.setAttribute('href', 'style.css');
|
|
|
|
doc.head.appendChild(link);
|
|
|
|
|
|
|
|
requestAnimationFrame(function wait() {
|
|
|
|
if (doc.styleSheets.length) {
|
|
|
|
callback(iframe);
|
|
|
|
} else {
|
|
|
|
requestAnimationFrame(wait);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
var iframe = null;
|
|
|
|
|
|
|
|
function computeDOMLayout(node) {
|
|
|
|
var body = iframe.contentDocument.body;
|
|
|
|
|
|
|
|
function transferPx(div, node, name) {
|
|
|
|
if (name in node.style) {
|
|
|
|
div.style[name] = node.style[name] + 'px';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function renderNode(parent, node) {
|
|
|
|
var div = document.createElement('div');
|
|
|
|
transferPx(div, node, 'width');
|
|
|
|
transferPx(div, node, 'height');
|
2014-03-30 19:51:14 -07:00
|
|
|
transferPx(div, node, 'margin');
|
2014-03-30 17:12:38 -07:00
|
|
|
parent.appendChild(div);
|
|
|
|
(node.children || []).forEach(function(child) {
|
|
|
|
renderNode(div, child);
|
|
|
|
});
|
|
|
|
return div;
|
|
|
|
}
|
|
|
|
|
|
|
|
var div = renderNode(body, node);
|
|
|
|
|
|
|
|
function buildLayout(parentRect, div) {
|
|
|
|
var rect = div.getBoundingClientRect();
|
|
|
|
var result = {
|
|
|
|
width: rect.width,
|
|
|
|
height: rect.height,
|
|
|
|
left: rect.left - parentRect.left,
|
|
|
|
top: rect.top - parentRect.top
|
|
|
|
};
|
|
|
|
|
|
|
|
var children = [];
|
|
|
|
for (var child = div.firstChild; child; child = child.nextSibling) {
|
|
|
|
children.push(buildLayout(result, child));
|
|
|
|
}
|
|
|
|
if (children.length) {
|
|
|
|
result.children = children;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
var layout = buildLayout({left: 0, top: 0}, div);
|
|
|
|
body.removeChild(div);
|
|
|
|
return layout;
|
|
|
|
}
|
|
|
|
|
|
|
|
function testLayout(node, expectedLayout) {
|
|
|
|
var layout = computeLayout(node);
|
|
|
|
var domLayout = computeDOMLayout(node);
|
|
|
|
expect(layout).toEqual(expectedLayout)
|
|
|
|
expect(layout).toEqual(domLayout);
|
|
|
|
}
|
|
|
|
|
|
|
|
describe('Layout', function() {
|
|
|
|
beforeEach(function(done) {
|
|
|
|
if (iframe) {
|
|
|
|
done();
|
|
|
|
} else {
|
|
|
|
setupIframe(function(ifrm) { iframe = ifrm; done(); });
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should layout a single node with width and height', function() {
|
|
|
|
testLayout({
|
|
|
|
style: {width: 100, height: 200}
|
|
|
|
}, {
|
2014-03-30 19:33:24 -07:00
|
|
|
width: 100, height: 200, top: 0, left: 0
|
2014-03-30 19:18:06 -07:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should layout node with children', function() {
|
|
|
|
testLayout({
|
|
|
|
style: {width: 1000, height: 1000},
|
|
|
|
children: [
|
|
|
|
{style: {width: 500, height: 500}},
|
|
|
|
{style: {width: 250, height: 250}},
|
|
|
|
{style: {width: 125, height: 125}}
|
|
|
|
]
|
|
|
|
}, {
|
2014-03-30 19:33:24 -07:00
|
|
|
width: 1000, height: 1000, top: 0, left: 0,
|
2014-03-30 19:18:06 -07:00
|
|
|
children: [
|
|
|
|
{width: 500, height: 500, top: 0, left: 0},
|
|
|
|
{width: 250, height: 250, top: 500, left: 0},
|
|
|
|
{width: 125, height: 125, top: 750, left: 0}
|
|
|
|
]
|
2014-03-30 17:12:38 -07:00
|
|
|
});
|
|
|
|
});
|
2014-03-30 19:33:24 -07:00
|
|
|
|
|
|
|
it('should layout node with nested children', function() {
|
|
|
|
testLayout({
|
|
|
|
style: {width: 1000, height: 1000},
|
|
|
|
children: [{
|
|
|
|
style: {width: 500, height: 500}
|
|
|
|
}, {
|
|
|
|
style: {width: 500, height: 500},
|
|
|
|
children: [
|
|
|
|
{style: {width: 250, height: 250}},
|
|
|
|
{style: {width: 250, height: 250}}
|
|
|
|
]
|
|
|
|
}]
|
|
|
|
}, {
|
|
|
|
width: 1000, height: 1000, top: 0, left: 0,
|
|
|
|
children: [{
|
|
|
|
width: 500, height: 500, top: 0, left: 0
|
|
|
|
}, {
|
|
|
|
width: 500, height: 500, top: 500, left: 0,
|
|
|
|
children: [
|
|
|
|
{width: 250, height: 250, top: 0, left: 0},
|
|
|
|
{width: 250, height: 250, top: 250, left: 0},
|
|
|
|
]
|
|
|
|
}]
|
|
|
|
});
|
|
|
|
});
|
2014-03-30 19:51:14 -07:00
|
|
|
|
|
|
|
it('should layout node with margin', function() {
|
|
|
|
testLayout({
|
|
|
|
style: {width: 100, height: 200, margin: 10}
|
|
|
|
}, {
|
|
|
|
width: 100, height: 200, top: 10, left: 10
|
|
|
|
});
|
|
|
|
});
|
2014-03-30 17:12:38 -07:00
|
|
|
});
|
|
|
|
|