2015-12-06 21:46:23 +03:00
/ *
2016-01-23 21:41:53 +03:00
html2canvas 0.5 . 0 - beta3 < http : //html2canvas.hertzen.com>
Copyright ( c ) 2016 Niklas von Hertzen
2015-12-06 21:46:23 +03:00
Released under License
* /
2016-01-23 21:41:53 +03:00
! function ( e ) { if ( "object" == typeof exports && "undefined" != typeof module ) module . exports = e ( ) ; else if ( "function" == typeof define && define . amd ) define ( [ ] , e ) ; else { var f ; "undefined" != typeof window ? f = window : "undefined" != typeof global ? f = global : "undefined" != typeof self && ( f = self ) , f . html2canvas = e ( ) } } ( function ( ) { var define , module , exports ; return ( function e ( t , n , r ) { function s ( o , u ) { if ( ! n [ o ] ) { if ( ! t [ o ] ) { var a = typeof require == "function" && require ; if ( ! u && a ) return a ( o , ! 0 ) ; if ( i ) return i ( o , ! 0 ) ; var f = new Error ( "Cannot find module '" + o + "'" ) ; throw f . code = "MODULE_NOT_FOUND" , f } var l = n [ o ] = { exports : { } } ; t [ o ] [ 0 ] . call ( l . exports , function ( e ) { var n = t [ o ] [ 1 ] [ e ] ; return s ( n ? n : e ) } , l , l . exports , e , t , n , r ) } return n [ o ] . exports } var i = typeof require == "function" && require ; for ( var o = 0 ; o < r . length ; o ++ ) s ( r [ o ] ) ; return s } ) ( { 1 : [ function ( require , module , exports ) {
2015-12-06 21:46:23 +03:00
( function ( global ) {
2016-01-23 21:41:53 +03:00
/*! http://mths.be/punycode v1.2.4 by @mathias */
2015-12-06 21:46:23 +03:00
; ( function ( root ) {
/** Detect free variables */
2016-01-23 21:41:53 +03:00
var freeExports = typeof exports == 'object' && exports ;
2015-12-06 21:46:23 +03:00
var freeModule = typeof module == 'object' && module &&
2016-01-23 21:41:53 +03:00
module . exports == freeExports && module ;
2015-12-06 21:46:23 +03:00
var freeGlobal = typeof global == 'object' && global ;
2016-01-23 21:41:53 +03:00
if ( freeGlobal . global === freeGlobal || freeGlobal . window === freeGlobal ) {
2015-12-06 21:46:23 +03:00
root = freeGlobal ;
}
/ * *
* The ` punycode ` object .
* @ name punycode
* @ type Object
* /
var punycode ,
/** Highest positive signed 32-bit float value */
maxInt = 2147483647 , // aka. 0x7FFFFFFF or 2^31-1
/** Bootstring parameters */
base = 36 ,
tMin = 1 ,
tMax = 26 ,
skew = 38 ,
damp = 700 ,
initialBias = 72 ,
initialN = 128 , // 0x80
delimiter = '-' , // '\x2D'
/** Regular expressions */
regexPunycode = /^xn--/ ,
2016-01-23 21:41:53 +03:00
regexNonASCII = /[^ -~]/ , // unprintable ASCII chars + non-ASCII chars
regexSeparators = /\x2E|\u3002|\uFF0E|\uFF61/g , // RFC 3490 separators
2015-12-06 21:46:23 +03:00
/** Error messages */
errors = {
'overflow' : 'Overflow: input needs wider integers to process' ,
'not-basic' : 'Illegal input >= 0x80 (not a basic code point)' ,
'invalid-input' : 'Invalid input'
} ,
/** Convenience shortcuts */
baseMinusTMin = base - tMin ,
floor = Math . floor ,
stringFromCharCode = String . fromCharCode ,
/** Temporary variable */
key ;
/*--------------------------------------------------------------------------*/
/ * *
* A generic error utility function .
* @ private
* @ param { String } type The error type .
* @ returns { Error } Throws a ` RangeError ` with the applicable error message .
* /
function error ( type ) {
throw RangeError ( errors [ type ] ) ;
}
/ * *
* A generic ` Array#map ` utility function .
* @ private
* @ param { Array } array The array to iterate over .
* @ param { Function } callback The function that gets called for every array
* item .
* @ returns { Array } A new array of values returned by the callback function .
* /
function map ( array , fn ) {
var length = array . length ;
while ( length -- ) {
2016-01-23 21:41:53 +03:00
array [ length ] = fn ( array [ length ] ) ;
2015-12-06 21:46:23 +03:00
}
2016-01-23 21:41:53 +03:00
return array ;
2015-12-06 21:46:23 +03:00
}
/ * *
2016-01-23 21:41:53 +03:00
* A simple ` Array#map ` - like wrapper to work with domain name strings .
2015-12-06 21:46:23 +03:00
* @ private
2016-01-23 21:41:53 +03:00
* @ param { String } domain The domain name .
2015-12-06 21:46:23 +03:00
* @ param { Function } callback The function that gets called for every
* character .
* @ returns { Array } A new string of characters returned by the callback
* function .
* /
function mapDomain ( string , fn ) {
2016-01-23 21:41:53 +03:00
return map ( string . split ( regexSeparators ) , fn ) . join ( '.' ) ;
2015-12-06 21:46:23 +03:00
}
/ * *
* Creates an array containing the numeric code points of each Unicode
* character in the string . While JavaScript uses UCS - 2 internally ,
* this function will convert a pair of surrogate halves ( each of which
* UCS - 2 exposes as separate characters ) into a single code point ,
* matching UTF - 16.
* @ see ` punycode.ucs2.encode `
2016-01-23 21:41:53 +03:00
* @ see < http : //mathiasbynens.be/notes/javascript-encoding>
2015-12-06 21:46:23 +03:00
* @ memberOf punycode . ucs2
* @ name decode
* @ param { String } string The Unicode input string ( UCS - 2 ) .
* @ returns { Array } The new array of code points .
* /
function ucs2decode ( string ) {
var output = [ ] ,
counter = 0 ,
length = string . length ,
value ,
extra ;
while ( counter < length ) {
value = string . charCodeAt ( counter ++ ) ;
if ( value >= 0xD800 && value <= 0xDBFF && counter < length ) {
// high surrogate, and there is a next character
extra = string . charCodeAt ( counter ++ ) ;
if ( ( extra & 0xFC00 ) == 0xDC00 ) { // low surrogate
output . push ( ( ( value & 0x3FF ) << 10 ) + ( extra & 0x3FF ) + 0x10000 ) ;
} else {
// unmatched surrogate; only append this code unit, in case the next
// code unit is the high surrogate of a surrogate pair
output . push ( value ) ;
counter -- ;
}
} else {
output . push ( value ) ;
}
}
return output ;
}
/ * *
* Creates a string based on an array of numeric code points .
* @ see ` punycode.ucs2.decode `
* @ memberOf punycode . ucs2
* @ name encode
* @ param { Array } codePoints The array of numeric code points .
* @ returns { String } The new Unicode string ( UCS - 2 ) .
* /
function ucs2encode ( array ) {
return map ( array , function ( value ) {
var output = '' ;
if ( value > 0xFFFF ) {
value -= 0x10000 ;
output += stringFromCharCode ( value >>> 10 & 0x3FF | 0xD800 ) ;
value = 0xDC00 | value & 0x3FF ;
}
output += stringFromCharCode ( value ) ;
return output ;
} ) . join ( '' ) ;
}
/ * *
* Converts a basic code point into a digit / integer .
* @ see ` digitToBasic() `
* @ private
* @ param { Number } codePoint The basic numeric code point value .
* @ returns { Number } The numeric value of a basic code point ( for use in
* representing integers ) in the range ` 0 ` to ` base - 1 ` , or ` base ` if
* the code point does not represent a value .
* /
function basicToDigit ( codePoint ) {
if ( codePoint - 48 < 10 ) {
return codePoint - 22 ;
}
if ( codePoint - 65 < 26 ) {
return codePoint - 65 ;
}
if ( codePoint - 97 < 26 ) {
return codePoint - 97 ;
}
return base ;
}
/ * *
* Converts a digit / integer into a basic code point .
* @ see ` basicToDigit() `
* @ private
* @ param { Number } digit The numeric value of a basic code point .
* @ returns { Number } The basic code point whose value ( when used for
* representing integers ) is ` digit ` , which needs to be in the range
* ` 0 ` to ` base - 1 ` . If ` flag ` is non - zero , the uppercase form is
* used ; else , the lowercase form is used . The behavior is undefined
* if ` flag ` is non - zero and ` digit ` has no uppercase form .
* /
function digitToBasic ( digit , flag ) {
// 0..25 map to ASCII a..z or A..Z
// 26..35 map to ASCII 0..9
return digit + 22 + 75 * ( digit < 26 ) - ( ( flag != 0 ) << 5 ) ;
}
/ * *
* Bias adaptation function as per section 3.4 of RFC 3492.
* http : //tools.ietf.org/html/rfc3492#section-3.4
* @ private
* /
function adapt ( delta , numPoints , firstTime ) {
var k = 0 ;
delta = firstTime ? floor ( delta / damp ) : delta >> 1 ;
delta += floor ( delta / numPoints ) ;
for ( /* no initialization */ ; delta > baseMinusTMin * tMax >> 1 ; k += base ) {
delta = floor ( delta / baseMinusTMin ) ;
}
return floor ( k + ( baseMinusTMin + 1 ) * delta / ( delta + skew ) ) ;
}
/ * *
* Converts a Punycode string of ASCII - only symbols to a string of Unicode
* symbols .
* @ memberOf punycode
* @ param { String } input The Punycode string of ASCII - only symbols .
* @ returns { String } The resulting string of Unicode symbols .
* /
function decode ( input ) {
// Don't use UCS-2
var output = [ ] ,
inputLength = input . length ,
out ,
i = 0 ,
n = initialN ,
bias = initialBias ,
basic ,
j ,
index ,
oldi ,
w ,
k ,
digit ,
t ,
/** Cached calculation results */
baseMinusT ;
// Handle the basic code points: let `basic` be the number of input code
// points before the last delimiter, or `0` if there is none, then copy
// the first basic code points to the output.
basic = input . lastIndexOf ( delimiter ) ;
if ( basic < 0 ) {
basic = 0 ;
}
for ( j = 0 ; j < basic ; ++ j ) {
// if it's not a basic code point
if ( input . charCodeAt ( j ) >= 0x80 ) {
error ( 'not-basic' ) ;
}
output . push ( input . charCodeAt ( j ) ) ;
}
// Main decoding loop: start just after the last delimiter if any basic code
// points were copied; start at the beginning otherwise.
for ( index = basic > 0 ? basic + 1 : 0 ; index < inputLength ; /* no final expression */ ) {
// `index` is the index of the next character to be consumed.
// Decode a generalized variable-length integer into `delta`,
// which gets added to `i`. The overflow checking is easier
// if we increase `i` as we go, then subtract off its starting
// value at the end to obtain `delta`.
for ( oldi = i , w = 1 , k = base ; /* no condition */ ; k += base ) {
if ( index >= inputLength ) {
error ( 'invalid-input' ) ;
}
digit = basicToDigit ( input . charCodeAt ( index ++ ) ) ;
if ( digit >= base || digit > floor ( ( maxInt - i ) / w ) ) {
error ( 'overflow' ) ;
}
i += digit * w ;
t = k <= bias ? tMin : ( k >= bias + tMax ? tMax : k - bias ) ;
if ( digit < t ) {
break ;
}
baseMinusT = base - t ;
if ( w > floor ( maxInt / baseMinusT ) ) {
error ( 'overflow' ) ;
}
w *= baseMinusT ;
}
out = output . length + 1 ;
bias = adapt ( i - oldi , out , oldi == 0 ) ;
// `i` was supposed to wrap around from `out` to `0`,
// incrementing `n` each time, so we'll fix that now:
if ( floor ( i / out ) > maxInt - n ) {
error ( 'overflow' ) ;
}
n += floor ( i / out ) ;
i %= out ;
// Insert `n` at position `i` of the output
output . splice ( i ++ , 0 , n ) ;
}
return ucs2encode ( output ) ;
}
/ * *
2016-01-23 21:41:53 +03:00
* Converts a string of Unicode symbols to a Punycode string of ASCII - only
* symbols .
2015-12-06 21:46:23 +03:00
* @ memberOf punycode
* @ param { String } input The string of Unicode symbols .
* @ returns { String } The resulting Punycode string of ASCII - only symbols .
* /
function encode ( input ) {
var n ,
delta ,
handledCPCount ,
basicLength ,
bias ,
j ,
m ,
q ,
k ,
t ,
currentValue ,
output = [ ] ,
/** `inputLength` will hold the number of code points in `input`. */
inputLength ,
/** Cached calculation results */
handledCPCountPlusOne ,
baseMinusT ,
qMinusT ;
// Convert the input in UCS-2 to Unicode
input = ucs2decode ( input ) ;
// Cache the length
inputLength = input . length ;
// Initialize the state
n = initialN ;
delta = 0 ;
bias = initialBias ;
// Handle the basic code points
for ( j = 0 ; j < inputLength ; ++ j ) {
currentValue = input [ j ] ;
if ( currentValue < 0x80 ) {
output . push ( stringFromCharCode ( currentValue ) ) ;
}
}
handledCPCount = basicLength = output . length ;
// `handledCPCount` is the number of code points that have been handled;
// `basicLength` is the number of basic code points.
// Finish the basic string - if it is not empty - with a delimiter
if ( basicLength ) {
output . push ( delimiter ) ;
}
// Main encoding loop:
while ( handledCPCount < inputLength ) {
// All non-basic code points < n have been handled already. Find the next
// larger one:
for ( m = maxInt , j = 0 ; j < inputLength ; ++ j ) {
currentValue = input [ j ] ;
if ( currentValue >= n && currentValue < m ) {
m = currentValue ;
}
}
// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
// but guard against overflow
handledCPCountPlusOne = handledCPCount + 1 ;
if ( m - n > floor ( ( maxInt - delta ) / handledCPCountPlusOne ) ) {
error ( 'overflow' ) ;
}
delta += ( m - n ) * handledCPCountPlusOne ;
n = m ;
for ( j = 0 ; j < inputLength ; ++ j ) {
currentValue = input [ j ] ;
if ( currentValue < n && ++ delta > maxInt ) {
error ( 'overflow' ) ;
}
if ( currentValue == n ) {
// Represent delta as a generalized variable-length integer
for ( q = delta , k = base ; /* no condition */ ; k += base ) {
t = k <= bias ? tMin : ( k >= bias + tMax ? tMax : k - bias ) ;
if ( q < t ) {
break ;
}
qMinusT = q - t ;
baseMinusT = base - t ;
output . push (
stringFromCharCode ( digitToBasic ( t + qMinusT % baseMinusT , 0 ) )
) ;
q = floor ( qMinusT / baseMinusT ) ;
}
output . push ( stringFromCharCode ( digitToBasic ( q , 0 ) ) ) ;
bias = adapt ( delta , handledCPCountPlusOne , handledCPCount == basicLength ) ;
delta = 0 ;
++ handledCPCount ;
}
}
++ delta ;
++ n ;
}
return output . join ( '' ) ;
}
/ * *
2016-01-23 21:41:53 +03:00
* Converts a Punycode string representing a domain name to Unicode . Only the
* Punycoded parts of the domain name will be converted , i . e . it doesn ' t
* matter if you call it on a string that has already been converted to
* Unicode .
2015-12-06 21:46:23 +03:00
* @ memberOf punycode
2016-01-23 21:41:53 +03:00
* @ param { String } domain The Punycode domain name to convert to Unicode .
2015-12-06 21:46:23 +03:00
* @ returns { String } The Unicode representation of the given Punycode
* string .
* /
2016-01-23 21:41:53 +03:00
function toUnicode ( domain ) {
return mapDomain ( domain , function ( string ) {
2015-12-06 21:46:23 +03:00
return regexPunycode . test ( string )
? decode ( string . slice ( 4 ) . toLowerCase ( ) )
: string ;
} ) ;
}
/ * *
2016-01-23 21:41:53 +03:00
* Converts a Unicode string representing a domain name to Punycode . Only the
* non - ASCII parts of the domain name will be converted , i . e . it doesn ' t
* matter if you call it with a domain that ' s already in ASCII .
2015-12-06 21:46:23 +03:00
* @ memberOf punycode
2016-01-23 21:41:53 +03:00
* @ param { String } domain The domain name to convert , as a Unicode string .
* @ returns { String } The Punycode representation of the given domain name .
2015-12-06 21:46:23 +03:00
* /
2016-01-23 21:41:53 +03:00
function toASCII ( domain ) {
return mapDomain ( domain , function ( string ) {
2015-12-06 21:46:23 +03:00
return regexNonASCII . test ( string )
? 'xn--' + encode ( string )
: string ;
} ) ;
}
/*--------------------------------------------------------------------------*/
/** Define the public API */
punycode = {
/ * *
* A string representing the current Punycode . js version number .
* @ memberOf punycode
* @ type String
* /
2016-01-23 21:41:53 +03:00
'version' : '1.2.4' ,
2015-12-06 21:46:23 +03:00
/ * *
* An object of methods to convert from JavaScript ' s internal character
* representation ( UCS - 2 ) to Unicode code points , and back .
2016-01-23 21:41:53 +03:00
* @ see < http : //mathiasbynens.be/notes/javascript-encoding>
2015-12-06 21:46:23 +03:00
* @ memberOf punycode
* @ type Object
* /
'ucs2' : {
'decode' : ucs2decode ,
'encode' : ucs2encode
} ,
'decode' : decode ,
'encode' : encode ,
'toASCII' : toASCII ,
'toUnicode' : toUnicode
} ;
/** Expose `punycode` */
// Some AMD build optimizers, like r.js, check for specific condition patterns
// like the following:
if (
typeof define == 'function' &&
typeof define . amd == 'object' &&
define . amd
) {
define ( 'punycode' , function ( ) {
return punycode ;
} ) ;
2016-01-23 21:41:53 +03:00
} else if ( freeExports && ! freeExports . nodeType ) {
if ( freeModule ) { // in Node.js or RingoJS v0.8.0+
2015-12-06 21:46:23 +03:00
freeModule . exports = punycode ;
} else { // in Narwhal or RingoJS v0.7.0-
for ( key in punycode ) {
punycode . hasOwnProperty ( key ) && ( freeExports [ key ] = punycode [ key ] ) ;
}
}
} else { // in Rhino or a web browser
root . punycode = punycode ;
}
} ( this ) ) ;
} ) . call ( this , typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : { } )
} , { } ] , 2 : [ function ( require , module , exports ) {
var log = require ( './log' ) ;
function restoreOwnerScroll ( ownerDocument , x , y ) {
if ( ownerDocument . defaultView && ( x !== ownerDocument . defaultView . pageXOffset || y !== ownerDocument . defaultView . pageYOffset ) ) {
ownerDocument . defaultView . scrollTo ( x , y ) ;
}
}
function cloneCanvasContents ( canvas , clonedCanvas ) {
try {
if ( clonedCanvas ) {
clonedCanvas . width = canvas . width ;
clonedCanvas . height = canvas . height ;
clonedCanvas . getContext ( "2d" ) . putImageData ( canvas . getContext ( "2d" ) . getImageData ( 0 , 0 , canvas . width , canvas . height ) , 0 , 0 ) ;
}
} catch ( e ) {
log ( "Unable to copy canvas content from" , canvas , e ) ;
}
}
function cloneNode ( node , javascriptEnabled ) {
var clone = node . nodeType === 3 ? document . createTextNode ( node . nodeValue ) : node . cloneNode ( false ) ;
var child = node . firstChild ;
while ( child ) {
if ( javascriptEnabled === true || child . nodeType !== 1 || child . nodeName !== 'SCRIPT' ) {
clone . appendChild ( cloneNode ( child , javascriptEnabled ) ) ;
}
child = child . nextSibling ;
}
if ( node . nodeType === 1 ) {
clone . _scrollTop = node . scrollTop ;
clone . _scrollLeft = node . scrollLeft ;
if ( node . nodeName === "CANVAS" ) {
cloneCanvasContents ( node , clone ) ;
} else if ( node . nodeName === "TEXTAREA" || node . nodeName === "SELECT" ) {
clone . value = node . value ;
}
}
return clone ;
}
function initNode ( node ) {
if ( node . nodeType === 1 ) {
node . scrollTop = node . _scrollTop ;
node . scrollLeft = node . _scrollLeft ;
var child = node . firstChild ;
while ( child ) {
initNode ( child ) ;
child = child . nextSibling ;
}
}
}
module . exports = function ( ownerDocument , containerDocument , width , height , options , x , y ) {
var documentElement = cloneNode ( ownerDocument . documentElement , options . javascriptEnabled ) ;
var container = containerDocument . createElement ( "iframe" ) ;
container . className = "html2canvas-container" ;
container . style . visibility = "hidden" ;
container . style . position = "fixed" ;
container . style . left = "-10000px" ;
container . style . top = "0px" ;
container . style . border = "0" ;
container . width = width ;
container . height = height ;
container . scrolling = "no" ; // ios won't scroll without it
containerDocument . body . appendChild ( container ) ;
return new Promise ( function ( resolve ) {
var documentClone = container . contentWindow . document ;
/ * C h r o m e d o e s n ' t d e t e c t r e l a t i v e b a c k g r o u n d - i m a g e s a s s i g n e d i n i n l i n e < s t y l e > s h e e t s w h e n f e t c h e d t h r o u g h g e t C o m p u t e d S t y l e
if window url is about : blank , we can assign the url to current by writing onto the document
* /
container . contentWindow . onload = container . onload = function ( ) {
var interval = setInterval ( function ( ) {
if ( documentClone . body . childNodes . length > 0 ) {
initNode ( documentClone . documentElement ) ;
clearInterval ( interval ) ;
if ( options . type === "view" ) {
container . contentWindow . scrollTo ( x , y ) ;
if ( ( /(iPad|iPhone|iPod)/g ) . test ( navigator . userAgent ) && ( container . contentWindow . scrollY !== y || container . contentWindow . scrollX !== x ) ) {
documentClone . documentElement . style . top = ( - y ) + "px" ;
documentClone . documentElement . style . left = ( - x ) + "px" ;
documentClone . documentElement . style . position = 'absolute' ;
}
}
resolve ( container ) ;
}
} , 50 ) ;
} ;
documentClone . open ( ) ;
documentClone . write ( "<!DOCTYPE html><html></html>" ) ;
// Chrome scrolls the parent document for some reason after the write to the cloned window???
restoreOwnerScroll ( ownerDocument , x , y ) ;
documentClone . replaceChild ( documentClone . adoptNode ( documentElement ) , documentClone . documentElement ) ;
documentClone . close ( ) ;
} ) ;
} ;
} , { "./log" : 13 } ] , 3 : [ function ( require , module , exports ) {
// http://dev.w3.org/csswg/css-color/
function Color ( value ) {
this . r = 0 ;
this . g = 0 ;
this . b = 0 ;
this . a = null ;
var result = this . fromArray ( value ) ||
this . namedColor ( value ) ||
this . rgb ( value ) ||
this . rgba ( value ) ||
this . hex6 ( value ) ||
this . hex3 ( value ) ;
}
Color . prototype . darken = function ( amount ) {
var a = 1 - amount ;
return new Color ( [
Math . round ( this . r * a ) ,
Math . round ( this . g * a ) ,
Math . round ( this . b * a ) ,
this . a
] ) ;
} ;
Color . prototype . isTransparent = function ( ) {
return this . a === 0 ;
} ;
Color . prototype . isBlack = function ( ) {
return this . r === 0 && this . g === 0 && this . b === 0 ;
} ;
Color . prototype . fromArray = function ( array ) {
if ( Array . isArray ( array ) ) {
this . r = Math . min ( array [ 0 ] , 255 ) ;
this . g = Math . min ( array [ 1 ] , 255 ) ;
this . b = Math . min ( array [ 2 ] , 255 ) ;
if ( array . length > 3 ) {
this . a = array [ 3 ] ;
}
}
return ( Array . isArray ( array ) ) ;
} ;
var _hex3 = /^#([a-f0-9]{3})$/i ;
Color . prototype . hex3 = function ( value ) {
var match = null ;
if ( ( match = value . match ( _hex3 ) ) !== null ) {
this . r = parseInt ( match [ 1 ] [ 0 ] + match [ 1 ] [ 0 ] , 16 ) ;
this . g = parseInt ( match [ 1 ] [ 1 ] + match [ 1 ] [ 1 ] , 16 ) ;
this . b = parseInt ( match [ 1 ] [ 2 ] + match [ 1 ] [ 2 ] , 16 ) ;
}
return match !== null ;
} ;
var _hex6 = /^#([a-f0-9]{6})$/i ;
Color . prototype . hex6 = function ( value ) {
var match = null ;
if ( ( match = value . match ( _hex6 ) ) !== null ) {
this . r = parseInt ( match [ 1 ] . substring ( 0 , 2 ) , 16 ) ;
this . g = parseInt ( match [ 1 ] . substring ( 2 , 4 ) , 16 ) ;
this . b = parseInt ( match [ 1 ] . substring ( 4 , 6 ) , 16 ) ;
}
return match !== null ;
} ;
var _rgb = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/ ;
Color . prototype . rgb = function ( value ) {
var match = null ;
if ( ( match = value . match ( _rgb ) ) !== null ) {
this . r = Number ( match [ 1 ] ) ;
this . g = Number ( match [ 2 ] ) ;
this . b = Number ( match [ 3 ] ) ;
}
return match !== null ;
} ;
var _rgba = /^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d?\.?\d+)\s*\)$/ ;
Color . prototype . rgba = function ( value ) {
var match = null ;
if ( ( match = value . match ( _rgba ) ) !== null ) {
this . r = Number ( match [ 1 ] ) ;
this . g = Number ( match [ 2 ] ) ;
this . b = Number ( match [ 3 ] ) ;
this . a = Number ( match [ 4 ] ) ;
}
return match !== null ;
} ;
Color . prototype . toString = function ( ) {
return this . a !== null && this . a !== 1 ?
"rgba(" + [ this . r , this . g , this . b , this . a ] . join ( "," ) + ")" :
"rgb(" + [ this . r , this . g , this . b ] . join ( "," ) + ")" ;
} ;
Color . prototype . namedColor = function ( value ) {
value = value . toLowerCase ( ) ;
var color = colors [ value ] ;
if ( color ) {
this . r = color [ 0 ] ;
this . g = color [ 1 ] ;
this . b = color [ 2 ] ;
} else if ( value === "transparent" ) {
this . r = this . g = this . b = this . a = 0 ;
return true ;
}
return ! ! color ;
} ;
Color . prototype . isColor = true ;
// JSON.stringify([].slice.call($$('.named-color-table tr'), 1).map(function(row) { return [row.childNodes[3].textContent, row.childNodes[5].textContent.trim().split(",").map(Number)] }).reduce(function(data, row) {data[row[0]] = row[1]; return data}, {}))
var colors = {
"aliceblue" : [ 240 , 248 , 255 ] ,
"antiquewhite" : [ 250 , 235 , 215 ] ,
"aqua" : [ 0 , 255 , 255 ] ,
"aquamarine" : [ 127 , 255 , 212 ] ,
"azure" : [ 240 , 255 , 255 ] ,
"beige" : [ 245 , 245 , 220 ] ,
"bisque" : [ 255 , 228 , 196 ] ,
"black" : [ 0 , 0 , 0 ] ,
"blanchedalmond" : [ 255 , 235 , 205 ] ,
"blue" : [ 0 , 0 , 255 ] ,
"blueviolet" : [ 138 , 43 , 226 ] ,
"brown" : [ 165 , 42 , 42 ] ,
"burlywood" : [ 222 , 184 , 135 ] ,
"cadetblue" : [ 95 , 158 , 160 ] ,
"chartreuse" : [ 127 , 255 , 0 ] ,
"chocolate" : [ 210 , 105 , 30 ] ,
"coral" : [ 255 , 127 , 80 ] ,
"cornflowerblue" : [ 100 , 149 , 237 ] ,
"cornsilk" : [ 255 , 248 , 220 ] ,
"crimson" : [ 220 , 20 , 60 ] ,
"cyan" : [ 0 , 255 , 255 ] ,
"darkblue" : [ 0 , 0 , 139 ] ,
"darkcyan" : [ 0 , 139 , 139 ] ,
"darkgoldenrod" : [ 184 , 134 , 11 ] ,
"darkgray" : [ 169 , 169 , 169 ] ,
"darkgreen" : [ 0 , 100 , 0 ] ,
"darkgrey" : [ 169 , 169 , 169 ] ,
"darkkhaki" : [ 189 , 183 , 107 ] ,
"darkmagenta" : [ 139 , 0 , 139 ] ,
"darkolivegreen" : [ 85 , 107 , 47 ] ,
"darkorange" : [ 255 , 140 , 0 ] ,
"darkorchid" : [ 153 , 50 , 204 ] ,
"darkred" : [ 139 , 0 , 0 ] ,
"darksalmon" : [ 233 , 150 , 122 ] ,
"darkseagreen" : [ 143 , 188 , 143 ] ,
"darkslateblue" : [ 72 , 61 , 139 ] ,
"darkslategray" : [ 47 , 79 , 79 ] ,
"darkslategrey" : [ 47 , 79 , 79 ] ,
"darkturquoise" : [ 0 , 206 , 209 ] ,
"darkviolet" : [ 148 , 0 , 211 ] ,
"deeppink" : [ 255 , 20 , 147 ] ,
"deepskyblue" : [ 0 , 191 , 255 ] ,
"dimgray" : [ 105 , 105 , 105 ] ,
"dimgrey" : [ 105 , 105 , 105 ] ,
"dodgerblue" : [ 30 , 144 , 255 ] ,
"firebrick" : [ 178 , 34 , 34 ] ,
"floralwhite" : [ 255 , 250 , 240 ] ,
"forestgreen" : [ 34 , 139 , 34 ] ,
"fuchsia" : [ 255 , 0 , 255 ] ,
"gainsboro" : [ 220 , 220 , 220 ] ,
"ghostwhite" : [ 248 , 248 , 255 ] ,
"gold" : [ 255 , 215 , 0 ] ,
"goldenrod" : [ 218 , 165 , 32 ] ,
"gray" : [ 128 , 128 , 128 ] ,
"green" : [ 0 , 128 , 0 ] ,
"greenyellow" : [ 173 , 255 , 47 ] ,
"grey" : [ 128 , 128 , 128 ] ,
"honeydew" : [ 240 , 255 , 240 ] ,
"hotpink" : [ 255 , 105 , 180 ] ,
"indianred" : [ 205 , 92 , 92 ] ,
"indigo" : [ 75 , 0 , 130 ] ,
"ivory" : [ 255 , 255 , 240 ] ,
"khaki" : [ 240 , 230 , 140 ] ,
"lavender" : [ 230 , 230 , 250 ] ,
"lavenderblush" : [ 255 , 240 , 245 ] ,
"lawngreen" : [ 124 , 252 , 0 ] ,
"lemonchiffon" : [ 255 , 250 , 205 ] ,
"lightblue" : [ 173 , 216 , 230 ] ,
"lightcoral" : [ 240 , 128 , 128 ] ,
"lightcyan" : [ 224 , 255 , 255 ] ,
"lightgoldenrodyellow" : [ 250 , 250 , 210 ] ,
"lightgray" : [ 211 , 211 , 211 ] ,
"lightgreen" : [ 144 , 238 , 144 ] ,
"lightgrey" : [ 211 , 211 , 211 ] ,
"lightpink" : [ 255 , 182 , 193 ] ,
"lightsalmon" : [ 255 , 160 , 122 ] ,
"lightseagreen" : [ 32 , 178 , 170 ] ,
"lightskyblue" : [ 135 , 206 , 250 ] ,
"lightslategray" : [ 119 , 136 , 153 ] ,
"lightslategrey" : [ 119 , 136 , 153 ] ,
"lightsteelblue" : [ 176 , 196 , 222 ] ,
"lightyellow" : [ 255 , 255 , 224 ] ,
"lime" : [ 0 , 255 , 0 ] ,
"limegreen" : [ 50 , 205 , 50 ] ,
"linen" : [ 250 , 240 , 230 ] ,
"magenta" : [ 255 , 0 , 255 ] ,
"maroon" : [ 128 , 0 , 0 ] ,
"mediumaquamarine" : [ 102 , 205 , 170 ] ,
"mediumblue" : [ 0 , 0 , 205 ] ,
"mediumorchid" : [ 186 , 85 , 211 ] ,
"mediumpurple" : [ 147 , 112 , 219 ] ,
"mediumseagreen" : [ 60 , 179 , 113 ] ,
"mediumslateblue" : [ 123 , 104 , 238 ] ,
"mediumspringgreen" : [ 0 , 250 , 154 ] ,
"mediumturquoise" : [ 72 , 209 , 204 ] ,
"mediumvioletred" : [ 199 , 21 , 133 ] ,
"midnightblue" : [ 25 , 25 , 112 ] ,
"mintcream" : [ 245 , 255 , 250 ] ,
"mistyrose" : [ 255 , 228 , 225 ] ,
"moccasin" : [ 255 , 228 , 181 ] ,
"navajowhite" : [ 255 , 222 , 173 ] ,
"navy" : [ 0 , 0 , 128 ] ,
"oldlace" : [ 253 , 245 , 230 ] ,
"olive" : [ 128 , 128 , 0 ] ,
"olivedrab" : [ 107 , 142 , 35 ] ,
"orange" : [ 255 , 165 , 0 ] ,
"orangered" : [ 255 , 69 , 0 ] ,
"orchid" : [ 218 , 112 , 214 ] ,
"palegoldenrod" : [ 238 , 232 , 170 ] ,
"palegreen" : [ 152 , 251 , 152 ] ,
"paleturquoise" : [ 175 , 238 , 238 ] ,
"palevioletred" : [ 219 , 112 , 147 ] ,
"papayawhip" : [ 255 , 239 , 213 ] ,
"peachpuff" : [ 255 , 218 , 185 ] ,
"peru" : [ 205 , 133 , 63 ] ,
"pink" : [ 255 , 192 , 203 ] ,
"plum" : [ 221 , 160 , 221 ] ,
"powderblue" : [ 176 , 224 , 230 ] ,
"purple" : [ 128 , 0 , 128 ] ,
"rebeccapurple" : [ 102 , 51 , 153 ] ,
"red" : [ 255 , 0 , 0 ] ,
"rosybrown" : [ 188 , 143 , 143 ] ,
"royalblue" : [ 65 , 105 , 225 ] ,
"saddlebrown" : [ 139 , 69 , 19 ] ,
"salmon" : [ 250 , 128 , 114 ] ,
"sandybrown" : [ 244 , 164 , 96 ] ,
"seagreen" : [ 46 , 139 , 87 ] ,
"seashell" : [ 255 , 245 , 238 ] ,
"sienna" : [ 160 , 82 , 45 ] ,
"silver" : [ 192 , 192 , 192 ] ,
"skyblue" : [ 135 , 206 , 235 ] ,
"slateblue" : [ 106 , 90 , 205 ] ,
"slategray" : [ 112 , 128 , 144 ] ,
"slategrey" : [ 112 , 128 , 144 ] ,
"snow" : [ 255 , 250 , 250 ] ,
"springgreen" : [ 0 , 255 , 127 ] ,
"steelblue" : [ 70 , 130 , 180 ] ,
"tan" : [ 210 , 180 , 140 ] ,
"teal" : [ 0 , 128 , 128 ] ,
"thistle" : [ 216 , 191 , 216 ] ,
"tomato" : [ 255 , 99 , 71 ] ,
"turquoise" : [ 64 , 224 , 208 ] ,
"violet" : [ 238 , 130 , 238 ] ,
"wheat" : [ 245 , 222 , 179 ] ,
"white" : [ 255 , 255 , 255 ] ,
"whitesmoke" : [ 245 , 245 , 245 ] ,
"yellow" : [ 255 , 255 , 0 ] ,
"yellowgreen" : [ 154 , 205 , 50 ]
} ;
module . exports = Color ;
} , { } ] , 4 : [ function ( require , module , exports ) {
var Support = require ( './support' ) ;
var CanvasRenderer = require ( './renderers/canvas' ) ;
var ImageLoader = require ( './imageloader' ) ;
var NodeParser = require ( './nodeparser' ) ;
var NodeContainer = require ( './nodecontainer' ) ;
var log = require ( './log' ) ;
var utils = require ( './utils' ) ;
var createWindowClone = require ( './clone' ) ;
var loadUrlDocument = require ( './proxy' ) . loadUrlDocument ;
var getBounds = utils . getBounds ;
var html2canvasNodeAttribute = "data-html2canvas-node" ;
var html2canvasCloneIndex = 0 ;
function html2canvas ( nodeList , options ) {
var index = html2canvasCloneIndex ++ ;
options = options || { } ;
if ( options . logging ) {
2016-01-23 21:41:53 +03:00
log . options . logging = true ;
log . options . start = Date . now ( ) ;
2015-12-06 21:46:23 +03:00
}
options . async = typeof ( options . async ) === "undefined" ? true : options . async ;
options . allowTaint = typeof ( options . allowTaint ) === "undefined" ? false : options . allowTaint ;
options . removeContainer = typeof ( options . removeContainer ) === "undefined" ? true : options . removeContainer ;
options . javascriptEnabled = typeof ( options . javascriptEnabled ) === "undefined" ? false : options . javascriptEnabled ;
options . imageTimeout = typeof ( options . imageTimeout ) === "undefined" ? 10000 : options . imageTimeout ;
options . renderer = typeof ( options . renderer ) === "function" ? options . renderer : CanvasRenderer ;
options . strict = ! ! options . strict ;
if ( typeof ( nodeList ) === "string" ) {
if ( typeof ( options . proxy ) !== "string" ) {
return Promise . reject ( "Proxy must be used when rendering url" ) ;
}
var width = options . width != null ? options . width : window . innerWidth ;
var height = options . height != null ? options . height : window . innerHeight ;
return loadUrlDocument ( absoluteUrl ( nodeList ) , options . proxy , document , width , height , options ) . then ( function ( container ) {
return renderWindow ( container . contentWindow . document . documentElement , container , options , width , height ) ;
} ) ;
}
var node = ( ( nodeList === undefined ) ? [ document . documentElement ] : ( ( nodeList . length ) ? nodeList : [ nodeList ] ) ) [ 0 ] ;
node . setAttribute ( html2canvasNodeAttribute + index , index ) ;
return renderDocument ( node . ownerDocument , options , node . ownerDocument . defaultView . innerWidth , node . ownerDocument . defaultView . innerHeight , index ) . then ( function ( canvas ) {
if ( typeof ( options . onrendered ) === "function" ) {
log ( "options.onrendered is deprecated, html2canvas returns a Promise containing the canvas" ) ;
options . onrendered ( canvas ) ;
}
return canvas ;
} ) ;
}
html2canvas . CanvasRenderer = CanvasRenderer ;
html2canvas . NodeContainer = NodeContainer ;
html2canvas . log = log ;
html2canvas . utils = utils ;
var html2canvasExport = ( typeof ( document ) === "undefined" || typeof ( Object . create ) !== "function" || typeof ( document . createElement ( "canvas" ) . getContext ) !== "function" ) ? function ( ) {
return Promise . reject ( "No canvas support" ) ;
} : html2canvas ;
module . exports = html2canvasExport ;
if ( typeof ( define ) === 'function' && define . amd ) {
define ( 'html2canvas' , [ ] , function ( ) {
return html2canvasExport ;
} ) ;
}
function renderDocument ( document , options , windowWidth , windowHeight , html2canvasIndex ) {
return createWindowClone ( document , document , windowWidth , windowHeight , options , document . defaultView . pageXOffset , document . defaultView . pageYOffset ) . then ( function ( container ) {
log ( "Document cloned" ) ;
var attributeName = html2canvasNodeAttribute + html2canvasIndex ;
var selector = "[" + attributeName + "='" + html2canvasIndex + "']" ;
document . querySelector ( selector ) . removeAttribute ( attributeName ) ;
var clonedWindow = container . contentWindow ;
var node = clonedWindow . document . querySelector ( selector ) ;
var oncloneHandler = ( typeof ( options . onclone ) === "function" ) ? Promise . resolve ( options . onclone ( clonedWindow . document ) ) : Promise . resolve ( true ) ;
return oncloneHandler . then ( function ( ) {
return renderWindow ( node , container , options , windowWidth , windowHeight ) ;
} ) ;
} ) ;
}
function renderWindow ( node , container , options , windowWidth , windowHeight ) {
var clonedWindow = container . contentWindow ;
var support = new Support ( clonedWindow . document ) ;
var imageLoader = new ImageLoader ( options , support ) ;
var bounds = getBounds ( node ) ;
var width = options . type === "view" ? windowWidth : documentWidth ( clonedWindow . document ) ;
var height = options . type === "view" ? windowHeight : documentHeight ( clonedWindow . document ) ;
var renderer = new options . renderer ( width , height , imageLoader , options , document ) ;
var parser = new NodeParser ( node , renderer , support , imageLoader , options ) ;
return parser . ready . then ( function ( ) {
log ( "Finished rendering" ) ;
var canvas ;
if ( options . type === "view" ) {
canvas = crop ( renderer . canvas , { width : renderer . canvas . width , height : renderer . canvas . height , top : 0 , left : 0 , x : 0 , y : 0 } ) ;
} else if ( node === clonedWindow . document . body || node === clonedWindow . document . documentElement || options . canvas != null ) {
canvas = renderer . canvas ;
} else {
canvas = crop ( renderer . canvas , { width : options . width != null ? options . width : bounds . width , height : options . height != null ? options . height : bounds . height , top : bounds . top , left : bounds . left , x : clonedWindow . pageXOffset , y : clonedWindow . pageYOffset } ) ;
}
cleanupContainer ( container , options ) ;
return canvas ;
} ) ;
}
function cleanupContainer ( container , options ) {
if ( options . removeContainer ) {
container . parentNode . removeChild ( container ) ;
log ( "Cleaned up container" ) ;
}
}
function crop ( canvas , bounds ) {
var croppedCanvas = document . createElement ( "canvas" ) ;
var x1 = Math . min ( canvas . width - 1 , Math . max ( 0 , bounds . left ) ) ;
var x2 = Math . min ( canvas . width , Math . max ( 1 , bounds . left + bounds . width ) ) ;
var y1 = Math . min ( canvas . height - 1 , Math . max ( 0 , bounds . top ) ) ;
var y2 = Math . min ( canvas . height , Math . max ( 1 , bounds . top + bounds . height ) ) ;
croppedCanvas . width = bounds . width ;
croppedCanvas . height = bounds . height ;
log ( "Cropping canvas at:" , "left:" , bounds . left , "top:" , bounds . top , "width:" , ( x2 - x1 ) , "height:" , ( y2 - y1 ) ) ;
log ( "Resulting crop with width" , bounds . width , "and height" , bounds . height , " with x" , x1 , "and y" , y1 ) ;
croppedCanvas . getContext ( "2d" ) . drawImage ( canvas , x1 , y1 , x2 - x1 , y2 - y1 , bounds . x , bounds . y , x2 - x1 , y2 - y1 ) ;
return croppedCanvas ;
}
function documentWidth ( doc ) {
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 ( doc ) {
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 absoluteUrl ( url ) {
var link = document . createElement ( "a" ) ;
link . href = url ;
link . href = link . href ;
return link ;
}
} , { "./clone" : 2 , "./imageloader" : 11 , "./log" : 13 , "./nodecontainer" : 14 , "./nodeparser" : 15 , "./proxy" : 16 , "./renderers/canvas" : 20 , "./support" : 22 , "./utils" : 26 } ] , 5 : [ function ( require , module , exports ) {
var log = require ( './log' ) ;
var smallImage = require ( './utils' ) . smallImage ;
function DummyImageContainer ( src ) {
this . src = src ;
log ( "DummyImageContainer for" , src ) ;
if ( ! this . promise || ! this . image ) {
log ( "Initiating DummyImageContainer" ) ;
DummyImageContainer . prototype . image = new Image ( ) ;
var image = this . image ;
DummyImageContainer . prototype . promise = new Promise ( function ( resolve , reject ) {
image . onload = resolve ;
image . onerror = reject ;
image . src = smallImage ( ) ;
if ( image . complete === true ) {
resolve ( image ) ;
}
} ) ;
}
}
module . exports = DummyImageContainer ;
} , { "./log" : 13 , "./utils" : 26 } ] , 6 : [ function ( require , module , exports ) {
var smallImage = require ( './utils' ) . smallImage ;
function Font ( family , size ) {
var container = document . createElement ( 'div' ) ,
img = document . createElement ( 'img' ) ,
span = document . createElement ( 'span' ) ,
sampleText = 'Hidden Text' ,
baseline ,
middle ;
container . style . visibility = "hidden" ;
container . style . fontFamily = family ;
container . style . fontSize = size ;
container . style . margin = 0 ;
container . style . padding = 0 ;
document . body . appendChild ( container ) ;
img . src = smallImage ( ) ;
img . width = 1 ;
img . height = 1 ;
img . style . margin = 0 ;
img . style . padding = 0 ;
img . style . verticalAlign = "baseline" ;
span . style . fontFamily = family ;
span . style . fontSize = size ;
span . style . margin = 0 ;
span . style . padding = 0 ;
span . appendChild ( document . createTextNode ( sampleText ) ) ;
container . appendChild ( span ) ;
container . appendChild ( img ) ;
baseline = ( img . offsetTop - span . offsetTop ) + 1 ;
container . removeChild ( span ) ;
container . appendChild ( document . createTextNode ( sampleText ) ) ;
container . style . lineHeight = "normal" ;
img . style . verticalAlign = "super" ;
middle = ( img . offsetTop - container . offsetTop ) + 1 ;
document . body . removeChild ( container ) ;
this . baseline = baseline ;
this . lineWidth = 1 ;
this . middle = middle ;
}
module . exports = Font ;
} , { "./utils" : 26 } ] , 7 : [ function ( require , module , exports ) {
var Font = require ( './font' ) ;
function FontMetrics ( ) {
this . data = { } ;
}
FontMetrics . prototype . getMetrics = function ( family , size ) {
if ( this . data [ family + "-" + size ] === undefined ) {
this . data [ family + "-" + size ] = new Font ( family , size ) ;
}
return this . data [ family + "-" + size ] ;
} ;
module . exports = FontMetrics ;
} , { "./font" : 6 } ] , 8 : [ function ( require , module , exports ) {
var utils = require ( './utils' ) ;
var getBounds = utils . getBounds ;
var loadUrlDocument = require ( './proxy' ) . loadUrlDocument ;
function FrameContainer ( container , sameOrigin , options ) {
this . image = null ;
this . src = container ;
var self = this ;
var bounds = getBounds ( container ) ;
this . promise = ( ! sameOrigin ? this . proxyLoad ( options . proxy , bounds , options ) : new Promise ( function ( resolve ) {
if ( container . contentWindow . document . URL === "about:blank" || container . contentWindow . document . documentElement == null ) {
container . contentWindow . onload = container . onload = function ( ) {
resolve ( container ) ;
} ;
} else {
resolve ( container ) ;
}
} ) ) . then ( function ( container ) {
var html2canvas = require ( './core' ) ;
return html2canvas ( container . contentWindow . document . documentElement , { type : 'view' , width : container . width , height : container . height , proxy : options . proxy , javascriptEnabled : options . javascriptEnabled , removeContainer : options . removeContainer , allowTaint : options . allowTaint , imageTimeout : options . imageTimeout / 2 } ) ;
} ) . then ( function ( canvas ) {
return self . image = canvas ;
} ) ;
}
FrameContainer . prototype . proxyLoad = function ( proxy , bounds , options ) {
var container = this . src ;
return loadUrlDocument ( container . src , proxy , container . ownerDocument , bounds . width , bounds . height , options ) ;
} ;
module . exports = FrameContainer ;
} , { "./core" : 4 , "./proxy" : 16 , "./utils" : 26 } ] , 9 : [ function ( require , module , exports ) {
function GradientContainer ( imageData ) {
this . src = imageData . value ;
this . colorStops = [ ] ;
this . type = null ;
this . x0 = 0.5 ;
this . y0 = 0.5 ;
this . x1 = 0.5 ;
this . y1 = 0.5 ;
this . promise = Promise . resolve ( true ) ;
}
GradientContainer . TYPES = {
LINEAR : 1 ,
RADIAL : 2
} ;
// TODO: support hsl[a], negative %/length values
// TODO: support <angle> (e.g. -?\d{1,3}(?:\.\d+)deg, etc. : https://developer.mozilla.org/docs/Web/CSS/angle )
GradientContainer . REGEXP _COLORSTOP = /^\s*(rgba?\(\s*\d{1,3},\s*\d{1,3},\s*\d{1,3}(?:,\s*[0-9\.]+)?\s*\)|[a-z]{3,20}|#[a-f0-9]{3,6})(?:\s+(\d{1,3}(?:\.\d+)?)(%|px)?)?(?:\s|$)/i ;
module . exports = GradientContainer ;
} , { } ] , 10 : [ function ( require , module , exports ) {
function ImageContainer ( src , cors ) {
this . src = src ;
this . image = new Image ( ) ;
var self = this ;
this . tainted = null ;
this . promise = new Promise ( function ( resolve , reject ) {
self . image . onload = resolve ;
self . image . onerror = reject ;
if ( cors ) {
self . image . crossOrigin = "anonymous" ;
}
self . image . src = src ;
if ( self . image . complete === true ) {
resolve ( self . image ) ;
}
} ) ;
}
module . exports = ImageContainer ;
} , { } ] , 11 : [ function ( require , module , exports ) {
var log = require ( './log' ) ;
var ImageContainer = require ( './imagecontainer' ) ;
var DummyImageContainer = require ( './dummyimagecontainer' ) ;
var ProxyImageContainer = require ( './proxyimagecontainer' ) ;
var FrameContainer = require ( './framecontainer' ) ;
var SVGContainer = require ( './svgcontainer' ) ;
var SVGNodeContainer = require ( './svgnodecontainer' ) ;
var LinearGradientContainer = require ( './lineargradientcontainer' ) ;
var WebkitGradientContainer = require ( './webkitgradientcontainer' ) ;
var bind = require ( './utils' ) . bind ;
function ImageLoader ( options , support ) {
this . link = null ;
this . options = options ;
this . support = support ;
this . origin = this . getOrigin ( window . location . href ) ;
}
ImageLoader . prototype . findImages = function ( nodes ) {
var images = [ ] ;
nodes . reduce ( function ( imageNodes , container ) {
switch ( container . node . nodeName ) {
case "IMG" :
return imageNodes . concat ( [ {
args : [ container . node . src ] ,
method : "url"
} ] ) ;
case "svg" :
case "IFRAME" :
return imageNodes . concat ( [ {
args : [ container . node ] ,
method : container . node . nodeName
} ] ) ;
}
return imageNodes ;
} , [ ] ) . forEach ( this . addImage ( images , this . loadImage ) , this ) ;
return images ;
} ;
ImageLoader . prototype . findBackgroundImage = function ( images , container ) {
container . parseBackgroundImages ( ) . filter ( this . hasImageBackground ) . forEach ( this . addImage ( images , this . loadImage ) , this ) ;
return images ;
} ;
ImageLoader . prototype . addImage = function ( images , callback ) {
return function ( newImage ) {
newImage . args . forEach ( function ( image ) {
if ( ! this . imageExists ( images , image ) ) {
images . splice ( 0 , 0 , callback . call ( this , newImage ) ) ;
log ( 'Added image #' + ( images . length ) , typeof ( image ) === "string" ? image . substring ( 0 , 100 ) : image ) ;
}
} , this ) ;
} ;
} ;
ImageLoader . prototype . hasImageBackground = function ( imageData ) {
return imageData . method !== "none" ;
} ;
ImageLoader . prototype . loadImage = function ( imageData ) {
if ( imageData . method === "url" ) {
var src = imageData . args [ 0 ] ;
if ( this . isSVG ( src ) && ! this . support . svg && ! this . options . allowTaint ) {
return new SVGContainer ( src ) ;
} else if ( src . match ( /data:image\/.*;base64,/i ) ) {
return new ImageContainer ( src . replace ( /url\(['"]{0,}|['"]{0,}\)$/ig , '' ) , false ) ;
} else if ( this . isSameOrigin ( src ) || this . options . allowTaint === true || this . isSVG ( src ) ) {
return new ImageContainer ( src , false ) ;
} else if ( this . support . cors && ! this . options . allowTaint && this . options . useCORS ) {
return new ImageContainer ( src , true ) ;
} else if ( this . options . proxy ) {
return new ProxyImageContainer ( src , this . options . proxy ) ;
} else {
return new DummyImageContainer ( src ) ;
}
} else if ( imageData . method === "linear-gradient" ) {
return new LinearGradientContainer ( imageData ) ;
} else if ( imageData . method === "gradient" ) {
return new WebkitGradientContainer ( imageData ) ;
} else if ( imageData . method === "svg" ) {
return new SVGNodeContainer ( imageData . args [ 0 ] , this . support . svg ) ;
} else if ( imageData . method === "IFRAME" ) {
return new FrameContainer ( imageData . args [ 0 ] , this . isSameOrigin ( imageData . args [ 0 ] . src ) , this . options ) ;
} else {
return new DummyImageContainer ( imageData ) ;
}
} ;
ImageLoader . prototype . isSVG = function ( src ) {
return src . substring ( src . length - 3 ) . toLowerCase ( ) === "svg" || SVGContainer . prototype . isInline ( src ) ;
} ;
ImageLoader . prototype . imageExists = function ( images , src ) {
return images . some ( function ( image ) {
return image . src === src ;
} ) ;
} ;
ImageLoader . prototype . isSameOrigin = function ( url ) {
return ( this . getOrigin ( url ) === this . origin ) ;
} ;
ImageLoader . prototype . getOrigin = function ( url ) {
var link = this . link || ( this . link = document . createElement ( "a" ) ) ;
link . href = url ;
link . href = link . href ; // IE9, LOL! - http://jsfiddle.net/niklasvh/2e48b/
return link . protocol + link . hostname + link . port ;
} ;
ImageLoader . prototype . getPromise = function ( container ) {
return this . timeout ( container , this . options . imageTimeout ) [ 'catch' ] ( function ( ) {
var dummy = new DummyImageContainer ( container . src ) ;
return dummy . promise . then ( function ( image ) {
container . image = image ;
} ) ;
} ) ;
} ;
ImageLoader . prototype . get = function ( src ) {
var found = null ;
return this . images . some ( function ( img ) {
return ( found = img ) . src === src ;
} ) ? found : null ;
} ;
ImageLoader . prototype . fetch = function ( nodes ) {
this . images = nodes . reduce ( bind ( this . findBackgroundImage , this ) , this . findImages ( nodes ) ) ;
this . images . forEach ( function ( image , index ) {
image . promise . then ( function ( ) {
log ( "Succesfully loaded image #" + ( index + 1 ) , image ) ;
} , function ( e ) {
log ( "Failed loading image #" + ( index + 1 ) , image , e ) ;
} ) ;
} ) ;
this . ready = Promise . all ( this . images . map ( this . getPromise , this ) ) ;
log ( "Finished searching images" ) ;
return this ;
} ;
ImageLoader . prototype . timeout = function ( container , timeout ) {
var timer ;
var promise = Promise . race ( [ container . promise , new Promise ( function ( res , reject ) {
timer = setTimeout ( function ( ) {
log ( "Timed out loading image" , container ) ;
reject ( container ) ;
} , timeout ) ;
} ) ] ) . then ( function ( container ) {
clearTimeout ( timer ) ;
return container ;
} ) ;
promise [ 'catch' ] ( function ( ) {
clearTimeout ( timer ) ;
} ) ;
return promise ;
} ;
module . exports = ImageLoader ;
} , { "./dummyimagecontainer" : 5 , "./framecontainer" : 8 , "./imagecontainer" : 10 , "./lineargradientcontainer" : 12 , "./log" : 13 , "./proxyimagecontainer" : 17 , "./svgcontainer" : 23 , "./svgnodecontainer" : 24 , "./utils" : 26 , "./webkitgradientcontainer" : 27 } ] , 12 : [ function ( require , module , exports ) {
var GradientContainer = require ( './gradientcontainer' ) ;
var Color = require ( './color' ) ;
function LinearGradientContainer ( imageData ) {
GradientContainer . apply ( this , arguments ) ;
this . type = GradientContainer . TYPES . LINEAR ;
var hasDirection = LinearGradientContainer . REGEXP _DIRECTION . test ( imageData . args [ 0 ] ) ||
! GradientContainer . REGEXP _COLORSTOP . test ( imageData . args [ 0 ] ) ;
if ( hasDirection ) {
imageData . args [ 0 ] . split ( /\s+/ ) . reverse ( ) . forEach ( function ( position , index ) {
switch ( position ) {
case "left" :
this . x0 = 0 ;
this . x1 = 1 ;
break ;
case "top" :
this . y0 = 0 ;
this . y1 = 1 ;
break ;
case "right" :
this . x0 = 1 ;
this . x1 = 0 ;
break ;
case "bottom" :
this . y0 = 1 ;
this . y1 = 0 ;
break ;
case "to" :
var y0 = this . y0 ;
var x0 = this . x0 ;
this . y0 = this . y1 ;
this . x0 = this . x1 ;
this . x1 = x0 ;
this . y1 = y0 ;
break ;
case "center" :
break ; // centered by default
// Firefox internally converts position keywords to percentages:
// http://www.w3.org/TR/2010/WD-CSS2-20101207/colors.html#propdef-background-position
default : // percentage or absolute length
// TODO: support absolute start point positions (e.g., use bounds to convert px to a ratio)
var ratio = parseFloat ( position , 10 ) * 1e-2 ;
if ( isNaN ( ratio ) ) { // invalid or unhandled value
break ;
}
if ( index === 0 ) {
this . y0 = ratio ;
this . y1 = 1 - this . y0 ;
} else {
this . x0 = ratio ;
this . x1 = 1 - this . x0 ;
}
break ;
}
} , this ) ;
} else {
this . y0 = 0 ;
this . y1 = 1 ;
}
this . colorStops = imageData . args . slice ( hasDirection ? 1 : 0 ) . map ( function ( colorStop ) {
var colorStopMatch = colorStop . match ( GradientContainer . REGEXP _COLORSTOP ) ;
var value = + colorStopMatch [ 2 ] ;
var unit = value === 0 ? "%" : colorStopMatch [ 3 ] ; // treat "0" as "0%"
return {
color : new Color ( colorStopMatch [ 1 ] ) ,
// TODO: support absolute stop positions (e.g., compute gradient line length & convert px to ratio)
stop : unit === "%" ? value / 100 : null
} ;
} ) ;
if ( this . colorStops [ 0 ] . stop === null ) {
this . colorStops [ 0 ] . stop = 0 ;
}
if ( this . colorStops [ this . colorStops . length - 1 ] . stop === null ) {
this . colorStops [ this . colorStops . length - 1 ] . stop = 1 ;
}
// calculates and fills-in explicit stop positions when omitted from rule
this . colorStops . forEach ( function ( colorStop , index ) {
if ( colorStop . stop === null ) {
this . colorStops . slice ( index ) . some ( function ( find , count ) {
if ( find . stop !== null ) {
colorStop . stop = ( ( find . stop - this . colorStops [ index - 1 ] . stop ) / ( count + 1 ) ) + this . colorStops [ index - 1 ] . stop ;
return true ;
} else {
return false ;
}
} , this ) ;
}
} , this ) ;
}
LinearGradientContainer . prototype = Object . create ( GradientContainer . prototype ) ;
// TODO: support <angle> (e.g. -?\d{1,3}(?:\.\d+)deg, etc. : https://developer.mozilla.org/docs/Web/CSS/angle )
LinearGradientContainer . REGEXP _DIRECTION = /^\s*(?:to|left|right|top|bottom|center|\d{1,3}(?:\.\d+)?%?)(?:\s|$)/i ;
module . exports = LinearGradientContainer ;
} , { "./color" : 3 , "./gradientcontainer" : 9 } ] , 13 : [ function ( require , module , exports ) {
2016-01-23 21:41:53 +03:00
var logger = function ( ) {
if ( logger . options . logging && window . console && window . console . log ) {
Function . prototype . bind . call ( window . console . log , ( window . console ) ) . apply ( window . console , [ ( Date . now ( ) - logger . options . start ) + "ms" , "html2canvas:" ] . concat ( [ ] . slice . call ( arguments , 0 ) ) ) ;
2015-12-06 21:46:23 +03:00
}
} ;
2016-01-23 21:41:53 +03:00
logger . options = { logging : false } ;
module . exports = logger ;
2015-12-06 21:46:23 +03:00
} , { } ] , 14 : [ function ( require , module , exports ) {
var Color = require ( './color' ) ;
var utils = require ( './utils' ) ;
var getBounds = utils . getBounds ;
var parseBackgrounds = utils . parseBackgrounds ;
var offsetBounds = utils . offsetBounds ;
function NodeContainer ( node , parent ) {
this . node = node ;
this . parent = parent ;
this . stack = null ;
this . bounds = null ;
this . borders = null ;
this . clip = [ ] ;
this . backgroundClip = [ ] ;
this . offsetBounds = null ;
this . visible = null ;
this . computedStyles = null ;
this . colors = { } ;
this . styles = { } ;
this . backgroundImages = null ;
this . transformData = null ;
this . transformMatrix = null ;
this . isPseudoElement = false ;
this . opacity = null ;
}
NodeContainer . prototype . cloneTo = function ( stack ) {
stack . visible = this . visible ;
stack . borders = this . borders ;
stack . bounds = this . bounds ;
stack . clip = this . clip ;
stack . backgroundClip = this . backgroundClip ;
stack . computedStyles = this . computedStyles ;
stack . styles = this . styles ;
stack . backgroundImages = this . backgroundImages ;
stack . opacity = this . opacity ;
} ;
NodeContainer . prototype . getOpacity = function ( ) {
return this . opacity === null ? ( this . opacity = this . cssFloat ( 'opacity' ) ) : this . opacity ;
} ;
NodeContainer . prototype . assignStack = function ( stack ) {
this . stack = stack ;
stack . children . push ( this ) ;
} ;
NodeContainer . prototype . isElementVisible = function ( ) {
return this . node . nodeType === Node . TEXT _NODE ? this . parent . visible : (
this . css ( 'display' ) !== "none" &&
this . css ( 'visibility' ) !== "hidden" &&
! this . node . hasAttribute ( "data-html2canvas-ignore" ) &&
( this . node . nodeName !== "INPUT" || this . node . getAttribute ( "type" ) !== "hidden" )
) ;
} ;
NodeContainer . prototype . css = function ( attribute ) {
if ( ! this . computedStyles ) {
this . computedStyles = this . isPseudoElement ? this . parent . computedStyle ( this . before ? ":before" : ":after" ) : this . computedStyle ( null ) ;
}
return this . styles [ attribute ] || ( this . styles [ attribute ] = this . computedStyles [ attribute ] ) ;
} ;
NodeContainer . prototype . prefixedCss = function ( attribute ) {
var prefixes = [ "webkit" , "moz" , "ms" , "o" ] ;
var value = this . css ( attribute ) ;
if ( value === undefined ) {
prefixes . some ( function ( prefix ) {
value = this . css ( prefix + attribute . substr ( 0 , 1 ) . toUpperCase ( ) + attribute . substr ( 1 ) ) ;
return value !== undefined ;
} , this ) ;
}
return value === undefined ? null : value ;
} ;
NodeContainer . prototype . computedStyle = function ( type ) {
return this . node . ownerDocument . defaultView . getComputedStyle ( this . node , type ) ;
} ;
NodeContainer . prototype . cssInt = function ( attribute ) {
var value = parseInt ( this . css ( attribute ) , 10 ) ;
return ( isNaN ( value ) ) ? 0 : value ; // borders in old IE are throwing 'medium' for demo.html
} ;
NodeContainer . prototype . color = function ( attribute ) {
return this . colors [ attribute ] || ( this . colors [ attribute ] = new Color ( this . css ( attribute ) ) ) ;
} ;
NodeContainer . prototype . cssFloat = function ( attribute ) {
var value = parseFloat ( this . css ( attribute ) ) ;
return ( isNaN ( value ) ) ? 0 : value ;
} ;
NodeContainer . prototype . fontWeight = function ( ) {
var weight = this . css ( "fontWeight" ) ;
switch ( parseInt ( weight , 10 ) ) {
case 401 :
weight = "bold" ;
break ;
case 400 :
weight = "normal" ;
break ;
}
return weight ;
} ;
NodeContainer . prototype . parseClip = function ( ) {
var matches = this . css ( 'clip' ) . match ( this . CLIP ) ;
if ( matches ) {
return {
top : parseInt ( matches [ 1 ] , 10 ) ,
right : parseInt ( matches [ 2 ] , 10 ) ,
bottom : parseInt ( matches [ 3 ] , 10 ) ,
left : parseInt ( matches [ 4 ] , 10 )
} ;
}
return null ;
} ;
NodeContainer . prototype . parseBackgroundImages = function ( ) {
return this . backgroundImages || ( this . backgroundImages = parseBackgrounds ( this . css ( "backgroundImage" ) ) ) ;
} ;
NodeContainer . prototype . cssList = function ( property , index ) {
var value = ( this . css ( property ) || '' ) . split ( ',' ) ;
value = value [ index || 0 ] || value [ 0 ] || 'auto' ;
value = value . trim ( ) . split ( ' ' ) ;
if ( value . length === 1 ) {
value = [ value [ 0 ] , isPercentage ( value [ 0 ] ) ? 'auto' : value [ 0 ] ] ;
}
return value ;
} ;
NodeContainer . prototype . parseBackgroundSize = function ( bounds , image , index ) {
var size = this . cssList ( "backgroundSize" , index ) ;
var width , height ;
if ( isPercentage ( size [ 0 ] ) ) {
width = bounds . width * parseFloat ( size [ 0 ] ) / 100 ;
} else if ( /contain|cover/ . test ( size [ 0 ] ) ) {
var targetRatio = bounds . width / bounds . height , currentRatio = image . width / image . height ;
return ( targetRatio < currentRatio ^ size [ 0 ] === 'contain' ) ? { width : bounds . height * currentRatio , height : bounds . height } : { width : bounds . width , height : bounds . width / currentRatio } ;
} else {
width = parseInt ( size [ 0 ] , 10 ) ;
}
if ( size [ 0 ] === 'auto' && size [ 1 ] === 'auto' ) {
height = image . height ;
} else if ( size [ 1 ] === 'auto' ) {
height = width / image . width * image . height ;
} else if ( isPercentage ( size [ 1 ] ) ) {
height = bounds . height * parseFloat ( size [ 1 ] ) / 100 ;
} else {
height = parseInt ( size [ 1 ] , 10 ) ;
}
if ( size [ 0 ] === 'auto' ) {
width = height / image . height * image . width ;
}
return { width : width , height : height } ;
} ;
NodeContainer . prototype . parseBackgroundPosition = function ( bounds , image , index , backgroundSize ) {
var position = this . cssList ( 'backgroundPosition' , index ) ;
var left , top ;
if ( isPercentage ( position [ 0 ] ) ) {
left = ( bounds . width - ( backgroundSize || image ) . width ) * ( parseFloat ( position [ 0 ] ) / 100 ) ;
} else {
left = parseInt ( position [ 0 ] , 10 ) ;
}
if ( position [ 1 ] === 'auto' ) {
top = left / image . width * image . height ;
} else if ( isPercentage ( position [ 1 ] ) ) {
top = ( bounds . height - ( backgroundSize || image ) . height ) * parseFloat ( position [ 1 ] ) / 100 ;
} else {
top = parseInt ( position [ 1 ] , 10 ) ;
}
if ( position [ 0 ] === 'auto' ) {
left = top / image . height * image . width ;
}
return { left : left , top : top } ;
} ;
NodeContainer . prototype . parseBackgroundRepeat = function ( index ) {
return this . cssList ( "backgroundRepeat" , index ) [ 0 ] ;
} ;
NodeContainer . prototype . parseTextShadows = function ( ) {
var textShadow = this . css ( "textShadow" ) ;
var results = [ ] ;
if ( textShadow && textShadow !== 'none' ) {
var shadows = textShadow . match ( this . TEXT _SHADOW _PROPERTY ) ;
for ( var i = 0 ; shadows && ( i < shadows . length ) ; i ++ ) {
var s = shadows [ i ] . match ( this . TEXT _SHADOW _VALUES ) ;
results . push ( {
color : new Color ( s [ 0 ] ) ,
offsetX : s [ 1 ] ? parseFloat ( s [ 1 ] . replace ( 'px' , '' ) ) : 0 ,
offsetY : s [ 2 ] ? parseFloat ( s [ 2 ] . replace ( 'px' , '' ) ) : 0 ,
blur : s [ 3 ] ? s [ 3 ] . replace ( 'px' , '' ) : 0
} ) ;
}
}
return results ;
} ;
NodeContainer . prototype . parseTransform = function ( ) {
if ( ! this . transformData ) {
if ( this . hasTransform ( ) ) {
var offset = this . parseBounds ( ) ;
var origin = this . prefixedCss ( "transformOrigin" ) . split ( " " ) . map ( removePx ) . map ( asFloat ) ;
origin [ 0 ] += offset . left ;
origin [ 1 ] += offset . top ;
this . transformData = {
origin : origin ,
matrix : this . parseTransformMatrix ( )
} ;
} else {
this . transformData = {
origin : [ 0 , 0 ] ,
matrix : [ 1 , 0 , 0 , 1 , 0 , 0 ]
} ;
}
}
return this . transformData ;
} ;
NodeContainer . prototype . parseTransformMatrix = function ( ) {
if ( ! this . transformMatrix ) {
var transform = this . prefixedCss ( "transform" ) ;
var matrix = transform ? parseMatrix ( transform . match ( this . MATRIX _PROPERTY ) ) : null ;
this . transformMatrix = matrix ? matrix : [ 1 , 0 , 0 , 1 , 0 , 0 ] ;
}
return this . transformMatrix ;
} ;
NodeContainer . prototype . parseBounds = function ( ) {
return this . bounds || ( this . bounds = this . hasTransform ( ) ? offsetBounds ( this . node ) : getBounds ( this . node ) ) ;
} ;
NodeContainer . prototype . hasTransform = function ( ) {
return this . parseTransformMatrix ( ) . join ( "," ) !== "1,0,0,1,0,0" || ( this . parent && this . parent . hasTransform ( ) ) ;
} ;
NodeContainer . prototype . getValue = function ( ) {
var value = this . node . value || "" ;
if ( this . node . tagName === "SELECT" ) {
value = selectionValue ( this . node ) ;
} else if ( this . node . type === "password" ) {
value = Array ( value . length + 1 ) . join ( '\u2022' ) ; // jshint ignore:line
}
return value . length === 0 ? ( this . node . placeholder || "" ) : value ;
} ;
NodeContainer . prototype . MATRIX _PROPERTY = /(matrix|matrix3d)\((.+)\)/ ;
NodeContainer . prototype . TEXT _SHADOW _PROPERTY = /((rgba|rgb)\([^\)]+\)(\s-?\d+px){0,})/g ;
NodeContainer . prototype . TEXT _SHADOW _VALUES = /(-?\d+px)|(#.+)|(rgb\(.+\))|(rgba\(.+\))/g ;
NodeContainer . prototype . CLIP = /^rect\((\d+)px,? (\d+)px,? (\d+)px,? (\d+)px\)$/ ;
function selectionValue ( node ) {
var option = node . options [ node . selectedIndex || 0 ] ;
return option ? ( option . text || "" ) : "" ;
}
function parseMatrix ( match ) {
if ( match && match [ 1 ] === "matrix" ) {
return match [ 2 ] . split ( "," ) . map ( function ( s ) {
return parseFloat ( s . trim ( ) ) ;
} ) ;
} else if ( match && match [ 1 ] === "matrix3d" ) {
var matrix3d = match [ 2 ] . split ( "," ) . map ( function ( s ) {
return parseFloat ( s . trim ( ) ) ;
} ) ;
return [ matrix3d [ 0 ] , matrix3d [ 1 ] , matrix3d [ 4 ] , matrix3d [ 5 ] , matrix3d [ 12 ] , matrix3d [ 13 ] ] ;
}
}
function isPercentage ( value ) {
return value . toString ( ) . indexOf ( "%" ) !== - 1 ;
}
function removePx ( str ) {
return str . replace ( "px" , "" ) ;
}
function asFloat ( str ) {
return parseFloat ( str ) ;
}
module . exports = NodeContainer ;
} , { "./color" : 3 , "./utils" : 26 } ] , 15 : [ function ( require , module , exports ) {
var log = require ( './log' ) ;
var punycode = require ( 'punycode' ) ;
var NodeContainer = require ( './nodecontainer' ) ;
var TextContainer = require ( './textcontainer' ) ;
var PseudoElementContainer = require ( './pseudoelementcontainer' ) ;
var FontMetrics = require ( './fontmetrics' ) ;
var Color = require ( './color' ) ;
var StackingContext = require ( './stackingcontext' ) ;
var utils = require ( './utils' ) ;
var bind = utils . bind ;
var getBounds = utils . getBounds ;
var parseBackgrounds = utils . parseBackgrounds ;
var offsetBounds = utils . offsetBounds ;
function NodeParser ( element , renderer , support , imageLoader , options ) {
log ( "Starting NodeParser" ) ;
this . renderer = renderer ;
this . options = options ;
this . range = null ;
this . support = support ;
this . renderQueue = [ ] ;
this . stack = new StackingContext ( true , 1 , element . ownerDocument , null ) ;
var parent = new NodeContainer ( element , null ) ;
if ( options . background ) {
renderer . rectangle ( 0 , 0 , renderer . width , renderer . height , new Color ( options . background ) ) ;
}
if ( element === element . ownerDocument . documentElement ) {
// http://www.w3.org/TR/css3-background/#special-backgrounds
var canvasBackground = new NodeContainer ( parent . color ( 'backgroundColor' ) . isTransparent ( ) ? element . ownerDocument . body : element . ownerDocument . documentElement , null ) ;
renderer . rectangle ( 0 , 0 , renderer . width , renderer . height , canvasBackground . color ( 'backgroundColor' ) ) ;
}
parent . visibile = parent . isElementVisible ( ) ;
this . createPseudoHideStyles ( element . ownerDocument ) ;
this . disableAnimations ( element . ownerDocument ) ;
this . nodes = flatten ( [ parent ] . concat ( this . getChildren ( parent ) ) . filter ( function ( container ) {
return container . visible = container . isElementVisible ( ) ;
} ) . map ( this . getPseudoElements , this ) ) ;
this . fontMetrics = new FontMetrics ( ) ;
log ( "Fetched nodes, total:" , this . nodes . length ) ;
log ( "Calculate overflow clips" ) ;
this . calculateOverflowClips ( ) ;
log ( "Start fetching images" ) ;
this . images = imageLoader . fetch ( this . nodes . filter ( isElement ) ) ;
this . ready = this . images . ready . then ( bind ( function ( ) {
log ( "Images loaded, starting parsing" ) ;
log ( "Creating stacking contexts" ) ;
this . createStackingContexts ( ) ;
log ( "Sorting stacking contexts" ) ;
this . sortStackingContexts ( this . stack ) ;
this . parse ( this . stack ) ;
log ( "Render queue created with " + this . renderQueue . length + " items" ) ;
return new Promise ( bind ( function ( resolve ) {
if ( ! options . async ) {
this . renderQueue . forEach ( this . paint , this ) ;
resolve ( ) ;
} else if ( typeof ( options . async ) === "function" ) {
options . async . call ( this , this . renderQueue , resolve ) ;
} else if ( this . renderQueue . length > 0 ) {
this . renderIndex = 0 ;
this . asyncRenderer ( this . renderQueue , resolve ) ;
} else {
resolve ( ) ;
}
} , this ) ) ;
} , this ) ) ;
}
NodeParser . prototype . calculateOverflowClips = function ( ) {
this . nodes . forEach ( function ( container ) {
if ( isElement ( container ) ) {
if ( isPseudoElement ( container ) ) {
container . appendToDOM ( ) ;
}
container . borders = this . parseBorders ( container ) ;
var clip = ( container . css ( 'overflow' ) === "hidden" ) ? [ container . borders . clip ] : [ ] ;
var cssClip = container . parseClip ( ) ;
if ( cssClip && [ "absolute" , "fixed" ] . indexOf ( container . css ( 'position' ) ) !== - 1 ) {
clip . push ( [ [ "rect" ,
container . bounds . left + cssClip . left ,
container . bounds . top + cssClip . top ,
cssClip . right - cssClip . left ,
cssClip . bottom - cssClip . top
] ] ) ;
}
container . clip = hasParentClip ( container ) ? container . parent . clip . concat ( clip ) : clip ;
container . backgroundClip = ( container . css ( 'overflow' ) !== "hidden" ) ? container . clip . concat ( [ container . borders . clip ] ) : container . clip ;
if ( isPseudoElement ( container ) ) {
container . cleanDOM ( ) ;
}
} else if ( isTextNode ( container ) ) {
container . clip = hasParentClip ( container ) ? container . parent . clip : [ ] ;
}
if ( ! isPseudoElement ( container ) ) {
container . bounds = null ;
}
} , this ) ;
} ;
function hasParentClip ( container ) {
return container . parent && container . parent . clip . length ;
}
NodeParser . prototype . asyncRenderer = function ( queue , resolve , asyncTimer ) {
asyncTimer = asyncTimer || Date . now ( ) ;
this . paint ( queue [ this . renderIndex ++ ] ) ;
if ( queue . length === this . renderIndex ) {
resolve ( ) ;
} else if ( asyncTimer + 20 > Date . now ( ) ) {
this . asyncRenderer ( queue , resolve , asyncTimer ) ;
} else {
setTimeout ( bind ( function ( ) {
this . asyncRenderer ( queue , resolve ) ;
} , this ) , 0 ) ;
}
} ;
NodeParser . prototype . createPseudoHideStyles = function ( document ) {
this . createStyles ( document , '.' + PseudoElementContainer . prototype . PSEUDO _HIDE _ELEMENT _CLASS _BEFORE + ':before { content: "" !important; display: none !important; }' +
'.' + PseudoElementContainer . prototype . PSEUDO _HIDE _ELEMENT _CLASS _AFTER + ':after { content: "" !important; display: none !important; }' ) ;
} ;
NodeParser . prototype . disableAnimations = function ( document ) {
this . createStyles ( document , '* { -webkit-animation: none !important; -moz-animation: none !important; -o-animation: none !important; animation: none !important; ' +
'-webkit-transition: none !important; -moz-transition: none !important; -o-transition: none !important; transition: none !important;}' ) ;
} ;
NodeParser . prototype . createStyles = function ( document , styles ) {
var hidePseudoElements = document . createElement ( 'style' ) ;
hidePseudoElements . innerHTML = styles ;
document . body . appendChild ( hidePseudoElements ) ;
} ;
NodeParser . prototype . getPseudoElements = function ( container ) {
var nodes = [ [ container ] ] ;
if ( container . node . nodeType === Node . ELEMENT _NODE ) {
var before = this . getPseudoElement ( container , ":before" ) ;
var after = this . getPseudoElement ( container , ":after" ) ;
if ( before ) {
nodes . push ( before ) ;
}
if ( after ) {
nodes . push ( after ) ;
}
}
return flatten ( nodes ) ;
} ;
function toCamelCase ( str ) {
return str . replace ( /(\-[a-z])/g , function ( match ) {
return match . toUpperCase ( ) . replace ( '-' , '' ) ;
} ) ;
}
NodeParser . prototype . getPseudoElement = function ( container , type ) {
var style = container . computedStyle ( type ) ;
if ( ! style || ! style . content || style . content === "none" || style . content === "-moz-alt-content" || style . display === "none" ) {
return null ;
}
var content = stripQuotes ( style . content ) ;
var isImage = content . substr ( 0 , 3 ) === 'url' ;
var pseudoNode = document . createElement ( isImage ? 'img' : 'html2canvaspseudoelement' ) ;
var pseudoContainer = new PseudoElementContainer ( pseudoNode , container , type ) ;
for ( var i = style . length - 1 ; i >= 0 ; i -- ) {
var property = toCamelCase ( style . item ( i ) ) ;
pseudoNode . style [ property ] = style [ property ] ;
}
pseudoNode . className = PseudoElementContainer . prototype . PSEUDO _HIDE _ELEMENT _CLASS _BEFORE + " " + PseudoElementContainer . prototype . PSEUDO _HIDE _ELEMENT _CLASS _AFTER ;
if ( isImage ) {
pseudoNode . src = parseBackgrounds ( content ) [ 0 ] . args [ 0 ] ;
return [ pseudoContainer ] ;
} else {
var text = document . createTextNode ( content ) ;
pseudoNode . appendChild ( text ) ;
return [ pseudoContainer , new TextContainer ( text , pseudoContainer ) ] ;
}
} ;
NodeParser . prototype . getChildren = function ( parentContainer ) {
return flatten ( [ ] . filter . call ( parentContainer . node . childNodes , renderableNode ) . map ( function ( node ) {
var container = [ node . nodeType === Node . TEXT _NODE ? new TextContainer ( node , parentContainer ) : new NodeContainer ( node , parentContainer ) ] . filter ( nonIgnoredElement ) ;
return node . nodeType === Node . ELEMENT _NODE && container . length && node . tagName !== "TEXTAREA" ? ( container [ 0 ] . isElementVisible ( ) ? container . concat ( this . getChildren ( container [ 0 ] ) ) : [ ] ) : container ;
} , this ) ) ;
} ;
NodeParser . prototype . newStackingContext = function ( container , hasOwnStacking ) {
var stack = new StackingContext ( hasOwnStacking , container . getOpacity ( ) , container . node , container . parent ) ;
container . cloneTo ( stack ) ;
var parentStack = hasOwnStacking ? stack . getParentStack ( this ) : stack . parent . stack ;
parentStack . contexts . push ( stack ) ;
container . stack = stack ;
} ;
NodeParser . prototype . createStackingContexts = function ( ) {
this . nodes . forEach ( function ( container ) {
if ( isElement ( container ) && ( this . isRootElement ( container ) || hasOpacity ( container ) || isPositionedForStacking ( container ) || this . isBodyWithTransparentRoot ( container ) || container . hasTransform ( ) ) ) {
this . newStackingContext ( container , true ) ;
} else if ( isElement ( container ) && ( ( isPositioned ( container ) && zIndex0 ( container ) ) || isInlineBlock ( container ) || isFloating ( container ) ) ) {
this . newStackingContext ( container , false ) ;
} else {
container . assignStack ( container . parent . stack ) ;
}
} , this ) ;
} ;
NodeParser . prototype . isBodyWithTransparentRoot = function ( container ) {
return container . node . nodeName === "BODY" && container . parent . color ( 'backgroundColor' ) . isTransparent ( ) ;
} ;
NodeParser . prototype . isRootElement = function ( container ) {
return container . parent === null ;
} ;
NodeParser . prototype . sortStackingContexts = function ( stack ) {
stack . contexts . sort ( zIndexSort ( stack . contexts . slice ( 0 ) ) ) ;
stack . contexts . forEach ( this . sortStackingContexts , this ) ;
} ;
NodeParser . prototype . parseTextBounds = function ( container ) {
return function ( text , index , textList ) {
if ( container . parent . css ( "textDecoration" ) . substr ( 0 , 4 ) !== "none" || text . trim ( ) . length !== 0 ) {
if ( this . support . rangeBounds && ! container . parent . hasTransform ( ) ) {
var offset = textList . slice ( 0 , index ) . join ( "" ) . length ;
return this . getRangeBounds ( container . node , offset , text . length ) ;
} else if ( container . node && typeof ( container . node . data ) === "string" ) {
var replacementNode = container . node . splitText ( text . length ) ;
var bounds = this . getWrapperBounds ( container . node , container . parent . hasTransform ( ) ) ;
container . node = replacementNode ;
return bounds ;
}
} else if ( ! this . support . rangeBounds || container . parent . hasTransform ( ) ) {
container . node = container . node . splitText ( text . length ) ;
}
return { } ;
} ;
} ;
NodeParser . prototype . getWrapperBounds = function ( node , transform ) {
var wrapper = node . ownerDocument . createElement ( 'html2canvaswrapper' ) ;
var parent = node . parentNode ,
backupText = node . cloneNode ( true ) ;
wrapper . appendChild ( node . cloneNode ( true ) ) ;
parent . replaceChild ( wrapper , node ) ;
var bounds = transform ? offsetBounds ( wrapper ) : getBounds ( wrapper ) ;
parent . replaceChild ( backupText , wrapper ) ;
return bounds ;
} ;
NodeParser . prototype . getRangeBounds = function ( node , offset , length ) {
var range = this . range || ( this . range = node . ownerDocument . createRange ( ) ) ;
range . setStart ( node , offset ) ;
range . setEnd ( node , offset + length ) ;
return range . getBoundingClientRect ( ) ;
} ;
function ClearTransform ( ) { }
NodeParser . prototype . parse = function ( stack ) {
// http://www.w3.org/TR/CSS21/visuren.html#z-index
var negativeZindex = stack . contexts . filter ( negativeZIndex ) ; // 2. the child stacking contexts with negative stack levels (most negative first).
var descendantElements = stack . children . filter ( isElement ) ;
var descendantNonFloats = descendantElements . filter ( not ( isFloating ) ) ;
var nonInlineNonPositionedDescendants = descendantNonFloats . filter ( not ( isPositioned ) ) . filter ( not ( inlineLevel ) ) ; // 3 the in-flow, non-inline-level, non-positioned descendants.
var nonPositionedFloats = descendantElements . filter ( not ( isPositioned ) ) . filter ( isFloating ) ; // 4. the non-positioned floats.
var inFlow = descendantNonFloats . filter ( not ( isPositioned ) ) . filter ( inlineLevel ) ; // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
var stackLevel0 = stack . contexts . concat ( descendantNonFloats . filter ( isPositioned ) ) . filter ( zIndex0 ) ; // 6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
var text = stack . children . filter ( isTextNode ) . filter ( hasText ) ;
var positiveZindex = stack . contexts . filter ( positiveZIndex ) ; // 7. the child stacking contexts with positive stack levels (least positive first).
negativeZindex . concat ( nonInlineNonPositionedDescendants ) . concat ( nonPositionedFloats )
. concat ( inFlow ) . concat ( stackLevel0 ) . concat ( text ) . concat ( positiveZindex ) . forEach ( function ( container ) {
this . renderQueue . push ( container ) ;
if ( isStackingContext ( container ) ) {
this . parse ( container ) ;
this . renderQueue . push ( new ClearTransform ( ) ) ;
}
} , this ) ;
} ;
NodeParser . prototype . paint = function ( container ) {
try {
if ( container instanceof ClearTransform ) {
this . renderer . ctx . restore ( ) ;
} else if ( isTextNode ( container ) ) {
if ( isPseudoElement ( container . parent ) ) {
container . parent . appendToDOM ( ) ;
}
this . paintText ( container ) ;
if ( isPseudoElement ( container . parent ) ) {
container . parent . cleanDOM ( ) ;
}
} else {
this . paintNode ( container ) ;
}
} catch ( e ) {
log ( e ) ;
if ( this . options . strict ) {
throw e ;
}
}
} ;
NodeParser . prototype . paintNode = function ( container ) {
if ( isStackingContext ( container ) ) {
this . renderer . setOpacity ( container . opacity ) ;
this . renderer . ctx . save ( ) ;
if ( container . hasTransform ( ) ) {
this . renderer . setTransform ( container . parseTransform ( ) ) ;
}
}
if ( container . node . nodeName === "INPUT" && container . node . type === "checkbox" ) {
this . paintCheckbox ( container ) ;
} else if ( container . node . nodeName === "INPUT" && container . node . type === "radio" ) {
this . paintRadio ( container ) ;
} else {
this . paintElement ( container ) ;
}
} ;
NodeParser . prototype . paintElement = function ( container ) {
var bounds = container . parseBounds ( ) ;
this . renderer . clip ( container . backgroundClip , function ( ) {
this . renderer . renderBackground ( container , bounds , container . borders . borders . map ( getWidth ) ) ;
} , this ) ;
this . renderer . clip ( container . clip , function ( ) {
this . renderer . renderBorders ( container . borders . borders ) ;
} , this ) ;
this . renderer . clip ( container . backgroundClip , function ( ) {
switch ( container . node . nodeName ) {
case "svg" :
case "IFRAME" :
var imgContainer = this . images . get ( container . node ) ;
if ( imgContainer ) {
this . renderer . renderImage ( container , bounds , container . borders , imgContainer ) ;
} else {
log ( "Error loading <" + container . node . nodeName + ">" , container . node ) ;
}
break ;
case "IMG" :
var imageContainer = this . images . get ( container . node . src ) ;
if ( imageContainer ) {
this . renderer . renderImage ( container , bounds , container . borders , imageContainer ) ;
} else {
log ( "Error loading <img>" , container . node . src ) ;
}
break ;
case "CANVAS" :
this . renderer . renderImage ( container , bounds , container . borders , { image : container . node } ) ;
break ;
case "SELECT" :
case "INPUT" :
case "TEXTAREA" :
this . paintFormValue ( container ) ;
break ;
}
} , this ) ;
} ;
NodeParser . prototype . paintCheckbox = function ( container ) {
var b = container . parseBounds ( ) ;
var size = Math . min ( b . width , b . height ) ;
var bounds = { width : size - 1 , height : size - 1 , top : b . top , left : b . left } ;
var r = [ 3 , 3 ] ;
var radius = [ r , r , r , r ] ;
var borders = [ 1 , 1 , 1 , 1 ] . map ( function ( w ) {
return { color : new Color ( '#A5A5A5' ) , width : w } ;
} ) ;
var borderPoints = calculateCurvePoints ( bounds , radius , borders ) ;
this . renderer . clip ( container . backgroundClip , function ( ) {
this . renderer . rectangle ( bounds . left + 1 , bounds . top + 1 , bounds . width - 2 , bounds . height - 2 , new Color ( "#DEDEDE" ) ) ;
this . renderer . renderBorders ( calculateBorders ( borders , bounds , borderPoints , radius ) ) ;
if ( container . node . checked ) {
this . renderer . font ( new Color ( '#424242' ) , 'normal' , 'normal' , 'bold' , ( size - 3 ) + "px" , 'arial' ) ;
this . renderer . text ( "\u2714" , bounds . left + size / 6 , bounds . top + size - 1 ) ;
}
} , this ) ;
} ;
NodeParser . prototype . paintRadio = function ( container ) {
var bounds = container . parseBounds ( ) ;
var size = Math . min ( bounds . width , bounds . height ) - 2 ;
this . renderer . clip ( container . backgroundClip , function ( ) {
this . renderer . circleStroke ( bounds . left + 1 , bounds . top + 1 , size , new Color ( '#DEDEDE' ) , 1 , new Color ( '#A5A5A5' ) ) ;
if ( container . node . checked ) {
this . renderer . circle ( Math . ceil ( bounds . left + size / 4 ) + 1 , Math . ceil ( bounds . top + size / 4 ) + 1 , Math . floor ( size / 2 ) , new Color ( '#424242' ) ) ;
}
} , this ) ;
} ;
NodeParser . prototype . paintFormValue = function ( container ) {
var value = container . getValue ( ) ;
if ( value . length > 0 ) {
var document = container . node . ownerDocument ;
var wrapper = document . createElement ( 'html2canvaswrapper' ) ;
var properties = [ 'lineHeight' , 'textAlign' , 'fontFamily' , 'fontWeight' , 'fontSize' , 'color' ,
'paddingLeft' , 'paddingTop' , 'paddingRight' , 'paddingBottom' ,
'width' , 'height' , 'borderLeftStyle' , 'borderTopStyle' , 'borderLeftWidth' , 'borderTopWidth' ,
'boxSizing' , 'whiteSpace' , 'wordWrap' ] ;
properties . forEach ( function ( property ) {
try {
wrapper . style [ property ] = container . css ( property ) ;
} catch ( e ) {
// Older IE has issues with "border"
log ( "html2canvas: Parse: Exception caught in renderFormValue: " + e . message ) ;
}
} ) ;
var bounds = container . parseBounds ( ) ;
wrapper . style . position = "fixed" ;
wrapper . style . left = bounds . left + "px" ;
wrapper . style . top = bounds . top + "px" ;
wrapper . textContent = value ;
document . body . appendChild ( wrapper ) ;
this . paintText ( new TextContainer ( wrapper . firstChild , container ) ) ;
document . body . removeChild ( wrapper ) ;
}
} ;
NodeParser . prototype . paintText = function ( container ) {
container . applyTextTransform ( ) ;
var characters = punycode . ucs2 . decode ( container . node . data ) ;
var textList = ( ! this . options . letterRendering || noLetterSpacing ( container ) ) && ! hasUnicode ( container . node . data ) ? getWords ( characters ) : characters . map ( function ( character ) {
return punycode . ucs2 . encode ( [ character ] ) ;
} ) ;
var weight = container . parent . fontWeight ( ) ;
var size = container . parent . css ( 'fontSize' ) ;
var family = container . parent . css ( 'fontFamily' ) ;
var shadows = container . parent . parseTextShadows ( ) ;
this . renderer . font ( container . parent . color ( 'color' ) , container . parent . css ( 'fontStyle' ) , container . parent . css ( 'fontVariant' ) , weight , size , family ) ;
if ( shadows . length ) {
// TODO: support multiple text shadows
this . renderer . fontShadow ( shadows [ 0 ] . color , shadows [ 0 ] . offsetX , shadows [ 0 ] . offsetY , shadows [ 0 ] . blur ) ;
} else {
this . renderer . clearShadow ( ) ;
}
this . renderer . clip ( container . parent . clip , function ( ) {
textList . map ( this . parseTextBounds ( container ) , this ) . forEach ( function ( bounds , index ) {
if ( bounds ) {
this . renderer . text ( textList [ index ] , bounds . left , bounds . bottom ) ;
this . renderTextDecoration ( container . parent , bounds , this . fontMetrics . getMetrics ( family , size ) ) ;
}
} , this ) ;
} , this ) ;
} ;
NodeParser . prototype . renderTextDecoration = function ( container , bounds , metrics ) {
switch ( container . css ( "textDecoration" ) . split ( " " ) [ 0 ] ) {
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
this . renderer . rectangle ( bounds . left , Math . round ( bounds . top + metrics . baseline + metrics . lineWidth ) , bounds . width , 1 , container . color ( "color" ) ) ;
break ;
case "overline" :
this . renderer . rectangle ( bounds . left , Math . round ( bounds . top ) , bounds . width , 1 , container . color ( "color" ) ) ;
break ;
case "line-through" :
// TODO try and find exact position for line-through
this . renderer . rectangle ( bounds . left , Math . ceil ( bounds . top + metrics . middle + metrics . lineWidth ) , bounds . width , 1 , container . color ( "color" ) ) ;
break ;
}
} ;
var borderColorTransforms = {
inset : [
[ "darken" , 0.60 ] ,
[ "darken" , 0.10 ] ,
[ "darken" , 0.10 ] ,
[ "darken" , 0.60 ]
]
} ;
NodeParser . prototype . parseBorders = function ( container ) {
var nodeBounds = container . parseBounds ( ) ;
var radius = getBorderRadiusData ( container ) ;
var borders = [ "Top" , "Right" , "Bottom" , "Left" ] . map ( function ( side , index ) {
var style = container . css ( 'border' + side + 'Style' ) ;
var color = container . color ( 'border' + side + 'Color' ) ;
if ( style === "inset" && color . isBlack ( ) ) {
color = new Color ( [ 255 , 255 , 255 , color . a ] ) ; // this is wrong, but
}
var colorTransform = borderColorTransforms [ style ] ? borderColorTransforms [ style ] [ index ] : null ;
return {
width : container . cssInt ( 'border' + side + 'Width' ) ,
color : colorTransform ? color [ colorTransform [ 0 ] ] ( colorTransform [ 1 ] ) : color ,
args : null
} ;
} ) ;
var borderPoints = calculateCurvePoints ( nodeBounds , radius , borders ) ;
return {
clip : this . parseBackgroundClip ( container , borderPoints , borders , radius , nodeBounds ) ,
borders : calculateBorders ( borders , nodeBounds , borderPoints , radius )
} ;
} ;
function calculateBorders ( borders , nodeBounds , borderPoints , radius ) {
return borders . map ( function ( border , borderSide ) {
if ( border . width > 0 ) {
var bx = nodeBounds . left ;
var by = nodeBounds . top ;
var bw = nodeBounds . width ;
var bh = nodeBounds . height - ( borders [ 2 ] . width ) ;
switch ( borderSide ) {
case 0 :
// top border
bh = borders [ 0 ] . width ;
border . args = drawSide ( {
c1 : [ bx , by ] ,
c2 : [ bx + bw , by ] ,
c3 : [ bx + bw - borders [ 1 ] . width , by + bh ] ,
c4 : [ bx + borders [ 3 ] . width , by + bh ]
} , radius [ 0 ] , radius [ 1 ] ,
borderPoints . topLeftOuter , borderPoints . topLeftInner , borderPoints . topRightOuter , borderPoints . topRightInner ) ;
break ;
case 1 :
// right border
bx = nodeBounds . left + nodeBounds . width - ( borders [ 1 ] . width ) ;
bw = borders [ 1 ] . width ;
border . args = drawSide ( {
c1 : [ bx + bw , by ] ,
c2 : [ bx + bw , by + bh + borders [ 2 ] . width ] ,
c3 : [ bx , by + bh ] ,
c4 : [ bx , by + borders [ 0 ] . width ]
} , radius [ 1 ] , radius [ 2 ] ,
borderPoints . topRightOuter , borderPoints . topRightInner , borderPoints . bottomRightOuter , borderPoints . bottomRightInner ) ;
break ;
case 2 :
// bottom border
by = ( by + nodeBounds . height ) - ( borders [ 2 ] . width ) ;
bh = borders [ 2 ] . width ;
border . args = drawSide ( {
c1 : [ bx + bw , by + bh ] ,
c2 : [ bx , by + bh ] ,
c3 : [ bx + borders [ 3 ] . width , by ] ,
c4 : [ bx + bw - borders [ 3 ] . width , by ]
} , radius [ 2 ] , radius [ 3 ] ,
borderPoints . bottomRightOuter , borderPoints . bottomRightInner , borderPoints . bottomLeftOuter , borderPoints . bottomLeftInner ) ;
break ;
case 3 :
// left border
bw = borders [ 3 ] . width ;
border . args = drawSide ( {
c1 : [ bx , by + bh + borders [ 2 ] . width ] ,
c2 : [ bx , by ] ,
c3 : [ bx + bw , by + borders [ 0 ] . width ] ,
c4 : [ bx + bw , by + bh ]
} , radius [ 3 ] , radius [ 0 ] ,
borderPoints . bottomLeftOuter , borderPoints . bottomLeftInner , borderPoints . topLeftOuter , borderPoints . topLeftInner ) ;
break ;
}
}
return border ;
} ) ;
}
NodeParser . prototype . parseBackgroundClip = function ( container , borderPoints , borders , radius , bounds ) {
var backgroundClip = container . css ( 'backgroundClip' ) ,
borderArgs = [ ] ;
switch ( backgroundClip ) {
case "content-box" :
case "padding-box" :
parseCorner ( borderArgs , radius [ 0 ] , radius [ 1 ] , borderPoints . topLeftInner , borderPoints . topRightInner , bounds . left + borders [ 3 ] . width , bounds . top + borders [ 0 ] . width ) ;
parseCorner ( borderArgs , radius [ 1 ] , radius [ 2 ] , borderPoints . topRightInner , borderPoints . bottomRightInner , bounds . left + bounds . width - borders [ 1 ] . width , bounds . top + borders [ 0 ] . width ) ;
parseCorner ( borderArgs , radius [ 2 ] , radius [ 3 ] , borderPoints . bottomRightInner , borderPoints . bottomLeftInner , bounds . left + bounds . width - borders [ 1 ] . width , bounds . top + bounds . height - borders [ 2 ] . width ) ;
parseCorner ( borderArgs , radius [ 3 ] , radius [ 0 ] , borderPoints . bottomLeftInner , borderPoints . topLeftInner , bounds . left + borders [ 3 ] . width , bounds . top + bounds . height - borders [ 2 ] . width ) ;
break ;
default :
parseCorner ( borderArgs , radius [ 0 ] , radius [ 1 ] , borderPoints . topLeftOuter , borderPoints . topRightOuter , bounds . left , bounds . top ) ;
parseCorner ( borderArgs , radius [ 1 ] , radius [ 2 ] , borderPoints . topRightOuter , borderPoints . bottomRightOuter , bounds . left + bounds . width , bounds . top ) ;
parseCorner ( borderArgs , radius [ 2 ] , radius [ 3 ] , borderPoints . bottomRightOuter , borderPoints . bottomLeftOuter , bounds . left + bounds . width , bounds . top + bounds . height ) ;
parseCorner ( borderArgs , radius [ 3 ] , radius [ 0 ] , borderPoints . bottomLeftOuter , borderPoints . topLeftOuter , bounds . left , bounds . top + bounds . height ) ;
break ;
}
return borderArgs ;
} ;
function getCurvePoints ( x , y , r1 , r2 ) {
var kappa = 4 * ( ( Math . sqrt ( 2 ) - 1 ) / 3 ) ;
var ox = ( r1 ) * kappa , // control point offset horizontal
oy = ( r2 ) * kappa , // control point offset vertical
xm = x + r1 , // x-middle
ym = y + r2 ; // y-middle
return {
topLeft : bezierCurve ( { x : x , y : ym } , { x : x , y : ym - oy } , { x : xm - ox , y : y } , { x : xm , y : y } ) ,
topRight : bezierCurve ( { x : x , y : y } , { x : x + ox , y : y } , { x : xm , y : ym - oy } , { x : xm , y : ym } ) ,
bottomRight : bezierCurve ( { x : xm , y : y } , { x : xm , y : y + oy } , { x : x + ox , y : ym } , { x : x , y : ym } ) ,
bottomLeft : bezierCurve ( { x : xm , y : ym } , { x : xm - ox , y : ym } , { x : x , y : y + oy } , { x : x , y : y } )
} ;
}
function calculateCurvePoints ( bounds , borderRadius , borders ) {
var x = bounds . left ,
y = bounds . top ,
width = bounds . width ,
height = bounds . height ,
tlh = borderRadius [ 0 ] [ 0 ] < width / 2 ? borderRadius [ 0 ] [ 0 ] : width / 2 ,
tlv = borderRadius [ 0 ] [ 1 ] < height / 2 ? borderRadius [ 0 ] [ 1 ] : height / 2 ,
trh = borderRadius [ 1 ] [ 0 ] < width / 2 ? borderRadius [ 1 ] [ 0 ] : width / 2 ,
trv = borderRadius [ 1 ] [ 1 ] < height / 2 ? borderRadius [ 1 ] [ 1 ] : height / 2 ,
brh = borderRadius [ 2 ] [ 0 ] < width / 2 ? borderRadius [ 2 ] [ 0 ] : width / 2 ,
brv = borderRadius [ 2 ] [ 1 ] < height / 2 ? borderRadius [ 2 ] [ 1 ] : height / 2 ,
blh = borderRadius [ 3 ] [ 0 ] < width / 2 ? borderRadius [ 3 ] [ 0 ] : width / 2 ,
blv = borderRadius [ 3 ] [ 1 ] < height / 2 ? borderRadius [ 3 ] [ 1 ] : height / 2 ;
var topWidth = width - trh ,
rightHeight = height - brv ,
bottomWidth = width - brh ,
leftHeight = height - blv ;
return {
topLeftOuter : getCurvePoints ( x , y , tlh , tlv ) . topLeft . subdivide ( 0.5 ) ,
topLeftInner : getCurvePoints ( x + borders [ 3 ] . width , y + borders [ 0 ] . width , Math . max ( 0 , tlh - borders [ 3 ] . width ) , Math . max ( 0 , tlv - borders [ 0 ] . width ) ) . topLeft . subdivide ( 0.5 ) ,
topRightOuter : getCurvePoints ( x + topWidth , y , trh , trv ) . topRight . subdivide ( 0.5 ) ,
topRightInner : getCurvePoints ( x + Math . min ( topWidth , width + borders [ 3 ] . width ) , y + borders [ 0 ] . width , ( topWidth > width + borders [ 3 ] . width ) ? 0 : trh - borders [ 3 ] . width , trv - borders [ 0 ] . width ) . topRight . subdivide ( 0.5 ) ,
bottomRightOuter : getCurvePoints ( x + bottomWidth , y + rightHeight , brh , brv ) . bottomRight . subdivide ( 0.5 ) ,
bottomRightInner : getCurvePoints ( x + Math . min ( bottomWidth , width - borders [ 3 ] . width ) , y + Math . min ( rightHeight , height + borders [ 0 ] . width ) , Math . max ( 0 , brh - borders [ 1 ] . width ) , brv - borders [ 2 ] . width ) . bottomRight . subdivide ( 0.5 ) ,
bottomLeftOuter : getCurvePoints ( x , y + leftHeight , blh , blv ) . bottomLeft . subdivide ( 0.5 ) ,
bottomLeftInner : getCurvePoints ( x + borders [ 3 ] . width , y + leftHeight , Math . max ( 0 , blh - borders [ 3 ] . width ) , blv - borders [ 2 ] . width ) . bottomLeft . subdivide ( 0.5 )
} ;
}
function bezierCurve ( start , startControl , endControl , end ) {
var lerp = function ( a , b , t ) {
return {
x : a . x + ( b . x - a . x ) * t ,
y : a . y + ( b . y - a . y ) * t
} ;
} ;
return {
start : start ,
startControl : startControl ,
endControl : endControl ,
end : end ,
subdivide : function ( t ) {
var ab = lerp ( start , startControl , t ) ,
bc = lerp ( startControl , endControl , t ) ,
cd = lerp ( endControl , end , t ) ,
abbc = lerp ( ab , bc , t ) ,
bccd = lerp ( bc , cd , t ) ,
dest = lerp ( abbc , bccd , t ) ;
return [ bezierCurve ( start , ab , abbc , dest ) , bezierCurve ( dest , bccd , cd , end ) ] ;
} ,
curveTo : function ( borderArgs ) {
borderArgs . push ( [ "bezierCurve" , startControl . x , startControl . y , endControl . x , endControl . y , end . x , end . y ] ) ;
} ,
curveToReversed : function ( borderArgs ) {
borderArgs . push ( [ "bezierCurve" , endControl . x , endControl . y , startControl . x , startControl . y , start . x , start . y ] ) ;
}
} ;
}
function drawSide ( borderData , radius1 , radius2 , outer1 , inner1 , outer2 , inner2 ) {
var borderArgs = [ ] ;
if ( radius1 [ 0 ] > 0 || radius1 [ 1 ] > 0 ) {
borderArgs . push ( [ "line" , outer1 [ 1 ] . start . x , outer1 [ 1 ] . start . y ] ) ;
outer1 [ 1 ] . curveTo ( borderArgs ) ;
} else {
borderArgs . push ( [ "line" , borderData . c1 [ 0 ] , borderData . c1 [ 1 ] ] ) ;
}
if ( radius2 [ 0 ] > 0 || radius2 [ 1 ] > 0 ) {
borderArgs . push ( [ "line" , outer2 [ 0 ] . start . x , outer2 [ 0 ] . start . y ] ) ;
outer2 [ 0 ] . curveTo ( borderArgs ) ;
borderArgs . push ( [ "line" , inner2 [ 0 ] . end . x , inner2 [ 0 ] . end . y ] ) ;
inner2 [ 0 ] . curveToReversed ( borderArgs ) ;
} else {
borderArgs . push ( [ "line" , borderData . c2 [ 0 ] , borderData . c2 [ 1 ] ] ) ;
borderArgs . push ( [ "line" , borderData . c3 [ 0 ] , borderData . c3 [ 1 ] ] ) ;
}
if ( radius1 [ 0 ] > 0 || radius1 [ 1 ] > 0 ) {
borderArgs . push ( [ "line" , inner1 [ 1 ] . end . x , inner1 [ 1 ] . end . y ] ) ;
inner1 [ 1 ] . curveToReversed ( borderArgs ) ;
} else {
borderArgs . push ( [ "line" , borderData . c4 [ 0 ] , borderData . c4 [ 1 ] ] ) ;
}
return borderArgs ;
}
function parseCorner ( borderArgs , radius1 , radius2 , corner1 , corner2 , x , y ) {
if ( radius1 [ 0 ] > 0 || radius1 [ 1 ] > 0 ) {
borderArgs . push ( [ "line" , corner1 [ 0 ] . start . x , corner1 [ 0 ] . start . y ] ) ;
corner1 [ 0 ] . curveTo ( borderArgs ) ;
corner1 [ 1 ] . curveTo ( borderArgs ) ;
} else {
borderArgs . push ( [ "line" , x , y ] ) ;
}
if ( radius2 [ 0 ] > 0 || radius2 [ 1 ] > 0 ) {
borderArgs . push ( [ "line" , corner2 [ 0 ] . start . x , corner2 [ 0 ] . start . y ] ) ;
}
}
function negativeZIndex ( container ) {
return container . cssInt ( "zIndex" ) < 0 ;
}
function positiveZIndex ( container ) {
return container . cssInt ( "zIndex" ) > 0 ;
}
function zIndex0 ( container ) {
return container . cssInt ( "zIndex" ) === 0 ;
}
function inlineLevel ( container ) {
return [ "inline" , "inline-block" , "inline-table" ] . indexOf ( container . css ( "display" ) ) !== - 1 ;
}
function isStackingContext ( container ) {
return ( container instanceof StackingContext ) ;
}
function hasText ( container ) {
return container . node . data . trim ( ) . length > 0 ;
}
function noLetterSpacing ( container ) {
return ( /^(normal|none|0px)$/ . test ( container . parent . css ( "letterSpacing" ) ) ) ;
}
function getBorderRadiusData ( container ) {
return [ "TopLeft" , "TopRight" , "BottomRight" , "BottomLeft" ] . map ( function ( side ) {
var value = container . css ( 'border' + side + 'Radius' ) ;
var arr = value . split ( " " ) ;
if ( arr . length <= 1 ) {
arr [ 1 ] = arr [ 0 ] ;
}
return arr . map ( asInt ) ;
} ) ;
}
function renderableNode ( node ) {
return ( node . nodeType === Node . TEXT _NODE || node . nodeType === Node . ELEMENT _NODE ) ;
}
function isPositionedForStacking ( container ) {
var position = container . css ( "position" ) ;
var zIndex = ( [ "absolute" , "relative" , "fixed" ] . indexOf ( position ) !== - 1 ) ? container . css ( "zIndex" ) : "auto" ;
return zIndex !== "auto" ;
}
function isPositioned ( container ) {
return container . css ( "position" ) !== "static" ;
}
function isFloating ( container ) {
return container . css ( "float" ) !== "none" ;
}
function isInlineBlock ( container ) {
return [ "inline-block" , "inline-table" ] . indexOf ( container . css ( "display" ) ) !== - 1 ;
}
function not ( callback ) {
var context = this ;
return function ( ) {
return ! callback . apply ( context , arguments ) ;
} ;
}
function isElement ( container ) {
return container . node . nodeType === Node . ELEMENT _NODE ;
}
function isPseudoElement ( container ) {
return container . isPseudoElement === true ;
}
function isTextNode ( container ) {
return container . node . nodeType === Node . TEXT _NODE ;
}
function zIndexSort ( contexts ) {
return function ( a , b ) {
return ( a . cssInt ( "zIndex" ) + ( contexts . indexOf ( a ) / contexts . length ) ) - ( b . cssInt ( "zIndex" ) + ( contexts . indexOf ( b ) / contexts . length ) ) ;
} ;
}
function hasOpacity ( container ) {
return container . getOpacity ( ) < 1 ;
}
function asInt ( value ) {
return parseInt ( value , 10 ) ;
}
function getWidth ( border ) {
return border . width ;
}
function nonIgnoredElement ( nodeContainer ) {
return ( nodeContainer . node . nodeType !== Node . ELEMENT _NODE || [ "SCRIPT" , "HEAD" , "TITLE" , "OBJECT" , "BR" , "OPTION" ] . indexOf ( nodeContainer . node . nodeName ) === - 1 ) ;
}
function flatten ( arrays ) {
return [ ] . concat . apply ( [ ] , arrays ) ;
}
function stripQuotes ( content ) {
var first = content . substr ( 0 , 1 ) ;
return ( first === content . substr ( content . length - 1 ) && first . match ( /'|"/ ) ) ? content . substr ( 1 , content . length - 2 ) : content ;
}
function getWords ( characters ) {
var words = [ ] , i = 0 , onWordBoundary = false , word ;
while ( characters . length ) {
if ( isWordBoundary ( characters [ i ] ) === onWordBoundary ) {
word = characters . splice ( 0 , i ) ;
if ( word . length ) {
words . push ( punycode . ucs2 . encode ( word ) ) ;
}
onWordBoundary = ! onWordBoundary ;
i = 0 ;
} else {
i ++ ;
}
if ( i >= characters . length ) {
word = characters . splice ( 0 , i ) ;
if ( word . length ) {
words . push ( punycode . ucs2 . encode ( word ) ) ;
}
}
}
return words ;
}
function isWordBoundary ( characterCode ) {
return [
32 , // <space>
13 , // \r
10 , // \n
9 , // \t
45 // -
] . indexOf ( characterCode ) !== - 1 ;
}
function hasUnicode ( string ) {
return ( /[^\u0000-\u00ff]/ ) . test ( string ) ;
}
module . exports = NodeParser ;
} , { "./color" : 3 , "./fontmetrics" : 7 , "./log" : 13 , "./nodecontainer" : 14 , "./pseudoelementcontainer" : 18 , "./stackingcontext" : 21 , "./textcontainer" : 25 , "./utils" : 26 , "punycode" : 1 } ] , 16 : [ function ( require , module , exports ) {
var XHR = require ( './xhr' ) ;
var utils = require ( './utils' ) ;
var log = require ( './log' ) ;
var createWindowClone = require ( './clone' ) ;
var decode64 = utils . decode64 ;
function Proxy ( src , proxyUrl , document ) {
var supportsCORS = ( 'withCredentials' in new XMLHttpRequest ( ) ) ;
if ( ! proxyUrl ) {
return Promise . reject ( "No proxy configured" ) ;
}
var callback = createCallback ( supportsCORS ) ;
var url = createProxyUrl ( proxyUrl , src , callback ) ;
return supportsCORS ? XHR ( url ) : ( jsonp ( document , url , callback ) . then ( function ( response ) {
return decode64 ( response . content ) ;
} ) ) ;
}
var proxyCount = 0 ;
function ProxyURL ( src , proxyUrl , document ) {
var supportsCORSImage = ( 'crossOrigin' in new Image ( ) ) ;
var callback = createCallback ( supportsCORSImage ) ;
var url = createProxyUrl ( proxyUrl , src , callback ) ;
return ( supportsCORSImage ? Promise . resolve ( url ) : jsonp ( document , url , callback ) . then ( function ( response ) {
return "data:" + response . type + ";base64," + response . content ;
} ) ) ;
}
function jsonp ( document , url , callback ) {
return new Promise ( function ( resolve , reject ) {
var s = document . createElement ( "script" ) ;
var cleanup = function ( ) {
delete window . html2canvas . proxy [ callback ] ;
document . body . removeChild ( s ) ;
} ;
window . html2canvas . proxy [ callback ] = function ( response ) {
cleanup ( ) ;
resolve ( response ) ;
} ;
s . src = url ;
s . onerror = function ( e ) {
cleanup ( ) ;
reject ( e ) ;
} ;
document . body . appendChild ( s ) ;
} ) ;
}
function createCallback ( useCORS ) {
return ! useCORS ? "html2canvas_" + Date . now ( ) + "_" + ( ++ proxyCount ) + "_" + Math . round ( Math . random ( ) * 100000 ) : "" ;
}
function createProxyUrl ( proxyUrl , src , callback ) {
return proxyUrl + "?url=" + encodeURIComponent ( src ) + ( callback . length ? "&callback=html2canvas.proxy." + callback : "" ) ;
}
function documentFromHTML ( src ) {
return function ( html ) {
var parser = new DOMParser ( ) , doc ;
try {
doc = parser . parseFromString ( html , "text/html" ) ;
} catch ( e ) {
log ( "DOMParser not supported, falling back to createHTMLDocument" ) ;
doc = document . implementation . createHTMLDocument ( "" ) ;
try {
doc . open ( ) ;
doc . write ( html ) ;
doc . close ( ) ;
} catch ( ee ) {
log ( "createHTMLDocument write not supported, falling back to document.body.innerHTML" ) ;
doc . body . innerHTML = html ; // ie9 doesnt support writing to documentElement
}
}
var b = doc . querySelector ( "base" ) ;
if ( ! b || ! b . href . host ) {
var base = doc . createElement ( "base" ) ;
base . href = src ;
doc . head . insertBefore ( base , doc . head . firstChild ) ;
}
return doc ;
} ;
}
function loadUrlDocument ( src , proxy , document , width , height , options ) {
return new Proxy ( src , proxy , window . document ) . then ( documentFromHTML ( src ) ) . then ( function ( doc ) {
return createWindowClone ( doc , document , width , height , options , 0 , 0 ) ;
} ) ;
}
exports . Proxy = Proxy ;
exports . ProxyURL = ProxyURL ;
exports . loadUrlDocument = loadUrlDocument ;
} , { "./clone" : 2 , "./log" : 13 , "./utils" : 26 , "./xhr" : 28 } ] , 17 : [ function ( require , module , exports ) {
var ProxyURL = require ( './proxy' ) . ProxyURL ;
function ProxyImageContainer ( src , proxy ) {
var link = document . createElement ( "a" ) ;
link . href = src ;
src = link . href ;
this . src = src ;
this . image = new Image ( ) ;
var self = this ;
this . promise = new Promise ( function ( resolve , reject ) {
self . image . crossOrigin = "Anonymous" ;
self . image . onload = resolve ;
self . image . onerror = reject ;
new ProxyURL ( src , proxy , document ) . then ( function ( url ) {
self . image . src = url ;
} ) [ 'catch' ] ( reject ) ;
} ) ;
}
module . exports = ProxyImageContainer ;
} , { "./proxy" : 16 } ] , 18 : [ function ( require , module , exports ) {
var NodeContainer = require ( './nodecontainer' ) ;
function PseudoElementContainer ( node , parent , type ) {
NodeContainer . call ( this , node , parent ) ;
this . isPseudoElement = true ;
this . before = type === ":before" ;
}
PseudoElementContainer . prototype . cloneTo = function ( stack ) {
PseudoElementContainer . prototype . cloneTo . call ( this , stack ) ;
stack . isPseudoElement = true ;
stack . before = this . before ;
} ;
PseudoElementContainer . prototype = Object . create ( NodeContainer . prototype ) ;
PseudoElementContainer . prototype . appendToDOM = function ( ) {
if ( this . before ) {
this . parent . node . insertBefore ( this . node , this . parent . node . firstChild ) ;
} else {
this . parent . node . appendChild ( this . node ) ;
}
this . parent . node . className += " " + this . getHideClass ( ) ;
} ;
PseudoElementContainer . prototype . cleanDOM = function ( ) {
this . node . parentNode . removeChild ( this . node ) ;
this . parent . node . className = this . parent . node . className . replace ( this . getHideClass ( ) , "" ) ;
} ;
PseudoElementContainer . prototype . getHideClass = function ( ) {
return this [ "PSEUDO_HIDE_ELEMENT_CLASS_" + ( this . before ? "BEFORE" : "AFTER" ) ] ;
} ;
PseudoElementContainer . prototype . PSEUDO _HIDE _ELEMENT _CLASS _BEFORE = "___html2canvas___pseudoelement_before" ;
PseudoElementContainer . prototype . PSEUDO _HIDE _ELEMENT _CLASS _AFTER = "___html2canvas___pseudoelement_after" ;
module . exports = PseudoElementContainer ;
} , { "./nodecontainer" : 14 } ] , 19 : [ function ( require , module , exports ) {
var log = require ( './log' ) ;
function Renderer ( width , height , images , options , document ) {
this . width = width ;
this . height = height ;
this . images = images ;
this . options = options ;
this . document = document ;
}
Renderer . prototype . renderImage = function ( container , bounds , borderData , imageContainer ) {
var paddingLeft = container . cssInt ( 'paddingLeft' ) ,
paddingTop = container . cssInt ( 'paddingTop' ) ,
paddingRight = container . cssInt ( 'paddingRight' ) ,
paddingBottom = container . cssInt ( 'paddingBottom' ) ,
borders = borderData . borders ;
var width = bounds . width - ( borders [ 1 ] . width + borders [ 3 ] . width + paddingLeft + paddingRight ) ;
var height = bounds . height - ( borders [ 0 ] . width + borders [ 2 ] . width + paddingTop + paddingBottom ) ;
this . drawImage (
imageContainer ,
0 ,
0 ,
imageContainer . image . width || width ,
imageContainer . image . height || height ,
bounds . left + paddingLeft + borders [ 3 ] . width ,
bounds . top + paddingTop + borders [ 0 ] . width ,
width ,
height
) ;
} ;
Renderer . prototype . renderBackground = function ( container , bounds , borderData ) {
if ( bounds . height > 0 && bounds . width > 0 ) {
this . renderBackgroundColor ( container , bounds ) ;
this . renderBackgroundImage ( container , bounds , borderData ) ;
}
} ;
Renderer . prototype . renderBackgroundColor = function ( container , bounds ) {
var color = container . color ( "backgroundColor" ) ;
if ( ! color . isTransparent ( ) ) {
this . rectangle ( bounds . left , bounds . top , bounds . width , bounds . height , color ) ;
}
} ;
Renderer . prototype . renderBorders = function ( borders ) {
borders . forEach ( this . renderBorder , this ) ;
} ;
Renderer . prototype . renderBorder = function ( data ) {
if ( ! data . color . isTransparent ( ) && data . args !== null ) {
this . drawShape ( data . args , data . color ) ;
}
} ;
Renderer . prototype . renderBackgroundImage = function ( container , bounds , borderData ) {
var backgroundImages = container . parseBackgroundImages ( ) ;
backgroundImages . reverse ( ) . forEach ( function ( backgroundImage , index , arr ) {
switch ( backgroundImage . method ) {
case "url" :
var image = this . images . get ( backgroundImage . args [ 0 ] ) ;
if ( image ) {
this . renderBackgroundRepeating ( container , bounds , image , arr . length - ( index + 1 ) , borderData ) ;
} else {
log ( "Error loading background-image" , backgroundImage . args [ 0 ] ) ;
}
break ;
case "linear-gradient" :
case "gradient" :
var gradientImage = this . images . get ( backgroundImage . value ) ;
if ( gradientImage ) {
this . renderBackgroundGradient ( gradientImage , bounds , borderData ) ;
} else {
log ( "Error loading background-image" , backgroundImage . args [ 0 ] ) ;
}
break ;
case "none" :
break ;
default :
log ( "Unknown background-image type" , backgroundImage . args [ 0 ] ) ;
}
} , this ) ;
} ;
Renderer . prototype . renderBackgroundRepeating = function ( container , bounds , imageContainer , index , borderData ) {
var size = container . parseBackgroundSize ( bounds , imageContainer . image , index ) ;
var position = container . parseBackgroundPosition ( bounds , imageContainer . image , index , size ) ;
var repeat = container . parseBackgroundRepeat ( index ) ;
switch ( repeat ) {
case "repeat-x" :
case "repeat no-repeat" :
this . backgroundRepeatShape ( imageContainer , position , size , bounds , bounds . left + borderData [ 3 ] , bounds . top + position . top + borderData [ 0 ] , 99999 , size . height , borderData ) ;
break ;
case "repeat-y" :
case "no-repeat repeat" :
this . backgroundRepeatShape ( imageContainer , position , size , bounds , bounds . left + position . left + borderData [ 3 ] , bounds . top + borderData [ 0 ] , size . width , 99999 , borderData ) ;
break ;
case "no-repeat" :
this . backgroundRepeatShape ( imageContainer , position , size , bounds , bounds . left + position . left + borderData [ 3 ] , bounds . top + position . top + borderData [ 0 ] , size . width , size . height , borderData ) ;
break ;
default :
this . renderBackgroundRepeat ( imageContainer , position , size , { top : bounds . top , left : bounds . left } , borderData [ 3 ] , borderData [ 0 ] ) ;
break ;
}
} ;
module . exports = Renderer ;
} , { "./log" : 13 } ] , 20 : [ function ( require , module , exports ) {
var Renderer = require ( '../renderer' ) ;
var LinearGradientContainer = require ( '../lineargradientcontainer' ) ;
var log = require ( '../log' ) ;
function CanvasRenderer ( width , height ) {
Renderer . apply ( this , arguments ) ;
this . canvas = this . options . canvas || this . document . createElement ( "canvas" ) ;
if ( ! this . options . canvas ) {
this . canvas . width = width ;
this . canvas . height = height ;
}
this . ctx = this . canvas . getContext ( "2d" ) ;
this . taintCtx = this . document . createElement ( "canvas" ) . getContext ( "2d" ) ;
this . ctx . textBaseline = "bottom" ;
this . variables = { } ;
log ( "Initialized CanvasRenderer with size" , width , "x" , height ) ;
}
CanvasRenderer . prototype = Object . create ( Renderer . prototype ) ;
CanvasRenderer . prototype . setFillStyle = function ( fillStyle ) {
this . ctx . fillStyle = typeof ( fillStyle ) === "object" && ! ! fillStyle . isColor ? fillStyle . toString ( ) : fillStyle ;
return this . ctx ;
} ;
CanvasRenderer . prototype . rectangle = function ( left , top , width , height , color ) {
this . setFillStyle ( color ) . fillRect ( left , top , width , height ) ;
} ;
CanvasRenderer . prototype . circle = function ( left , top , size , color ) {
this . setFillStyle ( color ) ;
this . ctx . beginPath ( ) ;
this . ctx . arc ( left + size / 2 , top + size / 2 , size / 2 , 0 , Math . PI * 2 , true ) ;
this . ctx . closePath ( ) ;
this . ctx . fill ( ) ;
} ;
CanvasRenderer . prototype . circleStroke = function ( left , top , size , color , stroke , strokeColor ) {
this . circle ( left , top , size , color ) ;
this . ctx . strokeStyle = strokeColor . toString ( ) ;
this . ctx . stroke ( ) ;
} ;
CanvasRenderer . prototype . drawShape = function ( shape , color ) {
this . shape ( shape ) ;
this . setFillStyle ( color ) . fill ( ) ;
} ;
CanvasRenderer . prototype . taints = function ( imageContainer ) {
if ( imageContainer . tainted === null ) {
this . taintCtx . drawImage ( imageContainer . image , 0 , 0 ) ;
try {
this . taintCtx . getImageData ( 0 , 0 , 1 , 1 ) ;
imageContainer . tainted = false ;
} catch ( e ) {
this . taintCtx = document . createElement ( "canvas" ) . getContext ( "2d" ) ;
imageContainer . tainted = true ;
}
}
return imageContainer . tainted ;
} ;
CanvasRenderer . prototype . drawImage = function ( imageContainer , sx , sy , sw , sh , dx , dy , dw , dh ) {
if ( ! this . taints ( imageContainer ) || this . options . allowTaint ) {
this . ctx . drawImage ( imageContainer . image , sx , sy , sw , sh , dx , dy , dw , dh ) ;
}
} ;
CanvasRenderer . prototype . clip = function ( shapes , callback , context ) {
this . ctx . save ( ) ;
shapes . filter ( hasEntries ) . forEach ( function ( shape ) {
this . shape ( shape ) . clip ( ) ;
} , this ) ;
callback . call ( context ) ;
this . ctx . restore ( ) ;
} ;
CanvasRenderer . prototype . shape = function ( shape ) {
this . ctx . beginPath ( ) ;
shape . forEach ( function ( point , index ) {
if ( point [ 0 ] === "rect" ) {
this . ctx . rect . apply ( this . ctx , point . slice ( 1 ) ) ;
} else {
this . ctx [ ( index === 0 ) ? "moveTo" : point [ 0 ] + "To" ] . apply ( this . ctx , point . slice ( 1 ) ) ;
}
} , this ) ;
this . ctx . closePath ( ) ;
return this . ctx ;
} ;
CanvasRenderer . prototype . font = function ( color , style , variant , weight , size , family ) {
this . setFillStyle ( color ) . font = [ style , variant , weight , size , family ] . join ( " " ) . split ( "," ) [ 0 ] ;
} ;
CanvasRenderer . prototype . fontShadow = function ( color , offsetX , offsetY , blur ) {
this . setVariable ( "shadowColor" , color . toString ( ) )
. setVariable ( "shadowOffsetY" , offsetX )
. setVariable ( "shadowOffsetX" , offsetY )
. setVariable ( "shadowBlur" , blur ) ;
} ;
CanvasRenderer . prototype . clearShadow = function ( ) {
this . setVariable ( "shadowColor" , "rgba(0,0,0,0)" ) ;
} ;
CanvasRenderer . prototype . setOpacity = function ( opacity ) {
this . ctx . globalAlpha = opacity ;
} ;
CanvasRenderer . prototype . setTransform = function ( transform ) {
this . ctx . translate ( transform . origin [ 0 ] , transform . origin [ 1 ] ) ;
this . ctx . transform . apply ( this . ctx , transform . matrix ) ;
this . ctx . translate ( - transform . origin [ 0 ] , - transform . origin [ 1 ] ) ;
} ;
CanvasRenderer . prototype . setVariable = function ( property , value ) {
if ( this . variables [ property ] !== value ) {
this . variables [ property ] = this . ctx [ property ] = value ;
}
return this ;
} ;
CanvasRenderer . prototype . text = function ( text , left , bottom ) {
this . ctx . fillText ( text , left , bottom ) ;
} ;
CanvasRenderer . prototype . backgroundRepeatShape = function ( imageContainer , backgroundPosition , size , bounds , left , top , width , height , borderData ) {
var shape = [
[ "line" , Math . round ( left ) , Math . round ( top ) ] ,
[ "line" , Math . round ( left + width ) , Math . round ( top ) ] ,
[ "line" , Math . round ( left + width ) , Math . round ( height + top ) ] ,
[ "line" , Math . round ( left ) , Math . round ( height + top ) ]
] ;
this . clip ( [ shape ] , function ( ) {
this . renderBackgroundRepeat ( imageContainer , backgroundPosition , size , bounds , borderData [ 3 ] , borderData [ 0 ] ) ;
} , this ) ;
} ;
CanvasRenderer . prototype . renderBackgroundRepeat = function ( imageContainer , backgroundPosition , size , bounds , borderLeft , borderTop ) {
var offsetX = Math . round ( bounds . left + backgroundPosition . left + borderLeft ) , offsetY = Math . round ( bounds . top + backgroundPosition . top + borderTop ) ;
this . setFillStyle ( this . ctx . createPattern ( this . resizeImage ( imageContainer , size ) , "repeat" ) ) ;
this . ctx . translate ( offsetX , offsetY ) ;
this . ctx . fill ( ) ;
this . ctx . translate ( - offsetX , - offsetY ) ;
} ;
CanvasRenderer . prototype . renderBackgroundGradient = function ( gradientImage , bounds ) {
if ( gradientImage instanceof LinearGradientContainer ) {
var gradient = this . ctx . createLinearGradient (
bounds . left + bounds . width * gradientImage . x0 ,
bounds . top + bounds . height * gradientImage . y0 ,
bounds . left + bounds . width * gradientImage . x1 ,
bounds . top + bounds . height * gradientImage . y1 ) ;
gradientImage . colorStops . forEach ( function ( colorStop ) {
gradient . addColorStop ( colorStop . stop , colorStop . color . toString ( ) ) ;
} ) ;
this . rectangle ( bounds . left , bounds . top , bounds . width , bounds . height , gradient ) ;
}
} ;
CanvasRenderer . prototype . resizeImage = function ( imageContainer , size ) {
var image = imageContainer . image ;
if ( image . width === size . width && image . height === size . height ) {
return image ;
}
var ctx , canvas = document . createElement ( 'canvas' ) ;
canvas . width = size . width ;
canvas . height = size . height ;
ctx = canvas . getContext ( "2d" ) ;
ctx . drawImage ( image , 0 , 0 , image . width , image . height , 0 , 0 , size . width , size . height ) ;
return canvas ;
} ;
function hasEntries ( array ) {
return array . length > 0 ;
}
module . exports = CanvasRenderer ;
} , { "../lineargradientcontainer" : 12 , "../log" : 13 , "../renderer" : 19 } ] , 21 : [ function ( require , module , exports ) {
var NodeContainer = require ( './nodecontainer' ) ;
function StackingContext ( hasOwnStacking , opacity , element , parent ) {
NodeContainer . call ( this , element , parent ) ;
this . ownStacking = hasOwnStacking ;
this . contexts = [ ] ;
this . children = [ ] ;
this . opacity = ( this . parent ? this . parent . stack . opacity : 1 ) * opacity ;
}
StackingContext . prototype = Object . create ( NodeContainer . prototype ) ;
StackingContext . prototype . getParentStack = function ( context ) {
var parentStack = ( this . parent ) ? this . parent . stack : null ;
return parentStack ? ( parentStack . ownStacking ? parentStack : parentStack . getParentStack ( context ) ) : context . stack ;
} ;
module . exports = StackingContext ;
} , { "./nodecontainer" : 14 } ] , 22 : [ function ( require , module , exports ) {
function Support ( document ) {
this . rangeBounds = this . testRangeBounds ( document ) ;
this . cors = this . testCORS ( ) ;
this . svg = this . testSVG ( ) ;
}
Support . prototype . testRangeBounds = function ( document ) {
var range , testElement , rangeBounds , rangeHeight , support = false ;
if ( document . createRange ) {
range = document . createRange ( ) ;
if ( range . getBoundingClientRect ) {
testElement = document . createElement ( 'boundtest' ) ;
testElement . style . height = "123px" ;
testElement . style . display = "block" ;
document . body . appendChild ( testElement ) ;
range . selectNode ( testElement ) ;
rangeBounds = range . getBoundingClientRect ( ) ;
rangeHeight = rangeBounds . height ;
if ( rangeHeight === 123 ) {
support = true ;
}
document . body . removeChild ( testElement ) ;
}
}
return support ;
} ;
Support . prototype . testCORS = function ( ) {
return typeof ( ( new Image ( ) ) . crossOrigin ) !== "undefined" ;
} ;
Support . prototype . testSVG = function ( ) {
var img = new Image ( ) ;
var canvas = document . createElement ( "canvas" ) ;
var ctx = canvas . getContext ( "2d" ) ;
img . src = "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>" ;
try {
ctx . drawImage ( img , 0 , 0 ) ;
canvas . toDataURL ( ) ;
} catch ( e ) {
return false ;
}
return true ;
} ;
module . exports = Support ;
} , { } ] , 23 : [ function ( require , module , exports ) {
var XHR = require ( './xhr' ) ;
var decode64 = require ( './utils' ) . decode64 ;
function SVGContainer ( src ) {
this . src = src ;
this . image = null ;
var self = this ;
this . promise = this . hasFabric ( ) . then ( function ( ) {
return ( self . isInline ( src ) ? Promise . resolve ( self . inlineFormatting ( src ) ) : XHR ( src ) ) ;
} ) . then ( function ( svg ) {
return new Promise ( function ( resolve ) {
window . html2canvas . svg . fabric . loadSVGFromString ( svg , self . createCanvas . call ( self , resolve ) ) ;
} ) ;
} ) ;
}
SVGContainer . prototype . hasFabric = function ( ) {
return ! window . html2canvas . svg || ! window . html2canvas . svg . fabric ? Promise . reject ( new Error ( "html2canvas.svg.js is not loaded, cannot render svg" ) ) : Promise . resolve ( ) ;
} ;
SVGContainer . prototype . inlineFormatting = function ( src ) {
return ( /^data:image\/svg\+xml;base64,/ . test ( src ) ) ? this . decode64 ( this . removeContentType ( src ) ) : this . removeContentType ( src ) ;
} ;
SVGContainer . prototype . removeContentType = function ( src ) {
return src . replace ( /^data:image\/svg\+xml(;base64)?,/ , '' ) ;
} ;
SVGContainer . prototype . isInline = function ( src ) {
return ( /^data:image\/svg\+xml/i . test ( src ) ) ;
} ;
SVGContainer . prototype . createCanvas = function ( resolve ) {
var self = this ;
return function ( objects , options ) {
var canvas = new window . html2canvas . svg . fabric . StaticCanvas ( 'c' ) ;
self . image = canvas . lowerCanvasEl ;
canvas
. setWidth ( options . width )
. setHeight ( options . height )
. add ( window . html2canvas . svg . fabric . util . groupSVGElements ( objects , options ) )
. renderAll ( ) ;
resolve ( canvas . lowerCanvasEl ) ;
} ;
} ;
SVGContainer . prototype . decode64 = function ( str ) {
return ( typeof ( window . atob ) === "function" ) ? window . atob ( str ) : decode64 ( str ) ;
} ;
module . exports = SVGContainer ;
} , { "./utils" : 26 , "./xhr" : 28 } ] , 24 : [ function ( require , module , exports ) {
var SVGContainer = require ( './svgcontainer' ) ;
function SVGNodeContainer ( node , _native ) {
this . src = node ;
this . image = null ;
var self = this ;
this . promise = _native ? new Promise ( function ( resolve , reject ) {
self . image = new Image ( ) ;
self . image . onload = resolve ;
self . image . onerror = reject ;
self . image . src = "data:image/svg+xml," + ( new XMLSerializer ( ) ) . serializeToString ( node ) ;
if ( self . image . complete === true ) {
resolve ( self . image ) ;
}
} ) : this . hasFabric ( ) . then ( function ( ) {
return new Promise ( function ( resolve ) {
window . html2canvas . svg . fabric . parseSVGDocument ( node , self . createCanvas . call ( self , resolve ) ) ;
} ) ;
} ) ;
}
SVGNodeContainer . prototype = Object . create ( SVGContainer . prototype ) ;
module . exports = SVGNodeContainer ;
} , { "./svgcontainer" : 23 } ] , 25 : [ function ( require , module , exports ) {
var NodeContainer = require ( './nodecontainer' ) ;
function TextContainer ( node , parent ) {
NodeContainer . call ( this , node , parent ) ;
}
TextContainer . prototype = Object . create ( NodeContainer . prototype ) ;
TextContainer . prototype . applyTextTransform = function ( ) {
this . node . data = this . transform ( this . parent . css ( "textTransform" ) ) ;
} ;
TextContainer . prototype . transform = function ( transform ) {
var text = this . node . data ;
switch ( transform ) {
case "lowercase" :
return text . toLowerCase ( ) ;
case "capitalize" :
return text . replace ( /(^|\s|:|-|\(|\))([a-z])/g , capitalize ) ;
case "uppercase" :
return text . toUpperCase ( ) ;
default :
return text ;
}
} ;
function capitalize ( m , p1 , p2 ) {
if ( m . length > 0 ) {
return p1 + p2 . toUpperCase ( ) ;
}
}
module . exports = TextContainer ;
} , { "./nodecontainer" : 14 } ] , 26 : [ function ( require , module , exports ) {
exports . smallImage = function smallImage ( ) {
return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" ;
} ;
exports . bind = function ( callback , context ) {
return function ( ) {
return callback . apply ( context , arguments ) ;
} ;
} ;
/ *
* base64 - arraybuffer
* https : //github.com/niklasvh/base64-arraybuffer
*
* Copyright ( c ) 2012 Niklas von Hertzen
* Licensed under the MIT license .
* /
exports . decode64 = function ( base64 ) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;
var len = base64 . length , i , encoded1 , encoded2 , encoded3 , encoded4 , byte1 , byte2 , byte3 ;
var output = "" ;
for ( i = 0 ; i < len ; i += 4 ) {
encoded1 = chars . indexOf ( base64 [ i ] ) ;
encoded2 = chars . indexOf ( base64 [ i + 1 ] ) ;
encoded3 = chars . indexOf ( base64 [ i + 2 ] ) ;
encoded4 = chars . indexOf ( base64 [ i + 3 ] ) ;
byte1 = ( encoded1 << 2 ) | ( encoded2 >> 4 ) ;
byte2 = ( ( encoded2 & 15 ) << 4 ) | ( encoded3 >> 2 ) ;
byte3 = ( ( encoded3 & 3 ) << 6 ) | encoded4 ;
if ( encoded3 === 64 ) {
output += String . fromCharCode ( byte1 ) ;
} else if ( encoded4 === 64 || encoded4 === - 1 ) {
output += String . fromCharCode ( byte1 , byte2 ) ;
} else {
output += String . fromCharCode ( byte1 , byte2 , byte3 ) ;
}
}
return output ;
} ;
exports . getBounds = function ( node ) {
if ( node . getBoundingClientRect ) {
var clientRect = node . getBoundingClientRect ( ) ;
var width = node . offsetWidth == null ? clientRect . width : node . offsetWidth ;
return {
top : clientRect . top ,
bottom : clientRect . bottom || ( clientRect . top + clientRect . height ) ,
right : clientRect . left + width ,
left : clientRect . left ,
width : width ,
height : node . offsetHeight == null ? clientRect . height : node . offsetHeight
} ;
}
return { } ;
} ;
exports . offsetBounds = function ( node ) {
var parent = node . offsetParent ? exports . offsetBounds ( node . offsetParent ) : { top : 0 , left : 0 } ;
return {
top : node . offsetTop + parent . top ,
bottom : node . offsetTop + node . offsetHeight + parent . top ,
right : node . offsetLeft + parent . left + node . offsetWidth ,
left : node . offsetLeft + parent . left ,
width : node . offsetWidth ,
height : node . offsetHeight
} ;
} ;
exports . parseBackgrounds = function ( backgroundImage ) {
var whitespace = ' \r\n\t' ,
method , definition , prefix , prefix _i , block , results = [ ] ,
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 ,
image : null
} ) ;
}
args = [ ] ;
method = prefix = definition = block = '' ;
} ;
args = [ ] ;
method = prefix = definition = block = '' ;
backgroundImage . split ( "" ) . forEach ( function ( c ) {
if ( mode === 0 && whitespace . indexOf ( c ) > - 1 ) {
return ;
}
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 ;
return ;
} else {
numParen ++ ;
}
break ;
case ')' :
if ( quote ) {
break ;
} else if ( mode === 1 ) {
if ( numParen === 0 ) {
mode = 0 ;
block += c ;
appendResult ( ) ;
return ;
} else {
numParen -- ;
}
}
break ;
case ',' :
if ( quote ) {
break ;
} else if ( mode === 0 ) {
appendResult ( ) ;
return ;
} else if ( mode === 1 ) {
if ( numParen === 0 && ! method . match ( /^url$/i ) ) {
args . push ( definition ) ;
definition = '' ;
block += c ;
return ;
}
}
break ;
}
block += c ;
if ( mode === 0 ) {
method += c ;
} else {
definition += c ;
}
} ) ;
appendResult ( ) ;
return results ;
} ;
} , { } ] , 27 : [ function ( require , module , exports ) {
var GradientContainer = require ( './gradientcontainer' ) ;
function WebkitGradientContainer ( imageData ) {
GradientContainer . apply ( this , arguments ) ;
this . type = imageData . args [ 0 ] === "linear" ? GradientContainer . TYPES . LINEAR : GradientContainer . TYPES . RADIAL ;
}
WebkitGradientContainer . prototype = Object . create ( GradientContainer . prototype ) ;
module . exports = WebkitGradientContainer ;
} , { "./gradientcontainer" : 9 } ] , 28 : [ function ( require , module , exports ) {
function XHR ( url ) {
return new Promise ( function ( resolve , reject ) {
var xhr = new XMLHttpRequest ( ) ;
xhr . open ( 'GET' , url ) ;
xhr . onload = function ( ) {
if ( xhr . status === 200 ) {
resolve ( xhr . responseText ) ;
} else {
reject ( new Error ( xhr . statusText ) ) ;
}
} ;
xhr . onerror = function ( ) {
reject ( new Error ( "Network Error" ) ) ;
} ;
xhr . send ( ) ;
} ) ;
}
module . exports = XHR ;
} , { } ] } , { } , [ 4 ] ) ( 4 )
} ) ;