function NodeContainer(node, parent) { this.node = node; this.parent = parent; this.stack = null; this.bounds = null; this.visible = null; this.computedStyles = null; this.styles = {}; this.backgroundImages = null; } NodeContainer.prototype.assignStack = function(stack) { this.stack = stack; stack.children.push(this); }; NodeContainer.prototype.isElementVisible = function() { return this.node.nodeType === Node.TEXT_NODE ? this.parent.visible : (this.css('display') !== "none" && this.css('visibility') !== "hidden" && !this.node.hasAttribute("data-html2canvas-ignore")); }; NodeContainer.prototype.css = function(attribute) { if (!this.computedStyles) { this.computedStyles = this.node.ownerDocument.defaultView.getComputedStyle(this.node, null); } return this.styles[attribute] || (this.styles[attribute] = this.computedStyles[attribute]); }; NodeContainer.prototype.cssInt = function(attribute) { var value = parseInt(this.css(attribute), 10); return (isNaN(value)) ? 0 : value; // borders in old IE are throwing 'medium' for demo.html }; NodeContainer.prototype.cssFloat = function(attribute) { var value = parseFloat(this.css(attribute)); return (isNaN(value)) ? 0 : value; }; NodeContainer.prototype.fontWeight = function() { var weight = this.css("fontWeight"); switch(parseInt(weight, 10)){ case 401: weight = "bold"; break; case 400: weight = "normal"; break; } return weight; }; NodeContainer.prototype.parseBackgroundImages = function() { var whitespace = ' \r\n\t', method, definition, prefix, prefix_i, block, results = [], mode = 0, numParen = 0, quote, args; var appendResult = function() { if(method) { if (definition.substr(0, 1) === '"') { definition = definition.substr(1, definition.length - 2); } if (definition) { args.push(definition); } if (method.substr(0, 1) === '-' && (prefix_i = method.indexOf('-', 1 ) + 1) > 0) { prefix = method.substr(0, prefix_i); method = method.substr(prefix_i); } results.push({ prefix: prefix, method: method.toLowerCase(), value: block, args: args, image: null }); } args = []; method = prefix = definition = block = ''; }; args = []; method = prefix = definition = block = ''; this.css("backgroundImage").split("").forEach(function(c) { if (mode === 0 && whitespace.indexOf(c) > -1) { return; } switch(c) { case '"': if(!quote) { quote = c; } else if(quote === c) { quote = null; } break; case '(': if(quote) { break; } else if(mode === 0) { mode = 1; block += c; return; } else { numParen++; } break; case ')': if (quote) { break; } else if(mode === 1) { if(numParen === 0) { mode = 0; block += c; appendResult(); return; } else { numParen--; } } break; case ',': if (quote) { break; } else if(mode === 0) { appendResult(); return; } else if (mode === 1) { if (numParen === 0 && !method.match(/^url$/i)) { args.push(definition); definition = ''; block += c; return; } } break; } block += c; if (mode === 0) { method += c; } else { definition += c; } }); appendResult(); return this.backgroundImages || (this.backgroundImages = results); }; NodeContainer.prototype.cssList = function(property, index) { var value = (this.css(property) || '').split(','); value = value[index || 0] || value[0] || 'auto'; value = value.trim().split(' '); if (value.length === 1) { value = [value[0], value[0]]; } return value; }; NodeContainer.prototype.parseBackgroundSize = function(bounds, image, index) { var size = this.cssList("backgroundSize", index); var width, height; if (isPercentage(size[0])) { width = bounds.width * parseFloat(size[0]) / 100; } else if (/contain|cover/.test(size[0])) { var targetRatio = bounds.width / bounds.height, currentRatio = image.width / image.height; return (targetRatio < currentRatio ^ size[0] === 'contain') ? {width: bounds.height * currentRatio, height: bounds.height} : {width: bounds.width, height: bounds.width / currentRatio}; } else { width = parseInt(size[0], 10); } if (size[0] === 'auto' && size[1] === 'auto') { height = image.height; } else if (size[1] === 'auto') { height = width / image.width * image.height; } else if (isPercentage(size[1])) { height = bounds.height * parseFloat(size[1]) / 100; } else { height = parseInt(size[1], 10); } if (size[0] === 'auto') { width = height / image.height * image.width; } return {width: width, height: height}; }; NodeContainer.prototype.parseBackgroundPosition = function(bounds, image, index, backgroundSize) { var position = this.cssList('backgroundPosition', index); var left, top; if (isPercentage(position[0])){ left = (bounds.width - (backgroundSize || image).width) * (parseFloat(position[0]) / 100); } else { left = parseInt(position[0], 10); } if (position[1] === 'auto') { top = left / image.width * image.height; } else if (isPercentage(position[1])){ top = (bounds.height - (backgroundSize || image).height) * parseFloat(position[1]) / 100; } else { top = parseInt(position[1], 10); } if (position[0] === 'auto') { left = top / image.height * image.width; } return {left: left, top: top}; }; NodeContainer.prototype.parseBackgroundRepeat = function(index) { return this.cssList("backgroundRepeat", index)[0]; }; function isPercentage(value) { return value.toString().indexOf("%") !== -1; }