cuprite 0.18 → 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.
@@ -1,27 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "uri"
4
3
  require "forwardable"
4
+ require "capybara/cuprite/finder"
5
+ require "capybara/cuprite/driver/deprecations"
6
+ require "capybara/cuprite/driver/extensions"
5
7
 
6
- # rubocop:disable-next Metrics/ClassLength
7
8
  module Capybara
8
9
  module Cuprite
9
10
  class Driver < Capybara::Driver::Base
10
11
  DEFAULT_MAXIMIZE_SCREEN_SIZE = [1366, 768].freeze
11
12
  EXTENSION = File.expand_path("javascripts/index.js", __dir__)
13
+ CALLBACK_JS = <<~JS
14
+ function(...args) {
15
+ return new Promise((resolve, reject) => {
16
+ try {
17
+ (function() {
18
+ SCRIPT
19
+ }).apply(this, [...args, resolve]);
20
+ } catch (error) {
21
+ reject(error);
22
+ }
23
+ });
24
+ }
25
+ JS
12
26
 
13
27
  extend Forwardable
28
+ include Deprecations
29
+ include Extensions
14
30
 
15
- delegate %i[restart quit status_code timeout timeout= current_url title body
16
- window_handles close_window switch_to_window within_window window_handle
17
- back forward refresh wait_for_reload viewport_size device_pixel_ratio] => :browser
31
+ delegate %i[quit current_url title
32
+ window_handles current_window_handle close_window switch_to_window
33
+ refresh] => :browser
18
34
  delegate %i[send_keys] => :active_element
19
- alias html body
20
- alias current_window_handle window_handle
21
- alias go_back back
22
- alias go_forward forward
35
+ def_delegator :browser, :body, :html
36
+ def_delegator :browser, :back, :go_back
37
+ def_delegator :browser, :forward, :go_forward
23
38
 
24
- attr_reader :app, :options, :screen_size
39
+ attr_reader :app, :options
25
40
 
26
41
  def initialize(app, options = {})
27
42
  @app = app
@@ -42,6 +57,7 @@ module Capybara
42
57
  unless @options[:browser_options][:"remote-allow-origins"]
43
58
  @options[:browser_options].merge!("remote-allow-origins": "*")
44
59
  end
60
+ @options[:browser_options] = { "disable-web-security": nil }.merge(@options[:browser_options])
45
61
 
46
62
  super()
47
63
  end
@@ -51,20 +67,21 @@ module Capybara
51
67
  end
52
68
 
53
69
  def browser
54
- @browser ||= Browser.new(@options)
70
+ @browser ||= Browser.new({ base_url: capybara_base_url }.compact.merge(@options))
55
71
  end
56
72
 
57
73
  def visit(url)
58
74
  @started = true
59
- browser.visit(url)
75
+ browser.base_url = url if url.match?(%r{\Ahttps?://})
76
+ browser.goto(url)
60
77
  end
61
78
 
62
- def frame_url
63
- evaluate_script("window.location.href")
79
+ def status_code
80
+ browser.network.status
64
81
  end
65
82
 
66
- def source
67
- browser.source.to_s
83
+ def frame_url
84
+ evaluate_script("window.location.href")
68
85
  end
69
86
 
70
87
  def frame_title
@@ -72,7 +89,7 @@ module Capybara
72
89
  end
73
90
 
74
91
  def active_element
75
- Node.new(self, browser.active_element)
92
+ Node.new(self, browser.evaluate("document.activeElement"))
76
93
  end
77
94
 
78
95
  def find_xpath(selector)
@@ -83,21 +100,13 @@ module Capybara
83
100
  find(:css, selector)
84
101
  end
85
102
 
86
- def find(method, selector)
87
- browser.find(method, selector).map { |native| Node.new(self, native) }
88
- end
89
-
90
- def click(x, y)
91
- browser.mouse.click(x: x, y: y)
92
- end
93
-
94
103
  def evaluate_script(script, *args)
95
104
  result = browser.evaluate(script, *native_args(args))
96
105
  unwrap_script_result(result)
97
106
  end
98
107
 
99
108
  def evaluate_async_script(script, *args)
100
- result = browser.evaluate_async(script, session_wait_time, *native_args(args))
109
+ result = browser.evaluate(CALLBACK_JS.sub("SCRIPT") { script }, *native_args(args), timeout: session_wait_time)
101
110
  unwrap_script_result(result)
102
111
  end
103
112
 
@@ -109,23 +118,20 @@ module Capybara
109
118
  def switch_to_frame(locator)
110
119
  handle = case locator
111
120
  when Capybara::Node::Element
112
- locator.native.description["frameId"]
121
+ locator.native.node.description["frameId"]
113
122
  when Capybara::Cuprite::Node
114
- locator.description["frameId"]
123
+ locator.node.description["frameId"]
115
124
  when :parent, :top
116
125
  locator
117
126
  end
118
127
 
119
128
  raise ArgumentError, "Unable to switch to frame from #{locator.class}" unless handle
120
129
 
121
- browser.switch_to_frame(handle)
130
+ browser.page.switch_to_frame(handle)
122
131
  end
123
132
 
124
133
  def open_new_window
125
- target = browser.default_context.create_target
126
- target.maybe_sleep_if_new_window
127
- target.page = Page.new(target.client, context_id: target.context_id, target_id: target.id)
128
- target.page
134
+ browser.open_new_window
129
135
  end
130
136
 
131
137
  def no_such_window_error
@@ -139,13 +145,16 @@ module Capybara
139
145
  browser.url_whitelist = @options[:url_whitelist]
140
146
  browser.raise_on_unhandled_modal = @options.fetch(:raise_on_unhandled_modal, false)
141
147
  browser.reset
148
+ base_url = @options[:base_url] || capybara_base_url
149
+ browser.base_url = base_url if base_url
150
+ ensure
142
151
  @started = false
143
152
  end
144
153
 
145
154
  def save_screenshot(path, options = {})
146
155
  options[:scale] = @zoom_factor if @zoom_factor
147
156
 
148
- if pdf?(path, options)
157
+ if File.extname(path) == ".pdf"
149
158
  options[:paperWidth] = @paper_size[:width].to_f if @paper_size
150
159
  options[:paperHeight] = @paper_size[:height].to_f if @paper_size
151
160
  browser.pdf(path: path, **options)
@@ -153,32 +162,10 @@ module Capybara
153
162
  browser.screenshot(path: path, **options)
154
163
  end
155
164
  end
156
- alias render save_screenshot
157
-
158
- def render_base64(format = :png, options = {})
159
- if pdf?(nil, options)
160
- options[:paperWidth] = @paper_size[:width].to_f if @paper_size
161
- options[:paperHeight] = @paper_size[:height].to_f if @paper_size
162
- browser.pdf(encoding: :base64, **options)
163
- else
164
- browser.screenshot(format: format, encoding: :base64, **options)
165
- end
166
- end
167
-
168
- def zoom_factor=(value)
169
- @zoom_factor = value.to_f
170
- end
171
-
172
- attr_writer :paper_size
173
-
174
- def resize(width, height)
175
- browser.resize(width: width, height: height)
176
- end
177
- alias resize_window resize
178
165
 
179
166
  def resize_window_to(handle, width, height)
180
167
  within_window(handle) do
181
- resize(width, height)
168
+ browser.resize(width: width, height: height)
182
169
  end
183
170
  end
184
171
 
@@ -198,129 +185,10 @@ module Capybara
198
185
  end
199
186
  end
200
187
 
201
- def scroll_to(left, top)
202
- browser.mouse.scroll_to(left, top)
203
- end
204
-
205
- def network_traffic(type = nil)
206
- traffic = browser.network.traffic
207
-
208
- case type.to_s
209
- when "all"
210
- traffic
211
- when "blocked"
212
- traffic.select(&:blocked?)
213
- else
214
- # when request isn't blocked
215
- traffic.reject(&:blocked?)
216
- end
217
- end
218
-
219
- def clear_network_traffic
220
- browser.network.clear(:traffic)
221
- end
222
-
223
- def set_proxy(host, port, user = nil, password = nil, bypass = nil)
224
- @options.merge!(proxy: { host: host, port: port, user: user, password: password, bypass: bypass })
225
- end
226
-
227
- def headers
228
- browser.headers.get
229
- end
230
-
231
- def headers=(headers)
232
- browser.headers.set(headers)
233
- end
234
-
235
- def add_headers(headers)
236
- browser.headers.add(headers)
237
- end
238
-
239
- def add_header(name, value, permanent: true)
240
- browser.headers.add({ name => value }, permanent: permanent)
241
- end
242
-
243
188
  def response_headers
244
189
  browser.network.response&.headers
245
190
  end
246
191
 
247
- def cookies
248
- browser.cookies.all
249
- end
250
-
251
- def set_cookie(name, value, options = {})
252
- options = options.dup
253
- options[:name] ||= name
254
- options[:value] ||= value
255
- options[:domain] ||= default_domain
256
- browser.cookies.set(**options)
257
- end
258
-
259
- def remove_cookie(name, **options)
260
- options[:domain] = default_domain if options.empty?
261
- options = options.merge(name: name)
262
- browser.cookies.remove(**options)
263
- end
264
-
265
- def clear_cookies
266
- browser.cookies.clear
267
- end
268
-
269
- def wait_for_network_idle(**options)
270
- browser.network.wait_for_idle(**options)
271
- end
272
-
273
- def clear_memory_cache
274
- browser.network.clear(:cache)
275
- end
276
-
277
- def basic_authorize(user, password)
278
- browser.network.authorize(user: user, password: password) do |request, _index, _total|
279
- request.continue
280
- end
281
- end
282
- alias authorize basic_authorize
283
-
284
- def debug(...)
285
- browser.debug(...)
286
- end
287
-
288
- def pause
289
- # STDIN is not necessarily connected to a keyboard. It might even be closed.
290
- # So we need a method other than keypress to continue.
291
-
292
- # In jRuby - STDIN returns immediately from select
293
- # see https://github.com/jruby/jruby/issues/1783
294
- read, write = IO.pipe
295
- thread = Thread.new do
296
- IO.copy_stream($stdin, write)
297
- write.close
298
- end
299
-
300
- warn "Cuprite execution paused. Press enter (or run 'kill -CONT #{Process.pid}') to continue."
301
-
302
- signal = false
303
- old_trap = trap("SIGCONT") do
304
- signal = true
305
- warn "\nSignal SIGCONT received"
306
- end
307
- keyboard = read.wait_readable(1) until keyboard || signal # wait for data on STDIN or signal SIGCONT received
308
-
309
- unless signal
310
- begin
311
- input = read.read_nonblock(80) # clear out the read buffer
312
- puts unless input&.end_with?("\n")
313
- rescue EOFError, IO::WaitReadable
314
- # Ignore problems reading from STDIN.
315
- end
316
- end
317
- ensure
318
- thread.kill
319
- read.close
320
- trap("SIGCONT", old_trap) # Restore the previous signal handler, if there was one.
321
- warn "Continuing"
322
- end
323
-
324
192
  def wait?
325
193
  true
326
194
  end
@@ -336,37 +204,49 @@ module Capybara
336
204
  def accept_modal(type, options = {})
337
205
  case type
338
206
  when :alert, :confirm
339
- browser.accept_confirm
207
+ browser.page.accept_confirm
340
208
  when :prompt
341
- browser.accept_prompt(options[:with])
209
+ browser.page.accept_prompt(options[:with])
342
210
  end
343
211
 
344
212
  yield if block_given?
345
213
 
346
- browser.find_modal(options)
214
+ browser.page.find_modal(options)
347
215
  end
348
216
 
349
217
  def dismiss_modal(type, options = {})
350
218
  case type
351
219
  when :confirm
352
- browser.dismiss_confirm
220
+ browser.page.dismiss_confirm
353
221
  when :prompt
354
- browser.dismiss_prompt
222
+ browser.page.dismiss_prompt
355
223
  end
356
224
 
357
225
  yield if block_given?
358
226
 
359
- browser.find_modal(options)
227
+ browser.page.find_modal(options)
360
228
  end
361
229
 
362
230
  private
363
231
 
364
- def default_domain
365
- if @started
366
- URI.parse(browser.current_url).host
367
- else
368
- URI.parse(default_cookie_host).host || "127.0.0.1"
369
- end
232
+ attr_reader :screen_size
233
+
234
+ def find(method, selector)
235
+ Finder.find(browser, method, selector).map { |native| Node.new(self, native) }
236
+ end
237
+
238
+ def within_window(handle)
239
+ original = browser.current_window_handle
240
+ raise Ferrum::NoSuchPageError unless browser.window_handles.include?(handle)
241
+
242
+ browser.switch_to_window(handle)
243
+ yield
244
+ ensure
245
+ browser.switch_to_window(original)
246
+ end
247
+
248
+ def capybara_base_url
249
+ session_options.app_host || session&.server&.base_url
370
250
  end
371
251
 
372
252
  def native_args(args)
@@ -385,14 +265,6 @@ module Capybara
385
265
  end
386
266
  end
387
267
 
388
- def default_cookie_host
389
- if respond_to?(:session_options)
390
- session_options.app_host
391
- else
392
- Capybara.app_host
393
- end || ""
394
- end
395
-
396
268
  def unwrap_script_result(arg)
397
269
  case arg
398
270
  when Array
@@ -405,11 +277,6 @@ module Capybara
405
277
  arg
406
278
  end
407
279
  end
408
-
409
- def pdf?(path, options)
410
- (path && File.extname(path).delete(".") == "pdf") ||
411
- options[:format].to_s == "pdf"
412
- end
413
280
  end
414
281
  end
415
282
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Capybara
4
+ module Cuprite
5
+ module Finder
6
+ module_function
7
+
8
+ def find(browser, method, selector, within = nil)
9
+ # Capybara appends its text-match hint as `(expr)[...]`, but with `exact_text: true` the hint
10
+ # is empty and the result `(expr)[]` is invalid XPath. An empty predicate is never valid, so
11
+ # dropping it can't change a valid selector. Remove once teamcapybara/capybara#2860 is released.
12
+ selector = selector.delete_suffix("[]") if method == :xpath
13
+ nodes = browser.evaluate("_cuprite.find(...arguments)", *[method, selector, within].compact)
14
+ nodes.select(&:node?)
15
+ rescue Ferrum::JavaScriptError => e
16
+ raise InvalidSelector.new(e.response, method, selector) if e.class_name == "InvalidSelector"
17
+
18
+ raise
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,118 +1,115 @@
1
1
  // HTML5 drag-and-drop emulation.
2
2
  //
3
- // Ported near-verbatim from Capybara's Selenium driver
4
- // (capybara/selenium/extensions/html5_drag.rb, HTML5_DRAG_DROP_SCRIPT) so
5
- // Cuprite matches Capybara's HTML5 drag behaviour and its shared specs. Kept
6
- // close to the source to ease future syncs; known upstream quirks (rectPt.top
7
- // in pointOnRect, undeclared `key`, callback.call(true)) are preserved
8
- // deliberately. See https://github.com/rubycdp/cuprite/issues/314.
9
-
10
- function rectCenter(rect){
11
- return new DOMPoint(
12
- (rect.left + rect.right)/2,
13
- (rect.top + rect.bottom)/2
14
- );
15
- }
16
-
17
- function pointOnRect(pt, rect) {
18
- var rectPt = rectCenter(rect);
19
- var slope = (rectPt.y - pt.y) / (rectPt.x - pt.x);
20
-
21
- if (pt.x <= rectPt.x) { // left side
22
- var minXy = slope * (rect.left - pt.x) + pt.y;
23
- if (rect.top <= minXy && minXy <= rect.bottom)
24
- return new DOMPoint(rect.left, minXy);
3
+ // Ported from Capybara's Selenium driver (capybara/selenium/extensions/html5_drag.rb,
4
+ // HTML5_DRAG_DROP_SCRIPT), see https://github.com/rubycdp/cuprite/issues/314.
5
+ // Unlike the original, a drag ends the way it does in a browser: with `drop` if
6
+ // the target accepted it, otherwise with `dragleave`, never both [#325].
7
+
8
+ (function(source, target, step_delay, drop_modifier_keys, drop_offset, callback) {
9
+ function rectCenter(rect){
10
+ return new DOMPoint(
11
+ (rect.left + rect.right)/2,
12
+ (rect.top + rect.bottom)/2
13
+ );
25
14
  }
26
15
 
27
- if (pt.x >= rectPt.x) { // right side
28
- var maxXy = slope * (rect.right - pt.x) + pt.y;
29
- if (rect.top <= maxXy && maxXy <= rect.bottom)
30
- return new DOMPoint(rect.right, maxXy);
16
+ function pointOnRect(pt, rect) {
17
+ var rectPt = rectCenter(rect);
18
+ var slope = (rectPt.y - pt.y) / (rectPt.x - pt.x);
19
+
20
+ if (pt.x <= rectPt.x) { // left side
21
+ var minXy = slope * (rect.left - pt.x) + pt.y;
22
+ if (rect.top <= minXy && minXy <= rect.bottom)
23
+ return new DOMPoint(rect.left, minXy);
24
+ }
25
+
26
+ if (pt.x >= rectPt.x) { // right side
27
+ var maxXy = slope * (rect.right - pt.x) + pt.y;
28
+ if (rect.top <= maxXy && maxXy <= rect.bottom)
29
+ return new DOMPoint(rect.right, maxXy);
30
+ }
31
+
32
+ if (pt.y <= rectPt.y) { // top side
33
+ var minYx = (rect.top - pt.y) / slope + pt.x;
34
+ if (rect.left <= minYx && minYx <= rect.right)
35
+ return new DOMPoint(minYx, rect.top);
36
+ }
37
+
38
+ if (pt.y >= rectPt.y) { // bottom side
39
+ var maxYx = (rect.bottom - pt.y) / slope + pt.x;
40
+ if (rect.left <= maxYx && maxYx <= rect.right)
41
+ return new DOMPoint(maxYx, rect.bottom);
42
+ }
43
+
44
+ return new DOMPoint(pt.x,pt.y);
45
+ }
46
+
47
+ function dragEnterTarget() {
48
+ target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
49
+ var targetRect = target.getBoundingClientRect();
50
+ var sourceCenter = rectCenter(source.getBoundingClientRect());
51
+
52
+ for (var i = 0; i < drop_modifier_keys.length; i++) {
53
+ var key = drop_modifier_keys[i];
54
+ if (key == "control"){
55
+ key = "ctrl"
56
+ }
57
+ opts[key + 'Key'] = true;
58
+ }
59
+
60
+ var dragEnterEvent = new DragEvent('dragenter', opts);
61
+ target.dispatchEvent(dragEnterEvent);
62
+
63
+ // fire 2 dragover events to simulate dragging with a direction
64
+ var entryPoint = pointOnRect(sourceCenter, targetRect)
65
+ var dragOverOpts = Object.assign({clientX: entryPoint.x, clientY: entryPoint.y}, opts);
66
+ var dragOverEvent = new DragEvent('dragover', dragOverOpts);
67
+ target.dispatchEvent(dragOverEvent);
68
+ window.setTimeout(dragOnTarget, step_delay);
31
69
  }
32
70
 
33
- if (pt.y <= rectPt.y) { // top side
34
- var minYx = (rectPt.top - pt.y) / slope + pt.x;
35
- if (rect.left <= minYx && minYx <= rect.right)
36
- return new DOMPoint(minYx, rect.top);
71
+ function dropPoint(rect) {
72
+ if (!drop_offset) return rectCenter(rect);
73
+
74
+ var origin = drop_offset.position == 'center' ? rectCenter(rect) : new DOMPoint(rect.left, rect.top);
75
+ return new DOMPoint(origin.x + drop_offset.x, origin.y + drop_offset.y);
37
76
  }
38
77
 
39
- if (pt.y >= rectPt.y) { // bottom side
40
- var maxYx = (rect.bottom - pt.y) / slope + pt.x;
41
- if (rect.left <= maxYx && maxYx <= rect.right)
42
- return new DOMPoint(maxYx, rect.bottom);
78
+ function dragOnTarget() {
79
+ var point = dropPoint(target.getBoundingClientRect());
80
+ var dragOverOpts = Object.assign({clientX: point.x, clientY: point.y}, opts);
81
+ var dragOverEvent = new DragEvent('dragover', dragOverOpts);
82
+ target.dispatchEvent(dragOverEvent);
83
+ window.setTimeout(dragEnd, step_delay, dragOverEvent.defaultPrevented, dragOverOpts);
43
84
  }
44
85
 
45
- return new DOMPoint(pt.x,pt.y);
46
- }
86
+ function dragEnd(drop, dragOverOpts) {
87
+ var dragEndOptions = Object.assign({}, opts, dragOverOpts);
88
+ var dropOrLeaveEvent = new DragEvent(drop ? 'drop' : 'dragleave', dragEndOptions);
89
+ target.dispatchEvent(dropOrLeaveEvent);
90
+ var dragEndEvent = new DragEvent('dragend', dragEndOptions);
91
+ source.dispatchEvent(dragEndEvent);
92
+ callback.call(true);
93
+ }
47
94
 
48
- function dragEnterTarget() {
49
- target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'});
50
- var targetRect = target.getBoundingClientRect();
51
- var sourceCenter = rectCenter(source.getBoundingClientRect());
95
+ var dt = new DataTransfer();
96
+ var opts = { cancelable: true, bubbles: true, dataTransfer: dt };
52
97
 
53
- for (var i = 0; i < drop_modifier_keys.length; i++) {
54
- key = drop_modifier_keys[i];
55
- if (key == "control"){
56
- key = "ctrl"
57
- }
58
- opts[key + 'Key'] = true;
98
+ while (source && !source.draggable) {
99
+ source = source.parentElement;
59
100
  }
60
101
 
61
- var dragEnterEvent = new DragEvent('dragenter', opts);
62
- target.dispatchEvent(dragEnterEvent);
63
-
64
- // fire 2 dragover events to simulate dragging with a direction
65
- var entryPoint = pointOnRect(sourceCenter, targetRect)
66
- var dragOverOpts = Object.assign({clientX: entryPoint.x, clientY: entryPoint.y}, opts);
67
- var dragOverEvent = new DragEvent('dragover', dragOverOpts);
68
- target.dispatchEvent(dragOverEvent);
69
- window.setTimeout(dragOnTarget, step_delay);
70
- }
71
-
72
- function dragOnTarget() {
73
- var targetCenter = rectCenter(target.getBoundingClientRect());
74
- var dragOverOpts = Object.assign({clientX: targetCenter.x, clientY: targetCenter.y}, opts);
75
- var dragOverEvent = new DragEvent('dragover', dragOverOpts);
76
- target.dispatchEvent(dragOverEvent);
77
- window.setTimeout(dragLeave, step_delay, dragOverEvent.defaultPrevented, dragOverOpts);
78
- }
79
-
80
- function dragLeave(drop, dragOverOpts) {
81
- var dragLeaveOptions = Object.assign({}, opts, dragOverOpts);
82
- var dragLeaveEvent = new DragEvent('dragleave', dragLeaveOptions);
83
- target.dispatchEvent(dragLeaveEvent);
84
- if (drop) {
85
- var dropEvent = new DragEvent('drop', dragLeaveOptions);
86
- target.dispatchEvent(dropEvent);
102
+ if (source.tagName == 'A'){
103
+ dt.setData('text/uri-list', source.href);
104
+ dt.setData('text', source.href);
105
+ }
106
+ if (source.tagName == 'IMG'){
107
+ dt.setData('text/uri-list', source.src);
108
+ dt.setData('text', source.src);
87
109
  }
88
- var dragEndEvent = new DragEvent('dragend', dragLeaveOptions);
89
- source.dispatchEvent(dragEndEvent);
90
- callback.call(true);
91
- }
92
-
93
- var source = arguments[0],
94
- target = arguments[1],
95
- step_delay = arguments[2],
96
- drop_modifier_keys = arguments[3],
97
- callback = arguments[4];
98
-
99
- var dt = new DataTransfer();
100
- var opts = { cancelable: true, bubbles: true, dataTransfer: dt };
101
-
102
- while (source && !source.draggable) {
103
- source = source.parentElement;
104
- }
105
-
106
- if (source.tagName == 'A'){
107
- dt.setData('text/uri-list', source.href);
108
- dt.setData('text', source.href);
109
- }
110
- if (source.tagName == 'IMG'){
111
- dt.setData('text/uri-list', source.src);
112
- dt.setData('text', source.src);
113
- }
114
-
115
- var dragEvent = new DragEvent('dragstart', opts);
116
- source.dispatchEvent(dragEvent);
117
-
118
- window.setTimeout(dragEnterTarget, step_delay);
110
+
111
+ var dragEvent = new DragEvent('dragstart', opts);
112
+ source.dispatchEvent(dragEvent);
113
+
114
+ window.setTimeout(dragEnterTarget, step_delay);
115
+ }).apply(this, arguments);