2012-03-02 21:07:15 +04:00
_html2canvas . Preload = function ( options ) {
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
var images = {
numLoaded : 0 , // also failed are counted here
numFailed : 0 ,
numTotal : 0 ,
cleanupDone : false
} ,
pageOrigin ,
methods ,
i ,
count = 0 ,
element = options . elements [ 0 ] || document . body ,
doc = element . ownerDocument ,
domImages = doc . images , // TODO probably should limit it to images present in the element only
imgLen = domImages . length ,
link = doc . createElement ( "a" ) ,
supportCORS = ( function ( img ) {
return ( img . crossOrigin !== undefined ) ;
} ) ( new Image ( ) ) ,
timeoutTimer ;
link . href = window . location . href ;
pageOrigin = link . protocol + link . host ;
function isSameOrigin ( url ) {
link . href = url ;
link . href = link . href ; // YES, BELIEVE IT OR NOT, that is required for IE9 - http://jsfiddle.net/niklasvh/2e48b/
var origin = link . protocol + link . host ;
return ( origin === pageOrigin ) ;
}
function start ( ) {
h2clog ( "html2canvas: start: images: " + images . numLoaded + " / " + images . numTotal + " (failed: " + images . numFailed + ")" ) ;
if ( ! images . firstRun && images . numLoaded >= images . numTotal ) {
h2clog ( "Finished loading images: # " + images . numTotal + " (failed: " + images . numFailed + ")" ) ;
if ( typeof options . complete === "function" ) {
options . complete ( images ) ;
}
2012-03-03 23:03:59 +04:00
2011-11-27 06:33:41 +04:00
}
2012-11-25 22:59:31 +04:00
}
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
// TODO modify proxy to serve images with CORS enabled, where available
function proxyGetImage ( url , img , imageObj ) {
var callback _name ,
scriptUrl = options . proxy ,
script ;
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
link . href = url ;
url = link . href ; // work around for pages with base href="" set - WARNING: this may change the url
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
callback _name = 'html2canvas_' + ( count ++ ) ;
imageObj . callbackname = callback _name ;
if ( scriptUrl . indexOf ( "?" ) > - 1 ) {
scriptUrl += "&" ;
} else {
scriptUrl += "?" ;
2011-11-27 06:33:41 +04:00
}
2012-11-25 22:59:31 +04:00
scriptUrl += 'url=' + encodeURIComponent ( url ) + '&callback=' + callback _name ;
script = doc . createElement ( "script" ) ;
window [ callback _name ] = function ( a ) {
if ( a . substring ( 0 , 6 ) === "error:" ) {
imageObj . succeeded = false ;
images . numLoaded ++ ;
images . numFailed ++ ;
start ( ) ;
} else {
setImageLoadHandlers ( img , imageObj ) ;
img . src = a ;
}
window [ callback _name ] = undefined ; // to work with IE<9 // NOTE: that the undefined callback property-name still exists on the window object (for IE<9)
try {
delete window [ callback _name ] ; // for all browser that support this
} catch ( ex ) { }
script . parentNode . removeChild ( script ) ;
script = null ;
delete imageObj . script ;
delete imageObj . callbackname ;
} ;
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
script . setAttribute ( "type" , "text/javascript" ) ;
script . setAttribute ( "src" , scriptUrl ) ;
imageObj . script = script ;
window . document . body . appendChild ( script ) ;
2011-11-27 06:33:41 +04:00
2012-11-25 22:59:31 +04:00
}
2011-11-27 06:33:41 +04:00
2013-01-12 00:36:23 +04:00
function loadPseudoElement ( element , type ) {
var style = window . getComputedStyle ( element , type ) ,
content = style . content ;
if ( content . substr ( 0 , 3 ) === 'url' ) {
methods . loadImage ( _html2canvas . Util . parseBackgroundImage ( content ) [ 0 ] . args [ 0 ] ) ;
}
loadBackgroundImages ( style . backgroundImage , element ) ;
}
2011-11-27 06:33:41 +04:00
2013-01-12 00:36:23 +04:00
function loadPseudoElementImages ( element ) {
loadPseudoElement ( element , ":before" ) ;
loadPseudoElement ( element , ":after" ) ;
}
function loadBackgroundImages ( background _image , el ) {
var background _images ,
2012-11-25 22:59:31 +04:00
src ,
img ,
2013-01-12 00:36:23 +04:00
bounds ;
// opera throws exception on external-content.html
background _images = _html2canvas . Util . parseBackgroundImage ( background _image ) ;
for ( var imageIndex = background _images . length ; imageIndex -- > 0 ; ) {
background _image = background _images [ imageIndex ] ;
if ( ! background _image ||
! background _image . method ||
! background _image . args ||
background _image . args . length === 0 ) {
continue ;
}
if ( background _image . method === 'url' ) {
src = background _image . args [ 0 ] ;
methods . loadImage ( src ) ;
} else if ( background _image . method . match ( /\-?gradient$/ ) ) {
if ( bounds === undefined ) {
bounds = _html2canvas . Util . Bounds ( el ) ;
}
img = _html2canvas . Generate . Gradient ( background _image . value , bounds ) ;
if ( img !== undefined ) {
images [ background _image . value ] = {
img : img ,
succeeded : true
} ;
images . numTotal ++ ;
images . numLoaded ++ ;
start ( ) ;
}
}
}
}
function getImages ( el ) {
var contents = _html2canvas . Util . Children ( el ) ,
i ,
2012-11-25 22:59:31 +04:00
elNodeType = false ;
// Firefox fails with permission denied on pages with iframes
try {
var contentsLen = contents . length ;
for ( i = 0 ; i < contentsLen ; i += 1 ) {
getImages ( contents [ i ] ) ;
}
2011-11-27 06:33:41 +04:00
}
2012-11-25 22:59:31 +04:00
catch ( e ) { }
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
try {
elNodeType = el . nodeType ;
} catch ( ex ) {
elNodeType = false ;
h2clog ( "html2canvas: failed to access some element's nodeType - Exception: " + ex . message ) ;
}
2012-03-03 23:03:59 +04:00
2013-01-12 00:36:23 +04:00
if ( elNodeType === 1 || elNodeType === undefined ) {
loadPseudoElementImages ( el ) ;
2012-11-25 22:59:31 +04:00
try {
2013-01-12 00:36:23 +04:00
loadBackgroundImages ( _html2canvas . Util . getCSS ( el , 'backgroundImage' ) , el ) ;
2013-01-04 00:25:35 +04:00
} catch ( e ) {
2012-11-25 22:59:31 +04:00
h2clog ( "html2canvas: failed to get background-image - Exception: " + e . message ) ;
}
2013-01-12 00:36:23 +04:00
loadBackgroundImages ( el ) ;
2012-11-25 22:59:31 +04:00
}
}
function setImageLoadHandlers ( img , imageObj ) {
img . onload = function ( ) {
if ( imageObj . timer !== undefined ) {
// CORS succeeded
window . clearTimeout ( imageObj . timer ) ;
}
images . numLoaded ++ ;
imageObj . succeeded = true ;
img . onerror = img . onload = null ;
start ( ) ;
} ;
img . onerror = function ( ) {
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
if ( img . crossOrigin === "anonymous" ) {
// CORS failed
window . clearTimeout ( imageObj . timer ) ;
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
// let's try with proxy instead
if ( options . proxy ) {
var src = img . src ;
img = new Image ( ) ;
imageObj . img = img ;
img . src = src ;
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
proxyGetImage ( img . src , img , imageObj ) ;
return ;
2011-11-27 06:33:41 +04:00
}
2012-11-25 22:59:31 +04:00
}
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
images . numLoaded ++ ;
images . numFailed ++ ;
imageObj . succeeded = false ;
img . onerror = img . onload = null ;
start ( ) ;
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
} ;
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
// TODO Opera has no load/error event for SVG images
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +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-11-25 22:59:31 +04:00
}
methods = {
loadImage : function ( src ) {
var img , imageObj ;
if ( src && images [ src ] === undefined ) {
img = new Image ( ) ;
if ( src . match ( /data:image\/.*;base64,/i ) ) {
img . src = src . replace ( /url\(['"]{0,}|['"]{0,}\)$/ig , '' ) ;
imageObj = images [ src ] = {
img : img
} ;
images . numTotal ++ ;
setImageLoadHandlers ( img , imageObj ) ;
} else if ( isSameOrigin ( src ) || options . allowTaint === true ) {
imageObj = images [ src ] = {
img : img
} ;
images . numTotal ++ ;
setImageLoadHandlers ( img , imageObj ) ;
img . src = src ;
} else if ( supportCORS && ! options . allowTaint && options . useCORS ) {
// attempt to load with CORS
img . crossOrigin = "anonymous" ;
imageObj = images [ src ] = {
img : img
} ;
images . numTotal ++ ;
setImageLoadHandlers ( img , imageObj ) ;
img . src = src ;
// work around for https://bugs.webkit.org/show_bug.cgi?id=80028
img . customComplete = function ( ) {
if ( ! this . img . complete ) {
this . timer = window . setTimeout ( this . img . customComplete , 100 ) ;
} else {
this . img . onerror ( ) ;
2012-03-03 23:03:59 +04:00
}
2012-11-25 22:59:31 +04:00
} . bind ( imageObj ) ;
img . customComplete ( ) ;
} else if ( options . proxy ) {
imageObj = images [ src ] = {
img : img
} ;
images . numTotal ++ ;
proxyGetImage ( src , img , imageObj ) ;
}
}
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
} ,
cleanupDOM : function ( cause ) {
var img , src ;
if ( ! images . cleanupDone ) {
if ( cause && typeof cause === "string" ) {
h2clog ( "html2canvas: Cleanup because: " + cause ) ;
} else {
h2clog ( "html2canvas: Cleanup after timeout: " + options . timeout + " ms." ) ;
2011-11-27 06:33:41 +04:00
}
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +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 ++ ;
h2clog ( "html2canvas: Cleaned up failed img: '" + src + "' Steps: " + images . numLoaded + " / " + images . numTotal ) ;
}
}
}
2011-11-27 06:33:41 +04:00
2012-11-25 22:59:31 +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 ( ) ;
}
}
} ,
2013-01-03 00:51:03 +04:00
2012-11-25 22:59:31 +04:00
renderingDone : function ( ) {
if ( timeoutTimer ) {
window . clearTimeout ( timeoutTimer ) ;
}
2011-11-27 06:33:41 +04:00
}
2012-11-25 22:59:31 +04:00
} ;
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
if ( options . timeout > 0 ) {
timeoutTimer = window . setTimeout ( methods . cleanupDOM , options . timeout ) ;
}
2013-01-02 22:58:48 +04:00
2012-11-25 22:59:31 +04:00
h2clog ( 'html2canvas: Preload starts: finding background-images' ) ;
images . firstRun = true ;
2012-03-03 23:03:59 +04:00
2013-01-12 00:36:23 +04:00
getImages ( element ) ;
2012-03-03 23:03:59 +04:00
2012-11-25 22:59:31 +04:00
h2clog ( 'html2canvas: Preload: Finding images' ) ;
// load <img> images
for ( i = 0 ; i < imgLen ; i += 1 ) {
methods . loadImage ( domImages [ i ] . getAttribute ( "src" ) ) ;
}
2011-11-27 06:33:41 +04:00
2012-11-25 22:59:31 +04:00
images . firstRun = false ;
h2clog ( 'html2canvas: Preload: Done.' ) ;
if ( images . numTotal === images . numLoaded ) {
start ( ) ;
}
2011-11-27 06:33:41 +04:00
2012-11-25 22:59:31 +04:00
return methods ;
2011-11-27 06:33:41 +04:00
2012-11-25 22:59:31 +04:00
} ;