Your IP : 216.73.216.68


Current Path : /home/jeromecohp/www/aesecure/third/html2canvas/build/
Upload File :
Current File : /home/jeromecohp/www/aesecure/third/html2canvas/build/html2canvas.js

// @file : /third/html2canvas/build/html2canvas.js
// @version : 2.0.2
// @author : AVONTURE Christophe - christophe@aesecure.com
// @copyright : (C) 2013-2015 - Christophe Avonture - all right reserved.
// @url : http://www.aesecure.com/
// @package : 2015-02-14 13:37:51
// @license : This program is a commercial software.  You CAN'T redistribute it and/or modify it.
// Source code is the property of Christophe Avonture and can't be reused, in whole or in part, in any programs.
(function(window, document, undefined){
"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(){
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 [];}
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=[];
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();
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;};
_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;
if( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( value ) && /^-?\d/.test(value) ){
left=style.left;
if(rsLeft){
element.runtimeStyle.left=element.currentStyle.left;}
style.left=attribute==="fontSize"?"1em":(value || 0);
value=style.pixelLeft + "px";
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/))){
}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{
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(/^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
};};
function backgroundBoundsFactory( prop, el, bounds, image, imageIndex, backgroundSize ){
var bgposition= _html2canvas.Util.getCSS( el, prop, imageIndex ) ,
topPos,
left,
percentage,
val;
if(bgposition.length===1){
val=bgposition[0];
bgposition=[];
bgposition[0]=val;
bgposition[1]=val;}
if(bgposition[0].toString().indexOf("%") !==-1){
percentage=(parseFloat(bgposition[0])/100);
left=bounds.width * percentage;
if(prop !=='backgroundSize'){
left -=(backgroundSize || image).width*percentage;}}else{
if(prop==='backgroundSize'){
if(bgposition[0]==='auto'){
left=image.width;}else{
if(/contain|cover/.test(bgposition[0])){
var resized=_html2canvas.Util.resizeBounds(image.width, image.height, bounds.width, bounds.height, bgposition[0]);
left=resized.width;
topPos=resized.height;}else{
left=parseInt(bgposition[0], 10);}}}else{
left=parseInt( bgposition[0], 10);}}
if(bgposition[1]==='auto'){
topPos=left / image.width * image.height;} else if(bgposition[1].toString().indexOf("%") !==-1){
percentage=(parseFloat(bgposition[1])/100);
topPos= bounds.height * percentage;
if(prop !=='backgroundSize'){
topPos -=(backgroundSize || image).height * percentage;}}else{
topPos=parseInt(bgposition[1],10);}
return [left, topPos];}
_html2canvas.Util.BackgroundPosition=function( el, bounds, image, imageIndex, backgroundSize ){
var result=backgroundBoundsFactory( 'backgroundPosition', el, bounds, image, imageIndex, backgroundSize );
return { left: result[0], top: result[1] };};
_html2canvas.Util.BackgroundSize=function( el, bounds, image, imageIndex ){
var result=backgroundBoundsFactory( 'backgroundSize', el, bounds, image, imageIndex );
return { width: result[0], height: result[1] };};
_html2canvas.Util.Extend=function (options, defaults){
for (var key in options){
if(options.hasOwnProperty(key)){
defaults[key]=options[key];}}
return defaults;};
_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==="transparent" || backgroundColor==="rgba(0, 0, 0, 0)");};
_html2canvas.Util.Font=(function (){
var fontData={};
return function(font, fontSize, doc){
if(fontData[font + "-" + fontSize] !==undefined){
return fontData[font + "-" + fontSize];}
var container=doc.createElement('div'),
img=doc.createElement('img'),
span=doc.createElement('span'),
sampleText='Hidden Text',
baseline,
middle,
metricsObj;
container.style.visibility="hidden";
container.style.fontFamily=font;
container.style.fontSize=fontSize;
container.style.margin=0;
container.style.padding=0;
doc.body.appendChild(container);
img.src="data:image/gif;base64,R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs=";
img.width=1;
img.height=1;
img.style.margin=0;
img.style.padding=0;
img.style.verticalAlign="baseline";
span.style.fontFamily=font;
span.style.fontSize=fontSize;
span.style.margin=0;
span.style.padding=0;
span.appendChild(doc.createTextNode(sampleText));
container.appendChild(span);
container.appendChild(img);
baseline=(img.offsetTop - span.offsetTop) + 1;
container.removeChild(span);
container.appendChild(doc.createTextNode(sampleText));
container.style.lineHeight="normal";
img.style.verticalAlign="super";
middle=(img.offsetTop-container.offsetTop) + 1;
metricsObj={
baseline: baseline,
lineWidth: 1,
middle: middle
};
fontData[font + "-" + fontSize]=metricsObj;
doc.body.removeChild(container);
return metricsObj;};})();
(function(){
var Util=_html2canvas.Util,
Generate={};
_html2canvas.Generate=Generate;var reGradients=[
/^(-webkit-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/,
/^(-o-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/,
/^(-webkit-gradient)\((linear|radial),\s((?:\d{1,3}%?)\s(?:\d{1,3}%?),\s(?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)\-]+)\)$/,
/^(-moz-linear-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?))([\w\d\.\s,%\(\)]+)\)$/,
/^(-webkit-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/,
/^(-moz-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s?([a-z\-]*)([\w\d\.\s,%\(\)]+)\)$/,
/^(-o-radial-gradient)\(((?:\d{1,3}%?)\s(?:\d{1,3}%?)),\s(\w+)\s([a-z\-]+)([\w\d\.\s,%\(\)]+)\)$/
];
Generate.parseGradient=function(css, bounds){
var gradient, i, len=reGradients.length, m1, stop, m2, m2Len, step, m3, tl,tr,br,bl;
for(i=0; i < len; i+=1){
m1=css.match(reGradients[i]);
if(m1){
break;}}
if(m1){
switch(m1[1]){
case '-webkit-linear-gradient':
case '-o-linear-gradient':
gradient={
type: 'linear',
x0: null,
y0: null,
x1: null,
y1: null,
colorStops: []
};
m2=m1[2].match(/\w+/g);
if(m2){
m2Len=m2.length;
for(i=0; i < m2Len; i+=1){
switch(m2[i]){
case 'top':
gradient.y0=0;
gradient.y1=bounds.height;
break;
case 'right':
gradient.x0=bounds.width;
gradient.x1=0;
break;
case 'bottom':
gradient.y0=bounds.height;
gradient.y1=0;
break;
case 'left':
gradient.x0=0;
gradient.x1=bounds.width;
break;}}}
if(gradient.x0===null && gradient.x1===null){
gradient.x0=gradient.x1=bounds.width / 2;}
if(gradient.y0===null && gradient.y1===null){
gradient.y0=gradient.y1=bounds.height / 2;}
m2=m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g);
if(m2){
m2Len=m2.length;
step=1 / Math.max(m2Len - 1, 1);
for(i=0; i < m2Len; i+=1){
m3=m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/);
if(m3[2]){
stop=parseFloat(m3[2]);
if(m3[3]==='%'){
stop /=100;}else{
stop /=bounds.width;}}else{
stop=i * step;}
gradient.colorStops.push({
color: m3[1],
stop: stop
});}}
break;
case '-webkit-gradient':
gradient={
type: m1[2]==='radial'?'circle':m1[2],
x0: 0,
y0: 0,
x1: 0,
y1: 0,
colorStops: []
};
m2=m1[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/);
if(m2){
gradient.x0=(m2[1] * bounds.width) / 100;
gradient.y0=(m2[2] * bounds.height) / 100;
gradient.x1=(m2[3] * bounds.width) / 100;
gradient.y1=(m2[4] * bounds.height) / 100;}
m2=m1[4].match(/((?:from|to|color-stop)\((?:[0-9\.]+,\s)?(?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)\))+/g);
if(m2){
m2Len=m2.length;
for(i=0; i < m2Len; i+=1){
m3=m2[i].match(/(from|to|color-stop)\(([0-9\.]+)?(?:,\s)?((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\)/);
stop=parseFloat(m3[2]);
if(m3[1]==='from'){
stop=0.0;}
if(m3[1]==='to'){
stop=1.0;}
gradient.colorStops.push({
color: m3[3],
stop: stop
});}}
break;
case '-moz-linear-gradient':
gradient={
type: 'linear',
x0: 0,
y0: 0,
x1: 0,
y1: 0,
colorStops: []
};
m2=m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/);
if(m2){
gradient.x0=(m2[1] * bounds.width) / 100;
gradient.y0=(m2[2] * bounds.height) / 100;
gradient.x1=bounds.width - gradient.x0;
gradient.y1=bounds.height - gradient.y0;}
m2=m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+/g);
if(m2){
m2Len=m2.length;
step=1 / Math.max(m2Len - 1, 1);
for(i=0; i < m2Len; i+=1){
m3=m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%)?/);
if(m3[2]){
stop=parseFloat(m3[2]);
if(m3[3]){
stop /=100;}}else{
stop=i * step;}
gradient.colorStops.push({
color: m3[1],
stop: stop
});}}
break;
case '-webkit-radial-gradient':
case '-moz-radial-gradient':
case '-o-radial-gradient':
gradient={
type: 'circle',
x0: 0,
y0: 0,
x1: bounds.width,
y1: bounds.height,
cx: 0,
cy: 0,
rx: 0,
ry: 0,
colorStops: []
};
m2=m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/);
if(m2){
gradient.cx=(m2[1] * bounds.width) / 100;
gradient.cy=(m2[2] * bounds.height) / 100;}
m2=m1[3].match(/\w+/);
m3=m1[4].match(/[a-z\-]*/);
if(m2 && m3){
switch(m3[0]){
case 'farthest-corner':
case 'cover':
case '':
tl=Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2));
tr=Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
br=Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
bl=Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2));
gradient.rx=gradient.ry=Math.max(tl, tr, br, bl);
break;
case 'closest-corner':
tl=Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2));
tr=Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
br=Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
bl=Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2));
gradient.rx=gradient.ry=Math.min(tl, tr, br, bl);
break;
case 'farthest-side':
if(m2[0]==='circle'){
gradient.rx=gradient.ry=Math.max(
gradient.cx,
gradient.cy,
gradient.x1 - gradient.cx,
gradient.y1 - gradient.cy
);}else{
gradient.type=m2[0];
gradient.rx=Math.max(
gradient.cx,
gradient.x1 - gradient.cx
);
gradient.ry=Math.max(
gradient.cy,
gradient.y1 - gradient.cy
);}
break;
case 'closest-side':
case 'contain':
if(m2[0]==='circle'){
gradient.rx=gradient.ry=Math.min(
gradient.cx,
gradient.cy,
gradient.x1 - gradient.cx,
gradient.y1 - gradient.cy
);}else{
gradient.type=m2[0];
gradient.rx=Math.min(
gradient.cx,
gradient.x1 - gradient.cx
);
gradient.ry=Math.min(
gradient.cy,
gradient.y1 - gradient.cy
);}
break;}}
m2=m1[5].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g);
if(m2){
m2Len=m2.length;
step=1 / Math.max(m2Len - 1, 1);
for(i=0; i < m2Len; i+=1){
m3=m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/);
if(m3[2]){
stop=parseFloat(m3[2]);
if(m3[3]==='%'){
stop /=100;}else{
stop /=bounds.width;}}else{
stop=i * step;}
gradient.colorStops.push({
color: m3[1],
stop: stop
});}}
break;}}
return gradient;};
function addScrollStops(grad){
return function(colorStop){
try {
grad.addColorStop(colorStop.stop, colorStop.color);}
catch(e){
Util.log(['failed to add color stop: ', e,'; tried to add: ', colorStop]);}};}
Generate.Gradient=function(src, bounds){
if(bounds.width===0 || bounds.height===0){
return;}
var canvas=document.createElement('canvas'),
ctx=canvas.getContext('2d'),
gradient, grad;
canvas.width=bounds.width;
canvas.height=bounds.height;
gradient=_html2canvas.Generate.parseGradient(src, bounds);
if(gradient){
switch(gradient.type){
case 'linear':
grad=ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1);
gradient.colorStops.forEach(addScrollStops(grad));
ctx.fillStyle=grad;
ctx.fillRect(0, 0, bounds.width, bounds.height);
break;
case 'circle':
grad=ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx);
gradient.colorStops.forEach(addScrollStops(grad));
ctx.fillStyle=grad;
ctx.fillRect(0, 0, bounds.width, bounds.height);
break;
case 'ellipse':
var canvasRadial=document.createElement('canvas'),
ctxRadial=canvasRadial.getContext('2d'),
ri=Math.max(gradient.rx, gradient.ry),
di=ri * 2;
canvasRadial.width=canvasRadial.height=di;
grad=ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri);
gradient.colorStops.forEach(addScrollStops(grad));
ctxRadial.fillStyle=grad;
ctxRadial.fillRect(0, 0, di, di);
ctx.fillStyle=gradient.colorStops[gradient.colorStops.length - 1].color;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(canvasRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry);
break;}}
return canvas;};
Generate.ListAlpha=function(number){
var tmp="",
modulus;
do {
modulus=number % 26;
tmp=String.fromCharCode((modulus) + 64) + tmp;
number=number / 26;}while((number*26) > 26);
return tmp;};
Generate.ListRoman=function(number){
var romanArray=["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"],
decimal=[1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
roman="",
v,
len=romanArray.length;
if(number <=0 || number >=4000){
return number;}
for (v=0; v < len; v+=1){
while (number >=decimal[v]){
number -=decimal[v];
roman +=romanArray[v];}}
return roman;};})();
function h2cRenderContext(width, height){
var storage=[];
return {
storage: storage,
width: width,
height: height,
clip: function(){
storage.push({
type: "function",
name: "clip",'arguments': arguments
});},
translate: function(){
storage.push({
type: "function",
name: "translate",'arguments': arguments
});},
fill: function(){
storage.push({
type: "function",
name: "fill",'arguments': arguments
});},
save: function(){
storage.push({
type: "function",
name: "save",'arguments': arguments
});},
restore: function(){
storage.push({
type: "function",
name: "restore",'arguments': arguments
});},
fillRect: function (){
storage.push({
type: "function",
name: "fillRect",'arguments': arguments
});},
createPattern: function(){
storage.push({
type: "function",
name: "createPattern",'arguments': arguments
});},
drawShape: function(){
var shape=[];
storage.push({
type: "function",
name: "drawShape",'arguments': shape
});
return {
moveTo: function(){
shape.push({
name: "moveTo",'arguments': arguments
});},
lineTo: function(){
shape.push({
name: "lineTo",'arguments': arguments
});},
arcTo: function(){
shape.push({
name: "arcTo",'arguments': arguments
});},
bezierCurveTo: function(){
shape.push({
name: "bezierCurveTo",'arguments': arguments
});},
quadraticCurveTo: function(){
shape.push({
name: "quadraticCurveTo",'arguments': arguments
});}};},
drawImage: function (){
storage.push({
type: "function",
name: "drawImage",'arguments': arguments
});},
fillText: function (){
storage.push({
type: "function",
name: "fillText",'arguments': arguments
});},
setVariable: function (variable, value){
storage.push({
type: "variable",
name: variable,'arguments': value
});
return value;}};}
_html2canvas.Parse=function (images, options){
window.scroll(0,0);var element=(( options.elements===undefined )?document.body:options.elements[0]),
numDraws=0,
doc=element.ownerDocument,
Util=_html2canvas.Util,
support=Util.Support(options, doc),
ignoreElementsRegExp=new RegExp("(" + options.ignoreElements + ")"),
body=doc.body,
getCSS=Util.getCSS,
pseudoHide="___html2canvas___pseudoelement",
hidePseudoElements=doc.createElement('style');
hidePseudoElements.innerHTML='.' + pseudoHide + '-before:before { content: "" !important; display: none !important;}' +
'.' + pseudoHide + '-after:after { content: "" !important; display: none !important;}';
body.appendChild(hidePseudoElements);
images=images || {};
function documentWidth (){
return Math.max(
Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth),
Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth),
Math.max(doc.body.clientWidth, doc.documentElement.clientWidth)
);}
function documentHeight (){
return Math.max(
Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight),
Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight),
Math.max(doc.body.clientHeight, doc.documentElement.clientHeight)
);}
function getCSSInt(element, attribute){
var val=parseInt(getCSS(element, attribute), 10);
return (isNaN(val))?0:val;}
function renderRect (ctx, x, y, w, h, bgcolor){
if(bgcolor !=="transparent"){
ctx.setVariable("fillStyle", bgcolor);
ctx.fillRect(x, y, w, h);
numDraws+=1;}}
function capitalize(m, p1, p2){
if(m.length > 0){
return p1 + p2.toUpperCase();}}
function textTransform (text, transform){
switch(transform){
case "lowercase":
return text.toLowerCase();
case "capitalize":
return text.replace( /(^|\s|:|-|\(|\))([a-z])/g, capitalize);
case "uppercase":
return text.toUpperCase();
default:
return text;}}
function noLetterSpacing(letter_spacing){
return (/^(normal|none|0px)$/.test(letter_spacing));}
function drawText(currentText, x, y, ctx){
if(currentText !==null && Util.trimText(currentText).length > 0){
ctx.fillText(currentText, x, y);
numDraws+=1;}}
function setTextVariables(ctx, el, text_decoration, color){
var align=false,
bold=getCSS(el, "fontWeight"),
family=getCSS(el, "fontFamily"),
size=getCSS(el, "fontSize"),
shadows=Util.parseTextShadows(getCSS(el, "textShadow"));
switch(parseInt(bold, 10)){
case 401:
bold="bold";
break;
case 400:
bold="normal";
break;}
ctx.setVariable("fillStyle", color);
ctx.setVariable("font", [getCSS(el, "fontStyle"), getCSS(el, "fontVariant"), bold, size, family].join(" "));
ctx.setVariable("textAlign", (align)?"right":"left");
if(shadows.length){
ctx.setVariable("shadowColor", shadows[0].color);
ctx.setVariable("shadowOffsetX", shadows[0].offsetX);
ctx.setVariable("shadowOffsetY", shadows[0].offsetY);
ctx.setVariable("shadowBlur", shadows[0].blur);}
if(text_decoration !=="none"){
return Util.Font(family, size, doc);}}
function renderTextDecoration(ctx, text_decoration, bounds, metrics, color){
switch(text_decoration){
case "underline":
renderRect(ctx, bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, color);
break;
case "overline":
renderRect(ctx, bounds.left, Math.round(bounds.top), bounds.width, 1, color);
break;
case "line-through":
renderRect(ctx, bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, color);
break;}}
function getTextBounds(state, text, textDecoration, isLast, transform){
var bounds;
if(support.rangeBounds && !transform){
if(textDecoration !=="none" || Util.trimText(text).length !==0){
bounds=textRangeBounds(text, state.node, state.textOffset);}
state.textOffset +=text.length;} else if(state.node && typeof state.node.nodeValue==="string" ){
var newTextNode=(isLast)?state.node.splitText(text.length):null;
bounds=textWrapperBounds(state.node, transform);
state.node=newTextNode;}
return bounds;}
function textRangeBounds(text, textNode, textOffset){
var range=doc.createRange();
range.setStart(textNode, textOffset);
range.setEnd(textNode, textOffset + text.length);
return range.getBoundingClientRect();}
function textWrapperBounds(oldTextNode, transform){
var parent=oldTextNode.parentNode,
wrapElement=doc.createElement('wrapper'),
backupText=oldTextNode.cloneNode(true);
wrapElement.appendChild(oldTextNode.cloneNode(true));
parent.replaceChild(wrapElement, oldTextNode);var bounds=transform?Util.OffsetBounds(wrapElement):Util.Bounds(wrapElement);
parent.replaceChild(backupText, wrapElement);
return bounds;}
function renderText(el, textNode, stack){
var ctx=stack.ctx,
color=getCSS(el, "color"),
textDecoration=getCSS(el, "textDecoration"),
textAlign=getCSS(el, "textAlign"),
metrics,
textList,
state={
node: textNode,
textOffset: 0
};
if(Util.trimText(textNode.nodeValue).length > 0){
textNode.nodeValue=textTransform(textNode.nodeValue, getCSS(el, "textTransform"));
textAlign=textAlign.replace(["-webkit-auto"],["auto"]);
textList=(!options.letterRendering && /^(left|right|justify|auto)$/.test(textAlign) && noLetterSpacing(getCSS(el, "letterSpacing"))) ?
textNode.nodeValue.split(/(\b| )/)
: textNode.nodeValue.split("");
metrics=setTextVariables(ctx, el, textDecoration, color);
if(options.chinese){
textList.forEach(function(word, index){
if(/.*[\u4E00-\u9FA5].*$/.test(word)){
word=word.split("");
word.unshift(index, 1);
textList.splice.apply(textList, word);}});}
textList.forEach(function(text, index){
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);}});}}
function listPosition (element, val){
var boundElement=doc.createElement( "boundelement" ),
originalType,
bounds;
boundElement.style.display="inline";
originalType=element.style.listStyleType;
element.style.listStyleType="none";
boundElement.appendChild(doc.createTextNode(val));
element.insertBefore(boundElement, element.firstChild);
bounds=Util.Bounds(boundElement);
element.removeChild(boundElement);
element.style.listStyleType=originalType;
return bounds;}
function elementIndex(el){
var i=-1,
count=1,
childs=el.parentNode.childNodes;
if(el.parentNode){
while(childs[++i] !==el){
if(childs[i].nodeType===1){
count++;}}
return count;}else{
return -1;}}
function listItemText(element, type){
var currentIndex=elementIndex(element), text;
switch(type){
case "decimal":
text=currentIndex;
break;
case "decimal-leading-zero":
text=(currentIndex.toString().length===1)?currentIndex="0" + currentIndex.toString():currentIndex.toString();
break;
case "upper-roman":
text=_html2canvas.Generate.ListRoman( currentIndex );
break;
case "lower-roman":
text=_html2canvas.Generate.ListRoman( currentIndex ).toLowerCase();
break;
case "lower-alpha":
text=_html2canvas.Generate.ListAlpha( currentIndex ).toLowerCase();
break;
case "upper-alpha":
text=_html2canvas.Generate.ListAlpha( currentIndex );
break;}
return text + ". ";}
function renderListItem(element, stack, elBounds){
var x,
text,
ctx=stack.ctx,
type=getCSS(element, "listStyleType"),
listBounds;
if(/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(type)){
text=listItemText(element, type);
listBounds=listPosition(element, text);
setTextVariables(ctx, element, "none", getCSS(element, "color"));
if(getCSS(element, "listStylePosition")==="inside"){
ctx.setVariable("textAlign", "left");
x=elBounds.left;}else{
return;}
drawText(text, x, listBounds.bottom, ctx);}}
function loadImage (src){
var img=images[src];
return (img && img.succeeded===true)?img.img:false;}
function clipBounds(src, dst){
var x=Math.max(src.left, dst.left),
y=Math.max(src.top, dst.top),
x2=Math.min((src.left + src.width), (dst.left + dst.width)),
y2=Math.min((src.top + src.height), (dst.top + dst.height));
return {
left:x,
top:y,
width:x2-x,
height:y2-y
};}
function setZ(element, stack, parentStack){
var newContext,
isPositioned=stack.cssPosition !=='static',
zIndex=isPositioned?getCSS(element,'zIndex'):'auto',
opacity=getCSS(element,'opacity'),
isFloated=getCSS(element,'cssFloat') !=='none';
stack.zIndex=newContext=h2czContext(zIndex);
newContext.isPositioned=isPositioned;
newContext.isFloated=isFloated;
newContext.opacity=opacity;
newContext.ownStacking=(zIndex !=='auto' || opacity < 1);
if(parentStack){
parentStack.zIndex.children.push(stack);}}
function renderImage(ctx, element, image, bounds, borders){
var paddingLeft=getCSSInt(element,'paddingLeft'),
paddingTop=getCSSInt(element,'paddingTop'),
paddingRight=getCSSInt(element,'paddingRight'),
paddingBottom=getCSSInt(element,'paddingBottom');
drawImage(
ctx,
image,
0,
0,
image.width,
image.height,
bounds.left + paddingLeft + borders[3].width,
bounds.top + paddingTop + borders[0].width,
bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight),
bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom)
);}
function getBorderData(element){
return ["Top", "Right", "Bottom", "Left"].map(function(side){
return {
width: getCSSInt(element,'border' + side + 'Width'),
color: getCSS(element,'border' + side + 'Color')
};});}
function getBorderRadiusData(element){
return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side){
return getCSS(element,'border' + side + 'Radius');});}
var getCurvePoints=(function(kappa){
return function(x, y, r1, r2){
var ox=(r1) * kappa,
oy=(r2) * kappa,
xm=x + r1,
ym=y + r2;
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
})
};};})(4 * ((Math.sqrt(2) - 1) / 3));
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 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 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 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],
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 getBorderClip(element, borderPoints, borders, radius, bounds){
var backgroundClip=getCSS(element,'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 parseBorders(element, bounds, borders){
var x=bounds.left,
y=bounds.top,
width=bounds.width,
height=bounds.height,
borderSide,
bx,
by,
bw,
bh,
borderArgs,
borderRadius=getBorderRadiusData(element),
borderPoints=calculateCurvePoints(bounds, borderRadius, borders),
borderData={
clip: getBorderClip(element, borderPoints, borders, borderRadius, bounds),
borders: []
};
for (borderSide=0; borderSide < 4; borderSide++){
if(borders[borderSide].width > 0){
bx=x;
by=y;
bw=width;
bh=height - (borders[2].width);
switch(borderSide){
case 0:
bh=borders[0].width;
borderArgs=drawSide({
c1: [bx, by],
c2: [bx + bw, by],
c3: [bx + bw - borders[1].width, by + bh],
c4: [bx + borders[3].width, by + bh]
}, borderRadius[0], borderRadius[1],
borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner);
break;
case 1:
bx=x + width - (borders[1].width);
bw=borders[1].width;
borderArgs=drawSide({
c1: [bx + bw, by],
c2: [bx + bw, by + bh + borders[2].width],
c3: [bx, by + bh],
c4: [bx, by + borders[0].width]
}, borderRadius[1], borderRadius[2],
borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner);
break;
case 2:
by=(by + height) - (borders[2].width);
bh=borders[2].width;
borderArgs=drawSide({
c1: [bx + bw, by + bh],
c2: [bx, by + bh],
c3: [bx + borders[3].width, by],
c4: [bx + bw - borders[3].width, by]
}, borderRadius[2], borderRadius[3],
borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner);
break;
case 3:
bw=borders[3].width;
borderArgs=drawSide({
c1: [bx, by + bh + borders[2].width],
c2: [bx, by],
c3: [bx + bw, by + borders[0].width],
c4: [bx + bw, by + bh]
}, borderRadius[3], borderRadius[0],
borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner);
break;}
borderData.borders.push({
args: borderArgs,
color: borders[borderSide].color
});}}
return borderData;}
function createShape(ctx, args){
var shape=ctx.drawShape();
args.forEach(function(border, index){
shape[(index===0)?"moveTo":border[0] + "To" ].apply(null, border.slice(1));});
return shape;}
function renderBorders(ctx, borderArgs, color){
if(color !=="transparent"){
ctx.setVariable( "fillStyle", color);
createShape(ctx, borderArgs);
ctx.fill();
numDraws+=1;}}
function renderFormValue (el, bounds, stack){
var valueWrap=doc.createElement('valuewrap'),
cssPropertyArray=['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'],
textValue,
textNode;
cssPropertyArray.forEach(function(property){
try {
valueWrap.style[property]=getCSS(el, property);} catch(e){
Util.log("html2canvas: Parse: Exception caught in renderFormValue: " + e.message);}});
valueWrap.style.borderColor="black";
valueWrap.style.borderStyle="solid";
valueWrap.style.display="block";
valueWrap.style.position="absolute";
if(/^(submit|reset|button|text|password)$/.test(el.type) || el.nodeName==="SELECT"){
valueWrap.style.lineHeight=getCSS(el, "height");}
valueWrap.style.top=bounds.top + "px";
valueWrap.style.left=bounds.left + "px";
textValue=(el.nodeName==="SELECT")?(el.options[el.selectedIndex] || 0).text:el.value;
if(!textValue){
textValue=el.placeholder;}
textNode=doc.createTextNode(textValue);
valueWrap.appendChild(textNode);
body.appendChild(valueWrap);
renderText(el, textNode, stack);
body.removeChild(valueWrap);}
function drawImage (ctx){
ctx.drawImage.apply(ctx, Array.prototype.slice.call(arguments, 1));
numDraws+=1;}
function getPseudoElement(el, which){
var elStyle=window.getComputedStyle(el, which);
if(!elStyle || !elStyle.content || elStyle.content==="none" || elStyle.content==="-moz-alt-content" || elStyle.display==="none"){
return;}
var content=elStyle.content + '',
first=content.substr( 0, 1 );
if(first===content.substr( content.length - 1 ) && first.match(/'|"/)){
content=content.substr( 1, content.length - 2 );}
var isImage=content.substr( 0, 3 )==='url',
elps=document.createElement( isImage?'img':'span' );
elps.className=pseudoHide + "-before " + pseudoHide + "-after";
Object.keys(elStyle).filter(indexedProperty).forEach(function(prop){
try {
elps.style[prop]=elStyle[prop];} catch (e){
Util.log(['Tried to assign readonly property ', prop,'Error:', e]);}});
if(isImage){
elps.src=Util.parseBackgroundImage(content)[0].args[0];}else{
elps.innerHTML=content;}
return elps;}
function indexedProperty(property){
return (isNaN(window.parseInt(property, 10)));}
function injectPseudoElements(el, stack){
var before=getPseudoElement(el,':before'),
after=getPseudoElement(el,':after');
if(!before && !after){
return;}
if(before){
el.className +=" " + pseudoHide + "-before";
el.parentNode.insertBefore(before, el);
parseElement(before, stack, true);
el.parentNode.removeChild(before);
el.className=el.className.replace(pseudoHide + "-before", "").trim();}
if(after){
el.className +=" " + pseudoHide + "-after";
el.appendChild(after);
parseElement(after, stack, true);
el.removeChild(after);
el.className=el.className.replace(pseudoHide + "-after", "").trim();}}
function renderBackgroundRepeat(ctx, image, backgroundPosition, bounds){
var offsetX=Math.round(bounds.left + backgroundPosition.left),
offsetY=Math.round(bounds.top + backgroundPosition.top);
ctx.createPattern(image);
ctx.translate(offsetX, offsetY);
ctx.fill();
ctx.translate(-offsetX, -offsetY);}
function backgroundRepeatShape(ctx, image, backgroundPosition, bounds, left, top, width, height){
var args=[];
args.push(["line", Math.round(left), Math.round(top)]);
args.push(["line", Math.round(left + width), Math.round(top)]);
args.push(["line", Math.round(left + width), Math.round(height + top)]);
args.push(["line", Math.round(left), Math.round(height + top)]);
createShape(ctx, args);
ctx.save();
ctx.clip();
renderBackgroundRepeat(ctx, image, backgroundPosition, bounds);
ctx.restore();}
function renderBackgroundColor(ctx, backgroundBounds, bgcolor){
renderRect(
ctx,
backgroundBounds.left,
backgroundBounds.top,
backgroundBounds.width,
backgroundBounds.height,
bgcolor
);}
function renderBackgroundRepeating(el, bounds, ctx, image, imageIndex){
var backgroundSize=Util.BackgroundSize(el, bounds, image, imageIndex),
backgroundPosition=Util.BackgroundPosition(el, bounds, image, imageIndex, backgroundSize),
backgroundRepeat=getCSS(el, "backgroundRepeat").split(",").map(Util.trimText);
image=resizeImage(image, backgroundSize);
backgroundRepeat=backgroundRepeat[imageIndex] || backgroundRepeat[0];
switch (backgroundRepeat){
case "repeat-x":
backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
bounds.left, bounds.top + backgroundPosition.top, 99999, image.height);
break;
case "repeat-y":
backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
bounds.left + backgroundPosition.left, bounds.top, image.width, 99999);
break;
case "no-repeat":
backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
bounds.left + backgroundPosition.left, bounds.top + backgroundPosition.top, image.width, image.height);
break;
default:
renderBackgroundRepeat(ctx, image, backgroundPosition, {
top: bounds.top,
left: bounds.left,
width: image.width,
height: image.height
});
break;}}
function renderBackgroundImage(element, bounds, ctx){
var backgroundImage=getCSS(element, "backgroundImage"),
backgroundImages=Util.parseBackgroundImage(backgroundImage),
image,
imageIndex=backgroundImages.length;
while(imageIndex--){
backgroundImage=backgroundImages[imageIndex];
if(!backgroundImage.args || backgroundImage.args.length===0){
continue;}
var key=backgroundImage.method==='url' ?
backgroundImage.args[0] :
backgroundImage.value;
image=loadImage(key);
if(image){
renderBackgroundRepeating(element, bounds, ctx, image, imageIndex);}else{
Util.log("html2canvas: Error loading background:", backgroundImage);}}}
function resizeImage(image, bounds){
if(image.width===bounds.width && image.height===bounds.height){
return image;}
var ctx, canvas=doc.createElement('canvas');
canvas.width=bounds.width;
canvas.height=bounds.height;
ctx=canvas.getContext("2d");
drawImage(ctx, image, 0, 0, image.width, image.height, 0, 0, bounds.width, bounds.height );
return canvas;}
function setOpacity(ctx, element, parentStack){
return ctx.setVariable("globalAlpha", getCSS(element, "opacity") * ((parentStack)?parentStack.opacity:1));}
function removePx(str){
return str.replace("px", "");}
var transformRegExp=/(matrix)\((.+)\)/;
function getTransform(element, parentStack){
var transform=getCSS(element, "transform") || getCSS(element, "-webkit-transform") || getCSS(element, "-moz-transform") || getCSS(element, "-ms-transform") || getCSS(element, "-o-transform");var transformOrigin=getCSS(element, "transform-origin") || getCSS(element, "-webkit-transform-origin") || getCSS(element, "-moz-transform-origin") || getCSS(element, "-ms-transform-origin") || getCSS(element, "-o-transform-origin") || "0px 0px";
transformOrigin=transformOrigin.split(" ").map(removePx).map(Util.asFloat);var matrix;
if(transform && transform !=="none"){
var match=transform.match(transformRegExp);
if(match){
switch(match[1]){
case "matrix":
matrix=match[2].split(",").map(Util.trimText).map(Util.asFloat);
break;}}}
return {
origin: transformOrigin,
matrix: matrix
};}
function createStack(element, parentStack, bounds, transform){
var ctx=h2cRenderContext((!parentStack)?documentWidth():bounds.width , (!parentStack)?documentHeight():bounds.height),
stack={
ctx: ctx,
opacity: setOpacity(ctx, element, parentStack),
cssPosition: getCSS(element, "position"),
borders: getBorderData(element),
transform: transform,
clip: (parentStack && parentStack.clip)?Util.Extend( {}, parentStack.clip ):null
};
setZ(element, stack, parentStack);
if(options.useOverflow===true && /(hidden|scroll|auto)/.test(getCSS(element, "overflow"))===true && /(BODY)/i.test(element.nodeName)===false){
stack.clip=(stack.clip)?clipBounds(stack.clip, bounds):bounds;}
return stack;}
function getBackgroundBounds(borders, bounds, clip){
var backgroundBounds={
left: bounds.left + borders[3].width,
top: bounds.top + borders[0].width,
width: bounds.width - (borders[1].width + borders[3].width),
height: bounds.height - (borders[0].width + borders[2].width)
};
if(clip){
backgroundBounds=clipBounds(backgroundBounds, clip);}
return backgroundBounds;}
function getBounds(element, transform){
var bounds=(transform.matrix)?Util.OffsetBounds(element):Util.Bounds(element);
transform.origin[0] +=bounds.left;
transform.origin[1] +=bounds.top;
return bounds;}
function renderElement(element, parentStack, pseudoElement, ignoreBackground){
var transform=getTransform(element, parentStack),
bounds=getBounds(element, transform),
image,
stack=createStack(element, parentStack, bounds, transform),
borders=stack.borders,
ctx=stack.ctx,
backgroundBounds=getBackgroundBounds(borders, bounds, stack.clip),
borderData=parseBorders(element, bounds, borders),
backgroundColor=(ignoreElementsRegExp.test(element.nodeName))?"#efefef":getCSS(element, "backgroundColor");
createShape(ctx, borderData.clip);
ctx.save();
ctx.clip();
if(backgroundBounds.height > 0 && backgroundBounds.width > 0 && !ignoreBackground){
renderBackgroundColor(ctx, bounds, backgroundColor);
renderBackgroundImage(element, backgroundBounds, ctx);} else if(ignoreBackground){
stack.backgroundColor= backgroundColor;}
ctx.restore();
borderData.borders.forEach(function(border){
renderBorders(ctx, border.args, border.color);});
if(!pseudoElement){
injectPseudoElements(element, stack);}
switch(element.nodeName){
case "IMG":
if((image=loadImage(element.getAttribute('src')))){
renderImage(ctx, element, image, bounds, borders);}else{
Util.log("html2canvas: Error loading <img>:" + element.getAttribute('src'));}
break;
case "INPUT":
if(/^(text|url|email|submit|button|reset)$/.test(element.type) && (element.value || element.placeholder || "").length > 0){
renderFormValue(element, bounds, stack);}
break;
case "TEXTAREA":
if((element.value || element.placeholder || "").length > 0){
renderFormValue(element, bounds, stack);}
break;
case "SELECT":
if((element.options||element.placeholder || "").length > 0){
renderFormValue(element, bounds, stack);}
break;
case "LI":
renderListItem(element, stack, backgroundBounds);
break;
case "CANVAS":
renderImage(ctx, element, element, bounds, borders);
break;}
return stack;}
function isElementVisible(element){
return (getCSS(element,'display') !=="none" && getCSS(element,'visibility') !=="hidden" && !element.hasAttribute("data-html2canvas-ignore"));}
function parseElement (element, stack, pseudoElement){
if(isElementVisible(element)){
stack=renderElement(element, stack, pseudoElement, false) || stack;
if(!ignoreElementsRegExp.test(element.nodeName)){
parseChildren(element, stack, pseudoElement);}}}
function parseChildren(element, stack, pseudoElement){
Util.Children(element).forEach(function(node){
if(node.nodeType===node.ELEMENT_NODE){
parseElement(node, stack, pseudoElement);} else if(node.nodeType===node.TEXT_NODE){
renderText(element, node, stack);}});}
function init(){
var background=getCSS(document.documentElement, "backgroundColor"),
transparentBackground=(Util.isTransparent(background) && element===document.body),
stack=renderElement(element, null, false, transparentBackground);
parseChildren(element, stack);
if(transparentBackground){
background=stack.backgroundColor;}
body.removeChild(hidePseudoElements);
return {
backgroundColor: background,
stack: stack
};}
return init();};
function h2czContext(zindex){
return {
zindex: zindex,
children: []
};}
_html2canvas.Preload=function( options ){
var images={
numLoaded: 0,
numFailed: 0,
numTotal: 0,
cleanupDone: false
},
pageOrigin,
Util=_html2canvas.Util,
methods,
i,
count=0,
element=options.elements[0] || document.body,
doc=element.ownerDocument,
domImages=element.getElementsByTagName('img'),
imgLen=domImages.length,
link=doc.createElement("a"),
supportCORS=(function( img ){
return (img.crossOrigin !==undefined);})(new Image()),
timeoutTimer;
link.href=window.location.href;
pageOrigin =link.protocol + link.host;
function isSameOrigin(url){
link.href=url;
link.href=link.href;var origin=link.protocol + link.host;
return (origin===pageOrigin);}
function start(){
Util.log("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")");
if(!images.firstRun && images.numLoaded >=images.numTotal){
Util.log("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")");
if(typeof options.complete==="function"){
options.complete(images);}}}
function proxyGetImage(url, img, imageObj){
var callback_name,
scriptUrl=options.proxy,
script;
link.href=url;
url=link.href;
callback_name='html2canvas_' + (count++);
imageObj.callbackname=callback_name;
if(scriptUrl.indexOf("?") > -1){
scriptUrl +="&";}else{
scriptUrl +="?";}
scriptUrl +='url=' + encodeURIComponent(url) + '&callback=' + callback_name;
script=doc.createElement("script");
window[callback_name]=function(a){
if(a.substring(0,6)==="error:"){
imageObj.succeeded=false;
images.numLoaded++;
images.numFailed++;
start();}else{
setImageLoadHandlers(img, imageObj);
img.src=a;}
window[callback_name]=undefined;
try {
delete window[callback_name];} catch(ex){}
script.parentNode.removeChild(script);
script=null;
delete imageObj.script;
delete imageObj.callbackname;};
script.setAttribute("type", "text/javascript");
script.setAttribute("src", scriptUrl);
imageObj.script=script;
window.document.body.appendChild(script);}
function loadPseudoElement(element, type){
var style=window.getComputedStyle(element, type),
content=style.content;
if(content.substr(0, 3)==='url'){
methods.loadImage(_html2canvas.Util.parseBackgroundImage(content)[0].args[0]);}
loadBackgroundImages(style.backgroundImage, element);}
function loadPseudoElementImages(element){
loadPseudoElement(element, ":before");
loadPseudoElement(element, ":after");}
function loadGradientImage(backgroundImage, bounds){
var img=_html2canvas.Generate.Gradient(backgroundImage, bounds);
if(img !==undefined){
images[backgroundImage]={
img: img,
succeeded: true
};
images.numTotal++;
images.numLoaded++;
start();}}
function invalidBackgrounds(background_image){
return (background_image && background_image.method && background_image.args && background_image.args.length > 0 );}
function loadBackgroundImages(background_image, el){
var bounds;
_html2canvas.Util.parseBackgroundImage(background_image).filter(invalidBackgrounds).forEach(function(background_image){
if(background_image.method==='url'){
methods.loadImage(background_image.args[0]);} else if(background_image.method.match(/\-?gradient$/)){
if(bounds===undefined){
bounds=_html2canvas.Util.Bounds(el);}
loadGradientImage(background_image.value, bounds);}});}
function getImages (el){
var elNodeType=false;
try {
Util.Children(el).forEach(getImages);}
catch( e ){}
try {
elNodeType=el.nodeType;} catch (ex){
elNodeType=false;
Util.log("html2canvas: failed to access some element's nodeType - Exception: " + ex.message);}
if(elNodeType===1 || elNodeType===undefined){
loadPseudoElementImages(el);
try {
loadBackgroundImages(Util.getCSS(el,'backgroundImage'), el);} catch(e){
Util.log("html2canvas: failed to get background-image - Exception: " + e.message);}
loadBackgroundImages(el);}}
function setImageLoadHandlers(img, imageObj){
img.onload=function(){
if( imageObj.timer !==undefined ){
window.clearTimeout( imageObj.timer );}
images.numLoaded++;
imageObj.succeeded=true;
img.onerror=img.onload=null;
start();};
img.onerror=function(){
if(img.crossOrigin==="anonymous"){
window.clearTimeout( imageObj.timer );
if( options.proxy ){
var src=img.src;
img=new Image();
imageObj.img=img;
img.src=src;
proxyGetImage( img.src, img, imageObj );
return;}}
images.numLoaded++;
images.numFailed++;
imageObj.succeeded=false;
img.onerror=img.onload=null;
start();};}
methods={
loadImage: function( src ){
var img, imageObj;
if( src && images[src]===undefined ){
img=new Image();
if( src.match(/data:image\/.*;base64,/i) ){
img.src=src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig,'');
imageObj=images[src]={
img: img
};
images.numTotal++;
setImageLoadHandlers(img, imageObj);} else if( isSameOrigin( src ) || options.allowTaint=== true ){
imageObj=images[src]={
img: img
};
images.numTotal++;
setImageLoadHandlers(img, imageObj);
img.src=src;} else if( supportCORS && !options.allowTaint && options.useCORS ){
img.crossOrigin="anonymous";
imageObj=images[src]={
img: img
};
images.numTotal++;
setImageLoadHandlers(img, imageObj);
img.src=src;} else if( options.proxy ){
imageObj=images[src]={
img: img
};
images.numTotal++;
proxyGetImage( src, img, imageObj );}}},
cleanupDOM: function(cause){
var img, src;
if(!images.cleanupDone){
if(cause && typeof cause==="string"){
Util.log("html2canvas: Cleanup because: " + cause);}else{
Util.log("html2canvas: Cleanup after timeout: " + options.timeout + " ms.");}
for (src in images){
if(images.hasOwnProperty(src)){
img=images[src];
if(typeof img==="object" && img.callbackname && img.succeeded===undefined){
window[img.callbackname]=undefined;
try {
delete window[img.callbackname];} catch(ex){}
if(img.script && img.script.parentNode){
img.script.setAttribute("src", "about:blank");
img.script.parentNode.removeChild(img.script);}
images.numLoaded++;
images.numFailed++;
Util.log("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal);}}}
if(window.stop !==undefined){
window.stop();} else if(document.execCommand !==undefined){
document.execCommand("Stop", false);}
if(document.close !==undefined){
document.close();}
images.cleanupDone=true;
if(!(cause && typeof cause==="string")){
start();}}},
renderingDone: function(){
if(timeoutTimer){
window.clearTimeout(timeoutTimer);}}};
if(options.timeout > 0){
timeoutTimer=window.setTimeout(methods.cleanupDOM, options.timeout);}
Util.log('html2canvas: Preload starts: finding background-images');
images.firstRun=true;
getImages(element);
Util.log('html2canvas: Preload: Finding images');
for (i=0; i < imgLen; i+=1){
methods.loadImage( domImages[i].getAttribute( "src" ) );}
images.firstRun=false;
Util.log('html2canvas: Preload: Done.');
if(images.numTotal===images.numLoaded){
start();}
return methods;};
_html2canvas.Renderer=function(parseQueue, options){
function createRenderQueue(parseQueue){
var queue=[],
rootContext;
rootContext=(function buildStackingContext(rootNode){
var rootContext={};
function insert(context, node, specialParent){
var zi=(node.zIndex.zindex==='auto')?0:Number(node.zIndex.zindex),
contextForChildren=context,
isPositioned=node.zIndex.isPositioned,
isFloated=node.zIndex.isFloated,
stub={node: node},
childrenDest=specialParent;
if(node.zIndex.ownStacking){
contextForChildren=stub.context={ '!': [{node:node, children: []}]};
childrenDest=undefined;} else if(isPositioned || isFloated){
childrenDest=stub.children=[];}
if(zi===0 && specialParent){
specialParent.push(stub);}else{
if(!context[zi]){ context[zi]=[];}
context[zi].push(stub);}
node.zIndex.children.forEach(function(childNode){
insert(contextForChildren, childNode, childrenDest);});}
insert(rootContext, rootNode);
return rootContext;})(parseQueue);
function sortZ(context){
Object.keys(context).sort().forEach(function(zi){
var nonPositioned=[],
floated=[],
positioned=[],
list=[];
context[zi].forEach(function(v){
if(v.node.zIndex.isPositioned || v.node.zIndex.opacity < 1){
positioned.push(v);} else if(v.node.zIndex.isFloated){
floated.push(v);}else{
nonPositioned.push(v);}});
(function walk(arr){
arr.forEach(function(v){
list.push(v);
if(v.children){ walk(v.children);}});})(nonPositioned.concat(floated, positioned));
list.forEach(function(v){
if(v.context){
sortZ(v.context);}else{
queue.push(v.node);}});});}
sortZ(rootContext);
return queue;}
function getRenderer(rendererName){
var renderer;
if(typeof options.renderer==="string" && _html2canvas.Renderer[rendererName] !==undefined){
renderer=_html2canvas.Renderer[rendererName](options);} else if(typeof rendererName==="function"){
renderer=rendererName(options);}else{
throw new Error("Unknown renderer");}
if( typeof renderer !=="function" ){
throw new Error("Invalid renderer defined");}
return renderer;}
return getRenderer(options.renderer)(parseQueue, options, document, createRenderQueue(parseQueue.stack), _html2canvas);};
_html2canvas.Util.Support=function (options, doc){
function supportSVGRendering(){
var img=new Image(),
canvas=doc.createElement("canvas"),
ctx=(canvas.getContext===undefined)?false:canvas.getContext("2d");
if(ctx===false){
return false;}
canvas.width=canvas.height=10;
img.src=[
"data:image/svg+xml,",
"<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'>",
"<foreignObject width='10' height='10'>",
"<div xmlns='http://www.w3.org/1999/xhtml' style='width:10;height:10;'>",
"sup",
"</div>",
"</foreignObject>",
"</svg>"
].join("");
try {
ctx.drawImage(img, 0, 0);
canvas.toDataURL();} catch(e){
return false;}
_html2canvas.Util.log('html2canvas: Parse: SVG powered rendering available');
return true;}
function supportRangeBounds(){
var r, testElement, rangeBounds, rangeHeight, support=false;
if(doc.createRange){
r=doc.createRange();
if(r.getBoundingClientRect){
testElement=doc.createElement('boundtest');
testElement.style.height="123px";
testElement.style.display="block";
doc.body.appendChild(testElement);
r.selectNode(testElement);
rangeBounds=r.getBoundingClientRect();
rangeHeight=rangeBounds.height;
if(rangeHeight===123){
support=true;}
doc.body.removeChild(testElement);}}
return support;}
return {
rangeBounds: supportRangeBounds(),
svgRendering: options.svgRendering && supportSVGRendering()
};};
window.html2canvas=function(elements, opts){
elements=(elements.length)?elements:[elements];var queue,
canvas,
options={
logging: false,
elements: elements,
background: "#fff",
proxy: null,
timeout: 0,
useCORS: false,
allowTaint: false,
svgRendering: false,
ignoreElements: "IFRAME|OBJECT|PARAM",
useOverflow: true,
letterRendering: false,
chinese: false,
width: null,
height: null,
taintTest: true,
renderer: "Canvas"
};
options=_html2canvas.Util.Extend(opts, options);
_html2canvas.logging=options.logging;
options.complete=function( images ){
if(typeof options.onpreloaded==="function"){
if( options.onpreloaded( images )===false ){
return;}}
queue=_html2canvas.Parse( images, options );
if(typeof options.onparsed==="function"){
if( options.onparsed( queue )===false ){
return;}}
canvas=_html2canvas.Renderer( queue, options );
if(typeof options.onrendered==="function"){
options.onrendered( canvas );}};
window.setTimeout( function(){
_html2canvas.Preload( options );}, 0 );
return {
render: function( queue, opts ){
return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) );},
parse: function( images, opts ){
return _html2canvas.Parse( images, _html2canvas.Util.Extend(opts, options) );},
preload: function( opts ){
return _html2canvas.Preload( _html2canvas.Util.Extend(opts, options) );},
log: _html2canvas.Util.log
};};
window.html2canvas.log=_html2canvas.Util.log;
window.html2canvas.Renderer={
Canvas: undefined
};
_html2canvas.Renderer.Canvas=function(options){
options=options || {};var doc=document,
safeImages=[],
testCanvas=document.createElement("canvas"),
testctx=testCanvas.getContext("2d"),
Util=_html2canvas.Util,
canvas=options.canvas || doc.createElement('canvas');
function createShape(ctx, args){
ctx.beginPath();
args.forEach(function(arg){
ctx[arg.name].apply(ctx, arg['arguments']);});
ctx.closePath();}
function safeImage(item){
if(safeImages.indexOf(item['arguments'][0].src )===-1){
testctx.drawImage(item['arguments'][0], 0, 0);
try {
testctx.getImageData(0, 0, 1, 1);} catch(e){
testCanvas=doc.createElement("canvas");
testctx=testCanvas.getContext("2d");
return false;}
safeImages.push(item['arguments'][0].src);}
return true;}
function renderItem(ctx, item){
switch(item.type){
case "variable":
ctx[item.name]=item['arguments'];
break;
case "function":
switch(item.name){
case "createPattern":
if(item['arguments'][0].width > 0 && item['arguments'][0].height > 0){
try {
ctx.fillStyle=ctx.createPattern(item['arguments'][0], "repeat");}
catch(e){
Util.log("html2canvas: Renderer: Error creating pattern", e.message);}}
break;
case "drawShape":
createShape(ctx, item['arguments']);
break;
case "drawImage":
if(item['arguments'][8] > 0 && item['arguments'][7] > 0){
if(!options.taintTest || (options.taintTest && safeImage(item))){
ctx.drawImage.apply( ctx, item['arguments'] );}}
break;
default:
ctx[item.name].apply(ctx, item['arguments']);}
break;}}
return function(parsedData, options, document, queue, _html2canvas){
var ctx=canvas.getContext("2d"),
newCanvas,
bounds,
fstyle,
zStack=parsedData.stack;
canvas.width=canvas.style.width= options.width || zStack.ctx.width;
canvas.height=canvas.style.height=options.height || zStack.ctx.height;
fstyle=ctx.fillStyle;
ctx.fillStyle=(Util.isTransparent(zStack.backgroundColor) && options.background !==undefined)?options.background:parsedData.backgroundColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle=fstyle;
queue.forEach(function(storageContext){
ctx.textBaseline="bottom";
ctx.save();
if(storageContext.transform.matrix){
ctx.translate(storageContext.transform.origin[0], storageContext.transform.origin[1]);
ctx.transform.apply(ctx, storageContext.transform.matrix);
ctx.translate(-storageContext.transform.origin[0], -storageContext.transform.origin[1]);}
if(storageContext.clip){
ctx.beginPath();
ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height);
ctx.clip();}
if(storageContext.ctx.storage){
storageContext.ctx.storage.forEach(function(item){
renderItem(ctx, item);});}
ctx.restore();});
Util.log("html2canvas: Renderer: Canvas renderer done - returning canvas obj");
if(options.elements.length===1){
if(typeof options.elements[0]==="object" && options.elements[0].nodeName !=="BODY"){
bounds=_html2canvas.Util.Bounds(options.elements[0]);
newCanvas=document.createElement('canvas');
newCanvas.width=Math.ceil(bounds.width);
newCanvas.height=Math.ceil(bounds.height);
ctx=newCanvas.getContext("2d");
ctx.drawImage(canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height);
canvas=null;
return newCanvas;}}
return canvas;};};})(window,document);