2011-11-27 06:33:41 +04:00
/ *
html2canvas @ VERSION @ < http : //html2canvas.hertzen.com>
Copyright ( c ) 2011 Niklas von Hertzen . All rights reserved .
http : //www.twitter.com/niklasvh
Released under MIT License
2012-06-26 02:30:45 +04:00
* /
2011-11-27 06:33:41 +04:00
2012-03-02 21:07:15 +04:00
_html2canvas . Preload = function ( options ) {
2012-03-03 23:03:59 +04:00
2012-03-02 20:05:03 +04:00
var images = {
2011-11-27 06:33:41 +04:00
numLoaded : 0 , // also failed are counted here
numFailed : 0 ,
numTotal : 0 ,
cleanupDone : false
} ,
pageOrigin ,
methods ,
i ,
count = 0 ,
2012-03-02 21:07:15 +04:00
element = options . elements [ 0 ] || document . body ,
2011-11-27 06:33:41 +04:00
doc = element . ownerDocument ,
domImages = doc . images , // TODO probably should limit it to images present in the element only
imgLen = domImages . length ,
2011-12-17 22:50:27 +04:00
link = doc . createElement ( "a" ) ,
2012-03-01 21:44:25 +04:00
supportCORS = ( function ( img ) {
return ( img . crossOrigin !== undefined ) ;
} ) ( new Image ( ) ) ,
2011-12-17 22:50:27 +04:00
timeoutTimer ;
2012-03-02 00:31:51 +04:00
2011-11-27 06:33:41 +04:00
link . href = window . location . href ;
pageOrigin = link . protocol + link . host ;
2012-03-02 20:05:03 +04:00
2012-03-03 23:03:59 +04:00
2011-11-27 06:33:41 +04:00
function isSameOrigin ( url ) {
2012-03-03 23:03:59 +04:00
link . href = url ;
2012-03-02 00:31:51 +04:00
link . href = link . href ; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/
2012-03-03 23:03:59 +04:00
var origin = link . protocol + link . host ;
2012-03-01 21:44:25 +04:00
return ( origin === pageOrigin ) ;
2011-11-27 06:33:41 +04:00
}
2012-03-03 23:03:59 +04:00
2011-11-27 06:33:41 +04:00
function start ( ) {
2012-02-25 22:58:04 +04:00
h2clog ( "html2canvas: start: images: " + images . numLoaded + " / " + images . numTotal + " (failed: " + images . numFailed + ")" ) ;
2011-11-27 06:33:41 +04:00
if ( ! images . firstRun && images . numLoaded >= images . numTotal ) {
2012-03-03 21:18:39 +04:00
h2clog ( "Finished loading images: # " + images . numTotal + " (failed: " + images . numFailed + ")" ) ;
2012-03-03 23:03:59 +04:00
2011-11-27 06:33:41 +04:00
if ( typeof options . complete === "function" ) {
options . complete ( images ) ;
}
2012-03-03 23:03:59 +04:00
2011-11-27 06:33:41 +04:00
}
}
2012-03-03 23:03:59 +04:00
2012-03-01 21:51:07 +04:00
// TODO modify proxy to serve images with CORS enabled, where available
2012-02-13 02:33:13 +04:00
function proxyGetImage ( url , img , imageObj ) {
2011-11-27 06:33:41 +04:00
var callback _name ,
2012-02-26 02:19:16 +04:00
scriptUrl = options . proxy ,
2012-02-13 02:33:13 +04:00
script ;
2011-11-27 06:33:41 +04:00
link . href = url ;
2012-02-13 02:33:13 +04:00
url = link . href ; // work around for pages with base href="" set - WARNING: this may change the url
2011-11-27 06:33:41 +04:00
2012-02-13 02:33:13 +04:00
callback _name = 'html2canvas_' + ( count ++ ) ;
imageObj . callbackname = callback _name ;
2012-03-03 23:03:59 +04:00
2011-11-27 06:33:41 +04:00
if ( scriptUrl . indexOf ( "?" ) > - 1 ) {
scriptUrl += "&" ;
} else {
scriptUrl += "?" ;
}
scriptUrl += 'url=' + encodeURIComponent ( url ) + '&callback=' + callback _name ;
2012-02-13 02:33:13 +04:00
script = doc . createElement ( "script" ) ;
2011-11-27 06:33:41 +04:00
window [ callback _name ] = function ( a ) {
if ( a . substring ( 0 , 6 ) === "error:" ) {
2012-02-13 02:33:13 +04:00
imageObj . succeeded = false ;
2011-11-27 06:33:41 +04:00
images . numLoaded ++ ;
images . numFailed ++ ;
2012-03-03 23:03:59 +04:00
start ( ) ;
2011-11-27 06:33:41 +04:00
} else {
2012-02-13 02:33:13 +04:00
setImageLoadHandlers ( img , imageObj ) ;
2012-03-03 23:03:59 +04:00
img . src = a ;
2011-11-27 06:33:41 +04:00
}
window [ callback _name ] = undefined ; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9)
try {
delete window [ callback _name ] ; // for all browser that support this
} catch ( ex ) { }
script . parentNode . removeChild ( script ) ;
script = null ;
2012-02-13 02:33:13 +04:00
delete imageObj . script ;
delete imageObj . callbackname ;
2011-11-27 06:33:41 +04:00
} ;
script . setAttribute ( "type" , "text/javascript" ) ;
2012-02-13 02:33:13 +04:00
script . setAttribute ( "src" , scriptUrl ) ;
imageObj . script = script ;
2011-11-27 06:33:41 +04:00
window . document . body . appendChild ( script ) ;
}
2012-03-03 23:03:59 +04:00
2011-11-27 06:33:41 +04:00
function getImages ( el ) {
2012-03-03 23:03:59 +04:00
2011-11-27 06:33:41 +04:00
// if (!this.ignoreRe.test(el.nodeName)){
2012-03-03 23:03:59 +04:00
//
2012-06-26 02:30:45 +04:00
2012-03-02 20:05:03 +04:00
var contents = _html2canvas . Util . Children ( el ) ,
2011-11-27 06:33:41 +04:00
i ,
background _image ,
src ,
img ,
elNodeType = false ;
2012-03-03 23:03:59 +04:00
2012-06-26 02:30:45 +04:00
// Firefox fails with permission denied on pages with iframes
try {
var contentsLen = contents . length ;
for ( i = 0 ; i < contentsLen ; i += 1 ) {
// var ignRe = new RegExp("("+this.ignoreElements+")");
// if (!ignRe.test(element.nodeName)){
getImages ( contents [ i ] ) ;
// }
}
2011-11-27 06:33:41 +04:00
}
2012-06-26 02:30:45 +04:00
catch ( e ) { }
2012-03-03 23:03:59 +04:00
2011-11-27 06:33:41 +04:00
// }
try {
elNodeType = el . nodeType ;
} catch ( ex ) {
elNodeType = false ;
2012-02-25 22:58:04 +04:00
h2clog ( "html2canvas: failed to access some element's nodeType - Exception: " + ex . message ) ;
2011-11-27 06:33:41 +04:00
}
if ( elNodeType === 1 || elNodeType === undefined ) {
2012-03-03 23:03:59 +04:00
2012-02-26 02:19:16 +04:00
// opera throws exception on external-content.html
try {
2012-03-02 20:05:03 +04:00
background _image = _html2canvas . Util . getCSS ( el , 'backgroundImage' ) ;
2012-02-26 02:19:16 +04:00
} catch ( e ) {
2012-02-28 14:40:44 +04:00
h2clog ( "html2canvas: failed to get background-image - Exception: " + e . message ) ;
2012-02-26 02:19:16 +04:00
}
2012-03-03 23:03:59 +04:00
if ( background _image && background _image !== "1" && background _image !== "none" ) {
2011-11-27 06:33:41 +04:00
// TODO add multi image background support
2012-03-03 23:03:59 +04:00
2012-03-04 23:20:22 +04:00
if ( /^(-webkit|-o|-moz|-ms|linear)-/ . test ( background _image ) ) {
2012-03-03 23:03:59 +04:00
2012-03-02 20:05:03 +04:00
img = _html2canvas . Generate . Gradient ( background _image , _html2canvas . Util . Bounds ( el ) ) ;
2011-11-27 06:33:41 +04:00
if ( img !== undefined ) {
2012-02-26 02:19:16 +04:00
images [ background _image ] = {
2012-03-03 23:03:59 +04:00
img : img ,
2012-02-26 02:19:16 +04:00
succeeded : true
} ;
2011-11-27 06:33:41 +04:00
images . numTotal ++ ;
images . numLoaded ++ ;
start ( ) ;
2012-03-03 23:03:59 +04:00
2011-11-27 06:33:41 +04:00
}
2012-03-03 23:03:59 +04:00
} else {
src = _html2canvas . Util . backgroundImage ( background _image . match ( /data:image\/.*;base64,/i ) ? background _image : background _image . split ( "," ) [ 0 ] ) ;
2012-03-02 20:05:03 +04:00
methods . loadImage ( src ) ;
2011-11-27 06:33:41 +04:00
}
2012-03-03 23:03:59 +04:00
2012-06-26 02:30:45 +04:00
/ *
2011-11-27 06:33:41 +04:00
if ( background _image && background _image !== "1" && background _image !== "none" && background _image . substring ( 0 , 7 ) !== "-webkit" && background _image . substring ( 0 , 3 ) !== "-o-" && background _image . substring ( 0 , 4 ) !== "-moz" ) {
// TODO add multi image background support
2012-03-12 10:15:03 +04:00
src = _html2canvas . Util . backgroundImage ( background _image . split ( "," ) [ 0 ] ) ;
2012-03-03 23:03:59 +04:00
methods . loadImage ( src ) ; * /
2011-11-27 06:33:41 +04:00
}
}
2012-03-03 23:03:59 +04:00
}
2012-02-13 02:33:13 +04:00
function setImageLoadHandlers ( img , imageObj ) {
img . onload = function ( ) {
2012-03-01 21:44:25 +04:00
if ( imageObj . timer !== undefined ) {
// CORS succeeded
window . clearTimeout ( imageObj . timer ) ;
}
2012-03-03 23:03:59 +04:00
2012-02-13 02:33:13 +04:00
images . numLoaded ++ ;
imageObj . succeeded = true ;
2012-03-05 10:54:42 +04:00
img . onerror = img . onload = null ;
2012-02-13 02:33:13 +04:00
start ( ) ;
} ;
img . onerror = function ( ) {
2012-03-03 23:03:59 +04:00
2012-03-01 21:44:25 +04:00
if ( img . crossOrigin === "anonymous" ) {
2012-03-02 20:05:03 +04:00
// CORS failed
2012-03-01 21:44:25 +04:00
window . clearTimeout ( imageObj . timer ) ;
// let's try with proxy instead
if ( options . proxy ) {
var src = img . src ;
img = new Image ( ) ;
imageObj . img = img ;
img . src = src ;
proxyGetImage ( img . src , img , imageObj ) ;
return ;
}
}
2012-03-03 23:03:59 +04:00
2012-02-13 02:33:13 +04:00
images . numLoaded ++ ;
images . numFailed ++ ;
imageObj . succeeded = false ;
2012-03-05 10:54:42 +04:00
img . onerror = img . onload = null ;
2012-02-13 02:33:13 +04:00
start ( ) ;
2012-03-03 23:03:59 +04:00
2012-02-13 02:33:13 +04:00
} ;
2012-03-03 23:03:59 +04:00
2012-06-26 02:30:45 +04:00
// TODO Opera has no load/error event for SVG images
2012-03-03 23:03:59 +04:00
2012-06-26 02:30:45 +04:00
// Opera ninja onload's cached images
/ *
2012-03-03 21:18:39 +04:00
window . setTimeout ( function ( ) {
if ( img . width !== 0 && imageObj . succeeded === undefined ) {
img . onload ( ) ;
2012-03-03 23:03:59 +04:00
}
2012-03-03 21:18:39 +04:00
} , 100 ) ; // needs a reflow for base64 encoded images? interestingly timeout of 0 doesn't work but 1 does.
2012-06-26 02:30:45 +04:00
* /
2012-02-13 02:33:13 +04:00
}
2012-03-03 23:03:59 +04:00
2012-02-13 02:33:13 +04:00
2011-11-27 06:33:41 +04:00
methods = {
loadImage : function ( src ) {
2012-03-03 23:03:59 +04:00
var img , imageObj ;
2011-12-17 22:50:27 +04:00
if ( src && images [ src ] === undefined ) {
2012-03-03 23:03:59 +04:00
img = new Image ( ) ;
2011-11-27 06:33:41 +04:00
if ( src . match ( /data:image\/.*;base64,/i ) ) {
img . src = src . replace ( /url\(['"]{0,}|['"]{0,}\)$/ig , '' ) ;
2012-03-02 20:05:03 +04:00
imageObj = images [ src ] = {
img : img
} ;
2011-11-27 06:33:41 +04:00
images . numTotal ++ ;
2012-02-13 02:33:13 +04:00
setImageLoadHandlers ( img , imageObj ) ;
2012-03-03 23:03:59 +04:00
} else if ( isSameOrigin ( src ) || options . allowTaint === true ) {
2012-03-02 20:05:03 +04:00
imageObj = images [ src ] = {
img : img
} ;
2011-11-27 06:33:41 +04:00
images . numTotal ++ ;
2012-02-13 02:33:13 +04:00
setImageLoadHandlers ( img , imageObj ) ;
2011-11-27 06:33:41 +04:00
img . src = src ;
2012-03-01 21:44:25 +04:00
} else if ( supportCORS && ! options . allowTaint && options . useCORS ) {
// attempt to load with CORS
2012-03-03 23:03:59 +04:00
img . crossOrigin = "anonymous" ;
2012-03-02 20:05:03 +04:00
imageObj = images [ src ] = {
img : img
} ;
2012-03-01 21:44:25 +04:00
images . numTotal ++ ;
setImageLoadHandlers ( img , imageObj ) ;
2012-03-03 23:03:59 +04:00
img . src = src ;
2012-03-02 20:05:03 +04:00
// work around for https://bugs.webkit.org/show_bug.cgi?id=80028
img . customComplete = function ( ) {
2012-03-03 23:03:59 +04:00
if ( ! this . img . complete ) {
2012-03-02 20:05:03 +04:00
this . timer = window . setTimeout ( this . img . customComplete , 100 ) ;
2012-03-03 23:03:59 +04:00
} else {
this . img . onerror ( ) ;
2012-03-02 20:05:03 +04:00
}
2012-03-03 23:03:59 +04:00
} . bind ( imageObj ) ;
2012-03-01 21:44:25 +04:00
img . customComplete ( ) ;
2012-03-03 23:03:59 +04:00
2012-03-01 21:44:25 +04:00
} else if ( options . proxy ) {
2012-03-02 20:05:03 +04:00
imageObj = images [ src ] = {
img : img
} ;
2011-11-27 06:33:41 +04:00
images . numTotal ++ ;
2012-02-13 02:33:13 +04:00
proxyGetImage ( src , img , imageObj ) ;
2011-11-27 06:33:41 +04:00
}
2012-03-03 23:03:59 +04:00
}
2011-11-27 06:33:41 +04:00
} ,
cleanupDOM : function ( cause ) {
2011-12-17 22:50:27 +04:00
var img , src ;
2011-11-27 06:33:41 +04:00
if ( ! images . cleanupDone ) {
if ( cause && typeof cause === "string" ) {
2012-02-25 22:58:04 +04:00
h2clog ( "html2canvas: Cleanup because: " + cause ) ;
2011-11-27 06:33:41 +04:00
} else {
2012-02-25 22:58:04 +04:00
h2clog ( "html2canvas: Cleanup after timeout: " + options . timeout + " ms." ) ;
2011-11-27 06:33:41 +04:00
}
for ( src in images ) {
if ( images . hasOwnProperty ( src ) ) {
img = images [ src ] ;
if ( typeof img === "object" && img . callbackname && img . succeeded === undefined ) {
// cancel proxy image request
window [ img . callbackname ] = undefined ; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9)
try {
delete window [ img . callbackname ] ; // for all browser that support this
} catch ( ex ) { }
if ( img . script && img . script . parentNode ) {
img . script . setAttribute ( "src" , "about:blank" ) ; // try to cancel running request
img . script . parentNode . removeChild ( img . script ) ;
}
images . numLoaded ++ ;
images . numFailed ++ ;
2012-02-25 22:58:04 +04:00
h2clog ( "html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images . numLoaded + " / " + images . numTotal ) ;
2011-11-27 06:33:41 +04:00
}
}
}
// cancel any pending requests
if ( window . stop !== undefined ) {
window . stop ( ) ;
} else if ( document . execCommand !== undefined ) {
document . execCommand ( "Stop" , false ) ;
}
if ( document . close !== undefined ) {
document . close ( ) ;
}
images . cleanupDone = true ;
if ( ! ( cause && typeof cause === "string" ) ) {
start ( ) ;
}
}
} ,
renderingDone : function ( ) {
2012-02-26 02:19:16 +04:00
if ( timeoutTimer ) {
window . clearTimeout ( timeoutTimer ) ;
}
2011-11-27 06:33:41 +04:00
}
2012-03-03 23:03:59 +04:00
2011-11-27 06:33:41 +04:00
} ;
if ( options . timeout > 0 ) {
timeoutTimer = window . setTimeout ( methods . cleanupDOM , options . timeout ) ;
}
2012-02-25 22:58:04 +04:00
h2clog ( 'html2canvas: Preload starts: finding background-images' ) ;
2011-11-27 06:33:41 +04:00
images . firstRun = true ;
getImages ( element ) ;
2012-03-03 23:03:59 +04:00
2012-02-25 22:58:04 +04:00
h2clog ( 'html2canvas: Preload: Finding images' ) ;
2011-11-27 06:33:41 +04:00
// load <img> images
for ( i = 0 ; i < imgLen ; i += 1 ) {
2011-12-17 22:50:27 +04:00
methods . loadImage ( domImages [ i ] . getAttribute ( "src" ) ) ;
2011-11-27 06:33:41 +04:00
}
2012-03-03 23:03:59 +04:00
2011-11-27 06:33:41 +04:00
images . firstRun = false ;
2012-02-25 22:58:04 +04:00
h2clog ( 'html2canvas: Preload: Done.' ) ;
2011-11-27 06:33:41 +04:00
if ( images . numTotal === images . numLoaded ) {
start ( ) ;
2012-03-03 23:03:59 +04:00
}
2011-11-27 06:33:41 +04:00
return methods ;
2012-03-03 23:03:59 +04:00
2011-11-27 06:33:41 +04:00
} ;