diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3201b22788..be768cfac5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,10 +20,11 @@ All notable changes to the Wazuh app project will be documented in this file.
- Added support for agents to Office 365 [#6558](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6558)
- Added pinned agent data validation when rendering the Inventory data, Stats and Configuration tabs in Agent preview of Endpoints Summary [#6800](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6800)
- Added wz-link component to make redirections [#6848](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6848)
+- Add embedded and customized `dom-to-image-more` dependency [#6902](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6902)
### Changed
-- Removed embedded discover [#6120](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6120) [#6235](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6235) [#6254](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6254) [#6285](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6285) [#6288](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6288) [#6290](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6290) [#6289](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6289) [#6286](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6286) [#6275](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6275) [#6287](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6287) [#6297](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6297) [#6291](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6287) [#6459](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6459) [#6434](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6434) [#6504](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6504) [#6649](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6649) [#6506](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6506) [#6537](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6537) [#6528](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6528) [#6675](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6675) [#6674](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6674) [#6558](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6558) [#6685](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6685) [#6691](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6691) [#6712](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6712) [#6734](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6734) [#6746](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6746) [#6752](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6752) [#6753](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6753) [#6756](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6756) [#6771](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6771) [#6792](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6792) [#6845](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6845) [#6857](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6857) [#6847](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6847) [#6865](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6865) [#6848](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6848) [#6843](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6843) [#6878](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6878) [#6883](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6883) [#6889](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6889)
+- Removed embedded discover [#6120](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6120) [#6235](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6235) [#6254](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6254) [#6285](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6285) [#6288](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6288) [#6290](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6290) [#6289](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6289) [#6286](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6286) [#6275](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6275) [#6287](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6287) [#6297](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6297) [#6291](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6287) [#6459](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6459) [#6434](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6434) [#6504](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6504) [#6649](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6649) [#6506](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6506) [#6537](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6537) [#6528](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6528) [#6675](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6675) [#6674](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6674) [#6558](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6558) [#6685](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6685) [#6691](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6691) [#6712](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6712) [#6734](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6734) [#6746](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6746) [#6752](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6752) [#6753](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6753) [#6756](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6756) [#6771](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6771) [#6792](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6792) [#6845](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6845) [#6857](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6857) [#6847](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6847) [#6865](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6865) [#6848](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6848) [#6843](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6843) [#6878](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6878) [#6883](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6883) [#6889](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6889) [#6902](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6902)
- Allow editing groups for an agent from Endpoints Summary [#6250](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6250)
- Change how the configuration is managed in the backend side [#6337](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6337) [#6519](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6519) [#6573](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6573)
- Change the view of API is down and check connection to Server APIs application [#6337](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6337)
@@ -81,6 +82,7 @@ All notable changes to the Wazuh app project will be documented in this file.
- Removed the `App logs` application [#6161](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6161)
- Removed API endpoint GET /utils/logs/ui [#6161](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6161)
- Removed API endpoint GET /utils/logs [#6161](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6161)
+- Removed embedded `dom-to-image` dependency [#6902](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6902)
## Wazuh v4.8.1 - OpenSearch Dashboards 2.10.0 - Revision 04
diff --git a/plugins/main/public/app.js b/plugins/main/public/app.js
index efc0d877fd..1fe4f431c0 100644
--- a/plugins/main/public/app.js
+++ b/plugins/main/public/app.js
@@ -15,5 +15,3 @@ import './styles';
import './utils/fontawesome/scss/font-awesome.scss';
// Dev tools
import './utils/codemirror';
-// Require lib to dashboards PDFs
-require('./utils/dom-to-image.js');
diff --git a/plugins/main/public/components/common/modules/buttons/generate_report.tsx b/plugins/main/public/components/common/modules/buttons/generate_report.tsx
index 311658588f..097bae7d6a 100644
--- a/plugins/main/public/components/common/modules/buttons/generate_report.tsx
+++ b/plugins/main/public/components/common/modules/buttons/generate_report.tsx
@@ -38,7 +38,9 @@ export const ButtonModuleGenerateReport = connect(mapStateToProps)(
const defaultTextColor = '#DFE5EF';
//Patch to fix dark backgrounds in visualizations dark-mode pdf reports
- const $labels = $('.euiButtonEmpty__text, .echLegendItem');
+ const $labels = $(
+ '.euiButtonEmpty__text, .echLegendItem, div.mtrVis__value ~ div',
+ );
const $vizBackground = $('.echChartBackground');
const defaultVizBackground = $vizBackground.css('background-color');
diff --git a/plugins/main/public/components/overview/hipaa/dashboards/dashboard-panels.ts b/plugins/main/public/components/overview/hipaa/dashboards/dashboard-panels.ts
index dcd53ff99c..ca68cf0d8b 100644
--- a/plugins/main/public/components/overview/hipaa/dashboards/dashboard-panels.ts
+++ b/plugins/main/public/components/overview/hipaa/dashboards/dashboard-panels.ts
@@ -366,10 +366,21 @@ const getVisStateStats = (indexPatternId: string) => {
metric: {
percentageMode: false,
useRanges: false,
- colorSchema: 'Green to Red',
- metricColorMode: 'None',
- colorsRange: [{ type: 'range', from: 0, to: 10000 }],
- labels: { show: true },
+ colorSchema: 'Greens',
+ metricColorMode: 'Labels',
+ colorsRange: [
+ {
+ from: 0,
+ to: 0,
+ },
+ {
+ from: 0,
+ to: 0,
+ },
+ ],
+ labels: {
+ show: true,
+ },
invertColors: false,
style: {
bgFill: '#000',
diff --git a/plugins/main/public/components/overview/nist/dashboards/dashboard-panels.ts b/plugins/main/public/components/overview/nist/dashboards/dashboard-panels.ts
index 34d5069697..f7271f7f73 100644
--- a/plugins/main/public/components/overview/nist/dashboards/dashboard-panels.ts
+++ b/plugins/main/public/components/overview/nist/dashboards/dashboard-panels.ts
@@ -554,10 +554,21 @@ const getVisStateMetrics = (indexPatternId: string) => {
metric: {
percentageMode: false,
useRanges: false,
- colorSchema: 'Green to Red',
- metricColorMode: 'None',
- colorsRange: [{ type: 'range', from: 0, to: 10000 }],
- labels: { show: true },
+ colorSchema: 'Greens',
+ metricColorMode: 'Labels',
+ colorsRange: [
+ {
+ from: 0,
+ to: 0,
+ },
+ {
+ from: 0,
+ to: 0,
+ },
+ ],
+ labels: {
+ show: true,
+ },
invertColors: false,
style: {
bgFill: '#000',
@@ -722,10 +733,21 @@ const getVisStateAgentStats = (indexPatternId: string) => {
metric: {
percentageMode: false,
useRanges: false,
- colorSchema: 'Green to Red',
- metricColorMode: 'None',
- colorsRange: [{ type: 'range', from: 0, to: 10000 }],
- labels: { show: true },
+ colorSchema: 'Greens',
+ metricColorMode: 'Labels',
+ colorsRange: [
+ {
+ from: 0,
+ to: 0,
+ },
+ {
+ from: 0,
+ to: 0,
+ },
+ ],
+ labels: {
+ show: true,
+ },
invertColors: false,
style: {
bgFill: '#000',
diff --git a/plugins/main/public/components/overview/tsc/dashboards/dashboard-panels.ts b/plugins/main/public/components/overview/tsc/dashboards/dashboard-panels.ts
index b7470cdc8b..488c7c0e68 100644
--- a/plugins/main/public/components/overview/tsc/dashboards/dashboard-panels.ts
+++ b/plugins/main/public/components/overview/tsc/dashboards/dashboard-panels.ts
@@ -547,82 +547,6 @@ const getVisStateRequirementsByAgent = (indexPatternId: string) => {
],
},
};
- return {
- title: 'Stats',
- type: 'metric',
- params: {
- metric: {
- percentageMode: false,
- useRanges: false,
- colorSchema: 'Green to Red',
- metricColorMode: 'None',
- colorsRange: [{ type: 'range', from: 0, to: 10000 }],
- labels: { show: true },
- invertColors: false,
- style: {
- bgFill: '#000',
- bgColor: false,
- labelColor: false,
- subText: '',
- fontSize: 20,
- },
- },
- dimensions: {
- metrics: [
- {
- type: 'vis_dimension',
- accessor: 0,
- format: { id: 'number', params: {} },
- },
- {
- type: 'vis_dimension',
- accessor: 1,
- format: { id: 'number', params: {} },
- },
- ],
- },
- addTooltip: true,
- addLegend: false,
- type: 'metric',
- },
- uiState: {},
- data: {
- searchSource: {
- query: {
- language: 'kuery',
- query: '',
- },
- filter: [],
- index: indexPatternId,
- },
- references: [
- {
- name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
- type: 'index-pattern',
- id: indexPatternId,
- },
- ],
- aggs: [
- {
- id: '1',
- enabled: true,
- type: 'count',
- schema: 'metric',
- params: { customLabel: 'Total alerts' },
- },
- {
- id: '2',
- enabled: true,
- type: 'max',
- schema: 'metric',
- params: {
- field: 'rule.level',
- customLabel: 'Max rule level detected',
- },
- },
- ],
- },
- };
};
const getVisStateAgentTopRuleGroups = (indexPatternId: string) => {
diff --git a/plugins/main/public/react-services/reporting.js b/plugins/main/public/react-services/reporting.js
index 2477701c6e..a5436236a5 100644
--- a/plugins/main/public/react-services/reporting.js
+++ b/plugins/main/public/react-services/reporting.js
@@ -19,7 +19,7 @@ import { UI_LOGGER_LEVELS } from '../../common/constants';
import { UI_ERROR_SEVERITIES } from './error-orchestrator/types';
import { getErrorOrchestrator } from './common-services';
import store from '../redux/store';
-import domtoimage from '../utils/dom-to-image';
+import domtoimage from '../utils/dom-to-image-more';
import dateMath from '@elastic/datemath';
import React from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiLink } from '@elastic/eui';
@@ -116,7 +116,18 @@ export class ReportingService {
return await Promise.all(
Array.from(domVisualizations).map(async node => {
return {
- element: await domtoimage.toPng(node),
+ /* WORKAROUND: Defining the width and height resolves a bug
+ related to cropped screenshot on Firefox.
+
+ This solution is based on
+ https://github.com/1904labs/dom-to-image-more/issues/160#issuecomment-1922491067
+
+ See https://github.com/wazuh/wazuh-dashboard-plugins/issues/6900#issuecomment-2275495245
+ */
+ element: await domtoimage.toPng(node, {
+ width: node.clientWidth,
+ height: node.clientHeight,
+ }),
width: node.clientWidth,
height: node.clientHeight,
title: node?.parentNode?.parentNode?.parentNode?.querySelector(
@@ -140,13 +151,13 @@ export class ReportingService {
const timeFilter =
dataSourceContext.time && dataSourceContext.indexPattern.timeFieldName
? buildRangeFilter(
- {
- name: dataSourceContext.indexPattern.timeFieldName,
- type: 'date',
- },
- dataSourceContext.time,
- dataSourceContext.indexPattern,
- )
+ {
+ name: dataSourceContext.indexPattern.timeFieldName,
+ type: 'date',
+ },
+ dataSourceContext.time,
+ dataSourceContext.indexPattern,
+ )
: null;
// Build the filters to use in the server side
// Based on https://github.com/opensearch-project/OpenSearch-Dashboards/blob/2.13.0/src/plugins/data/public/query/query_service.ts#L103-L113
@@ -166,12 +177,12 @@ export class ReportingService {
tab === 'syscollector'
? { to: dataSourceContext.time.to, from: dataSourceContext.time.from }
: {
- to: dateMath.parse(dataSourceContext.time.to, {
- roundUp: true,
- forceNow: getForceNow(),
- }),
- from: dateMath.parse(dataSourceContext.time.from),
- };
+ to: dateMath.parse(dataSourceContext.time.to, {
+ roundUp: true,
+ forceNow: getForceNow(),
+ }),
+ from: dateMath.parse(dataSourceContext.time.from),
+ };
const data = {
array: visualizations,
diff --git a/plugins/main/public/utils/dom-to-image-more.js b/plugins/main/public/utils/dom-to-image-more.js
new file mode 100644
index 0000000000..0aaa8de378
--- /dev/null
+++ b/plugins/main/public/utils/dom-to-image-more.js
@@ -0,0 +1,1550 @@
+/*
+The MIT License (MIT)
+
+Copyright 2018 Marc Brooks
+https://about.me/idisposable
+
+Copyright 2015 Anatolii Saienko
+https://github.com/tsayen
+
+Copyright 2012 Paul Bakaus
+http://paulbakaus.com/
+
+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.
+*/
+
+// dom-to-image-more 3.3.1
+// It contains some customization by Wazuh
+(function (global) {
+ 'use strict';
+
+ const util = newUtil();
+ const inliner = newInliner();
+ const fontFaces = newFontFaces();
+ const images = newImages();
+
+ // Default impl options
+ const defaultOptions = {
+ // Default is to copy default styles of elements
+ copyDefaultStyles: true,
+ // Default is to fail on error, no placeholder
+ imagePlaceholder: undefined,
+ // Default cache bust is false, it will use the cache
+ cacheBust: false,
+ // Use (existing) authentication credentials for external URIs (CORS requests)
+ useCredentials: false,
+ // Use (existing) authentication credentials for external URIs (CORS requests) on some filtered requests only
+ useCredentialsFilters: [],
+ // Default resolve timeout
+ httpTimeout: 30000,
+ // Style computation cache tag rules (options are strict, relaxed)
+ styleCaching: 'strict',
+ // Default cors config is to request the image address directly
+ corsImg: undefined,
+ // Callback for adjustClonedNode eventing (to allow adjusting clone's properties)
+ adjustClonedNode: undefined,
+ };
+
+ const domtoimage = {
+ toSvg: toSvg,
+ toPng: toPng,
+ toJpeg: toJpeg,
+ toBlob: toBlob,
+ toPixelData: toPixelData,
+ toCanvas: toCanvas,
+ impl: {
+ fontFaces: fontFaces,
+ images: images,
+ util: util,
+ inliner: inliner,
+ urlCache: [],
+ options: {},
+ },
+ };
+
+ if (typeof exports === 'object' && typeof module === 'object') {
+ module.exports = domtoimage; // eslint-disable-line no-undef
+ } else {
+ global.domtoimage = domtoimage;
+ }
+
+ // support node and browsers
+ const ELEMENT_NODE =
+ (typeof Node !== 'undefined' ? Node.ELEMENT_NODE : undefined) || 1;
+ const getComputedStyle =
+ (typeof global !== 'undefined' ? global.getComputedStyle : undefined) ||
+ (typeof window !== 'undefined' ? window.getComputedStyle : undefined) ||
+ globalThis.getComputedStyle;
+ const atob =
+ (typeof global !== 'undefined' ? global.atob : undefined) ||
+ (typeof window !== 'undefined' ? window.atob : undefined) ||
+ globalThis.atob;
+
+ /**
+ * @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 {Function} options.onclone - Callback function which is called when the Document has been cloned for
+ * rendering, can be used to modify the contents that will be rendered without affecting the original
+ * source document.
+ * @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 {Number} options.scale - a Number multiplier to scale up the canvas before rendering to reduce fuzzy images, 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
+ * @param {String} options.styleCaching - set to 'strict', 'relaxed' to select style caching rules
+ * @param {Boolean} options.copyDefaultStyles - set to false to disable use of default styles of elements
+ * @param {Object} options.corsImg - When the image is restricted by the server from cross-domain requests, the proxy address is passed in to get the image
+ * - @param {String} url - eg: https://cors-anywhere.herokuapp.com/
+ * - @param {Enumerator} method - get, post
+ * - @param {Object} headers - eg: { "Content-Type", "application/json;charset=UTF-8" }
+ * - @param {Object} data - post payload
+ * @param {Function} options.adjustClonedNode - callback for adjustClonedNode eventing (to allow adjusting clone's properties)
+ * @return {Promise} - A promise that is fulfilled with a SVG image data URL
+ * */
+ function toSvg(node, options) {
+ const ownerWindow = domtoimage.impl.util.getWindow(node);
+ options = options || {};
+ copyOptions(options);
+ let restorations = [];
+ return (
+ Promise.resolve(node)
+ .then(ensureElement)
+ .then(function (clonee) {
+ return cloneNode(clonee, options, null, ownerWindow);
+ })
+ // Wazuh
+ // Avoid downloading the embed fonts
+ // .then(embedFonts)
+ .then(inlineImages)
+ .then(applyOptions)
+ .then(makeSvgDataUri)
+ .then(restoreWrappers)
+ .then(clearCache)
+ );
+
+ function ensureElement(node) {
+ if (node.nodeType === ELEMENT_NODE) return node;
+
+ const originalChild = node;
+ const originalParent = node.parentNode;
+ const wrappingSpan = document.createElement('span');
+ originalParent.replaceChild(wrappingSpan, originalChild);
+ wrappingSpan.append(node);
+ restorations.push({
+ parent: originalParent,
+ child: originalChild,
+ wrapper: wrappingSpan,
+ });
+ return wrappingSpan;
+ }
+
+ function restoreWrappers(result) {
+ // put the original children back where the wrappers were inserted
+ while (restorations.length > 0) {
+ const restoration = restorations.pop();
+ restoration.parent.replaceChild(restoration.child, restoration.wrapper);
+ }
+
+ return result;
+ }
+
+ function clearCache(result) {
+ domtoimage.impl.urlCache = [];
+ removeSandbox();
+ return result;
+ }
+
+ 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];
+ });
+ }
+
+ let onCloneResult = null;
+
+ if (typeof options.onclone === 'function') {
+ onCloneResult = options.onclone(clone);
+ }
+
+ return Promise.resolve(onCloneResult).then(function () {
+ return clone;
+ });
+ }
+
+ function makeSvgDataUri(node) {
+ let width = options.width || util.width(node);
+ let height = options.height || util.height(node);
+
+ return Promise.resolve(node)
+ .then(function (svg) {
+ svg.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
+ return new XMLSerializer().serializeToString(svg);
+ })
+ .then(util.escapeXhtml)
+ .then(function (xhtml) {
+ const foreignObjectSizing =
+ (util.isDimensionMissing(width)
+ ? ' width="100%"'
+ : ` width="${width}"`) +
+ (util.isDimensionMissing(height)
+ ? ' height="100%"'
+ : ` height="${height}"`);
+ const svgSizing =
+ (util.isDimensionMissing(width) ? '' : ` width="${width}"`) +
+ (util.isDimensionMissing(height) ? '' : ` height="${height}"`);
+ return ``;
+ })
+ .then(function (svg) {
+ return `data:image/svg+xml;charset=utf-8,${svg}`;
+ });
+ }
+ }
+
+ /**
+ * @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) {
+ return draw(node, options).then(function (canvas) {
+ return canvas.toDataURL(
+ 'image/jpeg',
+ (options ? options.quality : undefined) || 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);
+ }
+
+ /**
+ * @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 canvas object
+ * */
+ function toCanvas(node, options) {
+ return draw(node, options);
+ }
+
+ function copyOptions(options) {
+ // Copy options to impl options for use in impl
+ if (typeof options.copyDefaultStyles === 'undefined') {
+ domtoimage.impl.options.copyDefaultStyles =
+ defaultOptions.copyDefaultStyles;
+ } else {
+ domtoimage.impl.options.copyDefaultStyles = options.copyDefaultStyles;
+ }
+
+ 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;
+ }
+
+ if (typeof options.corsImg === 'undefined') {
+ domtoimage.impl.options.corsImg = defaultOptions.corsImg;
+ } else {
+ domtoimage.impl.options.corsImg = options.corsImg;
+ }
+
+ if (typeof options.useCredentials === 'undefined') {
+ domtoimage.impl.options.useCredentials = defaultOptions.useCredentials;
+ } else {
+ domtoimage.impl.options.useCredentials = options.useCredentials;
+ }
+
+ if (typeof options.useCredentialsFilters === 'undefined') {
+ domtoimage.impl.options.useCredentialsFilters =
+ defaultOptions.useCredentialsFilters;
+ } else {
+ domtoimage.impl.options.useCredentialsFilters =
+ options.useCredentialsFilters;
+ }
+
+ if (typeof options.httpTimeout === 'undefined') {
+ domtoimage.impl.options.httpTimeout = defaultOptions.httpTimeout;
+ } else {
+ domtoimage.impl.options.httpTimeout = options.httpTimeout;
+ }
+
+ if (typeof options.styleCaching === 'undefined') {
+ domtoimage.impl.options.styleCaching = defaultOptions.styleCaching;
+ } else {
+ domtoimage.impl.options.styleCaching = options.styleCaching;
+ }
+ }
+
+ function draw(domNode, options) {
+ options = options || {};
+ return toSvg(domNode, options)
+ .then(util.makeImage)
+ .then(function (image) {
+ const scale = typeof options.scale !== 'number' ? 1 : options.scale;
+ const canvas = newCanvas(domNode, scale);
+ const ctx = canvas.getContext('2d');
+ ctx.msImageSmoothingEnabled = false;
+ ctx.imageSmoothingEnabled = false;
+ if (image) {
+ ctx.scale(scale, scale);
+ ctx.drawImage(image, 0, 0);
+ }
+ return canvas;
+ });
+
+ function newCanvas(node, scale) {
+ let width = options.width || util.width(node);
+ let height = options.height || util.height(node);
+
+ // per https://www.w3.org/TR/CSS2/visudet.html#inline-replaced-width the default width should be 300px if height
+ // not set, otherwise should be 2:1 aspect ratio for whatever height is specified
+ if (util.isDimensionMissing(width)) {
+ width = util.isDimensionMissing(height) ? 300 : height * 2.0;
+ }
+
+ if (util.isDimensionMissing(height)) {
+ height = width / 2.0;
+ }
+
+ const canvas = document.createElement('canvas');
+ canvas.width = width * scale;
+ canvas.height = height * scale;
+
+ if (options.bgcolor) {
+ const ctx = canvas.getContext('2d');
+ ctx.fillStyle = options.bgcolor;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ }
+
+ return canvas;
+ }
+ }
+
+ let sandbox = null;
+
+ function cloneNode(node, options, parentComputedStyles, ownerWindow) {
+ const filter = options.filter;
+ if (
+ node === sandbox ||
+ util.isHTMLScriptElement(node) ||
+ util.isHTMLStyleElement(node) ||
+ util.isHTMLLinkElement(node) ||
+ (parentComputedStyles !== null && filter && !filter(node))
+ ) {
+ return Promise.resolve();
+ }
+
+ return Promise.resolve(node)
+ .then(makeNodeCopy)
+ .then(adjustCloneBefore)
+ .then(function (clone) {
+ return cloneChildren(clone, getParentOfChildren(node));
+ })
+ .then(adjustCloneAfter)
+ .then(function (clone) {
+ return processClone(clone, node);
+ });
+
+ function makeNodeCopy(original) {
+ if (util.isHTMLCanvasElement(original)) {
+ return util.makeImage(original.toDataURL());
+ }
+ return original.cloneNode(false);
+ }
+
+ function adjustCloneBefore(clone) {
+ if (options.adjustClonedNode) {
+ options.adjustClonedNode(node, clone, false);
+ }
+ return Promise.resolve(clone);
+ }
+
+ function adjustCloneAfter(clone) {
+ if (options.adjustClonedNode) {
+ options.adjustClonedNode(node, clone, true);
+ }
+ return Promise.resolve(clone);
+ }
+
+ function getParentOfChildren(original) {
+ if (util.isElementHostForOpenShadowRoot(original)) {
+ return original.shadowRoot; // jump "down" to #shadow-root
+ }
+ return original;
+ }
+
+ function cloneChildren(clone, original) {
+ const originalChildren = getRenderedChildren(original);
+ let done = Promise.resolve();
+
+ if (originalChildren.length !== 0) {
+ const originalComputedStyles = getComputedStyle(
+ getRenderedParent(original),
+ );
+
+ util.asArray(originalChildren).forEach(function (originalChild) {
+ done = done.then(function () {
+ return cloneNode(
+ originalChild,
+ options,
+ originalComputedStyles,
+ ownerWindow,
+ ).then(function (clonedChild) {
+ if (clonedChild) {
+ clone.appendChild(clonedChild);
+ }
+ });
+ });
+ });
+ }
+
+ return done.then(function () {
+ return clone;
+ });
+
+ function getRenderedParent(original) {
+ if (util.isShadowRoot(original)) {
+ return original.host; // jump up from #shadow-root to its parent
+ }
+ return original;
+ }
+
+ function getRenderedChildren(original) {
+ if (util.isShadowSlotElement(original)) {
+ return original.assignedNodes(); // shadow DOM has "assigned nodes" as rendered children
+ }
+ return original.childNodes;
+ }
+ }
+
+ function processClone(clone, original) {
+ if (!util.isElement(clone) || util.isShadowSlotElement(original)) {
+ return Promise.resolve(clone);
+ }
+
+ return Promise.resolve()
+ .then(cloneStyle)
+ .then(clonePseudoElements)
+ .then(copyUserInput)
+ .then(fixSvg)
+ .then(function () {
+ return clone;
+ });
+
+ function cloneStyle() {
+ copyStyle(original, clone);
+
+ function copyFont(source, target) {
+ target.font = source.font;
+ target.fontFamily = source.fontFamily;
+ target.fontFeatureSettings = source.fontFeatureSettings;
+ target.fontKerning = source.fontKerning;
+ target.fontSize = source.fontSize;
+ target.fontStretch = source.fontStretch;
+ target.fontStyle = source.fontStyle;
+ target.fontVariant = source.fontVariant;
+ target.fontVariantCaps = source.fontVariantCaps;
+ target.fontVariantEastAsian = source.fontVariantEastAsian;
+ target.fontVariantLigatures = source.fontVariantLigatures;
+ target.fontVariantNumeric = source.fontVariantNumeric;
+ target.fontVariationSettings = source.fontVariationSettings;
+ target.fontWeight = source.fontWeight;
+ }
+
+ function copyStyle(sourceElement, targetElement) {
+ const sourceComputedStyles = getComputedStyle(sourceElement);
+ if (sourceComputedStyles.cssText) {
+ targetElement.style.cssText = sourceComputedStyles.cssText;
+ copyFont(sourceComputedStyles, targetElement.style); // here we re-assign the font props.
+ } else {
+ copyUserComputedStyleFast(
+ options,
+ sourceElement,
+ sourceComputedStyles,
+ parentComputedStyles,
+ targetElement,
+ );
+
+ // Remove positioning of initial element, which stops them from being captured correctly
+ if (parentComputedStyles === null) {
+ ['inset-block', 'inset-block-start', 'inset-block-end'].forEach(
+ prop => targetElement.style.removeProperty(prop),
+ );
+ ['left', 'right', 'top', 'bottom'].forEach(prop => {
+ if (targetElement.style.getPropertyValue(prop)) {
+ targetElement.style.setProperty(prop, '0px');
+ }
+ });
+ }
+ }
+ }
+ }
+
+ function clonePseudoElements() {
+ const cloneClassName = util.uid();
+
+ [':before', ':after'].forEach(function (element) {
+ clonePseudoElement(element);
+ });
+
+ function clonePseudoElement(element) {
+ const style = getComputedStyle(original, element);
+ const content = style.getPropertyValue('content');
+
+ if (content === '' || content === 'none') {
+ return;
+ }
+
+ const currentClass = clone.getAttribute('class') || '';
+ clone.setAttribute('class', `${currentClass} ${cloneClassName}`);
+
+ const styleElement = document.createElement('style');
+ styleElement.appendChild(formatPseudoElementStyle());
+ clone.appendChild(styleElement);
+
+ function formatPseudoElementStyle() {
+ const selector = `.${cloneClassName}:${element}`;
+ const cssText = style.cssText
+ ? formatCssText()
+ : formatCssProperties();
+
+ return document.createTextNode(`${selector}{${cssText}}`);
+
+ function formatCssText() {
+ return `${style.cssText} content: ${content};`;
+ }
+
+ function formatCssProperties() {
+ const styleText = util
+ .asArray(style)
+ .map(formatProperty)
+ .join('; ');
+ return `${styleText};`;
+
+ function formatProperty(name) {
+ const propertyValue = style.getPropertyValue(name);
+ const propertyPriority = style.getPropertyPriority(name)
+ ? ' !important'
+ : '';
+ return `${name}: ${propertyValue}${propertyPriority}`;
+ }
+ }
+ }
+ }
+ }
+
+ function copyUserInput() {
+ if (util.isHTMLTextAreaElement(original)) {
+ clone.innerHTML = original.value;
+ }
+ if (util.isHTMLInputElement(original)) {
+ clone.setAttribute('value', original.value);
+ }
+ }
+
+ function fixSvg() {
+ if (util.isSVGElement(clone)) {
+ clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
+
+ if (util.isSVGRectElement(clone)) {
+ ['width', 'height'].forEach(function (attribute) {
+ const value = clone.getAttribute(attribute);
+ if (value) {
+ clone.style.setProperty(attribute, value);
+ }
+ });
+ }
+ }
+ }
+ }
+ }
+
+ function embedFonts(node) {
+ return fontFaces.resolveAll().then(function (cssText) {
+ if (cssText !== '') {
+ const 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 newUtil() {
+ let uid_index = 0;
+
+ return {
+ escape: escapeRegEx,
+ isDataUrl: isDataUrl,
+ canvasToBlob: canvasToBlob,
+ resolveUrl: resolveUrl,
+ getAndEncode: getAndEncode,
+ uid: uid,
+ delay: delay,
+ asArray: asArray,
+ escapeXhtml: escapeXhtml,
+ makeImage: makeImage,
+ width: width,
+ height: height,
+ getWindow: getWindow,
+ isElement: isElement,
+ isElementHostForOpenShadowRoot: isElementHostForOpenShadowRoot,
+ isShadowRoot: isShadowRoot,
+ isInShadowRoot: isInShadowRoot,
+ isHTMLElement: isHTMLElement,
+ isHTMLCanvasElement: isHTMLCanvasElement,
+ isHTMLInputElement: isHTMLInputElement,
+ isHTMLImageElement: isHTMLImageElement,
+ isHTMLLinkElement: isHTMLLinkElement,
+ isHTMLScriptElement: isHTMLScriptElement,
+ isHTMLStyleElement: isHTMLStyleElement,
+ isHTMLTextAreaElement: isHTMLTextAreaElement,
+ isShadowSlotElement: isShadowSlotElement,
+ isSVGElement: isSVGElement,
+ isSVGRectElement: isSVGRectElement,
+ isDimensionMissing: isDimensionMissing,
+ };
+
+ function getWindow(node) {
+ const ownerDocument = node ? node.ownerDocument : undefined;
+ return (
+ (ownerDocument ? ownerDocument.defaultView : undefined) ||
+ global ||
+ window
+ );
+ }
+
+ function isElementHostForOpenShadowRoot(value) {
+ return isElement(value) && value.shadowRoot !== null;
+ }
+
+ function isShadowRoot(value) {
+ return value instanceof getWindow(value).ShadowRoot;
+ }
+
+ function isInShadowRoot(value) {
+ return (
+ value !== null &&
+ Object.prototype.hasOwnProperty.call(value, 'getRootNode') &&
+ isShadowRoot(value.getRootNode())
+ );
+ }
+
+ function isElement(value) {
+ return value instanceof getWindow(value).Element;
+ }
+
+ function isHTMLCanvasElement(value) {
+ return value instanceof getWindow(value).HTMLCanvasElement;
+ }
+
+ function isHTMLElement(value) {
+ return value instanceof getWindow(value).HTMLElement;
+ }
+
+ function isHTMLImageElement(value) {
+ return value instanceof getWindow(value).HTMLImageElement;
+ }
+
+ function isHTMLInputElement(value) {
+ return value instanceof getWindow(value).HTMLInputElement;
+ }
+
+ function isHTMLLinkElement(value) {
+ return value instanceof getWindow(value).HTMLLinkElement;
+ }
+
+ function isHTMLScriptElement(value) {
+ return value instanceof getWindow(value).HTMLScriptElement;
+ }
+
+ function isHTMLStyleElement(value) {
+ return value instanceof getWindow(value).HTMLStyleElement;
+ }
+
+ function isHTMLTextAreaElement(value) {
+ return value instanceof getWindow(value).HTMLTextAreaElement;
+ }
+
+ function isShadowSlotElement(value) {
+ return (
+ isInShadowRoot(value) &&
+ value instanceof getWindow(value).HTMLSlotElement
+ );
+ }
+
+ function isSVGElement(value) {
+ return value instanceof getWindow(value).SVGElement;
+ }
+
+ function isSVGRectElement(value) {
+ return value instanceof getWindow(value).SVGRectElement;
+ }
+
+ function isDataUrl(url) {
+ return url.search(/^(data:)/) !== -1;
+ }
+
+ function isDimensionMissing(value) {
+ return isNaN(value) || value <= 0;
+ }
+
+ function asBlob(canvas) {
+ return new Promise(function (resolve) {
+ const binaryString = atob(canvas.toDataURL().split(',')[1]);
+ const length = binaryString.length;
+ const binaryArray = new Uint8Array(length);
+
+ for (let 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 asBlob(canvas);
+ }
+
+ function resolveUrl(url, baseUrl) {
+ const doc = document.implementation.createHTMLDocument();
+ const base = doc.createElement('base');
+ doc.head.appendChild(base);
+ const a = doc.createElement('a');
+ doc.body.appendChild(a);
+ base.href = baseUrl;
+ a.href = url;
+ return a.href;
+ }
+
+ function uid() {
+ return `u${fourRandomChars()}${uid_index++}`;
+
+ function fourRandomChars() {
+ /* see https://stackoverflow.com/a/6248722/2519373 */
+ return `0000${((Math.random() * Math.pow(36, 4)) << 0).toString(
+ 36,
+ )}`.slice(-4);
+ }
+ }
+
+ function makeImage(uri) {
+ if (uri === 'data:,') {
+ return Promise.resolve();
+ }
+
+ return new Promise(function (resolve, reject) {
+ const image = new Image();
+ if (domtoimage.impl.options.useCredentials) {
+ image.crossOrigin = 'use-credentials';
+ }
+ image.onload = function () {
+ if (window && window.requestAnimationFrame) {
+ // In order to work around a Firefox bug (webcompat/web-bugs#119834) we
+ // need to wait one extra frame before it's safe to read the image data.
+ window.requestAnimationFrame(function () {
+ resolve(image);
+ });
+ } else {
+ // If we don't have a window or requestAnimationFrame function proceed immediately.
+ resolve(image);
+ }
+ };
+ image.onerror = reject;
+ image.src = uri;
+ });
+ }
+
+ function getAndEncode(url) {
+ let cacheEntry = domtoimage.impl.urlCache.find(function (el) {
+ return el.url === url;
+ });
+
+ if (!cacheEntry) {
+ cacheEntry = {
+ url: url,
+ promise: null,
+ };
+ domtoimage.impl.urlCache.push(cacheEntry);
+ }
+
+ if (cacheEntry.promise === null) {
+ 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();
+ }
+
+ cacheEntry.promise = new Promise(function (resolve) {
+ const httpTimeout = domtoimage.impl.options.httpTimeout;
+ const request = new XMLHttpRequest();
+
+ request.onreadystatechange = done;
+ request.ontimeout = timeout;
+ request.responseType = 'blob';
+ request.timeout = httpTimeout;
+
+ if (domtoimage.impl.options.useCredentialsFilters.length > 0) {
+ domtoimage.impl.options.useCredentials =
+ domtoimage.impl.options.useCredentialsFilters.filter(
+ credentialsFilter => url.search(credentialsFilter) >= 0,
+ ).length > 0;
+ }
+
+ if (domtoimage.impl.options.useCredentials) {
+ request.withCredentials = true;
+ }
+
+ if (
+ domtoimage.impl.options.corsImg &&
+ url.indexOf('http') === 0 &&
+ url.indexOf(window.location.origin) === -1
+ ) {
+ const method =
+ (
+ domtoimage.impl.options.corsImg.method || 'GET'
+ ).toUpperCase() === 'POST'
+ ? 'POST'
+ : 'GET';
+
+ request.open(
+ method,
+ (domtoimage.impl.options.corsImg.url || '').replace(
+ '#{cors}',
+ url,
+ ),
+ true,
+ );
+
+ let isJson = false;
+ const headers = domtoimage.impl.options.corsImg.headers || {};
+ Object.keys(headers).forEach(function (key) {
+ if (headers[key].indexOf('application/json') !== -1) {
+ isJson = true;
+ }
+ request.setRequestHeader(key, headers[key]);
+ });
+
+ const corsData = handleJson(
+ domtoimage.impl.options.corsImg.data || '',
+ );
+
+ Object.keys(corsData).forEach(function (key) {
+ if (typeof corsData[key] === 'string') {
+ corsData[key] = corsData[key].replace('#{cors}', url);
+ }
+ });
+
+ request.send(isJson ? JSON.stringify(corsData) : corsData);
+ } else {
+ request.open('GET', url, true);
+ request.send();
+ }
+
+ let placeholder;
+ if (domtoimage.impl.options.imagePlaceholder) {
+ const split = domtoimage.impl.options.imagePlaceholder.split(/,/);
+ if (split && split[1]) {
+ placeholder = split[1];
+ }
+ }
+
+ function done() {
+ if (request.readyState !== 4) {
+ return;
+ }
+
+ if (request.status >= 300) {
+ if (placeholder) {
+ resolve(placeholder);
+ } else {
+ fail(
+ `cannot fetch resource: ${url}, status: ${request.status}`,
+ );
+ }
+
+ return;
+ }
+
+ const encoder = new FileReader();
+ encoder.onloadend = function () {
+ resolve(encoder.result);
+ };
+ encoder.readAsDataURL(request.response);
+ }
+
+ function timeout() {
+ if (placeholder) {
+ resolve(placeholder);
+ } else {
+ fail(
+ `timeout of ${httpTimeout}ms occured while fetching resource: ${url}`,
+ );
+ }
+ }
+
+ function handleJson(data) {
+ try {
+ return JSON.parse(JSON.stringify(data));
+ } catch (e) {
+ fail('corsImg.data is missing or invalid');
+ return;
+ }
+ }
+
+ function fail(message) {
+ console.error(message);
+ resolve('');
+ }
+ });
+ }
+ return cacheEntry.promise;
+ }
+
+ function escapeRegEx(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) {
+ const array = [];
+ const length = arrayLike.length;
+ for (let i = 0; i < length; i++) {
+ array.push(arrayLike[i]);
+ }
+
+ return array;
+ }
+
+ function escapeXhtml(string) {
+ return string
+ .replace(/%/g, '%25')
+ .replace(/#/g, '%23')
+ .replace(/\n/g, '%0A');
+ }
+
+ function width(node) {
+ const width = px(node, 'width');
+
+ if (!isNaN(width)) return width;
+
+ const leftBorder = px(node, 'border-left-width');
+ const rightBorder = px(node, 'border-right-width');
+ return node.scrollWidth + leftBorder + rightBorder;
+ }
+
+ function height(node) {
+ const height = px(node, 'height');
+
+ if (!isNaN(height)) return height;
+
+ const topBorder = px(node, 'border-top-width');
+ const bottomBorder = px(node, 'border-bottom-width');
+ return node.scrollHeight + topBorder + bottomBorder;
+ }
+
+ function px(node, styleProperty) {
+ if (node.nodeType === ELEMENT_NODE) {
+ let value = getComputedStyle(node).getPropertyValue(styleProperty);
+ if (value.slice(-2) === 'px') {
+ value = value.slice(0, -2);
+ return parseFloat(value);
+ }
+ }
+
+ return NaN;
+ }
+ }
+
+ function newInliner() {
+ const 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) {
+ const result = [];
+ let 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 (urlValue) {
+ return baseUrl ? util.resolveUrl(urlValue, baseUrl) : urlValue;
+ })
+ .then(get || util.getAndEncode)
+ .then(function (dataUrl) {
+ return string.replace(urlAsRegex(url), `$1${dataUrl}$3`);
+ });
+
+ function urlAsRegex(urlValue) {
+ return new RegExp(
+ `(url\\(['"]?)(${util.escape(urlValue)})(['"]?\\))`,
+ 'g',
+ );
+ }
+ }
+
+ function inlineAll(string, baseUrl, get) {
+ if (nothingToInline()) {
+ return Promise.resolve(string);
+ }
+
+ return Promise.resolve(string)
+ .then(readUrls)
+ .then(function (urls) {
+ let done = Promise.resolve(string);
+ urls.forEach(function (url) {
+ done = done.then(function (prefix) {
+ return inline(prefix, url, baseUrl, get);
+ });
+ });
+ return done;
+ });
+
+ function nothingToInline() {
+ return !shouldProcess(string);
+ }
+ }
+ }
+
+ function newFontFaces() {
+ return {
+ resolveAll: resolveAll,
+ impl: {
+ readAll: readAll,
+ },
+ };
+
+ function resolveAll() {
+ return readAll()
+ .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) {
+ const cssRules = [];
+ styleSheets.forEach(function (sheet) {
+ if (
+ Object.prototype.hasOwnProperty.call(
+ Object.getPrototypeOf(sheet),
+ 'cssRules',
+ )
+ ) {
+ try {
+ util
+ .asArray(sheet.cssRules || [])
+ .forEach(cssRules.push.bind(cssRules));
+ } catch (e) {
+ console.error(
+ `domtoimage: Error while reading CSS rules from ${sheet.href}`,
+ e.toString(),
+ );
+ }
+ }
+ });
+ return cssRules;
+ }
+
+ function newWebFont(webFontRule) {
+ return {
+ resolve: function resolve() {
+ const 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 (dataUrl) {
+ return new Promise(function (resolve) {
+ element.onload = resolve;
+ // for any image with invalid src(such as ), just ignore it
+ element.onerror = resolve;
+ element.src = dataUrl;
+ });
+ });
+ }
+ }
+
+ function inlineAll(node) {
+ if (!util.isElement(node)) {
+ return Promise.resolve(node);
+ }
+
+ return inlineCSSProperty(node).then(function () {
+ if (util.isHTMLImageElement(node)) {
+ return newImage(node).inline();
+ } else {
+ return Promise.all(
+ util.asArray(node.childNodes).map(function (child) {
+ return inlineAll(child);
+ }),
+ );
+ }
+ });
+
+ function inlineCSSProperty(node) {
+ const properties = ['background', 'background-image'];
+
+ const inliningTasks = properties.map(function (propertyName) {
+ const value = node.style.getPropertyValue(propertyName);
+ const priority = node.style.getPropertyPriority(propertyName);
+
+ if (!value) {
+ return Promise.resolve();
+ }
+
+ return inliner.inlineAll(value).then(function (inlinedValue) {
+ node.style.setProperty(propertyName, inlinedValue, priority);
+ });
+ });
+
+ return Promise.all(inliningTasks).then(function () {
+ return node;
+ });
+ }
+ }
+ }
+
+ function setStyleProperty(targetStyle, name, value, priority) {
+ const needs_prefixing = ['background-clip'].indexOf(name) >= 0;
+ if (priority) {
+ targetStyle.setProperty(name, value, priority);
+ if (needs_prefixing) {
+ targetStyle.setProperty(`-webkit-${name}`, value, priority);
+ }
+ } else {
+ targetStyle.setProperty(name, value);
+ if (needs_prefixing) {
+ targetStyle.setProperty(`-webkit-${name}`, value);
+ }
+ }
+ }
+
+ function copyUserComputedStyleFast(
+ options,
+ sourceElement,
+ sourceComputedStyles,
+ parentComputedStyles,
+ targetElement,
+ ) {
+ const defaultStyle = domtoimage.impl.options.copyDefaultStyles
+ ? getDefaultStyle(options, sourceElement)
+ : {};
+ const targetStyle = targetElement.style;
+
+ util.asArray(sourceComputedStyles).forEach(function (name) {
+ const sourceValue = sourceComputedStyles.getPropertyValue(name);
+ const defaultValue = defaultStyle[name];
+ const parentValue = parentComputedStyles
+ ? parentComputedStyles.getPropertyValue(name)
+ : undefined;
+
+ // Ignore setting style property on clone node, if already it has a style (through adjustCloneNode)
+ const targetValue = targetStyle.getPropertyValue(name);
+ if (targetValue) return;
+
+ // If the style does not match the default, or it does not match the parent's, set it. We don't know which
+ // styles are inherited from the parent and which aren't, so we have to always check both.
+ if (
+ sourceValue !== defaultValue ||
+ (parentComputedStyles && sourceValue !== parentValue)
+ ) {
+ const priority = sourceComputedStyles.getPropertyPriority(name);
+ setStyleProperty(targetStyle, name, sourceValue, priority);
+ }
+ });
+ }
+
+ let removeDefaultStylesTimeoutId = null;
+ let tagNameDefaultStyles = {};
+
+ const ascentStoppers = [
+ // these come from https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements
+ 'ADDRESS',
+ 'ARTICLE',
+ 'ASIDE',
+ 'BLOCKQUOTE',
+ 'DETAILS',
+ 'DIALOG',
+ 'DD',
+ 'DIV',
+ 'DL',
+ 'DT',
+ 'FIELDSET',
+ 'FIGCAPTION',
+ 'FIGURE',
+ 'FOOTER',
+ 'FORM',
+ 'H1',
+ 'H2',
+ 'H3',
+ 'H4',
+ 'H5',
+ 'H6',
+ 'HEADER',
+ 'HGROUP',
+ 'HR',
+ 'LI',
+ 'MAIN',
+ 'NAV',
+ 'OL',
+ 'P',
+ 'PRE',
+ 'SECTION',
+ 'SVG',
+ 'TABLE',
+ 'UL',
+ // this is some non-standard ones
+ 'math', // intentionally lowercase, thanks Safari
+ 'svg', // in case we have an svg embedded element
+ // these are ultimate stoppers in case something drastic changes in how the DOM works
+ 'BODY',
+ 'HEAD',
+ 'HTML',
+ ];
+
+ function getDefaultStyle(options, sourceElement) {
+ const tagHierarchy = computeTagHierarchy(sourceElement);
+ const tagKey = computeTagKey(tagHierarchy);
+ if (tagNameDefaultStyles[tagKey]) {
+ return tagNameDefaultStyles[tagKey];
+ }
+
+ // We haven't cached the answer for that hierachy yet, build a
+ // sandbox (if not yet created), fill it with the hierarchy that
+ // matters, and grab the default styles associated
+ const sandboxWindow = ensureSandboxWindow();
+ const defaultElement = constructElementHierachy(
+ sandboxWindow.document,
+ tagHierarchy,
+ );
+ const defaultStyle = computeStyleForDefaults(sandboxWindow, defaultElement);
+ destroyElementHierarchy(defaultElement);
+
+ tagNameDefaultStyles[tagKey] = defaultStyle;
+ return defaultStyle;
+
+ function computeTagHierarchy(sourceNode) {
+ const tagNames = [];
+
+ do {
+ if (sourceNode.nodeType === ELEMENT_NODE) {
+ const tagName = sourceNode.tagName;
+ tagNames.push(tagName);
+
+ if (ascentStoppers.includes(tagName)) {
+ break;
+ }
+ }
+
+ sourceNode = sourceNode.parentNode;
+ } while (sourceNode);
+
+ return tagNames;
+ }
+
+ function computeTagKey(tagHierarchy) {
+ if (options.styleCaching === 'relaxed') {
+ // pick up only the ascent-stopping element tag and the element tag itself
+ /* jshint unused:true */
+ return tagHierarchy
+ .filter((_, i, a) => i === 0 || i === a.length - 1)
+ .join('>');
+ }
+ // for all other cases, fall back the the entire path
+ return tagHierarchy.join('>'); // it's like CSS
+ }
+
+ function constructElementHierachy(sandboxDocument, tagHierarchy) {
+ let element = sandboxDocument.body;
+ do {
+ const childTagName = tagHierarchy.pop();
+ const childElement = sandboxDocument.createElement(childTagName);
+ element.appendChild(childElement);
+ element = childElement;
+ } while (tagHierarchy.length > 0);
+
+ // Ensure that there is some content, so that properties like margin are applied.
+ // we use zero-width space to handle FireFox adding a pixel
+ element.textContent = '\u200b';
+ return element;
+ }
+
+ function computeStyleForDefaults(sandboxWindow, defaultElement) {
+ const defaultStyle = {};
+ const defaultComputedStyle =
+ sandboxWindow.getComputedStyle(defaultElement);
+
+ // Copy styles to an object, making sure that 'width' and 'height' are given the default value of 'auto', since
+ // their initial value is always 'auto' despite that the default computed value is sometimes an absolute length.
+ util.asArray(defaultComputedStyle).forEach(function (name) {
+ defaultStyle[name] =
+ name === 'width' || name === 'height'
+ ? 'auto'
+ : defaultComputedStyle.getPropertyValue(name);
+ });
+ return defaultStyle;
+ }
+
+ function destroyElementHierarchy(element) {
+ do {
+ const parentElement = element.parentElement;
+ if (parentElement !== null) {
+ parentElement.removeChild(element);
+ }
+ element = parentElement;
+ } while (element && element.tagName !== 'BODY');
+ }
+ }
+
+ function ensureSandboxWindow() {
+ if (sandbox) {
+ return sandbox.contentWindow;
+ }
+
+ // figure out how this document is defined (doctype and charset)
+ const charsetToUse = document.characterSet || 'UTF-8';
+ const docType = document.doctype;
+ const docTypeDeclaration = docType
+ ? `'
+ : '';
+
+ // Create a hidden sandbox