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.
- checksums.yaml +4 -4
- data/README.md +69 -37
- data/lib/capybara/cuprite/browser/deprecations.rb +26 -0
- data/lib/capybara/cuprite/browser/extensions.rb +59 -0
- data/lib/capybara/cuprite/browser.rb +28 -231
- data/lib/capybara/cuprite/deprecation.rb +13 -0
- data/lib/capybara/cuprite/devices.rb +188 -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 +68 -201
- data/lib/capybara/cuprite/finder.rb +22 -0
- data/lib/capybara/cuprite/javascripts/drag.js +100 -103
- data/lib/capybara/cuprite/javascripts/index.js +511 -489
- data/lib/capybara/cuprite/node/deprecations.rb +43 -0
- data/lib/capybara/cuprite/node/extensions.rb +27 -0
- data/lib/capybara/cuprite/node.rb +140 -116
- data/lib/capybara/cuprite/options.rb +19 -0
- data/lib/capybara/cuprite/page/frames.rb +47 -0
- data/lib/capybara/cuprite/page/modals.rb +118 -0
- data/lib/capybara/cuprite/page.rb +9 -207
- data/lib/capybara/cuprite/version.rb +1 -1
- data/lib/capybara/cuprite.rb +1 -0
- metadata +16 -6
- data/lib/capybara/cuprite/cookie.rb +0 -47
|
@@ -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[
|
|
16
|
-
window_handles close_window switch_to_window
|
|
17
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
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.
|
|
75
|
+
browser.base_url = url if url.match?(%r{\Ahttps?://})
|
|
76
|
+
browser.goto(url)
|
|
60
77
|
end
|
|
61
78
|
|
|
62
|
-
def
|
|
63
|
-
|
|
79
|
+
def status_code
|
|
80
|
+
browser.network.status
|
|
64
81
|
end
|
|
65
82
|
|
|
66
|
-
def
|
|
67
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
28
|
-
var
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
40
|
-
var
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
|
|
49
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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);
|