Switched build process to use grunt

This commit is contained in:
MoyuScript 2012-11-25 20:59:31 +02:00
parent 6675a219f3
commit c5d82acdf6
12 changed files with 2250 additions and 2262 deletions

61
grunt.js Normal file
View File

@ -0,0 +1,61 @@
/*global module:false*/
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: '<json:package.json>',
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: ['<banner:meta.banner>', '<banner:meta.pre>','src/*.js', 'src/renderers/Canvas.js', '<banner:meta.post>'],
dest: 'build/<%= pkg.name %>.js'
}
},
min: {
dist: {
src: ['<banner:meta.banner>', '<config:concat.dist.dest>'],
dest: 'build/<%= pkg.name %>.min.js'
}
},
watch: {
files: '<config:lint.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');
};

12
package.json Normal file
View File

@ -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"}]
}

View File

@ -1,10 +1,3 @@
/*
html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Released under MIT License
*/
"use strict"; "use strict";
var _html2canvas = {}, var _html2canvas = {},

View File

@ -1,332 +1,326 @@
/*
html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Contributor(s):
Niklas von Hertzen <http://www.twitter.com/niklasvh>
André Fiedler <http://www.twitter.com/sonnenkiste>
Released under MIT License
*/
(function(){ (function(){
_html2canvas.Generate = {}; _html2canvas.Generate = {};
var reGradients = [ var reGradients = [
/^(-webkit-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/, /^(-webkit-linear-gradient)\(([a-z\s]+)([\w\d\.\s,%\(\)]+)\)$/,
/^(-o-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,%\(\)-]+)\)$/, /^(-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,%\(\)]+)\)$/, /^(-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,%\(\)]+)\)$/, /^(-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,%\(\)]+)\)$/, /^(-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,%\(\)]+)\)$/ /^(-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 IE10 vendor prefix (-ms) support
* TODO: Add W3C gradient (linear-gradient) support * TODO: Add W3C gradient (linear-gradient) support
* TODO: Add old Webkit -webkit-gradient(radial, ...) support * TODO: Add old Webkit -webkit-gradient(radial, ...) support
* TODO: Maybe some RegExp optimizations are possible ;o) * TODO: Maybe some RegExp optimizations are possible ;o)
*/ */
_html2canvas.Generate.parseGradient = function(css, bounds) { _html2canvas.Generate.parseGradient = function(css, bounds) {
var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3; var gradient, i, len = reGradients.length, m1, stop, m2, m2Len, step, m3, tl,tr,br,bl;
for(i = 0; i < len; i+=1){ for(i = 0; i < len; i+=1){
m1 = css.match(reGradients[i]); m1 = css.match(reGradients[i]);
if(m1) break; if(m1) {
break;
}
} }
if(m1) { if(m1) {
switch(m1[1]) { switch(m1[1]) {
case '-webkit-linear-gradient': case '-webkit-linear-gradient':
case '-o-linear-gradient': case '-o-linear-gradient':
gradient = { gradient = {
type: 'linear', type: 'linear',
x0: null, x0: null,
y0: null, y0: null,
x1: null, x1: null,
y1: null, y1: null,
colorStops: [] colorStops: []
}; };
// get coordinates // get coordinates
m2 = m1[2].match(/\w+/g); m2 = m1[2].match(/\w+/g);
if(m2){ if(m2){
m2Len = m2.length; m2Len = m2.length;
for(i = 0; i < m2Len; i+=1){ for(i = 0; i < m2Len; i+=1){
switch(m2[i]) { switch(m2[i]) {
case 'top': case 'top':
gradient.y0 = 0; gradient.y0 = 0;
gradient.y1 = bounds.height; gradient.y1 = bounds.height;
break; break;
case 'right': case 'right':
gradient.x0 = bounds.width; gradient.x0 = bounds.width;
gradient.x1 = 0; gradient.x1 = 0;
break; break;
case 'bottom': case 'bottom':
gradient.y0 = bounds.height; gradient.y0 = bounds.height;
gradient.y1 = 0; gradient.y1 = 0;
break; break;
case 'left': case 'left':
gradient.x0 = 0; gradient.x0 = 0;
gradient.x1 = bounds.width; gradient.x1 = bounds.width;
break; break;
} }
} }
} }
if(gradient.x0 === null && gradient.x1 === null){ // center if(gradient.x0 === null && gradient.x1 === null){ // center
gradient.x0 = gradient.x1 = bounds.width / 2; gradient.x0 = gradient.x1 = bounds.width / 2;
} }
if(gradient.y0 === null && gradient.y1 === null){ // center if(gradient.y0 === null && gradient.y1 === null){ // center
gradient.y0 = gradient.y1 = bounds.height / 2; 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 case '-webkit-gradient':
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){ gradient = {
m2Len = m2.length; type: m1[2] === 'radial' ? 'circle' : m1[2], // TODO: Add radial gradient support for older mozilla definitions
step = 1 / Math.max(m2Len - 1, 1); x0: 0,
for(i = 0; i < m2Len; i+=1){ y0: 0,
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)?/); x1: 0,
if(m3[2]){ y1: 0,
stop = parseFloat(m3[2]); colorStops: []
if(m3[3] === '%'){ };
stop /= 100;
} else { // px - stupid opera // get coordinates
stop /= bounds.width; m2 = m1[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/);
} if(m2){
} else { gradient.x0 = (m2[1] * bounds.width) / 100;
stop = i * step; gradient.y0 = (m2[2] * bounds.height) / 100;
} gradient.x1 = (m2[3] * bounds.width) / 100;
gradient.colorStops.push({ gradient.y1 = (m2[4] * bounds.height) / 100;
color: m3[1], }
stop: stop
}); // 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; break;
case '-webkit-gradient': // TODO: add support for "30px 40px" sizes (webkit only)
}
}
gradient = { // color stops
type: m1[2] === 'radial' ? 'circle' : m1[2], // TODO: Add radial gradient support for older mozilla definitions 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);
x0: 0, if(m2){
y0: 0, m2Len = m2.length;
x1: 0, step = 1 / Math.max(m2Len - 1, 1);
y1: 0, for(i = 0; i < m2Len; i+=1){
colorStops: [] 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]);
// get coordinates if(m3[3] === '%'){
m2 = m1[3].match(/(\d{1,3})%?\s(\d{1,3})%?,\s(\d{1,3})%?\s(\d{1,3})%?/); stop /= 100;
if(m2){ } else { // px - stupid opera
gradient.x0 = (m2[1] * bounds.width) / 100; stop /= bounds.width;
gradient.y0 = (m2[2] * bounds.height) / 100;
gradient.x1 = (m2[3] * bounds.width) / 100;
gradient.y1 = (m2[4] * bounds.height) / 100;
} }
} else {
// get colors and stops stop = i * step;
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){ gradient.colorStops.push({
m2Len = m2.length; color: m3[1],
for(i = 0; i < m2Len; i+=1){ stop: stop
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; break;
gradient.colorStops.push({ }
color: m3[3],
stop: stop
});
}
}
break;
case '-moz-linear-gradient':
gradient = {
type: 'linear',
x0: 0,
y0: 0,
x1: 0,
y1: 0,
colorStops: []
};
// get coordinates
m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/);
// m2[1] == 0% -> left
// m2[1] == 50% -> center
// m2[1] == 100% -> right
// m2[2] == 0% -> top
// m2[2] == 50% -> center
// m2[2] == 100% -> bottom
if(m2){
gradient.x0 = (m2[1] * bounds.width) / 100;
gradient.y0 = (m2[2] * bounds.height) / 100;
gradient.x1 = bounds.width - gradient.x0;
gradient.y1 = bounds.height - gradient.y0;
}
// get colors and stops
m2 = m1[3].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}%)?)+/g);
if(m2){
m2Len = m2.length;
step = 1 / Math.max(m2Len - 1, 1);
for(i = 0; i < m2Len; i+=1){
m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%)?/);
if(m3[2]){
stop = parseFloat(m3[2]);
if(m3[3]){ // percentage
stop /= 100;
}
} else {
stop = i * step;
}
gradient.colorStops.push({
color: m3[1],
stop: stop
});
}
}
break;
case '-webkit-radial-gradient':
case '-moz-radial-gradient':
case '-o-radial-gradient':
gradient = {
type: 'circle',
x0: 0,
y0: 0,
x1: bounds.width,
y1: bounds.height,
cx: 0,
cy: 0,
rx: 0,
ry: 0,
colorStops: []
};
// center
m2 = m1[2].match(/(\d{1,3})%?\s(\d{1,3})%?/);
if(m2){
gradient.cx = (m2[1] * bounds.width) / 100;
gradient.cy = (m2[2] * bounds.height) / 100;
}
// size
m2 = m1[3].match(/\w+/);
m3 = m1[4].match(/[a-z-]*/);
if(m2 && m3){
switch(m3[0]){
case 'farthest-corner':
case 'cover': // is equivalent to farthest-corner
case '': // mozilla removes "cover" from definition :(
var tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2));
var tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
var br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
var bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2));
gradient.rx = gradient.ry = Math.max(tl, tr, br, bl);
break;
case 'closest-corner':
var tl = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.cy, 2));
var tr = Math.sqrt(Math.pow(gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
var br = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.y1 - gradient.cy, 2));
var bl = Math.sqrt(Math.pow(gradient.x1 - gradient.cx, 2) + Math.pow(gradient.cy, 2));
gradient.rx = gradient.ry = Math.min(tl, tr, br, bl);
break;
case 'farthest-side':
if(m2[0] === 'circle'){
gradient.rx = gradient.ry = Math.max(
gradient.cx,
gradient.cy,
gradient.x1 - gradient.cx,
gradient.y1 - gradient.cy
);
} else { // ellipse
gradient.type = m2[0];
gradient.rx = Math.max(
gradient.cx,
gradient.x1 - gradient.cx
);
gradient.ry = Math.max(
gradient.cy,
gradient.y1 - gradient.cy
);
}
break;
case 'closest-side':
case 'contain': // is equivalent to closest-side
if(m2[0] === 'circle'){
gradient.rx = gradient.ry = Math.min(
gradient.cx,
gradient.cy,
gradient.x1 - gradient.cx,
gradient.y1 - gradient.cy
);
} else { // ellipse
gradient.type = m2[0];
gradient.rx = Math.min(
gradient.cx,
gradient.x1 - gradient.cx
);
gradient.ry = Math.min(
gradient.cy,
gradient.y1 - gradient.cy
);
}
break;
// TODO: add support for "30px 40px" sizes (webkit only)
}
}
// color stops
m2 = m1[5].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\)(?:\s\d{1,3}(?:%|px))?)+/g);
if(m2){
m2Len = m2.length;
step = 1 / Math.max(m2Len - 1, 1);
for(i = 0; i < m2Len; i+=1){
m3 = m2[i].match(/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/);
if(m3[2]){
stop = parseFloat(m3[2]);
if(m3[3] === '%'){
stop /= 100;
} else { // px - stupid opera
stop /= bounds.width;
}
} else {
stop = i * step;
}
gradient.colorStops.push({
color: m3[1],
stop: stop
});
}
}
break;
}
} }
return gradient; return gradient;
}; };
_html2canvas.Generate.Gradient = function(src, bounds) { _html2canvas.Generate.Gradient = function(src, bounds) {
var canvas = document.createElement('canvas'), var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'), ctx = canvas.getContext('2d'),
gradient, grad, i, len, img; gradient, grad, i, len, img;
@ -340,96 +334,96 @@ _html2canvas.Generate.Gradient = function(src, bounds) {
img = new Image(); img = new Image();
if(gradient){ if(gradient){
if(gradient.type === 'linear'){ if(gradient.type === 'linear'){
grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1); grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1);
for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { for (i = 0, len = gradient.colorStops.length; i < len; i+=1) {
try { try {
grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color);
} }
catch(e) { catch(e) {
h2clog(['failed to add color stop: ', e, '; tried to add: ', gradient.colorStops[i], '; stop: ', i, '; in: ', src]); 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();
} }
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; return img;
}; };
_html2canvas.Generate.ListAlpha = function(number) { _html2canvas.Generate.ListAlpha = function(number) {
var tmp = "", var tmp = "",
modulus; modulus;
do { do {
modulus = number % 26; modulus = number % 26;
tmp = String.fromCharCode((modulus) + 64) + tmp; tmp = String.fromCharCode((modulus) + 64) + tmp;
number = number / 26; number = number / 26;
}while((number*26) > 26); }while((number*26) > 26);
return tmp; 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"], 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], decimal = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
roman = "", roman = "",
@ -437,18 +431,18 @@ _html2canvas.Generate.ListRoman = function(number) {
len = romanArray.length; len = romanArray.length;
if (number <= 0 || number >= 4000) { if (number <= 0 || number >= 4000) {
return number; return number;
} }
for (v=0; v < len; v+=1) { for (v=0; v < len; v+=1) {
while (number >= decimal[v]) { while (number >= decimal[v]) {
number -= decimal[v]; number -= decimal[v];
roman += romanArray[v]; roman += romanArray[v];
} }
} }
return roman; return roman;
}; };
})(); })();

File diff suppressed because it is too large Load Diff

View File

@ -1,360 +1,329 @@
/*
html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Released under MIT License
*/
_html2canvas.Preload = function( options ) { _html2canvas.Preload = function( options ) {
var images = { var images = {
numLoaded: 0, // also failed are counted here numLoaded: 0, // also failed are counted here
numFailed: 0, numFailed: 0,
numTotal: 0, numTotal: 0,
cleanupDone: false cleanupDone: false
}, },
pageOrigin, pageOrigin,
methods, 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, i,
count = 0, background_image,
element = options.elements[0] || document.body, src,
doc = element.ownerDocument, img,
domImages = doc.images, // TODO probably should limit it to images present in the element only elNodeType = false;
imgLen = domImages.length,
link = doc.createElement("a"),
supportCORS = (function( img ){
return (img.crossOrigin !== undefined);
})(new Image()),
timeoutTimer;
link.href = window.location.href; // Firefox fails with permission denied on pages with iframes
pageOrigin = link.protocol + link.host; try {
var contentsLen = contents.length;
for (i = 0; i < contentsLen; i+=1 ){
getImages(contents[i]);
}
}
catch( e ) {}
try {
elNodeType = el.nodeType;
} catch (ex) {
elNodeType = false;
h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message);
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(){ if (elNodeType === 1 || elNodeType === undefined){
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"){ // opera throws exception on external-content.html
options.complete(images); 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 if (/^(-webkit|-o|-moz|-ms|linear)-/.test( background_image )) {
function proxyGetImage(url, img, imageObj){
var callback_name,
scriptUrl = options.proxy,
script;
link.href = url; img = _html2canvas.Generate.Gradient( background_image, _html2canvas.Util.Bounds( el ) );
url = link.href; // work around for pages with base href="" set - WARNING: this may change the url
callback_name = 'html2canvas_' + (count++); if ( img !== undefined ){
imageObj.callbackname = callback_name; images[background_image] = {
img: img,
succeeded: true
};
images.numTotal++;
images.numLoaded++;
start();
}
if (scriptUrl.indexOf("?") > -1) {
scriptUrl += "&";
} else { } 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)){ // let's try with proxy instead
// if ( options.proxy ) {
var src = img.src;
img = new Image();
imageObj.img = img;
img.src = src;
var contents = _html2canvas.Util.Children(el), proxyGetImage( img.src, img, imageObj );
i, return;
background_image,
src,
img,
elNodeType = false;
// 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]);
// }
}
} }
catch( e ) {} }
// } images.numLoaded++;
try { images.numFailed++;
elNodeType = el.nodeType; imageObj.succeeded = false;
} catch (ex) { img.onerror = img.onload = null;
elNodeType = false; start();
h2clog("html2canvas: failed to access some element's nodeType - Exception: " + ex.message);
}
if (elNodeType === 1 || elNodeType === undefined){ };
// opera throws exception on external-content.html // TODO Opera has no load/error event for SVG images
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 // Opera ninja onload's cached images
/*
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
/*
window.setTimeout(function(){ window.setTimeout(function(){
if ( img.width !== 0 && imageObj.succeeded === undefined ) { if ( img.width !== 0 && imageObj.succeeded === undefined ) {
img.onload(); img.onload();
} }
}, 100); // needs a reflow for base64 encoded images? interestingly timeout of 0 doesn't work but 1 does. }, 100); // needs a reflow for base64 encoded images? interestingly timeout of 0 doesn't work but 1 does.
*/ */
} }
methods = { methods = {
loadImage: function( src ) { loadImage: function( src ) {
var img, imageObj; var img, imageObj;
if ( src && images[src] === undefined ) { if ( src && images[src] === undefined ) {
img = new Image(); img = new Image();
if ( src.match(/data:image\/.*;base64,/i) ) { if ( src.match(/data:image\/.*;base64,/i) ) {
img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''); img.src = src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, '');
imageObj = images[src] = { imageObj = images[src] = {
img: img img: img
}; };
images.numTotal++; images.numTotal++;
setImageLoadHandlers(img, imageObj); setImageLoadHandlers(img, imageObj);
} else if ( isSameOrigin( src ) || options.allowTaint === true ) { } else if ( isSameOrigin( src ) || options.allowTaint === true ) {
imageObj = images[src] = { imageObj = images[src] = {
img: img img: img
}; };
images.numTotal++; images.numTotal++;
setImageLoadHandlers(img, imageObj); setImageLoadHandlers(img, imageObj);
img.src = src; img.src = src;
} else if ( supportCORS && !options.allowTaint && options.useCORS ) { } else if ( supportCORS && !options.allowTaint && options.useCORS ) {
// attempt to load with CORS // attempt to load with CORS
img.crossOrigin = "anonymous"; img.crossOrigin = "anonymous";
imageObj = images[src] = { imageObj = images[src] = {
img: img img: img
}; };
images.numTotal++; images.numTotal++;
setImageLoadHandlers(img, imageObj); setImageLoadHandlers(img, imageObj);
img.src = src; img.src = src;
// work around for https://bugs.webkit.org/show_bug.cgi?id=80028 // work around for https://bugs.webkit.org/show_bug.cgi?id=80028
img.customComplete = function () { img.customComplete = function () {
if (!this.img.complete) { if (!this.img.complete) {
this.timer = window.setTimeout(this.img.customComplete, 100); this.timer = window.setTimeout(this.img.customComplete, 100);
} else { } else {
this.img.onerror(); this.img.onerror();
}
}.bind(imageObj);
img.customComplete();
} else if ( options.proxy ) {
imageObj = images[src] = {
img: img
};
images.numTotal++;
proxyGetImage( src, img, imageObj );
}
} }
}.bind(imageObj);
img.customComplete();
}, } else if ( options.proxy ) {
cleanupDOM: function(cause) { imageObj = images[src] = {
var img, src; img: img
if (!images.cleanupDone) { };
if (cause && typeof cause === "string") { images.numTotal++;
h2clog("html2canvas: Cleanup because: " + cause); proxyGetImage( src, img, imageObj );
} else { }
h2clog("html2canvas: Cleanup after timeout: " + options.timeout + " ms."); }
}
for (src in images) { },
if (images.hasOwnProperty(src)) { cleanupDOM: function(cause) {
img = images[src]; var img, src;
if (typeof img === "object" && img.callbackname && img.succeeded === undefined) { if (!images.cleanupDone) {
// cancel proxy image request if (cause && typeof cause === "string") {
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) h2clog("html2canvas: Cleanup because: " + cause);
try { } else {
delete window[img.callbackname]; // for all browser that support this h2clog("html2canvas: Cleanup after timeout: " + options.timeout + " ms.");
} 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);
}
} }
}; 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) { // cancel any pending requests
timeoutTimer = window.setTimeout(methods.cleanupDOM, options.timeout); if(window.stop !== undefined) {
} window.stop();
h2clog('html2canvas: Preload starts: finding background-images'); } else if(document.execCommand !== undefined) {
images.firstRun = true; document.execCommand("Stop", false);
}
getImages( element ); if (document.close !== undefined) {
document.close();
h2clog('html2canvas: Preload: Finding images'); }
// load <img> images images.cleanupDone = true;
for (i = 0; i < imgLen; i+=1){ if (!(cause && typeof cause === "string")) {
methods.loadImage( domImages[i].getAttribute( "src" ) ); 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 <img> 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;
}; };

View File

@ -1,81 +1,74 @@
/*
html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Released under MIT License
*/
function h2cRenderContext(width, height) { function h2cRenderContext(width, height) {
var storage = []; var storage = [];
return { return {
storage: storage, storage: storage,
width: width, width: width,
height: height, height: height,
fillRect: function () { fillRect: function () {
storage.push({ storage.push({
type: "function", type: "function",
name: "fillRect", name: "fillRect",
'arguments': arguments 'arguments': arguments
}); });
}, },
drawShape: function() { drawShape: function() {
var shape = []; var shape = [];
storage.push({ storage.push({
type: "function", type: "function",
name: "drawShape", name: "drawShape",
'arguments': shape '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
});
}
};
return {
moveTo: function() {
shape.push({
name: "moveTo",
'arguments': arguments
});
}, },
drawImage: function () { lineTo: function() {
storage.push({ shape.push({
type: "function", name: "lineTo",
name: "drawImage", 'arguments': arguments
'arguments': arguments });
});
}, },
fillText: function () { bezierCurveTo: function() {
storage.push({ shape.push({
type: "function", name: "bezierCurveTo",
name: "fillText", 'arguments': arguments
'arguments': arguments });
});
}, },
setVariable: function (variable, value) { quadraticCurveTo: function() {
storage.push({ shape.push({
type: "variable", name: "quadraticCurveTo",
name: variable, 'arguments': arguments
'arguments': value });
});
} }
}; };
},
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
});
}
};
} }

View File

@ -1,66 +1,57 @@
/*
html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Released under MIT License
*/
_html2canvas.Renderer = function(parseQueue, options){ _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){ stackChild = zStackChildren[s];
var subStacks = [],
stackValues = [],
zStackChildren = zStack.children,
s,
i,
stackLen,
zValue,
zLen,
stackChild,
b,
subStackLen;
if (stackChild.children && stackChild.children.length > 0){
for (s = 0, zLen = zStackChildren.length; s < zLen; s+=1){ subStacks.push(stackChild);
stackValues.push(stackChild.zindex);
stackChild = zStackChildren[s]; }else{
queue.push(stackChild);
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;
}
}
}
} }
stackValues.sort(function(a, b) {
return a - b;
});
sortZ(parseQueue.zIndex); for (i = 0, stackLen = stackValues.length; i < stackLen; i+=1){
if ( typeof options._renderer._create !== "function" ) { zValue = stackValues[i];
throw new Error("Invalid renderer defined"); 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 );
}; };

View File

@ -1,98 +1,89 @@
/*
html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Released under MIT License
*/
html2canvas = function( elements, opts ) { html2canvas = function( elements, opts ) {
var queue, var queue,
canvas, canvas,
options = { options = {
// general // general
logging: false, logging: false,
elements: elements, elements: elements,
// preload options // preload options
proxy: "http://html2canvas.appspot.com/", proxy: "http://html2canvas.appspot.com/",
timeout: 0, // no timeout timeout: 0, // no timeout
useCORS: false, // try to load images as CORS (where available), before falling back to proxy 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 allowTaint: false, // whether to allow images to taint the canvas, won't need proxy if set to true
// parse options // parse options
svgRendering: false, // use svg powered rendering where available (FF11+) svgRendering: false, // use svg powered rendering where available (FF11+)
iframeDefault: "default", iframeDefault: "default",
ignoreElements: "IFRAME|OBJECT|PARAM", ignoreElements: "IFRAME|OBJECT|PARAM",
useOverflow: true, useOverflow: true,
letterRendering: false, letterRendering: false,
// render options // render options
flashcanvas: undefined, // path to flashcanvas flashcanvas: undefined, // path to flashcanvas
width: null, width: null,
height: null, height: null,
taintTest: true, // do a taint test with all images before applying to canvas taintTest: true, // do a taint test with all images before applying to canvas
renderer: "Canvas" renderer: "Canvas"
}, renderer; }, renderer;
options = _html2canvas.Util.Extend(opts, options); options = _html2canvas.Util.Extend(opts, options);
if (typeof options.renderer === "string" && _html2canvas.Renderer[options.renderer] !== undefined) { if (typeof options.renderer === "string" && _html2canvas.Renderer[options.renderer] !== undefined) {
options._renderer = _html2canvas.Renderer[options.renderer]( options ); options._renderer = _html2canvas.Renderer[options.renderer]( options );
} else if (typeof options.renderer === "function") { } else if (typeof options.renderer === "function") {
options._renderer = options.renderer( options ); options._renderer = options.renderer( options );
} else { } else {
throw("Unknown renderer"); throw("Unknown renderer");
} }
_html2canvas.logging = options.logging; _html2canvas.logging = options.logging;
options.complete = function( images ) { options.complete = function( images ) {
if (typeof options.onpreloaded === "function") { if (typeof options.onpreloaded === "function") {
if ( options.onpreloaded( images ) === false ) { if ( options.onpreloaded( images ) === false ) {
return; return;
} }
} }
queue = _html2canvas.Parse( images, options ); queue = _html2canvas.Parse( images, options );
if (typeof options.onparsed === "function") { if (typeof options.onparsed === "function") {
if ( options.onparsed( queue ) === false ) { if ( options.onparsed( queue ) === false ) {
return; return;
} }
} }
canvas = _html2canvas.Renderer( queue, options ); canvas = _html2canvas.Renderer( queue, options );
if (typeof options.onrendered === "function") { if (typeof options.onrendered === "function") {
options.onrendered( canvas ); options.onrendered( canvas );
} }
}; };
// for pages without images, we still want this to be async, i.e. return methods before executing // for pages without images, we still want this to be async, i.e. return methods before executing
window.setTimeout( function(){ window.setTimeout( function(){
_html2canvas.Preload( options ); _html2canvas.Preload( options );
}, 0 ); }, 0 );
return { return {
render: function( queue, opts ) { render: function( queue, opts ) {
return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) ); return _html2canvas.Renderer( queue, _html2canvas.Util.Extend(opts, options) );
}, },
parse: function( images, opts ) { parse: function( images, opts ) {
return _html2canvas.Parse( images, _html2canvas.Util.Extend(opts, options) ); return _html2canvas.Parse( images, _html2canvas.Util.Extend(opts, options) );
}, },
preload: function( opts ) { preload: function( opts ) {
return _html2canvas.Preload( _html2canvas.Util.Extend(opts, options) ); return _html2canvas.Preload( _html2canvas.Util.Extend(opts, options) );
}, },
log: h2clog log: h2clog
}; };
}; };
html2canvas.log = h2clog; // for renderers html2canvas.log = h2clog; // for renderers
html2canvas.Renderer = { html2canvas.Renderer = {
Canvas: undefined // We are assuming this will be used Canvas: undefined // We are assuming this will be used
}; };

View File

@ -1,2 +0,0 @@
window.html2canvas = html2canvas;
}(window, document));

View File

@ -1 +0,0 @@
(function(window, document, undefined){

View File

@ -1,225 +1,216 @@
/*
html2canvas @VERSION@ <http://html2canvas.hertzen.com>
Copyright (c) 2011 Niklas von Hertzen. All rights reserved.
http://www.twitter.com/niklasvh
Released under MIT License
*/
_html2canvas.Renderer.Canvas = function( options ) { _html2canvas.Renderer.Canvas = function( options ) {
options = options || {}; options = options || {};
var doc = document, var doc = document,
canvas = options.canvas || doc.createElement('canvas'), canvas = options.canvas || doc.createElement('canvas'),
usingFlashcanvas = false, usingFlashcanvas = false,
_createCalled = false, _createCalled = false,
canvasReadyToDraw = false, canvasReadyToDraw = false,
methods, methods,
flashMaxSize = 2880; // flash bitmap limited to 2880x2880px // http://stackoverflow.com/questions/2033792/argumenterror-error-2015-invalid-bitmapdata flashMaxSize = 2880; // flash bitmap limited to 2880x2880px // http://stackoverflow.com/questions/2033792/argumenterror-error-2015-invalid-bitmapdata
if (canvas.getContext){ if (canvas.getContext){
h2clog("html2canvas: Renderer: using canvas renderer"); 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; canvasReadyToDraw = true;
} else if ( options.flashcanvas !== undefined ){ if ( _createCalled !== false ) {
usingFlashcanvas = true; methods._create.apply( null, _createCalled );
h2clog("html2canvas: Renderer: canvas not available, using flashcanvas"); }
var script = doc.createElement("script"); }
script.src = options.flashcanvas; });
script.onload = (function(script, func){ doc.body.appendChild( script );
var intervalFunc;
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 ( !canvasReadyToDraw ) {
if (script.readyState !== "loaded" && script.readyState !== "complete") { _createCalled = arguments;
window.setTimeout( intervalFunc, 250 ); return canvas;
}
} else { var ctx = canvas.getContext("2d"),
// it is loaded storageContext,
func(); 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 );
} }
}
}; ctx.drawImage.apply( ctx, renderItem['arguments'] );
window.setTimeout( intervalFunc, 250 );
} else {
h2clog("html2canvas: Renderer: Can't track when flashcanvas is loaded");
}
} else {
return func;
}
})(script, function(){
if (typeof window.FlashCanvas !== "undefined") {
h2clog("html2canvas: Renderer: Flashcanvas initialized");
window.FlashCanvas.initElement( canvas );
canvasReadyToDraw = true;
if ( _createCalled !== false ) {
methods._create.apply( null, _createCalled );
}
}
});
doc.body.appendChild( script );
}
methods = {
_create: function( zStack, options, doc, queue, _html2canvas ) {
if ( !canvasReadyToDraw ) {
_createCalled = arguments;
return canvas;
}
var ctx = canvas.getContext("2d"),
storageContext,
i,
queueLen,
a,
newCanvas,
bounds,
testCanvas = document.createElement("canvas"),
hasCTX = ( testCanvas.getContext !== undefined ),
storageLen,
renderItem,
testctx = ( hasCTX ) ? testCanvas.getContext("2d") : {},
safeImages = [],
fstyle;
canvas.width = canvas.style.width = (!usingFlashcanvas) ? options.width || zStack.ctx.width : Math.min(flashMaxSize, (options.width || zStack.ctx.width) );
canvas.height = canvas.style.height = (!usingFlashcanvas) ? options.height || zStack.ctx.height : Math.min(flashMaxSize, (options.height || zStack.ctx.height) );
fstyle = ctx.fillStyle;
ctx.fillStyle = zStack.backgroundColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = fstyle;
if ( options.svgRendering && zStack.svgRender !== undefined ) {
// TODO: enable async rendering to support this
ctx.drawImage( zStack.svgRender, 0, 0 );
} else {
for ( i = 0, queueLen = queue.length; i < queueLen; i+=1 ) {
storageContext = queue.splice(0, 1)[0];
storageContext.canvasPosition = storageContext.canvasPosition || {};
//this.canvasRenderContext(storageContext,parentctx);
// set common settings for canvas
ctx.textBaseline = "bottom";
if (storageContext.clip){
ctx.save();
ctx.beginPath();
// console.log(storageContext);
ctx.rect(storageContext.clip.left, storageContext.clip.top, storageContext.clip.width, storageContext.clip.height);
ctx.clip();
} }
}
if (storageContext.ctx.storage){
for (a = 0, storageLen = storageContext.ctx.storage.length; a < storageLen; a+=1){
renderItem = storageContext.ctx.storage[a];
switch(renderItem.type){ break;
case "variable": default:
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") {
( 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) { h2clog("html2canvas: Renderer: Canvas renderer done - returning canvas obj");
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 ); queueLen = options.elements.length;
canvas = null;
return newCanvas; if (queueLen === 1) {
} if (typeof options.elements[ 0 ] === "object" && options.elements[ 0 ].nodeName !== "BODY" && usingFlashcanvas === false) {
} /*else { // 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 // TODO clip and resize multiple elements
for ( i = 0; i < queueLen; i+=1 ) { for ( i = 0; i < queueLen; i+=1 ) {
@ -233,10 +224,10 @@ _html2canvas.Renderer.Canvas = function( options ) {
return canvas; return canvas;
} }
}; };
return methods; return methods;
}; };