mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Move NodeParser to seperate file
This commit is contained in:
parent
64668fe694
commit
b4bb34c95b
@ -13,6 +13,6 @@
|
||||
"globals": {
|
||||
"jQuery": true
|
||||
},
|
||||
"predef": ["NodeContainer", "StackingContext", "TextContainer", "ImageLoader", "CanvasRenderer", "Renderer", "Support", "bind", "Promise",
|
||||
"predef": ["NodeParser", "NodeContainer", "StackingContext", "TextContainer", "ImageLoader", "CanvasRenderer", "Renderer", "Support", "bind", "Promise",
|
||||
"ImageContainer", "ProxyImageContainer", "DummyImageContainer", "log"]
|
||||
}
|
||||
|
1054
build/html2canvas.js
1054
build/html2canvas.js
File diff suppressed because it is too large
Load Diff
2
build/html2canvas.min.js
vendored
2
build/html2canvas.min.js
vendored
File diff suppressed because one or more lines are too long
530
src/core.js
530
src/core.js
@ -7,7 +7,7 @@ window.html2canvas = function(nodeList, options) {
|
||||
createWindowClone(document, window.innerWidth, window.innerHeight).then(function(container) {
|
||||
log("Document cloned");
|
||||
var clonedWindow = container.contentWindow;
|
||||
var element = (nodeList === undefined) ? document.body : nodeList[0];
|
||||
//var element = (nodeList === undefined) ? document.body : nodeList[0];
|
||||
var node = clonedWindow.document.documentElement;
|
||||
var support = new Support();
|
||||
var imageLoader = new ImageLoader(options, support);
|
||||
@ -77,532 +77,6 @@ function createWindowClone(ownerDocument, width, height) {
|
||||
var style = documentClone.createElement("style");
|
||||
style.innerHTML = "body div.html2canvas-ready-test { background-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); }";
|
||||
documentClone.body.appendChild(style);
|
||||
window.setTimeout(loadedTimer, 10);
|
||||
window.setTimeout(loadedTimer, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
function NodeParser(element, renderer, support, imageLoader, options) {
|
||||
log("Starting NodeParser");
|
||||
this.renderer = renderer;
|
||||
this.options = options;
|
||||
this.range = null;
|
||||
this.support = support;
|
||||
this.stack = new StackingContext(true, 1, element.ownerDocument, null);
|
||||
var parent = new NodeContainer(element, null);
|
||||
parent.visibile = parent.isElementVisible();
|
||||
this.nodes = [parent].concat(this.getChildren(parent)).filter(function(container) {
|
||||
return container.visible = container.isElementVisible();
|
||||
});
|
||||
log("Fetched nodes");
|
||||
this.images = imageLoader.fetch(this.nodes.filter(isElement));
|
||||
log("Creating stacking contexts");
|
||||
this.createStackingContexts();
|
||||
log("Sorting stacking contexts");
|
||||
this.sortStackingContexts(this.stack);
|
||||
this.images.ready.then(bind(function() {
|
||||
log("Images loaded, starting parsing");
|
||||
this.parse(this.stack);
|
||||
log("Finished rendering");
|
||||
options.onrendered(renderer.canvas);
|
||||
}, this));
|
||||
}
|
||||
|
||||
NodeParser.prototype.getChildren = function(parentContainer) {
|
||||
return flatten([].filter.call(parentContainer.node.childNodes, renderableNode).map(function(node) {
|
||||
var container = [node.nodeType === Node.TEXT_NODE ? new TextContainer(node, parentContainer) : new NodeContainer(node, parentContainer)].filter(nonIgnoredElement);
|
||||
return node.nodeType === Node.ELEMENT_NODE && container.length ? (container[0].isElementVisible() ? container.concat(this.getChildren(container[0])) : []) : container;
|
||||
}, this));
|
||||
};
|
||||
|
||||
NodeParser.prototype.newStackingContext = function(container, hasOwnStacking) {
|
||||
var stack = new StackingContext(hasOwnStacking, container.cssFloat('opacity'), container.node, container.parent);
|
||||
stack.visible = container.visible;
|
||||
var parentStack = stack.getParentStack(this);
|
||||
parentStack.contexts.push(stack);
|
||||
container.stack = stack;
|
||||
};
|
||||
|
||||
NodeParser.prototype.createStackingContexts = function() {
|
||||
this.nodes.forEach(function(container) {
|
||||
if (isElement(container) && (this.isRootElement(container) || hasOpacity(container) || isPositionedForStacking(container) || this.isBodyWithTransparentRoot(container))) {
|
||||
this.newStackingContext(container, true);
|
||||
} else if (isElement(container) && (isPositioned(container))) {
|
||||
this.newStackingContext(container, false);
|
||||
} else {
|
||||
container.assignStack(container.parent.stack);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
NodeParser.prototype.isBodyWithTransparentRoot = function(container) {
|
||||
return container.node.nodeName === "BODY" && this.renderer.isTransparent(container.parent.css('backgroundColor'));
|
||||
};
|
||||
|
||||
NodeParser.prototype.isRootElement = function(container) {
|
||||
return container.node.nodeName === "HTML";
|
||||
};
|
||||
|
||||
NodeParser.prototype.sortStackingContexts = function(stack) {
|
||||
stack.contexts.sort(zIndexSort);
|
||||
stack.contexts.forEach(this.sortStackingContexts, this);
|
||||
};
|
||||
|
||||
NodeParser.prototype.parseBounds = function(nodeContainer) {
|
||||
return nodeContainer.bounds = this.getBounds(nodeContainer.node);
|
||||
};
|
||||
|
||||
NodeParser.prototype.getBounds = function(node) {
|
||||
if (node.getBoundingClientRect) {
|
||||
var clientRect = node.getBoundingClientRect();
|
||||
var isBody = node.nodeName === "BODY";
|
||||
return {
|
||||
top: clientRect.top,
|
||||
bottom: clientRect.bottom || (clientRect.top + clientRect.height),
|
||||
left: clientRect.left,
|
||||
width: isBody ? node.scrollWidth : node.offsetWidth,
|
||||
height: isBody ? node.scrollHeight : node.offsetHeight
|
||||
};
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
NodeParser.prototype.parseTextBounds = function(container) {
|
||||
return function(text, index, textList) {
|
||||
if (container.parent.css("textDecoration") !== "none" || text.trim().length !== 0) {
|
||||
var offset = textList.slice(0, index).join("").length;
|
||||
if (this.support.rangeBounds) {
|
||||
return this.getRangeBounds(container.node, offset, text.length);
|
||||
} else if (container.node && typeof(container.node.data) === "string") {
|
||||
var replacementNode = container.node.splitText(text.length);
|
||||
var bounds = this.getWrapperBounds(container.node);
|
||||
container.node = replacementNode;
|
||||
return bounds;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
NodeParser.prototype.getWrapperBounds = function(node) {
|
||||
var wrapper = node.ownerDocument.createElement('wrapper');
|
||||
var parent = node.parentNode,
|
||||
backupText = node.cloneNode(true);
|
||||
|
||||
wrapper.appendChild(node.cloneNode(true));
|
||||
parent.replaceChild(wrapper, node);
|
||||
|
||||
var bounds = this.getBounds(wrapper);
|
||||
parent.replaceChild(backupText, wrapper);
|
||||
return bounds;
|
||||
};
|
||||
|
||||
NodeParser.prototype.getRangeBounds = function(node, offset, length) {
|
||||
var range = this.range || (this.range = node.ownerDocument.createRange());
|
||||
range.setStart(node, offset);
|
||||
range.setEnd(node, offset + length);
|
||||
return range.getBoundingClientRect();
|
||||
};
|
||||
|
||||
|
||||
function negativeZIndex(container) {
|
||||
return container.cssInt("zIndex") < 0;
|
||||
}
|
||||
|
||||
function positiveZIndex(container) {
|
||||
return container.cssInt("zIndex") > 0;
|
||||
}
|
||||
|
||||
function zIndex0(container) {
|
||||
return container.cssInt("zIndex") === 0;
|
||||
}
|
||||
|
||||
function inlineLevel(container) {
|
||||
return ["inline", "inline-block", "inline-table"].indexOf(container.css("display")) !== -1;
|
||||
}
|
||||
|
||||
function isStackingContext(container) {
|
||||
return (container instanceof StackingContext);
|
||||
}
|
||||
|
||||
function hasText(container) {
|
||||
return container.node.data.trim().length > 0;
|
||||
}
|
||||
|
||||
function noLetterSpacing(container) {
|
||||
return (/^(normal|none|0px)$/.test(container.parent.css("letterSpacing")));
|
||||
}
|
||||
|
||||
NodeParser.prototype.parse = function(stack) {
|
||||
// http://www.w3.org/TR/CSS21/visuren.html#z-index
|
||||
var negativeZindex = stack.contexts.filter(negativeZIndex); // 2. the child stacking contexts with negative stack levels (most negative first).
|
||||
var descendantElements = stack.children.filter(isElement);
|
||||
var descendantNonFloats = descendantElements.filter(not(isFloating));
|
||||
var nonInlineNonPositionedDescendants = descendantNonFloats.filter(not(isPositioned)).filter(not(inlineLevel)); // 3 the in-flow, non-inline-level, non-positioned descendants.
|
||||
var nonPositionedFloats = descendantElements.filter(not(isPositioned)).filter(isFloating); // 4. the non-positioned floats.
|
||||
var inFlow = descendantNonFloats.filter(not(isPositioned)).filter(inlineLevel); // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
|
||||
var stackLevel0 = stack.contexts.concat(descendantNonFloats.filter(isPositioned)).filter(zIndex0); // 6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
|
||||
var text = stack.children.filter(isTextNode).filter(hasText);
|
||||
var positiveZindex = stack.contexts.filter(positiveZIndex); // 7. the child stacking contexts with positive stack levels (least positive first).
|
||||
var rendered = [];
|
||||
negativeZindex.concat(nonInlineNonPositionedDescendants).concat(nonPositionedFloats)
|
||||
.concat(inFlow).concat(stackLevel0).concat(text).concat(positiveZindex).forEach(function(container) {
|
||||
this.paint(container);
|
||||
if (rendered.indexOf(container.node) !== -1) {
|
||||
log(container, container.node);
|
||||
throw new Error("rendering twice");
|
||||
}
|
||||
rendered.push(container.node);
|
||||
|
||||
if (isStackingContext(container)) {
|
||||
this.parse(container);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
NodeParser.prototype.paint = function(container) {
|
||||
try {
|
||||
if (isTextNode(container)) {
|
||||
this.paintText(container);
|
||||
} else {
|
||||
this.paintNode(container);
|
||||
}
|
||||
} catch(e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
NodeParser.prototype.paintNode = function(container) {
|
||||
if (isStackingContext(container)) {
|
||||
this.renderer.setOpacity(container.opacity);
|
||||
}
|
||||
|
||||
var bounds = this.parseBounds(container);
|
||||
var borderData = this.parseBorders(container);
|
||||
this.renderer.clip(borderData.clip, function() {
|
||||
this.renderer.renderBackground(container, bounds);
|
||||
}, this);
|
||||
this.renderer.renderBorders(borderData.borders);
|
||||
|
||||
switch(container.node.nodeName) {
|
||||
case "IMG":
|
||||
var imageContainer = this.images.get(container.node.src);
|
||||
if (imageContainer) {
|
||||
this.renderer.renderImage(container, bounds, borderData, imageContainer.image);
|
||||
} else {
|
||||
log("Error loading <img>", container.node.src);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
NodeParser.prototype.paintText = function(container) {
|
||||
container.applyTextTransform();
|
||||
var textList = container.node.data.split(!this.options.letterRendering || noLetterSpacing(container) ? /(\b| )/ : "");
|
||||
var weight = container.parent.fontWeight();
|
||||
var size = container.parent.css('fontSize');
|
||||
var family = container.parent.css('fontFamily');
|
||||
this.renderer.font(container.parent.css('color'), container.parent.css('fontStyle'), container.parent.css('fontVariant'), weight, size, family);
|
||||
|
||||
textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) {
|
||||
if (bounds) {
|
||||
this.renderer.text(textList[index], bounds.left, bounds.bottom);
|
||||
// renderTextDecoration(ctx, textDecoration, bounds, metrics, color);
|
||||
}
|
||||
/* var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1), stack.transform.matrix);
|
||||
if (bounds) {
|
||||
drawText(text, bounds.left, bounds.bottom, ctx);
|
||||
renderTextDecoration(ctx, textDecoration, bounds, metrics, color);
|
||||
} */
|
||||
}, this);
|
||||
};
|
||||
|
||||
NodeParser.prototype.parseBorders = function(container) {
|
||||
var nodeBounds = container.bounds;
|
||||
var radius = getBorderRadiusData(container);
|
||||
var borders = ["Top", "Right", "Bottom", "Left"].map(function(side) {
|
||||
return {
|
||||
width: container.cssInt('border' + side + 'Width'),
|
||||
color: container.css('border' + side + 'Color'),
|
||||
args: null
|
||||
};
|
||||
});
|
||||
var borderPoints = calculateCurvePoints(nodeBounds, radius, borders);
|
||||
|
||||
return {
|
||||
clip: this.parseBackgroundClip(container, borderPoints, borders, radius, nodeBounds),
|
||||
borders: borders.map(function(border, borderSide) {
|
||||
if (border.width > 0) {
|
||||
var bx = nodeBounds.left;
|
||||
var by = nodeBounds.top;
|
||||
var bw = nodeBounds.width;
|
||||
var bh = nodeBounds.height - (borders[2].width);
|
||||
|
||||
switch(borderSide) {
|
||||
case 0:
|
||||
// top border
|
||||
bh = borders[0].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx, by],
|
||||
c2: [bx + bw, by],
|
||||
c3: [bx + bw - borders[1].width, by + bh],
|
||||
c4: [bx + borders[3].width, by + bh]
|
||||
}, radius[0], radius[1],
|
||||
borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner);
|
||||
break;
|
||||
case 1:
|
||||
// right border
|
||||
bx = nodeBounds.left + nodeBounds.width - (borders[1].width);
|
||||
bw = borders[1].width;
|
||||
|
||||
border.args = drawSide({
|
||||
c1: [bx + bw, by],
|
||||
c2: [bx + bw, by + bh + borders[2].width],
|
||||
c3: [bx, by + bh],
|
||||
c4: [bx, by + borders[0].width]
|
||||
}, radius[1], radius[2],
|
||||
borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner);
|
||||
break;
|
||||
case 2:
|
||||
// bottom border
|
||||
by = (by + nodeBounds.height) - (borders[2].width);
|
||||
bh = borders[2].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx + bw, by + bh],
|
||||
c2: [bx, by + bh],
|
||||
c3: [bx + borders[3].width, by],
|
||||
c4: [bx + bw - borders[3].width, by]
|
||||
}, radius[2], radius[3],
|
||||
borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner);
|
||||
break;
|
||||
case 3:
|
||||
// left border
|
||||
bw = borders[3].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx, by + bh + borders[2].width],
|
||||
c2: [bx, by],
|
||||
c3: [bx + bw, by + borders[0].width],
|
||||
c4: [bx + bw, by + bh]
|
||||
}, radius[3], radius[0],
|
||||
borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return border;
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
NodeParser.prototype.parseBackgroundClip = function(container, borderPoints, borders, radius, bounds) {
|
||||
var backgroundClip = container.css('backgroundClip'),
|
||||
borderArgs = [];
|
||||
|
||||
switch(backgroundClip) {
|
||||
case "content-box":
|
||||
case "padding-box":
|
||||
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width);
|
||||
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width);
|
||||
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width);
|
||||
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width);
|
||||
break;
|
||||
|
||||
default:
|
||||
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top);
|
||||
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top);
|
||||
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height);
|
||||
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height);
|
||||
break;
|
||||
}
|
||||
|
||||
return borderArgs;
|
||||
};
|
||||
|
||||
function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) {
|
||||
if (radius1[0] > 0 || radius1[1] > 0) {
|
||||
borderArgs.push(["line", corner1[0].start.x, corner1[0].start.y]);
|
||||
corner1[0].curveTo(borderArgs);
|
||||
corner1[1].curveTo(borderArgs);
|
||||
} else {
|
||||
borderArgs.push(["line", x, y]);
|
||||
}
|
||||
|
||||
if (radius2[0] > 0 || radius2[1] > 0) {
|
||||
borderArgs.push(["line", corner2[0].start.x, corner2[0].start.y]);
|
||||
}
|
||||
}
|
||||
|
||||
function getBorderRadiusData(container) {
|
||||
return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side) {
|
||||
var value = container.css('border' + side + 'Radius');
|
||||
var arr = value.split(" ");
|
||||
if (arr.length <= 1) {
|
||||
arr[1] = arr[0];
|
||||
}
|
||||
return arr.map(asInt);
|
||||
});
|
||||
}
|
||||
|
||||
function asInt(value) {
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
|
||||
function getCurvePoints(x, y, r1, r2) {
|
||||
var kappa = 4 * ((Math.sqrt(2) - 1) / 3);
|
||||
var ox = (r1) * kappa, // control point offset horizontal
|
||||
oy = (r2) * kappa, // control point offset vertical
|
||||
xm = x + r1, // x-middle
|
||||
ym = y + r2; // y-middle
|
||||
return {
|
||||
topLeft: bezierCurve({x: x, y: ym}, {x: x, y: ym - oy}, {x: xm - ox, y: y}, {x: xm, y: y}),
|
||||
topRight: bezierCurve({x: x, y: y}, {x: x + ox,y: y}, {x: xm, y: ym - oy}, {x: xm, y: ym}),
|
||||
bottomRight: bezierCurve({x: xm, y: y}, {x: xm, y: y + oy}, {x: x + ox, y: ym}, {x: x, y: ym}),
|
||||
bottomLeft: bezierCurve({x: xm, y: ym}, {x: xm - ox, y: ym}, {x: x, y: y + oy}, {x: x, y:y})
|
||||
};
|
||||
}
|
||||
|
||||
function calculateCurvePoints(bounds, borderRadius, borders) {
|
||||
var x = bounds.left,
|
||||
y = bounds.top,
|
||||
width = bounds.width,
|
||||
height = bounds.height,
|
||||
|
||||
tlh = borderRadius[0][0],
|
||||
tlv = borderRadius[0][1],
|
||||
trh = borderRadius[1][0],
|
||||
trv = borderRadius[1][1],
|
||||
brh = borderRadius[2][0],
|
||||
brv = borderRadius[2][1],
|
||||
blh = borderRadius[3][0],
|
||||
blv = borderRadius[3][1];
|
||||
|
||||
var topWidth = width - trh,
|
||||
rightHeight = height - brv,
|
||||
bottomWidth = width - brh,
|
||||
leftHeight = height - blv;
|
||||
|
||||
return {
|
||||
topLeftOuter: getCurvePoints(x, y, tlh, tlv).topLeft.subdivide(0.5),
|
||||
topLeftInner: getCurvePoints(x + borders[3].width, y + borders[0].width, Math.max(0, tlh - borders[3].width), Math.max(0, tlv - borders[0].width)).topLeft.subdivide(0.5),
|
||||
topRightOuter: getCurvePoints(x + topWidth, y, trh, trv).topRight.subdivide(0.5),
|
||||
topRightInner: getCurvePoints(x + Math.min(topWidth, width + borders[3].width), y + borders[0].width, (topWidth > width + borders[3].width) ? 0 :trh - borders[3].width, trv - borders[0].width).topRight.subdivide(0.5),
|
||||
bottomRightOuter: getCurvePoints(x + bottomWidth, y + rightHeight, brh, brv).bottomRight.subdivide(0.5),
|
||||
bottomRightInner: getCurvePoints(x + Math.min(bottomWidth, width + borders[3].width), y + Math.min(rightHeight, height + borders[0].width), Math.max(0, brh - borders[1].width), Math.max(0, brv - borders[2].width)).bottomRight.subdivide(0.5),
|
||||
bottomLeftOuter: getCurvePoints(x, y + leftHeight, blh, blv).bottomLeft.subdivide(0.5),
|
||||
bottomLeftInner: getCurvePoints(x + borders[3].width, y + leftHeight, Math.max(0, blh - borders[3].width), Math.max(0, blv - borders[2].width)).bottomLeft.subdivide(0.5)
|
||||
};
|
||||
}
|
||||
|
||||
function bezierCurve(start, startControl, endControl, end) {
|
||||
var lerp = function (a, b, t) {
|
||||
return {
|
||||
x: a.x + (b.x - a.x) * t,
|
||||
y: a.y + (b.y - a.y) * t
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
start: start,
|
||||
startControl: startControl,
|
||||
endControl: endControl,
|
||||
end: end,
|
||||
subdivide: function(t) {
|
||||
var ab = lerp(start, startControl, t),
|
||||
bc = lerp(startControl, endControl, t),
|
||||
cd = lerp(endControl, end, t),
|
||||
abbc = lerp(ab, bc, t),
|
||||
bccd = lerp(bc, cd, t),
|
||||
dest = lerp(abbc, bccd, t);
|
||||
return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)];
|
||||
},
|
||||
curveTo: function(borderArgs) {
|
||||
borderArgs.push(["bezierCurve", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]);
|
||||
},
|
||||
curveToReversed: function(borderArgs) {
|
||||
borderArgs.push(["bezierCurve", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) {
|
||||
var borderArgs = [];
|
||||
|
||||
if (radius1[0] > 0 || radius1[1] > 0) {
|
||||
borderArgs.push(["line", outer1[1].start.x, outer1[1].start.y]);
|
||||
outer1[1].curveTo(borderArgs);
|
||||
} else {
|
||||
borderArgs.push([ "line", borderData.c1[0], borderData.c1[1]]);
|
||||
}
|
||||
|
||||
if (radius2[0] > 0 || radius2[1] > 0) {
|
||||
borderArgs.push(["line", outer2[0].start.x, outer2[0].start.y]);
|
||||
outer2[0].curveTo(borderArgs);
|
||||
borderArgs.push(["line", inner2[0].end.x, inner2[0].end.y]);
|
||||
inner2[0].curveToReversed(borderArgs);
|
||||
} else {
|
||||
borderArgs.push(["line", borderData.c2[0], borderData.c2[1]]);
|
||||
borderArgs.push(["line", borderData.c3[0], borderData.c3[1]]);
|
||||
}
|
||||
|
||||
if (radius1[0] > 0 || radius1[1] > 0) {
|
||||
borderArgs.push(["line", inner1[1].end.x, inner1[1].end.y]);
|
||||
inner1[1].curveToReversed(borderArgs);
|
||||
} else {
|
||||
borderArgs.push(["line", borderData.c4[0], borderData.c4[1]]);
|
||||
}
|
||||
|
||||
return borderArgs;
|
||||
}
|
||||
|
||||
|
||||
function nonIgnoredElement(nodeContainer) {
|
||||
return (nodeContainer.node.nodeType !== Node.ELEMENT_NODE || ["SCRIPT", "HEAD", "TITLE", "OBJECT", "BR"].indexOf(nodeContainer.node.nodeName) === -1);
|
||||
}
|
||||
|
||||
function flatten(arrays) {
|
||||
return [].concat.apply([], arrays);
|
||||
}
|
||||
|
||||
function renderableNode(node) {
|
||||
return (node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ELEMENT_NODE);
|
||||
}
|
||||
|
||||
function isPositionedForStacking(container) {
|
||||
var position = container.css("position");
|
||||
var zIndex = (position === "absolute" || position === "relative") ? container.css("zIndex") : "auto";
|
||||
return zIndex !== "auto";
|
||||
}
|
||||
|
||||
function isPositioned(container) {
|
||||
return container.css("position") !== "static";
|
||||
}
|
||||
|
||||
function isFloating(container) {
|
||||
return container.css("float") !== "none";
|
||||
}
|
||||
|
||||
function not(callback) {
|
||||
var context = this;
|
||||
return function() {
|
||||
return !callback.apply(context, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
function isElement(container) {
|
||||
return container.node.nodeType === Node.ELEMENT_NODE;
|
||||
}
|
||||
|
||||
function isTextNode(container) {
|
||||
return container.node.nodeType === Node.TEXT_NODE;
|
||||
}
|
||||
|
||||
function zIndexSort(a, b) {
|
||||
return a.cssInt("zIndex") - b.cssInt("zIndex");
|
||||
}
|
||||
|
||||
function hasOpacity(container) {
|
||||
return container.css("opacity") < 1;
|
||||
}
|
||||
|
||||
function bind(callback, context) {
|
||||
return function() {
|
||||
return callback.apply(context, arguments);
|
||||
};
|
||||
}
|
||||
|
523
src/nodeparser.js
Normal file
523
src/nodeparser.js
Normal file
@ -0,0 +1,523 @@
|
||||
function NodeParser(element, renderer, support, imageLoader, options) {
|
||||
log("Starting NodeParser");
|
||||
this.renderer = renderer;
|
||||
this.options = options;
|
||||
this.range = null;
|
||||
this.support = support;
|
||||
this.stack = new StackingContext(true, 1, element.ownerDocument, null);
|
||||
var parent = new NodeContainer(element, null);
|
||||
parent.visibile = parent.isElementVisible();
|
||||
this.nodes = [parent].concat(this.getChildren(parent)).filter(function(container) {
|
||||
return container.visible = container.isElementVisible();
|
||||
});
|
||||
log("Fetched nodes");
|
||||
this.images = imageLoader.fetch(this.nodes.filter(isElement));
|
||||
log("Creating stacking contexts");
|
||||
this.createStackingContexts();
|
||||
log("Sorting stacking contexts");
|
||||
this.sortStackingContexts(this.stack);
|
||||
this.images.ready.then(bind(function() {
|
||||
log("Images loaded, starting parsing");
|
||||
this.parse(this.stack);
|
||||
log("Finished rendering");
|
||||
options.onrendered(renderer.canvas);
|
||||
}, this));
|
||||
}
|
||||
|
||||
NodeParser.prototype.getChildren = function(parentContainer) {
|
||||
return flatten([].filter.call(parentContainer.node.childNodes, renderableNode).map(function(node) {
|
||||
var container = [node.nodeType === Node.TEXT_NODE ? new TextContainer(node, parentContainer) : new NodeContainer(node, parentContainer)].filter(nonIgnoredElement);
|
||||
return node.nodeType === Node.ELEMENT_NODE && container.length ? (container[0].isElementVisible() ? container.concat(this.getChildren(container[0])) : []) : container;
|
||||
}, this));
|
||||
};
|
||||
|
||||
NodeParser.prototype.newStackingContext = function(container, hasOwnStacking) {
|
||||
var stack = new StackingContext(hasOwnStacking, container.cssFloat('opacity'), container.node, container.parent);
|
||||
stack.visible = container.visible;
|
||||
var parentStack = stack.getParentStack(this);
|
||||
parentStack.contexts.push(stack);
|
||||
container.stack = stack;
|
||||
};
|
||||
|
||||
NodeParser.prototype.createStackingContexts = function() {
|
||||
this.nodes.forEach(function(container) {
|
||||
if (isElement(container) && (this.isRootElement(container) || hasOpacity(container) || isPositionedForStacking(container) || this.isBodyWithTransparentRoot(container))) {
|
||||
this.newStackingContext(container, true);
|
||||
} else if (isElement(container) && (isPositioned(container))) {
|
||||
this.newStackingContext(container, false);
|
||||
} else {
|
||||
container.assignStack(container.parent.stack);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
NodeParser.prototype.isBodyWithTransparentRoot = function(container) {
|
||||
return container.node.nodeName === "BODY" && this.renderer.isTransparent(container.parent.css('backgroundColor'));
|
||||
};
|
||||
|
||||
NodeParser.prototype.isRootElement = function(container) {
|
||||
return container.node.nodeName === "HTML";
|
||||
};
|
||||
|
||||
NodeParser.prototype.sortStackingContexts = function(stack) {
|
||||
stack.contexts.sort(zIndexSort);
|
||||
stack.contexts.forEach(this.sortStackingContexts, this);
|
||||
};
|
||||
|
||||
NodeParser.prototype.parseBounds = function(nodeContainer) {
|
||||
return nodeContainer.bounds = this.getBounds(nodeContainer.node);
|
||||
};
|
||||
|
||||
NodeParser.prototype.getBounds = function(node) {
|
||||
if (node.getBoundingClientRect) {
|
||||
var clientRect = node.getBoundingClientRect();
|
||||
var isBody = node.nodeName === "BODY";
|
||||
return {
|
||||
top: clientRect.top,
|
||||
bottom: clientRect.bottom || (clientRect.top + clientRect.height),
|
||||
left: clientRect.left,
|
||||
width: isBody ? node.scrollWidth : node.offsetWidth,
|
||||
height: isBody ? node.scrollHeight : node.offsetHeight
|
||||
};
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
NodeParser.prototype.parseTextBounds = function(container) {
|
||||
return function(text, index, textList) {
|
||||
if (container.parent.css("textDecoration") !== "none" || text.trim().length !== 0) {
|
||||
var offset = textList.slice(0, index).join("").length;
|
||||
if (this.support.rangeBounds) {
|
||||
return this.getRangeBounds(container.node, offset, text.length);
|
||||
} else if (container.node && typeof(container.node.data) === "string") {
|
||||
var replacementNode = container.node.splitText(text.length);
|
||||
var bounds = this.getWrapperBounds(container.node);
|
||||
container.node = replacementNode;
|
||||
return bounds;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
NodeParser.prototype.getWrapperBounds = function(node) {
|
||||
var wrapper = node.ownerDocument.createElement('wrapper');
|
||||
var parent = node.parentNode,
|
||||
backupText = node.cloneNode(true);
|
||||
|
||||
wrapper.appendChild(node.cloneNode(true));
|
||||
parent.replaceChild(wrapper, node);
|
||||
|
||||
var bounds = this.getBounds(wrapper);
|
||||
parent.replaceChild(backupText, wrapper);
|
||||
return bounds;
|
||||
};
|
||||
|
||||
NodeParser.prototype.getRangeBounds = function(node, offset, length) {
|
||||
var range = this.range || (this.range = node.ownerDocument.createRange());
|
||||
range.setStart(node, offset);
|
||||
range.setEnd(node, offset + length);
|
||||
return range.getBoundingClientRect();
|
||||
};
|
||||
|
||||
NodeParser.prototype.parse = function(stack) {
|
||||
// http://www.w3.org/TR/CSS21/visuren.html#z-index
|
||||
var negativeZindex = stack.contexts.filter(negativeZIndex); // 2. the child stacking contexts with negative stack levels (most negative first).
|
||||
var descendantElements = stack.children.filter(isElement);
|
||||
var descendantNonFloats = descendantElements.filter(not(isFloating));
|
||||
var nonInlineNonPositionedDescendants = descendantNonFloats.filter(not(isPositioned)).filter(not(inlineLevel)); // 3 the in-flow, non-inline-level, non-positioned descendants.
|
||||
var nonPositionedFloats = descendantElements.filter(not(isPositioned)).filter(isFloating); // 4. the non-positioned floats.
|
||||
var inFlow = descendantNonFloats.filter(not(isPositioned)).filter(inlineLevel); // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
|
||||
var stackLevel0 = stack.contexts.concat(descendantNonFloats.filter(isPositioned)).filter(zIndex0); // 6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
|
||||
var text = stack.children.filter(isTextNode).filter(hasText);
|
||||
var positiveZindex = stack.contexts.filter(positiveZIndex); // 7. the child stacking contexts with positive stack levels (least positive first).
|
||||
var rendered = [];
|
||||
negativeZindex.concat(nonInlineNonPositionedDescendants).concat(nonPositionedFloats)
|
||||
.concat(inFlow).concat(stackLevel0).concat(text).concat(positiveZindex).forEach(function(container) {
|
||||
this.paint(container);
|
||||
if (rendered.indexOf(container.node) !== -1) {
|
||||
log(container, container.node);
|
||||
throw new Error("rendering twice");
|
||||
}
|
||||
rendered.push(container.node);
|
||||
|
||||
if (isStackingContext(container)) {
|
||||
this.parse(container);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
NodeParser.prototype.paint = function(container) {
|
||||
try {
|
||||
if (isTextNode(container)) {
|
||||
this.paintText(container);
|
||||
} else {
|
||||
this.paintNode(container);
|
||||
}
|
||||
} catch(e) {
|
||||
log(e);
|
||||
}
|
||||
};
|
||||
|
||||
NodeParser.prototype.paintNode = function(container) {
|
||||
if (isStackingContext(container)) {
|
||||
this.renderer.setOpacity(container.opacity);
|
||||
}
|
||||
|
||||
var bounds = this.parseBounds(container);
|
||||
var borderData = this.parseBorders(container);
|
||||
this.renderer.clip(borderData.clip, function() {
|
||||
this.renderer.renderBackground(container, bounds);
|
||||
}, this);
|
||||
this.renderer.renderBorders(borderData.borders);
|
||||
|
||||
switch(container.node.nodeName) {
|
||||
case "IMG":
|
||||
var imageContainer = this.images.get(container.node.src);
|
||||
if (imageContainer) {
|
||||
this.renderer.renderImage(container, bounds, borderData, imageContainer.image);
|
||||
} else {
|
||||
log("Error loading <img>", container.node.src);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
NodeParser.prototype.paintText = function(container) {
|
||||
container.applyTextTransform();
|
||||
var textList = container.node.data.split(!this.options.letterRendering || noLetterSpacing(container) ? /(\b| )/ : "");
|
||||
var weight = container.parent.fontWeight();
|
||||
var size = container.parent.css('fontSize');
|
||||
var family = container.parent.css('fontFamily');
|
||||
this.renderer.font(container.parent.css('color'), container.parent.css('fontStyle'), container.parent.css('fontVariant'), weight, size, family);
|
||||
|
||||
textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) {
|
||||
if (bounds) {
|
||||
this.renderer.text(textList[index], bounds.left, bounds.bottom);
|
||||
// renderTextDecoration(ctx, textDecoration, bounds, metrics, color);
|
||||
}
|
||||
/* var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1), stack.transform.matrix);
|
||||
if (bounds) {
|
||||
drawText(text, bounds.left, bounds.bottom, ctx);
|
||||
renderTextDecoration(ctx, textDecoration, bounds, metrics, color);
|
||||
} */
|
||||
}, this);
|
||||
};
|
||||
|
||||
NodeParser.prototype.parseBorders = function(container) {
|
||||
var nodeBounds = container.bounds;
|
||||
var radius = getBorderRadiusData(container);
|
||||
var borders = ["Top", "Right", "Bottom", "Left"].map(function(side) {
|
||||
return {
|
||||
width: container.cssInt('border' + side + 'Width'),
|
||||
color: container.css('border' + side + 'Color'),
|
||||
args: null
|
||||
};
|
||||
});
|
||||
var borderPoints = calculateCurvePoints(nodeBounds, radius, borders);
|
||||
|
||||
return {
|
||||
clip: this.parseBackgroundClip(container, borderPoints, borders, radius, nodeBounds),
|
||||
borders: borders.map(function(border, borderSide) {
|
||||
if (border.width > 0) {
|
||||
var bx = nodeBounds.left;
|
||||
var by = nodeBounds.top;
|
||||
var bw = nodeBounds.width;
|
||||
var bh = nodeBounds.height - (borders[2].width);
|
||||
|
||||
switch(borderSide) {
|
||||
case 0:
|
||||
// top border
|
||||
bh = borders[0].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx, by],
|
||||
c2: [bx + bw, by],
|
||||
c3: [bx + bw - borders[1].width, by + bh],
|
||||
c4: [bx + borders[3].width, by + bh]
|
||||
}, radius[0], radius[1],
|
||||
borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner);
|
||||
break;
|
||||
case 1:
|
||||
// right border
|
||||
bx = nodeBounds.left + nodeBounds.width - (borders[1].width);
|
||||
bw = borders[1].width;
|
||||
|
||||
border.args = drawSide({
|
||||
c1: [bx + bw, by],
|
||||
c2: [bx + bw, by + bh + borders[2].width],
|
||||
c3: [bx, by + bh],
|
||||
c4: [bx, by + borders[0].width]
|
||||
}, radius[1], radius[2],
|
||||
borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner);
|
||||
break;
|
||||
case 2:
|
||||
// bottom border
|
||||
by = (by + nodeBounds.height) - (borders[2].width);
|
||||
bh = borders[2].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx + bw, by + bh],
|
||||
c2: [bx, by + bh],
|
||||
c3: [bx + borders[3].width, by],
|
||||
c4: [bx + bw - borders[3].width, by]
|
||||
}, radius[2], radius[3],
|
||||
borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner);
|
||||
break;
|
||||
case 3:
|
||||
// left border
|
||||
bw = borders[3].width;
|
||||
border.args = drawSide({
|
||||
c1: [bx, by + bh + borders[2].width],
|
||||
c2: [bx, by],
|
||||
c3: [bx + bw, by + borders[0].width],
|
||||
c4: [bx + bw, by + bh]
|
||||
}, radius[3], radius[0],
|
||||
borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return border;
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
NodeParser.prototype.parseBackgroundClip = function(container, borderPoints, borders, radius, bounds) {
|
||||
var backgroundClip = container.css('backgroundClip'),
|
||||
borderArgs = [];
|
||||
|
||||
switch(backgroundClip) {
|
||||
case "content-box":
|
||||
case "padding-box":
|
||||
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width);
|
||||
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width);
|
||||
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width);
|
||||
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width);
|
||||
break;
|
||||
|
||||
default:
|
||||
parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top);
|
||||
parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top);
|
||||
parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height);
|
||||
parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height);
|
||||
break;
|
||||
}
|
||||
|
||||
return borderArgs;
|
||||
};
|
||||
|
||||
function getCurvePoints(x, y, r1, r2) {
|
||||
var kappa = 4 * ((Math.sqrt(2) - 1) / 3);
|
||||
var ox = (r1) * kappa, // control point offset horizontal
|
||||
oy = (r2) * kappa, // control point offset vertical
|
||||
xm = x + r1, // x-middle
|
||||
ym = y + r2; // y-middle
|
||||
return {
|
||||
topLeft: bezierCurve({x: x, y: ym}, {x: x, y: ym - oy}, {x: xm - ox, y: y}, {x: xm, y: y}),
|
||||
topRight: bezierCurve({x: x, y: y}, {x: x + ox,y: y}, {x: xm, y: ym - oy}, {x: xm, y: ym}),
|
||||
bottomRight: bezierCurve({x: xm, y: y}, {x: xm, y: y + oy}, {x: x + ox, y: ym}, {x: x, y: ym}),
|
||||
bottomLeft: bezierCurve({x: xm, y: ym}, {x: xm - ox, y: ym}, {x: x, y: y + oy}, {x: x, y:y})
|
||||
};
|
||||
}
|
||||
|
||||
function calculateCurvePoints(bounds, borderRadius, borders) {
|
||||
var x = bounds.left,
|
||||
y = bounds.top,
|
||||
width = bounds.width,
|
||||
height = bounds.height,
|
||||
|
||||
tlh = borderRadius[0][0],
|
||||
tlv = borderRadius[0][1],
|
||||
trh = borderRadius[1][0],
|
||||
trv = borderRadius[1][1],
|
||||
brh = borderRadius[2][0],
|
||||
brv = borderRadius[2][1],
|
||||
blh = borderRadius[3][0],
|
||||
blv = borderRadius[3][1];
|
||||
|
||||
var topWidth = width - trh,
|
||||
rightHeight = height - brv,
|
||||
bottomWidth = width - brh,
|
||||
leftHeight = height - blv;
|
||||
|
||||
return {
|
||||
topLeftOuter: getCurvePoints(x, y, tlh, tlv).topLeft.subdivide(0.5),
|
||||
topLeftInner: getCurvePoints(x + borders[3].width, y + borders[0].width, Math.max(0, tlh - borders[3].width), Math.max(0, tlv - borders[0].width)).topLeft.subdivide(0.5),
|
||||
topRightOuter: getCurvePoints(x + topWidth, y, trh, trv).topRight.subdivide(0.5),
|
||||
topRightInner: getCurvePoints(x + Math.min(topWidth, width + borders[3].width), y + borders[0].width, (topWidth > width + borders[3].width) ? 0 :trh - borders[3].width, trv - borders[0].width).topRight.subdivide(0.5),
|
||||
bottomRightOuter: getCurvePoints(x + bottomWidth, y + rightHeight, brh, brv).bottomRight.subdivide(0.5),
|
||||
bottomRightInner: getCurvePoints(x + Math.min(bottomWidth, width + borders[3].width), y + Math.min(rightHeight, height + borders[0].width), Math.max(0, brh - borders[1].width), Math.max(0, brv - borders[2].width)).bottomRight.subdivide(0.5),
|
||||
bottomLeftOuter: getCurvePoints(x, y + leftHeight, blh, blv).bottomLeft.subdivide(0.5),
|
||||
bottomLeftInner: getCurvePoints(x + borders[3].width, y + leftHeight, Math.max(0, blh - borders[3].width), Math.max(0, blv - borders[2].width)).bottomLeft.subdivide(0.5)
|
||||
};
|
||||
}
|
||||
|
||||
function bezierCurve(start, startControl, endControl, end) {
|
||||
var lerp = function (a, b, t) {
|
||||
return {
|
||||
x: a.x + (b.x - a.x) * t,
|
||||
y: a.y + (b.y - a.y) * t
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
start: start,
|
||||
startControl: startControl,
|
||||
endControl: endControl,
|
||||
end: end,
|
||||
subdivide: function(t) {
|
||||
var ab = lerp(start, startControl, t),
|
||||
bc = lerp(startControl, endControl, t),
|
||||
cd = lerp(endControl, end, t),
|
||||
abbc = lerp(ab, bc, t),
|
||||
bccd = lerp(bc, cd, t),
|
||||
dest = lerp(abbc, bccd, t);
|
||||
return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)];
|
||||
},
|
||||
curveTo: function(borderArgs) {
|
||||
borderArgs.push(["bezierCurve", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]);
|
||||
},
|
||||
curveToReversed: function(borderArgs) {
|
||||
borderArgs.push(["bezierCurve", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) {
|
||||
var borderArgs = [];
|
||||
|
||||
if (radius1[0] > 0 || radius1[1] > 0) {
|
||||
borderArgs.push(["line", outer1[1].start.x, outer1[1].start.y]);
|
||||
outer1[1].curveTo(borderArgs);
|
||||
} else {
|
||||
borderArgs.push([ "line", borderData.c1[0], borderData.c1[1]]);
|
||||
}
|
||||
|
||||
if (radius2[0] > 0 || radius2[1] > 0) {
|
||||
borderArgs.push(["line", outer2[0].start.x, outer2[0].start.y]);
|
||||
outer2[0].curveTo(borderArgs);
|
||||
borderArgs.push(["line", inner2[0].end.x, inner2[0].end.y]);
|
||||
inner2[0].curveToReversed(borderArgs);
|
||||
} else {
|
||||
borderArgs.push(["line", borderData.c2[0], borderData.c2[1]]);
|
||||
borderArgs.push(["line", borderData.c3[0], borderData.c3[1]]);
|
||||
}
|
||||
|
||||
if (radius1[0] > 0 || radius1[1] > 0) {
|
||||
borderArgs.push(["line", inner1[1].end.x, inner1[1].end.y]);
|
||||
inner1[1].curveToReversed(borderArgs);
|
||||
} else {
|
||||
borderArgs.push(["line", borderData.c4[0], borderData.c4[1]]);
|
||||
}
|
||||
|
||||
return borderArgs;
|
||||
}
|
||||
|
||||
function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) {
|
||||
if (radius1[0] > 0 || radius1[1] > 0) {
|
||||
borderArgs.push(["line", corner1[0].start.x, corner1[0].start.y]);
|
||||
corner1[0].curveTo(borderArgs);
|
||||
corner1[1].curveTo(borderArgs);
|
||||
} else {
|
||||
borderArgs.push(["line", x, y]);
|
||||
}
|
||||
|
||||
if (radius2[0] > 0 || radius2[1] > 0) {
|
||||
borderArgs.push(["line", corner2[0].start.x, corner2[0].start.y]);
|
||||
}
|
||||
}
|
||||
|
||||
function negativeZIndex(container) {
|
||||
return container.cssInt("zIndex") < 0;
|
||||
}
|
||||
|
||||
function positiveZIndex(container) {
|
||||
return container.cssInt("zIndex") > 0;
|
||||
}
|
||||
|
||||
function zIndex0(container) {
|
||||
return container.cssInt("zIndex") === 0;
|
||||
}
|
||||
|
||||
function inlineLevel(container) {
|
||||
return ["inline", "inline-block", "inline-table"].indexOf(container.css("display")) !== -1;
|
||||
}
|
||||
|
||||
function isStackingContext(container) {
|
||||
return (container instanceof StackingContext);
|
||||
}
|
||||
|
||||
function hasText(container) {
|
||||
return container.node.data.trim().length > 0;
|
||||
}
|
||||
|
||||
function noLetterSpacing(container) {
|
||||
return (/^(normal|none|0px)$/.test(container.parent.css("letterSpacing")));
|
||||
}
|
||||
|
||||
function getBorderRadiusData(container) {
|
||||
return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side) {
|
||||
var value = container.css('border' + side + 'Radius');
|
||||
var arr = value.split(" ");
|
||||
if (arr.length <= 1) {
|
||||
arr[1] = arr[0];
|
||||
}
|
||||
return arr.map(asInt);
|
||||
});
|
||||
}
|
||||
|
||||
function renderableNode(node) {
|
||||
return (node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ELEMENT_NODE);
|
||||
}
|
||||
|
||||
function isPositionedForStacking(container) {
|
||||
var position = container.css("position");
|
||||
var zIndex = (position === "absolute" || position === "relative") ? container.css("zIndex") : "auto";
|
||||
return zIndex !== "auto";
|
||||
}
|
||||
|
||||
function isPositioned(container) {
|
||||
return container.css("position") !== "static";
|
||||
}
|
||||
|
||||
function isFloating(container) {
|
||||
return container.css("float") !== "none";
|
||||
}
|
||||
|
||||
function not(callback) {
|
||||
var context = this;
|
||||
return function() {
|
||||
return !callback.apply(context, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
function isElement(container) {
|
||||
return container.node.nodeType === Node.ELEMENT_NODE;
|
||||
}
|
||||
|
||||
function isTextNode(container) {
|
||||
return container.node.nodeType === Node.TEXT_NODE;
|
||||
}
|
||||
|
||||
function zIndexSort(a, b) {
|
||||
return a.cssInt("zIndex") - b.cssInt("zIndex");
|
||||
}
|
||||
|
||||
function hasOpacity(container) {
|
||||
return container.css("opacity") < 1;
|
||||
}
|
||||
|
||||
function bind(callback, context) {
|
||||
return function() {
|
||||
return callback.apply(context, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
function asInt(value) {
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
|
||||
function nonIgnoredElement(nodeContainer) {
|
||||
return (nodeContainer.node.nodeType !== Node.ELEMENT_NODE || ["SCRIPT", "HEAD", "TITLE", "OBJECT", "BR"].indexOf(nodeContainer.node.nodeName) === -1);
|
||||
}
|
||||
|
||||
function flatten(arrays) {
|
||||
return [].concat.apply([], arrays);
|
||||
}
|
@ -11,7 +11,7 @@ var h2cSelector, h2cOptions;
|
||||
|
||||
document.write(srcStart + '/tests/assets/jquery-1.6.2.js' + scrEnd);
|
||||
document.write(srcStart + '/tests/assets/jquery.plugin.html2canvas.js' + scrEnd);
|
||||
var html2canvas = ['log', 'nodecontainer', 'stackingcontext', 'textcontainer', 'support', 'imagecontainer', 'imageloader', 'core', 'renderer', 'promise', 'renderers/canvas'], i;
|
||||
var html2canvas = ['log', 'nodecontainer', 'stackingcontext', 'textcontainer', 'support', 'imagecontainer', 'imageloader', 'nodeparser', 'core', 'renderer', 'promise', 'renderers/canvas'], i;
|
||||
for (i = 0; i < html2canvas.length; ++i) {
|
||||
document.write(srcStart + '/src/' + html2canvas[i] + '.js?' + Math.random() + scrEnd);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user