mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
417 lines
13 KiB
JavaScript
417 lines
13 KiB
JavaScript
"use strict";
|
|
|
|
var _html2canvas = {},
|
|
previousElement,
|
|
computedCSS,
|
|
html2canvas;
|
|
|
|
_html2canvas.Util = {};
|
|
|
|
_html2canvas.Util.log = function(a) {
|
|
if (_html2canvas.logging && window.console && window.console.log) {
|
|
window.console.log(a);
|
|
}
|
|
};
|
|
|
|
_html2canvas.Util.trimText = (function(isNative){
|
|
return function(input) {
|
|
return isNative ? isNative.apply(input) : ((input || '') + '').replace( /^\s+|\s+$/g , '' );
|
|
};
|
|
})(String.prototype.trim);
|
|
|
|
_html2canvas.Util.asFloat = function(v) {
|
|
return parseFloat(v);
|
|
};
|
|
|
|
(function() {
|
|
// TODO: support all possible length values
|
|
var TEXT_SHADOW_PROPERTY = /((rgba|rgb)\([^\)]+\)(\s-?\d+px){0,})/g;
|
|
var TEXT_SHADOW_VALUES = /(-?\d+px)|(#.+)|(rgb\(.+\))|(rgba\(.+\))/g;
|
|
_html2canvas.Util.parseTextShadows = function (value) {
|
|
if (!value || value === 'none') {
|
|
return [];
|
|
}
|
|
|
|
// find multiple shadow declarations
|
|
var shadows = value.match(TEXT_SHADOW_PROPERTY),
|
|
results = [];
|
|
for (var i = 0; shadows && (i < shadows.length); i++) {
|
|
var s = shadows[i].match(TEXT_SHADOW_VALUES);
|
|
results.push({
|
|
color: s[0],
|
|
offsetX: s[1] ? s[1].replace('px', '') : 0,
|
|
offsetY: s[2] ? s[2].replace('px', '') : 0,
|
|
blur: s[3] ? s[3].replace('px', '') : 0
|
|
});
|
|
}
|
|
return results;
|
|
};
|
|
})();
|
|
|
|
|
|
_html2canvas.Util.parseBackgroundImage = function (value) {
|
|
var whitespace = ' \r\n\t',
|
|
method, definition, prefix, prefix_i, block, results = [],
|
|
c, 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
|
|
});
|
|
}
|
|
args = []; //for some odd reason, setting .length = 0 didn't work in safari
|
|
method =
|
|
prefix =
|
|
definition =
|
|
block = '';
|
|
};
|
|
|
|
appendResult();
|
|
for(var i = 0, ii = value.length; i<ii; i++) {
|
|
c = value[i];
|
|
if(mode === 0 && whitespace.indexOf( c ) > -1){
|
|
continue;
|
|
}
|
|
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;
|
|
continue;
|
|
} else {
|
|
numParen++;
|
|
}
|
|
break;
|
|
|
|
case ')':
|
|
if(quote) { break; }
|
|
else if(mode === 1) {
|
|
if(numParen === 0) {
|
|
mode = 0;
|
|
block += c;
|
|
appendResult();
|
|
continue;
|
|
} else {
|
|
numParen--;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ',':
|
|
if(quote) { break; }
|
|
else if(mode === 0) {
|
|
appendResult();
|
|
continue;
|
|
}
|
|
else if (mode === 1) {
|
|
if(numParen === 0 && !method.match(/^url$/i)) {
|
|
args.push(definition);
|
|
definition = '';
|
|
block += c;
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
block += c;
|
|
if(mode === 0) { method += c; }
|
|
else { definition += c; }
|
|
}
|
|
appendResult();
|
|
|
|
return results;
|
|
};
|
|
|
|
_html2canvas.Util.Bounds = function (element) {
|
|
var clientRect, bounds = {};
|
|
|
|
if (element.getBoundingClientRect){
|
|
clientRect = element.getBoundingClientRect();
|
|
|
|
// TODO add scroll position to bounds, so no scrolling of window necessary
|
|
bounds.top = clientRect.top;
|
|
bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height);
|
|
bounds.left = clientRect.left;
|
|
|
|
bounds.width = element.offsetWidth;
|
|
bounds.height = element.offsetHeight;
|
|
}
|
|
|
|
return bounds;
|
|
};
|
|
|
|
// TODO ideally, we'd want everything to go through this function instead of Util.Bounds,
|
|
// but would require further work to calculate the correct positions for elements with offsetParents
|
|
_html2canvas.Util.OffsetBounds = function (element) {
|
|
var parent = element.offsetParent ? _html2canvas.Util.OffsetBounds(element.offsetParent) : {top: 0, left: 0};
|
|
|
|
return {
|
|
top: element.offsetTop + parent.top,
|
|
bottom: element.offsetTop + element.offsetHeight + parent.top,
|
|
left: element.offsetLeft + parent.left,
|
|
width: element.offsetWidth,
|
|
height: element.offsetHeight
|
|
};
|
|
};
|
|
|
|
function toPX(element, attribute, value ) {
|
|
var rsLeft = element.runtimeStyle && element.runtimeStyle[attribute],
|
|
left,
|
|
style = element.style;
|
|
|
|
// Check if we are not dealing with pixels, (Opera has issues with this)
|
|
// Ported from jQuery css.js
|
|
// From the awesome hack by Dean Edwards
|
|
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
|
|
|
|
// If we're not dealing with a regular pixel number
|
|
// but a number that has a weird ending, we need to convert it to pixels
|
|
|
|
if ( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( value ) && /^-?\d/.test(value) ) {
|
|
// Remember the original values
|
|
left = style.left;
|
|
|
|
// Put in the new values to get a computed value out
|
|
if (rsLeft) {
|
|
element.runtimeStyle.left = element.currentStyle.left;
|
|
}
|
|
style.left = attribute === "fontSize" ? "1em" : (value || 0);
|
|
value = style.pixelLeft + "px";
|
|
|
|
// Revert the changed values
|
|
style.left = left;
|
|
if (rsLeft) {
|
|
element.runtimeStyle.left = rsLeft;
|
|
}
|
|
}
|
|
|
|
if (!/^(thin|medium|thick)$/i.test(value)) {
|
|
return Math.round(parseFloat(value)) + "px";
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
function asInt(val) {
|
|
return parseInt(val, 10);
|
|
}
|
|
|
|
function parseBackgroundSizePosition(value, element, attribute, index) {
|
|
value = (value || '').split(',');
|
|
value = value[index || 0] || value[0] || 'auto';
|
|
value = _html2canvas.Util.trimText(value).split(' ');
|
|
if(attribute === 'backgroundSize' && (value[0] && value[0].match(/^(cover|contain|auto)$/))) {
|
|
return value;
|
|
} else {
|
|
value[0] = (value[0].indexOf( "%" ) === -1) ? toPX(element, attribute + "X", value[0]) : value[0];
|
|
if(value[1] === undefined) {
|
|
if(attribute === 'backgroundSize') {
|
|
value[1] = 'auto';
|
|
return value;
|
|
} else {
|
|
// IE 9 doesn't return double digit always
|
|
value[1] = value[0];
|
|
}
|
|
}
|
|
value[1] = (value[1].indexOf("%") === -1) ? toPX(element, attribute + "Y", value[1]) : value[1];
|
|
}
|
|
return value;
|
|
}
|
|
|
|
_html2canvas.Util.getCSS = function (element, attribute, index) {
|
|
if (previousElement !== element) {
|
|
computedCSS = document.defaultView.getComputedStyle(element, null);
|
|
}
|
|
|
|
var value = computedCSS[attribute];
|
|
if(attribute==="backgroundRepeat" && value.indexOf(" ")!==-1){
|
|
value = (
|
|
"no-repeat repeat"===value ? "repeat-y" : (
|
|
"repeat no-repeat"===value ? "repeat-x" : value
|
|
)
|
|
);
|
|
}
|
|
|
|
if (/^background(Size|Position)$/.test(attribute)) {
|
|
return parseBackgroundSizePosition(value, element, attribute, index);
|
|
} else if (/border(Top|Bottom)(Left|Right)Radius/.test(attribute)) {
|
|
var arr = value.split(" ");
|
|
if (arr.length <= 1) {
|
|
arr[1] = arr[0];
|
|
}
|
|
return arr.map(asInt);
|
|
}
|
|
|
|
return value;
|
|
};
|
|
|
|
_html2canvas.Util.resizeBounds = function( current_width, current_height, target_width, target_height, stretch_mode ){
|
|
var target_ratio = target_width / target_height,
|
|
current_ratio = current_width / current_height,
|
|
output_width, output_height;
|
|
|
|
if(!stretch_mode || stretch_mode === 'auto') {
|
|
output_width = target_width;
|
|
output_height = target_height;
|
|
} else if(target_ratio < current_ratio ^ stretch_mode === 'contain') {
|
|
output_height = target_height;
|
|
output_width = target_height * current_ratio;
|
|
} else {
|
|
output_width = target_width;
|
|
output_height = target_width / current_ratio;
|
|
}
|
|
|
|
return {
|
|
width: output_width,
|
|
height: output_height
|
|
};
|
|
};
|
|
|
|
_html2canvas.Util.BackgroundPosition = function(element, bounds, image, imageIndex, backgroundSize ) {
|
|
var backgroundPosition = _html2canvas.Util.getCSS(element, 'backgroundPosition', imageIndex),
|
|
leftPosition,
|
|
topPosition;
|
|
if (backgroundPosition.length === 1){
|
|
backgroundPosition = [backgroundPosition[0], backgroundPosition[0]];
|
|
}
|
|
|
|
if (backgroundPosition[0].toString().indexOf("%") !== -1){
|
|
leftPosition = (bounds.width - (backgroundSize || image).width) * (parseFloat(backgroundPosition[0]) / 100);
|
|
} else {
|
|
leftPosition = parseInt(backgroundPosition[0], 10);
|
|
}
|
|
|
|
if (backgroundPosition[1] === 'auto') {
|
|
topPosition = leftPosition / image.width * image.height;
|
|
} else if (backgroundPosition[1].toString().indexOf("%") !== -1){
|
|
topPosition = (bounds.height - (backgroundSize || image).height) * parseFloat(backgroundPosition[1]) / 100;
|
|
} else {
|
|
topPosition = parseInt(backgroundPosition[1], 10);
|
|
}
|
|
|
|
if (backgroundPosition[0] === 'auto') {
|
|
leftPosition = topPosition / image.height * image.width;
|
|
}
|
|
|
|
return {left: leftPosition, top: topPosition};
|
|
};
|
|
|
|
_html2canvas.Util.BackgroundSize = function(element, bounds, image, imageIndex) {
|
|
var backgroundSize = _html2canvas.Util.getCSS(element, 'backgroundSize', imageIndex),
|
|
width,
|
|
height;
|
|
|
|
if (backgroundSize.length === 1){
|
|
backgroundSize = [backgroundSize[0], backgroundSize[0]];
|
|
}
|
|
|
|
if (backgroundSize[0].toString().indexOf("%") !== -1){
|
|
width = bounds.width * parseFloat(backgroundSize[0]) / 100;
|
|
} else if(backgroundSize[0] === 'auto') {
|
|
width = image.width;
|
|
} else {
|
|
if (/contain|cover/.test(backgroundSize[0])) {
|
|
var resized = _html2canvas.Util.resizeBounds(image.width, image.height, bounds.width, bounds.height, backgroundSize[0]);
|
|
return {width: resized.width, height: resized.height};
|
|
} else {
|
|
width = parseInt(backgroundSize[0], 10);
|
|
}
|
|
}
|
|
|
|
if(backgroundSize[1] === 'auto') {
|
|
height = width / image.width * image.height;
|
|
} else if (backgroundSize[1].toString().indexOf("%") !== -1){
|
|
height = bounds.height * parseFloat(backgroundSize[1]) / 100;
|
|
} else {
|
|
height = parseInt(backgroundSize[1],10);
|
|
}
|
|
|
|
|
|
if (backgroundSize[0] === 'auto') {
|
|
width = height / image.height * image.width;
|
|
}
|
|
|
|
return {width: width, height: height};
|
|
};
|
|
|
|
_html2canvas.Util.Extend = function (options, defaults) {
|
|
for (var key in options) {
|
|
if (options.hasOwnProperty(key)) {
|
|
defaults[key] = options[key];
|
|
}
|
|
}
|
|
return defaults;
|
|
};
|
|
|
|
|
|
/*
|
|
* Derived from jQuery.contents()
|
|
* Copyright 2010, John Resig
|
|
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
* http://jquery.org/license
|
|
*/
|
|
_html2canvas.Util.Children = function( elem ) {
|
|
var children;
|
|
try {
|
|
children = (elem.nodeName && elem.nodeName.toUpperCase() === "IFRAME") ? elem.contentDocument || elem.contentWindow.document : (function(array) {
|
|
var ret = [];
|
|
if (array !== null) {
|
|
(function(first, second ) {
|
|
var i = first.length,
|
|
j = 0;
|
|
|
|
if (typeof second.length === "number") {
|
|
for (var l = second.length; j < l; j++) {
|
|
first[i++] = second[j];
|
|
}
|
|
} else {
|
|
while (second[j] !== undefined) {
|
|
first[i++] = second[j++];
|
|
}
|
|
}
|
|
|
|
first.length = i;
|
|
|
|
return first;
|
|
})(ret, array);
|
|
}
|
|
return ret;
|
|
})(elem.childNodes);
|
|
|
|
} catch (ex) {
|
|
_html2canvas.Util.log("html2canvas.Util.Children failed with exception: " + ex.message);
|
|
children = [];
|
|
}
|
|
return children;
|
|
};
|
|
|
|
_html2canvas.Util.isTransparent = function(backgroundColor) {
|
|
return (!backgroundColor || backgroundColor === "transparent" || backgroundColor === "rgba(0, 0, 0, 0)");
|
|
};
|