2013-09-07 22:24:41 +04:00
/ *
2014-01-19 20:04:27 +04:00
html2canvas 0.5 . 0 - rc1 < http : //html2canvas.hertzen.com>
Copyright ( c ) 2014 Niklas von Hertzen
2013-09-07 22:24:41 +04:00
Released under MIT License
* /
( function ( window , document , undefined ) {
2014-01-19 20:04:27 +04:00
window . html2canvas = function ( nodeList , options ) {
2014-01-21 23:41:00 +04:00
options = options || { } ;
if ( options . logging ) {
window . html2canvas . logging = true ;
window . html2canvas . start = Date . now ( ) ;
}
2014-01-26 18:06:16 +04:00
createWindowClone ( document , window . innerWidth , window . innerHeight ) . then ( function ( container ) {
log ( "Document cloned" ) ;
var clonedWindow = container . contentWindow ;
var element = ( nodeList === undefined ) ? document . body : nodeList [ 0 ] ;
var node = clonedWindow . document . documentElement ;
var support = new Support ( ) ;
var imageLoader = new ImageLoader ( options , support ) ;
2013-09-07 22:24:41 +04:00
2014-01-26 18:06:16 +04:00
var renderer = new CanvasRenderer ( documentWidth ( ) , documentHeight ( ) , imageLoader ) ;
var parser = new NodeParser ( node , renderer , support , imageLoader , options ) ;
2014-01-26 20:10:04 +04:00
window . console . log ( parser ) ;
2014-01-26 18:06:16 +04:00
} ) ;
2013-09-07 22:24:41 +04:00
} ;
2014-01-21 23:41:00 +04:00
function documentWidth ( ) {
return Math . max (
Math . max ( document . body . scrollWidth , document . documentElement . scrollWidth ) ,
Math . max ( document . body . offsetWidth , document . documentElement . offsetWidth ) ,
Math . max ( document . body . clientWidth , document . documentElement . clientWidth )
) ;
}
function documentHeight ( ) {
return Math . max (
Math . max ( document . body . scrollHeight , document . documentElement . scrollHeight ) ,
Math . max ( document . body . offsetHeight , document . documentElement . offsetHeight ) ,
Math . max ( document . body . clientHeight , document . documentElement . clientHeight )
) ;
}
2014-01-19 20:04:27 +04:00
function createWindowClone ( ownerDocument , width , height ) {
var documentElement = ownerDocument . documentElement . cloneNode ( true ) ,
container = ownerDocument . createElement ( "iframe" ) ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
container . style . display = "hidden" ;
container . style . position = "absolute" ;
container . style . width = width + "px" ;
container . style . height = height + "px" ;
ownerDocument . body . appendChild ( container ) ;
2013-09-07 22:24:41 +04:00
2014-01-26 18:06:16 +04:00
return new Promise ( function ( resolve ) {
var loadedTimer = function ( ) {
/ * 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 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 ,
before a certain time has passed
* /
if ( container . contentWindow . getComputedStyle ( div , null ) [ 'backgroundImage' ] !== "none" ) {
documentClone . body . removeChild ( div ) ;
documentClone . body . removeChild ( style ) ;
resolve ( container ) ;
} else {
window . setTimeout ( loadedTimer , 10 ) ;
}
} ;
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
* /
documentClone . open ( ) ;
documentClone . write ( "" ) ;
documentClone . close ( ) ;
documentClone . replaceChild ( documentClone . adoptNode ( documentElement ) , documentClone . documentElement ) ;
2014-01-26 20:10:04 +04:00
container . contentWindow . scrollTo ( window . scrollX , window . scrollY ) ;
2014-01-26 18:06:16 +04:00
var div = documentClone . createElement ( "div" ) ;
div . className = "html2canvas-ready-test" ;
documentClone . body . appendChild ( div ) ;
var style = documentClone . createElement ( "style" ) ;
style . innerHTML = "body div.html2canvas-ready-test { background-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); }" ;
documentClone . body . appendChild ( style ) ;
loadedTimer ( ) ;
} ) ;
2013-09-07 22:24:41 +04:00
}
2014-01-21 23:41:00 +04:00
function NodeParser ( element , renderer , support , imageLoader , options ) {
2014-01-19 20:04:27 +04:00
this . renderer = renderer ;
this . options = options ;
this . range = null ;
2014-01-21 23:41:00 +04:00
this . support = support ;
2014-01-19 20:04:27 +04:00
this . stack = new StackingContext ( true , 1 , element . ownerDocument , null ) ;
var parent = new NodeContainer ( element , null ) ;
2014-01-21 23:41:00 +04:00
parent . visibile = parent . isElementVisible ( ) ;
2014-01-19 20:04:27 +04:00
this . nodes = [ parent ] . concat ( this . getChildren ( parent ) ) . filter ( function ( container ) {
return container . visible = container . isElementVisible ( ) ;
} ) ;
2014-01-21 23:41:00 +04:00
this . images = imageLoader . fetch ( this . nodes . filter ( isElement ) ) ;
2014-01-19 20:04:27 +04:00
this . createStackingContexts ( ) ;
this . sortStackingContexts ( this . stack ) ;
2014-01-21 23:41:00 +04:00
this . images . ready . then ( bind ( function ( ) {
log ( "Images loaded, starting parsing" ) ;
this . parse ( this . stack ) ;
options . onrendered ( renderer . canvas ) ;
} , this ) ) ;
2013-09-07 22:24:41 +04:00
}
2014-01-19 20:04:27 +04:00
NodeParser . prototype . getChildren = function ( parentContainer ) {
return flatten ( [ ] . filter . call ( parentContainer . node . childNodes , renderableNode ) . map ( function ( node ) {
2014-01-21 23:41:00 +04:00
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 ? ( container [ 0 ] . isElementVisible ( ) ? container . concat ( this . getChildren ( container [ 0 ] ) ) : [ ] ) : container ;
2014-01-19 20:04:27 +04:00
} , this ) ) ;
2013-09-07 22:24:41 +04:00
} ;
2014-01-19 20:04:27 +04:00
NodeParser . prototype . newStackingContext = function ( container , hasOwnStacking ) {
var stack = new StackingContext ( hasOwnStacking , container . cssFloat ( 'opacity' ) , container . node , container . parent ) ;
2014-01-21 23:41:00 +04:00
stack . visible = container . visible ;
2014-01-19 20:04:27 +04:00
var parentStack = stack . getParentStack ( this ) ;
parentStack . contexts . push ( stack ) ;
container . stack = stack ;
2013-09-07 22:24:41 +04:00
} ;
2014-01-19 20:04:27 +04:00
NodeParser . prototype . createStackingContexts = function ( ) {
this . nodes . forEach ( function ( container ) {
if ( isElement ( container ) && ( this . isRootElement ( container ) || hasOpacity ( container ) || isPositionedForStacking ( container ) || this . isBodyWithTransparentRoot ( container ) ) ) {
this . newStackingContext ( container , true ) ;
} else if ( isElement ( container ) && ( isPositioned ( container ) ) ) {
this . newStackingContext ( container , false ) ;
} else {
container . assignStack ( container . parent . stack ) ;
}
} , this ) ;
2013-11-12 21:35:28 +04:00
} ;
2014-01-19 20:04:27 +04:00
NodeParser . prototype . isBodyWithTransparentRoot = function ( container ) {
return container . node . nodeName === "BODY" && this . renderer . isTransparent ( container . parent . css ( 'backgroundColor' ) ) ;
2013-12-23 18:07:49 +04:00
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
NodeParser . prototype . isRootElement = function ( container ) {
return container . node . nodeName === "HTML" ;
2013-09-07 22:24:41 +04:00
} ;
2014-01-19 20:04:27 +04:00
NodeParser . prototype . sortStackingContexts = function ( stack ) {
stack . contexts . sort ( zIndexSort ) ;
stack . contexts . forEach ( this . sortStackingContexts , this ) ;
2013-09-07 22:24:41 +04:00
} ;
2014-01-19 20:04:27 +04:00
NodeParser . prototype . parseBounds = function ( nodeContainer ) {
return nodeContainer . bounds = this . getBounds ( nodeContainer . node ) ;
2013-09-07 22:24:41 +04:00
} ;
2014-01-19 20:04:27 +04:00
NodeParser . prototype . getBounds = function ( node ) {
if ( node . getBoundingClientRect ) {
var clientRect = node . getBoundingClientRect ( ) ;
return {
top : clientRect . top ,
bottom : clientRect . bottom || ( clientRect . top + clientRect . height ) ,
left : clientRect . left ,
width : node . offsetWidth ,
height : node . offsetHeight
} ;
}
return { } ;
2013-09-07 22:24:41 +04:00
} ;
2013-12-23 18:07:49 +04:00
2014-01-19 20:04:27 +04:00
NodeParser . prototype . parseTextBounds = function ( container ) {
return function ( text , index , textList ) {
if ( container . parent . css ( "textDecoration" ) !== "none" || text . trim ( ) . length !== 0 ) {
var offset = textList . slice ( 0 , index ) . join ( "" ) . length ;
if ( this . support . rangeBounds ) {
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 . node = replacementNode ;
return bounds ;
2013-09-07 22:24:41 +04:00
}
2014-01-19 20:04:27 +04:00
}
2013-09-07 22:24:41 +04:00
} ;
2014-01-19 20:04:27 +04:00
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
NodeParser . prototype . getWrapperBounds = function ( node ) {
var wrapper = node . ownerDocument . createElement ( 'wrapper' ) ;
var parent = node . parentNode ,
backupText = node . cloneNode ( true ) ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
wrapper . appendChild ( node . cloneNode ( true ) ) ;
parent . replaceChild ( wrapper , node ) ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
var bounds = this . getBounds ( wrapper ) ;
parent . replaceChild ( backupText , wrapper ) ;
return bounds ;
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
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 ( ) ;
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function negativeZIndex ( container ) {
return container . cssInt ( "zIndex" ) < 0 ;
2013-09-07 22:24:41 +04:00
}
2014-01-19 20:04:27 +04:00
function positiveZIndex ( container ) {
return container . cssInt ( "zIndex" ) > 0 ;
}
Speed & accuracy improvements to pseudo element rendering.
Previously, pseudo elements would be processed as they were found in the DOM tree, which was
an expensive operation as each element's computed :before and :after style was checked for
'content' styles.
This commit traverses the user's stylesheets for :before and :after selectors, gathers the classes
affected, selects all elements that likely have a pseudo element present, then checks computed style.
If there is actually an element present, it is created but *not* appended to the DOM until after
all elements have been processed.
After all elements have been found and created, they are added to the DOM in a single batch, and the original
pseudo elements are hidden in a single batch. This prevents the layout invalidation / relayout loop that was
occuring previously, and in my tests speeds parsing by as much as 50% or more, depending on how many
pseudo elements your page uses.
Additionally, this commit contains a bugfix to the handling of ":before" pseudo elements; the browser effectively
inserts them as the first child of the element, not before the element. This fixes a few rendering inconsistencies
and complicated pages look almost perfect in my tests.
2013-09-18 09:19:39 +04:00
2014-01-19 20:04:27 +04:00
function zIndex0 ( container ) {
return container . cssInt ( "zIndex" ) === 0 ;
}
Speed & accuracy improvements to pseudo element rendering.
Previously, pseudo elements would be processed as they were found in the DOM tree, which was
an expensive operation as each element's computed :before and :after style was checked for
'content' styles.
This commit traverses the user's stylesheets for :before and :after selectors, gathers the classes
affected, selects all elements that likely have a pseudo element present, then checks computed style.
If there is actually an element present, it is created but *not* appended to the DOM until after
all elements have been processed.
After all elements have been found and created, they are added to the DOM in a single batch, and the original
pseudo elements are hidden in a single batch. This prevents the layout invalidation / relayout loop that was
occuring previously, and in my tests speeds parsing by as much as 50% or more, depending on how many
pseudo elements your page uses.
Additionally, this commit contains a bugfix to the handling of ":before" pseudo elements; the browser effectively
inserts them as the first child of the element, not before the element. This fixes a few rendering inconsistencies
and complicated pages look almost perfect in my tests.
2013-09-18 09:19:39 +04:00
2014-01-19 20:04:27 +04:00
function inlineLevel ( container ) {
return [ "inline" , "inline-block" , "inline-table" ] . indexOf ( container . css ( "display" ) ) !== - 1 ;
}
2013-12-23 18:07:49 +04:00
2014-01-19 20:04:27 +04:00
function isStackingContext ( container ) {
return ( container instanceof StackingContext ) ;
}
2013-09-18 05:54:20 +04:00
2014-01-19 20:04:27 +04:00
function hasText ( container ) {
return container . node . data . trim ( ) . length > 0 ;
}
Speed & accuracy improvements to pseudo element rendering.
Previously, pseudo elements would be processed as they were found in the DOM tree, which was
an expensive operation as each element's computed :before and :after style was checked for
'content' styles.
This commit traverses the user's stylesheets for :before and :after selectors, gathers the classes
affected, selects all elements that likely have a pseudo element present, then checks computed style.
If there is actually an element present, it is created but *not* appended to the DOM until after
all elements have been processed.
After all elements have been found and created, they are added to the DOM in a single batch, and the original
pseudo elements are hidden in a single batch. This prevents the layout invalidation / relayout loop that was
occuring previously, and in my tests speeds parsing by as much as 50% or more, depending on how many
pseudo elements your page uses.
Additionally, this commit contains a bugfix to the handling of ":before" pseudo elements; the browser effectively
inserts them as the first child of the element, not before the element. This fixes a few rendering inconsistencies
and complicated pages look almost perfect in my tests.
2013-09-18 09:19:39 +04:00
2014-01-19 20:04:27 +04:00
function noLetterSpacing ( container ) {
return ( /^(normal|none|0px)$/ . test ( container . parent . css ( "letterSpacing" ) ) ) ;
}
2013-09-18 05:54:20 +04:00
2014-01-19 20:04:27 +04:00
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).
2014-01-21 23:41:00 +04:00
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.
2014-01-19 20:04:27 +04:00
var nonPositionedFloats = descendantElements . filter ( not ( isPositioned ) ) . filter ( isFloating ) ; // 4. the non-positioned floats.
2014-01-21 23:41:00 +04:00
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.
2014-01-19 20:04:27 +04:00
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).
var rendered = [ ] ;
negativeZindex . concat ( nonInlineNonPositionedDescendants ) . concat ( nonPositionedFloats )
. concat ( inFlow ) . concat ( stackLevel0 ) . concat ( text ) . concat ( positiveZindex ) . forEach ( function ( container ) {
this . paint ( container ) ;
if ( rendered . indexOf ( container . node ) !== - 1 ) {
2014-01-26 18:06:16 +04:00
log ( container , container . node ) ;
2014-01-19 20:04:27 +04:00
throw new Error ( "rendering twice" ) ;
Speed & accuracy improvements to pseudo element rendering.
Previously, pseudo elements would be processed as they were found in the DOM tree, which was
an expensive operation as each element's computed :before and :after style was checked for
'content' styles.
This commit traverses the user's stylesheets for :before and :after selectors, gathers the classes
affected, selects all elements that likely have a pseudo element present, then checks computed style.
If there is actually an element present, it is created but *not* appended to the DOM until after
all elements have been processed.
After all elements have been found and created, they are added to the DOM in a single batch, and the original
pseudo elements are hidden in a single batch. This prevents the layout invalidation / relayout loop that was
occuring previously, and in my tests speeds parsing by as much as 50% or more, depending on how many
pseudo elements your page uses.
Additionally, this commit contains a bugfix to the handling of ":before" pseudo elements; the browser effectively
inserts them as the first child of the element, not before the element. This fixes a few rendering inconsistencies
and complicated pages look almost perfect in my tests.
2013-09-18 09:19:39 +04:00
}
2014-01-19 20:04:27 +04:00
rendered . push ( container . node ) ;
Speed & accuracy improvements to pseudo element rendering.
Previously, pseudo elements would be processed as they were found in the DOM tree, which was
an expensive operation as each element's computed :before and :after style was checked for
'content' styles.
This commit traverses the user's stylesheets for :before and :after selectors, gathers the classes
affected, selects all elements that likely have a pseudo element present, then checks computed style.
If there is actually an element present, it is created but *not* appended to the DOM until after
all elements have been processed.
After all elements have been found and created, they are added to the DOM in a single batch, and the original
pseudo elements are hidden in a single batch. This prevents the layout invalidation / relayout loop that was
occuring previously, and in my tests speeds parsing by as much as 50% or more, depending on how many
pseudo elements your page uses.
Additionally, this commit contains a bugfix to the handling of ":before" pseudo elements; the browser effectively
inserts them as the first child of the element, not before the element. This fixes a few rendering inconsistencies
and complicated pages look almost perfect in my tests.
2013-09-18 09:19:39 +04:00
2014-01-19 20:04:27 +04:00
if ( isStackingContext ( container ) ) {
this . parse ( container ) ;
Speed & accuracy improvements to pseudo element rendering.
Previously, pseudo elements would be processed as they were found in the DOM tree, which was
an expensive operation as each element's computed :before and :after style was checked for
'content' styles.
This commit traverses the user's stylesheets for :before and :after selectors, gathers the classes
affected, selects all elements that likely have a pseudo element present, then checks computed style.
If there is actually an element present, it is created but *not* appended to the DOM until after
all elements have been processed.
After all elements have been found and created, they are added to the DOM in a single batch, and the original
pseudo elements are hidden in a single batch. This prevents the layout invalidation / relayout loop that was
occuring previously, and in my tests speeds parsing by as much as 50% or more, depending on how many
pseudo elements your page uses.
Additionally, this commit contains a bugfix to the handling of ":before" pseudo elements; the browser effectively
inserts them as the first child of the element, not before the element. This fixes a few rendering inconsistencies
and complicated pages look almost perfect in my tests.
2013-09-18 09:19:39 +04:00
}
2014-01-19 20:04:27 +04:00
} , this ) ;
} ;
Speed & accuracy improvements to pseudo element rendering.
Previously, pseudo elements would be processed as they were found in the DOM tree, which was
an expensive operation as each element's computed :before and :after style was checked for
'content' styles.
This commit traverses the user's stylesheets for :before and :after selectors, gathers the classes
affected, selects all elements that likely have a pseudo element present, then checks computed style.
If there is actually an element present, it is created but *not* appended to the DOM until after
all elements have been processed.
After all elements have been found and created, they are added to the DOM in a single batch, and the original
pseudo elements are hidden in a single batch. This prevents the layout invalidation / relayout loop that was
occuring previously, and in my tests speeds parsing by as much as 50% or more, depending on how many
pseudo elements your page uses.
Additionally, this commit contains a bugfix to the handling of ":before" pseudo elements; the browser effectively
inserts them as the first child of the element, not before the element. This fixes a few rendering inconsistencies
and complicated pages look almost perfect in my tests.
2013-09-18 09:19:39 +04:00
2014-01-19 20:04:27 +04:00
NodeParser . prototype . paint = function ( container ) {
2014-01-26 20:10:04 +04:00
try {
if ( isTextNode ( container ) ) {
this . paintText ( container ) ;
} else {
this . paintNode ( container ) ;
}
} catch ( e ) {
log ( e ) ;
2013-09-07 22:24:41 +04:00
}
2014-01-19 20:04:27 +04:00
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
NodeParser . prototype . paintNode = function ( container ) {
if ( isStackingContext ( container ) ) {
this . renderer . setOpacity ( container . opacity ) ;
2013-09-07 22:24:41 +04:00
}
2014-01-19 20:04:27 +04:00
var bounds = this . parseBounds ( container ) ;
var borderData = this . parseBorders ( container ) ;
this . renderer . clip ( borderData . clip , function ( ) {
this . renderer . renderBackground ( container , bounds ) ;
} , this ) ;
this . renderer . renderBorders ( borderData . borders ) ;
2014-01-26 20:10:04 +04:00
switch ( container . node . nodeName ) {
case "IMG" :
var imageContainer = this . images . get ( container . node . src ) ;
if ( imageContainer ) {
this . renderer . renderImage ( container , bounds , borderData , imageContainer . image ) ;
} else {
log ( "Error loading <img>" , container . node . src ) ;
}
break ;
}
2014-01-19 20:04:27 +04:00
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
NodeParser . prototype . paintText = function ( container ) {
container . applyTextTransform ( ) ;
var textList = container . node . data . split ( ! this . options . letterRendering || noLetterSpacing ( container ) ? /(\b| )/ : "" ) ;
var weight = container . parent . fontWeight ( ) ;
var size = container . parent . css ( 'fontSize' ) ;
var family = container . parent . css ( 'fontFamily' ) ;
this . renderer . font ( container . parent . css ( 'color' ) , container . parent . css ( 'fontStyle' ) , container . parent . css ( 'fontVariant' ) , weight , size , family ) ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
textList . map ( this . parseTextBounds ( container ) , this ) . forEach ( function ( bounds , index ) {
2013-09-07 22:24:41 +04:00
if ( bounds ) {
2014-01-19 20:04:27 +04:00
this . renderer . text ( textList [ index ] , bounds . left , bounds . bottom ) ;
// renderTextDecoration(ctx, textDecoration, bounds, metrics, color);
2013-09-07 22:24:41 +04:00
}
2014-01-19 20:04:27 +04:00
/ * v a r b o u n d s = g e t T e x t B o u n d s ( s t a t e , t e x t , t e x t D e c o r a t i o n , ( i n d e x < t e x t L i s t . l e n g t h - 1 ) , s t a c k . t r a n s f o r m . m a t r i x ) ;
if ( bounds ) {
drawText ( text , bounds . left , bounds . bottom , ctx ) ;
renderTextDecoration ( ctx , textDecoration , bounds , metrics , color ) ;
} * /
} , this ) ;
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
NodeParser . prototype . parseBorders = function ( container ) {
var nodeBounds = container . bounds ;
var radius = getBorderRadiusData ( container ) ;
var borders = [ "Top" , "Right" , "Bottom" , "Left" ] . map ( function ( side ) {
return {
width : container . cssInt ( 'border' + side + 'Width' ) ,
color : container . css ( 'border' + side + 'Color' ) ,
args : null
} ;
} ) ;
var borderPoints = calculateCurvePoints ( nodeBounds , radius , borders ) ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
return {
clip : this . parseBackgroundClip ( container , borderPoints , borders , radius , nodeBounds ) ,
borders : 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 ;
} )
} ;
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
NodeParser . prototype . parseBackgroundClip = function ( container , borderPoints , borders , radius , bounds ) {
var backgroundClip = container . css ( 'backgroundClip' ) ,
borderArgs = [ ] ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
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 ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
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 ;
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
return borderArgs ;
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
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 ) ;
2013-09-07 22:24:41 +04:00
} else {
2014-01-19 20:04:27 +04:00
borderArgs . push ( [ "line" , x , y ] ) ;
2013-09-07 22:24:41 +04:00
}
2014-01-19 20:04:27 +04:00
if ( radius2 [ 0 ] > 0 || radius2 [ 1 ] > 0 ) {
borderArgs . push ( [ "line" , corner2 [ 0 ] . start . x , corner2 [ 0 ] . start . y ] ) ;
2013-09-07 22:24:41 +04:00
}
2014-01-19 20:04:27 +04:00
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function getBorderRadiusData ( container ) {
2013-09-07 22:24:41 +04:00
return [ "TopLeft" , "TopRight" , "BottomRight" , "BottomLeft" ] . map ( function ( side ) {
2014-01-19 20:04:27 +04:00
var value = container . css ( 'border' + side + 'Radius' ) ;
var arr = value . split ( " " ) ;
if ( arr . length <= 1 ) {
arr [ 1 ] = arr [ 0 ] ;
}
return arr . map ( asInt ) ;
2013-09-07 22:24:41 +04:00
} ) ;
2014-01-19 20:04:27 +04:00
}
function asInt ( value ) {
return parseInt ( value , 10 ) ;
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function getCurvePoints ( x , y , r1 , r2 ) {
2013-09-18 05:54:20 +04:00
var kappa = 4 * ( ( Math . sqrt ( 2 ) - 1 ) / 3 ) ;
var ox = ( r1 ) * kappa , // control point offset horizontal
2014-01-19 20:04:27 +04:00
oy = ( r2 ) * kappa , // control point offset vertical
xm = x + r1 , // x-middle
ym = y + r2 ; // y-middle
2013-09-18 05:54:20 +04:00
return {
2014-01-19 20:04:27 +04:00
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 } )
2013-09-07 22:24:41 +04:00
} ;
2014-01-19 20:04:27 +04:00
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function calculateCurvePoints ( bounds , borderRadius , borders ) {
var x = bounds . left ,
y = bounds . top ,
width = bounds . width ,
height = bounds . height ,
tlh = borderRadius [ 0 ] [ 0 ] ,
tlv = borderRadius [ 0 ] [ 1 ] ,
trh = borderRadius [ 1 ] [ 0 ] ,
trv = borderRadius [ 1 ] [ 1 ] ,
brh = borderRadius [ 2 ] [ 0 ] ,
brv = borderRadius [ 2 ] [ 1 ] ,
blh = borderRadius [ 3 ] [ 0 ] ,
blv = borderRadius [ 3 ] [ 1 ] ;
var topWidth = width - trh ,
rightHeight = height - brv ,
bottomWidth = width - brh ,
leftHeight = height - blv ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
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 ) , Math . max ( 0 , 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 ) , Math . max ( 0 , blv - borders [ 2 ] . width ) ) . bottomLeft . subdivide ( 0.5 )
} ;
}
function bezierCurve ( start , startControl , endControl , end ) {
2013-09-07 22:24:41 +04:00
var lerp = function ( a , b , t ) {
2014-01-19 20:04:27 +04:00
return {
x : a . x + ( b . x - a . x ) * t ,
y : a . y + ( b . y - a . y ) * t
} ;
2013-09-07 22:24:41 +04:00
} ;
return {
2014-01-19 20:04:27 +04:00
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 ] ) ;
}
2013-09-07 22:24:41 +04:00
} ;
2014-01-19 20:04:27 +04:00
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function drawSide ( borderData , radius1 , radius2 , outer1 , inner1 , outer2 , inner2 ) {
2013-09-07 22:24:41 +04:00
var borderArgs = [ ] ;
if ( radius1 [ 0 ] > 0 || radius1 [ 1 ] > 0 ) {
2014-01-19 20:04:27 +04:00
borderArgs . push ( [ "line" , outer1 [ 1 ] . start . x , outer1 [ 1 ] . start . y ] ) ;
outer1 [ 1 ] . curveTo ( borderArgs ) ;
2013-09-07 22:24:41 +04:00
} else {
2014-01-19 20:04:27 +04:00
borderArgs . push ( [ "line" , borderData . c1 [ 0 ] , borderData . c1 [ 1 ] ] ) ;
2013-09-07 22:24:41 +04:00
}
if ( radius2 [ 0 ] > 0 || radius2 [ 1 ] > 0 ) {
2014-01-19 20:04:27 +04:00
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 ) ;
2013-09-07 22:24:41 +04:00
} else {
2014-01-19 20:04:27 +04:00
borderArgs . push ( [ "line" , borderData . c2 [ 0 ] , borderData . c2 [ 1 ] ] ) ;
borderArgs . push ( [ "line" , borderData . c3 [ 0 ] , borderData . c3 [ 1 ] ] ) ;
2013-09-07 22:24:41 +04:00
}
if ( radius1 [ 0 ] > 0 || radius1 [ 1 ] > 0 ) {
2014-01-19 20:04:27 +04:00
borderArgs . push ( [ "line" , inner1 [ 1 ] . end . x , inner1 [ 1 ] . end . y ] ) ;
inner1 [ 1 ] . curveToReversed ( borderArgs ) ;
2013-09-07 22:24:41 +04:00
} else {
2014-01-19 20:04:27 +04:00
borderArgs . push ( [ "line" , borderData . c4 [ 0 ] , borderData . c4 [ 1 ] ] ) ;
2013-09-07 22:24:41 +04:00
}
return borderArgs ;
2014-01-19 20:04:27 +04:00
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function nonIgnoredElement ( nodeContainer ) {
return ( nodeContainer . node . nodeType !== Node . ELEMENT _NODE || [ "SCRIPT" , "HEAD" , "TITLE" , "OBJECT" , "BR" ] . indexOf ( nodeContainer . node . nodeName ) === - 1 ) ;
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function flatten ( arrays ) {
return [ ] . concat . apply ( [ ] , arrays ) ;
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function renderableNode ( node ) {
return ( node . nodeType === Node . TEXT _NODE || node . nodeType === Node . ELEMENT _NODE ) ;
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function isPositionedForStacking ( container ) {
var position = container . css ( "position" ) ;
var zIndex = ( position === "absolute" || position === "relative" ) ? container . css ( "zIndex" ) : "auto" ;
return zIndex !== "auto" ;
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function isPositioned ( container ) {
return container . css ( "position" ) !== "static" ;
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function isFloating ( container ) {
return container . css ( "float" ) !== "none" ;
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function not ( callback ) {
var context = this ;
return function ( ) {
return ! callback . apply ( context , arguments ) ;
2013-09-07 22:24:41 +04:00
} ;
2014-01-19 20:04:27 +04:00
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function isElement ( container ) {
return container . node . nodeType === Node . ELEMENT _NODE ;
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function isTextNode ( container ) {
return container . node . nodeType === Node . TEXT _NODE ;
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function zIndexSort ( a , b ) {
return a . cssInt ( "zIndex" ) - b . cssInt ( "zIndex" ) ;
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
function hasOpacity ( container ) {
return container . css ( "opacity" ) < 1 ;
}
2013-09-07 22:24:41 +04:00
2014-01-21 23:41:00 +04:00
function bind ( callback , context ) {
2014-01-19 20:04:27 +04:00
return function ( ) {
2014-01-21 23:41:00 +04:00
return callback . apply ( context , arguments ) ;
2014-01-19 20:04:27 +04:00
} ;
}
2013-09-18 05:54:20 +04:00
2014-01-21 23:41:00 +04:00
function ImageContainer ( src , cors ) {
this . src = src ;
this . image = new Image ( ) ;
var image = this . image ;
this . promise = new Promise ( function ( resolve , reject ) {
image . onload = resolve ;
image . onerror = reject ;
if ( cors ) {
image . crossOrigin = "anonymous" ;
}
image . src = src ;
} ) ;
}
function ImageLoader ( options , support ) {
this . link = null ;
this . options = options ;
this . support = support ;
this . origin = window . location . protocol + window . location . host ;
}
2014-01-26 20:10:04 +04:00
ImageLoader . prototype . findImages = function ( nodes ) {
var images = [ ] ;
nodes . filter ( isImage ) . map ( src ) . forEach ( this . addImage ( images , this . loadImage ) , this ) ;
return images ;
} ;
ImageLoader . prototype . findBackgroundImage = function ( images , container ) {
container . parseBackgroundImages ( ) . filter ( this . isImageBackground ) . map ( this . getBackgroundUrl ) . forEach ( this . addImage ( images , this . loadImage ) , this ) ;
return images ;
} ;
ImageLoader . prototype . addImage = function ( images , callback ) {
return function ( newImage ) {
if ( ! this . imageExists ( images , newImage ) ) {
images . splice ( 0 , 0 , callback . apply ( this , arguments ) ) ;
}
} ;
2014-01-21 23:41:00 +04:00
} ;
ImageLoader . prototype . getBackgroundUrl = function ( imageData ) {
return imageData . args [ 0 ] ;
} ;
ImageLoader . prototype . isImageBackground = function ( imageData ) {
return imageData . method === "url" ;
} ;
ImageLoader . prototype . loadImage = function ( src ) {
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 ) {
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 ) ;
} else {
return new DummyImageContainer ( src ) ;
}
} ;
2014-01-26 20:10:04 +04:00
ImageLoader . prototype . imageExists = function ( images , src ) {
return images . some ( function ( image ) {
return image . src === src ;
} ) ;
2014-01-21 23:41:00 +04:00
} ;
ImageLoader . prototype . isSameOrigin = 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/
var origin = link . protocol + link . host ;
return ( origin === this . origin ) ;
} ;
ImageLoader . prototype . getPromise = function ( container ) {
return container . promise ;
} ;
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 ) {
2014-01-26 20:10:04 +04:00
this . images = nodes . reduce ( bind ( this . findBackgroundImage , this ) , this . findImages ( nodes ) ) ;
2014-01-21 23:41:00 +04:00
this . ready = Promise . all ( this . images . map ( this . getPromise ) ) ;
return this ;
} ;
2014-01-26 20:10:04 +04:00
function isImage ( container ) {
return container . node . nodeName === "IMG" ;
}
function src ( container ) {
return container . node . src ;
}
2014-01-21 23:41:00 +04:00
function log ( ) {
if ( window . html2canvas . logging && window . console && window . console . log ) {
window . console . log . apply ( window . console , [ ( Date . now ( ) - window . html2canvas . start ) + "ms" , "html2canvas:" ] . concat ( [ ] . slice . call ( arguments , 0 ) ) ) ;
}
}
function NodeContainer ( node , parent ) {
this . node = node ;
this . parent = parent ;
this . stack = null ;
this . bounds = null ;
this . visible = null ;
this . computedStyles = null ;
this . styles = { } ;
this . backgroundImages = null ;
}
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" ) ) ;
} ;
NodeContainer . prototype . css = function ( attribute ) {
if ( ! this . computedStyles ) {
this . computedStyles = this . node . ownerDocument . defaultView . getComputedStyle ( this . node , null ) ;
}
return this . styles [ attribute ] || ( this . styles [ attribute ] = this . computedStyles [ attribute ] ) ;
} ;
NodeContainer . prototype . cssInt = function ( attribute ) {
var value = parseInt ( this . css ( attribute ) , 10 ) ;
return ( Number . isNaN ( value ) ) ? 0 : value ; // borders in old IE are throwing 'medium' for demo.html
} ;
NodeContainer . prototype . cssFloat = function ( attribute ) {
var value = parseFloat ( this . css ( attribute ) ) ;
return ( Number . 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 . parseBackgroundImages = function ( ) {
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 = '' ;
this . css ( "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 this . backgroundImages || ( this . backgroundImages = results ) ;
} ;
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 ] , 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 ] ;
} ;
function isPercentage ( value ) {
return value . toString ( ) . indexOf ( "%" ) !== - 1 ;
}
2014-01-26 18:06:16 +04:00
/ *
Copyright ( c ) 2013 Yehuda Katz , Tom Dale , and contributors
Permission is hereby granted , free of charge , to any person obtaining a copy of
this software and associated documentation files ( the "Software" ) , to deal in
the Software without restriction , including without limitation the rights to
use , copy , modify , merge , publish , distribute , sublicense , and / or sell copies
of the Software , and to permit persons to whom the Software is furnished to do
so , subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software .
THE SOFTWARE IS PROVIDED "AS IS" , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE .
* /
! function ( ) { var a , b , c , d ; ! function ( ) { var e = { } , f = { } ; a = function ( a , b , c ) { e [ a ] = { deps : b , callback : c } } , d = c = b = function ( a ) { function c ( b ) { if ( "." !== b . charAt ( 0 ) ) return b ; for ( var c = b . split ( "/" ) , d = a . split ( "/" ) . slice ( 0 , - 1 ) , e = 0 , f = c . length ; f > e ; e ++ ) { var g = c [ e ] ; if ( ".." === g ) d . pop ( ) ; else { if ( "." === g ) continue ; d . push ( g ) } } return d . join ( "/" ) } if ( d . _eak _seen = e , f [ a ] ) return f [ a ] ; if ( f [ a ] = { } , ! e [ a ] ) throw new Error ( "Could not find module " + a ) ; for ( var g , h = e [ a ] , i = h . deps , j = h . callback , k = [ ] , l = 0 , m = i . length ; m > l ; l ++ ) "exports" === i [ l ] ? k . push ( g = { } ) : k . push ( b ( c ( i [ l ] ) ) ) ; var n = j . apply ( this , k ) ; return f [ a ] = g || n } } ( ) , a ( "promise/all" , [ "./utils" , "exports" ] , function ( a , b ) { "use strict" ; function c ( a ) { var b = this ; if ( ! d ( a ) ) throw new TypeError ( "You must pass an array to all." ) ; return new b ( function ( b , c ) { function d ( a ) { return function ( b ) { f ( a , b ) } } function f ( a , c ) { h [ a ] = c , 0 === -- i && b ( h ) } var g , h = [ ] , i = a . length ; 0 === i && b ( [ ] ) ; for ( var j = 0 ; j < a . length ; j ++ ) g = a [ j ] , g && e ( g . then ) ? g . then ( d ( j ) , c ) : f ( j , g ) } ) } var d = a . isArray , e = a . isFunction ; b . all = c } ) , a ( "promise/asap" , [ "exports" ] , function ( a ) { "use strict" ; function b ( ) { return function ( ) { process . nextTick ( e ) } } function c ( ) { var a = 0 , b = new i ( e ) , c = document . createTextNode ( "" ) ; return b . observe ( c , { characterData : ! 0 } ) , function ( ) { c . data = a = ++ a % 2 } } function d ( ) { return function ( ) { j . setTimeout ( e , 1 ) } } function e ( ) { for ( var a = 0 ; a < k . length ; a ++ ) { var b = k [ a ] , c = b [ 0 ] , d = b [ 1 ] ; c ( d ) } k = [ ] } function f ( a , b ) { var c = k . push ( [ a , b ] ) ; 1 === c && g ( ) } var g , h = "undefined" != typeof window ? window : { } , i = h . MutationObserver || h . WebKitMutationObserver , j = "undefined" != typeof global ? global : this , k = [ ] ; g = "undefined" != typeof process && "[object process]" === { } . toString . call ( process ) ? b ( ) : i ? c ( ) : d ( ) , a . asap = f } ) , a ( "promise/cast" , [ "exports" ] , function ( a ) { "use strict" ; function b ( a ) { if ( a && "object" == typeof a && a . constructor === this ) return a ; var b = this ; return new b ( function ( b ) { b ( a ) } ) } a . cast = b } ) , a ( "promise/config" , [ "exports" ] , function ( a ) { "use strict" ; function b ( a , b ) { return 2 !== arguments . length ? c [ a ] : ( c [ a ] = b , void 0 ) } var c = { instrument : ! 1 } ; a . config = c , a . configure = b } ) , a ( "promise/polyfill" , [ "./promise" , "./utils" , "exports" ] , function ( a , b , c ) { "use strict" ; function d ( ) { var a = "Promise" in window && "cast" in window . Promise && "resolve" in window . Promise && "reject" in window . Promise && "all" in window . Promise && "race" in window . Promise && function ( ) { var a ; return new window . Promise ( function ( b ) { a = b } ) , f ( a ) } ( ) ; a || ( window . Promise = e ) } var e = a . Promise , f = b . isFunction ; c . polyfill = d } ) , a ( "promise/promise" , [ "./config" , "./utils" , "./cast" , "./all" , "./race" , "./resolve" , "./reject" , "./asap" , "exports" ] , function ( a , b , c , d , e , f , g , h , i ) { "use strict" ; function j ( a ) { if ( ! w ( a ) ) throw new TypeError ( "You must pass a resolver function as the first argument to the promise constructor" ) ; if ( ! ( this instanceof j ) ) throw new TypeError ( "Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function." ) ; this . _subscribers = [ ] , k ( a , this ) } function k ( a , b ) { function c ( a ) { p ( b , a ) } function d ( a ) { r ( b , a ) } try { a ( c , d ) } catch ( e ) { d ( e ) } } function l ( a , b , c , d ) { var e , f , g , h , i = w ( c ) ; if ( i ) try { e = c ( d ) , g = ! 0 } catch ( j ) { h = ! 0 , f = j } else e = d , g = ! 0 ; o ( b , e ) || ( i && g ? p ( b , e ) : h ? r ( b , f ) : a === F ? p ( b , e ) : a === G && r ( b , e ) ) } function m ( a , b , c , d ) { var e = a . _subscribers , f = e . length ; e [ f ] = b , e [ f + F ] = c , e [ f + G ] = d } function n ( a , b ) { for ( var c , d , e = a . _subscribers , f = a . _detail , g = 0 ; g < e . length ; g += 3 ) c = e [ g ] , d = e [ g + b ] , l ( b , c , d , f ) ; a . _subscribers = null } function o ( a , b ) { var c , d = null ; try { if ( a === b ) throw new TypeError ( "A promises callback cannot return that same promise." ) ; if ( v ( b ) && ( d = b . then , w ( d ) ) ) return d . call ( b , function ( d ) { return c ? ! 0 : ( c = ! 0 , b !== d ? p ( a , d ) : q ( a , d ) , void 0 ) } , function ( b ) { return c ? ! 0 : ( c = ! 0 , r ( a , b ) , void 0 ) } ) , ! 0 } catch ( e ) { return c ? ! 0 : ( r ( a , e ) , ! 0 ) } return ! 1 } function p ( a , b ) { a === b ? q ( a , b ) : o ( a , b ) || q ( a , b ) } function q ( a , b ) { a . _state === D && ( a . _state = E , a . _detail = b , u . async ( s , a ) ) } function r ( a , b ) { a . _state === D && ( a . _state = E , a . _detail = b , u . async ( t , a ) ) } function s ( a ) { n ( a , a . _state = F ) } function t ( a ) { n ( a , a . _state = G ) } var u = a . config , v = ( a . configure , b . objectOrFunction ) , w = b . isFunction , x = ( b . now , c . cast ) , y = d . all , z = e . race , A = f . resolve , B = g . reject , C = h . asap ; u . async = C ; var D = void 0 , E = 0 , F = 1 , G = 2 ; j . prototype = { constructor : j , _state : void 0 , _detail : void 0 , _subscribers : void 0 , then : function ( a , b ) { var c = this , d
2014-01-21 23:41:00 +04:00
function Renderer ( width , height , images ) {
this . width = width ;
this . height = height ;
this . images = images ;
}
2014-01-26 20:10:04 +04:00
Renderer . prototype . renderImage = function ( container , bounds , borderData , image ) {
var paddingLeft = container . cssInt ( 'paddingLeft' ) ,
paddingTop = container . cssInt ( 'paddingTop' ) ,
paddingRight = container . cssInt ( 'paddingRight' ) ,
paddingBottom = container . cssInt ( 'paddingBottom' ) ,
borders = borderData . borders ;
this . drawImage (
image ,
0 ,
0 ,
image . width ,
image . height ,
bounds . left + paddingLeft + borders [ 3 ] . width ,
bounds . top + paddingTop + borders [ 0 ] . width ,
bounds . width - ( borders [ 1 ] . width + borders [ 3 ] . width + paddingLeft + paddingRight ) ,
bounds . height - ( borders [ 0 ] . width + borders [ 2 ] . width + paddingTop + paddingBottom )
) ;
} ;
2014-01-19 20:04:27 +04:00
Renderer . prototype . renderBackground = function ( container , bounds ) {
if ( bounds . height > 0 && bounds . width > 0 ) {
this . renderBackgroundColor ( container , bounds ) ;
this . renderBackgroundImage ( container , bounds ) ;
2013-09-07 22:24:41 +04:00
}
} ;
2014-01-19 20:04:27 +04:00
Renderer . prototype . renderBackgroundColor = function ( container , bounds ) {
var color = container . css ( "backgroundColor" ) ;
if ( ! this . isTransparent ( color ) ) {
2014-01-21 23:41:00 +04:00
this . rectangle ( bounds . left , bounds . top , bounds . width , bounds . height , container . css ( "backgroundColor" ) ) ;
2013-09-07 22:24:41 +04:00
}
2014-01-19 20:04:27 +04:00
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
Renderer . prototype . renderBorders = function ( borders ) {
borders . forEach ( this . renderBorder , this ) ;
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
Renderer . prototype . renderBorder = function ( data ) {
if ( ! this . isTransparent ( data . color ) && data . args !== null ) {
this . drawShape ( data . args , data . color ) ;
2013-09-07 22:24:41 +04:00
}
2014-01-19 20:04:27 +04:00
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
Renderer . prototype . renderBackgroundImage = function ( container , bounds ) {
2014-01-21 23:41:00 +04:00
var backgroundImages = container . parseBackgroundImages ( ) ;
2014-01-26 18:06:16 +04:00
backgroundImages . reverse ( ) . forEach ( function ( backgroundImage , index , arr ) {
2014-01-21 23:41:00 +04:00
if ( backgroundImage . method === "url" ) {
var image = this . images . get ( backgroundImage . args [ 0 ] ) ;
if ( image ) {
2014-01-26 18:06:16 +04:00
this . renderBackgroundRepeating ( container , bounds , image , arr . length - ( index + 1 ) ) ;
2014-01-21 23:41:00 +04:00
} else {
log ( "Error loading background-image" , backgroundImage . args [ 0 ] ) ;
}
2014-01-26 20:10:04 +04:00
}
2014-01-21 23:41:00 +04:00
} , this ) ;
} ;
2013-09-07 22:24:41 +04:00
2014-01-21 23:41:00 +04:00
Renderer . prototype . renderBackgroundRepeating = function ( container , bounds , imageContainer , index ) {
var size = container . parseBackgroundSize ( bounds , imageContainer . image , index ) ;
var position = container . parseBackgroundPosition ( bounds , imageContainer . image , index , size ) ;
var repeat = container . parseBackgroundRepeat ( index ) ;
// image = resizeImage(image, backgroundSize);
switch ( repeat ) {
case "repeat-x" :
case "repeat no-repeat" :
2014-01-26 18:06:16 +04:00
this . backgroundRepeatShape ( imageContainer , position , size , bounds , bounds . left , bounds . top + position . top , 99999 , imageContainer . image . height ) ;
2014-01-21 23:41:00 +04:00
break ;
case "repeat-y" :
case "no-repeat repeat" :
2014-01-26 18:06:16 +04:00
this . backgroundRepeatShape ( imageContainer , position , size , bounds , bounds . left + position . left , bounds . top , imageContainer . image . width , 99999 ) ;
2014-01-21 23:41:00 +04:00
break ;
case "no-repeat" :
2014-01-26 18:06:16 +04:00
this . backgroundRepeatShape ( imageContainer , position , size , bounds , bounds . left + position . left , bounds . top + position . top , imageContainer . image . width , imageContainer . image . height ) ;
2014-01-21 23:41:00 +04:00
break ;
default :
2014-01-26 18:06:16 +04:00
this . renderBackgroundRepeat ( imageContainer , position , size , { top : bounds . top , left : bounds . left } ) ;
2014-01-21 23:41:00 +04:00
break ;
}
2014-01-19 20:04:27 +04:00
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
Renderer . prototype . isTransparent = function ( color ) {
return ( ! color || color === "transparent" || color === "rgba(0, 0, 0, 0)" ) ;
} ;
2013-09-07 22:24:41 +04:00
2014-01-21 23:41:00 +04:00
function CanvasRenderer ( width , height ) {
Renderer . apply ( this , arguments ) ;
2014-01-19 20:04:27 +04:00
this . canvas = document . createElement ( "canvas" ) ;
2014-01-21 23:41:00 +04:00
this . canvas . width = width ;
this . canvas . height = height ;
2014-01-19 20:04:27 +04:00
this . ctx = this . canvas . getContext ( "2d" ) ;
this . ctx . textBaseline = "bottom" ;
}
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
CanvasRenderer . prototype = Object . create ( Renderer . prototype ) ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
CanvasRenderer . prototype . setFillStyle = function ( color ) {
this . ctx . fillStyle = color ;
return this . ctx ;
2013-09-07 22:24:41 +04:00
} ;
2014-01-19 20:04:27 +04:00
CanvasRenderer . prototype . rectangle = function ( left , top , width , height , color ) {
this . setFillStyle ( color ) . fillRect ( left , top , width , height ) ;
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
CanvasRenderer . prototype . drawShape = function ( shape , color ) {
this . shape ( shape ) ;
this . setFillStyle ( color ) . fill ( ) ;
2013-09-07 22:24:41 +04:00
} ;
2014-01-26 20:10:04 +04:00
CanvasRenderer . prototype . drawImage = function ( image , sx , sy , sw , sh , dx , dy , dw , dh ) {
this . ctx . drawImage ( image , sx , sy , sw , sh , dx , dy , dw , dh ) ;
} ;
2014-01-19 20:04:27 +04:00
CanvasRenderer . prototype . clip = function ( shape , callback , context ) {
this . ctx . save ( ) ;
this . shape ( shape ) . clip ( ) ;
callback . call ( context ) ;
this . ctx . restore ( ) ;
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
CanvasRenderer . prototype . shape = function ( shape ) {
this . ctx . beginPath ( ) ;
shape . forEach ( function ( point , index ) {
this . ctx [ ( index === 0 ) ? "moveTo" : point [ 0 ] + "To" ] . apply ( this . ctx , point . slice ( 1 ) ) ;
} , this ) ;
this . ctx . closePath ( ) ;
return this . ctx ;
2013-09-07 22:24:41 +04:00
} ;
2014-01-19 20:04:27 +04:00
CanvasRenderer . prototype . font = function ( color , style , variant , weight , size , family ) {
this . setFillStyle ( color ) . font = [ style , variant , weight , size , family ] . join ( " " ) ;
2013-09-07 22:24:41 +04:00
} ;
2014-01-19 20:04:27 +04:00
CanvasRenderer . prototype . setOpacity = function ( opacity ) {
this . ctx . globalAlpha = opacity ;
} ;
2013-09-07 22:24:41 +04:00
2014-01-19 20:04:27 +04:00
CanvasRenderer . prototype . text = function ( text , left , bottom ) {
this . ctx . fillText ( text , left , bottom ) ;
2013-09-07 22:24:41 +04:00
} ;
2014-01-19 20:04:27 +04:00
2014-01-26 18:06:16 +04:00
CanvasRenderer . prototype . backgroundRepeatShape = function ( imageContainer , backgroundPosition , size , bounds , left , top , width , height ) {
2014-01-21 23:41:00 +04:00
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 ( ) {
2014-01-26 20:10:04 +04:00
this . renderBackgroundRepeat ( imageContainer , backgroundPosition , size , bounds ) ;
2014-01-21 23:41:00 +04:00
} , this ) ;
} ;
2014-01-26 18:06:16 +04:00
CanvasRenderer . prototype . renderBackgroundRepeat = function ( imageContainer , backgroundPosition , size , bounds ) {
2014-01-21 23:41:00 +04:00
var offsetX = Math . round ( bounds . left + backgroundPosition . left ) , offsetY = Math . round ( bounds . top + backgroundPosition . top ) ;
2014-01-26 18:06:16 +04:00
this . setFillStyle ( this . ctx . createPattern ( this . resizeImage ( imageContainer , size ) , "repeat" ) ) ;
2014-01-21 23:41:00 +04:00
this . ctx . translate ( offsetX , offsetY ) ;
this . ctx . fill ( ) ;
this . ctx . translate ( - offsetX , - offsetY ) ;
} ;
2014-01-26 18:06:16 +04:00
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 ;
} ;
2014-01-21 23:41:00 +04:00
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 ;
} ;
function Support ( ) {
this . rangeBounds = this . testRangeBounds ( ) ;
this . cors = this . testCORS ( ) ;
}
Support . prototype . testRangeBounds = function ( ) {
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" ;
} ;
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 ( ) ;
}
}
2013-09-07 22:24:41 +04:00
} ) ( window , document ) ;