cuprite 0.19 → 0.20
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.
- checksums.yaml +4 -4
- data/README.md +43 -41
- data/lib/capybara/cuprite/browser/deprecations.rb +26 -0
- data/lib/capybara/cuprite/browser/extensions.rb +59 -0
- data/lib/capybara/cuprite/browser.rb +18 -232
- data/lib/capybara/cuprite/deprecation.rb +13 -0
- data/lib/capybara/cuprite/driver/deprecations.rb +198 -0
- data/lib/capybara/cuprite/driver/extensions.rb +13 -0
- data/lib/capybara/cuprite/driver.rb +66 -201
- data/lib/capybara/cuprite/finder.rb +22 -0
- data/lib/capybara/cuprite/javascripts/drag.js +96 -101
- data/lib/capybara/cuprite/javascripts/index.js +507 -508
- data/lib/capybara/cuprite/node/deprecations.rb +43 -0
- data/lib/capybara/cuprite/node/extensions.rb +27 -0
- data/lib/capybara/cuprite/node.rb +130 -116
- data/lib/capybara/cuprite/options.rb +1 -1
- data/lib/capybara/cuprite/page/frames.rb +47 -0
- data/lib/capybara/cuprite/page/modals.rb +118 -0
- data/lib/capybara/cuprite/page.rb +6 -209
- data/lib/capybara/cuprite/version.rb +1 -1
- metadata +15 -6
- data/lib/capybara/cuprite/cookie.rb +0 -47
|
@@ -1,629 +1,628 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
constructor() {
|
|
14
|
-
this._json = JSON; // In case someone overrides it like mootools
|
|
1
|
+
(() => {
|
|
2
|
+
if (window._cuprite) return;
|
|
3
|
+
|
|
4
|
+
class InvalidSelector extends Error {}
|
|
5
|
+
class TimedOutPromise extends Error {}
|
|
6
|
+
class MouseEventFailed extends Error {}
|
|
7
|
+
|
|
8
|
+
const EVENTS = {
|
|
9
|
+
FOCUS: ["blur", "focus", "focusin", "focusout"],
|
|
10
|
+
MOUSE: ["click", "dblclick", "mousedown", "mouseenter", "mouseleave",
|
|
11
|
+
"mousemove", "mouseover", "mouseout", "mouseup", "contextmenu"],
|
|
12
|
+
FORM: ["submit"]
|
|
15
13
|
}
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
class Cuprite {
|
|
16
|
+
constructor() {
|
|
17
|
+
this._json = JSON; // In case someone overrides it like mootools
|
|
18
|
+
}
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
find(method, selector, within = document) {
|
|
21
|
+
try {
|
|
22
|
+
let results = [];
|
|
23
|
+
|
|
24
|
+
if (method == "xpath") {
|
|
25
|
+
let xpath = document.evaluate(selector, within, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
|
26
|
+
for (let i = 0; i < xpath.snapshotLength; i++) {
|
|
27
|
+
results.push(xpath.snapshotItem(i));
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
results = Array.from(within.querySelectorAll(selector));
|
|
25
31
|
}
|
|
26
|
-
} else {
|
|
27
|
-
results = Array.from(within.querySelectorAll(selector));
|
|
28
|
-
}
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
return results;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
// DOMException.INVALID_EXPRESSION_ERR is undefined, using pure code
|
|
36
|
+
if (error.code == DOMException.SYNTAX_ERR || error.code == 51) {
|
|
37
|
+
throw new InvalidSelector;
|
|
38
|
+
} else {
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
|
-
}
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
return nodes;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
visibleText(node) {
|
|
52
|
-
if (this.isVisible(node)) {
|
|
53
|
-
if (node.nodeName == "TEXTAREA") {
|
|
54
|
-
return node.textContent;
|
|
55
|
-
}
|
|
56
|
-
if (node instanceof SVGElement) {
|
|
57
|
-
return node.textContent;
|
|
44
|
+
parents(node) {
|
|
45
|
+
let nodes = [];
|
|
46
|
+
let parent = node.parentNode;
|
|
47
|
+
while (parent != document && parent !== null) {
|
|
48
|
+
nodes.push(parent);
|
|
49
|
+
parent = parent.parentNode;
|
|
58
50
|
}
|
|
59
|
-
|
|
60
|
-
return Array.from(node.children)
|
|
61
|
-
.map(child => this.visibleText(child))
|
|
62
|
-
.filter(text => text)
|
|
63
|
-
.join(" ");
|
|
64
|
-
}
|
|
65
|
-
return node.innerText;
|
|
51
|
+
return nodes;
|
|
66
52
|
}
|
|
67
|
-
}
|
|
68
53
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
54
|
+
visibleText(node) {
|
|
55
|
+
if (this.isVisible(node)) {
|
|
56
|
+
if (node.nodeName == "TEXTAREA") {
|
|
57
|
+
return node.textContent;
|
|
58
|
+
}
|
|
59
|
+
if (node instanceof SVGElement) {
|
|
60
|
+
return node.textContent;
|
|
61
|
+
}
|
|
62
|
+
if (node instanceof ShadowRoot) {
|
|
63
|
+
return Array.from(node.children)
|
|
64
|
+
.map(child => this.visibleText(child))
|
|
65
|
+
.filter(text => text)
|
|
66
|
+
.join(" ");
|
|
67
|
+
}
|
|
68
|
+
return node.innerText;
|
|
77
69
|
}
|
|
78
70
|
}
|
|
79
71
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (node
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (style.display === "none" || style.visibility === "hidden" || (ancestorOpacityApplies && parseFloat(style.opacity) === 0)) {
|
|
72
|
+
isVisible(node) {
|
|
73
|
+
let mapName, style;
|
|
74
|
+
// if node is area, check visibility of relevant image
|
|
75
|
+
if (node.tagName === "AREA") {
|
|
76
|
+
mapName = document.evaluate("./ancestor::map/@name", node, null, XPathResult.STRING_TYPE, null).stringValue;
|
|
77
|
+
node = document.querySelector(`img[usemap="#${mapName}"]`);
|
|
78
|
+
if (node == null) {
|
|
88
79
|
return false;
|
|
89
80
|
}
|
|
81
|
+
}
|
|
90
82
|
|
|
91
|
-
|
|
92
|
-
// an ancestor's opacity no longer applies to it once shown.
|
|
93
|
-
if (ancestorOpacityApplies) {
|
|
94
|
-
try {
|
|
95
|
-
if (node.matches(":modal, :popover-open")) {
|
|
96
|
-
ancestorOpacityApplies = false;
|
|
97
|
-
}
|
|
98
|
-
} catch (e) {}
|
|
99
|
-
}
|
|
83
|
+
let ancestorOpacityApplies = true;
|
|
100
84
|
|
|
101
|
-
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
|
|
85
|
+
while (node) {
|
|
86
|
+
if (node instanceof ShadowRoot) {
|
|
87
|
+
node = node.host;
|
|
88
|
+
} else {
|
|
89
|
+
style = window.getComputedStyle(node);
|
|
90
|
+
if (style.display === "none" || style.visibility === "hidden" || (ancestorOpacityApplies && parseFloat(style.opacity) === 0)) {
|
|
105
91
|
return false;
|
|
106
92
|
}
|
|
107
|
-
}
|
|
108
93
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
94
|
+
// A node matching :modal or :popover-open is a top-layer element, so
|
|
95
|
+
// an ancestor's opacity no longer applies to it once shown.
|
|
96
|
+
if (ancestorOpacityApplies) {
|
|
97
|
+
try {
|
|
98
|
+
if (node.matches(":modal, :popover-open")) {
|
|
99
|
+
ancestorOpacityApplies = false;
|
|
100
|
+
}
|
|
101
|
+
} catch (e) {}
|
|
102
|
+
}
|
|
112
103
|
|
|
113
|
-
|
|
114
|
-
|
|
104
|
+
let parent = node.parentElement;
|
|
105
|
+
if (parent && parent.tagName === "DETAILS" && !parent.open) {
|
|
106
|
+
// In a closed <details> only the first <summary> (and its subtree) is rendered.
|
|
107
|
+
if (node !== parent.querySelector(":scope > summary")) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
115
111
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
parent::fieldset[@disabled] | \
|
|
120
|
-
ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]";
|
|
112
|
+
node = parent ?? (node.getRootNode() instanceof ShadowRoot && node.getRootNode());
|
|
113
|
+
}
|
|
114
|
+
}
|
|
121
115
|
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
124
118
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
119
|
+
isDisabled(node) {
|
|
120
|
+
let xpath = "parent::optgroup[@disabled] | \
|
|
121
|
+
ancestor::select[@disabled] | \
|
|
122
|
+
parent::fieldset[@disabled] | \
|
|
123
|
+
ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]";
|
|
129
124
|
|
|
130
|
-
|
|
131
|
-
let parent = node.parentNode;
|
|
132
|
-
while (parent !== document && parent !== null) {
|
|
133
|
-
nodes.unshift(parent);
|
|
134
|
-
parent = parent.parentNode;
|
|
125
|
+
return node.disabled || document.evaluate(xpath, node, null, XPathResult.BOOLEAN_TYPE, null).booleanValue;
|
|
135
126
|
}
|
|
136
127
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
128
|
+
path(node) {
|
|
129
|
+
if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) {
|
|
130
|
+
return "(: Shadow DOM element - no XPath :)";
|
|
131
|
+
};
|
|
140
132
|
|
|
141
|
-
|
|
142
|
-
|
|
133
|
+
let nodes = [node];
|
|
134
|
+
let parent = node.parentNode;
|
|
135
|
+
while (parent !== document && parent !== null) {
|
|
136
|
+
nodes.unshift(parent);
|
|
137
|
+
parent = parent.parentNode;
|
|
143
138
|
}
|
|
144
139
|
|
|
145
|
-
|
|
146
|
-
|
|
140
|
+
let selectors = nodes.map(node => {
|
|
141
|
+
let prevSiblings = [];
|
|
142
|
+
let xpath = document.evaluate(`./preceding-sibling::${node.tagName}`, node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
|
147
143
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Returns true if the node is obscured in the viewport.
|
|
153
|
-
*
|
|
154
|
-
* @param {Element} node
|
|
155
|
-
* @return {boolean} true if the node is obscured, false otherwise
|
|
156
|
-
*/
|
|
157
|
-
isObscured(node) {
|
|
158
|
-
let win = window;
|
|
159
|
-
let rect = node.getBoundingClientRect();
|
|
160
|
-
let px = rect.left + rect.width / 2;
|
|
161
|
-
let py = rect.top + rect.height / 2;
|
|
144
|
+
for (let i = 0; i < xpath.snapshotLength; i++) {
|
|
145
|
+
prevSiblings.push(xpath.snapshotItem(i));
|
|
146
|
+
}
|
|
162
147
|
|
|
163
|
-
|
|
164
|
-
|
|
148
|
+
return `${node.tagName}[${(prevSiblings.length + 1)}]`;
|
|
149
|
+
});
|
|
165
150
|
|
|
166
|
-
|
|
151
|
+
return `//${selectors.join("/")}`;
|
|
152
|
+
}
|
|
167
153
|
|
|
168
|
-
|
|
169
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Returns true if the node is obscured in the viewport.
|
|
156
|
+
*
|
|
157
|
+
* @param {Element} node
|
|
158
|
+
* @return {boolean} true if the node is obscured, false otherwise
|
|
159
|
+
*/
|
|
160
|
+
isObscured(node) {
|
|
161
|
+
let win = window;
|
|
162
|
+
let rect = node.getBoundingClientRect();
|
|
163
|
+
let px = rect.left + rect.width / 2;
|
|
164
|
+
let py = rect.top + rect.height / 2;
|
|
165
|
+
|
|
166
|
+
while (win) {
|
|
167
|
+
let topNode = win.document.elementFromPoint(px, py);
|
|
168
|
+
|
|
169
|
+
if (node !== topNode && !node.contains(topNode)) return true;
|
|
170
|
+
|
|
171
|
+
node = win.frameElement;
|
|
172
|
+
if (!node) return false;
|
|
173
|
+
|
|
174
|
+
rect = node.getBoundingClientRect();
|
|
175
|
+
px = rect.left + px;
|
|
176
|
+
py = rect.top + py;
|
|
177
|
+
win = win.parent;
|
|
178
|
+
}
|
|
170
179
|
|
|
171
|
-
|
|
172
|
-
px = rect.left + px;
|
|
173
|
-
py = rect.top + py;
|
|
174
|
-
win = win.parent;
|
|
180
|
+
return false;
|
|
175
181
|
}
|
|
176
182
|
|
|
177
|
-
|
|
178
|
-
|
|
183
|
+
set(node, value) {
|
|
184
|
+
if (node.readOnly) return;
|
|
179
185
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (node.maxLength >= 0) {
|
|
184
|
-
value = value.substr(0, node.maxLength);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
let valueBefore = node.value;
|
|
188
|
-
|
|
189
|
-
node.focus();
|
|
190
|
-
this.trigger(node, "focus");
|
|
191
|
-
this.setValue(node, "");
|
|
192
|
-
|
|
193
|
-
if (node.type == "number" || node.type == "date" || node.type == "range" || node.type == "time" || node.type == "month" || node.type == "week") {
|
|
194
|
-
this.setValue(node, value);
|
|
195
|
-
this.input(node);
|
|
196
|
-
} else if (node.type == "datetime-local") {
|
|
197
|
-
value = new Date(value);
|
|
198
|
-
let year = value.getFullYear();
|
|
199
|
-
let month = ("0" + (value.getMonth() + 1)).slice(-2);
|
|
200
|
-
let date = ("0" + value.getDate()).slice(-2);
|
|
201
|
-
let hour = ("0" + value.getHours()).slice(-2);
|
|
202
|
-
let min = ("0" + value.getMinutes()).slice(-2);
|
|
203
|
-
let sec = ("0" + value.getSeconds()).slice(-2);
|
|
204
|
-
this.setValue(node, `${year}-${month}-${date}T${hour}:${min}:${sec}`);
|
|
205
|
-
this.input(node);
|
|
206
|
-
} else {
|
|
207
|
-
for (let i = 0; i < value.length; i++) {
|
|
208
|
-
let char = value[i];
|
|
209
|
-
let keyCode = this.characterToKeyCode(char);
|
|
210
|
-
// call the following functions in order, if one returns false (preventDefault),
|
|
211
|
-
// stop the call chain
|
|
212
|
-
[
|
|
213
|
-
() => this.keyupdowned(node, "keydown", char, keyCode),
|
|
214
|
-
() => this.keypressed(node, false, false, false, false, char.charCodeAt(0), char.charCodeAt(0)),
|
|
215
|
-
() => {
|
|
216
|
-
this.setValue(node, node.value + char)
|
|
217
|
-
this.input(node)
|
|
218
|
-
}
|
|
219
|
-
].some(fn => fn())
|
|
186
|
+
if (node.maxLength >= 0) {
|
|
187
|
+
value = value.substr(0, node.maxLength);
|
|
188
|
+
}
|
|
220
189
|
|
|
221
|
-
|
|
190
|
+
let valueBefore = node.value;
|
|
191
|
+
|
|
192
|
+
node.focus();
|
|
193
|
+
this.trigger(node, "focus");
|
|
194
|
+
this.setValue(node, "");
|
|
195
|
+
|
|
196
|
+
if (node.type == "number" || node.type == "date" || node.type == "range" || node.type == "time" || node.type == "month" || node.type == "week") {
|
|
197
|
+
this.setValue(node, value);
|
|
198
|
+
this.input(node);
|
|
199
|
+
} else if (node.type == "datetime-local") {
|
|
200
|
+
value = new Date(value);
|
|
201
|
+
let year = value.getFullYear();
|
|
202
|
+
let month = ("0" + (value.getMonth() + 1)).slice(-2);
|
|
203
|
+
let date = ("0" + value.getDate()).slice(-2);
|
|
204
|
+
let hour = ("0" + value.getHours()).slice(-2);
|
|
205
|
+
let min = ("0" + value.getMinutes()).slice(-2);
|
|
206
|
+
let sec = ("0" + value.getSeconds()).slice(-2);
|
|
207
|
+
this.setValue(node, `${year}-${month}-${date}T${hour}:${min}:${sec}`);
|
|
208
|
+
this.input(node);
|
|
209
|
+
} else {
|
|
210
|
+
for (let i = 0; i < value.length; i++) {
|
|
211
|
+
let char = value[i];
|
|
212
|
+
let keyCode = this.characterToKeyCode(char);
|
|
213
|
+
// call the following functions in order, if one returns false (preventDefault),
|
|
214
|
+
// stop the call chain
|
|
215
|
+
[
|
|
216
|
+
() => this.keyupdowned(node, "keydown", char, keyCode),
|
|
217
|
+
() => this.keypressed(node, false, false, false, false, char.charCodeAt(0), char.charCodeAt(0)),
|
|
218
|
+
() => {
|
|
219
|
+
this.setValue(node, node.value + char)
|
|
220
|
+
this.input(node)
|
|
221
|
+
}
|
|
222
|
+
].some(fn => fn())
|
|
223
|
+
|
|
224
|
+
this.keyupdowned(node, "keyup", char, keyCode);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (valueBefore !== "" && node.value === "") {
|
|
228
|
+
this.input(node, "deleteContentBackward");
|
|
229
|
+
}
|
|
222
230
|
}
|
|
223
231
|
|
|
224
|
-
if (valueBefore !==
|
|
225
|
-
this.
|
|
232
|
+
if (valueBefore !== node.value) {
|
|
233
|
+
this.changed(node);
|
|
226
234
|
}
|
|
235
|
+
this.trigger(node, "blur");
|
|
227
236
|
}
|
|
228
237
|
|
|
229
|
-
|
|
230
|
-
|
|
238
|
+
setValue(node, value) {
|
|
239
|
+
let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
|
|
240
|
+
let nativeTextareaValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
|
|
241
|
+
|
|
242
|
+
if (node.tagName.toLowerCase() === 'input') {
|
|
243
|
+
return nativeInputValueSetter.call(node, value);
|
|
244
|
+
}
|
|
245
|
+
return nativeTextareaValueSetter.call(node, value);
|
|
231
246
|
}
|
|
232
|
-
this.trigger(node, "blur");
|
|
233
|
-
}
|
|
234
247
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
248
|
+
input(node, inputType = "insertText") {
|
|
249
|
+
let event = new InputEvent("input", { inputType: inputType, bubbles: true, cancelable: false });
|
|
250
|
+
node.dispatchEvent(event);
|
|
251
|
+
}
|
|
238
252
|
|
|
239
|
-
|
|
240
|
-
|
|
253
|
+
/**
|
|
254
|
+
* @return {boolean} false when an event handler called preventDefault()
|
|
255
|
+
*/
|
|
256
|
+
keyupdowned(node, eventName, char, keyCode) {
|
|
257
|
+
let event = new KeyboardEvent(
|
|
258
|
+
eventName, {
|
|
259
|
+
bubbles: true,
|
|
260
|
+
cancelable: true,
|
|
261
|
+
key: char,
|
|
262
|
+
keyCode: keyCode
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
return !node.dispatchEvent(event);
|
|
241
266
|
}
|
|
242
|
-
return nativeTextareaValueSetter.call(node, value);
|
|
243
|
-
}
|
|
244
267
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
268
|
+
/**
|
|
269
|
+
* @return {boolean} false when an event handler called preventDefault()
|
|
270
|
+
*/
|
|
271
|
+
keypressed(node, altKey, ctrlKey, shiftKey, metaKey, keyCode, charCode) {
|
|
272
|
+
let event = new KeyboardEvent(
|
|
273
|
+
"keypress", {
|
|
274
|
+
bubbles: true,
|
|
275
|
+
cancelable: true,
|
|
276
|
+
altKey: altKey,
|
|
277
|
+
ctrlKey: ctrlKey,
|
|
278
|
+
shiftKey: shiftKey,
|
|
279
|
+
metaKey: metaKey,
|
|
280
|
+
keyCode: keyCode,
|
|
281
|
+
charCode: charCode
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
return !node.dispatchEvent(event);
|
|
285
|
+
}
|
|
249
286
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
287
|
+
characterToKeyCode(char) {
|
|
288
|
+
const specialKeys = {
|
|
289
|
+
96: 192, // `
|
|
290
|
+
45: 189, // -
|
|
291
|
+
61: 187, // =
|
|
292
|
+
91: 219, // [
|
|
293
|
+
93: 221, // ]
|
|
294
|
+
92: 220, // \
|
|
295
|
+
59: 186, // ;
|
|
296
|
+
39: 222, // '
|
|
297
|
+
44: 188, // ,
|
|
298
|
+
46: 190, // .
|
|
299
|
+
47: 191, // /
|
|
300
|
+
127: 46, // delete
|
|
301
|
+
126: 192, // ~
|
|
302
|
+
33: 49, // !
|
|
303
|
+
64: 50, // @
|
|
304
|
+
35: 51, // #
|
|
305
|
+
36: 52, // $
|
|
306
|
+
37: 53, // %
|
|
307
|
+
94: 54, // ^
|
|
308
|
+
38: 55, // &
|
|
309
|
+
42: 56, // *
|
|
310
|
+
40: 57, // (
|
|
311
|
+
41: 48, // )
|
|
312
|
+
95: 189, // _
|
|
313
|
+
43: 187, // +
|
|
314
|
+
123: 219, // {
|
|
315
|
+
125: 221, // }
|
|
316
|
+
124: 220, // |
|
|
317
|
+
58: 186, // :
|
|
318
|
+
34: 222, // "
|
|
319
|
+
60: 188, // <
|
|
320
|
+
62: 190, // >
|
|
321
|
+
63: 191, // ?
|
|
260
322
|
}
|
|
261
|
-
);
|
|
262
|
-
return !node.dispatchEvent(event);
|
|
263
|
-
}
|
|
264
323
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
keypressed(node, altKey, ctrlKey, shiftKey, metaKey, keyCode, charCode) {
|
|
269
|
-
let event = new KeyboardEvent(
|
|
270
|
-
"keypress", {
|
|
271
|
-
bubbles: true,
|
|
272
|
-
cancelable: true,
|
|
273
|
-
altKey: altKey,
|
|
274
|
-
ctrlKey: ctrlKey,
|
|
275
|
-
shiftKey: shiftKey,
|
|
276
|
-
metaKey: metaKey,
|
|
277
|
-
keyCode: keyCode,
|
|
278
|
-
charCode: charCode
|
|
279
|
-
}
|
|
280
|
-
);
|
|
281
|
-
return !node.dispatchEvent(event);
|
|
282
|
-
}
|
|
324
|
+
let code = char.toUpperCase().charCodeAt(0);
|
|
325
|
+
return specialKeys[code] || code;
|
|
326
|
+
}
|
|
283
327
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
96: 192, // `
|
|
287
|
-
45: 189, // -
|
|
288
|
-
61: 187, // =
|
|
289
|
-
91: 219, // [
|
|
290
|
-
93: 221, // ]
|
|
291
|
-
92: 220, // \
|
|
292
|
-
59: 186, // ;
|
|
293
|
-
39: 222, // '
|
|
294
|
-
44: 188, // ,
|
|
295
|
-
46: 190, // .
|
|
296
|
-
47: 191, // /
|
|
297
|
-
127: 46, // delete
|
|
298
|
-
126: 192, // ~
|
|
299
|
-
33: 49, // !
|
|
300
|
-
64: 50, // @
|
|
301
|
-
35: 51, // #
|
|
302
|
-
36: 52, // $
|
|
303
|
-
37: 53, // %
|
|
304
|
-
94: 54, // ^
|
|
305
|
-
38: 55, // &
|
|
306
|
-
42: 56, // *
|
|
307
|
-
40: 57, // (
|
|
308
|
-
41: 48, // )
|
|
309
|
-
95: 189, // _
|
|
310
|
-
43: 187, // +
|
|
311
|
-
123: 219, // {
|
|
312
|
-
125: 221, // }
|
|
313
|
-
124: 220, // |
|
|
314
|
-
58: 186, // :
|
|
315
|
-
34: 222, // "
|
|
316
|
-
60: 188, // <
|
|
317
|
-
62: 190, // >
|
|
318
|
-
63: 191, // ?
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
let code = char.toUpperCase().charCodeAt(0);
|
|
322
|
-
return specialKeys[code] || code;
|
|
323
|
-
}
|
|
328
|
+
scrollIntoViewport(node) {
|
|
329
|
+
let areaImage = this._getAreaImage(node);
|
|
324
330
|
|
|
325
|
-
|
|
326
|
-
|
|
331
|
+
if (areaImage) {
|
|
332
|
+
return this.scrollIntoViewport(areaImage);
|
|
333
|
+
} else {
|
|
334
|
+
node.scrollIntoViewIfNeeded();
|
|
327
335
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
336
|
+
if (!this._isInViewport(node)) {
|
|
337
|
+
node.scrollIntoView({block: "center", inline: "center", behavior: "instant"});
|
|
338
|
+
return this._isInViewport(node);
|
|
339
|
+
}
|
|
332
340
|
|
|
333
|
-
|
|
334
|
-
node.scrollIntoView({block: "center", inline: "center", behavior: "instant"});
|
|
335
|
-
return this._isInViewport(node);
|
|
341
|
+
return true;
|
|
336
342
|
}
|
|
337
|
-
|
|
338
|
-
return true;
|
|
339
343
|
}
|
|
340
|
-
}
|
|
341
344
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
345
|
+
mouseEventTest(node, name, x, y) {
|
|
346
|
+
let frameOffset = this._frameOffset();
|
|
347
|
+
x -= frameOffset.left;
|
|
348
|
+
y -= frameOffset.top;
|
|
346
349
|
|
|
347
|
-
|
|
350
|
+
let element = node.getRootNode().elementFromPoint(x, y);
|
|
348
351
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
352
|
+
let el = element;
|
|
353
|
+
while (el) {
|
|
354
|
+
if (el == node) {
|
|
355
|
+
return true;
|
|
356
|
+
} else {
|
|
357
|
+
el = el.parentNode;
|
|
358
|
+
}
|
|
355
359
|
}
|
|
360
|
+
|
|
361
|
+
let selector = element && this._getSelector(element) || "none";
|
|
362
|
+
throw new MouseEventFailed([name, selector, x, y].join(", "));
|
|
356
363
|
}
|
|
357
364
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
365
|
+
_getAreaImage(node) {
|
|
366
|
+
if ("area" == node.tagName.toLowerCase()) {
|
|
367
|
+
let map = node.parentNode;
|
|
368
|
+
if (map.tagName.toLowerCase() != "map") {
|
|
369
|
+
throw new Error("the area is not within a map");
|
|
370
|
+
}
|
|
361
371
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
throw new Error("the area is not within a map");
|
|
367
|
-
}
|
|
372
|
+
let mapName = map.getAttribute("name");
|
|
373
|
+
if (typeof mapName === "undefined" || mapName === null) {
|
|
374
|
+
throw new Error("area's parent map must have a name");
|
|
375
|
+
}
|
|
368
376
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
377
|
+
mapName = `#${mapName.toLowerCase()}`;
|
|
378
|
+
let imageNode = this.find("css", `img[usemap='${mapName}']`)[0];
|
|
379
|
+
if (typeof imageNode === "undefined" || imageNode === null) {
|
|
380
|
+
throw new Error("no image matches the map");
|
|
381
|
+
}
|
|
373
382
|
|
|
374
|
-
|
|
375
|
-
let imageNode = this.find("css", `img[usemap='${mapName}']`)[0];
|
|
376
|
-
if (typeof imageNode === "undefined" || imageNode === null) {
|
|
377
|
-
throw new Error("no image matches the map");
|
|
383
|
+
return imageNode;
|
|
378
384
|
}
|
|
379
|
-
|
|
380
|
-
return imageNode;
|
|
381
385
|
}
|
|
382
|
-
}
|
|
383
386
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
+
_frameOffset() {
|
|
388
|
+
let win = window;
|
|
389
|
+
let offset = { top: 0, left: 0 };
|
|
387
390
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
391
|
+
while (win.frameElement) {
|
|
392
|
+
let rect = win.frameElement.getClientRects()[0];
|
|
393
|
+
let style = win.getComputedStyle(win.frameElement);
|
|
394
|
+
win = win.parent;
|
|
392
395
|
|
|
393
|
-
|
|
394
|
-
|
|
396
|
+
offset.top += rect.top + parseInt(style.getPropertyValue("padding-top"), 10)
|
|
397
|
+
offset.left += rect.left + parseInt(style.getPropertyValue("padding-left"), 10)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return offset;
|
|
395
401
|
}
|
|
396
402
|
|
|
397
|
-
|
|
398
|
-
|
|
403
|
+
_getSelector(el) {
|
|
404
|
+
let selector = (el.tagName != 'HTML') ? this._getSelector(el.parentNode) + " " : "";
|
|
405
|
+
selector += el.tagName.toLowerCase();
|
|
406
|
+
if (el.id) { selector += `#${el.id}` };
|
|
407
|
+
el.classList.forEach(c => selector += `.${c}`);
|
|
408
|
+
return selector;
|
|
409
|
+
}
|
|
399
410
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
selector += el.tagName.toLowerCase();
|
|
403
|
-
if (el.id) { selector += `#${el.id}` };
|
|
404
|
-
el.classList.forEach(c => selector += `.${c}`);
|
|
405
|
-
return selector;
|
|
406
|
-
}
|
|
411
|
+
_isInViewport(node) {
|
|
412
|
+
let rect = node.getBoundingClientRect();
|
|
407
413
|
|
|
408
|
-
|
|
409
|
-
|
|
414
|
+
let inViewport = rect.top >= 0 &&
|
|
415
|
+
rect.left >= 0 &&
|
|
416
|
+
rect.bottom <= window.innerHeight &&
|
|
417
|
+
rect.right <= window.innerWidth;
|
|
410
418
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
419
|
+
if (inViewport) {
|
|
420
|
+
// check if obscured by another element
|
|
421
|
+
let x = rect.width/2;
|
|
422
|
+
let y = rect.height/2 ;
|
|
415
423
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
424
|
+
let px = rect.left + x,
|
|
425
|
+
py = rect.top + y,
|
|
426
|
+
e = document.elementFromPoint(px, py);
|
|
427
|
+
return node == e;
|
|
428
|
+
}
|
|
420
429
|
|
|
421
|
-
|
|
422
|
-
py = rect.top + y,
|
|
423
|
-
e = document.elementFromPoint(px, py);
|
|
424
|
-
return node == e;
|
|
430
|
+
return false;
|
|
425
431
|
}
|
|
426
432
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
} else {
|
|
436
|
-
let selectNode = node.closest("select");
|
|
437
|
-
this.trigger(selectNode, "focus");
|
|
433
|
+
select(node, value) {
|
|
434
|
+
if (this.isDisabled(node)) {
|
|
435
|
+
return false;
|
|
436
|
+
} else if (value == false && !node.parentNode.multiple) {
|
|
437
|
+
return false;
|
|
438
|
+
} else {
|
|
439
|
+
let selectNode = node.closest("select");
|
|
440
|
+
this.trigger(selectNode, "focus");
|
|
438
441
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
+
node.selected = value;
|
|
443
|
+
selectNode.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
444
|
+
this.changed(node);
|
|
442
445
|
|
|
443
|
-
|
|
444
|
-
|
|
446
|
+
this.trigger(selectNode, "blur");
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
445
449
|
}
|
|
446
|
-
}
|
|
447
450
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
451
|
+
changed(node) {
|
|
452
|
+
let element;
|
|
453
|
+
let event = document.createEvent("HTMLEvents");
|
|
454
|
+
event.initEvent("change", true, false);
|
|
455
|
+
|
|
456
|
+
// In the case of an OPTION tag, the change event should come
|
|
457
|
+
// from the parent SELECT
|
|
458
|
+
if (node.nodeName == "OPTION") {
|
|
459
|
+
element = node.parentNode
|
|
460
|
+
if (element.nodeName == "OPTGROUP") {
|
|
461
|
+
element = element.parentNode
|
|
462
|
+
}
|
|
463
|
+
element
|
|
464
|
+
} else {
|
|
465
|
+
element = node
|
|
459
466
|
}
|
|
460
|
-
element
|
|
461
|
-
} else {
|
|
462
|
-
element = node
|
|
463
|
-
}
|
|
464
467
|
|
|
465
|
-
|
|
466
|
-
|
|
468
|
+
element.dispatchEvent(event)
|
|
469
|
+
}
|
|
467
470
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
node.dispatchEvent(event);
|
|
494
|
-
}
|
|
471
|
+
trigger(node, name, options = {}) {
|
|
472
|
+
let event;
|
|
473
|
+
|
|
474
|
+
if (EVENTS.MOUSE.indexOf(name) != -1) {
|
|
475
|
+
event = document.createEvent("MouseEvent");
|
|
476
|
+
event.initMouseEvent(
|
|
477
|
+
name, true, true, window, 0,
|
|
478
|
+
options["screenX"] || 0, options["screenY"] || 0,
|
|
479
|
+
options["clientX"] || 0, options["clientY"] || 0,
|
|
480
|
+
options["ctrlKey"] || false,
|
|
481
|
+
options["altKey"] || false,
|
|
482
|
+
options["shiftKey"] || false,
|
|
483
|
+
options["metaKey"] || false,
|
|
484
|
+
options["button"] || 0, null
|
|
485
|
+
)
|
|
486
|
+
} else if (EVENTS.FOCUS.indexOf(name) != -1) {
|
|
487
|
+
event = new FocusEvent(name, { bubbles: true, cancelable: true });
|
|
488
|
+
} else if (EVENTS.FORM.indexOf(name) != -1) {
|
|
489
|
+
event = this.obtainEvent(name);
|
|
490
|
+
} else if (name == "input") {
|
|
491
|
+
event = new InputEvent(name, { inputType: "insertText", bubbles: true, composed: true });
|
|
492
|
+
} else {
|
|
493
|
+
event = new Event(name, { bubbles: true, cancelable: true });
|
|
494
|
+
}
|
|
495
495
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
event.initEvent(name, true, true);
|
|
499
|
-
return event;
|
|
500
|
-
}
|
|
496
|
+
node.dispatchEvent(event);
|
|
497
|
+
}
|
|
501
498
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
attrs[attr.name] = attr.value.replace("\n", "\\n");
|
|
499
|
+
obtainEvent(name) {
|
|
500
|
+
let event = document.createEvent("HTMLEvents");
|
|
501
|
+
event.initEvent(name, true, true);
|
|
502
|
+
return event;
|
|
507
503
|
}
|
|
508
504
|
|
|
509
|
-
|
|
510
|
-
|
|
505
|
+
getAttributes(node) {
|
|
506
|
+
let attrs = {};
|
|
507
|
+
for (let i = 0, len = node.attributes.length; i < len; i++) {
|
|
508
|
+
let attr = node.attributes[i];
|
|
509
|
+
attrs[attr.name] = attr.value.replace("\n", "\\n");
|
|
510
|
+
}
|
|
511
511
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
512
|
+
return this._json.stringify(attrs);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
getAttribute(node, name) {
|
|
516
|
+
if (name == "checked" || name == "selected") {
|
|
517
|
+
return node[name];
|
|
518
|
+
} else {
|
|
519
|
+
return node.getAttribute(name);
|
|
520
|
+
}
|
|
517
521
|
}
|
|
518
|
-
}
|
|
519
522
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
+
value(node) {
|
|
524
|
+
if (node.tagName == "SELECT" && node.multiple) {
|
|
525
|
+
let result = []
|
|
523
526
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
527
|
+
for (let i = 0, len = node.children.length; i < len; i++) {
|
|
528
|
+
let option = node.children[i];
|
|
529
|
+
if (option.selected) {
|
|
530
|
+
result.push(option.value);
|
|
531
|
+
}
|
|
528
532
|
}
|
|
533
|
+
|
|
534
|
+
return result;
|
|
535
|
+
} else {
|
|
536
|
+
return node.value;
|
|
529
537
|
}
|
|
538
|
+
}
|
|
530
539
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
540
|
+
deleteText(node) {
|
|
541
|
+
let range = document.createRange();
|
|
542
|
+
range.selectNodeContents(node);
|
|
543
|
+
window.getSelection().removeAllRanges();
|
|
544
|
+
window.getSelection().addRange(range);
|
|
545
|
+
window.getSelection().deleteFromDocument();
|
|
534
546
|
}
|
|
535
|
-
}
|
|
536
547
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
window.getSelection().removeAllRanges();
|
|
541
|
-
window.getSelection().addRange(range);
|
|
542
|
-
window.getSelection().deleteFromDocument();
|
|
543
|
-
}
|
|
548
|
+
hasFocus(node) {
|
|
549
|
+
return document.activeElement === node;
|
|
550
|
+
}
|
|
544
551
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
}
|
|
552
|
+
containsSelection(node) {
|
|
553
|
+
let selectedNode = document.getSelection().focusNode;
|
|
548
554
|
|
|
549
|
-
|
|
550
|
-
|
|
555
|
+
if (!selectedNode) {
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
551
558
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
559
|
+
if (selectedNode.nodeType == 3) {
|
|
560
|
+
selectedNode = selectedNode.parentNode;
|
|
561
|
+
}
|
|
555
562
|
|
|
556
|
-
|
|
557
|
-
selectedNode = selectedNode.parentNode;
|
|
563
|
+
return node.contains(selectedNode);
|
|
558
564
|
}
|
|
559
565
|
|
|
560
|
-
|
|
561
|
-
|
|
566
|
+
dragMousedownTracker() {
|
|
567
|
+
window._cupriteMousedownPrevented = null;
|
|
568
|
+
document.addEventListener('mousedown', ev => {
|
|
569
|
+
window._cupriteMousedownPrevented = ev.defaultPrevented;
|
|
570
|
+
}, { once: true, passive: true });
|
|
571
|
+
}
|
|
562
572
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
}, { once: true, passive: true });
|
|
568
|
-
}
|
|
573
|
+
legacyDragCheck(node) {
|
|
574
|
+
if ([true, null].indexOf(window._cupriteMousedownPrevented) >= 0) {
|
|
575
|
+
return true;
|
|
576
|
+
}
|
|
569
577
|
|
|
570
|
-
|
|
571
|
-
|
|
578
|
+
let el = node;
|
|
579
|
+
do {
|
|
580
|
+
if (el.draggable) return false;
|
|
581
|
+
} while (el = el.parentElement);
|
|
572
582
|
return true;
|
|
573
583
|
}
|
|
574
584
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
let opts = { cancelable: true, bubbles: true, dataTransfer: dt };
|
|
586
|
-
for (let i = 0; i < strings.length; i++) {
|
|
587
|
-
if (dt.items) {
|
|
588
|
-
dt.items.add(strings[i]["data"], strings[i]["type"]);
|
|
589
|
-
} else {
|
|
590
|
-
dt.setData(strings[i]["type"], strings[i]["data"]);
|
|
585
|
+
// Ported from Capybara's Selenium html5_drag.rb (DROP_STRING).
|
|
586
|
+
dropString(strings, target) {
|
|
587
|
+
let dt = new DataTransfer();
|
|
588
|
+
let opts = { cancelable: true, bubbles: true, dataTransfer: dt };
|
|
589
|
+
for (let i = 0; i < strings.length; i++) {
|
|
590
|
+
if (dt.items) {
|
|
591
|
+
dt.items.add(strings[i]["data"], strings[i]["type"]);
|
|
592
|
+
} else {
|
|
593
|
+
dt.setData(strings[i]["type"], strings[i]["data"]);
|
|
594
|
+
}
|
|
591
595
|
}
|
|
596
|
+
target.dispatchEvent(new DragEvent("drop", opts));
|
|
592
597
|
}
|
|
593
|
-
target.dispatchEvent(new DragEvent("drop", opts));
|
|
594
|
-
}
|
|
595
598
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
599
|
+
// Ported from Capybara's Selenium html5_drag.rb (ATTACH_FILE).
|
|
600
|
+
attachDropInput() {
|
|
601
|
+
document.getElementById("_cuprite_drop_file")?.remove();
|
|
602
|
+
let input = document.createElement("INPUT");
|
|
603
|
+
input.type = "file";
|
|
604
|
+
input.id = "_cuprite_drop_file";
|
|
605
|
+
input.multiple = true;
|
|
606
|
+
document.body.appendChild(input);
|
|
607
|
+
}
|
|
605
608
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
609
|
+
// Ported from Capybara's Selenium html5_drag.rb (DROP_FILE).
|
|
610
|
+
dropFile(target) {
|
|
611
|
+
let input = document.getElementById("_cuprite_drop_file");
|
|
612
|
+
let files = input.files;
|
|
613
|
+
let dt = new DataTransfer();
|
|
614
|
+
let opts = { cancelable: true, bubbles: true, dataTransfer: dt };
|
|
615
|
+
input.parentElement.removeChild(input);
|
|
616
|
+
if (dt.items) {
|
|
617
|
+
for (let i = 0; i < files.length; i++) {
|
|
618
|
+
dt.items.add(files[i]);
|
|
619
|
+
}
|
|
620
|
+
} else {
|
|
621
|
+
Object.defineProperty(dt, "files", { value: files, writable: false });
|
|
616
622
|
}
|
|
617
|
-
|
|
618
|
-
Object.defineProperty(dt, "files", { value: files, writable: false });
|
|
623
|
+
target.dispatchEvent(new DragEvent("drop", opts));
|
|
619
624
|
}
|
|
620
|
-
target.dispatchEvent(new DragEvent("drop", opts));
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
// This command is purely for testing error handling
|
|
624
|
-
browserError() {
|
|
625
|
-
throw new Error("zomg");
|
|
626
625
|
}
|
|
627
|
-
}
|
|
628
626
|
|
|
629
|
-
window._cuprite = new Cuprite;
|
|
627
|
+
window._cuprite = new Cuprite;
|
|
628
|
+
})();
|