-
Notifications
You must be signed in to change notification settings - Fork 478
Expand file tree
/
Copy pathhead_snapshot.js
More file actions
111 lines (96 loc) · 3.14 KB
/
head_snapshot.js
File metadata and controls
111 lines (96 loc) · 3.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { elementIsStylesheet } from "../../util"
import { Snapshot } from "../snapshot"
export class HeadSnapshot extends Snapshot {
detailsByOuterHTML = this.children
.filter((element) => !elementIsNoscript(element))
.filter((element) => element instanceof Element)
.map((element) => elementWithoutNonce(element))
.reduce((result, element) => {
const { outerHTML } = element
const details =
outerHTML in result
? result[outerHTML]
: {
type: elementType(element),
tracked: elementIsTracked(element),
elements: []
}
return {
...result,
[outerHTML]: {
...details,
elements: [...details.elements, element]
}
}
}, {})
get trackedElementSignature() {
return Object.keys(this.detailsByOuterHTML)
.filter((outerHTML) => this.detailsByOuterHTML[outerHTML].tracked)
.join("")
}
getScriptElementsNotInSnapshot(snapshot) {
return this.getElementsMatchingTypeNotInSnapshot("script", snapshot)
}
getStylesheetElementsNotInSnapshot(snapshot) {
return this.getElementsMatchingTypeNotInSnapshot("stylesheet", snapshot)
}
getElementsMatchingTypeNotInSnapshot(matchedType, snapshot) {
return Object.keys(this.detailsByOuterHTML)
.filter((outerHTML) => !(outerHTML in snapshot.detailsByOuterHTML))
.map((outerHTML) => this.detailsByOuterHTML[outerHTML])
.filter(({ type }) => type == matchedType)
.map(({ elements: [element] }) => element)
}
get provisionalElements() {
return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
const { type, tracked, elements } = this.detailsByOuterHTML[outerHTML]
if (type == null && !tracked) {
return [...result, ...elements]
} else if (elements.length > 1) {
return [...result, ...elements.slice(1)]
} else {
return result
}
}, [])
}
getMetaValue(name) {
const element = this.findMetaElementByName(name)
return element ? element.getAttribute("content") : null
}
findMetaElementByName(name) {
return Object.keys(this.detailsByOuterHTML).reduce((result, outerHTML) => {
const {
elements: [element]
} = this.detailsByOuterHTML[outerHTML]
return elementIsMetaElementWithName(element, name) ? element : result
}, undefined | undefined)
}
}
function elementType(element) {
if (elementIsScript(element)) {
return "script"
} else if (elementIsStylesheet(element)) {
return "stylesheet"
}
}
function elementIsTracked(element) {
return element.getAttribute("data-turbo-track") == "reload"
}
function elementIsScript(element) {
const tagName = element.localName
return tagName == "script"
}
function elementIsNoscript(element) {
const tagName = element.localName
return tagName == "noscript"
}
function elementIsMetaElementWithName(element, name) {
const tagName = element.localName
return tagName == "meta" && element.getAttribute("name") == name
}
function elementWithoutNonce(element) {
if (element.hasAttribute("nonce")) {
element.setAttribute("nonce", "")
}
return element
}