/**
* Dom-To-Image 2.6.0
* https://github.com/tsayen/dom-to-image
*
* Released under the MIT license
* https://github.com/tsayen/dom-to-image/blob/master/LICENSE
*/
(function ( global ) {
'use strict';
var util = newUtil();
var inliner = newInliner();
var fontFaces = newFontFaces();
var images = newImages();
// Default impl options
var defaultOptions = {
// Default is to fail on error, no placeholder
imagePlaceholder: undefined,
// Default cache bust is false, it will use the cache
cacheBust: false
};
var domtoimage = {
toSvg: toSvg,
toPng: toPng,
toJpeg: toJpeg,
toBlob: toBlob,
toPixelData: toPixelData,
impl: {
fontFaces: fontFaces,
images: images,
util: util,
inliner: inliner,
options: {}
}
};
if ( typeof module !== 'undefined' )
module.exports = domtoimage;
else
global.domtoimage = domtoimage;
/**
* @param {Node} node - The DOM Node object to render
* @param {Object} options - Rendering options
* @param {Function} options.filter - Should return true if passed node should be included in the output
* (excluding node means excluding it's children as well). Not called on the root node.
* @param {String} options.bgcolor - color for the background, any valid CSS color value.
* @param {Number} options.width - width to be applied to node before rendering.
* @param {Number} options.height - height to be applied to node before rendering.
* @param {Object} options.style - an object whose properties to be copied to node's style before rendering.
* @param {Number} options.quality - a Number between 0 and 1 indicating image quality (applicable to JPEG only),
defaults to 1.0.
* @param {String} options.imagePlaceholder - dataURL to use as a placeholder for failed images, default behaviour is to fail fast on images we can't fetch
* @param {Boolean} options.cacheBust - set to true to cache bust by appending the time to the request url
* @return {Promise} - A promise that is fulfilled with a SVG image data URL
* */
function toSvg( node, options ) {
options = options || {};
copyOptions( options );
return Promise.resolve( node )
.then( embedFonts )
.then( function ( node ) {
return cloneNode( node, options.filter, true );
} )
.then( inlineImages )
.then( applyOptions )
.then( function ( clone ) {
return makeSvgDataUri( clone,
options.width || util.width( node ),
options.height || util.height( node )
);
} );
function applyOptions( clone ) {
if ( options.bgcolor ) clone.style.backgroundColor = options.bgcolor;
if ( options.width ) clone.style.width = options.width + 'px';
if ( options.height ) clone.style.height = options.height + 'px';
if ( options.style )
Object.keys( options.style ).forEach( function ( property ) {
clone.style[ property ] = options.style[ property ];
} );
return clone;
}
}
/**
* @param {Node} node - The DOM Node object to render
* @param {Object} options - Rendering options, @see {@link toSvg}
* @return {Promise} - A promise that is fulfilled with a Uint8Array containing RGBA pixel data.
* */
function toPixelData( node, options ) {
return draw( node, options || {} )
.then( function ( canvas ) {
return canvas.getContext( '2d' ).getImageData(
0,
0,
util.width( node ),
util.height( node )
).data;
} );
}
/**
* @param {Node} node - The DOM Node object to render
* @param {Object} options - Rendering options, @see {@link toSvg}
* @return {Promise} - A promise that is fulfilled with a PNG image data URL
* */
function toPng( node, options ) {
return draw( node, options || {} )
.then( function ( canvas ) {
return canvas.toDataURL();
} );
}
/**
* @param {Node} node - The DOM Node object to render
* @param {Object} options - Rendering options, @see {@link toSvg}
* @return {Promise} - A promise that is fulfilled with a JPEG image data URL
* */
function toJpeg( node, options ) {
options = options || {};
return draw( node, options )
.then( function ( canvas ) {
return canvas.toDataURL( 'image/jpeg', options.quality || 1.0 );
} );
}
/**
* @param {Node} node - The DOM Node object to render
* @param {Object} options - Rendering options, @see {@link toSvg}
* @return {Promise} - A promise that is fulfilled with a PNG image blob
* */
function toBlob( node, options ) {
return draw( node, options || {} )
.then( util.canvasToBlob );
}
function copyOptions( options ) {
// Copy options to impl options for use in impl
if ( typeof (options.imagePlaceholder) === 'undefined' ) {
domtoimage.impl.options.imagePlaceholder = defaultOptions.imagePlaceholder;
} else {
domtoimage.impl.options.imagePlaceholder = options.imagePlaceholder;
}
if ( typeof (options.cacheBust) === 'undefined' ) {
domtoimage.impl.options.cacheBust = defaultOptions.cacheBust;
} else {
domtoimage.impl.options.cacheBust = options.cacheBust;
}
}
function draw( domNode, options ) {
return toSvg( domNode, options )
.then( util.makeImage )
.then( util.delay( 100 ) )
.then( function ( image ) {
var canvas = newCanvas( domNode );
canvas.getContext( '2d' ).drawImage( image, 0, 0 );
return canvas;
} );
function newCanvas( domNode ) {
var canvas = document.createElement( 'canvas' );
canvas.width = options.width || util.width( domNode );
canvas.height = options.height || util.height( domNode );
if ( options.bgcolor ) {
var ctx = canvas.getContext( '2d' );
ctx.fillStyle = options.bgcolor;
ctx.fillRect( 0, 0, canvas.width, canvas.height );
}
return canvas;
}
}
function cloneNode( node, filter, root ) {
if ( ! root && filter && ! filter( node ) ) return Promise.resolve();
return Promise.resolve( node )
.then( makeNodeCopy )
.then( function ( clone ) {
return cloneChildren( node, clone, filter );
} )
.then( function ( clone ) {
return processClone( node, clone );
} );
function makeNodeCopy( node ) {
if ( node instanceof HTMLCanvasElement ) return util.makeImage( node.toDataURL() );
return node.cloneNode( false );
}
function cloneChildren( original, clone, filter ) {
var children = original.childNodes;
if ( children.length === 0 ) return Promise.resolve( clone );
return cloneChildrenInOrder( clone, util.asArray( children ), filter )
.then( function () {
return clone;
} );
function cloneChildrenInOrder( parent, children, filter ) {
var done = Promise.resolve();
children.forEach( function ( child ) {
done = done
.then( function () {
return cloneNode( child, filter );
} )
.then( function ( childClone ) {
if ( childClone ) parent.appendChild( childClone );
} );
} );
return done;
}
}
function processClone( original, clone ) {
if ( ! (clone instanceof Element) ) return clone;
return Promise.resolve()
.then( cloneStyle )
.then( clonePseudoElements )
.then( copyUserInput )
.then( fixSvg )
.then( function () {
return clone;
} );
function cloneStyle() {
copyStyle( window.getComputedStyle( original ), clone.style );
function copyStyle( source, target ) {
if ( source.cssText ) target.cssText = source.cssText;
else copyProperties( source, target );
function copyProperties( source, target ) {
util.asArray( source ).forEach( function ( name ) {
target.setProperty(
name,
source.getPropertyValue( name ),
source.getPropertyPriority( name )
);
} );
}
}
}
function clonePseudoElements() {
[':before', ':after'].forEach( function ( element ) {
clonePseudoElement( element );
} );
function clonePseudoElement( element ) {
var style = window.getComputedStyle( original, element );
var content = style.getPropertyValue( 'content' );
if ( content === '' || content === 'none' ) return;
var className = util.uid();
clone.className = clone.className + ' ' + className;
var styleElement = document.createElement( 'style' );
styleElement.appendChild( formatPseudoElementStyle( className, element, style ) );
clone.appendChild( styleElement );
function formatPseudoElementStyle( className, element, style ) {
var selector = '.' + className + ':' + element;
var cssText = style.cssText ? formatCssText( style ) : formatCssProperties( style );
return document.createTextNode( selector + '{' + cssText + '}' );
function formatCssText( style ) {
var content = style.getPropertyValue( 'content' );
return style.cssText + ' content: ' + content + ';';
}
function formatCssProperties( style ) {
return util.asArray( style )
.map( formatProperty )
.join( '; ' ) + ';';
function formatProperty( name ) {
return name + ': ' +
style.getPropertyValue( name ) +
(style.getPropertyPriority( name ) ? ' !important' : '');
}
}
}
}
}
function copyUserInput() {
if ( original instanceof HTMLTextAreaElement ) clone.innerHTML = original.value;
if ( original instanceof HTMLInputElement ) clone.setAttribute( "value", original.value );
}
function fixSvg() {
if ( ! (clone instanceof SVGElement) ) return;
clone.setAttribute( 'xmlns', 'http://www.w3.org/2000/svg' );
if ( ! (clone instanceof SVGRectElement) ) return;
['width', 'height'].forEach( function ( attribute ) {
var value = clone.getAttribute( attribute );
if ( ! value ) return;
clone.style.setProperty( attribute, value );
} );
}
}
}
function embedFonts( node ) {
return fontFaces.resolveAll()
.then( function ( cssText ) {
var styleNode = document.createElement( 'style' );
node.appendChild( styleNode );
styleNode.appendChild( document.createTextNode( cssText ) );
return node;
} );
}
function inlineImages( node ) {
return images.inlineAll( node )
.then( function () {
return node;
} );
}
function makeSvgDataUri( node, width, height ) {
return Promise.resolve( node )
.then( function ( node ) {
node.setAttribute( 'xmlns', 'http://www.w3.org/1999/xhtml' );
return new XMLSerializer().serializeToString( node );
} )
.then( util.escapeXhtml )
.then( function ( xhtml ) {
return '