cuprite 0.2.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.
- checksums.yaml +7 -0
- data/lib/capybara/cuprite.rb +28 -0
- data/lib/capybara/cuprite/browser.rb +286 -0
- data/lib/capybara/cuprite/browser/client.rb +70 -0
- data/lib/capybara/cuprite/browser/dom.rb +50 -0
- data/lib/capybara/cuprite/browser/frame.rb +109 -0
- data/lib/capybara/cuprite/browser/input.rb +123 -0
- data/lib/capybara/cuprite/browser/javascripts/index.js +407 -0
- data/lib/capybara/cuprite/browser/page.rb +278 -0
- data/lib/capybara/cuprite/browser/process.rb +167 -0
- data/lib/capybara/cuprite/browser/runtime.rb +194 -0
- data/lib/capybara/cuprite/browser/targets.rb +109 -0
- data/lib/capybara/cuprite/browser/web_socket.rb +60 -0
- data/lib/capybara/cuprite/cookie.rb +47 -0
- data/lib/capybara/cuprite/driver.rb +396 -0
- data/lib/capybara/cuprite/errors.rb +131 -0
- data/lib/capybara/cuprite/network/error.rb +25 -0
- data/lib/capybara/cuprite/network/request.rb +33 -0
- data/lib/capybara/cuprite/network/response.rb +42 -0
- data/lib/capybara/cuprite/node.rb +216 -0
- data/lib/capybara/cuprite/version.rb +7 -0
- metadata +231 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
module Capybara::Cuprite
|
2
|
+
class Browser
|
3
|
+
module Input
|
4
|
+
def click(node, keys = [], offset = {})
|
5
|
+
x, y, modifiers = prepare_before_click(__method__, node, keys, offset)
|
6
|
+
command("Input.dispatchMouseEvent", type: "mousePressed", modifiers: modifiers, button: "left", x: x, y: y, clickCount: 1)
|
7
|
+
@wait = 0.05 # Potential wait because if network event is triggered then we have to wait until it's over.
|
8
|
+
command("Input.dispatchMouseEvent", type: "mouseReleased", modifiers: modifiers, button: "left", x: x, y: y, clickCount: 1)
|
9
|
+
end
|
10
|
+
|
11
|
+
def right_click(node, keys = [], offset = {})
|
12
|
+
x, y, modifiers = prepare_before_click(__method__, node, keys, offset)
|
13
|
+
command("Input.dispatchMouseEvent", type: "mousePressed", modifiers: modifiers, button: "right", x: x, y: y, clickCount: 1)
|
14
|
+
command("Input.dispatchMouseEvent", type: "mouseReleased", modifiers: modifiers, button: "right", x: x, y: y, clickCount: 1)
|
15
|
+
end
|
16
|
+
|
17
|
+
def double_click(node, keys = [], offset = {})
|
18
|
+
x, y, modifiers = prepare_before_click(__method__, node, keys, offset)
|
19
|
+
command("Input.dispatchMouseEvent", type: "mousePressed", modifiers: modifiers, button: "left", x: x, y: y, clickCount: 2)
|
20
|
+
command("Input.dispatchMouseEvent", type: "mouseReleased", modifiers: modifiers, button: "left", x: x, y: y, clickCount: 2)
|
21
|
+
end
|
22
|
+
|
23
|
+
def click_coordinates(x, y)
|
24
|
+
command("Input.dispatchMouseEvent", type: "mousePressed", button: "left", x: x, y: y, clickCount: 1)
|
25
|
+
@wait = 0.05 # Potential wait because if network event is triggered then we have to wait until it's over.
|
26
|
+
command("Input.dispatchMouseEvent", type: "mouseReleased", button: "left", x: x, y: y, clickCount: 1)
|
27
|
+
end
|
28
|
+
|
29
|
+
def hover(node)
|
30
|
+
evaluate_on(node: node, expr: "_cuprite.scrollIntoViewport(this)")
|
31
|
+
x, y = calculate_quads(node)
|
32
|
+
command("Input.dispatchMouseEvent", type: "mouseMoved", x: x, y: y)
|
33
|
+
end
|
34
|
+
|
35
|
+
def set(node, value)
|
36
|
+
object_id = command("DOM.resolveNode", nodeId: node["nodeId"]).dig("object", "objectId")
|
37
|
+
evaluate("_cuprite.set(arguments[0], arguments[1])", { "objectId" => object_id }, value)
|
38
|
+
end
|
39
|
+
|
40
|
+
def drag(node, other)
|
41
|
+
raise NotImplementedError
|
42
|
+
end
|
43
|
+
|
44
|
+
def drag_by(node, x, y)
|
45
|
+
raise NotImplementedError
|
46
|
+
end
|
47
|
+
|
48
|
+
def select(node, value)
|
49
|
+
evaluate_on(node: node, expr: "_cuprite.select(this, #{value})")
|
50
|
+
end
|
51
|
+
|
52
|
+
def trigger(node, event)
|
53
|
+
options = event.to_s == "click" ? { wait: 0.1 } : {}
|
54
|
+
evaluate_on(node: node, expr: %(_cuprite.trigger(this, "#{event}")), **options)
|
55
|
+
end
|
56
|
+
|
57
|
+
def scroll_to(left, top)
|
58
|
+
raise NotImplementedError
|
59
|
+
end
|
60
|
+
|
61
|
+
def send_keys(node, keys)
|
62
|
+
# value.each_char do |char|
|
63
|
+
# # Check puppeteer Input.js and USKeyboardLayout.js
|
64
|
+
# # also send_keys and modifiers from capybara API and unify all that.
|
65
|
+
# if /\n/.match?(char)
|
66
|
+
# command("Input.insertText", text: char)
|
67
|
+
# # command("Input.dispatchKeyEvent", type: "keyDown", code: "Enter", key: "Enter", text: "\r")
|
68
|
+
# # command("Input.dispatchKeyEvent", type: "keyUp", code: "Enter", key: "Enter")
|
69
|
+
# else
|
70
|
+
# command("Input.dispatchKeyEvent", type: "keyDown", text: char)
|
71
|
+
# command("Input.dispatchKeyEvent", type: "keyUp", text: char)
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
# command "send_keys", node, normalize_keys(keys)
|
75
|
+
raise NotImplementedError
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def prepare_before_click(name, node, keys, offset)
|
81
|
+
evaluate_on(node: node, expr: "_cuprite.scrollIntoViewport(this)")
|
82
|
+
x, y = calculate_quads(node, offset[:x], offset[:y])
|
83
|
+
evaluate_on(node: node, expr: "_cuprite.mouseEventTest(this, '#{name}', #{x}, #{y})")
|
84
|
+
|
85
|
+
click_modifiers = { alt: 1, ctrl: 2, control: 2, meta: 4, command: 4, shift: 8 }
|
86
|
+
modifiers = keys.map { |k| click_modifiers[k.to_sym] }.compact.reduce(0, :|)
|
87
|
+
|
88
|
+
command("Input.dispatchMouseEvent", type: "mouseMoved", x: x, y: y)
|
89
|
+
|
90
|
+
[x, y, modifiers]
|
91
|
+
end
|
92
|
+
|
93
|
+
def calculate_quads(node, offset_x = nil, offset_y = nil)
|
94
|
+
quads = get_content_quads(node)
|
95
|
+
offset_x, offset_y = offset_x.to_i, offset_y.to_i
|
96
|
+
|
97
|
+
if offset_x > 0 || offset_y > 0
|
98
|
+
point = quads.first
|
99
|
+
[point[:x] + offset_x, point[:y] + offset_y]
|
100
|
+
else
|
101
|
+
x, y = quads.inject([0, 0]) do |memo, point|
|
102
|
+
[memo[0] + point[:x],
|
103
|
+
memo[1] + point[:y]]
|
104
|
+
end
|
105
|
+
[x / 4, y / 4]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def get_content_quads(node)
|
110
|
+
result = command("DOM.getContentQuads", nodeId: node["nodeId"])
|
111
|
+
raise "Node is either not visible or not an HTMLElement" if result["quads"].size == 0
|
112
|
+
|
113
|
+
# FIXME: Case when a few quads returned
|
114
|
+
result["quads"].map do |quad|
|
115
|
+
[{x: quad[0], y: quad[1]},
|
116
|
+
{x: quad[2], y: quad[3]},
|
117
|
+
{x: quad[4], y: quad[5]},
|
118
|
+
{x: quad[6], y: quad[7]}]
|
119
|
+
end.first
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,407 @@
|
|
1
|
+
class InvalidSelector extends Error {}
|
2
|
+
class TimedOutPromise extends Error {}
|
3
|
+
class MouseEventFailed extends Error {}
|
4
|
+
|
5
|
+
const EVENTS = {
|
6
|
+
FOCUS: ["blur", "focus", "focusin", "focusout"],
|
7
|
+
MOUSE: ["click", "dblclick", "mousedown", "mouseenter", "mouseleave",
|
8
|
+
"mousemove", "mouseover", "mouseout", "mouseup", "contextmenu"],
|
9
|
+
FORM: ["submit"]
|
10
|
+
}
|
11
|
+
|
12
|
+
class Cuprite {
|
13
|
+
find(method, selector, within = document) {
|
14
|
+
try {
|
15
|
+
let results = [];
|
16
|
+
|
17
|
+
if (method == "xpath") {
|
18
|
+
let xpath = document.evaluate(selector, within, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
19
|
+
for (let i = 0; i < xpath.snapshotLength; i++) {
|
20
|
+
results.push(xpath.snapshotItem(i));
|
21
|
+
}
|
22
|
+
} else {
|
23
|
+
results = within.querySelectorAll(selector);
|
24
|
+
}
|
25
|
+
|
26
|
+
return results;
|
27
|
+
} catch (error) {
|
28
|
+
// DOMException.INVALID_EXPRESSION_ERR is undefined, using pure code
|
29
|
+
if (error.code == DOMException.SYNTAX_ERR || error.code == 51) {
|
30
|
+
throw new InvalidSelector;
|
31
|
+
} else {
|
32
|
+
throw error;
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
parents(node) {
|
38
|
+
let nodes = [];
|
39
|
+
let parent = node.parentNode;
|
40
|
+
while (parent != document) {
|
41
|
+
nodes.push(parent);
|
42
|
+
parent = parent.parentNode;
|
43
|
+
}
|
44
|
+
return nodes;
|
45
|
+
}
|
46
|
+
|
47
|
+
visibleText(node) {
|
48
|
+
if (this.isVisible(node)) {
|
49
|
+
if (node.nodeName == "TEXTAREA") {
|
50
|
+
return node.textContent;
|
51
|
+
} else {
|
52
|
+
if (node instanceof SVGElement) {
|
53
|
+
return node.textContent;
|
54
|
+
} else {
|
55
|
+
return node.innerText;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
isVisible(node) {
|
62
|
+
let mapName, style;
|
63
|
+
// if node is area, check visibility of relevant image
|
64
|
+
if (node.tagName === "AREA") {
|
65
|
+
mapName = document.evaluate("./ancestor::map/@name", node, null, XPathResult.STRING_TYPE, null).stringValue;
|
66
|
+
node = document.querySelector(`img[usemap="#${mapName}"]`);
|
67
|
+
if (node == null) {
|
68
|
+
return false;
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
while (node) {
|
73
|
+
style = window.getComputedStyle(node);
|
74
|
+
if (style.display === "none" || style.visibility === "hidden" || parseFloat(style.opacity) === 0) {
|
75
|
+
return false;
|
76
|
+
}
|
77
|
+
node = node.parentElement;
|
78
|
+
}
|
79
|
+
|
80
|
+
return true;
|
81
|
+
}
|
82
|
+
|
83
|
+
|
84
|
+
isDisabled(node) {
|
85
|
+
let xpath = "parent::optgroup[@disabled] | \
|
86
|
+
ancestor::select[@disabled] | \
|
87
|
+
parent::fieldset[@disabled] | \
|
88
|
+
ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]";
|
89
|
+
|
90
|
+
return node.disabled || document.evaluate(xpath, node, null, XPathResult.BOOLEAN_TYPE, null).booleanValue;
|
91
|
+
}
|
92
|
+
|
93
|
+
path(node) {
|
94
|
+
let nodes = [node];
|
95
|
+
let parent = node.parentNode;
|
96
|
+
while (parent !== document) {
|
97
|
+
nodes.unshift(parent);
|
98
|
+
parent = parent.parentNode;
|
99
|
+
}
|
100
|
+
|
101
|
+
let selectors = nodes.map(node => {
|
102
|
+
let prevSiblings = [];
|
103
|
+
let xpath = document.evaluate(`./preceding-sibling::${node.tagName}`, node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
104
|
+
|
105
|
+
for (let i = 0; i < xpath.snapshotLength; i++) {
|
106
|
+
prevSiblings.push(xpath.snapshotItem(i));
|
107
|
+
}
|
108
|
+
|
109
|
+
return `${node.tagName}[${(prevSiblings.length + 1)}]`;
|
110
|
+
});
|
111
|
+
|
112
|
+
return `//${selectors.join("/")}`;
|
113
|
+
}
|
114
|
+
|
115
|
+
set(node, value) {
|
116
|
+
if (node.readOnly) return;
|
117
|
+
|
118
|
+
if (node.maxLength >= 0) {
|
119
|
+
value = value.substr(0, node.maxLength);
|
120
|
+
}
|
121
|
+
|
122
|
+
this.trigger(node, "focus");
|
123
|
+
node.value = "";
|
124
|
+
|
125
|
+
if (node.type == "number") {
|
126
|
+
node.value = value
|
127
|
+
} else {
|
128
|
+
for (let i = 0; i < value.length; i++) {
|
129
|
+
let char = value[i];
|
130
|
+
let keyCode = this.characterToKeyCode(char);
|
131
|
+
this.keyupdowned(node, "keydown", keyCode);
|
132
|
+
node.value += char;
|
133
|
+
|
134
|
+
this.keypressed(node, false, false, false, false, char.charCodeAt(0), char.charCodeAt(0));
|
135
|
+
this.keyupdowned(node, "keyup", keyCode);
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
this.changed(node);
|
140
|
+
this.input(node);
|
141
|
+
this.trigger(node, "blur");
|
142
|
+
}
|
143
|
+
|
144
|
+
input(node) {
|
145
|
+
let event = document.createEvent("HTMLEvents");
|
146
|
+
event.initEvent("input", true, false);
|
147
|
+
node.dispatchEvent(event);
|
148
|
+
}
|
149
|
+
|
150
|
+
keyupdowned(node, eventName, keyCode) {
|
151
|
+
let event = document.createEvent("UIEvents");
|
152
|
+
event.initEvent(eventName, true, true);
|
153
|
+
event.keyCode = keyCode;
|
154
|
+
event.charCode = 0;
|
155
|
+
node.dispatchEvent(event);
|
156
|
+
}
|
157
|
+
|
158
|
+
keypressed(node, altKey, ctrlKey, shiftKey, metaKey, keyCode, charCode) {
|
159
|
+
event = document.createEvent("UIEvents");
|
160
|
+
event.initEvent("keypress", true, true);
|
161
|
+
event.window = window;
|
162
|
+
event.altKey = altKey;
|
163
|
+
event.ctrlKey = ctrlKey;
|
164
|
+
event.shiftKey = shiftKey;
|
165
|
+
event.metaKey = metaKey;
|
166
|
+
event.keyCode = keyCode;
|
167
|
+
event.charCode = charCode;
|
168
|
+
node.dispatchEvent(event);
|
169
|
+
}
|
170
|
+
|
171
|
+
characterToKeyCode(char) {
|
172
|
+
const specialKeys = {
|
173
|
+
96: 192, // `
|
174
|
+
45: 189, // -
|
175
|
+
61: 187, // =
|
176
|
+
91: 219, // [
|
177
|
+
93: 221, // ]
|
178
|
+
92: 220, // \
|
179
|
+
59: 186, // ;
|
180
|
+
39: 222, // '
|
181
|
+
44: 188, // ,
|
182
|
+
46: 190, // .
|
183
|
+
47: 191, // /
|
184
|
+
127: 46, // delete
|
185
|
+
126: 192, // ~
|
186
|
+
33: 49, // !
|
187
|
+
64: 50, // @
|
188
|
+
35: 51, // #
|
189
|
+
36: 52, // $
|
190
|
+
37: 53, // %
|
191
|
+
94: 54, // ^
|
192
|
+
38: 55, // &
|
193
|
+
42: 56, // *
|
194
|
+
40: 57, // (
|
195
|
+
41: 48, // )
|
196
|
+
95: 189, // _
|
197
|
+
43: 187, // +
|
198
|
+
123: 219, // {
|
199
|
+
125: 221, // }
|
200
|
+
124: 220, // |
|
201
|
+
58: 186, // :
|
202
|
+
34: 222, // "
|
203
|
+
60: 188, // <
|
204
|
+
62: 190, // >
|
205
|
+
63: 191, // ?
|
206
|
+
}
|
207
|
+
|
208
|
+
let code = char.toUpperCase().charCodeAt(0);
|
209
|
+
return specialKeys[code] || code;
|
210
|
+
}
|
211
|
+
|
212
|
+
scrollIntoViewport(node) {
|
213
|
+
let areaImage = this._getAreaImage(node);
|
214
|
+
|
215
|
+
if (areaImage) {
|
216
|
+
return this.scrollIntoViewport(areaImage);
|
217
|
+
} else {
|
218
|
+
node.scrollIntoViewIfNeeded();
|
219
|
+
|
220
|
+
if (!this._isInViewport(node)) {
|
221
|
+
node.scrollIntoView({block: "center", inline: "center", behavior: "instant"});
|
222
|
+
return this._isInViewport(node);
|
223
|
+
}
|
224
|
+
|
225
|
+
return true;
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
mouseEventTest(node, name, x, y) {
|
230
|
+
let frameOffset = this._frameOffset();
|
231
|
+
x -= frameOffset.left;
|
232
|
+
y -= frameOffset.top;
|
233
|
+
|
234
|
+
let element = document.elementFromPoint(x, y);
|
235
|
+
|
236
|
+
let el = element;
|
237
|
+
while (el) {
|
238
|
+
if (el == node) {
|
239
|
+
return true;
|
240
|
+
} else {
|
241
|
+
el = el.parentNode;
|
242
|
+
}
|
243
|
+
}
|
244
|
+
|
245
|
+
let selector = element && this._getSelector(element) || "none";
|
246
|
+
throw new MouseEventFailed([name, selector, x, y].join(", "));
|
247
|
+
}
|
248
|
+
|
249
|
+
_getAreaImage(node) {
|
250
|
+
if ("area" == node.tagName.toLowerCase()) {
|
251
|
+
let map = node.parentNode;
|
252
|
+
if (map.tagName.toLowerCase() != "map") {
|
253
|
+
throw new Error("the area is not within a map");
|
254
|
+
}
|
255
|
+
|
256
|
+
let mapName = map.getAttribute("name");
|
257
|
+
if (typeof mapName === "undefined" || mapName === null) {
|
258
|
+
throw new Error("area's parent map must have a name");
|
259
|
+
}
|
260
|
+
|
261
|
+
mapName = `#${mapName.toLowerCase()}`;
|
262
|
+
let imageNode = this.find("css", `img[usemap='${mapName}']`)[0];
|
263
|
+
if (typeof imageNode === "undefined" || imageNode === null) {
|
264
|
+
throw new Error("no image matches the map");
|
265
|
+
}
|
266
|
+
|
267
|
+
return imageNode;
|
268
|
+
}
|
269
|
+
}
|
270
|
+
|
271
|
+
_frameOffset() {
|
272
|
+
let win = window;
|
273
|
+
let offset = { top: 0, left: 0 };
|
274
|
+
|
275
|
+
while (win.frameElement) {
|
276
|
+
let rect = win.frameElement.getClientRects()[0];
|
277
|
+
let style = win.getComputedStyle(win.frameElement);
|
278
|
+
win = win.parent;
|
279
|
+
|
280
|
+
offset.top += rect.top + parseInt(style.getPropertyValue("padding-top"), 10)
|
281
|
+
offset.left += rect.left + parseInt(style.getPropertyValue("padding-left"), 10)
|
282
|
+
}
|
283
|
+
|
284
|
+
return offset;
|
285
|
+
}
|
286
|
+
|
287
|
+
_getSelector(el) {
|
288
|
+
let selector = (el.tagName != 'HTML') ? this._getSelector(el.parentNode) + " " : "";
|
289
|
+
selector += el.tagName.toLowerCase();
|
290
|
+
if (el.id) { selector += `#${el.id}` };
|
291
|
+
el.classList.forEach(c => selector += `.${c}`);
|
292
|
+
return selector;
|
293
|
+
}
|
294
|
+
|
295
|
+
_isInViewport(node) {
|
296
|
+
let rect = node.getBoundingClientRect();
|
297
|
+
return rect.top >= 0 &&
|
298
|
+
rect.left >= 0 &&
|
299
|
+
rect.bottom <= window.innerHeight &&
|
300
|
+
rect.right <= window.innerWidth;
|
301
|
+
}
|
302
|
+
|
303
|
+
select(node, value) {
|
304
|
+
if (this.isDisabled(node)) {
|
305
|
+
return false;
|
306
|
+
} else if (value == false && !node.parentNode.multiple) {
|
307
|
+
return false;
|
308
|
+
} else {
|
309
|
+
this.trigger(node.parentNode, "focus");
|
310
|
+
|
311
|
+
node.selected = value;
|
312
|
+
this.changed(node);
|
313
|
+
|
314
|
+
this.trigger(node.parentNode, "blur");
|
315
|
+
return true;
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
changed(node) {
|
320
|
+
let element;
|
321
|
+
let event = document.createEvent("HTMLEvents");
|
322
|
+
event.initEvent("change", true, false);
|
323
|
+
|
324
|
+
// In the case of an OPTION tag, the change event should come
|
325
|
+
// from the parent SELECT
|
326
|
+
if (node.nodeName == "OPTION") {
|
327
|
+
element = node.parentNode
|
328
|
+
if (element.nodeName == "OPTGROUP") {
|
329
|
+
element = element.parentNode
|
330
|
+
}
|
331
|
+
element
|
332
|
+
} else {
|
333
|
+
element = node
|
334
|
+
}
|
335
|
+
|
336
|
+
element.dispatchEvent(event)
|
337
|
+
}
|
338
|
+
|
339
|
+
trigger(node, name, options = {}) {
|
340
|
+
let event;
|
341
|
+
|
342
|
+
if (EVENTS.MOUSE.indexOf(name) != -1) {
|
343
|
+
event = document.createEvent("MouseEvent");
|
344
|
+
event.initMouseEvent(
|
345
|
+
name, true, true, window, 0,
|
346
|
+
options["screenX"] || 0, options["screenY"] || 0,
|
347
|
+
options["clientX"] || 0, options["clientY"] || 0,
|
348
|
+
options["ctrlKey"] || false,
|
349
|
+
options["altKey"] || false,
|
350
|
+
options["shiftKey"] || false,
|
351
|
+
options["metaKey"] || false,
|
352
|
+
options["button"] || 0, null
|
353
|
+
)
|
354
|
+
} else if (EVENTS.FOCUS.indexOf(name) != -1) {
|
355
|
+
event = this.obtainEvent(name);
|
356
|
+
} else if (EVENTS.FORM.indexOf(name) != -1) {
|
357
|
+
event = this.obtainEvent(name);
|
358
|
+
} else {
|
359
|
+
throw "Unknown event";
|
360
|
+
}
|
361
|
+
|
362
|
+
node.dispatchEvent(event);
|
363
|
+
}
|
364
|
+
|
365
|
+
obtainEvent(name) {
|
366
|
+
let event = document.createEvent("HTMLEvents");
|
367
|
+
event.initEvent(name, true, true);
|
368
|
+
return event;
|
369
|
+
}
|
370
|
+
|
371
|
+
getAttributes(node) {
|
372
|
+
let attrs = {};
|
373
|
+
for (let i = 0, len = node.attributes.length; i < len; i++) {
|
374
|
+
let attr = node.attributes[i];
|
375
|
+
attrs[attr.name] = attr.value.replace("\n", "\\n");
|
376
|
+
}
|
377
|
+
|
378
|
+
return JSON.stringify(attrs);
|
379
|
+
}
|
380
|
+
|
381
|
+
getAttribute(node, name) {
|
382
|
+
if (name == "checked" || name == "selected") {
|
383
|
+
return node[name];
|
384
|
+
} else {
|
385
|
+
return node.getAttribute(name);
|
386
|
+
}
|
387
|
+
}
|
388
|
+
|
389
|
+
value(node) {
|
390
|
+
if (node.tagName == "SELECT" && node.multiple) {
|
391
|
+
let result = []
|
392
|
+
|
393
|
+
for (let i = 0, len = node.children.length; i < len; i++) {
|
394
|
+
let option = node.children[i];
|
395
|
+
if (option.selected) {
|
396
|
+
result.push(option.value);
|
397
|
+
}
|
398
|
+
}
|
399
|
+
|
400
|
+
return result;
|
401
|
+
} else {
|
402
|
+
return node.value;
|
403
|
+
}
|
404
|
+
}
|
405
|
+
}
|
406
|
+
|
407
|
+
window._cuprite = new Cuprite;
|