View file File name : dom-to-image.js Content :/** * 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 '<foreignObject x="0" y="0" width="100%" height="100%">' + xhtml + '</foreignObject>'; } ) .then( function ( foreignObject ) { return '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="' + height + '">' + foreignObject + '</svg>'; } ) .then( function ( svg ) { return 'data:image/svg+xml;charset=utf-8,' + svg; } ); } function newUtil() { return { escape: escape, parseExtension: parseExtension, mimeType: mimeType, dataAsUrl: dataAsUrl, isDataUrl: isDataUrl, canvasToBlob: canvasToBlob, resolveUrl: resolveUrl, getAndEncode: getAndEncode, uid: uid(), delay: delay, asArray: asArray, escapeXhtml: escapeXhtml, makeImage: makeImage, width: width, height: height }; function mimes() { /* * Only WOFF and EOT mime types for fonts are 'real' * see http://www.iana.org/assignments/media-types/media-types.xhtml */ var WOFF = 'application/font-woff'; var JPEG = 'image/jpeg'; return { 'woff': WOFF, 'woff2': WOFF, 'ttf': 'application/font-truetype', 'eot': 'application/vnd.ms-fontobject', 'png': 'image/png', 'jpg': JPEG, 'jpeg': JPEG, 'gif': 'image/gif', 'tiff': 'image/tiff', 'svg': 'image/svg+xml' }; } function parseExtension( url ) { var match = /\.([^\.\/]*?)$/g.exec( url ); if ( match ) return match[ 1 ]; else return ''; } function mimeType( url ) { var extension = parseExtension( url ).toLowerCase(); return mimes()[ extension ] || ''; } function isDataUrl( url ) { return url.search( /^(data:)/ ) !== -1; } function toBlob( canvas ) { return new Promise( function ( resolve ) { var binaryString = window.atob( canvas.toDataURL().split( ',' )[ 1 ] ); var length = binaryString.length; var binaryArray = new Uint8Array( length ); for ( var i = 0; i < length; i++ ) binaryArray[ i ] = binaryString.charCodeAt( i ); resolve( new Blob( [binaryArray], { type: 'image/png' } ) ); } ); } function canvasToBlob( canvas ) { if ( canvas.toBlob ) return new Promise( function ( resolve ) { canvas.toBlob( resolve ); } ); return toBlob( canvas ); } function resolveUrl( url, baseUrl ) { var doc = document.implementation.createHTMLDocument(); var base = doc.createElement( 'base' ); doc.head.appendChild( base ); var a = doc.createElement( 'a' ); doc.body.appendChild( a ); base.href = baseUrl; a.href = url; return a.href; } function uid() { var index = 0; return function () { return 'u' + fourRandomChars() + index++; function fourRandomChars() { /* see http://stackoverflow.com/a/6248722/2519373 */ return ('0000' + (Math.random() * Math.pow( 36, 4 ) << 0).toString( 36 )).slice( -4 ); } }; } function makeImage( uri ) { return new Promise( function ( resolve, reject ) { var image = new Image(); image.onload = function () { resolve( image ); }; image.onerror = reject; image.src = uri; } ); } function getAndEncode( url ) { var TIMEOUT = 30000; if ( domtoimage.impl.options.cacheBust ) { // Cache bypass so we dont have CORS issues with cached images // Source: https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache url += ((/\?/).test( url ) ? "&" : "?") + (new Date()).getTime(); } return new Promise( function ( resolve ) { var request = new XMLHttpRequest(); request.onreadystatechange = done; request.ontimeout = timeout; request.responseType = 'blob'; request.timeout = TIMEOUT; request.open( 'GET', url, true ); request.send(); var placeholder; if ( domtoimage.impl.options.imagePlaceholder ) { var split = domtoimage.impl.options.imagePlaceholder.split( /,/ ); if ( split && split[ 1 ] ) { placeholder = split[ 1 ]; } } function done() { if ( request.readyState !== 4 ) return; if ( request.status !== 200 ) { if ( placeholder ) { resolve( placeholder ); } else { fail( 'cannot fetch resource: ' + url + ', status: ' + request.status ); } return; } var encoder = new FileReader(); encoder.onloadend = function () { var content = encoder.result.split( /,/ )[ 1 ]; resolve( content ); }; encoder.readAsDataURL( request.response ); } function timeout() { if ( placeholder ) { resolve( placeholder ); } else { fail( 'timeout of ' + TIMEOUT + 'ms occured while fetching resource: ' + url ); } } function fail( message ) { console.error( message ); resolve( '' ); } } ); } function dataAsUrl( content, type ) { return 'data:' + type + ';base64,' + content; } function escape( string ) { return string.replace( /([.*+?^${}()|\[\]\/\\])/g, '\\$1' ); } function delay( ms ) { return function ( arg ) { return new Promise( function ( resolve ) { setTimeout( function () { resolve( arg ); }, ms ); } ); }; } function asArray( arrayLike ) { var array = []; var length = arrayLike.length; for ( var i = 0; i < length; i++ ) array.push( arrayLike[ i ] ); return array; } function escapeXhtml( string ) { return string.replace( /#/g, '%23' ).replace( /\n/g, '%0A' ); } function width( node ) { var leftBorder = px( node, 'border-left-width' ); var rightBorder = px( node, 'border-right-width' ); return node.scrollWidth + leftBorder + rightBorder; } function height( node ) { var topBorder = px( node, 'border-top-width' ); var bottomBorder = px( node, 'border-bottom-width' ); return node.scrollHeight + topBorder + bottomBorder; } function px( node, styleProperty ) { var value = window.getComputedStyle( node ).getPropertyValue( styleProperty ); return parseFloat( value.replace( 'px', '' ) ); } } function newInliner() { var URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/g; return { inlineAll: inlineAll, shouldProcess: shouldProcess, impl: { readUrls: readUrls, inline: inline } }; function shouldProcess( string ) { return string.search( URL_REGEX ) !== -1; } function readUrls( string ) { var result = []; var match; while ( (match = URL_REGEX.exec( string )) !== null ) { result.push( match[ 1 ] ); } return result.filter( function ( url ) { return ! util.isDataUrl( url ); } ); } function inline( string, url, baseUrl, get ) { return Promise.resolve( url ) .then( function ( url ) { return baseUrl ? util.resolveUrl( url, baseUrl ) : url; } ) .then( get || util.getAndEncode ) .then( function ( data ) { return util.dataAsUrl( data, util.mimeType( url ) ); } ) .then( function ( dataUrl ) { return string.replace( urlAsRegex( url ), '$1' + dataUrl + '$3' ); } ); function urlAsRegex( url ) { return new RegExp( '(url\\([\'"]?)(' + util.escape( url ) + ')([\'"]?\\))', 'g' ); } } function inlineAll( string, baseUrl, get ) { if ( nothingToInline() ) return Promise.resolve( string ); return Promise.resolve( string ) .then( readUrls ) .then( function ( urls ) { var done = Promise.resolve( string ); urls.forEach( function ( url ) { done = done.then( function ( string ) { return inline( string, url, baseUrl, get ); } ); } ); return done; } ); function nothingToInline() { return ! shouldProcess( string ); } } } function newFontFaces() { return { resolveAll: resolveAll, impl: { readAll: readAll } }; function resolveAll() { return readAll( document ) .then( function ( webFonts ) { return Promise.all( webFonts.map( function ( webFont ) { return webFont.resolve(); } ) ); } ) .then( function ( cssStrings ) { return cssStrings.join( '\n' ); } ); } function readAll() { return Promise.resolve( util.asArray( document.styleSheets ) ) .then( getCssRules ) .then( selectWebFontRules ) .then( function ( rules ) { return rules.map( newWebFont ); } ); function selectWebFontRules( cssRules ) { return cssRules .filter( function ( rule ) { return rule.type === CSSRule.FONT_FACE_RULE; } ) .filter( function ( rule ) { return inliner.shouldProcess( rule.style.getPropertyValue( 'src' ) ); } ); } function getCssRules( styleSheets ) { var cssRules = []; styleSheets.forEach( function ( sheet ) { try { util.asArray( sheet.cssRules || [] ).forEach( cssRules.push.bind( cssRules ) ); } catch ( e ) { console.log( 'Error while reading CSS rules from ' + sheet.href, e.toString() ); } } ); return cssRules; } function newWebFont( webFontRule ) { return { resolve: function resolve() { var baseUrl = (webFontRule.parentStyleSheet || {}).href; return inliner.inlineAll( webFontRule.cssText, baseUrl ); }, src: function () { return webFontRule.style.getPropertyValue( 'src' ); } }; } } } function newImages() { return { inlineAll: inlineAll, impl: { newImage: newImage } }; function newImage( element ) { return { inline: inline }; function inline( get ) { if ( util.isDataUrl( element.src ) ) return Promise.resolve(); return Promise.resolve( element.src ) .then( get || util.getAndEncode ) .then( function ( data ) { return util.dataAsUrl( data, util.mimeType( element.src ) ); } ) .then( function ( dataUrl ) { return new Promise( function ( resolve, reject ) { element.onload = resolve; element.onerror = reject; element.src = dataUrl; } ); } ); } } function inlineAll( node ) { if ( ! (node instanceof Element) ) return Promise.resolve( node ); return inlineBackground( node ) .then( function () { if ( node instanceof HTMLImageElement ) return newImage( node ).inline(); else return Promise.all( util.asArray( node.childNodes ).map( function ( child ) { return inlineAll( child ); } ) ); } ); function inlineBackground( node ) { var background = node.style.getPropertyValue( 'background' ); if ( ! background ) return Promise.resolve( node ); return inliner.inlineAll( background ) .then( function ( inlined ) { node.style.setProperty( 'background', inlined, node.style.getPropertyPriority( 'background' ) ); } ) .then( function () { return node; } ); } } } })( this );