Module: Capybara::Selenium::Node::Html5Drag
Relationships & Source Files | |
Defined in: | lib/capybara/selenium/extensions/html5_drag.rb |
Constant Summary
-
ATTACH_FILE =
# File 'lib/capybara/selenium/extensions/html5_drag.rb', line 86<<~JS (function(){ var input = document.createElement('INPUT'); input.type = "file"; input.id = "_capybara_drop_file"; input.multiple = true; document.body.appendChild(input); return input; })() JS
-
DROP_FILE =
# File 'lib/capybara/selenium/extensions/html5_drag.rb', line 65<<~JS var el = arguments[0], input = arguments[1], files = input.files, dt = new DataTransfer(), opts = { cancelable: true, bubbles: true, dataTransfer: dt }; input.parentElement.removeChild(input); if (dt.items){ for (var i=0; i<files.length; i++){ dt.items.add(files[i]); } } else { Object.defineProperty(dt, "files", { value: files, writable: false }); } var dropEvent = new DragEvent('drop', opts); el.dispatchEvent(dropEvent); JS
-
DROP_STRING =
# File 'lib/capybara/selenium/extensions/html5_drag.rb', line 49<<~JS var strings = arguments[0], el = arguments[1], dt = new DataTransfer(), opts = { cancelable: true, bubbles: true, dataTransfer: dt }; for (var i=0; i < strings.length; i++){ if (dt.items) { dt.items.add(strings[i]['data'], strings[i]['type']); } else { dt.setData(strings[i]['type'], strings[i]['data']); } } var dropEvent = new DragEvent('drop', opts); el.dispatchEvent(dropEvent); JS
-
HTML5_DRAG_DROP_SCRIPT =
# File 'lib/capybara/selenium/extensions/html5_drag.rb', line 117<<~JS function rectCenter(rect){ return new DOMPoint( (rect.left + rect.right)/2, (rect.top + rect.bottom)/2 ); } function pointOnRect(pt, rect) { var rectPt = rectCenter(rect); var slope = (rectPt.y - pt.y) / (rectPt.x - pt.x); if (pt.x <= rectPt.x) { // left side var minXy = slope * (rect.left - pt.x) + pt.y; if (rect.top <= minXy && minXy <= rect.bottom) return new DOMPoint(rect.left, minXy); } if (pt.x >= rectPt.x) { // right side var maxXy = slope * (rect.right - pt.x) + pt.y; if (rect.top <= maxXy && maxXy <= rect.bottom) return new DOMPoint(rect.right, maxXy); } if (pt.y <= rectPt.y) { // top side var minYx = (rectPt.top - pt.y) / slope + pt.x; if (rect.left <= minYx && minYx <= rect.right) return new DOMPoint(minYx, rect.top); } if (pt.y >= rectPt.y) { // bottom side var maxYx = (rect.bottom - pt.y) / slope + pt.x; if (rect.left <= maxYx && maxYx <= rect.right) return new DOMPoint(maxYx, rect.bottom); } return new DOMPoint(pt.x,pt.y); } function dragEnterTarget() { target.scrollIntoView({behavior: 'instant', block: 'center', inline: 'center'}); var targetRect = target.getBoundingClientRect(); var sourceCenter = rectCenter(source.getBoundingClientRect()); for (var i = 0; i < drop_modifier_keys.length; i++) { key = drop_modifier_keys[i]; if (key == "control"){ key = "ctrl" } opts[key + 'Key'] = true; } var dragEnterEvent = new DragEvent('dragenter', opts); target.dispatchEvent(dragEnterEvent); // fire 2 dragover events to simulate dragging with a direction var entryPoint = pointOnRect(sourceCenter, targetRect) var dragOverOpts = Object.assign({clientX: entryPoint.x, clientY: entryPoint.y}, opts); var dragOverEvent = new DragEvent('dragover', dragOverOpts); target.dispatchEvent(dragOverEvent); window.setTimeout(dragOnTarget, step_delay); } function dragOnTarget() { var targetCenter = rectCenter(target.getBoundingClientRect()); var dragOverOpts = Object.assign({clientX: targetCenter.x, clientY: targetCenter.y}, opts); var dragOverEvent = new DragEvent('dragover', dragOverOpts); target.dispatchEvent(dragOverEvent); window.setTimeout(dragLeave, step_delay, dragOverEvent.defaultPrevented, dragOverOpts); } function dragLeave(drop, dragOverOpts) { var dragLeaveOptions = Object.assign({}, opts, dragOverOpts); var dragLeaveEvent = new DragEvent('dragleave', dragLeaveOptions); target.dispatchEvent(dragLeaveEvent); if (drop) { var dropEvent = new DragEvent('drop', dragLeaveOptions); target.dispatchEvent(dropEvent); } var dragEndEvent = new DragEvent('dragend', dragLeaveOptions); source.dispatchEvent(dragEndEvent); callback.call(true); } var source = arguments[0], target = arguments[1], step_delay = arguments[2], drop_modifier_keys = arguments[3], callback = arguments[4]; var dt = new DataTransfer(); var opts = { cancelable: true, bubbles: true, dataTransfer: dt }; while (source && !source.draggable) { source = source.parentElement; } if (source.tagName == 'A'){ dt.setData('text/uri-list', source.href); dt.setData('text', source.href); } if (source.tagName == 'IMG'){ dt.setData('text/uri-list', source.src); dt.setData('text', source.src); } var dragEvent = new DragEvent('dragstart', opts); source.dispatchEvent(dragEvent); window.setTimeout(dragEnterTarget, step_delay); JS
-
LEGACY_DRAG_CHECK =
# File 'lib/capybara/selenium/extensions/html5_drag.rb', line 104<<~JS (function(el){ if ([true, null].indexOf(window.capybara_mousedown_prevented) >= 0){ return true; } do { if (el.draggable) return false; } while (el = el.parentElement ); return true; })(arguments[0]) JS
-
MOUSEDOWN_TRACKER =
# File 'lib/capybara/selenium/extensions/html5_drag.rb', line 97<<~JS window.capybara_mousedown_prevented = null; document.addEventListener('mousedown', ev => { window.capybara_mousedown_prevented = ev.defaultPrevented; }, { once: true, passive: true }) JS
Instance Method Summary
-
#drag_to(element, html5: nil, delay: 0.05, drop_modifiers: [])
Implement methods to emulate HTML5 drag and drop.
- #html5_drop(*args) private
- #perform_html5_drag(element, delay, drop_modifiers) private
- #perform_legacy_drag(element, drop_modifiers) private
Instance Method Details
#drag_to(element, html5: nil, delay: 0.05, drop_modifiers: [])
Implement methods to emulate HTML5 drag and drop
# File 'lib/capybara/selenium/extensions/html5_drag.rb', line 7
def drag_to(element, html5: nil, delay: 0.05, drop_modifiers: []) drop_modifiers = Array(drop_modifiers) driver.execute_script MOUSEDOWN_TRACKER scroll_if_needed { browser_action.click_and_hold(native).perform } html5 = !driver.evaluate_script(LEGACY_DRAG_CHECK, self) if html5.nil? if html5 perform_html5_drag(element, delay, drop_modifiers) else perform_legacy_drag(element, drop_modifiers) end end
#html5_drop(*args) (private)
[ GitHub ]# File 'lib/capybara/selenium/extensions/html5_drag.rb', line 36
def html5_drop(*args) if args[0].is_a? String input = driver.evaluate_script ATTACH_FILE input.set_file(args) driver.execute_script DROP_FILE, self, input else items = args.flat_map do |arg| arg.map { |(type, data)| { type: type, data: data } } end driver.execute_script DROP_STRING, items, self end end
#perform_html5_drag(element, delay, drop_modifiers) (private)
[ GitHub ]# File 'lib/capybara/selenium/extensions/html5_drag.rb', line 31
def perform_html5_drag(element, delay, drop_modifiers) driver.evaluate_async_script HTML5_DRAG_DROP_SCRIPT, self, element, delay * 1000, normalize_keys(drop_modifiers) browser_action.release.perform end
#perform_legacy_drag(element, drop_modifiers) (private)
[ GitHub ]# File 'lib/capybara/selenium/extensions/html5_drag.rb', line 22
def perform_legacy_drag(element, drop_modifiers) element.scroll_if_needed do # browser_action.move_to(element.native).release.perform keys_down = modifiers_down(browser_action, drop_modifiers) keys_up = modifiers_up(keys_down.move_to(element.native).release, drop_modifiers) keys_up.perform end end