From c5d82acdf6a55b027e9aff27260d697a83092e83 Mon Sep 17 00:00:00 2001 From: MoyuScript Date: Sun, 25 Nov 2012 20:59:31 +0200 Subject: [PATCH] Switched build process to use grunt --- grunt.js | 61 + package.json | 12 + src/Core.js | 7 - src/Generate.js | 756 +++++++------ src/Parse.js | 2298 +++++++++++++++++++------------------- src/Preload.js | 587 +++++----- src/Queue.js | 145 ++- src/Renderer.js | 99 +- src/Util.js | 145 ++- src/html2canvas-post.txt | 2 - src/html2canvas-pre.txt | 1 - src/renderers/Canvas.js | 399 ++++--- 12 files changed, 2250 insertions(+), 2262 deletions(-) create mode 100644 grunt.js create mode 100644 package.json delete mode 100644 src/html2canvas-post.txt delete mode 100644 src/html2canvas-pre.txt diff --git a/grunt.js b/grunt.js new file mode 100644 index 0000000..81cfa9e --- /dev/null +++ b/grunt.js @@ -0,0 +1,61 @@ +/*global module:false*/ +module.exports = function(grunt) { + + // Project configuration. + grunt.initConfig({ + pkg: '', + meta: { + banner: '/*\n <%= pkg.title || pkg.name %> <%= pkg.version %>' + + '<%= pkg.homepage ? " <" + pkg.homepage + ">\n" : "" %>' + + ' Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>' + + '\n\n Released under <%= _.pluck(pkg.licenses, "type").join(", ") %> License\n*/', + pre: '(function(window, document, undefined){', + post: '})(window,document);' + }, + lint: { + files: ['grunt.js', 'build/<%= pkg.name %>.js'] + }, + qunit: { + files: ['tests/qunit/index.html'] + }, + concat: { + dist: { + src: ['', '','src/*.js', 'src/renderers/Canvas.js', ''], + dest: 'build/<%= pkg.name %>.js' + } + }, + min: { + dist: { + src: ['', ''], + dest: 'build/<%= pkg.name %>.min.js' + } + }, + watch: { + files: '', + tasks: 'lint qunit' + }, + jshint: { + options: { + curly: true, + eqeqeq: true, + immed: true, + latedef: true, + newcap: true, + noarg: true, + sub: true, + undef: true, + boss: true, + eqnull: true, + browser: true + }, + globals: { + jQuery: true + } + }, + uglify: {} + }); + + // Default task. + grunt.registerTask('default', 'concat lint qunit min'); + +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..6f89237 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "title": "html2canvas", + "name": "html2canvas", + "description": "File archive management library in JavaScript", + "version": "0.4.0", + "author": { + "name":"Niklas von Hertzen (@niklasvh)" + }, + "homepage": "http://html2canvas.hertzen.com", + "licenses": [{"type": "MIT"}] + +} \ No newline at end of file diff --git a/src/Core.js b/src/Core.js index 149830c..a5b4356 100644 --- a/src/Core.js +++ b/src/Core.js @@ -1,10 +1,3 @@ -/* - html2canvas @VERSION@ - Copyright (c) 2011 Niklas von Hertzen. All rights reserved. - http://www.twitter.com/niklasvh - - Released under MIT License - */ "use strict"; var _html2canvas = {}, diff --git a/src/Generate.js b/src/Generate.js index d095510..f07d8f7 100644 --- a/src/Generate.js +++ b/src/Generate.js @@ -1,332 +1,326 @@ -/* - html2canvas @VERSION@ - 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 = {}; + _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,%\(\)]+)\)$/ -]; + 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; + _html2canvas.Generate.parseGradient = function(css, bounds) { + var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3, tl,tr,br,bl; for(i = 0; i < len; i+=1){ - m1 = css.match(reGradients[i]); - if(m1) break; + m1 = css.match(reGradients[i]); + if(m1) { + break; + } } if(m1) { - switch(m1[1]) { - case '-webkit-linear-gradient': - case '-o-linear-gradient': + switch(m1[1]) { + case '-webkit-linear-gradient': + case '-o-linear-gradient': - gradient = { - type: 'linear', - x0: null, - y0: null, - x1: null, - y1: null, - colorStops: [] - }; + 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; + // 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 'right': + gradient.x0 = bounds.width; + gradient.x1 = 0; + break; - case 'bottom': - gradient.y0 = bounds.height; - gradient.y1 = 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; + 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; - // 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 - }); - } + 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 :( + tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); + tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2)); + gradient.rx = gradient.ry = Math.max(tl, tr, br, bl); + break; + case 'closest-corner': + tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2)); + tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2)); + bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2)); + gradient.rx = gradient.ry = Math.min(tl, tr, br, bl); + break; + case 'farthest-side': + if(m2[0] === 'circle'){ + gradient.rx = gradient.ry = Math.max( + gradient.cx, + gradient.cy, + gradient.x1 - gradient.cx, + gradient.y1 - gradient.cy + ); + } else { // 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; - case '-webkit-gradient': + // TODO: add support for "30px 40px" sizes (webkit only) + } + } - 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; + // 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; } - - // 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; - } + } else { + stop = i * step; + } + gradient.colorStops.push({ + color: m3[1], + stop: stop + }); + } + } + break; + } } return gradient; -}; + }; -_html2canvas.Generate.Gradient = function(src, bounds) { + _html2canvas.Generate.Gradient = function(src, bounds) { var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'), gradient, grad, i, len, img; @@ -340,96 +334,96 @@ _html2canvas.Generate.Gradient = function(src, bounds) { img = new Image(); if(gradient){ - if(gradient.type === 'linear'){ - grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1); + 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(); + 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) { + _html2canvas.Generate.ListAlpha = function(number) { var tmp = "", modulus; do { - modulus = number % 26; - tmp = String.fromCharCode((modulus) + 64) + tmp; - number = number / 26; + modulus = number % 26; + tmp = String.fromCharCode((modulus) + 64) + tmp; + number = number / 26; }while((number*26) > 26); return tmp; -}; + }; -_html2canvas.Generate.ListRoman = function(number) { + _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 = "", @@ -437,18 +431,18 @@ _html2canvas.Generate.ListRoman = function(number) { len = romanArray.length; if (number <= 0 || number >= 4000) { - return number; + return number; } for (v=0; v < len; v+=1) { - while (number >= decimal[v]) { - number -= decimal[v]; - roman += romanArray[v]; - } + while (number >= decimal[v]) { + number -= decimal[v]; + roman += romanArray[v]; + } } return roman; -}; + }; })(); \ No newline at end of file diff --git a/src/Parse.js b/src/Parse.js index 3a546b8..714b13c 100644 --- a/src/Parse.js +++ b/src/Parse.js @@ -11,107 +11,107 @@ */ _html2canvas.Parse = function ( images, options ) { - window.scroll(0,0); + 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; + 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; + })() + }, + 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(){ + 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); - 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); @@ -123,202 +123,202 @@ _html2canvas.Parse = function ( images, options ) { - 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; - } + 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(); + 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 "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(); + case "uppercase": + return text.toUpperCase(); - default: - return text; - - } + default: + return text; } - function trimText (text) { - return text.replace(/^\s*/g, "").replace(/\s*$/g, ""); - } + } - function fontMetrics (font, fontSize) { + function trimText (text) { + return text.replace(/^\s*/g, "").replace(/\s*$/g, ""); + } - 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 fontMetrics (font, fontSize) { + if (fontData[font + "-" + fontSize] !== undefined) { + return fontData[font + "-" + fontSize]; } - function drawText(currentText, x, y, ctx){ - if (trimText(currentText).length>0) { - ctx.fillText(currentText,x,y); - numDraws+=1; - } + 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; + 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 + // apply text-transform:ation to the text - textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform")); - text = trimText(textNode.nodeValue); + textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform")); + text = trimText(textNode.nodeValue); - if (text.length>0){ + if (text.length>0){ - if (text_decoration !== "none"){ - metrics = fontMetrics(family, size); - } + if (text_decoration !== "none"){ + metrics = fontMetrics(family, size); + } - text_align = text_align.replace(["-webkit-auto"],["auto"]); + text_align = text_align.replace(["-webkit-auto"],["auto"]); - if (options.letterRendering === false && /^(left|right|justify|auto)$/.test(text_align) && /^(normal|none|0px)$/.test(letter_spacing)){ - // this.setContextVariable(ctx,"textAlign",text_align); - renderList = textNode.nodeValue.split(/(\b| )/); + if (options.letterRendering === false && /^(left|right|justify|auto)$/.test(text_align) && /^(normal|none|0px)$/.test(letter_spacing)){ + // this.setContextVariable(ctx,"textAlign",text_align); + renderList = textNode.nodeValue.split(/(\b| )/); - }else{ - // this.setContextVariable(ctx,"textAlign","left"); - renderList = textNode.nodeValue.split(""); - } + }else{ + // this.setContextVariable(ctx,"textAlign","left"); + renderList = textNode.nodeValue.split(""); + } - switch(parseInt(bold, 10)){ - case 401: - bold = "bold"; - break; - case 400: - bold = "normal"; - break; - } + switch(parseInt(bold, 10)){ + case 401: + bold = "bold"; + break; + case 400: + bold = "normal"; + break; + } - ctx.setVariable("fillStyle", color); + 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); + ctx.setVariable("font", font_style+ " " + font_variant + " " + bold + " " + size + " " + family); - if (align){ - ctx.setVariable("textAlign", "right"); - }else{ - ctx.setVariable("textAlign", "left"); - } + 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(); @@ -326,215 +326,212 @@ _html2canvas.Parse = function ( images, options ) { */ - oldTextNode = textNode; + oldTextNode = textNode; - for ( c=0, listLen = renderList.length; c < listLen; c+=1 ) { - textValue = null; + 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; - } - - if (i < listLen-1) - newTextNode = oldTextNode.splitText(renderList[c].length); - else - newTextNode = null; - - 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; + 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(); } - - - } - - } - - 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 elementIndex( el ) { - var i = -1, - count = 1, - childs = el.parentNode.childNodes; - - if ( el.parentNode ) { - while( childs[ ++i ] !== el ) { - if ( childs[ i ].nodeType === 1 ) { - count++; - } - } - return count; - } else { - return -1; - } - - } - - function 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)) { - - currentIndex = elementIndex( element ); - - 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; - + if (range.getBoundingClientRect()) { + bounds = range.getBoundingClientRect(); }else{ - return; - /* + 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 = (i < listLen-1) ? oldTextNode.splitText(renderList[c].length) : null; + + 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 elementIndex( el ) { + var i = -1, + count = 1, + childs = el.parentNode.childNodes; + + if ( el.parentNode ) { + while( childs[ ++i ] !== el ) { + if ( childs[ i ].nodeType === 1 ) { + count++; + } + } + return count; + } else { + return -1; + } + + } + + function 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)) { + + currentIndex = elementIndex( element ); + + 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. @@ -544,411 +541,411 @@ _html2canvas.Parse = function ( images, options ) { // this.setFont(stack.ctx, element, true); x = elBounds.left - 10; */ - } + } - y = listBounds.bottom; + y = listBounds.bottom; - drawText(text, x, y, ctx); - - - } + drawText(text, x, y, ctx); } - function loadImage (src){ - var img = images[src]; - if (img && img.succeeded === true) { - return img.img; - } else { - return false; - } + + } + + 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; } - - - - - - 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 - }; + if (zIndex !== "auto"){ + needReorder = true; + newContext = h2czContext(zIndex); + parentZ.children.push(newContext); + return newContext; } - 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; - } + return parentZ; - if (zIndex !== "auto"){ - needReorder = true; - newContext = h2czContext(zIndex); - parentZ.children.push(newContext); - return newContext; + } - } + function renderBorders(el, ctx, bounds, clip){ - 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, - i, - borderArgs, - borderBounds, - borders = (function(el){ - var borders = [], - sides = ["Top","Right","Bottom","Left"], - s; + var x = bounds.left, + y = bounds.top, + w = bounds.width, + h = bounds.height, + borderSide, + borderData, + bx, + by, + bw, + bh, + i, + borderArgs, + 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') - }); - } + 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; + return borders; - }(el)), - // http://www.w3.org/TR/css3-background/#the-border-radius - borderRadius = (function( el ) { - var borders = [], - sides = ["TopLeft","TopRight","BottomRight","BottomLeft"], - s; - - for (s = 0; s < 4; s+=1){ - borders.push( getCSS(el, 'border' + sides[s] + 'Radius') ); - } + }(el)), + // http://www.w3.org/TR/css3-background/#the-border-radius + borderRadius = (function( el ) { + var borders = [], + sides = ["TopLeft","TopRight","BottomRight","BottomLeft"], + s; - return borders; - })( el ); + for (s = 0; s < 4; s+=1){ + borders.push( getCSS(el, 'border' + sides[s] + 'Radius') ); + } + + return borders; + })( el ); - for ( borderSide = 0; borderSide < 4; borderSide+=1 ) { - borderData = borders[ borderSide ]; - borderArgs = []; - if (borderData.width>0){ - bx = x; - by = y; - bw = w; - bh = h - (borders[2].width); + for ( borderSide = 0; borderSide < 4; borderSide+=1 ) { + borderData = borders[ borderSide ]; + borderArgs = []; + 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; - - i = 0; - borderArgs[ i++ ] = [ "line", bx, by ]; // top left - borderArgs[ i++ ] = [ "line", bx + bw, by ]; // top right - borderArgs[ i++ ] = [ "line", bx + bw - borders[ 1 ].width, by + bh ]; // bottom right - borderArgs[ i++ ] = [ "line", bx + borders[ 3 ].width, by + bh ]; // bottom left - - break; - case 1: - // right border - bx = x + w - (borders[1].width); - bw = borders[1].width; - - i = 0; - borderArgs[ i++ ] = [ "line", bx, by + borders[ 0 ].width]; // top left - borderArgs[ i++ ] = [ "line", bx + bw, by ]; // top right - borderArgs[ i++ ] = [ "line", bx + bw, by + bh + borders[ 2 ].width ]; // bottom right - borderArgs[ i++ ] = [ "line", bx, by + bh ]; // bottom left - - break; - case 2: - // bottom border - by = (by + h) - (borders[2].width); - bh = borders[2].width; - - - i = 0; - borderArgs[ i++ ] = [ "line", bx + borders[ 3 ].width, by ]; // top left - borderArgs[ i++ ] = [ "line", bx + bw - borders[ 2 ].width, by ]; // top right - borderArgs[ i++ ] = [ "line", bx + bw, by + bh ]; // bottom right - borderArgs[ i++ ] = [ "line", bx, by + bh ]; // bottom left - - break; - case 3: - // left border - bw = borders[3].width; - - i = 0; - borderArgs[ i++ ] = [ "line", bx, by ]; // top left - borderArgs[ i++ ] = [ "line", bx + bw, by + borders[ 0 ].width ]; // top right - borderArgs[ i++ ] = [ "line", bx + bw, by + bh ]; // bottom right - borderArgs[ i++ ] = [ "line", bx, by + bh + borders[ 2 ].width ]; // bottom left - - break; - } + switch(borderSide){ + case 0: + // top border + bh = borders[0].width; - borderBounds = { - left:bx, - top:by, - width: bw, - height:bh - }; + i = 0; + borderArgs[ i++ ] = [ "line", bx, by ]; // top left + borderArgs[ i++ ] = [ "line", bx + bw, by ]; // top right + borderArgs[ i++ ] = [ "line", bx + bw - borders[ 1 ].width, by + bh ]; // bottom right + borderArgs[ i++ ] = [ "line", bx + borders[ 3 ].width, by + bh ]; // bottom left - if (clip){ - borderBounds = clipBounds(borderBounds, clip); - } + break; + case 1: + // right border + bx = x + w - (borders[1].width); + bw = borders[1].width; + + i = 0; + borderArgs[ i++ ] = [ "line", bx, by + borders[ 0 ].width]; // top left + borderArgs[ i++ ] = [ "line", bx + bw, by ]; // top right + borderArgs[ i++ ] = [ "line", bx + bw, by + bh + borders[ 2 ].width ]; // bottom right + borderArgs[ i++ ] = [ "line", bx, by + bh ]; // bottom left + + break; + case 2: + // bottom border + by = (by + h) - (borders[2].width); + bh = borders[2].width; - if ( borderBounds.width > 0 && borderBounds.height > 0 ) { - - if ( borderData.color !== "transparent" ){ - ctx.setVariable( "fillStyle", borderData.color ); - - var shape = ctx.drawShape(), - numBorderArgs = borderArgs.length; - - for ( i = 0; i < numBorderArgs; i++ ) { - shape[( i === 0) ? "moveTo" : borderArgs[ i ][ 0 ] + "To" ].apply( null, borderArgs[ i ].slice(1) ); - } + i = 0; + borderArgs[ i++ ] = [ "line", bx + borders[ 3 ].width, by ]; // top left + borderArgs[ i++ ] = [ "line", bx + bw - borders[ 2 ].width, by ]; // top right + borderArgs[ i++ ] = [ "line", bx + bw, by + bh ]; // bottom right + borderArgs[ i++ ] = [ "line", bx, by + bh ]; // bottom left - numDraws+=1; - } - - - - - // renderRect(ctx, bx, by, borderBounds.width, borderBounds.height, borderData.color); - } + break; + case 3: + // left border + bw = borders[3].width; + i = 0; + borderArgs[ i++ ] = [ "line", bx, by ]; // top left + borderArgs[ i++ ] = [ "line", bx + bw, by + borders[ 0 ].width ]; // top right + borderArgs[ i++ ] = [ "line", bx + bw, by + bh ]; // bottom right + borderArgs[ i++ ] = [ "line", bx, by + bh + borders[ 2 ].width ]; // bottom left - } + break; } - return borders; + borderBounds = { + left:bx, + top:by, + width: bw, + height:bh + }; + if (clip){ + borderBounds = clipBounds(borderBounds, clip); + } + + + if ( borderBounds.width > 0 && borderBounds.height > 0 ) { + + if ( borderData.color !== "transparent" ){ + ctx.setVariable( "fillStyle", borderData.color ); + + var shape = ctx.drawShape(), + numBorderArgs = borderArgs.length; + + for ( i = 0; i < numBorderArgs; i++ ) { + shape[( i === 0) ? "moveTo" : borderArgs[ i ][ 0 ] + "To" ].apply( null, borderArgs[ i ].slice(1) ); + } + + numDraws+=1; + } + + + + + // 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); + } } - 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); - - - + 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 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; } - - 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 - ); + if (ely-y>0){ + sourceY = ely-y; } - - 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; + 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 + ); + } - 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); + 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]; } - function renderBackgroundRepeatX(ctx, image, bgp, x, y, w, h){ - - var height = Math.min(image.height,h), - width,bgx; + if ( typeof background_image !== "undefined" && /^(1|none)$/.test( background_image ) === false ) { + background_image = _html2canvas.Util.backgroundImage( background_image ); + image = loadImage( background_image ); - bgp.left = bgp.left-Math.ceil(bgp.left/image.width)*image.width; + bgp = _html2canvas.Util.BackgroundPosition(el, bounds, image); + // TODO add support for background-origin + if ( image ){ + switch ( background_repeat ) { - for (bgx=(x+bgp.left);bgxw+x){ - width = (w+x)-bgx; - }else{ - width = image.width; - } + case "repeat-y": + renderBackgroundRepeatY( ctx, image, bgp, bounds.left, bounds.top, bounds.width, bounds.height ); + break; - 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": - /* + case "no-repeat": + /* this.drawBackgroundRepeat( ctx, image, @@ -961,266 +958,266 @@ _html2canvas.Parse = function ( images, options ) { );*/ - - 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); + 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; - 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; - } + // + // bgw = Math.min(bgw,image.width); + // bgh = Math.min(bgh,image.height); - 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; - - - } + if (bgsx<0){ + bgsx = Math.abs(bgsx); + bgdx += bgsx; + bgw = Math.min(bounds.width,image.width-bgsx); }else{ - h2clog("html2canvas: Error loading background:" + background_image); - //console.log(images); + 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); - 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 ); + zindex = setZ( getCSS( el, "zIndex"), parentStack.zIndex ); - stack = { - ctx: h2cRenderContext( docDim.width || w , docDim.height || h ), - zIndex: zindex, - opacity: opacity * parentStack.opacity, - cssPosition: cssPosition - }; + 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 + // 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 (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; - } - } + 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); + stackLength = zindex.children.push(stack); - ctx = zindex.children[stackLength-1].ctx; + ctx = zindex.children[stackLength-1].ctx; - ctx.setVariable("globalAlpha", stack.opacity); + ctx.setVariable("globalAlpha", stack.opacity); - // draw element borders - borders = renderBorders(el, ctx, bounds, false); - stack.borders = borders; + // 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 + // 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; - } + 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){ - // 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); - - //} - - } + renderFormValue(el, bounds, stack); - 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,{ @@ -1231,169 +1228,168 @@ _html2canvas.Parse = function ( images, options ) { 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; + } + 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); + } + } - return zindex.children[stackLength - 1]; + } } + } + stack = renderElement(element, null); - - 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 = ""; + 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 + 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.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 += ""; - } - } + 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(""); + } + + } + + parseDOM( body ); + img.src = [ + "data:image/svg+xml,", + "", + "", + "", + html.replace(/\#/g,"%23"), + "", + "", + "" + ].join(""); - img.onload = function() { - stack.svgRender = img; - }; + img.onload = function() { + stack.svgRender = img; + }; - })( document.documentElement ); + })( document.documentElement ); - } + } - // parse every child element - for (i = 0, children = element.children, childrenLen = children.length; i < childrenLen; i+=1){ - parseElement(children[i], stack); - } + // 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" ); + stack.backgroundColor = getCSS( document.documentElement, "backgroundColor" ); - return stack; + return stack; }; function h2czContext(zindex) { - return { - zindex: zindex, - children: [] - }; + return { + zindex: zindex, + children: [] + }; } diff --git a/src/Preload.js b/src/Preload.js index 3c76026..4a6f168 100644 --- a/src/Preload.js +++ b/src/Preload.js @@ -1,360 +1,329 @@ -/* - html2canvas @VERSION@ - 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, + 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) { + + var contents = _html2canvas.Util.Children(el), 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; + background_image, + src, + img, + elNodeType = false; - link.href = window.location.href; - pageOrigin = link.protocol + link.host; + // Firefox fails with permission denied on pages with iframes + try { + var contentsLen = contents.length; + for (i = 0; i < contentsLen; i+=1 ){ + getImages(contents[i]); + } + } + catch( e ) {} - - - - - - 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); + try { + elNodeType = el.nodeType; + } catch (ex) { + elNodeType = false; + h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message); } - 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 (elNodeType === 1 || elNodeType === undefined){ - if (typeof options.complete === "function"){ - options.complete(images); - } + // 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 - // TODO modify proxy to serve images with CORS enabled, where available - function proxyGetImage(url, img, imageObj){ - var callback_name, - scriptUrl = options.proxy, - script; + if (/^(-webkit|-o|-moz|-ms|linear)-/.test( background_image )) { - link.href = url; - url = link.href; // work around for pages with base href="" set - WARNING: this may change the url + img = _html2canvas.Generate.Gradient( background_image, _html2canvas.Util.Bounds( el ) ); - callback_name = 'html2canvas_' + (count++); - imageObj.callbackname = callback_name; + if ( img !== undefined ){ + images[background_image] = { + img: img, + succeeded: true + }; + images.numTotal++; + images.numLoaded++; + start(); + + } - if (scriptUrl.indexOf("?") > -1) { - scriptUrl += "&"; } else { - scriptUrl += "?"; + src = _html2canvas.Util.backgroundImage(background_image.match(/data:image\/.*;base64,/i) ? background_image : background_image.split(",")[0]); + methods.loadImage(src); } - 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) { + 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 ); - // if (!this.ignoreRe.test(el.nodeName)){ - // - - var contents = _html2canvas.Util.Children(el), - i, - background_image, - src, - img, - elNodeType = false; + // let's try with proxy instead + if ( options.proxy ) { + var src = img.src; + img = new Image(); + imageObj.img = img; + img.src = src; - // Firefox fails with permission denied on pages with iframes - try { - var contentsLen = contents.length; - for (i = 0; i < contentsLen; i+=1 ){ - // var ignRe = new RegExp("("+this.ignoreElements+")"); - // if (!ignRe.test(element.nodeName)){ - getImages(contents[i]); - // } - } + proxyGetImage( img.src, img, imageObj ); + return; } - catch( e ) {} + } - // } - try { - elNodeType = el.nodeType; - } catch (ex) { - elNodeType = false; - h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message); - } + images.numLoaded++; + images.numFailed++; + imageObj.succeeded = false; + img.onerror = img.onload = null; + start(); - 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 Opera has no load/error event for SVG images - // TODO add multi image background support - - if (/^(-webkit|-o|-moz|-ms|linear)-/.test( background_image )) { - - 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 - /* + // 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 + 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; + 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 ); - } + // 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(); - }, - 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."); - } + } else if ( options.proxy ) { + imageObj = images[src] = { + img: img + }; + images.numTotal++; + proxyGetImage( src, img, imageObj ); + } + } - 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); - } + }, + 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); + } + } + } - 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" ) ); + // 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); + } } - images.firstRun = false; - h2clog('html2canvas: Preload: Done.'); - if ( images.numTotal === images.numLoaded ) { - start(); - } + }; - return methods; + 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; + +}; \ No newline at end of file diff --git a/src/Queue.js b/src/Queue.js index dab212d..bf8dcdf 100644 --- a/src/Queue.js +++ b/src/Queue.js @@ -1,81 +1,74 @@ -/* - html2canvas @VERSION@ - 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 - }); + var storage = []; + return { + storage: storage, + width: width, + height: height, + fillRect: function () { + storage.push({ + type: "function", + name: "fillRect", + 'arguments': arguments + }); + }, + drawShape: function() { + + var shape = []; + + storage.push({ + type: "function", + name: "drawShape", + 'arguments': shape + }); + + return { + moveTo: function() { + shape.push({ + name: "moveTo", + 'arguments': arguments + }); }, - drawShape: function() { - - var shape = []; - - storage.push({ - type: "function", - name: "drawShape", - 'arguments': shape - }); - - return { - moveTo: function() { - shape.push({ - name: "moveTo", - 'arguments': arguments - }); - }, - lineTo: function() { - shape.push({ - name: "lineTo", - 'arguments': arguments - }); - }, - bezierCurveTo: function() { - shape.push({ - name: "bezierCurveTo", - 'arguments': arguments - }); - }, - quadraticCurveTo: function() { - shape.push({ - name: "quadraticCurveTo", - 'arguments': arguments - }); - } - }; - + lineTo: function() { + shape.push({ + name: "lineTo", + 'arguments': arguments + }); }, - drawImage: function () { - storage.push({ - type: "function", - name: "drawImage", - 'arguments': arguments - }); + bezierCurveTo: function() { + shape.push({ + name: "bezierCurveTo", + 'arguments': arguments + }); }, - fillText: function () { - storage.push({ - type: "function", - name: "fillText", - 'arguments': arguments - }); - }, - setVariable: function (variable, value) { - storage.push({ - type: "variable", - name: variable, - 'arguments': value - }); + quadraticCurveTo: function() { + shape.push({ + name: "quadraticCurveTo", + 'arguments': arguments + }); } - }; -} + }; + + }, + drawImage: function () { + storage.push({ + type: "function", + name: "drawImage", + 'arguments': arguments + }); + }, + fillText: function () { + storage.push({ + type: "function", + name: "fillText", + 'arguments': arguments + }); + }, + setVariable: function (variable, value) { + storage.push({ + type: "variable", + name: variable, + 'arguments': value + }); + } + }; +} \ No newline at end of file diff --git a/src/Renderer.js b/src/Renderer.js index f88be36..1703cc8 100644 --- a/src/Renderer.js +++ b/src/Renderer.js @@ -1,66 +1,57 @@ -/* - html2canvas @VERSION@ - 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; - var queue = []; + for (s = 0, zLen = zStackChildren.length; s < zLen; s+=1){ - function sortZ(zStack){ - var subStacks = [], - stackValues = [], - zStackChildren = zStack.children, - s, - i, - stackLen, - zValue, - zLen, - stackChild, - b, - subStackLen; + stackChild = zStackChildren[s]; - - 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; - - } - } - } + 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; + }); - sortZ(parseQueue.zIndex); - if ( typeof options._renderer._create !== "function" ) { - throw new Error("Invalid renderer defined"); + 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; + + } + } } - return options._renderer._create( parseQueue, options, document, queue, _html2canvas ); + + } + + + sortZ(parseQueue.zIndex); + if ( typeof options._renderer._create !== "function" ) { + throw new Error("Invalid renderer defined"); + } + return options._renderer._create( parseQueue, options, document, queue, _html2canvas ); }; diff --git a/src/Util.js b/src/Util.js index 9dd865a..f17a70d 100644 --- a/src/Util.js +++ b/src/Util.js @@ -1,98 +1,89 @@ -/* - html2canvas @VERSION@ - 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, + 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 + // 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, + // parse options + svgRendering: false, // use svg powered rendering where available (FF11+) + iframeDefault: "default", + ignoreElements: "IFRAME|OBJECT|PARAM", + useOverflow: true, + letterRendering: false, - // render options + // 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; + 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); + 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"); - } + 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 ) { + _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.onpreloaded === "function") { + if ( options.onpreloaded( images ) === false ) { + return; + } + } + queue = _html2canvas.Parse( images, options ); - if (typeof options.onparsed === "function") { - if ( options.onparsed( queue ) === false ) { - return; - } - } + if (typeof options.onparsed === "function") { + if ( options.onparsed( queue ) === false ) { + return; + } + } - canvas = _html2canvas.Renderer( queue, options ); + canvas = _html2canvas.Renderer( queue, options ); - if (typeof options.onrendered === "function") { - options.onrendered( canvas ); - } + 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 ); + // 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 - }; + 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 -}; + Canvas: undefined // We are assuming this will be used +}; \ No newline at end of file diff --git a/src/html2canvas-post.txt b/src/html2canvas-post.txt deleted file mode 100644 index 77b193e..0000000 --- a/src/html2canvas-post.txt +++ /dev/null @@ -1,2 +0,0 @@ -window.html2canvas = html2canvas; -}(window, document)); diff --git a/src/html2canvas-pre.txt b/src/html2canvas-pre.txt deleted file mode 100644 index d9cc589..0000000 --- a/src/html2canvas-pre.txt +++ /dev/null @@ -1 +0,0 @@ -(function(window, document, undefined){ diff --git a/src/renderers/Canvas.js b/src/renderers/Canvas.js index 0b1a1bc..30cfe23 100644 --- a/src/renderers/Canvas.js +++ b/src/renderers/Canvas.js @@ -1,225 +1,216 @@ -/* - html2canvas @VERSION@ - 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 || {}; + 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 + 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"); + 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; - } else if ( options.flashcanvas !== undefined ){ - usingFlashcanvas = true; - h2clog("html2canvas: Renderer: canvas not available, using flashcanvas"); - var script = doc.createElement("script"); - script.src = options.flashcanvas; + if ( _createCalled !== false ) { + methods._create.apply( null, _createCalled ); + } + } + }); - script.onload = (function(script, func){ - var intervalFunc; + doc.body.appendChild( script ); - if (script.onload === undefined) { - // IE lack of support for script onload + } - if( script.onreadystatechange !== undefined ) { + methods = { + _create: function( zStack, options, doc, queue, _html2canvas ) { - intervalFunc = function() { - if (script.readyState !== "loaded" && script.readyState !== "complete") { - window.setTimeout( intervalFunc, 250 ); + if ( !canvasReadyToDraw ) { + _createCalled = arguments; + return canvas; + } - } else { - // it is loaded - func(); + 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; + + var drawShape = function(args) { + + var i, len = args.length; + ctx.beginPath(); + for ( i = 0; i < len; i++ ) { + ctx[ args[ i ].name ].apply( ctx, args[ i ]['arguments'] ); + } + ctx.closePath(); + ctx.fill(); + + }; + + 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 === "drawShape") { + drawShape(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 ); } - - }; - - 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(); - + } + ctx.drawImage.apply( ctx, renderItem['arguments'] ); } - - 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") { + break; + default: - 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 === "drawShape") { - - ( function( args ) { - - var i, len = args.length; - ctx.beginPath(); - for ( i = 0; i < len; i++ ) { - ctx[ args[ i ].name ].apply( ctx, args[ i ]['arguments'] ); - } - ctx.closePath(); - ctx.fill(); - })( 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"); + } + if (storageContext.clip){ + ctx.restore(); + } - 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"); + h2clog("html2canvas: Renderer: Canvas renderer done - returning canvas obj"); - ctx.drawImage( canvas, bounds.left, bounds.top, bounds.width, bounds.height, 0, 0, bounds.width, bounds.height ); - canvas = null; - return newCanvas; - } - } /*else { + 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 ) { @@ -233,10 +224,10 @@ _html2canvas.Renderer.Canvas = function( options ) { - return canvas; - } - }; + return canvas; + } + }; - return methods; + return methods; -}; +}; \ No newline at end of file