-
-
Notifications
You must be signed in to change notification settings - Fork 75
/
Copy pathbrowser.js
171 lines (140 loc) · 4.41 KB
/
browser.js
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import isValidReplacement from './is-valid-replacement.mjs';
const CSS_CLASS_LOADED = 'js-blank-pseudo';
// form control elements selector
function isFormControlElement(element) {
if (element.nodeName === 'INPUT' || element.nodeName === 'SELECT' || element.nodeName === 'TEXTAREA') {
return true;
}
return false;
}
function createNewEvent(eventName) {
let event;
if (typeof(Event) === 'function') {
event = new Event(eventName, { bubbles: true });
} else {
event = document.createEvent('Event');
event.initEvent(eventName, true, false);
}
return event;
}
function generateHandler(replaceWith) {
let selector;
let remove;
let add;
if (replaceWith[0] === '.') {
selector = replaceWith.slice(1);
remove = (el) => el.classList.remove(selector);
add = (el) => el.classList.add(selector);
} else {
// A bit naive
selector = replaceWith.slice(1, -1);
remove = (el) => el.removeAttribute(selector, '');
add = (el) => el.setAttribute(selector, '');
}
return function handleInputOrChangeEvent(event) {
const element = event.target;
if (!isFormControlElement(element)) {
return;
}
const isSelect = element.nodeName === 'SELECT';
const hasValue = isSelect
? !!element.options[element.selectedIndex]?.value
: !!element.value;
if (hasValue) {
remove(element);
} else {
add(element);
}
};
}
// observe changes to the "selected" property on an HTML Element
function observeSelectedOfHTMLElement(HTMLElement) {
const descriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'selected');
const nativeSet = descriptor.set;
descriptor.set = function set(value) { // eslint-disable-line no-unused-vars
nativeSet.apply(this, arguments);
const event = createNewEvent('change');
this.parentElement.dispatchEvent(event);
};
Object.defineProperty(HTMLElement.prototype, 'selected', descriptor);
}
// observe changes to the "value" property on an HTML Element
function observeValueOfHTMLElement(HTMLElement, handler) {
const descriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'value');
const nativeSet = descriptor.set;
descriptor.set = function set() {
nativeSet.apply(this, arguments);
handler({ target: this });
};
Object.defineProperty(HTMLElement.prototype, 'value', descriptor);
}
export default function cssBlankPseudoInit(opts) {
// configuration
const options = {
force: false,
replaceWith: '[blank]',
};
if (typeof opts !== 'undefined' && 'force' in opts) {
options.force = opts.force;
}
if (typeof opts !== 'undefined' && 'replaceWith' in opts) {
options.replaceWith = opts.replaceWith;
}
if (!isValidReplacement(options.replaceWith)) {
throw new Error(`${options.replaceWith} is not a valid replacement since it can't be applied to single elements.`);
}
try {
document.querySelector(':blank');
if (!options.force) {
return;
}
} catch (_) {}
const handler = generateHandler(options.replaceWith);
const bindEvents = () => {
if (document.body) {
document.body.addEventListener('change', handler);
document.body.addEventListener('input', handler);
}
};
const updateAllCandidates = () => {
Array.prototype.forEach.call(
document.querySelectorAll('input, select, textarea'),
node => {
handler({ target: node });
},
);
};
if (document.body) {
bindEvents();
} else {
window.addEventListener('load', bindEvents);
}
if (document.documentElement.className.indexOf(CSS_CLASS_LOADED) === -1) {
document.documentElement.className += ` ${CSS_CLASS_LOADED}`;
}
observeValueOfHTMLElement(self.HTMLInputElement, handler);
observeValueOfHTMLElement(self.HTMLSelectElement, handler);
observeValueOfHTMLElement(self.HTMLTextAreaElement, handler);
observeSelectedOfHTMLElement(self.HTMLOptionElement);
// conditionally update all form control elements
updateAllCandidates();
if (typeof self.MutationObserver !== 'undefined') {
// conditionally observe added or unobserve removed form control elements
new MutationObserver(mutationsList => {
mutationsList.forEach(mutation => {
Array.prototype.forEach.call(
mutation.addedNodes || [],
node => {
if (node.nodeType === 1 && isFormControlElement(node)) {
handler({ target: node });
}
},
);
});
}).observe(document, { childList: true, subtree: true });
} else {
const handleOnLoad = () => updateAllCandidates();
window.addEventListener('load', handleOnLoad);
window.addEventListener('DOMContentLoaded', handleOnLoad);
}
}