@sswroom/sswr 1.6.19 → 1.6.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Changelog +20 -0
- package/cesium.d.ts +1 -0
- package/cesium.js +6 -1
- package/data.d.ts +3 -0
- package/data.js +93 -0
- package/domtoimage/fontFaces.js +56 -0
- package/domtoimage/images.js +49 -0
- package/domtoimage/index.js +333 -0
- package/domtoimage/inliner.js +67 -0
- package/domtoimage/util.js +142 -0
- package/dummy/Cesium.d.ts +1 -0
- package/dummy/Cesium.js +1 -0
- package/exporter/XLSXExporter.js +578 -524
- package/leaflet/EasyPrint.js +739 -0
- package/package.json +1 -1
- package/parser.js +20 -0
- package/text.d.ts +3 -0
- package/text.js +24 -0
- package/web.d.ts +19 -0
- package/web.js +117 -0
package/Changelog
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
1.6.21
|
|
2
|
+
-Added data.dataURI2Blob
|
|
3
|
+
-Added EasyPrint.toSVG
|
|
4
|
+
-Added data.blob2DataURL
|
|
5
|
+
-Added data.fetchAsBlob
|
|
6
|
+
-Added text.isDataURL
|
|
7
|
+
-Added text.escapeXhtml
|
|
8
|
+
-Added text.svgStringToDataURI
|
|
9
|
+
-Added web.canvasToBlob
|
|
10
|
+
-Added web.elementToSVGString
|
|
11
|
+
-Added web.genPrintWindowHTML
|
|
12
|
+
-Added web.printImageData
|
|
13
|
+
-Added cesium.CesiumMap.getViewer
|
|
14
|
+
-parser support PNG without EXIF
|
|
15
|
+
|
|
16
|
+
1.6.20
|
|
17
|
+
-Enhance XLSXExporter
|
|
18
|
+
-Added leaflet/EasyPrint.js
|
|
19
|
+
-Added domtoimage/index.js
|
|
20
|
+
|
|
1
21
|
1.6.19
|
|
2
22
|
-data.Timestamp.fromTicks support floating point ticks
|
|
3
23
|
-data.Duration.fromTicks support floating point ticks
|
package/cesium.d.ts
CHANGED
package/cesium.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as data from "./data.js";
|
|
2
|
+
import * as geometry from "./geometry.js";
|
|
2
3
|
import * as kml from "./kml.js";
|
|
3
4
|
import * as map from "./map.js";
|
|
4
5
|
import * as math from "./math.js";
|
|
5
|
-
import * as
|
|
6
|
+
import * as text from "./text.js";
|
|
6
7
|
|
|
7
8
|
export function screenToLatLon(viewer, x, y, ellipsoid)
|
|
8
9
|
{
|
|
@@ -496,4 +497,8 @@ export class CesiumMap extends map.MapControl
|
|
|
496
497
|
layerAddGeometry(geometryLayer: any, geom: any): void;
|
|
497
498
|
layerRemoveGeometry(geometryLayer: any, geom: any): void;
|
|
498
499
|
layerClearGeometries(geometryLayer: any): void;*/
|
|
500
|
+
getViewer()
|
|
501
|
+
{
|
|
502
|
+
return this.viewer;
|
|
503
|
+
}
|
|
499
504
|
}
|
package/data.d.ts
CHANGED
|
@@ -39,6 +39,9 @@ export function shl32(v: number, n: number): number;
|
|
|
39
39
|
export function sar32(v: number, n: number): number;
|
|
40
40
|
export function shr32(v: number, n: number): number;
|
|
41
41
|
export function objectParseTS(o: object, items: string[]): void;
|
|
42
|
+
export function dataURI2Blob(dataURI: string): Blob;
|
|
43
|
+
export function blob2DataURL(blob: Blob): Promise<string|null>;
|
|
44
|
+
export function fetchAsBlob(url: string, options?: {cacheBust: boolean; imagePlaceholder?: string}): Promise<Blob|null>;
|
|
42
45
|
|
|
43
46
|
export class DateValue
|
|
44
47
|
{
|
package/data.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as data from "./data.js";
|
|
1
2
|
import * as text from "./text.js";
|
|
2
3
|
|
|
3
4
|
export const Weekday = {
|
|
@@ -455,6 +456,98 @@ export function objectParseTS(o, items)
|
|
|
455
456
|
}
|
|
456
457
|
}
|
|
457
458
|
|
|
459
|
+
/**
|
|
460
|
+
* @param {string} dataURI
|
|
461
|
+
*/
|
|
462
|
+
export function dataURI2Blob(dataURI)
|
|
463
|
+
{
|
|
464
|
+
let i = dataURI.indexOf(",");
|
|
465
|
+
let dsp = [dataURI.substring(0, i), dataURI.substring(i + 1)];
|
|
466
|
+
let types = dsp[0].split(':')[1].split(';');
|
|
467
|
+
let mimeString = types[0];
|
|
468
|
+
let ab;
|
|
469
|
+
if (types[1] == "base64")
|
|
470
|
+
{
|
|
471
|
+
let byteString = atob(dsp[1]);
|
|
472
|
+
ab = new ArrayBuffer(byteString.length);
|
|
473
|
+
let dw = new DataView(ab);
|
|
474
|
+
for(let i = 0; i < byteString.length; i++) {
|
|
475
|
+
dw.setUint8(i, byteString.charCodeAt(i));
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
else
|
|
479
|
+
{
|
|
480
|
+
let enc = new TextEncoder();
|
|
481
|
+
ab = enc.encode(dsp[1]);
|
|
482
|
+
}
|
|
483
|
+
return new Blob([ab], {type: mimeString});
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* @param {Blob} blob
|
|
488
|
+
*/
|
|
489
|
+
export function blob2DataURL(blob)
|
|
490
|
+
{
|
|
491
|
+
return new Promise((resolve, reject) => {
|
|
492
|
+
let encoder = new FileReader();
|
|
493
|
+
encoder.onloadend = function () {
|
|
494
|
+
let content = encoder.result;
|
|
495
|
+
if (typeof content == 'string')
|
|
496
|
+
resolve(content);
|
|
497
|
+
else
|
|
498
|
+
{
|
|
499
|
+
console.error("Error in converting to dataURL", content);
|
|
500
|
+
resolve(null);
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
encoder.readAsDataURL(blob);
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* @param {string} url
|
|
509
|
+
* @param {{ cacheBust: boolean; imagePlaceholder: string; }} options
|
|
510
|
+
*/
|
|
511
|
+
export async function fetchAsBlob(url, options)
|
|
512
|
+
{
|
|
513
|
+
let TIMEOUT = 30000;
|
|
514
|
+
if(options && options.cacheBust) {
|
|
515
|
+
// Cache bypass so we dont have CORS issues with cached images
|
|
516
|
+
// Source: https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache
|
|
517
|
+
url += ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime();
|
|
518
|
+
}
|
|
519
|
+
let placeholder;
|
|
520
|
+
if(options && options.imagePlaceholder) {
|
|
521
|
+
if (text.isDataURL(options.imagePlaceholder))
|
|
522
|
+
{
|
|
523
|
+
placeholder = data.dataURI2Blob(options.imagePlaceholder);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
try
|
|
527
|
+
{
|
|
528
|
+
let req = await fetch(url, {signal: AbortSignal.timeout(TIMEOUT)});
|
|
529
|
+
if (!req.ok)
|
|
530
|
+
{
|
|
531
|
+
if(placeholder) {
|
|
532
|
+
return placeholder;
|
|
533
|
+
} else {
|
|
534
|
+
console.error('Cannot fetch resource: ' + url + ', status: ' + req.status);
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return await req.blob();
|
|
539
|
+
}
|
|
540
|
+
catch (error)
|
|
541
|
+
{
|
|
542
|
+
if(placeholder) {
|
|
543
|
+
return placeholder;
|
|
544
|
+
} else {
|
|
545
|
+
console.error('Timeout of ' + TIMEOUT + 'ms occured while fetching resource: ' + url, error);
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
458
551
|
export class DateValue
|
|
459
552
|
{
|
|
460
553
|
constructor()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as util from "./util.js";
|
|
2
|
+
import * as inliner from "./inliner.js";
|
|
3
|
+
|
|
4
|
+
export function resolveAll() {
|
|
5
|
+
return readAll()
|
|
6
|
+
.then(function (webFonts) {
|
|
7
|
+
return Promise.all(
|
|
8
|
+
webFonts.map(function (webFont) {
|
|
9
|
+
return webFont.resolve();
|
|
10
|
+
})
|
|
11
|
+
);
|
|
12
|
+
})
|
|
13
|
+
.then(function (cssStrings) {
|
|
14
|
+
return cssStrings.join('\n');
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {{ cacheBust: boolean; imagePlaceholder?: string; } | undefined} [implOptions]
|
|
20
|
+
*/
|
|
21
|
+
async function readAll(implOptions) {
|
|
22
|
+
let styleSheets = [];
|
|
23
|
+
for (let i = 0; i < document.styleSheets.length; i++) styleSheets.push(document.styleSheets[i]);
|
|
24
|
+
let cssRules = [];
|
|
25
|
+
styleSheets.forEach(function (sheet) {
|
|
26
|
+
try {
|
|
27
|
+
if (sheet.cssRules)
|
|
28
|
+
{
|
|
29
|
+
for (let i = 0; i < sheet.cssRules.length; i++) cssRules.push(sheet.cssRules[i]);
|
|
30
|
+
}
|
|
31
|
+
} catch (e) {
|
|
32
|
+
console.log('Error while reading CSS rules from ' + sheet.href, e.toString());
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
/** @type {CSSRule[]} */
|
|
36
|
+
let rules = cssRules
|
|
37
|
+
.filter(function (rule) {
|
|
38
|
+
return rule.type === CSSRule.FONT_FACE_RULE;
|
|
39
|
+
})
|
|
40
|
+
.filter(function (rule) {
|
|
41
|
+
return inliner.shouldProcess(rule.style.getPropertyValue('src'));
|
|
42
|
+
});
|
|
43
|
+
return rules.map((webFontRule) => {
|
|
44
|
+
return {
|
|
45
|
+
resolve: function resolve() {
|
|
46
|
+
let baseUrl = (webFontRule.parentStyleSheet || {}).href;
|
|
47
|
+
return inliner.inlineAll(webFontRule.cssText, baseUrl, undefined, implOptions);
|
|
48
|
+
},
|
|
49
|
+
src: function () {
|
|
50
|
+
// @ts-ignore
|
|
51
|
+
return webFontRule.style.getPropertyValue('src');
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as data from "../data.js";
|
|
2
|
+
import * as text from "../text.js";
|
|
3
|
+
import * as util from "./util.js";
|
|
4
|
+
import * as inliner from "./inliner.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {HTMLImageElement} element
|
|
8
|
+
* @param {{ cacheBust: boolean; imagePlaceholder?: string; } | undefined} [options]
|
|
9
|
+
* @returns {Promise<HTMLImageElement>}
|
|
10
|
+
*/
|
|
11
|
+
async function inline(element, options) {
|
|
12
|
+
if (text.isDataURL(element.src)) return element;
|
|
13
|
+
let blob = await data.fetchAsBlob(element.src, options);
|
|
14
|
+
let dataUrl = await data.blob2DataURL(blob);
|
|
15
|
+
return await new Promise(function (resolve, reject) {
|
|
16
|
+
element.onload = () => {resolve(element);};
|
|
17
|
+
element.onerror = reject;
|
|
18
|
+
element.src = dataUrl;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {HTMLElement} node
|
|
24
|
+
* @param {{ cacheBust: boolean; imagePlaceholder?: string; } | undefined} options
|
|
25
|
+
*/
|
|
26
|
+
export async function inlineAll(node, options) {
|
|
27
|
+
if (!(node instanceof HTMLElement)) return node;
|
|
28
|
+
let background = node.style.getPropertyValue('background');
|
|
29
|
+
|
|
30
|
+
if (!background) return node;
|
|
31
|
+
|
|
32
|
+
let inlined = await inliner.inlineAll(background, undefined, undefined, options)
|
|
33
|
+
node.style.setProperty(
|
|
34
|
+
'background',
|
|
35
|
+
inlined,
|
|
36
|
+
node.style.getPropertyPriority('background'));
|
|
37
|
+
if (node instanceof HTMLImageElement)
|
|
38
|
+
return await inline(node);
|
|
39
|
+
else
|
|
40
|
+
{
|
|
41
|
+
let children = util.asArray(node.childNodes);
|
|
42
|
+
let leng = children.length;
|
|
43
|
+
for (let i = 0; i < leng; i++)
|
|
44
|
+
{
|
|
45
|
+
node.replaceChild(children[i], await inlineAll(children[i], options));
|
|
46
|
+
}
|
|
47
|
+
return node;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import * as text from "../text.js";
|
|
2
|
+
import * as web from "../web.js";
|
|
3
|
+
import * as util from "./util.js";
|
|
4
|
+
import * as images from "./images.js";
|
|
5
|
+
import * as fontFaces from "./fontFaces.js";
|
|
6
|
+
|
|
7
|
+
// Default impl options
|
|
8
|
+
const defaultOptions = {
|
|
9
|
+
// Default is to fail on error, no placeholder
|
|
10
|
+
imagePlaceholder: undefined,
|
|
11
|
+
// Default cache bust is false, it will use the cache
|
|
12
|
+
cacheBust: false
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {HTMLElement} node - The DOM Node object to render
|
|
17
|
+
* @param {{filter?:(node: Node)=>boolean,bgcolor?:string,width?:number,height?:number,style?:{[n:string]:string},quality?:number,imagePlaceholder?:string,cacheBust?:boolean}|null|undefined} options - Rendering options
|
|
18
|
+
**/
|
|
19
|
+
export async function toSvg(node, options) {
|
|
20
|
+
options = options || {};
|
|
21
|
+
let implOptions = copyOptions(options);
|
|
22
|
+
let clone = await cloneNode(node, options.filter, true);
|
|
23
|
+
clone = await embedFonts(clone);
|
|
24
|
+
clone = await inlineImages(clone, implOptions);
|
|
25
|
+
if (options.bgcolor) clone.style.backgroundColor = options.bgcolor;
|
|
26
|
+
if (options.width) clone.style.width = options.width + 'px';
|
|
27
|
+
if (options.height) clone.style.height = options.height + 'px';
|
|
28
|
+
if (options.style)
|
|
29
|
+
{
|
|
30
|
+
Object.keys(options.style).forEach(function (property) {
|
|
31
|
+
clone.style[property] = options.style[property];
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return makeSvgDataUri(clone,
|
|
35
|
+
options.width || util.width(node),
|
|
36
|
+
options.height || util.height(node)
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {HTMLElement} node - The DOM Node object to render
|
|
42
|
+
* @param {{filter?:(node: Node)=>boolean,bgcolor?:string,width?:number,height?:number,style?:{[n:string]:string},quality?:number,imagePlaceholder?:string,cacheBust?:boolean}|null|undefined} options - Rendering options, @see {@link toSvg}
|
|
43
|
+
* */
|
|
44
|
+
export function toPixelData(node, options) {
|
|
45
|
+
return draw(node, options || {})
|
|
46
|
+
.then(function (canvas) {
|
|
47
|
+
let ctx = canvas.getContext('2d');
|
|
48
|
+
if (ctx)
|
|
49
|
+
{
|
|
50
|
+
return ctx.getImageData(
|
|
51
|
+
0,
|
|
52
|
+
0,
|
|
53
|
+
util.width(node),
|
|
54
|
+
util.height(node)
|
|
55
|
+
).data;
|
|
56
|
+
}
|
|
57
|
+
else
|
|
58
|
+
{
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {HTMLElement} node - The DOM Node object to render
|
|
66
|
+
* @param {{filter?:(node: Node)=>boolean,bgcolor?:string,width?:number,height?:number,style?:{[n:string]:string},quality?:number,imagePlaceholder?:string,cacheBust?:boolean}|null|undefined} options - Rendering options, @see {@link toSvg}
|
|
67
|
+
* */
|
|
68
|
+
export async function toPng(node, options) {
|
|
69
|
+
let canvas = await draw(node, options || {});
|
|
70
|
+
return canvas.toDataURL();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {HTMLElement} node - The DOM Node object to render
|
|
75
|
+
* @param {{filter?:(node: Node)=>boolean,bgcolor?:string,width?:number,height?:number,style?:{[n:string]:string},quality?:number,imagePlaceholder?:string,cacheBust?:boolean}|null|undefined} options - Rendering options, @see {@link toSvg}
|
|
76
|
+
* */
|
|
77
|
+
export async function toJpeg(node, options) {
|
|
78
|
+
options = options || {};
|
|
79
|
+
let canvas = await draw(node, options || {});
|
|
80
|
+
return canvas.toDataURL('image/jpeg', options.quality || 1.0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {HTMLElement} node - The DOM Node object to render
|
|
85
|
+
* @param {{filter?:(node: Node)=>boolean,bgcolor?:string,width?:number,height?:number,style?:{[n:string]:string},quality?:number,imagePlaceholder?:string,cacheBust?:boolean}|null|undefined} options - Rendering options, @see {@link toSvg}
|
|
86
|
+
* */
|
|
87
|
+
export async function toBlob(node, options) {
|
|
88
|
+
let canvas = await draw(node, options || {});
|
|
89
|
+
return await web.canvasToBlob(canvas);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @param {{filter?:(node: Node)=>boolean,bgcolor?:string,width?:number,height?:number,style?:{[n:string]:string},quality?:number,imagePlaceholder?:string,cacheBust?:boolean}} options
|
|
94
|
+
*/
|
|
95
|
+
function copyOptions(options) {
|
|
96
|
+
let implOptions = {};
|
|
97
|
+
// Copy options to impl options for use in impl
|
|
98
|
+
if(typeof(options.imagePlaceholder) === 'undefined') {
|
|
99
|
+
implOptions.imagePlaceholder = defaultOptions.imagePlaceholder;
|
|
100
|
+
} else {
|
|
101
|
+
implOptions.imagePlaceholder = options.imagePlaceholder;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if(typeof(options.cacheBust) === 'undefined') {
|
|
105
|
+
implOptions.cacheBust = defaultOptions.cacheBust;
|
|
106
|
+
} else {
|
|
107
|
+
implOptions.cacheBust = options.cacheBust;
|
|
108
|
+
}
|
|
109
|
+
return implOptions;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @param {HTMLElement} domNode
|
|
114
|
+
* @param {{filter?:(node: Node)=>boolean,bgcolor?:string,width?:number,height?:number,style?:{[n:string]:string},quality?:number,imagePlaceholder?:string,cacheBust?:boolean}} options
|
|
115
|
+
*/
|
|
116
|
+
async function draw(domNode, options) {
|
|
117
|
+
let svgUrl = await toSvg(domNode, options);
|
|
118
|
+
let image = await util.makeImage(svgUrl);
|
|
119
|
+
let canvas = newCanvas(domNode, options);
|
|
120
|
+
let ctx = canvas.getContext('2d');
|
|
121
|
+
return await new Promise((resolve, reject) => {
|
|
122
|
+
setTimeout(() => {
|
|
123
|
+
if (ctx)
|
|
124
|
+
{
|
|
125
|
+
ctx.drawImage(image, 0, 0);
|
|
126
|
+
}
|
|
127
|
+
resolve(canvas);
|
|
128
|
+
}, 1);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @param {HTMLElement} domNode
|
|
134
|
+
* @param {{filter?:(node: Node)=>boolean,bgcolor?:string,width?:number,height?:number,style?:{[n:string]:string},quality?:number,imagePlaceholder?:string,cacheBust?:boolean}} options
|
|
135
|
+
*/
|
|
136
|
+
function newCanvas(domNode, options) {
|
|
137
|
+
let canvas = document.createElement('canvas');
|
|
138
|
+
canvas.width = options.width || domNode.offsetWidth;
|
|
139
|
+
canvas.height = options.height || domNode.offsetHeight;
|
|
140
|
+
|
|
141
|
+
if (options.bgcolor) {
|
|
142
|
+
let ctx = canvas.getContext('2d');
|
|
143
|
+
if (ctx != null)
|
|
144
|
+
{
|
|
145
|
+
ctx.globalAlpha = 1;
|
|
146
|
+
ctx.fillStyle = options.bgcolor;
|
|
147
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return canvas;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @param {HTMLElement} node
|
|
157
|
+
* @param {(node:Node)=>boolean|undefined} filter
|
|
158
|
+
* @param {boolean | undefined} [root]
|
|
159
|
+
*/
|
|
160
|
+
async function cloneNode(node, filter, root) {
|
|
161
|
+
if (!root && filter && !filter(node)) return node;
|
|
162
|
+
if (node instanceof HTMLCanvasElement) return util.makeImage(node.toDataURL());
|
|
163
|
+
let clone = node.cloneNode(false);
|
|
164
|
+
clone = await cloneChildren(node, clone, filter);
|
|
165
|
+
// @ts-ignore
|
|
166
|
+
return await processClone(node, clone);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @param {Node} original
|
|
171
|
+
* @param {Node} clone
|
|
172
|
+
* @param {(node: Node) => boolean | undefined} filter
|
|
173
|
+
*/
|
|
174
|
+
async function cloneChildren(original, clone, filter) {
|
|
175
|
+
var children = original.childNodes;
|
|
176
|
+
if (children.length === 0) return clone;
|
|
177
|
+
for (let i = 0; i < children.length; i++)
|
|
178
|
+
{
|
|
179
|
+
let child = children[i];
|
|
180
|
+
// @ts-ignore
|
|
181
|
+
let childClone = await cloneNode(child, filter);
|
|
182
|
+
if (childClone) clone.appendChild(childClone);
|
|
183
|
+
}
|
|
184
|
+
return clone;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @param {HTMLElement} original
|
|
189
|
+
* @param {HTMLElement} clone
|
|
190
|
+
*/
|
|
191
|
+
function processClone(original, clone) {
|
|
192
|
+
if (!(clone instanceof Element)) return clone;
|
|
193
|
+
|
|
194
|
+
return Promise.resolve()
|
|
195
|
+
.then(cloneStyle)
|
|
196
|
+
.then(clonePseudoElements)
|
|
197
|
+
.then(copyUserInput)
|
|
198
|
+
.then(fixSvg)
|
|
199
|
+
.then(function () {
|
|
200
|
+
return clone;
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
function cloneStyle() {
|
|
204
|
+
copyStyle(window.getComputedStyle(original), clone.style);
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @param {CSSStyleDeclaration} source
|
|
208
|
+
* @param {CSSStyleDeclaration} target
|
|
209
|
+
*/
|
|
210
|
+
function copyStyle(source, target) {
|
|
211
|
+
if (source.cssText) target.cssText = source.cssText;
|
|
212
|
+
else copyProperties(source, target);
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @param {CSSStyleDeclaration} source
|
|
216
|
+
* @param {{ setProperty: (arg0: any, arg1: any, arg2: any) => void; }} target
|
|
217
|
+
*/
|
|
218
|
+
function copyProperties(source, target) {
|
|
219
|
+
for (let i = 0; i < source.length; i++)
|
|
220
|
+
{
|
|
221
|
+
let name = source[i];
|
|
222
|
+
target.setProperty(
|
|
223
|
+
name,
|
|
224
|
+
source.getPropertyValue(name),
|
|
225
|
+
source.getPropertyPriority(name)
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function clonePseudoElements() {
|
|
233
|
+
[':before', ':after'].forEach(function (element) {
|
|
234
|
+
clonePseudoElement(element);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @param {string} element
|
|
239
|
+
*/
|
|
240
|
+
function clonePseudoElement(element) {
|
|
241
|
+
var style = window.getComputedStyle(original, element);
|
|
242
|
+
var content = style.getPropertyValue('content');
|
|
243
|
+
|
|
244
|
+
if (content === '' || content === 'none') return;
|
|
245
|
+
|
|
246
|
+
var className = util.uid();
|
|
247
|
+
clone.className = clone.className + ' ' + className;
|
|
248
|
+
var styleElement = document.createElement('style');
|
|
249
|
+
styleElement.appendChild(formatPseudoElementStyle(className, element, style));
|
|
250
|
+
clone.appendChild(styleElement);
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @param {string} className
|
|
254
|
+
* @param {string} element
|
|
255
|
+
* @param {CSSStyleDeclaration} style
|
|
256
|
+
*/
|
|
257
|
+
function formatPseudoElementStyle(className, element, style) {
|
|
258
|
+
let selector = '.' + className + ':' + element;
|
|
259
|
+
let cssText = style.cssText ? formatCssText(style) : formatCssProperties(style);
|
|
260
|
+
return document.createTextNode(selector + '{' + cssText + '}');
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @param {CSSStyleDeclaration} style
|
|
264
|
+
*/
|
|
265
|
+
function formatCssText(style) {
|
|
266
|
+
let content = style.getPropertyValue('content');
|
|
267
|
+
return style.cssText + ' content: ' + content + ';';
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* @param {CSSStyleDeclaration} style
|
|
272
|
+
*/
|
|
273
|
+
function formatCssProperties(style) {
|
|
274
|
+
let css = [];
|
|
275
|
+
for (let i = 0; i < style.length; i++)
|
|
276
|
+
{
|
|
277
|
+
let name = style[i];
|
|
278
|
+
css.push(name + ': ' +
|
|
279
|
+
style.getPropertyValue(name) +
|
|
280
|
+
(style.getPropertyPriority(name) ? ' !important' : ''));
|
|
281
|
+
}
|
|
282
|
+
return css.join('; ') + ';';
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function copyUserInput() {
|
|
289
|
+
if (original instanceof HTMLTextAreaElement) clone.innerHTML = original.value;
|
|
290
|
+
if (original instanceof HTMLInputElement) clone.setAttribute("value", original.value);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function fixSvg() {
|
|
294
|
+
if (!(clone instanceof SVGElement)) return;
|
|
295
|
+
clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
|
296
|
+
|
|
297
|
+
if (!(clone instanceof SVGRectElement)) return;
|
|
298
|
+
['width', 'height'].forEach(function (attribute) {
|
|
299
|
+
var value = clone.getAttribute(attribute);
|
|
300
|
+
if (!value) return;
|
|
301
|
+
|
|
302
|
+
clone.style.setProperty(attribute, value);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* @param {HTMLElement} node
|
|
309
|
+
*/
|
|
310
|
+
async function embedFonts(node) {
|
|
311
|
+
let cssText = await fontFaces.resolveAll();
|
|
312
|
+
let styleNode = document.createElement('style');
|
|
313
|
+
node.appendChild(styleNode);
|
|
314
|
+
styleNode.appendChild(document.createTextNode(cssText));
|
|
315
|
+
return node;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* @param {HTMLElement} node
|
|
320
|
+
* @param {{ cacheBust: boolean; imagePlaceholder?: string; } | undefined} implOptions
|
|
321
|
+
*/
|
|
322
|
+
async function inlineImages(node, implOptions) {
|
|
323
|
+
return await images.inlineAll(node, implOptions);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* @param {Element} node
|
|
328
|
+
* @param {string | number} width
|
|
329
|
+
* @param {string | number} height
|
|
330
|
+
*/
|
|
331
|
+
function makeSvgDataUri(node, width, height) {
|
|
332
|
+
return text.svgStringToDataURI(web.elementToSVGString(node, width, height));
|
|
333
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as data from "../data.js";
|
|
2
|
+
import * as text from "../text.js";
|
|
3
|
+
import * as util from "./util.js";
|
|
4
|
+
|
|
5
|
+
let URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/g;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {string} string
|
|
9
|
+
*/
|
|
10
|
+
export function shouldProcess(string) {
|
|
11
|
+
return string.search(URL_REGEX) !== -1;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} string
|
|
16
|
+
*/
|
|
17
|
+
function readUrls(string) {
|
|
18
|
+
var result = [];
|
|
19
|
+
var match;
|
|
20
|
+
while ((match = URL_REGEX.exec(string)) !== null) {
|
|
21
|
+
result.push(match[1]);
|
|
22
|
+
}
|
|
23
|
+
return result.filter(function (url) {
|
|
24
|
+
return !text.isDataURL(url);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {string} url
|
|
30
|
+
*/
|
|
31
|
+
function urlAsRegex(url) {
|
|
32
|
+
return new RegExp('(url\\([\'"]?)(' + util.escape(url) + ')([\'"]?\\))', 'g');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {string} string
|
|
37
|
+
* @param {string} url
|
|
38
|
+
* @param {string | null | undefined} baseUrl
|
|
39
|
+
* @param {((url: string)=>Blob)|undefined} get
|
|
40
|
+
* @param {{ cacheBust: boolean; imagePlaceholder?: string; } | undefined} options
|
|
41
|
+
*/
|
|
42
|
+
async function inline(string, url, baseUrl, get, options) {
|
|
43
|
+
url = baseUrl ? util.resolveUrl(url, baseUrl) : url;
|
|
44
|
+
let blob;
|
|
45
|
+
if (get)
|
|
46
|
+
blob = get(url);
|
|
47
|
+
else
|
|
48
|
+
blob = await data.fetchAsBlob(url, options);
|
|
49
|
+
let dataUrl = await data.blob2DataURL(blob);
|
|
50
|
+
return string.replace(urlAsRegex(url), '$1' + dataUrl + '$3');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {string} string
|
|
55
|
+
* @param {string | null | undefined} baseUrl
|
|
56
|
+
* @param {((url: string)=>Blob)|undefined} get
|
|
57
|
+
* @param {{ cacheBust: boolean; imagePlaceholder?: string; } | undefined} options
|
|
58
|
+
*/
|
|
59
|
+
export async function inlineAll(string, baseUrl, get, options) {
|
|
60
|
+
if (!shouldProcess(string)) return string;
|
|
61
|
+
let urls = readUrls(string);
|
|
62
|
+
let done = string;
|
|
63
|
+
urls.forEach(async function (url) {
|
|
64
|
+
done = await inline(done, url, baseUrl, get, options);
|
|
65
|
+
});
|
|
66
|
+
return done;
|
|
67
|
+
}
|