PluginProbe
Elementor Website Builder – more than just a page builder / trunk
Elementor Website Builder – more than just a page builder vtrunk
4.3.3 4.3.2 4.3.1 4.3.0 4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 All 456 releases
elementor / assets / lib / dom-to-image / js / dom-to-image.js

dom-to-image.js in Elementor Website Builder – more than just a page builder trunk, at assets/lib/dom-to-image/js/dom-to-image.js

778 lines 20.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Dom-To-Image 2.6.0
3 * https://github.com/tsayen/dom-to-image
4 *
5 * Released under the MIT license
6 * https://github.com/tsayen/dom-to-image/blob/master/LICENSE
7 */
8
9 (function ( global ) {
10 'use strict';
11
12 var util = newUtil();
13 var inliner = newInliner();
14 var fontFaces = newFontFaces();
15 var images = newImages();
16
17 // Default impl options
18 var defaultOptions = {
19 // Default is to fail on error, no placeholder
20 imagePlaceholder: undefined,
21 // Default cache bust is false, it will use the cache
22 cacheBust: false
23 };
24
25 var domtoimage = {
26 toSvg: toSvg,
27 toPng: toPng,
28 toJpeg: toJpeg,
29 toBlob: toBlob,
30 toPixelData: toPixelData,
31 impl: {
32 fontFaces: fontFaces,
33 images: images,
34 util: util,
35 inliner: inliner,
36 options: {}
37 }
38 };
39
40 if ( typeof module !== 'undefined' )
41 module.exports = domtoimage;
42 else
43 global.domtoimage = domtoimage;
44
45
46 /**
47 * @param {Node} node - The DOM Node object to render
48 * @param {Object} options - Rendering options
49 * @param {Function} options.filter - Should return true if passed node should be included in the output
50 * (excluding node means excluding it's children as well). Not called on the root node.
51 * @param {String} options.bgcolor - color for the background, any valid CSS color value.
52 * @param {Number} options.width - width to be applied to node before rendering.
53 * @param {Number} options.height - height to be applied to node before rendering.
54 * @param {Object} options.style - an object whose properties to be copied to node's style before rendering.
55 * @param {Number} options.quality - a Number between 0 and 1 indicating image quality (applicable to JPEG only),
56 defaults to 1.0.
57 * @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
58 * @param {Boolean} options.cacheBust - set to true to cache bust by appending the time to the request url
59 * @return {Promise} - A promise that is fulfilled with a SVG image data URL
60 * */
61 function toSvg( node, options ) {
62 options = options || {};
63 copyOptions( options );
64 return Promise.resolve( node )
65 .then( embedFonts )
66 .then( function ( node ) {
67 return cloneNode( node, options.filter, true );
68 } )
69 .then( inlineImages )
70 .then( applyOptions )
71 .then( function ( clone ) {
72 return makeSvgDataUri( clone,
73 options.width || util.width( node ),
74 options.height || util.height( node )
75 );
76 } );
77
78 function applyOptions( clone ) {
79 if ( options.bgcolor ) clone.style.backgroundColor = options.bgcolor;
80
81 if ( options.width ) clone.style.width = options.width + 'px';
82 if ( options.height ) clone.style.height = options.height + 'px';
83
84 if ( options.style )
85 Object.keys( options.style ).forEach( function ( property ) {
86 clone.style[ property ] = options.style[ property ];
87 } );
88
89 return clone;
90 }
91 }
92
93 /**
94 * @param {Node} node - The DOM Node object to render
95 * @param {Object} options - Rendering options, @see {@link toSvg}
96 * @return {Promise} - A promise that is fulfilled with a Uint8Array containing RGBA pixel data.
97 * */
98 function toPixelData( node, options ) {
99 return draw( node, options || {} )
100 .then( function ( canvas ) {
101 return canvas.getContext( '2d' ).getImageData(
102 0,
103 0,
104 util.width( node ),
105 util.height( node )
106 ).data;
107 } );
108 }
109
110 /**
111 * @param {Node} node - The DOM Node object to render
112 * @param {Object} options - Rendering options, @see {@link toSvg}
113 * @return {Promise} - A promise that is fulfilled with a PNG image data URL
114 * */
115 function toPng( node, options ) {
116 return draw( node, options || {} )
117 .then( function ( canvas ) {
118 return canvas.toDataURL();
119 } );
120 }
121
122 /**
123 * @param {Node} node - The DOM Node object to render
124 * @param {Object} options - Rendering options, @see {@link toSvg}
125 * @return {Promise} - A promise that is fulfilled with a JPEG image data URL
126 * */
127 function toJpeg( node, options ) {
128 options = options || {};
129 return draw( node, options )
130 .then( function ( canvas ) {
131 return canvas.toDataURL( 'image/jpeg', options.quality || 1.0 );
132 } );
133 }
134
135 /**
136 * @param {Node} node - The DOM Node object to render
137 * @param {Object} options - Rendering options, @see {@link toSvg}
138 * @return {Promise} - A promise that is fulfilled with a PNG image blob
139 * */
140 function toBlob( node, options ) {
141 return draw( node, options || {} )
142 .then( util.canvasToBlob );
143 }
144
145 function copyOptions( options ) {
146 // Copy options to impl options for use in impl
147 if ( typeof (options.imagePlaceholder) === 'undefined' ) {
148 domtoimage.impl.options.imagePlaceholder = defaultOptions.imagePlaceholder;
149 } else {
150 domtoimage.impl.options.imagePlaceholder = options.imagePlaceholder;
151 }
152
153 if ( typeof (options.cacheBust) === 'undefined' ) {
154 domtoimage.impl.options.cacheBust = defaultOptions.cacheBust;
155 } else {
156 domtoimage.impl.options.cacheBust = options.cacheBust;
157 }
158 }
159
160 function draw( domNode, options ) {
161 return toSvg( domNode, options )
162 .then( util.makeImage )
163 .then( util.delay( 100 ) )
164 .then( function ( image ) {
165 var canvas = newCanvas( domNode );
166 canvas.getContext( '2d' ).drawImage( image, 0, 0 );
167 return canvas;
168 } );
169
170 function newCanvas( domNode ) {
171 var canvas = document.createElement( 'canvas' );
172 canvas.width = options.width || util.width( domNode );
173 canvas.height = options.height || util.height( domNode );
174
175 if ( options.bgcolor ) {
176 var ctx = canvas.getContext( '2d' );
177 ctx.fillStyle = options.bgcolor;
178 ctx.fillRect( 0, 0, canvas.width, canvas.height );
179 }
180
181 return canvas;
182 }
183 }
184
185 function cloneNode( node, filter, root ) {
186 if ( ! root && filter && ! filter( node ) ) return Promise.resolve();
187
188 return Promise.resolve( node )
189 .then( makeNodeCopy )
190 .then( function ( clone ) {
191 return cloneChildren( node, clone, filter );
192 } )
193 .then( function ( clone ) {
194 return processClone( node, clone );
195 } );
196
197 function makeNodeCopy( node ) {
198 if ( node instanceof HTMLCanvasElement ) return util.makeImage( node.toDataURL() );
199 return node.cloneNode( false );
200 }
201
202 function cloneChildren( original, clone, filter ) {
203 var children = original.childNodes;
204 if ( children.length === 0 ) return Promise.resolve( clone );
205
206 return cloneChildrenInOrder( clone, util.asArray( children ), filter )
207 .then( function () {
208 return clone;
209 } );
210
211 function cloneChildrenInOrder( parent, children, filter ) {
212 var done = Promise.resolve();
213 children.forEach( function ( child ) {
214 done = done
215 .then( function () {
216 return cloneNode( child, filter );
217 } )
218 .then( function ( childClone ) {
219 if ( childClone ) parent.appendChild( childClone );
220 } );
221 } );
222 return done;
223 }
224 }
225
226 function processClone( original, clone ) {
227 if ( ! (clone instanceof Element) ) return clone;
228
229 return Promise.resolve()
230 .then( cloneStyle )
231 .then( clonePseudoElements )
232 .then( copyUserInput )
233 .then( fixSvg )
234 .then( function () {
235 return clone;
236 } );
237
238 function cloneStyle() {
239 copyStyle( window.getComputedStyle( original ), clone.style );
240
241 function copyStyle( source, target ) {
242 if ( source.cssText ) target.cssText = source.cssText;
243 else copyProperties( source, target );
244
245 function copyProperties( source, target ) {
246 util.asArray( source ).forEach( function ( name ) {
247 target.setProperty(
248 name,
249 source.getPropertyValue( name ),
250 source.getPropertyPriority( name )
251 );
252 } );
253 }
254 }
255 }
256
257 function clonePseudoElements() {
258 [':before', ':after'].forEach( function ( element ) {
259 clonePseudoElement( element );
260 } );
261
262 function clonePseudoElement( element ) {
263 var style = window.getComputedStyle( original, element );
264 var content = style.getPropertyValue( 'content' );
265
266 if ( content === '' || content === 'none' ) return;
267
268 var className = util.uid();
269 clone.className = clone.className + ' ' + className;
270 var styleElement = document.createElement( 'style' );
271 styleElement.appendChild( formatPseudoElementStyle( className, element, style ) );
272 clone.appendChild( styleElement );
273
274 function formatPseudoElementStyle( className, element, style ) {
275 var selector = '.' + className + ':' + element;
276 var cssText = style.cssText ? formatCssText( style ) : formatCssProperties( style );
277 return document.createTextNode( selector + '{' + cssText + '}' );
278
279 function formatCssText( style ) {
280 var content = style.getPropertyValue( 'content' );
281 return style.cssText + ' content: ' + content + ';';
282 }
283
284 function formatCssProperties( style ) {
285
286 return util.asArray( style )
287 .map( formatProperty )
288 .join( '; ' ) + ';';
289
290 function formatProperty( name ) {
291 return name + ': ' +
292 style.getPropertyValue( name ) +
293 (style.getPropertyPriority( name ) ? ' !important' : '');
294 }
295 }
296 }
297 }
298 }
299
300 function copyUserInput() {
301 if ( original instanceof HTMLTextAreaElement ) clone.innerHTML = original.value;
302 if ( original instanceof HTMLInputElement ) clone.setAttribute( "value", original.value );
303 }
304
305 function fixSvg() {
306 if ( ! (clone instanceof SVGElement) ) return;
307 clone.setAttribute( 'xmlns', 'http://www.w3.org/2000/svg' );
308
309 if ( ! (clone instanceof SVGRectElement) ) return;
310 ['width', 'height'].forEach( function ( attribute ) {
311 var value = clone.getAttribute( attribute );
312 if ( ! value ) return;
313
314 clone.style.setProperty( attribute, value );
315 } );
316 }
317 }
318 }
319
320 function embedFonts( node ) {
321 return fontFaces.resolveAll()
322 .then( function ( cssText ) {
323 var styleNode = document.createElement( 'style' );
324 node.appendChild( styleNode );
325 styleNode.appendChild( document.createTextNode( cssText ) );
326 return node;
327 } );
328 }
329
330 function inlineImages( node ) {
331 return images.inlineAll( node )
332 .then( function () {
333 return node;
334 } );
335 }
336
337 function makeSvgDataUri( node, width, height ) {
338 return Promise.resolve( node )
339 .then( function ( node ) {
340 node.setAttribute( 'xmlns', 'http://www.w3.org/1999/xhtml' );
341 return new XMLSerializer().serializeToString( node );
342 } )
343 .then( util.escapeXhtml )
344 .then( function ( xhtml ) {
345 return '<foreignObject x="0" y="0" width="100%" height="100%">' + xhtml + '</foreignObject>';
346 } )
347 .then( function ( foreignObject ) {
348 return '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="' + height + '">' +
349 foreignObject + '</svg>';
350 } )
351 .then( function ( svg ) {
352 return 'data:image/svg+xml;charset=utf-8,' + svg;
353 } );
354 }
355
356 function newUtil() {
357 return {
358 escape: escape,
359 parseExtension: parseExtension,
360 mimeType: mimeType,
361 dataAsUrl: dataAsUrl,
362 isDataUrl: isDataUrl,
363 canvasToBlob: canvasToBlob,
364 resolveUrl: resolveUrl,
365 getAndEncode: getAndEncode,
366 uid: uid(),
367 delay: delay,
368 asArray: asArray,
369 escapeXhtml: escapeXhtml,
370 makeImage: makeImage,
371 width: width,
372 height: height
373 };
374
375 function mimes() {
376 /*
377 * Only WOFF and EOT mime types for fonts are 'real'
378 * see http://www.iana.org/assignments/media-types/media-types.xhtml
379 */
380 var WOFF = 'application/font-woff';
381 var JPEG = 'image/jpeg';
382
383 return {
384 'woff': WOFF,
385 'woff2': WOFF,
386 'ttf': 'application/font-truetype',
387 'eot': 'application/vnd.ms-fontobject',
388 'png': 'image/png',
389 'jpg': JPEG,
390 'jpeg': JPEG,
391 'gif': 'image/gif',
392 'tiff': 'image/tiff',
393 'svg': 'image/svg+xml'
394 };
395 }
396
397 function parseExtension( url ) {
398 var match = /\.([^\.\/]*?)$/g.exec( url );
399 if ( match ) return match[ 1 ];
400 else return '';
401 }
402
403 function mimeType( url ) {
404 var extension = parseExtension( url ).toLowerCase();
405 return mimes()[ extension ] || '';
406 }
407
408 function isDataUrl( url ) {
409 return url.search( /^(data:)/ ) !== -1;
410 }
411
412 function toBlob( canvas ) {
413 return new Promise( function ( resolve ) {
414 var binaryString = window.atob( canvas.toDataURL().split( ',' )[ 1 ] );
415 var length = binaryString.length;
416 var binaryArray = new Uint8Array( length );
417
418 for ( var i = 0; i < length; i++ )
419 binaryArray[ i ] = binaryString.charCodeAt( i );
420
421 resolve( new Blob( [binaryArray], {
422 type: 'image/png'
423 } ) );
424 } );
425 }
426
427 function canvasToBlob( canvas ) {
428 if ( canvas.toBlob )
429 return new Promise( function ( resolve ) {
430 canvas.toBlob( resolve );
431 } );
432
433 return toBlob( canvas );
434 }
435
436 function resolveUrl( url, baseUrl ) {
437 var doc = document.implementation.createHTMLDocument();
438 var base = doc.createElement( 'base' );
439 doc.head.appendChild( base );
440 var a = doc.createElement( 'a' );
441 doc.body.appendChild( a );
442 base.href = baseUrl;
443 a.href = url;
444 return a.href;
445 }
446
447 function uid() {
448 var index = 0;
449
450 return function () {
451 return 'u' + fourRandomChars() + index++;
452
453 function fourRandomChars() {
454 /* see http://stackoverflow.com/a/6248722/2519373 */
455 return ('0000' + (Math.random() * Math.pow( 36, 4 ) << 0).toString( 36 )).slice( -4 );
456 }
457 };
458 }
459
460 function makeImage( uri ) {
461 return new Promise( function ( resolve, reject ) {
462 var image = new Image();
463 image.onload = function () {
464 resolve( image );
465 };
466 image.onerror = reject;
467 image.src = uri;
468 } );
469 }
470
471 function getAndEncode( url ) {
472 var TIMEOUT = 30000;
473 if ( domtoimage.impl.options.cacheBust ) {
474 // Cache bypass so we dont have CORS issues with cached images
475 // Source: https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache
476 url += ((/\?/).test( url ) ? "&" : "?") + (new Date()).getTime();
477 }
478
479 return new Promise( function ( resolve ) {
480 var request = new XMLHttpRequest();
481
482 request.onreadystatechange = done;
483 request.ontimeout = timeout;
484 request.responseType = 'blob';
485 request.timeout = TIMEOUT;
486 request.open( 'GET', url, true );
487 request.send();
488
489 var placeholder;
490 if ( domtoimage.impl.options.imagePlaceholder ) {
491 var split = domtoimage.impl.options.imagePlaceholder.split( /,/ );
492 if ( split && split[ 1 ] ) {
493 placeholder = split[ 1 ];
494 }
495 }
496
497 function done() {
498 if ( request.readyState !== 4 ) return;
499
500 if ( request.status !== 200 ) {
501 if ( placeholder ) {
502 resolve( placeholder );
503 } else {
504 fail( 'cannot fetch resource: ' + url + ', status: ' + request.status );
505 }
506
507 return;
508 }
509
510 var encoder = new FileReader();
511 encoder.onloadend = function () {
512 var content = encoder.result.split( /,/ )[ 1 ];
513 resolve( content );
514 };
515 encoder.readAsDataURL( request.response );
516 }
517
518 function timeout() {
519 if ( placeholder ) {
520 resolve( placeholder );
521 } else {
522 fail( 'timeout of ' + TIMEOUT + 'ms occured while fetching resource: ' + url );
523 }
524 }
525
526 function fail( message ) {
527 console.error( message );
528 resolve( '' );
529 }
530 } );
531 }
532
533 function dataAsUrl( content, type ) {
534 return 'data:' + type + ';base64,' + content;
535 }
536
537 function escape( string ) {
538 return string.replace( /([.*+?^${}()|\[\]\/\\])/g, '\\$1' );
539 }
540
541 function delay( ms ) {
542 return function ( arg ) {
543 return new Promise( function ( resolve ) {
544 setTimeout( function () {
545 resolve( arg );
546 }, ms );
547 } );
548 };
549 }
550
551 function asArray( arrayLike ) {
552 var array = [];
553 var length = arrayLike.length;
554 for ( var i = 0; i < length; i++ ) array.push( arrayLike[ i ] );
555 return array;
556 }
557
558 function escapeXhtml( string ) {
559 return string.replace( /#/g, '%23' ).replace( /\n/g, '%0A' );
560 }
561
562 function width( node ) {
563 var leftBorder = px( node, 'border-left-width' );
564 var rightBorder = px( node, 'border-right-width' );
565 return node.scrollWidth + leftBorder + rightBorder;
566 }
567
568 function height( node ) {
569 var topBorder = px( node, 'border-top-width' );
570 var bottomBorder = px( node, 'border-bottom-width' );
571 return node.scrollHeight + topBorder + bottomBorder;
572 }
573
574 function px( node, styleProperty ) {
575 var value = window.getComputedStyle( node ).getPropertyValue( styleProperty );
576 return parseFloat( value.replace( 'px', '' ) );
577 }
578 }
579
580 function newInliner() {
581 var URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/g;
582
583 return {
584 inlineAll: inlineAll,
585 shouldProcess: shouldProcess,
586 impl: {
587 readUrls: readUrls,
588 inline: inline
589 }
590 };
591
592 function shouldProcess( string ) {
593 return string.search( URL_REGEX ) !== -1;
594 }
595
596 function readUrls( string ) {
597 var result = [];
598 var match;
599 while ( (match = URL_REGEX.exec( string )) !== null ) {
600 result.push( match[ 1 ] );
601 }
602 return result.filter( function ( url ) {
603 return ! util.isDataUrl( url );
604 } );
605 }
606
607 function inline( string, url, baseUrl, get ) {
608 return Promise.resolve( url )
609 .then( function ( url ) {
610 return baseUrl ? util.resolveUrl( url, baseUrl ) : url;
611 } )
612 .then( get || util.getAndEncode )
613 .then( function ( data ) {
614 return util.dataAsUrl( data, util.mimeType( url ) );
615 } )
616 .then( function ( dataUrl ) {
617 return string.replace( urlAsRegex( url ), '$1' + dataUrl + '$3' );
618 } );
619
620 function urlAsRegex( url ) {
621 return new RegExp( '(url\\([\'"]?)(' + util.escape( url ) + ')([\'"]?\\))', 'g' );
622 }
623 }
624
625 function inlineAll( string, baseUrl, get ) {
626 if ( nothingToInline() ) return Promise.resolve( string );
627
628 return Promise.resolve( string )
629 .then( readUrls )
630 .then( function ( urls ) {
631 var done = Promise.resolve( string );
632 urls.forEach( function ( url ) {
633 done = done.then( function ( string ) {
634 return inline( string, url, baseUrl, get );
635 } );
636 } );
637 return done;
638 } );
639
640 function nothingToInline() {
641 return ! shouldProcess( string );
642 }
643 }
644 }
645
646 function newFontFaces() {
647 return {
648 resolveAll: resolveAll,
649 impl: {
650 readAll: readAll
651 }
652 };
653
654 function resolveAll() {
655 return readAll( document )
656 .then( function ( webFonts ) {
657 return Promise.all(
658 webFonts.map( function ( webFont ) {
659 return webFont.resolve();
660 } )
661 );
662 } )
663 .then( function ( cssStrings ) {
664 return cssStrings.join( '\n' );
665 } );
666 }
667
668 function readAll() {
669 return Promise.resolve( util.asArray( document.styleSheets ) )
670 .then( getCssRules )
671 .then( selectWebFontRules )
672 .then( function ( rules ) {
673 return rules.map( newWebFont );
674 } );
675
676 function selectWebFontRules( cssRules ) {
677 return cssRules
678 .filter( function ( rule ) {
679 return rule.type === CSSRule.FONT_FACE_RULE;
680 } )
681 .filter( function ( rule ) {
682 return inliner.shouldProcess( rule.style.getPropertyValue( 'src' ) );
683 } );
684 }
685
686 function getCssRules( styleSheets ) {
687 var cssRules = [];
688 styleSheets.forEach( function ( sheet ) {
689 try {
690 util.asArray( sheet.cssRules || [] ).forEach( cssRules.push.bind( cssRules ) );
691 } catch ( e ) {
692 console.log( 'Error while reading CSS rules from ' + sheet.href, e.toString() );
693 }
694 } );
695 return cssRules;
696 }
697
698 function newWebFont( webFontRule ) {
699 return {
700 resolve: function resolve() {
701 var baseUrl = (webFontRule.parentStyleSheet || {}).href;
702 return inliner.inlineAll( webFontRule.cssText, baseUrl );
703 },
704 src: function () {
705 return webFontRule.style.getPropertyValue( 'src' );
706 }
707 };
708 }
709 }
710 }
711
712 function newImages() {
713 return {
714 inlineAll: inlineAll,
715 impl: {
716 newImage: newImage
717 }
718 };
719
720 function newImage( element ) {
721 return {
722 inline: inline
723 };
724
725 function inline( get ) {
726 if ( util.isDataUrl( element.src ) ) return Promise.resolve();
727
728 return Promise.resolve( element.src )
729 .then( get || util.getAndEncode )
730 .then( function ( data ) {
731 return util.dataAsUrl( data, util.mimeType( element.src ) );
732 } )
733 .then( function ( dataUrl ) {
734 return new Promise( function ( resolve, reject ) {
735 element.onload = resolve;
736 element.onerror = reject;
737 element.src = dataUrl;
738 } );
739 } );
740 }
741 }
742
743 function inlineAll( node ) {
744 if ( ! (node instanceof Element) ) return Promise.resolve( node );
745
746 return inlineBackground( node )
747 .then( function () {
748 if ( node instanceof HTMLImageElement )
749 return newImage( node ).inline();
750 else
751 return Promise.all(
752 util.asArray( node.childNodes ).map( function ( child ) {
753 return inlineAll( child );
754 } )
755 );
756 } );
757
758 function inlineBackground( node ) {
759 var background = node.style.getPropertyValue( 'background' );
760
761 if ( ! background ) return Promise.resolve( node );
762
763 return inliner.inlineAll( background )
764 .then( function ( inlined ) {
765 node.style.setProperty(
766 'background',
767 inlined,
768 node.style.getPropertyPriority( 'background' )
769 );
770 } )
771 .then( function () {
772 return node;
773 } );
774 }
775 }
776 }
777 })( this );
778