@gemx-dev/clarity-visualize 0.8.39
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 +1 -0
- package/build/clarity.visualize.js +2128 -0
- package/build/clarity.visualize.min.js +1 -0
- package/build/clarity.visualize.module.js +2125 -0
- package/package.json +45 -0
- package/rollup.config.ts +79 -0
- package/src/clarity.ts +3 -0
- package/src/data.ts +103 -0
- package/src/enrich.ts +80 -0
- package/src/global.ts +9 -0
- package/src/heatmap.ts +299 -0
- package/src/index.ts +2 -0
- package/src/interaction.ts +507 -0
- package/src/layout.ts +727 -0
- package/src/styles/blobUnavailable/chineseSimplified.svg +5 -0
- package/src/styles/blobUnavailable/chineseTraditional.svg +5 -0
- package/src/styles/blobUnavailable/dutch.svg +5 -0
- package/src/styles/blobUnavailable/english.svg +5 -0
- package/src/styles/blobUnavailable/french.svg +5 -0
- package/src/styles/blobUnavailable/german.svg +5 -0
- package/src/styles/blobUnavailable/iconOnly.svg +4 -0
- package/src/styles/blobUnavailable/italian.svg +5 -0
- package/src/styles/blobUnavailable/japanese.svg +5 -0
- package/src/styles/blobUnavailable/korean.svg +5 -0
- package/src/styles/blobUnavailable/portuguese.svg +5 -0
- package/src/styles/blobUnavailable/russian.svg +5 -0
- package/src/styles/blobUnavailable/spanish.svg +5 -0
- package/src/styles/blobUnavailable/turkish.svg +5 -0
- package/src/styles/iframeUnavailable/chineseSimplified.svg +5 -0
- package/src/styles/iframeUnavailable/chineseTraditional.svg +5 -0
- package/src/styles/iframeUnavailable/dutch.svg +5 -0
- package/src/styles/iframeUnavailable/english.svg +5 -0
- package/src/styles/iframeUnavailable/french.svg +5 -0
- package/src/styles/iframeUnavailable/german.svg +5 -0
- package/src/styles/iframeUnavailable/iconOnly.svg +4 -0
- package/src/styles/iframeUnavailable/italian.svg +5 -0
- package/src/styles/iframeUnavailable/japanese.svg +5 -0
- package/src/styles/iframeUnavailable/korean.svg +5 -0
- package/src/styles/iframeUnavailable/portuguese.svg +5 -0
- package/src/styles/iframeUnavailable/russian.svg +5 -0
- package/src/styles/iframeUnavailable/spanish.svg +5 -0
- package/src/styles/iframeUnavailable/turkish.svg +5 -0
- package/src/styles/imageMasked/chineseSimplified.svg +5 -0
- package/src/styles/imageMasked/chineseTraditional.svg +5 -0
- package/src/styles/imageMasked/dutch.svg +5 -0
- package/src/styles/imageMasked/english.svg +5 -0
- package/src/styles/imageMasked/french.svg +5 -0
- package/src/styles/imageMasked/german.svg +5 -0
- package/src/styles/imageMasked/iconOnly.svg +4 -0
- package/src/styles/imageMasked/italian.svg +5 -0
- package/src/styles/imageMasked/japanese.svg +5 -0
- package/src/styles/imageMasked/korean.svg +5 -0
- package/src/styles/imageMasked/portuguese.svg +5 -0
- package/src/styles/imageMasked/russian.svg +5 -0
- package/src/styles/imageMasked/spanish.svg +5 -0
- package/src/styles/imageMasked/turkish.svg +5 -0
- package/src/styles/pointer/click.css +31 -0
- package/src/styles/pointer/pointerIcon.svg +18 -0
- package/src/styles/shared.css +6 -0
- package/src/visualizer.ts +260 -0
- package/tsconfig.json +21 -0
- package/tslint.json +33 -0
- package/types/index.d.ts +10 -0
- package/types/string-import.d.ts +9 -0
- package/types/visualize.d.ts +236 -0
package/src/layout.ts
ADDED
|
@@ -0,0 +1,727 @@
|
|
|
1
|
+
import { Data, Layout } from "clarity-js";
|
|
2
|
+
import type { Layout as DecodedLayout } from "clarity-decode";
|
|
3
|
+
import { Asset, Constant, type LinkHandler, NodeType, type PlaybackState, Setting } from "@clarity-types/visualize";
|
|
4
|
+
import { StyleSheetOperation } from "clarity-js/types/layout";
|
|
5
|
+
import { AnimationOperation } from "clarity-js/types/layout";
|
|
6
|
+
import { Constant as LayoutConstants } from "clarity-js/types/layout";
|
|
7
|
+
import sharedStyle from "./styles/shared.css";
|
|
8
|
+
|
|
9
|
+
/* BEGIN blobUnavailableSvgs */
|
|
10
|
+
import blobUnavailableSvgEnglish from "./styles/blobUnavailable/english.svg";
|
|
11
|
+
import blobUnavailableSvgSmall from "./styles/blobUnavailable/iconOnly.svg";
|
|
12
|
+
import blobUnavailableSvgChineseSimplified from "./styles/blobUnavailable/chineseSimplified.svg";
|
|
13
|
+
import blobUnavailableSvgChineseTraditional from "./styles/blobUnavailable/chineseTraditional.svg";
|
|
14
|
+
import blobUnavailableSvgJapanese from "./styles/blobUnavailable/japanese.svg";
|
|
15
|
+
import blobUnavailableSvgKorean from "./styles/blobUnavailable/korean.svg";
|
|
16
|
+
import blobUnavailableSvgRussian from "./styles/blobUnavailable/russian.svg";
|
|
17
|
+
import blobUnavailableSvgSpanish from "./styles/blobUnavailable/spanish.svg";
|
|
18
|
+
import blobUnavailableSvgTurkish from "./styles/blobUnavailable/turkish.svg";
|
|
19
|
+
import blobUnavailableSvgDutch from "./styles/blobUnavailable/dutch.svg";
|
|
20
|
+
import blobUnavailableSvgFrench from "./styles/blobUnavailable/french.svg";
|
|
21
|
+
import blobUnavailableSvgGerman from "./styles/blobUnavailable/german.svg";
|
|
22
|
+
import blobUnavailableSvgItalian from "./styles/blobUnavailable/italian.svg";
|
|
23
|
+
import blobUnavailableSvgPortuguese from "./styles/blobUnavailable/portuguese.svg";
|
|
24
|
+
const blobUnavailableSvg = {
|
|
25
|
+
"de-de": blobUnavailableSvgGerman,
|
|
26
|
+
"en-gb": blobUnavailableSvgEnglish,
|
|
27
|
+
"en-us": blobUnavailableSvgEnglish,
|
|
28
|
+
"es-es": blobUnavailableSvgSpanish,
|
|
29
|
+
"fr-fr": blobUnavailableSvgFrench,
|
|
30
|
+
"it-it": blobUnavailableSvgItalian,
|
|
31
|
+
"ja-jp": blobUnavailableSvgJapanese,
|
|
32
|
+
"ko-kr": blobUnavailableSvgKorean,
|
|
33
|
+
"nl-nl": blobUnavailableSvgDutch,
|
|
34
|
+
"pt-br": blobUnavailableSvgPortuguese,
|
|
35
|
+
"ru-ru": blobUnavailableSvgRussian,
|
|
36
|
+
"tr-tr": blobUnavailableSvgTurkish,
|
|
37
|
+
"zh-hans": blobUnavailableSvgChineseSimplified,
|
|
38
|
+
"zh-hant": blobUnavailableSvgChineseTraditional,
|
|
39
|
+
}
|
|
40
|
+
/* END blobUnavailableSvgs */
|
|
41
|
+
|
|
42
|
+
/* BEGIN iframeUnavailableSvgs */
|
|
43
|
+
import iframeUnavailableSvgEnglish from "./styles/iframeUnavailable/english.svg";
|
|
44
|
+
import iframeUnavailableSvgSmall from "./styles/iframeUnavailable/iconOnly.svg";
|
|
45
|
+
import iframeUnavailableSvgChineseSimplified from "./styles/iframeUnavailable/chineseSimplified.svg";
|
|
46
|
+
import iframeUnavailableSvgChineseTraditional from "./styles/iframeUnavailable/chineseTraditional.svg";
|
|
47
|
+
import iframeUnavailableSvgJapanese from "./styles/iframeUnavailable/japanese.svg";
|
|
48
|
+
import iframeUnavailableSvgKorean from "./styles/iframeUnavailable/korean.svg";
|
|
49
|
+
import iframeUnavailableSvgRussian from "./styles/iframeUnavailable/russian.svg";
|
|
50
|
+
import iframeUnavailableSvgSpanish from "./styles/iframeUnavailable/spanish.svg";
|
|
51
|
+
import iframeUnavailableSvgTurkish from "./styles/iframeUnavailable/turkish.svg";
|
|
52
|
+
import iframeUnavailableSvgDutch from "./styles/iframeUnavailable/dutch.svg";
|
|
53
|
+
import iframeUnavailableSvgFrench from "./styles/iframeUnavailable/french.svg";
|
|
54
|
+
import iframeUnavailableSvgGerman from "./styles/iframeUnavailable/german.svg";
|
|
55
|
+
import iframeUnavailableSvgItalian from "./styles/iframeUnavailable/italian.svg";
|
|
56
|
+
import iframeUnavailableSvgPortuguese from "./styles/iframeUnavailable/portuguese.svg";
|
|
57
|
+
const iframeUnavailableSvg = {
|
|
58
|
+
"de-de": iframeUnavailableSvgGerman,
|
|
59
|
+
"en-gb": iframeUnavailableSvgEnglish,
|
|
60
|
+
"en-us": iframeUnavailableSvgEnglish,
|
|
61
|
+
"es-es": iframeUnavailableSvgSpanish,
|
|
62
|
+
"fr-fr": iframeUnavailableSvgFrench,
|
|
63
|
+
"it-it": iframeUnavailableSvgItalian,
|
|
64
|
+
"ja-jp": iframeUnavailableSvgJapanese,
|
|
65
|
+
"ko-kr": iframeUnavailableSvgKorean,
|
|
66
|
+
"nl-nl": iframeUnavailableSvgDutch,
|
|
67
|
+
"pt-br": iframeUnavailableSvgPortuguese,
|
|
68
|
+
"ru-ru": iframeUnavailableSvgRussian,
|
|
69
|
+
"tr-tr": iframeUnavailableSvgTurkish,
|
|
70
|
+
"zh-hans": iframeUnavailableSvgChineseSimplified,
|
|
71
|
+
"zh-hant": iframeUnavailableSvgChineseTraditional,
|
|
72
|
+
}
|
|
73
|
+
/* END iframeUnavailableSvgs */
|
|
74
|
+
|
|
75
|
+
/* BEGIN imageMaskedSvgs */
|
|
76
|
+
import imageMaskedSvgEnglish from "./styles/imageMasked/english.svg";
|
|
77
|
+
import imageMaskedSvgSmall from "./styles/imageMasked/iconOnly.svg";
|
|
78
|
+
import imageMaskedSvgChineseSimplified from "./styles/imageMasked/chineseSimplified.svg";
|
|
79
|
+
import imageMaskedSvgChineseTraditional from "./styles/imageMasked/chineseTraditional.svg";
|
|
80
|
+
import imageMaskedSvgJapanese from "./styles/imageMasked/japanese.svg";
|
|
81
|
+
import imageMaskedSvgKorean from "./styles/imageMasked/korean.svg";
|
|
82
|
+
import imageMaskedSvgRussian from "./styles/imageMasked/russian.svg";
|
|
83
|
+
import imageMaskedSvgSpanish from "./styles/imageMasked/spanish.svg";
|
|
84
|
+
import imageMaskedSvgTurkish from "./styles/imageMasked/turkish.svg";
|
|
85
|
+
import imageMaskedSvgDutch from "./styles/imageMasked/dutch.svg";
|
|
86
|
+
import imageMaskedSvgFrench from "./styles/imageMasked/french.svg";
|
|
87
|
+
import imageMaskedSvgGerman from "./styles/imageMasked/german.svg";
|
|
88
|
+
import imageMaskedSvgItalian from "./styles/imageMasked/italian.svg";
|
|
89
|
+
import imageMaskedSvgPortuguese from "./styles/imageMasked/portuguese.svg";
|
|
90
|
+
const imageMaskedSvg = {
|
|
91
|
+
"de-de": imageMaskedSvgGerman,
|
|
92
|
+
"en-gb": imageMaskedSvgEnglish,
|
|
93
|
+
"en-us": imageMaskedSvgEnglish,
|
|
94
|
+
"es-es": imageMaskedSvgSpanish,
|
|
95
|
+
"fr-fr": imageMaskedSvgFrench,
|
|
96
|
+
"it-it": imageMaskedSvgItalian,
|
|
97
|
+
"ja-jp": imageMaskedSvgJapanese,
|
|
98
|
+
"ko-kr": imageMaskedSvgKorean,
|
|
99
|
+
"nl-nl": imageMaskedSvgDutch,
|
|
100
|
+
"pt-br": imageMaskedSvgPortuguese,
|
|
101
|
+
"ru-ru": imageMaskedSvgRussian,
|
|
102
|
+
"tr-tr": imageMaskedSvgTurkish,
|
|
103
|
+
"zh-hans": imageMaskedSvgChineseSimplified,
|
|
104
|
+
"zh-hant": imageMaskedSvgChineseTraditional,
|
|
105
|
+
}
|
|
106
|
+
/* END imageMaskedSvgs */
|
|
107
|
+
|
|
108
|
+
export class LayoutHelper {
|
|
109
|
+
static TIMEOUT = 3000;
|
|
110
|
+
|
|
111
|
+
primaryHtmlNodeId: number | null = null;
|
|
112
|
+
isMobile: boolean;
|
|
113
|
+
stylesheets: Promise<void>[] = [];
|
|
114
|
+
fonts: Promise<void>[] = [];
|
|
115
|
+
nodes = {};
|
|
116
|
+
events = {};
|
|
117
|
+
hashMapAlpha = {};
|
|
118
|
+
hashMapBeta = {};
|
|
119
|
+
adoptedStyleSheets = {};
|
|
120
|
+
animations = {};
|
|
121
|
+
state: PlaybackState = null;
|
|
122
|
+
stylesToApply: { [id: string] : string[] } = {};
|
|
123
|
+
BackgroundImageEligibleElements = ['DIV', 'SECTION', 'ARTICLE', 'HEADER', 'FOOTER', 'ASIDE', 'NAV', 'SPAN', 'P', 'MAIN'];
|
|
124
|
+
MaskedBackgroundImageStyle = `#CCC no-repeat center url("${Asset.Hide}")`;
|
|
125
|
+
vNext: boolean;
|
|
126
|
+
locale: string;
|
|
127
|
+
|
|
128
|
+
constructor(state: PlaybackState, isMobile = false, vNext = false, locale = 'en-us') {
|
|
129
|
+
this.state = state;
|
|
130
|
+
this.isMobile = isMobile;
|
|
131
|
+
this.vNext = vNext;
|
|
132
|
+
this.locale = locale;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public reset = (): void => {
|
|
136
|
+
this.nodes = {};
|
|
137
|
+
this.stylesheets = [];
|
|
138
|
+
this.fonts = [];
|
|
139
|
+
this.events = {};
|
|
140
|
+
this.hashMapAlpha = {};
|
|
141
|
+
this.hashMapBeta = {};
|
|
142
|
+
this.primaryHtmlNodeId = null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public get = (hash) => {
|
|
146
|
+
if (hash in this.hashMapBeta && this.hashMapBeta[hash].isConnected) {
|
|
147
|
+
return this.hashMapBeta[hash];
|
|
148
|
+
} else if (hash in this.hashMapAlpha && this.hashMapAlpha[hash].isConnected) {
|
|
149
|
+
return this.hashMapAlpha[hash];
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private addToHashMap = (data: DecodedLayout.DomData, parent: Node) => {
|
|
155
|
+
// In case of selector collision, prefer the first inserted node
|
|
156
|
+
this.hashMapAlpha[data.hashAlpha] = this.get(data.hashAlpha) || parent;
|
|
157
|
+
this.hashMapBeta[data.hashBeta] = this.get(data.hashBeta) || parent;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private resize = (el: HTMLElement, width: number, height: number): void => {
|
|
161
|
+
if (el && el.nodeType === NodeType.ELEMENT_NODE && width && height) {
|
|
162
|
+
el.style.width = width + Layout.Constant.Pixel;
|
|
163
|
+
el.style.height = height + Layout.Constant.Pixel;
|
|
164
|
+
el.style.boxSizing = Layout.Constant.BorderBox; // Reference: https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public element = (nodeId: number): Node | null => {
|
|
169
|
+
return nodeId !== null && nodeId > 0 && nodeId in this.nodes ? this.nodes[nodeId] : null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
public animateChange = (event: DecodedLayout.AnimationEvent): void => {
|
|
173
|
+
let animation: Animation = this.animations[event.data.id];
|
|
174
|
+
if (!animation && event.data.operation !== AnimationOperation.Create) {
|
|
175
|
+
// We didn't have a reference to this animation. This shouldn't happen, but returning here
|
|
176
|
+
// to ensure we don't throw any errors.
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
switch(event.data.operation) {
|
|
180
|
+
case AnimationOperation.Create:
|
|
181
|
+
let target = this.element(event.data.targetId);
|
|
182
|
+
// only create the animation if we successfully found the target, an animation without a target will throw an error
|
|
183
|
+
if (target) {
|
|
184
|
+
this.animations[event.data.id] = (target as HTMLElement).animate(JSON.parse(event.data.keyFrames), JSON.parse(event.data.timing));
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
case AnimationOperation.Cancel:
|
|
188
|
+
animation.cancel();
|
|
189
|
+
break;
|
|
190
|
+
case AnimationOperation.Finish:
|
|
191
|
+
animation.finish();
|
|
192
|
+
break;
|
|
193
|
+
case AnimationOperation.Pause:
|
|
194
|
+
animation.pause();
|
|
195
|
+
break;
|
|
196
|
+
case AnimationOperation.Play:
|
|
197
|
+
animation.play();
|
|
198
|
+
break;
|
|
199
|
+
case AnimationOperation.CommitStyles:
|
|
200
|
+
animation.commitStyles();
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
public dom = async (event: DecodedLayout.DomEvent, useproxy?: LinkHandler): Promise<void> => {
|
|
206
|
+
if (event) {
|
|
207
|
+
// When setting up rendering for the first time, start off with hidden target window
|
|
208
|
+
// This ensures we do not show flickers to the end user
|
|
209
|
+
let doc = this.state.window.document;
|
|
210
|
+
if (doc && doc.documentElement) {
|
|
211
|
+
doc.documentElement.style.visibility = Constant.Hidden;
|
|
212
|
+
// Render all DOM events to reconstruct the page
|
|
213
|
+
this.markup(event, useproxy);
|
|
214
|
+
// Wait on all stylesheets and fonts to finish loading
|
|
215
|
+
await Promise.all(this.stylesheets.concat(this.fonts));
|
|
216
|
+
// Toggle back the visibility of target window
|
|
217
|
+
doc.documentElement.style.visibility = Constant.Visible;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
public styleChange = (event: DecodedLayout.StyleSheetEvent): void => {
|
|
223
|
+
switch (event.event) {
|
|
224
|
+
case Data.Event.StyleSheetUpdate:
|
|
225
|
+
let styleSheet: CSSStyleSheet = this.adoptedStyleSheets[event.data.id];
|
|
226
|
+
if (!styleSheet && event.data.operation !== StyleSheetOperation.Create) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
switch (event.data.operation) {
|
|
230
|
+
case StyleSheetOperation.Create:
|
|
231
|
+
this.adoptedStyleSheets[event.data.id] = new (this.state.window as any).CSSStyleSheet();
|
|
232
|
+
break;
|
|
233
|
+
case StyleSheetOperation.Replace:
|
|
234
|
+
styleSheet.replace(event.data.cssRules);
|
|
235
|
+
break;
|
|
236
|
+
case StyleSheetOperation.ReplaceSync:
|
|
237
|
+
styleSheet.replaceSync(event.data.cssRules);
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
break;
|
|
241
|
+
case Data.Event.StyleSheetAdoption:
|
|
242
|
+
this.setDocumentStyles(event.data.id as number, event.data.newIds);
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
public customElement = (event: DecodedLayout.CustomElementEvent): void => {
|
|
248
|
+
if (!this.state.window.customElements.get(event.data.name)) {
|
|
249
|
+
this.state.window.customElements.define(event.data.name, class extends (this.state.window as typeof window).HTMLElement {});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private setDocumentStyles(documentId: number, styleIds: string[]) {
|
|
254
|
+
let targetDocument = documentId === -1 ? this.state.window.document : this.element(documentId) as Document;
|
|
255
|
+
|
|
256
|
+
if (!targetDocument) {
|
|
257
|
+
if (!this.stylesToApply[documentId]) {
|
|
258
|
+
this.stylesToApply[documentId] = [];
|
|
259
|
+
}
|
|
260
|
+
this.stylesToApply[documentId] = styleIds;
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
let newSheets: CSSStyleSheet[] = [];
|
|
265
|
+
for (var styleId of styleIds) {
|
|
266
|
+
let styleSheet = this.adoptedStyleSheets[styleId];
|
|
267
|
+
if (styleSheet) {
|
|
268
|
+
newSheets.push(styleSheet);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
targetDocument.adoptedStyleSheets = newSheets
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
public exists = (hash: string): boolean => {
|
|
275
|
+
if (hash) {
|
|
276
|
+
let match = this.get(hash);
|
|
277
|
+
if (match) {
|
|
278
|
+
let rectangle = match.getBoundingClientRect();
|
|
279
|
+
return rectangle && rectangle.width > 0 && rectangle.height > 0;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
public markup = (event: DecodedLayout.DomEvent, useproxy?: LinkHandler): void => {
|
|
286
|
+
let data = event.data;
|
|
287
|
+
let type = event.event;
|
|
288
|
+
let doc = this.state.window.document;
|
|
289
|
+
let retryEvent: DecodedLayout.DomEvent = {
|
|
290
|
+
data: [],
|
|
291
|
+
time: event.time,
|
|
292
|
+
event: event.event
|
|
293
|
+
}
|
|
294
|
+
for (let node of data) {
|
|
295
|
+
let parent = this.element(node.parent);
|
|
296
|
+
let pivot = this.element(node.previous);
|
|
297
|
+
let insert = this.insertAfter;
|
|
298
|
+
|
|
299
|
+
let tag = node.tag;
|
|
300
|
+
if (tag && tag.indexOf(Layout.Constant.IFramePrefix) === 0) { tag = node.tag.substr(Layout.Constant.IFramePrefix.length); }
|
|
301
|
+
if (parent === null && node.parent !== null && node.parent > -1 && tag !== "HTML") {
|
|
302
|
+
// We are referencing a parent for this node that hasn't been created yet. Push it to a list of nodes to
|
|
303
|
+
// try once we are finished with other nodes within this event. Though we don't require HTML tags to
|
|
304
|
+
// have a parent as they are typically the root.
|
|
305
|
+
retryEvent.data.push(node);
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
switch (tag) {
|
|
309
|
+
case Layout.Constant.DocumentTag:
|
|
310
|
+
let tagDoc = tag !== node.tag ? (parent ? (parent as HTMLIFrameElement).contentDocument : null): doc;
|
|
311
|
+
if (tagDoc && tagDoc === doc && type === Data.Event.Discover) { this.reset(); }
|
|
312
|
+
if (typeof XMLSerializer !== "undefined" && tagDoc) {
|
|
313
|
+
tagDoc.open();
|
|
314
|
+
tagDoc.write(new XMLSerializer().serializeToString(
|
|
315
|
+
tagDoc.implementation.createDocumentType(
|
|
316
|
+
node.attributes["name"],
|
|
317
|
+
node.attributes["publicId"],
|
|
318
|
+
node.attributes["systemId"]
|
|
319
|
+
)
|
|
320
|
+
));
|
|
321
|
+
tagDoc.close();
|
|
322
|
+
}
|
|
323
|
+
break;
|
|
324
|
+
case Layout.Constant.PolyfillShadowDomTag:
|
|
325
|
+
// In case of polyfill, map shadow dom to it's parent for rendering purposes
|
|
326
|
+
// All its children should be inserted as regular children to the parent node.
|
|
327
|
+
this.nodes[node.id] = parent;
|
|
328
|
+
this.addToHashMap(node, parent);
|
|
329
|
+
break;
|
|
330
|
+
case Layout.Constant.ShadowDomTag:
|
|
331
|
+
if (parent) {
|
|
332
|
+
let shadowRoot = this.element(node.id);
|
|
333
|
+
shadowRoot = shadowRoot ? shadowRoot : (parent as HTMLElement).attachShadow({ mode: "open" });
|
|
334
|
+
this.nodes[node.id] = shadowRoot;
|
|
335
|
+
this.addToHashMap(node, shadowRoot);
|
|
336
|
+
this.addStyles(node.id);
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
case Layout.Constant.TextTag:
|
|
340
|
+
let textElement = this.element(node.id);
|
|
341
|
+
textElement = textElement ? textElement : doc.createTextNode(null);
|
|
342
|
+
textElement.nodeValue = node.value;
|
|
343
|
+
insert(node, parent, textElement, pivot);
|
|
344
|
+
break;
|
|
345
|
+
case Layout.Constant.SuspendMutationTag:
|
|
346
|
+
let suspendedElement = this.element(node.id);
|
|
347
|
+
if (suspendedElement && suspendedElement.nodeType === Node.ELEMENT_NODE) {
|
|
348
|
+
(suspendedElement as HTMLElement).setAttribute(Constant.Suspend, Layout.Constant.Empty);
|
|
349
|
+
}
|
|
350
|
+
break;
|
|
351
|
+
case "HTML":
|
|
352
|
+
if (this.primaryHtmlNodeId === null) {
|
|
353
|
+
this.primaryHtmlNodeId = node.id;
|
|
354
|
+
}
|
|
355
|
+
let isIframe = tag !== node.tag;
|
|
356
|
+
// when we see multiple HTML nodes in the same document we should treat subsequent ones as child elements
|
|
357
|
+
// rather than redefining our visualization base on them. It's technically illegal HTML but enough sites have
|
|
358
|
+
// this structure that we are robust against it.
|
|
359
|
+
if (this.primaryHtmlNodeId !== node.id && !isIframe) {
|
|
360
|
+
this.insertDefaultElement(node, parent, pivot, doc, insert);
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
let htmlDoc = isIframe ? (parent ? (parent as HTMLIFrameElement).contentDocument : null): doc;
|
|
364
|
+
if (htmlDoc !== null) {
|
|
365
|
+
let docElement = this.element(node.id) as HTMLElement;
|
|
366
|
+
if (docElement === null) {
|
|
367
|
+
let newDoc = htmlDoc.implementation.createHTMLDocument(Layout.Constant.Empty);
|
|
368
|
+
docElement = newDoc.documentElement;
|
|
369
|
+
let p = htmlDoc.importNode(docElement, true);
|
|
370
|
+
htmlDoc.replaceChild(p, htmlDoc.documentElement);
|
|
371
|
+
if (htmlDoc.head) { htmlDoc.head.parentNode.removeChild(htmlDoc.head); }
|
|
372
|
+
if (htmlDoc.body) { htmlDoc.body.parentNode.removeChild(htmlDoc.body); }
|
|
373
|
+
}
|
|
374
|
+
this.setAttributes(htmlDoc.documentElement, node);
|
|
375
|
+
// If we are still processing discover events, keep the markup hidden until we are done
|
|
376
|
+
if (type === Data.Event.Discover && !parent) { htmlDoc.documentElement.style.visibility = Constant.Hidden; }
|
|
377
|
+
this.nodes[node.id] = htmlDoc.documentElement;
|
|
378
|
+
this.addToHashMap(node, htmlDoc.documentElement);
|
|
379
|
+
}
|
|
380
|
+
break;
|
|
381
|
+
case "HEAD":
|
|
382
|
+
let headElement = this.element(node.id);
|
|
383
|
+
if (headElement === null) {
|
|
384
|
+
headElement = doc.createElement(node.tag);
|
|
385
|
+
if (node.attributes && Layout.Constant.Base in node.attributes) {
|
|
386
|
+
let base = doc.createElement("base");
|
|
387
|
+
base.href = node.attributes[Layout.Constant.Base];
|
|
388
|
+
headElement.appendChild(base);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Add custom styles to assist with visualization
|
|
392
|
+
let custom = doc.createElement("style");
|
|
393
|
+
custom.setAttribute(Constant.CustomStyleTag, "true");
|
|
394
|
+
custom.innerText = this.getCustomStyle();
|
|
395
|
+
headElement.appendChild(custom);
|
|
396
|
+
}
|
|
397
|
+
this.setAttributes(headElement as HTMLElement, node);
|
|
398
|
+
insert(node, parent, headElement, pivot);
|
|
399
|
+
break;
|
|
400
|
+
case "LINK":
|
|
401
|
+
let linkElement = this.element(node.id) as HTMLLinkElement;
|
|
402
|
+
linkElement = linkElement ? linkElement : this.createElement(doc, node.tag) as HTMLLinkElement;
|
|
403
|
+
if (!node.attributes) { node.attributes = {}; }
|
|
404
|
+
this.setAttributes(linkElement, node);
|
|
405
|
+
if ("rel" in node.attributes) {
|
|
406
|
+
if (node.attributes["rel"] === Constant.StyleSheet) {
|
|
407
|
+
this.stylesheets.push(new Promise((resolve: () => void): void => {
|
|
408
|
+
const proxy = useproxy ?? this.state.options.useproxy;
|
|
409
|
+
if (proxy) {
|
|
410
|
+
if (linkElement.integrity) {
|
|
411
|
+
linkElement.removeAttribute('integrity');
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
linkElement.href = proxy(linkElement.href, linkElement.id, Constant.StyleSheet);
|
|
415
|
+
}
|
|
416
|
+
linkElement.onload = linkElement.onerror = this.style.bind(this, linkElement, resolve);
|
|
417
|
+
setTimeout(resolve, LayoutHelper.TIMEOUT);
|
|
418
|
+
}));
|
|
419
|
+
} else if ((node.attributes["rel"].includes("preload") || node.attributes["rel"].includes("preconnect"))
|
|
420
|
+
&& (node.attributes?.as === "style" || node.attributes?.as === "font")) {
|
|
421
|
+
this.fonts.push(new Promise((resolve: () => void): void => {
|
|
422
|
+
const proxy = useproxy ?? this.state.options.useproxy;
|
|
423
|
+
linkElement.href = proxy ? proxy(linkElement.href, linkElement.id, node.attributes.as) : linkElement.href;
|
|
424
|
+
linkElement.onload = linkElement.onerror = this.style.bind(this, linkElement, resolve);
|
|
425
|
+
setTimeout(resolve, LayoutHelper.TIMEOUT);
|
|
426
|
+
}));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
insert(node, parent, linkElement, pivot);
|
|
430
|
+
break;
|
|
431
|
+
case Layout.Constant.ImageTag:
|
|
432
|
+
let imgElement = this.element(node.id) as HTMLImageElement ?? this.createElement(doc, node.tag) as HTMLImageElement;
|
|
433
|
+
const proxy = useproxy ?? this.state.options.useproxy;
|
|
434
|
+
if (proxy && !!node.attributes?.src) {
|
|
435
|
+
node.attributes.src = proxy(node.attributes.src, node.attributes.id, Layout.Constant.ImageTag);
|
|
436
|
+
}
|
|
437
|
+
this.setAttributes(imgElement, node);
|
|
438
|
+
this.resize(imgElement, node.width, node.height);
|
|
439
|
+
insert(node, parent, imgElement, pivot);
|
|
440
|
+
break;
|
|
441
|
+
case "STYLE":
|
|
442
|
+
let styleElement = this.element(node.id) as HTMLStyleElement ?? doc.createElement(node.tag) as HTMLStyleElement;
|
|
443
|
+
this.setAttributes(styleElement, node);
|
|
444
|
+
styleElement.textContent = node.value;
|
|
445
|
+
insert(node, parent, styleElement, pivot);
|
|
446
|
+
this.style(styleElement);
|
|
447
|
+
break;
|
|
448
|
+
case "IFRAME":
|
|
449
|
+
let iframeElement = this.element(node.id) as HTMLIFrameElement;
|
|
450
|
+
iframeElement = iframeElement ? iframeElement : this.createElement(doc, node.tag) as HTMLIFrameElement;
|
|
451
|
+
if (!node.attributes) { node.attributes = {}; }
|
|
452
|
+
this.setAttributes(iframeElement, node);
|
|
453
|
+
insert(node, parent, iframeElement, pivot);
|
|
454
|
+
break;
|
|
455
|
+
default:
|
|
456
|
+
this.insertDefaultElement(node, parent, pivot, doc, insert);
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
// Track state for this node
|
|
460
|
+
if (node.id) { this.events[node.id] = node; }
|
|
461
|
+
}
|
|
462
|
+
// only retry failed nodes if we are still making positive progress. If we have the same number of
|
|
463
|
+
// nodes we started with, then we would just be spinning on an orphaned subtree.
|
|
464
|
+
if (retryEvent.data.length > 0 && retryEvent.data.length !== event.data.length) {
|
|
465
|
+
this.markup(retryEvent, useproxy);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
private insertDefaultElement = (node: DecodedLayout.DomData, parent: Node, pivot: Node, doc: Document, insert: (data: DecodedLayout.DomData, parent: Node, node: Node, previous: Node) => void): void => {
|
|
470
|
+
let domElement = this.element(node.id) as HTMLElement;
|
|
471
|
+
domElement = domElement ? domElement : this.createElement(doc, node.tag);
|
|
472
|
+
this.setAttributes(domElement as HTMLElement, node);
|
|
473
|
+
this.resize(domElement, node.width, node.height);
|
|
474
|
+
insert(node, parent, domElement, pivot);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
private style = (node: HTMLLinkElement | HTMLStyleElement, resolve: () => void = null): void => {
|
|
478
|
+
// Firefox throws a SecurityError when trying to access cssRules of a stylesheet from a different domain
|
|
479
|
+
try {
|
|
480
|
+
const sheet = node.sheet as CSSStyleSheet;
|
|
481
|
+
let cssRules = sheet ? sheet.cssRules : [];
|
|
482
|
+
for (let i = 0; i < cssRules.length; i++) {
|
|
483
|
+
if (cssRules[i].cssText.indexOf(Constant.Hover) >= 0) {
|
|
484
|
+
let css = cssRules[i].cssText.replace(/:hover/g, `[${Constant.CustomHover}]`);
|
|
485
|
+
sheet.removeRule(i);
|
|
486
|
+
sheet.insertRule(css, i);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
} catch (e) {
|
|
490
|
+
if (this.state.options.logerror) {
|
|
491
|
+
this.state.options.logerror(e);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (resolve) { resolve(); }
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
private addStyles = (id: number): void => {
|
|
499
|
+
let adoptedStylesToAdd = this.stylesToApply[id];
|
|
500
|
+
if (adoptedStylesToAdd && adoptedStylesToAdd.length > 0) {
|
|
501
|
+
this.setDocumentStyles(id, this.stylesToApply[id]);
|
|
502
|
+
delete this.stylesToApply[id];
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
private createElement = (doc: Document, tag: string): HTMLElement => {
|
|
507
|
+
if (tag && tag.indexOf(Layout.Constant.SvgPrefix) === 0) {
|
|
508
|
+
return doc.createElementNS(Layout.Constant.SvgNamespace as string, tag.substr(Layout.Constant.SvgPrefix.length)) as HTMLElement;
|
|
509
|
+
}
|
|
510
|
+
try {
|
|
511
|
+
return doc.createElement(tag);
|
|
512
|
+
} catch (ex) {
|
|
513
|
+
// We log the warning on non-standard markup but continue with the visualization
|
|
514
|
+
console.warn(`Exception encountered while creating element ${tag}: ${ex}`);
|
|
515
|
+
return doc.createElement(Constant.UnknownTag);
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
private insertAfter = (data: DecodedLayout.DomData, parent: Node, node: Node, previous: Node): void => {
|
|
520
|
+
// Skip over no-op changes where parent and previous element is still the same
|
|
521
|
+
// In case of IFRAME, re-adding DOM at the exact same place will lead to loss of state and the markup inside will be destroyed
|
|
522
|
+
if (this.events[data.id] && this.events[data.id].parent === data.parent && this.events[data.id].previous === data.previous) { return; }
|
|
523
|
+
// In case parent is a Shadow DOM, previous.parentElement will return null but previous.parentNode will return a valid node
|
|
524
|
+
let next = previous && (previous.parentElement === parent || previous.parentNode === parent) ? previous.nextSibling : null;
|
|
525
|
+
next = previous === null && parent ? this.firstChild(parent) : next;
|
|
526
|
+
this.insertBefore(data, parent, node, next);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
private firstChild = (node: Node): ChildNode => {
|
|
530
|
+
let child = node.firstChild;
|
|
531
|
+
// BASE tag should always be the first child to ensure resources with relative URLs are loaded correctly
|
|
532
|
+
if (child && child.nodeType === NodeType.ELEMENT_NODE && (child as HTMLElement).tagName === Layout.Constant.BaseTag) {
|
|
533
|
+
if((child.nextSibling as HTMLElement)?.hasAttribute('clarity-custom-styles')){
|
|
534
|
+
// Keep the custom style tag on top of the head to let client tags override its values.
|
|
535
|
+
return child.nextSibling.nextSibling;
|
|
536
|
+
}
|
|
537
|
+
return child.nextSibling;
|
|
538
|
+
}
|
|
539
|
+
return child;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
// Mask images within a masked ancestor element in the node has a background image.
|
|
544
|
+
private mask = (node: HTMLElement) => {
|
|
545
|
+
if (node && this.BackgroundImageEligibleElements.includes(node.nodeName) && 'getComputedStyle' in window && 'closest' in node) {
|
|
546
|
+
const urlPattern = /url\(['"]?([^'")]+)['"]?\)/;
|
|
547
|
+
const computedStyles = window.getComputedStyle(node);
|
|
548
|
+
const hasBackgroundImage = computedStyles.backgroundImage?.match(urlPattern) || computedStyles.background?.match(urlPattern);
|
|
549
|
+
const masked = node.closest?.(`[${LayoutConstants.MaskData}]`);
|
|
550
|
+
|
|
551
|
+
if (hasBackgroundImage && masked) {
|
|
552
|
+
node.style.background = this.MaskedBackgroundImageStyle;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
private insertBefore = (data: DecodedLayout.DomData, parent: Node, node: Node, next: Node): void => {
|
|
558
|
+
if (parent !== null) {
|
|
559
|
+
// Compare against both parentNode and parentElement to ensure visualization works correctly for shadow DOMs
|
|
560
|
+
next = next && (next.parentElement !== parent && next.parentNode !== parent) ? null : next;
|
|
561
|
+
try {
|
|
562
|
+
parent.insertBefore(node, next);
|
|
563
|
+
this.mask(node as HTMLElement);
|
|
564
|
+
} catch (ex) {
|
|
565
|
+
console.warn("Node: " + node + " | Parent: " + parent + " | Data: " + JSON.stringify(data));
|
|
566
|
+
console.warn("Exception encountered while inserting node: " + ex);
|
|
567
|
+
}
|
|
568
|
+
} else if (parent === null && node.parentElement !== null) {
|
|
569
|
+
node.parentElement.removeChild(node);
|
|
570
|
+
} else if (parent === null && node.parentNode !== null) {
|
|
571
|
+
node.parentNode.removeChild(node);
|
|
572
|
+
}
|
|
573
|
+
this.nodes[data.id] = node;
|
|
574
|
+
this.addToHashMap(data, node);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
private setAttributes = (node: HTMLElement, data: DecodedLayout.DomData): void => {
|
|
578
|
+
let attributes = data.attributes || {};
|
|
579
|
+
let sameorigin = false;
|
|
580
|
+
|
|
581
|
+
// Clarity attributes
|
|
582
|
+
attributes[Constant.Id] = `${data.id}`;
|
|
583
|
+
attributes[Constant.HashAlpha] = `${data.hashAlpha}`;
|
|
584
|
+
attributes[Constant.HashBeta] = `${data.hashBeta}`;
|
|
585
|
+
|
|
586
|
+
let tag = node.nodeType === NodeType.ELEMENT_NODE ? node.tagName.toLowerCase() : null;
|
|
587
|
+
// First remove all its existing attributes
|
|
588
|
+
if (node.attributes) {
|
|
589
|
+
let length = node.attributes.length;
|
|
590
|
+
while (node.attributes && length > 0) {
|
|
591
|
+
// Do not remove "clarity-hover" attribute and let it be managed by interaction module
|
|
592
|
+
// This helps avoid flickers during visualization
|
|
593
|
+
if (node.attributes[0].name !== Constant.HoverAttribute) {
|
|
594
|
+
node.removeAttribute(node.attributes[0].name);
|
|
595
|
+
}
|
|
596
|
+
length--;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Add new attributes
|
|
601
|
+
for (let attribute in attributes) {
|
|
602
|
+
if (attributes[attribute] !== undefined) {
|
|
603
|
+
try {
|
|
604
|
+
let v = attributes[attribute];
|
|
605
|
+
if (attribute.indexOf("xlink:") === 0) {
|
|
606
|
+
node.setAttributeNS("http://www.w3.org/1999/xlink", attribute, v);
|
|
607
|
+
} else if (attribute.indexOf(Layout.Constant.SameOrigin) === 0) {
|
|
608
|
+
sameorigin = true;
|
|
609
|
+
} else if (attribute.indexOf("*") === 0) {
|
|
610
|
+
// Do nothing if we encounter internal Clarity attributes
|
|
611
|
+
} else if (tag === Constant.IFrameTag && (attribute.indexOf("src") === 0 || attribute.indexOf("allow") === 0) || attribute === "sandbox") {
|
|
612
|
+
node.setAttribute(`data-clarity-${attribute}`, v);
|
|
613
|
+
} else if (tag === Constant.ImageTag && attribute.indexOf("src") === 0 && ((v === null || v.length === 0 || v?.startsWith('blob:')))) {
|
|
614
|
+
if (this.vNext) {
|
|
615
|
+
if (v.startsWith('blob:')) {
|
|
616
|
+
if (data.width >= Setting.LargeSvg && data.height >= Setting.LargeSvg) {
|
|
617
|
+
node.setAttribute(Constant.BlobUnavailable, `${Constant.Large}${Constant.Beta}`);
|
|
618
|
+
} else {
|
|
619
|
+
node.setAttribute(Constant.BlobUnavailable, `${Constant.Small}${Constant.Beta}`);
|
|
620
|
+
}
|
|
621
|
+
} else {
|
|
622
|
+
if (data.width >= Setting.LargeSvg && data.height >= Setting.LargeSvg) {
|
|
623
|
+
node.setAttribute(Constant.Hide, `${Constant.Large}${Constant.Beta}`);
|
|
624
|
+
} else {
|
|
625
|
+
node.setAttribute(Constant.Hide, `${Constant.Small}${Constant.Beta}`);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
} else {
|
|
629
|
+
node.setAttribute(attribute, Asset.Transparent);
|
|
630
|
+
let size = Constant.Large;
|
|
631
|
+
if (data.width) {
|
|
632
|
+
size = data.width <= Setting.Medium ? Constant.Medium : (data.width <= Setting.Small ? Constant.Small : size);
|
|
633
|
+
}
|
|
634
|
+
node.setAttribute(Constant.Hide, size);
|
|
635
|
+
}
|
|
636
|
+
} else {
|
|
637
|
+
node.setAttribute(attribute, v);
|
|
638
|
+
}
|
|
639
|
+
} catch (ex) {
|
|
640
|
+
console.warn("Node: " + node + " | " + JSON.stringify(attributes));
|
|
641
|
+
console.warn("Exception encountered while adding attributes: " + ex);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (sameorigin === false && tag === Constant.IFrameTag && typeof node.setAttribute === Constant.Function) {
|
|
647
|
+
if (this.svgFitsText(node)) {
|
|
648
|
+
node.setAttribute(Constant.Unavailable, Layout.Constant.Empty);
|
|
649
|
+
} else {
|
|
650
|
+
node.setAttribute(Constant.UnavailableSmall, Layout.Constant.Empty);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Add an empty ALT tag on all IMG elements
|
|
655
|
+
if (tag === Constant.ImageTag && !node.hasAttribute(Constant.AltAttribute)) { node.setAttribute(Constant.AltAttribute, Constant.Empty); }
|
|
656
|
+
|
|
657
|
+
// During visualization This will prevent the browser from auto filling form fields with saved details of user who is seeing the visualization
|
|
658
|
+
if (tag === Constant.FormTag || tag === Constant.InputTag) {
|
|
659
|
+
if (node.hasAttribute(Constant.AutoComplete)) {
|
|
660
|
+
node.removeAttribute(Constant.AutoComplete);
|
|
661
|
+
}
|
|
662
|
+
node.setAttribute(Constant.AutoComplete, Constant.NewPassword);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
private getMobileCustomStyle = (): string => {
|
|
667
|
+
if(this.isMobile){
|
|
668
|
+
return `*{scrollbar-width: none; scrollbar-gutter: unset;};`
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
return '';
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
private getCustomStyle = (): string => {
|
|
675
|
+
return this.getImageHiddenCss() +
|
|
676
|
+
this.getIframeUnavailableCss() +
|
|
677
|
+
this.getBlobUnavailableCss() +
|
|
678
|
+
this.getBackgroundCss() +
|
|
679
|
+
`*[${Constant.Suspend}] { filter: grayscale(100%); }` +
|
|
680
|
+
`body { font-size: initial; }
|
|
681
|
+
${this.getMobileCustomStyle()}`;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
private svgFitsText = (inputElement: HTMLElement): boolean => {
|
|
685
|
+
var dimensions = inputElement.getBoundingClientRect();
|
|
686
|
+
if (dimensions.width >= Setting.LargeSvg && dimensions.height >= Setting.LargeSvg) {
|
|
687
|
+
return true;
|
|
688
|
+
}
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
private getIframeUnavailableCss = (): string => {
|
|
693
|
+
if (this.vNext) {
|
|
694
|
+
return `${Constant.IFrameTag}[${Constant.UnavailableSmall}] { ${iframeUnavailableSvgSmall} }` +
|
|
695
|
+
`${Constant.IFrameTag}[${Constant.Unavailable}] { ${iframeUnavailableSvg[this.locale]} }`;
|
|
696
|
+
} else {
|
|
697
|
+
return `${Constant.IFrameTag}[${Constant.Unavailable}] { background: url(${Asset.Unavailable}) no-repeat center center, url('${Asset.Cross}'); }`;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
private getBlobUnavailableCss = (): string => {
|
|
702
|
+
if (this.vNext) {
|
|
703
|
+
return `${Constant.ImageTag}[${Constant.BlobUnavailable}=${Constant.Small}${Constant.Beta}] { ${blobUnavailableSvgSmall} }` +
|
|
704
|
+
`${Constant.ImageTag}[${Constant.BlobUnavailable}=${Constant.Large}${Constant.Beta}] { ${blobUnavailableSvg[this.locale]} }`;
|
|
705
|
+
}
|
|
706
|
+
return '';
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
private getImageHiddenCss = (): string => {
|
|
710
|
+
if (this.vNext) {
|
|
711
|
+
return `${Constant.ImageTag}[${Constant.Hide}=${Constant.Small}${Constant.Beta}] { ${imageMaskedSvgSmall} }` +
|
|
712
|
+
`${Constant.ImageTag}[${Constant.Hide}=${Constant.Large}${Constant.Beta}] { ${imageMaskedSvg[this.locale]} }`;
|
|
713
|
+
} else {
|
|
714
|
+
return `${Constant.ImageTag}[${Constant.Hide}] { background-color: #CCC; background-image: url(${Asset.Hide}); background-repeat:no-repeat; background-position: center; }` +
|
|
715
|
+
`${Constant.ImageTag}[${Constant.Hide}=${Constant.Small}] { background-size: 18px 18px; }` +
|
|
716
|
+
`${Constant.ImageTag}[${Constant.Hide}=${Constant.Medium}] { background-size: 24px 24px; }` +
|
|
717
|
+
`${Constant.ImageTag}[${Constant.Hide}=${Constant.Large}] { background-size: 36px 36px; }`;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
private getBackgroundCss = (): string => {
|
|
722
|
+
if (this.vNext) {
|
|
723
|
+
return sharedStyle;
|
|
724
|
+
}
|
|
725
|
+
return '';
|
|
726
|
+
}
|
|
727
|
+
}
|