From 8d965029daa3d143f2147841a4818b17a93cedac Mon Sep 17 00:00:00 2001 From: Paolo Date: Tue, 17 Apr 2012 10:57:25 +0200 Subject: [PATCH 1/8] Fixed support for other Renderers other than Canvas --- build.xml | 2 ++ src/Renderer.js | 4 ++-- src/Util.js | 18 ++++++++++++------ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/build.xml b/build.xml index 9376385..a0e5da7 100644 --- a/build.xml +++ b/build.xml @@ -23,6 +23,7 @@ + @@ -76,6 +77,7 @@ + diff --git a/src/Renderer.js b/src/Renderer.js index 3513391..834767e 100644 --- a/src/Renderer.js +++ b/src/Renderer.js @@ -58,9 +58,9 @@ _html2canvas.Renderer = function(parseQueue, options){ sortZ(parseQueue.zIndex); - if ( typeof options.renderer._create !== "function" ) { + if ( typeof options._renderer._create !== "function" ) { throw new Error("Invalid renderer defined"); } - return options.renderer._create( parseQueue, options, document, queue, _html2canvas ); + return options._renderer._create( parseQueue, options, document, queue, _html2canvas ); }; diff --git a/src/Util.js b/src/Util.js index 9934691..ddd394d 100644 --- a/src/Util.js +++ b/src/Util.js @@ -34,14 +34,20 @@ html2canvas = function( elements, opts ) { flashcanvas: undefined, // path to flashcanvas width: null, height: null, - taintTest: true // do a taint test with all images before applying to canvas - - }; + taintTest: true, // do a taint test with all images before applying to canvas + renderer: "Canvas" + }, renderer; options = _html2canvas.Util.Extend(opts, options); - - options.renderer = options.renderer || _html2canvas.Renderer.Canvas( options ); - + + if (typeof options.renderer === "string" && _html2canvas.Renderer[options.renderer] !== undefined) { + options._renderer = _html2canvas.Renderer[options.renderer]( options ); + } else if (typeof options.renderer === "function") { + options._renderer = _html2canvas.Renderer.Canvas( options ); + } else { + throw("Unknown renderer"); + } + _html2canvas.logging = options.logging; options.complete = function( images ) { From cfc45e4f6e734117fd631ea0ad2080576b15f2aa Mon Sep 17 00:00:00 2001 From: Paolo Date: Tue, 17 Apr 2012 10:58:14 +0200 Subject: [PATCH 2/8] Fixed image rendering in SVG renderer --- src/renderers/SVG.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/renderers/SVG.js b/src/renderers/SVG.js index eb4a8f1..cc34da3 100644 --- a/src/renderers/SVG.js +++ b/src/renderers/SVG.js @@ -135,10 +135,10 @@ _html2canvas.Renderer.SVG = function( options ) { el = doc.createElementNS(svgNS, "image"); el.setAttributeNS(xlinkNS, "xlink:href", renderItem['arguments'][0].src); - el.setAttribute("width", renderItem['arguments'][0].width); - el.setAttribute("height", renderItem['arguments'][0].height); - el.setAttribute("x", renderItem['arguments'][5] - renderItem['arguments'][1]); - el.setAttribute("y", renderItem['arguments'][6] - renderItem['arguments'][2]); + el.setAttribute("width", renderItem['arguments'][7]); + el.setAttribute("height", renderItem['arguments'][8]); + el.setAttribute("x", renderItem['arguments'][5]); + el.setAttribute("y", renderItem['arguments'][6]); el.setAttribute("clip-path", "url(#clipId" + clipId + ")"); // el.setAttribute("xlink:href", ); From d494b8dfbde43f4bbe3704ac5ebd3ef0e00d6c06 Mon Sep 17 00:00:00 2001 From: Paolo Date: Tue, 17 Apr 2012 11:09:09 +0200 Subject: [PATCH 3/8] Added direnv conf file to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index aa66a8c..b439c63 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ image.jpg /.project /.settings/ +.envrc From de1d1d7087238434542bdff3624b08d50a84f7eb Mon Sep 17 00:00:00 2001 From: Binduck Date: Tue, 17 Apr 2012 12:43:51 +0200 Subject: [PATCH 4/8] Minor changes --- demo.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demo.html b/demo.html index cf53259..b065445 100644 --- a/demo.html +++ b/demo.html @@ -1,4 +1,4 @@ - + @@ -2370,4 +2370,4 @@ body - \ No newline at end of file + From a5969be6f6d376f01b821f80abc62f27720ff44d Mon Sep 17 00:00:00 2001 From: Bdkzero Date: Mon, 23 Apr 2012 23:04:23 +0200 Subject: [PATCH 5/8] Fixed custom renderer option --- src/Util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Util.js b/src/Util.js index ddd394d..93971cd 100644 --- a/src/Util.js +++ b/src/Util.js @@ -43,7 +43,7 @@ html2canvas = function( elements, opts ) { if (typeof options.renderer === "string" && _html2canvas.Renderer[options.renderer] !== undefined) { options._renderer = _html2canvas.Renderer[options.renderer]( options ); } else if (typeof options.renderer === "function") { - options._renderer = _html2canvas.Renderer.Canvas( options ); + options._renderer = options.renderer( options ); } else { throw("Unknown renderer"); } From 98bc1f0833670c1242a34b21e64fb3c0f34c9e2c Mon Sep 17 00:00:00 2001 From: Bdkzero Date: Tue, 8 May 2012 10:59:14 +0200 Subject: [PATCH 6/8] Prebuilt distribution files --- .gitignore | 1 - build/html2canvas.js | 3007 ++++++++++++++++++++++++++++ build/jquery.plugin.html2canvas.js | 87 + 3 files changed, 3094 insertions(+), 1 deletion(-) create mode 100644 build/html2canvas.js create mode 100644 build/jquery.plugin.html2canvas.js diff --git a/.gitignore b/.gitignore index b439c63..0da05c1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ /tests/cache/ /tests/flashcanvas.html /lib/ -/build/ image.jpg /.project /.settings/ diff --git a/build/html2canvas.js b/build/html2canvas.js new file mode 100644 index 0000000..d96769b --- /dev/null +++ b/build/html2canvas.js @@ -0,0 +1,3007 @@ +/** + @license html2canvas v0.33 + Copyright (c) 2011 Niklas von Hertzen. All rights reserved. + http://www.twitter.com/niklasvh + + Released under MIT License + */ +(function(window, document, undefined){ +/* + html2canvas v0.33 + Copyright (c) 2011 Niklas von Hertzen. All rights reserved. + http://www.twitter.com/niklasvh + + Released under MIT License + */ +"use strict"; + +var _html2canvas = {}, +html2canvas; + + +function h2clog(a) { + if (_html2canvas.logging && window.console && window.console.log) { + window.console.log(a); + } +} + +_html2canvas.Util = {}; + +_html2canvas.Util.backgroundImage = function (src) { + + if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) { + return src; + } + + if (src.toLowerCase().substr( 0, 5 ) === 'url("') { + src = src.substr( 5 ); + src = src.substr( 0, src.length - 2 ); + } else { + src = src.substr( 4 ); + src = src.substr( 0, src.length - 1 ); + } + + return src; +}; + +_html2canvas.Util.Bounds = function getBounds (el) { + var clientRect, + bounds = {}; + + if (el.getBoundingClientRect){ + clientRect = el.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; + + // older IE doesn't have width/height, but top/bottom instead + bounds.width = clientRect.width || (clientRect.right - clientRect.left); + bounds.height = clientRect.height || (clientRect.bottom - clientRect.top); + + return bounds; + + } +}; + +_html2canvas.Util.getCSS = function (el, attribute) { + // return $(el).css(attribute); + + var val; + + function toPX( attribute, val ) { + var rsLeft = el.runtimeStyle && el.runtimeStyle[ attribute ], + left, + style = el.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( val ) && /^-?\d/.test( val ) ) { + + // Remember the original values + left = style.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + el.runtimeStyle.left = el.currentStyle.left; + } + style.left = attribute === "fontSize" ? "1em" : (val || 0); + val = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + el.runtimeStyle.left = rsLeft; + } + + } + + if (!/^(thin|medium|thick)$/i.test( val )) { + return Math.round(parseFloat( val )) + "px"; + } + + return val; + + } + + + if ( window.getComputedStyle ) { + val = document.defaultView.getComputedStyle(el, null)[ attribute ]; + + if ( attribute === "backgroundPosition" ) { + + val = (val.split(",")[0] || "0 0").split(" "); + + val[ 0 ] = ( val[0].indexOf( "%" ) === -1 ) ? toPX( attribute + "X", val[ 0 ] ) : val[ 0 ]; + val[ 1 ] = ( val[1] === undefined ) ? val[0] : val[1]; // IE 9 doesn't return double digit always + val[ 1 ] = ( val[1].indexOf( "%" ) === -1 ) ? toPX( attribute + "Y", val[ 1 ] ) : val[ 1 ]; + } + + } else if ( el.currentStyle ) { + // IE 9> + if (attribute === "backgroundPosition") { + // Older IE uses -x and -y + val = [ toPX( attribute + "X", el.currentStyle[ attribute + "X" ] ), toPX( attribute + "Y", el.currentStyle[ attribute + "Y" ] ) ]; + } else { + + val = toPX( attribute, el.currentStyle[ attribute ] ); + + if (/^(border)/i.test( attribute ) && /^(medium|thin|thick)$/i.test( val )) { + switch (val) { + case "thin": + val = "1px"; + break; + case "medium": + val = "0px"; // this is wrong, it should be 3px but IE uses medium for no border as well.. TODO find a work around + break; + case "thick": + val = "5px"; + break; + } + } + } + + + + } + + + + + return val; + + + +//return $(el).css(attribute); + + +}; + + +_html2canvas.Util.BackgroundPosition = function ( el, bounds, image ) { + // TODO add support for multi image backgrounds + + var bgposition = _html2canvas.Util.getCSS( el, "backgroundPosition" ) , + topPos, + left, + percentage, + val; + + if (bgposition.length === 1){ + val = bgposition; + + bgposition = []; + + bgposition[0] = val; + bgposition[1] = val; + } + + + + if (bgposition[0].toString().indexOf("%") !== -1){ + percentage = (parseFloat(bgposition[0])/100); + left = ((bounds.width * percentage)-(image.width*percentage)); + + }else{ + left = parseInt(bgposition[0],10); + } + + if (bgposition[1].toString().indexOf("%") !== -1){ + + percentage = (parseFloat(bgposition[1])/100); + topPos = ((bounds.height * percentage)-(image.height*percentage)); + }else{ + topPos = parseInt(bgposition[1],10); + } + + + + + return { + top: topPos, + left: left + }; + +}; + +_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(el) { + // $(el).contents() !== el.childNodes, Opera / IE have issues with that + var children; + try { + children = $(el).contents(); + //children = (el.nodeName && el.nodeName.toUpperCase() === "IFRAME") ? el.contentDocument || el.contentWindow.document : el.childNodes ; + + } catch (ex) { + h2clog("html2canvas.Util.Children failed with exception: " + ex.message); + children = []; + } + return children; +}; +/* + html2canvas v0.33 + Copyright (c) 2011 Niklas von Hertzen. All rights reserved. + http://www.twitter.com/niklasvh + + Contributor(s): + Niklas von Hertzen + André Fiedler + + Released under MIT License + */ + +(function(){ + +_html2canvas.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,%\(\)]+)\)$/ +]; + +/* + * TODO: Add IE10 vendor prefix (-ms) support + * TODO: Add W3C gradient (linear-gradient) support + * TODO: Add old Webkit -webkit-gradient(radial, ...) support + * TODO: Maybe some RegExp optimizations are possible ;o) + */ +_html2canvas.Generate.parseGradient = function(css, bounds) { + var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3; + + 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: [] + }; + + // get coordinates + 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){ // center + gradient.x0 = gradient.x1 = bounds.width / 2; + } + if(gradient.y0 === null && gradient.y1 === null){ // center + gradient.y0 = gradient.y1 = bounds.height / 2; + } + + // get colors and stops + 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 { // px - stupid opera + 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], // TODO: Add radial gradient support for older mozilla definitions + x0: 0, + y0: 0, + x1: 0, + y1: 0, + colorStops: [] + }; + + // get coordinates + 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; + } + + // get colors and stops + 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: [] + }; + + // get coordinates + m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/); + + // m2[1] == 0% -> left + // m2[1] == 50% -> center + // m2[1] == 100% -> right + + // m2[2] == 0% -> top + // m2[2] == 50% -> center + // m2[2] == 100% -> bottom + + 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; + } + + // get colors and stops + 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]){ // percentage + 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: [] + }; + + // center + 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; + } + + // size + m2 = m1[3].match(/\w+/); + m3 = m1[4].match(/[a-z-]*/); + if(m2 && m3){ + switch(m3[0]){ + case 'farthest-corner': + case 'cover': // is equivalent to farthest-corner + case '': // mozilla removes "cover" from definition :( + var tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); + var tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + var br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + var 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': + var tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); + var tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + var br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + var 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 { // ellipse + + 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': // is equivalent to closest-side + if(m2[0] === 'circle'){ + gradient.rx = gradient.ry = Math.min( + gradient.cx, + gradient.cy, + gradient.x1 - gradient.cx, + gradient.y1 - gradient.cy + ); + } else { // ellipse + + 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; + + // TODO: add support for "30px 40px" sizes (webkit only) + } + } + + // color stops + 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 { // px - stupid opera + stop /= bounds.width; + } + } else { + stop = i * step; + } + gradient.colorStops.push({ + color: m3[1], + stop: stop + }); + } + } + break; + } + } + + return gradient; +}; + +_html2canvas.Generate.Gradient = function(src, bounds) { + var canvas = document.createElement('canvas'), + ctx = canvas.getContext('2d'), + gradient, grad, i, len, img; + + canvas.width = bounds.width; + canvas.height = bounds.height; + + // TODO: add support for multi defined background gradients (like radial gradient example in background.html) + gradient = _html2canvas.Generate.parseGradient(src, bounds); + + img = new Image(); + + if(gradient){ + if(gradient.type === 'linear'){ + grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1); + + for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { + try { + grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); + } + catch(e) { + h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]); + } + } + + ctx.fillStyle = grad; + ctx.fillRect(0, 0, bounds.width, bounds.height); + + img.src = canvas.toDataURL(); + } else if(gradient.type === 'circle'){ + + grad = ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx); + + for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { + try { + grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); + } + catch(e) { + h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]); + } + } + + ctx.fillStyle = grad; + ctx.fillRect(0, 0, bounds.width, bounds.height); + + img.src = canvas.toDataURL(); + } else if(gradient.type === 'ellipse'){ + + // draw circle + var canvasRadial = document.createElement('canvas'), + ctxRadial = canvasRadial.getContext('2d'), + ri = Math.max(gradient.rx, gradient.ry), + di = ri * 2, imgRadial; + + canvasRadial.width = canvasRadial.height = di; + + grad = ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri); + + for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { + try { + grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); + } + catch(e) { + h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]); + } + } + + ctxRadial.fillStyle = grad; + ctxRadial.fillRect(0, 0, di, di); + + ctx.fillStyle = gradient.colorStops[i - 1].color; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + imgRadial = new Image(); + imgRadial.onload = function() { // wait until the image is filled + + // transform circle to ellipse + ctx.drawImage(imgRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry); + + img.src = canvas.toDataURL(); + + } + imgRadial.src = canvasRadial.toDataURL(); + } + } + + return img; +}; + +_html2canvas.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; +}; + +_html2canvas.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; + +}; + +})(); +/* + html2canvas v0.33 + Copyright (c) 2011 Niklas von Hertzen. All rights reserved. + http://www.twitter.com/niklasvh + + Released under MIT License +*/ + +/* + * New function for traversing elements + */ + +_html2canvas.Parse = function ( images, options ) { + window.scroll(0,0); + + var support = { + rangeBounds: false, + svgRendering: options.svgRendering && (function( ){ + var img = new Image(), + canvas = document.createElement("canvas"), + ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d"); + if (ctx === false) { + // browser doesn't support canvas, good luck supporting SVG on canvas + return false; + } + canvas.width = canvas.height = 10; + img.src = [ + "data:image/svg+xml,", + "", + "", + "
", + "sup", + "
", + "
", + "
" + ].join(""); + try { + ctx.drawImage(img, 0, 0); + canvas.toDataURL(); + } catch(e) { + return false; + } + h2clog('html2canvas: Parse: SVG powered rendering available'); + return true; + + })() + }, + element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default + needReorder = false, + numDraws = 0, + fontData = {}, + doc = element.ownerDocument, + ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"), + body = doc.body, + r, + testElement, + rangeBounds, + rangeHeight, + stack, + ctx, + docDim, + i, + children, + childrenLen; + + + function docSize(){ + + return { + width: 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) + ), + height: 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) + ) + }; + + } + + images = images || {}; + + // Test whether we can use ranges to measure bounding boxes + // Opera doesn't provide valid bounds.height/bottom even though it supports the method. + + + if (doc.createRange) { + r = doc.createRange(); + //this.support.rangeBounds = new Boolean(r.getBoundingClientRect); + if (r.getBoundingClientRect){ + testElement = doc.createElement('boundtest'); + testElement.style.height = "123px"; + testElement.style.display = "block"; + body.appendChild(testElement); + + r.selectNode(testElement); + rangeBounds = r.getBoundingClientRect(); + rangeHeight = rangeBounds.height; + + if (rangeHeight === 123) { + support.rangeBounds = true; + } + body.removeChild(testElement); + + + } + + } + + + /* + var rootStack = new this.storageContext($(document).width(),$(document).height()); + rootStack.opacity = this.getCSS(this.element,"opacity"); + var stack = this.newElement(this.element,rootStack); + + + this.parseElement(this.element,stack); + */ + + + + + var getCSS = _html2canvas.Util.getCSS; + function getCSSInt(element, attribute) { + var val = parseInt(getCSS(element, attribute), 10); + return (isNaN(val)) ? 0 : val; // borders in old IE are throwing 'medium' for demo.html + } + + // Drawing a rectangle + function renderRect (ctx, x, y, w, h, bgcolor) { + if (bgcolor !=="transparent"){ + ctx.setVariable("fillStyle", bgcolor); + ctx.fillRect (x, y, w, h); + numDraws+=1; + } + } + + + function textTransform (text, transform) { + switch(transform){ + case "lowercase": + return text.toLowerCase(); + + case "capitalize": + return text.replace( /(^|\s|:|-|\(|\))([a-z])/g , function (m, p1, p2) { + if (m.length > 0) { + return p1 + p2.toUpperCase(); + } + } ); + + case "uppercase": + return text.toUpperCase(); + + default: + return text; + + } + + } + + function trimText (text) { + return text.replace(/^\s*/g, "").replace(/\s*$/g, ""); + } + + function fontMetrics (font, fontSize) { + + if (fontData[font + "-" + fontSize] !== undefined) { + return fontData[font + "-" + fontSize]; + } + + + var container = doc.createElement('div'), + img = doc.createElement('img'), + span = doc.createElement('span'), + baseline, + middle, + metricsObj; + + + container.style.visibility = "hidden"; + container.style.fontFamily = font; + container.style.fontSize = fontSize; + container.style.margin = 0; + container.style.padding = 0; + + body.appendChild(container); + + + + // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever (handtinywhite.gif) + 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('Hidden Text')); + container.appendChild(span); + container.appendChild(img); + baseline = (img.offsetTop - span.offsetTop) + 1; + + container.removeChild(span); + container.appendChild(doc.createTextNode('Hidden Text')); + + 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; + + body.removeChild(container); + + return metricsObj; + + } + + + function drawText(currentText, x, y, ctx){ + if (trimText(currentText).length>0) { + ctx.fillText(currentText,x,y); + numDraws+=1; + } + } + + + function renderText(el, textNode, stack) { + var ctx = stack.ctx, + family = getCSS(el, "fontFamily"), + size = getCSS(el, "fontSize"), + color = getCSS(el, "color"), + text_decoration = getCSS(el, "textDecoration"), + text_align = getCSS(el, "textAlign"), + letter_spacing = getCSS(el, "letterSpacing"), + bounds, + text, + metrics, + renderList, + listLen, + bold = getCSS(el, "fontWeight"), + font_style = getCSS(el, "fontStyle"), + font_variant = getCSS(el, "fontVariant"), + align = false, + newTextNode, + textValue, + textOffset = 0, + oldTextNode, + c, + range, + parent, + wrapElement, + backupText; + + // apply text-transform:ation to the text + + + + textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform")); + text = trimText(textNode.nodeValue); + + if (text.length>0){ + + if (text_decoration !== "none"){ + metrics = fontMetrics(family, size); + } + + text_align = text_align.replace(["-webkit-auto"],["auto"]); + + if (options.letterRendering === false && /^(left|right|justify|auto)$/.test(text_align) && /^(normal|none)$/.test(letter_spacing)){ + // this.setContextVariable(ctx,"textAlign",text_align); + renderList = textNode.nodeValue.split(/(\b| )/); + + }else{ + // this.setContextVariable(ctx,"textAlign","left"); + renderList = textNode.nodeValue.split(""); + } + + switch(parseInt(bold, 10)){ + case 401: + bold = "bold"; + break; + case 400: + bold = "normal"; + break; + } + + ctx.setVariable("fillStyle", color); + + /* + need to be defined in the order as defined in http://www.w3.org/TR/CSS21/fonts.html#font-shorthand + to properly work in Firefox + */ + ctx.setVariable("font", font_style+ " " + font_variant + " " + bold + " " + size + " " + family); + + if (align){ + ctx.setVariable("textAlign", "right"); + }else{ + ctx.setVariable("textAlign", "left"); + } + + + /* + if (stack.clip){ + ctx.rect (stack.clip.left, stack.clip.top, stack.clip.width, stack.clip.height); + ctx.clip(); + } + */ + + + oldTextNode = textNode; + + + for ( c=0, listLen = renderList.length; c < listLen; c+=1 ) { + textValue = null; + + + + if (support.rangeBounds){ + // getBoundingClientRect is supported for ranges + if (text_decoration !== "none" || trimText(renderList[c]).length !== 0) { + textValue = renderList[c]; + if (doc.createRange){ + range = doc.createRange(); + + range.setStart(textNode, textOffset); + range.setEnd(textNode, textOffset + textValue.length); + }else{ + // TODO add IE support + range = body.createTextRange(); + } + + if (range.getBoundingClientRect()) { + bounds = range.getBoundingClientRect(); + }else{ + bounds = {}; + } + + } + }else{ + // it isn't supported, so let's wrap it inside an element instead and get the bounds there + + // IE 9 bug + if (typeof oldTextNode.nodeValue !== "string" ){ + continue; + } + + newTextNode = oldTextNode.splitText(renderList[c].length); + + parent = oldTextNode.parentNode; + wrapElement = doc.createElement('wrapper'); + backupText = oldTextNode.cloneNode(true); + + wrapElement.appendChild(oldTextNode.cloneNode(true)); + parent.replaceChild(wrapElement, oldTextNode); + + bounds = _html2canvas.Util.Bounds(wrapElement); + + textValue = oldTextNode.nodeValue; + + oldTextNode = newTextNode; + parent.replaceChild(backupText, wrapElement); + + + } + + if (textValue !== null){ + drawText(textValue, bounds.left, bounds.bottom, ctx); + } + + switch(text_decoration) { + case "underline": + // Draws a line at the baseline of the font + // TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size + renderRect(ctx, bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, color); + break; + case "overline": + renderRect(ctx, bounds.left, bounds.top, bounds.width, 1, color); + break; + case "line-through": + // TODO try and find exact position for line-through + renderRect(ctx, bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, color); + break; + + } + + + + + + textOffset += renderList[c].length; + + } + + + + } + + } + + function listPosition (element, val) { + var boundElement = doc.createElement( "boundelement" ), + type, + bounds; + + boundElement.style.display = "inline"; + //boundElement.style.width = "1px"; + //boundElement.style.height = "1px"; + + type = element.style.listStyleType; + element.style.listStyleType = "none"; + + boundElement.appendChild( doc.createTextNode( val ) ); + + + element.insertBefore(boundElement, element.firstChild); + + + bounds = _html2canvas.Util.Bounds( boundElement ); + element.removeChild( boundElement ); + element.style.listStyleType = type; + return bounds; + + } + + + function renderListItem(element, stack, elBounds) { + + + var position = getCSS(element, "listStylePosition"), + x, + y, + type = getCSS(element, "listStyleType"), + currentIndex, + text, + listBounds, + bold = getCSS(element, "fontWeight"); + + if (/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(type)) { + + // TODO remove jQuery dependency + currentIndex = $(element).index()+1; + + switch(type){ + case "decimal": + text = currentIndex; + break; + case "decimal-leading-zero": + if (currentIndex.toString().length === 1){ + text = currentIndex = "0" + currentIndex.toString(); + }else{ + text = 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; + } + + + text += ". "; + listBounds = listPosition(element, text); + + + + switch(bold){ + case 401: + bold = "bold"; + break; + case 400: + bold = "normal"; + break; + } + + + + + ctx.setVariable( "fillStyle", getCSS(element, "color") ); + ctx.setVariable( "font", getCSS(element, "fontVariant") + " " + bold + " " + getCSS(element, "fontStyle") + " " + getCSS(element, "fontSize") + " " + getCSS(element, "fontFamily") ); + + + if ( position === "inside" ) { + ctx.setVariable("textAlign", "left"); + // this.setFont(stack.ctx, element, false); + x = elBounds.left; + + }else{ + return; + /* + TODO really need to figure out some more accurate way to try and find the position. + as defined in http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-position, it does not even have a specified "correct" position, so each browser + may display it whatever way it feels like. + "The position of the list-item marker adjacent to floats is undefined in CSS 2.1. CSS 2.1 does not specify the precise location of the marker box or its position in the painting order" + + ctx.setVariable("textAlign", "right"); + // this.setFont(stack.ctx, element, true); + x = elBounds.left - 10; + */ + } + + y = listBounds.bottom; + + drawText(text, x, y, ctx); + + + } + + + } + + function loadImage (src){ + var img = images[src]; + if (img && img.succeeded === true) { + return img.img; + } else { + return 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(zIndex, parentZ){ + // TODO fix static elements overlapping relative/absolute elements under same stack, if they are defined after them + var newContext; + if (!parentZ){ + newContext = h2czContext(0); + return newContext; + } + + if (zIndex !== "auto"){ + needReorder = true; + newContext = h2czContext(zIndex); + parentZ.children.push(newContext); + return newContext; + + } + + return parentZ; + + } + + function renderBorders(el, ctx, bounds, clip){ + + /* + * TODO add support for different border-style's than solid + */ + + var x = bounds.left, + y = bounds.top, + w = bounds.width, + h = bounds.height, + borderSide, + borderData, + bx, + by, + bw, + bh, + borderBounds, + borders = (function(el){ + var borders = [], + sides = ["Top","Right","Bottom","Left"], + s; + + for (s = 0; s < 4; s+=1){ + borders.push({ + width: getCSSInt(el, 'border' + sides[s] + 'Width'), + color: getCSS(el, 'border' + sides[s] + 'Color') + }); + } + + return borders; + + }(el)); + + + for (borderSide = 0; borderSide < 4; borderSide+=1){ + borderData = borders[borderSide]; + + if (borderData.width>0){ + bx = x; + by = y; + bw = w; + bh = h - (borders[2].width); + + switch(borderSide){ + case 0: + // top border + bh = borders[0].width; + break; + case 1: + // right border + bx = x + w - (borders[1].width); + bw = borders[1].width; + break; + case 2: + // bottom border + by = (by + h) - (borders[2].width); + bh = borders[2].width; + break; + case 3: + // left border + bw = borders[3].width; + break; + } + + borderBounds = { + left:bx, + top:by, + width: bw, + height:bh + }; + + if (clip){ + borderBounds = clipBounds(borderBounds, clip); + } + + + if (borderBounds.width>0 && borderBounds.height>0){ + renderRect(ctx, bx, by, borderBounds.width, borderBounds.height, borderData.color); + } + + + } + } + + return borders; + + } + + + function renderFormValue (el, bounds, stack){ + + var valueWrap = doc.createElement('valuewrap'), + cssArr = ['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'], + i, + textValue, + textNode, + arrLen, + style; + + for (i = 0, arrLen = cssArr.length; i < arrLen; i+=1){ + style = cssArr[i]; + + try { + valueWrap.style[style] = getCSS(el, style); + } catch( e ) { + // Older IE has issues with "border" + h2clog("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"; + + if (el.nodeName === "SELECT"){ + // TODO increase accuracy of text position + textValue = el.options[el.selectedIndex].text; + } else{ + textValue = el.value; + } + textNode = doc.createTextNode(textValue); + + valueWrap.appendChild(textNode); + body.appendChild(valueWrap); + + + renderText(el, textNode, stack); + body.removeChild(valueWrap); + + + + } + + + + + + function renderImage (ctx, image, sx, sy, sw, sh, dx, dy, dw, dh) { + ctx.drawImage( + image, + sx, //sx + sy, //sy + sw, //sw + sh, //sh + dx, //dx + dy, // dy + dw, //dw + dh //dh + ); + numDraws+=1; + + } + + + function renderBackgroundRepeat (ctx, image, x, y, width, height, elx, ely){ + var sourceX = 0, + sourceY=0; + if (elx-x>0){ + sourceX = elx-x; + } + + if (ely-y>0){ + sourceY = ely-y; + } + + renderImage( + ctx, + image, + sourceX, // source X + sourceY, // source Y + width-sourceX, // source Width + height-sourceY, // source Height + x+sourceX, // destination X + y+sourceY, // destination Y + width-sourceX, // destination width + height-sourceY // destination height + ); + } + + + function renderBackgroundRepeatY (ctx, image, bgp, x, y, w, h){ + + var height, + width = Math.min(image.width,w),bgy; + + bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height; + + + for(bgy=(y+bgp.top);bgyh+y){ + height = (h+y)-bgy; + }else{ + height = image.height; + } + renderBackgroundRepeat(ctx,image,x+bgp.left,bgy,width,height,x,y); + + bgy = Math.floor(bgy+image.height); + + } + } + + function renderBackgroundRepeatX(ctx, image, bgp, x, y, w, h){ + + var height = Math.min(image.height,h), + width,bgx; + + + bgp.left = bgp.left-Math.ceil(bgp.left/image.width)*image.width; + + + for (bgx=(x+bgp.left);bgxw+x){ + width = (w+x)-bgx; + }else{ + width = image.width; + } + + renderBackgroundRepeat(ctx,image,bgx,(y+bgp.top),width,height,x,y); + + bgx = Math.floor(bgx+image.width); + + + } + } + + function renderBackground(el,bounds,ctx){ + + // TODO add support for multi background-images + var background_image = getCSS(el, "backgroundImage"), + background_repeat = getCSS(el, "backgroundRepeat").split(",")[0], + image, + bgp, + bgy, + bgw, + bgsx, + bgsy, + bgdx, + bgdy, + bgh, + h, + height, + add; + + // if (typeof background_image !== "undefined" && /^(1|none)$/.test(background_image) === false && /^(-webkit|-moz|linear-gradient|-o-)/.test(background_image)===false){ + + if ( !/data:image\/.*;base64,/i.test(background_image) && !/^(-webkit|-moz|linear-gradient|-o-)/.test(background_image) ) { + background_image = background_image.split(",")[0]; + } + + if ( typeof background_image !== "undefined" && /^(1|none)$/.test( background_image ) === false ) { + background_image = _html2canvas.Util.backgroundImage( background_image ); + image = loadImage( background_image ); + + + bgp = _html2canvas.Util.BackgroundPosition(el, bounds, image); + + // TODO add support for background-origin + if ( image ){ + switch ( background_repeat ) { + + case "repeat-x": + renderBackgroundRepeatX( ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height ); + break; + + case "repeat-y": + renderBackgroundRepeatY( ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height ); + break; + + case "no-repeat": + /* + this.drawBackgroundRepeat( + ctx, + image, + bgp.left+bounds.left, // sx + bgp.top+bounds.top, // sy + Math.min(bounds.width,image.width), + Math.min(bounds.height,image.height), + bounds.left, + bounds.top + );*/ + + + // console.log($(el).css('background-image')); + bgw = bounds.width - bgp.left; + bgh = bounds.height - bgp.top; + bgsx = bgp.left; + bgsy = bgp.top; + bgdx = bgp.left+bounds.left; + bgdy = bgp.top+bounds.top; + + // + // bgw = Math.min(bgw,image.width); + // bgh = Math.min(bgh,image.height); + + if (bgsx<0){ + bgsx = Math.abs(bgsx); + bgdx += bgsx; + bgw = Math.min(bounds.width,image.width-bgsx); + }else{ + bgw = Math.min(bgw,image.width); + bgsx = 0; + } + + if (bgsy<0){ + bgsy = Math.abs(bgsy); + bgdy += bgsy; + // bgh = bgh-bgsy; + bgh = Math.min(bounds.height,image.height-bgsy); + }else{ + bgh = Math.min(bgh,image.height); + bgsy = 0; + } + + + if (bgh>0 && bgw > 0){ + renderImage( + ctx, + image, + bgsx, // source X : 0 + bgsy, // source Y : 1695 + bgw, // source Width : 18 + bgh, // source Height : 1677 + bgdx, // destination X :906 + bgdy, // destination Y : 1020 + bgw, // destination width : 18 + bgh // destination height : 1677 + ); + + } + break; + default: + + + + bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height; + + + for(bgy=(bounds.top+bgp.top);bgyh+bgy){ + height = (h+bgy)-bgy; + }else{ + height = image.height; + } + // console.log(height); + + if (bgy0){ + bgp.top += add; + } + bgy = Math.floor(bgy+image.height)-add; + } + break; + + + } + }else{ + h2clog("html2canvas: Error loading background:" + background_image); + //console.log(images); + } + + } + } + + + + function renderElement(el, parentStack){ + + var bounds = _html2canvas.Util.Bounds(el), + x = bounds.left, + y = bounds.top, + w = bounds.width, + h = bounds.height, + image, + bgcolor = getCSS(el, "backgroundColor"), + cssPosition = getCSS(el, "position"), + zindex, + opacity = getCSS(el, "opacity"), + stack, + stackLength, + borders, + ctx, + bgbounds, + imgSrc, + paddingLeft, + paddingTop, + paddingRight, + paddingBottom; + + if (!parentStack){ + docDim = docSize(); + parentStack = { + opacity: 1 + }; + }else{ + docDim = {}; + } + + + //var zindex = this.formatZ(this.getCSS(el,"zIndex"),cssPosition,parentStack.zIndex,el.parentNode); + + zindex = setZ( getCSS( el, "zIndex"), parentStack.zIndex ); + + + + stack = { + ctx: h2cRenderContext( docDim.width || w , docDim.height || h ), + zIndex: zindex, + opacity: opacity * parentStack.opacity, + cssPosition: cssPosition + }; + + + + // TODO correct overflow for absolute content residing under a static position + + if (parentStack.clip){ + stack.clip = _html2canvas.Util.Extend( {}, parentStack.clip ); + //stack.clip = parentStack.clip; + // stack.clip.height = stack.clip.height - parentStack.borders[2].width; + } + + + if ( options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(el, "overflow")) === true && /(BODY)/i.test(el.nodeName) === false ){ + if (stack.clip){ + stack.clip = clipBounds(stack.clip, bounds); + }else{ + stack.clip = bounds; + } + } + + + stackLength = zindex.children.push(stack); + + ctx = zindex.children[stackLength-1].ctx; + + ctx.setVariable("globalAlpha", stack.opacity); + + // draw element borders + borders = renderBorders(el, ctx, bounds, false); + stack.borders = borders; + + + // let's modify clip area for child elements, so borders dont get overwritten + + /* + if (stack.clip){ + stack.clip.width = stack.clip.width-(borders[1].width); + stack.clip.height = stack.clip.height-(borders[2].width); + } + */ + if (ignoreElementsRegExp.test(el.nodeName) && options.iframeDefault !== "transparent"){ + if (options.iframeDefault === "default"){ + bgcolor = "#efefef"; + }else{ + bgcolor = options.iframeDefault; + } + } + + // draw base element bgcolor + + bgbounds = { + left: x + borders[3].width, + top: y + borders[0].width, + width: w - (borders[1].width + borders[3].width), + height: h - (borders[0].width + borders[2].width) + }; + + //if (this.withinBounds(stack.clip,bgbounds)){ + + if (stack.clip){ + bgbounds = clipBounds(bgbounds, stack.clip); + + //} + + } + + + if (bgbounds.height > 0 && bgbounds.width > 0){ + renderRect( + ctx, + bgbounds.left, + bgbounds.top, + bgbounds.width, + bgbounds.height, + bgcolor + ); + + renderBackground(el, bgbounds, ctx); + } + + switch(el.nodeName){ + case "IMG": + imgSrc = el.getAttribute('src'); + image = loadImage(imgSrc); + if (image){ + + paddingLeft = getCSSInt(el, 'paddingLeft'); + paddingTop = getCSSInt(el, 'paddingTop'); + paddingRight = getCSSInt(el, 'paddingRight'); + paddingBottom = getCSSInt(el, 'paddingBottom'); + + + renderImage( + ctx, + image, + 0, //sx + 0, //sy + image.width, //sw + image.height, //sh + x + paddingLeft + borders[3].width, //dx + y + paddingTop + borders[0].width, // dy + bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw + bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh + ); + + }else{ + h2clog("html2canvas: Error loading :" + imgSrc); + } + break; + case "INPUT": + // TODO add all relevant type's, i.e. HTML5 new stuff + // todo add support for placeholder attribute for browsers which support it + if (/^(text|url|email|submit|button|reset)$/.test(el.type) && el.value.length > 0){ + + renderFormValue(el, bounds, stack); + + + /* + this just doesn't work well enough + + this.newText(el,{ + nodeValue:el.value, + splitText: function(){ + return this; + }, + formValue:true + },stack); + */ + } + break; + case "TEXTAREA": + if (el.value.length > 0){ + renderFormValue(el, bounds, stack); + } + break; + case "SELECT": + if (el.options.length > 0){ + renderFormValue(el, bounds, stack); + } + break; + case "LI": + renderListItem(el, stack, bgbounds); + break; + case "CANVAS": + paddingLeft = getCSSInt(el, 'paddingLeft'); + paddingTop = getCSSInt(el, 'paddingTop'); + paddingRight = getCSSInt(el, 'paddingRight'); + paddingBottom = getCSSInt(el, 'paddingBottom'); + renderImage( + ctx, + el, + 0, //sx + 0, //sy + el.width, //sw + el.height, //sh + x + paddingLeft + borders[3].width, //dx + y + paddingTop + borders[0].width, // dy + bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw + bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh + ); + break; + } + + return zindex.children[stackLength - 1]; + } + + + + function parseElement (el, stack) { + + // skip hidden elements and their children + if (getCSS(el, 'display') !== "none" && getCSS(el, 'visibility') !== "hidden") { + + stack = renderElement(el, stack) || stack; + + ctx = stack.ctx; + + if ( !ignoreElementsRegExp.test( el.nodeName ) ) { + var elementChildren = _html2canvas.Util.Children( el ), + i, + node, + childrenLen; + for (i = 0, childrenLen = elementChildren.length; i < childrenLen; i+=1) { + node = elementChildren[i]; + + if ( node.nodeType === 1 ) { + parseElement(node, stack); + }else if ( node.nodeType === 3 ) { + renderText(el, node, stack); + } + + } + + } + } + } + + stack = renderElement(element, null); + + /* + SVG powered HTML rendering, non-tainted canvas available from FF 11+ onwards + */ + + if ( support.svgRendering ) { + (function( body ){ + var img = new Image(), + size = docSize(), + html = ""; + + function parseDOM( el ) { + var children = _html2canvas.Util.Children( el ), + len = children.length, + attr, + a, + alen, + elm, + i; + for ( i = 0; i < len; i+=1 ) { + elm = children[ i ]; + if ( elm.nodeType === 3 ) { + // Text node + + html += elm.nodeValue.replace(/\/g,">"); + } else if ( elm.nodeType === 1 ) { + // Element + if ( !/^(script|meta|title)$/.test(elm.nodeName.toLowerCase()) ) { + + html += "<" + elm.nodeName.toLowerCase(); + + // add attributes + if ( elm.hasAttributes() ) { + attr = elm.attributes; + alen = attr.length; + for ( a = 0; a < alen; a+=1 ) { + html += " " + attr[ a ].name + '="' + attr[ a ].value + '"'; + } + } + + + html += '>'; + + parseDOM( elm ); + + + html += ""; + } + } + + } + + } + + parseDOM( body ); + img.src = [ + "data:image/svg+xml,", + "", + "", + "", + html.replace(/\#/g,"%23"), + "", + "", + "" + ].join(""); + + + + + img.onload = function() { + stack.svgRender = img; + }; + + })( document.documentElement ); + + } + + + // parse every child element + for (i = 0, children = element.children, childrenLen = children.length; i < childrenLen; i+=1){ + parseElement(children[i], stack); + } + + + stack.backgroundColor = getCSS( document.documentElement, "backgroundColor" ); + + return stack; + +}; + +function h2czContext(zindex) { + return { + zindex: zindex, + children: [] + }; +} +/* + html2canvas v0.33 + Copyright (c) 2011 Niklas von Hertzen. All rights reserved. + http://www.twitter.com/niklasvh + + Released under MIT License +*/ + +_html2canvas.Preload = function( options ) { + + var images = { + numLoaded: 0, // also failed are counted here + numFailed: 0, + numTotal: 0, + cleanupDone: false + }, + pageOrigin, + methods, + i, + count = 0, + element = options.elements[0] || document.body, + doc = element.ownerDocument, + domImages = doc.images, // TODO probably should limit it to images present in the element only + 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; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/ + var origin = link.protocol + link.host; + return (origin === pageOrigin); + } + + function start(){ + h2clog("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")"); + if (!images.firstRun && images.numLoaded >= images.numTotal){ + h2clog("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")"); + + if (typeof options.complete === "function"){ + options.complete(images); + } + + } + } + + // TODO modify proxy to serve images with CORS enabled, where available + function proxyGetImage(url, img, imageObj){ + var callback_name, + scriptUrl = options.proxy, + script; + + link.href = url; + url = link.href; // work around for pages with base href="" set - WARNING: this may change the url + + 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; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) + try { + delete window[callback_name]; // for all browser that support this + } 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 getImages (el) { + + + + // if (!this.ignoreRe.test(el.nodeName)){ + // + + var contents = _html2canvas.Util.Children(el), + i, + contentsLen = contents.length, + background_image, + src, + img, + elNodeType = false; + + for (i = 0; i < contentsLen; i+=1 ){ + // var ignRe = new RegExp("("+this.ignoreElements+")"); + // if (!ignRe.test(element.nodeName)){ + getImages(contents[i]); + // } + } + + // } + try { + elNodeType = el.nodeType; + } catch (ex) { + elNodeType = false; + h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message); + } + + if (elNodeType === 1 || elNodeType === undefined){ + + // opera throws exception on external-content.html + try { + background_image = _html2canvas.Util.getCSS(el, 'backgroundImage'); + }catch(e) { + h2clog("html2canvas: failed to get background-image - Exception: " + e.message); + } + if ( background_image && background_image !== "1" && background_image !== "none" ) { + + // TODO add multi image background support + + if (/^(-webkit|-o|-moz|-ms|linear)-/.test( background_image )) { + // if (background_image.substring(0,7) === "-webkit" || background_image.substring(0,3) === "-o-" || background_image.substring(0,4) === "-moz") { + + img = _html2canvas.Generate.Gradient( background_image, _html2canvas.Util.Bounds( el ) ); + + if ( img !== undefined ){ + images[background_image] = { + img: img, + succeeded: true + }; + images.numTotal++; + images.numLoaded++; + start(); + + } + + } else { + src = _html2canvas.Util.backgroundImage(background_image.match(/data:image\/.*;base64,/i) ? background_image : background_image.split(",")[0]); + methods.loadImage(src); + } + + /* + if (background_image && background_image !== "1" && background_image !== "none" && background_image.substring(0,7) !== "-webkit" && background_image.substring(0,3)!== "-o-" && background_image.substring(0,4) !== "-moz"){ + // TODO add multi image background support + src = _html2canvas.Util.backgroundImage(background_image.split(",")[0]); + methods.loadImage(src); */ + } + } + } + + function setImageLoadHandlers(img, imageObj) { + img.onload = function() { + if ( imageObj.timer !== undefined ) { + // CORS succeeded + window.clearTimeout( imageObj.timer ); + } + + images.numLoaded++; + imageObj.succeeded = true; + img.onerror = img.onload = null; + start(); + }; + img.onerror = function() { + + if (img.crossOrigin === "anonymous") { + // CORS failed + window.clearTimeout( imageObj.timer ); + + // let's try with proxy instead + 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(); + + }; + + // TODO Opera has no load/error event for SVG images + + // Opera ninja onload's cached images + /* + window.setTimeout(function(){ + if ( img.width !== 0 && imageObj.succeeded === undefined ) { + img.onload(); + } + }, 100); // needs a reflow for base64 encoded images? interestingly timeout of 0 doesn't work but 1 does. + */ + } + + + 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 ) { + // attempt to load with CORS + + img.crossOrigin = "anonymous"; + imageObj = images[src] = { + img: img + }; + images.numTotal++; + setImageLoadHandlers(img, imageObj); + img.src = src; + + // work around for https://bugs.webkit.org/show_bug.cgi?id=80028 + img.customComplete = function () { + if (!this.img.complete) { + this.timer = window.setTimeout(this.img.customComplete, 100); + } else { + this.img.onerror(); + } + }.bind(imageObj); + img.customComplete(); + + } 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") { + h2clog("html2canvas: Cleanup because: " + cause); + } else { + h2clog("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) { + // cancel proxy image request + window[img.callbackname] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) + try { + delete window[img.callbackname]; // for all browser that support this + } catch(ex) {} + if (img.script && img.script.parentNode) { + img.script.setAttribute("src", "about:blank"); // try to cancel running request + img.script.parentNode.removeChild(img.script); + } + images.numLoaded++; + images.numFailed++; + h2clog("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal); + } + } + } + + // cancel any pending requests + 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); + } + h2clog('html2canvas: Preload starts: finding background-images'); + images.firstRun = true; + + getImages( element ); + + h2clog('html2canvas: Preload: Finding images'); + // load images + for (i = 0; i < imgLen; i+=1){ + methods.loadImage( domImages[i].getAttribute( "src" ) ); + } + + images.firstRun = false; + h2clog('html2canvas: Preload: Done.'); + if ( images.numTotal === images.numLoaded ) { + start(); + } + + return methods; + +}; + + + +/* + html2canvas v0.33 + Copyright (c) 2011 Niklas von Hertzen. All rights reserved. + http://www.twitter.com/niklasvh + + Released under MIT License +*/ +function h2cRenderContext(width, height) { + var storage = []; + return { + storage: storage, + width: width, + height: height, + fillRect: function () { + storage.push({ + type: "function", + name: "fillRect", + '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 + }); + } + }; +} +/* + html2canvas v0.33 + Copyright (c) 2011 Niklas von Hertzen. All rights reserved. + http://www.twitter.com/niklasvh + + Released under MIT License +*/ +_html2canvas.Renderer = function(parseQueue, options){ + + + var queue = []; + + function sortZ(zStack){ + var subStacks = [], + stackValues = [], + zStackChildren = zStack.children, + s, + i, + stackLen, + zValue, + zLen, + stackChild, + b, + subStackLen; + + + for (s = 0, zLen = zStackChildren.length; s < zLen; s+=1){ + + stackChild = zStackChildren[s]; + + if (stackChild.children && stackChild.children.length > 0){ + subStacks.push(stackChild); + stackValues.push(stackChild.zindex); + }else{ + queue.push(stackChild); + } + + } + + stackValues.sort(function(a, b) { + return a - b; + }); + + for (i = 0, stackLen = stackValues.length; i < stackLen; i+=1){ + zValue = stackValues[i]; + for (b = 0, subStackLen = subStacks.length; b <= subStackLen; b+=1){ + + if (subStacks[b].zindex === zValue){ + stackChild = subStacks.splice(b, 1); + sortZ(stackChild[0]); + break; + + } + } + } + + } + + + sortZ(parseQueue.zIndex); + if ( typeof options._renderer._create !== "function" ) { + throw new Error("Invalid renderer defined"); + } + return options._renderer._create( parseQueue, options, document, queue, _html2canvas ); + +}; +/* + html2canvas v0.33 + Copyright (c) 2011 Niklas von Hertzen. All rights reserved. + http://www.twitter.com/niklasvh + + Released under MIT License +*/ + + +html2canvas = function( elements, opts ) { + + var queue, + canvas, + options = { + // general + logging: false, + elements: elements, + + // preload options + proxy: "http://html2canvas.appspot.com/", + timeout: 0, // no timeout + useCORS: false, // try to load images as CORS (where available), before falling back to proxy + allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true + + // parse options + svgRendering: false, // use svg powered rendering where available (FF11+) + iframeDefault: "default", + ignoreElements: "IFRAME|OBJECT|PARAM", + useOverflow: true, + letterRendering: false, + + // render options + + flashcanvas: undefined, // path to flashcanvas + width: null, + height: null, + taintTest: true, // do a taint test with all images before applying to canvas + renderer: "Canvas" + }, renderer; + + options = _html2canvas.Util.Extend(opts, options); + + if (typeof options.renderer === "string" && _html2canvas.Renderer[options.renderer] !== undefined) { + options._renderer = _html2canvas.Renderer[options.renderer]( options ); + } else if (typeof options.renderer === "function") { + options._renderer = options.renderer( options ); + } else { + throw("Unknown renderer"); + } + + _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 ); + } + + + }; + + // for pages without images, we still want this to be async, i.e. return methods before executing + 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: h2clog + }; +}; + +html2canvas.log = h2clog; // for renderers +html2canvas.Renderer = { + Canvas: undefined // We are assuming this will be used +}; +/* + html2canvas v0.33 + Copyright (c) 2011 Niklas von Hertzen. All rights reserved. + http://www.twitter.com/niklasvh + + Released under MIT License +*/ + + +_html2canvas.Renderer.Canvas = function( options ) { + + options = options || {}; + + var doc = document, + canvas = options.canvas || doc.createElement('canvas'), + usingFlashcanvas = false, + _createCalled = false, + canvasReadyToDraw = false, + methods, + flashMaxSize = 2880; // flash bitmap limited to 2880x2880px // http://stackoverflow.com/questions/2033792/argumenterror-error-2015-invalid-bitmapdata + + + if (canvas.getContext){ + h2clog("html2canvas: Renderer: using canvas renderer"); + canvasReadyToDraw = true; + } else if ( options.flashcanvas !== undefined ){ + usingFlashcanvas = true; + h2clog("html2canvas: Renderer: canvas not available, using flashcanvas"); + var script = doc.createElement("script"); + script.src = options.flashcanvas; + + script.onload = (function(script, func){ + var intervalFunc; + + if (script.onload === undefined) { + // IE lack of support for script onload + + if( script.onreadystatechange !== undefined ) { + + intervalFunc = function() { + if (script.readyState !== "loaded" && script.readyState !== "complete") { + window.setTimeout( intervalFunc, 250 ); + + } else { + // it is loaded + func(); + + } + + }; + + window.setTimeout( intervalFunc, 250 ); + + } else { + h2clog("html2canvas: Renderer: Can't track when flashcanvas is loaded"); + } + + } else { + return func; + } + + })(script, function(){ + + if (typeof window.FlashCanvas !== "undefined") { + h2clog("html2canvas: Renderer: Flashcanvas initialized"); + window.FlashCanvas.initElement( canvas ); + + canvasReadyToDraw = true; + if ( _createCalled !== false ) { + methods._create.apply( null, _createCalled ); + } + } + }); + + doc.body.appendChild( script ); + + } + + methods = { + _create: function( zStack, options, doc, queue, _html2canvas ) { + + if ( !canvasReadyToDraw ) { + _createCalled = arguments; + return canvas; + } + + var ctx = canvas.getContext("2d"), + storageContext, + i, + queueLen, + a, + newCanvas, + bounds, + testCanvas = document.createElement("canvas"), + hasCTX = ( testCanvas.getContext !== undefined ), + storageLen, + renderItem, + testctx = ( hasCTX ) ? testCanvas.getContext("2d") : {}, + safeImages = [], + fstyle; + + canvas.width = canvas.style.width = (!usingFlashcanvas) ? options.width || zStack.ctx.width : Math.min(flashMaxSize, (options.width || zStack.ctx.width) ); + canvas.height = canvas.style.height = (!usingFlashcanvas) ? options.height || zStack.ctx.height : Math.min(flashMaxSize, (options.height || zStack.ctx.height) ); + + fstyle = ctx.fillStyle; + ctx.fillStyle = zStack.backgroundColor; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = fstyle; + + if ( options.svgRendering && zStack.svgRender !== undefined ) { + // TODO: enable async rendering to support this + ctx.drawImage( zStack.svgRender, 0, 0 ); + } else { + for ( i = 0, queueLen = queue.length; i < queueLen; i+=1 ) { + + storageContext = queue.splice(0, 1)[0]; + storageContext.canvasPosition = storageContext.canvasPosition || {}; + + //this.canvasRenderContext(storageContext,parentctx); + + // set common settings for canvas + ctx.textBaseline = "bottom"; + + if (storageContext.clip){ + ctx.save(); + ctx.beginPath(); + // console.log(storageContext); + ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); + ctx.clip(); + + } + + if (storageContext.ctx.storage){ + + for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){ + + renderItem = storageContext.ctx.storage[a]; + + + switch(renderItem.type){ + case "variable": + ctx[renderItem.name] = renderItem['arguments']; + break; + case "function": + if (renderItem.name === "fillRect") { + + if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) { + ctx.fillRect.apply( ctx, renderItem['arguments'] ); + } + }else if(renderItem.name === "fillText") { + if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) { + ctx.fillText.apply( ctx, renderItem['arguments'] ); + } + }else if(renderItem.name === "drawImage") { + + if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ + if ( hasCTX && options.taintTest ) { + if ( safeImages.indexOf( renderItem['arguments'][ 0 ].src ) === -1 ) { + testctx.drawImage( renderItem['arguments'][ 0 ], 0, 0 ); + try { + testctx.getImageData( 0, 0, 1, 1 ); + } catch(e) { + testCanvas = doc.createElement("canvas"); + testctx = testCanvas.getContext("2d"); + continue; + } + + safeImages.push( renderItem['arguments'][ 0 ].src ); + + } + } + ctx.drawImage.apply( ctx, renderItem['arguments'] ); + } + } + + + break; + default: + + } + + } + + } + if (storageContext.clip){ + ctx.restore(); + } + + } + } + + h2clog("html2canvas: Renderer: Canvas renderer done - returning canvas obj"); + + queueLen = options.elements.length; + + if (queueLen === 1) { + if (typeof options.elements[ 0 ] === "object" && options.elements[ 0 ].nodeName !== "BODY" && usingFlashcanvas === false) { + // crop image to the bounds of selected (single) element + bounds = _html2canvas.Util.Bounds( options.elements[ 0 ] ); + newCanvas = doc.createElement('canvas'); + newCanvas.width = bounds.width; + newCanvas.height = 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; + } + } /*else { + // TODO clip and resize multiple elements + + for ( i = 0; i < queueLen; i+=1 ) { + if (options.elements[ i ] instanceof Element) { + + } + + } + } + */ + + + + return canvas; + } + }; + + return methods; + +}; +/* + html2canvas v0.33 + Copyright (c) 2011 Niklas von Hertzen. All rights reserved. + http://www.twitter.com/niklasvh + + Released under MIT License +*/ + + +// WARNING THIS file is outdated, and hasn't been tested in quite a while + +_html2canvas.Renderer.SVG = function( options ) { + + options = options || {}; + + var doc = document, + svgNS = "http://www.w3.org/2000/svg", + svg = doc.createElementNS(svgNS, "svg"), + xlinkNS = "http://www.w3.org/1999/xlink", + defs = doc.createElementNS(svgNS, "defs"), + i, + a, + queueLen, + storageLen, + storageContext, + renderItem, + el, + settings = {}, + text, + fontStyle, + clipId = 0, + methods; + + + methods = { + _create: function( zStack, options, doc, queue, _html2canvas ) { + svg.setAttribute("version", "1.1"); + svg.setAttribute("baseProfile", "full"); + + svg.setAttribute("viewBox", "0 0 " + Math.max(zStack.ctx.width, options.width) + " " + Math.max(zStack.ctx.height, options.height)); + svg.setAttribute("width", Math.max(zStack.ctx.width, options.width) + "px"); + svg.setAttribute("height", Math.max(zStack.ctx.height, options.height) + "px"); + svg.setAttribute("preserveAspectRatio", "none"); + svg.appendChild(defs); + + + + for (i = 0, queueLen = queue.length; i < queueLen; i+=1){ + + storageContext = queue.splice(0, 1)[0]; + storageContext.canvasPosition = storageContext.canvasPosition || {}; + + //this.canvasRenderContext(storageContext,parentctx); + + + /* + if (storageContext.clip){ + ctx.save(); + ctx.beginPath(); + // console.log(storageContext); + ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); + ctx.clip(); + + }*/ + + if (storageContext.ctx.storage){ + + for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){ + + renderItem = storageContext.ctx.storage[a]; + + + + switch(renderItem.type){ + case "variable": + settings[renderItem.name] = renderItem['arguments']; + break; + case "function": + if (renderItem.name === "fillRect") { + + el = doc.createElementNS(svgNS, "rect"); + el.setAttribute("x", renderItem['arguments'][0]); + el.setAttribute("y", renderItem['arguments'][1]); + el.setAttribute("width", renderItem['arguments'][2]); + el.setAttribute("height", renderItem['arguments'][3]); + el.setAttribute("fill", settings.fillStyle); + svg.appendChild(el); + + } else if(renderItem.name === "fillText") { + el = doc.createElementNS(svgNS, "text"); + + fontStyle = settings.font.split(" "); + + el.style.fontVariant = fontStyle.splice(0, 1)[0]; + el.style.fontWeight = fontStyle.splice(0, 1)[0]; + el.style.fontStyle = fontStyle.splice(0, 1)[0]; + el.style.fontSize = fontStyle.splice(0, 1)[0]; + + el.setAttribute("x", renderItem['arguments'][1]); + el.setAttribute("y", renderItem['arguments'][2] - (parseInt(el.style.fontSize, 10) + 3)); + + el.setAttribute("fill", settings.fillStyle); + + + + + // TODO get proper baseline + el.style.dominantBaseline = "text-before-edge"; + el.style.fontFamily = fontStyle.join(" "); + + text = doc.createTextNode(renderItem['arguments'][0]); + el.appendChild(text); + + + svg.appendChild(el); + + + + } else if(renderItem.name === "drawImage") { + + if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ + + // TODO check whether even any clipping is necessary for this particular image + el = doc.createElementNS(svgNS, "clipPath"); + el.setAttribute("id", "clipId" + clipId); + + text = doc.createElementNS(svgNS, "rect"); + text.setAttribute("x", renderItem['arguments'][5] ); + text.setAttribute("y", renderItem['arguments'][6]); + + text.setAttribute("width", renderItem['arguments'][3]); + text.setAttribute("height", renderItem['arguments'][4]); + el.appendChild(text); + defs.appendChild(el); + + el = doc.createElementNS(svgNS, "image"); + el.setAttributeNS(xlinkNS, "xlink:href", renderItem['arguments'][0].src); + el.setAttribute("width", renderItem['arguments'][7]); + el.setAttribute("height", renderItem['arguments'][8]); + el.setAttribute("x", renderItem['arguments'][5]); + el.setAttribute("y", renderItem['arguments'][6]); + el.setAttribute("clip-path", "url(#clipId" + clipId + ")"); + // el.setAttribute("xlink:href", ); + + + el.setAttribute("preserveAspectRatio", "none"); + + svg.appendChild(el); + + + clipId += 1; + /* + ctx.drawImage( + renderItem['arguments'][0], + renderItem['arguments'][1], + renderItem['arguments'][2], + renderItem['arguments'][3], + renderItem['arguments'][4], + renderItem['arguments'][5], + renderItem['arguments'][6], + renderItem['arguments'][7], + renderItem['arguments'][8] + ); + */ + } + } + + + + break; + default: + + } + + } + + } + /* + if (storageContext.clip){ + ctx.restore(); + } + */ + + + + } + + + + + + + + + + + h2clog("html2canvas: Renderer: SVG Renderer done - returning SVG DOM obj"); + + return svg; + } + }; + + return methods; + + +}; +window.html2canvas = html2canvas; +}(window, document)); diff --git a/build/jquery.plugin.html2canvas.js b/build/jquery.plugin.html2canvas.js new file mode 100644 index 0000000..3270bf1 --- /dev/null +++ b/build/jquery.plugin.html2canvas.js @@ -0,0 +1,87 @@ +/** + @license html2canvas v0.33 + Copyright (c) 2011 Niklas von Hertzen. All rights reserved. + http://www.twitter.com/niklasvh + + Released under MIT License + */ +/* + * jQuery helper plugin for examples and tests + */ +(function( $ ){ + $.fn.html2canvas = function(options) { + if (options && options.profile && window.console && window.console.profile) { + console.profile(); + } + var date = new Date(), + html2obj, + $message = null, + timeoutTimer = false, + timer = date.getTime(); + options = options || {}; + + options.onrendered = options.onrendered || function( canvas ) { + var $canvas = $(canvas), + finishTime = new Date(); + + if (options && options.profile && window.console && window.console.profileEnd) { + console.profileEnd(); + } + $canvas.css({ + position: 'absolute', + left: 0, + top: 0 + }).appendTo(document.body); + $canvas.siblings().toggle(); + + $(window).click(function(){ + $canvas.toggle().siblings().toggle(); + throwMessage("Canvas Render " + ($canvas.is(':visible') ? "visible" : "hidden")); + }); + throwMessage('Screenshot created in '+ ((finishTime.getTime()-timer)) + " ms
",4000); + + // test if canvas is read-able + try { + $canvas[0].toDataURL(); + } catch(e) { + if ($canvas[0].nodeName.toLowerCase() === "canvas") { + // TODO, maybe add a bit less offensive way to present this, but still something that can easily be noticed + alert("Canvas is tainted, unable to read data"); + } + } + }; + + html2obj = html2canvas(this, options); + + function throwMessage(msg,duration){ + window.clearTimeout(timeoutTimer); + timeoutTimer = window.setTimeout(function(){ + $message.fadeOut(function(){ + $message.remove(); + $message = null; + }); + },duration || 2000); + if ($message) + $message.remove(); + $message = $('
').html(msg).css({ + margin:0, + padding:10, + background: "#000", + opacity:0.7, + position:"fixed", + top:10, + right:10, + fontFamily: 'Tahoma', + color:'#fff', + fontSize:12, + borderRadius:12, + width:'auto', + height:'auto', + textAlign:'center', + textDecoration:'none', + display:'none' + }).appendTo(document.body).fadeIn(); + html2obj.log(msg); + } + }; +})( jQuery ); From 00c3fb791cde991f1579db8ab58794308e4224a2 Mon Sep 17 00:00:00 2001 From: Bdkzero Date: Sun, 20 May 2012 23:55:23 +0200 Subject: [PATCH 7/8] Readded /build/ directory to .gitignore and removed /build directory --- .gitignore | 1 + build/html2canvas.js | 3007 ---------------------------- build/jquery.plugin.html2canvas.js | 87 - 3 files changed, 1 insertion(+), 3094 deletions(-) delete mode 100644 build/html2canvas.js delete mode 100644 build/jquery.plugin.html2canvas.js diff --git a/.gitignore b/.gitignore index 0da05c1..b439c63 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /tests/cache/ /tests/flashcanvas.html /lib/ +/build/ image.jpg /.project /.settings/ diff --git a/build/html2canvas.js b/build/html2canvas.js deleted file mode 100644 index d96769b..0000000 --- a/build/html2canvas.js +++ /dev/null @@ -1,3007 +0,0 @@ -/** - @license html2canvas v0.33 - Copyright (c) 2011 Niklas von Hertzen. All rights reserved. - http://www.twitter.com/niklasvh - - Released under MIT License - */ -(function(window, document, undefined){ -/* - html2canvas v0.33 - Copyright (c) 2011 Niklas von Hertzen. All rights reserved. - http://www.twitter.com/niklasvh - - Released under MIT License - */ -"use strict"; - -var _html2canvas = {}, -html2canvas; - - -function h2clog(a) { - if (_html2canvas.logging && window.console && window.console.log) { - window.console.log(a); - } -} - -_html2canvas.Util = {}; - -_html2canvas.Util.backgroundImage = function (src) { - - if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) { - return src; - } - - if (src.toLowerCase().substr( 0, 5 ) === 'url("') { - src = src.substr( 5 ); - src = src.substr( 0, src.length - 2 ); - } else { - src = src.substr( 4 ); - src = src.substr( 0, src.length - 1 ); - } - - return src; -}; - -_html2canvas.Util.Bounds = function getBounds (el) { - var clientRect, - bounds = {}; - - if (el.getBoundingClientRect){ - clientRect = el.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; - - // older IE doesn't have width/height, but top/bottom instead - bounds.width = clientRect.width || (clientRect.right - clientRect.left); - bounds.height = clientRect.height || (clientRect.bottom - clientRect.top); - - return bounds; - - } -}; - -_html2canvas.Util.getCSS = function (el, attribute) { - // return $(el).css(attribute); - - var val; - - function toPX( attribute, val ) { - var rsLeft = el.runtimeStyle && el.runtimeStyle[ attribute ], - left, - style = el.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( val ) && /^-?\d/.test( val ) ) { - - // Remember the original values - left = style.left; - - // Put in the new values to get a computed value out - if ( rsLeft ) { - el.runtimeStyle.left = el.currentStyle.left; - } - style.left = attribute === "fontSize" ? "1em" : (val || 0); - val = style.pixelLeft + "px"; - - // Revert the changed values - style.left = left; - if ( rsLeft ) { - el.runtimeStyle.left = rsLeft; - } - - } - - if (!/^(thin|medium|thick)$/i.test( val )) { - return Math.round(parseFloat( val )) + "px"; - } - - return val; - - } - - - if ( window.getComputedStyle ) { - val = document.defaultView.getComputedStyle(el, null)[ attribute ]; - - if ( attribute === "backgroundPosition" ) { - - val = (val.split(",")[0] || "0 0").split(" "); - - val[ 0 ] = ( val[0].indexOf( "%" ) === -1 ) ? toPX( attribute + "X", val[ 0 ] ) : val[ 0 ]; - val[ 1 ] = ( val[1] === undefined ) ? val[0] : val[1]; // IE 9 doesn't return double digit always - val[ 1 ] = ( val[1].indexOf( "%" ) === -1 ) ? toPX( attribute + "Y", val[ 1 ] ) : val[ 1 ]; - } - - } else if ( el.currentStyle ) { - // IE 9> - if (attribute === "backgroundPosition") { - // Older IE uses -x and -y - val = [ toPX( attribute + "X", el.currentStyle[ attribute + "X" ] ), toPX( attribute + "Y", el.currentStyle[ attribute + "Y" ] ) ]; - } else { - - val = toPX( attribute, el.currentStyle[ attribute ] ); - - if (/^(border)/i.test( attribute ) && /^(medium|thin|thick)$/i.test( val )) { - switch (val) { - case "thin": - val = "1px"; - break; - case "medium": - val = "0px"; // this is wrong, it should be 3px but IE uses medium for no border as well.. TODO find a work around - break; - case "thick": - val = "5px"; - break; - } - } - } - - - - } - - - - - return val; - - - -//return $(el).css(attribute); - - -}; - - -_html2canvas.Util.BackgroundPosition = function ( el, bounds, image ) { - // TODO add support for multi image backgrounds - - var bgposition = _html2canvas.Util.getCSS( el, "backgroundPosition" ) , - topPos, - left, - percentage, - val; - - if (bgposition.length === 1){ - val = bgposition; - - bgposition = []; - - bgposition[0] = val; - bgposition[1] = val; - } - - - - if (bgposition[0].toString().indexOf("%") !== -1){ - percentage = (parseFloat(bgposition[0])/100); - left = ((bounds.width * percentage)-(image.width*percentage)); - - }else{ - left = parseInt(bgposition[0],10); - } - - if (bgposition[1].toString().indexOf("%") !== -1){ - - percentage = (parseFloat(bgposition[1])/100); - topPos = ((bounds.height * percentage)-(image.height*percentage)); - }else{ - topPos = parseInt(bgposition[1],10); - } - - - - - return { - top: topPos, - left: left - }; - -}; - -_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(el) { - // $(el).contents() !== el.childNodes, Opera / IE have issues with that - var children; - try { - children = $(el).contents(); - //children = (el.nodeName && el.nodeName.toUpperCase() === "IFRAME") ? el.contentDocument || el.contentWindow.document : el.childNodes ; - - } catch (ex) { - h2clog("html2canvas.Util.Children failed with exception: " + ex.message); - children = []; - } - return children; -}; -/* - html2canvas v0.33 - Copyright (c) 2011 Niklas von Hertzen. All rights reserved. - http://www.twitter.com/niklasvh - - Contributor(s): - Niklas von Hertzen - André Fiedler - - Released under MIT License - */ - -(function(){ - -_html2canvas.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,%\(\)]+)\)$/ -]; - -/* - * TODO: Add IE10 vendor prefix (-ms) support - * TODO: Add W3C gradient (linear-gradient) support - * TODO: Add old Webkit -webkit-gradient(radial, ...) support - * TODO: Maybe some RegExp optimizations are possible ;o) - */ -_html2canvas.Generate.parseGradient = function(css, bounds) { - var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3; - - 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: [] - }; - - // get coordinates - 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){ // center - gradient.x0 = gradient.x1 = bounds.width / 2; - } - if(gradient.y0 === null && gradient.y1 === null){ // center - gradient.y0 = gradient.y1 = bounds.height / 2; - } - - // get colors and stops - 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 { // px - stupid opera - 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], // TODO: Add radial gradient support for older mozilla definitions - x0: 0, - y0: 0, - x1: 0, - y1: 0, - colorStops: [] - }; - - // get coordinates - 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; - } - - // get colors and stops - 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: [] - }; - - // get coordinates - m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/); - - // m2[1] == 0% -> left - // m2[1] == 50% -> center - // m2[1] == 100% -> right - - // m2[2] == 0% -> top - // m2[2] == 50% -> center - // m2[2] == 100% -> bottom - - 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; - } - - // get colors and stops - 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]){ // percentage - 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: [] - }; - - // center - 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; - } - - // size - m2 = m1[3].match(/\w+/); - m3 = m1[4].match(/[a-z-]*/); - if(m2 && m3){ - switch(m3[0]){ - case 'farthest-corner': - case 'cover': // is equivalent to farthest-corner - case '': // mozilla removes "cover" from definition :( - var tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); - var tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); - var br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); - var 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': - var tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); - var tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); - var br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); - var 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 { // ellipse - - 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': // is equivalent to closest-side - if(m2[0] === 'circle'){ - gradient.rx = gradient.ry = Math.min( - gradient.cx, - gradient.cy, - gradient.x1 - gradient.cx, - gradient.y1 - gradient.cy - ); - } else { // ellipse - - 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; - - // TODO: add support for "30px 40px" sizes (webkit only) - } - } - - // color stops - 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 { // px - stupid opera - stop /= bounds.width; - } - } else { - stop = i * step; - } - gradient.colorStops.push({ - color: m3[1], - stop: stop - }); - } - } - break; - } - } - - return gradient; -}; - -_html2canvas.Generate.Gradient = function(src, bounds) { - var canvas = document.createElement('canvas'), - ctx = canvas.getContext('2d'), - gradient, grad, i, len, img; - - canvas.width = bounds.width; - canvas.height = bounds.height; - - // TODO: add support for multi defined background gradients (like radial gradient example in background.html) - gradient = _html2canvas.Generate.parseGradient(src, bounds); - - img = new Image(); - - if(gradient){ - if(gradient.type === 'linear'){ - grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1); - - for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { - try { - grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); - } - catch(e) { - h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]); - } - } - - ctx.fillStyle = grad; - ctx.fillRect(0, 0, bounds.width, bounds.height); - - img.src = canvas.toDataURL(); - } else if(gradient.type === 'circle'){ - - grad = ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx); - - for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { - try { - grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); - } - catch(e) { - h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]); - } - } - - ctx.fillStyle = grad; - ctx.fillRect(0, 0, bounds.width, bounds.height); - - img.src = canvas.toDataURL(); - } else if(gradient.type === 'ellipse'){ - - // draw circle - var canvasRadial = document.createElement('canvas'), - ctxRadial = canvasRadial.getContext('2d'), - ri = Math.max(gradient.rx, gradient.ry), - di = ri * 2, imgRadial; - - canvasRadial.width = canvasRadial.height = di; - - grad = ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri); - - for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { - try { - grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); - } - catch(e) { - h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]); - } - } - - ctxRadial.fillStyle = grad; - ctxRadial.fillRect(0, 0, di, di); - - ctx.fillStyle = gradient.colorStops[i - 1].color; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - imgRadial = new Image(); - imgRadial.onload = function() { // wait until the image is filled - - // transform circle to ellipse - ctx.drawImage(imgRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry); - - img.src = canvas.toDataURL(); - - } - imgRadial.src = canvasRadial.toDataURL(); - } - } - - return img; -}; - -_html2canvas.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; -}; - -_html2canvas.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; - -}; - -})(); -/* - html2canvas v0.33 - Copyright (c) 2011 Niklas von Hertzen. All rights reserved. - http://www.twitter.com/niklasvh - - Released under MIT License -*/ - -/* - * New function for traversing elements - */ - -_html2canvas.Parse = function ( images, options ) { - window.scroll(0,0); - - var support = { - rangeBounds: false, - svgRendering: options.svgRendering && (function( ){ - var img = new Image(), - canvas = document.createElement("canvas"), - ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d"); - if (ctx === false) { - // browser doesn't support canvas, good luck supporting SVG on canvas - return false; - } - canvas.width = canvas.height = 10; - img.src = [ - "data:image/svg+xml,", - "", - "", - "
", - "sup", - "
", - "
", - "
" - ].join(""); - try { - ctx.drawImage(img, 0, 0); - canvas.toDataURL(); - } catch(e) { - return false; - } - h2clog('html2canvas: Parse: SVG powered rendering available'); - return true; - - })() - }, - element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default - needReorder = false, - numDraws = 0, - fontData = {}, - doc = element.ownerDocument, - ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"), - body = doc.body, - r, - testElement, - rangeBounds, - rangeHeight, - stack, - ctx, - docDim, - i, - children, - childrenLen; - - - function docSize(){ - - return { - width: 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) - ), - height: 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) - ) - }; - - } - - images = images || {}; - - // Test whether we can use ranges to measure bounding boxes - // Opera doesn't provide valid bounds.height/bottom even though it supports the method. - - - if (doc.createRange) { - r = doc.createRange(); - //this.support.rangeBounds = new Boolean(r.getBoundingClientRect); - if (r.getBoundingClientRect){ - testElement = doc.createElement('boundtest'); - testElement.style.height = "123px"; - testElement.style.display = "block"; - body.appendChild(testElement); - - r.selectNode(testElement); - rangeBounds = r.getBoundingClientRect(); - rangeHeight = rangeBounds.height; - - if (rangeHeight === 123) { - support.rangeBounds = true; - } - body.removeChild(testElement); - - - } - - } - - - /* - var rootStack = new this.storageContext($(document).width(),$(document).height()); - rootStack.opacity = this.getCSS(this.element,"opacity"); - var stack = this.newElement(this.element,rootStack); - - - this.parseElement(this.element,stack); - */ - - - - - var getCSS = _html2canvas.Util.getCSS; - function getCSSInt(element, attribute) { - var val = parseInt(getCSS(element, attribute), 10); - return (isNaN(val)) ? 0 : val; // borders in old IE are throwing 'medium' for demo.html - } - - // Drawing a rectangle - function renderRect (ctx, x, y, w, h, bgcolor) { - if (bgcolor !=="transparent"){ - ctx.setVariable("fillStyle", bgcolor); - ctx.fillRect (x, y, w, h); - numDraws+=1; - } - } - - - function textTransform (text, transform) { - switch(transform){ - case "lowercase": - return text.toLowerCase(); - - case "capitalize": - return text.replace( /(^|\s|:|-|\(|\))([a-z])/g , function (m, p1, p2) { - if (m.length > 0) { - return p1 + p2.toUpperCase(); - } - } ); - - case "uppercase": - return text.toUpperCase(); - - default: - return text; - - } - - } - - function trimText (text) { - return text.replace(/^\s*/g, "").replace(/\s*$/g, ""); - } - - function fontMetrics (font, fontSize) { - - if (fontData[font + "-" + fontSize] !== undefined) { - return fontData[font + "-" + fontSize]; - } - - - var container = doc.createElement('div'), - img = doc.createElement('img'), - span = doc.createElement('span'), - baseline, - middle, - metricsObj; - - - container.style.visibility = "hidden"; - container.style.fontFamily = font; - container.style.fontSize = fontSize; - container.style.margin = 0; - container.style.padding = 0; - - body.appendChild(container); - - - - // http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever (handtinywhite.gif) - 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('Hidden Text')); - container.appendChild(span); - container.appendChild(img); - baseline = (img.offsetTop - span.offsetTop) + 1; - - container.removeChild(span); - container.appendChild(doc.createTextNode('Hidden Text')); - - 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; - - body.removeChild(container); - - return metricsObj; - - } - - - function drawText(currentText, x, y, ctx){ - if (trimText(currentText).length>0) { - ctx.fillText(currentText,x,y); - numDraws+=1; - } - } - - - function renderText(el, textNode, stack) { - var ctx = stack.ctx, - family = getCSS(el, "fontFamily"), - size = getCSS(el, "fontSize"), - color = getCSS(el, "color"), - text_decoration = getCSS(el, "textDecoration"), - text_align = getCSS(el, "textAlign"), - letter_spacing = getCSS(el, "letterSpacing"), - bounds, - text, - metrics, - renderList, - listLen, - bold = getCSS(el, "fontWeight"), - font_style = getCSS(el, "fontStyle"), - font_variant = getCSS(el, "fontVariant"), - align = false, - newTextNode, - textValue, - textOffset = 0, - oldTextNode, - c, - range, - parent, - wrapElement, - backupText; - - // apply text-transform:ation to the text - - - - textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform")); - text = trimText(textNode.nodeValue); - - if (text.length>0){ - - if (text_decoration !== "none"){ - metrics = fontMetrics(family, size); - } - - text_align = text_align.replace(["-webkit-auto"],["auto"]); - - if (options.letterRendering === false && /^(left|right|justify|auto)$/.test(text_align) && /^(normal|none)$/.test(letter_spacing)){ - // this.setContextVariable(ctx,"textAlign",text_align); - renderList = textNode.nodeValue.split(/(\b| )/); - - }else{ - // this.setContextVariable(ctx,"textAlign","left"); - renderList = textNode.nodeValue.split(""); - } - - switch(parseInt(bold, 10)){ - case 401: - bold = "bold"; - break; - case 400: - bold = "normal"; - break; - } - - ctx.setVariable("fillStyle", color); - - /* - need to be defined in the order as defined in http://www.w3.org/TR/CSS21/fonts.html#font-shorthand - to properly work in Firefox - */ - ctx.setVariable("font", font_style+ " " + font_variant + " " + bold + " " + size + " " + family); - - if (align){ - ctx.setVariable("textAlign", "right"); - }else{ - ctx.setVariable("textAlign", "left"); - } - - - /* - if (stack.clip){ - ctx.rect (stack.clip.left, stack.clip.top, stack.clip.width, stack.clip.height); - ctx.clip(); - } - */ - - - oldTextNode = textNode; - - - for ( c=0, listLen = renderList.length; c < listLen; c+=1 ) { - textValue = null; - - - - if (support.rangeBounds){ - // getBoundingClientRect is supported for ranges - if (text_decoration !== "none" || trimText(renderList[c]).length !== 0) { - textValue = renderList[c]; - if (doc.createRange){ - range = doc.createRange(); - - range.setStart(textNode, textOffset); - range.setEnd(textNode, textOffset + textValue.length); - }else{ - // TODO add IE support - range = body.createTextRange(); - } - - if (range.getBoundingClientRect()) { - bounds = range.getBoundingClientRect(); - }else{ - bounds = {}; - } - - } - }else{ - // it isn't supported, so let's wrap it inside an element instead and get the bounds there - - // IE 9 bug - if (typeof oldTextNode.nodeValue !== "string" ){ - continue; - } - - newTextNode = oldTextNode.splitText(renderList[c].length); - - parent = oldTextNode.parentNode; - wrapElement = doc.createElement('wrapper'); - backupText = oldTextNode.cloneNode(true); - - wrapElement.appendChild(oldTextNode.cloneNode(true)); - parent.replaceChild(wrapElement, oldTextNode); - - bounds = _html2canvas.Util.Bounds(wrapElement); - - textValue = oldTextNode.nodeValue; - - oldTextNode = newTextNode; - parent.replaceChild(backupText, wrapElement); - - - } - - if (textValue !== null){ - drawText(textValue, bounds.left, bounds.bottom, ctx); - } - - switch(text_decoration) { - case "underline": - // Draws a line at the baseline of the font - // TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size - renderRect(ctx, bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, color); - break; - case "overline": - renderRect(ctx, bounds.left, bounds.top, bounds.width, 1, color); - break; - case "line-through": - // TODO try and find exact position for line-through - renderRect(ctx, bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, color); - break; - - } - - - - - - textOffset += renderList[c].length; - - } - - - - } - - } - - function listPosition (element, val) { - var boundElement = doc.createElement( "boundelement" ), - type, - bounds; - - boundElement.style.display = "inline"; - //boundElement.style.width = "1px"; - //boundElement.style.height = "1px"; - - type = element.style.listStyleType; - element.style.listStyleType = "none"; - - boundElement.appendChild( doc.createTextNode( val ) ); - - - element.insertBefore(boundElement, element.firstChild); - - - bounds = _html2canvas.Util.Bounds( boundElement ); - element.removeChild( boundElement ); - element.style.listStyleType = type; - return bounds; - - } - - - function renderListItem(element, stack, elBounds) { - - - var position = getCSS(element, "listStylePosition"), - x, - y, - type = getCSS(element, "listStyleType"), - currentIndex, - text, - listBounds, - bold = getCSS(element, "fontWeight"); - - if (/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(type)) { - - // TODO remove jQuery dependency - currentIndex = $(element).index()+1; - - switch(type){ - case "decimal": - text = currentIndex; - break; - case "decimal-leading-zero": - if (currentIndex.toString().length === 1){ - text = currentIndex = "0" + currentIndex.toString(); - }else{ - text = 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; - } - - - text += ". "; - listBounds = listPosition(element, text); - - - - switch(bold){ - case 401: - bold = "bold"; - break; - case 400: - bold = "normal"; - break; - } - - - - - ctx.setVariable( "fillStyle", getCSS(element, "color") ); - ctx.setVariable( "font", getCSS(element, "fontVariant") + " " + bold + " " + getCSS(element, "fontStyle") + " " + getCSS(element, "fontSize") + " " + getCSS(element, "fontFamily") ); - - - if ( position === "inside" ) { - ctx.setVariable("textAlign", "left"); - // this.setFont(stack.ctx, element, false); - x = elBounds.left; - - }else{ - return; - /* - TODO really need to figure out some more accurate way to try and find the position. - as defined in http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-position, it does not even have a specified "correct" position, so each browser - may display it whatever way it feels like. - "The position of the list-item marker adjacent to floats is undefined in CSS 2.1. CSS 2.1 does not specify the precise location of the marker box or its position in the painting order" - - ctx.setVariable("textAlign", "right"); - // this.setFont(stack.ctx, element, true); - x = elBounds.left - 10; - */ - } - - y = listBounds.bottom; - - drawText(text, x, y, ctx); - - - } - - - } - - function loadImage (src){ - var img = images[src]; - if (img && img.succeeded === true) { - return img.img; - } else { - return 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(zIndex, parentZ){ - // TODO fix static elements overlapping relative/absolute elements under same stack, if they are defined after them - var newContext; - if (!parentZ){ - newContext = h2czContext(0); - return newContext; - } - - if (zIndex !== "auto"){ - needReorder = true; - newContext = h2czContext(zIndex); - parentZ.children.push(newContext); - return newContext; - - } - - return parentZ; - - } - - function renderBorders(el, ctx, bounds, clip){ - - /* - * TODO add support for different border-style's than solid - */ - - var x = bounds.left, - y = bounds.top, - w = bounds.width, - h = bounds.height, - borderSide, - borderData, - bx, - by, - bw, - bh, - borderBounds, - borders = (function(el){ - var borders = [], - sides = ["Top","Right","Bottom","Left"], - s; - - for (s = 0; s < 4; s+=1){ - borders.push({ - width: getCSSInt(el, 'border' + sides[s] + 'Width'), - color: getCSS(el, 'border' + sides[s] + 'Color') - }); - } - - return borders; - - }(el)); - - - for (borderSide = 0; borderSide < 4; borderSide+=1){ - borderData = borders[borderSide]; - - if (borderData.width>0){ - bx = x; - by = y; - bw = w; - bh = h - (borders[2].width); - - switch(borderSide){ - case 0: - // top border - bh = borders[0].width; - break; - case 1: - // right border - bx = x + w - (borders[1].width); - bw = borders[1].width; - break; - case 2: - // bottom border - by = (by + h) - (borders[2].width); - bh = borders[2].width; - break; - case 3: - // left border - bw = borders[3].width; - break; - } - - borderBounds = { - left:bx, - top:by, - width: bw, - height:bh - }; - - if (clip){ - borderBounds = clipBounds(borderBounds, clip); - } - - - if (borderBounds.width>0 && borderBounds.height>0){ - renderRect(ctx, bx, by, borderBounds.width, borderBounds.height, borderData.color); - } - - - } - } - - return borders; - - } - - - function renderFormValue (el, bounds, stack){ - - var valueWrap = doc.createElement('valuewrap'), - cssArr = ['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'], - i, - textValue, - textNode, - arrLen, - style; - - for (i = 0, arrLen = cssArr.length; i < arrLen; i+=1){ - style = cssArr[i]; - - try { - valueWrap.style[style] = getCSS(el, style); - } catch( e ) { - // Older IE has issues with "border" - h2clog("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"; - - if (el.nodeName === "SELECT"){ - // TODO increase accuracy of text position - textValue = el.options[el.selectedIndex].text; - } else{ - textValue = el.value; - } - textNode = doc.createTextNode(textValue); - - valueWrap.appendChild(textNode); - body.appendChild(valueWrap); - - - renderText(el, textNode, stack); - body.removeChild(valueWrap); - - - - } - - - - - - function renderImage (ctx, image, sx, sy, sw, sh, dx, dy, dw, dh) { - ctx.drawImage( - image, - sx, //sx - sy, //sy - sw, //sw - sh, //sh - dx, //dx - dy, // dy - dw, //dw - dh //dh - ); - numDraws+=1; - - } - - - function renderBackgroundRepeat (ctx, image, x, y, width, height, elx, ely){ - var sourceX = 0, - sourceY=0; - if (elx-x>0){ - sourceX = elx-x; - } - - if (ely-y>0){ - sourceY = ely-y; - } - - renderImage( - ctx, - image, - sourceX, // source X - sourceY, // source Y - width-sourceX, // source Width - height-sourceY, // source Height - x+sourceX, // destination X - y+sourceY, // destination Y - width-sourceX, // destination width - height-sourceY // destination height - ); - } - - - function renderBackgroundRepeatY (ctx, image, bgp, x, y, w, h){ - - var height, - width = Math.min(image.width,w),bgy; - - bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height; - - - for(bgy=(y+bgp.top);bgyh+y){ - height = (h+y)-bgy; - }else{ - height = image.height; - } - renderBackgroundRepeat(ctx,image,x+bgp.left,bgy,width,height,x,y); - - bgy = Math.floor(bgy+image.height); - - } - } - - function renderBackgroundRepeatX(ctx, image, bgp, x, y, w, h){ - - var height = Math.min(image.height,h), - width,bgx; - - - bgp.left = bgp.left-Math.ceil(bgp.left/image.width)*image.width; - - - for (bgx=(x+bgp.left);bgxw+x){ - width = (w+x)-bgx; - }else{ - width = image.width; - } - - renderBackgroundRepeat(ctx,image,bgx,(y+bgp.top),width,height,x,y); - - bgx = Math.floor(bgx+image.width); - - - } - } - - function renderBackground(el,bounds,ctx){ - - // TODO add support for multi background-images - var background_image = getCSS(el, "backgroundImage"), - background_repeat = getCSS(el, "backgroundRepeat").split(",")[0], - image, - bgp, - bgy, - bgw, - bgsx, - bgsy, - bgdx, - bgdy, - bgh, - h, - height, - add; - - // if (typeof background_image !== "undefined" && /^(1|none)$/.test(background_image) === false && /^(-webkit|-moz|linear-gradient|-o-)/.test(background_image)===false){ - - if ( !/data:image\/.*;base64,/i.test(background_image) && !/^(-webkit|-moz|linear-gradient|-o-)/.test(background_image) ) { - background_image = background_image.split(",")[0]; - } - - if ( typeof background_image !== "undefined" && /^(1|none)$/.test( background_image ) === false ) { - background_image = _html2canvas.Util.backgroundImage( background_image ); - image = loadImage( background_image ); - - - bgp = _html2canvas.Util.BackgroundPosition(el, bounds, image); - - // TODO add support for background-origin - if ( image ){ - switch ( background_repeat ) { - - case "repeat-x": - renderBackgroundRepeatX( ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height ); - break; - - case "repeat-y": - renderBackgroundRepeatY( ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height ); - break; - - case "no-repeat": - /* - this.drawBackgroundRepeat( - ctx, - image, - bgp.left+bounds.left, // sx - bgp.top+bounds.top, // sy - Math.min(bounds.width,image.width), - Math.min(bounds.height,image.height), - bounds.left, - bounds.top - );*/ - - - // console.log($(el).css('background-image')); - bgw = bounds.width - bgp.left; - bgh = bounds.height - bgp.top; - bgsx = bgp.left; - bgsy = bgp.top; - bgdx = bgp.left+bounds.left; - bgdy = bgp.top+bounds.top; - - // - // bgw = Math.min(bgw,image.width); - // bgh = Math.min(bgh,image.height); - - if (bgsx<0){ - bgsx = Math.abs(bgsx); - bgdx += bgsx; - bgw = Math.min(bounds.width,image.width-bgsx); - }else{ - bgw = Math.min(bgw,image.width); - bgsx = 0; - } - - if (bgsy<0){ - bgsy = Math.abs(bgsy); - bgdy += bgsy; - // bgh = bgh-bgsy; - bgh = Math.min(bounds.height,image.height-bgsy); - }else{ - bgh = Math.min(bgh,image.height); - bgsy = 0; - } - - - if (bgh>0 && bgw > 0){ - renderImage( - ctx, - image, - bgsx, // source X : 0 - bgsy, // source Y : 1695 - bgw, // source Width : 18 - bgh, // source Height : 1677 - bgdx, // destination X :906 - bgdy, // destination Y : 1020 - bgw, // destination width : 18 - bgh // destination height : 1677 - ); - - } - break; - default: - - - - bgp.top = bgp.top-Math.ceil(bgp.top/image.height)*image.height; - - - for(bgy=(bounds.top+bgp.top);bgyh+bgy){ - height = (h+bgy)-bgy; - }else{ - height = image.height; - } - // console.log(height); - - if (bgy0){ - bgp.top += add; - } - bgy = Math.floor(bgy+image.height)-add; - } - break; - - - } - }else{ - h2clog("html2canvas: Error loading background:" + background_image); - //console.log(images); - } - - } - } - - - - function renderElement(el, parentStack){ - - var bounds = _html2canvas.Util.Bounds(el), - x = bounds.left, - y = bounds.top, - w = bounds.width, - h = bounds.height, - image, - bgcolor = getCSS(el, "backgroundColor"), - cssPosition = getCSS(el, "position"), - zindex, - opacity = getCSS(el, "opacity"), - stack, - stackLength, - borders, - ctx, - bgbounds, - imgSrc, - paddingLeft, - paddingTop, - paddingRight, - paddingBottom; - - if (!parentStack){ - docDim = docSize(); - parentStack = { - opacity: 1 - }; - }else{ - docDim = {}; - } - - - //var zindex = this.formatZ(this.getCSS(el,"zIndex"),cssPosition,parentStack.zIndex,el.parentNode); - - zindex = setZ( getCSS( el, "zIndex"), parentStack.zIndex ); - - - - stack = { - ctx: h2cRenderContext( docDim.width || w , docDim.height || h ), - zIndex: zindex, - opacity: opacity * parentStack.opacity, - cssPosition: cssPosition - }; - - - - // TODO correct overflow for absolute content residing under a static position - - if (parentStack.clip){ - stack.clip = _html2canvas.Util.Extend( {}, parentStack.clip ); - //stack.clip = parentStack.clip; - // stack.clip.height = stack.clip.height - parentStack.borders[2].width; - } - - - if ( options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(el, "overflow")) === true && /(BODY)/i.test(el.nodeName) === false ){ - if (stack.clip){ - stack.clip = clipBounds(stack.clip, bounds); - }else{ - stack.clip = bounds; - } - } - - - stackLength = zindex.children.push(stack); - - ctx = zindex.children[stackLength-1].ctx; - - ctx.setVariable("globalAlpha", stack.opacity); - - // draw element borders - borders = renderBorders(el, ctx, bounds, false); - stack.borders = borders; - - - // let's modify clip area for child elements, so borders dont get overwritten - - /* - if (stack.clip){ - stack.clip.width = stack.clip.width-(borders[1].width); - stack.clip.height = stack.clip.height-(borders[2].width); - } - */ - if (ignoreElementsRegExp.test(el.nodeName) && options.iframeDefault !== "transparent"){ - if (options.iframeDefault === "default"){ - bgcolor = "#efefef"; - }else{ - bgcolor = options.iframeDefault; - } - } - - // draw base element bgcolor - - bgbounds = { - left: x + borders[3].width, - top: y + borders[0].width, - width: w - (borders[1].width + borders[3].width), - height: h - (borders[0].width + borders[2].width) - }; - - //if (this.withinBounds(stack.clip,bgbounds)){ - - if (stack.clip){ - bgbounds = clipBounds(bgbounds, stack.clip); - - //} - - } - - - if (bgbounds.height > 0 && bgbounds.width > 0){ - renderRect( - ctx, - bgbounds.left, - bgbounds.top, - bgbounds.width, - bgbounds.height, - bgcolor - ); - - renderBackground(el, bgbounds, ctx); - } - - switch(el.nodeName){ - case "IMG": - imgSrc = el.getAttribute('src'); - image = loadImage(imgSrc); - if (image){ - - paddingLeft = getCSSInt(el, 'paddingLeft'); - paddingTop = getCSSInt(el, 'paddingTop'); - paddingRight = getCSSInt(el, 'paddingRight'); - paddingBottom = getCSSInt(el, 'paddingBottom'); - - - renderImage( - ctx, - image, - 0, //sx - 0, //sy - image.width, //sw - image.height, //sh - x + paddingLeft + borders[3].width, //dx - y + paddingTop + borders[0].width, // dy - bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw - bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh - ); - - }else{ - h2clog("html2canvas: Error loading :" + imgSrc); - } - break; - case "INPUT": - // TODO add all relevant type's, i.e. HTML5 new stuff - // todo add support for placeholder attribute for browsers which support it - if (/^(text|url|email|submit|button|reset)$/.test(el.type) && el.value.length > 0){ - - renderFormValue(el, bounds, stack); - - - /* - this just doesn't work well enough - - this.newText(el,{ - nodeValue:el.value, - splitText: function(){ - return this; - }, - formValue:true - },stack); - */ - } - break; - case "TEXTAREA": - if (el.value.length > 0){ - renderFormValue(el, bounds, stack); - } - break; - case "SELECT": - if (el.options.length > 0){ - renderFormValue(el, bounds, stack); - } - break; - case "LI": - renderListItem(el, stack, bgbounds); - break; - case "CANVAS": - paddingLeft = getCSSInt(el, 'paddingLeft'); - paddingTop = getCSSInt(el, 'paddingTop'); - paddingRight = getCSSInt(el, 'paddingRight'); - paddingBottom = getCSSInt(el, 'paddingBottom'); - renderImage( - ctx, - el, - 0, //sx - 0, //sy - el.width, //sw - el.height, //sh - x + paddingLeft + borders[3].width, //dx - y + paddingTop + borders[0].width, // dy - bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight), //dw - bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom) //dh - ); - break; - } - - return zindex.children[stackLength - 1]; - } - - - - function parseElement (el, stack) { - - // skip hidden elements and their children - if (getCSS(el, 'display') !== "none" && getCSS(el, 'visibility') !== "hidden") { - - stack = renderElement(el, stack) || stack; - - ctx = stack.ctx; - - if ( !ignoreElementsRegExp.test( el.nodeName ) ) { - var elementChildren = _html2canvas.Util.Children( el ), - i, - node, - childrenLen; - for (i = 0, childrenLen = elementChildren.length; i < childrenLen; i+=1) { - node = elementChildren[i]; - - if ( node.nodeType === 1 ) { - parseElement(node, stack); - }else if ( node.nodeType === 3 ) { - renderText(el, node, stack); - } - - } - - } - } - } - - stack = renderElement(element, null); - - /* - SVG powered HTML rendering, non-tainted canvas available from FF 11+ onwards - */ - - if ( support.svgRendering ) { - (function( body ){ - var img = new Image(), - size = docSize(), - html = ""; - - function parseDOM( el ) { - var children = _html2canvas.Util.Children( el ), - len = children.length, - attr, - a, - alen, - elm, - i; - for ( i = 0; i < len; i+=1 ) { - elm = children[ i ]; - if ( elm.nodeType === 3 ) { - // Text node - - html += elm.nodeValue.replace(/\/g,">"); - } else if ( elm.nodeType === 1 ) { - // Element - if ( !/^(script|meta|title)$/.test(elm.nodeName.toLowerCase()) ) { - - html += "<" + elm.nodeName.toLowerCase(); - - // add attributes - if ( elm.hasAttributes() ) { - attr = elm.attributes; - alen = attr.length; - for ( a = 0; a < alen; a+=1 ) { - html += " " + attr[ a ].name + '="' + attr[ a ].value + '"'; - } - } - - - html += '>'; - - parseDOM( elm ); - - - html += ""; - } - } - - } - - } - - parseDOM( body ); - img.src = [ - "data:image/svg+xml,", - "", - "", - "", - html.replace(/\#/g,"%23"), - "", - "", - "" - ].join(""); - - - - - img.onload = function() { - stack.svgRender = img; - }; - - })( document.documentElement ); - - } - - - // parse every child element - for (i = 0, children = element.children, childrenLen = children.length; i < childrenLen; i+=1){ - parseElement(children[i], stack); - } - - - stack.backgroundColor = getCSS( document.documentElement, "backgroundColor" ); - - return stack; - -}; - -function h2czContext(zindex) { - return { - zindex: zindex, - children: [] - }; -} -/* - html2canvas v0.33 - Copyright (c) 2011 Niklas von Hertzen. All rights reserved. - http://www.twitter.com/niklasvh - - Released under MIT License -*/ - -_html2canvas.Preload = function( options ) { - - var images = { - numLoaded: 0, // also failed are counted here - numFailed: 0, - numTotal: 0, - cleanupDone: false - }, - pageOrigin, - methods, - i, - count = 0, - element = options.elements[0] || document.body, - doc = element.ownerDocument, - domImages = doc.images, // TODO probably should limit it to images present in the element only - 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; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/ - var origin = link.protocol + link.host; - return (origin === pageOrigin); - } - - function start(){ - h2clog("html2canvas: start: images: " + images.numLoaded + " / " + images.numTotal + " (failed: " + images.numFailed + ")"); - if (!images.firstRun && images.numLoaded >= images.numTotal){ - h2clog("Finished loading images: # " + images.numTotal + " (failed: " + images.numFailed + ")"); - - if (typeof options.complete === "function"){ - options.complete(images); - } - - } - } - - // TODO modify proxy to serve images with CORS enabled, where available - function proxyGetImage(url, img, imageObj){ - var callback_name, - scriptUrl = options.proxy, - script; - - link.href = url; - url = link.href; // work around for pages with base href="" set - WARNING: this may change the url - - 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; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) - try { - delete window[callback_name]; // for all browser that support this - } 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 getImages (el) { - - - - // if (!this.ignoreRe.test(el.nodeName)){ - // - - var contents = _html2canvas.Util.Children(el), - i, - contentsLen = contents.length, - background_image, - src, - img, - elNodeType = false; - - for (i = 0; i < contentsLen; i+=1 ){ - // var ignRe = new RegExp("("+this.ignoreElements+")"); - // if (!ignRe.test(element.nodeName)){ - getImages(contents[i]); - // } - } - - // } - try { - elNodeType = el.nodeType; - } catch (ex) { - elNodeType = false; - h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message); - } - - if (elNodeType === 1 || elNodeType === undefined){ - - // opera throws exception on external-content.html - try { - background_image = _html2canvas.Util.getCSS(el, 'backgroundImage'); - }catch(e) { - h2clog("html2canvas: failed to get background-image - Exception: " + e.message); - } - if ( background_image && background_image !== "1" && background_image !== "none" ) { - - // TODO add multi image background support - - if (/^(-webkit|-o|-moz|-ms|linear)-/.test( background_image )) { - // if (background_image.substring(0,7) === "-webkit" || background_image.substring(0,3) === "-o-" || background_image.substring(0,4) === "-moz") { - - img = _html2canvas.Generate.Gradient( background_image, _html2canvas.Util.Bounds( el ) ); - - if ( img !== undefined ){ - images[background_image] = { - img: img, - succeeded: true - }; - images.numTotal++; - images.numLoaded++; - start(); - - } - - } else { - src = _html2canvas.Util.backgroundImage(background_image.match(/data:image\/.*;base64,/i) ? background_image : background_image.split(",")[0]); - methods.loadImage(src); - } - - /* - if (background_image && background_image !== "1" && background_image !== "none" && background_image.substring(0,7) !== "-webkit" && background_image.substring(0,3)!== "-o-" && background_image.substring(0,4) !== "-moz"){ - // TODO add multi image background support - src = _html2canvas.Util.backgroundImage(background_image.split(",")[0]); - methods.loadImage(src); */ - } - } - } - - function setImageLoadHandlers(img, imageObj) { - img.onload = function() { - if ( imageObj.timer !== undefined ) { - // CORS succeeded - window.clearTimeout( imageObj.timer ); - } - - images.numLoaded++; - imageObj.succeeded = true; - img.onerror = img.onload = null; - start(); - }; - img.onerror = function() { - - if (img.crossOrigin === "anonymous") { - // CORS failed - window.clearTimeout( imageObj.timer ); - - // let's try with proxy instead - 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(); - - }; - - // TODO Opera has no load/error event for SVG images - - // Opera ninja onload's cached images - /* - window.setTimeout(function(){ - if ( img.width !== 0 && imageObj.succeeded === undefined ) { - img.onload(); - } - }, 100); // needs a reflow for base64 encoded images? interestingly timeout of 0 doesn't work but 1 does. - */ - } - - - 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 ) { - // attempt to load with CORS - - img.crossOrigin = "anonymous"; - imageObj = images[src] = { - img: img - }; - images.numTotal++; - setImageLoadHandlers(img, imageObj); - img.src = src; - - // work around for https://bugs.webkit.org/show_bug.cgi?id=80028 - img.customComplete = function () { - if (!this.img.complete) { - this.timer = window.setTimeout(this.img.customComplete, 100); - } else { - this.img.onerror(); - } - }.bind(imageObj); - img.customComplete(); - - } 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") { - h2clog("html2canvas: Cleanup because: " + cause); - } else { - h2clog("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) { - // cancel proxy image request - window[img.callbackname] = undefined; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9) - try { - delete window[img.callbackname]; // for all browser that support this - } catch(ex) {} - if (img.script && img.script.parentNode) { - img.script.setAttribute("src", "about:blank"); // try to cancel running request - img.script.parentNode.removeChild(img.script); - } - images.numLoaded++; - images.numFailed++; - h2clog("html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images.numLoaded + " / " + images.numTotal); - } - } - } - - // cancel any pending requests - 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); - } - h2clog('html2canvas: Preload starts: finding background-images'); - images.firstRun = true; - - getImages( element ); - - h2clog('html2canvas: Preload: Finding images'); - // load images - for (i = 0; i < imgLen; i+=1){ - methods.loadImage( domImages[i].getAttribute( "src" ) ); - } - - images.firstRun = false; - h2clog('html2canvas: Preload: Done.'); - if ( images.numTotal === images.numLoaded ) { - start(); - } - - return methods; - -}; - - - -/* - html2canvas v0.33 - Copyright (c) 2011 Niklas von Hertzen. All rights reserved. - http://www.twitter.com/niklasvh - - Released under MIT License -*/ -function h2cRenderContext(width, height) { - var storage = []; - return { - storage: storage, - width: width, - height: height, - fillRect: function () { - storage.push({ - type: "function", - name: "fillRect", - '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 - }); - } - }; -} -/* - html2canvas v0.33 - Copyright (c) 2011 Niklas von Hertzen. All rights reserved. - http://www.twitter.com/niklasvh - - Released under MIT License -*/ -_html2canvas.Renderer = function(parseQueue, options){ - - - var queue = []; - - function sortZ(zStack){ - var subStacks = [], - stackValues = [], - zStackChildren = zStack.children, - s, - i, - stackLen, - zValue, - zLen, - stackChild, - b, - subStackLen; - - - for (s = 0, zLen = zStackChildren.length; s < zLen; s+=1){ - - stackChild = zStackChildren[s]; - - if (stackChild.children && stackChild.children.length > 0){ - subStacks.push(stackChild); - stackValues.push(stackChild.zindex); - }else{ - queue.push(stackChild); - } - - } - - stackValues.sort(function(a, b) { - return a - b; - }); - - for (i = 0, stackLen = stackValues.length; i < stackLen; i+=1){ - zValue = stackValues[i]; - for (b = 0, subStackLen = subStacks.length; b <= subStackLen; b+=1){ - - if (subStacks[b].zindex === zValue){ - stackChild = subStacks.splice(b, 1); - sortZ(stackChild[0]); - break; - - } - } - } - - } - - - sortZ(parseQueue.zIndex); - if ( typeof options._renderer._create !== "function" ) { - throw new Error("Invalid renderer defined"); - } - return options._renderer._create( parseQueue, options, document, queue, _html2canvas ); - -}; -/* - html2canvas v0.33 - Copyright (c) 2011 Niklas von Hertzen. All rights reserved. - http://www.twitter.com/niklasvh - - Released under MIT License -*/ - - -html2canvas = function( elements, opts ) { - - var queue, - canvas, - options = { - // general - logging: false, - elements: elements, - - // preload options - proxy: "http://html2canvas.appspot.com/", - timeout: 0, // no timeout - useCORS: false, // try to load images as CORS (where available), before falling back to proxy - allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true - - // parse options - svgRendering: false, // use svg powered rendering where available (FF11+) - iframeDefault: "default", - ignoreElements: "IFRAME|OBJECT|PARAM", - useOverflow: true, - letterRendering: false, - - // render options - - flashcanvas: undefined, // path to flashcanvas - width: null, - height: null, - taintTest: true, // do a taint test with all images before applying to canvas - renderer: "Canvas" - }, renderer; - - options = _html2canvas.Util.Extend(opts, options); - - if (typeof options.renderer === "string" && _html2canvas.Renderer[options.renderer] !== undefined) { - options._renderer = _html2canvas.Renderer[options.renderer]( options ); - } else if (typeof options.renderer === "function") { - options._renderer = options.renderer( options ); - } else { - throw("Unknown renderer"); - } - - _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 ); - } - - - }; - - // for pages without images, we still want this to be async, i.e. return methods before executing - 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: h2clog - }; -}; - -html2canvas.log = h2clog; // for renderers -html2canvas.Renderer = { - Canvas: undefined // We are assuming this will be used -}; -/* - html2canvas v0.33 - Copyright (c) 2011 Niklas von Hertzen. All rights reserved. - http://www.twitter.com/niklasvh - - Released under MIT License -*/ - - -_html2canvas.Renderer.Canvas = function( options ) { - - options = options || {}; - - var doc = document, - canvas = options.canvas || doc.createElement('canvas'), - usingFlashcanvas = false, - _createCalled = false, - canvasReadyToDraw = false, - methods, - flashMaxSize = 2880; // flash bitmap limited to 2880x2880px // http://stackoverflow.com/questions/2033792/argumenterror-error-2015-invalid-bitmapdata - - - if (canvas.getContext){ - h2clog("html2canvas: Renderer: using canvas renderer"); - canvasReadyToDraw = true; - } else if ( options.flashcanvas !== undefined ){ - usingFlashcanvas = true; - h2clog("html2canvas: Renderer: canvas not available, using flashcanvas"); - var script = doc.createElement("script"); - script.src = options.flashcanvas; - - script.onload = (function(script, func){ - var intervalFunc; - - if (script.onload === undefined) { - // IE lack of support for script onload - - if( script.onreadystatechange !== undefined ) { - - intervalFunc = function() { - if (script.readyState !== "loaded" && script.readyState !== "complete") { - window.setTimeout( intervalFunc, 250 ); - - } else { - // it is loaded - func(); - - } - - }; - - window.setTimeout( intervalFunc, 250 ); - - } else { - h2clog("html2canvas: Renderer: Can't track when flashcanvas is loaded"); - } - - } else { - return func; - } - - })(script, function(){ - - if (typeof window.FlashCanvas !== "undefined") { - h2clog("html2canvas: Renderer: Flashcanvas initialized"); - window.FlashCanvas.initElement( canvas ); - - canvasReadyToDraw = true; - if ( _createCalled !== false ) { - methods._create.apply( null, _createCalled ); - } - } - }); - - doc.body.appendChild( script ); - - } - - methods = { - _create: function( zStack, options, doc, queue, _html2canvas ) { - - if ( !canvasReadyToDraw ) { - _createCalled = arguments; - return canvas; - } - - var ctx = canvas.getContext("2d"), - storageContext, - i, - queueLen, - a, - newCanvas, - bounds, - testCanvas = document.createElement("canvas"), - hasCTX = ( testCanvas.getContext !== undefined ), - storageLen, - renderItem, - testctx = ( hasCTX ) ? testCanvas.getContext("2d") : {}, - safeImages = [], - fstyle; - - canvas.width = canvas.style.width = (!usingFlashcanvas) ? options.width || zStack.ctx.width : Math.min(flashMaxSize, (options.width || zStack.ctx.width) ); - canvas.height = canvas.style.height = (!usingFlashcanvas) ? options.height || zStack.ctx.height : Math.min(flashMaxSize, (options.height || zStack.ctx.height) ); - - fstyle = ctx.fillStyle; - ctx.fillStyle = zStack.backgroundColor; - ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.fillStyle = fstyle; - - if ( options.svgRendering && zStack.svgRender !== undefined ) { - // TODO: enable async rendering to support this - ctx.drawImage( zStack.svgRender, 0, 0 ); - } else { - for ( i = 0, queueLen = queue.length; i < queueLen; i+=1 ) { - - storageContext = queue.splice(0, 1)[0]; - storageContext.canvasPosition = storageContext.canvasPosition || {}; - - //this.canvasRenderContext(storageContext,parentctx); - - // set common settings for canvas - ctx.textBaseline = "bottom"; - - if (storageContext.clip){ - ctx.save(); - ctx.beginPath(); - // console.log(storageContext); - ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); - ctx.clip(); - - } - - if (storageContext.ctx.storage){ - - for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){ - - renderItem = storageContext.ctx.storage[a]; - - - switch(renderItem.type){ - case "variable": - ctx[renderItem.name] = renderItem['arguments']; - break; - case "function": - if (renderItem.name === "fillRect") { - - if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) { - ctx.fillRect.apply( ctx, renderItem['arguments'] ); - } - }else if(renderItem.name === "fillText") { - if (!usingFlashcanvas || renderItem['arguments'][1] < flashMaxSize && renderItem['arguments'][2] < flashMaxSize) { - ctx.fillText.apply( ctx, renderItem['arguments'] ); - } - }else if(renderItem.name === "drawImage") { - - if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ - if ( hasCTX && options.taintTest ) { - if ( safeImages.indexOf( renderItem['arguments'][ 0 ].src ) === -1 ) { - testctx.drawImage( renderItem['arguments'][ 0 ], 0, 0 ); - try { - testctx.getImageData( 0, 0, 1, 1 ); - } catch(e) { - testCanvas = doc.createElement("canvas"); - testctx = testCanvas.getContext("2d"); - continue; - } - - safeImages.push( renderItem['arguments'][ 0 ].src ); - - } - } - ctx.drawImage.apply( ctx, renderItem['arguments'] ); - } - } - - - break; - default: - - } - - } - - } - if (storageContext.clip){ - ctx.restore(); - } - - } - } - - h2clog("html2canvas: Renderer: Canvas renderer done - returning canvas obj"); - - queueLen = options.elements.length; - - if (queueLen === 1) { - if (typeof options.elements[ 0 ] === "object" && options.elements[ 0 ].nodeName !== "BODY" && usingFlashcanvas === false) { - // crop image to the bounds of selected (single) element - bounds = _html2canvas.Util.Bounds( options.elements[ 0 ] ); - newCanvas = doc.createElement('canvas'); - newCanvas.width = bounds.width; - newCanvas.height = 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; - } - } /*else { - // TODO clip and resize multiple elements - - for ( i = 0; i < queueLen; i+=1 ) { - if (options.elements[ i ] instanceof Element) { - - } - - } - } - */ - - - - return canvas; - } - }; - - return methods; - -}; -/* - html2canvas v0.33 - Copyright (c) 2011 Niklas von Hertzen. All rights reserved. - http://www.twitter.com/niklasvh - - Released under MIT License -*/ - - -// WARNING THIS file is outdated, and hasn't been tested in quite a while - -_html2canvas.Renderer.SVG = function( options ) { - - options = options || {}; - - var doc = document, - svgNS = "http://www.w3.org/2000/svg", - svg = doc.createElementNS(svgNS, "svg"), - xlinkNS = "http://www.w3.org/1999/xlink", - defs = doc.createElementNS(svgNS, "defs"), - i, - a, - queueLen, - storageLen, - storageContext, - renderItem, - el, - settings = {}, - text, - fontStyle, - clipId = 0, - methods; - - - methods = { - _create: function( zStack, options, doc, queue, _html2canvas ) { - svg.setAttribute("version", "1.1"); - svg.setAttribute("baseProfile", "full"); - - svg.setAttribute("viewBox", "0 0 " + Math.max(zStack.ctx.width, options.width) + " " + Math.max(zStack.ctx.height, options.height)); - svg.setAttribute("width", Math.max(zStack.ctx.width, options.width) + "px"); - svg.setAttribute("height", Math.max(zStack.ctx.height, options.height) + "px"); - svg.setAttribute("preserveAspectRatio", "none"); - svg.appendChild(defs); - - - - for (i = 0, queueLen = queue.length; i < queueLen; i+=1){ - - storageContext = queue.splice(0, 1)[0]; - storageContext.canvasPosition = storageContext.canvasPosition || {}; - - //this.canvasRenderContext(storageContext,parentctx); - - - /* - if (storageContext.clip){ - ctx.save(); - ctx.beginPath(); - // console.log(storageContext); - ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height); - ctx.clip(); - - }*/ - - if (storageContext.ctx.storage){ - - for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){ - - renderItem = storageContext.ctx.storage[a]; - - - - switch(renderItem.type){ - case "variable": - settings[renderItem.name] = renderItem['arguments']; - break; - case "function": - if (renderItem.name === "fillRect") { - - el = doc.createElementNS(svgNS, "rect"); - el.setAttribute("x", renderItem['arguments'][0]); - el.setAttribute("y", renderItem['arguments'][1]); - el.setAttribute("width", renderItem['arguments'][2]); - el.setAttribute("height", renderItem['arguments'][3]); - el.setAttribute("fill", settings.fillStyle); - svg.appendChild(el); - - } else if(renderItem.name === "fillText") { - el = doc.createElementNS(svgNS, "text"); - - fontStyle = settings.font.split(" "); - - el.style.fontVariant = fontStyle.splice(0, 1)[0]; - el.style.fontWeight = fontStyle.splice(0, 1)[0]; - el.style.fontStyle = fontStyle.splice(0, 1)[0]; - el.style.fontSize = fontStyle.splice(0, 1)[0]; - - el.setAttribute("x", renderItem['arguments'][1]); - el.setAttribute("y", renderItem['arguments'][2] - (parseInt(el.style.fontSize, 10) + 3)); - - el.setAttribute("fill", settings.fillStyle); - - - - - // TODO get proper baseline - el.style.dominantBaseline = "text-before-edge"; - el.style.fontFamily = fontStyle.join(" "); - - text = doc.createTextNode(renderItem['arguments'][0]); - el.appendChild(text); - - - svg.appendChild(el); - - - - } else if(renderItem.name === "drawImage") { - - if (renderItem['arguments'][8] > 0 && renderItem['arguments'][7]){ - - // TODO check whether even any clipping is necessary for this particular image - el = doc.createElementNS(svgNS, "clipPath"); - el.setAttribute("id", "clipId" + clipId); - - text = doc.createElementNS(svgNS, "rect"); - text.setAttribute("x", renderItem['arguments'][5] ); - text.setAttribute("y", renderItem['arguments'][6]); - - text.setAttribute("width", renderItem['arguments'][3]); - text.setAttribute("height", renderItem['arguments'][4]); - el.appendChild(text); - defs.appendChild(el); - - el = doc.createElementNS(svgNS, "image"); - el.setAttributeNS(xlinkNS, "xlink:href", renderItem['arguments'][0].src); - el.setAttribute("width", renderItem['arguments'][7]); - el.setAttribute("height", renderItem['arguments'][8]); - el.setAttribute("x", renderItem['arguments'][5]); - el.setAttribute("y", renderItem['arguments'][6]); - el.setAttribute("clip-path", "url(#clipId" + clipId + ")"); - // el.setAttribute("xlink:href", ); - - - el.setAttribute("preserveAspectRatio", "none"); - - svg.appendChild(el); - - - clipId += 1; - /* - ctx.drawImage( - renderItem['arguments'][0], - renderItem['arguments'][1], - renderItem['arguments'][2], - renderItem['arguments'][3], - renderItem['arguments'][4], - renderItem['arguments'][5], - renderItem['arguments'][6], - renderItem['arguments'][7], - renderItem['arguments'][8] - ); - */ - } - } - - - - break; - default: - - } - - } - - } - /* - if (storageContext.clip){ - ctx.restore(); - } - */ - - - - } - - - - - - - - - - - h2clog("html2canvas: Renderer: SVG Renderer done - returning SVG DOM obj"); - - return svg; - } - }; - - return methods; - - -}; -window.html2canvas = html2canvas; -}(window, document)); diff --git a/build/jquery.plugin.html2canvas.js b/build/jquery.plugin.html2canvas.js deleted file mode 100644 index 3270bf1..0000000 --- a/build/jquery.plugin.html2canvas.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - @license html2canvas v0.33 - Copyright (c) 2011 Niklas von Hertzen. All rights reserved. - http://www.twitter.com/niklasvh - - Released under MIT License - */ -/* - * jQuery helper plugin for examples and tests - */ -(function( $ ){ - $.fn.html2canvas = function(options) { - if (options && options.profile && window.console && window.console.profile) { - console.profile(); - } - var date = new Date(), - html2obj, - $message = null, - timeoutTimer = false, - timer = date.getTime(); - options = options || {}; - - options.onrendered = options.onrendered || function( canvas ) { - var $canvas = $(canvas), - finishTime = new Date(); - - if (options && options.profile && window.console && window.console.profileEnd) { - console.profileEnd(); - } - $canvas.css({ - position: 'absolute', - left: 0, - top: 0 - }).appendTo(document.body); - $canvas.siblings().toggle(); - - $(window).click(function(){ - $canvas.toggle().siblings().toggle(); - throwMessage("Canvas Render " + ($canvas.is(':visible') ? "visible" : "hidden")); - }); - throwMessage('Screenshot created in '+ ((finishTime.getTime()-timer)) + " ms
",4000); - - // test if canvas is read-able - try { - $canvas[0].toDataURL(); - } catch(e) { - if ($canvas[0].nodeName.toLowerCase() === "canvas") { - // TODO, maybe add a bit less offensive way to present this, but still something that can easily be noticed - alert("Canvas is tainted, unable to read data"); - } - } - }; - - html2obj = html2canvas(this, options); - - function throwMessage(msg,duration){ - window.clearTimeout(timeoutTimer); - timeoutTimer = window.setTimeout(function(){ - $message.fadeOut(function(){ - $message.remove(); - $message = null; - }); - },duration || 2000); - if ($message) - $message.remove(); - $message = $('
').html(msg).css({ - margin:0, - padding:10, - background: "#000", - opacity:0.7, - position:"fixed", - top:10, - right:10, - fontFamily: 'Tahoma', - color:'#fff', - fontSize:12, - borderRadius:12, - width:'auto', - height:'auto', - textAlign:'center', - textDecoration:'none', - display:'none' - }).appendTo(document.body).fadeIn(); - html2obj.log(msg); - } - }; -})( jQuery ); From 17f4701ee50e687940138d2e2063c2a8e38b26de Mon Sep 17 00:00:00 2001 From: Bdkzero Date: Mon, 21 May 2012 00:34:21 +0200 Subject: [PATCH 8/8] Created new build target 'build-allrends'. Use this to build html2canvas including all renderers --- build.xml | 79 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/build.xml b/build.xml index a0e5da7..984d861 100644 --- a/build.xml +++ b/build.xml @@ -22,11 +22,27 @@ - - + - - + + + + + + + + + + + + + + + + + + + @@ -47,11 +63,21 @@ + + Concatenating files:${line.separator}${prettty-sourcefiles}${line.separator}into ${build.dir}/${JS_NAME}... - - + + + + + + + + Concatenating files:${line.separator}${prettty-sourcefiles-allrends}${line.separator}into ${build.dir}/${JS_NAME}... + + @@ -76,8 +102,31 @@ - - + + + + + + + + + + + + + + + + + + + + + + + @@ -97,6 +146,20 @@ + + + + + + + + + + + +