mirror of
https://github.com/niklasvh/html2canvas.git
synced 2023-08-10 21:13:10 +03:00
Merge remote-tracking branch 'niklasvh/develop' into develop
Conflicts: src/Core.js src/Parse.js src/Util.js
This commit is contained in:
commit
473ff45267
4
.gitignore
vendored
4
.gitignore
vendored
@ -4,6 +4,7 @@
|
||||
/tests/cache/
|
||||
/tests/flashcanvas.html
|
||||
/lib/
|
||||
/bin/
|
||||
/build/
|
||||
image.jpg
|
||||
/.project
|
||||
@ -12,4 +13,5 @@ node_modules/
|
||||
.envrc
|
||||
server.js
|
||||
*.sublime-workspace
|
||||
chromedriver.log
|
||||
chromedriver.log
|
||||
*.baseline
|
37
LICENSE
37
LICENSE
@ -1,21 +1,22 @@
|
||||
/*
|
||||
The MIT License
|
||||
Copyright (c) 2012 Niklas von Hertzen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
16
grunt.js
16
grunt.js
@ -13,7 +13,7 @@ module.exports = function(grunt) {
|
||||
post: '})(window,document);'
|
||||
},
|
||||
lint: {
|
||||
files: ['grunt.js', 'build/<%= pkg.name %>.js']
|
||||
files: ['build/<%= pkg.name %>.js']
|
||||
},
|
||||
qunit: {
|
||||
files: ['tests/qunit/index.html']
|
||||
@ -55,7 +55,19 @@ module.exports = function(grunt) {
|
||||
uglify: {}
|
||||
});
|
||||
|
||||
var selenium = require("./tests/selenium.js");
|
||||
grunt.registerTask('webdriver', 'Browser render tests', function(arg1) {
|
||||
|
||||
var done = this.async();
|
||||
|
||||
if (arguments.length === 0) {
|
||||
selenium.tests();
|
||||
} else {
|
||||
selenium[arg1].apply(null, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
// Default task.
|
||||
grunt.registerTask('default', 'concat lint qunit min');
|
||||
grunt.registerTask('default', 'concat lint qunit webdriver min');
|
||||
|
||||
};
|
||||
|
@ -6,7 +6,14 @@
|
||||
"author": {
|
||||
"name":"Niklas von Hertzen (@niklasvh)"
|
||||
},
|
||||
"dependencies": {
|
||||
"base64-arraybuffer": ">= 0.1.0",
|
||||
"png-js": ">= 0.1.1",
|
||||
"webdriver.js": ">= 0.1.0"
|
||||
},
|
||||
"homepage": "http://html2canvas.hertzen.com",
|
||||
"licenses": [{"type": "MIT"}]
|
||||
"licenses": [{
|
||||
"type": "MIT"
|
||||
}]
|
||||
|
||||
}
|
53
readme.md
53
readme.md
@ -7,13 +7,12 @@ html2canvas
|
||||
|
||||
|
||||
###How does it work?###
|
||||
The script renders the current page as a canvas image, by reading the DOM and the different styles applied to the elements. However, as many elements are displayed differently on different browsers and operating systems (such as form elements such as radio buttons or checkboxes) as well as
|
||||
The script renders the current page as a canvas image, by reading the DOM and the different styles applied to the elements.
|
||||
|
||||
It does <b>not require any rendering from the server</b>, as the whole image is created on the <b>clients browser</b>. However, for browsers without <code>canvas</code> support alternatives such as <a href="http://flashcanvas.net/">flashcanvas</a> or <a href="http://excanvas.sourceforge.net/">ExplorerCanvas</a> are necessary to create the image.
|
||||
It does <b>not require any rendering from the server</b>, as the whole image is created on the <b>clients browser</b>. However, as it is heavily dependent on the browser, this library is *not suitable* to be used on for example on node.js.
|
||||
It doesn't magically circumvent and browser content policy restrictions either, so rendering cross origin content will require a <a href="https://github.com/niklasvh/html2canvas/wiki/Proxies">proxy</a> to get the content to the <a href="http://en.wikipedia.org/wiki/Same_origin_policy">same origin</a>.
|
||||
|
||||
Additionally, to render <code>iframe</code> content or images situated outside of the <a href="http://en.wikipedia.org/wiki/Same_origin_policy">same origin policy</a> a proxy will be necessary to load the content to the users browser.
|
||||
|
||||
The script is still in a very experimental state, so I don't recommend using it in a production environment nor start building applications with it yet, as there will be still major changes made. However, please do test it out and report your findings, especially if something should be working, but is displaying it incorrectly.
|
||||
The script is still in a **very experimental state**, so I don't recommend using it in a production environment nor start building applications with it yet, as there will be still major changes made.
|
||||
|
||||
###Browser compatibility###
|
||||
|
||||
@ -21,8 +20,8 @@ The script should work fine on the following browsers:
|
||||
|
||||
* Firefox 3.5+
|
||||
* Google Chrome
|
||||
* Newer versions of Opera (exactly how new is yet to be determined)
|
||||
* >=IE9 (Older versions compatible with the use of flashcanvas)
|
||||
* Opera 12+
|
||||
* IE9+
|
||||
|
||||
Note that the compatibility will most likely be increased in future builds, as many of the current restrictions have at least partial work arounds, which can be used with older browser versions.
|
||||
|
||||
@ -46,6 +45,41 @@ To access the created canvas, provide the `onrendered` event in the options whic
|
||||
}
|
||||
});
|
||||
|
||||
### Building ###
|
||||
|
||||
The library uses <a href="http://gruntjs.com/">grunt</a> for building. Alternatively, you can download ready builds from the <a href="https://github.com/niklasvh/html2canvas/downloads">downloads page</a>.
|
||||
|
||||
Run the full build process (including lint, qunit and webdriver tests):
|
||||
|
||||
$ grunt
|
||||
|
||||
Skip lint and tests and simply build from source:
|
||||
|
||||
$ grunt concat
|
||||
$ grunt min
|
||||
|
||||
### Running tests ###
|
||||
|
||||
The library has two sets of tests. The first set is a number of qunit tests that check that different values parsed by browsers are correctly converted in html2canvas. To run these tests with grunt you'll need <a href="http://phantomjs.org/">phantomjs</a>.
|
||||
|
||||
The other set of tests run Firefox, Chrome and Internet Explorer with <a href="https://github.com/niklasvh/webdriver.js">webdriver</a>. The selenium standalone server (runs on Java) is required for these tests and can be downloaded from <a href="http://code.google.com/p/selenium/downloads/list">here</a>. They capture an actual screenshot from the test pages and compare the image to the screenshot created by html2canvas and calculate the percentage differences. These tests generally aren't expected to provide 100% matches, but while commiting changes, these should generally not go decrease from the baseline values.
|
||||
|
||||
If you didn't download `html2canvas` from `npm`, start by downloading the dependencies:
|
||||
|
||||
$ npm update
|
||||
|
||||
Run qunit tests:
|
||||
|
||||
$ grunt test
|
||||
|
||||
Run webdriver tests:
|
||||
|
||||
$ java -jar /path/to/selenium-server-standalone-2.xx.x.jar
|
||||
$ grunt webdriver
|
||||
|
||||
Commiting improvements in baseline values:
|
||||
|
||||
$ grunt webdriver:baseline
|
||||
|
||||
### Examples ###
|
||||
|
||||
@ -53,6 +87,11 @@ For more information and examples, please visit the <a href="http://html2canvas.
|
||||
|
||||
### Changelog ###
|
||||
|
||||
v0.40 -
|
||||
* Switched to using grunt for building
|
||||
* Reformatted all tests to small units to test specific features
|
||||
* Added rendering tests with <a href="https://github.com/niklasvh/webdriver.js">webdriver</a>
|
||||
|
||||
v0.34 - 26.6.2012
|
||||
|
||||
* Removed (last?) jQuery dependencies (<a href="https://github.com/niklasvh/html2canvas/commit/343b86705fe163766fcf735eb0217130e4bd5b17">niklasvh</a>)
|
||||
|
348
src/Core.js
348
src/Core.js
@ -7,28 +7,28 @@ html2canvas;
|
||||
|
||||
|
||||
function h2clog(a) {
|
||||
if (_html2canvas.logging && window.console && window.console.log) {
|
||||
window.console.log(a);
|
||||
}
|
||||
if (_html2canvas.logging && window.console && window.console.log) {
|
||||
window.console.log(a);
|
||||
}
|
||||
}
|
||||
|
||||
_html2canvas.Util = {};
|
||||
|
||||
_html2canvas.Util.backgroundImage = function (src) {
|
||||
|
||||
if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) {
|
||||
return src;
|
||||
}
|
||||
|
||||
if (src.toLowerCase().substr( 0, 5 ) === 'url("') {
|
||||
src = src.substr( 5 );
|
||||
src = src.substr( 0, src.length - 2 );
|
||||
} else {
|
||||
src = src.substr( 4 );
|
||||
src = src.substr( 0, src.length - 1 );
|
||||
}
|
||||
|
||||
if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) {
|
||||
return src;
|
||||
}
|
||||
|
||||
if (src.toLowerCase().substr( 0, 5 ) === 'url("') {
|
||||
src = src.substr( 5 );
|
||||
src = src.substr( 0, src.length - 2 );
|
||||
} else {
|
||||
src = src.substr( 4 );
|
||||
src = src.substr( 0, src.length - 1 );
|
||||
}
|
||||
|
||||
return src;
|
||||
};
|
||||
|
||||
_html2canvas.Util.parseBackgroundImage = function (value) {
|
||||
@ -131,196 +131,178 @@ _html2canvas.Util.parseBackgroundImage = function (value) {
|
||||
};
|
||||
|
||||
_html2canvas.Util.Bounds = function getBounds (el) {
|
||||
var clientRect,
|
||||
bounds = {};
|
||||
var clientRect,
|
||||
bounds = {};
|
||||
|
||||
if (el.getBoundingClientRect){
|
||||
clientRect = el.getBoundingClientRect();
|
||||
if (el.getBoundingClientRect){
|
||||
clientRect = el.getBoundingClientRect();
|
||||
|
||||
|
||||
// TODO add scroll position to bounds, so no scrolling of window necessary
|
||||
bounds.top = clientRect.top;
|
||||
bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height);
|
||||
bounds.left = clientRect.left;
|
||||
// TODO add scroll position to bounds, so no scrolling of window necessary
|
||||
bounds.top = clientRect.top;
|
||||
bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height);
|
||||
bounds.left = clientRect.left;
|
||||
|
||||
// older IE doesn't have width/height, but top/bottom instead
|
||||
bounds.width = clientRect.width || (clientRect.right - clientRect.left);
|
||||
bounds.height = clientRect.height || (clientRect.bottom - clientRect.top);
|
||||
// older IE doesn't have width/height, but top/bottom instead
|
||||
bounds.width = clientRect.width || (clientRect.right - clientRect.left);
|
||||
bounds.height = clientRect.height || (clientRect.bottom - clientRect.top);
|
||||
|
||||
return bounds;
|
||||
return bounds;
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_html2canvas.Util.getCSS = function (el, attribute) {
|
||||
// return $(el).css(attribute);
|
||||
// return $(el).css(attribute);
|
||||
|
||||
var val,
|
||||
isBackgroundSizePosition = !!attribute.match( /^background(Size|Position)$/ );
|
||||
|
||||
function toPX( attribute, val ) {
|
||||
var rsLeft = el.runtimeStyle && el.runtimeStyle[ attribute ],
|
||||
left,
|
||||
style = el.style;
|
||||
function toPX( attribute, val ) {
|
||||
var rsLeft = el.runtimeStyle && el.runtimeStyle[ attribute ],
|
||||
left,
|
||||
style = el.style;
|
||||
|
||||
// Check if we are not dealing with pixels, (Opera has issues with this)
|
||||
// Ported from jQuery css.js
|
||||
// From the awesome hack by Dean Edwards
|
||||
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
|
||||
// Check if we are not dealing with pixels, (Opera has issues with this)
|
||||
// Ported from jQuery css.js
|
||||
// From the awesome hack by Dean Edwards
|
||||
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
|
||||
|
||||
// If we're not dealing with a regular pixel number
|
||||
// but a number that has a weird ending, we need to convert it to pixels
|
||||
// If we're not dealing with a regular pixel number
|
||||
// but a number that has a weird ending, we need to convert it to pixels
|
||||
|
||||
if ( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( val ) && /^-?\d/.test( val ) ) {
|
||||
if ( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( val ) && /^-?\d/.test( val ) ) {
|
||||
|
||||
// Remember the original values
|
||||
left = style.left;
|
||||
// Remember the original values
|
||||
left = style.left;
|
||||
|
||||
// Put in the new values to get a computed value out
|
||||
if ( rsLeft ) {
|
||||
el.runtimeStyle.left = el.currentStyle.left;
|
||||
}
|
||||
style.left = attribute === "fontSize" ? "1em" : (val || 0);
|
||||
val = style.pixelLeft + "px";
|
||||
// Put in the new values to get a computed value out
|
||||
if ( rsLeft ) {
|
||||
el.runtimeStyle.left = el.currentStyle.left;
|
||||
}
|
||||
style.left = attribute === "fontSize" ? "1em" : (val || 0);
|
||||
val = style.pixelLeft + "px";
|
||||
|
||||
// Revert the changed values
|
||||
style.left = left;
|
||||
if ( rsLeft ) {
|
||||
el.runtimeStyle.left = rsLeft;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!/^(thin|medium|thick)$/i.test( val )) {
|
||||
return Math.round(parseFloat( val )) + "px";
|
||||
}
|
||||
|
||||
return val;
|
||||
// Revert the changed values
|
||||
style.left = left;
|
||||
if ( rsLeft ) {
|
||||
el.runtimeStyle.left = rsLeft;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ( window.getComputedStyle ) {
|
||||
if ( previousElement !== el ) {
|
||||
computedCSS = document.defaultView.getComputedStyle(el, null);
|
||||
}
|
||||
val = computedCSS[ attribute ];
|
||||
|
||||
if ( isBackgroundSizePosition ) {
|
||||
|
||||
val = (val.split(",")[0] || "0 0").split(" ");
|
||||
|
||||
val[ 0 ] = ( val[0].indexOf( "%" ) === -1 ) ? toPX( attribute + "X", val[ 0 ] ) : val[ 0 ];
|
||||
val[ 1 ] = ( val[1] === undefined ) ? val[0] : val[1]; // IE 9 doesn't return double digit always
|
||||
val[ 1 ] = ( val[1].indexOf( "%" ) === -1 ) ? toPX( attribute + "Y", val[ 1 ] ) : val[ 1 ];
|
||||
} else if ( /border(Top|Bottom)(Left|Right)Radius/.test( attribute) ) {
|
||||
var arr = val.split(" ");
|
||||
if ( arr.length <= 1 ) {
|
||||
arr[ 1 ] = arr[ 0 ];
|
||||
}
|
||||
arr[ 0 ] = parseInt( arr[ 0 ], 10 );
|
||||
arr[ 1 ] = parseInt( arr[ 1 ], 10 );
|
||||
val = arr;
|
||||
}
|
||||
|
||||
} else if ( el.currentStyle ) {
|
||||
// IE 9>
|
||||
if ( isBackgroundSizePosition ) {
|
||||
// Older IE uses -x and -y
|
||||
val = [ toPX( attribute + "X", el.currentStyle[ attribute + "X" ] ), toPX( attribute + "Y", el.currentStyle[ attribute + "Y" ] ) ];
|
||||
} else {
|
||||
|
||||
val = toPX( attribute, el.currentStyle[ attribute ] );
|
||||
|
||||
if (/^(border)/i.test( attribute ) && /^(medium|thin|thick)$/i.test( val )) {
|
||||
switch (val) {
|
||||
case "thin":
|
||||
val = "1px";
|
||||
break;
|
||||
case "medium":
|
||||
val = "0px"; // this is wrong, it should be 3px but IE uses medium for no border as well.. TODO find a work around
|
||||
break;
|
||||
case "thick":
|
||||
val = "5px";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!/^(thin|medium|thick)$/i.test( val )) {
|
||||
return Math.round(parseFloat( val )) + "px";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return val;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//return $(el).css(attribute);
|
||||
|
||||
if ( window.getComputedStyle ) {
|
||||
if ( previousElement !== el ) {
|
||||
computedCSS = document.defaultView.getComputedStyle(el, null);
|
||||
}
|
||||
val = computedCSS[ attribute ];
|
||||
|
||||
if ( isBackgroundSizePosition ) {
|
||||
|
||||
val = (val.split(",")[0] || "0 0").split(" ");
|
||||
|
||||
val[ 0 ] = ( val[0].indexOf( "%" ) === -1 ) ? toPX( attribute + "X", val[ 0 ] ) : val[ 0 ];
|
||||
val[ 1 ] = ( val[1] === undefined ) ? val[0] : val[1]; // IE 9 doesn't return double digit always
|
||||
val[ 1 ] = ( val[1].indexOf( "%" ) === -1 ) ? toPX( attribute + "Y", val[ 1 ] ) : val[ 1 ];
|
||||
} else if ( /border(Top|Bottom)(Left|Right)Radius/.test( attribute) ) {
|
||||
var arr = val.split(" ");
|
||||
if ( arr.length <= 1 ) {
|
||||
arr[ 1 ] = arr[ 0 ];
|
||||
}
|
||||
arr[ 0 ] = parseInt( arr[ 0 ], 10 );
|
||||
arr[ 1 ] = parseInt( arr[ 1 ], 10 );
|
||||
val = arr;
|
||||
}
|
||||
|
||||
} else if ( el.currentStyle ) {
|
||||
// IE 9>
|
||||
if ( isBackgroundSizePosition ) {
|
||||
// Older IE uses -x and -y
|
||||
val = [ toPX( attribute + "X", el.currentStyle[ attribute + "X" ] ), toPX( attribute + "Y", el.currentStyle[ attribute + "Y" ] ) ];
|
||||
} else {
|
||||
|
||||
val = toPX( attribute, el.currentStyle[ attribute ] );
|
||||
|
||||
if (/^(border)/i.test( attribute ) && /^(medium|thin|thick)$/i.test( val )) {
|
||||
switch (val) {
|
||||
case "thin":
|
||||
val = "1px";
|
||||
break;
|
||||
case "medium":
|
||||
val = "0px"; // this is wrong, it should be 3px but IE uses medium for no border as well.. TODO find a work around
|
||||
break;
|
||||
case "thick":
|
||||
val = "5px";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
};
|
||||
|
||||
|
||||
function backgroundBoundsFactory( prop, el, bounds, image ) {
|
||||
// TODO add support for multi image backgrounds
|
||||
// TODO add support for multi image backgrounds
|
||||
|
||||
var bgposition = _html2canvas.Util.getCSS( el, prop ) ,
|
||||
topPos,
|
||||
left,
|
||||
percentage,
|
||||
val;
|
||||
var bgposition = _html2canvas.Util.getCSS( el, prop ) ,
|
||||
topPos,
|
||||
left,
|
||||
percentage,
|
||||
val;
|
||||
|
||||
if (bgposition.length === 1){
|
||||
val = bgposition;
|
||||
if (bgposition.length === 1){
|
||||
val = bgposition;
|
||||
|
||||
bgposition = [];
|
||||
bgposition = [];
|
||||
|
||||
bgposition[0] = val;
|
||||
bgposition[1] = val;
|
||||
}
|
||||
bgposition[0] = val;
|
||||
bgposition[1] = val;
|
||||
}
|
||||
|
||||
if (bgposition[0].toString().indexOf("%") !== -1){
|
||||
percentage = (parseFloat(bgposition[0])/100);
|
||||
left = ((bounds.width * percentage)-(image.width*percentage));
|
||||
} else {
|
||||
left = parseInt(bgposition[0],10);
|
||||
}
|
||||
|
||||
|
||||
if (bgposition[0].toString().indexOf("%") !== -1){
|
||||
percentage = (parseFloat(bgposition[0])/100);
|
||||
left = ((bounds.width * percentage)-(image.width*percentage));
|
||||
|
||||
}else{
|
||||
left = parseInt(bgposition[0],10);
|
||||
}
|
||||
|
||||
if (bgposition[1].toString().indexOf("%") !== -1){
|
||||
|
||||
percentage = (parseFloat(bgposition[1])/100);
|
||||
topPos = ((bounds.height * percentage)-(image.height*percentage));
|
||||
}else{
|
||||
topPos = parseInt(bgposition[1],10);
|
||||
}
|
||||
if (bgposition[1].toString().indexOf("%") !== -1){
|
||||
percentage = (parseFloat(bgposition[1])/100);
|
||||
topPos = ((bounds.height * percentage)-(image.height*percentage));
|
||||
} else {
|
||||
topPos = parseInt(bgposition[1],10);
|
||||
}
|
||||
|
||||
return [left, topPos];
|
||||
}
|
||||
|
||||
_html2canvas.Util.BackgroundPosition = function( el, bounds, image ) {
|
||||
var result = backgroundBoundsFactory( 'backgroundPosition', el, bounds, image );
|
||||
return { left: result[0], top: result[1] };
|
||||
};
|
||||
|
||||
_html2canvas.Util.BackgroundSize = function( el, bounds, image ) {
|
||||
var result = backgroundBoundsFactory( 'backgroundSize', el, bounds, image );
|
||||
return { width: result[1], height: result[0] };
|
||||
};
|
||||
|
||||
_html2canvas.Util.Extend = function (options, defaults) {
|
||||
for (var key in options) {
|
||||
if (options.hasOwnProperty(key)) {
|
||||
defaults[key] = options[key];
|
||||
}
|
||||
for (var key in options) {
|
||||
if (options.hasOwnProperty(key)) {
|
||||
defaults[key] = options[key];
|
||||
}
|
||||
return defaults;
|
||||
}
|
||||
return defaults;
|
||||
};
|
||||
|
||||
|
||||
@ -333,43 +315,43 @@ _html2canvas.Util.Extend = function (options, defaults) {
|
||||
_html2canvas.Util.Children = function( elem ) {
|
||||
|
||||
|
||||
var children;
|
||||
try {
|
||||
var children;
|
||||
try {
|
||||
|
||||
children = (elem.nodeName && elem.nodeName.toUpperCase() === "IFRAME") ?
|
||||
elem.contentDocument || elem.contentWindow.document : (function( array ){
|
||||
var ret = [];
|
||||
children = (elem.nodeName && elem.nodeName.toUpperCase() === "IFRAME") ?
|
||||
elem.contentDocument || elem.contentWindow.document : (function( array ){
|
||||
var ret = [];
|
||||
|
||||
if ( array !== null ) {
|
||||
if ( array !== null ) {
|
||||
|
||||
(function( first, second ) {
|
||||
var i = first.length,
|
||||
j = 0;
|
||||
|
||||
if ( typeof second.length === "number" ) {
|
||||
for ( var l = second.length; j < l; j++ ) {
|
||||
first[ i++ ] = second[ j ];
|
||||
}
|
||||
|
||||
} else {
|
||||
while ( second[j] !== undefined ) {
|
||||
first[ i++ ] = second[ j++ ];
|
||||
}
|
||||
}
|
||||
|
||||
first.length = i;
|
||||
|
||||
return first;
|
||||
})( ret, array );
|
||||
(function( first, second ) {
|
||||
var i = first.length,
|
||||
j = 0;
|
||||
|
||||
if ( typeof second.length === "number" ) {
|
||||
for ( var l = second.length; j < l; j++ ) {
|
||||
first[ i++ ] = second[ j ];
|
||||
}
|
||||
|
||||
return ret;
|
||||
})( elem.childNodes );
|
||||
} else {
|
||||
while ( second[j] !== undefined ) {
|
||||
first[ i++ ] = second[ j++ ];
|
||||
}
|
||||
}
|
||||
|
||||
} catch (ex) {
|
||||
h2clog("html2canvas.Util.Children failed with exception: " + ex.message);
|
||||
children = [];
|
||||
}
|
||||
return children;
|
||||
first.length = i;
|
||||
|
||||
return first;
|
||||
})( ret, array );
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
})( elem.childNodes );
|
||||
|
||||
} catch (ex) {
|
||||
h2clog("html2canvas.Util.Children failed with exception: " + ex.message);
|
||||
children = [];
|
||||
}
|
||||
return children;
|
||||
};
|
||||
|
451
src/Core.js.orig
Normal file
451
src/Core.js.orig
Normal file
@ -0,0 +1,451 @@
|
||||
"use strict";
|
||||
|
||||
var _html2canvas = {},
|
||||
previousElement,
|
||||
computedCSS,
|
||||
html2canvas;
|
||||
|
||||
|
||||
function h2clog(a) {
|
||||
if (_html2canvas.logging && window.console && window.console.log) {
|
||||
window.console.log(a);
|
||||
}
|
||||
}
|
||||
|
||||
_html2canvas.Util = {};
|
||||
|
||||
_html2canvas.Util.backgroundImage = function (src) {
|
||||
|
||||
if (/data:image\/.*;base64,/i.test( src ) || /^(-webkit|-moz|linear-gradient|-o-)/.test( src )) {
|
||||
return src;
|
||||
}
|
||||
|
||||
if (src.toLowerCase().substr( 0, 5 ) === 'url("') {
|
||||
src = src.substr( 5 );
|
||||
src = src.substr( 0, src.length - 2 );
|
||||
} else {
|
||||
src = src.substr( 4 );
|
||||
src = src.substr( 0, src.length - 1 );
|
||||
}
|
||||
|
||||
return src;
|
||||
};
|
||||
|
||||
_html2canvas.Util.parseBackgroundImage = function (value) {
|
||||
var whitespace = ' \r\n\t',
|
||||
method, definition, prefix, prefix_i, block, results = [],
|
||||
c, mode = 0, numParen = 0, quote, args;
|
||||
|
||||
var appendResult = function(){
|
||||
if(method) {
|
||||
if(definition.substr( 0, 1 ) === '"') {
|
||||
definition = definition.substr( 1, definition.length - 2 );
|
||||
}
|
||||
if(definition) {
|
||||
args.push(definition);
|
||||
}
|
||||
if(method.substr( 0, 1 ) === '-' &&
|
||||
(prefix_i = method.indexOf( '-', 1 ) + 1) > 0) {
|
||||
prefix = method.substr( 0, prefix_i);
|
||||
method = method.substr( prefix_i );
|
||||
}
|
||||
results.push({
|
||||
prefix: prefix,
|
||||
method: method.toLowerCase(),
|
||||
value: block,
|
||||
args: args
|
||||
});
|
||||
}
|
||||
args = []; //for some odd reason, setting .length = 0 didn't work in safari
|
||||
method =
|
||||
prefix =
|
||||
definition =
|
||||
block = '';
|
||||
};
|
||||
|
||||
appendResult();
|
||||
for(var i = 0, ii = value.length; i<ii; i++) {
|
||||
c = value[i];
|
||||
if(mode === 0 && whitespace.indexOf( c ) > -1){
|
||||
continue;
|
||||
}
|
||||
switch(c) {
|
||||
case '"':
|
||||
if(!quote) {
|
||||
quote = c;
|
||||
}
|
||||
else if(quote === c) {
|
||||
quote = null;
|
||||
}
|
||||
break;
|
||||
|
||||
case '(':
|
||||
if(quote) { break; }
|
||||
else if(mode === 0) {
|
||||
mode = 1;
|
||||
block += c;
|
||||
continue;
|
||||
} else {
|
||||
numParen++;
|
||||
}
|
||||
break;
|
||||
|
||||
case ')':
|
||||
if(quote) { break; }
|
||||
else if(mode === 1) {
|
||||
if(numParen === 0) {
|
||||
mode = 0;
|
||||
block += c;
|
||||
appendResult();
|
||||
continue;
|
||||
} else {
|
||||
numParen--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ',':
|
||||
if(quote) { break; }
|
||||
else if(mode === 0) {
|
||||
appendResult();
|
||||
continue;
|
||||
}
|
||||
else if (mode === 1) {
|
||||
if(numParen === 0 && !method.match(/^url$/i)) {
|
||||
args.push(definition);
|
||||
definition = '';
|
||||
block += c;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
block += c;
|
||||
if(mode === 0) { method += c; }
|
||||
else { definition += c; }
|
||||
}
|
||||
appendResult();
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
_html2canvas.Util.Bounds = function getBounds (el) {
|
||||
var clientRect,
|
||||
bounds = {};
|
||||
|
||||
if (el.getBoundingClientRect){
|
||||
clientRect = el.getBoundingClientRect();
|
||||
|
||||
|
||||
// TODO add scroll position to bounds, so no scrolling of window necessary
|
||||
bounds.top = clientRect.top;
|
||||
bounds.bottom = clientRect.bottom || (clientRect.top + clientRect.height);
|
||||
bounds.left = clientRect.left;
|
||||
|
||||
// older IE doesn't have width/height, but top/bottom instead
|
||||
bounds.width = clientRect.width || (clientRect.right - clientRect.left);
|
||||
bounds.height = clientRect.height || (clientRect.bottom - clientRect.top);
|
||||
|
||||
return bounds;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
_html2canvas.Util.getCSS = function (el, attribute) {
|
||||
<<<<<<< HEAD
|
||||
// return $(el).css(attribute);
|
||||
|
||||
var val,
|
||||
isBackgroundSizePosition = !!attribute.match( /^background(Size|Position)$/ );
|
||||
=======
|
||||
// return $(el).css(attribute);
|
||||
>>>>>>> niklasvh/develop
|
||||
|
||||
var val;
|
||||
|
||||
function toPX( attribute, val ) {
|
||||
var rsLeft = el.runtimeStyle && el.runtimeStyle[ attribute ],
|
||||
left,
|
||||
style = el.style;
|
||||
|
||||
// Check if we are not dealing with pixels, (Opera has issues with this)
|
||||
// Ported from jQuery css.js
|
||||
// From the awesome hack by Dean Edwards
|
||||
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
|
||||
|
||||
// If we're not dealing with a regular pixel number
|
||||
// but a number that has a weird ending, we need to convert it to pixels
|
||||
|
||||
if ( !/^-?[0-9]+\.?[0-9]*(?:px)?$/i.test( val ) && /^-?\d/.test( val ) ) {
|
||||
|
||||
// Remember the original values
|
||||
left = style.left;
|
||||
|
||||
// Put in the new values to get a computed value out
|
||||
if ( rsLeft ) {
|
||||
el.runtimeStyle.left = el.currentStyle.left;
|
||||
}
|
||||
style.left = attribute === "fontSize" ? "1em" : (val || 0);
|
||||
val = style.pixelLeft + "px";
|
||||
|
||||
// Revert the changed values
|
||||
style.left = left;
|
||||
if ( rsLeft ) {
|
||||
el.runtimeStyle.left = rsLeft;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
|
||||
|
||||
if ( window.getComputedStyle ) {
|
||||
if ( previousElement !== el ) {
|
||||
computedCSS = document.defaultView.getComputedStyle(el, null);
|
||||
}
|
||||
val = computedCSS[ attribute ];
|
||||
|
||||
if ( isBackgroundSizePosition ) {
|
||||
|
||||
val = (val.split(",")[0] || "0 0").split(" ");
|
||||
|
||||
val[ 0 ] = ( val[0].indexOf( "%" ) === -1 ) ? toPX( attribute + "X", val[ 0 ] ) : val[ 0 ];
|
||||
val[ 1 ] = ( val[1] === undefined ) ? val[0] : val[1]; // IE 9 doesn't return double digit always
|
||||
val[ 1 ] = ( val[1].indexOf( "%" ) === -1 ) ? toPX( attribute + "Y", val[ 1 ] ) : val[ 1 ];
|
||||
} else if ( /border(Top|Bottom)(Left|Right)Radius/.test( attribute) ) {
|
||||
var arr = val.split(" ");
|
||||
if ( arr.length <= 1 ) {
|
||||
arr[ 1 ] = arr[ 0 ];
|
||||
}
|
||||
arr[ 0 ] = parseInt( arr[ 0 ], 10 );
|
||||
arr[ 1 ] = parseInt( arr[ 1 ], 10 );
|
||||
val = arr;
|
||||
}
|
||||
|
||||
} else if ( el.currentStyle ) {
|
||||
// IE 9>
|
||||
if ( isBackgroundSizePosition ) {
|
||||
// Older IE uses -x and -y
|
||||
val = [ toPX( attribute + "X", el.currentStyle[ attribute + "X" ] ), toPX( attribute + "Y", el.currentStyle[ attribute + "Y" ] ) ];
|
||||
} else {
|
||||
|
||||
val = toPX( attribute, el.currentStyle[ attribute ] );
|
||||
|
||||
if (/^(border)/i.test( attribute ) && /^(medium|thin|thick)$/i.test( val )) {
|
||||
switch (val) {
|
||||
case "thin":
|
||||
val = "1px";
|
||||
break;
|
||||
case "medium":
|
||||
val = "0px"; // this is wrong, it should be 3px but IE uses medium for no border as well.. TODO find a work around
|
||||
break;
|
||||
case "thick":
|
||||
val = "5px";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
=======
|
||||
if (!/^(thin|medium|thick)$/i.test( val )) {
|
||||
return Math.round(parseFloat( val )) + "px";
|
||||
>>>>>>> niklasvh/develop
|
||||
}
|
||||
|
||||
return val;
|
||||
|
||||
}
|
||||
|
||||
|
||||
<<<<<<< HEAD
|
||||
//return $(el).css(attribute);
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
function backgroundBoundsFactory( prop, el, bounds, image ) {
|
||||
// TODO add support for multi image backgrounds
|
||||
|
||||
var bgposition = _html2canvas.Util.getCSS( el, prop ) ,
|
||||
topPos,
|
||||
left,
|
||||
percentage,
|
||||
val;
|
||||
|
||||
if (bgposition.length === 1){
|
||||
val = bgposition;
|
||||
|
||||
bgposition = [];
|
||||
|
||||
bgposition[0] = val;
|
||||
bgposition[1] = val;
|
||||
=======
|
||||
if ( window.getComputedStyle ) {
|
||||
if ( previousElement !== el ) {
|
||||
computedCSS = document.defaultView.getComputedStyle(el, null);
|
||||
>>>>>>> niklasvh/develop
|
||||
}
|
||||
val = computedCSS[ attribute ];
|
||||
|
||||
if ( attribute === "backgroundPosition" ) {
|
||||
|
||||
val = (val.split(",")[0] || "0 0").split(" ");
|
||||
|
||||
val[ 0 ] = ( val[0].indexOf( "%" ) === -1 ) ? toPX( attribute + "X", val[ 0 ] ) : val[ 0 ];
|
||||
val[ 1 ] = ( val[1] === undefined ) ? val[0] : val[1]; // IE 9 doesn't return double digit always
|
||||
val[ 1 ] = ( val[1].indexOf( "%" ) === -1 ) ? toPX( attribute + "Y", val[ 1 ] ) : val[ 1 ];
|
||||
} else if ( /border(Top|Bottom)(Left|Right)Radius/.test( attribute) ) {
|
||||
var arr = val.split(" ");
|
||||
if ( arr.length <= 1 ) {
|
||||
arr[ 1 ] = arr[ 0 ];
|
||||
}
|
||||
arr[ 0 ] = parseInt( arr[ 0 ], 10 );
|
||||
arr[ 1 ] = parseInt( arr[ 1 ], 10 );
|
||||
val = arr;
|
||||
}
|
||||
|
||||
} else if ( el.currentStyle ) {
|
||||
// IE 9>
|
||||
if (attribute === "backgroundPosition") {
|
||||
// Older IE uses -x and -y
|
||||
val = [ toPX( attribute + "X", el.currentStyle[ attribute + "X" ] ), toPX( attribute + "Y", el.currentStyle[ attribute + "Y" ] ) ];
|
||||
} else {
|
||||
|
||||
val = toPX( attribute, el.currentStyle[ attribute ] );
|
||||
|
||||
if (/^(border)/i.test( attribute ) && /^(medium|thin|thick)$/i.test( val )) {
|
||||
switch (val) {
|
||||
case "thin":
|
||||
val = "1px";
|
||||
break;
|
||||
case "medium":
|
||||
val = "0px"; // this is wrong, it should be 3px but IE uses medium for no border as well.. TODO find a work around
|
||||
break;
|
||||
case "thick":
|
||||
val = "5px";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
return [left, topPos];
|
||||
}
|
||||
=======
|
||||
return val;
|
||||
};
|
||||
>>>>>>> niklasvh/develop
|
||||
|
||||
_html2canvas.Util.BackgroundPosition = function( el, bounds, image ) {
|
||||
var result = backgroundBoundsFactory( 'backgroundPosition', el, bounds, image );
|
||||
return { left: result[0], top: result[1] };
|
||||
};
|
||||
|
||||
<<<<<<< HEAD
|
||||
_html2canvas.Util.BackgroundSize = function( el, bounds, image ) {
|
||||
var result = backgroundBoundsFactory( 'backgroundSize', el, bounds, image );
|
||||
return { width: result[1], height: result[0] };
|
||||
=======
|
||||
_html2canvas.Util.BackgroundPosition = function ( el, bounds, image ) {
|
||||
// TODO add support for multi image backgrounds
|
||||
|
||||
var bgposition = _html2canvas.Util.getCSS( el, "backgroundPosition" ) ,
|
||||
topPos,
|
||||
left,
|
||||
percentage,
|
||||
val;
|
||||
|
||||
if (bgposition.length === 1){
|
||||
val = bgposition;
|
||||
|
||||
bgposition = [];
|
||||
|
||||
bgposition[0] = val;
|
||||
bgposition[1] = val;
|
||||
}
|
||||
|
||||
if (bgposition[0].toString().indexOf("%") !== -1){
|
||||
percentage = (parseFloat(bgposition[0])/100);
|
||||
left = ((bounds.width * percentage)-(image.width*percentage));
|
||||
} else {
|
||||
left = parseInt(bgposition[0],10);
|
||||
}
|
||||
|
||||
if (bgposition[1].toString().indexOf("%") !== -1){
|
||||
percentage = (parseFloat(bgposition[1])/100);
|
||||
topPos = ((bounds.height * percentage)-(image.height*percentage));
|
||||
} else {
|
||||
topPos = parseInt(bgposition[1],10);
|
||||
}
|
||||
|
||||
return {
|
||||
top: topPos,
|
||||
left: left
|
||||
};
|
||||
>>>>>>> niklasvh/develop
|
||||
};
|
||||
|
||||
_html2canvas.Util.Extend = function (options, defaults) {
|
||||
for (var key in options) {
|
||||
if (options.hasOwnProperty(key)) {
|
||||
defaults[key] = options[key];
|
||||
}
|
||||
}
|
||||
return defaults;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Derived from jQuery.contents()
|
||||
* Copyright 2010, John Resig
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
* http://jquery.org/license
|
||||
*/
|
||||
_html2canvas.Util.Children = function( elem ) {
|
||||
|
||||
|
||||
var children;
|
||||
try {
|
||||
|
||||
children = (elem.nodeName && elem.nodeName.toUpperCase() === "IFRAME") ?
|
||||
elem.contentDocument || elem.contentWindow.document : (function( array ){
|
||||
var ret = [];
|
||||
|
||||
if ( array !== null ) {
|
||||
|
||||
(function( first, second ) {
|
||||
var i = first.length,
|
||||
j = 0;
|
||||
|
||||
if ( typeof second.length === "number" ) {
|
||||
for ( var l = second.length; j < l; j++ ) {
|
||||
first[ i++ ] = second[ j ];
|
||||
}
|
||||
|
||||
} else {
|
||||
while ( second[j] !== undefined ) {
|
||||
first[ i++ ] = second[ j++ ];
|
||||
}
|
||||
}
|
||||
|
||||
first.length = i;
|
||||
|
||||
return first;
|
||||
})( ret, array );
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
})( elem.childNodes );
|
||||
|
||||
} catch (ex) {
|
||||
h2clog("html2canvas.Util.Children failed with exception: " + ex.message);
|
||||
children = [];
|
||||
}
|
||||
return children;
|
||||
};
|
64
src/Font.js
Normal file
64
src/Font.js
Normal file
@ -0,0 +1,64 @@
|
||||
_html2canvas.Util.Font = (function () {
|
||||
|
||||
var fontData = {};
|
||||
|
||||
return function(font, fontSize, doc) {
|
||||
if (fontData[font + "-" + fontSize] !== undefined) {
|
||||
return fontData[font + "-" + fontSize];
|
||||
}
|
||||
|
||||
var container = doc.createElement('div'),
|
||||
img = doc.createElement('img'),
|
||||
span = doc.createElement('span'),
|
||||
sampleText = 'Hidden Text',
|
||||
baseline,
|
||||
middle,
|
||||
metricsObj;
|
||||
|
||||
container.style.visibility = "hidden";
|
||||
container.style.fontFamily = font;
|
||||
container.style.fontSize = fontSize;
|
||||
container.style.margin = 0;
|
||||
container.style.padding = 0;
|
||||
|
||||
doc.body.appendChild(container);
|
||||
|
||||
// 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(sampleText));
|
||||
container.appendChild(span);
|
||||
container.appendChild(img);
|
||||
baseline = (img.offsetTop - span.offsetTop) + 1;
|
||||
|
||||
container.removeChild(span);
|
||||
container.appendChild(doc.createTextNode(sampleText));
|
||||
|
||||
container.style.lineHeight = "normal";
|
||||
img.style.verticalAlign = "super";
|
||||
|
||||
middle = (img.offsetTop-container.offsetTop) + 1;
|
||||
metricsObj = {
|
||||
baseline: baseline,
|
||||
lineWidth: 1,
|
||||
middle: middle
|
||||
};
|
||||
|
||||
fontData[font + "-" + fontSize] = metricsObj;
|
||||
|
||||
doc.body.removeChild(container);
|
||||
|
||||
return metricsObj;
|
||||
};
|
||||
})();
|
958
src/Parse.js
958
src/Parse.js
File diff suppressed because it is too large
Load Diff
947
src/Parse.js.orig
Normal file
947
src/Parse.js.orig
Normal file
@ -0,0 +1,947 @@
|
||||
_html2canvas.Parse = function (images, options) {
|
||||
window.scroll(0,0);
|
||||
|
||||
var element = (( options.elements === undefined ) ? document.body : options.elements[0]), // select body by default
|
||||
numDraws = 0,
|
||||
doc = element.ownerDocument,
|
||||
support = _html2canvas.Util.Support(options, doc),
|
||||
ignoreElementsRegExp = new RegExp("(" + options.ignoreElements + ")"),
|
||||
body = doc.body,
|
||||
getCSS = _html2canvas.Util.getCSS;
|
||||
|
||||
images = images || {};
|
||||
|
||||
function documentWidth () {
|
||||
return Math.max(
|
||||
Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth),
|
||||
Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth),
|
||||
Math.max(doc.body.clientWidth, doc.documentElement.clientWidth)
|
||||
);
|
||||
}
|
||||
|
||||
function documentHeight () {
|
||||
return Math.max(
|
||||
Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight),
|
||||
Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight),
|
||||
Math.max(doc.body.clientHeight, doc.documentElement.clientHeight)
|
||||
);
|
||||
}
|
||||
|
||||
function getCSSInt(element, attribute) {
|
||||
var val = parseInt(getCSS(element, attribute), 10);
|
||||
return (isNaN(val)) ? 0 : val; // borders in old IE are throwing 'medium' for demo.html
|
||||
}
|
||||
|
||||
function renderRect (ctx, x, y, w, h, bgcolor) {
|
||||
if (bgcolor !== "transparent"){
|
||||
ctx.setVariable("fillStyle", bgcolor);
|
||||
ctx.fillRect(x, y, w, h);
|
||||
numDraws+=1;
|
||||
}
|
||||
}
|
||||
|
||||
function textTransform (text, transform) {
|
||||
switch(transform){
|
||||
case "lowercase":
|
||||
return text.toLowerCase();
|
||||
case "capitalize":
|
||||
return text.replace( /(^|\s|:|-|\(|\))([a-z])/g , function (m, p1, p2) {
|
||||
if (m.length > 0) {
|
||||
return p1 + p2.toUpperCase();
|
||||
}
|
||||
} );
|
||||
case "uppercase":
|
||||
return text.toUpperCase();
|
||||
default:
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
function noLetterSpacing(letter_spacing) {
|
||||
return (/^(normal|none|0px)$/.test(letter_spacing));
|
||||
}
|
||||
|
||||
function trimText (text) {
|
||||
return text.replace(/^\s*/g, "").replace(/\s*$/g, "");
|
||||
}
|
||||
|
||||
function drawText(currentText, x, y, ctx){
|
||||
if (currentText !== null && trimText(currentText).length > 0) {
|
||||
ctx.fillText(currentText, x, y);
|
||||
numDraws+=1;
|
||||
}
|
||||
}
|
||||
|
||||
function setTextVariables(ctx, el, text_decoration, color) {
|
||||
var align = false,
|
||||
bold = getCSS(el, "fontWeight"),
|
||||
family = getCSS(el, "fontFamily"),
|
||||
size = getCSS(el, "fontSize");
|
||||
|
||||
switch(parseInt(bold, 10)){
|
||||
case 401:
|
||||
bold = "bold";
|
||||
break;
|
||||
case 400:
|
||||
bold = "normal";
|
||||
break;
|
||||
}
|
||||
|
||||
ctx.setVariable("fillStyle", color);
|
||||
ctx.setVariable("font", [getCSS(el, "fontStyle"), getCSS(el, "fontVariant"), bold, size, family].join(" "));
|
||||
ctx.setVariable("textAlign", (align) ? "right" : "left");
|
||||
|
||||
if (text_decoration !== "none"){
|
||||
return _html2canvas.Util.Font(family, size, doc);
|
||||
}
|
||||
}
|
||||
|
||||
function renderTextDecoration(ctx, text_decoration, bounds, metrics, color) {
|
||||
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, Math.round(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;
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
function renderText(el, textNode, stack) {
|
||||
var ctx = stack.ctx,
|
||||
color = getCSS(el, "color"),
|
||||
text_decoration = getCSS(el, "textDecoration"),
|
||||
text_align = getCSS(el, "textAlign"),
|
||||
letter_spacing = getCSS(el, "letterSpacing"),
|
||||
bounds,
|
||||
text,
|
||||
metrics,
|
||||
renderList,
|
||||
listLen,
|
||||
newTextNode,
|
||||
textValue,
|
||||
textOffset = 0,
|
||||
oldTextNode,
|
||||
c,
|
||||
range,
|
||||
parent,
|
||||
wrapElement,
|
||||
backupText;
|
||||
|
||||
textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform"));
|
||||
text = trimText(textNode.nodeValue);
|
||||
|
||||
if (text.length > 0){
|
||||
|
||||
text_align = text_align.replace(["-webkit-auto"],["auto"]);
|
||||
|
||||
renderList = (!options.letterRendering && /^(left|right|justify|auto)$/.test(text_align) && noLetterSpacing(letter_spacing)) ?
|
||||
textNode.nodeValue.split(/(\b| )/) :
|
||||
textNode.nodeValue.split("");
|
||||
|
||||
metrics = setTextVariables(ctx, el, text_decoration, color);
|
||||
oldTextNode = textNode;
|
||||
|
||||
for ( c=0, listLen = renderList.length; c < listLen; c+=1 ) {
|
||||
textValue = null;
|
||||
|
||||
if (support.rangeBounds){
|
||||
// getBoundingClientRect is supported for ranges
|
||||
if (text_decoration !== "none" || trimText(renderList[c]).length !== 0) {
|
||||
textValue = renderList[c];
|
||||
if (doc.createRange){
|
||||
range = doc.createRange();
|
||||
|
||||
range.setStart(textNode, textOffset);
|
||||
range.setEnd(textNode, textOffset + textValue.length);
|
||||
} else {
|
||||
// TODO add IE support
|
||||
range = body.createTextRange();
|
||||
}
|
||||
|
||||
if (range.getBoundingClientRect()) {
|
||||
bounds = range.getBoundingClientRect();
|
||||
} else {
|
||||
bounds = {};
|
||||
}
|
||||
=======
|
||||
function getTextBounds(state, text, textDecoration, isLast) {
|
||||
var bounds;
|
||||
if (support.rangeBounds) {
|
||||
if (textDecoration !== "none" || trimText(text).length !== 0) {
|
||||
bounds = textRangeBounds(text, state.node, state.textOffset);
|
||||
}
|
||||
state.textOffset += text.length;
|
||||
} else if (state.node && typeof state.node.nodeValue === "string" ){
|
||||
var newTextNode = (isLast) ? state.node.splitText(text.length) : null;
|
||||
bounds = textWrapperBounds(state.node);
|
||||
state.node = newTextNode;
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
>>>>>>> niklasvh/develop
|
||||
|
||||
function textRangeBounds(text, textNode, textOffset) {
|
||||
var range = doc.createRange();
|
||||
range.setStart(textNode, textOffset);
|
||||
range.setEnd(textNode, textOffset + text.length);
|
||||
return range.getBoundingClientRect();
|
||||
}
|
||||
|
||||
function textWrapperBounds(oldTextNode) {
|
||||
var parent = oldTextNode.parentNode,
|
||||
wrapElement = doc.createElement('wrapper'),
|
||||
backupText = oldTextNode.cloneNode(true);
|
||||
|
||||
wrapElement.appendChild(oldTextNode.cloneNode(true));
|
||||
parent.replaceChild(wrapElement, oldTextNode);
|
||||
|
||||
var bounds = _html2canvas.Util.Bounds(wrapElement);
|
||||
parent.replaceChild(backupText, wrapElement);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
function renderText(el, textNode, stack) {
|
||||
var ctx = stack.ctx,
|
||||
color = getCSS(el, "color"),
|
||||
textDecoration = getCSS(el, "textDecoration"),
|
||||
textAlign = getCSS(el, "textAlign"),
|
||||
metrics,
|
||||
textList,
|
||||
state = {
|
||||
node: textNode,
|
||||
textOffset: 0
|
||||
};
|
||||
|
||||
if (trimText(textNode.nodeValue).length > 0) {
|
||||
textNode.nodeValue = textTransform(textNode.nodeValue, getCSS(el, "textTransform"));
|
||||
textAlign = textAlign.replace(["-webkit-auto"],["auto"]);
|
||||
|
||||
textList = (!options.letterRendering && /^(left|right|justify|auto)$/.test(textAlign) && noLetterSpacing(getCSS(el, "letterSpacing"))) ?
|
||||
textNode.nodeValue.split(/(\b| )/)
|
||||
: textNode.nodeValue.split("");
|
||||
|
||||
metrics = setTextVariables(ctx, el, textDecoration, color);
|
||||
|
||||
textList.forEach(function(text, index) {
|
||||
var bounds = getTextBounds(state, text, textDecoration, (index < textList.length - 1));
|
||||
if (bounds) {
|
||||
drawText(text, bounds.left, bounds.bottom, ctx);
|
||||
renderTextDecoration(ctx, textDecoration, bounds, metrics, color);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function listPosition (element, val) {
|
||||
var boundElement = doc.createElement( "boundelement" ),
|
||||
originalType,
|
||||
bounds;
|
||||
|
||||
boundElement.style.display = "inline";
|
||||
|
||||
originalType = element.style.listStyleType;
|
||||
element.style.listStyleType = "none";
|
||||
|
||||
boundElement.appendChild(doc.createTextNode(val));
|
||||
|
||||
element.insertBefore(boundElement, element.firstChild);
|
||||
|
||||
bounds = _html2canvas.Util.Bounds(boundElement);
|
||||
element.removeChild(boundElement);
|
||||
element.style.listStyleType = originalType;
|
||||
return bounds;
|
||||
}
|
||||
|
||||
function elementIndex( el ) {
|
||||
var i = -1,
|
||||
count = 1,
|
||||
childs = el.parentNode.childNodes;
|
||||
|
||||
if (el.parentNode) {
|
||||
while( childs[ ++i ] !== el ) {
|
||||
if ( childs[ i ].nodeType === 1 ) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
function listItemText(element, type) {
|
||||
var currentIndex = elementIndex(element),
|
||||
text;
|
||||
switch(type){
|
||||
case "decimal":
|
||||
text = currentIndex;
|
||||
break;
|
||||
case "decimal-leading-zero":
|
||||
text = (currentIndex.toString().length === 1) ? currentIndex = "0" + currentIndex.toString() : currentIndex.toString();
|
||||
break;
|
||||
case "upper-roman":
|
||||
text = _html2canvas.Generate.ListRoman( currentIndex );
|
||||
break;
|
||||
case "lower-roman":
|
||||
text = _html2canvas.Generate.ListRoman( currentIndex ).toLowerCase();
|
||||
break;
|
||||
case "lower-alpha":
|
||||
text = _html2canvas.Generate.ListAlpha( currentIndex ).toLowerCase();
|
||||
break;
|
||||
case "upper-alpha":
|
||||
text = _html2canvas.Generate.ListAlpha( currentIndex );
|
||||
break;
|
||||
}
|
||||
|
||||
text += ". ";
|
||||
return text;
|
||||
}
|
||||
|
||||
function renderListItem(element, stack, elBounds) {
|
||||
var x,
|
||||
text,
|
||||
ctx = stack.ctx,
|
||||
type = getCSS(element, "listStyleType"),
|
||||
listBounds;
|
||||
|
||||
if (/^(decimal|decimal-leading-zero|upper-alpha|upper-latin|upper-roman|lower-alpha|lower-greek|lower-latin|lower-roman)$/i.test(type)) {
|
||||
text = listItemText(element, type);
|
||||
listBounds = listPosition(element, text);
|
||||
setTextVariables(ctx, element, "none", getCSS(element, "color"));
|
||||
|
||||
if (getCSS(element, "listStylePosition") === "inside") {
|
||||
ctx.setVariable("textAlign", "left");
|
||||
x = elBounds.left;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
drawText(text, x, listBounds.bottom, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
function loadImage (src){
|
||||
var img = images[src];
|
||||
if (img && img.succeeded === true) {
|
||||
return img.img;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function clipBounds(src, dst){
|
||||
var x = Math.max(src.left, dst.left),
|
||||
y = Math.max(src.top, dst.top),
|
||||
x2 = Math.min((src.left + src.width), (dst.left + dst.width)),
|
||||
y2 = Math.min((src.top + src.height), (dst.top + dst.height));
|
||||
|
||||
return {
|
||||
left:x,
|
||||
top:y,
|
||||
width:x2-x,
|
||||
height:y2-y
|
||||
};
|
||||
}
|
||||
|
||||
function setZ(zIndex, parentZ){
|
||||
// TODO fix static elements overlapping relative/absolute elements under same stack, if they are defined after them
|
||||
var newContext;
|
||||
if (!parentZ){
|
||||
newContext = h2czContext(0);
|
||||
return newContext;
|
||||
}
|
||||
|
||||
if (zIndex !== "auto"){
|
||||
newContext = h2czContext(zIndex);
|
||||
parentZ.children.push(newContext);
|
||||
return newContext;
|
||||
|
||||
}
|
||||
|
||||
return parentZ;
|
||||
}
|
||||
|
||||
function renderImage(ctx, element, image, bounds, borders) {
|
||||
|
||||
var paddingLeft = getCSSInt(element, 'paddingLeft'),
|
||||
paddingTop = getCSSInt(element, 'paddingTop'),
|
||||
paddingRight = getCSSInt(element, 'paddingRight'),
|
||||
paddingBottom = getCSSInt(element, 'paddingBottom');
|
||||
|
||||
drawImage(
|
||||
ctx,
|
||||
image,
|
||||
0, //sx
|
||||
0, //sy
|
||||
image.width, //sw
|
||||
image.height, //sh
|
||||
bounds.left + paddingLeft + borders[3].width, //dx
|
||||
bounds.top + 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
|
||||
);
|
||||
}
|
||||
|
||||
function renderBorders(el, ctx, bounds, clip){
|
||||
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')
|
||||
});
|
||||
}
|
||||
|
||||
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') );
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return borders;
|
||||
}
|
||||
|
||||
|
||||
function renderFormValue (el, bounds, stack){
|
||||
|
||||
var valueWrap = doc.createElement('valuewrap'),
|
||||
cssPropertyArray = ['lineHeight','textAlign','fontFamily','color','fontSize','paddingLeft','paddingTop','width','height','border','borderLeftWidth','borderTopWidth'],
|
||||
textValue,
|
||||
textNode;
|
||||
|
||||
cssPropertyArray.forEach(function(property) {
|
||||
try {
|
||||
valueWrap.style[property] = getCSS(el, property);
|
||||
} catch(e) {
|
||||
// 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";
|
||||
|
||||
textValue = (el.nodeName === "SELECT") ? el.options[el.selectedIndex].text : el.value;
|
||||
textNode = doc.createTextNode(textValue);
|
||||
|
||||
valueWrap.appendChild(textNode);
|
||||
body.appendChild(valueWrap);
|
||||
|
||||
renderText(el, textNode, stack);
|
||||
body.removeChild(valueWrap);
|
||||
}
|
||||
|
||||
function drawImage (ctx) {
|
||||
ctx.drawImage.apply(ctx, Array.prototype.slice.call(arguments, 1));
|
||||
numDraws+=1;
|
||||
}
|
||||
|
||||
function renderBackgroundSlice (ctx, image, x, y, width, height, elx, ely){
|
||||
var sourceX = (elx - x > 0) ? elx-x :0,
|
||||
sourceY= (ely - y > 0) ? ely-y : 0;
|
||||
|
||||
drawImage(
|
||||
ctx,
|
||||
image,
|
||||
Math.floor(sourceX), // source X
|
||||
Math.floor(sourceY), // source Y
|
||||
Math.ceil(width-sourceX), // source Width
|
||||
Math.ceil(height-sourceY), // source Height
|
||||
Math.ceil(x+sourceX), // destination X
|
||||
Math.ceil(y+sourceY), // destination Y
|
||||
Math.ceil(width-sourceX), // destination width
|
||||
Math.ceil(height-sourceY) // destination height
|
||||
);
|
||||
}
|
||||
|
||||
function renderBackgroundRepeat(ctx, image, backgroundPosition, bounds) {
|
||||
var bgy,
|
||||
height,
|
||||
add,
|
||||
h;
|
||||
|
||||
backgroundPosition.top -= Math.ceil(backgroundPosition.top / image.height) * image.height;
|
||||
|
||||
for(bgy = (bounds.top + backgroundPosition.top); bgy < (bounds.height + bounds.top); bgy = Math.floor(bgy+image.height) - add) {
|
||||
h = Math.min(image.height,(bounds.height + bounds.top) - bgy);
|
||||
|
||||
height = (Math.floor(bgy + image.height) > h + bgy) ? (h + bgy) - bgy : image.height;
|
||||
|
||||
if (bgy < bounds.top){
|
||||
add = bounds.top - bgy;
|
||||
bgy = bounds.top;
|
||||
} else {
|
||||
add = 0;
|
||||
}
|
||||
|
||||
renderBackgroundRepeatX(ctx, image, backgroundPosition, bounds.left, bgy, bounds.width, height);
|
||||
|
||||
if (add > 0){
|
||||
backgroundPosition.top += add;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderBackgroundNoRepeat(ctx, image, backgroundPosition, x, y, w, h) {
|
||||
var bgdw = w - backgroundPosition.left,
|
||||
bgdh = h - backgroundPosition.top,
|
||||
bgsx = backgroundPosition.left,
|
||||
bgsy = backgroundPosition.top,
|
||||
bgdx = backgroundPosition.left + x,
|
||||
bgdy = backgroundPosition.top + y;
|
||||
|
||||
if (bgsx<0){
|
||||
bgsx = Math.abs(bgsx);
|
||||
bgdx += bgsx;
|
||||
bgdw = Math.min(w,image.width-bgsx);
|
||||
} else {
|
||||
bgdw = Math.min(bgdw,image.width);
|
||||
bgsx = 0;
|
||||
}
|
||||
|
||||
if (bgsy < 0){
|
||||
bgsy = Math.abs(bgsy);
|
||||
bgdy += bgsy;
|
||||
bgdh = Math.min(h, image.height - bgsy);
|
||||
} else {
|
||||
bgdh = Math.min(bgdh, image.height);
|
||||
bgsy = 0;
|
||||
}
|
||||
|
||||
if (bgdh > 0 && bgdw > 0){
|
||||
drawImage(
|
||||
ctx,
|
||||
image,
|
||||
bgsx,
|
||||
bgsy,
|
||||
bgdw,
|
||||
bgdh,
|
||||
bgdx,
|
||||
bgdy,
|
||||
bgdw,
|
||||
bgdh
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function renderBackgroundRepeatY (ctx, image, backgroundPosition, x, y, w, h){
|
||||
var height,
|
||||
width = Math.min(image.width, w),
|
||||
bgy;
|
||||
|
||||
backgroundPosition.top -= Math.ceil(backgroundPosition.top / image.height) * image.height;
|
||||
|
||||
for (bgy = y + backgroundPosition.top; bgy < h + y; bgy = Math.round(bgy + image.height)){
|
||||
height = (Math.floor(bgy + image.height) > h + y) ? (h+y) - bgy : image.height;
|
||||
renderBackgroundSlice(ctx, image, x + backgroundPosition.left, bgy,width, height, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
function renderBackgroundRepeatX(ctx, image, backgroundPosition, x, y, w, h){
|
||||
var height = Math.min(image.height, h),
|
||||
width,
|
||||
bgx;
|
||||
|
||||
backgroundPosition.left -= Math.ceil(backgroundPosition.left / image.width) * image.width;
|
||||
|
||||
for (bgx = x + backgroundPosition.left; bgx < w + x; bgx = Math.round(bgx + image.width)) {
|
||||
width = (Math.floor(bgx + image.width) > w + x) ? (w + x) - bgx : image.width;
|
||||
renderBackgroundSlice(ctx, image, bgx,(y + backgroundPosition.top), width, height, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
function renderBackgroundColor(ctx, backgroundBounds, bgcolor) {
|
||||
renderRect(
|
||||
ctx,
|
||||
backgroundBounds.left,
|
||||
backgroundBounds.top,
|
||||
backgroundBounds.width,
|
||||
backgroundBounds.height,
|
||||
bgcolor
|
||||
);
|
||||
}
|
||||
|
||||
function renderBackgroundRepeating(el, bounds, ctx, image) {
|
||||
var backgroundPosition = _html2canvas.Util.BackgroundPosition(el, bounds, image),
|
||||
backgroundRepeat = getCSS(el, "backgroundRepeat").split(",")[0];
|
||||
switch (backgroundRepeat) {
|
||||
case "repeat-x":
|
||||
renderBackgroundRepeatX(ctx, image, backgroundPosition, bounds.left, bounds.top, bounds.width, bounds.height);
|
||||
break;
|
||||
|
||||
case "repeat-y":
|
||||
renderBackgroundRepeatY(ctx, image, backgroundPosition, bounds.left, bounds.top, bounds.width, bounds.height);
|
||||
break;
|
||||
|
||||
case "no-repeat":
|
||||
renderBackgroundNoRepeat(ctx, image, backgroundPosition, bounds.left, bounds.top, bounds.width, bounds.height);
|
||||
break;
|
||||
|
||||
default:
|
||||
renderBackgroundRepeat(ctx, image, backgroundPosition, bounds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function renderBackgroundImage(element, bounds, ctx) {
|
||||
// TODO add support for multi background-images
|
||||
var backgroundImage = getCSS(element, "backgroundImage"),
|
||||
image;
|
||||
|
||||
if (!/data:image\/.*;base64,/i.test(backgroundImage) && !/^(-webkit|-moz|linear-gradient|-o-)/.test(backgroundImage)) {
|
||||
backgroundImage = backgroundImage.split(",")[0];
|
||||
}
|
||||
|
||||
if (typeof backgroundImage !== "undefined" && /^(1|none)$/.test(backgroundImage) === false) {
|
||||
image = loadImage(_html2canvas.Util.backgroundImage(backgroundImage));
|
||||
|
||||
// TODO add support for background-origin
|
||||
if (image) {
|
||||
renderBackgroundRepeating(element, bounds, ctx, image);
|
||||
} else {
|
||||
h2clog("html2canvas: Error loading background:" + backgroundImage);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function setOpacity(ctx, element, parentStack) {
|
||||
var opacity = getCSS(element, "opacity") * ((parentStack) ? parentStack.opacity : 1);
|
||||
ctx.setVariable("globalAlpha", opacity);
|
||||
return opacity;
|
||||
}
|
||||
|
||||
function createStack(element, parentStack, bounds) {
|
||||
|
||||
var ctx = h2cRenderContext((!parentStack) ? documentWidth() : bounds.width , (!parentStack) ? documentHeight() : bounds.height),
|
||||
stack = {
|
||||
ctx: ctx,
|
||||
zIndex: setZ(getCSS(element, "zIndex"), (parentStack) ? parentStack.zIndex : null),
|
||||
opacity: setOpacity(ctx, element, parentStack),
|
||||
cssPosition: getCSS(element, "position"),
|
||||
borders: renderBorders(element, ctx, bounds, false),
|
||||
clip: (parentStack && parentStack.clip) ? _html2canvas.Util.Extend( {}, parentStack.clip ) : null
|
||||
};
|
||||
|
||||
// TODO correct overflow for absolute content residing under a static position
|
||||
if (options.useOverflow === true && /(hidden|scroll|auto)/.test(getCSS(element, "overflow")) === true && /(BODY)/i.test(element.nodeName) === false){
|
||||
stack.clip = (stack.clip) ? clipBounds(stack.clip, bounds) : bounds;
|
||||
}
|
||||
|
||||
stack.zIndex.children.push(stack);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
function getBackgroundBounds(borders, bounds, clip) {
|
||||
var backgroundBounds = {
|
||||
left: bounds.left + borders[3].width,
|
||||
top: bounds.top + borders[0].width,
|
||||
width: bounds.width - (borders[1].width + borders[3].width),
|
||||
height: bounds.height - (borders[0].width + borders[2].width)
|
||||
};
|
||||
|
||||
if (clip) {
|
||||
backgroundBounds = clipBounds(backgroundBounds, clip);
|
||||
}
|
||||
|
||||
return backgroundBounds;
|
||||
}
|
||||
|
||||
function renderElement(element, parentStack){
|
||||
var bounds = _html2canvas.Util.Bounds(element),
|
||||
image,
|
||||
bgcolor = (ignoreElementsRegExp.test(element.nodeName)) ? "#efefef" : getCSS(element, "backgroundColor"),
|
||||
stack = createStack(element, parentStack, bounds),
|
||||
borders = stack.borders,
|
||||
ctx = stack.ctx,
|
||||
backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip);
|
||||
|
||||
if (backgroundBounds.height > 0 && backgroundBounds.width > 0){
|
||||
renderBackgroundColor(ctx, backgroundBounds, bgcolor);
|
||||
renderBackgroundImage(element, backgroundBounds, ctx);
|
||||
}
|
||||
|
||||
switch(element.nodeName){
|
||||
case "IMG":
|
||||
if ((image = loadImage(element.getAttribute('src')))) {
|
||||
renderImage(ctx, element, image, bounds, borders);
|
||||
} else {
|
||||
h2clog("html2canvas: Error loading <img>:" + element.getAttribute('src'));
|
||||
}
|
||||
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(element.type) && element.value.length > 0){
|
||||
renderFormValue(element, bounds, stack);
|
||||
}
|
||||
break;
|
||||
case "TEXTAREA":
|
||||
if (element.value.length > 0){
|
||||
renderFormValue(element, bounds, stack);
|
||||
}
|
||||
break;
|
||||
case "SELECT":
|
||||
if (element.options.length > 0){
|
||||
renderFormValue(element, bounds, stack);
|
||||
}
|
||||
break;
|
||||
case "LI":
|
||||
renderListItem(element, stack, backgroundBounds);
|
||||
break;
|
||||
case "CANVAS":
|
||||
renderImage(ctx, element, element, bounds, borders);
|
||||
break;
|
||||
}
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
function isElementVisible(element) {
|
||||
return (getCSS(element, 'display') !== "none" && getCSS(element, 'visibility') !== "hidden" && !element.hasAttribute("data-html2canvas-ignore"));
|
||||
}
|
||||
|
||||
function parseElement (el, stack) {
|
||||
|
||||
if (isElementVisible(el)) {
|
||||
stack = renderElement(el, stack) || stack;
|
||||
if (!ignoreElementsRegExp.test(el.nodeName)) {
|
||||
_html2canvas.Util.Children(el).forEach(function(node) {
|
||||
if (node.nodeType === 1) {
|
||||
parseElement(node, stack);
|
||||
} else if (node.nodeType === 3) {
|
||||
renderText(el, node, stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function svgDOMRender(body, stack) {
|
||||
var img = new Image(),
|
||||
docWidth = documentWidth(),
|
||||
docHeight = documentHeight(),
|
||||
html = "";
|
||||
|
||||
function parseDOM(el) {
|
||||
var children = _html2canvas.Util.Children( el ),
|
||||
len = children.length,
|
||||
attr,
|
||||
a,
|
||||
alen,
|
||||
elm,
|
||||
i;
|
||||
for ( i = 0; i < len; i+=1 ) {
|
||||
elm = children[ i ];
|
||||
if ( elm.nodeType === 3 ) {
|
||||
// Text node
|
||||
html += elm.nodeValue.replace(/</g,"<").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 += "</" + elm.nodeName.toLowerCase() + ">";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
parseDOM(body);
|
||||
img.src = [
|
||||
"data:image/svg+xml,",
|
||||
"<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='" + docWidth + "' height='" + docHeight + "'>",
|
||||
"<foreignObject width='" + docWidth + "' height='" + docHeight + "'>",
|
||||
"<html xmlns='http://www.w3.org/1999/xhtml' style='margin:0;'>",
|
||||
html.replace(/\#/g,"%23"),
|
||||
"</html>",
|
||||
"</foreignObject>",
|
||||
"</svg>"
|
||||
].join("");
|
||||
|
||||
img.onload = function() {
|
||||
stack.svgRender = img;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function init() {
|
||||
var stack = renderElement(element, null);
|
||||
|
||||
if (support.svgRendering) {
|
||||
svgDOMRender(document.documentElement, stack);
|
||||
}
|
||||
|
||||
Array.prototype.slice.call(element.children, 0).forEach(function(childElement) {
|
||||
parseElement(childElement, stack);
|
||||
});
|
||||
|
||||
stack.backgroundColor = getCSS(document.documentElement, "backgroundColor");
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
return init();
|
||||
};
|
||||
|
||||
function h2czContext(zindex) {
|
||||
return {
|
||||
zindex: zindex,
|
||||
children: []
|
||||
};
|
||||
}
|
@ -1,57 +1,58 @@
|
||||
_html2canvas.Renderer = function(parseQueue, options){
|
||||
var queue = [];
|
||||
|
||||
function sortZ(zStack){
|
||||
var subStacks = [],
|
||||
stackValues = [],
|
||||
zStackChildren = zStack.children,
|
||||
s,
|
||||
i,
|
||||
stackLen,
|
||||
zValue,
|
||||
zLen,
|
||||
stackChild,
|
||||
b,
|
||||
subStackLen;
|
||||
function createRenderQueue(parseQueue) {
|
||||
var queue = [];
|
||||
|
||||
var sortZ = function(zStack){
|
||||
var subStacks = [],
|
||||
stackValues = [];
|
||||
|
||||
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;
|
||||
|
||||
zStack.children.forEach(function(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;
|
||||
});
|
||||
|
||||
stackValues.forEach(function(zValue) {
|
||||
var index;
|
||||
|
||||
subStacks.some(function(stack, i){
|
||||
index = i;
|
||||
return (stack.zindex === zValue);
|
||||
});
|
||||
sortZ(subStacks.splice(index, 1)[0]);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
sortZ(parseQueue.zIndex);
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
function getRenderer(rendererName) {
|
||||
var renderer;
|
||||
|
||||
if (typeof options.renderer === "string" && _html2canvas.Renderer[rendererName] !== undefined) {
|
||||
renderer = _html2canvas.Renderer[rendererName](options);
|
||||
} else if (typeof rendererName === "function") {
|
||||
renderer = rendererName(options);
|
||||
} else {
|
||||
throw new Error("Unknown renderer");
|
||||
}
|
||||
|
||||
if ( typeof renderer._create !== "function" ) {
|
||||
throw new Error("Invalid renderer defined");
|
||||
}
|
||||
return renderer;
|
||||
}
|
||||
|
||||
|
||||
sortZ(parseQueue.zIndex);
|
||||
if ( typeof options._renderer._create !== "function" ) {
|
||||
throw new Error("Invalid renderer defined");
|
||||
}
|
||||
return options._renderer._create( parseQueue, options, document, queue, _html2canvas );
|
||||
|
||||
return getRenderer(options.renderer)._create(parseQueue, options, document, createRenderQueue(parseQueue), _html2canvas);
|
||||
};
|
||||
|
63
src/Support.js
Normal file
63
src/Support.js
Normal file
@ -0,0 +1,63 @@
|
||||
_html2canvas.Util.Support = function (options, doc) {
|
||||
|
||||
function supportSVGRendering() {
|
||||
var img = new Image(),
|
||||
canvas = doc.createElement("canvas"),
|
||||
ctx = (canvas.getContext === undefined) ? false : canvas.getContext("2d");
|
||||
if (ctx === false) {
|
||||
return false;
|
||||
}
|
||||
canvas.width = canvas.height = 10;
|
||||
img.src = [
|
||||
"data:image/svg+xml,",
|
||||
"<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'>",
|
||||
"<foreignObject width='10' height='10'>",
|
||||
"<div xmlns='http://www.w3.org/1999/xhtml' style='width:10;height:10;'>",
|
||||
"sup",
|
||||
"</div>",
|
||||
"</foreignObject>",
|
||||
"</svg>"
|
||||
].join("");
|
||||
try {
|
||||
ctx.drawImage(img, 0, 0);
|
||||
canvas.toDataURL();
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
h2clog('html2canvas: Parse: SVG powered rendering available');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test whether we can use ranges to measure bounding boxes
|
||||
// Opera doesn't provide valid bounds.height/bottom even though it supports the method.
|
||||
|
||||
function supportRangeBounds() {
|
||||
var r, testElement, rangeBounds, rangeHeight, support = false;
|
||||
|
||||
if (doc.createRange) {
|
||||
r = doc.createRange();
|
||||
if (r.getBoundingClientRect) {
|
||||
testElement = doc.createElement('boundtest');
|
||||
testElement.style.height = "123px";
|
||||
testElement.style.display = "block";
|
||||
doc.body.appendChild(testElement);
|
||||
|
||||
r.selectNode(testElement);
|
||||
rangeBounds = r.getBoundingClientRect();
|
||||
rangeHeight = rangeBounds.height;
|
||||
|
||||
if (rangeHeight === 123) {
|
||||
support = true;
|
||||
}
|
||||
doc.body.removeChild(testElement);
|
||||
}
|
||||
}
|
||||
|
||||
return support;
|
||||
}
|
||||
|
||||
return {
|
||||
rangeBounds: supportRangeBounds(),
|
||||
svgRendering: options.svgRendering && supportSVGRendering()
|
||||
};
|
||||
};
|
20
src/Util.js
20
src/Util.js
@ -1,5 +1,4 @@
|
||||
html2canvas = window.html2canvas = function( elements, opts ) {
|
||||
|
||||
window.html2canvas = function(elements, opts) {
|
||||
var queue,
|
||||
canvas,
|
||||
options = {
|
||||
@ -8,14 +7,13 @@ html2canvas = window.html2canvas = function( elements, opts ) {
|
||||
elements: elements,
|
||||
|
||||
// preload options
|
||||
proxy: "http://html2canvas.appspot.com/",
|
||||
proxy: "",
|
||||
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,
|
||||
@ -27,18 +25,10 @@ html2canvas = window.html2canvas = function( elements, opts ) {
|
||||
height: null,
|
||||
taintTest: true, // do a taint test with all images before applying to canvas
|
||||
renderer: "Canvas"
|
||||
}, renderer;
|
||||
};
|
||||
|
||||
options = _html2canvas.Util.Extend(opts, options);
|
||||
|
||||
if (typeof options.renderer === "string" && _html2canvas.Renderer[options.renderer] !== undefined) {
|
||||
options._renderer = _html2canvas.Renderer[options.renderer]( options );
|
||||
} else if (typeof options.renderer === "function") {
|
||||
options._renderer = options.renderer( options );
|
||||
} else {
|
||||
throw("Unknown renderer");
|
||||
}
|
||||
|
||||
_html2canvas.logging = options.logging;
|
||||
options.complete = function( images ) {
|
||||
|
||||
@ -83,7 +73,7 @@ html2canvas = window.html2canvas = function( elements, opts ) {
|
||||
};
|
||||
};
|
||||
|
||||
html2canvas.log = h2clog; // for renderers
|
||||
html2canvas.Renderer = {
|
||||
window.html2canvas.log = h2clog; // for renderers
|
||||
window.html2canvas.Renderer = {
|
||||
Canvas: undefined // We are assuming this will be used
|
||||
};
|
84
src/Util.js.orig
Normal file
84
src/Util.js.orig
Normal file
@ -0,0 +1,84 @@
|
||||
<<<<<<< HEAD
|
||||
html2canvas = window.html2canvas = function( elements, opts ) {
|
||||
|
||||
=======
|
||||
window.html2canvas = function(elements, opts) {
|
||||
>>>>>>> niklasvh/develop
|
||||
var queue,
|
||||
canvas,
|
||||
options = {
|
||||
// general
|
||||
logging: false,
|
||||
elements: elements,
|
||||
|
||||
// preload options
|
||||
proxy: "",
|
||||
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+)
|
||||
ignoreElements: "IFRAME|OBJECT|PARAM",
|
||||
useOverflow: true,
|
||||
letterRendering: false,
|
||||
|
||||
// render options
|
||||
|
||||
flashcanvas: undefined, // path to flashcanvas
|
||||
width: null,
|
||||
height: null,
|
||||
taintTest: true, // do a taint test with all images before applying to canvas
|
||||
renderer: "Canvas"
|
||||
};
|
||||
|
||||
options = _html2canvas.Util.Extend(opts, options);
|
||||
|
||||
_html2canvas.logging = options.logging;
|
||||
options.complete = function( images ) {
|
||||
|
||||
if (typeof options.onpreloaded === "function") {
|
||||
if ( options.onpreloaded( images ) === false ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
queue = _html2canvas.Parse( images, options );
|
||||
|
||||
if (typeof options.onparsed === "function") {
|
||||
if ( options.onparsed( queue ) === false ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
canvas = _html2canvas.Renderer( queue, options );
|
||||
|
||||
if (typeof options.onrendered === "function") {
|
||||
options.onrendered( canvas );
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
// 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
|
||||
};
|
||||
};
|
||||
|
||||
window.html2canvas.log = h2clog; // for renderers
|
||||
window.html2canvas.Renderer = {
|
||||
Canvas: undefined // We are assuming this will be used
|
||||
};
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
(function( $ ){
|
||||
$.fn.html2canvas = function(options) {
|
||||
if (options && options.profile && window.console && window.console.profile) {
|
||||
if (options && options.profile && window.console && window.console.profile && window.location.search !== "?selenium2") {
|
||||
console.profile();
|
||||
}
|
||||
var date = new Date(),
|
||||
|
@ -1,109 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>border tests</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<link href="#" type="text/css" rel="stylesheet">
|
||||
<script type="text/javascript" src="test.js"></script>
|
||||
<style type="text/css">
|
||||
|
||||
div { font: 12px Arial; }
|
||||
|
||||
span.bold { font-weight: bold; }
|
||||
|
||||
#div2 { z-index: 2; }
|
||||
#div3 { z-index: 1; }
|
||||
#div4 { z-index: 10; }
|
||||
|
||||
#div1,#div3 {
|
||||
height: 80px;
|
||||
position: relative;
|
||||
border: 23px double #669966;
|
||||
background-color: #ccffcc;
|
||||
padding-left: 5px;
|
||||
border-left-color:yellow;
|
||||
border-right-color: blueviolet;
|
||||
}
|
||||
|
||||
#div2 {
|
||||
opacity: 0.8;
|
||||
position: absolute;
|
||||
width: 150px;
|
||||
height: 201px;
|
||||
top: 20px;
|
||||
left: 170px;
|
||||
border: 20px solid #990000;
|
||||
background-color: #ffdddd;
|
||||
text-align: center;
|
||||
border-radius: 50px/160px;
|
||||
border-left-color: violet;
|
||||
border-top-width:0px;
|
||||
}
|
||||
|
||||
#div4 {
|
||||
opacity: 0.8;
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: 70px;
|
||||
top: 65px;
|
||||
left: 50px;
|
||||
border: 15px dashed #000099;
|
||||
background-color: #ddddff;
|
||||
text-align: left;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
#div5{
|
||||
border: 15px dashed #669966;
|
||||
background-color: #ccffcc;
|
||||
padding-left: 5px;
|
||||
position:relative;
|
||||
margin-bottom:-15px;
|
||||
height:50px;
|
||||
margin-top:10px;
|
||||
|
||||
}
|
||||
|
||||
#div6{
|
||||
border: 1px dashed #000099;
|
||||
background-color: #ddddff;
|
||||
text-align: left;
|
||||
padding-left: 10px;
|
||||
|
||||
}
|
||||
|
||||
</style></head>
|
||||
|
||||
<body>
|
||||
|
||||
<br />
|
||||
|
||||
<div id="div1">
|
||||
<br /><span class="bold">DIV #1</span>
|
||||
<br />position: relative;
|
||||
<div id="div2">
|
||||
<br /><span class="bold">DIV #2</span>
|
||||
<br />position: absolute;
|
||||
<br />z-index: 2;
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<div id="div3" style="background-image:url(image.jpg)">
|
||||
<br /><span class="bold">DIV #3</span>
|
||||
<br />position: relative;
|
||||
<br />z-index: 1;
|
||||
<div id="div4">
|
||||
<br /><span class="bold">DIV #4</span>
|
||||
<br />position: absolute;
|
||||
<br />z-index: 10;
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="div5"><br />DIV #5<br />position:relative;<br /></div>
|
||||
|
||||
<div id ="div6"><br />DIV #6<br />position:static;<br /></div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -41,8 +41,8 @@
|
||||
<body>
|
||||
|
||||
<div class="medium">
|
||||
<div style="background:url(../../assets/image.jpg) center center;"></div>
|
||||
<div style="background:url(../../assets/image.jpg) repeat-x center center;"></div>
|
||||
<div style='background:url("../../assets/image.jpg") center center;'></div>
|
||||
<div style="background:url('../../assets/image.jpg') repeat-x center center;"></div>
|
||||
<div style="background:url(../../assets/image.jpg) repeat-y center center;"></div>
|
||||
<div style="background:url(../../assets/image.jpg) no-repeat center center;"></div>
|
||||
</div>
|
||||
|
47
tests/cases/border/dashed.html
Normal file
47
tests/cases/border/dashed.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Borders tests</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<script type="text/javascript" src="../../test.js"></script>
|
||||
<style type="text/css">
|
||||
div {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
display: inline-block;
|
||||
margin: 10px;
|
||||
background:#6F428C;
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
.box1 {
|
||||
border-width: 1px;
|
||||
border-color: #00b5e2;
|
||||
}
|
||||
|
||||
.box2 {
|
||||
border-width: 3px;
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.box3 {
|
||||
border-width: 10px;
|
||||
}
|
||||
|
||||
.box4 {
|
||||
border-width: 50px;
|
||||
border-color: green;
|
||||
}
|
||||
|
||||
html {
|
||||
background: #3a84c3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="box1"> </div>
|
||||
<div class="box2"> </div>
|
||||
<div class="box3"> </div>
|
||||
<div class="box4"> </div>
|
||||
</body>
|
||||
</html>
|
47
tests/cases/border/dotted.html
Normal file
47
tests/cases/border/dotted.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Borders tests</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<script type="text/javascript" src="../../test.js"></script>
|
||||
<style type="text/css">
|
||||
div {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
display: inline-block;
|
||||
margin: 10px;
|
||||
background:#6F428C;
|
||||
border-style: dotted;
|
||||
}
|
||||
|
||||
.box1 {
|
||||
border-width: 1px;
|
||||
border-color: #00b5e2;
|
||||
}
|
||||
|
||||
.box2 {
|
||||
border-width: 3px;
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.box3 {
|
||||
border-width: 10px;
|
||||
}
|
||||
|
||||
.box4 {
|
||||
border-width: 50px;
|
||||
border-color: green;
|
||||
}
|
||||
|
||||
html {
|
||||
background: #3a84c3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="box1"> </div>
|
||||
<div class="box2"> </div>
|
||||
<div class="box3"> </div>
|
||||
<div class="box4"> </div>
|
||||
</body>
|
||||
</html>
|
47
tests/cases/border/double.html
Normal file
47
tests/cases/border/double.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Borders tests</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<script type="text/javascript" src="../../test.js"></script>
|
||||
<style type="text/css">
|
||||
div {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
display: inline-block;
|
||||
margin: 10px;
|
||||
background:#6F428C;
|
||||
border-style:double;
|
||||
}
|
||||
|
||||
.box1 {
|
||||
border-width: 1px;
|
||||
border-color: #00b5e2;
|
||||
}
|
||||
|
||||
.box2 {
|
||||
border-width: 3px;
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.box3 {
|
||||
border-width: 10px;
|
||||
}
|
||||
|
||||
.box4 {
|
||||
border-width: 50px;
|
||||
border-color: green;
|
||||
}
|
||||
|
||||
html {
|
||||
background: #3a84c3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="box1"> </div>
|
||||
<div class="box2"> </div>
|
||||
<div class="box3"> </div>
|
||||
<div class="box4"> </div>
|
||||
</body>
|
||||
</html>
|
47
tests/cases/border/solid.html
Normal file
47
tests/cases/border/solid.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Borders tests</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<script type="text/javascript" src="../../test.js"></script>
|
||||
<style type="text/css">
|
||||
div {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
display: inline-block;
|
||||
margin: 10px;
|
||||
background:#6F428C;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.box1 {
|
||||
border-width: 1px;
|
||||
border-color: #00b5e2;
|
||||
}
|
||||
|
||||
.box2 {
|
||||
border-width: 3px;
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.box3 {
|
||||
border-width: 10px;
|
||||
}
|
||||
|
||||
.box4 {
|
||||
border-width: 50px;
|
||||
border-color: green;
|
||||
}
|
||||
|
||||
html {
|
||||
background: #3a84c3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="box1"> </div>
|
||||
<div class="box2"> </div>
|
||||
<div class="box3"> </div>
|
||||
<div class="box4"> </div>
|
||||
</body>
|
||||
</html>
|
@ -97,7 +97,7 @@
|
||||
<h2><h2> text-transform</h2>
|
||||
<ul>
|
||||
<li style="text-transform:none;">text-transform:none;</li>
|
||||
<li style="text-transform:capitalize;">text-transform:capitalize; (including foreign characters such as Öaäå)</li>
|
||||
<li style="text-transform:capitalize;">text-transform: capitalize; (including foreign characters such as Öaäå)</li>
|
||||
<li style="text-transform:uppercase;">text-transform:uppercase;</li>
|
||||
<li style="text-transform:lowercase;">text-transform:lowercase;</li>
|
||||
</ul>
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
|
||||
<!-- src files -->
|
||||
<script type="text/javascript" src="../../external/jquery-1.6.2.js"></script>
|
||||
<script type="text/javascript" src="../assets/jquery-1.6.2.js"></script>
|
||||
|
||||
<!-- 'Core', 'Generate', 'Parse', 'Preload', 'Queue', 'Renderer', 'Util', 'renderers/Canvas' -->
|
||||
<script type="text/javascript" src="../../src/Core.js"></script>
|
||||
@ -53,9 +53,9 @@
|
||||
<div style="border-width: 5% 6px 12%;"></div> <!-- percentages aren't valid -->
|
||||
<div style="border-width: 5em 5ex 5in 5cm;"></div>
|
||||
<div style="border-width: 500em 500ex 500in 500cm;"></div>
|
||||
<div style="border-width: 5mm 5pt 5pc 5px;"></div>
|
||||
<div style="border-width: auto inherit;"></div>
|
||||
<div style="border-width: 500mm 500pt 500pc 500px;"></div>
|
||||
<div style="border-width: 5mm 5pt 5pc 5px;"></div>
|
||||
<div style="border-width: auto inherit;"></div>
|
||||
<div style="border-width: 500mm 500pt 500pc 500px;"></div>
|
||||
</div>
|
||||
|
||||
<div id="padding">
|
||||
@ -64,8 +64,8 @@
|
||||
<div style="padding: thin medium thick;"></div>
|
||||
<div style="padding: 5em 5ex 5in 5cm;"></div>
|
||||
<div style="padding: 500em 500ex 500in 500cm;"></div>
|
||||
<div style="padding: 5mm 5pt 5pc 5px;"></div>
|
||||
<div style="padding: 500mm 500pt 500pc 500px;"></div>
|
||||
<div style="padding: 5mm 5pt 5pc 5px;"></div>
|
||||
<div style="padding: 500mm 500pt 500pc 500px;"></div>
|
||||
<div style="padding: 1px 5%;"></div>
|
||||
<div style="padding: 15% 0 3%;"></div>
|
||||
</div>
|
||||
@ -78,16 +78,16 @@
|
||||
<div style="background-position: 5in 5cm;"></div>
|
||||
<div style="background-position: 500in 500cm;"></div>
|
||||
<div style="background-position: 500em 500ex;"></div>
|
||||
<div style="background-position: 5pc 5px;"></div>
|
||||
<div style="background-position: 500pc 500px;"></div>
|
||||
<div style="background-position: 5mm 5pt;"></div>
|
||||
<div style="background-position: 500mm 500pt;"></div>
|
||||
<div style="background-position: 5pc 5px;"></div>
|
||||
<div style="background-position: 500pc 500px;"></div>
|
||||
<div style="background-position: 5mm 5pt;"></div>
|
||||
<div style="background-position: 500mm 500pt;"></div>
|
||||
</div>
|
||||
|
||||
<div id="backgroundPositionPercentage">
|
||||
<div style="background-position: 5% 6px;"></div>
|
||||
<div style="background-position: center center;"></div>
|
||||
<div style="background-position: left bottom;"></div>
|
||||
<div style="background-position: 5% 6px;"></div>
|
||||
<div style="background-position: center center;"></div>
|
||||
<div style="background-position: left bottom;"></div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@ -138,7 +138,7 @@
|
||||
background: -ms-radial-gradient(75% 19%, ellipse closest-side, #ababab, #0000ff 33%,#991f1f 100%);
|
||||
background: radial-gradient(75% 19%, ellipse closest-side, #ababab, #0000ff 33%,#991f1f 100%);
|
||||
}
|
||||
|
||||
|
||||
.radialGradient2 {
|
||||
background: -moz-radial-gradient(75% 19%, ellipse closest-corner, #ababab, #0000ff 33%,#991f1f 100%);
|
||||
background: -webkit-radial-gradient(75% 19%, ellipse closest-corner, #ababab, #0000ff 33%,#991f1f 100%);
|
||||
@ -193,16 +193,16 @@
|
||||
<script>
|
||||
var pages = ["background","borders","external-content","forms","images","lists","origin","overflow","proxy","text-linethrough"],
|
||||
fixtures = $("#fixture-iframes");
|
||||
|
||||
|
||||
|
||||
$.each(pages, function(i, page){
|
||||
|
||||
|
||||
var iframe = $('<iframe />').attr('src',"../" + page + ".html");
|
||||
|
||||
|
||||
iframe.insertAfter( fixtures );
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<iframe src="../borders.html" id="borders"></iframe> -->
|
||||
|
31
tests/readme.md
Normal file
31
tests/readme.md
Normal file
@ -0,0 +1,31 @@
|
||||
<table><thead><tr><td></td><th>chrome<br />23.0.1271.97</th><th>firefox<br />12.0</th><th>iexplorer<br />9</th></tr></thead><tbody>
|
||||
<tr><td>background/encoded.html</td><td>100%</td><td>100%</td><td>100%</td></tr>
|
||||
<tr><td>background/linear-gradient.html</td><td>82.27%</td><td>85.64%</td><td>100%</td></tr>
|
||||
<tr><td>background/multi.html</td><td>96.6%</td><td>96.45%</td><td>96.89%</td></tr>
|
||||
<tr><td>background/position.html</td><td>97.03%</td><td>96.97%</td><td>97.03%</td></tr>
|
||||
<tr><td>background/radial-gradient.html</td><td>57.9%</td><td>54.87%</td><td>94.02%</td></tr>
|
||||
<tr><td>background/repeat.html</td><td>100%</td><td>100%</td><td>100%</td></tr>
|
||||
<tr><td>border/dashed.html</td><td>96.45%</td><td>98.38%</td><td>97.7%</td></tr>
|
||||
<tr><td>border/dotted.html</td><td>97.41%</td><td>96.46%</td><td>95.93%</td></tr>
|
||||
<tr><td>border/double.html</td><td>97.96%</td><td>97.87%</td><td>97.95%</td></tr>
|
||||
<tr><td>border/solid.html</td><td>99.97%</td><td>99.97%</td><td>99.98%</td></tr>
|
||||
<tr><td>forms.html</td><td>95.96%</td><td>94.55%</td><td>95.02%</td></tr>
|
||||
<tr><td>images/canvas.html</td><td>99.86%</td><td>100%</td><td>100%</td></tr>
|
||||
<tr><td>images/cross-origin.html</td><td>97.99%</td><td>97.58%</td><td>99.35%</td></tr>
|
||||
<tr><td>images/empty.html</td><td>99.86%</td><td>99.87%</td><td>99.85%</td></tr>
|
||||
<tr><td>images/images.html</td><td>83.72%</td><td>96.93%</td><td>55.09%</td></tr>
|
||||
<tr><td>images/svg.html</td><td>99.92%</td><td>96.79%</td><td>99.93%</td></tr>
|
||||
<tr><td>list/decimal-leading-zero.html</td><td>99.63%</td><td>99.72%</td><td>35.04%</td></tr>
|
||||
<tr><td>list/decimal.html</td><td>99.64%</td><td>99.73%</td><td>35.06%</td></tr>
|
||||
<tr><td>list/lower-alpha.html</td><td>99.65%</td><td>99.73%</td><td>35.05%</td></tr>
|
||||
<tr><td>list/upper-roman.html</td><td>99.45%</td><td>99.61%</td><td>35.11%</td></tr>
|
||||
<tr><td>overflow.html</td><td>96.85%</td><td>97.49%</td><td>96.51%</td></tr>
|
||||
<tr><td>text/linethrough.html</td><td>97.14%</td><td>94.12%</td><td>45.74%</td></tr>
|
||||
<tr><td>text/text.html</td><td>95.71%</td><td>94.67%</td><td>79.85%</td></tr>
|
||||
<tr><td>text/underline-lineheight.html</td><td>97.06%</td><td>92.35%</td><td>51.38%</td></tr>
|
||||
<tr><td>text/underline.html</td><td>97.65%</td><td>93.5%</td><td>45.69%</td></tr>
|
||||
<tr><td>visibility.html</td><td>99.19%</td><td>98.92%</td><td>99.39%</td></tr>
|
||||
<tr><td>zindex/z-index1.html</td><td>97.09%</td><td>99.38%</td><td>99.54%</td></tr>
|
||||
<tr><td>zindex/z-index2.html</td><td>95.94%</td><td>98.16%</td><td>97.81%</td></tr>
|
||||
<tr><td>zindex/z-index3.html</td><td>98.98%</td><td>98.55%</td><td>98.68%</td></tr>
|
||||
</tbody></table>
|
2
tests/results/.gitignore
vendored
Normal file
2
tests/results/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
@ -1,154 +1,272 @@
|
||||
var webdriver = require("webdriver.js").webdriver,
|
||||
http = require("http"),
|
||||
url = require("url"),
|
||||
path = require("path"),
|
||||
base64_arraybuffer = require('base64-arraybuffer'),
|
||||
PNG = require('png-js'),
|
||||
fs = require("fs");
|
||||
(function(){
|
||||
"use strict;"
|
||||
var webdriver = require("webdriver.js").webdriver,
|
||||
http = require("http"),
|
||||
url = require("url"),
|
||||
path = require("path"),
|
||||
base64_arraybuffer = require('base64-arraybuffer'),
|
||||
PNG = require('png-js'),
|
||||
fs = require("fs");
|
||||
|
||||
function createServer(port) {
|
||||
return http.createServer(function(request, response) {
|
||||
var uri = url.parse(request.url).pathname,
|
||||
filename = path.join(process.cwd(), uri);
|
||||
|
||||
function createServer(port) {
|
||||
return http.createServer(function(request, response) {
|
||||
var uri = url.parse(request.url).pathname,
|
||||
filename = path.join(process.cwd(), "../" + uri);
|
||||
|
||||
fs.exists(filename, function(exists) {
|
||||
if(!exists) {
|
||||
response.writeHead(404, {
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
response.write("404 Not Found\n");
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (fs.statSync(filename).isDirectory()) filename += '/index.html';
|
||||
|
||||
fs.readFile(filename, "binary", function(err, file) {
|
||||
if(err) {
|
||||
response.writeHead(500, {
|
||||
fs.exists(filename, function(exists) {
|
||||
if(!exists) {
|
||||
response.writeHead(404, {
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
response.write(err + "\n");
|
||||
response.write("404 Not Found\n");
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
|
||||
response.writeHead(200);
|
||||
response.write(file, "binary");
|
||||
response.end();
|
||||
if (fs.statSync(filename).isDirectory()) filename += '/index.html';
|
||||
|
||||
fs.readFile(filename, "binary", function(err, file) {
|
||||
if(err) {
|
||||
response.writeHead(500, {
|
||||
"Content-Type": "text/plain"
|
||||
});
|
||||
response.write(err + "\n");
|
||||
response.end();
|
||||
return;
|
||||
}
|
||||
|
||||
response.writeHead(200);
|
||||
response.write(file, "binary");
|
||||
response.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}).listen(port);
|
||||
}
|
||||
}).listen(port);
|
||||
}
|
||||
|
||||
function walkDir(dir, done) {
|
||||
var results = [];
|
||||
fs.readdir(dir, function(err, list) {
|
||||
if (err) return done(err);
|
||||
var i = 0;
|
||||
(function next() {
|
||||
var file = list[i++];
|
||||
if (!file) return done(null, results);
|
||||
file = dir + '/' + file;
|
||||
fs.stat(file, function(err, stat) {
|
||||
if (stat && stat.isDirectory()) {
|
||||
walkDir(file, function(err, res) {
|
||||
results = results.concat(res);
|
||||
function walkDir(dir, done) {
|
||||
var results = [];
|
||||
fs.readdir(dir, function(err, list) {
|
||||
if (err) return done(err);
|
||||
var i = 0;
|
||||
(function next() {
|
||||
var file = list[i++];
|
||||
if (!file) return done(null, results);
|
||||
file = dir + '/' + file;
|
||||
fs.stat(file, function(err, stat) {
|
||||
if (stat && stat.isDirectory()) {
|
||||
walkDir(file, function(err, res) {
|
||||
results = results.concat(res);
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
results.push(file);
|
||||
next();
|
||||
});
|
||||
} else {
|
||||
results.push(file);
|
||||
next();
|
||||
}
|
||||
});
|
||||
})();
|
||||
});
|
||||
};
|
||||
|
||||
function getPixelArray(base64, func) {
|
||||
var arraybuffer = base64_arraybuffer.decode(base64);
|
||||
(new PNG(arraybuffer)).decode(func);
|
||||
}
|
||||
|
||||
|
||||
function testPage(browser, url, done) {
|
||||
|
||||
browser.url(url)
|
||||
.$(".html2canvas", 5000, function(){
|
||||
this.execute(function(){
|
||||
var canvas = $('.html2canvas')[0];
|
||||
return canvas.toDataURL("image/png").substring(22);
|
||||
},[], function(dataurl) {
|
||||
getPixelArray(dataurl, function(h2cPixels) {
|
||||
browser.screenshot(function(base64){
|
||||
getPixelArray(base64, function(screenPixels) {
|
||||
var len = h2cPixels.length, index = 0, diff = 0;
|
||||
for (; index < len; index++) {
|
||||
if (screenPixels[index] - h2cPixels[index] !== 0) {
|
||||
diff++;
|
||||
}
|
||||
}
|
||||
done(100 - (Math.round((diff/h2cPixels.length) * 10000) / 100));
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function runBrowsers(pages){
|
||||
|
||||
var port = 5555,
|
||||
stats = {},
|
||||
browsers = ["chrome", "firefox", "internet explorer"],
|
||||
browsersDone = 0,
|
||||
server = createServer(port),
|
||||
numPages = pages.length;
|
||||
|
||||
var browserDone = function() {
|
||||
if (++browsersDone >= browsers.length) {
|
||||
server.close();
|
||||
console.log(stats);
|
||||
}
|
||||
};
|
||||
|
||||
browsers.forEach(function(browserName){
|
||||
var browser = new webdriver({
|
||||
browser: browserName
|
||||
}),
|
||||
browserType;
|
||||
function getPixelArray(base64, func) {
|
||||
var arraybuffer = base64_arraybuffer.decode(base64);
|
||||
(new PNG(arraybuffer)).decode(func);
|
||||
}
|
||||
|
||||
browser.status(function(browserInfo){
|
||||
browserType = [browserName, browser.version, browserInfo.os.name.replace(/\s+/g, "-").toLowerCase()].join("-");
|
||||
var date = new Date(),
|
||||
obj = {
|
||||
tests: {},
|
||||
date: date.toISOString()
|
||||
function getBaselineFiles() {
|
||||
return fs.readdirSync("tests/results/").filter(function(name) {
|
||||
return /\.baseline$/.test(name);
|
||||
}).map(function(item) {
|
||||
return "tests/results/" + item;
|
||||
});
|
||||
}
|
||||
|
||||
function testPage(browser, url, done) {
|
||||
browser.url(url)
|
||||
.$(".html2canvas", 5000, function(){
|
||||
this.execute(function(){
|
||||
var canvas = $('.html2canvas')[0];
|
||||
return canvas.toDataURL("image/png").substring(22);
|
||||
},[], function(dataurl) {
|
||||
getPixelArray(dataurl, function(h2cPixels) {
|
||||
browser.screenshot(function(base64){
|
||||
getPixelArray(base64, function(screenPixels) {
|
||||
var len = h2cPixels.length, index = 0, diff = 0;
|
||||
for (; index < len; index++) {
|
||||
if (screenPixels[index] - h2cPixels[index] !== 0) {
|
||||
diff++;
|
||||
}
|
||||
}
|
||||
done(100 - (Math.round((diff/h2cPixels.length) * 10000) / 100));
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var writeResultFile = function(filename, json, append) {
|
||||
fs.writeFile(filename + (append || ""), json);
|
||||
};
|
||||
|
||||
var openResultFile = function(stats, browser) {
|
||||
var tests = stats[browser].tests,
|
||||
filename = "tests/results/" + browser + ".json",
|
||||
write = writeResultFile.bind(null, filename, JSON.stringify(stats[browser]));
|
||||
|
||||
fs.exists(filename, function(exists) {
|
||||
if(exists) {
|
||||
fs.readFile(filename, "binary", parseResultFile.bind(null, tests, browser, write));
|
||||
} else {
|
||||
write();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var setColor = function(color, text) {
|
||||
return [color, " * ", ((isNaN(text.amount)) ? "NEW" : text.amount + "%"), " ", text.test].join("");
|
||||
};
|
||||
|
||||
var parseResultFile = function(tests, browser, createResultFile, err, file) {
|
||||
if (err) throw err;
|
||||
var data = JSON.parse(file),
|
||||
improved = [],
|
||||
regressed = [],
|
||||
newItems = [],
|
||||
colors = {
|
||||
red: "\x1b[1;31m",
|
||||
blue: "\x1b[1;36m",
|
||||
violet: "\x1b[0;35m",
|
||||
green: "\x1b[0;32m"
|
||||
};
|
||||
|
||||
Object.keys(tests).forEach(function(test){
|
||||
var testResult = tests[test],
|
||||
dataResult = data.tests[test],
|
||||
dataObject = {
|
||||
amount: (Math.abs(testResult - dataResult) < 0.02) ? 0 : testResult - dataResult,
|
||||
test: test
|
||||
};
|
||||
stats[browserType] = obj;
|
||||
stats[browserName] = obj;
|
||||
processPage(0);
|
||||
|
||||
if (dataObject.amount > 0) {
|
||||
improved.push(dataObject);
|
||||
} else if (dataObject.amount < 0) {
|
||||
regressed.push(dataObject);
|
||||
} else if (dataResult === undefined) {
|
||||
newItems.push(dataObject);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function processPage(index) {
|
||||
var page = pages[index++];
|
||||
testPage(browser, "http://localhost:" + port + "/tests/" + page + "?selenium", function(result) {
|
||||
if (numPages > index) {
|
||||
processPage(index);
|
||||
} else {
|
||||
browser.close(browserDone);
|
||||
}
|
||||
stats[browserType].tests[page] = result;
|
||||
if (newItems.length > 0 || improved.length > 0 || regressed.length > 0) {
|
||||
if (regressed.length === 0) {
|
||||
createResultFile(".baseline");
|
||||
}
|
||||
|
||||
console.log(colors.violet, "********************");
|
||||
console.log((regressed.length > 0) ? colors.red : colors.green, browser);
|
||||
|
||||
improved.map(setColor.bind(null, colors.green))
|
||||
.concat(regressed.map(setColor.bind(null, colors.red)))
|
||||
.concat(newItems.map(setColor.bind(null, colors.blue)))
|
||||
.forEach(function(item) {
|
||||
console.log(item);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
walkDir("cases", function(err, results) {
|
||||
if (err) throw err;
|
||||
runBrowsers(results);
|
||||
});
|
||||
function handleResults(stats) {
|
||||
Object.keys(stats).forEach(openResultFile.bind(null, stats));
|
||||
}
|
||||
|
||||
function runBrowsers(pages){
|
||||
|
||||
var port = 5555,
|
||||
stats = {},
|
||||
browsers = ["chrome", "firefox", "internet explorer"],
|
||||
browsersDone = 0,
|
||||
server = createServer(port),
|
||||
numPages = pages.length;
|
||||
|
||||
var browserDone = function() {
|
||||
if (++browsersDone >= browsers.length) {
|
||||
server.close();
|
||||
handleResults(stats);
|
||||
}
|
||||
};
|
||||
|
||||
browsers.forEach(function(browserName){
|
||||
var browser = new webdriver({
|
||||
browser: browserName
|
||||
}),
|
||||
browserType;
|
||||
browserName = browserName.replace("internet explorer", "iexplorer");
|
||||
browser.status(function(browserInfo){
|
||||
browserType = [browserName, browser.version, browserInfo.os.name.replace(/\s+/g, "-").toLowerCase()].join("-");
|
||||
var date = new Date(),
|
||||
obj = {
|
||||
tests: {},
|
||||
date: date.toISOString(),
|
||||
version: browser.version
|
||||
};
|
||||
stats[browserType] = obj;
|
||||
stats[browserName] = obj;
|
||||
processPage(0);
|
||||
});
|
||||
|
||||
function processPage(index) {
|
||||
var page = pages[index++];
|
||||
testPage(browser, "http://localhost:" + port + "/" + page + "?selenium", function(result) {
|
||||
if (numPages > index) {
|
||||
processPage(index);
|
||||
} else {
|
||||
browser.close(browserDone);
|
||||
}
|
||||
stats[browserType].tests[page] = result;
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
exports.tests = function() {
|
||||
getBaselineFiles().forEach(fs.unlinkSync.bind(fs));
|
||||
walkDir("tests/cases", function(err, results) {
|
||||
if (err) throw err;
|
||||
runBrowsers(results);
|
||||
});
|
||||
};
|
||||
|
||||
exports.baseline = function() {
|
||||
getBaselineFiles().forEach(function(file) {
|
||||
var newName = file.substring(0, file.length - 9);
|
||||
fs.renameSync(file, newName);
|
||||
console.log(newName, "created");
|
||||
});
|
||||
};
|
||||
|
||||
exports.markdown = function() {
|
||||
var data = {}, html = "<table><thead><tr><td></td>",
|
||||
browsers = ["chrome", "firefox", "iexplorer"];
|
||||
|
||||
browsers.forEach(function(browser) {
|
||||
data[browser] = JSON.parse(fs.readFileSync("tests/results/" + browser + ".json"));
|
||||
html += "<th>" + browser + "<br />" + data[browser].version + "</th>";
|
||||
});
|
||||
html += "</tr></thead><tbody>\n";
|
||||
|
||||
Object.keys(data[browsers[0]].tests).forEach(function(testFile) {
|
||||
html += "<tr><td>" + testFile.substring(12) + "</td>";
|
||||
browsers.forEach(function(browser) {
|
||||
html += "<td>" + Math.round(data[browser].tests[testFile] * 100) / 100 + "%</td>";
|
||||
});
|
||||
html += "</tr>\n"
|
||||
});
|
||||
|
||||
html += "</tbody></table>";
|
||||
|
||||
fs.writeFileSync("tests/readme.md", html);
|
||||
};
|
||||
|
||||
})();
|
@ -11,7 +11,7 @@ var h2cSelector, h2cOptions;
|
||||
|
||||
document.write(srcStart + '/tests/assets/jquery-1.6.2.js' + scrEnd);
|
||||
document.write(srcStart + '/tests/assets/jquery.plugin.html2canvas.js' + scrEnd);
|
||||
var html2canvas = ['Core', 'Generate', 'Parse', 'Preload', 'Queue', 'Renderer', 'Util', 'renderers/Canvas'], i;
|
||||
var html2canvas = ['Core', 'Generate', 'Parse', 'Preload', 'Queue', 'Renderer', 'Util', 'Support', 'Font', 'renderers/Canvas'], i;
|
||||
for (i = 0; i < html2canvas.length; ++i) {
|
||||
document.write(srcStart + '/src/' + html2canvas[i] + '.js' + scrEnd);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user