diff --git a/.jshintrc b/.jshintrc
index 60584a5..58bab57 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -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"]
}
diff --git a/build/html2canvas.js b/build/html2canvas.js
index 95ea46b..24a4ba7 100644
--- a/build/html2canvas.js
+++ b/build/html2canvas.js
@@ -16,7 +16,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);
@@ -86,536 +86,10 @@ 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
", 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);
- };
-}
-
function ImageContainer(src, cors) {
this.src = src;
this.image = new Image();
@@ -957,6 +431,530 @@ function isPercentage(value) {
return value.toString().indexOf("%") !== -1;
}
+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
", 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);
+}
+
/*
Copyright (c) 2013 Yehuda Katz, Tom Dale, and contributors
diff --git a/build/html2canvas.min.js b/build/html2canvas.min.js
index 3df77d4..8fc6e15 100644
--- a/build/html2canvas.min.js
+++ b/build/html2canvas.min.js
@@ -4,4 +4,4 @@
Released under MIT License
*/
-(function(t,e,n){function i(){return Math.max(Math.max(e.body.scrollWidth,e.documentElement.scrollWidth),Math.max(e.body.offsetWidth,e.documentElement.offsetWidth),Math.max(e.body.clientWidth,e.documentElement.clientWidth))}function o(){return Math.max(Math.max(e.body.scrollHeight,e.documentElement.scrollHeight),Math.max(e.body.offsetHeight,e.documentElement.offsetHeight),Math.max(e.body.clientHeight,e.documentElement.clientHeight))}function r(e,n,i){var o=e.documentElement.cloneNode(!0),r=e.createElement("iframe");return r.style.display="hidden",r.style.position="absolute",r.width=n,r.height=i,r.scrolling="no",e.body.appendChild(r),new Promise(function(e){var n=function(){"none"!==r.contentWindow.getComputedStyle(s,null).backgroundImage?(i.body.removeChild(s),i.body.removeChild(a),e(r)):t.setTimeout(n,10)},i=r.contentWindow.document;i.open(),i.write(""),i.close(),i.replaceChild(i.adoptNode(o),i.documentElement),r.contentWindow.scrollTo(t.scrollX,t.scrollY);var s=i.createElement("div");s.className="html2canvas-ready-test",i.body.appendChild(s);var a=i.createElement("style");a.innerHTML="body div.html2canvas-ready-test { background-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); }",i.body.appendChild(a),t.setTimeout(n,10)})}function s(t,e,n,i,o){D("Starting NodeParser"),this.renderer=e,this.options=o,this.range=null,this.support=n,this.stack=new H(!0,1,t.ownerDocument,null);var r=new F(t,null);r.visibile=r.isElementVisible(),this.nodes=[r].concat(this.getChildren(r)).filter(function(t){return t.visible=t.isElementVisible()}),D("Fetched nodes"),this.images=i.fetch(this.nodes.filter(C)),D("Creating stacking contexts"),this.createStackingContexts(),D("Sorting stacking contexts"),this.sortStackingContexts(this.stack),this.images.ready.then(N(function(){D("Images loaded, starting parsing"),this.parse(this.stack),D("Finished rendering"),o.onrendered(e.canvas)},this))}function a(t){return 0>t.cssInt("zIndex")}function c(t){return t.cssInt("zIndex")>0}function h(t){return 0===t.cssInt("zIndex")}function u(t){return-1!==["inline","inline-block","inline-table"].indexOf(t.css("display"))}function p(t){return t instanceof H}function d(t){return t.node.data.trim().length>0}function l(t){return/^(normal|none|0px)$/.test(t.parent.css("letterSpacing"))}function f(t,e,n,i,o,r,s){e[0]>0||e[1]>0?(t.push(["line",i[0].start.x,i[0].start.y]),i[0].curveTo(t),i[1].curveTo(t)):t.push(["line",r,s]),(n[0]>0||n[1]>0)&&t.push(["line",o[0].start.x,o[0].start.y])}function g(t){return["TopLeft","TopRight","BottomRight","BottomLeft"].map(function(e){var n=t.css("border"+e+"Radius"),i=n.split(" ");return 1>=i.length&&(i[1]=i[0]),i.map(m)})}function m(t){return parseInt(t,10)}function y(t,e,n,i){var o=4*((Math.sqrt(2)-1)/3),r=n*o,s=i*o,a=t+n,c=e+i;return{topLeft:v({x:t,y:c},{x:t,y:c-s},{x:a-r,y:e},{x:a,y:e}),topRight:v({x:t,y:e},{x:t+r,y:e},{x:a,y:c-s},{x:a,y:c}),bottomRight:v({x:a,y:e},{x:a,y:e+s},{x:t+r,y:c},{x:t,y:c}),bottomLeft:v({x:a,y:c},{x:a-r,y:c},{x:t,y:e+s},{x:t,y:e})}}function w(t,e,n){var i=t.left,o=t.top,r=t.width,s=t.height,a=e[0][0],c=e[0][1],h=e[1][0],u=e[1][1],p=e[2][0],d=e[2][1],l=e[3][0],f=e[3][1],g=r-h,m=s-d,w=r-p,v=s-f;return{topLeftOuter:y(i,o,a,c).topLeft.subdivide(.5),topLeftInner:y(i+n[3].width,o+n[0].width,Math.max(0,a-n[3].width),Math.max(0,c-n[0].width)).topLeft.subdivide(.5),topRightOuter:y(i+g,o,h,u).topRight.subdivide(.5),topRightInner:y(i+Math.min(g,r+n[3].width),o+n[0].width,g>r+n[3].width?0:h-n[3].width,u-n[0].width).topRight.subdivide(.5),bottomRightOuter:y(i+w,o+m,p,d).bottomRight.subdivide(.5),bottomRightInner:y(i+Math.min(w,r+n[3].width),o+Math.min(m,s+n[0].width),Math.max(0,p-n[1].width),Math.max(0,d-n[2].width)).bottomRight.subdivide(.5),bottomLeftOuter:y(i,o+v,l,f).bottomLeft.subdivide(.5),bottomLeftInner:y(i+n[3].width,o+v,Math.max(0,l-n[3].width),Math.max(0,f-n[2].width)).bottomLeft.subdivide(.5)}}function v(t,e,n,i){var o=function(t,e,n){return{x:t.x+(e.x-t.x)*n,y:t.y+(e.y-t.y)*n}};return{start:t,startControl:e,endControl:n,end:i,subdivide:function(r){var s=o(t,e,r),a=o(e,n,r),c=o(n,i,r),h=o(s,a,r),u=o(a,c,r),p=o(h,u,r);return[v(t,s,h,p),v(p,u,c,i)]},curveTo:function(t){t.push(["bezierCurve",e.x,e.y,n.x,n.y,i.x,i.y])},curveToReversed:function(i){i.push(["bezierCurve",n.x,n.y,e.x,e.y,t.x,t.y])}}}function b(t,e,n,i,o,r,s){var a=[];return e[0]>0||e[1]>0?(a.push(["line",i[1].start.x,i[1].start.y]),i[1].curveTo(a)):a.push(["line",t.c1[0],t.c1[1]]),n[0]>0||n[1]>0?(a.push(["line",r[0].start.x,r[0].start.y]),r[0].curveTo(a),a.push(["line",s[0].end.x,s[0].end.y]),s[0].curveToReversed(a)):(a.push(["line",t.c2[0],t.c2[1]]),a.push(["line",t.c3[0],t.c3[1]])),e[0]>0||e[1]>0?(a.push(["line",o[1].end.x,o[1].end.y]),o[1].curveToReversed(a)):a.push(["line",t.c4[0],t.c4[1]]),a}function x(t){return t.node.nodeType!==Node.ELEMENT_NODE||-1===["SCRIPT","HEAD","TITLE","OBJECT","BR"].indexOf(t.node.nodeName)}function k(t){return[].concat.apply([],t)}function I(t){return t.nodeType===Node.TEXT_NODE||t.nodeType===Node.ELEMENT_NODE}function E(t){var e=t.css("position"),n="absolute"===e||"relative"===e?t.css("zIndex"):"auto";return"auto"!==n}function R(t){return"static"!==t.css("position")}function T(t){return"none"!==t.css("float")}function B(t){var e=this;return function(){return!t.apply(e,arguments)}}function C(t){return t.node.nodeType===Node.ELEMENT_NODE}function O(t){return t.node.nodeType===Node.TEXT_NODE}function S(t,e){return t.cssInt("zIndex")-e.cssInt("zIndex")}function L(t){return 1>t.css("opacity")}function N(t,e){return function(){return t.apply(e,arguments)}}function M(t,e){this.src=t,this.image=new Image;var n=this.image;this.promise=new Promise(function(i,o){n.onload=i,n.onerror=o,e&&(n.crossOrigin="anonymous"),n.src=t,n.complete===!0&&i(n)})}function A(e,n){this.link=null,this.options=e,this.support=n,this.origin=t.location.protocol+t.location.host}function P(t){return"IMG"===t.node.nodeName}function _(t){return t.node.src}function D(){t.html2canvas.logging&&t.console&&t.console.log&&t.console.log.apply(t.console,[Date.now()-t.html2canvas.start+"ms","html2canvas:"].concat([].slice.call(arguments,0)))}function F(t,e){this.node=t,this.parent=e,this.stack=null,this.bounds=null,this.visible=null,this.computedStyles=null,this.styles={},this.backgroundImages=null}function W(t){return-1!==(""+t).indexOf("%")}function j(t,e,n){this.width=t,this.height=e,this.images=n}function z(t,n){j.apply(this,arguments),this.canvas=e.createElement("canvas"),this.canvas.width=t,this.canvas.height=n,this.ctx=this.canvas.getContext("2d"),this.ctx.textBaseline="bottom",D("Initialized CanvasRenderer")}function H(t,e,n,i){F.call(this,n,i),this.ownStacking=t,this.contexts=[],this.children=[],this.opacity=(this.parent?this.parent.stack.opacity:1)*e}function V(){this.rangeBounds=this.testRangeBounds(),this.cors=this.testCORS()}function Y(t,e){F.call(this,t,e)}function X(t,e,i){return t.length>0?e+i.toUpperCase():n}t.html2canvas=function(a,c){c=c||{},c.logging&&(t.html2canvas.logging=!0,t.html2canvas.start=Date.now()),r(e,t.innerWidth,t.innerHeight).then(function(r){D("Document cloned");var h=r.contentWindow;a===n?e.body:a[0];var u=h.document.documentElement,p=new V,d=new A(c,p),l=s.prototype.getBounds(u),f="view"===c.type?Math.min(l.width,t.innerWidth):i(),g="view"===c.type?Math.min(l.height,t.innerHeight):o(),m=new z(f,g,d),y=new s(u,m,p,d,c);t.console.log(y)})},s.prototype.getChildren=function(t){return k([].filter.call(t.node.childNodes,I).map(function(e){var n=[e.nodeType===Node.TEXT_NODE?new Y(e,t):new F(e,t)].filter(x);return e.nodeType===Node.ELEMENT_NODE&&n.length?n[0].isElementVisible()?n.concat(this.getChildren(n[0])):[]:n},this))},s.prototype.newStackingContext=function(t,e){var n=new H(e,t.cssFloat("opacity"),t.node,t.parent);n.visible=t.visible;var i=n.getParentStack(this);i.contexts.push(n),t.stack=n},s.prototype.createStackingContexts=function(){this.nodes.forEach(function(t){C(t)&&(this.isRootElement(t)||L(t)||E(t)||this.isBodyWithTransparentRoot(t))?this.newStackingContext(t,!0):C(t)&&R(t)?this.newStackingContext(t,!1):t.assignStack(t.parent.stack)},this)},s.prototype.isBodyWithTransparentRoot=function(t){return"BODY"===t.node.nodeName&&this.renderer.isTransparent(t.parent.css("backgroundColor"))},s.prototype.isRootElement=function(t){return"HTML"===t.node.nodeName},s.prototype.sortStackingContexts=function(t){t.contexts.sort(S),t.contexts.forEach(this.sortStackingContexts,this)},s.prototype.parseBounds=function(t){return t.bounds=this.getBounds(t.node)},s.prototype.getBounds=function(t){if(t.getBoundingClientRect){var e=t.getBoundingClientRect(),n="BODY"===t.nodeName;return{top:e.top,bottom:e.bottom||e.top+e.height,left:e.left,width:n?t.scrollWidth:t.offsetWidth,height:n?t.scrollHeight:t.offsetHeight}}return{}},s.prototype.parseTextBounds=function(t){return function(e,n,i){if("none"!==t.parent.css("textDecoration")||0!==e.trim().length){var o=i.slice(0,n).join("").length;if(this.support.rangeBounds)return this.getRangeBounds(t.node,o,e.length);if(t.node&&"string"==typeof t.node.data){var r=t.node.splitText(e.length),s=this.getWrapperBounds(t.node);return t.node=r,s}}}},s.prototype.getWrapperBounds=function(t){var e=t.ownerDocument.createElement("wrapper"),n=t.parentNode,i=t.cloneNode(!0);e.appendChild(t.cloneNode(!0)),n.replaceChild(e,t);var o=this.getBounds(e);return n.replaceChild(i,e),o},s.prototype.getRangeBounds=function(t,e,n){var i=this.range||(this.range=t.ownerDocument.createRange());return i.setStart(t,e),i.setEnd(t,e+n),i.getBoundingClientRect()},s.prototype.parse=function(t){var e=t.contexts.filter(a),n=t.children.filter(C),i=n.filter(B(T)),o=i.filter(B(R)).filter(B(u)),r=n.filter(B(R)).filter(T),s=i.filter(B(R)).filter(u),l=t.contexts.concat(i.filter(R)).filter(h),f=t.children.filter(O).filter(d),g=t.contexts.filter(c),m=[];e.concat(o).concat(r).concat(s).concat(l).concat(f).concat(g).forEach(function(t){if(this.paint(t),-1!==m.indexOf(t.node))throw D(t,t.node),Error("rendering twice");m.push(t.node),p(t)&&this.parse(t)},this)},s.prototype.paint=function(t){try{O(t)?this.paintText(t):this.paintNode(t)}catch(e){D(e)}},s.prototype.paintNode=function(t){p(t)&&this.renderer.setOpacity(t.opacity);var e=this.parseBounds(t),n=this.parseBorders(t);switch(this.renderer.clip(n.clip,function(){this.renderer.renderBackground(t,e)},this),this.renderer.renderBorders(n.borders),t.node.nodeName){case"IMG":var i=this.images.get(t.node.src);i?this.renderer.renderImage(t,e,n,i.image):D("Error loading
",t.node.src)}},s.prototype.paintText=function(t){t.applyTextTransform();var e=t.node.data.split(!this.options.letterRendering||l(t)?/(\b| )/:""),n=t.parent.fontWeight(),i=t.parent.css("fontSize"),o=t.parent.css("fontFamily");this.renderer.font(t.parent.css("color"),t.parent.css("fontStyle"),t.parent.css("fontVariant"),n,i,o),e.map(this.parseTextBounds(t),this).forEach(function(t,n){t&&this.renderer.text(e[n],t.left,t.bottom)},this)},s.prototype.parseBorders=function(t){var e=t.bounds,n=g(t),i=["Top","Right","Bottom","Left"].map(function(e){return{width:t.cssInt("border"+e+"Width"),color:t.css("border"+e+"Color"),args:null}}),o=w(e,n,i);return{clip:this.parseBackgroundClip(t,o,i,n,e),borders:i.map(function(t,r){if(t.width>0){var s=e.left,a=e.top,c=e.width,h=e.height-i[2].width;switch(r){case 0:h=i[0].width,t.args=b({c1:[s,a],c2:[s+c,a],c3:[s+c-i[1].width,a+h],c4:[s+i[3].width,a+h]},n[0],n[1],o.topLeftOuter,o.topLeftInner,o.topRightOuter,o.topRightInner);break;case 1:s=e.left+e.width-i[1].width,c=i[1].width,t.args=b({c1:[s+c,a],c2:[s+c,a+h+i[2].width],c3:[s,a+h],c4:[s,a+i[0].width]},n[1],n[2],o.topRightOuter,o.topRightInner,o.bottomRightOuter,o.bottomRightInner);break;case 2:a=a+e.height-i[2].width,h=i[2].width,t.args=b({c1:[s+c,a+h],c2:[s,a+h],c3:[s+i[3].width,a],c4:[s+c-i[3].width,a]},n[2],n[3],o.bottomRightOuter,o.bottomRightInner,o.bottomLeftOuter,o.bottomLeftInner);break;case 3:c=i[3].width,t.args=b({c1:[s,a+h+i[2].width],c2:[s,a],c3:[s+c,a+i[0].width],c4:[s+c,a+h]},n[3],n[0],o.bottomLeftOuter,o.bottomLeftInner,o.topLeftOuter,o.topLeftInner)}}return t})}},s.prototype.parseBackgroundClip=function(t,e,n,i,o){var r=t.css("backgroundClip"),s=[];switch(r){case"content-box":case"padding-box":f(s,i[0],i[1],e.topLeftInner,e.topRightInner,o.left+n[3].width,o.top+n[0].width),f(s,i[1],i[2],e.topRightInner,e.bottomRightInner,o.left+o.width-n[1].width,o.top+n[0].width),f(s,i[2],i[3],e.bottomRightInner,e.bottomLeftInner,o.left+o.width-n[1].width,o.top+o.height-n[2].width),f(s,i[3],i[0],e.bottomLeftInner,e.topLeftInner,o.left+n[3].width,o.top+o.height-n[2].width);break;default:f(s,i[0],i[1],e.topLeftOuter,e.topRightOuter,o.left,o.top),f(s,i[1],i[2],e.topRightOuter,e.bottomRightOuter,o.left+o.width,o.top),f(s,i[2],i[3],e.bottomRightOuter,e.bottomLeftOuter,o.left+o.width,o.top+o.height),f(s,i[3],i[0],e.bottomLeftOuter,e.topLeftOuter,o.left,o.top+o.height)}return s},A.prototype.findImages=function(t){var e=[];return t.filter(P).map(_).forEach(this.addImage(e,this.loadImage),this),e},A.prototype.findBackgroundImage=function(t,e){return e.parseBackgroundImages().filter(this.isImageBackground).map(this.getBackgroundUrl).forEach(this.addImage(t,this.loadImage),this),t},A.prototype.addImage=function(t,e){return function(n){this.imageExists(t,n)||(t.splice(0,0,e.apply(this,arguments)),D("Added image #"+t.length,n.substring(0,100)))}},A.prototype.getBackgroundUrl=function(t){return t.args[0]},A.prototype.isImageBackground=function(t){return"url"===t.method},A.prototype.loadImage=function(t){return t.match(/data:image\/.*;base64,/i)?new M(t.replace(/url\(['"]{0,}|['"]{0,}\)$/gi,""),!1):this.isSameOrigin(t)||this.options.allowTaint===!0?new M(t,!1):this.support.cors&&!this.options.allowTaint&&this.options.useCORS?new M(t,!0):this.options.proxy?new ProxyImageContainer(t):new DummyImageContainer(t)},A.prototype.imageExists=function(t,e){return t.some(function(t){return t.src===e})},A.prototype.isSameOrigin=function(t){var n=this.link||(this.link=e.createElement("a"));n.href=t,n.href=n.href;var i=n.protocol+n.host;return i===this.origin},A.prototype.getPromise=function(t){return t.promise},A.prototype.get=function(t){var e=null;return this.images.some(function(n){return(e=n).src===t})?e:null},A.prototype.fetch=function(t){return this.images=t.reduce(N(this.findBackgroundImage,this),this.findImages(t)),this.images.forEach(function(t,e){t.promise.then(function(){D("Succesfully loaded image #"+(e+1))},function(){D("Failed loading image #"+(e+1))})}),this.ready=Promise.all(this.images.map(this.getPromise)),D("Finished searching images"),this},F.prototype.assignStack=function(t){this.stack=t,t.children.push(this)},F.prototype.isElementVisible=function(){return this.node.nodeType===Node.TEXT_NODE?this.parent.visible:"none"!==this.css("display")&&"hidden"!==this.css("visibility")&&!this.node.hasAttribute("data-html2canvas-ignore")},F.prototype.css=function(t){return this.computedStyles||(this.computedStyles=this.node.ownerDocument.defaultView.getComputedStyle(this.node,null)),this.styles[t]||(this.styles[t]=this.computedStyles[t])},F.prototype.cssInt=function(t){var e=parseInt(this.css(t),10);return isNaN(e)?0:e},F.prototype.cssFloat=function(t){var e=parseFloat(this.css(t));return isNaN(e)?0:e},F.prototype.fontWeight=function(){var t=this.css("fontWeight");switch(parseInt(t,10)){case 401:t="bold";break;case 400:t="normal"}return t},F.prototype.parseBackgroundImages=function(){var t,e,i,o,r,s,a,c=" \r\n ",h=[],u=0,p=0,d=function(){t&&('"'===e.substr(0,1)&&(e=e.substr(1,e.length-2)),e&&a.push(e),"-"===t.substr(0,1)&&(o=t.indexOf("-",1)+1)>0&&(i=t.substr(0,o),t=t.substr(o)),h.push({prefix:i,method:t.toLowerCase(),value:r,args:a,image:null})),a=[],t=i=e=r=""};return a=[],t=i=e=r="",this.css("backgroundImage").split("").forEach(function(i){if(!(0===u&&c.indexOf(i)>-1)){switch(i){case'"':s?s===i&&(s=null):s=i;break;case"(":if(s)break;if(0===u)return u=1,r+=i,n;p++;break;case")":if(s)break;if(1===u){if(0===p)return u=0,r+=i,d(),n;p--}break;case",":if(s)break;if(0===u)return d(),n;if(1===u&&0===p&&!t.match(/^url$/i))return a.push(e),e="",r+=i,n}r+=i,0===u?t+=i:e+=i}}),d(),this.backgroundImages||(this.backgroundImages=h)},F.prototype.cssList=function(t,e){var n=(this.css(t)||"").split(",");return n=n[e||0]||n[0]||"auto",n=n.trim().split(" "),1===n.length&&(n=[n[0],n[0]]),n},F.prototype.parseBackgroundSize=function(t,e,n){var i,o,r=this.cssList("backgroundSize",n);if(W(r[0]))i=t.width*parseFloat(r[0])/100;else{if(/contain|cover/.test(r[0])){var s=t.width/t.height,a=e.width/e.height;return a>s^"contain"===r[0]?{width:t.height*a,height:t.height}:{width:t.width,height:t.width/a}}i=parseInt(r[0],10)}return o="auto"===r[0]&&"auto"===r[1]?e.height:"auto"===r[1]?i/e.width*e.height:W(r[1])?t.height*parseFloat(r[1])/100:parseInt(r[1],10),"auto"===r[0]&&(i=o/e.height*e.width),{width:i,height:o}},F.prototype.parseBackgroundPosition=function(t,e,n,i){var o,r,s=this.cssList("backgroundPosition",n);return o=W(s[0])?(t.width-(i||e).width)*(parseFloat(s[0])/100):parseInt(s[0],10),r="auto"===s[1]?o/e.width*e.height:W(s[1])?(t.height-(i||e).height)*parseFloat(s[1])/100:parseInt(s[1],10),"auto"===s[0]&&(o=r/e.height*e.width),{left:o,top:r}},F.prototype.parseBackgroundRepeat=function(t){return this.cssList("backgroundRepeat",t)[0]},!function(){var i,o,r,s;!function(){var t={},e={};i=function(e,n,i){t[e]={deps:n,callback:i}},s=r=o=function(n){function i(t){if("."!==t.charAt(0))return t;for(var e=t.split("/"),i=n.split("/").slice(0,-1),o=0,r=e.length;r>o;o++){var s=e[o];if(".."===s)i.pop();else{if("."===s)continue;i.push(s)}}return i.join("/")}if(s._eak_seen=t,e[n])return e[n];if(e[n]={},!t[n])throw Error("Could not find module "+n);for(var r,a=t[n],c=a.deps,h=a.callback,u=[],p=0,d=c.length;d>p;p++)"exports"===c[p]?u.push(r={}):u.push(o(i(c[p])));var l=h.apply(this,u);return e[n]=r||l}}(),i("promise/all",["./utils","exports"],function(t,e){"use strict";function n(t){var e=this;if(!i(t))throw new TypeError("You must pass an array to all.");return new e(function(e,n){function i(t){return function(e){r(t,e)}}function r(t,n){a[t]=n,0===--c&&e(a)}var s,a=[],c=t.length;0===c&&e([]);for(var h=0;t.length>h;h++)s=t[h],s&&o(s.then)?s.then(i(h),n):r(h,s)})}var i=t.isArray,o=t.isFunction;e.all=n}),i("promise/asap",["exports"],function(i){"use strict";function o(){return function(){process.nextTick(a)}}function r(){var t=0,n=new p(a),i=e.createTextNode("");return n.observe(i,{characterData:!0}),function(){i.data=t=++t%2}}function s(){return function(){d.setTimeout(a,1)}}function a(){for(var t=0;l.length>t;t++){var e=l[t],n=e[0],i=e[1];n(i)}l=[]}function c(t,e){var n=l.push([t,e]);1===n&&h()}var h,u=t!==n?t:{},p=u.MutationObserver||u.WebKitMutationObserver,d="undefined"!=typeof global?global:this,l=[];h="undefined"!=typeof process&&"[object process]"==={}.toString.call(process)?o():p?r():s(),i.asap=c}),i("promise/cast",["exports"],function(t){"use strict";function e(t){if(t&&"object"==typeof t&&t.constructor===this)return t;var e=this;return new e(function(e){e(t)})}t.cast=e}),i("promise/config",["exports"],function(t){"use strict";function e(t,e){return 2!==arguments.length?n[t]:(n[t]=e,void 0)}var n={instrument:!1};t.config=n,t.configure=e}),i("promise/polyfill",["./promise","./utils","exports"],function(e,n,i){"use strict";function o(){var e="Promise"in t&&"cast"in t.Promise&&"resolve"in t.Promise&&"reject"in t.Promise&&"all"in t.Promise&&"race"in t.Promise&&function(){var e;return new t.Promise(function(t){e=t}),s(e)}();e||(t.Promise=r)}var r=e.Promise,s=n.isFunction;i.polyfill=o}),i("promise/promise",["./config","./utils","./cast","./all","./race","./resolve","./reject","./asap","exports"],function(t,e,n,i,o,r,s,a,c){"use strict";function h(t){if(!k(t))throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");if(!(this instanceof h))throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");this._subscribers=[],u(t,this)}function u(t,e){function n(t){g(e,t)}function i(t){y(e,t)}try{t(n,i)}catch(o){i(o)}}function p(t,e,n,i){var o,r,s,a,c=k(n);if(c)try{o=n(i),s=!0}catch(h){a=!0,r=h}else o=i,s=!0;f(e,o)||(c&&s?g(e,o):a?y(e,r):t===L?g(e,o):t===N&&y(e,o))}function d(t,e,n,i){var o=t._subscribers,r=o.length;o[r]=e,o[r+L]=n,o[r+N]=i}function l(t,e){for(var n,i,o=t._subscribers,r=t._detail,s=0;o.length>s;s+=3)n=o[s],i=o[s+e],p(e,n,i,r);t._subscribers=null}function f(t,e){var n,i=null;try{if(t===e)throw new TypeError("A promises callback cannot return that same promise.");if(x(e)&&(i=e.then,k(i)))return i.call(e,function(i){return n?!0:(n=!0,e!==i?g(t,i):m(t,i),void 0)},function(e){return n?!0:(n=!0,y(t,e),void 0)}),!0}catch(o){return n?!0:(y(t,o),!0)}return!1}function g(t,e){t===e?m(t,e):f(t,e)||m(t,e)}function m(t,e){t._state===O&&(t._state=S,t._detail=e,b.async(w,t))}function y(t,e){t._state===O&&(t._state=S,t._detail=e,b.async(v,t))}function w(t){l(t,t._state=L)}function v(t){l(t,t._state=N)}var b=t.config,x=(t.configure,e.objectOrFunction),k=e.isFunction,I=(e.now,n.cast),E=i.all,R=o.race,T=r.resolve,B=s.reject,C=a.asap;b.async=C;var O=void 0,S=0,L=1,N=2;h.prototype={constructor:h,_state:void 0,_detail:void 0,_subscribers:void 0,then:function(t,e){var n=this,i=new this.constructor(function(){});if(this._state){var o=arguments;b.async(function(){p(n._state,i,o[n._state-1],n._detail)})}else d(this,i,t,e);return i},"catch":function(t){return this.then(null,t)}},h.all=E,h.cast=I,h.race=R,h.resolve=T,h.reject=B,c.Promise=h}),i("promise/race",["./utils","exports"],function(t,e){"use strict";function n(t){var e=this;if(!i(t))throw new TypeError("You must pass an array to race.");return new e(function(e,n){for(var i,o=0;t.length>o;o++)i=t[o],i&&"function"==typeof i.then?i.then(e,n):e(i)})}var i=t.isArray;e.race=n}),i("promise/reject",["exports"],function(t){"use strict";function e(t){var e=this;return new e(function(e,n){n(t)})}t.reject=e}),i("promise/resolve",["exports"],function(t){"use strict";function e(t){var e=this;return new e(function(e){e(t)})}t.resolve=e}),i("promise/utils",["exports"],function(t){"use strict";function e(t){return n(t)||"object"==typeof t&&null!==t}function n(t){return"function"==typeof t}function i(t){return"[object Array]"===Object.prototype.toString.call(t)}var o=Date.now||function(){return(new Date).getTime()};t.objectOrFunction=e,t.isFunction=n,t.isArray=i,t.now=o}),o("promise/polyfill").polyfill()}(),j.prototype.renderImage=function(t,e,n,i){var o=t.cssInt("paddingLeft"),r=t.cssInt("paddingTop"),s=t.cssInt("paddingRight"),a=t.cssInt("paddingBottom"),c=n.borders;this.drawImage(i,0,0,i.width,i.height,e.left+o+c[3].width,e.top+r+c[0].width,e.width-(c[1].width+c[3].width+o+s),e.height-(c[0].width+c[2].width+r+a))},j.prototype.renderBackground=function(t,e){e.height>0&&e.width>0&&(this.renderBackgroundColor(t,e),this.renderBackgroundImage(t,e))},j.prototype.renderBackgroundColor=function(t,e){var n=t.css("backgroundColor");this.isTransparent(n)||this.rectangle(e.left,e.top,e.width,e.height,t.css("backgroundColor"))},j.prototype.renderBorders=function(t){t.forEach(this.renderBorder,this)},j.prototype.renderBorder=function(t){this.isTransparent(t.color)||null===t.args||this.drawShape(t.args,t.color)},j.prototype.renderBackgroundImage=function(t,e){var n=t.parseBackgroundImages();n.reverse().forEach(function(n,i,o){if("url"===n.method){var r=this.images.get(n.args[0]);r?this.renderBackgroundRepeating(t,e,r,o.length-(i+1)):D("Error loading background-image",n.args[0])}},this)},j.prototype.renderBackgroundRepeating=function(t,e,n,i){var o=t.parseBackgroundSize(e,n.image,i),r=t.parseBackgroundPosition(e,n.image,i,o),s=t.parseBackgroundRepeat(i);switch(s){case"repeat-x":case"repeat no-repeat":this.backgroundRepeatShape(n,r,o,e,e.left,e.top+r.top,99999,n.image.height);break;case"repeat-y":case"no-repeat repeat":this.backgroundRepeatShape(n,r,o,e,e.left+r.left,e.top,n.image.width,99999);break;case"no-repeat":this.backgroundRepeatShape(n,r,o,e,e.left+r.left,e.top+r.top,n.image.width,n.image.height);break;default:this.renderBackgroundRepeat(n,r,o,{top:e.top,left:e.left})}},j.prototype.isTransparent=function(t){return!t||"transparent"===t||"rgba(0, 0, 0, 0)"===t},z.prototype=Object.create(j.prototype),z.prototype.setFillStyle=function(t){return this.ctx.fillStyle=t,this.ctx},z.prototype.rectangle=function(t,e,n,i,o){this.setFillStyle(o).fillRect(t,e,n,i)},z.prototype.drawShape=function(t,e){this.shape(t),this.setFillStyle(e).fill()},z.prototype.drawImage=function(t,e,n,i,o,r,s,a,c){this.ctx.drawImage(t,e,n,i,o,r,s,a,c)},z.prototype.clip=function(t,e,n){this.ctx.save(),this.shape(t).clip(),e.call(n),this.ctx.restore()},z.prototype.shape=function(t){return this.ctx.beginPath(),t.forEach(function(t,e){this.ctx[0===e?"moveTo":t[0]+"To"].apply(this.ctx,t.slice(1))},this),this.ctx.closePath(),this.ctx},z.prototype.font=function(t,e,n,i,o,r){this.setFillStyle(t).font=[e,n,i,o,r].join(" ")},z.prototype.setOpacity=function(t){this.ctx.globalAlpha=t},z.prototype.text=function(t,e,n){this.ctx.fillText(t,e,n)},z.prototype.backgroundRepeatShape=function(t,e,n,i,o,r,s,a){var c=[["line",Math.round(o),Math.round(r)],["line",Math.round(o+s),Math.round(r)],["line",Math.round(o+s),Math.round(a+r)],["line",Math.round(o),Math.round(a+r)]];this.clip(c,function(){this.renderBackgroundRepeat(t,e,n,i)},this)},z.prototype.renderBackgroundRepeat=function(t,e,n,i){var o=Math.round(i.left+e.left),r=Math.round(i.top+e.top);this.setFillStyle(this.ctx.createPattern(this.resizeImage(t,n),"repeat")),this.ctx.translate(o,r),this.ctx.fill(),this.ctx.translate(-o,-r)},z.prototype.resizeImage=function(t,n){var i=t.image;if(i.width===n.width&&i.height===n.height)return i;var o,r=e.createElement("canvas");return r.width=n.width,r.height=n.height,o=r.getContext("2d"),o.drawImage(i,0,0,i.width,i.height,0,0,n.width,n.height),r},H.prototype=Object.create(F.prototype),H.prototype.getParentStack=function(t){var e=this.parent?this.parent.stack:null;return e?e.ownStacking?e:e.getParentStack(t):t.stack},V.prototype.testRangeBounds=function(){var t,n,i,o,r=!1;return e.createRange&&(t=e.createRange(),t.getBoundingClientRect&&(n=e.createElement("boundtest"),n.style.height="123px",n.style.display="block",e.body.appendChild(n),t.selectNode(n),i=t.getBoundingClientRect(),o=i.height,123===o&&(r=!0),e.body.removeChild(n))),r},V.prototype.testCORS=function(){return(new Image).crossOrigin!==n},Y.prototype=Object.create(F.prototype),Y.prototype.applyTextTransform=function(){this.node.data=this.transform(this.parent.css("textTransform"))},Y.prototype.transform=function(t){var e=this.node.data;switch(t){case"lowercase":return e.toLowerCase();case"capitalize":return e.replace(/(^|\s|:|-|\(|\))([a-z])/g,X);case"uppercase":return e.toUpperCase();default:return e}}})(window,document);
\ No newline at end of file
+(function(t,e){function n(){return Math.max(Math.max(e.body.scrollWidth,e.documentElement.scrollWidth),Math.max(e.body.offsetWidth,e.documentElement.offsetWidth),Math.max(e.body.clientWidth,e.documentElement.clientWidth))}function i(){return Math.max(Math.max(e.body.scrollHeight,e.documentElement.scrollHeight),Math.max(e.body.offsetHeight,e.documentElement.offsetHeight),Math.max(e.body.clientHeight,e.documentElement.clientHeight))}function o(e,n,i){var o=e.documentElement.cloneNode(!0),r=e.createElement("iframe");return r.style.display="hidden",r.style.position="absolute",r.width=n,r.height=i,r.scrolling="no",e.body.appendChild(r),new Promise(function(e){var n=function(){"none"!==r.contentWindow.getComputedStyle(s,null).backgroundImage?(i.body.removeChild(s),i.body.removeChild(a),e(r)):t.setTimeout(n,10)},i=r.contentWindow.document;i.open(),i.write(""),i.close(),i.replaceChild(i.adoptNode(o),i.documentElement),r.contentWindow.scrollTo(t.scrollX,t.scrollY);var s=i.createElement("div");s.className="html2canvas-ready-test",i.body.appendChild(s);var a=i.createElement("style");a.innerHTML="body div.html2canvas-ready-test { background-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); }",i.body.appendChild(a),t.setTimeout(n,1e3)})}function r(t,e){this.src=t,this.image=new Image;var n=this.image;this.promise=new Promise(function(i,o){n.onload=i,n.onerror=o,e&&(n.crossOrigin="anonymous"),n.src=t,n.complete===!0&&i(n)})}function s(e,n){this.link=null,this.options=e,this.support=n,this.origin=t.location.protocol+t.location.host}function a(t){return"IMG"===t.node.nodeName}function c(t){return t.node.src}function u(){t.html2canvas.logging&&t.console&&t.console.log&&t.console.log.apply(t.console,[Date.now()-t.html2canvas.start+"ms","html2canvas:"].concat([].slice.call(arguments,0)))}function h(t,e){this.node=t,this.parent=e,this.stack=null,this.bounds=null,this.visible=null,this.computedStyles=null,this.styles={},this.backgroundImages=null}function p(t){return-1!==(""+t).indexOf("%")}function d(t,e,n,i,o){u("Starting NodeParser"),this.renderer=e,this.options=o,this.range=null,this.support=n,this.stack=new z(!0,1,t.ownerDocument,null);var r=new h(t,null);r.visibile=r.isElementVisible(),this.nodes=[r].concat(this.getChildren(r)).filter(function(t){return t.visible=t.isElementVisible()}),u("Fetched nodes"),this.images=i.fetch(this.nodes.filter(L)),u("Creating stacking contexts"),this.createStackingContexts(),u("Sorting stacking contexts"),this.sortStackingContexts(this.stack),this.images.ready.then(P(function(){u("Images loaded, starting parsing"),this.parse(this.stack),u("Finished rendering"),o.onrendered(e.canvas)},this))}function l(t,e,n,i){var o=4*((Math.sqrt(2)-1)/3),r=n*o,s=i*o,a=t+n,c=e+i;return{topLeft:g({x:t,y:c},{x:t,y:c-s},{x:a-r,y:e},{x:a,y:e}),topRight:g({x:t,y:e},{x:t+r,y:e},{x:a,y:c-s},{x:a,y:c}),bottomRight:g({x:a,y:e},{x:a,y:e+s},{x:t+r,y:c},{x:t,y:c}),bottomLeft:g({x:a,y:c},{x:a-r,y:c},{x:t,y:e+s},{x:t,y:e})}}function f(t,e,n){var i=t.left,o=t.top,r=t.width,s=t.height,a=e[0][0],c=e[0][1],u=e[1][0],h=e[1][1],p=e[2][0],d=e[2][1],f=e[3][0],g=e[3][1],m=r-u,y=s-d,w=r-p,v=s-g;return{topLeftOuter:l(i,o,a,c).topLeft.subdivide(.5),topLeftInner:l(i+n[3].width,o+n[0].width,Math.max(0,a-n[3].width),Math.max(0,c-n[0].width)).topLeft.subdivide(.5),topRightOuter:l(i+m,o,u,h).topRight.subdivide(.5),topRightInner:l(i+Math.min(m,r+n[3].width),o+n[0].width,m>r+n[3].width?0:u-n[3].width,h-n[0].width).topRight.subdivide(.5),bottomRightOuter:l(i+w,o+y,p,d).bottomRight.subdivide(.5),bottomRightInner:l(i+Math.min(w,r+n[3].width),o+Math.min(y,s+n[0].width),Math.max(0,p-n[1].width),Math.max(0,d-n[2].width)).bottomRight.subdivide(.5),bottomLeftOuter:l(i,o+v,f,g).bottomLeft.subdivide(.5),bottomLeftInner:l(i+n[3].width,o+v,Math.max(0,f-n[3].width),Math.max(0,g-n[2].width)).bottomLeft.subdivide(.5)}}function g(t,e,n,i){var o=function(t,e,n){return{x:t.x+(e.x-t.x)*n,y:t.y+(e.y-t.y)*n}};return{start:t,startControl:e,endControl:n,end:i,subdivide:function(r){var s=o(t,e,r),a=o(e,n,r),c=o(n,i,r),u=o(s,a,r),h=o(a,c,r),p=o(u,h,r);return[g(t,s,u,p),g(p,h,c,i)]},curveTo:function(t){t.push(["bezierCurve",e.x,e.y,n.x,n.y,i.x,i.y])},curveToReversed:function(i){i.push(["bezierCurve",n.x,n.y,e.x,e.y,t.x,t.y])}}}function m(t,e,n,i,o,r,s){var a=[];return e[0]>0||e[1]>0?(a.push(["line",i[1].start.x,i[1].start.y]),i[1].curveTo(a)):a.push(["line",t.c1[0],t.c1[1]]),n[0]>0||n[1]>0?(a.push(["line",r[0].start.x,r[0].start.y]),r[0].curveTo(a),a.push(["line",s[0].end.x,s[0].end.y]),s[0].curveToReversed(a)):(a.push(["line",t.c2[0],t.c2[1]]),a.push(["line",t.c3[0],t.c3[1]])),e[0]>0||e[1]>0?(a.push(["line",o[1].end.x,o[1].end.y]),o[1].curveToReversed(a)):a.push(["line",t.c4[0],t.c4[1]]),a}function y(t,e,n,i,o,r,s){e[0]>0||e[1]>0?(t.push(["line",i[0].start.x,i[0].start.y]),i[0].curveTo(t),i[1].curveTo(t)):t.push(["line",r,s]),(n[0]>0||n[1]>0)&&t.push(["line",o[0].start.x,o[0].start.y])}function w(t){return 0>t.cssInt("zIndex")}function v(t){return t.cssInt("zIndex")>0}function b(t){return 0===t.cssInt("zIndex")}function x(t){return-1!==["inline","inline-block","inline-table"].indexOf(t.css("display"))}function k(t){return t instanceof z}function I(t){return t.node.data.trim().length>0}function E(t){return/^(normal|none|0px)$/.test(t.parent.css("letterSpacing"))}function R(t){return["TopLeft","TopRight","BottomRight","BottomLeft"].map(function(e){var n=t.css("border"+e+"Radius"),i=n.split(" ");return 1>=i.length&&(i[1]=i[0]),i.map(_)})}function T(t){return t.nodeType===Node.TEXT_NODE||t.nodeType===Node.ELEMENT_NODE}function B(t){var e=t.css("position"),n="absolute"===e||"relative"===e?t.css("zIndex"):"auto";return"auto"!==n}function C(t){return"static"!==t.css("position")}function O(t){return"none"!==t.css("float")}function S(t){var e=this;return function(){return!t.apply(e,arguments)}}function L(t){return t.node.nodeType===Node.ELEMENT_NODE}function N(t){return t.node.nodeType===Node.TEXT_NODE}function M(t,e){return t.cssInt("zIndex")-e.cssInt("zIndex")}function A(t){return 1>t.css("opacity")}function P(t,e){return function(){return t.apply(e,arguments)}}function _(t){return parseInt(t,10)}function D(t){return t.node.nodeType!==Node.ELEMENT_NODE||-1===["SCRIPT","HEAD","TITLE","OBJECT","BR"].indexOf(t.node.nodeName)}function F(t){return[].concat.apply([],t)}function W(t,e,n){this.width=t,this.height=e,this.images=n}function j(t,n){W.apply(this,arguments),this.canvas=e.createElement("canvas"),this.canvas.width=t,this.canvas.height=n,this.ctx=this.canvas.getContext("2d"),this.ctx.textBaseline="bottom",u("Initialized CanvasRenderer")}function z(t,e,n,i){h.call(this,n,i),this.ownStacking=t,this.contexts=[],this.children=[],this.opacity=(this.parent?this.parent.stack.opacity:1)*e}function H(){this.rangeBounds=this.testRangeBounds(),this.cors=this.testCORS()}function V(t,e){h.call(this,t,e)}function Y(t,e,n){return t.length>0?e+n.toUpperCase():undefined}t.html2canvas=function(r,a){a=a||{},a.logging&&(t.html2canvas.logging=!0,t.html2canvas.start=Date.now()),o(e,t.innerWidth,t.innerHeight).then(function(e){u("Document cloned");var o=e.contentWindow,r=o.document.documentElement,c=new H,h=new s(a,c),p=d.prototype.getBounds(r),l="view"===a.type?Math.min(p.width,t.innerWidth):n(),f="view"===a.type?Math.min(p.height,t.innerHeight):i(),g=new j(l,f,h),m=new d(r,g,c,h,a);t.console.log(m)})},s.prototype.findImages=function(t){var e=[];return t.filter(a).map(c).forEach(this.addImage(e,this.loadImage),this),e},s.prototype.findBackgroundImage=function(t,e){return e.parseBackgroundImages().filter(this.isImageBackground).map(this.getBackgroundUrl).forEach(this.addImage(t,this.loadImage),this),t},s.prototype.addImage=function(t,e){return function(n){this.imageExists(t,n)||(t.splice(0,0,e.apply(this,arguments)),u("Added image #"+t.length,n.substring(0,100)))}},s.prototype.getBackgroundUrl=function(t){return t.args[0]},s.prototype.isImageBackground=function(t){return"url"===t.method},s.prototype.loadImage=function(t){return t.match(/data:image\/.*;base64,/i)?new r(t.replace(/url\(['"]{0,}|['"]{0,}\)$/gi,""),!1):this.isSameOrigin(t)||this.options.allowTaint===!0?new r(t,!1):this.support.cors&&!this.options.allowTaint&&this.options.useCORS?new r(t,!0):this.options.proxy?new ProxyImageContainer(t):new DummyImageContainer(t)},s.prototype.imageExists=function(t,e){return t.some(function(t){return t.src===e})},s.prototype.isSameOrigin=function(t){var n=this.link||(this.link=e.createElement("a"));n.href=t,n.href=n.href;var i=n.protocol+n.host;return i===this.origin},s.prototype.getPromise=function(t){return t.promise},s.prototype.get=function(t){var e=null;return this.images.some(function(n){return(e=n).src===t})?e:null},s.prototype.fetch=function(t){return this.images=t.reduce(P(this.findBackgroundImage,this),this.findImages(t)),this.images.forEach(function(t,e){t.promise.then(function(){u("Succesfully loaded image #"+(e+1))},function(){u("Failed loading image #"+(e+1))})}),this.ready=Promise.all(this.images.map(this.getPromise)),u("Finished searching images"),this},h.prototype.assignStack=function(t){this.stack=t,t.children.push(this)},h.prototype.isElementVisible=function(){return this.node.nodeType===Node.TEXT_NODE?this.parent.visible:"none"!==this.css("display")&&"hidden"!==this.css("visibility")&&!this.node.hasAttribute("data-html2canvas-ignore")},h.prototype.css=function(t){return this.computedStyles||(this.computedStyles=this.node.ownerDocument.defaultView.getComputedStyle(this.node,null)),this.styles[t]||(this.styles[t]=this.computedStyles[t])},h.prototype.cssInt=function(t){var e=parseInt(this.css(t),10);return isNaN(e)?0:e},h.prototype.cssFloat=function(t){var e=parseFloat(this.css(t));return isNaN(e)?0:e},h.prototype.fontWeight=function(){var t=this.css("fontWeight");switch(parseInt(t,10)){case 401:t="bold";break;case 400:t="normal"}return t},h.prototype.parseBackgroundImages=function(){var t,e,n,i,o,r,s,a=" \r\n ",c=[],u=0,h=0,p=function(){t&&('"'===e.substr(0,1)&&(e=e.substr(1,e.length-2)),e&&s.push(e),"-"===t.substr(0,1)&&(i=t.indexOf("-",1)+1)>0&&(n=t.substr(0,i),t=t.substr(i)),c.push({prefix:n,method:t.toLowerCase(),value:o,args:s,image:null})),s=[],t=n=e=o=""};return s=[],t=n=e=o="",this.css("backgroundImage").split("").forEach(function(n){if(!(0===u&&a.indexOf(n)>-1)){switch(n){case'"':r?r===n&&(r=null):r=n;break;case"(":if(r)break;if(0===u)return u=1,o+=n,undefined;h++;break;case")":if(r)break;if(1===u){if(0===h)return u=0,o+=n,p(),undefined;h--}break;case",":if(r)break;if(0===u)return p(),undefined;if(1===u&&0===h&&!t.match(/^url$/i))return s.push(e),e="",o+=n,undefined}o+=n,0===u?t+=n:e+=n}}),p(),this.backgroundImages||(this.backgroundImages=c)},h.prototype.cssList=function(t,e){var n=(this.css(t)||"").split(",");return n=n[e||0]||n[0]||"auto",n=n.trim().split(" "),1===n.length&&(n=[n[0],n[0]]),n},h.prototype.parseBackgroundSize=function(t,e,n){var i,o,r=this.cssList("backgroundSize",n);if(p(r[0]))i=t.width*parseFloat(r[0])/100;else{if(/contain|cover/.test(r[0])){var s=t.width/t.height,a=e.width/e.height;return a>s^"contain"===r[0]?{width:t.height*a,height:t.height}:{width:t.width,height:t.width/a}}i=parseInt(r[0],10)}return o="auto"===r[0]&&"auto"===r[1]?e.height:"auto"===r[1]?i/e.width*e.height:p(r[1])?t.height*parseFloat(r[1])/100:parseInt(r[1],10),"auto"===r[0]&&(i=o/e.height*e.width),{width:i,height:o}},h.prototype.parseBackgroundPosition=function(t,e,n,i){var o,r,s=this.cssList("backgroundPosition",n);return o=p(s[0])?(t.width-(i||e).width)*(parseFloat(s[0])/100):parseInt(s[0],10),r="auto"===s[1]?o/e.width*e.height:p(s[1])?(t.height-(i||e).height)*parseFloat(s[1])/100:parseInt(s[1],10),"auto"===s[0]&&(o=r/e.height*e.width),{left:o,top:r}},h.prototype.parseBackgroundRepeat=function(t){return this.cssList("backgroundRepeat",t)[0]},d.prototype.getChildren=function(t){return F([].filter.call(t.node.childNodes,T).map(function(e){var n=[e.nodeType===Node.TEXT_NODE?new V(e,t):new h(e,t)].filter(D);return e.nodeType===Node.ELEMENT_NODE&&n.length?n[0].isElementVisible()?n.concat(this.getChildren(n[0])):[]:n},this))},d.prototype.newStackingContext=function(t,e){var n=new z(e,t.cssFloat("opacity"),t.node,t.parent);n.visible=t.visible;var i=n.getParentStack(this);i.contexts.push(n),t.stack=n},d.prototype.createStackingContexts=function(){this.nodes.forEach(function(t){L(t)&&(this.isRootElement(t)||A(t)||B(t)||this.isBodyWithTransparentRoot(t))?this.newStackingContext(t,!0):L(t)&&C(t)?this.newStackingContext(t,!1):t.assignStack(t.parent.stack)},this)},d.prototype.isBodyWithTransparentRoot=function(t){return"BODY"===t.node.nodeName&&this.renderer.isTransparent(t.parent.css("backgroundColor"))},d.prototype.isRootElement=function(t){return"HTML"===t.node.nodeName},d.prototype.sortStackingContexts=function(t){t.contexts.sort(M),t.contexts.forEach(this.sortStackingContexts,this)},d.prototype.parseBounds=function(t){return t.bounds=this.getBounds(t.node)},d.prototype.getBounds=function(t){if(t.getBoundingClientRect){var e=t.getBoundingClientRect(),n="BODY"===t.nodeName;return{top:e.top,bottom:e.bottom||e.top+e.height,left:e.left,width:n?t.scrollWidth:t.offsetWidth,height:n?t.scrollHeight:t.offsetHeight}}return{}},d.prototype.parseTextBounds=function(t){return function(e,n,i){if("none"!==t.parent.css("textDecoration")||0!==e.trim().length){var o=i.slice(0,n).join("").length;if(this.support.rangeBounds)return this.getRangeBounds(t.node,o,e.length);if(t.node&&"string"==typeof t.node.data){var r=t.node.splitText(e.length),s=this.getWrapperBounds(t.node);return t.node=r,s}}}},d.prototype.getWrapperBounds=function(t){var e=t.ownerDocument.createElement("wrapper"),n=t.parentNode,i=t.cloneNode(!0);e.appendChild(t.cloneNode(!0)),n.replaceChild(e,t);var o=this.getBounds(e);return n.replaceChild(i,e),o},d.prototype.getRangeBounds=function(t,e,n){var i=this.range||(this.range=t.ownerDocument.createRange());return i.setStart(t,e),i.setEnd(t,e+n),i.getBoundingClientRect()},d.prototype.parse=function(t){var e=t.contexts.filter(w),n=t.children.filter(L),i=n.filter(S(O)),o=i.filter(S(C)).filter(S(x)),r=n.filter(S(C)).filter(O),s=i.filter(S(C)).filter(x),a=t.contexts.concat(i.filter(C)).filter(b),c=t.children.filter(N).filter(I),h=t.contexts.filter(v),p=[];e.concat(o).concat(r).concat(s).concat(a).concat(c).concat(h).forEach(function(t){if(this.paint(t),-1!==p.indexOf(t.node))throw u(t,t.node),Error("rendering twice");p.push(t.node),k(t)&&this.parse(t)},this)},d.prototype.paint=function(t){try{N(t)?this.paintText(t):this.paintNode(t)}catch(e){u(e)}},d.prototype.paintNode=function(t){k(t)&&this.renderer.setOpacity(t.opacity);var e=this.parseBounds(t),n=this.parseBorders(t);switch(this.renderer.clip(n.clip,function(){this.renderer.renderBackground(t,e)},this),this.renderer.renderBorders(n.borders),t.node.nodeName){case"IMG":var i=this.images.get(t.node.src);i?this.renderer.renderImage(t,e,n,i.image):u("Error loading
",t.node.src)}},d.prototype.paintText=function(t){t.applyTextTransform();var e=t.node.data.split(!this.options.letterRendering||E(t)?/(\b| )/:""),n=t.parent.fontWeight(),i=t.parent.css("fontSize"),o=t.parent.css("fontFamily");this.renderer.font(t.parent.css("color"),t.parent.css("fontStyle"),t.parent.css("fontVariant"),n,i,o),e.map(this.parseTextBounds(t),this).forEach(function(t,n){t&&this.renderer.text(e[n],t.left,t.bottom)},this)},d.prototype.parseBorders=function(t){var e=t.bounds,n=R(t),i=["Top","Right","Bottom","Left"].map(function(e){return{width:t.cssInt("border"+e+"Width"),color:t.css("border"+e+"Color"),args:null}}),o=f(e,n,i);return{clip:this.parseBackgroundClip(t,o,i,n,e),borders:i.map(function(t,r){if(t.width>0){var s=e.left,a=e.top,c=e.width,u=e.height-i[2].width;switch(r){case 0:u=i[0].width,t.args=m({c1:[s,a],c2:[s+c,a],c3:[s+c-i[1].width,a+u],c4:[s+i[3].width,a+u]},n[0],n[1],o.topLeftOuter,o.topLeftInner,o.topRightOuter,o.topRightInner);break;case 1:s=e.left+e.width-i[1].width,c=i[1].width,t.args=m({c1:[s+c,a],c2:[s+c,a+u+i[2].width],c3:[s,a+u],c4:[s,a+i[0].width]},n[1],n[2],o.topRightOuter,o.topRightInner,o.bottomRightOuter,o.bottomRightInner);break;case 2:a=a+e.height-i[2].width,u=i[2].width,t.args=m({c1:[s+c,a+u],c2:[s,a+u],c3:[s+i[3].width,a],c4:[s+c-i[3].width,a]},n[2],n[3],o.bottomRightOuter,o.bottomRightInner,o.bottomLeftOuter,o.bottomLeftInner);break;case 3:c=i[3].width,t.args=m({c1:[s,a+u+i[2].width],c2:[s,a],c3:[s+c,a+i[0].width],c4:[s+c,a+u]},n[3],n[0],o.bottomLeftOuter,o.bottomLeftInner,o.topLeftOuter,o.topLeftInner)}}return t})}},d.prototype.parseBackgroundClip=function(t,e,n,i,o){var r=t.css("backgroundClip"),s=[];switch(r){case"content-box":case"padding-box":y(s,i[0],i[1],e.topLeftInner,e.topRightInner,o.left+n[3].width,o.top+n[0].width),y(s,i[1],i[2],e.topRightInner,e.bottomRightInner,o.left+o.width-n[1].width,o.top+n[0].width),y(s,i[2],i[3],e.bottomRightInner,e.bottomLeftInner,o.left+o.width-n[1].width,o.top+o.height-n[2].width),y(s,i[3],i[0],e.bottomLeftInner,e.topLeftInner,o.left+n[3].width,o.top+o.height-n[2].width);break;default:y(s,i[0],i[1],e.topLeftOuter,e.topRightOuter,o.left,o.top),y(s,i[1],i[2],e.topRightOuter,e.bottomRightOuter,o.left+o.width,o.top),y(s,i[2],i[3],e.bottomRightOuter,e.bottomLeftOuter,o.left+o.width,o.top+o.height),y(s,i[3],i[0],e.bottomLeftOuter,e.topLeftOuter,o.left,o.top+o.height)}return s},!function(){var n,i,o,r;!function(){var t={},e={};n=function(e,n,i){t[e]={deps:n,callback:i}},r=o=i=function(n){function o(t){if("."!==t.charAt(0))return t;for(var e=t.split("/"),i=n.split("/").slice(0,-1),o=0,r=e.length;r>o;o++){var s=e[o];if(".."===s)i.pop();else{if("."===s)continue;i.push(s)}}return i.join("/")}if(r._eak_seen=t,e[n])return e[n];if(e[n]={},!t[n])throw Error("Could not find module "+n);for(var s,a=t[n],c=a.deps,u=a.callback,h=[],p=0,d=c.length;d>p;p++)"exports"===c[p]?h.push(s={}):h.push(i(o(c[p])));var l=u.apply(this,h);return e[n]=s||l}}(),n("promise/all",["./utils","exports"],function(t,e){"use strict";function n(t){var e=this;if(!i(t))throw new TypeError("You must pass an array to all.");return new e(function(e,n){function i(t){return function(e){r(t,e)}}function r(t,n){a[t]=n,0===--c&&e(a)}var s,a=[],c=t.length;0===c&&e([]);for(var u=0;t.length>u;u++)s=t[u],s&&o(s.then)?s.then(i(u),n):r(u,s)})}var i=t.isArray,o=t.isFunction;e.all=n}),n("promise/asap",["exports"],function(n){"use strict";function i(){return function(){process.nextTick(s)}}function o(){var t=0,n=new h(s),i=e.createTextNode("");return n.observe(i,{characterData:!0}),function(){i.data=t=++t%2}}function r(){return function(){p.setTimeout(s,1)}}function s(){for(var t=0;d.length>t;t++){var e=d[t],n=e[0],i=e[1];n(i)}d=[]}function a(t,e){var n=d.push([t,e]);1===n&&c()}var c,u=t!==undefined?t:{},h=u.MutationObserver||u.WebKitMutationObserver,p="undefined"!=typeof global?global:this,d=[];c="undefined"!=typeof process&&"[object process]"==={}.toString.call(process)?i():h?o():r(),n.asap=a}),n("promise/cast",["exports"],function(t){"use strict";function e(t){if(t&&"object"==typeof t&&t.constructor===this)return t;var e=this;return new e(function(e){e(t)})}t.cast=e}),n("promise/config",["exports"],function(t){"use strict";function e(t,e){return 2!==arguments.length?n[t]:(n[t]=e,void 0)}var n={instrument:!1};t.config=n,t.configure=e}),n("promise/polyfill",["./promise","./utils","exports"],function(e,n,i){"use strict";function o(){var e="Promise"in t&&"cast"in t.Promise&&"resolve"in t.Promise&&"reject"in t.Promise&&"all"in t.Promise&&"race"in t.Promise&&function(){var e;return new t.Promise(function(t){e=t}),s(e)}();e||(t.Promise=r)}var r=e.Promise,s=n.isFunction;i.polyfill=o}),n("promise/promise",["./config","./utils","./cast","./all","./race","./resolve","./reject","./asap","exports"],function(t,e,n,i,o,r,s,a,c){"use strict";function u(t){if(!k(t))throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");if(!(this instanceof u))throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");this._subscribers=[],h(t,this)}function h(t,e){function n(t){g(e,t)}function i(t){y(e,t)}try{t(n,i)}catch(o){i(o)}}function p(t,e,n,i){var o,r,s,a,c=k(n);if(c)try{o=n(i),s=!0}catch(u){a=!0,r=u}else o=i,s=!0;f(e,o)||(c&&s?g(e,o):a?y(e,r):t===L?g(e,o):t===N&&y(e,o))}function d(t,e,n,i){var o=t._subscribers,r=o.length;o[r]=e,o[r+L]=n,o[r+N]=i}function l(t,e){for(var n,i,o=t._subscribers,r=t._detail,s=0;o.length>s;s+=3)n=o[s],i=o[s+e],p(e,n,i,r);t._subscribers=null}function f(t,e){var n,i=null;try{if(t===e)throw new TypeError("A promises callback cannot return that same promise.");if(x(e)&&(i=e.then,k(i)))return i.call(e,function(i){return n?!0:(n=!0,e!==i?g(t,i):m(t,i),void 0)},function(e){return n?!0:(n=!0,y(t,e),void 0)}),!0}catch(o){return n?!0:(y(t,o),!0)}return!1}function g(t,e){t===e?m(t,e):f(t,e)||m(t,e)}function m(t,e){t._state===O&&(t._state=S,t._detail=e,b.async(w,t))}function y(t,e){t._state===O&&(t._state=S,t._detail=e,b.async(v,t))}function w(t){l(t,t._state=L)}function v(t){l(t,t._state=N)}var b=t.config,x=(t.configure,e.objectOrFunction),k=e.isFunction,I=(e.now,n.cast),E=i.all,R=o.race,T=r.resolve,B=s.reject,C=a.asap;b.async=C;var O=void 0,S=0,L=1,N=2;u.prototype={constructor:u,_state:void 0,_detail:void 0,_subscribers:void 0,then:function(t,e){var n=this,i=new this.constructor(function(){});if(this._state){var o=arguments;b.async(function(){p(n._state,i,o[n._state-1],n._detail)})}else d(this,i,t,e);return i},"catch":function(t){return this.then(null,t)}},u.all=E,u.cast=I,u.race=R,u.resolve=T,u.reject=B,c.Promise=u}),n("promise/race",["./utils","exports"],function(t,e){"use strict";function n(t){var e=this;if(!i(t))throw new TypeError("You must pass an array to race.");return new e(function(e,n){for(var i,o=0;t.length>o;o++)i=t[o],i&&"function"==typeof i.then?i.then(e,n):e(i)})}var i=t.isArray;e.race=n}),n("promise/reject",["exports"],function(t){"use strict";function e(t){var e=this;return new e(function(e,n){n(t)})}t.reject=e}),n("promise/resolve",["exports"],function(t){"use strict";function e(t){var e=this;return new e(function(e){e(t)})}t.resolve=e}),n("promise/utils",["exports"],function(t){"use strict";function e(t){return n(t)||"object"==typeof t&&null!==t}function n(t){return"function"==typeof t}function i(t){return"[object Array]"===Object.prototype.toString.call(t)}var o=Date.now||function(){return(new Date).getTime()};t.objectOrFunction=e,t.isFunction=n,t.isArray=i,t.now=o}),i("promise/polyfill").polyfill()}(),W.prototype.renderImage=function(t,e,n,i){var o=t.cssInt("paddingLeft"),r=t.cssInt("paddingTop"),s=t.cssInt("paddingRight"),a=t.cssInt("paddingBottom"),c=n.borders;this.drawImage(i,0,0,i.width,i.height,e.left+o+c[3].width,e.top+r+c[0].width,e.width-(c[1].width+c[3].width+o+s),e.height-(c[0].width+c[2].width+r+a))},W.prototype.renderBackground=function(t,e){e.height>0&&e.width>0&&(this.renderBackgroundColor(t,e),this.renderBackgroundImage(t,e))},W.prototype.renderBackgroundColor=function(t,e){var n=t.css("backgroundColor");this.isTransparent(n)||this.rectangle(e.left,e.top,e.width,e.height,t.css("backgroundColor"))},W.prototype.renderBorders=function(t){t.forEach(this.renderBorder,this)},W.prototype.renderBorder=function(t){this.isTransparent(t.color)||null===t.args||this.drawShape(t.args,t.color)},W.prototype.renderBackgroundImage=function(t,e){var n=t.parseBackgroundImages();n.reverse().forEach(function(n,i,o){if("url"===n.method){var r=this.images.get(n.args[0]);r?this.renderBackgroundRepeating(t,e,r,o.length-(i+1)):u("Error loading background-image",n.args[0])}},this)},W.prototype.renderBackgroundRepeating=function(t,e,n,i){var o=t.parseBackgroundSize(e,n.image,i),r=t.parseBackgroundPosition(e,n.image,i,o),s=t.parseBackgroundRepeat(i);switch(s){case"repeat-x":case"repeat no-repeat":this.backgroundRepeatShape(n,r,o,e,e.left,e.top+r.top,99999,n.image.height);break;case"repeat-y":case"no-repeat repeat":this.backgroundRepeatShape(n,r,o,e,e.left+r.left,e.top,n.image.width,99999);break;case"no-repeat":this.backgroundRepeatShape(n,r,o,e,e.left+r.left,e.top+r.top,n.image.width,n.image.height);break;default:this.renderBackgroundRepeat(n,r,o,{top:e.top,left:e.left})}},W.prototype.isTransparent=function(t){return!t||"transparent"===t||"rgba(0, 0, 0, 0)"===t},j.prototype=Object.create(W.prototype),j.prototype.setFillStyle=function(t){return this.ctx.fillStyle=t,this.ctx},j.prototype.rectangle=function(t,e,n,i,o){this.setFillStyle(o).fillRect(t,e,n,i)},j.prototype.drawShape=function(t,e){this.shape(t),this.setFillStyle(e).fill()},j.prototype.drawImage=function(t,e,n,i,o,r,s,a,c){this.ctx.drawImage(t,e,n,i,o,r,s,a,c)},j.prototype.clip=function(t,e,n){this.ctx.save(),this.shape(t).clip(),e.call(n),this.ctx.restore()},j.prototype.shape=function(t){return this.ctx.beginPath(),t.forEach(function(t,e){this.ctx[0===e?"moveTo":t[0]+"To"].apply(this.ctx,t.slice(1))},this),this.ctx.closePath(),this.ctx},j.prototype.font=function(t,e,n,i,o,r){this.setFillStyle(t).font=[e,n,i,o,r].join(" ")},j.prototype.setOpacity=function(t){this.ctx.globalAlpha=t},j.prototype.text=function(t,e,n){this.ctx.fillText(t,e,n)},j.prototype.backgroundRepeatShape=function(t,e,n,i,o,r,s,a){var c=[["line",Math.round(o),Math.round(r)],["line",Math.round(o+s),Math.round(r)],["line",Math.round(o+s),Math.round(a+r)],["line",Math.round(o),Math.round(a+r)]];this.clip(c,function(){this.renderBackgroundRepeat(t,e,n,i)},this)},j.prototype.renderBackgroundRepeat=function(t,e,n,i){var o=Math.round(i.left+e.left),r=Math.round(i.top+e.top);this.setFillStyle(this.ctx.createPattern(this.resizeImage(t,n),"repeat")),this.ctx.translate(o,r),this.ctx.fill(),this.ctx.translate(-o,-r)},j.prototype.resizeImage=function(t,n){var i=t.image;if(i.width===n.width&&i.height===n.height)return i;var o,r=e.createElement("canvas");return r.width=n.width,r.height=n.height,o=r.getContext("2d"),o.drawImage(i,0,0,i.width,i.height,0,0,n.width,n.height),r},z.prototype=Object.create(h.prototype),z.prototype.getParentStack=function(t){var e=this.parent?this.parent.stack:null;return e?e.ownStacking?e:e.getParentStack(t):t.stack},H.prototype.testRangeBounds=function(){var t,n,i,o,r=!1;return e.createRange&&(t=e.createRange(),t.getBoundingClientRect&&(n=e.createElement("boundtest"),n.style.height="123px",n.style.display="block",e.body.appendChild(n),t.selectNode(n),i=t.getBoundingClientRect(),o=i.height,123===o&&(r=!0),e.body.removeChild(n))),r},H.prototype.testCORS=function(){return(new Image).crossOrigin!==undefined},V.prototype=Object.create(h.prototype),V.prototype.applyTextTransform=function(){this.node.data=this.transform(this.parent.css("textTransform"))},V.prototype.transform=function(t){var e=this.node.data;switch(t){case"lowercase":return e.toLowerCase();case"capitalize":return e.replace(/(^|\s|:|-|\(|\))([a-z])/g,Y);case"uppercase":return e.toUpperCase();default:return e}}})(window,document);
\ No newline at end of file
diff --git a/src/core.js b/src/core.js
index 4ab7bc1..b444a56 100644
--- a/src/core.js
+++ b/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
", 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);
- };
-}
diff --git a/src/nodeparser.js b/src/nodeparser.js
new file mode 100644
index 0000000..a363bd4
--- /dev/null
+++ b/src/nodeparser.js
@@ -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
", 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);
+}
diff --git a/tests/test.js b/tests/test.js
index 35943dc..34fd3ca 100644
--- a/tests/test.js
+++ b/tests/test.js
@@ -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);
}