@percy/dom 1.0.0-beta.9 → 1.0.0

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/README.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  Serializes a document's DOM into a DOM string suitable for re-rendering.
4
4
 
5
+ - [Usage](#usage)
6
+ - [ES6 imports](#es6-imports)
7
+ - [Browser injection](#browser-injection)
8
+ - [Options](#options)
9
+ - [Serialized Content](#serialize-content)
10
+ - [Input elements](#input-elements)
11
+ - [Frame elements](#frame-elements)
12
+ - [CSSOM rules](#cssom-rules)
13
+ - [Canvas elements](#canvas-elements)
14
+ - [Video elements](#video-elements)
15
+ - [Other elements](#other-elements)
16
+
5
17
  ## Usage
6
18
 
7
19
  ### ES6 imports
@@ -10,7 +22,7 @@ Serializes a document's DOM into a DOM string suitable for re-rendering.
10
22
  import serializeDOM from '@percy/dom';
11
23
 
12
24
  // optional arguments shown with defaults
13
- const domSnapshot = serializeDOM(/* options */)
25
+ const domSnapshot = serializeDOM(options)
14
26
  ```
15
27
 
16
28
  ### Browser injection
@@ -18,13 +30,13 @@ const domSnapshot = serializeDOM(/* options */)
18
30
  ```js
19
31
  // via puppeteer
20
32
  await page.addScriptTag({ path: require.resolve('@percy/dom') })
21
- const domSnapshot = await page.evaluate(() => PercyDOM.serialize(/* options */))
33
+ const domSnapshot = await page.evaluate(() => PercyDOM.serialize(options))
22
34
  ```
23
35
 
24
- ### Available options
36
+ ### Options
25
37
 
26
- - `enableJavaScript` - when true, does not serialize some DOM elements
27
- - `domTransformation` - function to transform the DOM after serialization
38
+ - `enableJavaScript` When true, does not serialize some DOM elements
39
+ - `domTransformation` Function to transform the DOM after serialization
28
40
 
29
41
  ## Serialized Content
30
42
 
@@ -53,6 +65,12 @@ with image elements. The image elements reference the serialized data URI and ha
53
65
  attributes as their respective canvas elements. The image elements also have a max-width of 100% to
54
66
  accomidate responsive layouts in situations where canvases may be expected to resize with JS.
55
67
 
68
+ ### Video elements
69
+
70
+ Videos without a `poster` attribute will have the current frame of the video
71
+ serialized into an image and set as the `poster` attribute automatically. This is
72
+ to ensure videos have a stable image to display when screenshots are captured.
73
+
56
74
  ### Other elements
57
75
 
58
76
  _All other elements are not serialized._ The resulting cloned document is passed to any provided
package/dist/bundle.js ADDED
@@ -0,0 +1,251 @@
1
+ (function() {
2
+ (function (exports) {
3
+ 'use strict';
4
+
5
+ const process = (typeof globalThis !== "undefined" && globalThis.process) || {};
6
+ process.env = process.env || {};
7
+ process.env.__PERCY_BROWSERIFIED__ = true;
8
+
9
+ // Returns a mostly random uid.
10
+ function uid() {
11
+ return `_${Math.random().toString(36).substr(2, 9)}`;
12
+ } // Marks elements that are to be serialized later with a data attribute.
13
+
14
+
15
+ function prepareDOM(dom) {
16
+ for (let elem of dom.querySelectorAll('input, textarea, select, iframe, canvas, video, style')) {
17
+ if (!elem.getAttribute('data-percy-element-id')) {
18
+ elem.setAttribute('data-percy-element-id', uid());
19
+ }
20
+ }
21
+ }
22
+
23
+ // Translates JavaScript properties of inputs into DOM attributes.
24
+ function serializeInputElements(dom, clone) {
25
+ for (let elem of dom.querySelectorAll('input, textarea, select')) {
26
+ let inputId = elem.getAttribute('data-percy-element-id');
27
+ let cloneEl = clone.querySelector(`[data-percy-element-id="${inputId}"]`);
28
+
29
+ switch (elem.type) {
30
+ case 'checkbox':
31
+ case 'radio':
32
+ if (elem.checked) {
33
+ cloneEl.setAttribute('checked', '');
34
+ }
35
+
36
+ break;
37
+
38
+ case 'select-one':
39
+ if (elem.selectedIndex !== -1) {
40
+ cloneEl.options[elem.selectedIndex].setAttribute('selected', 'true');
41
+ }
42
+
43
+ break;
44
+
45
+ case 'select-multiple':
46
+ for (let option of elem.selectedOptions) {
47
+ cloneEl.options[option.index].setAttribute('selected', 'true');
48
+ }
49
+
50
+ break;
51
+
52
+ case 'textarea':
53
+ cloneEl.innerHTML = elem.value;
54
+ break;
55
+
56
+ default:
57
+ cloneEl.setAttribute('value', elem.value);
58
+ }
59
+ }
60
+ }
61
+
62
+ // embedded documents are serialized and their contents become root-relative.
63
+
64
+ function setBaseURI(dom) {
65
+ if (!new URL(dom.baseURI).hostname) return;
66
+ let $base = document.createElement('base');
67
+ $base.href = dom.baseURI;
68
+ dom.querySelector('head').prepend($base);
69
+ } // Recursively serializes iframe documents into srcdoc attributes.
70
+
71
+
72
+ function serializeFrames(dom, clone, _ref) {
73
+ let {
74
+ enableJavaScript
75
+ } = _ref;
76
+
77
+ for (let frame of dom.querySelectorAll('iframe')) {
78
+ let percyElementId = frame.getAttribute('data-percy-element-id');
79
+ let cloneEl = clone.querySelector(`[data-percy-element-id="${percyElementId}"]`);
80
+ let builtWithJs = !frame.srcdoc && (!frame.src || frame.src.split(':')[0] === 'javascript'); // delete frames within the head since they usually break pages when
81
+ // rerendered and do not effect the visuals of a page
82
+
83
+ if (clone.head.contains(cloneEl)) {
84
+ cloneEl.remove(); // if the frame document is accessible and not empty, we can serialize it
85
+ } else if (frame.contentDocument && frame.contentDocument.documentElement) {
86
+ // js is enabled and this frame was built with js, don't serialize it
87
+ if (enableJavaScript && builtWithJs) continue; // the frame has yet to load and wasn't built with js, it is unsafe to serialize
88
+
89
+ if (!builtWithJs && !frame.contentWindow.performance.timing.loadEventEnd) continue; // recersively serialize contents
90
+
91
+ let serialized = serializeDOM({
92
+ domTransformation: setBaseURI,
93
+ dom: frame.contentDocument,
94
+ enableJavaScript
95
+ }); // assign to srcdoc and remove src
96
+
97
+ cloneEl.setAttribute('srcdoc', serialized);
98
+ cloneEl.removeAttribute('src'); // delete inaccessible frames built with js when js is disabled because they
99
+ // break asset discovery by creating non-captured requests that hang
100
+ } else if (!enableJavaScript && builtWithJs) {
101
+ cloneEl.remove();
102
+ }
103
+ }
104
+ }
105
+
106
+ // Returns true if a stylesheet is a CSSOM-based stylesheet.
107
+ function isCSSOM(styleSheet) {
108
+ var _styleSheet$ownerNode, _styleSheet$ownerNode2;
109
+
110
+ // no href, has a rulesheet, and isn't already in the DOM
111
+ return !styleSheet.href && styleSheet.cssRules && !((_styleSheet$ownerNode = styleSheet.ownerNode) !== null && _styleSheet$ownerNode !== void 0 && (_styleSheet$ownerNode2 = _styleSheet$ownerNode.innerText) !== null && _styleSheet$ownerNode2 !== void 0 && _styleSheet$ownerNode2.trim().length);
112
+ } // Outputs in-memory CSSOM into their respective DOM nodes.
113
+
114
+
115
+ function serializeCSSOM(dom, clone) {
116
+ for (let styleSheet of dom.styleSheets) {
117
+ if (isCSSOM(styleSheet)) {
118
+ let style = clone.createElement('style');
119
+ let styleId = styleSheet.ownerNode.getAttribute('data-percy-element-id');
120
+ let cloneOwnerNode = clone.querySelector(`[data-percy-element-id="${styleId}"]`);
121
+ style.type = 'text/css';
122
+ style.setAttribute('data-percy-cssom-serialized', 'true');
123
+ style.innerHTML = Array.from(styleSheet.cssRules).reduce((prev, cssRule) => prev + cssRule.cssText, '');
124
+ cloneOwnerNode.parentNode.insertBefore(style, cloneOwnerNode.nextSibling);
125
+ }
126
+ }
127
+ }
128
+
129
+ // Serialize in-memory canvas elements into images.
130
+ function serializeCanvas(dom, clone) {
131
+ for (let canvas of dom.querySelectorAll('canvas')) {
132
+ // Note: the `.toDataURL` API requires WebGL canvas elements to use
133
+ // `preserveDrawingBuffer: true`. This is because `.toDataURL` uses the
134
+ // drawing buffer, which is cleared after each render for WebGL by default.
135
+ let dataUrl = canvas.toDataURL(); // skip empty canvases
136
+
137
+ if (!dataUrl || dataUrl === 'data:,') continue; // create an image element in the cloned dom
138
+
139
+ let img = clone.createElement('img');
140
+ img.src = dataUrl; // copy canvas element attributes to the image element such as style, class,
141
+ // or data attributes that may be targeted by CSS
142
+
143
+ for (let {
144
+ name,
145
+ value
146
+ } of canvas.attributes) {
147
+ img.setAttribute(name, value);
148
+ } // mark the image as serialized (can be targeted by CSS)
149
+
150
+
151
+ img.setAttribute('data-percy-canvas-serialized', ''); // set a default max width to account for canvases that might resize with JS
152
+
153
+ img.style.maxWidth = img.style.maxWidth || '100%'; // insert the image into the cloned DOM and remove the cloned canvas element
154
+
155
+ let percyElementId = canvas.getAttribute('data-percy-element-id');
156
+ let cloneEl = clone.querySelector(`[data-percy-element-id=${percyElementId}]`);
157
+ cloneEl.parentElement.insertBefore(img, cloneEl);
158
+ cloneEl.remove();
159
+ }
160
+ }
161
+
162
+ // Captures the current frame of videos and sets the poster image
163
+ function serializeVideos(dom, clone) {
164
+ for (let video of dom.querySelectorAll('video')) {
165
+ // If the video already has a poster image, no work for us to do
166
+ if (video.getAttribute('poster')) continue;
167
+ let videoId = video.getAttribute('data-percy-element-id');
168
+ let cloneEl = clone.querySelector(`[data-percy-element-id="${videoId}"]`);
169
+ let canvas = document.createElement('canvas');
170
+ let width = canvas.width = video.videoWidth;
171
+ let height = canvas.height = video.videoHeight;
172
+ let dataUrl;
173
+ canvas.getContext('2d').drawImage(video, 0, 0, width, height);
174
+
175
+ try {
176
+ dataUrl = canvas.toDataURL();
177
+ } catch {} // If the canvas produces a blank image, skip
178
+
179
+
180
+ if (!dataUrl || dataUrl === 'data:,') continue;
181
+ cloneEl.setAttribute('poster', dataUrl);
182
+ }
183
+ }
184
+
185
+ function doctype(dom) {
186
+ let {
187
+ name = 'html',
188
+ publicId = '',
189
+ systemId = ''
190
+ } = (dom === null || dom === void 0 ? void 0 : dom.doctype) ?? {};
191
+ let deprecated = '';
192
+
193
+ if (publicId && systemId) {
194
+ deprecated = ` PUBLIC "${publicId}" "${systemId}"`;
195
+ } else if (publicId) {
196
+ deprecated = ` PUBLIC "${publicId}"`;
197
+ } else if (systemId) {
198
+ deprecated = ` SYSTEM "${systemId}"`;
199
+ }
200
+
201
+ return `<!DOCTYPE ${name}${deprecated}>`;
202
+ } // Serializes a document and returns the resulting DOM string.
203
+
204
+
205
+ function serializeDOM(options) {
206
+ let {
207
+ dom = document,
208
+ // allow snake_case or camelCase
209
+ enableJavaScript = options === null || options === void 0 ? void 0 : options.enable_javascript,
210
+ domTransformation = options === null || options === void 0 ? void 0 : options.dom_transformation
211
+ } = options || {};
212
+ prepareDOM(dom);
213
+ let clone = dom.cloneNode(true);
214
+ serializeInputElements(dom, clone);
215
+ serializeFrames(dom, clone, {
216
+ enableJavaScript
217
+ });
218
+ serializeVideos(dom, clone);
219
+
220
+ if (!enableJavaScript) {
221
+ serializeCSSOM(dom, clone);
222
+ serializeCanvas(dom, clone);
223
+ }
224
+
225
+ let doc = clone.documentElement;
226
+
227
+ if (domTransformation) {
228
+ try {
229
+ domTransformation(doc);
230
+ } catch (err) {
231
+ console.error('Could not transform the dom:', err.message);
232
+ }
233
+ }
234
+
235
+ return doctype(dom) + doc.outerHTML;
236
+ }
237
+
238
+ exports["default"] = serializeDOM;
239
+ exports.serialize = serializeDOM;
240
+ exports.serializeDOM = serializeDOM;
241
+
242
+ Object.defineProperty(exports, '__esModule', { value: true });
243
+
244
+ })(this.PercyDOM = this.PercyDOM || {});
245
+ }).call(window);
246
+
247
+ if (typeof define === "function" && define.amd) {
248
+ define([], () => window.PercyDOM);
249
+ } else if (typeof module === "object" && module.exports) {
250
+ module.exports = window.PercyDOM;
251
+ }
package/package.json CHANGED
@@ -1,45 +1,38 @@
1
1
  {
2
2
  "name": "@percy/dom",
3
- "version": "1.0.0-beta.9",
3
+ "version": "1.0.0",
4
4
  "license": "MIT",
5
- "main": "dist/index.js",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/percy/cli",
8
+ "directory": "packages/dom"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "main": "dist/bundle.js",
14
+ "browser": "dist/bundle.js",
6
15
  "files": [
7
16
  "dist"
8
17
  ],
9
18
  "scripts": {
10
- "build": "webpack",
19
+ "build": "node ../../scripts/build",
11
20
  "lint": "eslint --ignore-path ../../.gitignore .",
12
- "test": "cross-env NODE_ENV=test karma start --single-run",
21
+ "test": "node ../../scripts/test",
13
22
  "test:coverage": "yarn test --coverage"
14
23
  },
15
- "publishConfig": {
16
- "access": "public"
17
- },
18
- "babel": {
19
- "extends": "../../babel.config.js",
20
- "presets": [
21
- [
22
- "@babel/env",
23
- {
24
- "targets": "last 2 version"
25
- }
26
- ]
27
- ]
24
+ "rollup": {
25
+ "output": {
26
+ "name": "PercyDOM"
27
+ },
28
+ "test": {
29
+ "output": {
30
+ "exports": "named"
31
+ }
32
+ }
28
33
  },
29
34
  "devDependencies": {
30
- "babel-loader": "^8.0.6",
31
- "cheerio": "^1.0.0-rc.3",
32
- "interactor.js": "^1.6.0",
33
- "karma": "^5.0.9",
34
- "karma-chrome-launcher": "^3.1.0",
35
- "karma-coverage": "^2.0.1",
36
- "karma-firefox-launcher": "^1.3.0",
37
- "karma-mocha": "^2.0.1",
38
- "karma-mocha-reporter": "^2.2.5",
39
- "karma-webpack": "^4.0.2",
40
- "regenerator-runtime": "^0.13.3",
41
- "webpack": "^4.41.6",
42
- "webpack-cli": "^3.3.11"
35
+ "interactor.js": "^2.0.0-beta.10"
43
36
  },
44
- "gitHead": "57a2eeb90c7f5cdf8827c78be1e5c12df581f4b5"
37
+ "gitHead": "6df509421a60144e4f9f5d59dc57a5675372a0b2"
45
38
  }
package/dist/index.js DELETED
@@ -1 +0,0 @@
1
- !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.PercyDOM=t():e.PercyDOM=t()}(window,(function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";function n(e,t){var r;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(r=function(e,t){if(!e)return;if("string"==typeof e)return o(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return o(e,t)}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,a=function(){};return{s:a,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,u=!0,c=!1;return{s:function(){r=e[Symbol.iterator]()},n:function(){var e=r.next();return u=e.done,e},e:function(e){c=!0,i=e},f:function(){try{u||null==r.return||r.return()}finally{if(c)throw i}}}}function o(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n}function a(e,t){var r;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(r=function(e,t){if(!e)return;if("string"==typeof e)return i(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return i(e,t)}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,o=function(){};return{s:o,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,u=!0,c=!1;return{s:function(){r=e[Symbol.iterator]()},n:function(){var e=r.next();return u=e.done,e},e:function(e){c=!0,a=e},f:function(){try{u||null==r.return||r.return()}finally{if(c)throw a}}}}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n}function u(e,t){var r;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(r=function(e,t){if(!e)return;if("string"==typeof e)return c(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return c(e,t)}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,o=function(){};return{s:o,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,i=!0,u=!1;return{s:function(){r=e[Symbol.iterator]()},n:function(){var e=r.next();return i=e.done,e},e:function(e){u=!0,a=e},f:function(){try{i||null==r.return||r.return()}finally{if(u)throw a}}}}function c(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n}function l(e,t){var r;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(r=function(e,t){if(!e)return;if("string"==typeof e)return f(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return f(e,t)}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,o=function(){};return{s:o,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,i=!0,u=!1;return{s:function(){r=e[Symbol.iterator]()},n:function(){var e=r.next();return i=e.done,e},e:function(e){u=!0,a=e},f:function(){try{i||null==r.return||r.return()}finally{if(u)throw a}}}}function f(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n}function s(e){var t;return!e.href&&e.cssRules&&!(null===(t=e.ownerNode)||void 0===t?void 0:t.innerText.trim().length)}function y(e,t){var r;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(r=function(e,t){if(!e)return;if("string"==typeof e)return d(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return d(e,t)}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,o=function(){};return{s:o,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,i=!0,u=!1;return{s:function(){r=e[Symbol.iterator]()},n:function(){var e=r.next();return i=e.done,e},e:function(e){u=!0,a=e},f:function(){try{i||null==r.return||r.return()}finally{if(u)throw a}}}}function d(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n}function m(e){var t=e||{},r=t.dom,o=void 0===r?document:r,i=t.enableJavaScript,c=t.domTransformation;!function(e){var t,r=n(e.querySelectorAll("input, textarea, select, iframe, canvas"));try{for(r.s();!(t=r.n()).done;){var o=t.value;o.getAttribute("data-percy-element-id")||o.setAttribute("data-percy-element-id","_".concat(Math.random().toString(36).substr(2,9)))}}catch(e){r.e(e)}finally{r.f()}}(o);var f=o.cloneNode(!0);!function(e,t){var r,n=a(e.querySelectorAll("input, textarea, select"));try{for(n.s();!(r=n.n()).done;){var o=r.value,i=o.getAttribute("data-percy-element-id"),u=t.querySelector('[data-percy-element-id="'.concat(i,'"]'));switch(o.type){case"checkbox":case"radio":o.checked&&u.setAttribute("checked","");break;case"select-one":-1!==o.selectedIndex&&u.options[o.selectedIndex].setAttribute("selected","true");break;case"select-multiple":var c,l=a(o.selectedOptions);try{for(l.s();!(c=l.n()).done;){var f=c.value;u.options[f.index].setAttribute("selected","true")}}catch(e){l.e(e)}finally{l.f()}break;case"textarea":u.innerHTML=o.value;break;default:u.setAttribute("value",o.value)}}}catch(e){n.e(e)}finally{n.f()}}(o,f),function(e,t,r){var n,o=r.enableJavaScript,a=u(e.querySelectorAll("iframe"));try{for(a.s();!(n=a.n()).done;){var i=n.value,c=i.getAttribute("data-percy-element-id"),l=t.querySelector('[data-percy-element-id="'.concat(c,'"]')),f=!(i.srcdoc||i.src&&"javascript"!==i.src.split(":")[0]);if(t.head.contains(l))l.remove();else if(i.contentDocument){if(o&&f)continue;if(!f&&!i.contentWindow.performance.timing.loadEventEnd)continue;var s=m({dom:i.contentDocument,enableJavaScript:o});l.setAttribute("srcdoc",s),l.removeAttribute("src")}else!o&&f&&l.remove()}}catch(e){a.e(e)}finally{a.f()}}(o,f,{enableJavaScript:i}),i||(function(e,t){var r,n=l(e.styleSheets);try{for(n.s();!(r=n.n()).done;){var o=r.value;if(s(o)){var a=t.createElement("style");a.type="text/css",a.setAttribute("data-percy-cssom-serialized","true"),a.innerHTML=Array.from(o.cssRules).reduce((function(e,t){return e+t.cssText}),""),t.head.appendChild(a)}}}catch(e){n.e(e)}finally{n.f()}}(o,f),function(e,t){var r,n=y(e.querySelectorAll("canvas"));try{for(n.s();!(r=n.n()).done;){var o,a=r.value,i=t.createElement("img"),u=y(a.attributes);try{for(u.s();!(o=u.n()).done;){var c=o.value,l=c.name,f=c.value;i.setAttribute(l,f)}}catch(e){u.e(e)}finally{u.f()}i.src=a.toDataURL(),i.setAttribute("data-percy-canvas-serialized",""),i.style.maxWidth=i.style.maxWidth||"100%";var s=a.getAttribute("data-percy-element-id"),d=t.querySelector("[data-percy-element-id=".concat(s,"]"));d.parentElement.insertBefore(i,d),d.remove()}}catch(e){n.e(e)}finally{n.f()}}(o,f));var d=f.documentElement;if(c)try{c(d)}catch(e){console.error("Could not transform the dom:",e.message)}return function(e){var t,r=null!==(t=null==e?void 0:e.doctype)&&void 0!==t?t:{},n=r.name,o=void 0===n?"html":n,a=r.publicId,i=void 0===a?"":a,u=r.systemId,c=void 0===u?"":u,l="";return i&&c?l=' PUBLIC "'.concat(i,'" "').concat(c,'"'):i?l=' PUBLIC "'.concat(i,'"'):c&&(l=' SYSTEM "'.concat(c,'"')),"<!DOCTYPE ".concat(o).concat(l,">")}(o)+d.outerHTML}r.r(t),r.d(t,"default",(function(){return m})),r.d(t,"serialize",(function(){return m}))}])}));