summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/test_utils/simulate_drag.js
blob: e39213cb0987d198d7e98a4ef2b1e7190e977818 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
function simulateEvent(el, type, options = {}) {
  let event;
  if (!el) return null;

  if (/^mouse/.test(type)) {
    event = el.ownerDocument.createEvent('MouseEvents');
    event.initMouseEvent(type, true, true, el.ownerDocument.defaultView,
    options.button, options.screenX, options.screenY, options.clientX, options.clientY,
    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el);
  } else {
    event = el.ownerDocument.createEvent('CustomEvent');

    event.initCustomEvent(type, true, true, el.ownerDocument.defaultView,
    options.button, options.screenX, options.screenY, options.clientX, options.clientY,
    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, el);

    event.dataTransfer = {
      data: {},

      setData(key, val) {
        this.data[key] = val;
      },

      getData(key) {
        return this.data[key];
      },
    };
  }

  if (el.dispatchEvent) {
    el.dispatchEvent(event);
  } else if (el.fireEvent) {
    el.fireEvent(`on${type}`, event);
  }

  return event;
}

function isLast(target) {
  const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
  const children = el.children;

  return children.length - 1 === target.index;
}

function getTarget(target) {
  const el = typeof target.el === 'string' ? document.getElementById(target.el.substr(1)) : target.el;
  const children = el.children;

  return (
    children[target.index] ||
    children[target.index === 'first' ? 0 : -1] ||
    children[target.index === 'last' ? children.length - 1 : -1] ||
    el
  );
}

function getRect(el) {
  const rect = el.getBoundingClientRect();
  const width = rect.right - rect.left;
  const height = (rect.bottom - rect.top) + 10;

  return {
    x: rect.left,
    y: rect.top,
    cx: rect.left + (width / 2),
    cy: rect.top + (height / 2),
    w: width,
    h: height,
    hw: width / 2,
    wh: height / 2,
  };
}

export default function simulateDrag(options) {
  const { to, from } = options;
  to.el = to.el || from.el;

  const fromEl = getTarget(from);
  const toEl = getTarget(to);
  const firstEl = getTarget({
    el: to.el,
    index: 'first',
  });
  const lastEl = getTarget({
    el: options.to.el,
    index: 'last',
  });

  const fromRect = getRect(fromEl);
  const toRect = getRect(toEl);
  const firstRect = getRect(firstEl);
  const lastRect = getRect(lastEl);

  const startTime = new Date().getTime();
  const duration = options.duration || 1000;

  simulateEvent(fromEl, 'mousedown', {
    button: 0,
    clientX: fromRect.cx,
    clientY: fromRect.cy,
  });

  if (options.ontap) options.ontap();
  window.SIMULATE_DRAG_ACTIVE = 1;

  if (options.to.index === 0) {
    toRect.cy = firstRect.y;
  } else if (isLast(options.to)) {
    toRect.cy = lastRect.y + lastRect.h + 50;
  }

  const dragInterval = setInterval(() => {
    const progress = (new Date().getTime() - startTime) / duration;
    const x = (fromRect.cx + ((toRect.cx - fromRect.cx) * progress));
    const y = (fromRect.cy + ((toRect.cy - fromRect.cy) * progress));
    const overEl = fromEl.ownerDocument.elementFromPoint(x, y);

    simulateEvent(overEl, 'mousemove', {
      clientX: x,
      clientY: y,
    });

    if (progress >= 1) {
      if (options.ondragend) options.ondragend();
      simulateEvent(toEl, 'mouseup');
      clearInterval(dragInterval);
      window.SIMULATE_DRAG_ACTIVE = 0;
    }
  }, 100);

  return {
    target: fromEl,
    fromList: fromEl.parentNode,
    toList: toEl.parentNode,
  };
}